msgdif.pp 14 KB

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