{ 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+} {.$define debugmatches} interface uses Classes, SysUtils, CustApp, RegExpr, Math, UFilter, UWriter; type { TLogFilterApplication } TLogFilterApplication = class(TCustomApplication) protected FLineFilters: TLineFilters; FCurrentLineFilter: TLineFilter; FCommandMatcher: TRegExpr; FCommandFileName: String; FLogFileName: String; FWriter: TWriterList; 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 { TLogFilterApplication } procedure TLogFilterApplication.DoRun; var commandFile: TextFile; logFile: TextFile; groupRanges: TGroupRanges; lineFilter: TLineFilter; line: String; begin if HasOption('h', 'help') or not HasOption('c', 'commandfile') then begin WriteHelp; Terminate; Exit; end; FCommandFileName := GetOptionValue('c', 'commandfile'); if not FileExists(FCommandFileName) then begin Writeln('Commandfile not found: ', FCommandFileName); ExitCode := 1; Terminate; Exit; end; AssignFile(commandFile, FCommandFileName); Reset(commandFile); // 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; CloseFile(commandFile); if HasOption('f', 'logfile') then FLogFileName := GetOptionValue('f', 'logfile'); if not FileExists(FLogFileName) then begin Writeln('Logfile not found: ', FLogFileName); ExitCode := 1; Terminate; Exit; end; FWriter.Add(TConsoleWriter.Create); if HasOption('html') then FWriter.Add(THTMLWriter.Create(GetOptionValue('html'))); AssignFile(logFile, FLogFileName); Reset(logFile); // Filter log while not EOF(logFile) do begin Readln(logFile, line); for lineFilter in FLineFilters do begin if lineFilter.Matches(line, groupRanges) then begin WriteContent(line, lineFilter.Filters, groupRanges); Break; end; end; end; CloseFile(logFile); // One run is enough. 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; 'file': begin FLogFileName := AParams; end; end; end; procedure TLogFilterApplication.WriteContent(AContent: String; AFilters: TFilterList; AGroupRanges: TGroupRanges); var i: Integer; matchPos, offset: Integer; highlights: THighlights; highlight: THighlight; group: Byte; writer: TWriter; begin highlights := THighlights.Create; offset := 0; 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 writer in FWriter do writer.WriteContent(AContent, highlights); highlights.Free; end; constructor TLogFilterApplication.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FLineFilters := TLineFilters.Create; FCommandMatcher := TRegExpr.Create('^(\w+)( (.*)|)$'); // 1 = command, 3 = OPTIONAL params FWriter := TWriterList.Create; end; destructor TLogFilterApplication.Destroy; begin FLineFilters.Free; FCommandMatcher.Free; FWriter.Free; inherited Destroy; end; procedure TLogFilterApplication.WriteHelp; begin Writeln('Usage: ', ExtractFileName(ExeName), ' [options]'); Writeln; Writeln('Options:'); Writeln(' -c --commandfile='); Writeln(' specifies the filename with filter commands'); Writeln(' -f --logfile='); Writeln(' specifies the logfile to be parsed'); Writeln(' --html='); Writeln(' outputs filtered results in a formatted HTML file'); Writeln(' -h --help'); Writeln(' show this help screen'); end; end.