unitdiff.pp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. {
  2. $Id$
  3. FPDoc - Free Pascal Documentation Tool
  4. Copyright (C) 2000 - 2003 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. * Skeleton XML description file generator
  7. See the file COPYING, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. }
  13. program MakeSkel;
  14. uses
  15. SysUtils, Classes, Gettext,
  16. dGlobals, PasTree, PParser,PScanner;
  17. resourcestring
  18. SIdentifiersIn = 'Identifiers in file "%s"';
  19. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  20. SErrNoInputFile = 'No input file specified';
  21. SWarnAssumingList = 'Only one input file specified. Assuming --list option.';
  22. SExtraIdentifier = 'Extra identifier in file "%s" : Name: %s';
  23. SExtraTypedIdentifier = 'Extra identifier in file "%s" : Type %s, Name: %s';
  24. SIdenticalUnits = 'Unit interfaces are identical.';
  25. type
  26. TCmdLineAction = (actionHelp, actionDiff,ActionList);
  27. TSkelEngine = class(TFPDocEngine)
  28. public
  29. FList: TStringList;
  30. Constructor Create;
  31. Destructor Destroy;override;
  32. function CreateElement(AClass: TPTreeElement; const AName: String;
  33. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  34. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  35. end;
  36. Constructor TSkelEngine.Create;
  37. begin
  38. Inherited Create;
  39. FList:=TStringList.Create;
  40. FList.Sorted:=True;
  41. end;
  42. Destructor TSkelEngine.Destroy;
  43. begin
  44. FreeAndNil(FList);
  45. Inherited;
  46. end;
  47. const
  48. CmdLineAction: TCmdLineAction = actionDiff;
  49. OSTarget: String = {$I %FPCTARGETOS%};
  50. CPUTarget: String = {$I %FPCTARGETCPU%};
  51. var
  52. InputFile1,
  53. InputFile2 : String;
  54. DocLang: String;
  55. Engine1,
  56. Engine2: TSkelEngine;
  57. SparseList,
  58. DisableArguments,
  59. DisableProtected,
  60. DisablePrivate,
  61. DisableFunctionResults: Boolean;
  62. OutputName: String;
  63. f: Text;
  64. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  65. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  66. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  67. Function ExamineThisNode(APasElement : TPasElement) : Boolean;
  68. begin
  69. Result:=Assigned(AParent) and (Length(AName) > 0) and
  70. (not DisableArguments or (APasElement.ClassType <> TPasArgument)) and
  71. (not DisableFunctionResults or (APasElement.ClassType <> TPasResultElement)) and
  72. (not DisablePrivate or (AVisibility<>visPrivate)) and
  73. (not DisableProtected or (AVisibility<>visProtected));
  74. end;
  75. begin
  76. Result := AClass.Create(AName, AParent);
  77. if AClass.InheritsFrom(TPasModule) then
  78. CurModule := TPasModule(Result)
  79. else if ExamineThisNode(Result) then
  80. Flist.AddObject(Result.FullName,Result);
  81. end;
  82. Procedure Usage;
  83. begin
  84. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options] file1 file2');
  85. Writeln('Where [options] is one or more of :');
  86. Writeln(' --disable-arguments Do not check function arguments.');
  87. Writeln(' --disable-private Do not check class private fields.');
  88. Writeln(' --disable-protected Do not check class protected fields.');
  89. Writeln(' --help Emit help.');
  90. Writeln(' --input=cmdline Input file to create skeleton for.');
  91. Writeln(' Use options are as for compiler.');
  92. Writeln(' --lang=language Use selected language.');
  93. Writeln(' --list List identifiers instead of making a diff');
  94. Writeln(' --output=filename Send output to file.');
  95. Writeln(' --sparse Sparse list/diff (skip type identification)');
  96. end;
  97. procedure ParseOption(const s: String);
  98. var
  99. i: Integer;
  100. Cmd, Arg: String;
  101. begin
  102. if (s = '-h') or (s = '--help') then
  103. CmdLineAction := actionHelp
  104. else if s = '--disable-arguments' then
  105. DisableArguments := True
  106. else if s = '--disable-private' then
  107. DisablePrivate := True
  108. else if s = '--sparse' then
  109. SparseList := True
  110. else if s = '--disable-protected' then
  111. begin
  112. DisableProtected := True;
  113. DisablePrivate :=True;
  114. end
  115. else
  116. begin
  117. i := Pos('=', s);
  118. if i > 0 then
  119. begin
  120. Cmd := Copy(s, 1, i - 1);
  121. Arg := Copy(s, i + 1, Length(s));
  122. end
  123. else
  124. begin
  125. Cmd := s;
  126. SetLength(Arg, 0);
  127. end;
  128. if (Cmd = '-l') or (Cmd = '--lang') then
  129. DocLang := Arg
  130. else if (Cmd = '-o') or (Cmd = '--output') then
  131. OutputName := Arg
  132. else
  133. if (length(cmd)>0) and (cmd[1]='-') then
  134. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]))
  135. else if (InputFile1='') then
  136. InputFile1:=Cmd
  137. else if (InputFile2='') then
  138. InputFile2:=Cmd
  139. else
  140. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  141. end;
  142. end;
  143. procedure ParseCommandLine;
  144. Const
  145. {$IFDEF Unix}
  146. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  147. {$ELSE}
  148. MoFileTemplate ='intl/makeskel.%s.mo';
  149. {$ENDIF}
  150. var
  151. MOFilename: string;
  152. i: Integer;
  153. begin
  154. CmdLineAction := actionDiff;
  155. DocLang:='';
  156. SparseList:=False;
  157. for i := 1 to ParamCount do
  158. ParseOption(ParamStr(i));
  159. If (DocLang<>'') then
  160. begin
  161. MOFilename:=Format(MOFileTemplate,[DocLang]);
  162. if FileExists(MOFilename) then
  163. gettext.TranslateResourceStrings(MoFileName)
  164. else
  165. writeln('NOTE: unable to find tranlation file ',MOFilename);
  166. // Translate internal documentation strings
  167. TranslateDocStrings(DocLang);
  168. end;
  169. if (cmdLineAction<>ActionHelp) and (InputFile1='') and (InputFile2='') then
  170. Writeln(StdErr,SErrNoInputFile)
  171. else if (InputFile2='') and (CmdLineAction<>ActionList) then
  172. begin
  173. Writeln(StdErr,SWarnAssumingList);
  174. CmdLineAction:=ActionList;
  175. end;
  176. end;
  177. Function GetTypeDescription(El : TPasElement) : String;
  178. begin
  179. If Assigned(El) then
  180. Result:=El.ElementTypeName
  181. else
  182. Result:='(unknown)';
  183. end;
  184. Procedure ListIdentifiers(Fn : String; List : TStrings);
  185. Var
  186. I : Integer;
  187. begin
  188. Writeln(f,Format(SIdentifiersIn,[FN]));
  189. For I:=0 to List.Count-1 do
  190. begin
  191. If Not SparseList then
  192. Write(GetTypeDescription(TPasElement(List.Objects[i])),' : ');
  193. Writeln(List[i]);
  194. end;
  195. end;
  196. Procedure WriteExtra(FN,Id : String; El: TPaselement);
  197. begin
  198. If SparseList then
  199. Writeln(F,Format(SExtraIdentifier,[FN,ID]))
  200. else
  201. Writeln(F,Format(SExtraTypedIdentifier,[FN,GetTypeDescription(El),ID]));
  202. end;
  203. Procedure DoExtra(FN : String; L : TStrings);
  204. Var
  205. I,Len : Integer;
  206. S : String;
  207. begin
  208. I:=0;
  209. While (I<L.Count) do
  210. begin
  211. WriteExtra(FN,L[I],TPasElement(L.Objects[I]));
  212. // Delete possible subelements.
  213. S:=L[I]+'.';
  214. Len:=Length(S);
  215. While (I+1<L.Count) and (CompareText(Copy(L[I+1],1,Len),S)=0) do
  216. L.Delete(I+1);
  217. Inc(I);
  218. end;
  219. end;
  220. Procedure DiffIdentifiers(List1,List2 : TStrings);
  221. Var
  222. L1,L2 : TStrings;
  223. I,J : Integer;
  224. begin
  225. L1:=List1;
  226. L2:=List2;
  227. If List2.Count>List1.Count then
  228. begin
  229. L1:=List2;
  230. L2:=List1;
  231. end;
  232. // Remove all common elements.
  233. For I:=L1.Count-1 downto 0 do
  234. begin
  235. J:=L2.IndexOf(L1[i]);
  236. If (J<>-1) then
  237. begin
  238. L1.Delete(I);
  239. L2.Delete(J);
  240. end;
  241. end;
  242. If (List1.Count=0) and (List2.Count=0) then
  243. Writeln(F,SIdenticalUnits)
  244. else
  245. begin
  246. DoExtra(InputFile1,List1);
  247. DoExtra(InputFile2,List2);
  248. end;
  249. end;
  250. begin
  251. ParseCommandLine;
  252. if CmdLineAction = actionHelp then
  253. Usage
  254. else
  255. begin
  256. Assign(f, OutputName);
  257. Rewrite(f);
  258. Try
  259. Engine1:=TSkelEngine.Create;
  260. Try
  261. Engine1.SetPackageName('diff'); // do not localize
  262. ParseSource(Engine1, InputFile1, OSTarget, CPUTarget);
  263. if (InputFile2<>'') then
  264. begin
  265. Engine2:=TSkelEngine.Create;
  266. Try
  267. Engine2.SetPackageName('diff'); // do not localize
  268. ParseSource(Engine2, InputFile2, OSTarget, CPUTarget);
  269. If cmdLineAction=ActionList then
  270. begin
  271. ListIdentifiers(InputFile1,Engine1.FList);
  272. ListIdentifiers(InputFile2,Engine2.FList);
  273. end
  274. else
  275. DiffIdentifiers(Engine1.Flist,Engine2.Flist);
  276. finally
  277. Engine2.Free;
  278. end;
  279. end
  280. else
  281. ListIdentifiers(InputFile1,Engine1.FList);
  282. Finally
  283. Engine1.Free;
  284. end;
  285. Finally
  286. Close(f);
  287. end;
  288. end;
  289. end.
  290. {
  291. $Log$
  292. Revision 1.1 2004-11-14 21:18:58 michael
  293. + Initial check-in
  294. Revision 1.13 2004/09/13 16:04:52 peter
  295. * fix nested for-loop with same index
  296. Revision 1.12 2004/08/29 15:32:41 michael
  297. + More intelligent handling of nodes. Do not write unused nodes.
  298. Revision 1.11 2004/08/28 18:18:59 michael
  299. + Do not write descr nodes for module when updating
  300. Revision 1.10 2004/08/28 18:15:14 michael
  301. + Check whether outputfile not in inputfilenames
  302. Revision 1.9 2004/08/28 18:04:06 michael
  303. + Added update mode
  304. Revision 1.8 2004/08/25 07:16:43 michael
  305. + Improved translation handling
  306. Revision 1.7 2004/08/24 14:48:25 michael
  307. + Translate now called correctly...
  308. Revision 1.6 2004/05/01 20:13:40 marco
  309. * got fed up with exceptions on file not found. Fileresolver now raises a
  310. EFileNotFound error, and makeskel catches and exists gracefully
  311. Revision 1.5 2003/11/28 12:51:37 sg
  312. * Added support for source references
  313. Revision 1.4 2003/09/02 13:26:47 mattias
  314. MG: makeskel now ignores missing translation file
  315. Revision 1.3 2003/05/07 16:31:32 sg
  316. * Fixed a severe memory corruption problem on termination
  317. Revision 1.2 2003/03/28 13:01:36 michael
  318. + Patch from Charlie/iNQ to work with new scanner/parser
  319. Revision 1.1 2003/03/17 23:03:20 michael
  320. + Initial import in CVS
  321. }