2
0

dglobals.pp 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2002 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. * Global declarations
  6. * Link list management
  7. * Document node tree
  8. * Main engine
  9. See the file COPYING, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. }
  15. {$MODE objfpc}
  16. {$H+}
  17. unit dGlobals;
  18. interface
  19. uses Classes, DOM, PasTree, PParser, uriparser, SysUtils;
  20. Const
  21. CacheSize = 20;
  22. ContentBufSize = 4096 * 8;
  23. Var
  24. LEOL : Integer;
  25. modir : string;
  26. resourcestring
  27. // Output strings
  28. SDocPackageTitle = 'Reference for package ''%s''';
  29. SDocPackageMenuTitle = 'Package ''%s''';
  30. SDocPackageLinkTitle = 'Package';
  31. SDocPrograms = 'Programs';
  32. SDocUnits = 'Units';
  33. SDocUnitTitle = 'Reference for unit ''%s''';
  34. SDocUnitMenuTitle = 'Unit ''%s''';
  35. SDocInheritanceHierarchy = 'Inheritance Hierarchy';
  36. SDocInterfaceSection = 'Interface section';
  37. SDocImplementationSection = 'Implementation section';
  38. SDocUsedUnits = 'Used units';
  39. SDocUsedUnitsByUnitXY = 'Used units by unit ''%s''';
  40. SDocConstsTypesVars = 'Constants, types and variables';
  41. SDocResStrings = 'Resource strings';
  42. SDocTypes = 'Types';
  43. SDocType = 'Type';
  44. SDocConstants = 'Constants';
  45. SDocConstant = 'Constant';
  46. SDocClasses = 'Classes';
  47. SDocClass = 'Class';
  48. SDocProceduresAndFunctions = 'Procedures and functions';
  49. SDocProcedureOrFunction = 'Procedure/function';
  50. SDocVariables = 'Variables';
  51. SDocVariable = 'Variable';
  52. SDocIdentifierIndex = 'Index';
  53. SDocPackageClassHierarchy = 'Class hierarchy';
  54. SDocModuleIndex = 'Index of all identifiers in unit ''%s''';
  55. SDocPackageIndex = 'Index of all identifiers in package ''%s''';
  56. SDocUnitOverview = 'Overview of unit ''%s''';
  57. SDocOverview = 'Overview';
  58. SDocSearch = 'Search';
  59. SDocDeclaration = 'Declaration';
  60. SDocDescription = 'Description';
  61. SDocErrors = 'Errors';
  62. SDocVersion = 'Version info';
  63. SDocSeeAlso = 'See also';
  64. SDocExample = 'Example';
  65. SDocArguments = 'Arguments';
  66. SDocFunctionResult = 'Function result';
  67. SDocRemark = 'Remark: ';
  68. SDocMethodOverview = 'Method overview';
  69. SDocPropertyOverview = 'Property overview';
  70. SDocEventOverview = 'Event overview';
  71. SDocInterfacesOverview = 'Interfaces overview';
  72. SDocInterface = 'Interfaces';
  73. SDocPage = 'Page';
  74. SDocMember = 'Member';
  75. SDocMembers = 'Members';
  76. SDocField = 'Field';
  77. SDocMethod = 'Method';
  78. SDocProperty = 'Property';
  79. SDocAccess = 'Access';
  80. SDocInheritance = 'Inheritance';
  81. SDocProperties = 'Properties';
  82. SDocMethods = 'Methods';
  83. SDocEvents = 'Events';
  84. SDocByName = 'by Name';
  85. SDocByInheritance = 'By inheritance';
  86. SDocValue = 'Value';
  87. SDocExplanation = 'Explanation';
  88. SDocProcedure = 'Procedure';
  89. SDocValuesForEnum = 'Enumeration values for type %s';
  90. SDocSourcePosition = 'Source position: %s line %d';
  91. SDocSynopsis = 'Synopsis';
  92. SDocVisibility = 'Visibility';
  93. SDocOpaque = 'Opaque type';
  94. SDocDateGenerated = 'Documentation generated on: %s';
  95. // The next line requires leading/trailing space due to XML comment layout:
  96. SDocGeneratedByComment = ' Generated using FPDoc - (c) 2000-2012 FPC contributors and Sebastian Guenther, [email protected] ';
  97. SDocNotes = 'Notes';
  98. SDocName = 'Name';
  99. SDocType_s = 'Type(s)';
  100. SDocTopic = 'Topic';
  101. SDocNoneAVailable = 'No members available';
  102. // Topics
  103. SDocRelatedTopics = 'Related topics';
  104. SDocUp = 'Up';
  105. SDocNext = 'Next';
  106. SDocPrevious = 'Previous';
  107. // Various backend constants
  108. SDocChapter = 'Chapter';
  109. SDocSection = 'Section';
  110. SDocSubSection = 'Subsection';
  111. SDocTable = 'Table';
  112. SDocListing = 'Listing';
  113. // Man page usage
  114. SManUsageManSection = 'Use ASection as the man page section';
  115. SManUsageNoUnitPrefix = 'Do not prefix man pages with unit name.';
  116. SManUsageWriterDescr = 'UNIX man page output.';
  117. SManUsagePackageDescription = 'Use descr as the description of man pages';
  118. // HTML usage
  119. SHTMLUsageFooter = 'Append xhtml (@filename reads from file) as footer to html page';
  120. SHTMLUsageNavigator = 'Append xhtml (@filename reads from file) in navigator bar';
  121. SHTMLUsageHeader = 'Append xhtml (@filename reads from file) as header to html page below navigation bar';
  122. SHTMLUsageFooterDate = 'Append footer with date. fmt is Optional format for FormatDateTime';
  123. SHTMLUsageCharset = 'Set the HTML character set';
  124. SHTMLHtmlSearch = 'Add search page with given name to the menu bar';
  125. SHTMLIndexColcount = 'Use N columns in the identifier index pages';
  126. SHTMLImageUrl = 'Prefix image URLs with url';
  127. SHTMLDisableMenuBrackets = 'Disable ''['' and '']'' characters around menu items at the top of the page. Useful for custom css';
  128. // CHM usage
  129. SCHMUsageTOC = 'Use [File] as the table of contents. Usually a .hhc file.';
  130. SCHMUsageIndex = 'Use [File] as the index. Usually a .hhk file.';
  131. SCHMUsageDefPage = 'Set the "Home" page relative to where it lives in the chm. i.e. "/index.html"';
  132. SCHMUsageOtrFiles= 'A txt file containing a list of files to be added relative to the working directory.';
  133. SCHMUsageCSSFile = 'Filename of a .css file to be included in the chm.';
  134. SCHMUsageAutoTOC = 'Automatically generate a Table of Contents. Ignores --toc-file';
  135. SCHMUsageAutoIDX = 'Automatically generate an Index. Ignores --index-file';
  136. SCHMUsageMakeSearch = 'Automatically generate a Search Index from filenames that match *.htm*';
  137. SCHMUsageChmTitle= 'Title of the chm. Defaults to the value from --package';
  138. SXMLUsageSource = 'Include source file and line info in generated XML';
  139. // Linear usage
  140. SLinearUsageDupLinkedDocsP1 = 'Duplicate linked element documentation in';
  141. SLinearUsageDupLinkedDocsP2 = 'descendant classes.';
  142. STitle = 'FPDoc - Free Pascal Documentation Tool';
  143. SVersion = 'Version %s [%s]';
  144. SCopyright1 = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, [email protected]';
  145. SCopyright2 = '(c) 2005 - 2012 various FPC contributors';
  146. SCmdLineHelp = 'Usage: %s [options]';
  147. SUsageOption008 = '--base-descr-dir=DIR prefix all description files with this directory';
  148. SUsageOption009 = '--base-input-dir=DIR prefix all input files with this directory';
  149. SUsageOption010 = '--content Create content file for package cross-references';
  150. SUsageOption020 = '--cputarget=value Set the target CPU for the scanner.';
  151. SUsageOption030 = '--descr=file use file as description file, e.g.: ';
  152. SUsageOption035 = ' --descr=c:\WIP\myzipperdoc.xml';
  153. SUsageOption040 = ' This option is allowed more than once';
  154. SUsageOption050 = '--descr-dir=Dir Add All XML files in Dir to list of description files';
  155. SUsageOption060 = '--format=fmt Select output format.';
  156. SUsageOption070 = '--help Show this help.';
  157. SUsageOption080 = '--hide-protected Do not show protected methods in overview';
  158. SUsageOption090 = '--import=file Import content file for package cross-references';
  159. SUsageOption100 = '--input=cmd use cmd as input for the parser, e.g.:';
  160. SUsageOption110 = ' --input=C:\fpc\packages\paszlib\src\zipper.pp';
  161. SUsageOption120 = ' At least one input option is required.';
  162. SUsageOption130 = '--input-dir=Dir Add All *.pp and *.pas files in Dir to list of input files';
  163. SUsageOption140 = '--lang=lng Select output language.';
  164. SUsageOption145 = '--macro=name=value Define a macro to preprocess the project file with.';
  165. SUsageOption150 = '--ostarget=value Set the target OS for the scanner.';
  166. SUsageOption160 = '--output=name use name as the output name.';
  167. SUsageOption170 = ' Each backend interprets this as needed.';
  168. SUsageOption180 = '--package=name Set the package name for which to create output,';
  169. SUsageOption190 = ' e.g. --package=fcl';
  170. SUsageOption200 = '--project=file Use file as project file';
  171. SUsageOption210 = '--show-private Show private methods.';
  172. SUsageOption215 = '--stop-on-parser-error';
  173. SUsageOption215A = ' Stop when a parser error occurs. Default is to ignore parser errors.';
  174. SUsageOption220 = '--warn-no-node Warn if no documentation node was found.';
  175. SUsageOption230 = '--mo-dir=dir Set directory where language files reside to dir';
  176. SUsageOption240 = '--parse-impl (Experimental) try to parse implementation too';
  177. SUsageOption250 = '--dont-trim Do not trim XML contents. Useful for preserving';
  178. SUsageOption260 = ' formatting inside e.g <pre> tags';
  179. SUsageOption270 = '--write-project=file';
  180. SUsageOption280 = ' Do not write documentation, create project file instead';
  181. SUsageOption290 = '--verbose Write more information on the screen';
  182. SUsageOption300 = '--dry-run Only parse sources and XML, do not create output';
  183. SUsageOption310 = '--write-project=file';
  184. SUsageOption320 = ' Write all command-line options to a project file';
  185. SUsageFormats = 'The following output formats are supported by this fpdoc:';
  186. SUsageBackendHelp = 'Specify an output format, combined with --help to get more help for this backend.';
  187. SUsageFormatSpecific = 'Output format "%s" supports the following options:';
  188. SCmdLineErrInvalidMacro = 'Macro needs to be in the form name=value';
  189. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  190. SCmdLineInvalidFormat = 'Invalid format "%s" specified';
  191. SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=<filename>';
  192. SWritingPages = 'Writing %d pages...';
  193. SNeedPackageName = 'No package name specified. Please specify one using the --package option.';
  194. SAvailablePackages = 'Available packages: ';
  195. SDone = 'Done.';
  196. SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';
  197. SErrCouldNotCreateFile = 'Could not create file "%s": %s';
  198. SSeeURL = '(See %s)'; // For linear text writers.
  199. SParsingUsedUnit = 'Parsing used unit "%s" with commandLine "%s"';
  200. Const
  201. SVisibility: array[TPasMemberVisibility] of string =
  202. ('Default', 'Private', 'Protected', 'Public',
  203. 'Published', 'Automated','Strict Private','Strict Protected',
  204. 'Required', 'Optional' // ObjCClass
  205. );
  206. type
  207. TBufType = Array[1..ContentBufSize-1] of byte;
  208. // Assumes a list of TObject instances and frees them on destruction
  209. TObjectList = class(TFPList)
  210. public
  211. destructor Destroy; override;
  212. end;
  213. TPasExternalClassType = Class(TPasClassType);
  214. TPasExternalModule = Class(TPasModule);
  215. { Link entry tree
  216. TFPDocEngine stores the root of the entry tree in its property
  217. "RootLinkNode". The root has one child node for each package, for which
  218. documentation links are available. The children of a package node
  219. are module nodes; and the children of a module node are the top-level
  220. declarations of this module; the next level in the tree stores e.g. record
  221. members, and so on...
  222. }
  223. TLinkNode = class
  224. private
  225. FFirstChild, FNextSibling: TLinkNode;
  226. FName: String;
  227. FLink: String;
  228. public
  229. constructor Create(const AName, ALink: String);
  230. destructor Destroy; override;
  231. function FindChild(const APathName: String): TLinkNode;
  232. function CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  233. // Properties for tree structure
  234. property FirstChild: TLinkNode read FFirstChild;
  235. property NextSibling: TLinkNode read FNextSibling;
  236. // Link properties
  237. property Name: String read FName;
  238. property Link: String read FLink;
  239. end;
  240. { Documentation entry tree
  241. TFPDocEngine stores the root of the entry tree in its property
  242. "RootDocNode". The root has one child node for each package, for which
  243. documentation is being provided by the user. The children of a package node
  244. are module nodes; and the children of a module node are the top-level
  245. declarations of this module; the next level in the tree stores e.g. record
  246. members, and so on...
  247. }
  248. { TDocNode }
  249. TDocNode = class
  250. private
  251. FFirstChild, FNextSibling: TDocNode;
  252. FName: String;
  253. FNode: TDOMElement;
  254. FIsSkipped: Boolean;
  255. FShortDescr: TDOMElement;
  256. FDescr: TDOMElement;
  257. FErrorsDoc: TDOMElement;
  258. FSeeAlso: TDOMElement;
  259. FFirstExample: TDOMElement;
  260. FNotes : TDomElement;
  261. FLink: String;
  262. FTopicNode : Boolean;
  263. FRefCount : Integer;
  264. FVersion: TDomElement;
  265. public
  266. constructor Create(const AName: String; ANode: TDOMElement);
  267. destructor Destroy; override;
  268. Function IncRefcount : Integer;
  269. function FindChild(const APathName: String): TDocNode;
  270. function CreateChildren(const APathName: String): TDocNode;
  271. // Properties for tree structure
  272. property FirstChild: TDocNode read FFirstChild;
  273. property NextSibling: TDocNode read FNextSibling;
  274. // Basic properties
  275. property Name: String read FName;
  276. property Node: TDOMElement read FNode;
  277. // Data fetched from the XML document
  278. property IsSkipped: Boolean read FIsSkipped;
  279. property ShortDescr: TDOMElement read FShortDescr;
  280. property Descr: TDOMElement read FDescr;
  281. property ErrorsDoc: TDOMElement read FErrorsDoc;
  282. Property Version : TDomElement Read FVersion;
  283. property SeeAlso: TDOMElement read FSeeAlso;
  284. property FirstExample: TDOMElement read FFirstExample;
  285. property Notes : TDOMElement read FNotes;
  286. property Link: String read FLink;
  287. Property TopicNode : Boolean Read FTopicNode;
  288. Property RefCount : Integer Read FRefCount;
  289. end;
  290. // The main FPDoc engine
  291. TFPDocLogLevel = (dleWarnNoNode);
  292. TFPDocLogLevels = set of TFPDocLogLevel;
  293. TOnParseUnitEvent = Procedure (Sender : TObject; Const AUnitName : String; Out AInputFile,OSTarget,CPUTarget : String) of Object;
  294. { TFPDocEngine }
  295. TFPDocEngine = class(TPasTreeContainer)
  296. private
  297. FDocLogLevels: TFPDocLogLevels;
  298. FOnParseUnit: TOnParseUnitEvent;
  299. function ResolveLinkInPackages(AModule: TPasModule; const ALinkDest: String; Strict: Boolean=False): String;
  300. function ResolveLinkInUsedUnits(AModule: TPasModule; const ALinkDest: String; Strict: Boolean=False): String;
  301. protected
  302. FAlwaysVisible : TStringList;
  303. DescrDocs: TObjectList; // List of XML documents
  304. DescrDocNames: TStringList; // Names of the XML documents
  305. FRootLinkNode: TLinkNode; // Global tree of TlinkNode from the imported .xct files
  306. FRootDocNode: TDocNode; // Global tree of TDocNode from the .xml documentation files
  307. FPackages: TFPList; // Global list of TPasPackage objects and full tree of sources
  308. CurModule: TPasModule;
  309. CurPackageDocNode: TDocNode;
  310. function ParseUsedUnit(AName, AInputLine,AOSTarget,ACPUTarget: String): TPasModule; virtual;
  311. Function LogEvent(E : TFPDocLogLevel) : Boolean;
  312. Procedure DoLog(Const Msg : String);overload;
  313. Procedure DoLog(Const Fmt : String; Args : Array of const);overload;
  314. public
  315. Output: String;
  316. HasContentFile: Boolean;
  317. HidePrivate: Boolean; // Hide private class members in output?
  318. HideProtected: Boolean; // Hide protected class members in output?
  319. WarnNoNode : Boolean; // Warn if no description node found for element.
  320. constructor Create;
  321. destructor Destroy; override;
  322. procedure SetPackageName(const APackageName: String);
  323. // The process importing of objects from external .xct file
  324. procedure ReadContentFile(const AFilename, ALinkPrefix: String);
  325. // Creation of an own .xct output file
  326. procedure WriteContentFile(const AFilename: String);
  327. function CreateElement(AClass: TPTreeElement; const AName: String;
  328. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  329. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  330. override;
  331. function FindInModule(const AName: String ; AModule: TPasModule): TPasElement;
  332. function FindElement(const AName: String): TPasElement; override;
  333. function FindModule(const AName: String): TPasModule; override;
  334. Function HintsToStr(Hints : TPasMemberHints) : String;
  335. // Link tree support
  336. procedure AddLink(const APathName, ALinkTo: String);
  337. function FindAbsoluteLink(const AName: String): String;
  338. function ResolveLink(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  339. function FindLinkedNode(ANode: TDocNode): TDocNode;
  340. Function ShowElement(El : TPasElement) : Boolean; inline;
  341. // Call this before documenting.
  342. Procedure StartDocumenting; virtual;
  343. // Documentation file support
  344. procedure AddDocFile(const AFilename: String;DontTrim:boolean=false);
  345. // Documentation retrieval
  346. function FindDocNode(AElement: TPasElement): TDocNode;
  347. function FindDocNode(ARefModule: TPasModule; const AName: String): TDocNode;
  348. function FindShortDescr(AElement: TPasElement): TDOMElement;
  349. function FindShortDescr(ARefModule: TPasModule; const AName: String): TDOMElement;
  350. function GetExampleFilename(const ExElement: TDOMElement): String;
  351. property RootLinkNode: TLinkNode read FRootLinkNode;
  352. property RootDocNode: TDocNode read FRootDocNode;
  353. Property DocLogLevels : TFPDocLogLevels Read FDocLogLevels Write FDocLogLevels;
  354. Property OnParseUnit : TOnParseUnitEvent Read FOnParseUnit Write FOnParseUnit;
  355. end;
  356. procedure TranslateDocStrings(const Lang: String);
  357. function DumpExceptionCallStack(E: Exception):String;
  358. Function IsLinkNode(Node : TDomNode) : Boolean;
  359. Function IsExampleNode(Example : TDomNode) : Boolean;
  360. // returns true is link is an absolute URI
  361. Function IsLinkAbsolute(ALink: String): boolean;
  362. implementation
  363. uses Gettext, XMLRead;
  364. const
  365. AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');
  366. { TObjectList }
  367. destructor TObjectList.Destroy;
  368. var
  369. i: Integer;
  370. begin
  371. for i := 0 to Count - 1 do
  372. TObject(Items[i]).Free;
  373. inherited Destroy;
  374. end;
  375. { TLinkNode }
  376. constructor TLinkNode.Create(const AName, ALink: String);
  377. begin
  378. inherited Create;
  379. FName := AName;
  380. FLink := ALink;
  381. end;
  382. destructor TLinkNode.Destroy;
  383. begin
  384. if Assigned(FirstChild) then
  385. FirstChild.Free;
  386. if Assigned(NextSibling) then
  387. NextSibling.Free;
  388. inherited Destroy;
  389. end;
  390. function TLinkNode.FindChild(const APathName: String): TLinkNode;
  391. var
  392. DotPos: Integer;
  393. ChildName: String;
  394. Child: TLinkNode;
  395. begin
  396. if Length(APathName) = 0 then
  397. Result := Self
  398. else
  399. begin
  400. DotPos := Pos('.', APathName);
  401. if DotPos = 0 then
  402. ChildName := APathName
  403. else
  404. ChildName := Copy(APathName, 1, DotPos - 1);
  405. Child := FirstChild;
  406. while Assigned(Child) do
  407. begin
  408. if CompareText(Child.Name, ChildName) = 0 then
  409. begin
  410. if DotPos = 0 then
  411. Result := Child
  412. else
  413. Result := Child.FindChild(
  414. Copy(APathName, DotPos + 1, Length(APathName)));
  415. exit;
  416. end;
  417. Child := Child.NextSibling;
  418. end;
  419. Result := nil;
  420. end;
  421. end;
  422. function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  423. var
  424. DotPos: Integer;
  425. ChildName: String;
  426. Child, LastChild: TLinkNode;
  427. begin
  428. if Length(APathName) = 0 then
  429. Result := Self
  430. else
  431. begin
  432. DotPos := Pos('.', APathName);
  433. if DotPos = 0 then
  434. ChildName := APathName
  435. else
  436. ChildName := Copy(APathName, 1, DotPos - 1);
  437. Child := FirstChild;
  438. LastChild := nil;
  439. while Assigned(Child) do
  440. begin
  441. if CompareText(Child.Name, ChildName) = 0 then
  442. begin
  443. if DotPos = 0 then
  444. Result := Child
  445. else
  446. Result := Child.CreateChildren(
  447. Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);
  448. exit;
  449. end;
  450. LastChild := Child;
  451. Child := Child.NextSibling;
  452. end;
  453. Result := TLinkNode.Create(ChildName, ALinkTo);
  454. if Assigned(LastChild) then
  455. LastChild.FNextSibling := Result
  456. else
  457. FFirstChild := Result;
  458. end;
  459. end;
  460. { TDocNode }
  461. constructor TDocNode.Create(const AName: String; ANode: TDOMElement);
  462. begin
  463. inherited Create;
  464. FName := AName;
  465. FNode := ANode;
  466. end;
  467. destructor TDocNode.Destroy;
  468. begin
  469. if Assigned(FirstChild) then
  470. FirstChild.Free;
  471. if Assigned(NextSibling) then
  472. NextSibling.Free;
  473. inherited Destroy;
  474. end;
  475. Function TDocNode.IncRefcount : Integer;
  476. begin
  477. Inc(FRefCount);
  478. Result:=FRefCount;
  479. end;
  480. function TDocNode.FindChild(const APathName: String): TDocNode;
  481. var
  482. DotPos: Integer;
  483. ChildName: String;
  484. Child: TDocNode;
  485. begin
  486. if Length(APathName) = 0 then
  487. Result := Self
  488. else
  489. begin
  490. DotPos := Pos('.', APathName);
  491. if DotPos = 0 then
  492. ChildName := APathName
  493. else
  494. ChildName := Copy(APathName, 1, DotPos - 1);
  495. Child := FirstChild;
  496. while Assigned(Child) do
  497. begin
  498. if CompareText(Child.Name, ChildName) = 0 then
  499. begin
  500. if DotPos = 0 then
  501. Result := Child
  502. else
  503. Result := Child.FindChild(
  504. Copy(APathName, DotPos + 1, Length(APathName)));
  505. exit;
  506. end;
  507. Child := Child.NextSibling;
  508. end;
  509. Result := nil;
  510. end;
  511. end;
  512. function TDocNode.CreateChildren(const APathName: String): TDocNode;
  513. var
  514. DotPos: Integer;
  515. ChildName: String;
  516. Child: TDocNode;
  517. begin
  518. if Length(APathName) = 0 then
  519. Result := Self
  520. else
  521. begin
  522. DotPos := Pos('.', APathName);
  523. if DotPos = 0 then
  524. ChildName := APathName
  525. else
  526. ChildName := Copy(APathName, 1, DotPos - 1);
  527. Child := FirstChild;
  528. while Assigned(Child) do
  529. begin
  530. if CompareText(Child.Name, ChildName) = 0 then
  531. begin
  532. if DotPos = 0 then
  533. Result := Child
  534. else
  535. Result := Child.CreateChildren(
  536. Copy(APathName, DotPos + 1, Length(APathName)));
  537. exit;
  538. end;
  539. Child := Child.NextSibling;
  540. end;
  541. // No child found, let's create one
  542. Result := TDocNode.Create(ChildName, nil);
  543. if Assigned(FirstChild) then
  544. begin
  545. Result.FNextSibling := FirstChild;
  546. FFirstChild := Result;
  547. end else
  548. FFirstChild := Result;
  549. if DotPos > 0 then
  550. Result := Result.CreateChildren(
  551. Copy(APathName, DotPos + 1, Length(APathName)));
  552. end;
  553. end;
  554. { TFPDocEngine }
  555. function TFPDocEngine.LogEvent(E: TFPDocLogLevel): Boolean;
  556. begin
  557. Result:=E in FDocLogLevels;
  558. end;
  559. procedure TFPDocEngine.DoLog(const Msg: String);
  560. begin
  561. If Assigned(OnLog) then
  562. OnLog(Self,Msg);
  563. end;
  564. procedure TFPDocEngine.DoLog(const Fmt: String; Args: array of const);
  565. begin
  566. DoLog(Format(Fmt,Args));
  567. end;
  568. constructor TFPDocEngine.Create;
  569. begin
  570. inherited Create;
  571. DescrDocs := TObjectList.Create;
  572. FAlwaysVisible := TStringList.Create;
  573. FAlwaysVisible.CaseSensitive:=True;
  574. DescrDocNames := TStringList.Create;
  575. FRootLinkNode := TLinkNode.Create('', '');
  576. FRootDocNode := TDocNode.Create('', nil);
  577. HidePrivate := True;
  578. InterfaceOnly:=True;
  579. FPackages := TFPList.Create;
  580. end;
  581. destructor TFPDocEngine.Destroy;
  582. var
  583. i: Integer;
  584. begin
  585. for i := 0 to FPackages.Count - 1 do
  586. TPasPackage(FPackages[i]).Release;
  587. FreeAndNil(FRootDocNode);
  588. FreeAndNil(FRootLinkNode);
  589. FreeAndNil(DescrDocNames);
  590. FreeAndNil(DescrDocs);
  591. FreeAndNil(FAlwaysVisible);
  592. FreeAndNil(FPackages);
  593. inherited Destroy;
  594. end;
  595. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  596. begin
  597. ASSERT(not Assigned(Package));
  598. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  599. '#' + APackageName, nil, '', 0));
  600. FPackages.Add(FPackage);
  601. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  602. If Assigned(CurPackageDocNode) then
  603. CurPackageDocNode.IncRefCount;
  604. end;
  605. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  606. var
  607. f: Text;
  608. inheritanceinfo : TStringlist; // contents list of TPasClass with inheritance info
  609. // like this #PackageName.ModuleName.ClassName
  610. tmpLinkPrefix : string;
  611. procedure ReadLinkTree;
  612. var
  613. s: String;
  614. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  615. CurParent, PrevSibling, NewNode: TLinkNode;
  616. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  617. begin
  618. PrevSpaces := 0;
  619. CurParent := RootLinkNode;
  620. PrevSibling := CurParent.FirstChild;
  621. if assigned(PrevSibling) then
  622. while assigned(PrevSibling.NextSibling) do
  623. PrevSibling := PrevSibling.NextSibling;
  624. StackIndex := 0;
  625. while True do
  626. begin
  627. ReadLn(f, s);
  628. if Length(s) = 0 then
  629. break;
  630. ThisSpaces := 0;
  631. while s[ThisSpaces + 1] = ' ' do
  632. Inc(ThisSpaces);
  633. if ThisSpaces <> PrevSpaces then
  634. begin
  635. if ThisSpaces > PrevSpaces then
  636. begin
  637. { Dive down one level }
  638. ParentStack[StackIndex] := CurParent;
  639. SiblingStack[StackIndex] := PrevSibling;
  640. Inc(StackIndex);
  641. CurParent := PrevSibling;
  642. PrevSibling := nil;
  643. end else
  644. while PrevSpaces > ThisSpaces do
  645. begin
  646. Dec(StackIndex);
  647. CurParent := ParentStack[StackIndex];
  648. PrevSibling := SiblingStack[StackIndex];
  649. Dec(PrevSpaces);
  650. end;
  651. PrevSpaces := ThisSpaces;
  652. end;
  653. i := ThisSpaces + 1;
  654. while s[i] <> ' ' do
  655. Inc(i);
  656. if ALinkPrefix <> '' then
  657. tmpLinkPrefix := ExcludeTrailingPathDelimiter(ALinkPrefix)+'/';
  658. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  659. tmpLinkPrefix + Copy(s, i + 1, Length(s)));
  660. if pos(' ',newnode.link)>0 then
  661. writeln(stderr,'Bad format imported node: name="',newnode.name,'" link="',newnode.link,'"');
  662. if Assigned(PrevSibling) then
  663. PrevSibling.FNextSibling := NewNode
  664. else
  665. CurParent.FFirstChild := NewNode;
  666. PrevSibling := NewNode;
  667. end;
  668. end;
  669. function ResolvePackageModule(AName:String;out pkg:TPasPackage;out module:TPasModule;createnew:boolean):String;
  670. var
  671. DotPos, DotPos2, i: Integer;
  672. s: String;
  673. HPackage: TPasPackage;
  674. begin
  675. pkg:=nil; module:=nil; result:='';
  676. // Find or create package
  677. DotPos := Pos('.', AName);
  678. s := Copy(AName, 1, DotPos - 1);
  679. HPackage := nil;
  680. for i := 0 to FPackages.Count - 1 do
  681. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  682. begin
  683. HPackage := TPasPackage(FPackages[i]);
  684. break;
  685. end;
  686. if not Assigned(HPackage) then
  687. begin
  688. if not CreateNew then
  689. exit;
  690. HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  691. '', 0));
  692. FPackages.Add(HPackage);
  693. end;
  694. // Find or create module
  695. DotPos2 := DotPos;
  696. repeat
  697. Inc(DotPos2);
  698. until AName[DotPos2] = '.';
  699. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  700. Module := nil;
  701. for i := 0 to HPackage.Modules.Count - 1 do
  702. if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then
  703. begin
  704. Module := TPasModule(HPackage.Modules[i]);
  705. break;
  706. end;
  707. if not Assigned(Module) then
  708. begin
  709. if not CreateNew then
  710. exit;
  711. Module := TPasExternalModule.Create(s, HPackage);
  712. Module.InterfaceSection := TInterfaceSection.Create('', Module);
  713. Module.PackageName:= HPackage.Name;
  714. HPackage.Modules.Add(Module);
  715. end;
  716. pkg:=hpackage;
  717. result:=Copy(AName, DotPos2 + 1, length(AName)-dotpos2);
  718. end;
  719. function SearchInList(clslist:TFPList;s:string):TPasElement;
  720. var i : integer;
  721. ClassEl: TPasElement;
  722. begin
  723. result:=nil;
  724. for i:=0 to clslist.count-1 do
  725. begin
  726. ClassEl := TPasElement(clslist[i]);
  727. if CompareText(ClassEl.Name,s) =0 then
  728. exit(Classel);
  729. end;
  730. end;
  731. function ResolveClassType(AName:String):TPasClassType;
  732. var
  733. pkg : TPasPackage;
  734. module : TPasModule;
  735. s : string;
  736. begin
  737. Result:=nil;
  738. s:=ResolvePackageModule(AName,pkg,module,False);
  739. if not assigned(module) then
  740. exit;
  741. result:=TPasClassType(SearchInList(Module.InterfaceSection.Classes,s));
  742. end;
  743. function ResolveAliasType(AName:String):TPasAliasType;
  744. var
  745. pkg : TPasPackage;
  746. module : TPasModule;
  747. s : string;
  748. begin
  749. Result:=nil;
  750. s:=ResolvePackageModule(AName,pkg,module,False);
  751. if not assigned(module) then
  752. exit;
  753. result:=TPasAliasType(SearchInList(Module.InterfaceSection.Types,s));
  754. if not (result is TPasAliasType) then
  755. result:=nil;
  756. end;
  757. procedure ReadClasses;
  758. function CreateClass(const AName: String;InheritanceStr:String): TPasClassType;
  759. var
  760. s: String;
  761. HPackage: TPasPackage;
  762. Module: TPasModule;
  763. begin
  764. s:= ResolvePackageModule(AName,HPackage,Module,True);
  765. // Create node for class
  766. Result := TPasExternalClassType.Create(s, Module.InterfaceSection);
  767. Result.ObjKind := okClass;
  768. Module.InterfaceSection.Declarations.Add(Result);
  769. Module.InterfaceSection.Classes.Add(Result);
  770. // defer processing inheritancestr till all classes are loaded.
  771. if inheritancestr<>'' then
  772. InheritanceInfo.AddObject(Inheritancestr,result);
  773. end;
  774. procedure splitalias(var instr:string;out outstr:string);
  775. var i,j:integer;
  776. begin
  777. if length(instr)=0 then exit;
  778. instr:=trim(instr);
  779. i:=pos('(',instr);
  780. if i>0 then
  781. begin
  782. j:=length(instr)-i;
  783. if instr[length(instr)]=')' then
  784. dec(j);
  785. outstr:=copy(instr,i+1,j);
  786. delete(instr,i,j+2);
  787. end
  788. end;
  789. Function ResolveAndLinkClass(clname:String;IsClass:boolean;cls:TPasClassType):TPasClassType;
  790. begin
  791. result:=TPasClassType(ResolveClassType(clname));
  792. if assigned(result) and not (cls=result) then // save from tobject=implicit tobject
  793. begin
  794. result.addref;
  795. if IsClass then
  796. begin
  797. cls.ancestortype:=result;
  798. // writeln(cls.name, ' has as ancestor ',result.pathname);
  799. end
  800. else
  801. begin
  802. cls.interfaces.add(result);
  803. // writeln(cls.name, ' implements ',result.pathname);
  804. end;
  805. end
  806. else
  807. if cls<>result then
  808. DoLog('Warning : ancestor class %s of class %s could not be resolved',[clname,cls.name]);
  809. end;
  810. function CreateAliasType (alname,clname : string;parentclass:TPasClassType; out cl2 :TPasClassType):TPasAliasType;
  811. // create alias clname = alname
  812. var
  813. pkg : TPasPackage;
  814. module : TPasModule;
  815. s : string;
  816. begin
  817. Result:=nil;
  818. s:=ResolvePackageModule(Alname,pkg,module,True);
  819. if not assigned(module) then
  820. exit;
  821. cl2:=TPasClassType(ResolveClassType(alname));
  822. if assigned( cl2) and not (parentclass=cl2) then
  823. begin
  824. result:=ResolveAliasType(clname);
  825. if assigned(result) then
  826. begin
  827. // writeln('found alias ',clname,' (',s,') ',result.classname);
  828. end
  829. else
  830. begin
  831. // writeln('new alias ',clname,' (',s,') ');
  832. cl2.addref;
  833. Result := TPasAliasType(CreateElement(TPasAliasType,s,module.interfacesection,vispublic,'',0));
  834. module.interfacesection.Declarations.Add(Result);
  835. TPasAliasType(Result).DestType := cl2;
  836. end
  837. end
  838. end;
  839. procedure ProcessInheritanceStrings(inhInfo:TStringList);
  840. var i,j : integer;
  841. cls : TPasClassType;
  842. cls2: TPasClassType;
  843. clname,
  844. alname : string;
  845. inhclass : TStringList;
  846. begin
  847. inhclass:=TStringList.Create;
  848. inhclass.delimiter:=',';
  849. if InhInfo.Count>0 then
  850. for i:=0 to InhInfo.Count-1 do
  851. begin
  852. cls:=TPasClassType(InhInfo.Objects[i]);
  853. inhclass.clear;
  854. inhclass.delimitedtext:=InhInfo[i];
  855. for j:= 0 to inhclass.count-1 do
  856. begin
  857. // writeln('processing',inhclass[j]);
  858. clname:=inhclass[j];
  859. splitalias(clname,alname);
  860. if alname<>'' then // the class//interface we refered to is an alias
  861. begin
  862. // writeln('Found alias pair ',clname,' = ',alname);
  863. if not assigned(CreateAliasType(alname,clname,cls,cls2)) then
  864. DoLog('Warning: creating alias %s for %s failed!',[alname,clname]);
  865. end
  866. else
  867. cls2:=ResolveAndLinkClass(clname,j=0,cls);
  868. end;
  869. end;
  870. inhclass.free;
  871. end;
  872. var
  873. s, Name: String;
  874. CurClass: TPasClassType;
  875. i: Integer;
  876. Member: TPasElement;
  877. begin
  878. inheritanceinfo :=TStringlist.Create;
  879. Try
  880. CurClass := nil;
  881. while True do
  882. begin
  883. ReadLn(f, s);
  884. if Length(s) = 0 then
  885. break;
  886. if s[1] = '#' then
  887. begin
  888. // New class
  889. i := Pos(' ', s);
  890. CurClass := CreateClass(Copy(s, 1, i - 1), copy(s,i+1,length(s)));
  891. end else
  892. begin
  893. i := Pos(' ', s);
  894. if i = 0 then
  895. Name := Copy(s, 3, Length(s))
  896. else
  897. Name := Copy(s, 3, i - 3);
  898. case s[2] of
  899. 'M':
  900. Member := TPasProcedure.Create(Name, CurClass);
  901. 'P':
  902. begin
  903. Member := TPasProperty.Create(Name, CurClass);
  904. if i > 0 then
  905. while i <= Length(s) do
  906. begin
  907. case s[i] of
  908. 'r':
  909. TPasProperty(Member).ReadAccessorName := '<dummy>';
  910. 'w':
  911. TPasProperty(Member).WriteAccessorName := '<dummy>';
  912. 's':
  913. TPasProperty(Member).StoredAccessorName := '<dummy>';
  914. end;
  915. Inc(i);
  916. end;
  917. end;
  918. 'V':
  919. Member := TPasVariable.Create(Name, CurClass);
  920. else
  921. raise Exception.Create('Invalid member type: ' + s[2]);
  922. end;
  923. CurClass.Members.Add(Member);
  924. end;
  925. end;
  926. ProcessInheritanceStrings(Inheritanceinfo);
  927. finally
  928. inheritanceinfo.Free;
  929. end;
  930. end;
  931. var
  932. s: String;
  933. buf : TBufType;
  934. begin
  935. buf:=Default(TBufType);
  936. if not FileExists(AFileName) then
  937. raise EInOutError.Create('File not found: ' + AFileName);
  938. Assign(f, AFilename);
  939. Reset(f);
  940. SetTextBuf(F,Buf,SizeOf(Buf));
  941. while not EOF(f) do
  942. begin
  943. ReadLn(f, s);
  944. if (Length(s) = 0) or (s[1] = '#') then
  945. continue;
  946. if s = ':link tree' then
  947. ReadLinkTree
  948. else if s = ':classes' then
  949. ReadClasses
  950. else
  951. repeat
  952. ReadLn(f, s);
  953. until EOF(f) or (Length(s) = 0);
  954. end;
  955. Close(f);
  956. end;
  957. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  958. var
  959. ContentFile: Text;
  960. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  961. var
  962. ChildNode: TLinkNode;
  963. begin
  964. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  965. ChildNode := ALinkNode.FirstChild;
  966. while Assigned(ChildNode) do
  967. begin
  968. ProcessLinkNode(ChildNode, AIdent + ' ');
  969. ChildNode := ChildNode.NextSibling;
  970. end;
  971. end;
  972. function CheckImplicitLink(const s : String):String;
  973. begin
  974. if uppercase(s)='IUNKNOWN' then
  975. Result:='#rtl.System.IUnknown'
  976. else if uppercase(s)='TOBJECT' then
  977. Result:='#rtl.System.TObject'
  978. else
  979. Result:=s;
  980. end;
  981. var
  982. LinkNode: TLinkNode;
  983. i, j, k: Integer;
  984. Module: TPasModule;
  985. Alias : TPasAliasType;
  986. MemberDecl: TPasMembersType;
  987. ClassLikeDecl : TPasClassType;
  988. Member: TPasElement;
  989. s: String;
  990. Buf : TBufType;
  991. begin
  992. Buf:=Default(TBufType);
  993. Assign(ContentFile, AFilename);
  994. Rewrite(ContentFile);
  995. SetTextBuf(ContentFile,Buf,SizeOf(Buf));
  996. try
  997. WriteLn(ContentFile, '# FPDoc Content File');
  998. WriteLn(ContentFile, ':link tree');
  999. LinkNode := RootLinkNode.FirstChild;
  1000. while Assigned(LinkNode) do
  1001. begin
  1002. if LinkNode.Name = Package.Name then
  1003. begin
  1004. ProcessLinkNode(LinkNode, '');
  1005. end;
  1006. LinkNode := LinkNode.NextSibling;
  1007. end;
  1008. if Assigned(Package) then
  1009. begin
  1010. WriteLn(ContentFile);
  1011. WriteLn(ContentFile, ':classes');
  1012. for i := 0 to Package.Modules.Count - 1 do
  1013. begin
  1014. Module := TPasModule(Package.Modules[i]);
  1015. if not assigned(Module.InterfaceSection) then
  1016. continue;
  1017. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  1018. begin
  1019. MemberDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  1020. if MemberDecl is TPasClassType then
  1021. ClassLikeDecl:=MemberDecl as TPasClassType
  1022. else
  1023. ClassLikeDecl:=nil;
  1024. Write(ContentFile, CheckImplicitLink(MemberDecl.PathName), ' ');
  1025. if Assigned(ClassLikeDecl) then
  1026. begin
  1027. if Assigned(ClassLikeDecl.AncestorType) then
  1028. begin
  1029. // simple aliases to class types are coded as "alias(classtype)"
  1030. Write(ContentFile, CheckImplicitLink(ClassLikeDecl.AncestorType.PathName));
  1031. if ClassLikeDecl.AncestorType is TPasAliasType then
  1032. begin
  1033. alias:= TPasAliasType(ClassLikeDecl.AncestorType);
  1034. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  1035. write(ContentFile,'(',alias.desttype.PathName,')');
  1036. end;
  1037. end
  1038. else if ClassLikeDecl.ObjKind = okClass then
  1039. Write(ContentFile, '#rtl.System.TObject')
  1040. else if ClassLikeDecl.ObjKind = okInterface then
  1041. Write(ContentFile, '#rtl.System.IUnknown');
  1042. if ClassLikeDecl.Interfaces.Count>0 then
  1043. begin
  1044. for k:=0 to ClassLikeDecl.Interfaces.count-1 do
  1045. begin
  1046. write(contentfile,',',CheckImplicitLink(TPasType(ClassLikeDecl.Interfaces[k]).PathName));
  1047. if TPasElement(ClassLikeDecl.Interfaces[k]) is TPasAliasType then
  1048. begin
  1049. alias:= TPasAliasType(ClassLikeDecl.Interfaces[k]);
  1050. if assigned(alias.desttype) and (alias.desttype is TPasClassType) then
  1051. write(ContentFile,'(',CheckImplicitLink(alias.desttype.PathName),')');
  1052. end;
  1053. end;
  1054. end;
  1055. end;
  1056. writeln(contentfile);
  1057. for k := 0 to MemberDecl.Members.Count - 1 do
  1058. begin
  1059. Member := TPasElement(MemberDecl.Members[k]);
  1060. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  1061. S:='';
  1062. if Member.ClassType = TPasVariable then
  1063. Write(ContentFile, 'V')
  1064. else if Member.ClassType = TPasProperty then
  1065. begin
  1066. Write(ContentFile, 'P');
  1067. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  1068. s := s + 'r';
  1069. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  1070. s := s + 'w';
  1071. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  1072. s := s + 's';
  1073. end else
  1074. Write(ContentFile, 'M'); // Member must be a method
  1075. Write(ContentFile, Member.Name);
  1076. if Length(s) > 0 then
  1077. WriteLn(ContentFile, ' ', s)
  1078. else
  1079. WriteLn(ContentFile);
  1080. end;
  1081. end;
  1082. end;
  1083. end;
  1084. finally
  1085. Close(ContentFile);
  1086. end;
  1087. end;
  1088. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  1089. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  1090. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  1091. begin
  1092. Result := AClass.Create(AName, AParent);
  1093. Result.Visibility := AVisibility;
  1094. if AClass.InheritsFrom(TPasModule) then
  1095. CurModule := TPasModule(Result);
  1096. Result.SourceFilename := ASourceFilename;
  1097. Result.SourceLinenumber := ASourceLinenumber;
  1098. end;
  1099. function TFPDocEngine.FindInModule ( const AName: String; AModule: TPasModule
  1100. ) : TPasElement;
  1101. var
  1102. l: TFPList;
  1103. i: Integer;
  1104. begin
  1105. If Assigned(AModule) and Assigned(AModule.InterfaceSection) and
  1106. Assigned(AModule.InterfaceSection.Declarations) then
  1107. begin
  1108. l:=AModule.InterfaceSection.Declarations;
  1109. for i := 0 to l.Count - 1 do
  1110. begin
  1111. Result := TPasElement(l[i]);
  1112. if CompareText(Result.Name, AName) = 0 then
  1113. exit;
  1114. end;
  1115. end;
  1116. Result := nil;
  1117. end;
  1118. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  1119. var
  1120. i: Integer;
  1121. Module: TPasElement;
  1122. begin
  1123. Result := FindInModule( AName, CurModule );
  1124. if not Assigned(Result) and assigned (CurModule.InterfaceSection) then
  1125. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  1126. begin
  1127. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  1128. if Module.ClassType.InheritsFrom(TPasModule) then
  1129. begin
  1130. Result := FindInModule(AName, TPasModule(Module));
  1131. if Assigned(Result) then
  1132. exit;
  1133. end;
  1134. end;
  1135. end;
  1136. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  1137. function FindInPackage(APackage: TPasPackage): TPasModule;
  1138. var
  1139. i: Integer;
  1140. begin
  1141. for i := 0 to APackage.Modules.Count - 1 do
  1142. begin
  1143. Result := TPasModule(APackage.Modules[i]);
  1144. if CompareText(Result.Name, AName) = 0 then
  1145. exit;
  1146. end;
  1147. Result := nil;
  1148. end;
  1149. var
  1150. i: Integer;
  1151. AInPutLine,OSTarget,CPUTarget : String;
  1152. begin
  1153. Result := FindInPackage(Package);
  1154. if not Assigned(Result) then
  1155. for i := FPackages.Count - 1 downto 0 do
  1156. begin
  1157. if TPasPackage(FPackages[i]) = Package then
  1158. continue;
  1159. Result := FindInPackage(TPasPackage(FPackages[i]));
  1160. if Assigned(Result) then
  1161. exit;
  1162. end;
  1163. if Not Assigned(Result) and Assigned(FOnParseUnit) then
  1164. begin
  1165. FOnParseUnit(Self,AName,AInputLine,OSTarget,CPUTarget);
  1166. If (AInPutLine<>'') then
  1167. Result:=ParseUsedUnit(AName,AInputLine,OSTarget,CPUTarget);
  1168. end;
  1169. end;
  1170. function TFPDocEngine.HintsToStr(Hints: TPasMemberHints): String;
  1171. Var
  1172. H : TPasMemberHint;
  1173. begin
  1174. Result:='';
  1175. For h:=Low(TPasMemberHint) to High(TPasMemberHint) do
  1176. if h in Hints then
  1177. begin
  1178. if (Result<>'') then
  1179. Result:=Result+', ';
  1180. Result:=Result+cPasMemberHint[h]
  1181. end;
  1182. end;
  1183. function TFPDocEngine.ParseUsedUnit(AName, AInputLine, AOSTarget,
  1184. ACPUTarget: String): TPasModule;
  1185. Var
  1186. M : TPasModule;
  1187. begin
  1188. DoLog(SParsingUsedUnit,[AName,AInputLine]);
  1189. M:=CurModule;
  1190. CurModule:=Nil;
  1191. try
  1192. ParseSource(Self,AInputLine,AOSTarget,ACPUTarget,[poUseStreams,poSkipDefaultDefs]);
  1193. Result:=CurModule;
  1194. finally
  1195. CurModule:=M;
  1196. end;
  1197. end;
  1198. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  1199. begin
  1200. RootLinkNode.CreateChildren(APathName, ALinkTo);
  1201. end;
  1202. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  1203. var
  1204. LinkNode: TLinkNode;
  1205. begin
  1206. LinkNode := RootLinkNode.FindChild(AName);
  1207. if Assigned(LinkNode) then
  1208. Result := LinkNode.Link
  1209. else
  1210. SetLength(Result, 0);
  1211. end;
  1212. function TFPDocEngine.ResolveLinkInPackages(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1213. Var
  1214. ThisPackage: TLinkNode;
  1215. begin
  1216. { Try all packages }
  1217. Result:='';
  1218. ThisPackage:=RootLinkNode.FirstChild;
  1219. while Assigned(ThisPackage) and (Result='') do
  1220. begin
  1221. Result:=ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest, Strict);
  1222. ThisPackage := ThisPackage.NextSibling;
  1223. end;
  1224. end;
  1225. function TFPDocEngine.ResolveLinkInUsedUnits(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1226. var
  1227. i: Integer;
  1228. UL: TFPList;
  1229. begin
  1230. Result:='';
  1231. UL:=AModule.InterfaceSection.UsesList;
  1232. I:=UL.Count-1;
  1233. While (Result='') and (I>=0) do
  1234. begin
  1235. Result:=ResolveLinkInPackages(AModule,TPasType(UL[i]).Name+'.'+ALinkDest, strict);
  1236. Dec(I);
  1237. end;
  1238. end;
  1239. function TFPDocEngine.ResolveLink(AModule: TPasModule; const ALinkDest: String; Strict : Boolean = False): String;
  1240. var
  1241. i: Integer;
  1242. begin
  1243. {
  1244. if Assigned(AModule) then
  1245. system.WriteLn('ResolveLink(', AModule.Name, ' - ', ALinkDest, ')... ')
  1246. else
  1247. system.WriteLn('ResolveLink(Nil - ', ALinkDest, ')... ');
  1248. }
  1249. if (ALinkDest='') then
  1250. Exit('');
  1251. if (ALinkDest[1] = '#') then
  1252. Result := FindAbsoluteLink(ALinkDest)
  1253. else if (AModule=Nil) then
  1254. Result:= FindAbsoluteLink(RootLinkNode.FirstChild.Name+'.'+ALinkDest)
  1255. else
  1256. begin
  1257. if Pos(AModule.Name,ALinkDest) = 1 then
  1258. Result := ResolveLink(AModule, AModule.packagename + '.' + ALinkDest, Strict)
  1259. else
  1260. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest, Strict);
  1261. if (Result='') then
  1262. begin
  1263. Result:=ResolveLinkInPackages(AModule,ALinkDest,Strict);
  1264. if (Result='') then
  1265. Result:=ResolveLinkInUsedUnits(Amodule,AlinkDest,Strict);
  1266. end;
  1267. end;
  1268. // Match on parent : class/enumerated/record/module
  1269. if (Result='') and not strict then
  1270. for i := Length(ALinkDest) downto 1 do
  1271. if ALinkDest[i] = '.' then
  1272. begin
  1273. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1), Strict);
  1274. exit;
  1275. end;
  1276. end;
  1277. procedure ReadXMLFileALT(OUT ADoc:TXMLDocument;const AFileName:ansistring);
  1278. var
  1279. Parser: TDOMParser;
  1280. Src: TXMLInputSource;
  1281. FileStream: TStream;
  1282. begin
  1283. ADoc := nil;
  1284. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  1285. try
  1286. Parser := TDOMParser.Create; // create a parser object
  1287. try
  1288. Src := TXMLInputSource.Create(FileStream); // and the input source
  1289. src.SystemId:=UTF8Decode(FileNameToUri(AFileName));
  1290. try
  1291. Parser.Options.PreserveWhitespace := True;
  1292. Parser.Parse(Src, ADoc);
  1293. finally
  1294. Src.Free; // cleanup
  1295. end;
  1296. finally
  1297. Parser.Free;
  1298. end;
  1299. finally
  1300. FileStream.Free;
  1301. end;
  1302. end;
  1303. procedure TFPDocEngine.AddDocFile(const AFilename: String;DontTrim:boolean=false);
  1304. Var
  1305. PN : String;
  1306. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  1307. var
  1308. Subnode: TDOMNode;
  1309. begin
  1310. if OwnerDocNode = RootDocNode then
  1311. Result := OwnerDocNode.CreateChildren('#' + UTF8Encode(Element['name']))
  1312. else
  1313. Result := OwnerDocNode.CreateChildren(UTF8Encode(Element['name']));
  1314. Result.FNode := Element;
  1315. Result.FLink := UTF8Encode(Element['link']);
  1316. if (Element['alwaysvisible'] = '1') and (Element.NodeName='element') then
  1317. FAlwaysVisible.Add(LowerCase(PN+'.'+TDocNode(OwnerDocNode).Name+'.'+UTF8Encode(Element['name'])));
  1318. Result.FIsSkipped := Element['skip'] = '1';
  1319. Subnode := Element.FirstChild;
  1320. while Assigned(Subnode) do
  1321. begin
  1322. if Subnode.NodeType = ELEMENT_NODE then
  1323. begin
  1324. if Subnode.NodeName = 'short' then
  1325. Result.FShortDescr := TDOMElement(Subnode)
  1326. else if Subnode.NodeName = 'descr' then
  1327. Result.FDescr := TDOMElement(Subnode)
  1328. else if Subnode.NodeName = 'version' then
  1329. begin
  1330. Result.FVersion := TDOMElement(Subnode)
  1331. end
  1332. else if Subnode.NodeName = 'errors' then
  1333. Result.FErrorsDoc := TDOMElement(Subnode)
  1334. else if Subnode.NodeName = 'seealso' then
  1335. Result.FSeeAlso := TDOMElement(Subnode)
  1336. else if (Subnode.NodeName = 'example') and
  1337. not Assigned(Result.FirstExample) then
  1338. Result.FFirstExample := TDOMElement(Subnode)
  1339. else if (Subnode.NodeName = 'notes') then
  1340. Result.FNotes := TDOMElement(Subnode);
  1341. end;
  1342. Subnode := Subnode.NextSibling;
  1343. end;
  1344. end;
  1345. Procedure ReadTopics(TopicNode : TDocNode);
  1346. Var
  1347. SubNode : TDOMNode;
  1348. begin
  1349. SubNode:=TopicNode.FNode.FirstChilD;
  1350. While Assigned(SubNode) do
  1351. begin
  1352. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  1353. With ReadNode(TopicNode,TDomElement(SubNode)) do
  1354. // We could allow recursion here, but we won't, because it doesn't work on paper.
  1355. FTopicNode:=True;
  1356. SubNode:=Subnode.NextSibling;
  1357. end;
  1358. end;
  1359. var
  1360. Node, Subnode, Subsubnode: TDOMNode;
  1361. Doc: TXMLDocument;
  1362. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  1363. begin
  1364. if DontTrim then
  1365. ReadXMLFileALT(Doc, AFilename)
  1366. else
  1367. ReadXMLFile(Doc, AFilename);
  1368. DescrDocs.Add(Doc);
  1369. DescrDocNames.Add(AFilename);
  1370. Node := Doc.DocumentElement.FirstChild;
  1371. while Assigned(Node) do
  1372. begin
  1373. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  1374. begin
  1375. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  1376. PackageDocNode.IncRefCount;
  1377. PN:=PackageDocNode.Name;
  1378. // Scan all 'module' elements within this package element
  1379. Subnode := Node.FirstChild;
  1380. while Assigned(Subnode) do
  1381. begin
  1382. if (Subnode.NodeType = ELEMENT_NODE) then
  1383. begin
  1384. If (Subnode.NodeName = 'module') then
  1385. begin
  1386. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  1387. // Scan all 'element' elements within this module element
  1388. Subsubnode := Subnode.FirstChild;
  1389. while Assigned(Subsubnode) do
  1390. begin
  1391. if (Subsubnode.NodeType = ELEMENT_NODE) then
  1392. begin
  1393. if (Subsubnode.NodeName = 'element') then
  1394. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  1395. else if (SubSubNode.NodeName='topic') then
  1396. begin
  1397. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  1398. TopicNode.FTopicNode:=True;
  1399. ReadTopics(TopicNode);
  1400. end;
  1401. end;
  1402. Subsubnode := Subsubnode.NextSibling;
  1403. end;
  1404. end
  1405. else if (SubNode.NodeName='topic') then
  1406. begin
  1407. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  1408. TopicNode.FTopicNode:=True;
  1409. ReadTopics(TopicNode);
  1410. end;
  1411. end;
  1412. Subnode := Subnode.NextSibling;
  1413. end;
  1414. end;
  1415. Node := Node.NextSibling;
  1416. end;
  1417. end;
  1418. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  1419. begin
  1420. Result:=Nil;
  1421. If not Assigned(AElement) then
  1422. exit;
  1423. if aElement.CustomData is TDocNode then
  1424. Exit(TDocNode(aElement.CustomData));
  1425. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  1426. Result := FindDocNode(AElement.GetModule, AElement.Name)
  1427. else
  1428. begin
  1429. Result := RootDocNode.FindChild(AElement.PathName);
  1430. if (Result=Nil) and (AElement is TPasoperator) then
  1431. Result:=RootDocNode.FindChild(TPasOperator(AElement).OldName(True));
  1432. end;
  1433. if (Result<>Nil) then
  1434. begin
  1435. if aElement.CustomData=Nil then
  1436. aElement.CustomData:=Result;
  1437. end
  1438. else if WarnNoNode and
  1439. (Length(AElement.PathName)>0) and
  1440. (AElement.PathName[1]='#') then
  1441. DoLog(Format('No documentation node found for identifier : %s',[AElement.PathName]));
  1442. end;
  1443. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  1444. const AName: String): TDocNode;
  1445. var
  1446. CurPackage: TDocNode;
  1447. UnitList: TFPList;
  1448. i: Integer;
  1449. begin
  1450. if Length(AName) = 0 then
  1451. Result := nil
  1452. else
  1453. begin
  1454. if AName[1] = '#' then
  1455. Result := RootDocNode.FindChild(AName)
  1456. else
  1457. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  1458. if (not Assigned(Result)) and Assigned(ARefModule) then
  1459. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  1460. if (not Assigned(Result)) and (AName[1] <> '#') then
  1461. begin
  1462. CurPackage := RootDocNode.FirstChild;
  1463. while Assigned(CurPackage) do
  1464. begin
  1465. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  1466. if Assigned(Result) then
  1467. break;
  1468. CurPackage := CurPackage.NextSibling;
  1469. end;
  1470. if not Assigned(Result) and assigned(CurModule.InterfaceSection) then
  1471. begin
  1472. { Okay, then we have to try all imported units of the current module }
  1473. UnitList := CurModule.InterfaceSection.UsesList;
  1474. for i := UnitList.Count - 1 downto 0 do
  1475. begin
  1476. { Try all packages }
  1477. CurPackage := RootDocNode.FirstChild;
  1478. while Assigned(CurPackage) do
  1479. begin
  1480. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  1481. TPasType(UnitList[i]).Name + '.' + AName);
  1482. if Assigned(Result) then
  1483. break;
  1484. CurPackage := CurPackage.NextSibling;
  1485. end;
  1486. end;
  1487. end;
  1488. end;
  1489. end;
  1490. end;
  1491. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  1492. var
  1493. DocNode,N: TDocNode;
  1494. begin
  1495. DocNode := FindDocNode(AElement);
  1496. if Assigned(DocNode) then
  1497. begin
  1498. N:=FindLinkedNode(DocNode);
  1499. If (N<>Nil) then
  1500. DocNode:=N;
  1501. Result := DocNode.ShortDescr;
  1502. end
  1503. else
  1504. Result := nil;
  1505. end;
  1506. function TFPDocEngine.FindLinkedNode(ANode : TDocNode) : TDocNode;
  1507. begin
  1508. If (ANode.Link='') then
  1509. Result:=Nil
  1510. else
  1511. Result:=FindDocNode(CurModule,ANode.Link);
  1512. end;
  1513. function TFPDocEngine.ShowElement(El: TPasElement): Boolean;
  1514. begin
  1515. Case El.Visibility of
  1516. visStrictPrivate,
  1517. visPrivate :
  1518. Result:=Not HidePrivate;
  1519. visStrictProtected,
  1520. visProtected :
  1521. begin
  1522. Result:=Not HideProtected;
  1523. if not Result then
  1524. Result:=FAlwaysVisible.IndexOf(LowerCase(El.PathName))<>-1;
  1525. end
  1526. Else
  1527. Result:=True
  1528. end;
  1529. end;
  1530. procedure TFPDocEngine.StartDocumenting;
  1531. begin
  1532. FAlwaysVisible.Sorted:=True;
  1533. end;
  1534. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  1535. const AName: String): TDOMElement;
  1536. var
  1537. N,DocNode: TDocNode;
  1538. begin
  1539. DocNode := FindDocNode(ARefModule, AName);
  1540. if Assigned(DocNode) then
  1541. begin
  1542. N:=FindLinkedNode(DocNode);
  1543. If (N<>Nil) then
  1544. DocNode:=N;
  1545. Result := DocNode.ShortDescr;
  1546. end
  1547. else
  1548. Result := nil;
  1549. end;
  1550. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1551. var
  1552. i: Integer;
  1553. fn : String;
  1554. begin
  1555. Result:='';
  1556. for i := 0 to DescrDocs.Count - 1 do
  1557. begin
  1558. Fn:=UTF8Encode(ExElement['file']);
  1559. if (FN<>'') and (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then
  1560. begin
  1561. Result := ExtractFilePath(DescrDocNames[i]) + FN;
  1562. if (ExtractFileExt(Result)='') then
  1563. Result:=Result+'.pp';
  1564. end;
  1565. end;
  1566. end;
  1567. { Global helpers }
  1568. procedure TranslateDocStrings(const Lang: String);
  1569. Const
  1570. {$ifdef unix}
  1571. DefDir = '/usr/local/share/locale';
  1572. {$else}
  1573. DefDir = 'intl';
  1574. {$endif}
  1575. var
  1576. mo: TMOFile;
  1577. dir : string;
  1578. begin
  1579. dir:=modir;
  1580. If Dir='' then
  1581. Dir:=DefDir;
  1582. Dir:=IncludeTrailingPathDelimiter(Dir);
  1583. {$IFDEF Unix}
  1584. mo := TMOFile.Create(Format(Dir+'%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1585. {$ELSE}
  1586. mo := TMOFile.Create(Format(Dir+'dglobals.%s.mo', [Lang]));
  1587. {$ENDIF}
  1588. try
  1589. TranslateResourceStrings(mo);
  1590. finally
  1591. mo.Free;
  1592. end;
  1593. end;
  1594. Function IsLinkNode(Node : TDomNode) : Boolean;
  1595. begin
  1596. Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');
  1597. end;
  1598. Function IsExampleNode(Example : TDomNode) : Boolean;
  1599. begin
  1600. Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')
  1601. end;
  1602. function IsLinkAbsolute(ALink: String): boolean;
  1603. var
  1604. i: integer;
  1605. begin
  1606. Result := false;
  1607. for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do
  1608. if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin
  1609. Result := true;
  1610. break;
  1611. end;
  1612. end;
  1613. function DumpExceptionCallStack(E: Exception):String;
  1614. var
  1615. I: Integer;
  1616. Frames: PPointer;
  1617. begin
  1618. Result := 'Program exception! ' + LineEnding +
  1619. 'Stacktrace:' + LineEnding + LineEnding;
  1620. if E <> nil then begin
  1621. Result := Result + 'Exception class: ' + E.ClassName + LineEnding +
  1622. 'Message: ' + E.Message + LineEnding;
  1623. end;
  1624. Result := Result + BackTraceStrFunc(ExceptAddr);
  1625. Frames := ExceptFrames;
  1626. for I := 0 to ExceptFrameCount - 1 do
  1627. Result := Result + LineEnding + BackTraceStrFunc(Frames[I]);
  1628. end;
  1629. initialization
  1630. LEOL:=Length(LineEnding);
  1631. end.