msgcomp.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2003 by the Free Pascal development team
  5. Windows message compiler unit.
  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. {$ifdef fpc}
  13. {$mode objfpc}
  14. {$h+}
  15. {$endif}
  16. unit msgcomp;
  17. interface
  18. Uses Classes;
  19. Type
  20. TMessagehandler = Procedure(Sender : Tobject; Msg : String) Of Object;
  21. TMessageCompiler = Class
  22. Private
  23. FErrors : Integer;
  24. FEscapeNeeded : Boolean;
  25. FOnVerbose,
  26. FOnError : TMessageHandler;
  27. FCompiling : Boolean;
  28. FMC: TStream;
  29. FPas: TStream;
  30. FRC: TStream;
  31. FMsg: TStream;
  32. FLocaleID: Integer;
  33. FSubLocaleID: Integer;
  34. FMsgFileName: String;
  35. FUnitName: String;
  36. Procedure SWriteLn(S : String; Stream : TStream);
  37. Procedure CompileError(EMsg : String;Line : Integer; Value : String);
  38. Procedure ProcessMessages(Lines : TStrings; MsgList : Tlist);
  39. Procedure WriteMsgFile(MsgList : TList; Stream : TStream);
  40. Procedure WriteRCFile(MsgList : TList; Stream : TStream);
  41. Procedure WritePasFile(MsgList : TList; Stream : TStream);
  42. Procedure ClearList(MsgList : Tlist);
  43. procedure SetStream(const Index: Integer; const Value: TStream);
  44. Function GetStream(const Index: Integer) : TStream;
  45. Procedure Verbose(Fmt : String;Args : Array of const);
  46. Public
  47. Constructor Create;
  48. Function Compile : Boolean;
  49. Property MC : TStream index 1 Read GetStream Write SetStream;
  50. Property Msg : TStream index 2 Read GetStream Write SetStream;
  51. Property Pas : TStream index 3 Read GetStream Write SetStream;
  52. Property RC : TStream Index 4 Read GetStream Write SetStream;
  53. Property LocaleID : Integer Read FLocaleID Write FlocaleID;
  54. Property SubLocaleID : Integer Read FSubLocaleID Write FSublocaleID;
  55. Property MessageFileName : String Read FMsgFileName Write FMsgFileName;
  56. Property UnitName : String Read FUnitName Write FUnitName;
  57. Property EscapeNeeded : Boolean Read FEscapeNeeded Write FEscapeNeeded;
  58. Property OnVerbose : TMessageHandler Read FOnVerbose Write FOnVerbose;
  59. Property OnError : TMessageHandler Read FOnError Write FOnError;
  60. Property Errors : Integer Read FErrors;
  61. End;
  62. TMessageEntry = Class(TObject)
  63. MessageID : Cardinal;
  64. TotalMessageOfs : Cardinal;
  65. MessageAlias,
  66. Language,
  67. MessageText : String;
  68. Function OffsetToNext : Cardinal;
  69. End;
  70. implementation
  71. Uses SysUtils ;
  72. Const
  73. SC = SizeOF(Cardinal);
  74. Resourcestring
  75. SErrSetStreamNotAllowed = 'Setting stream during compile is not allowed.';
  76. SUnknownLine = 'Unknown error: "%s"';
  77. SNoMessage = 'Message starts without MessageID : "%s"';
  78. SErrUnknownDirective = 'Unknown directive "%s"';
  79. SErrInLine = 'Error: line %d: %s';
  80. SFoundMessageID = 'Found messageID : %s';
  81. SStartCompiling = 'Start compiling: %d lines.';
  82. SErrUnexpected = 'Unexpected error : %s';
  83. SWritingMessageFile = 'Writing %d entries to message file.';
  84. SWritingPasFile = 'Writing to unit "%s"';
  85. SWrotePasFile = 'Wrote %d constants to unit "%s"';
  86. SWritingRCFile = 'Writing rc file.';
  87. SErrNoNumber = 'Not a valid integer: %s';
  88. procedure TMessageCompiler.ClearList(MsgList: Tlist);
  89. Var
  90. I : Integer;
  91. begin
  92. For I:=0 to MsgList.Count-1 do
  93. TMessageEntry(MsgList[I]).Free;
  94. MsgList.Clear;
  95. end;
  96. Function TMessageCompiler.Compile : Boolean;
  97. Var
  98. Lines : TStrings;
  99. MsgList : TList;
  100. Begin
  101. FErrors:=0;
  102. MsgList:=TList.Create;
  103. try
  104. Lines:=TStringList.Create;
  105. Try
  106. Lines.LoadFromStream(MC);
  107. ProcessMessages(Lines,MSgList);
  108. If (FErrors=0) then
  109. begin
  110. If Assigned(Msg) then
  111. WriteMsgFile(MsgList,Msg);
  112. if Assigned(Pas) then
  113. WritePasFile(MsgList,Pas);
  114. if Assigned(RC) then
  115. WriteRCFile(MsgList,RC);
  116. end;
  117. Finally
  118. Lines.Free;
  119. end;
  120. Finally
  121. ClearList(MsgList);
  122. end;
  123. Result:=(FErrors=0);
  124. End;
  125. Procedure TMessageCompiler.Verbose(Fmt : String;Args : Array of const);
  126. begin
  127. if Assigned(FOnverbose) then
  128. FOnVerBose(Self,Format(Fmt,Args));
  129. end;
  130. Function HexToInt(Hex : String) : Integer;
  131. Const HexSymbols : String = '0123456789ABCDEF';
  132. Var I,J : Integer;
  133. Begin
  134. Hex := UpperCase(Hex);
  135. Result := 0;
  136. J := Length(Hex);
  137. For I := 1 to J do
  138. Result := Result+((Pos(Hex[J-I+1],HexSymbols)-1) shl ((I-1)*4));
  139. End;
  140. Constructor TMessageCompiler.Create;
  141. begin
  142. // English
  143. LocaleID:=9;
  144. SubLocaleID:=1;
  145. end;
  146. Procedure TMessageCompiler.CompileError(EMsg : String;Line : Integer; Value : String);
  147. begin
  148. Inc(FErrors);
  149. EMsg:=Format(EMsg,[Value]);
  150. If Assigned(FOnError) then
  151. FOnError(Self,Format(SErrInLine,[Line,EMsg]));
  152. end;
  153. Procedure TMessageCompiler.ProcessMessages(Lines : TStrings; MsgList : TList);
  154. Var
  155. Line : Integer;
  156. Me : TMessageEntry;
  157. Function Pad(S : String; Len : Integer) : String;
  158. Var I : Integer;
  159. Begin
  160. For I := Length(S) to Len-1 do S := S+' ';
  161. Result := S;
  162. End;
  163. Function SkipLine(Var S : String) : Boolean;
  164. Var
  165. I : Integer;
  166. begin
  167. I:=Pos(';',S);
  168. If (I<>0) then
  169. S:=Copy(S,1,I-1);
  170. Result:=Length(S)=0;
  171. end;
  172. Procedure DoDirective(S : String; I : Integer);
  173. Var
  174. MsgID : Integer;
  175. T : String;
  176. begin
  177. T := UpperCase(Copy(S,1,I-1));
  178. Delete(S,1,I);
  179. S:=Trim(S);
  180. If (T='MESSAGEID') Then
  181. begin
  182. Verbose(SFoundMessageID,[S]);
  183. S:=Uppercase(S);
  184. If Pos('0X',S)<>0 then
  185. begin
  186. Delete(S,1,2);
  187. MsgId:=HexToInt(S);
  188. end
  189. else
  190. MsgID:=StrToIntDef(S,-1);
  191. If (MsgID=-1) then
  192. CompileError(Format(SErrNoNumber,[S]),Line,T)
  193. else
  194. begin
  195. Me:=TMessageENtry.Create;
  196. Me.MessageID:=MsgID;
  197. end;
  198. End
  199. Else If (T = 'SYMBOLICNAME') Then
  200. Begin
  201. If Assigned(me) then
  202. Me.MessageAlias:=S;
  203. End
  204. Else If (T = 'LANGUAGE') Then
  205. begin
  206. If assigned(ME) then
  207. Me.Language:=S;
  208. end
  209. else
  210. CompileError(SErrUnknownDirective,Line,T);
  211. End;
  212. Var
  213. I,Count : Integer;
  214. S : String;
  215. Begin
  216. Count := Lines.Count-1;
  217. Verbose(SStartCOmpiling,[Count]);
  218. Line:=0;
  219. Me:=Nil;
  220. While Line<=Count do
  221. Begin
  222. Try
  223. S:=Lines[Line];
  224. If Not SkipLine(S) then
  225. begin
  226. I:=Pos('=',S);
  227. If (I<>0) then
  228. DoDirective(S,I)
  229. else
  230. If (Me=Nil) Then
  231. CompileError(SNoMessage,Line,S)
  232. else
  233. begin
  234. // Message starts.
  235. While (S<>'.') do
  236. begin
  237. If Length(Me.MessageText)>0 then
  238. Me.MessageText:=Me.MessageText+#13#10+S
  239. else
  240. Me.MessageText:=S;
  241. Inc(Line);
  242. If Line<=Count then
  243. S:=Lines[Line]
  244. end;
  245. MsgList.Add(Me);
  246. Me:=Nil;
  247. end;
  248. End;
  249. Except
  250. On E : Exception do
  251. CompileError(SErrUnexpected,Line,E.Message);
  252. End;
  253. Inc(Line);
  254. end;
  255. End;
  256. procedure TMessageCompiler.SetStream(const Index: Integer;
  257. const Value: TStream);
  258. begin
  259. If FCompiling then
  260. Raise Exception.Create(SErrSetStreamNotAllowed);
  261. Case index of
  262. 1 : FMC := Value;
  263. 2 : FMsg := Value;
  264. 3 : FPas := Value;
  265. 4 : FRC := Value;
  266. end;
  267. end;
  268. Function TMessageCompiler.GetStream(const Index: Integer) : TStream;
  269. begin
  270. Case index of
  271. 1 : Result:=FMC;
  272. 2 : Result:=FMsg;
  273. 3 : Result:=FPas;
  274. 4 : Result:=FRC;
  275. end;
  276. end;
  277. procedure TMessageCompiler.SWriteLn(S: String; Stream: TStream);
  278. begin
  279. S:=S+#13#10;
  280. Stream.Write(S[1],Length(S));
  281. end;
  282. Procedure TMessageCompiler.WriteMSGFile(MsgList : Tlist; Stream : TStream);
  283. Var
  284. I,Count : Integer;
  285. HeaderEntry,NullEntry: Array[1..3] of cardinal;
  286. O,BO : Cardinal;
  287. M : TMessageEntry;
  288. S : String;
  289. Begin
  290. Verbose(SWritingMessageFile,[MsgList.Count]);
  291. NullEntry[1]:=0;
  292. NullEntry[2]:=0;
  293. NullEntry[3]:=0;
  294. Count:=MsgList.Count;
  295. Stream.Write(Count,SC);
  296. BO:=((SC*3)*Count)+SC; // Header size...
  297. // Loop 1: Header entries.
  298. For I:=0 to Count-1 do
  299. begin
  300. M:=TMessageEntry(MsgList[I]);
  301. HeaderEntry[1]:=M.MessageID;
  302. HeaderEntry[2]:=M.MessageID;
  303. HeaderEntry[3]:=BO;
  304. BO:=BO+M.OffsetToNext;
  305. Stream.Write(HeaderEntry,SizeOf(HeaderEntry));
  306. end;
  307. For I:=0 to Count-1 do
  308. begin
  309. M:=TMessageEntry(MsgList[I]);
  310. O:=M.OffsetToNext;
  311. Stream.Write(O,SizeOf(O));
  312. Dec(O,SC);
  313. S:=M.MessageText;
  314. Stream.Write(S[1],Length(S));
  315. If (Length(S)<O) then
  316. Stream.Write(NullEntry,O-Length(S));
  317. end;
  318. End;
  319. procedure TMessageCompiler.WritePasFile(MsgList: TList; Stream: TStream);
  320. Var
  321. I,Count : Integer;
  322. ME : TMessageEntry;
  323. begin
  324. Verbose(SWritingPasFile,[UnitName]);
  325. SWriteln(Format('Unit %s;',[UnitName]),Stream);
  326. SWriteln('',Stream);
  327. SWriteln('Interface',Stream);
  328. SWriteln('',Stream);
  329. SWriteln('Const',Stream);
  330. Count:=0;
  331. For I:=0 to MsgList.Count-1 do
  332. begin
  333. Me:=TMessageEntry(MsgList[I]);
  334. With Me do
  335. If (MessageAlias<>'') then
  336. begin
  337. Swriteln(Format(' %s = %d; ',[MessageAlias,MessageID]),Stream);
  338. Inc(Count);
  339. end;
  340. end;
  341. SWriteln('',Stream);
  342. SWriteln('Implementation',Stream);
  343. SWriteln('',Stream);
  344. SWriteln('end.',Stream);
  345. Verbose(SWrotePasFile,[Count,UnitName]);
  346. end;
  347. procedure TMessageCompiler.WriteRCFile(MsgList: TList; Stream: TStream);
  348. Const
  349. LangLine = 'LANGUAGE 0x%s,0x%s';
  350. FileLine = '1 11 "%s"';
  351. Var
  352. S : String;
  353. begin
  354. Verbose(SWritingRCFile,[]);
  355. S:=Format(LangLine,[IntToHex(LocaleID,1),IntToHex(SubLocaleID,1)]);
  356. SWriteLn(S,Stream);
  357. S:=MessageFileName;
  358. If EscapeNeeded Then
  359. S:=StringReplace(S,'\','\\',[rfReplaceAll]);
  360. SWriteLn(Format(FileLine,[S]),Stream);
  361. end;
  362. { TMessageEntry }
  363. function TMessageEntry.OffsetToNext: Cardinal;
  364. begin
  365. Result:=((Length(MessageText) div SC) +2) * SC;
  366. end;
  367. end.
  368. {
  369. $Log$
  370. Revision 1.1 2003-02-14 21:59:21 michael
  371. + Initial implementation
  372. }