custapp.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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 string);
  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. end;
  167. procedure TCustomApplication.Initialize;
  168. begin
  169. FTerminated:=False;
  170. end;
  171. procedure TCustomApplication.Run;
  172. begin
  173. Repeat
  174. ExceptObject:=nil;
  175. ExceptObjectJS:=nil;
  176. Try
  177. DoRun;
  178. except
  179. on E: Exception do
  180. begin
  181. ExceptObject:=E;
  182. ExceptObjectJS:=E;
  183. HandleException(Self);
  184. end
  185. else begin
  186. ExceptObject:=nil;
  187. ExceptObjectJS := JS.JSExceptValue;
  188. end;
  189. end;
  190. break;
  191. Until FTerminated;
  192. end;
  193. procedure TCustomApplication.Terminate;
  194. begin
  195. Terminate(ExitCode);
  196. end;
  197. procedure TCustomApplication.Terminate(AExitCode: Integer);
  198. begin
  199. FTerminated:=True;
  200. ExitCode:=AExitCode;
  201. end;
  202. function TCustomApplication.FindOptionIndex(const S: String;
  203. var Longopt: Boolean; StartAt: Integer): Integer;
  204. Var
  205. SO,O : String;
  206. I,P : Integer;
  207. begin
  208. If Not CaseSensitiveOptions then
  209. SO:=UpperCase(S)
  210. else
  211. SO:=S;
  212. Result:=-1;
  213. I:=StartAt;
  214. if I=-1 then
  215. I:=ParamCount;
  216. While (Result=-1) and (I>0) do
  217. begin
  218. O:=Params[i];
  219. // - must be seen as an option value
  220. If (Length(O)>1) and (O[1]=FOptionChar) then
  221. begin
  222. Delete(O,1,1);
  223. LongOpt:=(Length(O)>0) and (O[1]=FOptionChar);
  224. If LongOpt then
  225. begin
  226. Delete(O,1,1);
  227. P:=Pos('=',O);
  228. If (P<>0) then
  229. O:=Copy(O,1,P-1);
  230. end;
  231. If Not CaseSensitiveOptions then
  232. O:=UpperCase(O);
  233. If (O=SO) then
  234. Result:=i;
  235. end;
  236. Dec(i);
  237. end;
  238. end;
  239. function TCustomApplication.GetOptionValue(const S: String): String;
  240. begin
  241. Result:=GetOptionValue(' ',S);
  242. end;
  243. function TCustomApplication.GetOptionValue(const C: Char; const S: String
  244. ): String;
  245. Var
  246. B : Boolean;
  247. I : integer;
  248. begin
  249. Result:='';
  250. I:=FindOptionIndex(C,B);
  251. If I=-1 then
  252. I:=FindOptionIndex(S,B);
  253. If I<>-1 then
  254. Result:=GetOptionAtIndex(I,B);
  255. end;
  256. function TCustomApplication.GetOptionValues(const C: Char; const S: String
  257. ): TStringDynArray;
  258. Var
  259. I,Cnt : Integer;
  260. B : Boolean;
  261. begin
  262. SetLength(Result,ParamCount);
  263. Cnt:=0;
  264. Repeat
  265. I:=FindOptionIndex(C,B,I);
  266. If I<>-1 then
  267. begin
  268. Inc(Cnt);
  269. Dec(I);
  270. end;
  271. Until I=-1;
  272. Repeat
  273. I:=FindOptionIndex(S,B,I);
  274. If I<>-1 then
  275. begin
  276. Inc(Cnt);
  277. Dec(I);
  278. end;
  279. Until I=-1;
  280. SetLength(Result,Cnt);
  281. Cnt:=0;
  282. I:=-1;
  283. Repeat
  284. I:=FindOptionIndex(C,B,I);
  285. If (I<>-1) then
  286. begin
  287. Result[Cnt]:=GetOptionAtIndex(I,False);
  288. Inc(Cnt);
  289. Dec(i);
  290. end;
  291. Until (I=-1);
  292. I:=-1;
  293. Repeat
  294. I:=FindOptionIndex(S,B,I);
  295. If I<>-1 then
  296. begin
  297. Result[Cnt]:=GetOptionAtIndex(I,True);
  298. Inc(Cnt);
  299. Dec(i);
  300. end;
  301. Until (I=-1);
  302. end;
  303. function TCustomApplication.HasOption(const S: String): Boolean;
  304. Var
  305. B : Boolean;
  306. begin
  307. Result:=FindOptionIndex(S,B)<>-1;
  308. end;
  309. function TCustomApplication.HasOption(const C: Char; const S: String): Boolean;
  310. Var
  311. B : Boolean;
  312. begin
  313. Result:=(FindOptionIndex(C,B)<>-1) or (FindOptionIndex(S,B)<>-1);
  314. end;
  315. function TCustomApplication.CheckOptions(const ShortOptions: String;
  316. const Longopts: TStrings; Opts, NonOpts: TStrings; AllErrors: Boolean
  317. ): String;
  318. Var
  319. I,J,L,P : Integer;
  320. O,OV,SO : String;
  321. UsedArg,HaveArg : Boolean;
  322. Function FindLongOpt(S : String) : boolean;
  323. Var
  324. I : integer;
  325. begin
  326. Result:=Assigned(LongOpts);
  327. if Not Result then
  328. exit;
  329. If CaseSensitiveOptions then
  330. begin
  331. I:=LongOpts.Count-1;
  332. While (I>=0) and (LongOpts[i]<>S) do
  333. Dec(i);
  334. end
  335. else
  336. begin
  337. S:=UpperCase(S);
  338. I:=LongOpts.Count-1;
  339. While (I>=0) and (UpperCase(LongOpts[i])<>S) do
  340. Dec(i);
  341. end;
  342. Result:=(I<>-1);
  343. end;
  344. Procedure AddToResult(Const Msg : string);
  345. begin
  346. If (Result<>'') then
  347. Result:=Result+sLineBreak;
  348. Result:=Result+Msg;
  349. end;
  350. begin
  351. If CaseSensitiveOptions then
  352. SO:=Shortoptions
  353. else
  354. SO:=LowerCase(Shortoptions);
  355. Result:='';
  356. I:=1;
  357. While (I<=ParamCount) and ((Result='') or AllErrors) do
  358. begin
  359. O:=Paramstr(I);
  360. If (Length(O)=0) or (O[1]<>FOptionChar) then
  361. begin
  362. If Assigned(NonOpts) then
  363. NonOpts.Add(O);
  364. end
  365. else
  366. begin
  367. If (Length(O)<2) then
  368. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O]))
  369. else
  370. begin
  371. HaveArg:=False;
  372. OV:='';
  373. // Long option ?
  374. If (O[2]=FOptionChar) then
  375. begin
  376. Delete(O,1,2);
  377. J:=Pos('=',O);
  378. If J<>0 then
  379. begin
  380. HaveArg:=true;
  381. OV:=O;
  382. Delete(OV,1,J);
  383. O:=Copy(O,1,J-1);
  384. end;
  385. // Switch Option
  386. If FindLongopt(O) then
  387. begin
  388. If HaveArg then
  389. AddToResult(Format(SErrNoOptionAllowed,[IntToStr(I),O]));
  390. end
  391. else
  392. begin // Required argument
  393. If FindLongOpt(O+':') then
  394. begin
  395. If Not HaveArg then
  396. AddToResult(Format(SErrOptionNeeded,[IntToStr(I),O]));
  397. end
  398. else
  399. begin // Optional Argument.
  400. If not FindLongOpt(O+'::') then
  401. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O]));
  402. end;
  403. end;
  404. end
  405. else // Short Option.
  406. begin
  407. HaveArg:=(I<ParamCount) and (Length(ParamStr(I+1))>0) and (ParamStr(I+1)[1]<>FOptionChar);
  408. UsedArg:=False;
  409. If Not CaseSensitiveOptions then
  410. O:=LowerCase(O);
  411. L:=Length(O);
  412. J:=2;
  413. While ((Result='') or AllErrors) and (J<=L) do
  414. begin
  415. P:=Pos(O[J],SO);
  416. If (P=0) or (O[j]=':') then
  417. AddToResult(Format(SErrInvalidOption,[IntToStr(I),O[J]]))
  418. else
  419. begin
  420. If (P<Length(SO)) and (SO[P+1]=':') then
  421. begin
  422. // Required argument
  423. If ((P+1)=Length(SO)) or (SO[P+2]<>':') Then
  424. If (J<L) or not haveArg then // Must be last in multi-opt !!
  425. begin
  426. AddToResult(Format(SErrOptionNeeded,[IntToStr(I),O[J]]));
  427. end;
  428. O:=O[j]; // O is added to arguments.
  429. UsedArg:=True;
  430. end;
  431. end;
  432. Inc(J);
  433. end;
  434. HaveArg:=HaveArg and UsedArg;
  435. If HaveArg then
  436. begin
  437. Inc(I); // Skip argument.
  438. OV:=Paramstr(I);
  439. end;
  440. end;
  441. If HaveArg and ((Result='') or AllErrors) then
  442. If Assigned(Opts) then
  443. Opts.Add(O+'='+OV);
  444. end;
  445. end;
  446. Inc(I);
  447. end;
  448. end;
  449. function TCustomApplication.CheckOptions(const ShortOptions: String;
  450. const Longopts: array of string; Opts, NonOpts: TStrings; AllErrors: Boolean
  451. ): String;
  452. Var
  453. L : TStringList;
  454. I : Integer;
  455. begin
  456. L:=TStringList.Create;
  457. try
  458. For I:=0 to High(LongOpts) do
  459. L.Add(LongOpts[i]);
  460. Result:=CheckOptions(ShortOptions,L,Opts,NonOpts,AllErrors);
  461. finally
  462. L.Destroy;
  463. end;
  464. end;
  465. function TCustomApplication.CheckOptions(const ShortOptions: String;
  466. const Longopts: TStrings; AllErrors: Boolean): String;
  467. begin
  468. Result:=CheckOptions(ShortOptions,LongOpts,Nil,Nil,AllErrors);
  469. end;
  470. function TCustomApplication.CheckOptions(const ShortOptions: String;
  471. const LongOpts: array of string; AllErrors: Boolean): String;
  472. Var
  473. L : TStringList;
  474. I : Integer;
  475. begin
  476. L:=TStringList.Create;
  477. Try
  478. For I:=0 to High(LongOpts) do
  479. L.Add(LongOpts[i]);
  480. Result:=CheckOptions(ShortOptions,L,AllErrors);
  481. Finally
  482. L.Destroy;
  483. end;
  484. end;
  485. function TCustomApplication.CheckOptions(const ShortOptions: String;
  486. const LongOpts: String; AllErrors: Boolean): String;
  487. Const
  488. SepChars = ' '#10#13#9;
  489. Var
  490. L : TStringList;
  491. Len,I,J : Integer;
  492. begin
  493. L:=TStringList.Create;
  494. Try
  495. I:=1;
  496. Len:=Length(LongOpts);
  497. While I<=Len do
  498. begin
  499. While Isdelimiter(SepChars,LongOpts,I) do
  500. Inc(I);
  501. J:=I;
  502. While (J<=Len) and Not IsDelimiter(SepChars,LongOpts,J) do
  503. Inc(J);
  504. If (I<=J) then
  505. L.Add(Copy(LongOpts,I,(J-I)));
  506. I:=J+1;
  507. end;
  508. Result:=CheckOptions(Shortoptions,L,AllErrors);
  509. Finally
  510. L.Destroy;
  511. end;
  512. end;
  513. function TCustomApplication.GetNonOptions(const ShortOptions: String;
  514. const Longopts: array of string): TStringDynArray;
  515. Var
  516. NO : TStrings;
  517. I : Integer;
  518. begin
  519. No:=TStringList.Create;
  520. try
  521. GetNonOptions(ShortOptions,LongOpts,No);
  522. SetLength(Result,NO.Count);
  523. For I:=0 to NO.Count-1 do
  524. Result[I]:=NO[i];
  525. finally
  526. NO.Destroy;
  527. end;
  528. end;
  529. procedure TCustomApplication.GetNonOptions(const ShortOptions: String;
  530. const Longopts: array of string; NonOptions: TStrings);
  531. Var
  532. S : String;
  533. begin
  534. S:=CheckOptions(ShortOptions,LongOpts,Nil,NonOptions,true);
  535. if (S<>'') then
  536. Raise EListError.Create(S);
  537. end;
  538. procedure TCustomApplication.GetEnvironmentList(List: TStrings);
  539. begin
  540. GetEnvironmentList(List,False);
  541. end;
  542. procedure TCustomApplication.Log(EventType: TEventType; const Msg: String);
  543. begin
  544. If (FEventLogFilter=[]) or (EventType in FEventLogFilter) then
  545. DoLog(EventType,Msg);
  546. end;
  547. procedure TCustomApplication.Log(EventType: TEventType; const Fmt: String;
  548. const Args: array of string);
  549. begin
  550. try
  551. Log(EventType, Format(Fmt, Args));
  552. except
  553. On E: Exception do
  554. Log(etError,Format('Error formatting message "%s" with %d arguments: %s',
  555. [Fmt,IntToStr(Length(Args)),E.Message]));
  556. end
  557. end;
  558. end.