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.