1 { Finds missing svn revisions in wiki list format,
2   used for updating the wiki pages fixes branch.
3 
4   Usage:
5     ./findmissingsvnrevisionwiki -h
6     ./findmissingsvnrevisionwiki -s <svnlogfile> -w <wiki-list-file>
7 
8   Copyright (C) 2017 Mattias Gaertner mattias@freepascal.org
9 
10   This library is free software; you can redistribute it and/or modify it
11   under the terms of the GNU Library General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or (at your
13   option) any later version with the following modification:
14 
15   As a special exception, the copyright holders of this library give you
16   permission to link this library with independent modules to produce an
17   executable, regardless of the license terms of these independent modules,and
18   to copy and distribute the resulting executable under terms of your choice,
19   provided that you also meet, for each linked independent module, the terms
20   and conditions of the license of that module. An independent module is a
21   module which is not derived from or based on this library. If you modify
22   this library, you may extend this exception to your version of the library,
23   but you are not obligated to do so. If you do not wish to do so, delete this
24   exception statement from your version.
25 
26   This program is distributed in the hope that it will be useful, but WITHOUT
27   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
28   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
29   for more details.
30 
31   You should have received a copy of the GNU Library General Public License
32   along with this library; if not, write to the Free Software Foundation,
33   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 }
35 program find_missing_wiki_svnrevisions;
36 
37 {$mode objfpc}{$H+}
38 
39 uses
40   Classes, SysUtils, CustApp, LazFileUtils;
41 
42 type
43 
44   { TMissingWikiSVNRevisions }
45 
46   TMissingWikiSVNRevisions = class(TCustomApplication)
47   protected
48     procedure DoRun; override;
49     procedure ParamError(Msg: string);
50     procedure ExtractSVN(Lines: TStrings; UserFilter: String);
51     procedure ExtractWikiRevs(Lines: TStrings);
52   public
53     constructor Create(TheOwner: TComponent); override;
54     destructor Destroy; override;
55     procedure WriteHelp; virtual;
56   end;
57 
58 { TMissingWikiSVNRevisions }
59 
60 procedure TMissingWikiSVNRevisions.DoRun;
61 var
62   ErrorMsg, SVNFile, WikiFile, Line, Rev, UserFilter: String;
63   SVNLines, WIKILines: TStringList;
64   i: Integer;
65   p: SizeInt;
66 begin
67   // quick check parameters
68   ErrorMsg:=CheckOptions('hs:w:u:', 'help svnlog wikilist user');
69   if ErrorMsg<>'' then
70     ParamError(ErrorMsg);
71 
72   // parse parameters
73   if HasOption('h', 'help') then begin
74     WriteHelp;
75     Halt;
76   end;
77 
78   if not HasOption('s','svnlog') then begin
79     WriteHelp;
80     Halt;
81   end;
82   if not HasOption('w','wikilist') then begin
83     WriteHelp;
84     Halt;
85   end;
86 
87   SVNFile:=ExpandFileNameUTF8(GetOptionValue('s','svnlog'));
88   if not FileExists(SVNFile) then
89     ParamError('svn file not found "'+SVNFile+'"');
90 
91   WikiFile:=ExpandFileNameUTF8(GetOptionValue('w','wikilist'));
92   if not FileExists(WikiFile) then
93     ParamError('wiki list file not found "'+WikiFile+'"');
94 
95   UserFilter:=GetOptionValue('u','user');
96 
97   SVNLines:=TStringList.Create;
98   SVNLines.LoadFromFile(SVNFile);
99   WIKILines:=TStringList.Create;
100   WIKILines.LoadFromFile(WikiFile);
101 
102   ExtractSVN(SVNLines,UserFilter);
103   ExtractWikiRevs(WIKILines);
104   //writeln('SVN: ',SVNLines.Text);
105   //writeln('WIKI: ',WIKILines.Text);
106 
107   // write missing svn revisions
108   writeln('Missing svn revisions:');
109   for i:=0 to SVNLines.Count-1 do begin
110     Line:=SVNLines[i];
111     p:=Pos(' ',Line);
112     Rev:=LeftStr(Line,p-1);
113     if WIKILines.IndexOf(Rev)>=0 then continue;
114     writeln('*',Line);
115   end;
116 
117   // stop program loop
118   Terminate;
119 end;
120 
121 procedure TMissingWikiSVNRevisions.ParamError(Msg: string);
122 begin
123   writeln('Invalid param: ',Msg);
124   writeln;
125   writeln('Usage: ',ExeName,' -h');
126   Halt;
127 end;
128 
129 procedure TMissingWikiSVNRevisions.ExtractSVN(Lines: TStrings;
130   UserFilter: String);
131 { Remove empty and separator lines.
132   Combine each log entry into a single line "*r12345 message"
133 
134 ------------------------------------------------------------------------
135 r54919 | martin | 2017-05-14 12:59:15 +0200 (So, 14 Mai 2017) | 1 line
136 
137 Fixed compile ifdef
138 
139 ->
140 
141 *r54919 Fixed compile ifdef
142 }
143 const MaxCol=80;
144 var
145   i: Integer;
146   Line, Revision, UserName: String;
147   p, StartP: PChar;
148 begin
149   i:=0;
150   UserName:='';
151   while i<Lines.Count do begin
152     Line:=Lines[i];
153     p:=PChar(Line);
154     if (LeftStr(Line,10)='----------') or (Trim(Line)='') then begin
155       Lines.Delete(i);
156     end else if (p^='r') and (p[1] in ['0'..'9']) then begin
157       // revision
158       inc(p,1);
159       while p^ in ['0'..'9'] do inc(p);
160       Revision:=LeftStr(Line,p-PChar(Line)); // 'r12345'
161       while p^ in [' ','|'] do inc(p);
162       StartP:=p;
163       while not (p^ in [' ','|',#0]) do inc(p);
164       UserName:=copy(Line,StartP-PChar(Line)+1,p-StartP);
165       if (UserFilter<>'') and (UserName<>UserFilter) then
166         Lines.Delete(i)
167       else begin
168         Lines[i]:=Revision;
169         inc(i);
170       end;
171     end else begin
172       // comment
173       Line:=Trim(Line);
174       if (UserFilter='') or (UserName=UserFilter) then begin
175         if (i>0) and (length(Lines[i-1])<MaxCol) then begin
176           Line:=Lines[i-1]+' '+Line;
177           if length(Line)>MaxCol then
178             Line:=LeftStr(Line,MaxCol)+'...';
179           Lines[i-1]:=Line;
180         end;
181       end;
182       Lines.Delete(i);
183     end;
184   end;
185 end;
186 
187 procedure TMissingWikiSVNRevisions.ExtractWikiRevs(Lines: TStrings);
188 var
189   i: Integer;
190   Line: String;
191   p: PChar;
192 begin
193   i:=0;
194   while i<Lines.Count do begin
195     Line:=Lines[i];
196     if Trim(Line)='' then begin
197       Lines.Delete(i);
198     end else begin
199       p:=PChar(Line);
200       if (p[0]='*') and (p[1]='r') and (p[2] in ['0'..'9']) then begin
201         inc(p,2);
202         while p^ in ['0'..'9'] do inc(p);
203         Lines[i]:=copy(Line,2,p-PChar(Line)-1); // 'r12345'
204         inc(i);
205       end else begin
206         writeln('WARNING: invalid wiki line: "',Line,'"');
207         Lines.Delete(i);
208       end;
209     end;
210   end;
211 end;
212 
213 constructor TMissingWikiSVNRevisions.Create(TheOwner: TComponent);
214 begin
215   inherited Create(TheOwner);
216   StopOnException:=True;
217 end;
218 
219 destructor TMissingWikiSVNRevisions.Destroy;
220 begin
221   inherited Destroy;
222 end;
223 
224 procedure TMissingWikiSVNRevisions.WriteHelp;
225 begin
226   writeln('Usage: ', ExeName, ' -s svn.log -w wikilist.txt [-u username]');
227   writeln;
228   writeln('Shows svn log entries not listed in the wikilist.');
229   writeln;
230   writeln('-h, --help');
231   writeln('        show this help');
232   writeln('-s <file>, --svnlog=<file>');
233   writeln('        SVN log, e.g. "svn log --limit=1000 > log.txt"');
234   writeln('-w <file>, --wikilist=<file>');
235   writeln('        List in wiki format: Every line "*r12345 text"');
236   writeln('-u <user>, --user=<user>');
237   writeln('        only user ');
238 end;
239 
240 var
241   Application: TMissingWikiSVNRevisions;
242 begin
243   Application:=TMissingWikiSVNRevisions.Create(nil);
244   Application.Title:='Find missing svn revisions in wiki';
245   Application.Run;
246   Application.Free;
247 end.
248 
249