makeskel.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. 2005-2012 by
  6. various FPC contributors
  7. * Skeleton XML description file generator.
  8. This generator scans Pascal source code for identifiers and emits XML files
  9. suitable for further processing with the fpdoc documentation system:
  10. users can edit the XML file and add (help) description.
  11. See the file COPYING, included in this distribution,
  12. for details about the copyright.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. }
  17. program MakeSkel;
  18. {$mode objfpc}
  19. {$h+}
  20. uses
  21. SysUtils, Classes, Gettext,
  22. dGlobals, PasTree, PParser,PScanner;
  23. resourcestring
  24. STitle = 'MakeSkel - FPDoc skeleton XML description file generator';
  25. SVersion = 'Version %s [%s]';
  26. SCmdLineHelp = 'See documentation for usage.';
  27. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  28. SNoPackageNameProvided = 'Please specify a package name with --package=<name>';
  29. SOutputMustNotBeDescr = 'Output file must be different from description filenames.';
  30. SCreatingNewNode = 'Creating documentation for new node : %s';
  31. SNodeNotReferenced = 'Documentation node "%s" no longer used';
  32. SDone = 'Done.';
  33. type
  34. TCmdLineAction = (actionHelp, actionConvert);
  35. TNodePair = Class(TObject)
  36. Private
  37. FEl : TPasElement;
  38. FNode : TDocNode;
  39. Public
  40. Constructor Create(AnElement : TPasElement; ADocNode : TDocNode);
  41. Property Element : TPasElement Read FEl;
  42. Property DocNode : TDocNode Read FNode;
  43. end;
  44. TSkelEngine = class(TFPDocEngine)
  45. Private
  46. FEmittedList,
  47. FNodeList,
  48. FModules : TStringList;
  49. Procedure DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  50. public
  51. Destructor Destroy; override;
  52. Function MustWriteElement(El : TPasElement; Full : Boolean) : Boolean;
  53. Function WriteElement(Var F : Text; El : TPasElement; ADocNode : TDocNode) : Boolean;
  54. function FindModule(const AName: String): TPasModule; override;
  55. function CreateElement(AClass: TPTreeElement; const AName: String;
  56. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  57. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  58. procedure WriteUnReferencedNodes;
  59. Procedure WriteNodes(Var F : Text; AModule : TPasModule; List : TStrings);
  60. Procedure DocumentFile(Var F : Text; Const AFileName,ATarget,ACPU : String);
  61. Property NodeList : TStringList Read FNodeList;
  62. Property EmittedList : TStringList Read FEmittedList;
  63. end;
  64. const
  65. CmdLineAction: TCmdLineAction = actionConvert;
  66. OSTarget: String = {$I %FPCTARGETOS%};
  67. CPUTarget: String = {$I %FPCTARGETCPU%};
  68. FPCVersion: String = {$I %FPCVERSION%};
  69. FPCDate: String = {$I %FPCDATE%};
  70. var
  71. WriteDeclaration,
  72. UpdateMode,
  73. SortNodes,
  74. DisableOverride,
  75. DisableErrors,
  76. DisableSeealso,
  77. DisableArguments,
  78. DisableProtected,
  79. DisablePrivate,
  80. DisableFunctionResults: Boolean;
  81. EmitClassSeparator: Boolean;
  82. Constructor TNodePair.Create(AnElement : TPasElement; ADocNode : TDocNode);
  83. begin
  84. Fel:=Anelement;
  85. FNode:=ADocNode;
  86. end;
  87. function TSkelEngine.FindModule(const AName: String): TPasModule;
  88. Var
  89. I : Integer;
  90. begin
  91. Result:=Inherited FindModule(AName);
  92. If (Result=Nil) then
  93. begin // Create dummy list and search in that.
  94. If (FModules=Nil) then
  95. begin
  96. FModules:=TStringList.Create;
  97. FModules.Sorted:=True;
  98. end;
  99. I:=FModules.IndexOf(AName);
  100. IF (I=-1) then
  101. begin
  102. Result:=TPasModule.Create(AName,Nil);
  103. FModules.AddObject(AName,Result);
  104. end
  105. else
  106. Result:=FModules.Objects[i] as TPasModule;
  107. end;
  108. end;
  109. Destructor TSkelEngine.Destroy;
  110. Var
  111. I : Integer;
  112. begin
  113. If Assigned(FModules) then
  114. begin
  115. { For I:=0 to FModules.Count-1 do
  116. FModules.Objects[i].Release;}
  117. FreeAndNil(FModules);
  118. end;
  119. end;
  120. Function TSkelEngine.MustWriteElement(El : TPasElement; Full : Boolean) : Boolean;
  121. Var
  122. ParentVisible:Boolean;
  123. PT,PP : TPasElement;
  124. begin
  125. ParentVisible:=True;
  126. If (El is TPasArgument) or (El is TPasResultElement) or (el is TPasExpr) then
  127. begin
  128. PT:=El.Parent;
  129. // Skip ProcedureType or PasFunctionType
  130. If (PT<>Nil) then
  131. begin
  132. if (PT is TPasProcedureType) or (PT is TPasFunctionType) then
  133. PT:=PT.Parent;
  134. If (PT<>Nil) and ((PT is TPasProcedure) or (PT is TPasProcedure)) then
  135. PP:=PT.Parent
  136. else
  137. PP:=Nil;
  138. If (PP<>Nil) and (PP is TPasClassType) then
  139. begin
  140. ParentVisible:=((not DisablePrivate or (PT.Visibility<>visPrivate)) and
  141. (not DisableProtected or (PT.Visibility<>visProtected)));
  142. end;
  143. end;
  144. end;
  145. Result:=Assigned(El.Parent) and (Length(El.Name) > 0) and
  146. (Not (El is TPasExpr)) and
  147. (ParentVisible and (not DisableArguments or (El.ClassType <> TPasArgument))) and
  148. (ParentVisible and (not DisableFunctionResults or (El.ClassType <> TPasResultElement))) and
  149. (not DisablePrivate or (Not (el.Visibility in [visPrivate,visStrictPrivate]))) and
  150. (not DisableProtected or (Not (el.Visibility in [visProtected,visStrictProtected])));
  151. If Result and Full then
  152. begin
  153. Result:=(Not Assigned(FEmittedList) or (FEmittedList.IndexOf(El.FullName)=-1));
  154. If DisableOverride and (El is TPasProcedure) then
  155. Result:=Not TPasProcedure(El).IsOverride;
  156. end;
  157. end;
  158. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  159. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  160. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  161. Var
  162. DN : TDocNode;
  163. begin
  164. Result := AClass.Create(AName, AParent);
  165. Result.Visibility:=AVisibility;
  166. // Let function/procedure arguments and function results
  167. // inherit visibility from their parents if visDefault visibility is
  168. // specified.
  169. // This allows easier text searches on visibility in the resulting XML
  170. if (AVisibility=visDefault) and
  171. ((Result is TPasArgument) or (Result is TPasResultElement)) then
  172. Result.Visibility:=AParent.Visibility;
  173. if AClass.InheritsFrom(TPasModule) then
  174. CurModule := TPasModule(Result);
  175. // Track this element
  176. If UpdateMode then
  177. begin
  178. DN:=FindDocNode(Result);
  179. If Assigned(DN) then
  180. DN.IncRefCount;
  181. end
  182. else
  183. DN:=Nil;
  184. // See if we need to write documentation for it
  185. If MustWriteElement(Result,False) then
  186. FNodeList.AddObject(Result.PathName,TNodePair.Create(Result,DN));
  187. end;
  188. Function TSkelEngine.WriteElement(Var F : Text;El : TPasElement; ADocNode : TDocNode) : Boolean;
  189. Function WriteOnlyShort(APasElement : TPasElement) : Boolean;
  190. begin
  191. Result:=(APasElement.ClassType=TPasArgument) or
  192. (APasElement.ClassType=TPasResultElement) or
  193. (APasElement.ClassType=TPasEnumValue);
  194. end;
  195. Function IsTypeVarConst(APasElement : TPasElement) : Boolean;
  196. begin
  197. With APasElement do
  198. Result:=(InheritsFrom(TPasType) and not InheritsFrom(TPasClassType)) or
  199. (InheritsFrom(TPasResString)) or
  200. (InheritsFrom(TPasVariable));
  201. end;
  202. Function NeedDeclaration(El : TPasElement) : boolean;
  203. begin
  204. Result:=IsTypeVarConst(El)
  205. or WriteOnlyShort(El)
  206. or EL.InheritsFrom(TPasProcedure)
  207. end;
  208. begin
  209. // Check again, this time with full declaration.
  210. Result:=MustWriteElement(El,True);
  211. If Result and UpdateMode then
  212. Result:=(ADocNode=Nil);
  213. If Not Result Then
  214. Exit;
  215. If UpdateMode then
  216. Writeln(stderr,Format(ScreatingNewNode,[el.PathName]));
  217. FEmittedList.Add(El.FullName); // So we don't emit again.
  218. WriteLn(f);
  219. if EmitClassSeparator and (El.ClassType = TPasClassType) then
  220. begin
  221. WriteLn(f, '<!--');
  222. WriteLn(f, ' ********************************************************************');
  223. WriteLn(f, ' ', El.PathName);
  224. WriteLn(f, ' ********************************************************************');
  225. WriteLn(f, '-->');
  226. WriteLn(f);
  227. end;
  228. If Not (WriteDeclaration and NeedDeclaration(El)) then
  229. Writeln(F,'<!-- ', El.ElementTypeName,' Visibility: ',VisibilityNames[El.Visibility], ' -->')
  230. else
  231. begin
  232. Writeln(F,'<!-- ',El.ElementTypeName,' Visibility: ',VisibilityNames[El.Visibility]);
  233. Writeln(F,' Declaration: ',El.GetDeclaration(True),' -->');
  234. end;
  235. WriteLn(f,'<element name="', El.FullName, '">');
  236. WriteLn(f, '<short></short>');
  237. if Not WriteOnlyShort(El) then
  238. begin
  239. WriteLn(f, '<descr>');
  240. WriteLn(f, '</descr>');
  241. if not (DisableErrors or IsTypeVarConst(El)) then
  242. begin
  243. WriteLn(f, '<errors>');
  244. WriteLn(f, '</errors>');
  245. end;
  246. if not DisableSeealso then
  247. begin
  248. WriteLn(f, '<seealso>');
  249. WriteLn(f, '</seealso>');
  250. end;
  251. end;
  252. WriteLn(f, '</element>');
  253. end;
  254. Procedure TSkelEngine.DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  255. begin
  256. If (N<>Nil) then
  257. begin
  258. If (NodePath<>'') then
  259. NodePath:=NodePath+'.';
  260. DoWriteUnReferencedNodes(N.FirstChild,NodePath+N.Name);
  261. While (N<>Nil) do
  262. begin
  263. if (N.RefCount=0) and (N.Node<>Nil) and (Not N.TopicNode) then
  264. Writeln(stderr,Format(SNodeNotReferenced,[NodePath+N.Name]));
  265. N:=N.NextSibling;
  266. end;
  267. end;
  268. end;
  269. procedure TSkelEngine.WriteUnReferencedNodes;
  270. begin
  271. DoWriteUnReferencedNodes(RootDocNode,'');
  272. end;
  273. Procedure TSkelEngine.WriteNodes(Var F : Text; AModule : TPasModule; List : TStrings);
  274. Var
  275. P : TNodePair;
  276. I : integer;
  277. begin
  278. WriteLn(f);
  279. WriteLn(f, '<!--');
  280. WriteLn(f, ' ====================================================================');
  281. WriteLn(f, ' ', Amodule.Name);
  282. WriteLn(f, ' ====================================================================');
  283. WriteLn(f, '-->');
  284. WriteLn(f);
  285. WriteLn(f, '<module name="', AModule.Name, '">');
  286. if not UpdateMode then
  287. begin
  288. WriteLn(f, '<short></short>');
  289. WriteLn(f, '<descr>');
  290. WriteLn(f, '</descr>');
  291. end;
  292. Try
  293. For I:=0 to List.Count-1 do
  294. begin
  295. P:=List.Objects[i] as TNodePair;
  296. If (P.Element<>AModule) then
  297. WriteElement(F,P.Element,P.DocNode);
  298. end;
  299. Finally
  300. WriteLn(f, '');
  301. WriteLn(f, '</module> <!-- ', AModule.Name, ' -->');
  302. WriteLn(f, '');
  303. end;
  304. end;
  305. Procedure TSkelEngine.DocumentFile(Var F : Text; Const AFileName,ATarget,ACPU : String);
  306. Procedure ResolveOperators;
  307. Var
  308. E : TPasElement;
  309. P : TNodePair;
  310. N : TDocNode;
  311. I : integer;
  312. begin
  313. For I:=0 to FNodeList.Count-1 do
  314. begin
  315. P:=TNodePair(FNodeList.Objects[i]);
  316. if P.Element.InheritsFrom(TPasOperator) then
  317. begin
  318. N:=FindDocNode(P.Element);
  319. If Assigned(N) then
  320. N.IncRefCount;
  321. P.FNode:=N;
  322. end;
  323. end;
  324. end;
  325. Var
  326. Module : TPasModule;
  327. I : Integer;
  328. N : TDocNode;
  329. begin
  330. // wrong because afilename is a cmdline with other options. Straight testing filename is therefore wrong.
  331. // if not(FileExists(AFileName)) then
  332. // raise Exception.CreateFmt('Cannot find source file %s to document.',[AFileName]);
  333. FNodeList:=TStringList.Create;
  334. Try
  335. FEmittedList:=TStringList.Create;
  336. FEmittedList.Sorted:=True;
  337. try
  338. Module:=ParseSource(Self,AFileName,ATarget,ACPU);
  339. If UpdateMode then
  340. begin
  341. N:=FindDocNode(Module);
  342. If Assigned(N) then
  343. N.IncRefCount;
  344. ResolveOperators;
  345. end;
  346. If SortNodes then
  347. FNodelist.Sorted:=True;
  348. WriteNodes(F,Module,FNodeList);
  349. If UpdateMode then
  350. WriteUnReferencedNodes;
  351. Finally
  352. FEmittedList.Free;
  353. end;
  354. Finally
  355. For I:=0 to FNodeList.Count-1 do
  356. FNodeList.Objects[i].Free;
  357. FNodeList.Free;
  358. end;
  359. end;
  360. { ---------------------------------------------------------------------
  361. Main program. Document all units.
  362. ---------------------------------------------------------------------}
  363. Function DocumentPackage(Const APackageName,AOutputName : String; InputFiles,DescrFiles : TStrings) : String;
  364. Var
  365. F : Text;
  366. I,J : Integer;
  367. Engine: TSkelEngine;
  368. begin
  369. Result:='';
  370. Assign(f, AOutputName);
  371. Rewrite(f);
  372. Try
  373. WriteLn(f, '<?xml version="1.0" encoding="ISO-8859-1"?>');
  374. WriteLn(f, '<fpdoc-descriptions>');
  375. WriteLn(f, '<package name="', APackageName, '">');
  376. Try
  377. I:=0;
  378. While (Result='') And (I<InputFiles.Count) do
  379. begin
  380. Engine := TSkelEngine.Create;
  381. Try
  382. Engine.SetPackageName(APackageName);
  383. if UpdateMode then
  384. For J:=0 to DescrFiles.Count-1 do
  385. Engine.AddDocFile(DescrFiles[J]);
  386. Try
  387. Engine.DocumentFile(F,InputFiles[I],OSTarget,CPUTarget);
  388. except
  389. on E:Exception do
  390. begin
  391. WriteLn('Error while documenting: '+E.message);
  392. Result:='Error while documenting: '+E.message;
  393. end;
  394. end;
  395. Finally
  396. Engine.Free;
  397. end;
  398. Inc(I);
  399. end;
  400. Finally
  401. WriteLn(f, '</package>');
  402. WriteLn(f, '</fpdoc-descriptions>');
  403. end;
  404. finally
  405. Close(f);
  406. end;
  407. end;
  408. { ---------------------------------------------------------------------
  409. Option management
  410. ---------------------------------------------------------------------}
  411. var
  412. InputFiles,
  413. DescrFiles : TStringList;
  414. DocLang : String;
  415. PackageName,
  416. OutputName: String;
  417. procedure InitOptions;
  418. begin
  419. InputFiles := TStringList.Create;
  420. DescrFiles := TStringList.Create;
  421. end;
  422. procedure FreeOptions;
  423. begin
  424. DescrFiles.Free;
  425. InputFiles.Free;
  426. end;
  427. procedure ParseOption(const s: String);
  428. procedure AddToFileList(List: TStringList; const FileName: String);
  429. var
  430. f: Text;
  431. s: String;
  432. begin
  433. if Copy(FileName, 1, 1) = '@' then
  434. begin
  435. Assign(f, Copy(FileName, 2, Length(FileName)));
  436. Reset(f);
  437. while not EOF(f) do
  438. begin
  439. ReadLn(f, s);
  440. List.Add(s);
  441. end;
  442. Close(f);
  443. end else
  444. List.Add(FileName);
  445. end;
  446. var
  447. i: Integer;
  448. Cmd, Arg: String;
  449. begin
  450. if (s = '-h') or (s = '--help') then
  451. CmdLineAction := actionHelp
  452. else if s = '--update' then
  453. UpdateMode := True
  454. else if s = '--disable-arguments' then
  455. DisableArguments := True
  456. else if s = '--disable-errors' then
  457. DisableErrors := True
  458. else if s = '--disable-function-results' then
  459. DisableFunctionResults := True
  460. else if s = '--disable-seealso' then
  461. DisableSeealso := True
  462. else if s = '--disable-private' then
  463. DisablePrivate := True
  464. else if s = '--disable-override' then
  465. DisableOverride := True
  466. else if s = '--disable-protected' then
  467. begin
  468. DisableProtected := True;
  469. DisablePrivate :=True;
  470. end
  471. else if (s = '--emitclassseparator') or (s='--emit-class-separator') then
  472. EmitClassSeparator := True
  473. else if (s = '--emit-declaration') then
  474. WriteDeclaration := True
  475. else if (s = '--sort-nodes') then
  476. SortNodes := True
  477. else
  478. begin
  479. i := Pos('=', s);
  480. if i > 0 then
  481. begin
  482. Cmd := Copy(s, 1, i - 1);
  483. Arg := Copy(s, i + 1, Length(s));
  484. end else
  485. begin
  486. Cmd := s;
  487. SetLength(Arg, 0);
  488. end;
  489. if (Cmd = '-i') or (Cmd = '--input') then
  490. AddToFileList(InputFiles, Arg)
  491. else if (Cmd = '-l') or (Cmd = '--lang') then
  492. DocLang := Arg
  493. else if (Cmd = '-o') or (Cmd = '--output') then
  494. OutputName := Arg
  495. else if Cmd = '--package' then
  496. PackageName := Arg
  497. else if Cmd = '--descr' then
  498. begin
  499. if FileExists(Arg) then
  500. DescrFiles.Add(Arg);
  501. end
  502. else
  503. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  504. end;
  505. end;
  506. Function ParseCommandLine : Integer;
  507. Const
  508. {$IFDEF Unix}
  509. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  510. {$ELSE}
  511. MoFileTemplate ='intl/makeskel.%s.mo';
  512. {$ENDIF}
  513. var
  514. MOFilename: string;
  515. i: Integer;
  516. begin
  517. Result:=0;
  518. DocLang:='';
  519. for i := 1 to ParamCount do
  520. ParseOption(ParamStr(i));
  521. If (DocLang<>'') then
  522. begin
  523. MOFilename:=Format(MOFileTemplate,[DocLang]);
  524. if FileExists(MOFilename) then
  525. gettext.TranslateResourceStrings(MoFileName)
  526. else
  527. writeln('NOTE: unable to find translation file ',MOFilename);
  528. // Translate internal documentation strings
  529. TranslateDocStrings(DocLang);
  530. end;
  531. // Action is to create the XML skeleton
  532. if (Length(PackageName) = 0) and (CmdLineAction<>ActionHelp) then
  533. begin
  534. WriteLn(SNoPackageNameProvided);
  535. Result:=2;
  536. end;
  537. if DescrFiles.IndexOf(OutputName)<>-1 then
  538. begin
  539. Writeln(SOutputMustNotBeDescr);
  540. Result:=3;
  541. end;
  542. end;
  543. { ---------------------------------------------------------------------
  544. Usage
  545. ---------------------------------------------------------------------}
  546. Procedure Usage;
  547. begin
  548. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
  549. Writeln('Where [options] is one or more of :');
  550. Writeln(' --descr=filename Filename for update.');
  551. Writeln(' --disable-arguments Do not create nodes for function arguments.');
  552. Writeln(' --disable-errors Do not create errors node.');
  553. Writeln(' --disable-function-results');
  554. Writeln(' Do not create nodes for function arguments.');
  555. Writeln(' --disable-override Do not create nodes for override methods.');
  556. Writeln(' --disable-private Do not create nodes for class private fields.');
  557. Writeln(' --disable-protected Do not create nodes for class protected fields.');
  558. Writeln(' --disable-seealso Do not create seealso node.');
  559. Writeln(' --emit-class-separator');
  560. Writeln(' Emit descriptive comment between classes.');
  561. Writeln(' --emit-declaration Emit declaration for elements.');
  562. Writeln(' --help Emit help.');
  563. Writeln(' --input=cmdline Input file to create skeleton for.');
  564. Writeln(' Use options are as for compiler.');
  565. Writeln(' --lang=language Use selected language.');
  566. Writeln(' --output=filename Send output to file.');
  567. Writeln(' --package=name Specify package name (mandatory).');
  568. Writeln(' --sort-nodes Sort element nodes (not modules)');
  569. Writeln(' --update Update mode. Output only missing nodes.');
  570. end;
  571. { ---------------------------------------------------------------------
  572. Main Program
  573. ---------------------------------------------------------------------}
  574. Procedure Run;
  575. var
  576. E: Integer;
  577. begin
  578. WriteLn(STitle);
  579. WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
  580. WriteLn(SCopyright1);
  581. WriteLn(SCopyright2);
  582. InitOptions;
  583. Try
  584. E:=ParseCommandLine;
  585. If E<>0 then
  586. Halt(E);
  587. WriteLn;
  588. if CmdLineAction = actionHelp then
  589. Usage
  590. else
  591. begin
  592. DocumentPackage(PackageName,OutputName,InputFiles,DescrFiles);
  593. WriteLn(SDone);
  594. end;
  595. Finally
  596. FreeOptions;
  597. end;
  598. end;
  599. Begin
  600. Run;
  601. end.