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
37 (iii) plans for version 3
38 1. Merging with existing files
42 // =============================================================================
44 // =============================================================================
46 // -----------------------------------------------------------------------------
48 // -----------------------------------------------------------------------------
51 #include "wx/wxprec.h"
54 #include <wx/string.h>
56 #include <wx/dynarray.h>
61 // C++ parsing classes
68 // -----------------------------------------------------------------------------
70 // -----------------------------------------------------------------------------
72 // just a copy of argv
73 static char **g_argv
= NULL
;
75 // -----------------------------------------------------------------------------
77 // -----------------------------------------------------------------------------
79 // return the label for the given function name (i.e. argument of \label)
80 static wxString
MakeLabel(const char *classname
, const char *funcname
= NULL
);
82 // return the whole \helpref{arg}{arg_label} string
83 static wxString
MakeHelpref(const char *argument
);
85 // [un]quote special TeX characters (in place)
86 static void TeXFilter(wxString
* str
);
87 static void TeXUnfilter(wxString
* str
); // also trims spaces
89 // get all comments associated with this context
90 static wxString
GetAllComments(const spContext
& ctx
);
92 // get the string with current time (returns pointer to static buffer)
93 // timeFormat is used for the call of strftime(3)
98 static const char *GetCurrentTime(const char *timeFormat
);
100 // -----------------------------------------------------------------------------
102 // -----------------------------------------------------------------------------
104 // add a function which sanitazes the string before writing it to the file
105 class wxTeXFile
: public wxFile
110 bool WriteTeX(const wxString
& s
)
115 return wxFile::Write(t
);
119 wxTeXFile(const wxTeXFile
&);
120 wxTeXFile
& operator=(const wxTeXFile
&);
123 // visitor implementation which writes all collected data to a .tex file
124 class HelpGenVisitor
: public spVisitor
128 HelpGenVisitor(const wxString
& directoryOut
) : m_directoryOut(directoryOut
)
133 virtual void VisitFile( spFile
& fl
);
134 virtual void VisitClass( spClass
& cl
);
135 virtual void VisitEnumeration( spEnumeration
& en
);
136 virtual void VisitTypeDef( spTypeDef
& td
);
137 virtual void VisitPreprocessorLine( spPreprocessorLine
& pd
);
138 virtual void VisitAttribute( spAttribute
& attr
);
139 virtual void VisitOperation( spOperation
& op
);
140 virtual void VisitParameter( spParameter
& param
);
144 // shut up g++ warning (ain't it stupid?)
145 virtual ~HelpGenVisitor() { }
148 // (re)initialize the state
151 // insert documentation for enums/typedefs coming immediately before the
152 // class declaration into the class documentation
153 void InsertTypedefDocs();
154 void InsertEnumDocs();
156 // write the headers for corresponding sections (only once)
157 void InsertDataStructuresHeader();
158 void InsertMethodsHeader();
160 // terminate the function documentation if it was started
161 void CloseFunction();
163 wxString m_directoryOut
; // directory for the output
164 wxTeXFile m_file
; // file we're writing to now
167 bool m_inClass
, // TRUE after file successfully opened
168 m_inTypesSection
, // enums & typedefs go there
169 m_inMethodSection
, // functions go here
170 m_isFirstParam
, // first parameter of current function?
171 m_inFunction
; // we're parsing a function declaration
173 // holders for "saved" documentation
174 wxString m_textStoredEnums
,
175 m_textStoredTypedefs
,
176 m_textStoredFunctionComment
;
178 // headers included by this file
179 wxArrayString m_headers
;
182 HelpGenVisitor(const HelpGenVisitor
&);
183 HelpGenVisitor
& operator=(const HelpGenVisitor
&);
186 // documentation manager - a class which parses TeX files and remembers the
187 // functions documented in them and can later compare them with all functions
188 // found under ctxTop by C++ parser
192 DocManager() : m_ignore(CompareIgnoreListEntries
) { }
195 // load file with class names and function names to ignore during diff
196 bool LoadIgnoreFile(const wxString
& filename
);
198 // returns FALSE on failure
199 bool ParseTeXFile(const wxString
& filename
);
201 // returns FALSE if there were any differences
202 bool DumpDifferences(spContext
*ctxTop
) const;
208 // returns the length of 'match' if the string 'str' starts with it or 0
210 static size_t TryMatch(const char *str
, const char *match
);
212 // skip spaces: returns pointer to first non space character (also
213 // updates the value of m_line)
214 const char *SkipSpaces(const char *p
)
216 while ( isspace(*p
) ) {
224 // skips characters until the next 'c' in '*pp' unless it ends before in
225 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
226 // returned and pp points to 'c'
227 bool SkipUntil(const char **pp
, char c
);
229 // the same as SkipUntil() but only spaces are skipped: on first non space
230 // character different from 'c' the function stops and returns FALSE
231 bool SkipSpaceUntil(const char **pp
, char c
);
233 // extract the string between {} and modify '*pp' to point at the
234 // character immediately after the closing '}'. The returned string is empty
236 wxString
ExtractStringBetweenBraces(const char **pp
);
238 // the current file and line while we're in ParseTeXFile (for error
243 // functions and classes to ignore during diff
244 // -------------------------------------------
245 struct IgnoreListEntry
247 IgnoreListEntry(const wxString
& classname
,
248 const wxString
& funcname
)
249 : m_classname(classname
), m_funcname(funcname
)
253 wxString m_classname
;
254 wxString m_funcname
; // if empty, ignore class entirely
257 static int CompareIgnoreListEntries(IgnoreListEntry
*first
,
258 IgnoreListEntry
*second
);
260 // for efficiency, let's sort it
261 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry
*, ArrayNamesToIgnore
);
263 ArrayNamesToIgnore m_ignore
;
265 // return TRUE if we ignore this function
266 bool IgnoreMethod(const wxString
& classname
,
267 const wxString
& funcname
) const
269 IgnoreListEntry
ignore(classname
, funcname
);
271 return m_ignore
.Index(&ignore
) != wxNOT_FOUND
;
274 // return TRUE if we ignore this class entirely
275 bool IgnoreClass(const wxString
& classname
) const
277 return IgnoreMethod(classname
, "");
280 // information about all functions documented in the TeX file(s)
281 // -------------------------------------------------------------
283 // info about a type: for now stored as text string, but must be parsed
284 // further later (to know that "char *" == "char []" - TODO)
288 TypeInfo(const wxString
& type
) : m_type(type
) { }
290 bool operator==(const wxString
& type
) const { return m_type
== type
; }
291 bool operator!=(const wxString
& type
) const { return m_type
!= type
; }
293 const wxString
& GetName() const { return m_type
; }
299 // info abotu a function parameter
303 ParamInfo(const wxString
& type
,
304 const wxString
& name
,
305 const wxString
& value
)
306 : m_type(type
), m_name(name
), m_value(value
)
310 const TypeInfo
& GetType() const { return m_type
; }
311 const wxString
& GetName() const { return m_name
; }
312 const wxString
& GetDefValue() const { return m_value
; }
315 TypeInfo m_type
; // type of parameter
316 wxString m_name
; // name
317 wxString m_value
; // default value
320 WX_DEFINE_ARRAY(ParamInfo
*, ArrayParamInfo
);
322 // info about a function
335 MethodInfo(const wxString
& type
,
336 const wxString
& name
,
337 const ArrayParamInfo
& params
)
338 : m_typeRet(type
), m_name(name
), m_params(params
)
343 void SetFlag(MethodFlags flag
) { m_flags
|= flag
; }
345 const TypeInfo
& GetType() const { return m_typeRet
; }
346 const wxString
& GetName() const { return m_name
; }
347 const ParamInfo
& GetParam(size_t n
) const { return *(m_params
[n
]); }
348 size_t GetParamCount() const { return m_params
.GetCount(); }
350 bool HasFlag(MethodFlags flag
) const { return (m_flags
& flag
) != 0; }
352 ~MethodInfo() { WX_CLEAR_ARRAY(m_params
); }
355 TypeInfo m_typeRet
; // return type
357 int m_flags
; // bit mask of the value from the enum above
359 ArrayParamInfo m_params
;
362 WX_DEFINE_ARRAY(MethodInfo
*, ArrayMethodInfo
);
363 WX_DEFINE_ARRAY(ArrayMethodInfo
*, ArrayMethodInfos
);
365 // first array contains the names of all classes we found, the second has a
366 // pointer to the array of methods of the given class at the same index as
367 // the class name appears in m_classes
368 wxArrayString m_classes
;
369 ArrayMethodInfos m_methods
;
372 // -----------------------------------------------------------------------------
374 // -----------------------------------------------------------------------------
376 // =============================================================================
378 // =============================================================================
380 // this function never returns
383 wxString prog
= g_argv
[0];
384 wxString basename
= prog
.BeforeLast('/');
387 basename
= prog
.BeforeLast('\\');
393 "usage: %s [global options] <mode> [mode options] <files...>\n"
395 " where global options are:\n"
398 " -H give this usage message\n"
399 " -V print the version info\n"
401 " where mode is one of: dump, diff\n"
403 " dump means generate .tex files for TeX2RTF converter from specified\n"
404 " headers files, mode options are:\n"
405 " -o outdir directory for generated files\n"
407 " diff means compare the set of methods documented .tex file with the\n"
408 " methods declared in the header:\n"
409 " %s diff <file.h> <files.tex...>.\n"
411 " -i file file with classes/function to ignore during diff\n"
412 "\n", basename
.c_str(), basename
.c_str());
417 int main(int argc
, char **argv
)
432 wxArrayString filesH
, filesTeX
;
433 wxString directoryOut
, ignoreFile
;
435 for ( int current
= 1; current
< argc
; current
++ ) {
436 // all options have one letter
437 if ( argv
[current
][0] == '-' ) {
438 if ( argv
[current
][2] == '\0' ) {
439 switch ( argv
[current
][1] ) {
442 wxLog::GetActiveTarget()->SetVerbose();
447 wxLog::GetActiveTarget()->SetVerbose(FALSE
);
455 if ( mode
!= Mode_Diff
) {
456 wxLogError("-i is only valid with diff.");
462 if ( current
>= argc
) {
463 wxLogError("-i option requires an argument.");
468 ignoreFile
= argv
[current
];
472 if ( mode
!= Mode_Dump
) {
473 wxLogError("-o is only valid with dump.");
479 if ( current
>= argc
) {
480 wxLogError("-o option requires an argument.");
485 directoryOut
= argv
[current
];
486 if ( !!directoryOut
) {
487 // terminate with a '/' if it doesn't have it
488 switch ( directoryOut
.Last() ) {
499 //else: it's empty, do nothing
508 // only get here after a break from switch or from else branch of if
509 wxLogError("unknown option '%s'", argv
[current
]);
514 if ( mode
== Mode_None
) {
515 if ( strcmp(argv
[current
], "diff") == 0 )
517 else if ( strcmp(argv
[current
], "dump") == 0 )
520 wxLogError("unknown mode '%s'.");
526 if ( mode
== Mode_Dump
|| filesH
.IsEmpty() ) {
527 filesH
.Add(argv
[current
]);
530 // 2nd files and further are TeX files in diff mode
531 wxASSERT( mode
== Mode_Diff
);
533 filesTeX
.Add(argv
[current
]);
539 // create a parser object and a visitor derivation
540 CJSourceParser parser
;
541 HelpGenVisitor
visitor(directoryOut
);
542 spContext
*ctxTop
= NULL
;
544 // parse all header files
545 size_t nFiles
= filesH
.GetCount();
546 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
547 wxString header
= filesH
[n
];
548 ctxTop
= parser
.ParseFile(header
);
550 wxLogWarning("Header file '%s' couldn't be processed.",
553 else if ( mode
== Mode_Dump
) {
554 ((spFile
*)ctxTop
)->mFileName
= header
;
555 visitor
.VisitAll(*ctxTop
);
560 // parse all TeX files
561 if ( mode
== Mode_Diff
) {
563 wxLogError("Can't complete diff.");
571 size_t nFiles
= filesTeX
.GetCount();
572 for ( size_t n
= 0; n
< nFiles
; n
++ ) {
573 wxString file
= filesTeX
[n
];
574 if ( !docman
.ParseTeXFile(file
) ) {
575 wxLogWarning("TeX file '%s' couldn't be processed.",
581 docman
.LoadIgnoreFile(ignoreFile
);
583 docman
.DumpDifferences(ctxTop
);
589 // -----------------------------------------------------------------------------
590 // HelpGenVisitor implementation
591 // -----------------------------------------------------------------------------
593 void HelpGenVisitor::Reset()
598 m_inMethodSection
= FALSE
;
600 m_textStoredTypedefs
=
602 m_textStoredFunctionComment
= "";
606 void HelpGenVisitor::InsertTypedefDocs()
608 m_file
.WriteTeX(m_textStoredTypedefs
);
609 m_textStoredTypedefs
.Empty();
612 void HelpGenVisitor::InsertEnumDocs()
614 m_file
.WriteTeX(m_textStoredEnums
);
615 m_textStoredEnums
.Empty();
618 void HelpGenVisitor::InsertDataStructuresHeader()
620 if ( !m_inTypesSection
) {
621 m_inTypesSection
= TRUE
;
623 m_file
.WriteTeX("\\wxheading{Data structures}\n\n");
627 void HelpGenVisitor::InsertMethodsHeader()
629 if ( !m_inMethodSection
) {
630 m_inMethodSection
= TRUE
;
632 m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
636 void HelpGenVisitor::CloseFunction()
638 if ( m_inFunction
) {
639 m_inFunction
= FALSE
;
642 if ( m_isFirstParam
) {
644 totalText
<< "\\void";
647 totalText
<< "}\n\n";
649 if ( !m_textStoredFunctionComment
.IsEmpty() )
650 totalText
<< m_textStoredFunctionComment
<< '\n';
652 m_file
.WriteTeX(totalText
);
656 void HelpGenVisitor::EndVisit()
660 wxLogVerbose("%s: finished generating for the current file.",
661 GetCurrentTime("%H:%M:%S"));
664 void HelpGenVisitor::VisitFile( spFile
& file
)
666 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
667 GetCurrentTime("%H:%M:%S"), file
.mFileName
.c_str());
670 void HelpGenVisitor::VisitClass( spClass
& cl
)
672 wxString name
= cl
.GetName();
674 // the file name is built from the class name by removing the leading "wx"
675 // if any and converting it to the lower case
676 wxString filename
= m_directoryOut
;
677 if ( name(0, 2) == "wx" ) {
678 filename
<< name
.c_str() + 2;
684 filename
.MakeLower();
687 if ( wxFile::Exists(filename
) ) {
688 wxLogError("Won't overwrite existing file '%s' - please use '-o'.",
696 m_inClass
= m_file
.Open(filename
, wxFile::write
);
698 wxLogError("Can't generate documentation for the class '%s'.",
705 m_inTypesSection
= FALSE
;
707 wxLogInfo("Created new file '%s' for class '%s'.",
708 filename
.c_str(), name
.c_str());
710 // the entire text we're writing to file
713 // write out the header
717 "%% automatically generated by HelpGen from\n"
722 "\\section{\\class{%s}}\\label{%s}\n",
723 filename
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
724 name
.c_str(), wxString(name
).MakeLower().c_str());
726 totalText
<< header
<< '\n';
729 // if the header includes other headers they must be related to it... try to
730 // automatically generate the "See also" clause
731 if ( !m_headers
.IsEmpty() ) {
732 // correspondence between wxWindows headers and class names
733 static const char *headers
[] = {
742 // NULL here means not to insert anything in "See also" for the
743 // corresponding header
744 static const char *classes
[] = {
753 wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
),
754 "arrays must be in sync!" );
756 wxArrayInt interestingClasses
;
758 size_t count
= m_headers
.Count(), index
;
759 for ( size_t n
= 0; n
< count
; n
++ ) {
760 wxString baseHeaderName
= m_headers
[n
].Before('.');
761 if ( baseHeaderName(0, 3) != "wx/" )
764 baseHeaderName
.erase(0, 3);
765 for ( index
= 0; index
< WXSIZEOF(headers
); index
++ ) {
766 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 )
770 if ( (index
< WXSIZEOF(headers
)) && classes
[index
] ) {
771 // interesting header
772 interestingClasses
.Add(index
);
776 if ( !interestingClasses
.IsEmpty() ) {
777 // do generate "See also" clause
778 totalText
<< "\\wxheading{See also:}\n\n";
780 count
= interestingClasses
.Count();
781 for ( index
= 0; index
< count
; index
++ ) {
785 totalText
<< MakeHelpref(classes
[interestingClasses
[index
]]);
792 // the comment before the class generally explains what is it for so put it
793 // in place of the class description
794 if ( cl
.HasComments() ) {
795 wxString comment
= GetAllComments(cl
);
797 totalText
<< '\n' << comment
<< '\n';
800 // derived from section
801 wxString derived
= "\\wxheading{Derived from}\n\n";
803 const StrListT
& baseClasses
= cl
.mSuperClassNames
;
804 if ( baseClasses
.size() == 0 ) {
805 derived
<< "No base class";
809 for ( StrListT::const_iterator i
= baseClasses
.begin();
810 i
!= baseClasses
.end();
813 // separate from the previous one
820 wxString baseclass
= *i
;
821 derived
<< "\\helpref{" << baseclass
<< "}";
822 derived
<< "{" << baseclass
.MakeLower() << "}";
825 totalText
<< derived
<< "\n\n";
827 // write all this to file
828 m_file
.WriteTeX(totalText
);
830 // if there were any enums/typedefs before, insert their documentation now
831 InsertDataStructuresHeader();
836 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en
)
840 if ( m_inMethodSection
) {
841 // FIXME that's a bug, but tell the user aboit it nevertheless... we
842 // should be smart enough to process even the enums which come after the
844 wxLogWarning("enum '%s' ignored, please put it before the class "
845 "methods.", en
.GetName().c_str());
849 // simply copy the enum text in the docs
850 wxString enumeration
= GetAllComments(en
);
851 enumeration
<< "{\\small \\begin{verbatim}\n"
853 << "\n\\end{verbatim}}\n";
855 // remember for later use if we're not inside a class yet
857 if ( !m_textStoredEnums
.IsEmpty() ) {
858 m_textStoredEnums
<< '\n';
861 m_textStoredEnums
<< enumeration
;
864 // write the header for this section if not done yet
865 InsertDataStructuresHeader();
868 m_file
.WriteTeX(enumeration
);
872 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td
)
876 if ( m_inMethodSection
) {
877 // FIXME that's a bug, but tell the user aboit it nevertheless...
878 wxLogWarning("typedef '%s' ignored, please put it before the class "
879 "methods.", td
.GetName().c_str());
884 typedefdoc
<< "{\\small \\begin{verbatim}\n"
885 << "typedef " << td
.mOriginalType
<< ' ' << td
.GetName()
886 << "\n\\end{verbatim}}\n"
887 << GetAllComments(td
);
889 // remember for later use if we're not inside a class yet
891 if ( !m_textStoredTypedefs
.IsEmpty() ) {
892 m_textStoredTypedefs
<< '\n';
895 m_textStoredTypedefs
<< typedefdoc
;
898 // write the header for this section if not done yet
899 InsertDataStructuresHeader();
902 m_file
.WriteTeX(typedefdoc
);
906 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd
)
908 switch ( pd
.GetStatementType() ) {
909 case SP_PREP_DEF_INCLUDE_FILE
:
910 m_headers
.Add(pd
.CPP_GetIncludedFileNeme());
913 case SP_PREP_DEF_DEFINE_SYMBOL
:
914 // TODO decide if it's a constant and document it if it is
919 void HelpGenVisitor::VisitAttribute( spAttribute
& attr
)
923 // only document the public member variables
924 if ( !m_inClass
|| !attr
.IsPublic() )
927 wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str());
930 void HelpGenVisitor::VisitOperation( spOperation
& op
)
934 if ( !m_inClass
|| !op
.IsInClass() ) {
935 // FIXME that's a bug too
936 wxLogWarning("skipped global function '%s'.", op
.GetName().c_str());
941 if ( op
.mVisibility
== SP_VIS_PRIVATE
) {
942 // FIXME should we document protected functions?
946 InsertMethodsHeader();
950 m_isFirstParam
= TRUE
;
952 m_textStoredFunctionComment
= GetAllComments(op
);
954 // start function documentation
956 const char *funcname
= op
.GetName().c_str();
957 const char *classname
= op
.GetClass().GetName().c_str();
959 // check for the special case of dtor
961 if ( (funcname
[0] == '~') && (strcmp(funcname
+ 1, classname
) == 0) ) {
962 dtor
.Printf("\\destruct{%s}", classname
);
966 totalText
.Printf("\n"
967 "\\membersection{%s::%s}\\label{%s}\n"
969 "\\%sfunc{%s%s}{%s}{",
971 MakeLabel(classname
, funcname
).c_str(),
972 op
.mIsConstant
? "const" : "",
973 op
.mIsVirtual
? "virtual " : "",
977 m_file
.WriteTeX(totalText
);
980 void HelpGenVisitor::VisitParameter( spParameter
& param
)
986 if ( m_isFirstParam
) {
987 m_isFirstParam
= FALSE
;
993 totalText
<< "\\param{" << param
.mType
<< " }{" << param
.GetName();
994 wxString defvalue
= param
.mInitVal
;
995 if ( !defvalue
.IsEmpty() ) {
996 totalText
<< " = " << defvalue
;
1001 m_file
.WriteTeX(totalText
);
1004 // ---------------------------------------------------------------------------
1006 // ---------------------------------------------------------------------------
1008 size_t DocManager::TryMatch(const char *str
, const char *match
)
1010 size_t lenMatch
= 0;
1011 while ( str
[lenMatch
] == match
[lenMatch
] ) {
1014 if ( match
[lenMatch
] == '\0' )
1021 bool DocManager::SkipUntil(const char **pp
, char c
)
1023 const char *p
= *pp
;
1039 bool DocManager::SkipSpaceUntil(const char **pp
, char c
)
1041 const char *p
= *pp
;
1043 if ( !isspace(*p
) || *p
== '\0' )
1057 wxString
DocManager::ExtractStringBetweenBraces(const char **pp
)
1061 if ( !SkipSpaceUntil(pp
, '{') ) {
1062 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1063 m_filename
.c_str(), m_line
);
1067 const char *startParam
= ++*pp
; // skip '{'
1069 if ( !SkipUntil(pp
, '}') ) {
1070 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1071 m_filename
.c_str(), m_line
);
1074 result
= wxString(startParam
, (*pp
)++ - startParam
);
1081 bool DocManager::ParseTeXFile(const wxString
& filename
)
1083 m_filename
= filename
;
1085 wxFile
file(m_filename
, wxFile::read
);
1086 if ( !file
.IsOpened() )
1089 off_t len
= file
.Length();
1090 if ( len
== wxInvalidOffset
)
1093 char *buf
= new char[len
+ 1];
1096 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1102 // reinit everything
1105 wxLogVerbose("%s: starting to parse doc file '%s'.",
1106 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1108 // the name of the class from the last "\membersection" command: we assume
1109 // that the following "\func" or "\constfunc" always documents a method of
1110 // this class (and it should always be like that in wxWindows documentation)
1113 for ( const char *current
= buf
; current
- buf
< len
; current
++ ) {
1114 // FIXME parsing is awfully inefficient
1116 if ( *current
== '%' ) {
1117 // comment, skip until the end of line
1119 SkipUntil(¤t
, '\n');
1124 // all the command we're interested in start with '\\'
1125 while ( *current
!= '\\' && *current
!= '\0' ) {
1126 if ( *current
++ == '\n' )
1130 if ( *current
== '\0' ) {
1131 // no more TeX commands left
1135 current
++; // skip '\\'
1143 } foundCommand
= Nothing
;
1145 size_t lenMatch
= TryMatch(current
, "func");
1147 foundCommand
= Func
;
1150 lenMatch
= TryMatch(current
, "constfunc");
1152 foundCommand
= ConstFunc
;
1154 lenMatch
= TryMatch(current
, "membersection");
1157 foundCommand
= MemberSect
;
1161 if ( foundCommand
== Nothing
)
1164 current
+= lenMatch
;
1166 if ( !SkipSpaceUntil(¤t
, '{') ) {
1167 wxLogWarning("file %s(%d): '{' expected after \\func, "
1168 "\\constfunc or \\membersection.",
1169 m_filename
.c_str(), m_line
);
1176 if ( foundCommand
== MemberSect
) {
1177 // what follows has the form <classname>::<funcname>
1178 const char *startClass
= current
;
1179 if ( !SkipUntil(¤t
, ':') || *(current
+ 1) != ':' ) {
1180 wxLogWarning("file %s(%d): '::' expected after "
1181 "\\membersection.", m_filename
.c_str(), m_line
);
1184 classname
= wxString(startClass
, current
- startClass
);
1185 TeXUnfilter(&classname
);
1191 // extract the return type
1192 const char *startRetType
= current
;
1194 if ( !SkipUntil(¤t
, '}') ) {
1195 wxLogWarning("file %s(%d): '}' expected after return type",
1196 m_filename
.c_str(), m_line
);
1201 wxString returnType
= wxString(startRetType
, current
- startRetType
);
1202 TeXUnfilter(&returnType
);
1205 if ( !SkipSpaceUntil(¤t
, '{') ) {
1206 wxLogWarning("file %s(%d): '{' expected after return type",
1207 m_filename
.c_str(), m_line
);
1213 const char *funcEnd
= current
;
1214 if ( !SkipUntil(&funcEnd
, '}') ) {
1215 wxLogWarning("file %s(%d): '}' expected after function name",
1216 m_filename
.c_str(), m_line
);
1221 wxString funcName
= wxString(current
, funcEnd
- current
);
1222 current
= funcEnd
+ 1;
1224 // trim spaces from both sides
1225 funcName
.Trim(FALSE
);
1226 funcName
.Trim(TRUE
);
1228 // special cases: '$...$' may be used for LaTeX inline math, remove the
1230 if ( funcName
.Find('$') != wxNOT_FOUND
) {
1232 for ( const char *p
= funcName
.c_str(); *p
!= '\0'; p
++ ) {
1233 if ( *p
!= '$' && !isspace(*p
) )
1240 // \destruct{foo} is really ~foo
1241 if ( funcName
[0u] == '\\' ) {
1242 size_t len
= strlen("\\destruct{");
1243 if ( funcName(0, len
) != "\\destruct{" ) {
1244 wxLogWarning("file %s(%d): \\destruct expected",
1245 m_filename
.c_str(), m_line
);
1250 funcName
.erase(0, len
);
1251 funcName
.Prepend('~');
1253 if ( !SkipSpaceUntil(¤t
, '}') ) {
1254 wxLogWarning("file %s(%d): '}' expected after destructor",
1255 m_filename
.c_str(), m_line
);
1260 funcEnd
++; // there is an extra '}' to count
1263 TeXUnfilter(&funcName
);
1266 current
= funcEnd
+ 1; // skip '}'
1267 if ( !SkipSpaceUntil(¤t
, '{') ||
1268 (current
++, !SkipSpaceUntil(¤t
, '\\')) ) {
1269 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1270 m_filename
.c_str(), m_line
);
1275 wxArrayString paramNames
, paramTypes
, paramValues
;
1277 bool isVararg
= FALSE
;
1279 current
++; // skip '\\'
1280 lenMatch
= TryMatch(current
, "void");
1282 lenMatch
= TryMatch(current
, "param");
1283 while ( lenMatch
) {
1284 current
+= lenMatch
;
1286 // now come {paramtype}{paramname}
1287 wxString paramType
= ExtractStringBetweenBraces(¤t
);
1288 if ( !!paramType
) {
1289 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1290 if ( !!paramText
) {
1291 // the param declaration may contain default value
1292 wxString paramName
= paramText
.BeforeFirst('='),
1293 paramValue
= paramText
.AfterFirst('=');
1295 // sanitize all strings
1296 TeXUnfilter(¶mValue
);
1297 TeXUnfilter(¶mName
);
1298 TeXUnfilter(¶mType
);
1300 paramValues
.Add(paramValue
);
1301 paramNames
.Add(paramName
);
1302 paramTypes
.Add(paramType
);
1307 wxString paramText
= ExtractStringBetweenBraces(¤t
);
1308 if ( paramText
== "..." ) {
1312 wxLogWarning("Parameters of '%s::%s' are in "
1314 classname
.c_str(), funcName
.c_str());
1319 current
= SkipSpaces(current
);
1320 if ( *current
== ',' || *current
== '}' ) {
1321 current
= SkipSpaces(++current
);
1323 lenMatch
= TryMatch(current
, "\\param");
1326 wxLogWarning("file %s(%d): ',' or '}' expected after "
1327 "'\\param'", m_filename
.c_str(), m_line
);
1333 // if we got here there was no '\\void', so must have some params
1334 if ( paramNames
.IsEmpty() ) {
1335 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1336 m_filename
.c_str(), m_line
);
1342 // verbose diagnostic output
1344 size_t param
, paramCount
= paramNames
.GetCount();
1345 for ( param
= 0; param
< paramCount
; param
++ ) {
1350 paramsAll
<< paramTypes
[param
] << ' ' << paramNames
[param
];
1353 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1354 m_filename
.c_str(), m_line
,
1359 foundCommand
== ConstFunc
? " const" : "");
1361 // store the info about the just found function
1362 ArrayMethodInfo
*methods
;
1363 int index
= m_classes
.Index(classname
);
1364 if ( index
== wxNOT_FOUND
) {
1365 m_classes
.Add(classname
);
1367 methods
= new ArrayMethodInfo
;
1368 m_methods
.Add(methods
);
1371 methods
= m_methods
[(size_t)index
];
1374 ArrayParamInfo params
;
1375 for ( param
= 0; param
< paramCount
; param
++ ) {
1376 params
.Add(new ParamInfo(paramTypes
[param
],
1378 paramValues
[param
]));
1381 MethodInfo
*method
= new MethodInfo(returnType
, funcName
, params
);
1382 if ( foundCommand
== ConstFunc
)
1383 method
->SetFlag(MethodInfo::Const
);
1385 method
->SetFlag(MethodInfo::Vararg
);
1387 methods
->Add(method
);
1392 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1393 GetCurrentTime("%H:%M:%S"), m_filename
.c_str());
1398 bool DocManager::DumpDifferences(spContext
*ctxTop
) const
1400 typedef MMemberListT::const_iterator MemberIndex
;
1402 bool foundDiff
= FALSE
;
1404 // flag telling us whether the given class was found at all in the header
1405 size_t nClass
, countClassesInDocs
= m_classes
.GetCount();
1406 bool *classExists
= new bool[countClassesInDocs
];
1407 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1408 classExists
[nClass
] = FALSE
;
1411 // ctxTop is normally an spFile
1412 wxASSERT( ctxTop
->GetContextType() == SP_CTX_FILE
);
1414 const MMemberListT
& classes
= ctxTop
->GetMembers();
1415 for ( MemberIndex i
= classes
.begin(); i
!= classes
.end(); i
++ ) {
1416 spContext
*ctx
= *i
;
1417 if ( ctx
->GetContextType() != SP_CTX_CLASS
) {
1418 // TODO process also global functions, macros, ...
1422 spClass
*ctxClass
= (spClass
*)ctx
;
1423 const wxString
& nameClass
= ctxClass
->mName
;
1424 int index
= m_classes
.Index(nameClass
);
1425 if ( index
== wxNOT_FOUND
) {
1426 if ( !IgnoreClass(nameClass
) ) {
1429 wxLogError("Class '%s' is not documented at all.",
1433 // it makes no sense to check for its functions
1437 classExists
[index
] = TRUE
;
1440 // array of method descriptions for this class
1441 const ArrayMethodInfo
& methods
= *(m_methods
[index
]);
1442 size_t nMethod
, countMethods
= methods
.GetCount();
1444 // flags telling if we already processed given function
1445 bool *methodExists
= new bool[countMethods
];
1446 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1447 methodExists
[nMethod
] = FALSE
;
1450 wxArrayString aOverloadedMethods
;
1452 const MMemberListT
& functions
= ctxClass
->GetMembers();
1453 for ( MemberIndex j
= functions
.begin(); j
!= functions
.end(); j
++ ) {
1455 if ( ctx
->GetContextType() != SP_CTX_OPERATION
)
1458 spOperation
*ctxMethod
= (spOperation
*)ctx
;
1459 const wxString
& nameMethod
= ctxMethod
->mName
;
1461 // find all functions with the same name
1462 wxArrayInt aMethodsWithSameName
;
1463 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1464 if ( methods
[nMethod
]->GetName() == nameMethod
)
1465 aMethodsWithSameName
.Add(nMethod
);
1468 if ( aMethodsWithSameName
.IsEmpty() && ctxMethod
->IsPublic() ) {
1469 if ( !IgnoreMethod(nameClass
, nameMethod
) ) {
1472 wxLogError("'%s::%s' is not documented.",
1474 nameMethod
.c_str());
1477 // don't check params
1480 else if ( aMethodsWithSameName
.GetCount() == 1 ) {
1481 index
= (size_t)aMethodsWithSameName
[0u];
1482 methodExists
[index
] = TRUE
;
1484 if ( IgnoreMethod(nameClass
, nameMethod
) )
1487 if ( !ctxMethod
->IsPublic() ) {
1488 wxLogWarning("'%s::%s' is documented but not public.",
1490 nameMethod
.c_str());
1493 // check that the flags match
1494 const MethodInfo
& method
= *(methods
[index
]);
1496 bool isVirtual
= ctxMethod
->mIsVirtual
;
1497 if ( isVirtual
!= method
.HasFlag(MethodInfo::Virtual
) ) {
1498 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1502 isVirtual
? "not " : "");
1505 bool isConst
= ctxMethod
->mIsConstant
;
1506 if ( isConst
!= method
.HasFlag(MethodInfo::Const
) ) {
1507 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1511 isConst
? "not " : "");
1514 // check that the params match
1515 const MMemberListT
& params
= ctxMethod
->GetMembers();
1517 if ( params
.size() != method
.GetParamCount() ) {
1518 wxLogError("Incorrect number of parameters for '%s::%s' "
1519 "in the docs: should be %d instead of %d.",
1522 params
.size(), method
.GetParamCount());
1526 for ( MemberIndex k
= params
.begin();
1531 // what else can a function have?
1532 wxASSERT( ctx
->GetContextType() == SP_CTX_PARAMETER
);
1534 spParameter
*ctxParam
= (spParameter
*)ctx
;
1535 const ParamInfo
& param
= method
.GetParam(nParam
);
1536 if ( param
.GetName() != ctxParam
->mName
) {
1539 wxLogError("Parameter #%d of '%s::%s' should be "
1540 "'%s' and not '%s'.",
1544 ctxParam
->mName
.c_str(),
1545 param
.GetName().c_str());
1550 if ( param
.GetType() != ctxParam
->mType
) {
1553 wxLogError("Type of parameter '%s' of '%s::%s' "
1554 "should be '%s' and not '%s'.",
1555 ctxParam
->mName
.c_str(),
1558 ctxParam
->mType
.c_str(),
1559 param
.GetType().GetName().c_str());
1564 if ( param
.GetDefValue() != ctxParam
->mInitVal
) {
1565 wxLogWarning("Default value of parameter '%s' of "
1566 "'%s::%s' should be '%s' and not "
1568 ctxParam
->mName
.c_str(),
1571 ctxParam
->mInitVal
.c_str(),
1572 param
.GetDefValue().c_str());
1578 // TODO add real support for overloaded methods
1580 if ( IgnoreMethod(nameClass
, nameMethod
) )
1583 if ( aOverloadedMethods
.Index(nameMethod
) == wxNOT_FOUND
) {
1584 // mark all methods with this name as existing
1585 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1586 if ( methods
[nMethod
]->GetName() == nameMethod
)
1587 methodExists
[nMethod
] = TRUE
;
1590 aOverloadedMethods
.Add(nameMethod
);
1592 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1593 "stupid to find the right match - skipping "
1594 "the param and flags checks.",
1596 nameMethod
.c_str());
1598 //else: warning already given
1602 for ( nMethod
= 0; nMethod
< countMethods
; nMethod
++ ) {
1603 if ( !methodExists
[nMethod
] ) {
1604 const wxString
& nameMethod
= methods
[nMethod
]->GetName();
1605 if ( !IgnoreMethod(nameClass
, nameMethod
) ) {
1608 wxLogError("'%s::%s' is documented but doesn't exist.",
1610 nameMethod
.c_str());
1615 delete [] methodExists
;
1618 // check that all classes we found in the docs really exist
1619 for ( nClass
= 0; nClass
< countClassesInDocs
; nClass
++ ) {
1620 if ( !classExists
[nClass
] ) {
1623 wxLogError("Class '%s' is documented but doesn't exist.",
1624 m_classes
[nClass
].c_str());
1628 delete [] classExists
;
1633 DocManager::~DocManager()
1635 WX_CLEAR_ARRAY(m_methods
);
1636 WX_CLEAR_ARRAY(m_ignore
);
1639 int DocManager::CompareIgnoreListEntries(IgnoreListEntry
*first
,
1640 IgnoreListEntry
*second
)
1642 // first compare the classes
1643 int rc
= first
->m_classname
.Cmp(second
->m_classname
);
1645 rc
= first
->m_funcname
.Cmp(second
->m_funcname
);
1650 bool DocManager::LoadIgnoreFile(const wxString
& filename
)
1652 wxFile
file(filename
, wxFile::read
);
1653 if ( !file
.IsOpened() )
1656 off_t len
= file
.Length();
1657 if ( len
== wxInvalidOffset
)
1660 char *buf
= new char[len
+ 1];
1663 if ( file
.Read(buf
, len
) == wxInvalidOffset
) {
1670 for ( const char *current
= buf
; ; current
++ ) {
1672 // skip DOS line separator
1673 if ( *current
== '\r' )
1677 if ( *current
== '\n' || *current
== '\0' ) {
1678 if ( line
[0u] != '#' ) {
1679 if ( line
.Find(':') != wxNOT_FOUND
) {
1680 wxString classname
= line
.BeforeFirst(':'),
1681 funcname
= line
.AfterLast(':');
1682 m_ignore
.Add(new IgnoreListEntry(classname
, funcname
));
1686 m_ignore
.Add(new IgnoreListEntry(line
, ""));
1691 if ( *current
== '\0' )
1706 // -----------------------------------------------------------------------------
1707 // global function implementation
1708 // -----------------------------------------------------------------------------
1710 static wxString
MakeLabel(const char *classname
, const char *funcname
)
1712 wxString
label(classname
);
1713 if ( funcname
&& funcname
[0] == '\\' ) {
1714 // we may have some special TeX macro - so far only \destruct exists,
1715 // but may be later others will be added
1716 static const char *macros
[] = { "destruct" };
1717 static const char *replacement
[] = { "dtor" };
1720 for ( n
= 0; n
< WXSIZEOF(macros
); n
++ ) {
1721 if ( strncmp(funcname
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) {
1727 if ( n
== WXSIZEOF(macros
) ) {
1728 wxLogWarning("unknown function name '%s' - leaving as is.",
1732 funcname
= replacement
[n
];
1744 static wxString
MakeHelpref(const char *argument
)
1747 helpref
<< "\\helpref{" << argument
<< "}{" << MakeLabel(argument
) << '}';
1752 static void TeXUnfilter(wxString
* str
)
1754 // FIXME may be done much more quickly
1758 str
->Replace("\\&", "&");
1759 str
->Replace("\\_", "_");
1762 static void TeXFilter(wxString
* str
)
1764 // FIXME may be done much more quickly
1765 str
->Replace("&", "\\&");
1766 str
->Replace("_", "\\_");
1769 static wxString
GetAllComments(const spContext
& ctx
)
1772 const MCommentListT
& commentsList
= ctx
.GetCommentList();
1773 for ( MCommentListT::const_iterator i
= commentsList
.begin();
1774 i
!= commentsList
.end();
1776 wxString comment
= (*i
)->GetText();
1778 // don't take comments like "// ----------" &c
1779 comment
.Trim(FALSE
);
1781 comment
== wxString(comment
[0u], comment
.length() - 1) + '\n' )
1784 comments
<< comment
;
1790 static const char *GetCurrentTime(const char *timeFormat
)
1792 static char s_timeBuffer
[128];
1797 ptmNow
= localtime(&timeNow
);
1799 strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
);
1801 return s_timeBuffer
;
1806 Revision 1.6 1999/02/20 23:00:26 VZ
1807 1. new 'diff' mode which seems to work
1808 2. output files are not overwritten in 'dmup' mode
1809 3. fixes for better handling of const functions and operators
1813 /* vi: set tw=80 et ts=4 sw=4: */