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>
62 // C++ parsing classes
69 // -----------------------------------------------------------------------------
71 // -----------------------------------------------------------------------------
73 // just a copy of argv
74 static char **g_argv
= NULL
;
76 class HelpGenApp
: public wxApp
84 IMPLEMENT_APP(HelpGenApp
);
86 // -----------------------------------------------------------------------------
88 // -----------------------------------------------------------------------------
90 // return the label for the given function name (i.e. argument of \label)
91 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
93 // return the whole \helpref{arg}{arg_label} string
94 static wxString
MakeHelpref(const char *argument
);
96 // [un]quote special TeX characters (in place)
97 static void TeXFilter(wxString
* str
);
98 static void TeXUnfilter(wxString
* str
); // also trims spaces
100 // get all comments associated with this context
101 static wxString
GetAllComments(const spContext
& ctx
);
103 // get the string with current time (returns pointer to static buffer)
104 // timeFormat is used for the call of strftime(3)
105 #ifdef GetCurrentTime
106 #undef GetCurrentTime
109 static const char *GetCurrentTime(const char *timeFormat
);
111 // -----------------------------------------------------------------------------
113 // -----------------------------------------------------------------------------
115 // add a function which sanitazes the string before writing it to the file
116 class wxTeXFile
: public wxFile
121 bool WriteTeX(const wxString
& s
)
126 return wxFile::Write(t
);
130 wxTeXFile(const wxTeXFile
&);
131 wxTeXFile
& operator=(const wxTeXFile
&);
134 // helper class which manages the classes and function names to ignore for
135 // the documentation purposes (used by both HelpGenVisitor and DocManager)
136 class IgnoreNamesHandler
139 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries
) { }
140 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore
); }
142 // load file with classes/functions to ignore (add them to the names we
144 bool AddNamesFromFile(const wxString
& filename
);
146 // return TRUE if we ignore this function
147 bool IgnoreMethod(const wxString
& classname
,
148 const wxString
& funcname
) const
150 if ( IgnoreClass(classname
) )
153 IgnoreListEntry
ignore(classname
, funcname
);
155 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
158 // return TRUE if we ignore this class entirely
159 bool IgnoreClass(const wxString
& classname
) const
161 IgnoreListEntry
ignore(classname
, "");
163 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
167 struct IgnoreListEntry
169 IgnoreListEntry(const wxString
& classname
,
170 const wxString
& funcname
)
171 : m_classname(classname
), m_funcname(funcname
)
175 wxString m_classname
;
176 wxString m_funcname
; // if empty, ignore class entirely
179 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
180 IgnoreListEntry
*second
);
182 // for efficiency, let's sort it
183 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
185 ArrayNamesToIgnore m_ignore
;
188 IgnoreNamesHandler(const IgnoreNamesHandler
&);
189 IgnoreNamesHandler
& operator=(const IgnoreNamesHandler
&);
192 // visitor implementation which writes all collected data to a .tex file
193 class HelpGenVisitor
: public spVisitor
197 HelpGenVisitor(const wxString
& directoryOut
, bool overwrite
);
199 virtual void VisitFile( spFile
& fl
);
200 virtual void VisitClass( spClass
& cl
);
201 virtual void VisitEnumeration( spEnumeration
& en
);
202 virtual void VisitTypeDef( spTypeDef
& td
);
203 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
204 virtual void VisitAttribute( spAttribute
& attr
);
205 virtual void VisitOperation( spOperation
& op
);
206 virtual void VisitParameter( spParameter
& param
);
210 // get our `ignore' object
211 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
213 // shut up g++ warning (ain't it stupid?)
214 virtual ~HelpGenVisitor() { }
217 // (re)initialize the state
220 // insert documentation for enums/typedefs coming immediately before the
221 // class declaration into the class documentation
222 void InsertTypedefDocs();
223 void InsertEnumDocs();
225 // write the headers for corresponding sections (only once)
226 void InsertDataStructuresHeader();
227 void InsertMethodsHeader();
229 // terminate the function documentation if it was started
230 void CloseFunction();
232 wxString m_directoryOut
, // directory for the output
233 m_fileHeader
; // name of the .h file we parse
234 bool m_overwrite
; // overwrite existing files?
235 wxTeXFile m_file
; // file we're writing to now
238 bool m_inClass
, // TRUE after file successfully opened
239 m_inTypesSection
, // enums & typedefs go there
240 m_inMethodSection
, // functions go here
241 m_isFirstParam
, // first parameter of current function?
242 m_inFunction
; // we're parsing a function declaration
244 // holders for "saved" documentation
245 wxString m_textStoredEnums
,
246 m_textStoredTypedefs
,
247 m_textStoredFunctionComment
;
249 // headers included by this file
250 wxArrayString m_headers
;
252 // ignore handler: tells us which classes to ignore for doc generation
254 IgnoreNamesHandler m_ignoreNames
;
257 HelpGenVisitor(const HelpGenVisitor
&);
258 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
261 // documentation manager - a class which parses TeX files and remembers the
262 // functions documented in them and can later compare them with all functions
263 // found under ctxTop by C++ parser
267 DocManager(bool checkParamNames
);
270 // returns FALSE on failure
271 bool ParseTeXFile(const wxString
& filename
);
273 // returns FALSE if there were any differences
274 bool DumpDifferences(spContext
*ctxTop
) const;
276 // get our `ignore' object
277 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
283 // returns the length of 'match' if the string 'str' starts with it or 0
285 static size_t TryMatch(const char *str
, const char *match
);
287 // skip spaces: returns pointer to first non space character (also
288 // updates the value of m_line)
289 const char *SkipSpaces(const char *p
)
291 while ( isspace(*p
) ) {
299 // skips characters until the next 'c' in '*pp' unless it ends before in
300 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
301 // returned and pp points to 'c'
302 bool SkipUntil(const char **pp
, char c
);
304 // the same as SkipUntil() but only spaces are skipped: on first non space
305 // character different from 'c' the function stops and returns FALSE
306 bool SkipSpaceUntil(const char **pp
, char c
);
308 // extract the string between {} and modify '*pp' to point at the
309 // character immediately after the closing '}'. The returned string is empty
311 wxString
ExtractStringBetweenBraces(const char **pp
);
313 // the current file and line while we're in ParseTeXFile (for error
318 // functions and classes to ignore during diff
319 // -------------------------------------------
321 IgnoreNamesHandler m_ignoreNames
;
323 // information about all functions documented in the TeX file(s)
324 // -------------------------------------------------------------
326 // info about a type: for now stored as text string, but must be parsed
327 // further later (to know that "char *" == "char []" - TODO)
331 TypeInfo(const wxString
& type
) : m_type(type
) { }
333 bool operator==(const wxString
& type
) const { return m_type
== type
; }
334 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
336 const wxString
& GetName() const { return m_type
; }
342 // info abotu a function parameter
346 ParamInfo(const wxString
& type
,
347 const wxString
& name
,
348 const wxString
& value
)
349 : m_type(type
), m_name(name
), m_value(value
)
353 const TypeInfo
& GetType() const { return m_type
; }
354 const wxString
& GetName() const { return m_name
; }
355 const wxString
& GetDefValue() const { return m_value
; }
358 TypeInfo m_type
; // type of parameter
359 wxString m_name
; // name
360 wxString m_value
; // default value
363 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
365 // info about a function
378 MethodInfo(const wxString
& type
,
379 const wxString
& name
,
380 const ArrayParamInfo
& params
)
381 : m_typeRet(type
), m_name(name
), m_params(params
)
386 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
388 const TypeInfo
& GetType() const { return m_typeRet
; }
389 const wxString
& GetName() const { return m_name
; }
390 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
391 size_t GetParamCount() const { return m_params
.GetCount(); }
393 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
395 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
398 TypeInfo m_typeRet
; // return type
400 int m_flags
; // bit mask of the value from the enum above
402 ArrayParamInfo m_params
;
405 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
406 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
408 // first array contains the names of all classes we found, the second has a
409 // pointer to the array of methods of the given class at the same index as
410 // the class name appears in m_classes
411 wxArrayString m_classes
;
412 ArrayMethodInfos m_methods
;
414 // are we checking parameter names?
415 bool m_checkParamNames
;
418 DocManager(const DocManager
&);
419 DocManager
& operator=(const DocManager
&);
422 // -----------------------------------------------------------------------------
424 // -----------------------------------------------------------------------------
426 // =============================================================================
428 // =============================================================================
430 // this function never returns
433 wxString prog
= g_argv
[0];
434 wxString basename
= prog
.BeforeLast('/');
437 basename
= prog
.BeforeLast('\\');
443 "usage: %s [global options] <mode> [mode options] <files...>\n"
445 " where global options are:\n"
448 " -H give this usage message\n"
449 " -V print the version info\n"
450 " -i file file with classes/function to ignore\n"
452 " where mode is one of: dump, diff\n"
454 " dump means generate .tex files for TeX2RTF converter from specified\n"
455 " headers files, mode options are:\n"
456 " -f overwrite existing files\n"
457 " -o outdir directory for generated files\n"
459 " diff means compare the set of methods documented .tex file with the\n"
460 " methods declared in the header:\n"
461 " %s diff <file.h> <files.tex...>.\n"
462 " mode specific options are:\n"
463 " -p do check parameter names (not done by default)\n"
464 "\n", basename
.c_str(), basename
.c_str());
470 int main(int argc, char **argv)
474 bool HelpGenApp::OnInit()
489 wxArrayString filesH
, filesTeX
;
490 wxString directoryOut
, // directory for 'dmup' output
491 ignoreFile
; // file with classes/functions to ignore
492 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
493 paramNames
= FALSE
; // check param names during 'diff'?
495 for ( int current
= 1; current
< argc
; current
++ ) {
496 // all options have one letter
497 if ( argv
[current
][0] == '-' ) {
498 if ( argv
[current
][2] == '\0' ) {
499 switch ( argv
[current
][1] ) {
502 wxLog::GetActiveTarget()->SetVerbose();
507 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
516 if ( current
>= argc
) {
517 wxLogError("-i option requires an argument.");
522 ignoreFile
= argv
[current
];
526 if ( mode
!= Mode_Diff
) {
527 wxLogError("-p is only valid with diff.");
536 if ( mode
!= Mode_Dump
) {
537 wxLogError("-f is only valid with dump.");
546 if ( mode
!= Mode_Dump
) {
547 wxLogError("-o is only valid with dump.");
553 if ( current
>= argc
) {
554 wxLogError("-o option requires an argument.");
559 directoryOut
= argv
[current
];
560 if ( !!directoryOut
) {
561 // terminate with a '/' if it doesn't have it
562 switch ( directoryOut
.Last() ) {
573 //else: it's empty, do nothing
578 wxLogError("unknown option '%s'", argv
[current
]);
583 wxLogError("only one letter options are allowed, not '%s'.",
587 // only get here after a break from switch or from else branch of if
592 if ( mode
== Mode_None
) {
593 if ( strcmp(argv
[current
], "diff") == 0 )
595 else if ( strcmp(argv
[current
], "dump") == 0 )
598 wxLogError("unknown mode '%s'.", argv
[current
]);
604 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
605 filesH
.Add(argv
[current
]);
608 // 2nd files and further are TeX files in diff mode
609 wxASSERT( mode
== Mode_Diff
);
611 filesTeX
.Add(argv
[current
]);
617 // create a parser object and a visitor derivation
618 CJSourceParser parser
;
619 HelpGenVisitor
visitor(directoryOut
, overwrite
);
620 if ( !!ignoreFile
&& mode
== Mode_Dump
)
621 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
623 spContext
*ctxTop
= NULL
;
625 // parse all header files
626 size_t nFiles
= filesH
.GetCount();
627 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
628 wxString header
= filesH
[n
];
629 ctxTop
= parser
.ParseFile(header
);
631 wxLogWarning("Header file '%s' couldn't be processed.",
634 else if ( mode
== Mode_Dump
) {
635 ((spFile
*)ctxTop
)->mFileName
= header
;
636 visitor
.VisitAll(*ctxTop
);
643 #endif // __WXDEBUG__
646 // parse all TeX files
647 if ( mode
== Mode_Diff
) {
649 wxLogError("Can't complete diff.");
655 DocManager
docman(paramNames
);
657 size_t nFiles
= filesTeX
.GetCount();
658 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
659 wxString file
= filesTeX
[n
];
660 if ( !docman
.ParseTeXFile(file
) ) {
661 wxLogWarning("TeX file '%s' couldn't be processed.",
667 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
669 docman
.DumpDifferences(ctxTop
);
675 // -----------------------------------------------------------------------------
676 // HelpGenVisitor implementation
677 // -----------------------------------------------------------------------------
679 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
681 : m_directoryOut(directoryOut
)
683 m_overwrite
= overwrite
;
688 void HelpGenVisitor::Reset()
693 m_inMethodSection
= FALSE
;
695 m_textStoredTypedefs
=
697 m_textStoredFunctionComment
= "";
701 void HelpGenVisitor::InsertTypedefDocs()
703 m_file
.WriteTeX(m_textStoredTypedefs
);
704 m_textStoredTypedefs
.Empty();
707 void HelpGenVisitor::InsertEnumDocs()
709 m_file
.WriteTeX(m_textStoredEnums
);
710 m_textStoredEnums
.Empty();
713 void HelpGenVisitor::InsertDataStructuresHeader()
715 if ( !m_inTypesSection
) {
716 m_inTypesSection
= TRUE
;
718 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
722 void HelpGenVisitor::InsertMethodsHeader()
724 if ( !m_inMethodSection
) {
725 m_inMethodSection
= TRUE
;
727 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
731 void HelpGenVisitor::CloseFunction()
733 if ( m_inFunction
) {
734 m_inFunction
= FALSE
;
737 if ( m_isFirstParam
) {
739 totalText
<< "\\void";
742 totalText
<< "}\n\n";
744 if ( !m_textStoredFunctionComment
.IsEmpty() )
745 totalText
<< m_textStoredFunctionComment
<< '\n';
747 m_file
.WriteTeX(totalText
);
751 void HelpGenVisitor::EndVisit()
755 m_fileHeader
.Empty();
757 wxLogVerbose("%s: finished generating for the current file.",
758 GetCurrentTime("%H:%M:%S"));
761 void HelpGenVisitor::VisitFile( spFile
& file
)
763 m_fileHeader
= file
.mFileName
;
764 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
765 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
768 void HelpGenVisitor::VisitClass( spClass
& cl
)
770 m_inClass
= FALSE
; // will be left FALSE on error
772 wxString name
= cl
.GetName();
774 if ( m_ignoreNames
.IgnoreClass(name
) ) {
775 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
780 // the file name is built from the class name by removing the leading "wx"
781 // if any and converting it to the lower case
782 wxString filename
= m_directoryOut
;
783 if ( name(0, 2) == "wx" ) {
784 filename
<< name
.c_str() + 2;
790 filename
.MakeLower();
793 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
794 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
800 m_inClass
= m_file
.Open(filename
, wxFile::write
);
802 wxLogError("Can't generate documentation for the class '%s'.",
809 m_inTypesSection
= FALSE
;
811 wxLogInfo("Created new file '%s' for class '%s'.",
812 filename
.c_str(), name
.c_str());
814 // the entire text we're writing to file
817 // write out the header
821 "%% automatically generated by HelpGen from\n"
826 "\\section{\\class{%s}}\\label{%s}\n",
827 m_fileHeader
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
828 name
.c_str(), wxString(name
).MakeLower().c_str());
830 totalText
<< header
<< '\n';
833 // if the header includes other headers they must be related to it... try to
834 // automatically generate the "See also" clause
835 if ( !m_headers
.IsEmpty() ) {
836 // correspondence between wxWindows headers and class names
837 static const char *headers
[] = {
846 // NULL here means not to insert anything in "See also" for the
847 // corresponding header
848 static const char *classes
[] = {
857 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
858 "arrays must be in sync!" );
860 wxArrayInt interestingClasses
;
862 size_t count
= m_headers
.Count(), index
;
863 for ( size_t n
= 0; n
< count
; n
++ ) {
864 wxString baseHeaderName
= m_headers
[n
].Before('.');
865 if ( baseHeaderName(0, 3) != "wx/" )
868 baseHeaderName
.erase(0, 3);
869 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
870 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
874 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
875 // interesting header
876 interestingClasses
.Add(index
);
880 if ( !interestingClasses
.IsEmpty() ) {
881 // do generate "See also" clause
882 totalText
<< "\\wxheading{See also:}\n\n";
884 count
= interestingClasses
.Count();
885 for ( index
= 0; index
< count
; index
++ ) {
889 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
896 // the comment before the class generally explains what is it for so put it
897 // in place of the class description
898 if ( cl
.HasComments() ) {
899 wxString comment
= GetAllComments(cl
);
901 totalText
<< '\n' << comment
<< '\n';
904 // derived from section
905 wxString derived
= "\\wxheading{Derived from}\n\n";
907 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
908 if ( baseClasses
.size() == 0 ) {
909 derived
<< "No base class";
913 for ( StrListT::const_iterator i
= baseClasses
.begin();
914 i
!= baseClasses
.end();
917 // separate from the previous one
924 wxString baseclass
= *i
;
925 derived
<< "\\helpref{" << baseclass
<< "}";
926 derived
<< "{" << baseclass
.MakeLower() << "}";
929 totalText
<< derived
<< "\n\n";
931 // write all this to file
932 m_file
.WriteTeX(totalText
);
934 // if there were any enums/typedefs before, insert their documentation now
935 InsertDataStructuresHeader();
940 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
944 if ( m_inMethodSection
) {
945 // FIXME that's a bug, but tell the user aboit it nevertheless... we
946 // should be smart enough to process even the enums which come after the
948 wxLogWarning("enum '%s' ignored, please put it before the class "
949 "methods.", en
.GetName().c_str());
953 // simply copy the enum text in the docs
954 wxString enumeration
= GetAllComments(en
);
955 enumeration
<< "{\\small \\begin{verbatim}\n"
957 << "\n\\end{verbatim}}\n";
959 // remember for later use if we're not inside a class yet
961 if ( !m_textStoredEnums
.IsEmpty() ) {
962 m_textStoredEnums
<< '\n';
965 m_textStoredEnums
<< enumeration
;
968 // write the header for this section if not done yet
969 InsertDataStructuresHeader();
972 m_file
.WriteTeX(enumeration
);
976 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
980 if ( m_inMethodSection
) {
981 // FIXME that's a bug, but tell the user aboit it nevertheless...
982 wxLogWarning("typedef '%s' ignored, please put it before the class "
983 "methods.", td
.GetName().c_str());
988 typedefdoc
<< "{\\small \\begin{verbatim}\n"
989 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
990 << "\n\\end{verbatim}}\n"
991 << GetAllComments(td
);
993 // remember for later use if we're not inside a class yet
995 if ( !m_textStoredTypedefs
.IsEmpty() ) {
996 m_textStoredTypedefs
<< '\n';
999 m_textStoredTypedefs
<< typedefdoc
;
1002 // write the header for this section if not done yet
1003 InsertDataStructuresHeader();
1006 m_file
.WriteTeX(typedefdoc
);
1010 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1012 switch ( pd
.GetStatementType() ) {
1013 case SP_PREP_DEF_INCLUDE_FILE
:
1014 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1017 case SP_PREP_DEF_DEFINE_SYMBOL
:
1018 // TODO decide if it's a constant and document it if it is
1023 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1027 // only document the public member variables
1028 if ( !m_inClass
|| !attr
.IsPublic() )
1031 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1034 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1039 // we don't generate docs right now - either we ignore this class
1040 // entirely or we couldn't open the file
1044 if ( !op
.IsInClass() ) {
1045 // TODO document global functions
1046 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1051 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1052 // FIXME should we document protected functions?
1056 wxString funcname
= op
.GetName(),
1057 classname
= op
.GetClass().GetName();
1058 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1059 wxLogVerbose("Skipping ignored '%s::%s'.",
1060 classname
.c_str(), funcname
.c_str());
1065 InsertMethodsHeader();
1069 m_isFirstParam
= TRUE
;
1071 m_textStoredFunctionComment
= GetAllComments(op
);
1073 // start function documentation
1076 // check for the special case of dtor
1078 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1079 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1083 totalText
.Printf("\n"
1084 "\\membersection{%s::%s}\\label{%s}\n"
1086 "\\%sfunc{%s%s}{%s}{",
1087 classname
.c_str(), funcname
.c_str(),
1088 MakeLabel(classname
, funcname
).c_str(),
1089 op
.mIsConstant
? "const" : "",
1090 op
.mIsVirtual
? "virtual " : "",
1091 op
.mRetType
.c_str(),
1094 m_file
.WriteTeX(totalText
);
1097 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1099 if ( !m_inFunction
)
1103 if ( m_isFirstParam
) {
1104 m_isFirstParam
= FALSE
;
1110 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1111 wxString defvalue
= param
.mInitVal
;
1112 if ( !defvalue
.IsEmpty() ) {
1113 totalText
<< " = " << defvalue
;
1118 m_file
.WriteTeX(totalText
);
1121 // ---------------------------------------------------------------------------
1123 // ---------------------------------------------------------------------------
1125 DocManager::DocManager(bool checkParamNames
)
1127 m_checkParamNames
= checkParamNames
;
1130 size_t DocManager::TryMatch(const char *str
, const char *match
)
1132 size_t lenMatch
= 0;
1133 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1136 if ( match
[lenMatch
] == '\0' )
1143 bool DocManager::SkipUntil(const char **pp
, char c
)
1145 const char *p
= *pp
;
1161 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1163 const char *p
= *pp
;
1165 if ( !isspace(*p
) || *p
== '\0' )
1179 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1183 if ( !SkipSpaceUntil(pp
, '{') ) {
1184 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1185 m_filename
.c_str(), m_line
);
1189 const char *startParam
= ++*pp
; // skip '{'
1191 if ( !SkipUntil(pp
, '}') ) {
1192 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1193 m_filename
.c_str(), m_line
);
1196 result
= wxString(startParam
, (*pp
)++ - startParam
);
1203 bool DocManager::ParseTeXFile(const wxString
& filename
)
1205 m_filename
= filename
;
1207 wxFile
file(m_filename
, wxFile::read
);
1208 if ( !file
.IsOpened() )
1211 off_t len
= file
.Length();
1212 if ( len
== wxInvalidOffset
)
1215 char *buf
= new char[len
+ 1];
1218 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1224 // reinit everything
1227 wxLogVerbose("%s: starting to parse doc file '%s'.",
1228 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1230 // the name of the class from the last "\membersection" command: we assume
1231 // that the following "\func" or "\constfunc" always documents a method of
1232 // this class (and it should always be like that in wxWindows documentation)
1235 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1236 // FIXME parsing is awfully inefficient
1238 if ( *current
== '%' ) {
1239 // comment, skip until the end of line
1241 SkipUntil(¤t
, '\n');
1246 // all the command we're interested in start with '\\'
1247 while ( *current
!= '\\' && *current
!= '\0' ) {
1248 if ( *current
++ == '\n' )
1252 if ( *current
== '\0' ) {
1253 // no more TeX commands left
1257 current
++; // skip '\\'
1265 } foundCommand
= Nothing
;
1267 size_t lenMatch
= TryMatch(current
, "func");
1269 foundCommand
= Func
;
1272 lenMatch
= TryMatch(current
, "constfunc");
1274 foundCommand
= ConstFunc
;
1276 lenMatch
= TryMatch(current
, "membersection");
1279 foundCommand
= MemberSect
;
1283 if ( foundCommand
== Nothing
)
1286 current
+= lenMatch
;
1288 if ( !SkipSpaceUntil(¤t
, '{') ) {
1289 wxLogWarning("file %s(%d): '{' expected after \\func, "
1290 "\\constfunc or \\membersection.",
1291 m_filename
.c_str(), m_line
);
1298 if ( foundCommand
== MemberSect
) {
1299 // what follows has the form <classname>::<funcname>
1300 const char *startClass
= current
;
1301 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1302 wxLogWarning("file %s(%d): '::' expected after "
1303 "\\membersection.", m_filename
.c_str(), m_line
);
1306 classname
= wxString(startClass
, current
- startClass
);
1307 TeXUnfilter(&classname
);
1313 // extract the return type
1314 const char *startRetType
= current
;
1316 if ( !SkipUntil(¤t
, '}') ) {
1317 wxLogWarning("file %s(%d): '}' expected after return type",
1318 m_filename
.c_str(), m_line
);
1323 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1324 TeXUnfilter(&returnType
);
1327 if ( !SkipSpaceUntil(¤t
, '{') ) {
1328 wxLogWarning("file %s(%d): '{' expected after return type",
1329 m_filename
.c_str(), m_line
);
1335 const char *funcEnd
= current
;
1336 if ( !SkipUntil(&funcEnd
, '}') ) {
1337 wxLogWarning("file %s(%d): '}' expected after function name",
1338 m_filename
.c_str(), m_line
);
1343 wxString funcName
= wxString(current
, funcEnd
- current
);
1344 current
= funcEnd
+ 1;
1346 // trim spaces from both sides
1347 funcName
.Trim(FALSE
);
1348 funcName
.Trim(TRUE
);
1350 // special cases: '$...$' may be used for LaTeX inline math, remove the
1352 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1354 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1355 if ( *p
!= '$' && !isspace(*p
) )
1362 // \destruct{foo} is really ~foo
1363 if ( funcName
[0u] == '\\' ) {
1364 size_t len
= strlen("\\destruct{");
1365 if ( funcName(0, len
) != "\\destruct{" ) {
1366 wxLogWarning("file %s(%d): \\destruct expected",
1367 m_filename
.c_str(), m_line
);
1372 funcName
.erase(0, len
);
1373 funcName
.Prepend('~');
1375 if ( !SkipSpaceUntil(¤t
, '}') ) {
1376 wxLogWarning("file %s(%d): '}' expected after destructor",
1377 m_filename
.c_str(), m_line
);
1382 funcEnd
++; // there is an extra '}' to count
1385 TeXUnfilter(&funcName
);
1388 current
= funcEnd
+ 1; // skip '}'
1389 if ( !SkipSpaceUntil(¤t
, '{') ||
1390 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1391 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1392 m_filename
.c_str(), m_line
);
1397 wxArrayString paramNames
, paramTypes
, paramValues
;
1399 bool isVararg
= FALSE
;
1401 current
++; // skip '\\'
1402 lenMatch
= TryMatch(current
, "void");
1404 lenMatch
= TryMatch(current
, "param");
1405 while ( lenMatch
&& (current
- buf
< len
) ) {
1406 current
+= lenMatch
;
1408 // now come {paramtype}{paramname}
1409 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1410 if ( !!paramType
) {
1411 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1412 if ( !!paramText
) {
1413 // the param declaration may contain default value
1414 wxString paramName
= paramText
.BeforeFirst('='),
1415 paramValue
= paramText
.AfterFirst('=');
1417 // sanitize all strings
1418 TeXUnfilter(¶mValue
);
1419 TeXUnfilter(¶mName
);
1420 TeXUnfilter(¶mType
);
1422 paramValues
.Add(paramValue
);
1423 paramNames
.Add(paramName
);
1424 paramTypes
.Add(paramType
);
1429 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1430 if ( paramText
== "..." ) {
1434 wxLogWarning("Parameters of '%s::%s' are in "
1436 classname
.c_str(), funcName
.c_str());
1441 current
= SkipSpaces(current
);
1442 if ( *current
== ',' || *current
== '}' ) {
1443 current
= SkipSpaces(++current
);
1445 lenMatch
= TryMatch(current
, "\\param");
1448 wxLogWarning("file %s(%d): ',' or '}' expected after "
1449 "'\\param'", m_filename
.c_str(), m_line
);
1455 // if we got here there was no '\\void', so must have some params
1456 if ( paramNames
.IsEmpty() ) {
1457 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1458 m_filename
.c_str(), m_line
);
1464 // verbose diagnostic output
1466 size_t param
, paramCount
= paramNames
.GetCount();
1467 for ( param
= 0; param
< paramCount
; param
++ ) {
1472 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1475 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1476 m_filename
.c_str(), m_line
,
1481 foundCommand
== ConstFunc
? " const" : "");
1483 // store the info about the just found function
1484 ArrayMethodInfo
*methods
;
1485 int index
= m_classes
.Index(classname
);
1486 if ( index
== wxNOT_FOUND
) {
1487 m_classes
.Add(classname
);
1489 methods
= new ArrayMethodInfo
;
1490 m_methods
.Add(methods
);
1493 methods
= m_methods
[(size_t)index
];
1496 ArrayParamInfo params
;
1497 for ( param
= 0; param
< paramCount
; param
++ ) {
1498 params
.Add(new ParamInfo(paramTypes
[param
],
1500 paramValues
[param
]));
1503 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1504 if ( foundCommand
== ConstFunc
)
1505 method
->SetFlag(MethodInfo::Const
);
1507 method
->SetFlag(MethodInfo::Vararg
);
1509 methods
->Add(method
);
1514 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1515 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1520 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1522 typedef MMemberListT::const_iterator MemberIndex
;
1524 bool foundDiff
= FALSE
;
1526 // flag telling us whether the given class was found at all in the header
1527 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1528 bool *classExists
= new bool[countClassesInDocs
];
1529 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1530 classExists
[nClass
] = FALSE
;
1533 // ctxTop is normally an spFile
1534 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1536 const MMemberListT
& classes
= ctxTop
->GetMembers();
1537 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1538 spContext
*ctx
= *i
;
1539 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1540 // TODO process also global functions, macros, ...
1544 spClass
*ctxClass
= (spClass
*)ctx
;
1545 const wxString
& nameClass
= ctxClass
->mName
;
1546 int index
= m_classes
.Index(nameClass
);
1547 if ( index
== wxNOT_FOUND
) {
1548 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1551 wxLogError("Class '%s' is not documented at all.",
1555 // it makes no sense to check for its functions
1559 classExists
[index
] = TRUE
;
1562 // array of method descriptions for this class
1563 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1564 size_t nMethod
, countMethods
= methods
.GetCount();
1566 // flags telling if we already processed given function
1567 bool *methodExists
= new bool[countMethods
];
1568 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1569 methodExists
[nMethod
] = FALSE
;
1572 wxArrayString aOverloadedMethods
;
1574 const MMemberListT
& functions
= ctxClass
->GetMembers();
1575 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1577 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1580 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1581 const wxString
& nameMethod
= ctxMethod
->mName
;
1583 // find all functions with the same name
1584 wxArrayInt aMethodsWithSameName
;
1585 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1586 if ( methods
[nMethod
]->GetName() == nameMethod
)
1587 aMethodsWithSameName
.Add(nMethod
);
1590 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1591 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1594 wxLogError("'%s::%s' is not documented.",
1596 nameMethod
.c_str());
1599 // don't check params
1602 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1603 index
= (size_t)aMethodsWithSameName
[0u];
1604 methodExists
[index
] = TRUE
;
1606 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1609 if ( !ctxMethod
->IsPublic() ) {
1610 wxLogWarning("'%s::%s' is documented but not public.",
1612 nameMethod
.c_str());
1615 // check that the flags match
1616 const MethodInfo
& method
= *(methods
[index
]);
1618 bool isVirtual
= ctxMethod
->mIsVirtual
;
1619 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1620 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1624 isVirtual
? "not " : "");
1627 bool isConst
= ctxMethod
->mIsConstant
;
1628 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1629 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1633 isConst
? "not " : "");
1636 // check that the params match
1637 const MMemberListT
& params
= ctxMethod
->GetMembers();
1639 if ( params
.size() != method
.GetParamCount() ) {
1640 wxLogError("Incorrect number of parameters for '%s::%s' "
1641 "in the docs: should be %d instead of %d.",
1644 params
.size(), method
.GetParamCount());
1648 for ( MemberIndex k
= params
.begin();
1653 // what else can a function have?
1654 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1656 spParameter
*ctxParam
= (spParameter
*)ctx
;
1657 const ParamInfo
& param
= method
.GetParam(nParam
);
1658 if ( m_checkParamNames
&&
1659 (param
.GetName() != ctxParam
->mName
) ) {
1662 wxLogError("Parameter #%d of '%s::%s' should be "
1663 "'%s' and not '%s'.",
1667 ctxParam
->mName
.c_str(),
1668 param
.GetName().c_str());
1673 if ( param
.GetType() != ctxParam
->mType
) {
1676 wxLogError("Type of parameter '%s' of '%s::%s' "
1677 "should be '%s' and not '%s'.",
1678 ctxParam
->mName
.c_str(),
1681 ctxParam
->mType
.c_str(),
1682 param
.GetType().GetName().c_str());
1687 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1688 wxLogWarning("Default value of parameter '%s' of "
1689 "'%s::%s' should be '%s' and not "
1691 ctxParam
->mName
.c_str(),
1694 ctxParam
->mInitVal
.c_str(),
1695 param
.GetDefValue().c_str());
1701 // TODO OVER add real support for overloaded methods
1703 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1706 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1707 // mark all methods with this name as existing
1708 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1709 if ( methods
[nMethod
]->GetName() == nameMethod
)
1710 methodExists
[nMethod
] = TRUE
;
1713 aOverloadedMethods
.Add(nameMethod
);
1715 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1716 "stupid to find the right match - skipping "
1717 "the param and flags checks.",
1719 nameMethod
.c_str());
1721 //else: warning already given
1725 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1726 if ( !methodExists
[nMethod
] ) {
1727 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1728 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1731 wxLogError("'%s::%s' is documented but doesn't exist.",
1733 nameMethod
.c_str());
1738 delete [] methodExists
;
1741 // check that all classes we found in the docs really exist
1742 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1743 if ( !classExists
[nClass
] ) {
1746 wxLogError("Class '%s' is documented but doesn't exist.",
1747 m_classes
[nClass
].c_str());
1751 delete [] classExists
;
1756 DocManager::~DocManager()
1758 WX_CLEAR_ARRAY(m_methods
);
1761 // ---------------------------------------------------------------------------
1762 // IgnoreNamesHandler implementation
1763 // ---------------------------------------------------------------------------
1765 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1766 IgnoreListEntry
*second
)
1768 // first compare the classes
1769 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1771 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1776 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1778 wxFile
file(filename
, wxFile::read
);
1779 if ( !file
.IsOpened() )
1782 off_t len
= file
.Length();
1783 if ( len
== wxInvalidOffset
)
1786 char *buf
= new char[len
+ 1];
1789 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1796 for ( const char *current
= buf
; ; current
++ ) {
1798 // skip DOS line separator
1799 if ( *current
== '\r' )
1803 if ( *current
== '\n' || *current
== '\0' ) {
1804 if ( line
[0u] != '#' ) {
1805 if ( line
.Find(':') != wxNOT_FOUND
) {
1806 wxString classname
= line
.BeforeFirst(':'),
1807 funcname
= line
.AfterLast(':');
1808 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1812 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1817 if ( *current
== '\0' )
1832 // -----------------------------------------------------------------------------
1833 // global function implementation
1834 // -----------------------------------------------------------------------------
1836 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1838 wxString
label(classname
);
1839 if ( funcname
&& funcname
[0] == '\\' ) {
1840 // we may have some special TeX macro - so far only \destruct exists,
1841 // but may be later others will be added
1842 static const char *macros
[] = { "destruct" };
1843 static const char *replacement
[] = { "dtor" };
1846 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1847 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1853 if ( n
== WXSIZEOF(macros
) ) {
1854 wxLogWarning("unknown function name '%s' - leaving as is.",
1858 funcname
= replacement
[n
];
1870 static wxString
MakeHelpref(const char *argument
)
1873 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1878 static void TeXUnfilter(wxString
* str
)
1880 // FIXME may be done much more quickly
1884 str
->Replace("\\&", "&");
1885 str
->Replace("\\_", "_");
1888 static void TeXFilter(wxString
* str
)
1890 // FIXME may be done much more quickly
1891 str
->Replace("&", "\\&");
1892 str
->Replace("_", "\\_");
1895 static wxString
GetAllComments(const spContext
& ctx
)
1898 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1899 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1900 i
!= commentsList
.end();
1902 wxString comment
= (*i
)->GetText();
1904 // don't take comments like "// ----------" &c
1905 comment
.Trim(FALSE
);
1907 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1910 comments
<< comment
;
1916 static const char *GetCurrentTime(const char *timeFormat
)
1918 static char s_timeBuffer
[128];
1923 ptmNow
= localtime(&timeNow
);
1925 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1927 return s_timeBuffer
;
1932 Revision 1.8 1999/09/13 14:29:39 JS
1933 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1934 into src for simplicity; added VC++ 5 project file
1936 Revision 1.7 1999/02/21 22:32:32 VZ
1937 1. more C++ parser fixes - now it almost parses wx/string.h
1938 a) #if/#ifdef/#else (very) limited support
1939 b) param type fix - now indirection chars are correctly handled
1940 c) class/struct/union distinction
1941 d) public/private fixes
1942 e) Dump() function added - very useful for debugging
1944 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1945 by default, and this option switches it on)
1947 Revision 1.6 1999/02/20 23:00:26 VZ
1948 1. new 'diff' mode which seems to work
1949 2. output files are not overwritten in 'dmup' mode
1950 3. fixes for better handling of const functions and operators
1951 ----------------------------
1953 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1954 1. Parser improvements
1955 a) const and virtual methods are parsed correctly (not static yet)
1956 b) "const" which is part of the return type is not swallowed
1958 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1959 "//---------" kind comments discarded now.
1960 ----------------------------
1962 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1964 some tweaks to HelpGen
1965 ----------------------------
1967 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1969 HelpGen starting to compile with VC++
1970 ----------------------------
1972 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
1974 supports typedefs, generates "See also:" and adds "virtual " for virtual
1976 ----------------------------
1978 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
1980 HelpGen is a prototype of the tool for automatic generation of the .tex files
1981 for wxWindows documentation from C++ headers
1984 /* vi: set tw=80 et ts=4 sw=4: */