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());
476 int main(int argc, char **argv)
481 bool HelpGenApp::OnInit()
483 int HelpGenApp::OnRun()
499 wxArrayString filesH
, filesTeX
;
500 wxString directoryOut
, // directory for 'dmup' output
501 ignoreFile
; // file with classes/functions to ignore
502 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
503 paramNames
= FALSE
; // check param names during 'diff'?
505 for ( int current
= 1; current
< argc
; current
++ ) {
506 // all options have one letter
507 if ( argv
[current
][0] == '-' ) {
508 if ( argv
[current
][2] == '\0' ) {
509 switch ( argv
[current
][1] ) {
512 wxLog::GetActiveTarget()->SetVerbose();
517 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
526 if ( current
>= argc
) {
527 wxLogError("-i option requires an argument.");
532 ignoreFile
= argv
[current
];
536 if ( mode
!= Mode_Diff
) {
537 wxLogError("-p is only valid with diff.");
546 if ( mode
!= Mode_Dump
) {
547 wxLogError("-f is only valid with dump.");
556 if ( mode
!= Mode_Dump
) {
557 wxLogError("-o is only valid with dump.");
563 if ( current
>= argc
) {
564 wxLogError("-o option requires an argument.");
569 directoryOut
= argv
[current
];
570 if ( !!directoryOut
) {
571 // terminate with a '/' if it doesn't have it
572 switch ( directoryOut
.Last() ) {
583 //else: it's empty, do nothing
588 wxLogError("unknown option '%s'", argv
[current
]);
593 wxLogError("only one letter options are allowed, not '%s'.",
597 // only get here after a break from switch or from else branch of if
602 if ( mode
== Mode_None
) {
603 if ( strcmp(argv
[current
], "diff") == 0 )
605 else if ( strcmp(argv
[current
], "dump") == 0 )
608 wxLogError("unknown mode '%s'.", argv
[current
]);
614 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
615 filesH
.Add(argv
[current
]);
618 // 2nd files and further are TeX files in diff mode
619 wxASSERT( mode
== Mode_Diff
);
621 filesTeX
.Add(argv
[current
]);
627 // create a parser object and a visitor derivation
628 CJSourceParser parser
;
629 HelpGenVisitor
visitor(directoryOut
, overwrite
);
630 if ( !!ignoreFile
&& mode
== Mode_Dump
)
631 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
633 spContext
*ctxTop
= NULL
;
635 // parse all header files
636 size_t nFiles
= filesH
.GetCount();
637 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
638 wxString header
= filesH
[n
];
639 ctxTop
= parser
.ParseFile(header
);
641 wxLogWarning("Header file '%s' couldn't be processed.",
644 else if ( mode
== Mode_Dump
) {
645 ((spFile
*)ctxTop
)->mFileName
= header
;
646 visitor
.VisitAll(*ctxTop
);
653 #endif // __WXDEBUG__
656 // parse all TeX files
657 if ( mode
== Mode_Diff
) {
659 wxLogError("Can't complete diff.");
665 DocManager
docman(paramNames
);
667 size_t nFiles
= filesTeX
.GetCount();
668 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
669 wxString file
= filesTeX
[n
];
670 if ( !docman
.ParseTeXFile(file
) ) {
671 wxLogWarning("TeX file '%s' couldn't be processed.",
677 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
679 docman
.DumpDifferences(ctxTop
);
685 // -----------------------------------------------------------------------------
686 // HelpGenVisitor implementation
687 // -----------------------------------------------------------------------------
689 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
691 : m_directoryOut(directoryOut
)
693 m_overwrite
= overwrite
;
698 void HelpGenVisitor::Reset()
703 m_inMethodSection
= FALSE
;
705 m_textStoredTypedefs
=
707 m_textStoredFunctionComment
= "";
711 void HelpGenVisitor::InsertTypedefDocs()
713 m_file
.WriteTeX(m_textStoredTypedefs
);
714 m_textStoredTypedefs
.Empty();
717 void HelpGenVisitor::InsertEnumDocs()
719 m_file
.WriteTeX(m_textStoredEnums
);
720 m_textStoredEnums
.Empty();
723 void HelpGenVisitor::InsertDataStructuresHeader()
725 if ( !m_inTypesSection
) {
726 m_inTypesSection
= TRUE
;
728 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
732 void HelpGenVisitor::InsertMethodsHeader()
734 if ( !m_inMethodSection
) {
735 m_inMethodSection
= TRUE
;
737 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
741 void HelpGenVisitor::CloseFunction()
743 if ( m_inFunction
) {
744 m_inFunction
= FALSE
;
747 if ( m_isFirstParam
) {
749 totalText
<< "\\void";
752 totalText
<< "}\n\n";
754 if ( !m_textStoredFunctionComment
.IsEmpty() )
755 totalText
<< m_textStoredFunctionComment
<< '\n';
757 m_file
.WriteTeX(totalText
);
761 void HelpGenVisitor::EndVisit()
765 m_fileHeader
.Empty();
767 wxLogVerbose("%s: finished generating for the current file.",
768 GetCurrentTime("%H:%M:%S"));
771 void HelpGenVisitor::VisitFile( spFile
& file
)
773 m_fileHeader
= file
.mFileName
;
774 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
775 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
778 void HelpGenVisitor::VisitClass( spClass
& cl
)
780 m_inClass
= FALSE
; // will be left FALSE on error
782 wxString name
= cl
.GetName();
784 if ( m_ignoreNames
.IgnoreClass(name
) ) {
785 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
790 // the file name is built from the class name by removing the leading "wx"
791 // if any and converting it to the lower case
793 if ( name(0, 2) == "wx" ) {
794 filename
<< name
.c_str() + 2;
800 filename
.MakeLower();
802 filename
.Prepend(m_directoryOut
);
804 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
805 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
811 m_inClass
= m_file
.Open(filename
, wxFile::write
);
813 wxLogError("Can't generate documentation for the class '%s'.",
820 m_inTypesSection
= FALSE
;
822 wxLogInfo("Created new file '%s' for class '%s'.",
823 filename
.c_str(), name
.c_str());
825 // the entire text we're writing to file
828 // write out the header
832 "%% automatically generated by HelpGen from\n"
837 "\\section{\\class{%s}}\\label{%s}\n",
838 m_fileHeader
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
839 name
.c_str(), wxString(name
).MakeLower().c_str());
841 totalText
<< header
<< '\n';
844 // if the header includes other headers they must be related to it... try to
845 // automatically generate the "See also" clause
846 if ( !m_headers
.IsEmpty() ) {
847 // correspondence between wxWindows headers and class names
848 static const char *headers
[] = {
857 // NULL here means not to insert anything in "See also" for the
858 // corresponding header
859 static const char *classes
[] = {
868 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
869 "arrays must be in sync!" );
871 wxArrayInt interestingClasses
;
873 size_t count
= m_headers
.Count(), index
;
874 for ( size_t n
= 0; n
< count
; n
++ ) {
875 wxString baseHeaderName
= m_headers
[n
].Before('.');
876 if ( baseHeaderName(0, 3) != "wx/" )
879 baseHeaderName
.erase(0, 3);
880 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
881 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
885 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
886 // interesting header
887 interestingClasses
.Add(index
);
891 if ( !interestingClasses
.IsEmpty() ) {
892 // do generate "See also" clause
893 totalText
<< "\\wxheading{See also:}\n\n";
895 count
= interestingClasses
.Count();
896 for ( index
= 0; index
< count
; index
++ ) {
900 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
907 // the comment before the class generally explains what is it for so put it
908 // in place of the class description
909 if ( cl
.HasComments() ) {
910 wxString comment
= GetAllComments(cl
);
912 totalText
<< '\n' << comment
<< '\n';
915 // derived from section
916 wxString derived
= "\\wxheading{Derived from}\n\n";
918 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
919 if ( baseClasses
.size() == 0 ) {
920 derived
<< "No base class";
924 for ( StrListT::const_iterator i
= baseClasses
.begin();
925 i
!= baseClasses
.end();
928 // separate from the previous one
935 wxString baseclass
= *i
;
936 derived
<< "\\helpref{" << baseclass
<< "}";
937 derived
<< "{" << baseclass
.MakeLower() << "}";
940 totalText
<< derived
<< "\n\n";
942 // write all this to file
943 m_file
.WriteTeX(totalText
);
945 // if there were any enums/typedefs before, insert their documentation now
946 InsertDataStructuresHeader();
951 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
955 if ( m_inMethodSection
) {
956 // FIXME that's a bug, but tell the user aboit it nevertheless... we
957 // should be smart enough to process even the enums which come after the
959 wxLogWarning("enum '%s' ignored, please put it before the class "
960 "methods.", en
.GetName().c_str());
964 // simply copy the enum text in the docs
965 wxString enumeration
= GetAllComments(en
);
966 enumeration
<< "{\\small \\begin{verbatim}\n"
968 << "\n\\end{verbatim}}\n";
970 // remember for later use if we're not inside a class yet
972 if ( !m_textStoredEnums
.IsEmpty() ) {
973 m_textStoredEnums
<< '\n';
976 m_textStoredEnums
<< enumeration
;
979 // write the header for this section if not done yet
980 InsertDataStructuresHeader();
983 m_file
.WriteTeX(enumeration
);
987 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
991 if ( m_inMethodSection
) {
992 // FIXME that's a bug, but tell the user aboit it nevertheless...
993 wxLogWarning("typedef '%s' ignored, please put it before the class "
994 "methods.", td
.GetName().c_str());
999 typedefdoc
<< "{\\small \\begin{verbatim}\n"
1000 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
1001 << "\n\\end{verbatim}}\n"
1002 << GetAllComments(td
);
1004 // remember for later use if we're not inside a class yet
1006 if ( !m_textStoredTypedefs
.IsEmpty() ) {
1007 m_textStoredTypedefs
<< '\n';
1010 m_textStoredTypedefs
<< typedefdoc
;
1013 // write the header for this section if not done yet
1014 InsertDataStructuresHeader();
1017 m_file
.WriteTeX(typedefdoc
);
1021 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1023 switch ( pd
.GetStatementType() ) {
1024 case SP_PREP_DEF_INCLUDE_FILE
:
1025 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1028 case SP_PREP_DEF_DEFINE_SYMBOL
:
1029 // TODO decide if it's a constant and document it if it is
1034 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1038 // only document the public member variables
1039 if ( !m_inClass
|| !attr
.IsPublic() )
1042 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1045 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1050 // we don't generate docs right now - either we ignore this class
1051 // entirely or we couldn't open the file
1055 if ( !op
.IsInClass() ) {
1056 // TODO document global functions
1057 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1062 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1063 // FIXME should we document protected functions?
1067 wxString funcname
= op
.GetName(),
1068 classname
= op
.GetClass().GetName();
1069 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1070 wxLogVerbose("Skipping ignored '%s::%s'.",
1071 classname
.c_str(), funcname
.c_str());
1076 InsertMethodsHeader();
1080 m_isFirstParam
= TRUE
;
1082 m_textStoredFunctionComment
= GetAllComments(op
);
1084 // start function documentation
1087 // check for the special case of dtor
1089 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1090 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1094 totalText
.Printf("\n"
1095 "\\membersection{%s::%s}\\label{%s}\n"
1097 "\\%sfunc{%s%s}{%s}{",
1098 classname
.c_str(), funcname
.c_str(),
1099 MakeLabel(classname
, funcname
).c_str(),
1100 op
.mIsConstant
? "const" : "",
1101 op
.mIsVirtual
? "virtual " : "",
1102 op
.mRetType
.c_str(),
1105 m_file
.WriteTeX(totalText
);
1108 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1110 if ( !m_inFunction
)
1114 if ( m_isFirstParam
) {
1115 m_isFirstParam
= FALSE
;
1121 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1122 wxString defvalue
= param
.mInitVal
;
1123 if ( !defvalue
.IsEmpty() ) {
1124 totalText
<< " = " << defvalue
;
1129 m_file
.WriteTeX(totalText
);
1132 // ---------------------------------------------------------------------------
1134 // ---------------------------------------------------------------------------
1136 DocManager::DocManager(bool checkParamNames
)
1138 m_checkParamNames
= checkParamNames
;
1141 size_t DocManager::TryMatch(const char *str
, const char *match
)
1143 size_t lenMatch
= 0;
1144 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1147 if ( match
[lenMatch
] == '\0' )
1154 bool DocManager::SkipUntil(const char **pp
, char c
)
1156 const char *p
= *pp
;
1172 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1174 const char *p
= *pp
;
1176 if ( !isspace(*p
) || *p
== '\0' )
1190 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1194 if ( !SkipSpaceUntil(pp
, '{') ) {
1195 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1196 m_filename
.c_str(), m_line
);
1200 const char *startParam
= ++*pp
; // skip '{'
1202 if ( !SkipUntil(pp
, '}') ) {
1203 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1204 m_filename
.c_str(), m_line
);
1207 result
= wxString(startParam
, (*pp
)++ - startParam
);
1214 bool DocManager::ParseTeXFile(const wxString
& filename
)
1216 m_filename
= filename
;
1218 wxFile
file(m_filename
, wxFile::read
);
1219 if ( !file
.IsOpened() )
1222 off_t len
= file
.Length();
1223 if ( len
== wxInvalidOffset
)
1226 char *buf
= new char[len
+ 1];
1229 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1235 // reinit everything
1238 wxLogVerbose("%s: starting to parse doc file '%s'.",
1239 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1241 // the name of the class from the last "\membersection" command: we assume
1242 // that the following "\func" or "\constfunc" always documents a method of
1243 // this class (and it should always be like that in wxWindows documentation)
1246 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1247 // FIXME parsing is awfully inefficient
1249 if ( *current
== '%' ) {
1250 // comment, skip until the end of line
1252 SkipUntil(¤t
, '\n');
1257 // all the command we're interested in start with '\\'
1258 while ( *current
!= '\\' && *current
!= '\0' ) {
1259 if ( *current
++ == '\n' )
1263 if ( *current
== '\0' ) {
1264 // no more TeX commands left
1268 current
++; // skip '\\'
1276 } foundCommand
= Nothing
;
1278 size_t lenMatch
= TryMatch(current
, "func");
1280 foundCommand
= Func
;
1283 lenMatch
= TryMatch(current
, "constfunc");
1285 foundCommand
= ConstFunc
;
1287 lenMatch
= TryMatch(current
, "membersection");
1290 foundCommand
= MemberSect
;
1294 if ( foundCommand
== Nothing
)
1297 current
+= lenMatch
;
1299 if ( !SkipSpaceUntil(¤t
, '{') ) {
1300 wxLogWarning("file %s(%d): '{' expected after \\func, "
1301 "\\constfunc or \\membersection.",
1302 m_filename
.c_str(), m_line
);
1309 if ( foundCommand
== MemberSect
) {
1310 // what follows has the form <classname>::<funcname>
1311 const char *startClass
= current
;
1312 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1313 wxLogWarning("file %s(%d): '::' expected after "
1314 "\\membersection.", m_filename
.c_str(), m_line
);
1317 classname
= wxString(startClass
, current
- startClass
);
1318 TeXUnfilter(&classname
);
1324 // extract the return type
1325 const char *startRetType
= current
;
1327 if ( !SkipUntil(¤t
, '}') ) {
1328 wxLogWarning("file %s(%d): '}' expected after return type",
1329 m_filename
.c_str(), m_line
);
1334 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1335 TeXUnfilter(&returnType
);
1338 if ( !SkipSpaceUntil(¤t
, '{') ) {
1339 wxLogWarning("file %s(%d): '{' expected after return type",
1340 m_filename
.c_str(), m_line
);
1346 const char *funcEnd
= current
;
1347 if ( !SkipUntil(&funcEnd
, '}') ) {
1348 wxLogWarning("file %s(%d): '}' expected after function name",
1349 m_filename
.c_str(), m_line
);
1354 wxString funcName
= wxString(current
, funcEnd
- current
);
1355 current
= funcEnd
+ 1;
1357 // trim spaces from both sides
1358 funcName
.Trim(FALSE
);
1359 funcName
.Trim(TRUE
);
1361 // special cases: '$...$' may be used for LaTeX inline math, remove the
1363 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1365 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1366 if ( *p
!= '$' && !isspace(*p
) )
1373 // \destruct{foo} is really ~foo
1374 if ( funcName
[0u] == '\\' ) {
1375 size_t len
= strlen("\\destruct{");
1376 if ( funcName(0, len
) != "\\destruct{" ) {
1377 wxLogWarning("file %s(%d): \\destruct expected",
1378 m_filename
.c_str(), m_line
);
1383 funcName
.erase(0, len
);
1384 funcName
.Prepend('~');
1386 if ( !SkipSpaceUntil(¤t
, '}') ) {
1387 wxLogWarning("file %s(%d): '}' expected after destructor",
1388 m_filename
.c_str(), m_line
);
1393 funcEnd
++; // there is an extra '}' to count
1396 TeXUnfilter(&funcName
);
1399 current
= funcEnd
+ 1; // skip '}'
1400 if ( !SkipSpaceUntil(¤t
, '{') ||
1401 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1402 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1403 m_filename
.c_str(), m_line
);
1408 wxArrayString paramNames
, paramTypes
, paramValues
;
1410 bool isVararg
= FALSE
;
1412 current
++; // skip '\\'
1413 lenMatch
= TryMatch(current
, "void");
1415 lenMatch
= TryMatch(current
, "param");
1416 while ( lenMatch
&& (current
- buf
< len
) ) {
1417 current
+= lenMatch
;
1419 // now come {paramtype}{paramname}
1420 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1421 if ( !!paramType
) {
1422 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1423 if ( !!paramText
) {
1424 // the param declaration may contain default value
1425 wxString paramName
= paramText
.BeforeFirst('='),
1426 paramValue
= paramText
.AfterFirst('=');
1428 // sanitize all strings
1429 TeXUnfilter(¶mValue
);
1430 TeXUnfilter(¶mName
);
1431 TeXUnfilter(¶mType
);
1433 paramValues
.Add(paramValue
);
1434 paramNames
.Add(paramName
);
1435 paramTypes
.Add(paramType
);
1440 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1441 if ( paramText
== "..." ) {
1445 wxLogWarning("Parameters of '%s::%s' are in "
1447 classname
.c_str(), funcName
.c_str());
1452 current
= SkipSpaces(current
);
1453 if ( *current
== ',' || *current
== '}' ) {
1454 current
= SkipSpaces(++current
);
1456 lenMatch
= TryMatch(current
, "\\param");
1459 wxLogWarning("file %s(%d): ',' or '}' expected after "
1460 "'\\param'", m_filename
.c_str(), m_line
);
1466 // if we got here there was no '\\void', so must have some params
1467 if ( paramNames
.IsEmpty() ) {
1468 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1469 m_filename
.c_str(), m_line
);
1475 // verbose diagnostic output
1477 size_t param
, paramCount
= paramNames
.GetCount();
1478 for ( param
= 0; param
< paramCount
; param
++ ) {
1483 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1486 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1487 m_filename
.c_str(), m_line
,
1492 foundCommand
== ConstFunc
? " const" : "");
1494 // store the info about the just found function
1495 ArrayMethodInfo
*methods
;
1496 int index
= m_classes
.Index(classname
);
1497 if ( index
== wxNOT_FOUND
) {
1498 m_classes
.Add(classname
);
1500 methods
= new ArrayMethodInfo
;
1501 m_methods
.Add(methods
);
1504 methods
= m_methods
[(size_t)index
];
1507 ArrayParamInfo params
;
1508 for ( param
= 0; param
< paramCount
; param
++ ) {
1509 params
.Add(new ParamInfo(paramTypes
[param
],
1511 paramValues
[param
]));
1514 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1515 if ( foundCommand
== ConstFunc
)
1516 method
->SetFlag(MethodInfo::Const
);
1518 method
->SetFlag(MethodInfo::Vararg
);
1520 methods
->Add(method
);
1525 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1526 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1531 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1533 typedef MMemberListT::const_iterator MemberIndex
;
1535 bool foundDiff
= FALSE
;
1537 // flag telling us whether the given class was found at all in the header
1538 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1539 bool *classExists
= new bool[countClassesInDocs
];
1540 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1541 classExists
[nClass
] = FALSE
;
1544 // ctxTop is normally an spFile
1545 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1547 const MMemberListT
& classes
= ctxTop
->GetMembers();
1548 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1549 spContext
*ctx
= *i
;
1550 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1551 // TODO process also global functions, macros, ...
1555 spClass
*ctxClass
= (spClass
*)ctx
;
1556 const wxString
& nameClass
= ctxClass
->mName
;
1557 int index
= m_classes
.Index(nameClass
);
1558 if ( index
== wxNOT_FOUND
) {
1559 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1562 wxLogError("Class '%s' is not documented at all.",
1566 // it makes no sense to check for its functions
1570 classExists
[index
] = TRUE
;
1573 // array of method descriptions for this class
1574 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1575 size_t nMethod
, countMethods
= methods
.GetCount();
1577 // flags telling if we already processed given function
1578 bool *methodExists
= new bool[countMethods
];
1579 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1580 methodExists
[nMethod
] = FALSE
;
1583 wxArrayString aOverloadedMethods
;
1585 const MMemberListT
& functions
= ctxClass
->GetMembers();
1586 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1588 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1591 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1592 const wxString
& nameMethod
= ctxMethod
->mName
;
1594 // find all functions with the same name
1595 wxArrayInt aMethodsWithSameName
;
1596 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1597 if ( methods
[nMethod
]->GetName() == nameMethod
)
1598 aMethodsWithSameName
.Add(nMethod
);
1601 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1602 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1605 wxLogError("'%s::%s' is not documented.",
1607 nameMethod
.c_str());
1610 // don't check params
1613 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1614 index
= (size_t)aMethodsWithSameName
[0u];
1615 methodExists
[index
] = TRUE
;
1617 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1620 if ( !ctxMethod
->IsPublic() ) {
1621 wxLogWarning("'%s::%s' is documented but not public.",
1623 nameMethod
.c_str());
1626 // check that the flags match
1627 const MethodInfo
& method
= *(methods
[index
]);
1629 bool isVirtual
= ctxMethod
->mIsVirtual
;
1630 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1631 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1635 isVirtual
? "not " : "");
1638 bool isConst
= ctxMethod
->mIsConstant
;
1639 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1640 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1644 isConst
? "not " : "");
1647 // check that the params match
1648 const MMemberListT
& params
= ctxMethod
->GetMembers();
1650 if ( params
.size() != method
.GetParamCount() ) {
1651 wxLogError("Incorrect number of parameters for '%s::%s' "
1652 "in the docs: should be %d instead of %d.",
1655 params
.size(), method
.GetParamCount());
1659 for ( MemberIndex k
= params
.begin();
1664 // what else can a function have?
1665 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1667 spParameter
*ctxParam
= (spParameter
*)ctx
;
1668 const ParamInfo
& param
= method
.GetParam(nParam
);
1669 if ( m_checkParamNames
&&
1670 (param
.GetName() != ctxParam
->mName
) ) {
1673 wxLogError("Parameter #%d of '%s::%s' should be "
1674 "'%s' and not '%s'.",
1678 ctxParam
->mName
.c_str(),
1679 param
.GetName().c_str());
1684 if ( param
.GetType() != ctxParam
->mType
) {
1687 wxLogError("Type of parameter '%s' of '%s::%s' "
1688 "should be '%s' and not '%s'.",
1689 ctxParam
->mName
.c_str(),
1692 ctxParam
->mType
.c_str(),
1693 param
.GetType().GetName().c_str());
1698 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1699 wxLogWarning("Default value of parameter '%s' of "
1700 "'%s::%s' should be '%s' and not "
1702 ctxParam
->mName
.c_str(),
1705 ctxParam
->mInitVal
.c_str(),
1706 param
.GetDefValue().c_str());
1712 // TODO OVER add real support for overloaded methods
1714 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1717 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1718 // mark all methods with this name as existing
1719 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1720 if ( methods
[nMethod
]->GetName() == nameMethod
)
1721 methodExists
[nMethod
] = TRUE
;
1724 aOverloadedMethods
.Add(nameMethod
);
1726 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1727 "stupid to find the right match - skipping "
1728 "the param and flags checks.",
1730 nameMethod
.c_str());
1732 //else: warning already given
1736 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1737 if ( !methodExists
[nMethod
] ) {
1738 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1739 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1742 wxLogError("'%s::%s' is documented but doesn't exist.",
1744 nameMethod
.c_str());
1749 delete [] methodExists
;
1752 // check that all classes we found in the docs really exist
1753 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1754 if ( !classExists
[nClass
] ) {
1757 wxLogError("Class '%s' is documented but doesn't exist.",
1758 m_classes
[nClass
].c_str());
1762 delete [] classExists
;
1767 DocManager::~DocManager()
1769 WX_CLEAR_ARRAY(m_methods
);
1772 // ---------------------------------------------------------------------------
1773 // IgnoreNamesHandler implementation
1774 // ---------------------------------------------------------------------------
1776 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1777 IgnoreListEntry
*second
)
1779 // first compare the classes
1780 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1782 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1787 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1789 wxFile
file(filename
, wxFile::read
);
1790 if ( !file
.IsOpened() )
1793 off_t len
= file
.Length();
1794 if ( len
== wxInvalidOffset
)
1797 char *buf
= new char[len
+ 1];
1800 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1807 for ( const char *current
= buf
; ; current
++ ) {
1809 // skip DOS line separator
1810 if ( *current
== '\r' )
1814 if ( *current
== '\n' || *current
== '\0' ) {
1815 if ( line
[0u] != '#' ) {
1816 if ( line
.Find(':') != wxNOT_FOUND
) {
1817 wxString classname
= line
.BeforeFirst(':'),
1818 funcname
= line
.AfterLast(':');
1819 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1823 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1828 if ( *current
== '\0' )
1843 // -----------------------------------------------------------------------------
1844 // global function implementation
1845 // -----------------------------------------------------------------------------
1847 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1849 wxString
label(classname
);
1850 if ( funcname
&& funcname
[0] == '\\' ) {
1851 // we may have some special TeX macro - so far only \destruct exists,
1852 // but may be later others will be added
1853 static const char *macros
[] = { "destruct" };
1854 static const char *replacement
[] = { "dtor" };
1857 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1858 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1864 if ( n
== WXSIZEOF(macros
) ) {
1865 wxLogWarning("unknown function name '%s' - leaving as is.",
1869 funcname
= replacement
[n
];
1881 static wxString
MakeHelpref(const char *argument
)
1884 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1889 static void TeXUnfilter(wxString
* str
)
1891 // FIXME may be done much more quickly
1895 str
->Replace("\\&", "&");
1896 str
->Replace("\\_", "_");
1899 static void TeXFilter(wxString
* str
)
1901 // FIXME may be done much more quickly
1902 str
->Replace("&", "\\&");
1903 str
->Replace("_", "\\_");
1906 static wxString
GetAllComments(const spContext
& ctx
)
1909 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1910 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1911 i
!= commentsList
.end();
1913 wxString comment
= (*i
)->GetText();
1915 // don't take comments like "// ----------" &c
1916 comment
.Trim(FALSE
);
1918 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1921 comments
<< comment
;
1927 static const char *GetCurrentTime(const char *timeFormat
)
1929 static char s_timeBuffer
[128];
1934 ptmNow
= localtime(&timeNow
);
1936 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1938 return s_timeBuffer
;
1943 Revision 1.12 2000/10/09 13:53:33 juliansmart
1944 Doc corrections; added HelpGen project files
1946 Revision 1.11 2000/07/15 19:50:42 cvsuser
1949 Revision 1.10.2.2 2000/03/27 15:33:10 VZ
1950 don't trasnform output dir name to lower case
1952 Revision 1.10 2000/03/11 10:05:23 VS
1953 now compiles with wxBase
1955 Revision 1.9 2000/01/16 13:25:21 VS
1956 compilation fixes (gcc)
1958 Revision 1.8 1999/09/13 14:29:39 JS
1960 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1961 into src for simplicity; added VC++ 5 project file
1963 Revision 1.7 1999/02/21 22:32:32 VZ
1964 1. more C++ parser fixes - now it almost parses wx/string.h
1965 a) #if/#ifdef/#else (very) limited support
1966 b) param type fix - now indirection chars are correctly handled
1967 c) class/struct/union distinction
1968 d) public/private fixes
1969 e) Dump() function added - very useful for debugging
1971 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1972 by default, and this option switches it on)
1974 Revision 1.6 1999/02/20 23:00:26 VZ
1975 1. new 'diff' mode which seems to work
1976 2. output files are not overwritten in 'dmup' mode
1977 3. fixes for better handling of const functions and operators
1978 ----------------------------
1980 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1981 1. Parser improvements
1982 a) const and virtual methods are parsed correctly (not static yet)
1983 b) "const" which is part of the return type is not swallowed
1985 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1986 "//---------" kind comments discarded now.
1987 ----------------------------
1989 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1991 some tweaks to HelpGen
1992 ----------------------------
1994 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1996 HelpGen starting to compile with VC++
1997 ----------------------------
1999 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
2001 supports typedefs, generates "See also:" and adds "virtual " for virtual
2003 ----------------------------
2005 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
2007 HelpGen is a prototype of the tool for automatic generation of the .tex files
2008 for wxWindows documentation from C++ headers
2011 /* vi: set tw=80 et ts=4 sw=4: */