msgcomp.pp 9.9 KB

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