custapp.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team
  4. CustomApplication class.
  5. Port to pas2js by Mattias Gaertner [email protected]
  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. unit CustApp;
  13. {$mode objfpc}
  14. interface
  15. uses
  16. Classes, SysUtils, Types, JS;
  17. Const
  18. SErrInvalidOption: String = 'Invalid option at position %s: "%s"';
  19. SErrNoOptionAllowed: String = 'Option at position %s does not allow an argument: %s';
  20. SErrOptionNeeded: String = 'Option at position %s needs an argument : %s';
  21. Type
  22. TExceptionEvent = procedure (Sender : TObject; E : Exception) of object;
  23. TEventLogTypes = set of TEventType;
  24. { TCustomApplication }
  25. TCustomApplication = Class(TComponent)
  26. Private
  27. FEventLogFilter: TEventLogTypes;
  28. FExceptObjectJS: JSValue;
  29. FOnException: TExceptionEvent;
  30. FTerminated: Boolean;
  31. FTitle: String;
  32. FOptionChar: Char;
  33. FCaseSensitiveOptions: Boolean;
  34. FStopOnException: Boolean;
  35. FExceptionExitCode: Integer;
  36. FExceptObject: Exception;
  37. Protected
  38. function GetEnvironmentVar(VarName: String): String; virtual;
  39. function GetExeName: string; virtual;
  40. function GetLocation: String; virtual; abstract;
  41. function GetOptionAtIndex(AIndex: Integer; IsLong: Boolean): String;
  42. procedure SetTitle(const AValue: string); virtual;
  43. function GetConsoleApplication: boolean; virtual; abstract;
  44. procedure DoRun; virtual; abstract;
  45. function GetParams(Index: Integer): String; virtual;
  46. function GetParamCount: Integer; virtual;
  47. procedure DoLog(EventType: TEventType; const Msg: String); virtual;
  48. Public
  49. constructor Create(AOwner: TComponent); override;
  50. // Some Delphi methods.
  51. procedure HandleException(Sender: TObject); virtual;
  52. procedure Initialize; virtual;
  53. procedure Run;
  54. procedure ShowException(E: Exception); virtual; abstract;
  55. procedure Terminate; virtual;
  56. procedure Terminate(AExitCode: Integer); virtual;
  57. // Extra methods.
  58. function FindOptionIndex(Const S: String; var Longopt: Boolean; StartAt: Integer = -1): Integer;
  59. function GetOptionValue(Const S: String): String;
  60. function GetOptionValue(Const C: Char; Const S: String): String;
  61. function GetOptionValues(Const C: Char; Const S: String): TStringDynArray;
  62. function HasOption(Const S: String) : Boolean;
  63. function HasOption(Const C: Char; Const S: String): Boolean;
  64. function CheckOptions(Const ShortOptions: String; Const Longopts: TStrings;
  65. Opts,NonOpts: TStrings; AllErrors: Boolean = False): String;
  66. function CheckOptions(Const ShortOptions: String; Const Longopts: Array of string;
  67. Opts,NonOpts: TStrings; AllErrors: Boolean = False): String;
  68. function CheckOptions(Const ShortOptions: String; Const Longopts: TStrings;
  69. AllErrors: Boolean = False): String;
  70. function CheckOptions(Const ShortOptions: String; Const LongOpts: Array of string;
  71. AllErrors: Boolean = False): String;
  72. function CheckOptions(Const ShortOptions: String; Const LongOpts: String;
  73. AllErrors: Boolean = False): String;
  74. function GetNonOptions(Const ShortOptions: String; Const Longopts: Array of string): TStringDynArray;
  75. procedure GetNonOptions(Const ShortOptions: String; Const Longopts: Array of string;
  76. NonOptions: TStrings);
  77. procedure GetEnvironmentList(List: TStrings; NamesOnly: Boolean); virtual; abstract;
  78. procedure GetEnvironmentList(List: TStrings); virtual;
  79. procedure Log(EventType: TEventType; const Msg: String);
  80. procedure Log(EventType: TEventType; const Fmt: String; const Args: Array of const);
  81. // Delphi properties
  82. property ExeName: string read GetExeName;
  83. property Terminated: Boolean read FTerminated;
  84. property Title: string read FTitle write SetTitle;
  85. property OnException: TExceptionEvent read FOnException write FOnException;
  86. // Extra properties
  87. property ConsoleApplication: Boolean Read GetConsoleApplication;
  88. property Location: String Read GetLocation;
  89. property Params[Index: integer]: String Read GetParams;
  90. property ParamCount: Integer Read GetParamCount;
  91. property EnvironmentVariable[EnvName: String]: String Read GetEnvironmentVar;
  92. property OptionChar: Char Read FoptionChar Write FOptionChar;
  93. property CaseSensitiveOptions: Boolean Read FCaseSensitiveOptions Write FCaseSensitiveOptions;
  94. property StopOnException: Boolean Read FStopOnException Write FStopOnException;
  95. property ExceptionExitCode: Longint Read FExceptionExitCode Write FExceptionExitCode;
  96. property ExceptObject: Exception read FExceptObject write FExceptObject;
  97. property ExceptObjectJS: JSValue read FExceptObjectJS write FExceptObjectJS;
  98. property EventLogFilter: TEventLogTypes Read FEventLogFilter Write FEventLogFilter;
  99. end;
  100. var CustomApplication: TCustomApplication = nil;
  101. implementation
  102. { TCustomApplication }
  103. function TCustomApplication.GetEnvironmentVar(VarName: String): String;
  104. begin
  105. Result:=GetEnvironmentVariable(VarName);
  106. end;
  107. function TCustomApplication.GetExeName: string;
  108. begin
  109. Result:=ParamStr(0);
  110. end;
  111. function TCustomApplication.GetOptionAtIndex(AIndex: Integer; IsLong: Boolean
  112. ): String;
  113. Var
  114. P : Integer;
  115. O : String;
  116. begin
  117. Result:='';
  118. If AIndex=-1 then
  119. Exit;
  120. If IsLong then
  121. begin // Long options have form --option=value
  122. O:=Params[AIndex];
  123. P:=Pos('=',O);
  124. If P=0 then
  125. P:=Length(O);
  126. Delete(O,1,P);
  127. Result:=O;
  128. end
  129. else
  130. begin // short options have form '-o value'
  131. If AIndex<ParamCount then
  132. if Copy(Params[AIndex+1],1,1)<>'-' then
  133. Result:=Params[AIndex+1];
  134. end;
  135. end;
  136. procedure TCustomApplication.SetTitle(const AValue: string);
  137. begin
  138. FTitle:=AValue;
  139. end;
  140. function TCustomApplication.GetParams(Index: Integer): String;
  141. begin
  142. Result:=ParamStr(Index);
  143. end;
  144. function TCustomApplication.GetParamCount: Integer;
  145. begin
  146. Result:=System.ParamCount;
  147. end;
  148. procedure TCustomApplication.DoLog(EventType: TEventType; const Msg: String);
  149. begin
  150. // Do nothing, override in descendants
  151. if EventType=etCustom then ;
  152. if Msg='' then ;
  153. end;
  154. constructor TCustomApplication.Create(AOwner: TComponent);
  155. begin
  156. inherited Create(AOwner);
  157. FOptionChar:='-';
  158. FCaseSensitiveOptions:=True;
  159. FStopOnException:=False;
  160. end;
  161. procedure TCustomApplication.HandleException(Sender: TObject);
  162. begin
  163. ShowException(ExceptObject);
  164. if FStopOnException then
  165. Terminate(ExceptionExitCode);
  166. if Sender=nil then ;
  167. end;
  168. procedure TCustomApplication.Initialize;
  169. begin
  170. FTerminated:=False;
  171. end;
  172. procedure TCustomApplication.Run;
  173. begin
  174. Repeat
  175. ExceptObject:=nil;
  176. ExceptObjectJS:=nil;
  177. Try
  178. DoRun;
  179. except
  180. on E: Exception do
  181. begin
  182. ExceptObject:=E;
  183. ExceptObjectJS:=E;
  184. HandleException(Self);
  185. end
  186. else begin
  187. ExceptObject:=nil;
  188. ExceptObjectJS := JS.JSExceptValue;
  189. end;
  190. end;
  191. break;
  192. Until FTerminated;
  193. end;
  194. procedure TCustomApplication.Terminate;
  195. begin
  196. Terminate(ExitCode);
  197. end;
  198. procedure TCustomApplication.Terminate(AExitCode: Integer);
  199. begin
  200. FTerminated:=True;
  201. ExitCode:=AExitCode;
  202. end;
  203. function TCustomApplication.FindOptionIndex(const S: String;
  204. var Longopt: Boolean; StartAt: Integer): Integer;
  205. Var
  206. SO,O : String;
  207. I,P : Integer;
  208. begin
  209. If Not CaseSensitiveOptions then
  210. SO:=UpperCase(S)
  211. else
  212. SO:=S;
  213. Result:=-1;
  214. I:=StartAt;
  215. if I=-1 then
  216. I:=ParamCount;
  217. While (Result=-1) and (I>0) do
  218. begin
  219. O:=Params[i];
  220. // - must be seen as an option value
  221. If (Length(O)>1) and (O[1]=FOptionChar) then
  222. begin
  223. Delete(O,1,1);
  224. LongOpt:=(Length(O)>0) and (O[1]=FOptionChar);
  225. If LongOpt then
  226. begin
  227. Delete(O,1,1);
  228. P:=Pos('=',O);
  229. If (P<>0) then
  230. O:=Copy(O,1,P-1);
  231. end;
  232. If Not CaseSensitiveOptions then
  233. O:=UpperCase(O);
  234. If (O=SO) then
  235. Result:=i;
  236. end;
  237. Dec(i);
  238. end;
  239. end;
  240. function TCustomApplication.GetOptionValue(const S: String): String;
  241. begin
  242. Result:=GetOptionValue(' ',S);
  243. end;
  244. function TCustomApplication.GetOptionValue(const C: Char; const S: String
  245. ): String;
  246. Var
  247. B : Boolean;
  248. I : integer;
  249. begin
  250. Result:='';
  251. I:=FindOptionIndex(C,B);
  252. If I=-1 then
  253. I:=FindOptionIndex(S,B);
  254. If I<>-1 then
  255. Result:=GetOptionAtIndex(I,B);
  256. end;
  257. function TCustomApplication.GetOptionValues(const C: Char; const S: String
  258. ): TStringDynArray;
  259. Var
  260. I,Cnt : Integer;
  261. B : Boolean;
  262. begin
  263. SetLength(Result,ParamCount);
  264. Cnt:=0;
  265. Repeat
  266. I:=FindOptionIndex(C,B,I);
  267. If I<>-1 then
  268. begin
  269. Inc(Cnt);
  270. Dec(I);
  271. end;
  272. Until I=-1;
  273. Repeat
  274. I:=FindOptionIndex(S,B,I);
  275. If I<>-1 then
  276. begin
  277. Inc(Cnt);
  278. Dec(I);
  279. end;
  280. Until I=-1;
  281. SetLength(Result,Cnt);
  282. Cnt:=0;
  283. I:=-1;
  284. Repeat
  285. I:=FindOptionIndex(C,B,I);
  286. If (I<>-1) then
  287. begin
  288. Result[Cnt]:=GetOptionAtIndex(I,False);
  289. Inc(Cnt);
  290. Dec(i);
  291. end;
  292. Until (I=-1);
  293. I:=-1;
  294. Repeat
  295. I:=FindOptionIndex(S,B,I);
  296. If I<>-1 then
  297. begin
  298. Result[Cnt]:=GetOptionAtIndex(I,True);
  299. Inc(Cnt);
  300. Dec(i);
  301. end;
  302. Until (I=-1);
  303. end;
  304. function TCustomApplication.HasOption(const S: String): Boolean;
  305. Var
  306. B : Boolean;
  307. begin
  308. Result:=FindOptionIndex(S,B)<>-1;
  309. end;
  310. function TCustomApplication.HasOption(const C: Char; const S: String): Boolean;
  311. Var
  312. B : Boolean;
  313. begin
  314. Result:=(FindOptionIndex(C,B)<>-1) or (FindOptionIndex(S,B)<>-1);
  315. end;
  316. function TCustomApplication.CheckOptions(const ShortOptions: String;
  317. const Longopts: TStrings; Opts, NonOpts: TStrings; AllErrors: Boolean
  318. ): String;
  319. Var
  320. I,J,L,P : Integer;
  321. O,OV,SO : String;
  322. UsedArg,HaveArg : Boolean;
  323. Function FindLongOpt(S : String) : boolean;
  324. Var
  325. I : integer;
  326. begin
  327. Result:=Assigned(LongOpts);
  328. if Not Result then
  329. exit;
  330. If CaseSensitiveOptions then
  331. begin
  332. I:=LongOpts.Count-1;
  333. While (I>=0) and (LongOpts[i]<>S) do
  334. Dec(i);
  335. end
  336. else
  337. begin
  338. S:=UpperCase(S);
  339. I:=LongOpts.Count-1;
  340. While (I>=0) and (UpperCase(LongOpts[i])<>S) do
  341. Dec(i);
  342. end;
  343. Result:=(I<>-1);
  344. end;
  345. Procedure AddToResult(Const Msg : string);
  346. begin
  347. If (Result<>'') then
  348. Result:=Result+sLineBreak;
  349. Result:=Result+Msg;
  350. end;
  351. begin
  352. If CaseSensitiveOptions then
  353. SO:=Shortoptions
  354. else
  355. SO:=LowerCase(Shortoptions);
  356. Result:='';
  357. I:=1;
  358. While (I<=ParamCount) and ((Result='') or AllErrors) do
  359. begin
  360. O:=Paramstr(I);
  361. If (Length(O)=0) or (O[1]<>FOptionChar) then
  362. begin
  363. If Assigned(NonOpts) then
  364. NonOpts.Add(O);
  365. end
  366. else
  367. begin
  368. If (Length(O)<2) then
  369. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O]))
  370. else
  371. begin
  372. HaveArg:=False;
  373. OV:='';
  374. // Long option ?
  375. If (O[2]=FOptionChar) then
  376. begin
  377. Delete(O,1,2);
  378. J:=Pos('=',O);
  379. If J<>0 then
  380. begin
  381. HaveArg:=true;
  382. OV:=O;
  383. Delete(OV,1,J);
  384. O:=Copy(O,1,J-1);
  385. end;
  386. // Switch Option
  387. If FindLongopt(O) then
  388. begin
  389. If HaveArg then
  390. AddToResult(Format(SErrNoOptionAllowed,[IntToStr(I),O]));
  391. end
  392. else
  393. begin // Required argument
  394. If FindLongOpt(O+':') then
  395. begin
  396. If Not HaveArg then
  397. AddToResult(Format(SErrOptionNeeded,[IntToStr(I),O]));
  398. end
  399. else
  400. begin // Optional Argument.
  401. If not FindLongOpt(O+'::') then
  402. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O]));
  403. end;
  404. end;
  405. end
  406. else // Short Option.
  407. begin
  408. HaveArg:=(I<ParamCount) and (Length(ParamStr(I+1))>0) and (ParamStr(I+1)[1]<>FOptionChar);
  409. UsedArg:=False;
  410. If Not CaseSensitiveOptions then
  411. O:=LowerCase(O);
  412. L:=Length(O);
  413. J:=2;
  414. While ((Result='') or AllErrors) and (J<=L) do
  415. begin
  416. P:=Pos(O[J],SO);
  417. If (P=0) or (O[j]=':') then
  418. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O[J]]))
  419. else
  420. begin
  421. If (P<Length(SO)) and (SO[P+1]=':') then
  422. begin
  423. // Required argument
  424. If ((P+1)=Length(SO)) or (SO[P+2]<>':') Then
  425. If (J<L) or not haveArg then // Must be last in multi-opt !!
  426. begin
  427. AddToResult(Format(SErrOptionNeeded,[IntToStr(I),O[J]]));
  428. end;
  429. O:=O[j]; // O is added to arguments.
  430. UsedArg:=True;
  431. end;
  432. end;
  433. Inc(J);
  434. end;
  435. HaveArg:=HaveArg and UsedArg;
  436. If HaveArg then
  437. begin
  438. Inc(I); // Skip argument.
  439. OV:=Paramstr(I);
  440. end;
  441. end;
  442. If HaveArg and ((Result='') or AllErrors) then
  443. If Assigned(Opts) then
  444. Opts.Add(O+'='+OV);
  445. end;
  446. end;
  447. Inc(I);
  448. end;
  449. end;
  450. function TCustomApplication.CheckOptions(const ShortOptions: String;
  451. const Longopts: array of string; Opts, NonOpts: TStrings; AllErrors: Boolean
  452. ): String;
  453. Var
  454. L : TStringList;
  455. I : Integer;
  456. begin
  457. L:=TStringList.Create;
  458. try
  459. For I:=0 to High(LongOpts) do
  460. L.Add(LongOpts[i]);
  461. Result:=CheckOptions(ShortOptions,L,Opts,NonOpts,AllErrors);
  462. finally
  463. L.Destroy;
  464. end;
  465. end;
  466. function TCustomApplication.CheckOptions(const ShortOptions: String;
  467. const Longopts: TStrings; AllErrors: Boolean): String;
  468. begin
  469. Result:=CheckOptions(ShortOptions,LongOpts,Nil,Nil,AllErrors);
  470. end;
  471. function TCustomApplication.CheckOptions(const ShortOptions: String;
  472. const LongOpts: array of string; AllErrors: Boolean): String;
  473. Var
  474. L : TStringList;
  475. I : Integer;
  476. begin
  477. L:=TStringList.Create;
  478. Try
  479. For I:=0 to High(LongOpts) do
  480. L.Add(LongOpts[i]);
  481. Result:=CheckOptions(ShortOptions,L,AllErrors);
  482. Finally
  483. L.Destroy;
  484. end;
  485. end;
  486. function TCustomApplication.CheckOptions(const ShortOptions: String;
  487. const LongOpts: String; AllErrors: Boolean): String;
  488. Const
  489. SepChars = ' '#10#13#9;
  490. Var
  491. L : TStringList;
  492. Len,I,J : Integer;
  493. begin
  494. L:=TStringList.Create;
  495. Try
  496. I:=1;
  497. Len:=Length(LongOpts);
  498. While I<=Len do
  499. begin
  500. While Isdelimiter(SepChars,LongOpts,I) do
  501. Inc(I);
  502. J:=I;
  503. While (J<=Len) and Not IsDelimiter(SepChars,LongOpts,J) do
  504. Inc(J);
  505. If (I<=J) then
  506. L.Add(Copy(LongOpts,I,(J-I)));
  507. I:=J+1;
  508. end;
  509. Result:=CheckOptions(Shortoptions,L,AllErrors);
  510. Finally
  511. L.Destroy;
  512. end;
  513. end;
  514. function TCustomApplication.GetNonOptions(const ShortOptions: String;
  515. const Longopts: array of string): TStringDynArray;
  516. Var
  517. NO : TStrings;
  518. I : Integer;
  519. begin
  520. No:=TStringList.Create;
  521. try
  522. GetNonOptions(ShortOptions,LongOpts,No);
  523. SetLength(Result,NO.Count);
  524. For I:=0 to NO.Count-1 do
  525. Result[I]:=NO[i];
  526. finally
  527. NO.Destroy;
  528. end;
  529. end;
  530. procedure TCustomApplication.GetNonOptions(const ShortOptions: String;
  531. const Longopts: array of string; NonOptions: TStrings);
  532. Var
  533. S : String;
  534. begin
  535. S:=CheckOptions(ShortOptions,LongOpts,Nil,NonOptions,true);
  536. if (S<>'') then
  537. Raise EListError.Create(S);
  538. end;
  539. procedure TCustomApplication.GetEnvironmentList(List: TStrings);
  540. begin
  541. GetEnvironmentList(List,False);
  542. end;
  543. procedure TCustomApplication.Log(EventType: TEventType; const Msg: String);
  544. begin
  545. If (FEventLogFilter=[]) or (EventType in FEventLogFilter) then
  546. DoLog(EventType,Msg);
  547. end;
  548. procedure TCustomApplication.Log(EventType: TEventType; const Fmt: String;
  549. const Args: array of const);
  550. begin
  551. try
  552. Log(EventType, Format(Fmt, Args));
  553. except
  554. On E: Exception do
  555. Log(etError,Format('Error formatting message "%s" with %d arguments: %s',
  556. [Fmt,IntToStr(Length(Args)),E.Message]));
  557. end
  558. end;
  559. end.