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
85 IMPLEMENT_APP(HelpGenApp
);
87 // -----------------------------------------------------------------------------
89 // -----------------------------------------------------------------------------
91 // return the label for the given function name (i.e. argument of \label)
92 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
94 // return the whole \helpref{arg}{arg_label} string
95 static wxString
MakeHelpref(const char *argument
);
97 // [un]quote special TeX characters (in place)
98 static void TeXFilter(wxString
* str
);
99 static void TeXUnfilter(wxString
* str
); // also trims spaces
101 // get all comments associated with this context
102 static wxString
GetAllComments(const spContext
& ctx
);
104 // get the string with current time (returns pointer to static buffer)
105 // timeFormat is used for the call of strftime(3)
106 #ifdef GetCurrentTime
107 #undef GetCurrentTime
110 static const char *GetCurrentTime(const char *timeFormat
);
112 // -----------------------------------------------------------------------------
114 // -----------------------------------------------------------------------------
116 // add a function which sanitazes the string before writing it to the file
117 class wxTeXFile
: public wxFile
122 bool WriteTeX(const wxString
& s
)
127 return wxFile::Write(t
);
131 wxTeXFile(const wxTeXFile
&);
132 wxTeXFile
& operator=(const wxTeXFile
&);
135 // helper class which manages the classes and function names to ignore for
136 // the documentation purposes (used by both HelpGenVisitor and DocManager)
137 class IgnoreNamesHandler
140 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries
) { }
141 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore
); }
143 // load file with classes/functions to ignore (add them to the names we
145 bool AddNamesFromFile(const wxString
& filename
);
147 // return TRUE if we ignore this function
148 bool IgnoreMethod(const wxString
& classname
,
149 const wxString
& funcname
) const
151 if ( IgnoreClass(classname
) )
154 IgnoreListEntry
ignore(classname
, funcname
);
156 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
159 // return TRUE if we ignore this class entirely
160 bool IgnoreClass(const wxString
& classname
) const
162 IgnoreListEntry
ignore(classname
, "");
164 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
168 struct IgnoreListEntry
170 IgnoreListEntry(const wxString
& classname
,
171 const wxString
& funcname
)
172 : m_classname(classname
), m_funcname(funcname
)
176 wxString m_classname
;
177 wxString m_funcname
; // if empty, ignore class entirely
180 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
181 IgnoreListEntry
*second
);
183 // for efficiency, let's sort it
184 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
186 ArrayNamesToIgnore m_ignore
;
189 IgnoreNamesHandler(const IgnoreNamesHandler
&);
190 IgnoreNamesHandler
& operator=(const IgnoreNamesHandler
&);
193 // visitor implementation which writes all collected data to a .tex file
194 class HelpGenVisitor
: public spVisitor
198 HelpGenVisitor(const wxString
& directoryOut
, bool overwrite
);
200 virtual void VisitFile( spFile
& fl
);
201 virtual void VisitClass( spClass
& cl
);
202 virtual void VisitEnumeration( spEnumeration
& en
);
203 virtual void VisitTypeDef( spTypeDef
& td
);
204 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
205 virtual void VisitAttribute( spAttribute
& attr
);
206 virtual void VisitOperation( spOperation
& op
);
207 virtual void VisitParameter( spParameter
& param
);
211 // get our `ignore' object
212 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
214 // shut up g++ warning (ain't it stupid?)
215 virtual ~HelpGenVisitor() { }
218 // (re)initialize the state
221 // insert documentation for enums/typedefs coming immediately before the
222 // class declaration into the class documentation
223 void InsertTypedefDocs();
224 void InsertEnumDocs();
226 // write the headers for corresponding sections (only once)
227 void InsertDataStructuresHeader();
228 void InsertMethodsHeader();
230 // terminate the function documentation if it was started
231 void CloseFunction();
233 wxString m_directoryOut
, // directory for the output
234 m_fileHeader
; // name of the .h file we parse
235 bool m_overwrite
; // overwrite existing files?
236 wxTeXFile m_file
; // file we're writing to now
239 bool m_inClass
, // TRUE after file successfully opened
240 m_inTypesSection
, // enums & typedefs go there
241 m_inMethodSection
, // functions go here
242 m_isFirstParam
, // first parameter of current function?
243 m_inFunction
; // we're parsing a function declaration
245 // holders for "saved" documentation
246 wxString m_textStoredEnums
,
247 m_textStoredTypedefs
,
248 m_textStoredFunctionComment
;
250 // headers included by this file
251 wxArrayString m_headers
;
253 // ignore handler: tells us which classes to ignore for doc generation
255 IgnoreNamesHandler m_ignoreNames
;
258 HelpGenVisitor(const HelpGenVisitor
&);
259 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
262 // documentation manager - a class which parses TeX files and remembers the
263 // functions documented in them and can later compare them with all functions
264 // found under ctxTop by C++ parser
268 DocManager(bool checkParamNames
);
271 // returns FALSE on failure
272 bool ParseTeXFile(const wxString
& filename
);
274 // returns FALSE if there were any differences
275 bool DumpDifferences(spContext
*ctxTop
) const;
277 // get our `ignore' object
278 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
284 // returns the length of 'match' if the string 'str' starts with it or 0
286 static size_t TryMatch(const char *str
, const char *match
);
288 // skip spaces: returns pointer to first non space character (also
289 // updates the value of m_line)
290 const char *SkipSpaces(const char *p
)
292 while ( isspace(*p
) ) {
300 // skips characters until the next 'c' in '*pp' unless it ends before in
301 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
302 // returned and pp points to 'c'
303 bool SkipUntil(const char **pp
, char c
);
305 // the same as SkipUntil() but only spaces are skipped: on first non space
306 // character different from 'c' the function stops and returns FALSE
307 bool SkipSpaceUntil(const char **pp
, char c
);
309 // extract the string between {} and modify '*pp' to point at the
310 // character immediately after the closing '}'. The returned string is empty
312 wxString
ExtractStringBetweenBraces(const char **pp
);
314 // the current file and line while we're in ParseTeXFile (for error
319 // functions and classes to ignore during diff
320 // -------------------------------------------
322 IgnoreNamesHandler m_ignoreNames
;
324 // information about all functions documented in the TeX file(s)
325 // -------------------------------------------------------------
327 // info about a type: for now stored as text string, but must be parsed
328 // further later (to know that "char *" == "char []" - TODO)
332 TypeInfo(const wxString
& type
) : m_type(type
) { }
334 bool operator==(const wxString
& type
) const { return m_type
== type
; }
335 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
337 const wxString
& GetName() const { return m_type
; }
343 // info abotu a function parameter
347 ParamInfo(const wxString
& type
,
348 const wxString
& name
,
349 const wxString
& value
)
350 : m_type(type
), m_name(name
), m_value(value
)
354 const TypeInfo
& GetType() const { return m_type
; }
355 const wxString
& GetName() const { return m_name
; }
356 const wxString
& GetDefValue() const { return m_value
; }
359 TypeInfo m_type
; // type of parameter
360 wxString m_name
; // name
361 wxString m_value
; // default value
364 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
366 // info about a function
379 MethodInfo(const wxString
& type
,
380 const wxString
& name
,
381 const ArrayParamInfo
& params
)
382 : m_typeRet(type
), m_name(name
), m_params(params
)
387 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
389 const TypeInfo
& GetType() const { return m_typeRet
; }
390 const wxString
& GetName() const { return m_name
; }
391 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
392 size_t GetParamCount() const { return m_params
.GetCount(); }
394 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
396 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
399 TypeInfo m_typeRet
; // return type
401 int m_flags
; // bit mask of the value from the enum above
403 ArrayParamInfo m_params
;
406 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
407 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
409 // first array contains the names of all classes we found, the second has a
410 // pointer to the array of methods of the given class at the same index as
411 // the class name appears in m_classes
412 wxArrayString m_classes
;
413 ArrayMethodInfos m_methods
;
415 // are we checking parameter names?
416 bool m_checkParamNames
;
419 DocManager(const DocManager
&);
420 DocManager
& operator=(const DocManager
&);
423 // -----------------------------------------------------------------------------
425 // -----------------------------------------------------------------------------
427 // =============================================================================
429 // =============================================================================
431 // this function never returns
434 wxString prog
= g_argv
[0];
435 wxString basename
= prog
.BeforeLast('/');
438 basename
= prog
.BeforeLast('\\');
444 "usage: %s [global options] <mode> [mode options] <files...>\n"
446 " where global options are:\n"
449 " -H give this usage message\n"
450 " -V print the version info\n"
451 " -i file file with classes/function to ignore\n"
453 " where mode is one of: dump, diff\n"
455 " dump means generate .tex files for TeX2RTF converter from specified\n"
456 " headers files, mode options are:\n"
457 " -f overwrite existing files\n"
458 " -o outdir directory for generated files\n"
460 " diff means compare the set of methods documented .tex file with the\n"
461 " methods declared in the header:\n"
462 " %s diff <file.h> <files.tex...>.\n"
463 " mode specific options are:\n"
464 " -p do check parameter names (not done by default)\n"
465 "\n", basename
.c_str(), basename
.c_str());
471 int main(int argc, char **argv)
475 bool HelpGenApp::OnInit()
490 wxArrayString filesH
, filesTeX
;
491 wxString directoryOut
, // directory for 'dmup' output
492 ignoreFile
; // file with classes/functions to ignore
493 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
494 paramNames
= FALSE
; // check param names during 'diff'?
496 for ( int current
= 1; current
< argc
; current
++ ) {
497 // all options have one letter
498 if ( argv
[current
][0] == '-' ) {
499 if ( argv
[current
][2] == '\0' ) {
500 switch ( argv
[current
][1] ) {
503 wxLog::GetActiveTarget()->SetVerbose();
508 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
517 if ( current
>= argc
) {
518 wxLogError("-i option requires an argument.");
523 ignoreFile
= argv
[current
];
527 if ( mode
!= Mode_Diff
) {
528 wxLogError("-p is only valid with diff.");
537 if ( mode
!= Mode_Dump
) {
538 wxLogError("-f is only valid with dump.");
547 if ( mode
!= Mode_Dump
) {
548 wxLogError("-o is only valid with dump.");
554 if ( current
>= argc
) {
555 wxLogError("-o option requires an argument.");
560 directoryOut
= argv
[current
];
561 if ( !!directoryOut
) {
562 // terminate with a '/' if it doesn't have it
563 switch ( directoryOut
.Last() ) {
574 //else: it's empty, do nothing
579 wxLogError("unknown option '%s'", argv
[current
]);
584 wxLogError("only one letter options are allowed, not '%s'.",
588 // only get here after a break from switch or from else branch of if
593 if ( mode
== Mode_None
) {
594 if ( strcmp(argv
[current
], "diff") == 0 )
596 else if ( strcmp(argv
[current
], "dump") == 0 )
599 wxLogError("unknown mode '%s'.", argv
[current
]);
605 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
606 filesH
.Add(argv
[current
]);
609 // 2nd files and further are TeX files in diff mode
610 wxASSERT( mode
== Mode_Diff
);
612 filesTeX
.Add(argv
[current
]);
618 // create a parser object and a visitor derivation
619 CJSourceParser parser
;
620 HelpGenVisitor
visitor(directoryOut
, overwrite
);
621 if ( !!ignoreFile
&& mode
== Mode_Dump
)
622 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
624 spContext
*ctxTop
= NULL
;
626 // parse all header files
627 size_t nFiles
= filesH
.GetCount();
628 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
629 wxString header
= filesH
[n
];
630 ctxTop
= parser
.ParseFile(header
);
632 wxLogWarning("Header file '%s' couldn't be processed.",
635 else if ( mode
== Mode_Dump
) {
636 ((spFile
*)ctxTop
)->mFileName
= header
;
637 visitor
.VisitAll(*ctxTop
);
644 #endif // __WXDEBUG__
647 // parse all TeX files
648 if ( mode
== Mode_Diff
) {
650 wxLogError("Can't complete diff.");
656 DocManager
docman(paramNames
);
658 size_t nFiles
= filesTeX
.GetCount();
659 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
660 wxString file
= filesTeX
[n
];
661 if ( !docman
.ParseTeXFile(file
) ) {
662 wxLogWarning("TeX file '%s' couldn't be processed.",
668 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
670 docman
.DumpDifferences(ctxTop
);
676 // -----------------------------------------------------------------------------
677 // HelpGenVisitor implementation
678 // -----------------------------------------------------------------------------
680 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
682 : m_directoryOut(directoryOut
)
684 m_overwrite
= overwrite
;
689 void HelpGenVisitor::Reset()
694 m_inMethodSection
= FALSE
;
696 m_textStoredTypedefs
=
698 m_textStoredFunctionComment
= "";
702 void HelpGenVisitor::InsertTypedefDocs()
704 m_file
.WriteTeX(m_textStoredTypedefs
);
705 m_textStoredTypedefs
.Empty();
708 void HelpGenVisitor::InsertEnumDocs()
710 m_file
.WriteTeX(m_textStoredEnums
);
711 m_textStoredEnums
.Empty();
714 void HelpGenVisitor::InsertDataStructuresHeader()
716 if ( !m_inTypesSection
) {
717 m_inTypesSection
= TRUE
;
719 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
723 void HelpGenVisitor::InsertMethodsHeader()
725 if ( !m_inMethodSection
) {
726 m_inMethodSection
= TRUE
;
728 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
732 void HelpGenVisitor::CloseFunction()
734 if ( m_inFunction
) {
735 m_inFunction
= FALSE
;
738 if ( m_isFirstParam
) {
740 totalText
<< "\\void";
743 totalText
<< "}\n\n";
745 if ( !m_textStoredFunctionComment
.IsEmpty() )
746 totalText
<< m_textStoredFunctionComment
<< '\n';
748 m_file
.WriteTeX(totalText
);
752 void HelpGenVisitor::EndVisit()
756 m_fileHeader
.Empty();
758 wxLogVerbose("%s: finished generating for the current file.",
759 GetCurrentTime("%H:%M:%S"));
762 void HelpGenVisitor::VisitFile( spFile
& file
)
764 m_fileHeader
= file
.mFileName
;
765 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
766 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
769 void HelpGenVisitor::VisitClass( spClass
& cl
)
771 m_inClass
= FALSE
; // will be left FALSE on error
773 wxString name
= cl
.GetName();
775 if ( m_ignoreNames
.IgnoreClass(name
) ) {
776 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
781 // the file name is built from the class name by removing the leading "wx"
782 // if any and converting it to the lower case
783 wxString filename
= m_directoryOut
;
784 if ( name(0, 2) == "wx" ) {
785 filename
<< name
.c_str() + 2;
791 filename
.MakeLower();
794 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
795 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
801 m_inClass
= m_file
.Open(filename
, wxFile::write
);
803 wxLogError("Can't generate documentation for the class '%s'.",
810 m_inTypesSection
= FALSE
;
812 wxLogInfo("Created new file '%s' for class '%s'.",
813 filename
.c_str(), name
.c_str());
815 // the entire text we're writing to file
818 // write out the header
822 "%% automatically generated by HelpGen from\n"
827 "\\section{\\class{%s}}\\label{%s}\n",
828 m_fileHeader
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
829 name
.c_str(), wxString(name
).MakeLower().c_str());
831 totalText
<< header
<< '\n';
834 // if the header includes other headers they must be related to it... try to
835 // automatically generate the "See also" clause
836 if ( !m_headers
.IsEmpty() ) {
837 // correspondence between wxWindows headers and class names
838 static const char *headers
[] = {
847 // NULL here means not to insert anything in "See also" for the
848 // corresponding header
849 static const char *classes
[] = {
858 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
859 "arrays must be in sync!" );
861 wxArrayInt interestingClasses
;
863 size_t count
= m_headers
.Count(), index
;
864 for ( size_t n
= 0; n
< count
; n
++ ) {
865 wxString baseHeaderName
= m_headers
[n
].Before('.');
866 if ( baseHeaderName(0, 3) != "wx/" )
869 baseHeaderName
.erase(0, 3);
870 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
871 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
875 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
876 // interesting header
877 interestingClasses
.Add(index
);
881 if ( !interestingClasses
.IsEmpty() ) {
882 // do generate "See also" clause
883 totalText
<< "\\wxheading{See also:}\n\n";
885 count
= interestingClasses
.Count();
886 for ( index
= 0; index
< count
; index
++ ) {
890 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
897 // the comment before the class generally explains what is it for so put it
898 // in place of the class description
899 if ( cl
.HasComments() ) {
900 wxString comment
= GetAllComments(cl
);
902 totalText
<< '\n' << comment
<< '\n';
905 // derived from section
906 wxString derived
= "\\wxheading{Derived from}\n\n";
908 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
909 if ( baseClasses
.size() == 0 ) {
910 derived
<< "No base class";
914 for ( StrListT::const_iterator i
= baseClasses
.begin();
915 i
!= baseClasses
.end();
918 // separate from the previous one
925 wxString baseclass
= *i
;
926 derived
<< "\\helpref{" << baseclass
<< "}";
927 derived
<< "{" << baseclass
.MakeLower() << "}";
930 totalText
<< derived
<< "\n\n";
932 // write all this to file
933 m_file
.WriteTeX(totalText
);
935 // if there were any enums/typedefs before, insert their documentation now
936 InsertDataStructuresHeader();
941 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
945 if ( m_inMethodSection
) {
946 // FIXME that's a bug, but tell the user aboit it nevertheless... we
947 // should be smart enough to process even the enums which come after the
949 wxLogWarning("enum '%s' ignored, please put it before the class "
950 "methods.", en
.GetName().c_str());
954 // simply copy the enum text in the docs
955 wxString enumeration
= GetAllComments(en
);
956 enumeration
<< "{\\small \\begin{verbatim}\n"
958 << "\n\\end{verbatim}}\n";
960 // remember for later use if we're not inside a class yet
962 if ( !m_textStoredEnums
.IsEmpty() ) {
963 m_textStoredEnums
<< '\n';
966 m_textStoredEnums
<< enumeration
;
969 // write the header for this section if not done yet
970 InsertDataStructuresHeader();
973 m_file
.WriteTeX(enumeration
);
977 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
981 if ( m_inMethodSection
) {
982 // FIXME that's a bug, but tell the user aboit it nevertheless...
983 wxLogWarning("typedef '%s' ignored, please put it before the class "
984 "methods.", td
.GetName().c_str());
989 typedefdoc
<< "{\\small \\begin{verbatim}\n"
990 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
991 << "\n\\end{verbatim}}\n"
992 << GetAllComments(td
);
994 // remember for later use if we're not inside a class yet
996 if ( !m_textStoredTypedefs
.IsEmpty() ) {
997 m_textStoredTypedefs
<< '\n';
1000 m_textStoredTypedefs
<< typedefdoc
;
1003 // write the header for this section if not done yet
1004 InsertDataStructuresHeader();
1007 m_file
.WriteTeX(typedefdoc
);
1011 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1013 switch ( pd
.GetStatementType() ) {
1014 case SP_PREP_DEF_INCLUDE_FILE
:
1015 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1018 case SP_PREP_DEF_DEFINE_SYMBOL
:
1019 // TODO decide if it's a constant and document it if it is
1024 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1028 // only document the public member variables
1029 if ( !m_inClass
|| !attr
.IsPublic() )
1032 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1035 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1040 // we don't generate docs right now - either we ignore this class
1041 // entirely or we couldn't open the file
1045 if ( !op
.IsInClass() ) {
1046 // TODO document global functions
1047 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1052 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1053 // FIXME should we document protected functions?
1057 wxString funcname
= op
.GetName(),
1058 classname
= op
.GetClass().GetName();
1059 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1060 wxLogVerbose("Skipping ignored '%s::%s'.",
1061 classname
.c_str(), funcname
.c_str());
1066 InsertMethodsHeader();
1070 m_isFirstParam
= TRUE
;
1072 m_textStoredFunctionComment
= GetAllComments(op
);
1074 // start function documentation
1077 // check for the special case of dtor
1079 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1080 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1084 totalText
.Printf("\n"
1085 "\\membersection{%s::%s}\\label{%s}\n"
1087 "\\%sfunc{%s%s}{%s}{",
1088 classname
.c_str(), funcname
.c_str(),
1089 MakeLabel(classname
, funcname
).c_str(),
1090 op
.mIsConstant
? "const" : "",
1091 op
.mIsVirtual
? "virtual " : "",
1092 op
.mRetType
.c_str(),
1095 m_file
.WriteTeX(totalText
);
1098 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1100 if ( !m_inFunction
)
1104 if ( m_isFirstParam
) {
1105 m_isFirstParam
= FALSE
;
1111 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1112 wxString defvalue
= param
.mInitVal
;
1113 if ( !defvalue
.IsEmpty() ) {
1114 totalText
<< " = " << defvalue
;
1119 m_file
.WriteTeX(totalText
);
1122 // ---------------------------------------------------------------------------
1124 // ---------------------------------------------------------------------------
1126 DocManager::DocManager(bool checkParamNames
)
1128 m_checkParamNames
= checkParamNames
;
1131 size_t DocManager::TryMatch(const char *str
, const char *match
)
1133 size_t lenMatch
= 0;
1134 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1137 if ( match
[lenMatch
] == '\0' )
1144 bool DocManager::SkipUntil(const char **pp
, char c
)
1146 const char *p
= *pp
;
1162 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1164 const char *p
= *pp
;
1166 if ( !isspace(*p
) || *p
== '\0' )
1180 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1184 if ( !SkipSpaceUntil(pp
, '{') ) {
1185 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1186 m_filename
.c_str(), m_line
);
1190 const char *startParam
= ++*pp
; // skip '{'
1192 if ( !SkipUntil(pp
, '}') ) {
1193 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1194 m_filename
.c_str(), m_line
);
1197 result
= wxString(startParam
, (*pp
)++ - startParam
);
1204 bool DocManager::ParseTeXFile(const wxString
& filename
)
1206 m_filename
= filename
;
1208 wxFile
file(m_filename
, wxFile::read
);
1209 if ( !file
.IsOpened() )
1212 off_t len
= file
.Length();
1213 if ( len
== wxInvalidOffset
)
1216 char *buf
= new char[len
+ 1];
1219 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1225 // reinit everything
1228 wxLogVerbose("%s: starting to parse doc file '%s'.",
1229 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1231 // the name of the class from the last "\membersection" command: we assume
1232 // that the following "\func" or "\constfunc" always documents a method of
1233 // this class (and it should always be like that in wxWindows documentation)
1236 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1237 // FIXME parsing is awfully inefficient
1239 if ( *current
== '%' ) {
1240 // comment, skip until the end of line
1242 SkipUntil(¤t
, '\n');
1247 // all the command we're interested in start with '\\'
1248 while ( *current
!= '\\' && *current
!= '\0' ) {
1249 if ( *current
++ == '\n' )
1253 if ( *current
== '\0' ) {
1254 // no more TeX commands left
1258 current
++; // skip '\\'
1266 } foundCommand
= Nothing
;
1268 size_t lenMatch
= TryMatch(current
, "func");
1270 foundCommand
= Func
;
1273 lenMatch
= TryMatch(current
, "constfunc");
1275 foundCommand
= ConstFunc
;
1277 lenMatch
= TryMatch(current
, "membersection");
1280 foundCommand
= MemberSect
;
1284 if ( foundCommand
== Nothing
)
1287 current
+= lenMatch
;
1289 if ( !SkipSpaceUntil(¤t
, '{') ) {
1290 wxLogWarning("file %s(%d): '{' expected after \\func, "
1291 "\\constfunc or \\membersection.",
1292 m_filename
.c_str(), m_line
);
1299 if ( foundCommand
== MemberSect
) {
1300 // what follows has the form <classname>::<funcname>
1301 const char *startClass
= current
;
1302 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1303 wxLogWarning("file %s(%d): '::' expected after "
1304 "\\membersection.", m_filename
.c_str(), m_line
);
1307 classname
= wxString(startClass
, current
- startClass
);
1308 TeXUnfilter(&classname
);
1314 // extract the return type
1315 const char *startRetType
= current
;
1317 if ( !SkipUntil(¤t
, '}') ) {
1318 wxLogWarning("file %s(%d): '}' expected after return type",
1319 m_filename
.c_str(), m_line
);
1324 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1325 TeXUnfilter(&returnType
);
1328 if ( !SkipSpaceUntil(¤t
, '{') ) {
1329 wxLogWarning("file %s(%d): '{' expected after return type",
1330 m_filename
.c_str(), m_line
);
1336 const char *funcEnd
= current
;
1337 if ( !SkipUntil(&funcEnd
, '}') ) {
1338 wxLogWarning("file %s(%d): '}' expected after function name",
1339 m_filename
.c_str(), m_line
);
1344 wxString funcName
= wxString(current
, funcEnd
- current
);
1345 current
= funcEnd
+ 1;
1347 // trim spaces from both sides
1348 funcName
.Trim(FALSE
);
1349 funcName
.Trim(TRUE
);
1351 // special cases: '$...$' may be used for LaTeX inline math, remove the
1353 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1355 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1356 if ( *p
!= '$' && !isspace(*p
) )
1363 // \destruct{foo} is really ~foo
1364 if ( funcName
[0u] == '\\' ) {
1365 size_t len
= strlen("\\destruct{");
1366 if ( funcName(0, len
) != "\\destruct{" ) {
1367 wxLogWarning("file %s(%d): \\destruct expected",
1368 m_filename
.c_str(), m_line
);
1373 funcName
.erase(0, len
);
1374 funcName
.Prepend('~');
1376 if ( !SkipSpaceUntil(¤t
, '}') ) {
1377 wxLogWarning("file %s(%d): '}' expected after destructor",
1378 m_filename
.c_str(), m_line
);
1383 funcEnd
++; // there is an extra '}' to count
1386 TeXUnfilter(&funcName
);
1389 current
= funcEnd
+ 1; // skip '}'
1390 if ( !SkipSpaceUntil(¤t
, '{') ||
1391 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1392 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1393 m_filename
.c_str(), m_line
);
1398 wxArrayString paramNames
, paramTypes
, paramValues
;
1400 bool isVararg
= FALSE
;
1402 current
++; // skip '\\'
1403 lenMatch
= TryMatch(current
, "void");
1405 lenMatch
= TryMatch(current
, "param");
1406 while ( lenMatch
&& (current
- buf
< len
) ) {
1407 current
+= lenMatch
;
1409 // now come {paramtype}{paramname}
1410 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1411 if ( !!paramType
) {
1412 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1413 if ( !!paramText
) {
1414 // the param declaration may contain default value
1415 wxString paramName
= paramText
.BeforeFirst('='),
1416 paramValue
= paramText
.AfterFirst('=');
1418 // sanitize all strings
1419 TeXUnfilter(¶mValue
);
1420 TeXUnfilter(¶mName
);
1421 TeXUnfilter(¶mType
);
1423 paramValues
.Add(paramValue
);
1424 paramNames
.Add(paramName
);
1425 paramTypes
.Add(paramType
);
1430 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1431 if ( paramText
== "..." ) {
1435 wxLogWarning("Parameters of '%s::%s' are in "
1437 classname
.c_str(), funcName
.c_str());
1442 current
= SkipSpaces(current
);
1443 if ( *current
== ',' || *current
== '}' ) {
1444 current
= SkipSpaces(++current
);
1446 lenMatch
= TryMatch(current
, "\\param");
1449 wxLogWarning("file %s(%d): ',' or '}' expected after "
1450 "'\\param'", m_filename
.c_str(), m_line
);
1456 // if we got here there was no '\\void', so must have some params
1457 if ( paramNames
.IsEmpty() ) {
1458 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1459 m_filename
.c_str(), m_line
);
1465 // verbose diagnostic output
1467 size_t param
, paramCount
= paramNames
.GetCount();
1468 for ( param
= 0; param
< paramCount
; param
++ ) {
1473 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1476 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1477 m_filename
.c_str(), m_line
,
1482 foundCommand
== ConstFunc
? " const" : "");
1484 // store the info about the just found function
1485 ArrayMethodInfo
*methods
;
1486 int index
= m_classes
.Index(classname
);
1487 if ( index
== wxNOT_FOUND
) {
1488 m_classes
.Add(classname
);
1490 methods
= new ArrayMethodInfo
;
1491 m_methods
.Add(methods
);
1494 methods
= m_methods
[(size_t)index
];
1497 ArrayParamInfo params
;
1498 for ( param
= 0; param
< paramCount
; param
++ ) {
1499 params
.Add(new ParamInfo(paramTypes
[param
],
1501 paramValues
[param
]));
1504 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1505 if ( foundCommand
== ConstFunc
)
1506 method
->SetFlag(MethodInfo::Const
);
1508 method
->SetFlag(MethodInfo::Vararg
);
1510 methods
->Add(method
);
1515 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1516 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1521 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1523 typedef MMemberListT::const_iterator MemberIndex
;
1525 bool foundDiff
= FALSE
;
1527 // flag telling us whether the given class was found at all in the header
1528 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1529 bool *classExists
= new bool[countClassesInDocs
];
1530 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1531 classExists
[nClass
] = FALSE
;
1534 // ctxTop is normally an spFile
1535 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1537 const MMemberListT
& classes
= ctxTop
->GetMembers();
1538 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1539 spContext
*ctx
= *i
;
1540 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1541 // TODO process also global functions, macros, ...
1545 spClass
*ctxClass
= (spClass
*)ctx
;
1546 const wxString
& nameClass
= ctxClass
->mName
;
1547 int index
= m_classes
.Index(nameClass
);
1548 if ( index
== wxNOT_FOUND
) {
1549 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1552 wxLogError("Class '%s' is not documented at all.",
1556 // it makes no sense to check for its functions
1560 classExists
[index
] = TRUE
;
1563 // array of method descriptions for this class
1564 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1565 size_t nMethod
, countMethods
= methods
.GetCount();
1567 // flags telling if we already processed given function
1568 bool *methodExists
= new bool[countMethods
];
1569 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1570 methodExists
[nMethod
] = FALSE
;
1573 wxArrayString aOverloadedMethods
;
1575 const MMemberListT
& functions
= ctxClass
->GetMembers();
1576 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1578 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1581 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1582 const wxString
& nameMethod
= ctxMethod
->mName
;
1584 // find all functions with the same name
1585 wxArrayInt aMethodsWithSameName
;
1586 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1587 if ( methods
[nMethod
]->GetName() == nameMethod
)
1588 aMethodsWithSameName
.Add(nMethod
);
1591 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1592 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1595 wxLogError("'%s::%s' is not documented.",
1597 nameMethod
.c_str());
1600 // don't check params
1603 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1604 index
= (size_t)aMethodsWithSameName
[0u];
1605 methodExists
[index
] = TRUE
;
1607 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1610 if ( !ctxMethod
->IsPublic() ) {
1611 wxLogWarning("'%s::%s' is documented but not public.",
1613 nameMethod
.c_str());
1616 // check that the flags match
1617 const MethodInfo
& method
= *(methods
[index
]);
1619 bool isVirtual
= ctxMethod
->mIsVirtual
;
1620 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1621 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1625 isVirtual
? "not " : "");
1628 bool isConst
= ctxMethod
->mIsConstant
;
1629 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1630 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1634 isConst
? "not " : "");
1637 // check that the params match
1638 const MMemberListT
& params
= ctxMethod
->GetMembers();
1640 if ( params
.size() != method
.GetParamCount() ) {
1641 wxLogError("Incorrect number of parameters for '%s::%s' "
1642 "in the docs: should be %d instead of %d.",
1645 params
.size(), method
.GetParamCount());
1649 for ( MemberIndex k
= params
.begin();
1654 // what else can a function have?
1655 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1657 spParameter
*ctxParam
= (spParameter
*)ctx
;
1658 const ParamInfo
& param
= method
.GetParam(nParam
);
1659 if ( m_checkParamNames
&&
1660 (param
.GetName() != ctxParam
->mName
) ) {
1663 wxLogError("Parameter #%d of '%s::%s' should be "
1664 "'%s' and not '%s'.",
1668 ctxParam
->mName
.c_str(),
1669 param
.GetName().c_str());
1674 if ( param
.GetType() != ctxParam
->mType
) {
1677 wxLogError("Type of parameter '%s' of '%s::%s' "
1678 "should be '%s' and not '%s'.",
1679 ctxParam
->mName
.c_str(),
1682 ctxParam
->mType
.c_str(),
1683 param
.GetType().GetName().c_str());
1688 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1689 wxLogWarning("Default value of parameter '%s' of "
1690 "'%s::%s' should be '%s' and not "
1692 ctxParam
->mName
.c_str(),
1695 ctxParam
->mInitVal
.c_str(),
1696 param
.GetDefValue().c_str());
1702 // TODO OVER add real support for overloaded methods
1704 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1707 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1708 // mark all methods with this name as existing
1709 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1710 if ( methods
[nMethod
]->GetName() == nameMethod
)
1711 methodExists
[nMethod
] = TRUE
;
1714 aOverloadedMethods
.Add(nameMethod
);
1716 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1717 "stupid to find the right match - skipping "
1718 "the param and flags checks.",
1720 nameMethod
.c_str());
1722 //else: warning already given
1726 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1727 if ( !methodExists
[nMethod
] ) {
1728 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1729 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1732 wxLogError("'%s::%s' is documented but doesn't exist.",
1734 nameMethod
.c_str());
1739 delete [] methodExists
;
1742 // check that all classes we found in the docs really exist
1743 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1744 if ( !classExists
[nClass
] ) {
1747 wxLogError("Class '%s' is documented but doesn't exist.",
1748 m_classes
[nClass
].c_str());
1752 delete [] classExists
;
1757 DocManager::~DocManager()
1759 WX_CLEAR_ARRAY(m_methods
);
1762 // ---------------------------------------------------------------------------
1763 // IgnoreNamesHandler implementation
1764 // ---------------------------------------------------------------------------
1766 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1767 IgnoreListEntry
*second
)
1769 // first compare the classes
1770 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1772 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1777 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1779 wxFile
file(filename
, wxFile::read
);
1780 if ( !file
.IsOpened() )
1783 off_t len
= file
.Length();
1784 if ( len
== wxInvalidOffset
)
1787 char *buf
= new char[len
+ 1];
1790 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1797 for ( const char *current
= buf
; ; current
++ ) {
1799 // skip DOS line separator
1800 if ( *current
== '\r' )
1804 if ( *current
== '\n' || *current
== '\0' ) {
1805 if ( line
[0u] != '#' ) {
1806 if ( line
.Find(':') != wxNOT_FOUND
) {
1807 wxString classname
= line
.BeforeFirst(':'),
1808 funcname
= line
.AfterLast(':');
1809 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1813 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1818 if ( *current
== '\0' )
1833 // -----------------------------------------------------------------------------
1834 // global function implementation
1835 // -----------------------------------------------------------------------------
1837 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1839 wxString
label(classname
);
1840 if ( funcname
&& funcname
[0] == '\\' ) {
1841 // we may have some special TeX macro - so far only \destruct exists,
1842 // but may be later others will be added
1843 static const char *macros
[] = { "destruct" };
1844 static const char *replacement
[] = { "dtor" };
1847 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1848 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1854 if ( n
== WXSIZEOF(macros
) ) {
1855 wxLogWarning("unknown function name '%s' - leaving as is.",
1859 funcname
= replacement
[n
];
1871 static wxString
MakeHelpref(const char *argument
)
1874 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1879 static void TeXUnfilter(wxString
* str
)
1881 // FIXME may be done much more quickly
1885 str
->Replace("\\&", "&");
1886 str
->Replace("\\_", "_");
1889 static void TeXFilter(wxString
* str
)
1891 // FIXME may be done much more quickly
1892 str
->Replace("&", "\\&");
1893 str
->Replace("_", "\\_");
1896 static wxString
GetAllComments(const spContext
& ctx
)
1899 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1900 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1901 i
!= commentsList
.end();
1903 wxString comment
= (*i
)->GetText();
1905 // don't take comments like "// ----------" &c
1906 comment
.Trim(FALSE
);
1908 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1911 comments
<< comment
;
1917 static const char *GetCurrentTime(const char *timeFormat
)
1919 static char s_timeBuffer
[128];
1924 ptmNow
= localtime(&timeNow
);
1926 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1928 return s_timeBuffer
;
1933 Revision 1.9 2000/01/16 13:25:21 VS
1934 compilation fixes (gcc)
1936 Revision 1.8 1999/09/13 14:29:39 JS
1938 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1939 into src for simplicity; added VC++ 5 project file
1941 Revision 1.7 1999/02/21 22:32:32 VZ
1942 1. more C++ parser fixes - now it almost parses wx/string.h
1943 a) #if/#ifdef/#else (very) limited support
1944 b) param type fix - now indirection chars are correctly handled
1945 c) class/struct/union distinction
1946 d) public/private fixes
1947 e) Dump() function added - very useful for debugging
1949 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1950 by default, and this option switches it on)
1952 Revision 1.6 1999/02/20 23:00:26 VZ
1953 1. new 'diff' mode which seems to work
1954 2. output files are not overwritten in 'dmup' mode
1955 3. fixes for better handling of const functions and operators
1956 ----------------------------
1958 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1959 1. Parser improvements
1960 a) const and virtual methods are parsed correctly (not static yet)
1961 b) "const" which is part of the return type is not swallowed
1963 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1964 "//---------" kind comments discarded now.
1965 ----------------------------
1967 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1969 some tweaks to HelpGen
1970 ----------------------------
1972 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1974 HelpGen starting to compile with VC++
1975 ----------------------------
1977 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
1979 supports typedefs, generates "See also:" and adds "virtual " for virtual
1981 ----------------------------
1983 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
1985 HelpGen is a prototype of the tool for automatic generation of the .tex files
1986 for wxWindows documentation from C++ headers
1989 /* vi: set tw=80 et ts=4 sw=4: */