ISPP.Preprocess.pas 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. {
  2. Inno Setup Preprocessor
  3. Copyright (C) 2001-2002 Alex Yackimoff
  4. Inno Setup
  5. Copyright (C) 1997-2024 Jordan Russell
  6. Portions by Martijn Laan
  7. For conditions of distribution and use, see LICENSE.TXT.
  8. }
  9. unit ISPP.Preprocess;
  10. interface
  11. uses
  12. Shared.PreprocInt;
  13. function ISPreprocessScript(var Params: TPreprocessScriptParams): Integer; stdcall;
  14. implementation
  15. uses
  16. SysUtils, Shared.CommonFunc, PathFunc,
  17. ISPP.Base, ISPP.Preprocessor, ISPP.Sessions, ISPP.Intf,
  18. ISPP.IdentMan, ISPP.VarUtils, ISPP.Consts;
  19. procedure ReadScript(const Params: TPreprocessScriptParams;
  20. const Preprocessor: TPreprocessor);
  21. var
  22. I: Integer;
  23. LineText: PChar;
  24. LineTextStr: String;
  25. begin
  26. I := 0;
  27. while True do
  28. begin
  29. LineText := Params.LineInProc(Params.CompilerData, 0, I);
  30. if LineText = nil then
  31. Break;
  32. LineTextStr := LineText;
  33. Preprocessor.QueueLine(LineTextStr);
  34. Inc(I);
  35. end;
  36. end;
  37. function CleanupProc(CleanupProcData: Pointer): Integer; stdcall;
  38. begin
  39. if PopPreproc = nil then
  40. Result := 0
  41. else
  42. Result := 1; { should never get here }
  43. end;
  44. function DecodeStringOptions(const S: String; var Options: TOptions): Boolean;
  45. var
  46. I: Integer;
  47. begin
  48. Options := [];
  49. Result := True;
  50. for I := 1 to Length(S) do begin
  51. case S[I] of
  52. 'a'..'z': Include(Options, Ord(S[I]) - Ord('a'));
  53. else
  54. Result := False;
  55. end;
  56. end;
  57. end;
  58. function ISPreprocessScript(var Params: TPreprocessScriptParams): Integer; stdcall;
  59. const
  60. Delta = Ord('a');
  61. DefaultOptions: TIsppOptions =
  62. (ParserOptions: (Options: [Ord('b') - Delta, Ord('p') - Delta]);
  63. Options: [Ord('c') - Delta, Ord('e') - Delta]; VerboseLevel: 0;
  64. InlineStart: '{#'; InlineEnd: '}'; SpanSymbol: #0);
  65. var
  66. ISPPOptions: TIsppOptions;
  67. Definitions, IncludePath, IncludeFiles: String;
  68. Preprocessor: TPreprocessor;
  69. V: TIsppVariant;
  70. function ParseOption(const OptName, OptValue: String): Boolean;
  71. begin
  72. Result := True;
  73. if OptName = 'ISPP:ParserOptions' then
  74. Result := DecodeStringOptions(OptValue, ISPPOptions.ParserOptions.Options)
  75. else if OptName = 'ISPP:Options' then
  76. Result := DecodeStringOptions(OptValue, ISPPOptions.Options)
  77. else if OptName = 'ISPP:VerboseLevel' then
  78. ISPPOptions.VerboseLevel := StrToIntDef(OptValue, 0)
  79. else if OptName = 'ISPP:InlineStart' then
  80. ISPPOptions.InlineStart := OptValue
  81. else if OptName = 'ISPP:InlineEnd' then
  82. ISPPOptions.InlineEnd := OptValue
  83. else if OptName = 'ISPP:Definitions' then
  84. Definitions := OptValue
  85. else if OptName = 'ISPP:IncludePath' then
  86. IncludePath := OptValue
  87. else if OptName = 'ISPP:IncludeFiles' then
  88. IncludeFiles := OptValue
  89. else
  90. Result := False;
  91. end;
  92. function ParseOptions(P: PChar): Boolean;
  93. var
  94. EqPos: PChar;
  95. OptName: String;
  96. begin
  97. Result := True;
  98. if P = nil then
  99. Exit;
  100. while P^ <> #0 do begin
  101. EqPos := StrScan(P, '=');
  102. if EqPos = nil then begin
  103. Result := False;
  104. Break;
  105. end;
  106. SetString(OptName, P, EqPos - P);
  107. P := EqPos + 1;
  108. if not ParseOption(OptName, P) then begin
  109. Result := False;
  110. Break;
  111. end;
  112. Inc(P, StrLen(P) + 1);
  113. end;
  114. end;
  115. function ParseDefinitions(Definitions: PChar; VarMan: TIdentManager): Boolean;
  116. procedure ParseDefinition(const S: string);
  117. var
  118. I: Integer;
  119. Name, V: string;
  120. Value: TIsppVariant;
  121. begin
  122. Value := NULL;
  123. I := Pos('=', S);
  124. if I > 0 then
  125. begin
  126. Name := Copy(S, 1, I - 1);
  127. V := Copy(S, I + 1, MaxInt);
  128. if V <> '' then
  129. MakeStr(Value, V)
  130. end
  131. else
  132. Name := Trim(S);
  133. VarMan.DefineVariable(Name, -1, Value, dsPublic);
  134. end;
  135. var
  136. DelimPos: PChar;
  137. N: Integer;
  138. Definition: string;
  139. begin
  140. Result := True;
  141. while Definitions^ <> #0 do begin
  142. DelimPos := StrScan(Definitions, #1);
  143. if DelimPos = nil then begin
  144. Result := False;
  145. Break;
  146. end;
  147. N := DelimPos - Definitions;
  148. if N > 0 then begin
  149. SetString(Definition, Definitions, N);
  150. ParseDefinition(Definition);
  151. end;
  152. Inc(Definitions, N + 1);
  153. end;
  154. end;
  155. function IncludeBuiltinsAndParseIncludeFiles(BuiltinsDir: String; IncludeFiles: PChar; Options: TOptions): Boolean;
  156. function Escape(const S: string): string;
  157. var
  158. I: Integer;
  159. begin
  160. Result := '';
  161. for I := 1 to Length(S) do
  162. begin
  163. Result := Result + S[I];
  164. if S[I] = '\' then Result := Result + '\';
  165. end;
  166. end;
  167. procedure Include(FileName: String; Builtins: Boolean);
  168. begin
  169. if not GetOption(Options, 'P') then
  170. FileName := Escape(FileName);
  171. Preprocessor.IncludeFile(FileName, Builtins, False, True);
  172. end;
  173. const
  174. SBuiltins = {$IFDEF DEBUG} '..\..\Files\ISPPBuiltins.iss' {$ELSE} 'ISPPBuiltins.iss' {$ENDIF};
  175. var
  176. DelimPos: PChar;
  177. N: Integer;
  178. IncludeFile: String;
  179. begin
  180. Result := True;
  181. IncludeFile := BuiltinsDir + SBuiltins;
  182. if FileExists(IncludeFile) then
  183. Include(IncludeFile, True)
  184. else
  185. Preprocessor.WarningMsg(SFileNotFound, [SBuiltins]);
  186. while IncludeFiles^ <> #0 do begin
  187. DelimPos := StrScan(IncludeFiles, #1);
  188. if DelimPos = nil then begin
  189. Result := False;
  190. Break;
  191. end;
  192. N := DelimPos - IncludeFiles;
  193. if N > 0 then begin
  194. SetString(IncludeFile, IncludeFiles, N);
  195. Include(IncludeFile, False);
  196. end;
  197. Inc(IncludeFiles, N + 1);
  198. end;
  199. end;
  200. var
  201. SourcePath, CompilerPath, LineFilename, LineText: string;
  202. LineNumber: Integer;
  203. begin
  204. if (Params.Size <> SizeOf(Params)) or
  205. (Params.InterfaceVersion <> 3) then
  206. begin
  207. Result := ispeInvalidParam;
  208. Exit;
  209. end;
  210. SourcePath := Params.SourcePath;
  211. CompilerPath := Params.CompilerPath;
  212. ISPPOptions := DefaultOptions;
  213. Definitions := '';
  214. IncludePath := RemoveBackslashUnlessRoot(CompilerPath);
  215. IncludeFiles := '';
  216. if not ParseOptions(Params.Options) then
  217. begin
  218. Result := ispeInvalidParam;
  219. Exit;
  220. end;
  221. { Hack: push a dummy item onto the stack to defer deletion of temp. files }
  222. PushPreproc(nil);
  223. try
  224. Preprocessor := TPreprocessor.Create(Params, nil, ISPPOptions, SourcePath,
  225. CompilerPath, Params.Filename);
  226. try
  227. Preprocessor.IncludePath := IncludePath;
  228. MakeStr(V, SourcePath);
  229. Preprocessor.VarMan.DefineVariable('SourcePath', -1, V, dsPublic);
  230. MakeStr(V, CompilerPath);
  231. Preprocessor.VarMan.DefineVariable('CompilerPath', -1, V, dsPublic);
  232. MakeInt(V, Params.CompilerBinVersion);
  233. Preprocessor.VarMan.DefineVariable('Ver', -1, V, dsPublic);
  234. if not ParseDefinitions(PChar(Definitions), Preprocessor.VarMan) or
  235. not IncludeBuiltinsAndParseIncludeFiles(Params.CompilerPath, PChar(IncludeFiles),
  236. Preprocessor.FOptions.ParserOptions.Options) then
  237. begin
  238. Result := ispeInvalidParam;
  239. Exit;
  240. end;
  241. ReadScript(Params, Preprocessor);
  242. Preprocessor.Stack.Resolved;
  243. if not GetOption(Preprocessor.FOptions.Options, 'C') then
  244. Result := ispeSilentAbort
  245. else
  246. begin
  247. Preprocessor.GetNextOutputLineReset;
  248. while Preprocessor.GetNextOutputLine(LineFilename, LineNumber, LineText) do
  249. Params.LineOutProc(Params.CompilerData, PChar(LineFilename),
  250. LineNumber, PChar(LineText));
  251. Result := ispeSuccess;
  252. end;
  253. finally
  254. Preprocessor.Free;
  255. end;
  256. except
  257. on E: EPreprocError do {preprocessor (syntax most likely) error}
  258. begin
  259. Params.ErrorProc(Params.CompilerData, PChar(E.Message),
  260. PChar(E.FileName), E.LineNumber, E.ColumnNumber);
  261. Result := ispePreprocessError;
  262. end;
  263. on E: Exception do
  264. begin
  265. Params.ErrorProc(Params.CompilerData,
  266. PChar(Format('Unexpected exception of class %s in ISPP.' +
  267. #13#10#13#10'%s.', [E.ClassName, E.Message])), nil, 0, 0);
  268. Result := ispePreprocessError;
  269. end;
  270. end;
  271. if Result = ispeSuccess then
  272. Params.PreprocCleanupProc := CleanupProc
  273. else
  274. PopPreproc;
  275. end;
  276. end.