diff --git a/.gitattributes b/.gitattributes index 5707aeb2cf..11dcc8c3cb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19372,6 +19372,7 @@ utils/fpdoc/fpdocclasstree.pp svneol=native#text/plain utils/fpdoc/fpdocproj.pas svneol=native#text/plain utils/fpdoc/fpdocstripper.lpi svneol=native#text/plain utils/fpdoc/fpdocstripper.pp svneol=native#text/plain +utils/fpdoc/fpdocstrs.pp svneol=native#text/plain utils/fpdoc/fpdocxmlopts.pas svneol=native#text/plain utils/fpdoc/fpmake.pp svneol=native#text/plain utils/fpdoc/images/minus.png -text svneol=unset#image/png diff --git a/utils/fpdoc/dglobals.pp b/utils/fpdoc/dglobals.pp index 2cfed6aed8..05e2c4f10b 100644 --- a/utils/fpdoc/dglobals.pp +++ b/utils/fpdoc/dglobals.pp @@ -33,203 +33,6 @@ Var LEOL : Integer; modir : string; -resourcestring - // Output strings - SDocPackageTitle = 'Reference for package ''%s'''; - SDocPackageMenuTitle = 'Package ''%s'''; - SDocPackageLinkTitle = 'Package'; - SDocPrograms = 'Programs'; - SDocUnits = 'Units'; - SDocUnitTitle = 'Reference for unit ''%s'''; - SDocUnitMenuTitle = 'Unit ''%s'''; - SDocInheritanceHierarchy = 'Inheritance Hierarchy'; - SDocInterfaceSection = 'Interface section'; - SDocImplementationSection = 'Implementation section'; - SDocUsedUnits = 'Used units'; - SDocUsedUnitsByUnitXY = 'Used units by unit ''%s'''; - SDocConstsTypesVars = 'Constants, types and variables'; - SDocResStrings = 'Resource strings'; - SDocTypes = 'Types'; - SDocType = 'Type'; - SDocConstants = 'Constants'; - SDocConstant = 'Constant'; - SDocClasses = 'Classes'; - SDocClass = 'Class'; - SDocProceduresAndFunctions = 'Procedures and functions'; - SDocProcedureOrFunction = 'Procedure/function'; - SDocVariables = 'Variables'; - SDocVariable = 'Variable'; - SDocIdentifierIndex = 'Index'; - SDocPackageClassHierarchy = 'Class hierarchy'; - SDocModuleIndex = 'Index of all identifiers in unit ''%s'''; - SDocPackageIndex = 'Index of all identifiers in package ''%s'''; - SDocUnitOverview = 'Overview of unit ''%s'''; - SDocOverview = 'Overview'; - SDocSearch = 'Search'; - SDocDeclaration = 'Declaration'; - SDocDescription = 'Description'; - SDocErrors = 'Errors'; - SDocVersion = 'Version info'; - SDocSeeAlso = 'See also'; - SDocExample = 'Example'; - SDocArguments = 'Arguments'; - SDocFunctionResult = 'Function result'; - SDocRemark = 'Remark: '; - SDocMethodOverview = 'Method overview'; - SDocPropertyOverview = 'Property overview'; - SDocEventOverview = 'Event overview'; - SDocInterfacesOverview = 'Interfaces overview'; - SDocInterface = 'Interfaces'; - SDocPage = 'Page'; - SDocMember = 'Member'; - SDocMembers = 'Members'; - SDocField = 'Field'; - SDocMethod = 'Method'; - SDocProperty = 'Property'; - SDocAccess = 'Access'; - SDocInheritance = 'Inheritance'; - SDocProperties = 'Properties'; - SDocMethods = 'Methods'; - SDocEvents = 'Events'; - SDocByName = 'by Name'; - SDocByInheritance = 'By inheritance'; - SDocValue = 'Value'; - SDocExplanation = 'Explanation'; - SDocProcedure = 'Procedure'; - SDocValuesForEnum = 'Enumeration values for type %s'; - SDocSourcePosition = 'Source position: %s line %d'; - SDocSynopsis = 'Synopsis'; - SDocVisibility = 'Visibility'; - SDocOpaque = 'Opaque type'; - SDocDateGenerated = 'Documentation generated on: %s'; - // The next line requires leading/trailing space due to XML comment layout: - SDocGeneratedByComment = ' Generated using FPDoc - (c) 2000-2012 FPC contributors and Sebastian Guenther, sg@freepascal.org '; - SDocNotes = 'Notes'; - SDocName = 'Name'; - SDocType_s = 'Type(s)'; - SDocTopic = 'Topic'; - SDocNoneAVailable = 'No members available'; - - // Topics - SDocRelatedTopics = 'Related topics'; - SDocUp = 'Up'; - SDocNext = 'Next'; - SDocPrevious = 'Previous'; - - // Various backend constants - SDocChapter = 'Chapter'; - SDocSection = 'Section'; - SDocSubSection = 'Subsection'; - SDocTable = 'Table'; - SDocListing = 'Listing'; - - // Man page usage - SManUsageManSection = 'Use ASection as the man page section'; - SManUsageNoUnitPrefix = 'Do not prefix man pages with unit name.'; - SManUsageWriterDescr = 'UNIX man page output.'; - SManUsagePackageDescription = 'Use descr as the description of man pages'; - - // HTML usage - SHTMLUsageFooter = 'Append xhtml (@filename reads from file) as footer to html page'; - SHTMLUsageNavigator = 'Append xhtml (@filename reads from file) in navigator bar'; - SHTMLUsageHeader = 'Append xhtml (@filename reads from file) as header to html page below navigation bar'; - SHTMLUsageFooterDate = 'Append footer with date. fmt is Optional format for FormatDateTime'; - SHTMLUsageCharset = 'Set the HTML character set'; - SHTMLHtmlSearch = 'Add search page with given name to the menu bar'; - SHTMLIndexColcount = 'Use N columns in the identifier index pages'; - SHTMLImageUrl = 'Prefix image URLs with url'; - SHTMLDisableMenuBrackets = 'Disable ''['' and '']'' characters around menu items at the top of the page. Useful for custom css'; - - // CHM usage - SCHMUsageTOC = 'Use [File] as the table of contents. Usually a .hhc file.'; - SCHMUsageIndex = 'Use [File] as the index. Usually a .hhk file.'; - SCHMUsageDefPage = 'Set the "Home" page relative to where it lives in the chm. i.e. "/index.html"'; - SCHMUsageOtrFiles= 'A txt file containing a list of files to be added relative to the working directory.'; - SCHMUsageCSSFile = 'Filename of a .css file to be included in the chm.'; - SCHMUsageAutoTOC = 'Automatically generate a Table of Contents. Ignores --toc-file'; - SCHMUsageAutoIDX = 'Automatically generate an Index. Ignores --index-file'; - SCHMUsageMakeSearch = 'Automatically generate a Search Index from filenames that match *.htm*'; - SCHMUsageChmTitle= 'Title of the chm. Defaults to the value from --package'; - - // MarkDown usage - SMDUsageFooter = 'Append markdown (@filename reads from file) as footer to every markdown page'; - SMDUsageHeader = 'Prepend markdown (@filename reads from file) as header to every markdown page'; - SMDIndexColcount = 'Use N columns in the identifier index pages'; - SMDImageUrl = 'Prefix image URLs with url'; - SMDTheme = 'Use name as theme name'; - SMDNavigation = 'Use scheme for navigation tree, here scheme is one of:'; - SMDNavSubtree = ' UnitSubTree : put all units in a sub tree of a Units node'; - SMDNavTree = ' UnitTree : put every units as a node on the same level as packages node'; - - SXMLUsageFlatStructure = 'Use a flat output structure of XML files and directories'; - SXMLUsageSource = 'Include source file and line info in generated XML'; - - // Linear usage - SLinearUsageDupLinkedDocsP1 = 'Duplicate linked element documentation in'; - SLinearUsageDupLinkedDocsP2 = 'descendant classes.'; - - STitle = 'FPDoc - Free Pascal Documentation Tool'; - SVersion = 'Version %s [%s]'; - SCopyright1 = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org'; - SCopyright2 = '(c) 2005 - 2021 various FPC contributors'; - - SCmdLineHelp = 'Usage: %s [options]'; - SUsageOption008 = '--base-descr-dir=DIR prefix all description files with this directory'; - SUsageOption009 = '--base-input-dir=DIR prefix all input files with this directory'; - SUsageOption010 = '--content Create content file for package cross-references'; - SUsageOption020 = '--cputarget=value Set the target CPU for the scanner.'; - SUsageOption030 = '--descr=file use file as description file, e.g.: '; - SUsageOption035 = ' --descr=c:\WIP\myzipperdoc.xml'; - SUsageOption040 = ' This option is allowed more than once'; - SUsageOption050 = '--descr-dir=Dir Add All XML files in Dir to list of description files'; - SUsageOption060 = '--format=fmt Select output format.'; - SUsageOption070 = '--help Show this help.'; - SUsageOption080 = '--hide-protected Do not show protected methods in overview'; - SUsageOption090 = '--import=file Import content file for package cross-references'; - SUsageOption100 = '--input=cmd use cmd as input for the parser, e.g.:'; - SUsageOption110 = ' --input=C:\fpc\packages\paszlib\src\zipper.pp'; - SUsageOption120 = ' At least one input option is required.'; - SUsageOption130 = '--input-dir=Dir Add All *.pp and *.pas files in Dir to list of input files'; - SUsageOption140 = '--lang=lng Select output language.'; - SUsageOption145 = '--macro=name=value Define a macro to preprocess the project file with.'; - SUsageOption150 = '--ostarget=value Set the target OS for the scanner.'; - SUsageOption160 = '--output=name use name as the output name.'; - SUsageOption170 = ' Each backend interprets this as needed.'; - SUsageOption180 = '--package=name Set the package name for which to create output,'; - SUsageOption190 = ' e.g. --package=fcl'; - SUsageOption200 = '--project=file Use file as project file'; - SUsageOption210 = '--show-private Show private methods.'; - SUsageOption215 = '--stop-on-parser-error'; - SUsageOption215A = ' Stop when a parser error occurs. Default is to ignore parser errors.'; - SUsageOption220 = '--warn-no-node Warn if no documentation node was found.'; - SUsageOption230 = '--mo-dir=dir Set directory where language files reside to dir'; - SUsageOption240 = '--parse-impl (Experimental) try to parse implementation too'; - SUsageOption250 = '--dont-trim Do not trim XML contents. Useful for preserving'; - SUsageOption260 = ' formatting inside e.g
 tags';
-  SUsageOption270  = '--write-project=file';
-  SUsageOption280  = '                  Do not write documentation, create project file instead';
-  SUsageOption290  = '--verbose         Write more information on the screen';
-  SUsageOption300  = '--dry-run         Only parse sources and XML, do not create output';
-  SUsageOption310  = '--write-project=file';
-  SUsageOption320  = '                  Write all command-line options to a project file';
-  SUsageSubNames   = 'Use the file subnames instead the indexes as postfixes';
-
-  SUsageFormats        = 'The following output formats are supported by this fpdoc:';
-  SUsageBackendHelp    = 'Specify an output format, combined with --help to get more help for this backend.';
-  SUsageFormatSpecific = 'Output format "%s" supports the following options:';
-  SCmdLineErrInvalidMacro     = 'Macro needs to be in the form name=value';
-
-  SCmdLineInvalidOption       = 'Ignoring unknown option "%s"';
-  SCmdLineInvalidFormat       = 'Invalid format "%s" specified';
-  SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=';
-  SWritingPages               = 'Writing %d pages...';
-  SNeedPackageName            = 'No package name specified. Please specify one using the --package option.';
-  SAvailablePackages          = 'Available packages: ';
-  SDone                       = 'Done.';
-  SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';
-  SErrCouldNotCreateFile      = 'Could not create file "%s": %s';
-  SSeeURL                     = '(See %s)';      // For linear text writers.
-  SParsingUsedUnit            = 'Parsing used unit "%s" with commandLine "%s"';
 
 Const
   SVisibility: array[TPasMemberVisibility] of string =
@@ -335,7 +138,7 @@ type
 
 
   // The main FPDoc engine
-  TFPDocLogLevel = (dleWarnNoNode);
+  TFPDocLogLevel = (dleWarnNoNode, dleWarnUsedFile, dleDocumentationEmpty, dleXCT);
   TFPDocLogLevels = set of TFPDocLogLevel;
   TOnParseUnitEvent = Procedure (Sender : TObject; Const AUnitName : String; Out AInputFile,OSTarget,CPUTarget : String) of  Object;
 
@@ -364,7 +167,6 @@ type
     HasContentFile: Boolean;
     HidePrivate: Boolean;       // Hide private class members in output?
     HideProtected: Boolean;     // Hide protected class members in output?
-    WarnNoNode : Boolean;       // Warn if no description node found for element.
 
     constructor Create;
     destructor Destroy; override;
@@ -378,7 +180,7 @@ type
       AParent: TPasElement; AVisibility: TPasMemberVisibility;
       const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
       override;
-    function FindInModule(const AName: String ; AModule: TPasModule): TPasElement;
+    function FindElement(const AName: String ; AModule: TPasModule): TPasElement; overload;
     function FindElement(const AName: String): TPasElement; override;
     function FindModule(const AName: String): TPasModule; override;
     Function HintsToStr(Hints : TPasMemberHints) : String;
@@ -411,7 +213,9 @@ type
 
 
 procedure TranslateDocStrings(const Lang: String);
+{$IFDEF EXCEPTION_STACK}
 function DumpExceptionCallStack(E: Exception):String;
+{$ENDIF}
 
 Function IsLinkNode(Node : TDomNode) : Boolean;
 Function IsExampleNode(Example : TDomNode) : Boolean;
@@ -422,7 +226,7 @@ Function IsLinkAbsolute(ALink: String): boolean;
 
 implementation
 
-uses Gettext, XMLRead;
+uses Gettext, XMLRead, fpdocstrs;
 
 const
   AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');
@@ -670,8 +474,11 @@ destructor TFPDocEngine.Destroy;
 var
   i: Integer;
 begin
+  if FPackages.Count > 0 then
   for i := 0 to FPackages.Count - 1 do
-    TPasPackage(FPackages[i]).Release{$IFDEF CheckPasTreeRefCount}('TFPDocEngine.Destroy'){$ENDIF};
+    TPasPackage(FPackages[i]).Release{$IFDEF CheckPasTreeRefCount}('TFPDocEngine.Destroy'){$ENDIF}
+  else
+    FreeAndNil(FPackages);
   FreeAndNil(FRootDocNode);
   FreeAndNil(FRootLinkNode);
   FreeAndNil(DescrDocNames);
@@ -910,7 +717,7 @@ var
            end;
        end
      else
-       if cls<>result then
+       if (dleXCT in FDocLogLevels) and (cls<>result) then
          DoLog('Warning : ancestor class %s of class %s could not be resolved',[clname,cls.name]);
     end;
 
@@ -970,7 +777,7 @@ var
                if alname<>'' then // the class//interface we refered to is an alias
                  begin
                    // writeln('Found alias pair ',clname,' = ',alname);
-                   if not assigned(CreateAliasType(alname,clname,cls,cls2)) then
+                   if (dleXCT in FDocLogLevels) and not assigned(CreateAliasType(alname,clname,cls,cls2)) then
                       DoLog('Warning: creating alias %s for %s failed!',[alname,clname]);
                  end 
                else
@@ -1217,7 +1024,7 @@ begin
   Result.SourceLinenumber := ASourceLinenumber;
 end;
 
-function TFPDocEngine.FindInModule ( const AName: String; AModule: TPasModule
+function TFPDocEngine.FindElement ( const AName: String; AModule: TPasModule
   ) : TPasElement;
 var
   l: TFPList;
@@ -1244,14 +1051,14 @@ var
   i: Integer;
   Module: TPasElement;
 begin
-  Result := FindInModule( AName, CurModule );
+  Result := FindElement( AName, CurModule );
   if not Assigned(Result) and assigned (CurModule.InterfaceSection) then
     for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
     begin
       Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
       if Module.ClassType.InheritsFrom(TPasModule) then
       begin
-        Result := FindInModule(AName, TPasModule(Module));
+        Result := FindElement(AName, TPasModule(Module));
         if Assigned(Result) then
           exit;
       end;
@@ -1264,6 +1071,7 @@ function TFPDocEngine.FindModule(const AName: String): TPasModule;
   var
     i: Integer;
   begin
+    if not Assigned(APackage) then Exit;
     for i := 0 to APackage.Modules.Count - 1 do
     begin
       Result := TPasModule(APackage.Modules[i]);
@@ -1279,7 +1087,7 @@ var
 
 begin
   Result := FindInPackage(Package);
-  if not Assigned(Result) then
+  if not Assigned(Result) and (FPackages.Count > 0) then
     for i := FPackages.Count - 1 downto 0 do
     begin
       if TPasPackage(FPackages[i]) = Package then
@@ -1319,11 +1127,12 @@ Var
   M : TPasModule;
 
 begin
-  DoLog(SParsingUsedUnit,[AName,AInputLine]);
+  if dleWarnUsedFile in FDocLogLevels then
+    DoLog(SParsingUsedUnit,[AName,AInputLine]);
   M:=CurModule;
   CurModule:=Nil;
   try
-    ParseSource(Self,AInputLine,AOSTarget,ACPUTarget,[poUseStreams,poSkipDefaultDefs]);
+    ParseSource(Self,AInputLine,AOSTarget,ACPUTarget,[poUseStreams]); //[poSkipDefaultDefs];
     Result:=CurModule;
   finally
     CurModule:=M;
@@ -1590,7 +1399,7 @@ begin
     if aElement.CustomData=Nil then
       aElement.CustomData:=Result;
     end
-  else if WarnNoNode and
+  else if (dleWarnNoNode in FDocLogLevels) and
           (Length(AElement.PathName)>0) and
           (AElement.PathName[1]='#') then
     DoLog(Format('No documentation node found for identifier : %s',[AElement.PathName]));
@@ -1791,6 +1600,7 @@ begin
     end;
 end;
 
+{$IFDEF EXCEPTION_STACK}
 function DumpExceptionCallStack(E: Exception):String;
 var
   I: Integer;
@@ -1807,6 +1617,7 @@ begin
   for I := 0 to ExceptFrameCount - 1 do
     Result := Result + LineEnding + BackTraceStrFunc(Frames[I]);
 end;
+{$ENDIF}
 
 initialization
   LEOL:=Length(LineEnding);
diff --git a/utils/fpdoc/dw_basehtml.pp b/utils/fpdoc/dw_basehtml.pp
index c0a82aae25..38e8340682 100644
--- a/utils/fpdoc/dw_basehtml.pp
+++ b/utils/fpdoc/dw_basehtml.pp
@@ -158,7 +158,7 @@ Function FixHTMLpath(S : String) : STring;
 
 implementation
 
-uses xmlread, sysutils, sh_pas;
+uses fpdocstrs, xmlread, sysutils, sh_pas;
 
 Function FixHTMLpath(S : String) : STring;
 
diff --git a/utils/fpdoc/dw_basemd.pp b/utils/fpdoc/dw_basemd.pp
index 6ab3e04c39..cf55c6c84e 100644
--- a/utils/fpdoc/dw_basemd.pp
+++ b/utils/fpdoc/dw_basemd.pp
@@ -184,15 +184,8 @@ Type
 
 implementation
 
-resourcestring
-  SErrCannotChangeIndentSizeWhenIndented = 'Cannot change indent size while text is indented.';
-  SErrIndentMismatch = 'Indent mismatch: trying to undent when current indent too small';
-  SErrNotInList = 'Not in list';
-  SErrPopListStack = 'Pop list stack list type mismatch';
-  SErrMinListStack = 'Min list stack reached';
-  SErrMaxListStack = 'Max list stack reached';
-  SErrMinIndentStack = 'Min indent stack reached';
-  SErrMaxIndentStack = 'Max indent stack reached';
+uses fpdocstrs;
+
 
 procedure TBaseMarkdownWriter.SetIndentSize(AValue: Byte);
 begin
diff --git a/utils/fpdoc/dw_chm.pp b/utils/fpdoc/dw_chm.pp
index 30195d5b07..7c0bc30932 100644
--- a/utils/fpdoc/dw_chm.pp
+++ b/utils/fpdoc/dw_chm.pp
@@ -63,7 +63,7 @@ type
 
 implementation
 
-uses SysUtils, HTMWrite, dw_basehtml;
+uses fpdocstrs, SysUtils, HTMWrite, dw_basehtml;
 
 { TCHmFileNameAllocator }
 
diff --git a/utils/fpdoc/dw_html.pp b/utils/fpdoc/dw_html.pp
index 298114f9f7..ea8be55842 100644
--- a/utils/fpdoc/dw_html.pp
+++ b/utils/fpdoc/dw_html.pp
@@ -129,7 +129,7 @@ type
 
 implementation
 
-uses SysUtils, HTMWrite, fpdocclasstree;
+uses fpdocstrs, SysUtils, HTMWrite, fpdocclasstree;
 
 {$i css.inc}
 {$i plusimage.inc}
diff --git a/utils/fpdoc/dw_ipflin.pas b/utils/fpdoc/dw_ipflin.pas
index 8bf9e117cc..b8bdf9c981 100644
--- a/utils/fpdoc/dw_ipflin.pas
+++ b/utils/fpdoc/dw_ipflin.pas
@@ -151,7 +151,7 @@ type
 implementation
 
 uses
-  SysUtils, dwriter;
+  fpdocstrs, SysUtils, dwriter;
 
 
 { TFPDocWriter overrides }
diff --git a/utils/fpdoc/dw_latex.pp b/utils/fpdoc/dw_latex.pp
index ce04c3a082..d49227e280 100644
--- a/utils/fpdoc/dw_latex.pp
+++ b/utils/fpdoc/dw_latex.pp
@@ -30,7 +30,7 @@ Procedure CreateLaTeXDocForPackage(APackage: TPasPackage; AEngine: TFPDocEngine)
 
 implementation
 
-uses SysUtils, Classes, dwLinear, dwriter;
+uses fpdocstrs, SysUtils, Classes, dwLinear, dwriter;
 
 
 Type
diff --git a/utils/fpdoc/dw_linrtf.pp b/utils/fpdoc/dw_linrtf.pp
index dd25f5a7f0..3ab507ccca 100644
--- a/utils/fpdoc/dw_linrtf.pp
+++ b/utils/fpdoc/dw_linrtf.pp
@@ -28,7 +28,7 @@ Procedure CreateRTFDocForPackage(APackage: TPasPackage; AEngine: TFPDocEngine);
 
 implementation
 
-uses SysUtils, Classes, dwLinear, dwriter;
+uses fpdocstrs, SysUtils, Classes, dwLinear, dwriter;
 
 const
   Indent = 300;
diff --git a/utils/fpdoc/dw_man.pp b/utils/fpdoc/dw_man.pp
index 1874bdf1ef..30122d4906 100644
--- a/utils/fpdoc/dw_man.pp
+++ b/utils/fpdoc/dw_man.pp
@@ -185,6 +185,8 @@ Type
 
 implementation
 
+uses fpdocstrs;
+
 { TManWriter }
 
 constructor TManWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
diff --git a/utils/fpdoc/dw_markdown.pp b/utils/fpdoc/dw_markdown.pp
index e9a74d90b6..aafa82808a 100644
--- a/utils/fpdoc/dw_markdown.pp
+++ b/utils/fpdoc/dw_markdown.pp
@@ -142,7 +142,7 @@ type
 
 implementation
 
-uses SysUtils, fpdocclasstree;
+uses fpdocstrs, SysUtils, fpdocclasstree;
 
 
 Function FixHTMLpath(S : String) : STring;
diff --git a/utils/fpdoc/dw_txt.pp b/utils/fpdoc/dw_txt.pp
index 9557b196d2..2c057b966b 100644
--- a/utils/fpdoc/dw_txt.pp
+++ b/utils/fpdoc/dw_txt.pp
@@ -28,7 +28,7 @@ Procedure CreateTxtDocForPackage(APackage: TPasPackage; AEngine: TFPDocEngine);
 
 implementation
 
-uses SysUtils, Classes, dwLinear;
+uses fpdocstrs, SysUtils, Classes, dwLinear;
 
 Const
   MaxListLevel     = 10;
diff --git a/utils/fpdoc/dw_xml.pp b/utils/fpdoc/dw_xml.pp
index 1f7bfbce51..2a548ae637 100644
--- a/utils/fpdoc/dw_xml.pp
+++ b/utils/fpdoc/dw_xml.pp
@@ -61,6 +61,8 @@ Type
 
 implementation
 
+uses fpdocstrs;
+
 const
   DefaultVisibility = [visDefault, visPublic, visPublished, visProtected];
 
diff --git a/utils/fpdoc/dwlinear.pp b/utils/fpdoc/dwlinear.pp
index 5b36cec805..c024067e05 100644
--- a/utils/fpdoc/dwlinear.pp
+++ b/utils/fpdoc/dwlinear.pp
@@ -121,6 +121,8 @@ Type
 
 implementation
 
+uses fpdocstrs;
+
 const
   cDupLinkedDocParam = '--duplinkeddoc';
 
diff --git a/utils/fpdoc/dwriter.pp b/utils/fpdoc/dwriter.pp
index eb0bf599cf..2b25dfbad6 100644
--- a/utils/fpdoc/dwriter.pp
+++ b/utils/fpdoc/dwriter.pp
@@ -27,31 +27,6 @@ interface
 
 uses Classes, DOM, contnrs, dGlobals, PasTree, SysUtils, fpdocclasstree;
 
-resourcestring
-  SErrFileWriting = 'An error occurred during writing of file "%s": %s';
-
-  SErrInvalidShortDescr = 'Invalid short description';
-  SErrInvalidDescr = 'Invalid description (illegal XML element: "%s")';
-  SErrInvalidParaContent = 'Invalid paragraph content';
-  SErrInvalidElementInList = 'Invalid element in list - only "li" allowed';
-  SErrInvalidListContent = 'Invalid list content';
-  SErrInvalidRemarkContent = 'Invalid  content (illegal XML element: "%s")';
-  SErrListIsEmpty = 'List is empty - need at least one "li" element';
-  SErrInvalidDefinitionTermContent = 'Invalid content in definition term';
-  SErrDefinitionEntryMissing = 'Definition entry after definition term is missing';
-  SErrInvalidBorderValue = 'Invalid "border" value for %s';
-  SErrInvalidTableContent = 'Invalid table content';
-  SErrTableRowEmpty = 'Table row is empty (no "td" elements found)';
-  SErrInvalidContentBeforeSectionTitle = 'Invalid content before section title';
-  SErrSectionTitleExpected = 'Section title ("title" element) expected';
-
-  SErrDescrTagUnknown = 'Warning: Unknown tag "%s" in description';
-  SErrUnknownEntityReference = 'Warning: Unknown entity reference "&%s;" found';
-  SErrUnknownLinkID = 'Warning: Target ID of  in unit "%s", element "%s", is unknown: "%s"';
-  SErrUnknownPrintShortID = 'Warning: Target ID of  is unknown: "%s"';
-  SErrUnknownLink = 'Could not resolve link to "%s"';
-  SErralreadyRegistered = 'Class for output format "%s" already registered';
-  SErrUnknownWriterClass = 'Unknown output format "%s"';
 
 type
   // Phony element for pas pages.
@@ -339,6 +314,8 @@ function SortPasElements(Item1, Item2: Pointer): Integer;
 
 implementation
 
+uses fpdocstrs;
+
 function SortPasElements(Item1, Item2: Pointer): Integer;
 begin
   Result:=CompareText(TPasElement(Item1).Name,TPasElement(Item2).Name)
@@ -1065,8 +1042,8 @@ begin
   FPackage := APackage;
   FTopics:=Tlist.Create;
   FImgExt:='.png';
-  TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okClass);
-  TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, okInterface);
+  TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okWithFields);
+  TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, [okInterface]);
   CreateClassTree;
 end;
 
@@ -1722,8 +1699,8 @@ begin
   if Node.NodeType <> ELEMENT_NODE then
   begin
     if Node.NodeType = TEXT_NODE then
-	  Result := IsWhitespaceNode(TDOMText(Node))
-	else  
+      Result := IsWhitespaceNode(TDOMText(Node))
+    else
       Result := Node.NodeType = COMMENT_NODE;
     exit;
   end;
diff --git a/utils/fpdoc/fpclasschart.pp b/utils/fpdoc/fpclasschart.pp
index 1ce706f8bb..4172a5d81b 100644
--- a/utils/fpdoc/fpclasschart.pp
+++ b/utils/fpdoc/fpclasschart.pp
@@ -42,7 +42,7 @@ type
     FTree : TClassTreeBuilder;
     FObjects : TStringList;
   public
-    Constructor Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
+    Constructor Create(AClassTree : TXMLDocument; AObjectKindSet : TPasObjKindSet);
     Destructor Destroy; override;
     function CreateElement(AClass: TPTreeElement; const AName: String;
       AParent: TPasElement; AVisibility :TPasMemberVisibility;
@@ -442,14 +442,12 @@ begin
     end;
 end;
 
-Constructor TClassTreeEngine.Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
-
-
+Constructor TClassTreeEngine.Create(AClassTree : TXMLDocument; AObjectKindSet : TPasObjKindSet);
 begin
-  FPackage:=TPasPackage.Create('dummy',Nil);
-  FTree:=TClassTreeBuilder.Create(Self,FPackage,AObjectKind);
-  FObjects:=TStringList.Create;
   Inherited Create;
+  FPackage:=TPasPackage.Create('dummy',Nil);
+  FTree:=TClassTreeBuilder.Create(Self,FPackage,AObjectKindSet);
+  FObjects:=TStringList.Create;
 end;
 
 destructor TClassTreeEngine.Destroy;
@@ -538,11 +536,13 @@ Var
   end;
 
 begin
+  Result:= 0;
   aSrc:=TXMLDocument.Create();
   try
     aSrc.AppendChild(aSrc.CreateElement('TObject'));
     AppendChildClasses(aSrc.DocumentElement,aRootNode);
     MergeTrees(Dest,aSrc);
+    Inc(Result);
   finally
     aSrc.Free;
   end;
@@ -578,7 +578,7 @@ begin
       end;
     For I:=0 to InputFiles.Count-1 do
       begin
-      Engine := TClassTreeEngine.Create(XML,AObjectKind);
+      Engine := TClassTreeEngine.Create(XML,[AObjectKind]);
       Try
         ParseSource(Engine,InputFiles[I],OSTarget,CPUTarget);
         Engine.Ftree.BuildTree(Engine.FObjects);
diff --git a/utils/fpdoc/fpdoc.lpi b/utils/fpdoc/fpdoc.lpi
index b9c99b0c3f..d4126e5fdf 100644
--- a/utils/fpdoc/fpdoc.lpi
+++ b/utils/fpdoc/fpdoc.lpi
@@ -8,6 +8,7 @@
         
         
         
+        
         
         
         
@@ -46,7 +47,7 @@
         
       
     
-    
+    
       
         
         
@@ -134,6 +135,10 @@
         
         
       
+      
+        
+        
+      
     
   
   
@@ -143,13 +148,22 @@
     
     
       
+      
       
     
     
       
         
+        
       
     
+    
+      
+      
+        
+      
+    
   
   
     
diff --git a/utils/fpdoc/fpdoc.pp b/utils/fpdoc/fpdoc.pp
index 5e789409e1..3892bb8e3b 100644
--- a/utils/fpdoc/fpdoc.pp
+++ b/utils/fpdoc/fpdoc.pp
@@ -37,7 +37,7 @@ uses
   dw_man,    // Man page writer
   dw_linrtf, // linear RTF writer
   dw_txt,    // TXT writer
-  fpdocproj, mkfpdoc, dw_basemd, dw_basehtml;
+  fpdocproj, mkfpdoc, dw_basemd, dw_basehtml, fpdocstrs;
 
 
 Type
@@ -104,6 +104,9 @@ begin
   Writeln(SUsageOption215);
   Writeln(SUsageOption215A);
   Writeln(SUsageOption220);
+  Writeln(SUsageOption221);
+  Writeln(SUsageOption222);
+  Writeln(SUsageOption223);
   Writeln(SUsageOption230);
   Writeln(SUsageOption240);
   Writeln(SUsageOption250);
@@ -151,7 +154,9 @@ end;
 
 procedure TFPDocApplication.ExceptProc(Sender: TObject; E: Exception);
 begin
+{$IFDEF EXCEPTION_STACK}
   OutputLog(Sender, DumpExceptionCallStack(E));
+{$ENDIF}
 end;
 
 destructor TFPDocApplication.Destroy;
@@ -307,6 +312,12 @@ begin
     FCreator.Options.HideProtected := True
   else if s = '--warn-no-node' then
     FCreator.Options.WarnNoNode := True
+  else if s = '--warn-documentation-empty' then
+    FCreator.Options.WarnDocumentationEmpty := True
+  else if s = '--warn-used-file' then
+    FCreator.Options.WarnUsedFile := True
+  else if s = '--warn-XCT' then
+    FCreator.Options.WarnXCT := True
   else if s = '--show-private' then
     FCreator.Options.ShowPrivate := True
   else if s = '--stop-on-parser-error' then
diff --git a/utils/fpdoc/fpdocclasstree.pp b/utils/fpdoc/fpdocclasstree.pp
index 3ea8c84d65..fdb2618996 100644
--- a/utils/fpdoc/fpdocclasstree.pp
+++ b/utils/fpdoc/fpdocclasstree.pp
@@ -2,6 +2,7 @@ unit fpdocclasstree;
 
 {$mode objfpc}{$H+}
 
+
 interface
 
 uses
@@ -9,6 +10,8 @@ uses
 
 Type
 
+  TPasObjKindSet = set of TPasObjKind;
+
   { TPasElementNode }
 
   TPasElementNode = Class
@@ -35,7 +38,7 @@ Type
   Private
     FEngine:TFPDocEngine;
     FElementList : TFPObjectHashTable;
-    FObjectKind : TPasObjKind;
+    FObjectKind : TPasObjKindSet;
     FPackage: TPasPackage;
     FParentObject : TPasClassType;
     FRootNode : TPasElementNode;
@@ -45,7 +48,7 @@ Type
     function AddToList(aElement: TPasClassType): TPasElementNode;
   Public
     Constructor Create(AEngine:TFPDocEngine; APackage : TPasPackage;
-                          AObjectKind : TPasObjKind = okClass);
+                          AObjectKind : TPasObjKindSet = okWithFields);
     Destructor Destroy; override;
     Function BuildTree(AObjects : TStringList) : Integer;
     Procedure SaveToXml(AFileName: String);
@@ -56,6 +59,9 @@ Type
 
 implementation
 
+uses
+  fpdocstrs, pasresolver;
+
 { TPasElementNode }
 
 function SortOnElementName(Item1, Item2: Pointer): Integer;
@@ -104,33 +110,36 @@ begin
 end;
 
 constructor TClassTreeBuilder.Create(AEngine:TFPDocEngine; APackage : TPasPackage;
-  AObjectKind: TPasObjKind);
+  AObjectKind: TPasObjKindSet);
 
 begin
   FEngine:= AEngine;
   FPackage:= APAckage;
   FObjectKind:=AObjectKind;
-  Case FObjectkind of
-    okInterface :
+  if (okInterface in FObjectkind) then
       begin
         FRootObjectPathName:='#rtl.System.IInterface';
         FRootObjectName:= 'IInterface';
-      end;
-    okObject, okClass :
+      end
+  else if (FObjectkind * okWithFields) <> [] then
       begin
         FRootObjectPathName:='#rtl.System.TObject';
         FRootObjectName:= 'TObject';
       end
-  else
+  else  // TODO: I don`t know need it ? Without that the code may be simplified.
     begin
       FRootObjectPathName:='#rtl.System.TObject';
       FRootObjectName:= 'TObject';
     end;
-  end;
   FParentObject:=TPasClassType.Create(FRootObjectName,FEngine.FindModule('System'));
   if not Assigned(FParentObject) then
     FParentObject:=TPasClassType.Create(FRootObjectName,FPackage);
-  FParentObject.ObjKind:=FObjectKind;
+  if (okInterface in FObjectkind) then
+      FParentObject.ObjKind:=okInterface
+  else if (FObjectkind * okWithFields) <> [] then
+    FParentObject.ObjKind:=okClass
+  else
+    FParentObject.ObjKind:=okClass;
   FRootNode:=TPasElementNode.Create(FParentObject);
   FRootNode.FParentNode := nil;
   FElementList:=TFPObjectHashTable.Create(False);
@@ -154,7 +163,8 @@ Var
 
 begin
   Result:= nil;
-  if (aElement.ObjKind <> FObjectKind) then exit;
+  if not (aElement.ObjKind in FObjectKind) then exit;
+
   aParentNode:= nil;
   if aElement=Nil then
     aName:=FRootObjectName
@@ -227,10 +237,11 @@ procedure TClassTreeBuilder.SaveToXml ( AFileName: String );
     for CounterVar := 0 to ParentPasEl.ChildCount-1 do
     begin
       PasElNode:= ParentPasEl.Children[CounterVar];
-      xmlEl:= AXmlDoc.CreateElement(UnicodeString(PasElNode.Element.Name));
+      xmlEl:= AXmlDoc.CreateElement(UTF8Decode(PasElNode.Element.Name));
       M:= PasElNode.Element.GetModule;
-      xmlEl['unit'] := UnicodeString(M.Name);
-      xmlEl['package'] := UnicodeString(M.PackageName);
+      xmlEl['unit'] := UTF8Decode(M.Name);
+      xmlEl['package'] := UTF8Decode(M.PackageName);
+      xmlEl['type'] := UTF8Decode(GetElementTypeName(PasElNode.Element));
       ParentxmlEl.AppendChild(xmlEl);
       AddPasElChildsToXml(xmlEl, PasElNode);
     end;
@@ -244,17 +255,24 @@ begin
   XmlDoc:= TXMLDocument.Create;
   XmlDoc.AppendChild(XmlDoc.CreateComment(UTF8Decode(SDocGeneratedByComment)));
   try
-    XmlRootEl:= XmlDoc.CreateElement(UnicodeString(FRootNode.Element.Name));
+    XmlRootEl:= XmlDoc.CreateElement(UTF8Decode(FRootNode.Element.Name));
     M:= FRootNode.Element.GetModule;
     if Assigned(M) then
     begin
-      XmlRootEl['unit'] := UnicodeString(M.Name);
-      XmlRootEl['package'] := UnicodeString(M.PackageName);
+      XmlRootEl['unit'] := UTF8Decode(M.Name);
+      XmlRootEl['package'] := UTF8Decode(M.PackageName);
+      XmlRootEl['type'] := UTF8Decode(GetElementTypeName(FRootNode.Element));
     end
       else
     begin
       XmlRootEl['unit'] := 'system';
       XmlRootEl['package'] := 'rtl';
+      if (okWithFields * FObjectKind) <> [] then
+        XmlRootEl['type'] := 'class'
+      else if (okInterface in FObjectKind) then
+        XmlRootEl['type'] := 'interface'
+      else
+        XmlRootEl['type'] := 'class';
     end;
     XmlDoc.AppendChild(XmlRootEl);
     AddPasElChildsToXml(XmlRootEl, FRootNode);
diff --git a/utils/fpdoc/fpdocproj.pas b/utils/fpdoc/fpdocproj.pas
index 7ea90f3401..040ff81007 100644
--- a/utils/fpdoc/fpdocproj.pas
+++ b/utils/fpdoc/fpdocproj.pas
@@ -60,8 +60,11 @@ Type
     FMoDir: String;
     FOSTarget: String;
     FSOPE: Boolean;
+    FWarnDocumentationEmpty: Boolean;
     FWarnNoNode: Boolean;
     FDontTrim : Boolean;
+    FWarnUsedFile: Boolean;
+    FWarnXCT: Boolean;
     procedure SetBackendOptions(const AValue: TStrings);
   Public
     Constructor Create;
@@ -77,6 +80,9 @@ Type
     Property StopOnParseError : Boolean Read FSOPE Write FSOPE;
     Property HideProtected : Boolean Read FHideProtected Write FHideProtected;
     Property WarnNoNode : Boolean Read FWarnNoNode Write FWarnNoNode;
+    Property WarnUsedFile : Boolean Read FWarnUsedFile Write FWarnUsedFile;
+    Property WarnDocumentationEmpty : Boolean Read FWarnDocumentationEmpty Write FWarnDocumentationEmpty;
+    Property WarnXCT : Boolean Read FWarnXCT Write FWarnXCT;
     Property ShowPrivate : Boolean Read FHidePrivate Write FHidePrivate;
     Property InterfaceOnly : Boolean Read FIO Write FIO;
     Property MoDir : String Read FMoDir Write FMODir;
@@ -189,6 +195,9 @@ begin
     FSOPE:=O.StopOnParseError;
     HideProtected:=O.HideProtected;
     WarnNoNode:=O.WarnNoNode;
+    WarnUsedFile:=O.WarnUsedFile;
+    WarnDocumentationEmpty:=O.WarnDocumentationEmpty;
+    WarnXCT:=O.WarnXCT;
     ShowPrivate:=O.ShowPrivate;
     InterfaceOnly:=O.InterfaceOnly;
     MoDir:=O.MoDir;
diff --git a/utils/fpdoc/fpdocstrs.pp b/utils/fpdoc/fpdocstrs.pp
new file mode 100644
index 0000000000..e61871e393
--- /dev/null
+++ b/utils/fpdoc/fpdocstrs.pp
@@ -0,0 +1,253 @@
+unit fpdocstrs;
+
+{$mode objfpc}{$H+}
+
+interface
+
+resourcestring
+  // Output strings
+  SDocPackageTitle           = 'Reference for package ''%s''';
+  SDocPackageMenuTitle       = 'Package ''%s''';
+  SDocPackageLinkTitle       = 'Package';
+  SDocPrograms               = 'Programs';
+  SDocUnits                  = 'Units';
+  SDocUnitTitle              = 'Reference for unit ''%s''';
+  SDocUnitMenuTitle          = 'Unit ''%s''';
+  SDocInheritanceHierarchy   = 'Inheritance Hierarchy';
+  SDocInterfaceSection       = 'Interface section';
+  SDocImplementationSection  = 'Implementation section';
+  SDocUsedUnits              = 'Used units';
+  SDocUsedUnitsByUnitXY      = 'Used units by unit ''%s''';
+  SDocConstsTypesVars        = 'Constants, types and variables';
+  SDocResStrings             = 'Resource strings';
+  SDocTypes                  = 'Types';
+  SDocType                   = 'Type';
+  SDocConstants              = 'Constants';
+  SDocConstant               = 'Constant';
+  SDocClasses                = 'Classes';
+  SDocClass                  = 'Class';
+  SDocProceduresAndFunctions = 'Procedures and functions';
+  SDocProcedureOrFunction    = 'Procedure/function';
+  SDocVariables              = 'Variables';
+  SDocVariable               = 'Variable';
+  SDocIdentifierIndex        = 'Index';
+  SDocPackageClassHierarchy  = 'Class hierarchy';
+  SDocModuleIndex            = 'Index of all identifiers in unit ''%s''';
+  SDocPackageIndex           = 'Index of all identifiers in package ''%s''';
+  SDocUnitOverview           = 'Overview of unit ''%s''';
+  SDocOverview               = 'Overview';
+  SDocSearch                 = 'Search';
+  SDocDeclaration            = 'Declaration';
+  SDocDescription            = 'Description';
+  SDocErrors                 = 'Errors';
+  SDocVersion                = 'Version info';
+  SDocSeeAlso                = 'See also';
+  SDocExample                = 'Example';
+  SDocArguments              = 'Arguments';
+  SDocFunctionResult         = 'Function result';
+  SDocRemark                 = 'Remark:   ';
+  SDocMethodOverview         = 'Method overview';
+  SDocPropertyOverview       = 'Property overview';
+  SDocEventOverview          = 'Event overview';
+  SDocInterfacesOverview     = 'Interfaces overview';
+  SDocInterface              = 'Interfaces';
+  SDocPage                   = 'Page';
+  SDocMember                 = 'Member';
+  SDocMembers                = 'Members';
+  SDocField                  = 'Field';
+  SDocMethod                 = 'Method';
+  SDocProperty               = 'Property';
+  SDocAccess                 = 'Access';
+  SDocInheritance            = 'Inheritance';
+  SDocProperties             = 'Properties';
+  SDocMethods                = 'Methods';
+  SDocEvents                 = 'Events';
+  SDocByName                 = 'by Name';
+  SDocByInheritance          = 'By inheritance';
+  SDocValue                  = 'Value';
+  SDocExplanation            = 'Explanation';
+  SDocProcedure              = 'Procedure';
+  SDocValuesForEnum          = 'Enumeration values for type %s';
+  SDocSourcePosition         = 'Source position: %s line %d';
+  SDocSynopsis               = 'Synopsis';
+  SDocVisibility             = 'Visibility';
+  SDocOpaque                 = 'Opaque type';
+  SDocDateGenerated          = 'Documentation generated on: %s';
+  // The next line requires leading/trailing space due to XML comment layout:
+  SDocGeneratedByComment     = ' Generated using FPDoc - (c) 2000-2021 FPC contributors and Sebastian Guenther, sg@freepascal.org ';
+  SDocNotes                  = 'Notes';
+  SDocName                   = 'Name';
+  SDocType_s                 = 'Type(s)';
+  SDocTopic                  = 'Topic';
+  SDocNoneAVailable          = 'No members available';
+
+  // Topics
+  SDocRelatedTopics = 'Related topics';
+  SDocUp            = 'Up';
+  SDocNext          = 'Next';
+  SDocPrevious      = 'Previous';
+
+  // Various backend constants
+  SDocChapter    = 'Chapter';
+  SDocSection    = 'Section';
+  SDocSubSection = 'Subsection';
+  SDocTable      = 'Table';
+  SDocListing    = 'Listing';
+
+  // Man page usage
+  SManUsageManSection         = 'Use ASection as the man page section';
+  SManUsageNoUnitPrefix       = 'Do not prefix man pages with unit name.';
+  SManUsageWriterDescr        = 'UNIX man page output.';
+  SManUsagePackageDescription = 'Use descr as the description of man pages';
+
+  // HTML usage
+  SHTMLUsageFooter = 'Append xhtml (@filename reads from file) as footer to html page';
+  SHTMLUsageNavigator = 'Append xhtml (@filename reads from file) in navigator bar';
+  SHTMLUsageHeader = 'Append xhtml (@filename reads from file) as header to html page below navigation bar';
+  SHTMLUsageFooterDate = 'Append footer with date. fmt is Optional format for FormatDateTime';
+  SHTMLUsageCharset = 'Set the HTML character set';
+  SHTMLHtmlSearch = 'Add search page with given name to the menu bar';
+  SHTMLIndexColcount = 'Use N columns in the identifier index pages';
+  SHTMLImageUrl = 'Prefix image URLs with url';
+  SHTMLDisableMenuBrackets = 'Disable ''['' and '']'' characters around menu items at the top of the page. Useful for custom css';
+
+  // CHM usage
+  SCHMUsageTOC     = 'Use [File] as the table of contents. Usually a .hhc file.';
+  SCHMUsageIndex   = 'Use [File] as the index. Usually a .hhk file.';
+  SCHMUsageDefPage = 'Set the "Home" page relative to where it lives in the chm. i.e. "/index.html"';
+  SCHMUsageOtrFiles= 'A txt file containing a list of files to be added relative to the working directory.';
+  SCHMUsageCSSFile = 'Filename of a .css file to be included in the chm.';
+  SCHMUsageAutoTOC = 'Automatically generate a Table of Contents. Ignores --toc-file';
+  SCHMUsageAutoIDX = 'Automatically generate an Index. Ignores --index-file';
+  SCHMUsageMakeSearch = 'Automatically generate a Search Index from filenames that match *.htm*';
+  SCHMUsageChmTitle= 'Title of the chm. Defaults to the value from --package';
+
+  // MarkDown usage
+  SMDUsageFooter = 'Append markdown (@filename reads from file) as footer to every markdown page';
+  SMDUsageHeader = 'Prepend markdown (@filename reads from file) as header to every markdown page';
+  SMDIndexColcount = 'Use N columns in the identifier index pages';
+  SMDImageUrl = 'Prefix image URLs with url';
+  SMDTheme = 'Use name as theme name';
+  SMDNavigation = 'Use scheme for navigation tree, here scheme is one of:';
+  SMDNavSubtree = '    UnitSubTree : put all units in a sub tree of a Units node';
+  SMDNavTree =    '    UnitTree : put every units as a node on the same level as packages node';
+
+  SXMLUsageFlatStructure  = 'Use a flat output structure of XML files and directories';
+  SXMLUsageSource  = 'Include source file and line info in generated XML';
+
+  // Linear usage
+  SLinearUsageDupLinkedDocsP1 = 'Duplicate linked element documentation in';
+  SLinearUsageDupLinkedDocsP2 = 'descendant classes.';
+
+  STitle           = 'FPDoc - Free Pascal Documentation Tool';
+  SVersion         = 'Version %s [%s]';
+  SCopyright1      = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org';
+  SCopyright2      = '(c) 2005 - 2021 various FPC contributors';
+
+  SCmdLineHelp     = 'Usage: %s [options]';
+  SUsageOption008  = '--base-descr-dir=DIR prefix all description files with this directory';
+  SUsageOption009  = '--base-input-dir=DIR prefix all input files with this directory';
+  SUsageOption010  = '--content         Create content file for package cross-references';
+  SUsageOption020  = '--cputarget=value Set the target CPU for the scanner.';
+  SUsageOption030  = '--descr=file      use file as description file, e.g.: ';
+  SUsageOption035  = '                  --descr=c:\WIP\myzipperdoc.xml';
+  SUsageOption040  = '                  This option is allowed more than once';
+  SUsageOption050  = '--descr-dir=Dir   Add All XML files in Dir to list of description files';
+  SUsageOption060  = '--format=fmt      Select output format.';
+  SUsageOption070  = '--help            Show this help.';
+  SUsageOption080  = '--hide-protected  Do not show protected methods in overview';
+  SUsageOption090  = '--import=file     Import content file for package cross-references';
+  SUsageOption100  = '--input=cmd       use cmd as input for the parser, e.g.:';
+  SUsageOption110  = '           --input=C:\fpc\packages\paszlib\src\zipper.pp';
+  SUsageOption120  = '                  At least one input option is required.';
+  SUsageOption130  = '--input-dir=Dir   Add All *.pp and *.pas files in Dir to list of input files';
+  SUsageOption140  = '--lang=lng        Select output language.';
+  SUsageOption145  = '--macro=name=value Define a macro to preprocess the project file with.';
+  SUsageOption150  = '--ostarget=value  Set the target OS for the scanner.';
+  SUsageOption160  = '--output=name     use name as the output name.';
+  SUsageOption170  = '                  Each backend interprets this as needed.';
+  SUsageOption180  = '--package=name    Set the package name for which to create output,';
+  SUsageOption190  = '                  e.g. --package=fcl';
+  SUsageOption200  = '--project=file    Use file as project file';
+  SUsageOption210  = '--show-private    Show private methods.';
+  SUsageOption215  = '--stop-on-parser-error';
+  SUsageOption215A = '                  Stop when a parser error occurs. Default is to ignore parser errors.';
+  SUsageOption220  = '--warn-no-node    Warn if no documentation node was found.';
+  SUsageOption221  = '--warn-documentation-empty    Warn if documentation is empty.';
+  SUsageOption222  = '--warn-xct        Warn if an external class could not be resolved.';
+  SUsageOption223  = '--warn-used-file  Warn if an external class could not be resolved.';
+  SUsageOption230  = '--mo-dir=dir      Set directory where language files reside to dir';
+  SUsageOption240  = '--parse-impl      (Experimental) try to parse implementation too';
+  SUsageOption250  = '--dont-trim       Do not trim XML contents. Useful for preserving';
+  SUsageOption260  = '                  formatting inside e.g 
 tags';
+  SUsageOption270  = '--write-project=file';
+  SUsageOption280  = '                  Do not write documentation, create project file instead';
+  SUsageOption290  = '--verbose         Write more information on the screen';
+  SUsageOption300  = '--dry-run         Only parse sources and XML, do not create output';
+  SUsageOption310  = '--write-project=file';
+  SUsageOption320  = '                  Write all command-line options to a project file';
+  SUsageSubNames   = 'Use the file subnames instead the indexes as postfixes';
+
+  SUsageFormats        = 'The following output formats are supported by this fpdoc:';
+  SUsageBackendHelp    = 'Specify an output format, combined with --help to get more help for this backend.';
+  SUsageFormatSpecific = 'Output format "%s" supports the following options:';
+  SCmdLineErrInvalidMacro     = 'Macro needs to be in the form name=value';
+
+  SCmdLineInvalidOption       = 'Ignoring unknown option "%s"';
+  SCmdLineInvalidFormat       = 'Invalid format "%s" specified';
+  SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=';
+  SWritingPages               = 'Writing %d pages...';
+  SNeedPackageName            = 'No package name specified. Please specify one using the --package option.';
+  SAvailablePackages          = 'Available packages: ';
+  SDone                       = 'Done.';
+  SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';
+  SErrCouldNotCreateFile      = 'Could not create file "%s": %s';
+  SSeeURL                     = '(See %s)';      // For linear text writers.
+  SParsingUsedUnit            = 'Parsing used unit "%s" with commandLine "%s"';
+
+  SErrFileWriting = 'An error occurred during writing of file "%s": %s';
+
+  SErrInvalidShortDescr = 'Invalid short description';
+  SErrInvalidDescr = 'Invalid description (illegal XML element: "%s")';
+  SErrInvalidParaContent = 'Invalid paragraph content';
+  SErrInvalidElementInList = 'Invalid element in list - only "li" allowed';
+  SErrInvalidListContent = 'Invalid list content';
+  SErrInvalidRemarkContent = 'Invalid  content (illegal XML element: "%s")';
+  SErrListIsEmpty = 'List is empty - need at least one "li" element';
+  SErrInvalidDefinitionTermContent = 'Invalid content in definition term';
+  SErrDefinitionEntryMissing = 'Definition entry after definition term is missing';
+  SErrInvalidBorderValue = 'Invalid "border" value for %s';
+  SErrInvalidTableContent = 'Invalid table content';
+  SErrTableRowEmpty = 'Table row is empty (no "td" elements found)';
+  SErrInvalidContentBeforeSectionTitle = 'Invalid content before section title';
+  SErrSectionTitleExpected = 'Section title ("title" element) expected';
+
+  SErrDescrTagUnknown = 'Warning: Unknown tag "%s" in description';
+  SErrUnknownEntityReference = 'Warning: Unknown entity reference "&%s;" found';
+  SErrUnknownLinkID = 'Warning: Target ID of  in unit "%s", element "%s", is unknown: "%s"';
+  SErrUnknownPrintShortID = 'Warning: Target ID of  is unknown: "%s"';
+  SErrUnknownLink = 'Could not resolve link to "%s"';
+  SErralreadyRegistered = 'Class for output format "%s" already registered';
+  SErrUnknownWriterClass = 'Unknown output format "%s"';
+
+  SErrCannotChangeIndentSizeWhenIndented = 'Cannot change indent size while text is indented.';
+  SErrIndentMismatch = 'Indent mismatch: trying to undent when current indent too small';
+  SErrNotInList = 'Not in list';
+  SErrPopListStack = 'Pop list stack list type mismatch';
+  SErrMinListStack = 'Min list stack reached';
+  SErrMaxListStack = 'Max list stack reached';
+  SErrMinIndentStack = 'Min indent stack reached';
+  SErrMaxIndentStack = 'Max indent stack reached';
+
+  // doc xml
+  SErrInvalidRootNode = 'Invalid options root node: Got "%s", expected "docproject"';
+  SErrNoPackagesNode = 'No "packages" node found in docproject';
+  SErrNoInputFile = 'unit tag without file attribute found';
+  SErrNoDescrFile = 'description tag without file attribute';
+  SErrNoImportFile = 'Import tag without file attribute';
+  SErrNoImportPrefix = 'Import tag without prefix attribute';
+
+implementation
+
+end.
+
diff --git a/utils/fpdoc/fpmake.pp b/utils/fpdoc/fpmake.pp
index 1dabb89934..88f3c39b8b 100644
--- a/utils/fpdoc/fpmake.pp
+++ b/utils/fpdoc/fpmake.pp
@@ -42,6 +42,7 @@ begin
     P.Options.Add('-S2h');
 
     T:=P.Targets.AddProgram('fpdoc.pp');
+    T.Dependencies.AddUnit('fpdocstrs');
     T.Dependencies.AddUnit('dglobals');
     T.Dependencies.AddUnit('dw_ipflin');
     T.Dependencies.AddUnit('dwriter');
@@ -65,17 +66,15 @@ begin
     T:=P.Targets.AddProgram('fpclasschart.pp');
     T.ResourceStrings:=true;
 
-    T := P.Targets.AddUnit('dglobals.pp');
+    T := P.Targets.AddUnit('fpdocstrs.pp');
     T.install:=false;
     T.ResourceStrings:=true;
 
     T := P.Targets.AddUnit('dwriter.pp');
     T.install:=false;
-    T.ResourceStrings:=true;
 
     T := P.Targets.AddUnit('fpdocxmlopts.pas');
     T.install:=false;
-    T.ResourceStrings:=true;
 
     P.Targets.AddUnit('dw_xml.pp').install:=false;
     P.Targets.AddUnit('sh_pas.pp').install:=false;
@@ -84,7 +83,7 @@ begin
     P.Targets.AddUnit('dw_markdown.pp').install:=false;
     T:=P.Targets.AddUnit('dw_latex.pp');
     T.install:=false;
-    T.ResourceStrings:=true;
+
     P.Targets.AddUnit('dw_txt.pp').install:=false;
     P.Targets.AddUnit('dw_man.pp').install:=false;
     P.Targets.AddUnit('dwlinear.pp').install:=false;
diff --git a/utils/fpdoc/mkfpdoc.pp b/utils/fpdoc/mkfpdoc.pp
index 019369425b..56f4bdabf5 100644
--- a/utils/fpdoc/mkfpdoc.pp
+++ b/utils/fpdoc/mkfpdoc.pp
@@ -34,6 +34,7 @@ Type
     FProjectMacros: TStrings;
     FScannerLogEvents: TPScannerLogEvents;
     FVerbose: Boolean;
+    function GetLogLevels: TFPDocLogLevels;
     function GetOptions: TEngineOptions;
     function GetPackages: TFPDocPackages;
     procedure SetBaseDescrDir(AValue: String);
@@ -73,6 +74,7 @@ Type
 
 implementation
 
+uses fpdocstrs;
 
 { TFPDocCreator }
 
@@ -255,6 +257,23 @@ begin
     Engine.WriteContentFile(APackage.ContentFile);
 end;
 
+Function TFPDocCreator.GetLogLevels : TFPDocLogLevels;
+
+  Procedure DoOpt(doSet : Boolean; aLevel: TFPDocLogLevel);
+
+  begin
+    if DoSet then
+      Result:=Result+[aLevel];
+  end;
+
+begin
+  Result:=[];
+  DoOpt(Options.WarnNoNode,dleWarnNoNode);
+  DoOpt(Options.WarnUsedFile,dleWarnUsedFile);
+  DoOpt(Options.WarnDocumentationEmpty,dleDocumentationEmpty);
+  DoOpt(Options.WarnXCT,dleXCT);
+end;
+
 procedure TFPDocCreator.CreateDocumentation(APackage: TFPDocPackage;
   ParseOnly: Boolean);
 
@@ -291,7 +310,7 @@ begin
     Engine.HideProtected:=Options.HideProtected;
     Engine.HidePrivate:=Not Options.ShowPrivate;
     Engine.OnParseUnit:=@HandleOnParseUnit;
-    Engine.WarnNoNode:=Options.WarnNoNode;
+    Engine.DocLogLevels:=GetLogLevels;
     if Length(Options.Language) > 0 then
       TranslateDocStrings(Options.Language);
     // scan the input source files