1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Main program file for HelpGen
4 // Author: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Copyright: (c) 1999 VZ
10 /////////////////////////////////////////////////////////////////////////////
15 1. wx/string.h confuses C++ parser terribly
16 2. C++ parser doesn't know about virtual functions, nor static ones
17 3. param checking is not done for vararg functions
18 4. type comparison is dumb: it doesn't know that "char *" is the same
19 that "char []" nor that "const char *" is the same as "char const *"
21 TODO (+ means fixed), see also the change log at the end of the file.
23 (i) small fixes in the current version
25 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
27 3. Document global variables
30 6. Include file name/line number in the "diff" messages?
31 +7. Support for vararg functions
33 (ii) plans for version 2
34 1. Use wxTextFile for direct file access to avoid one scan method problems
35 2. Use command line parser class for the options
36 3. support for overloaded functions in diff mode (search for OVER)
38 (iii) plans for version 3
39 1. Merging with existing files
43 // =============================================================================
45 // =============================================================================
47 // -----------------------------------------------------------------------------
49 // -----------------------------------------------------------------------------
52 #include "wx/wxprec.h"
55 #include <wx/string.h>
57 #include <wx/dynarray.h>
63 // C++ parsing classes
70 // -----------------------------------------------------------------------------
72 // -----------------------------------------------------------------------------
74 // just a copy of argv
75 static char **g_argv
= NULL
;
77 class HelpGenApp
: public wxApp
89 IMPLEMENT_APP(HelpGenApp
);
91 // -----------------------------------------------------------------------------
93 // -----------------------------------------------------------------------------
95 // return the label for the given function name (i.e. argument of \label)
96 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
98 // return the whole \helpref{arg}{arg_label} string
99 static wxString
MakeHelpref(const char *argument
);
101 // [un]quote special TeX characters (in place)
102 static void TeXFilter(wxString
* str
);
103 static void TeXUnfilter(wxString
* str
); // also trims spaces
105 // get all comments associated with this context
106 static wxString
GetAllComments(const spContext
& ctx
);
108 // get the string with current time (returns pointer to static buffer)
109 // timeFormat is used for the call of strftime(3)
110 #ifdef GetCurrentTime
111 #undef GetCurrentTime
114 static const char *GetCurrentTime(const char *timeFormat
);
116 // -----------------------------------------------------------------------------
118 // -----------------------------------------------------------------------------
120 // add a function which sanitazes the string before writing it to the file
121 class wxTeXFile
: public wxFile
126 bool WriteTeX(const wxString
& s
)
131 return wxFile::Write(t
);
135 wxTeXFile(const wxTeXFile
&);
136 wxTeXFile
& operator=(const wxTeXFile
&);
139 // helper class which manages the classes and function names to ignore for
140 // the documentation purposes (used by both HelpGenVisitor and DocManager)
141 class IgnoreNamesHandler
144 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries
) { }
145 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore
); }
147 // load file with classes/functions to ignore (add them to the names we
149 bool AddNamesFromFile(const wxString
& filename
);
151 // return TRUE if we ignore this function
152 bool IgnoreMethod(const wxString
& classname
,
153 const wxString
& funcname
) const
155 if ( IgnoreClass(classname
) )
158 IgnoreListEntry
ignore(classname
, funcname
);
160 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
163 // return TRUE if we ignore this class entirely
164 bool IgnoreClass(const wxString
& classname
) const
166 IgnoreListEntry
ignore(classname
, "");
168 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
172 struct IgnoreListEntry
174 IgnoreListEntry(const wxString
& classname
,
175 const wxString
& funcname
)
176 : m_classname(classname
), m_funcname(funcname
)
180 wxString m_classname
;
181 wxString m_funcname
; // if empty, ignore class entirely
184 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
185 IgnoreListEntry
*second
);
187 // for efficiency, let's sort it
188 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
190 ArrayNamesToIgnore m_ignore
;
193 IgnoreNamesHandler(const IgnoreNamesHandler
&);
194 IgnoreNamesHandler
& operator=(const IgnoreNamesHandler
&);
197 // visitor implementation which writes all collected data to a .tex file
198 class HelpGenVisitor
: public spVisitor
202 HelpGenVisitor(const wxString
& directoryOut
, bool overwrite
);
204 virtual void VisitFile( spFile
& fl
);
205 virtual void VisitClass( spClass
& cl
);
206 virtual void VisitEnumeration( spEnumeration
& en
);
207 virtual void VisitTypeDef( spTypeDef
& td
);
208 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
209 virtual void VisitAttribute( spAttribute
& attr
);
210 virtual void VisitOperation( spOperation
& op
);
211 virtual void VisitParameter( spParameter
& param
);
215 // get our `ignore' object
216 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
218 // shut up g++ warning (ain't it stupid?)
219 virtual ~HelpGenVisitor() { }
222 // (re)initialize the state
225 // insert documentation for enums/typedefs coming immediately before the
226 // class declaration into the class documentation
227 void InsertTypedefDocs();
228 void InsertEnumDocs();
230 // write the headers for corresponding sections (only once)
231 void InsertDataStructuresHeader();
232 void InsertMethodsHeader();
234 // terminate the function documentation if it was started
235 void CloseFunction();
237 wxString m_directoryOut
, // directory for the output
238 m_fileHeader
; // name of the .h file we parse
239 bool m_overwrite
; // overwrite existing files?
240 wxTeXFile m_file
; // file we're writing to now
243 bool m_inClass
, // TRUE after file successfully opened
244 m_inTypesSection
, // enums & typedefs go there
245 m_inMethodSection
, // functions go here
246 m_isFirstParam
, // first parameter of current function?
247 m_inFunction
; // we're parsing a function declaration
249 // holders for "saved" documentation
250 wxString m_textStoredEnums
,
251 m_textStoredTypedefs
,
252 m_textStoredFunctionComment
;
254 // headers included by this file
255 wxArrayString m_headers
;
257 // ignore handler: tells us which classes to ignore for doc generation
259 IgnoreNamesHandler m_ignoreNames
;
262 HelpGenVisitor(const HelpGenVisitor
&);
263 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
266 // documentation manager - a class which parses TeX files and remembers the
267 // functions documented in them and can later compare them with all functions
268 // found under ctxTop by C++ parser
272 DocManager(bool checkParamNames
);
275 // returns FALSE on failure
276 bool ParseTeXFile(const wxString
& filename
);
278 // returns FALSE if there were any differences
279 bool DumpDifferences(spContext
*ctxTop
) const;
281 // get our `ignore' object
282 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
288 // returns the length of 'match' if the string 'str' starts with it or 0
290 static size_t TryMatch(const char *str
, const char *match
);
292 // skip spaces: returns pointer to first non space character (also
293 // updates the value of m_line)
294 const char *SkipSpaces(const char *p
)
296 while ( isspace(*p
) ) {
304 // skips characters until the next 'c' in '*pp' unless it ends before in
305 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
306 // returned and pp points to 'c'
307 bool SkipUntil(const char **pp
, char c
);
309 // the same as SkipUntil() but only spaces are skipped: on first non space
310 // character different from 'c' the function stops and returns FALSE
311 bool SkipSpaceUntil(const char **pp
, char c
);
313 // extract the string between {} and modify '*pp' to point at the
314 // character immediately after the closing '}'. The returned string is empty
316 wxString
ExtractStringBetweenBraces(const char **pp
);
318 // the current file and line while we're in ParseTeXFile (for error
323 // functions and classes to ignore during diff
324 // -------------------------------------------
326 IgnoreNamesHandler m_ignoreNames
;
328 // information about all functions documented in the TeX file(s)
329 // -------------------------------------------------------------
331 // info about a type: for now stored as text string, but must be parsed
332 // further later (to know that "char *" == "char []" - TODO)
336 TypeInfo(const wxString
& type
) : m_type(type
) { }
338 bool operator==(const wxString
& type
) const { return m_type
== type
; }
339 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
341 const wxString
& GetName() const { return m_type
; }
347 // info abotu a function parameter
351 ParamInfo(const wxString
& type
,
352 const wxString
& name
,
353 const wxString
& value
)
354 : m_type(type
), m_name(name
), m_value(value
)
358 const TypeInfo
& GetType() const { return m_type
; }
359 const wxString
& GetName() const { return m_name
; }
360 const wxString
& GetDefValue() const { return m_value
; }
363 TypeInfo m_type
; // type of parameter
364 wxString m_name
; // name
365 wxString m_value
; // default value
368 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
370 // info about a function
383 MethodInfo(const wxString
& type
,
384 const wxString
& name
,
385 const ArrayParamInfo
& params
)
386 : m_typeRet(type
), m_name(name
), m_params(params
)
391 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
393 const TypeInfo
& GetType() const { return m_typeRet
; }
394 const wxString
& GetName() const { return m_name
; }
395 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
396 size_t GetParamCount() const { return m_params
.GetCount(); }
398 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
400 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
403 TypeInfo m_typeRet
; // return type
405 int m_flags
; // bit mask of the value from the enum above
407 ArrayParamInfo m_params
;
410 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
411 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
413 // first array contains the names of all classes we found, the second has a
414 // pointer to the array of methods of the given class at the same index as
415 // the class name appears in m_classes
416 wxArrayString m_classes
;
417 ArrayMethodInfos m_methods
;
419 // are we checking parameter names?
420 bool m_checkParamNames
;
423 DocManager(const DocManager
&);
424 DocManager
& operator=(const DocManager
&);
427 // -----------------------------------------------------------------------------
429 // -----------------------------------------------------------------------------
431 // =============================================================================
433 // =============================================================================
435 // this function never returns
438 wxString prog
= g_argv
[0];
439 wxString basename
= prog
.AfterLast('/');
442 basename
= prog
.AfterLast('\\');
448 "usage: %s [global options] <mode> [mode options] <files...>\n"
450 " where global options are:\n"
453 " -H give this usage message\n"
454 " -V print the version info\n"
455 " -i file file with classes/function to ignore\n"
457 " where mode is one of: dump, diff\n"
459 " dump means generate .tex files for TeX2RTF converter from specified\n"
460 " headers files, mode options are:\n"
461 " -f overwrite existing files\n"
462 " -o outdir directory for generated files\n"
464 " diff means compare the set of methods documented .tex file with the\n"
465 " methods declared in the header:\n"
466 " %s diff <file.h> <files.tex...>.\n"
467 " mode specific options are:\n"
468 " -p do check parameter names (not done by default)\n"
469 "\n", basename
.c_str(), basename
.c_str());
475 int main(int argc, char **argv)
480 bool HelpGenApp::OnInit()
482 int HelpGenApp::OnRun()
498 wxArrayString filesH
, filesTeX
;
499 wxString directoryOut
, // directory for 'dmup' output
500 ignoreFile
; // file with classes/functions to ignore
501 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
502 paramNames
= FALSE
; // check param names during 'diff'?
504 for ( int current
= 1; current
< argc
; current
++ ) {
505 // all options have one letter
506 if ( argv
[current
][0] == '-' ) {
507 if ( argv
[current
][2] == '\0' ) {
508 switch ( argv
[current
][1] ) {
511 wxLog::GetActiveTarget()->SetVerbose();
516 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
525 if ( current
>= argc
) {
526 wxLogError("-i option requires an argument.");
531 ignoreFile
= argv
[current
];
535 if ( mode
!= Mode_Diff
) {
536 wxLogError("-p is only valid with diff.");
545 if ( mode
!= Mode_Dump
) {
546 wxLogError("-f is only valid with dump.");
555 if ( mode
!= Mode_Dump
) {
556 wxLogError("-o is only valid with dump.");
562 if ( current
>= argc
) {
563 wxLogError("-o option requires an argument.");
568 directoryOut
= argv
[current
];
569 if ( !!directoryOut
) {
570 // terminate with a '/' if it doesn't have it
571 switch ( directoryOut
.Last() ) {
582 //else: it's empty, do nothing
587 wxLogError("unknown option '%s'", argv
[current
]);
592 wxLogError("only one letter options are allowed, not '%s'.",
596 // only get here after a break from switch or from else branch of if
601 if ( mode
== Mode_None
) {
602 if ( strcmp(argv
[current
], "diff") == 0 )
604 else if ( strcmp(argv
[current
], "dump") == 0 )
607 wxLogError("unknown mode '%s'.", argv
[current
]);
613 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
614 filesH
.Add(argv
[current
]);
617 // 2nd files and further are TeX files in diff mode
618 wxASSERT( mode
== Mode_Diff
);
620 filesTeX
.Add(argv
[current
]);
626 // create a parser object and a visitor derivation
627 CJSourceParser parser
;
628 HelpGenVisitor
visitor(directoryOut
, overwrite
);
629 if ( !!ignoreFile
&& mode
== Mode_Dump
)
630 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
632 spContext
*ctxTop
= NULL
;
634 // parse all header files
635 size_t nFiles
= filesH
.GetCount();
636 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
637 wxString header
= filesH
[n
];
638 ctxTop
= parser
.ParseFile(header
);
640 wxLogWarning("Header file '%s' couldn't be processed.",
643 else if ( mode
== Mode_Dump
) {
644 ((spFile
*)ctxTop
)->mFileName
= header
;
645 visitor
.VisitAll(*ctxTop
);
652 #endif // __WXDEBUG__
655 // parse all TeX files
656 if ( mode
== Mode_Diff
) {
658 wxLogError("Can't complete diff.");
664 DocManager
docman(paramNames
);
666 size_t nFiles
= filesTeX
.GetCount();
667 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
668 wxString file
= filesTeX
[n
];
669 if ( !docman
.ParseTeXFile(file
) ) {
670 wxLogWarning("TeX file '%s' couldn't be processed.",
676 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
678 docman
.DumpDifferences(ctxTop
);
684 // -----------------------------------------------------------------------------
685 // HelpGenVisitor implementation
686 // -----------------------------------------------------------------------------
688 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
690 : m_directoryOut(directoryOut
)
692 m_overwrite
= overwrite
;
697 void HelpGenVisitor::Reset()
702 m_inMethodSection
= FALSE
;
704 m_textStoredTypedefs
=
706 m_textStoredFunctionComment
= "";
710 void HelpGenVisitor::InsertTypedefDocs()
712 m_file
.WriteTeX(m_textStoredTypedefs
);
713 m_textStoredTypedefs
.Empty();
716 void HelpGenVisitor::InsertEnumDocs()
718 m_file
.WriteTeX(m_textStoredEnums
);
719 m_textStoredEnums
.Empty();
722 void HelpGenVisitor::InsertDataStructuresHeader()
724 if ( !m_inTypesSection
) {
725 m_inTypesSection
= TRUE
;
727 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
731 void HelpGenVisitor::InsertMethodsHeader()
733 if ( !m_inMethodSection
) {
734 m_inMethodSection
= TRUE
;
736 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
740 void HelpGenVisitor::CloseFunction()
742 if ( m_inFunction
) {
743 m_inFunction
= FALSE
;
746 if ( m_isFirstParam
) {
748 totalText
<< "\\void";
751 totalText
<< "}\n\n";
753 if ( !m_textStoredFunctionComment
.IsEmpty() )
754 totalText
<< m_textStoredFunctionComment
<< '\n';
756 m_file
.WriteTeX(totalText
);
760 void HelpGenVisitor::EndVisit()
764 m_fileHeader
.Empty();
766 wxLogVerbose("%s: finished generating for the current file.",
767 GetCurrentTime("%H:%M:%S"));
770 void HelpGenVisitor::VisitFile( spFile
& file
)
772 m_fileHeader
= file
.mFileName
;
773 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
774 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
777 void HelpGenVisitor::VisitClass( spClass
& cl
)
779 m_inClass
= FALSE
; // will be left FALSE on error
781 wxString name
= cl
.GetName();
783 if ( m_ignoreNames
.IgnoreClass(name
) ) {
784 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
789 // the file name is built from the class name by removing the leading "wx"
790 // if any and converting it to the lower case
792 if ( name(0, 2) == "wx" ) {
793 filename
<< name
.c_str() + 2;
799 filename
.MakeLower();
801 filename
.Prepend(m_directoryOut
);
803 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
804 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
810 m_inClass
= m_file
.Open(filename
, wxFile::write
);
812 wxLogError("Can't generate documentation for the class '%s'.",
819 m_inTypesSection
= FALSE
;
821 wxLogInfo("Created new file '%s' for class '%s'.",
822 filename
.c_str(), name
.c_str());
824 // the entire text we're writing to file
827 // write out the header
831 "%% automatically generated by HelpGen from\n"
836 "\\section{\\class{%s}}\\label{%s}\n",
837 m_fileHeader
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
838 name
.c_str(), wxString(name
).MakeLower().c_str());
840 totalText
<< header
<< '\n';
843 // if the header includes other headers they must be related to it... try to
844 // automatically generate the "See also" clause
845 if ( !m_headers
.IsEmpty() ) {
846 // correspondence between wxWindows headers and class names
847 static const char *headers
[] = {
856 // NULL here means not to insert anything in "See also" for the
857 // corresponding header
858 static const char *classes
[] = {
867 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
868 "arrays must be in sync!" );
870 wxArrayInt interestingClasses
;
872 size_t count
= m_headers
.Count(), index
;
873 for ( size_t n
= 0; n
< count
; n
++ ) {
874 wxString baseHeaderName
= m_headers
[n
].Before('.');
875 if ( baseHeaderName(0, 3) != "wx/" )
878 baseHeaderName
.erase(0, 3);
879 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
880 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
884 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
885 // interesting header
886 interestingClasses
.Add(index
);
890 if ( !interestingClasses
.IsEmpty() ) {
891 // do generate "See also" clause
892 totalText
<< "\\wxheading{See also:}\n\n";
894 count
= interestingClasses
.Count();
895 for ( index
= 0; index
< count
; index
++ ) {
899 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
906 // the comment before the class generally explains what is it for so put it
907 // in place of the class description
908 if ( cl
.HasComments() ) {
909 wxString comment
= GetAllComments(cl
);
911 totalText
<< '\n' << comment
<< '\n';
914 // derived from section
915 wxString derived
= "\\wxheading{Derived from}\n\n";
917 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
918 if ( baseClasses
.size() == 0 ) {
919 derived
<< "No base class";
923 for ( StrListT::const_iterator i
= baseClasses
.begin();
924 i
!= baseClasses
.end();
927 // separate from the previous one
934 wxString baseclass
= *i
;
935 derived
<< "\\helpref{" << baseclass
<< "}";
936 derived
<< "{" << baseclass
.MakeLower() << "}";
939 totalText
<< derived
<< "\n\n";
941 // write all this to file
942 m_file
.WriteTeX(totalText
);
944 // if there were any enums/typedefs before, insert their documentation now
945 InsertDataStructuresHeader();
950 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
954 if ( m_inMethodSection
) {
955 // FIXME that's a bug, but tell the user aboit it nevertheless... we
956 // should be smart enough to process even the enums which come after the
958 wxLogWarning("enum '%s' ignored, please put it before the class "
959 "methods.", en
.GetName().c_str());
963 // simply copy the enum text in the docs
964 wxString enumeration
= GetAllComments(en
);
965 enumeration
<< "{\\small \\begin{verbatim}\n"
967 << "\n\\end{verbatim}}\n";
969 // remember for later use if we're not inside a class yet
971 if ( !m_textStoredEnums
.IsEmpty() ) {
972 m_textStoredEnums
<< '\n';
975 m_textStoredEnums
<< enumeration
;
978 // write the header for this section if not done yet
979 InsertDataStructuresHeader();
982 m_file
.WriteTeX(enumeration
);
986 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
990 if ( m_inMethodSection
) {
991 // FIXME that's a bug, but tell the user aboit it nevertheless...
992 wxLogWarning("typedef '%s' ignored, please put it before the class "
993 "methods.", td
.GetName().c_str());
998 typedefdoc
<< "{\\small \\begin{verbatim}\n"
999 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
1000 << "\n\\end{verbatim}}\n"
1001 << GetAllComments(td
);
1003 // remember for later use if we're not inside a class yet
1005 if ( !m_textStoredTypedefs
.IsEmpty() ) {
1006 m_textStoredTypedefs
<< '\n';
1009 m_textStoredTypedefs
<< typedefdoc
;
1012 // write the header for this section if not done yet
1013 InsertDataStructuresHeader();
1016 m_file
.WriteTeX(typedefdoc
);
1020 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1022 switch ( pd
.GetStatementType() ) {
1023 case SP_PREP_DEF_INCLUDE_FILE
:
1024 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1027 case SP_PREP_DEF_DEFINE_SYMBOL
:
1028 // TODO decide if it's a constant and document it if it is
1033 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1037 // only document the public member variables
1038 if ( !m_inClass
|| !attr
.IsPublic() )
1041 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1044 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1049 // we don't generate docs right now - either we ignore this class
1050 // entirely or we couldn't open the file
1054 if ( !op
.IsInClass() ) {
1055 // TODO document global functions
1056 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1061 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1062 // FIXME should we document protected functions?
1066 wxString funcname
= op
.GetName(),
1067 classname
= op
.GetClass().GetName();
1068 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1069 wxLogVerbose("Skipping ignored '%s::%s'.",
1070 classname
.c_str(), funcname
.c_str());
1075 InsertMethodsHeader();
1079 m_isFirstParam
= TRUE
;
1081 m_textStoredFunctionComment
= GetAllComments(op
);
1083 // start function documentation
1086 // check for the special case of dtor
1088 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1089 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1093 totalText
.Printf("\n"
1094 "\\membersection{%s::%s}\\label{%s}\n"
1096 "\\%sfunc{%s%s}{%s}{",
1097 classname
.c_str(), funcname
.c_str(),
1098 MakeLabel(classname
, funcname
).c_str(),
1099 op
.mIsConstant
? "const" : "",
1100 op
.mIsVirtual
? "virtual " : "",
1101 op
.mRetType
.c_str(),
1104 m_file
.WriteTeX(totalText
);
1107 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1109 if ( !m_inFunction
)
1113 if ( m_isFirstParam
) {
1114 m_isFirstParam
= FALSE
;
1120 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1121 wxString defvalue
= param
.mInitVal
;
1122 if ( !defvalue
.IsEmpty() ) {
1123 totalText
<< " = " << defvalue
;
1128 m_file
.WriteTeX(totalText
);
1131 // ---------------------------------------------------------------------------
1133 // ---------------------------------------------------------------------------
1135 DocManager::DocManager(bool checkParamNames
)
1137 m_checkParamNames
= checkParamNames
;
1140 size_t DocManager::TryMatch(const char *str
, const char *match
)
1142 size_t lenMatch
= 0;
1143 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1146 if ( match
[lenMatch
] == '\0' )
1153 bool DocManager::SkipUntil(const char **pp
, char c
)
1155 const char *p
= *pp
;
1171 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1173 const char *p
= *pp
;
1175 if ( !isspace(*p
) || *p
== '\0' )
1189 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1193 if ( !SkipSpaceUntil(pp
, '{') ) {
1194 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1195 m_filename
.c_str(), m_line
);
1199 const char *startParam
= ++*pp
; // skip '{'
1201 if ( !SkipUntil(pp
, '}') ) {
1202 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1203 m_filename
.c_str(), m_line
);
1206 result
= wxString(startParam
, (*pp
)++ - startParam
);
1213 bool DocManager::ParseTeXFile(const wxString
& filename
)
1215 m_filename
= filename
;
1217 wxFile
file(m_filename
, wxFile::read
);
1218 if ( !file
.IsOpened() )
1221 off_t len
= file
.Length();
1222 if ( len
== wxInvalidOffset
)
1225 char *buf
= new char[len
+ 1];
1228 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1234 // reinit everything
1237 wxLogVerbose("%s: starting to parse doc file '%s'.",
1238 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1240 // the name of the class from the last "\membersection" command: we assume
1241 // that the following "\func" or "\constfunc" always documents a method of
1242 // this class (and it should always be like that in wxWindows documentation)
1245 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1246 // FIXME parsing is awfully inefficient
1248 if ( *current
== '%' ) {
1249 // comment, skip until the end of line
1251 SkipUntil(¤t
, '\n');
1256 // all the command we're interested in start with '\\'
1257 while ( *current
!= '\\' && *current
!= '\0' ) {
1258 if ( *current
++ == '\n' )
1262 if ( *current
== '\0' ) {
1263 // no more TeX commands left
1267 current
++; // skip '\\'
1275 } foundCommand
= Nothing
;
1277 size_t lenMatch
= TryMatch(current
, "func");
1279 foundCommand
= Func
;
1282 lenMatch
= TryMatch(current
, "constfunc");
1284 foundCommand
= ConstFunc
;
1286 lenMatch
= TryMatch(current
, "membersection");
1289 foundCommand
= MemberSect
;
1293 if ( foundCommand
== Nothing
)
1296 current
+= lenMatch
;
1298 if ( !SkipSpaceUntil(¤t
, '{') ) {
1299 wxLogWarning("file %s(%d): '{' expected after \\func, "
1300 "\\constfunc or \\membersection.",
1301 m_filename
.c_str(), m_line
);
1308 if ( foundCommand
== MemberSect
) {
1309 // what follows has the form <classname>::<funcname>
1310 const char *startClass
= current
;
1311 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1312 wxLogWarning("file %s(%d): '::' expected after "
1313 "\\membersection.", m_filename
.c_str(), m_line
);
1316 classname
= wxString(startClass
, current
- startClass
);
1317 TeXUnfilter(&classname
);
1323 // extract the return type
1324 const char *startRetType
= current
;
1326 if ( !SkipUntil(¤t
, '}') ) {
1327 wxLogWarning("file %s(%d): '}' expected after return type",
1328 m_filename
.c_str(), m_line
);
1333 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1334 TeXUnfilter(&returnType
);
1337 if ( !SkipSpaceUntil(¤t
, '{') ) {
1338 wxLogWarning("file %s(%d): '{' expected after return type",
1339 m_filename
.c_str(), m_line
);
1345 const char *funcEnd
= current
;
1346 if ( !SkipUntil(&funcEnd
, '}') ) {
1347 wxLogWarning("file %s(%d): '}' expected after function name",
1348 m_filename
.c_str(), m_line
);
1353 wxString funcName
= wxString(current
, funcEnd
- current
);
1354 current
= funcEnd
+ 1;
1356 // trim spaces from both sides
1357 funcName
.Trim(FALSE
);
1358 funcName
.Trim(TRUE
);
1360 // special cases: '$...$' may be used for LaTeX inline math, remove the
1362 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1364 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1365 if ( *p
!= '$' && !isspace(*p
) )
1372 // \destruct{foo} is really ~foo
1373 if ( funcName
[0u] == '\\' ) {
1374 size_t len
= strlen("\\destruct{");
1375 if ( funcName(0, len
) != "\\destruct{" ) {
1376 wxLogWarning("file %s(%d): \\destruct expected",
1377 m_filename
.c_str(), m_line
);
1382 funcName
.erase(0, len
);
1383 funcName
.Prepend('~');
1385 if ( !SkipSpaceUntil(¤t
, '}') ) {
1386 wxLogWarning("file %s(%d): '}' expected after destructor",
1387 m_filename
.c_str(), m_line
);
1392 funcEnd
++; // there is an extra '}' to count
1395 TeXUnfilter(&funcName
);
1398 current
= funcEnd
+ 1; // skip '}'
1399 if ( !SkipSpaceUntil(¤t
, '{') ||
1400 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1401 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1402 m_filename
.c_str(), m_line
);
1407 wxArrayString paramNames
, paramTypes
, paramValues
;
1409 bool isVararg
= FALSE
;
1411 current
++; // skip '\\'
1412 lenMatch
= TryMatch(current
, "void");
1414 lenMatch
= TryMatch(current
, "param");
1415 while ( lenMatch
&& (current
- buf
< len
) ) {
1416 current
+= lenMatch
;
1418 // now come {paramtype}{paramname}
1419 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1420 if ( !!paramType
) {
1421 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1422 if ( !!paramText
) {
1423 // the param declaration may contain default value
1424 wxString paramName
= paramText
.BeforeFirst('='),
1425 paramValue
= paramText
.AfterFirst('=');
1427 // sanitize all strings
1428 TeXUnfilter(¶mValue
);
1429 TeXUnfilter(¶mName
);
1430 TeXUnfilter(¶mType
);
1432 paramValues
.Add(paramValue
);
1433 paramNames
.Add(paramName
);
1434 paramTypes
.Add(paramType
);
1439 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1440 if ( paramText
== "..." ) {
1444 wxLogWarning("Parameters of '%s::%s' are in "
1446 classname
.c_str(), funcName
.c_str());
1451 current
= SkipSpaces(current
);
1452 if ( *current
== ',' || *current
== '}' ) {
1453 current
= SkipSpaces(++current
);
1455 lenMatch
= TryMatch(current
, "\\param");
1458 wxLogWarning("file %s(%d): ',' or '}' expected after "
1459 "'\\param'", m_filename
.c_str(), m_line
);
1465 // if we got here there was no '\\void', so must have some params
1466 if ( paramNames
.IsEmpty() ) {
1467 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1468 m_filename
.c_str(), m_line
);
1474 // verbose diagnostic output
1476 size_t param
, paramCount
= paramNames
.GetCount();
1477 for ( param
= 0; param
< paramCount
; param
++ ) {
1482 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1485 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1486 m_filename
.c_str(), m_line
,
1491 foundCommand
== ConstFunc
? " const" : "");
1493 // store the info about the just found function
1494 ArrayMethodInfo
*methods
;
1495 int index
= m_classes
.Index(classname
);
1496 if ( index
== wxNOT_FOUND
) {
1497 m_classes
.Add(classname
);
1499 methods
= new ArrayMethodInfo
;
1500 m_methods
.Add(methods
);
1503 methods
= m_methods
[(size_t)index
];
1506 ArrayParamInfo params
;
1507 for ( param
= 0; param
< paramCount
; param
++ ) {
1508 params
.Add(new ParamInfo(paramTypes
[param
],
1510 paramValues
[param
]));
1513 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1514 if ( foundCommand
== ConstFunc
)
1515 method
->SetFlag(MethodInfo::Const
);
1517 method
->SetFlag(MethodInfo::Vararg
);
1519 methods
->Add(method
);
1524 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1525 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1530 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1532 typedef MMemberListT::const_iterator MemberIndex
;
1534 bool foundDiff
= FALSE
;
1536 // flag telling us whether the given class was found at all in the header
1537 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1538 bool *classExists
= new bool[countClassesInDocs
];
1539 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1540 classExists
[nClass
] = FALSE
;
1543 // ctxTop is normally an spFile
1544 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1546 const MMemberListT
& classes
= ctxTop
->GetMembers();
1547 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1548 spContext
*ctx
= *i
;
1549 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1550 // TODO process also global functions, macros, ...
1554 spClass
*ctxClass
= (spClass
*)ctx
;
1555 const wxString
& nameClass
= ctxClass
->mName
;
1556 int index
= m_classes
.Index(nameClass
);
1557 if ( index
== wxNOT_FOUND
) {
1558 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1561 wxLogError("Class '%s' is not documented at all.",
1565 // it makes no sense to check for its functions
1569 classExists
[index
] = TRUE
;
1572 // array of method descriptions for this class
1573 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1574 size_t nMethod
, countMethods
= methods
.GetCount();
1576 // flags telling if we already processed given function
1577 bool *methodExists
= new bool[countMethods
];
1578 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1579 methodExists
[nMethod
] = FALSE
;
1582 wxArrayString aOverloadedMethods
;
1584 const MMemberListT
& functions
= ctxClass
->GetMembers();
1585 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1587 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1590 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1591 const wxString
& nameMethod
= ctxMethod
->mName
;
1593 // find all functions with the same name
1594 wxArrayInt aMethodsWithSameName
;
1595 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1596 if ( methods
[nMethod
]->GetName() == nameMethod
)
1597 aMethodsWithSameName
.Add(nMethod
);
1600 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1601 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1604 wxLogError("'%s::%s' is not documented.",
1606 nameMethod
.c_str());
1609 // don't check params
1612 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1613 index
= (size_t)aMethodsWithSameName
[0u];
1614 methodExists
[index
] = TRUE
;
1616 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1619 if ( !ctxMethod
->IsPublic() ) {
1620 wxLogWarning("'%s::%s' is documented but not public.",
1622 nameMethod
.c_str());
1625 // check that the flags match
1626 const MethodInfo
& method
= *(methods
[index
]);
1628 bool isVirtual
= ctxMethod
->mIsVirtual
;
1629 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1630 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1634 isVirtual
? "not " : "");
1637 bool isConst
= ctxMethod
->mIsConstant
;
1638 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1639 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1643 isConst
? "not " : "");
1646 // check that the params match
1647 const MMemberListT
& params
= ctxMethod
->GetMembers();
1649 if ( params
.size() != method
.GetParamCount() ) {
1650 wxLogError("Incorrect number of parameters for '%s::%s' "
1651 "in the docs: should be %d instead of %d.",
1654 params
.size(), method
.GetParamCount());
1658 for ( MemberIndex k
= params
.begin();
1663 // what else can a function have?
1664 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1666 spParameter
*ctxParam
= (spParameter
*)ctx
;
1667 const ParamInfo
& param
= method
.GetParam(nParam
);
1668 if ( m_checkParamNames
&&
1669 (param
.GetName() != ctxParam
->mName
) ) {
1672 wxLogError("Parameter #%d of '%s::%s' should be "
1673 "'%s' and not '%s'.",
1677 ctxParam
->mName
.c_str(),
1678 param
.GetName().c_str());
1683 if ( param
.GetType() != ctxParam
->mType
) {
1686 wxLogError("Type of parameter '%s' of '%s::%s' "
1687 "should be '%s' and not '%s'.",
1688 ctxParam
->mName
.c_str(),
1691 ctxParam
->mType
.c_str(),
1692 param
.GetType().GetName().c_str());
1697 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1698 wxLogWarning("Default value of parameter '%s' of "
1699 "'%s::%s' should be '%s' and not "
1701 ctxParam
->mName
.c_str(),
1704 ctxParam
->mInitVal
.c_str(),
1705 param
.GetDefValue().c_str());
1711 // TODO OVER add real support for overloaded methods
1713 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1716 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1717 // mark all methods with this name as existing
1718 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1719 if ( methods
[nMethod
]->GetName() == nameMethod
)
1720 methodExists
[nMethod
] = TRUE
;
1723 aOverloadedMethods
.Add(nameMethod
);
1725 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1726 "stupid to find the right match - skipping "
1727 "the param and flags checks.",
1729 nameMethod
.c_str());
1731 //else: warning already given
1735 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1736 if ( !methodExists
[nMethod
] ) {
1737 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1738 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1741 wxLogError("'%s::%s' is documented but doesn't exist.",
1743 nameMethod
.c_str());
1748 delete [] methodExists
;
1751 // check that all classes we found in the docs really exist
1752 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1753 if ( !classExists
[nClass
] ) {
1756 wxLogError("Class '%s' is documented but doesn't exist.",
1757 m_classes
[nClass
].c_str());
1761 delete [] classExists
;
1766 DocManager::~DocManager()
1768 WX_CLEAR_ARRAY(m_methods
);
1771 // ---------------------------------------------------------------------------
1772 // IgnoreNamesHandler implementation
1773 // ---------------------------------------------------------------------------
1775 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1776 IgnoreListEntry
*second
)
1778 // first compare the classes
1779 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1781 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1786 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1788 wxFile
file(filename
, wxFile::read
);
1789 if ( !file
.IsOpened() )
1792 off_t len
= file
.Length();
1793 if ( len
== wxInvalidOffset
)
1796 char *buf
= new char[len
+ 1];
1799 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1806 for ( const char *current
= buf
; ; current
++ ) {
1808 // skip DOS line separator
1809 if ( *current
== '\r' )
1813 if ( *current
== '\n' || *current
== '\0' ) {
1814 if ( line
[0u] != '#' ) {
1815 if ( line
.Find(':') != wxNOT_FOUND
) {
1816 wxString classname
= line
.BeforeFirst(':'),
1817 funcname
= line
.AfterLast(':');
1818 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1822 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1827 if ( *current
== '\0' )
1842 // -----------------------------------------------------------------------------
1843 // global function implementation
1844 // -----------------------------------------------------------------------------
1846 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1848 wxString
label(classname
);
1849 if ( funcname
&& funcname
[0] == '\\' ) {
1850 // we may have some special TeX macro - so far only \destruct exists,
1851 // but may be later others will be added
1852 static const char *macros
[] = { "destruct" };
1853 static const char *replacement
[] = { "dtor" };
1856 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1857 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1863 if ( n
== WXSIZEOF(macros
) ) {
1864 wxLogWarning("unknown function name '%s' - leaving as is.",
1868 funcname
= replacement
[n
];
1880 static wxString
MakeHelpref(const char *argument
)
1883 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1888 static void TeXUnfilter(wxString
* str
)
1890 // FIXME may be done much more quickly
1894 str
->Replace("\\&", "&");
1895 str
->Replace("\\_", "_");
1898 static void TeXFilter(wxString
* str
)
1900 // FIXME may be done much more quickly
1901 str
->Replace("&", "\\&");
1902 str
->Replace("_", "\\_");
1905 static wxString
GetAllComments(const spContext
& ctx
)
1908 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1909 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1910 i
!= commentsList
.end();
1912 wxString comment
= (*i
)->GetText();
1914 // don't take comments like "// ----------" &c
1915 comment
.Trim(FALSE
);
1917 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1920 comments
<< comment
;
1926 static const char *GetCurrentTime(const char *timeFormat
)
1928 static char s_timeBuffer
[128];
1933 ptmNow
= localtime(&timeNow
);
1935 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1937 return s_timeBuffer
;
1942 Revision 1.11 2000/07/15 19:50:42 cvsuser
1945 Revision 1.10.2.2 2000/03/27 15:33:10 VZ
1946 don't trasnform output dir name to lower case
1948 Revision 1.10 2000/03/11 10:05:23 VS
1949 now compiles with wxBase
1951 Revision 1.9 2000/01/16 13:25:21 VS
1952 compilation fixes (gcc)
1954 Revision 1.8 1999/09/13 14:29:39 JS
1956 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1957 into src for simplicity; added VC++ 5 project file
1959 Revision 1.7 1999/02/21 22:32:32 VZ
1960 1. more C++ parser fixes - now it almost parses wx/string.h
1961 a) #if/#ifdef/#else (very) limited support
1962 b) param type fix - now indirection chars are correctly handled
1963 c) class/struct/union distinction
1964 d) public/private fixes
1965 e) Dump() function added - very useful for debugging
1967 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1968 by default, and this option switches it on)
1970 Revision 1.6 1999/02/20 23:00:26 VZ
1971 1. new 'diff' mode which seems to work
1972 2. output files are not overwritten in 'dmup' mode
1973 3. fixes for better handling of const functions and operators
1974 ----------------------------
1976 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1977 1. Parser improvements
1978 a) const and virtual methods are parsed correctly (not static yet)
1979 b) "const" which is part of the return type is not swallowed
1981 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1982 "//---------" kind comments discarded now.
1983 ----------------------------
1985 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1987 some tweaks to HelpGen
1988 ----------------------------
1990 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1992 HelpGen starting to compile with VC++
1993 ----------------------------
1995 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
1997 supports typedefs, generates "See also:" and adds "virtual " for virtual
1999 ----------------------------
2001 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
2003 HelpGen is a prototype of the tool for automatic generation of the .tex files
2004 for wxWindows documentation from C++ headers
2007 /* vi: set tw=80 et ts=4 sw=4: */