2
0

msgdif.pp 14 KB

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