rstconv.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. WriteLn(f, 'msgid "', s, '"');
  147. WriteLn(f, 'msgstr ""');
  148. WriteLn(f);
  149. end;
  150. Close(f);
  151. end;
  152. // This routine stores rst file in rc format. Can be written as MESSAGETABLE
  153. // as STRINGTABLE. Beware! OS/2 RC doesn't support lines longer whan 255 chars.
  154. procedure ConvertToRC;
  155. var
  156. i, j: Integer;
  157. f: Text;
  158. item: TConstItem;
  159. s: String;
  160. c: Char;
  161. begin
  162. Assign(f, OutFilename);
  163. Rewrite(f);
  164. If MessageTable then
  165. WriteLn(F, 'MESSAGETABLE')
  166. else
  167. WriteLn(F, 'STRINGTABLE');
  168. WriteLn(F, 'BEGIN');
  169. If Identifier<>'' then WriteLn(F, '#define ', Identifier);
  170. for i := 0 to ConstItems.Count - 1 do begin
  171. item := TConstItem(ConstItems.items[i]);
  172. // Convert string to C-style syntax
  173. s := '';
  174. for j := 1 to Length(item.Value) do begin
  175. c := item.Value[j];
  176. case c of
  177. #9: s := s + '\t';
  178. #10: s := s + '\n"'#13#10'"';
  179. {$IFNDEF UNIX}
  180. #13: ;
  181. #1..#8, #11..#12, #14..#31, #128..#255:
  182. {$ELSE}
  183. #1..#8, #11..#31, #128..#255:
  184. {$ENDIF}
  185. s := s + '\' +
  186. Chr(Ord(c) shr 6 + 48) +
  187. Chr((Ord(c) shr 3) and 7 + 48) +
  188. Chr(Ord(c) and 7 + 48);
  189. '\': s := s + '\\';
  190. '"': s := s + '\"';
  191. else s := s + c;
  192. end;
  193. end;
  194. // Write msg entry
  195. WriteLn(f, '/* ', item.ModuleName, ':', item.ConstName, '*/');
  196. WriteLn(f, '/* ', s, ' */');
  197. If Identifier<>'' then Write(F, Identifier, '+');
  198. WriteLn(f, I+FirstMessage,' "', s, '"');
  199. WriteLn(f);
  200. end;
  201. WriteLn(F, 'END');
  202. Close(f);
  203. end;
  204. // This routine stores rst file in OS/2 msg format. This format is preffered
  205. // for help screens, messages, etc.
  206. procedure ConvertToOS2MSG;
  207. var
  208. i, j: Integer;
  209. f: Text;
  210. item: TConstItem;
  211. s: String;
  212. begin
  213. If (ConstItems.Count+FirstMessage-1)>9999 then
  214. begin
  215. WriteLn(MessageNumberTooBig);
  216. Halt(1);
  217. end;
  218. Identifier:=Copy(UpperCase(Identifier), 1, 3);
  219. Assign(f, OutFilename);
  220. Rewrite(f);
  221. WriteLn(f, Identifier);
  222. // Fake entry, because MKMSGF limitation
  223. WriteLn(f, Format('%s%.4d?: ',[Identifier, FirstMessage-1]));
  224. for i := 0 to ConstItems.Count - 1 do begin
  225. item := TConstItem(ConstItems.items[i]);
  226. // Prepare comment string
  227. // Convert string to C-style syntax
  228. s := '';
  229. j:=1;
  230. while j<=Length(item.Value) do
  231. begin
  232. if copy(item.Value, j, 2)=#13#10 then
  233. begin
  234. s:=s+#13#10';';
  235. Inc(j, 2);
  236. end else begin
  237. s := s + item.Value[j];
  238. Inc(j);
  239. end;
  240. end;
  241. // Write msg entry
  242. WriteLn(f, ';', item.ModuleName, '.', item.ConstName);
  243. WriteLn(f, Format(';%s%.4dP: %s %%0',[Identifier, i+FirstMessage, s]));
  244. WriteLn(f, Format('%s%.4dP: %s %%0',[Identifier, i+FirstMessage, Item.Value]));
  245. end;
  246. Close(f);
  247. end;
  248. type
  249. TConversionProc = procedure;
  250. var
  251. i: Integer;
  252. ConversionProc: TConversionProc;
  253. OutputFormat: String;
  254. begin
  255. if (ParamStr(1) = '-h') or (ParamStr(1) = '--help') then begin
  256. WriteLn(help);
  257. exit;
  258. end;
  259. ConversionProc := @ConvertToGettextPO;
  260. OutputFormat:='';
  261. CharSet:='';
  262. Identifier:='';
  263. FirstMessage:=0;
  264. MessageTable:=True;
  265. i := 1;
  266. while i <= ParamCount do begin
  267. if ParamStr(i) = '-i' then begin
  268. if InFilename <> '' then begin
  269. WriteLn(StdErr, OptionAlreadySpecified, '-i');
  270. Halt(1);
  271. end;
  272. InFilename := ParamStr(i + 1);
  273. Inc(i, 2);
  274. end else if ParamStr(i) = '-o' then begin
  275. if OutFilename <> '' then begin
  276. WriteLn(StdErr, OptionAlreadySpecified, '-o');
  277. Halt(1);
  278. end;
  279. OutFilename := ParamStr(i + 1);
  280. Inc(i, 2);
  281. end else if ParamStr(i) = '-f' then begin
  282. if OutputFormat <> '' then begin
  283. WriteLn(StdErr, OptionAlreadySpecified, '-f');
  284. Halt(1);
  285. end;
  286. if ParamStr(i + 1) = 'po' then
  287. OutputFormat:='po'
  288. else if ParamStr(i + 1) = 'msg' then begin
  289. OutputFormat:='msg';
  290. ConversionProc := @ConvertToOS2MSG;
  291. end else if ParamStr(i + 1) = 'rc' then begin
  292. OutputFormat:='rc';
  293. ConversionProc := @ConvertToRC;
  294. end else begin
  295. WriteLn(StdErr, InvalidOutputFormat, ParamStr(i + 1));
  296. Halt(1);
  297. end;
  298. Inc(i, 2);
  299. end else if ParamStr(i) = '-c' then begin
  300. if (OutputFormat='') or (OutputFormat='po') then begin
  301. if CharSet <> '' then begin
  302. WriteLn(StdErr, OptionAlreadySpecified, '-c');
  303. Halt(1);
  304. end;
  305. CharSet:=ParamStr(i+1);
  306. end else
  307. begin
  308. if Identifier <> '' then begin
  309. WriteLn(StdErr, OptionAlreadySpecified, '-c');
  310. Halt(1);
  311. end;
  312. Identifier:=ParamStr(i+1);
  313. end;
  314. Inc(i, 2);
  315. end else if ParamStr(i) = '-s' then begin
  316. if not MessageTable then begin
  317. WriteLn(StdErr, OptionAlreadySpecified, '-s');
  318. Halt(1);
  319. end;
  320. MessageTable:=False;
  321. Inc(i);
  322. end else if ParamStr(i) = '-n' then begin
  323. if FirstMessage <> 0 then begin
  324. WriteLn(StdErr, OptionAlreadySpecified, '-n');
  325. Halt(1);
  326. end;
  327. try
  328. FirstMessage := StrToInt(ParamStr(i + 1));
  329. If (FirstMessage<1) then raise EConvertError.Create(InvalidRange+' '+ParamStr(i+1));
  330. except
  331. on EConvertError do
  332. begin
  333. WriteLn(StdErr, InvalidOption, ParamStr(i));
  334. Halt(1);
  335. end;
  336. end;
  337. Inc(i, 2);
  338. end else begin
  339. WriteLn(StdErr, InvalidOption, ParamStr(i));
  340. Halt(1);
  341. end;
  342. end;
  343. If ((OutputFormat<>'') and (OutputFormat<>'po')) and (CharSet<>'') then begin
  344. WriteLn(StdErr, InvalidOption, '');
  345. Halt(1);
  346. end;
  347. If ((OutputFormat<>'msg') and (OutputFormat<>'rc')) and ((Identifier<>'') or (FirstMessage<>0)) then begin
  348. WriteLn(StdErr, InvalidOption, '');
  349. Halt(1);
  350. end;
  351. If (OutputFormat='msg') and (Identifier='') then begin
  352. WriteLn(StdErr, RequiredOption, '-c');
  353. Halt(1);
  354. end;
  355. if OutFilename = '' then begin
  356. WriteLn(StdErr, NoOutFilename);
  357. Halt(1);
  358. end;
  359. ConstItems := TCollection.Create(TConstItem);
  360. ReadRSTFile;
  361. ConversionProc;
  362. end.