rstconv.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Sebastian Guenther
  4. Added .rc and OS/2 MSG support in 2002 by Yuri Prokushev
  5. .rst resource string table file converter.
  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. {$MODE objfpc}
  13. {$H+}
  14. program rstconv;
  15. uses sysutils, classes;
  16. resourcestring
  17. help =
  18. 'rstconv [-h|--help] Displays this help'+LineEnding+
  19. 'rstconv options Convert rst file'+LineEnding+LineEnding+
  20. 'Options are:'+LineEnding+
  21. ' -i file Use specified file instead of stdin as input .rst (OPTIONAL)'+LineEnding+
  22. ' -o file Write output to specified file (REQUIRED)'+LineEnding+
  23. ' -f format Specifies the output format:'+LineEnding+
  24. ' po GNU gettext .po (portable) format (DEFAULT)'+LineEnding+
  25. ' msg IBM OS/2 MSG file format'+LineEnding+
  26. ' rc Resource compiler .rc format'+LineEnding+LineEnding+
  27. '.po format only options are:'+LineEnding+
  28. ' -c char set Adds a header specifying the given character set (OPTIONAL).'+LineEnding+LineEnding+
  29. 'OS/2 MSG file only options are:'+LineEnding+
  30. ' -c identifier Specifies the component identifier (REQUIRED).'+LineEnding+
  31. ' Identifier is any three chars in upper case.'+LineEnding+
  32. ' -n number Specifies the first message number [1-9999] (OPTIONAL).'+LineEnding+LineEnding+
  33. 'Resource compiler script only options are:'+LineEnding+
  34. ' -s Use STRINGTABLE instead of MESSAGETABLE'+LineEnding+
  35. ' -c identifier Use identifier as ID base (ID+n) (OPTIONAL)'+LineEnding+
  36. ' -n number Specifies the first ID number (OPTIONAL)'+LineEnding;
  37. InvalidOption = 'Invalid option - ';
  38. RequiredOption = 'Required option is absent - ';
  39. OptionAlreadySpecified = 'Option has already been specified - ';
  40. NoOutFilename = 'No output filename specified';
  41. InvalidOutputFormat = 'Invalid output format -';
  42. MessageNumberTooBig = 'Message number too big';
  43. InvalidRange = 'Invalid range of the first message number';
  44. type
  45. TConstItem = class(TCollectionItem)
  46. public
  47. ModuleName, ConstName, Value: String;
  48. end;
  49. var
  50. InFilename, OutFilename: String;
  51. ConstItems: TCollection;
  52. CharSet: String;
  53. Identifier: String;
  54. FirstMessage: Word;
  55. MessageTable: Boolean;
  56. procedure ReadRSTFile;
  57. var
  58. f: Text;
  59. s: String;
  60. item: TConstItem;
  61. DotPos, EqPos, i, j: Integer;
  62. begin
  63. Assign(f, InFilename);
  64. Reset(f);
  65. while not eof(f) do begin
  66. ReadLn(f, s);
  67. If (Length(S)=0) or (S[1]='#') then
  68. continue;
  69. item := TConstItem(ConstItems.Add);
  70. DotPos := Pos('.', s);
  71. EqPos := Pos('=', s);
  72. if DotPos > EqPos then // paranoia checking.
  73. DotPos := 0;
  74. item.ModuleName := Copy(s, 1, DotPos - 1);
  75. item.ConstName := Copy(s, DotPos + 1, EqPos - DotPos - 1);
  76. item.Value := '';
  77. i := EqPos + 1;
  78. while i <= Length(s) do begin
  79. if s[i] = '''' then begin
  80. Inc(i);
  81. j := i;
  82. while (i <= Length(s)) and (s[i] <> '''') do
  83. Inc(i);
  84. item.Value := item.Value + Copy(s, j, i - j);
  85. Inc(i);
  86. end else if s[i] = '#' then begin
  87. Inc(i);
  88. j := i;
  89. while (i <= Length(s)) and (s[i] in ['0'..'9']) do
  90. Inc(i);
  91. item.Value := item.Value + Chr(StrToInt(Copy(s, j, i - j)));
  92. end else if s[i] = '+' then begin
  93. ReadLn(f, s);
  94. i := 1;
  95. end else
  96. Inc(i);
  97. end;
  98. end;
  99. Close(f);
  100. end;
  101. procedure ConvertToGettextPO;
  102. var
  103. i, j: Integer;
  104. f: Text;
  105. item: TConstItem;
  106. s: String;
  107. c: Char;
  108. begin
  109. Assign(f, OutFilename);
  110. Rewrite(f);
  111. if CharSet<>'' then begin
  112. // Write file header with
  113. WriteLn(f, 'msgid ""');
  114. WriteLn(f, 'msgstr ""');
  115. WriteLn(f, '"MIME-Version: 1.0\n"');
  116. WriteLn(f, '"Content-Type: text/plain; charset=', CharSet, '\n"');
  117. WriteLn(f, '"Content-Transfer-Encoding: 8bit\n"');
  118. WriteLn(f);
  119. end;
  120. for i := 0 to ConstItems.Count - 1 do begin
  121. item := TConstItem(ConstItems.items[i]);
  122. // Convert string to C-style syntax
  123. s := '';
  124. for j := 1 to Length(item.Value) do begin
  125. c := item.Value[j];
  126. case c of
  127. #9: s := s + '\t';
  128. #10: s := s + '\n';
  129. {$IFNDEF UNIX}
  130. #13: ;
  131. #1..#8, #11..#12, #14..#31, #128..#255:
  132. {$ELSE}
  133. #1..#8, #11..#31, #128..#255:
  134. {$ENDIF}
  135. s := s + '\' +
  136. Chr(Ord(c) shr 6 + 48) +
  137. Chr((Ord(c) shr 3) and 7 + 48) +
  138. Chr(Ord(c) and 7 + 48);
  139. '\': s := s + '\\';
  140. '"': s := s + '\"';
  141. else s := s + c;
  142. end;
  143. end;
  144. // Write msg entry
  145. WriteLn(f, '#: ', item.ModuleName, ':', item.ConstName);
  146. j := Pos('\n', s);
  147. if j > 0 then begin
  148. WriteLn(f, 'msgid ""');
  149. while j > 0 do begin
  150. Writeln(f, '"',copy(s, 1, j+1),'"');
  151. Delete(s, 1, j+1);
  152. j := Pos('\n', s);
  153. end;
  154. if s <> '' then
  155. Writeln(f, '"',s,'"');
  156. end
  157. else
  158. WriteLn(f, 'msgid "', s, '"');
  159. WriteLn(f, 'msgstr ""');
  160. WriteLn(f);
  161. end;
  162. Close(f);
  163. end;
  164. // This routine stores rst file in rc format. Can be written as MESSAGETABLE
  165. // as STRINGTABLE. Beware! OS/2 RC doesn't support lines longer whan 255 chars.
  166. procedure ConvertToRC;
  167. var
  168. i, j: Integer;
  169. f: Text;
  170. item: TConstItem;
  171. s: String;
  172. c: Char;
  173. begin
  174. Assign(f, OutFilename);
  175. Rewrite(f);
  176. If MessageTable then
  177. WriteLn(F, 'MESSAGETABLE')
  178. else
  179. WriteLn(F, 'STRINGTABLE');
  180. WriteLn(F, 'BEGIN');
  181. If Identifier<>'' then WriteLn(F, '#define ', Identifier);
  182. for i := 0 to ConstItems.Count - 1 do begin
  183. item := TConstItem(ConstItems.items[i]);
  184. // Convert string to C-style syntax
  185. s := '';
  186. for j := 1 to Length(item.Value) do begin
  187. c := item.Value[j];
  188. case c of
  189. #9: s := s + '\t';
  190. #10: s := s + '\n"'#13#10'"';
  191. {$IFNDEF UNIX}
  192. #13: ;
  193. #1..#8, #11..#12, #14..#31, #128..#255:
  194. {$ELSE}
  195. #1..#8, #11..#31, #128..#255:
  196. {$ENDIF}
  197. s := s + '\' +
  198. Chr(Ord(c) shr 6 + 48) +
  199. Chr((Ord(c) shr 3) and 7 + 48) +
  200. Chr(Ord(c) and 7 + 48);
  201. '\': s := s + '\\';
  202. '"': s := s + '\"';
  203. else s := s + c;
  204. end;
  205. end;
  206. // Write msg entry
  207. WriteLn(f, '/* ', item.ModuleName, ':', item.ConstName, '*/');
  208. WriteLn(f, '/* ', s, ' */');
  209. If Identifier<>'' then Write(F, Identifier, '+');
  210. WriteLn(f, I+FirstMessage,' "', s, '"');
  211. WriteLn(f);
  212. end;
  213. WriteLn(F, 'END');
  214. Close(f);
  215. end;
  216. // This routine stores rst file in OS/2 msg format. This format is preffered
  217. // for help screens, messages, etc.
  218. procedure ConvertToOS2MSG;
  219. var
  220. i, j: Integer;
  221. f: Text;
  222. item: TConstItem;
  223. s: String;
  224. begin
  225. If (ConstItems.Count+FirstMessage-1)>9999 then
  226. begin
  227. WriteLn(MessageNumberTooBig);
  228. Halt(1);
  229. end;
  230. Identifier:=Copy(UpperCase(Identifier), 1, 3);
  231. Assign(f, OutFilename);
  232. Rewrite(f);
  233. WriteLn(f, Identifier);
  234. // Fake entry, because MKMSGF limitation
  235. WriteLn(f, Format('%s%.4d?: ',[Identifier, FirstMessage-1]));
  236. for i := 0 to ConstItems.Count - 1 do begin
  237. item := TConstItem(ConstItems.items[i]);
  238. // Prepare comment string
  239. // Convert string to C-style syntax
  240. s := '';
  241. j:=1;
  242. while j<=Length(item.Value) do
  243. begin
  244. if copy(item.Value, j, 2)=#13#10 then
  245. begin
  246. s:=s+#13#10';';
  247. Inc(j, 2);
  248. end else begin
  249. s := s + item.Value[j];
  250. Inc(j);
  251. end;
  252. end;
  253. // Write msg entry
  254. WriteLn(f, ';', item.ModuleName, '.', item.ConstName);
  255. WriteLn(f, Format(';%s%.4dP: %s %%0',[Identifier, i+FirstMessage, s]));
  256. WriteLn(f, Format('%s%.4dP: %s %%0',[Identifier, i+FirstMessage, Item.Value]));
  257. end;
  258. Close(f);
  259. end;
  260. type
  261. TConversionProc = procedure;
  262. var
  263. i: Integer;
  264. ConversionProc: TConversionProc;
  265. OutputFormat: String;
  266. begin
  267. if (ParamStr(1) = '-h') or (ParamStr(1) = '--help') then begin
  268. WriteLn(help);
  269. exit;
  270. end;
  271. ConversionProc := @ConvertToGettextPO;
  272. OutputFormat:='';
  273. CharSet:='';
  274. Identifier:='';
  275. FirstMessage:=0;
  276. MessageTable:=True;
  277. i := 1;
  278. while i <= ParamCount do begin
  279. if ParamStr(i) = '-i' then begin
  280. if InFilename <> '' then begin
  281. WriteLn(StdErr, OptionAlreadySpecified, '-i');
  282. Halt(1);
  283. end;
  284. InFilename := ParamStr(i + 1);
  285. Inc(i, 2);
  286. end else if ParamStr(i) = '-o' then begin
  287. if OutFilename <> '' then begin
  288. WriteLn(StdErr, OptionAlreadySpecified, '-o');
  289. Halt(1);
  290. end;
  291. OutFilename := ParamStr(i + 1);
  292. Inc(i, 2);
  293. end else if ParamStr(i) = '-f' then begin
  294. if OutputFormat <> '' then begin
  295. WriteLn(StdErr, OptionAlreadySpecified, '-f');
  296. Halt(1);
  297. end;
  298. if ParamStr(i + 1) = 'po' then
  299. OutputFormat:='po'
  300. else if ParamStr(i + 1) = 'msg' then begin
  301. OutputFormat:='msg';
  302. ConversionProc := @ConvertToOS2MSG;
  303. end else if ParamStr(i + 1) = 'rc' then begin
  304. OutputFormat:='rc';
  305. ConversionProc := @ConvertToRC;
  306. end else begin
  307. WriteLn(StdErr, InvalidOutputFormat, ParamStr(i + 1));
  308. Halt(1);
  309. end;
  310. Inc(i, 2);
  311. end else if ParamStr(i) = '-c' then begin
  312. if (OutputFormat='') or (OutputFormat='po') then begin
  313. if CharSet <> '' then begin
  314. WriteLn(StdErr, OptionAlreadySpecified, '-c');
  315. Halt(1);
  316. end;
  317. CharSet:=ParamStr(i+1);
  318. end else
  319. begin
  320. if Identifier <> '' then begin
  321. WriteLn(StdErr, OptionAlreadySpecified, '-c');
  322. Halt(1);
  323. end;
  324. Identifier:=ParamStr(i+1);
  325. end;
  326. Inc(i, 2);
  327. end else if ParamStr(i) = '-s' then begin
  328. if not MessageTable then begin
  329. WriteLn(StdErr, OptionAlreadySpecified, '-s');
  330. Halt(1);
  331. end;
  332. MessageTable:=False;
  333. Inc(i);
  334. end else if ParamStr(i) = '-n' then begin
  335. if FirstMessage <> 0 then begin
  336. WriteLn(StdErr, OptionAlreadySpecified, '-n');
  337. Halt(1);
  338. end;
  339. try
  340. FirstMessage := StrToInt(ParamStr(i + 1));
  341. If (FirstMessage<1) then raise EConvertError.Create(InvalidRange+' '+ParamStr(i+1));
  342. except
  343. on EConvertError do
  344. begin
  345. WriteLn(StdErr, InvalidOption, ParamStr(i));
  346. Halt(1);
  347. end;
  348. end;
  349. Inc(i, 2);
  350. end else begin
  351. WriteLn(StdErr, InvalidOption, ParamStr(i));
  352. Halt(1);
  353. end;
  354. end;
  355. If ((OutputFormat<>'') and (OutputFormat<>'po')) and (CharSet<>'') then begin
  356. WriteLn(StdErr, InvalidOption, '');
  357. Halt(1);
  358. end;
  359. If ((OutputFormat<>'msg') and (OutputFormat<>'rc')) and ((Identifier<>'') or (FirstMessage<>0)) then begin
  360. WriteLn(StdErr, InvalidOption, '');
  361. Halt(1);
  362. end;
  363. If (OutputFormat='msg') and (Identifier='') then begin
  364. WriteLn(StdErr, RequiredOption, '-c');
  365. Halt(1);
  366. end;
  367. if OutFilename = '' then begin
  368. WriteLn(StdErr, NoOutFilename);
  369. Halt(1);
  370. end;
  371. ConstItems := TCollection.Create(TConstItem);
  372. ReadRSTFile;
  373. ConversionProc;
  374. end.