1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Main program file for HelpGen
4 // Author: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Copyright: (c) 1999 VZ
10 /////////////////////////////////////////////////////////////////////////////
15 1. wx/string.h confuses C++ parser terribly
16 2. C++ parser doesn't know about virtual functions, nor static ones
17 3. param checking is not done for vararg functions
18 4. type comparison is dumb: it doesn't know that "char *" is the same
19 that "char []" nor that "const char *" is the same as "char const *"
21 TODO (+ means fixed), see also the change log at the end of the file.
23 (i) small fixes in the current version
25 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
27 3. Document global variables
30 6. Include file name/line number in the "diff" messages?
31 +7. Support for vararg functions
33 (ii) plans for version 2
34 1. Use wxTextFile for direct file access to avoid one scan method problems
35 2. Use command line parser class for the options
36 3. support for overloaded functions in diff mode (search for OVER)
38 (iii) plans for version 3
39 1. Merging with existing files
43 // =============================================================================
45 // =============================================================================
47 // -----------------------------------------------------------------------------
49 // -----------------------------------------------------------------------------
52 #include "wx/wxprec.h"
55 #include <wx/string.h>
57 #include <wx/dynarray.h>
63 // C++ parsing classes
70 // -----------------------------------------------------------------------------
72 // -----------------------------------------------------------------------------
74 // just a copy of argv
75 static char **g_argv
= NULL
;
77 class HelpGenApp
: public wxApp
89 IMPLEMENT_APP(HelpGenApp
);
91 // -----------------------------------------------------------------------------
93 // -----------------------------------------------------------------------------
95 // return the label for the given function name (i.e. argument of \label)
96 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
98 // return the whole \helpref{arg}{arg_label} string
99 static wxString
MakeHelpref(const char *argument
);
101 // [un]quote special TeX characters (in place)
102 static void TeXFilter(wxString
* str
);
103 static void TeXUnfilter(wxString
* str
); // also trims spaces
105 // get all comments associated with this context
106 static wxString
GetAllComments(const spContext
& ctx
);
108 // get the string with current time (returns pointer to static buffer)
109 // timeFormat is used for the call of strftime(3)
110 #ifdef GetCurrentTime
111 #undef GetCurrentTime
114 static const char *GetCurrentTime(const char *timeFormat
);
116 // -----------------------------------------------------------------------------
118 // -----------------------------------------------------------------------------
120 // add a function which sanitazes the string before writing it to the file
121 class wxTeXFile
: public wxFile
126 bool WriteTeX(const wxString
& s
)
131 return wxFile::Write(t
);
135 wxTeXFile(const wxTeXFile
&);
136 wxTeXFile
& operator=(const wxTeXFile
&);
139 // helper class which manages the classes and function names to ignore for
140 // the documentation purposes (used by both HelpGenVisitor and DocManager)
141 class IgnoreNamesHandler
144 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries
) { }
145 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore
); }
147 // load file with classes/functions to ignore (add them to the names we
149 bool AddNamesFromFile(const wxString
& filename
);
151 // return TRUE if we ignore this function
152 bool IgnoreMethod(const wxString
& classname
,
153 const wxString
& funcname
) const
155 if ( IgnoreClass(classname
) )
158 IgnoreListEntry
ignore(classname
, funcname
);
160 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
163 // return TRUE if we ignore this class entirely
164 bool IgnoreClass(const wxString
& classname
) const
166 IgnoreListEntry
ignore(classname
, "");
168 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
172 struct IgnoreListEntry
174 IgnoreListEntry(const wxString
& classname
,
175 const wxString
& funcname
)
176 : m_classname(classname
), m_funcname(funcname
)
180 wxString m_classname
;
181 wxString m_funcname
; // if empty, ignore class entirely
184 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
185 IgnoreListEntry
*second
);
187 // for efficiency, let's sort it
188 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
190 ArrayNamesToIgnore m_ignore
;
193 IgnoreNamesHandler(const IgnoreNamesHandler
&);
194 IgnoreNamesHandler
& operator=(const IgnoreNamesHandler
&);
197 // visitor implementation which writes all collected data to a .tex file
198 class HelpGenVisitor
: public spVisitor
202 HelpGenVisitor(const wxString
& directoryOut
, bool overwrite
);
204 virtual void VisitFile( spFile
& fl
);
205 virtual void VisitClass( spClass
& cl
);
206 virtual void VisitEnumeration( spEnumeration
& en
);
207 virtual void VisitTypeDef( spTypeDef
& td
);
208 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
209 virtual void VisitAttribute( spAttribute
& attr
);
210 virtual void VisitOperation( spOperation
& op
);
211 virtual void VisitParameter( spParameter
& param
);
215 // get our `ignore' object
216 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
218 // shut up g++ warning (ain't it stupid?)
219 virtual ~HelpGenVisitor() { }
222 // (re)initialize the state
225 // insert documentation for enums/typedefs coming immediately before the
226 // class declaration into the class documentation
227 void InsertTypedefDocs();
228 void InsertEnumDocs();
230 // write the headers for corresponding sections (only once)
231 void InsertDataStructuresHeader();
232 void InsertMethodsHeader();
234 // terminate the function documentation if it was started
235 void CloseFunction();
237 wxString m_directoryOut
, // directory for the output
238 m_fileHeader
; // name of the .h file we parse
239 bool m_overwrite
; // overwrite existing files?
240 wxTeXFile m_file
; // file we're writing to now
243 bool m_inClass
, // TRUE after file successfully opened
244 m_inTypesSection
, // enums & typedefs go there
245 m_inMethodSection
, // functions go here
246 m_isFirstParam
, // first parameter of current function?
247 m_inFunction
; // we're parsing a function declaration
249 // holders for "saved" documentation
250 wxString m_textStoredEnums
,
251 m_textStoredTypedefs
,
252 m_textStoredFunctionComment
;
254 // headers included by this file
255 wxArrayString m_headers
;
257 // ignore handler: tells us which classes to ignore for doc generation
259 IgnoreNamesHandler m_ignoreNames
;
262 HelpGenVisitor(const HelpGenVisitor
&);
263 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
266 // documentation manager - a class which parses TeX files and remembers the
267 // functions documented in them and can later compare them with all functions
268 // found under ctxTop by C++ parser
272 DocManager(bool checkParamNames
);
275 // returns FALSE on failure
276 bool ParseTeXFile(const wxString
& filename
);
278 // returns FALSE if there were any differences
279 bool DumpDifferences(spContext
*ctxTop
) const;
281 // get our `ignore' object
282 IgnoreNamesHandler
& GetIgnoreHandler() { return m_ignoreNames
; }
288 // returns the length of 'match' if the string 'str' starts with it or 0
290 static size_t TryMatch(const char *str
, const char *match
);
292 // skip spaces: returns pointer to first non space character (also
293 // updates the value of m_line)
294 const char *SkipSpaces(const char *p
)
296 while ( isspace(*p
) ) {
304 // skips characters until the next 'c' in '*pp' unless it ends before in
305 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
306 // returned and pp points to 'c'
307 bool SkipUntil(const char **pp
, char c
);
309 // the same as SkipUntil() but only spaces are skipped: on first non space
310 // character different from 'c' the function stops and returns FALSE
311 bool SkipSpaceUntil(const char **pp
, char c
);
313 // extract the string between {} and modify '*pp' to point at the
314 // character immediately after the closing '}'. The returned string is empty
316 wxString
ExtractStringBetweenBraces(const char **pp
);
318 // the current file and line while we're in ParseTeXFile (for error
323 // functions and classes to ignore during diff
324 // -------------------------------------------
326 IgnoreNamesHandler m_ignoreNames
;
328 // information about all functions documented in the TeX file(s)
329 // -------------------------------------------------------------
331 // info about a type: for now stored as text string, but must be parsed
332 // further later (to know that "char *" == "char []" - TODO)
336 TypeInfo(const wxString
& type
) : m_type(type
) { }
338 bool operator==(const wxString
& type
) const { return m_type
== type
; }
339 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
341 const wxString
& GetName() const { return m_type
; }
347 // info abotu a function parameter
351 ParamInfo(const wxString
& type
,
352 const wxString
& name
,
353 const wxString
& value
)
354 : m_type(type
), m_name(name
), m_value(value
)
358 const TypeInfo
& GetType() const { return m_type
; }
359 const wxString
& GetName() const { return m_name
; }
360 const wxString
& GetDefValue() const { return m_value
; }
363 TypeInfo m_type
; // type of parameter
364 wxString m_name
; // name
365 wxString m_value
; // default value
368 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
370 // info about a function
383 MethodInfo(const wxString
& type
,
384 const wxString
& name
,
385 const ArrayParamInfo
& params
)
386 : m_typeRet(type
), m_name(name
), m_params(params
)
391 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
393 const TypeInfo
& GetType() const { return m_typeRet
; }
394 const wxString
& GetName() const { return m_name
; }
395 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
396 size_t GetParamCount() const { return m_params
.GetCount(); }
398 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
400 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
403 TypeInfo m_typeRet
; // return type
405 int m_flags
; // bit mask of the value from the enum above
407 ArrayParamInfo m_params
;
410 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
411 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
413 // first array contains the names of all classes we found, the second has a
414 // pointer to the array of methods of the given class at the same index as
415 // the class name appears in m_classes
416 wxArrayString m_classes
;
417 ArrayMethodInfos m_methods
;
419 // are we checking parameter names?
420 bool m_checkParamNames
;
423 DocManager(const DocManager
&);
424 DocManager
& operator=(const DocManager
&);
427 // -----------------------------------------------------------------------------
429 // -----------------------------------------------------------------------------
431 // =============================================================================
433 // =============================================================================
435 // this function never returns
438 wxString prog
= g_argv
[0];
439 wxString basename
= prog
.BeforeLast('/');
442 basename
= prog
.BeforeLast('\\');
448 "usage: %s [global options] <mode> [mode options] <files...>\n"
450 " where global options are:\n"
453 " -H give this usage message\n"
454 " -V print the version info\n"
455 " -i file file with classes/function to ignore\n"
457 " where mode is one of: dump, diff\n"
459 " dump means generate .tex files for TeX2RTF converter from specified\n"
460 " headers files, mode options are:\n"
461 " -f overwrite existing files\n"
462 " -o outdir directory for generated files\n"
464 " diff means compare the set of methods documented .tex file with the\n"
465 " methods declared in the header:\n"
466 " %s diff <file.h> <files.tex...>.\n"
467 " mode specific options are:\n"
468 " -p do check parameter names (not done by default)\n"
469 "\n", basename
.c_str(), basename
.c_str());
475 int main(int argc, char **argv)
480 bool HelpGenApp::OnInit()
482 int HelpGenApp::OnRun()
498 wxArrayString filesH
, filesTeX
;
499 wxString directoryOut
, // directory for 'dmup' output
500 ignoreFile
; // file with classes/functions to ignore
501 bool overwrite
= FALSE
, // overwrite existing files during 'dump'?
502 paramNames
= FALSE
; // check param names during 'diff'?
504 for ( int current
= 1; current
< argc
; current
++ ) {
505 // all options have one letter
506 if ( argv
[current
][0] == '-' ) {
507 if ( argv
[current
][2] == '\0' ) {
508 switch ( argv
[current
][1] ) {
511 wxLog::GetActiveTarget()->SetVerbose();
516 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
525 if ( current
>= argc
) {
526 wxLogError("-i option requires an argument.");
531 ignoreFile
= argv
[current
];
535 if ( mode
!= Mode_Diff
) {
536 wxLogError("-p is only valid with diff.");
545 if ( mode
!= Mode_Dump
) {
546 wxLogError("-f is only valid with dump.");
555 if ( mode
!= Mode_Dump
) {
556 wxLogError("-o is only valid with dump.");
562 if ( current
>= argc
) {
563 wxLogError("-o option requires an argument.");
568 directoryOut
= argv
[current
];
569 if ( !!directoryOut
) {
570 // terminate with a '/' if it doesn't have it
571 switch ( directoryOut
.Last() ) {
582 //else: it's empty, do nothing
587 wxLogError("unknown option '%s'", argv
[current
]);
592 wxLogError("only one letter options are allowed, not '%s'.",
596 // only get here after a break from switch or from else branch of if
601 if ( mode
== Mode_None
) {
602 if ( strcmp(argv
[current
], "diff") == 0 )
604 else if ( strcmp(argv
[current
], "dump") == 0 )
607 wxLogError("unknown mode '%s'.", argv
[current
]);
613 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
614 filesH
.Add(argv
[current
]);
617 // 2nd files and further are TeX files in diff mode
618 wxASSERT( mode
== Mode_Diff
);
620 filesTeX
.Add(argv
[current
]);
626 // create a parser object and a visitor derivation
627 CJSourceParser parser
;
628 HelpGenVisitor
visitor(directoryOut
, overwrite
);
629 if ( !!ignoreFile
&& mode
== Mode_Dump
)
630 visitor
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
632 spContext
*ctxTop
= NULL
;
634 // parse all header files
635 size_t nFiles
= filesH
.GetCount();
636 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
637 wxString header
= filesH
[n
];
638 ctxTop
= parser
.ParseFile(header
);
640 wxLogWarning("Header file '%s' couldn't be processed.",
643 else if ( mode
== Mode_Dump
) {
644 ((spFile
*)ctxTop
)->mFileName
= header
;
645 visitor
.VisitAll(*ctxTop
);
652 #endif // __WXDEBUG__
655 // parse all TeX files
656 if ( mode
== Mode_Diff
) {
658 wxLogError("Can't complete diff.");
664 DocManager
docman(paramNames
);
666 size_t nFiles
= filesTeX
.GetCount();
667 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
668 wxString file
= filesTeX
[n
];
669 if ( !docman
.ParseTeXFile(file
) ) {
670 wxLogWarning("TeX file '%s' couldn't be processed.",
676 docman
.GetIgnoreHandler().AddNamesFromFile(ignoreFile
);
678 docman
.DumpDifferences(ctxTop
);
684 // -----------------------------------------------------------------------------
685 // HelpGenVisitor implementation
686 // -----------------------------------------------------------------------------
688 HelpGenVisitor::HelpGenVisitor(const wxString
& directoryOut
,
690 : m_directoryOut(directoryOut
)
692 m_overwrite
= overwrite
;
697 void HelpGenVisitor::Reset()
702 m_inMethodSection
= FALSE
;
704 m_textStoredTypedefs
=
706 m_textStoredFunctionComment
= "";
710 void HelpGenVisitor::InsertTypedefDocs()
712 m_file
.WriteTeX(m_textStoredTypedefs
);
713 m_textStoredTypedefs
.Empty();
716 void HelpGenVisitor::InsertEnumDocs()
718 m_file
.WriteTeX(m_textStoredEnums
);
719 m_textStoredEnums
.Empty();
722 void HelpGenVisitor::InsertDataStructuresHeader()
724 if ( !m_inTypesSection
) {
725 m_inTypesSection
= TRUE
;
727 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
731 void HelpGenVisitor::InsertMethodsHeader()
733 if ( !m_inMethodSection
) {
734 m_inMethodSection
= TRUE
;
736 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
740 void HelpGenVisitor::CloseFunction()
742 if ( m_inFunction
) {
743 m_inFunction
= FALSE
;
746 if ( m_isFirstParam
) {
748 totalText
<< "\\void";
751 totalText
<< "}\n\n";
753 if ( !m_textStoredFunctionComment
.IsEmpty() )
754 totalText
<< m_textStoredFunctionComment
<< '\n';
756 m_file
.WriteTeX(totalText
);
760 void HelpGenVisitor::EndVisit()
764 m_fileHeader
.Empty();
766 wxLogVerbose("%s: finished generating for the current file.",
767 GetCurrentTime("%H:%M:%S"));
770 void HelpGenVisitor::VisitFile( spFile
& file
)
772 m_fileHeader
= file
.mFileName
;
773 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
774 GetCurrentTime("%H:%M:%S"), m_fileHeader
.c_str());
777 void HelpGenVisitor::VisitClass( spClass
& cl
)
779 m_inClass
= FALSE
; // will be left FALSE on error
781 wxString name
= cl
.GetName();
783 if ( m_ignoreNames
.IgnoreClass(name
) ) {
784 wxLogVerbose("Skipping ignored class '%s'.", name
.c_str());
789 // the file name is built from the class name by removing the leading "wx"
790 // if any and converting it to the lower case
791 wxString filename
= m_directoryOut
;
792 if ( name(0, 2) == "wx" ) {
793 filename
<< name
.c_str() + 2;
799 filename
.MakeLower();
802 if ( !m_overwrite
&& wxFile::Exists(filename
) ) {
803 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
809 m_inClass
= m_file
.Open(filename
, wxFile::write
);
811 wxLogError("Can't generate documentation for the class '%s'.",
818 m_inTypesSection
= FALSE
;
820 wxLogInfo("Created new file '%s' for class '%s'.",
821 filename
.c_str(), name
.c_str());
823 // the entire text we're writing to file
826 // write out the header
830 "%% automatically generated by HelpGen from\n"
835 "\\section{\\class{%s}}\\label{%s}\n",
836 m_fileHeader
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
837 name
.c_str(), wxString(name
).MakeLower().c_str());
839 totalText
<< header
<< '\n';
842 // if the header includes other headers they must be related to it... try to
843 // automatically generate the "See also" clause
844 if ( !m_headers
.IsEmpty() ) {
845 // correspondence between wxWindows headers and class names
846 static const char *headers
[] = {
855 // NULL here means not to insert anything in "See also" for the
856 // corresponding header
857 static const char *classes
[] = {
866 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
867 "arrays must be in sync!" );
869 wxArrayInt interestingClasses
;
871 size_t count
= m_headers
.Count(), index
;
872 for ( size_t n
= 0; n
< count
; n
++ ) {
873 wxString baseHeaderName
= m_headers
[n
].Before('.');
874 if ( baseHeaderName(0, 3) != "wx/" )
877 baseHeaderName
.erase(0, 3);
878 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
879 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
883 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
884 // interesting header
885 interestingClasses
.Add(index
);
889 if ( !interestingClasses
.IsEmpty() ) {
890 // do generate "See also" clause
891 totalText
<< "\\wxheading{See also:}\n\n";
893 count
= interestingClasses
.Count();
894 for ( index
= 0; index
< count
; index
++ ) {
898 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
905 // the comment before the class generally explains what is it for so put it
906 // in place of the class description
907 if ( cl
.HasComments() ) {
908 wxString comment
= GetAllComments(cl
);
910 totalText
<< '\n' << comment
<< '\n';
913 // derived from section
914 wxString derived
= "\\wxheading{Derived from}\n\n";
916 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
917 if ( baseClasses
.size() == 0 ) {
918 derived
<< "No base class";
922 for ( StrListT::const_iterator i
= baseClasses
.begin();
923 i
!= baseClasses
.end();
926 // separate from the previous one
933 wxString baseclass
= *i
;
934 derived
<< "\\helpref{" << baseclass
<< "}";
935 derived
<< "{" << baseclass
.MakeLower() << "}";
938 totalText
<< derived
<< "\n\n";
940 // write all this to file
941 m_file
.WriteTeX(totalText
);
943 // if there were any enums/typedefs before, insert their documentation now
944 InsertDataStructuresHeader();
949 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
953 if ( m_inMethodSection
) {
954 // FIXME that's a bug, but tell the user aboit it nevertheless... we
955 // should be smart enough to process even the enums which come after the
957 wxLogWarning("enum '%s' ignored, please put it before the class "
958 "methods.", en
.GetName().c_str());
962 // simply copy the enum text in the docs
963 wxString enumeration
= GetAllComments(en
);
964 enumeration
<< "{\\small \\begin{verbatim}\n"
966 << "\n\\end{verbatim}}\n";
968 // remember for later use if we're not inside a class yet
970 if ( !m_textStoredEnums
.IsEmpty() ) {
971 m_textStoredEnums
<< '\n';
974 m_textStoredEnums
<< enumeration
;
977 // write the header for this section if not done yet
978 InsertDataStructuresHeader();
981 m_file
.WriteTeX(enumeration
);
985 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
989 if ( m_inMethodSection
) {
990 // FIXME that's a bug, but tell the user aboit it nevertheless...
991 wxLogWarning("typedef '%s' ignored, please put it before the class "
992 "methods.", td
.GetName().c_str());
997 typedefdoc
<< "{\\small \\begin{verbatim}\n"
998 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
999 << "\n\\end{verbatim}}\n"
1000 << GetAllComments(td
);
1002 // remember for later use if we're not inside a class yet
1004 if ( !m_textStoredTypedefs
.IsEmpty() ) {
1005 m_textStoredTypedefs
<< '\n';
1008 m_textStoredTypedefs
<< typedefdoc
;
1011 // write the header for this section if not done yet
1012 InsertDataStructuresHeader();
1015 m_file
.WriteTeX(typedefdoc
);
1019 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
1021 switch ( pd
.GetStatementType() ) {
1022 case SP_PREP_DEF_INCLUDE_FILE
:
1023 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
1026 case SP_PREP_DEF_DEFINE_SYMBOL
:
1027 // TODO decide if it's a constant and document it if it is
1032 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
1036 // only document the public member variables
1037 if ( !m_inClass
|| !attr
.IsPublic() )
1040 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
1043 void HelpGenVisitor::VisitOperation( spOperation
& op
)
1048 // we don't generate docs right now - either we ignore this class
1049 // entirely or we couldn't open the file
1053 if ( !op
.IsInClass() ) {
1054 // TODO document global functions
1055 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
1060 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
1061 // FIXME should we document protected functions?
1065 wxString funcname
= op
.GetName(),
1066 classname
= op
.GetClass().GetName();
1067 if ( m_ignoreNames
.IgnoreMethod(classname
, funcname
) ) {
1068 wxLogVerbose("Skipping ignored '%s::%s'.",
1069 classname
.c_str(), funcname
.c_str());
1074 InsertMethodsHeader();
1078 m_isFirstParam
= TRUE
;
1080 m_textStoredFunctionComment
= GetAllComments(op
);
1082 // start function documentation
1085 // check for the special case of dtor
1087 if ( (funcname
[0] == '~') && (classname
== funcname
.c_str() + 1) ) {
1088 dtor
.Printf("\\destruct{%s}", classname
.c_str());
1092 totalText
.Printf("\n"
1093 "\\membersection{%s::%s}\\label{%s}\n"
1095 "\\%sfunc{%s%s}{%s}{",
1096 classname
.c_str(), funcname
.c_str(),
1097 MakeLabel(classname
, funcname
).c_str(),
1098 op
.mIsConstant
? "const" : "",
1099 op
.mIsVirtual
? "virtual " : "",
1100 op
.mRetType
.c_str(),
1103 m_file
.WriteTeX(totalText
);
1106 void HelpGenVisitor::VisitParameter( spParameter
& param
)
1108 if ( !m_inFunction
)
1112 if ( m_isFirstParam
) {
1113 m_isFirstParam
= FALSE
;
1119 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
1120 wxString defvalue
= param
.mInitVal
;
1121 if ( !defvalue
.IsEmpty() ) {
1122 totalText
<< " = " << defvalue
;
1127 m_file
.WriteTeX(totalText
);
1130 // ---------------------------------------------------------------------------
1132 // ---------------------------------------------------------------------------
1134 DocManager::DocManager(bool checkParamNames
)
1136 m_checkParamNames
= checkParamNames
;
1139 size_t DocManager::TryMatch(const char *str
, const char *match
)
1141 size_t lenMatch
= 0;
1142 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1145 if ( match
[lenMatch
] == '\0' )
1152 bool DocManager::SkipUntil(const char **pp
, char c
)
1154 const char *p
= *pp
;
1170 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1172 const char *p
= *pp
;
1174 if ( !isspace(*p
) || *p
== '\0' )
1188 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1192 if ( !SkipSpaceUntil(pp
, '{') ) {
1193 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1194 m_filename
.c_str(), m_line
);
1198 const char *startParam
= ++*pp
; // skip '{'
1200 if ( !SkipUntil(pp
, '}') ) {
1201 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1202 m_filename
.c_str(), m_line
);
1205 result
= wxString(startParam
, (*pp
)++ - startParam
);
1212 bool DocManager::ParseTeXFile(const wxString
& filename
)
1214 m_filename
= filename
;
1216 wxFile
file(m_filename
, wxFile::read
);
1217 if ( !file
.IsOpened() )
1220 off_t len
= file
.Length();
1221 if ( len
== wxInvalidOffset
)
1224 char *buf
= new char[len
+ 1];
1227 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1233 // reinit everything
1236 wxLogVerbose("%s: starting to parse doc file '%s'.",
1237 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1239 // the name of the class from the last "\membersection" command: we assume
1240 // that the following "\func" or "\constfunc" always documents a method of
1241 // this class (and it should always be like that in wxWindows documentation)
1244 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1245 // FIXME parsing is awfully inefficient
1247 if ( *current
== '%' ) {
1248 // comment, skip until the end of line
1250 SkipUntil(¤t
, '\n');
1255 // all the command we're interested in start with '\\'
1256 while ( *current
!= '\\' && *current
!= '\0' ) {
1257 if ( *current
++ == '\n' )
1261 if ( *current
== '\0' ) {
1262 // no more TeX commands left
1266 current
++; // skip '\\'
1274 } foundCommand
= Nothing
;
1276 size_t lenMatch
= TryMatch(current
, "func");
1278 foundCommand
= Func
;
1281 lenMatch
= TryMatch(current
, "constfunc");
1283 foundCommand
= ConstFunc
;
1285 lenMatch
= TryMatch(current
, "membersection");
1288 foundCommand
= MemberSect
;
1292 if ( foundCommand
== Nothing
)
1295 current
+= lenMatch
;
1297 if ( !SkipSpaceUntil(¤t
, '{') ) {
1298 wxLogWarning("file %s(%d): '{' expected after \\func, "
1299 "\\constfunc or \\membersection.",
1300 m_filename
.c_str(), m_line
);
1307 if ( foundCommand
== MemberSect
) {
1308 // what follows has the form <classname>::<funcname>
1309 const char *startClass
= current
;
1310 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1311 wxLogWarning("file %s(%d): '::' expected after "
1312 "\\membersection.", m_filename
.c_str(), m_line
);
1315 classname
= wxString(startClass
, current
- startClass
);
1316 TeXUnfilter(&classname
);
1322 // extract the return type
1323 const char *startRetType
= current
;
1325 if ( !SkipUntil(¤t
, '}') ) {
1326 wxLogWarning("file %s(%d): '}' expected after return type",
1327 m_filename
.c_str(), m_line
);
1332 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1333 TeXUnfilter(&returnType
);
1336 if ( !SkipSpaceUntil(¤t
, '{') ) {
1337 wxLogWarning("file %s(%d): '{' expected after return type",
1338 m_filename
.c_str(), m_line
);
1344 const char *funcEnd
= current
;
1345 if ( !SkipUntil(&funcEnd
, '}') ) {
1346 wxLogWarning("file %s(%d): '}' expected after function name",
1347 m_filename
.c_str(), m_line
);
1352 wxString funcName
= wxString(current
, funcEnd
- current
);
1353 current
= funcEnd
+ 1;
1355 // trim spaces from both sides
1356 funcName
.Trim(FALSE
);
1357 funcName
.Trim(TRUE
);
1359 // special cases: '$...$' may be used for LaTeX inline math, remove the
1361 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1363 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1364 if ( *p
!= '$' && !isspace(*p
) )
1371 // \destruct{foo} is really ~foo
1372 if ( funcName
[0u] == '\\' ) {
1373 size_t len
= strlen("\\destruct{");
1374 if ( funcName(0, len
) != "\\destruct{" ) {
1375 wxLogWarning("file %s(%d): \\destruct expected",
1376 m_filename
.c_str(), m_line
);
1381 funcName
.erase(0, len
);
1382 funcName
.Prepend('~');
1384 if ( !SkipSpaceUntil(¤t
, '}') ) {
1385 wxLogWarning("file %s(%d): '}' expected after destructor",
1386 m_filename
.c_str(), m_line
);
1391 funcEnd
++; // there is an extra '}' to count
1394 TeXUnfilter(&funcName
);
1397 current
= funcEnd
+ 1; // skip '}'
1398 if ( !SkipSpaceUntil(¤t
, '{') ||
1399 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1400 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1401 m_filename
.c_str(), m_line
);
1406 wxArrayString paramNames
, paramTypes
, paramValues
;
1408 bool isVararg
= FALSE
;
1410 current
++; // skip '\\'
1411 lenMatch
= TryMatch(current
, "void");
1413 lenMatch
= TryMatch(current
, "param");
1414 while ( lenMatch
&& (current
- buf
< len
) ) {
1415 current
+= lenMatch
;
1417 // now come {paramtype}{paramname}
1418 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1419 if ( !!paramType
) {
1420 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1421 if ( !!paramText
) {
1422 // the param declaration may contain default value
1423 wxString paramName
= paramText
.BeforeFirst('='),
1424 paramValue
= paramText
.AfterFirst('=');
1426 // sanitize all strings
1427 TeXUnfilter(¶mValue
);
1428 TeXUnfilter(¶mName
);
1429 TeXUnfilter(¶mType
);
1431 paramValues
.Add(paramValue
);
1432 paramNames
.Add(paramName
);
1433 paramTypes
.Add(paramType
);
1438 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1439 if ( paramText
== "..." ) {
1443 wxLogWarning("Parameters of '%s::%s' are in "
1445 classname
.c_str(), funcName
.c_str());
1450 current
= SkipSpaces(current
);
1451 if ( *current
== ',' || *current
== '}' ) {
1452 current
= SkipSpaces(++current
);
1454 lenMatch
= TryMatch(current
, "\\param");
1457 wxLogWarning("file %s(%d): ',' or '}' expected after "
1458 "'\\param'", m_filename
.c_str(), m_line
);
1464 // if we got here there was no '\\void', so must have some params
1465 if ( paramNames
.IsEmpty() ) {
1466 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1467 m_filename
.c_str(), m_line
);
1473 // verbose diagnostic output
1475 size_t param
, paramCount
= paramNames
.GetCount();
1476 for ( param
= 0; param
< paramCount
; param
++ ) {
1481 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1484 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1485 m_filename
.c_str(), m_line
,
1490 foundCommand
== ConstFunc
? " const" : "");
1492 // store the info about the just found function
1493 ArrayMethodInfo
*methods
;
1494 int index
= m_classes
.Index(classname
);
1495 if ( index
== wxNOT_FOUND
) {
1496 m_classes
.Add(classname
);
1498 methods
= new ArrayMethodInfo
;
1499 m_methods
.Add(methods
);
1502 methods
= m_methods
[(size_t)index
];
1505 ArrayParamInfo params
;
1506 for ( param
= 0; param
< paramCount
; param
++ ) {
1507 params
.Add(new ParamInfo(paramTypes
[param
],
1509 paramValues
[param
]));
1512 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1513 if ( foundCommand
== ConstFunc
)
1514 method
->SetFlag(MethodInfo::Const
);
1516 method
->SetFlag(MethodInfo::Vararg
);
1518 methods
->Add(method
);
1523 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1524 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1529 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1531 typedef MMemberListT::const_iterator MemberIndex
;
1533 bool foundDiff
= FALSE
;
1535 // flag telling us whether the given class was found at all in the header
1536 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1537 bool *classExists
= new bool[countClassesInDocs
];
1538 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1539 classExists
[nClass
] = FALSE
;
1542 // ctxTop is normally an spFile
1543 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1545 const MMemberListT
& classes
= ctxTop
->GetMembers();
1546 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1547 spContext
*ctx
= *i
;
1548 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1549 // TODO process also global functions, macros, ...
1553 spClass
*ctxClass
= (spClass
*)ctx
;
1554 const wxString
& nameClass
= ctxClass
->mName
;
1555 int index
= m_classes
.Index(nameClass
);
1556 if ( index
== wxNOT_FOUND
) {
1557 if ( !m_ignoreNames
.IgnoreClass(nameClass
) ) {
1560 wxLogError("Class '%s' is not documented at all.",
1564 // it makes no sense to check for its functions
1568 classExists
[index
] = TRUE
;
1571 // array of method descriptions for this class
1572 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1573 size_t nMethod
, countMethods
= methods
.GetCount();
1575 // flags telling if we already processed given function
1576 bool *methodExists
= new bool[countMethods
];
1577 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1578 methodExists
[nMethod
] = FALSE
;
1581 wxArrayString aOverloadedMethods
;
1583 const MMemberListT
& functions
= ctxClass
->GetMembers();
1584 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1586 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1589 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1590 const wxString
& nameMethod
= ctxMethod
->mName
;
1592 // find all functions with the same name
1593 wxArrayInt aMethodsWithSameName
;
1594 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1595 if ( methods
[nMethod
]->GetName() == nameMethod
)
1596 aMethodsWithSameName
.Add(nMethod
);
1599 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1600 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1603 wxLogError("'%s::%s' is not documented.",
1605 nameMethod
.c_str());
1608 // don't check params
1611 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1612 index
= (size_t)aMethodsWithSameName
[0u];
1613 methodExists
[index
] = TRUE
;
1615 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1618 if ( !ctxMethod
->IsPublic() ) {
1619 wxLogWarning("'%s::%s' is documented but not public.",
1621 nameMethod
.c_str());
1624 // check that the flags match
1625 const MethodInfo
& method
= *(methods
[index
]);
1627 bool isVirtual
= ctxMethod
->mIsVirtual
;
1628 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1629 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1633 isVirtual
? "not " : "");
1636 bool isConst
= ctxMethod
->mIsConstant
;
1637 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1638 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1642 isConst
? "not " : "");
1645 // check that the params match
1646 const MMemberListT
& params
= ctxMethod
->GetMembers();
1648 if ( params
.size() != method
.GetParamCount() ) {
1649 wxLogError("Incorrect number of parameters for '%s::%s' "
1650 "in the docs: should be %d instead of %d.",
1653 params
.size(), method
.GetParamCount());
1657 for ( MemberIndex k
= params
.begin();
1662 // what else can a function have?
1663 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1665 spParameter
*ctxParam
= (spParameter
*)ctx
;
1666 const ParamInfo
& param
= method
.GetParam(nParam
);
1667 if ( m_checkParamNames
&&
1668 (param
.GetName() != ctxParam
->mName
) ) {
1671 wxLogError("Parameter #%d of '%s::%s' should be "
1672 "'%s' and not '%s'.",
1676 ctxParam
->mName
.c_str(),
1677 param
.GetName().c_str());
1682 if ( param
.GetType() != ctxParam
->mType
) {
1685 wxLogError("Type of parameter '%s' of '%s::%s' "
1686 "should be '%s' and not '%s'.",
1687 ctxParam
->mName
.c_str(),
1690 ctxParam
->mType
.c_str(),
1691 param
.GetType().GetName().c_str());
1696 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1697 wxLogWarning("Default value of parameter '%s' of "
1698 "'%s::%s' should be '%s' and not "
1700 ctxParam
->mName
.c_str(),
1703 ctxParam
->mInitVal
.c_str(),
1704 param
.GetDefValue().c_str());
1710 // TODO OVER add real support for overloaded methods
1712 if ( m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) )
1715 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1716 // mark all methods with this name as existing
1717 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1718 if ( methods
[nMethod
]->GetName() == nameMethod
)
1719 methodExists
[nMethod
] = TRUE
;
1722 aOverloadedMethods
.Add(nameMethod
);
1724 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1725 "stupid to find the right match - skipping "
1726 "the param and flags checks.",
1728 nameMethod
.c_str());
1730 //else: warning already given
1734 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1735 if ( !methodExists
[nMethod
] ) {
1736 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1737 if ( !m_ignoreNames
.IgnoreMethod(nameClass
, nameMethod
) ) {
1740 wxLogError("'%s::%s' is documented but doesn't exist.",
1742 nameMethod
.c_str());
1747 delete [] methodExists
;
1750 // check that all classes we found in the docs really exist
1751 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1752 if ( !classExists
[nClass
] ) {
1755 wxLogError("Class '%s' is documented but doesn't exist.",
1756 m_classes
[nClass
].c_str());
1760 delete [] classExists
;
1765 DocManager::~DocManager()
1767 WX_CLEAR_ARRAY(m_methods
);
1770 // ---------------------------------------------------------------------------
1771 // IgnoreNamesHandler implementation
1772 // ---------------------------------------------------------------------------
1774 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1775 IgnoreListEntry
*second
)
1777 // first compare the classes
1778 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1780 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1785 bool IgnoreNamesHandler::AddNamesFromFile(const wxString
& filename
)
1787 wxFile
file(filename
, wxFile::read
);
1788 if ( !file
.IsOpened() )
1791 off_t len
= file
.Length();
1792 if ( len
== wxInvalidOffset
)
1795 char *buf
= new char[len
+ 1];
1798 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1805 for ( const char *current
= buf
; ; current
++ ) {
1807 // skip DOS line separator
1808 if ( *current
== '\r' )
1812 if ( *current
== '\n' || *current
== '\0' ) {
1813 if ( line
[0u] != '#' ) {
1814 if ( line
.Find(':') != wxNOT_FOUND
) {
1815 wxString classname
= line
.BeforeFirst(':'),
1816 funcname
= line
.AfterLast(':');
1817 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1821 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1826 if ( *current
== '\0' )
1841 // -----------------------------------------------------------------------------
1842 // global function implementation
1843 // -----------------------------------------------------------------------------
1845 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1847 wxString
label(classname
);
1848 if ( funcname
&& funcname
[0] == '\\' ) {
1849 // we may have some special TeX macro - so far only \destruct exists,
1850 // but may be later others will be added
1851 static const char *macros
[] = { "destruct" };
1852 static const char *replacement
[] = { "dtor" };
1855 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1856 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1862 if ( n
== WXSIZEOF(macros
) ) {
1863 wxLogWarning("unknown function name '%s' - leaving as is.",
1867 funcname
= replacement
[n
];
1879 static wxString
MakeHelpref(const char *argument
)
1882 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1887 static void TeXUnfilter(wxString
* str
)
1889 // FIXME may be done much more quickly
1893 str
->Replace("\\&", "&");
1894 str
->Replace("\\_", "_");
1897 static void TeXFilter(wxString
* str
)
1899 // FIXME may be done much more quickly
1900 str
->Replace("&", "\\&");
1901 str
->Replace("_", "\\_");
1904 static wxString
GetAllComments(const spContext
& ctx
)
1907 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1908 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1909 i
!= commentsList
.end();
1911 wxString comment
= (*i
)->GetText();
1913 // don't take comments like "// ----------" &c
1914 comment
.Trim(FALSE
);
1916 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1919 comments
<< comment
;
1925 static const char *GetCurrentTime(const char *timeFormat
)
1927 static char s_timeBuffer
[128];
1932 ptmNow
= localtime(&timeNow
);
1934 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1936 return s_timeBuffer
;
1941 Revision 1.10 2000/03/11 10:05:23 VS
1942 now compiles with wxBase
1944 Revision 1.9 2000/01/16 13:25:21 VS
1945 compilation fixes (gcc)
1947 Revision 1.8 1999/09/13 14:29:39 JS
1949 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1950 into src for simplicity; added VC++ 5 project file
1952 Revision 1.7 1999/02/21 22:32:32 VZ
1953 1. more C++ parser fixes - now it almost parses wx/string.h
1954 a) #if/#ifdef/#else (very) limited support
1955 b) param type fix - now indirection chars are correctly handled
1956 c) class/struct/union distinction
1957 d) public/private fixes
1958 e) Dump() function added - very useful for debugging
1960 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1961 by default, and this option switches it on)
1963 Revision 1.6 1999/02/20 23:00:26 VZ
1964 1. new 'diff' mode which seems to work
1965 2. output files are not overwritten in 'dmup' mode
1966 3. fixes for better handling of const functions and operators
1967 ----------------------------
1969 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1970 1. Parser improvements
1971 a) const and virtual methods are parsed correctly (not static yet)
1972 b) "const" which is part of the return type is not swallowed
1974 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1975 "//---------" kind comments discarded now.
1976 ----------------------------
1978 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1980 some tweaks to HelpGen
1981 ----------------------------
1983 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1985 HelpGen starting to compile with VC++
1986 ----------------------------
1988 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
1990 supports typedefs, generates "See also:" and adds "virtual " for virtual
1992 ----------------------------
1994 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
1996 HelpGen is a prototype of the tool for automatic generation of the .tex files
1997 for wxWindows documentation from C++ headers
2000 /* vi: set tw=80 et ts=4 sw=4: */