msgdif.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. {
  2. $Id$
  3. This program is part of the Free Pascal run time library.
  4. Copyright (c) 1998-2002 by Peter Vreman
  5. Show the differences between two .msg files
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. { May be we need to compare a prefixes of option_help_pages too?
  13. Currently this is not performed }
  14. Program messagedif;
  15. {$h+} {Huge strings}
  16. Uses
  17. Strings;
  18. Type
  19. TEnum = String;
  20. TText = String;
  21. PMsg = ^TMsg;
  22. TMsg = Record
  23. Line, ctxt, cnb : Longint;
  24. enum : TEnum;
  25. text : TText;
  26. comment : pchar;
  27. Next,Prev : PMsg;
  28. FileNext,
  29. Equivalent : PMsg;
  30. end;
  31. Var
  32. OrgFileName,DiffFileName : String;
  33. OrgRoot,DiffRoot : PMsg;
  34. OrgFirst,DiffFirst : PMsg;
  35. Last : PMsg;
  36. const
  37. NewFileName = 'new.msg';
  38. Is_interactive : boolean = false;
  39. Auto_verbosity : boolean = false;
  40. Procedure GetTranslation( p : PMsg);
  41. var
  42. s : string;
  43. i,j : longint;
  44. begin
  45. i:=pos('_',p^.text);
  46. if (i>0) and (i<=5) then
  47. Writeln(P^.Enum,' type "',copy(p^.text,1,i-1),'" "',copy(p^.text,i+1,255),'"')
  48. else
  49. Writeln(P^.enum,' "',p^.text,'"');
  50. Readln(s);
  51. if s='' then
  52. begin
  53. Is_interactive:=false;
  54. exit;
  55. end;
  56. j:=pos('_',s);
  57. if (j>0) and (j<=5) then
  58. begin
  59. if copy(p^.text,1,i)<>copy(s,1,j) then
  60. Writeln('Different verbosity !!');
  61. p^.text:=s;
  62. end
  63. else
  64. p^.text:=copy(p^.text,1,i)+s;
  65. end;
  66. Function NewMsg (Var RM : PMsg; L : Longint; Const E : TEnum;Const T : TText;C : pchar;NbLn,TxtLn : longint) : PMsg;
  67. Var
  68. P,R : PMsg;
  69. begin
  70. New(P);
  71. with P^ do
  72. begin
  73. Line:=L;
  74. Text:=T;
  75. enum:=E;
  76. comment:=c;
  77. cnb:=NbLn;
  78. ctxt:=TxtLn;
  79. next:=Nil;
  80. prev:=Nil;
  81. filenext:=nil;
  82. equivalent:=nil;
  83. if assigned(last) then
  84. last^.FileNext:=P;
  85. last:=P;
  86. end;
  87. R:=RM;
  88. While (R<>Nil) and (UpCase(R^.enum)>UpCase(P^.Enum)) do
  89. begin
  90. P^.Prev:=R;
  91. R:=R^.next;
  92. end;
  93. if assigned(R) and (UpCase(R^.Enum)=UpCase(P^.Enum)) then
  94. Writeln('Error ',R^.Enum,' duplicate');
  95. P^.Next:=R;
  96. If R<>Nil then
  97. R^.Prev:=P;
  98. If P^.Prev<>Nil then
  99. P^.Prev^.Next:=P
  100. else
  101. RM:=P;
  102. NewMsg:=P;
  103. end;
  104. Procedure PrintList(const name : string;R : PMsg);
  105. var
  106. P : PMsg;
  107. f : text;
  108. begin
  109. P:=R;
  110. Assign(f,name);
  111. Rewrite(f);
  112. while assigned(P) do
  113. begin
  114. Writeln(f,UpCase(P^.Enum));
  115. P:=P^.Next;
  116. end;
  117. Close(f);
  118. end;
  119. Procedure Usage;
  120. begin
  121. Writeln('Usage : msgdif [options] <org-file> <dif-file>');
  122. Writeln('Options:');
  123. Writeln(' -i allow to enter translated messages interactively');
  124. Writeln(' -y1 use <org-file> verbosity (do not query acknowledge)');
  125. Writeln('');
  126. Writeln('Generates "',NewFileName,'" that contain the messages from <dif-file>');
  127. Writeln('with a new messages from <org-file>');
  128. Writeln('');
  129. Writeln('Example:');
  130. Writeln(' msgdif errore.msg errorr.msg');
  131. halt(1)
  132. end;
  133. Procedure ProcessOptions;
  134. var
  135. i,count : longint;
  136. begin
  137. Is_interactive:=false;
  138. Auto_verbosity:=false;
  139. count:=paramcount; i:=1;
  140. while (count>0) and (Paramstr(i)[1]='-') do
  141. case UpCase(Paramstr(i)[2]) of
  142. 'I': begin
  143. Is_interactive:=true;
  144. dec(count); Inc(i);
  145. end;
  146. 'Y': case Paramstr(i)[3] of
  147. '1': begin
  148. Auto_verbosity:=true;
  149. dec(count); Inc(i);
  150. end;
  151. else
  152. Writeln ('Error: unknown option ', Paramstr(i));
  153. Usage;
  154. end;
  155. else
  156. Writeln ('Error: unknown option ', Paramstr(i));
  157. Usage;
  158. end;
  159. If Count<>2 then begin
  160. Writeln ('Error: there must be exactly two message files');
  161. Usage;
  162. end;
  163. OrgfileName:=Paramstr(i);
  164. DiffFileName:=Paramstr(i+1);
  165. if (OrgFileName=NewFileName) or (DiffFileName=NewFileName) then
  166. begin
  167. Writeln('The file names must be different from ',NewFileName);
  168. Halt(1);
  169. end;
  170. end;
  171. Procedure ProcessFile (FileName : String; Var Root,First : PMsg);
  172. Const
  173. ArrayLength = 65500;
  174. Var F : Text;
  175. S,prevS : String;
  176. J,LineNo,Count,NbLn,TxtLn : Longint;
  177. chararray : array[0..ArrayLength] of char;
  178. currentindex : longint;
  179. c : pchar;
  180. multiline : boolean;
  181. begin
  182. Assign(F,FileName);
  183. Reset(F);
  184. Write ('Processing: ',Filename,'...');
  185. LineNo:=0;
  186. NbLn:=0;
  187. TxtLn:=0;
  188. Count:=0;
  189. currentindex:=0;
  190. Root:=Nil;
  191. First:=nil;
  192. Last:=nil;
  193. PrevS:='';
  194. multiline:=false;
  195. While not eof(f) do
  196. begin
  197. Readln(F,S);
  198. Inc(LineNo);
  199. If multiline then
  200. begin
  201. PrevS:=PrevS+#10+S; Inc(TxtLn);
  202. if (Length(S)<>0) and (S[1]=']') then
  203. multiline:=false;
  204. end
  205. else
  206. if (length(S)>0) and Not (S[1] in ['%','#']) Then
  207. begin
  208. J:=Pos('=',S);
  209. If j<1 then
  210. writeln (Filename,'(',LineNo,') : Invalid entry')
  211. else
  212. begin
  213. chararray[currentindex]:=#0;
  214. c:=strnew(@chararray);
  215. if PrevS<>'' then
  216. NewMsg(Root,LineNo,Copy(PrevS,1,Pos('=',PrevS)-1),
  217. Copy(PrevS,Pos('=',PrevS)+1,Length(PrevS)),c,NbLn,TxtLn)
  218. else
  219. StrDispose(c);
  220. currentindex:=0;
  221. NbLn:=0; TxtLn:=0;
  222. PrevS:=S; Inc(TxtLn);
  223. if S[j+7]='[' then multiline:=true;
  224. if First=nil then
  225. First:=Root;
  226. Inc(Count);
  227. end;
  228. end
  229. else
  230. begin
  231. if currentindex+length(s)+1>ArrayLength then
  232. Writeln('Comment too long : over ',ArrayLength,' chars')
  233. else
  234. begin
  235. strpcopy(@chararray[currentindex],s+#10);
  236. inc(currentindex,length(s)+1);
  237. inc(NbLn);
  238. end;
  239. end;
  240. end;
  241. chararray[currentindex]:=#0;
  242. c:=strnew(@chararray);
  243. if PrevS<>'' then
  244. NewMsg(Root,LineNo,Copy(PrevS,1,Pos('=',PrevS)-1),
  245. Copy(PrevS,Pos('=',PrevS)+1,Length(PrevS)),c,NbLn,TxtLn);
  246. Writeln (' Done. Read ',LineNo,' lines, got ',Count,' constants.');
  247. Close(f);
  248. end;
  249. Procedure ShowDiff (POrg,PDiff : PMsg);
  250. Var
  251. count,orgcount,diffcount : longint;
  252. Procedure NotFound (Org : Boolean; P : PMsg);
  253. begin
  254. With P^ do
  255. If Org Then
  256. Writeln ('Not found in ',DiffFileName,' : ',Enum,' ',OrgFileName,'(',Line,')')
  257. else
  258. Writeln ('Extra in ',DiffFileName,'(',line,') : ',enum);
  259. if org then
  260. inc(orgcount)
  261. else
  262. inc(diffcount);
  263. end;
  264. begin
  265. orgcount:=0;
  266. diffcount:=0;
  267. count:=0;
  268. While (Porg<>Nil) and (PDiff<>Nil) do
  269. begin
  270. // Writeln (POrg^.enum,'<=>',PDiff^.Enum);
  271. If UpCase(Porg^.Enum)>UpCase(PDiff^.Enum) then
  272. begin
  273. NotFound (True,Porg);
  274. POrg:=POrg^.Next
  275. end
  276. else If UpCase(POrg^.enum)=UpCase(PDiff^.Enum) then
  277. begin
  278. inc(count);
  279. POrg^.Equivalent:=PDiff;
  280. PDiff^.Equivalent:=POrg;
  281. POrg:=POrg^.Next;
  282. PDiff:=PDiff^.Next;
  283. end
  284. else
  285. begin
  286. NotFound (False,PDiff);
  287. PDiff:=PDiff^.Next
  288. end;
  289. end;
  290. While POrg<>Nil do
  291. begin
  292. NotFound(True,Porg);
  293. POrg:=pOrg^.Next;
  294. end;
  295. While PDiff<>Nil do
  296. begin
  297. NotFound(False,PDiff);
  298. PDiff:=PDiff^.Next;
  299. end;
  300. Writeln(count,' messages found in common to both files');
  301. Writeln(orgcount,' messages only in ',OrgFileName);
  302. Writeln(diffcount,' messages only in ',DiffFileName);
  303. end;
  304. type TArgSet = set of 0..31;
  305. function MsgToSet(const Msg, FileName: string; var R: TArgSet): Boolean;
  306. var
  307. i, j, num : integer;
  308. code : word;
  309. begin
  310. R:=[];
  311. MsgToSet:=false;
  312. for i:=1 to Length(Msg) do
  313. if Msg[i]='$' then
  314. begin
  315. j:=i+1;
  316. while Msg[j] in ['0'..'9'] do Inc(j);
  317. if j > i+1 then
  318. begin
  319. val(copy(Msg,i+1,j-i-1),num,code);
  320. if num > high(TArgSet) then begin
  321. WriteLn('Error in ', FileName,': ', Msg);
  322. WriteLn(' number at position ', i);
  323. WriteLn(' must be LE ', high(TArgSet));
  324. Exit;
  325. end;
  326. R:=R+[num];
  327. end;
  328. end;
  329. MsgToSet:=true;
  330. end;
  331. procedure CheckParm(const s1, s2: string);
  332. var
  333. R1, R2: TArgSet;
  334. begin
  335. if MsgToSet(s1,OrgFileName, R1) <> true then Exit;
  336. if MsgToSet(s2,DiffFileName,R2) <> true then Exit;
  337. if R1<>R2 then begin
  338. WriteLn('Error: set of arguments is different');
  339. WriteLn(' ',s1);
  340. WriteLn(' ',s2);
  341. end;
  342. end;
  343. procedure WriteReorderedFile(FileName : string;orgnext,diffnext : PMsg);
  344. var t,t2,t3 : text;
  345. i,ntcount : longint;
  346. j : integer;
  347. s,s2,s3 : string;
  348. is_msg : boolean;
  349. nextdiffkept : pmsg;
  350. begin
  351. ntcount:=0;
  352. Assign(t,FileName);
  353. Rewrite(t);
  354. Writeln(t,'%%% Reordering of ',DiffFileName,' respective to ',OrgFileName);
  355. Writeln(t,'%%% Contains all comments from ',DiffFileName);
  356. Assign(t2,DiffFileName);
  357. Reset(t2);
  358. Assign(t3,OrgFileName);
  359. Reset(t3);
  360. i:=2;
  361. s:='';s3:='';
  362. nextdiffkept:=diffnext;
  363. while assigned(nextdiffkept) and (nextdiffkept^.equivalent=nil) do
  364. nextdiffkept:=nextdiffkept^.filenext;
  365. { First write the header of diff }
  366. repeat
  367. Readln(t2,s);
  368. is_msg:=(pos('=',s)>1) and (s[1]<>'%') and (s[1]<>'#');
  369. if not is_msg then
  370. begin
  371. Writeln(t,s);
  372. inc(i);
  373. end;
  374. until is_msg;
  375. { Write all messages in Org order }
  376. while assigned(orgnext) do
  377. begin
  378. if not assigned(orgnext^.equivalent) then
  379. begin
  380. { Insert a new error msg with the english comments }
  381. Writeln('New error ',orgnext^.enum,' added');
  382. If Is_interactive then
  383. GetTranslation(orgnext);
  384. Writeln(t,orgnext^.enum,'=',orgnext^.text);
  385. inc(i,orgnext^.ctxt);
  386. Write(t,orgnext^.comment);
  387. inc(i,orgnext^.cnb);
  388. end
  389. else
  390. begin
  391. inc(i);
  392. if orgnext^.text=orgnext^.equivalent^.text then
  393. begin
  394. Writeln(FileName,'(',i,') ',orgnext^.enum,' not translated');
  395. If Is_interactive then
  396. GetTranslation(orgnext^.equivalent);
  397. if orgnext^.text=orgnext^.equivalent^.text then
  398. inc(ntcount);
  399. end;
  400. s2:=orgnext^.text;
  401. j:=pos('_',copy(s2,7,20)) + 6;
  402. s2:=upcase(copy(s2,1,j));
  403. s3:=orgnext^.equivalent^.text;
  404. j:=pos('_',copy(s3,7,20)) + 6;
  405. s3:=upcase(copy(s3,1,j));
  406. { that are the conditions in verbose unit }
  407. if (length(s3)<12) and (s2<>s3) then
  408. begin
  409. Writeln('Warning: different options for ',orgnext^.enum);
  410. Writeln(' ',orgnext^.text);
  411. Writeln(' ',orgnext^.equivalent^.text);
  412. s:='N';
  413. if Auto_verbosity then
  414. s:='Y'
  415. else
  416. If Is_interactive then
  417. begin
  418. Write('Use ',s2,' verbosity ? [y/n] ');
  419. Readln(s);
  420. end;
  421. if UpCase(s[1])='Y' then
  422. begin
  423. orgnext^.equivalent^.text:=s2+copy(orgnext^.equivalent^.text,
  424. length(s3)+1,Length(orgnext^.equivalent^.text));
  425. WriteLn(' Using ', s2);
  426. end;
  427. end;
  428. CheckParm(orgnext^.text, orgnext^.equivalent^.text);
  429. Writeln(t,orgnext^.enum,'=',orgnext^.equivalent^.text);
  430. Dec(i); Inc(i,orgnext^.equivalent^.ctxt);
  431. if assigned(orgnext^.equivalent^.comment) and
  432. (strlen(orgnext^.equivalent^.comment)>0) then
  433. begin
  434. Write(t,orgnext^.equivalent^.comment);
  435. inc(i,orgnext^.equivalent^.cnb);
  436. end
  437. else if assigned(orgnext^.comment) and
  438. (strlen(orgnext^.comment)>0) then
  439. begin
  440. Writeln('Comment from ',OrgFileName,' for enum ',orgnext^.enum,' added');
  441. Write(t,orgnext^.comment);
  442. inc(i,orgnext^.cnb);
  443. end;
  444. end;
  445. orgnext:=orgnext^.filenext;
  446. end;
  447. while assigned(diffnext) do
  448. begin
  449. if not assigned(diffnext^.Equivalent) then
  450. begin
  451. { Skip removed enum in errore.msg}
  452. { maybe a renaming of an enum !}
  453. Writeln(diffnext^.enum,' commented out');
  454. Writeln(t,'%%% ',diffnext^.enum,'=',diffnext^.text);
  455. inc(i,diffnext^.ctxt);
  456. Write(t,diffnext^.comment);
  457. inc(i,diffnext^.cnb);
  458. end;
  459. diffnext:=diffnext^.filenext;
  460. end;
  461. Close(t);
  462. Close(t2);
  463. Close(t3);
  464. Writeln(ntcount,' not translated items found');
  465. end;
  466. begin
  467. ProcessOptions;
  468. ProcessFile(OrgFileName,orgroot,orgfirst);
  469. ProcessFile(DiffFileName,diffRoot,difffirst);
  470. PrintList('org.lst',OrgRoot);
  471. PrintList('diff.lst',DiffRoot);
  472. ShowDiff (OrgRoot,DiffRoot);
  473. WriteReorderedFile(NewFileName,orgfirst,difffirst);
  474. end.
  475. {
  476. $Log$
  477. Revision 1.8 2002-05-18 13:34:27 peter
  478. * readded missing revisions
  479. Revision 1.7 2002/05/16 19:46:53 carl
  480. + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
  481. + try to fix temp allocation (still in ifdef)
  482. + generic constructor calls
  483. + start of tassembler / tmodulebase class cleanup
  484. }