From c9b6b19479d82b6cb592b36ff321157113bd5b58 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 24 Sep 2015 14:06:37 +0200 Subject: [PATCH] Split implementation into units --- UApp.pas | 242 ++++++++++++++++++++++++++++++++++++++++++ UFilter.pas | 140 ++++++++++++++++++++++++ logfilter.lpi | 10 +- logfilter.pas | 288 ++------------------------------------------------ 4 files changed, 397 insertions(+), 283 deletions(-) create mode 100644 UApp.pas create mode 100644 UFilter.pas diff --git a/UApp.pas b/UApp.pas new file mode 100644 index 0000000..4fc7b88 --- /dev/null +++ b/UApp.pas @@ -0,0 +1,242 @@ +{ + This file is part of logfilter. + + Copyright (C) 2015 Andreas Schneider + + logfilter is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + logfilter is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with logfilter. If not, see . +} +unit UApp; + +{$mode objfpc}{$H+} +{$modeswitch advancedrecords} + +{.$define debugmatches} + +interface + +uses + Classes, SysUtils, CustApp, math, fgl, RegExpr, crt, + UFilter; + +type + + { THighlight } + + THighlight = record + FGColor: Byte; + BGColor: Byte; + Start: Integer; + Length: Integer; + class operator =(A, B: THighlight): Boolean; + end; + THighlights = specialize TFPGList; + + { TLogFilterApplication } + + TLogFilterApplication = class(TCustomApplication) + protected + FLineFilters: TLineFilters; + FCurrentLineFilter: TLineFilter; + FCommandMatcher: TRegExpr; + procedure DoRun; override; + procedure ProcessCommand(ACommand, AParams: String); + procedure WriteContent(AContent: String; AFilters: TFilterList; + AGroupRanges: TGroupRanges); + public + constructor Create(TheOwner: TComponent); override; + destructor Destroy; override; + procedure WriteHelp; virtual; + end; + +implementation + +{ THighlight } + +class operator THighlight. = (A, B: THighlight): Boolean; +begin + Result := (A.Start = B.Start) and (A.Length = B.Length); +end; + +function CompareHighlights(const AHL1: THighlight; const AHL2: THighlight): Integer; +begin + if AHL1.Start = AHL2.Start then + begin + Result := AHL1.Length - AHL2.Length; + end else + Result := AHL1.Start - AHL2.Start; +end; + +{ TLogFilterApplication } + +procedure TLogFilterApplication.DoRun; +var + commandFile: TextFile; + logFile: TextFile; + groupRanges: TGroupRanges; + lineFilter: TLineFilter; + line: String; +begin + AssignFile(commandFile, ParamStr(1)); + Reset(commandFile); + AssignFile(logFile, ParamStr(2)); + Reset(logFile); + + // Parse command file first + while not EOF(commandFile) do + begin + Readln(commandFile, line); + if FCommandMatcher.Exec(line) then + ProcessCommand(FCommandMatcher.Match[1], FCommandMatcher.Match[3]); + end; + + // Filter log + while not EOF(logFile) do + begin + Readln(logFile, line); + for lineFilter in FLineFilters do + begin + if lineFilter.Matches(line, groupRanges) then + WriteContent(line, lineFilter.Filters, groupRanges); + end; + end; + + CloseFile(commandFile); //TODO: narrow scope! + CloseFile(logFile); + + Terminate; +end; + +procedure TLogFilterApplication.ProcessCommand(ACommand, AParams: String); +var + highlightFilter: THighlightFilter; +begin + case LowerCase(ACommand) of + 'filter': + begin + FCurrentLineFilter := TLineFilter.Create(AParams); + FLineFilters.Add(FCurrentLineFilter); + end; + 'highlight': + begin + highlightFilter := THighlightFilter.Create(AParams); + FCurrentLineFilter.Filters.Add(highlightFilter); + end; + end; +end; + +procedure TLogFilterApplication.WriteContent(AContent: String; + AFilters: TFilterList; AGroupRanges: TGroupRanges); +var + i: Integer; + matchPos, offset, lastPos: Integer; + highlights: THighlights; + highlight: THighlight; + group: Byte; +begin + highlights := THighlights.Create; + + offset := 0; + lastPos := 1; + + for i := 0 to AFilters.Count - 1 do + begin + if AFilters[i].Expression.Exec(AContent) then + begin + repeat + // We need these values anyway. + matchPos := AFilters[i].Expression.MatchPos[0]; + offset := AFilters[i].Expression.MatchLen[0]; + + group := AFilters[i].Group; + if group < Length(AGroupRanges) then + begin + if (matchPos + offset < AGroupRanges[group].StartIdx) or + (matchPos > AGroupRanges[group].EndIdx) then + continue; //Pointless; nothing we can do here + + highlight.Start := Max(AGroupRanges[group].StartIdx, matchPos); + highlight.Length := Min(AGroupRanges[group].EndIdx - highlight.Start, + highlight.Start + offset - matchPos); + + {$ifdef debugmatches} + writeln(' Highlight: ', highlight.Start, ', ', highlight.Length); + writeln(' MatchPos: ', matchPos, ', StartIdx: ', AGroupRanges[group].StartIdx); + writeln(' Offset: ', offset, ', EndIdx: ', AGroupRanges[group].EndIdx); + {$endif} + end else + begin + highlight.Start := matchPos; + highlight.Length := offset; + end; + + highlight.FGColor := AFilters[i].FGColor; + highlight.BGColor := AFilters[i].BGColor; + highlights.Add(highlight); + until not AFilters[i].Expression.ExecNext; + end; + end; + + highlights.Sort(@CompareHighlights); + + // Sanitize highlights + for i := 0 to highlights.Count - 2 do + begin + if (highlights[i].Start + highlights[i].Length) > highlights[i+1].Start then + begin + highlight := highlights[i]; + highlight.Length := highlights[i+1].Start - highlights[i].Start; + highlights[i] := highlight; + end; + end; + + for highlight in highlights do + begin + matchPos := highlight.Start; + offset := highlight.Length; + write(Copy(AContent, lastPos, matchPos - lastPos)); + if highlight.FGColor < $FF then + TextColor(highlight.FGColor); + if highlight.BGColor < $FF then + TextBackground(highlight.BGColor); + write(Copy(AContent, matchPos, offset)); + NormVideo; + lastPos := matchPos + offset; + end; + + writeln(Copy(AContent, lastPos, Length(AContent))); + + highlights.Free; +end; + +constructor TLogFilterApplication.Create(TheOwner: TComponent); +begin + inherited Create(TheOwner); + FLineFilters := TLineFilters.Create; + FCommandMatcher := TRegExpr.Create('^(\w+)( (.*)|)$'); // 1 = command, 3 = OPTIONAL params +end; + +destructor TLogFilterApplication.Destroy; +begin + FLineFilters.Free; + FCommandMatcher.Free; + inherited Destroy; +end; + +procedure TLogFilterApplication.WriteHelp; +begin + +end; + +end. + diff --git a/UFilter.pas b/UFilter.pas new file mode 100644 index 0000000..3cda62a --- /dev/null +++ b/UFilter.pas @@ -0,0 +1,140 @@ +{ + This file is part of logfilter. + + Copyright (C) 2015 Andreas Schneider + + logfilter is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + logfilter is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with logfilter. If not, see . +} +unit UFilter; + +{$mode objfpc}{$H+} + +{.$define debugmatches} + +interface + +uses + Classes, SysUtils, fgl, RegExpr; + +type + { THighlightFilter } + + THighlightFilter = class + Expression: TRegExpr; + FGColor: Byte; + BGColor: Byte; + Group: Byte; + constructor Create(AString: String); + destructor Destroy; override; + class var + FilterExpression: TRegExpr; + ParamExpression: TRegExpr; + end; + TFilterList = specialize TFPGObjectList; + + { TGroupRange } + + TGroupRange = record + StartIdx: Integer; + EndIdx: Integer; + end; + TGroupRanges = array of TGroupRange; + + { TLineFilter } + + TLineFilter = class + constructor Create(AExpression: String); + destructor Destroy; override; + protected + FExpression: TRegExpr; + FFilters: TFilterList; + public + function Matches(ALine: String; var GroupRanges: TGroupRanges): Boolean; + property Filters: TFilterList read FFilters; + end; + TLineFilters = specialize TFPGObjectList; + +implementation + +{ THighlightFilter } + +constructor THighlightFilter.Create(AString: String); +begin + FGColor := $FF; + BGColor := $FF; + Group := $FF; + if FilterExpression.Exec(AString) then + begin + Expression := TRegExpr.Create(Copy(AString, 1, FilterExpression.MatchPos[0] - 1)); + if ParamExpression.Exec(FilterExpression.Match[0]) then + repeat + if ParamExpression.Match[1] = 'FG' then + FGColor := StrToInt(ParamExpression.Match[2]) + else if ParamExpression.Match[1] = 'BG' then + BGColor := StrToInt(ParamExpression.Match[2]) + else if ParamExpression.Match[1] = 'Grp' then + Group := StrToInt(ParamExpression.Match[2]); + until not ParamExpression.ExecNext; + end else + Expression := TRegExpr.Create(AString); +end; + +destructor THighlightFilter.Destroy; +begin + Expression.Free; + inherited Destroy; +end; + +{ TLineFilter } + +constructor TLineFilter.Create(AExpression: String); +begin + FExpression := TRegExpr.Create(AExpression); + FFilters := TFilterList.Create; +end; + +destructor TLineFilter.Destroy; +begin + FExpression.Free; + FFilters.Free; + inherited Destroy; +end; + +function TLineFilter.Matches(ALine: String; var GroupRanges: TGroupRanges + ): Boolean; +var + i: Integer; +begin + Result := FExpression.Exec(ALine); + if Result then + begin + {$ifdef debugmatches}writeln(' Match: ', FExpression.Match[0]);{$endif} + SetLength(GroupRanges, FExpression.SubExprMatchCount + 1); + for i := 0 to FExpression.SubExprMatchCount do + begin + {$ifdef debugmatches}writeln(' [', i, ']: ', FExpression.Match[i]);{$endif} + GroupRanges[i].StartIdx := FExpression.MatchPos[i]; + GroupRanges[i].EndIdx := GroupRanges[i].StartIdx + FExpression.MatchLen[i]; + end; + end; +end; + +initialization + THighlightFilter.FilterExpression := TRegExpr.Create('( (FG|BG|Grp)(\d+))*$'); + THighlightFilter.ParamExpression := TRegExpr.Create('(FG|BG|Grp)(\d+)'); +finalization + THighlightFilter.FilterExpression.Free; + THighlightFilter.ParamExpression.Free; +end. + diff --git a/logfilter.lpi b/logfilter.lpi index 8926bba..ccec9cb 100644 --- a/logfilter.lpi +++ b/logfilter.lpi @@ -30,11 +30,19 @@ - + + + + + + + + + diff --git a/logfilter.pas b/logfilter.pas index bd3676c..d658309 100644 --- a/logfilter.pas +++ b/logfilter.pas @@ -19,293 +19,17 @@ program logfilter; {$mode objfpc}{$H+} -{$modeswitch advancedrecords} - -{.$define debugmatches} uses - Classes, SysUtils, math, fgl, RegExpr, crt; - -type - - { THighlightFilter } - - THighlightFilter = class - Expression: TRegExpr; - FGColor: Byte; - BGColor: Byte; - Group: Byte; - constructor Create(AString: String); - destructor Destroy; override; - class var - FilterExpression: TRegExpr; - ParamExpression: TRegExpr; - end; - TFilterList = specialize TFPGObjectList; - - { THighlight } - - THighlight = record - FGColor: Byte; - BGColor: Byte; - Start: Integer; - Length: Integer; - class operator =(A, B: THighlight): Boolean; - end; - THighlights = specialize TFPGList; - - TGroupRange = record - StartIdx: Integer; - EndIdx: Integer; - end; - TGroupRanges = array of TGroupRange; - - { TLineFilter } - - TLineFilter = class - constructor Create(AExpression: String); - destructor Destroy; override; - protected - FExpression: TRegExpr; - FFilters: TFilterList; - public - function Matches(ALine: String; var GroupRanges: TGroupRanges): Boolean; - property Filters: TFilterList read FFilters; - end; - TLineFilters = specialize TFPGObjectList; - -{ THighlight } - -class operator THighlight. = (A, B: THighlight): Boolean; -begin - Result := (A.Start = B.Start) and (A.Length = B.Length); -end; - - -{ THighlightFilter } - -constructor THighlightFilter.Create(AString: String); -begin - FGColor := $FF; - BGColor := $FF; - Group := $FF; - if FilterExpression.Exec(AString) then - begin - Expression := TRegExpr.Create(Copy(AString, 1, FilterExpression.MatchPos[0] - 1)); - if ParamExpression.Exec(FilterExpression.Match[0]) then - repeat - if ParamExpression.Match[1] = 'FG' then - FGColor := StrToInt(ParamExpression.Match[2]) - else if ParamExpression.Match[1] = 'BG' then - BGColor := StrToInt(ParamExpression.Match[2]) - else if ParamExpression.Match[1] = 'Grp' then - Group := StrToInt(ParamExpression.Match[2]); - until not ParamExpression.ExecNext; - end else - Expression := TRegExpr.Create(AString); -end; - -destructor THighlightFilter.Destroy; -begin - Expression.Free; - inherited Destroy; -end; - -function CompareHighlights(const AHL1: THighlight; const AHL2: THighlight): Integer; -begin - if AHL1.Start = AHL2.Start then - begin - Result := AHL1.Length - AHL2.Length; - end else - Result := AHL1.Start - AHL2.Start; -end; - -{ TLineFilter } - -constructor TLineFilter.Create(AExpression: String); -begin - FExpression := TRegExpr.Create(AExpression); - FFilters := TFilterList.Create; -end; - -destructor TLineFilter.Destroy; -begin - FExpression.Free; - FFilters.Free; - inherited Destroy; -end; - -function TLineFilter.Matches(ALine: String; var GroupRanges: TGroupRanges - ): Boolean; -var - i: Integer; -begin - Result := FExpression.Exec(ALine); - if Result then - begin - {$ifdef debugmatches}writeln(' Match: ', FExpression.Match[0]);{$endif} - SetLength(GroupRanges, FExpression.SubExprMatchCount + 1); - for i := 0 to FExpression.SubExprMatchCount do - begin - {$ifdef debugmatches}writeln(' [', i, ']: ', FExpression.Match[i]);{$endif} - GroupRanges[i].StartIdx := FExpression.MatchPos[i]; - GroupRanges[i].EndIdx := GroupRanges[i].StartIdx + FExpression.MatchLen[i]; - end; - end; -end; - -//----------------------------------------------------------- + Classes, SysUtils, UApp, UFilter; var - lineFilters: TLineFilters; - currentLineFilter: TLineFilter; - commandMatcher: TRegExpr; - commandFile, logFile: TextFile; - line: String; - -procedure ProcessCommand(ACommand, AParams: String); -var - highlightFilter: THighlightFilter; -begin - case LowerCase(ACommand) of - 'filter': - begin - currentLineFilter := TLineFilter.Create(AParams); - lineFilters.Add(currentLineFilter); - end; - 'highlight': - begin - highlightFilter := THighlightFilter.Create(AParams); - currentLineFilter.Filters.Add(highlightFilter); - end; - end; -end; - -procedure WriteContent(AContent: String; AFilters: TFilterList; - AGroupRanges: TGroupRanges); -var - i: Integer; - matchPos, offset, lastPos: Integer; - highlights: THighlights; - highlight: THighlight; - group: Byte; -begin - highlights := THighlights.Create; - - offset := 0; - lastPos := 1; - - for i := 0 to AFilters.Count - 1 do - begin - if AFilters[i].Expression.Exec(AContent) then - begin - repeat - // We need these values anyway. - matchPos := AFilters[i].Expression.MatchPos[0]; - offset := AFilters[i].Expression.MatchLen[0]; - - group := AFilters[i].Group; - if group < Length(AGroupRanges) then - begin - if (matchPos + offset < AGroupRanges[group].StartIdx) or - (matchPos > AGroupRanges[group].EndIdx) then - continue; //Pointless; nothing we can do here - - highlight.Start := Max(AGroupRanges[group].StartIdx, matchPos); - highlight.Length := Min(AGroupRanges[group].EndIdx - highlight.Start, - highlight.Start + offset - matchPos); - - {$ifdef debugmatches} - writeln(' Highlight: ', highlight.Start, ', ', highlight.Length); - writeln(' MatchPos: ', matchPos, ', StartIdx: ', AGroupRanges[group].StartIdx); - writeln(' Offset: ', offset, ', EndIdx: ', AGroupRanges[group].EndIdx); - {$endif} - end else - begin - highlight.Start := matchPos; - highlight.Length := offset; - end; - - highlight.FGColor := AFilters[i].FGColor; - highlight.BGColor := AFilters[i].BGColor; - highlights.Add(highlight); - until not AFilters[i].Expression.ExecNext; - end; - end; - - highlights.Sort(@CompareHighlights); - - // Sanitize highlights - for i := 0 to highlights.Count - 2 do - begin - if (highlights[i].Start + highlights[i].Length) > highlights[i+1].Start then - begin - highlight := highlights[i]; - highlight.Length := highlights[i+1].Start - highlights[i].Start; - highlights[i] := highlight; - end; - end; - - for highlight in highlights do - begin - matchPos := highlight.Start; - offset := highlight.Length; - write(Copy(AContent, lastPos, matchPos - lastPos)); - if highlight.FGColor < $FF then - TextColor(highlight.FGColor); - if highlight.BGColor < $FF then - TextBackground(highlight.BGColor); - write(Copy(AContent, matchPos, offset)); - NormVideo; - lastPos := matchPos + offset; - end; - - writeln(Copy(AContent, lastPos, Length(AContent))); - - highlights.Free; -end; - -var - groupRanges: TGroupRanges; + Application: TLogFilterApplication; begin - AssignFile(commandFile, ParamStr(1)); - Reset(commandFile); - AssignFile(logFile, ParamStr(2)); - Reset(logFile); - - THighlightFilter.FilterExpression := TRegExpr.Create('( (FG|BG|Grp)(\d+))*$'); - THighlightFilter.ParamExpression := TRegExpr.Create('(FG|BG|Grp)(\d+)'); - lineFilters := TLineFilters.Create; - commandMatcher := TRegExpr.Create('^(\w+)( (.*)|)$'); // 1 = command, 3 = OPTIONAL params - - try - // Parse command file first - while not EOF(commandFile) do - begin - Readln(commandFile, line); - if commandMatcher.Exec(line) then - ProcessCommand(commandMatcher.Match[1], commandMatcher.Match[3]); - end; - - // Filter log - while not EOF(logFile) do - begin - Readln(logFile, line); - for currentLineFilter in lineFilters do - begin - if currentLineFilter.Matches(line, groupRanges) then - WriteContent(line, currentLineFilter.Filters, groupRanges); - end; - end; - finally - commandMatcher.Free; - lineFilters.Free; - THighlightFilter.FilterExpression.Free; - THighlightFilter.ParamExpression.Free; - end; - - CloseFile(commandFile); //TODO: narrow scope! - CloseFile(logFile); + Application := TLogFilterApplication.Create(nil); + Application.Title := 'LogFilter'; + Application.Run; + Application.Free; end.