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>
58 #include <wx/dynarray.h>
64 // C++ parsing classes
71 // argh, Windows defines this
76 // ----------------------------------------------------------------------------
78 // ----------------------------------------------------------------------------
80 #define VERSION_STRING "$Revision$"
82 // -----------------------------------------------------------------------------
84 // -----------------------------------------------------------------------------
86 class HelpGenApp
: public wxApp
91 // don't let wxWin parse our cmd line, we do it ourselves
92 virtual bool OnInit() { return TRUE
; }
97 IMPLEMENT_APP(HelpGenApp
);
99 // -----------------------------------------------------------------------------
101 // -----------------------------------------------------------------------------
103 // return the label for the given function name (i.e. argument of \label)
104 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
106 // return the whole \helpref{arg}{arg_label} string
107 static wxString
MakeHelpref(const char *argument
);
109 // [un]quote special TeX characters (in place)
110 static void TeXFilter(wxString
* str
);
111 static void TeXUnfilter(wxString
* str
); // also trims spaces
113 // get all comments associated with this context
114 static wxString
GetAllComments(const spContext
& ctx
);
116 // get the string with current time (returns pointer to static buffer)
117 // timeFormat is used for the call of strftime(3)
118 static const char *GetCurrentTime(const char *timeFormat
);
120 // -----------------------------------------------------------------------------
122 // -----------------------------------------------------------------------------
124 // add a function which sanitazes the string before writing it to the file
125 class wxTeXFile
: public wxFile
130 // write a string to file verbatim (should only be used for the strings
131 // inside verbatim environment)
132 bool WriteVerbatim(const wxString
& s
)
134 return wxFile::Write(s
);
137 // write a string quoting TeX specials in it
138 bool WriteTeX(const wxString
& s
)
143 return wxFile::Write(t
);
147 wxTeXFile(const wxTeXFile
&);
148 wxTeXFile
& operator=(const wxTeXFile
&);
151 // helper class which manages the classes and function names to ignore for
152 // the documentation purposes (used by both HelpGenVisitor and DocManager)
153 class IgnoreNamesHandler
156 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries
) { }
157 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore
); }
159 // load file with classes/functions to ignore (add them to the names we
161 bool AddNamesFromFile(const wxString
& filename
);
163 // return TRUE if we ignore this function
164 bool IgnoreMethod(const wxString
& classname
,
165 const wxString
& funcname
) const
167 if ( IgnoreClass(classname
) )
170 IgnoreListEntry
ignore(classname
, funcname
);
172 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
175 // return TRUE if we ignore this class entirely
176 bool IgnoreClass(const wxString
& classname
) const
178 IgnoreListEntry
ignore(classname
, "");
180 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
184 struct IgnoreListEntry
186 IgnoreListEntry(const wxString
& classname
,
187 const wxString
& funcname
)
188 : m_classname(classname
), m_funcname(funcname
)
192 wxString m_classname
;
193 wxString m_funcname
; // if empty, ignore class entirely
196 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
197 IgnoreListEntry
*second
);
199 // for efficiency, let's sort it
200 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
202 ArrayNamesToIgnore m_ignore
;
205 IgnoreNamesHandler(const IgnoreNamesHandler
&);
206 IgnoreNamesHandler
& operator=(const IgnoreNamesHandler
&);
209 // visitor implementation which writes all collected data to a .tex file
210 class HelpGenVisitor
: public spVisitor
214 HelpGenVisitor(const wxString
& directoryOut
, bool overwrite
);
216 virtual void VisitFile( spFile
& fl
);
217 virtual void VisitClass( spClass
& cl
);
218 virtual void VisitEnumeration( spEnumeration
& en
);
219 virtual void VisitTypeDef( spTypeDef
& td
);
220 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
221 virtual void VisitAttribute( spAttribute
& attr
);
222 virtual void VisitOperation( spOperation
& op
);
223 virtual void VisitParameter( spParameter
& param
);
227 // get our `ignore' object
228 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
230 // shut up g++ warning (ain't it stupid?)
231 virtual ~HelpGenVisitor() { }
234 // (re)initialize the state
237 // insert documentation for enums/typedefs coming immediately before the
238 // class declaration into the class documentation
239 void InsertTypedefDocs();
240 void InsertEnumDocs();
242 // write the headers for corresponding sections (only once)
243 void InsertDataStructuresHeader();
244 void InsertMethodsHeader();
246 // terminate the function documentation if it was started
247 void CloseFunction();
249 wxString m_directoryOut
, // directory for the output
250 m_fileHeader
; // name of the .h file we parse
251 bool m_overwrite
; // overwrite existing files?
252 wxTeXFile m_file
; // file we're writing to now
255 bool m_inClass
, // TRUE after file successfully opened
256 m_inTypesSection
, // enums & typedefs go there
257 m_inMethodSection
, // functions go here
258 m_isFirstParam
, // first parameter of current function?
259 m_inFunction
; // we're parsing a function declaration
261 // holders for "saved" documentation
262 wxString m_textStoredTypedefs
,
263 m_textStoredFunctionComment
;
265 // for enums we have to use an array as we can't intermix the normal text
266 // and the text inside verbatim environment
267 wxArrayString m_storedEnums
,
270 // headers included by this file
271 wxArrayString m_headers
;
273 // ignore handler: tells us which classes to ignore for doc generation
275 IgnoreNamesHandler m_ignoreNames
;
278 HelpGenVisitor(const HelpGenVisitor
&);
279 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
282 // documentation manager - a class which parses TeX files and remembers the
283 // functions documented in them and can later compare them with all functions
284 // found under ctxTop by C++ parser
288 DocManager(bool checkParamNames
);
291 // returns FALSE on failure
292 bool ParseTeXFile(const wxString
& filename
);
294 // returns FALSE if there were any differences
295 bool DumpDifferences(spContext
*ctxTop
) const;
297 // get our `ignore' object
298 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
304 // returns the length of 'match' if the string 'str' starts with it or 0
306 static size_t TryMatch(const char *str
, const char *match
);
308 // skip spaces: returns pointer to first non space character (also
309 // updates the value of m_line)
310 const char *SkipSpaces(const char *p
)
312 while ( isspace(*p
) ) {
320 // skips characters until the next 'c' in '*pp' unless it ends before in
321 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
322 // returned and pp points to 'c'
323 bool SkipUntil(const char **pp
, char c
);
325 // the same as SkipUntil() but only spaces are skipped: on first non space
326 // character different from 'c' the function stops and returns FALSE
327 bool SkipSpaceUntil(const char **pp
, char c
);
329 // extract the string between {} and modify '*pp' to point at the
330 // character immediately after the closing '}'. The returned string is empty
332 wxString
ExtractStringBetweenBraces(const char **pp
);
334 // the current file and line while we're in ParseTeXFile (for error
339 // functions and classes to ignore during diff
340 // -------------------------------------------
342 IgnoreNamesHandler m_ignoreNames
;
344 // information about all functions documented in the TeX file(s)
345 // -------------------------------------------------------------
347 // info about a type: for now stored as text string, but must be parsed
348 // further later (to know that "char *" == "char []" - TODO)
352 TypeInfo(const wxString
& type
) : m_type(type
) { }
354 bool operator==(const wxString
& type
) const { return m_type
== type
; }
355 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
357 const wxString
& GetName() const { return m_type
; }
363 // info abotu a function parameter
367 ParamInfo(const wxString
& type
,
368 const wxString
& name
,
369 const wxString
& value
)
370 : m_type(type
), m_name(name
), m_value(value
)
374 const TypeInfo
& GetType() const { return m_type
; }
375 const wxString
& GetName() const { return m_name
; }
376 const wxString
& GetDefValue() const { return m_value
; }
379 TypeInfo m_type
; // type of parameter
380 wxString m_name
; // name
381 wxString m_value
; // default value
384 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
386 // info about a function
399 MethodInfo(const wxString
& type
,
400 const wxString
& name
,
401 const ArrayParamInfo
& params
)
402 : m_typeRet(type
), m_name(name
), m_params(params
)
407 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
409 const TypeInfo
& GetType() const { return m_typeRet
; }
410 const wxString
& GetName() const { return m_name
; }
411 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
412 size_t GetParamCount() const { return m_params
.GetCount(); }
414 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
416 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
419 TypeInfo m_typeRet
; // return type
421 int m_flags
; // bit mask of the value from the enum above
423 ArrayParamInfo m_params
;
426 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
427 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
429 // first array contains the names of all classes we found, the second has a
430 // pointer to the array of methods of the given class at the same index as
431 // the class name appears in m_classes
432 wxArrayString m_classes
;
433 ArrayMethodInfos m_methods
;
435 // are we checking parameter names?
436 bool m_checkParamNames
;
439 DocManager(const DocManager
&);
440 DocManager
& operator=(const DocManager
&);
443 // -----------------------------------------------------------------------------
445 // -----------------------------------------------------------------------------
447 // =============================================================================
449 // =============================================================================
451 // this function never returns
454 wxString prog
= wxTheApp
->argv
[0];
455 wxString basename
= prog
.AfterLast('/');
458 basename
= prog
.AfterLast('\\');
464 "usage: %s [global options] <mode> [mode options] <files...>\n"
466 " where global options are:\n"
469 " -H give this usage message\n"
470 " -V print the version info\n"
471 " -i file file with classes/function to ignore\n"
473 " where mode is one of: dump, diff\n"
475 " dump means generate .tex files for TeX2RTF converter from specified\n"
476 " headers files, mode options are:\n"
477 " -f overwrite existing files\n"
478 " -o outdir directory for generated files\n"
480 " diff means compare the set of methods documented .tex file with the\n"
481 " methods declared in the header:\n"
482 " %s diff <file.h> <files.tex...>.\n"
483 " mode specific options are:\n"
484 " -p do check parameter names (not done by default)\n"
485 "\n", basename
.c_str(), basename
.c_str());
490 int HelpGenApp::OnRun()
503 wxArrayString filesH
, filesTeX
;
504 wxString directoryOut
, // directory for 'dmup' output
505 ignoreFile
; // file with classes/functions to ignore
506 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
507 paramNames
= FALSE
; // check param names during 'diff'?
509 for ( int current
= 1; current
< argc
; current
++ ) {
510 // all options have one letter
511 if ( argv
[current
][0] == '-' ) {
512 if ( argv
[current
][2] == '\0' ) {
513 switch ( argv
[current
][1] ) {
516 wxLog::GetActiveTarget()->SetVerbose();
521 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
531 wxLogMessage("HelpGen version %s\n"
532 "(c) 1999-2001 Vadim Zeitlin\n",
538 if ( current
>= argc
) {
539 wxLogError("-i option requires an argument.");
544 ignoreFile
= argv
[current
];
548 if ( mode
!= Mode_Diff
) {
549 wxLogError("-p is only valid with diff.");
558 if ( mode
!= Mode_Dump
) {
559 wxLogError("-f is only valid with dump.");
568 if ( mode
!= Mode_Dump
) {
569 wxLogError("-o is only valid with dump.");
575 if ( current
>= argc
) {
576 wxLogError("-o option requires an argument.");
581 directoryOut
= argv
[current
];
582 if ( !!directoryOut
) {
583 // terminate with a '/' if it doesn't have it
584 switch ( directoryOut
.Last() ) {
595 //else: it's empty, do nothing
600 wxLogError("unknown option '%s'", argv
[current
]);
605 wxLogError("only one letter options are allowed, not '%s'.",
609 // only get here after a break from switch or from else branch of if
614 if ( mode
== Mode_None
) {
615 if ( strcmp(argv
[current
], "diff") == 0 )
617 else if ( strcmp(argv
[current
], "dump") == 0 )
620 wxLogError("unknown mode '%s'.", argv
[current
]);
626 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
627 filesH
.Add(argv
[current
]);
630 // 2nd files and further are TeX files in diff mode
631 wxASSERT( mode
== Mode_Diff
);
633 filesTeX
.Add(argv
[current
]);
639 // create a parser object and a visitor derivation
640 CJSourceParser parser
;
641 HelpGenVisitor
visitor(directoryOut
, overwrite
);
642 if ( !!ignoreFile
&& mode
== Mode_Dump
)
643 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
645 spContext
*ctxTop
= NULL
;
647 // parse all header files
648 size_t nFiles
= filesH
.GetCount();
649 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
650 wxString header
= filesH
[n
];
651 ctxTop
= parser
.ParseFile(header
);
653 wxLogWarning("Header file '%s' couldn't be processed.",
656 else if ( mode
== Mode_Dump
) {
657 ((spFile
*)ctxTop
)->mFileName
= header
;
658 visitor
.VisitAll(*ctxTop
);
665 #endif // __WXDEBUG__
668 // parse all TeX files
669 if ( mode
== Mode_Diff
) {
671 wxLogError("Can't complete diff.");
677 DocManager
docman(paramNames
);
679 size_t nFiles
= filesTeX
.GetCount();
680 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
681 wxString file
= filesTeX
[n
];
682 if ( !docman
.ParseTeXFile(file
) ) {
683 wxLogWarning("TeX file '%s' couldn't be processed.",
689 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
691 docman
.DumpDifferences(ctxTop
);
697 // -----------------------------------------------------------------------------
698 // HelpGenVisitor implementation
699 // -----------------------------------------------------------------------------
701 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
703 : m_directoryOut(directoryOut
)
705 m_overwrite
= overwrite
;
710 void HelpGenVisitor::Reset()
715 m_inMethodSection
= FALSE
;
717 m_textStoredTypedefs
=
718 m_textStoredFunctionComment
= "";
720 m_storedEnums
.Empty();
721 m_storedEnumsVerb
.Empty();
725 void HelpGenVisitor::InsertTypedefDocs()
727 m_file
.WriteTeX(m_textStoredTypedefs
);
728 m_textStoredTypedefs
.Empty();
731 void HelpGenVisitor::InsertEnumDocs()
733 size_t count
= m_storedEnums
.GetCount();
734 for ( size_t n
= 0; n
< count
; n
++ )
736 m_file
.WriteTeX(m_storedEnums
[n
]);
737 m_file
.WriteVerbatim(m_storedEnumsVerb
[n
] + '\n');
740 m_storedEnums
.Empty();
741 m_storedEnumsVerb
.Empty();
744 void HelpGenVisitor::InsertDataStructuresHeader()
746 if ( !m_inTypesSection
) {
747 m_inTypesSection
= TRUE
;
749 m_file
.Write("\\wxheading{Data structures}\n\n");
753 void HelpGenVisitor::InsertMethodsHeader()
755 if ( !m_inMethodSection
) {
756 m_inMethodSection
= TRUE
;
758 m_file
.Write( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
762 void HelpGenVisitor::CloseFunction()
764 if ( m_inFunction
) {
765 m_inFunction
= FALSE
;
768 if ( m_isFirstParam
) {
770 totalText
<< "\\void";
773 totalText
<< "}\n\n";
774 m_file
.Write(totalText
);
776 if ( !m_textStoredFunctionComment
.IsEmpty() )
777 m_file
.WriteTeX(m_textStoredFunctionComment
+ '\n');
781 void HelpGenVisitor::EndVisit()
785 m_fileHeader
.Empty();
787 wxLogVerbose("%s: finished generating for the current file.",
788 GetCurrentTime("%H:%M:%S"));
791 void HelpGenVisitor::VisitFile( spFile
& file
)
793 m_fileHeader
= file
.mFileName
;
794 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
795 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
798 void HelpGenVisitor::VisitClass( spClass
& cl
)
800 m_inClass
= FALSE
; // will be left FALSE on error
802 wxString name
= cl
.GetName();
804 if ( m_ignoreNames
.IgnoreClass(name
) ) {
805 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
810 // the file name is built from the class name by removing the leading "wx"
811 // if any and converting it to the lower case
813 if ( name(0, 2) == "wx" ) {
814 filename
<< name
.c_str() + 2;
820 filename
.MakeLower();
822 filename
.Prepend(m_directoryOut
);
824 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
825 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
831 m_inClass
= m_file
.Open(filename
, wxFile::write
);
833 wxLogError("Can't generate documentation for the class '%s'.",
840 m_inTypesSection
= FALSE
;
842 wxLogInfo("Created new file '%s' for class '%s'.",
843 filename
.c_str(), name
.c_str());
845 // write out the header
848 "%% automatically generated by HelpGen %s from\n"
853 "\\section{\\class{%s}}\\label{%s}\n\n",
855 m_fileHeader
.c_str(),
856 GetCurrentTime("%d/%b/%y %H:%M:%S"),
858 wxString(name
).MakeLower().c_str());
860 m_file
.Write(header
);
862 // the entire text we're writing to file
865 // if the header includes other headers they must be related to it... try to
866 // automatically generate the "See also" clause
867 if ( !m_headers
.IsEmpty() ) {
868 // correspondence between wxWindows headers and class names
869 static const char *headers
[] = {
878 // NULL here means not to insert anything in "See also" for the
879 // corresponding header
880 static const char *classes
[] = {
889 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
890 "arrays must be in sync!" );
892 wxArrayInt interestingClasses
;
894 size_t count
= m_headers
.Count(), index
;
895 for ( size_t n
= 0; n
< count
; n
++ ) {
896 wxString baseHeaderName
= m_headers
[n
].Before('.');
897 if ( baseHeaderName(0, 3) != "wx/" )
900 baseHeaderName
.erase(0, 3);
901 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
902 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
906 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
907 // interesting header
908 interestingClasses
.Add(index
);
912 if ( !interestingClasses
.IsEmpty() ) {
913 // do generate "See also" clause
914 totalText
<< "\\wxheading{See also:}\n\n";
916 count
= interestingClasses
.Count();
917 for ( index
= 0; index
< count
; index
++ ) {
921 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
928 // the comment before the class generally explains what is it for so put it
929 // in place of the class description
930 if ( cl
.HasComments() ) {
931 wxString comment
= GetAllComments(cl
);
933 totalText
<< '\n' << comment
<< '\n';
936 // derived from section
937 wxString derived
= "\\wxheading{Derived from}\n\n";
939 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
940 if ( baseClasses
.size() == 0 ) {
941 derived
<< "No base class";
945 for ( StrListT::const_iterator i
= baseClasses
.begin();
946 i
!= baseClasses
.end();
949 // separate from the previous one
956 wxString baseclass
= *i
;
957 derived
<< "\\helpref{" << baseclass
<< "}";
958 derived
<< "{" << baseclass
.MakeLower() << "}";
961 totalText
<< derived
<< "\n\n";
963 // write all this to file
964 m_file
.WriteTeX(totalText
);
966 // if there were any enums/typedefs before, insert their documentation now
967 InsertDataStructuresHeader();
972 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
976 if ( m_inMethodSection
) {
977 // FIXME that's a bug, but tell the user aboit it nevertheless... we
978 // should be smart enough to process even the enums which come after the
980 wxLogWarning("enum '%s' ignored, please put it before the class "
981 "methods.", en
.GetName().c_str());
985 // simply copy the enum text in the docs
986 wxString enumeration
= GetAllComments(en
),
989 enumerationVerb
<< "\\begin{verbatim}\n"
991 << "\n\\end{verbatim}\n";
993 // remember for later use if we're not inside a class yet
995 m_storedEnums
.Add(enumeration
);
996 m_storedEnumsVerb
.Add(enumerationVerb
);
999 // write the header for this section if not done yet
1000 InsertDataStructuresHeader();
1002 m_file
.WriteTeX(enumeration
);
1003 m_file
.WriteVerbatim(enumerationVerb
);
1008 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
1012 if ( m_inMethodSection
) {
1013 // FIXME that's a bug, but tell the user aboit it nevertheless...
1014 wxLogWarning("typedef '%s' ignored, please put it before the class "
1015 "methods.", td
.GetName().c_str());
1019 wxString typedefdoc
;
1020 typedefdoc
<< "{\\small \\begin{verbatim}\n"
1021 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
1022 << "\n\\end{verbatim}}\n"
1023 << GetAllComments(td
);
1025 // remember for later use if we're not inside a class yet
1027 if ( !m_textStoredTypedefs
.IsEmpty() ) {
1028 m_textStoredTypedefs
<< '\n';
1031 m_textStoredTypedefs
<< typedefdoc
;
1034 // write the header for this section if not done yet
1035 InsertDataStructuresHeader();
1038 m_file
.WriteTeX(typedefdoc
);
1042 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1044 switch ( pd
.GetStatementType() ) {
1045 case SP_PREP_DEF_INCLUDE_FILE
:
1046 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1049 case SP_PREP_DEF_DEFINE_SYMBOL
:
1050 // TODO decide if it's a constant and document it if it is
1055 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1059 // only document the public member variables
1060 if ( !m_inClass
|| !attr
.IsPublic() )
1063 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1066 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1071 // we don't generate docs right now - either we ignore this class
1072 // entirely or we couldn't open the file
1076 if ( !op
.IsInClass() ) {
1077 // TODO document global functions
1078 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1083 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1084 // FIXME should we document protected functions?
1088 wxString funcname
= op
.GetName(),
1089 classname
= op
.GetClass().GetName();
1090 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1091 wxLogVerbose("Skipping ignored '%s::%s'.",
1092 classname
.c_str(), funcname
.c_str());
1097 InsertMethodsHeader();
1101 m_isFirstParam
= TRUE
;
1103 m_textStoredFunctionComment
= GetAllComments(op
);
1105 // start function documentation
1108 // check for the special case of dtor
1110 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1111 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1115 totalText
.Printf("\n"
1116 "\\membersection{%s::%s}\\label{%s}\n"
1118 "\\%sfunc{%s%s}{%s}{",
1119 classname
.c_str(), funcname
.c_str(),
1120 MakeLabel(classname
, funcname
).c_str(),
1121 op
.mIsConstant
? "const" : "",
1122 op
.mIsVirtual
? "virtual " : "",
1123 op
.mRetType
.c_str(),
1126 m_file
.WriteTeX(totalText
);
1129 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1131 if ( !m_inFunction
)
1135 if ( m_isFirstParam
) {
1136 m_isFirstParam
= FALSE
;
1142 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1143 wxString defvalue
= param
.mInitVal
;
1144 if ( !defvalue
.IsEmpty() ) {
1145 totalText
<< " = " << defvalue
;
1150 m_file
.WriteTeX(totalText
);
1153 // ---------------------------------------------------------------------------
1155 // ---------------------------------------------------------------------------
1157 DocManager::DocManager(bool checkParamNames
)
1159 m_checkParamNames
= checkParamNames
;
1162 size_t DocManager::TryMatch(const char *str
, const char *match
)
1164 size_t lenMatch
= 0;
1165 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1168 if ( match
[lenMatch
] == '\0' )
1175 bool DocManager::SkipUntil(const char **pp
, char c
)
1177 const char *p
= *pp
;
1193 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1195 const char *p
= *pp
;
1197 if ( !isspace(*p
) || *p
== '\0' )
1211 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1215 if ( !SkipSpaceUntil(pp
, '{') ) {
1216 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1217 m_filename
.c_str(), m_line
);
1221 const char *startParam
= ++*pp
; // skip '{'
1223 if ( !SkipUntil(pp
, '}') ) {
1224 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1225 m_filename
.c_str(), m_line
);
1228 result
= wxString(startParam
, (*pp
)++ - startParam
);
1235 bool DocManager::ParseTeXFile(const wxString
& filename
)
1237 m_filename
= filename
;
1239 wxFile
file(m_filename
, wxFile::read
);
1240 if ( !file
.IsOpened() )
1243 off_t len
= file
.Length();
1244 if ( len
== wxInvalidOffset
)
1247 char *buf
= new char[len
+ 1];
1250 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1256 // reinit everything
1259 wxLogVerbose("%s: starting to parse doc file '%s'.",
1260 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1262 // the name of the class from the last "\membersection" command: we assume
1263 // that the following "\func" or "\constfunc" always documents a method of
1264 // this class (and it should always be like that in wxWindows documentation)
1267 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1268 // FIXME parsing is awfully inefficient
1270 if ( *current
== '%' ) {
1271 // comment, skip until the end of line
1273 SkipUntil(¤t
, '\n');
1278 // all the command we're interested in start with '\\'
1279 while ( *current
!= '\\' && *current
!= '\0' ) {
1280 if ( *current
++ == '\n' )
1284 if ( *current
== '\0' ) {
1285 // no more TeX commands left
1289 current
++; // skip '\\'
1297 } foundCommand
= Nothing
;
1299 size_t lenMatch
= TryMatch(current
, "func");
1301 foundCommand
= Func
;
1304 lenMatch
= TryMatch(current
, "constfunc");
1306 foundCommand
= ConstFunc
;
1308 lenMatch
= TryMatch(current
, "membersection");
1311 foundCommand
= MemberSect
;
1315 if ( foundCommand
== Nothing
)
1318 current
+= lenMatch
;
1320 if ( !SkipSpaceUntil(¤t
, '{') ) {
1321 wxLogWarning("file %s(%d): '{' expected after \\func, "
1322 "\\constfunc or \\membersection.",
1323 m_filename
.c_str(), m_line
);
1330 if ( foundCommand
== MemberSect
) {
1331 // what follows has the form <classname>::<funcname>
1332 const char *startClass
= current
;
1333 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1334 wxLogWarning("file %s(%d): '::' expected after "
1335 "\\membersection.", m_filename
.c_str(), m_line
);
1338 classname
= wxString(startClass
, current
- startClass
);
1339 TeXUnfilter(&classname
);
1345 // extract the return type
1346 const char *startRetType
= current
;
1348 if ( !SkipUntil(¤t
, '}') ) {
1349 wxLogWarning("file %s(%d): '}' expected after return type",
1350 m_filename
.c_str(), m_line
);
1355 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1356 TeXUnfilter(&returnType
);
1359 if ( !SkipSpaceUntil(¤t
, '{') ) {
1360 wxLogWarning("file %s(%d): '{' expected after return type",
1361 m_filename
.c_str(), m_line
);
1367 const char *funcEnd
= current
;
1368 if ( !SkipUntil(&funcEnd
, '}') ) {
1369 wxLogWarning("file %s(%d): '}' expected after function name",
1370 m_filename
.c_str(), m_line
);
1375 wxString funcName
= wxString(current
, funcEnd
- current
);
1376 current
= funcEnd
+ 1;
1378 // trim spaces from both sides
1379 funcName
.Trim(FALSE
);
1380 funcName
.Trim(TRUE
);
1382 // special cases: '$...$' may be used for LaTeX inline math, remove the
1384 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1386 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1387 if ( *p
!= '$' && !isspace(*p
) )
1394 // \destruct{foo} is really ~foo
1395 if ( funcName
[0u] == '\\' ) {
1396 size_t len
= strlen("\\destruct{");
1397 if ( funcName(0, len
) != "\\destruct{" ) {
1398 wxLogWarning("file %s(%d): \\destruct expected",
1399 m_filename
.c_str(), m_line
);
1404 funcName
.erase(0, len
);
1405 funcName
.Prepend('~');
1407 if ( !SkipSpaceUntil(¤t
, '}') ) {
1408 wxLogWarning("file %s(%d): '}' expected after destructor",
1409 m_filename
.c_str(), m_line
);
1414 funcEnd
++; // there is an extra '}' to count
1417 TeXUnfilter(&funcName
);
1420 current
= funcEnd
+ 1; // skip '}'
1421 if ( !SkipSpaceUntil(¤t
, '{') ||
1422 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1423 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1424 m_filename
.c_str(), m_line
);
1429 wxArrayString paramNames
, paramTypes
, paramValues
;
1431 bool isVararg
= FALSE
;
1433 current
++; // skip '\\'
1434 lenMatch
= TryMatch(current
, "void");
1436 lenMatch
= TryMatch(current
, "param");
1437 while ( lenMatch
&& (current
- buf
< len
) ) {
1438 current
+= lenMatch
;
1440 // now come {paramtype}{paramname}
1441 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1442 if ( !!paramType
) {
1443 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1444 if ( !!paramText
) {
1445 // the param declaration may contain default value
1446 wxString paramName
= paramText
.BeforeFirst('='),
1447 paramValue
= paramText
.AfterFirst('=');
1449 // sanitize all strings
1450 TeXUnfilter(¶mValue
);
1451 TeXUnfilter(¶mName
);
1452 TeXUnfilter(¶mType
);
1454 paramValues
.Add(paramValue
);
1455 paramNames
.Add(paramName
);
1456 paramTypes
.Add(paramType
);
1461 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1462 if ( paramText
== "..." ) {
1466 wxLogWarning("Parameters of '%s::%s' are in "
1468 classname
.c_str(), funcName
.c_str());
1473 current
= SkipSpaces(current
);
1474 if ( *current
== ',' || *current
== '}' ) {
1475 current
= SkipSpaces(++current
);
1477 lenMatch
= TryMatch(current
, "\\param");
1480 wxLogWarning("file %s(%d): ',' or '}' expected after "
1481 "'\\param'", m_filename
.c_str(), m_line
);
1487 // if we got here there was no '\\void', so must have some params
1488 if ( paramNames
.IsEmpty() ) {
1489 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1490 m_filename
.c_str(), m_line
);
1496 // verbose diagnostic output
1498 size_t param
, paramCount
= paramNames
.GetCount();
1499 for ( param
= 0; param
< paramCount
; param
++ ) {
1504 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1507 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1508 m_filename
.c_str(), m_line
,
1513 foundCommand
== ConstFunc
? " const" : "");
1515 // store the info about the just found function
1516 ArrayMethodInfo
*methods
;
1517 int index
= m_classes
.Index(classname
);
1518 if ( index
== wxNOT_FOUND
) {
1519 m_classes
.Add(classname
);
1521 methods
= new ArrayMethodInfo
;
1522 m_methods
.Add(methods
);
1525 methods
= m_methods
[(size_t)index
];
1528 ArrayParamInfo params
;
1529 for ( param
= 0; param
< paramCount
; param
++ ) {
1530 params
.Add(new ParamInfo(paramTypes
[param
],
1532 paramValues
[param
]));
1535 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1536 if ( foundCommand
== ConstFunc
)
1537 method
->SetFlag(MethodInfo::Const
);
1539 method
->SetFlag(MethodInfo::Vararg
);
1541 methods
->Add(method
);
1546 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1547 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1552 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1554 typedef MMemberListT::const_iterator MemberIndex
;
1556 bool foundDiff
= FALSE
;
1558 // flag telling us whether the given class was found at all in the header
1559 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1560 bool *classExists
= new bool[countClassesInDocs
];
1561 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1562 classExists
[nClass
] = FALSE
;
1565 // ctxTop is normally an spFile
1566 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1568 const MMemberListT
& classes
= ctxTop
->GetMembers();
1569 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1570 spContext
*ctx
= *i
;
1571 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1572 // TODO process also global functions, macros, ...
1576 spClass
*ctxClass
= (spClass
*)ctx
;
1577 const wxString
& nameClass
= ctxClass
->mName
;
1578 int index
= m_classes
.Index(nameClass
);
1579 if ( index
== wxNOT_FOUND
) {
1580 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1583 wxLogError("Class '%s' is not documented at all.",
1587 // it makes no sense to check for its functions
1591 classExists
[index
] = TRUE
;
1594 // array of method descriptions for this class
1595 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1596 size_t nMethod
, countMethods
= methods
.GetCount();
1598 // flags telling if we already processed given function
1599 bool *methodExists
= new bool[countMethods
];
1600 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1601 methodExists
[nMethod
] = FALSE
;
1604 wxArrayString aOverloadedMethods
;
1606 const MMemberListT
& functions
= ctxClass
->GetMembers();
1607 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1609 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1612 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1613 const wxString
& nameMethod
= ctxMethod
->mName
;
1615 // find all functions with the same name
1616 wxArrayInt aMethodsWithSameName
;
1617 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1618 if ( methods
[nMethod
]->GetName() == nameMethod
)
1619 aMethodsWithSameName
.Add(nMethod
);
1622 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1623 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1626 wxLogError("'%s::%s' is not documented.",
1628 nameMethod
.c_str());
1631 // don't check params
1634 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1635 index
= (size_t)aMethodsWithSameName
[0u];
1636 methodExists
[index
] = TRUE
;
1638 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1641 if ( !ctxMethod
->IsPublic() ) {
1642 wxLogWarning("'%s::%s' is documented but not public.",
1644 nameMethod
.c_str());
1647 // check that the flags match
1648 const MethodInfo
& method
= *(methods
[index
]);
1650 bool isVirtual
= ctxMethod
->mIsVirtual
;
1651 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1652 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1656 isVirtual
? "not " : "");
1659 bool isConst
= ctxMethod
->mIsConstant
;
1660 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1661 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1665 isConst
? "not " : "");
1668 // check that the params match
1669 const MMemberListT
& params
= ctxMethod
->GetMembers();
1671 if ( params
.size() != method
.GetParamCount() ) {
1672 wxLogError("Incorrect number of parameters for '%s::%s' "
1673 "in the docs: should be %d instead of %d.",
1676 params
.size(), method
.GetParamCount());
1680 for ( MemberIndex k
= params
.begin();
1685 // what else can a function have?
1686 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1688 spParameter
*ctxParam
= (spParameter
*)ctx
;
1689 const ParamInfo
& param
= method
.GetParam(nParam
);
1690 if ( m_checkParamNames
&&
1691 (param
.GetName() != ctxParam
->mName
) ) {
1694 wxLogError("Parameter #%d of '%s::%s' should be "
1695 "'%s' and not '%s'.",
1699 ctxParam
->mName
.c_str(),
1700 param
.GetName().c_str());
1705 if ( param
.GetType() != ctxParam
->mType
) {
1708 wxLogError("Type of parameter '%s' of '%s::%s' "
1709 "should be '%s' and not '%s'.",
1710 ctxParam
->mName
.c_str(),
1713 ctxParam
->mType
.c_str(),
1714 param
.GetType().GetName().c_str());
1719 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1720 wxLogWarning("Default value of parameter '%s' of "
1721 "'%s::%s' should be '%s' and not "
1723 ctxParam
->mName
.c_str(),
1726 ctxParam
->mInitVal
.c_str(),
1727 param
.GetDefValue().c_str());
1733 // TODO OVER add real support for overloaded methods
1735 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1738 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1739 // mark all methods with this name as existing
1740 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1741 if ( methods
[nMethod
]->GetName() == nameMethod
)
1742 methodExists
[nMethod
] = TRUE
;
1745 aOverloadedMethods
.Add(nameMethod
);
1747 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1748 "stupid to find the right match - skipping "
1749 "the param and flags checks.",
1751 nameMethod
.c_str());
1753 //else: warning already given
1757 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1758 if ( !methodExists
[nMethod
] ) {
1759 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1760 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1763 wxLogError("'%s::%s' is documented but doesn't exist.",
1765 nameMethod
.c_str());
1770 delete [] methodExists
;
1773 // check that all classes we found in the docs really exist
1774 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1775 if ( !classExists
[nClass
] ) {
1778 wxLogError("Class '%s' is documented but doesn't exist.",
1779 m_classes
[nClass
].c_str());
1783 delete [] classExists
;
1788 DocManager::~DocManager()
1790 WX_CLEAR_ARRAY(m_methods
);
1793 // ---------------------------------------------------------------------------
1794 // IgnoreNamesHandler implementation
1795 // ---------------------------------------------------------------------------
1797 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1798 IgnoreListEntry
*second
)
1800 // first compare the classes
1801 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1803 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1808 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1810 wxFile
file(filename
, wxFile::read
);
1811 if ( !file
.IsOpened() )
1814 off_t len
= file
.Length();
1815 if ( len
== wxInvalidOffset
)
1818 char *buf
= new char[len
+ 1];
1821 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1828 for ( const char *current
= buf
; ; current
++ ) {
1830 // skip DOS line separator
1831 if ( *current
== '\r' )
1835 if ( *current
== '\n' || *current
== '\0' ) {
1836 if ( line
[0u] != '#' ) {
1837 if ( line
.Find(':') != wxNOT_FOUND
) {
1838 wxString classname
= line
.BeforeFirst(':'),
1839 funcname
= line
.AfterLast(':');
1840 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1844 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1849 if ( *current
== '\0' )
1864 // -----------------------------------------------------------------------------
1865 // global function implementation
1866 // -----------------------------------------------------------------------------
1868 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1870 wxString
label(classname
);
1871 if ( funcname
&& funcname
[0] == '\\' ) {
1872 // we may have some special TeX macro - so far only \destruct exists,
1873 // but may be later others will be added
1874 static const char *macros
[] = { "destruct" };
1875 static const char *replacement
[] = { "dtor" };
1878 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1879 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1885 if ( n
== WXSIZEOF(macros
) ) {
1886 wxLogWarning("unknown function name '%s' - leaving as is.",
1890 funcname
= replacement
[n
];
1902 static wxString
MakeHelpref(const char *argument
)
1905 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1910 static void TeXFilter(wxString
* str
)
1912 // TeX special which can be quoted (don't include backslash nor braces as
1914 static wxRegEx
reNonSpecialSpecials("[#$%&_]"),
1918 reNonSpecialSpecials
.ReplaceAll(str
, "\\\\\\0");
1920 // can't quote these ones as they produce accents when preceded by
1921 // backslash, so put them inside verb
1922 reAccents
.ReplaceAll(str
, "\\\\verb|\\0|");
1925 static void TeXUnfilter(wxString
* str
)
1927 // FIXME may be done much more quickly
1932 static wxRegEx
reNonSpecialSpecials("\\\\([#$%&_{}])"),
1933 reAccents("\\\\verb|([~^])|");
1935 reNonSpecialSpecials
.ReplaceAll(str
, "\\1");
1936 reAccents
.ReplaceAll(str
, "\\1");
1939 static wxString
GetAllComments(const spContext
& ctx
)
1942 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1943 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1944 i
!= commentsList
.end();
1946 wxString comment
= (*i
)->GetText();
1948 // don't take comments like "// ----------" &c
1949 comment
.Trim(FALSE
);
1951 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1954 comments
<< comment
;
1960 static const char *GetCurrentTime(const char *timeFormat
)
1962 static char s_timeBuffer
[128];
1967 ptmNow
= localtime(&timeNow
);
1969 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1971 return s_timeBuffer
;
1976 Revision 1.13 2001/07/19 13:44:57 VZ
1977 1. compilation fixes
1978 2. don't quote special characters inside verbatim environment
1980 Revision 1.12 2000/10/09 13:53:33 juliansmart
1982 Doc corrections; added HelpGen project files
1984 Revision 1.11 2000/07/15 19:50:42 cvsuser
1987 Revision 1.10.2.2 2000/03/27 15:33:10 VZ
1988 don't trasnform output dir name to lower case
1990 Revision 1.10 2000/03/11 10:05:23 VS
1991 now compiles with wxBase
1993 Revision 1.9 2000/01/16 13:25:21 VS
1994 compilation fixes (gcc)
1996 Revision 1.8 1999/09/13 14:29:39 JS
1998 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1999 into src for simplicity; added VC++ 5 project file
2001 Revision 1.7 1999/02/21 22:32:32 VZ
2002 1. more C++ parser fixes - now it almost parses wx/string.h
2003 a) #if/#ifdef/#else (very) limited support
2004 b) param type fix - now indirection chars are correctly handled
2005 c) class/struct/union distinction
2006 d) public/private fixes
2007 e) Dump() function added - very useful for debugging
2009 2. option to ignore parameter names during 'diff' (in fact, they're ignored
2010 by default, and this option switches it on)
2012 Revision 1.6 1999/02/20 23:00:26 VZ
2013 1. new 'diff' mode which seems to work
2014 2. output files are not overwritten in 'dmup' mode
2015 3. fixes for better handling of const functions and operators
2016 ----------------------------
2018 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
2019 1. Parser improvements
2020 a) const and virtual methods are parsed correctly (not static yet)
2021 b) "const" which is part of the return type is not swallowed
2023 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
2024 "//---------" kind comments discarded now.
2025 ----------------------------
2027 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
2029 some tweaks to HelpGen
2030 ----------------------------
2032 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
2034 HelpGen starting to compile with VC++
2035 ----------------------------
2037 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
2039 supports typedefs, generates "See also:" and adds "virtual " for virtual
2041 ----------------------------
2043 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
2045 HelpGen is a prototype of the tool for automatic generation of the .tex files
2046 for wxWindows documentation from C++ headers
2049 /* vi: set tw=80 et ts=4 sw=4: */