1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        HelpGen.cpp
3 // Purpose:     Main program file for HelpGen
4 // Author:      Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
5 // Modified by:
6 // Created:     06/01/99
7 // RCS-ID:      $Id: HelpGen.cpp 34465 2005-05-31 17:47:46Z ABX $
8 // Copyright:   (c) 1999 VZ
9 // Licence:     wxWindows Licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 /*
13    BUGS
14 
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 *"
20 
21    TODO (+ means fixed), see also the change log at the end of the file.
22 
23    (i) small fixes in the current version
24 
25    +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
26     2. Document typedefs
27     3. Document global variables
28     4. Document #defines
29    +5. Program options
30     6. Include file name/line number in the "diff" messages?
31    +7. Support for vararg functions
32 
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)
37 
38    (iii) plans for version 3
39     1. Merging with existing files
40     2. GUI
41 */
42 
43 // =============================================================================
44 // declarations
45 // =============================================================================
46 
47 // -----------------------------------------------------------------------------
48 // headers
49 // -----------------------------------------------------------------------------
50 
51 // wxWidgets
52 #include "wx/wxprec.h"
53 
54 #ifdef __BORLANDC__
55     #pragma hdrstop
56 #endif
57 
58 #if wxUSE_UNICODE
59     #error "HelpGen doesn't build in Unicode mode"
60 #endif
61 
62 #ifndef WX_PRECOMP
63     #include "wx/string.h"
64     #include "wx/log.h"
65     #include "wx/dynarray.h"
66     #include "wx/app.h"
67 #endif // WX_PRECOMP
68 
69 #include "wx/file.h"
70 #include "wx/regex.h"
71 #include "wx/hash.h"
72 
73 // C++ parsing classes
74 #include "cjparser.h"
75 
76 // standard headers
77 #include <stdio.h>
78 #include <time.h>
79 
80 // -----------------------------------------------------------------------------
81 // private functions
82 // -----------------------------------------------------------------------------
83 
84 // return the label for the given function name (i.e. argument of \label)
85 static wxString MakeLabel(const wxChar *classname, const wxChar *funcname = NULL);
86 
87 // return the whole \helpref{arg}{arg_label} string
88 static wxString MakeHelpref(const wxChar *argument);
89 
90 // [un]quote special TeX characters (in place)
91 static void TeXFilter(wxString* str);
92 static void TeXUnfilter(wxString* str); // also trims spaces
93 
94 // get all comments associated with this context
95 static wxString GetAllComments(const spContext& ctx);
96 
97 // get the string with current time (returns pointer to static buffer)
98 // timeFormat is used for the call of strftime(3)
99 static const char *GetCurrentTimeFormatted(const char *timeFormat);
100 
101 // get the string containing the program version
102 static const wxString GetVersionString();
103 
104 // -----------------------------------------------------------------------------
105 // private classes
106 // -----------------------------------------------------------------------------
107 
108 // a function documentation entry
109 struct FunctionDocEntry
110 {
FunctionDocEntryFunctionDocEntry111     FunctionDocEntry(const wxString& name_, const wxString& text_)
112         : name(name_), text(text_) { }
113 
114     // the function name
115     wxString name;
116 
117     // the function doc text
118     wxString text;
119 
120     // sorting stuff
CompareFunctionDocEntry121     static int Compare(FunctionDocEntry **pp1, FunctionDocEntry **pp2)
122     {
123         // the methods should appear in the following order: ctors, dtor, all
124         // the rest in the alphabetical order
125         bool isCtor1 = (*pp1)->name == classname;
126         bool isCtor2 = (*pp2)->name == classname;
127 
128         if ( isCtor1 ) {
129             if ( isCtor2 ) {
130                 // we don't order the ctors because we don't know how to do it
131                 return 0;
132             }
133 
134             // ctor comes before non-ctor
135             return -1;
136         }
137         else {
138             if ( isCtor2 ) {
139                 // non-ctor must come after ctor
140                 return 1;
141             }
142 
143             wxString dtorname = wxString(_T("~")) + classname;
144 
145             // there is only one dtor, so the logic here is simpler
146             if ( (*pp1)->name == dtorname ) {
147                 return -1;
148             }
149             else if ( (*pp2)->name == dtorname ) {
150                 return 1;
151             }
152 
153             // two normal methods
154             return wxStrcmp((*pp1)->name, (*pp2)->name);
155         }
156     }
157 
158     static wxString classname;
159 };
160 
161 wxString FunctionDocEntry::classname;
162 
163 WX_DECLARE_OBJARRAY(FunctionDocEntry, FunctionDocEntries);
164 
165 #include "wx/arrimpl.cpp"
166 
167 WX_DEFINE_OBJARRAY(FunctionDocEntries);
168 
169 // add a function which sanitazes the string before writing it to the file and
170 // also capable of delaying output and sorting it before really writing it to
171 // the file (done from FlushAll())
172 class wxTeXFile : public wxFile
173 {
174 public:
wxTeXFile()175     wxTeXFile() { }
176 
177     // write a string to file verbatim (should only be used for the strings
178     // inside verbatim environment)
WriteVerbatim(const wxString & s)179     void WriteVerbatim(const wxString& s)
180     {
181         m_text += s;
182     }
183 
184     // write a string quoting TeX specials in it
WriteTeX(const wxString & s)185     void WriteTeX(const wxString& s)
186     {
187         wxString t(s);
188         TeXFilter(&t);
189 
190         m_text += t;
191     }
192 
193     // do write everything to file
FlushAll()194     bool FlushAll()
195     {
196         if ( m_text.empty() )
197             return true;
198 
199         if ( !Write(m_text) ) {
200             wxLogError(_T("Failed to output generated documentation."));
201 
202             return false;
203         }
204 
205         m_text.clear();
206 
207         return true;
208     }
209 
210 private:
211     wxTeXFile(const wxTeXFile&);
212     wxTeXFile& operator=(const wxTeXFile&);
213 
214     wxString m_text;
215 };
216 
217 // helper class which manages the classes and function names to ignore for
218 // the documentation purposes (used by both HelpGenVisitor and DocManager)
219 class IgnoreNamesHandler
220 {
221 public:
IgnoreNamesHandler()222     IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries) { }
~IgnoreNamesHandler()223     ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore); }
224 
225     // load file with classes/functions to ignore (add them to the names we
226     // already have)
227     bool AddNamesFromFile(const wxString& filename);
228 
229     // return true if we ignore this function
IgnoreMethod(const wxString & classname,const wxString & funcname) const230     bool IgnoreMethod(const wxString& classname,
231                       const wxString& funcname) const
232     {
233         if ( IgnoreClass(classname) )
234             return true;
235 
236         IgnoreListEntry ignore(classname, funcname);
237 
238         return m_ignore.Index(&ignore) != wxNOT_FOUND;
239     }
240 
241     // return true if we ignore this class entirely
IgnoreClass(const wxString & classname) const242     bool IgnoreClass(const wxString& classname) const
243     {
244         IgnoreListEntry ignore(classname, wxEmptyString);
245 
246         return m_ignore.Index(&ignore) != wxNOT_FOUND;
247     }
248 
249 protected:
250     struct IgnoreListEntry
251     {
IgnoreListEntryIgnoreNamesHandler::IgnoreListEntry252         IgnoreListEntry(const wxString& classname,
253                         const wxString& funcname)
254             : m_classname(classname), m_funcname(funcname)
255         {
256         }
257 
258         wxString m_classname;
259         wxString m_funcname;    // if empty, ignore class entirely
260     };
261 
262     static int CompareIgnoreListEntries(IgnoreListEntry *first,
263                                         IgnoreListEntry *second);
264 
265     // for efficiency, let's sort it
266 public: // FIXME: macro requires it
267     WX_DEFINE_SORTED_ARRAY(IgnoreListEntry *, ArrayNamesToIgnore);
268 
269 protected:
270     ArrayNamesToIgnore m_ignore;
271 
272 private:
273     IgnoreNamesHandler(const IgnoreNamesHandler&);
274     IgnoreNamesHandler& operator=(const IgnoreNamesHandler&);
275 };
276 
277 // visitor implementation which writes all collected data to a .tex file
278 class HelpGenVisitor : public spVisitor
279 {
280 public:
281     // ctor
282     HelpGenVisitor(const wxString& directoryOut, bool overwrite);
283 
284     virtual void VisitFile( spFile& fl );
285     virtual void VisitClass( spClass& cl );
286     virtual void VisitEnumeration( spEnumeration& en );
287     virtual void VisitTypeDef( spTypeDef& td );
288     virtual void VisitPreprocessorLine( spPreprocessorLine& pd );
289     virtual void VisitAttribute( spAttribute& attr );
290     virtual void VisitOperation( spOperation& op );
291     virtual void VisitParameter( spParameter& param );
292 
293     void EndVisit();
294 
295     // get our `ignore' object
GetIgnoreHandler()296     IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
297 
298     // shut up g++ warning (ain't it stupid?)
~HelpGenVisitor()299     virtual ~HelpGenVisitor() { }
300 
301 protected:
302     // (re)initialize the state
303     void Reset();
304 
305     // insert documentation for enums/typedefs coming immediately before the
306     // class declaration into the class documentation
307     void InsertTypedefDocs();
308     void InsertEnumDocs();
309 
310     // write the headers for corresponding sections (only once)
311     void InsertDataStructuresHeader();
312     void InsertMethodsHeader();
313 
314     // terminate the function documentation if it was started
315     void CloseFunction();
316 
317     // write out all function docs when there are no more left in this class
318     // after sorting them in alphabetical order
319     void CloseClass();
320 
321     wxString  m_directoryOut,   // directory for the output
322               m_fileHeader;     // name of the .h file we parse
323     bool      m_overwrite;      // overwrite existing files?
324     wxTeXFile m_file;           // file we're writing to now
325 
326     // state variables
327     bool m_inClass,         // true after file successfully opened
328          m_inTypesSection,  // enums & typedefs go there
329          m_inMethodSection, // functions go here
330          m_isFirstParam;    // first parameter of current function?
331 
332     // non empty while parsing a class
333     wxString m_classname;
334 
335     // these are only non-empty while parsing a method:
336     wxString m_funcName,    // the function name
337              m_textFunc;    // the function doc text
338 
339     // the array containing the documentation entries for the functions in the
340     // class currently being parsed
341     FunctionDocEntries m_arrayFuncDocs;
342 
343     // holders for "saved" documentation
344     wxString m_textStoredTypedefs,
345              m_textStoredFunctionComment;
346 
347     // for enums we have to use an array as we can't intermix the normal text
348     // and the text inside verbatim environment
349     wxArrayString m_storedEnums,
350                   m_storedEnumsVerb;
351 
352     // headers included by this file
353     wxArrayString m_headers;
354 
355     // ignore handler: tells us which classes to ignore for doc generation
356     // purposes
357     IgnoreNamesHandler m_ignoreNames;
358 
359 private:
360     HelpGenVisitor(const HelpGenVisitor&);
361     HelpGenVisitor& operator=(const HelpGenVisitor&);
362 };
363 
364 // documentation manager - a class which parses TeX files and remembers the
365 // functions documented in them and can later compare them with all functions
366 // found under ctxTop by C++ parser
367 class DocManager
368 {
369 public:
370     DocManager(bool checkParamNames);
371     ~DocManager();
372 
373     // returns false on failure
374     bool ParseTeXFile(const wxString& filename);
375 
376     // returns false if there were any differences
377     bool DumpDifferences(spContext *ctxTop) const;
378 
379     // get our `ignore' object
GetIgnoreHandler()380     IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
381 
382 protected:
383     // parsing TeX files
384     // -----------------
385 
386     // returns the length of 'match' if the string 'str' starts with it or 0
387     // otherwise
388     static size_t TryMatch(const wxChar *str, const wxChar *match);
389 
390     // skip spaces: returns pointer to first non space character (also
391     // updates the value of m_line)
SkipSpaces(const char * p)392     const char *SkipSpaces(const char *p)
393     {
394         while ( isspace(*p) ) {
395             if ( *p++ == '\n' )
396                 m_line++;
397         }
398 
399         return p;
400     }
401 
402     // skips characters until the next 'c' in '*pp' unless it ends before in
403     // which case false is returned and pp points to '\0', otherwise true is
404     // returned and pp points to 'c'
405     bool SkipUntil(const char **pp, char c);
406 
407     // the same as SkipUntil() but only spaces are skipped: on first non space
408     // character different from 'c' the function stops and returns false
409     bool SkipSpaceUntil(const char **pp, char c);
410 
411     // extract the string between {} and modify '*pp' to point at the
412     // character immediately after the closing '}'. The returned string is empty
413     // on error.
414     wxString ExtractStringBetweenBraces(const char **pp);
415 
416     // the current file and line while we're in ParseTeXFile (for error
417     // messages)
418     wxString m_filename;
419     size_t   m_line;
420 
421     // functions and classes to ignore during diff
422     // -------------------------------------------
423 
424     IgnoreNamesHandler m_ignoreNames;
425 
426     // information about all functions documented in the TeX file(s)
427     // -------------------------------------------------------------
428 
429 public: // Note: Sun C++ 5.5 requires TypeInfo and ParamInfo to be public
430 
431     // info about a type: for now stored as text string, but must be parsed
432     // further later (to know that "char *" == "char []" - TODO)
433     class TypeInfo
434     {
435     public:
TypeInfo(const wxString & type)436         TypeInfo(const wxString& type) : m_type(type) { }
437 
operator ==(const wxString & type) const438         bool operator==(const wxString& type) const { return m_type == type; }
operator !=(const wxString & type) const439         bool operator!=(const wxString& type) const { return m_type != type; }
440 
GetName() const441         const wxString& GetName() const { return m_type; }
442 
443     private:
444         wxString m_type;
445     };
446 
447     friend class ParamInfo; // for access to TypeInfo
448 
449     // info abotu a function parameter
450     class ParamInfo
451     {
452     public:
ParamInfo(const wxString & type,const wxString & name,const wxString & value)453         ParamInfo(const wxString& type,
454                   const wxString& name,
455                   const wxString& value)
456             : m_type(type), m_name(name), m_value(value)
457         {
458         }
459 
GetType() const460         const TypeInfo& GetType() const { return m_type; }
GetName() const461         const wxString& GetName() const { return m_name; }
GetDefValue() const462         const wxString& GetDefValue() const { return m_value; }
463 
464     private:
465         TypeInfo m_type;      // type of parameter
466         wxString m_name;      // name
467         wxString m_value;     // default value
468     };
469 
470 public: // FIXME: macro requires it
471     WX_DEFINE_ARRAY_PTR(ParamInfo *, ArrayParamInfo);
472 
473     // info about a function
474     struct MethodInfo
475     {
476     public:
477         enum MethodFlags
478         {
479             Const   = 0x0001,
480             Virtual = 0x0002,
481             Pure    = 0x0004,
482             Static  = 0x0008,
483             Vararg  = 0x0010
484         };
485 
MethodInfoDocManager::MethodInfo486         MethodInfo(const wxString& type,
487                    const wxString& name,
488                    const ArrayParamInfo& params)
489             : m_typeRet(type), m_name(name), m_params(params)
490         {
491             m_flags = 0;
492         }
493 
SetFlagDocManager::MethodInfo494         void SetFlag(MethodFlags flag) { m_flags |= flag; }
495 
GetTypeDocManager::MethodInfo496         const TypeInfo& GetType() const { return m_typeRet; }
GetNameDocManager::MethodInfo497         const wxString& GetName() const { return m_name; }
GetParamDocManager::MethodInfo498         const ParamInfo& GetParam(size_t n) const { return *(m_params[n]); }
GetParamCountDocManager::MethodInfo499         size_t GetParamCount() const { return m_params.GetCount(); }
500 
HasFlagDocManager::MethodInfo501         bool HasFlag(MethodFlags flag) const { return (m_flags & flag) != 0; }
502 
~MethodInfoDocManager::MethodInfo503         ~MethodInfo() { WX_CLEAR_ARRAY(m_params); }
504 
505     private:
506         TypeInfo m_typeRet;     // return type
507         wxString m_name;
508         int      m_flags;       // bit mask of the value from the enum above
509 
510         ArrayParamInfo m_params;
511     };
512 
513     WX_DEFINE_ARRAY_PTR(MethodInfo *, ArrayMethodInfo);
514     WX_DEFINE_ARRAY_PTR(ArrayMethodInfo *, ArrayMethodInfos);
515 
516 private:
517     // first array contains the names of all classes we found, the second has a
518     // pointer to the array of methods of the given class at the same index as
519     // the class name appears in m_classes
520     wxArrayString    m_classes;
521     ArrayMethodInfos m_methods;
522 
523     // are we checking parameter names?
524     bool m_checkParamNames;
525 
526 private:
527     DocManager(const DocManager&);
528     DocManager& operator=(const DocManager&);
529 };
530 
531 // =============================================================================
532 // implementation
533 // =============================================================================
534 
535 static char **g_argv = NULL;
536 
537 // this function never returns
usage()538 static void usage()
539 {
540     wxString prog = g_argv[0];
541     wxString basename = prog.AfterLast('/');
542 #ifdef __WXMSW__
543     if ( !basename )
544         basename = prog.AfterLast('\\');
545 #endif
546     if ( !basename )
547         basename = prog;
548 
549     wxLogMessage(
550 "usage: %s [global options] <mode> [mode options] <files...>\n"
551 "\n"
552 "   where global options are:\n"
553 "       -q          be quiet\n"
554 "       -v          be verbose\n"
555 "       -H          give this usage message\n"
556 "       -V          print the version info\n"
557 "       -i file     file with classes/function to ignore\n"
558 "\n"
559 "   where mode is one of: dump, diff\n"
560 "\n"
561 "   dump means generate .tex files for TeX2RTF converter from specified\n"
562 "   headers files, mode options are:\n"
563 "       -f          overwrite existing files\n"
564 "       -o outdir   directory for generated files\n"
565 "\n"
566 "   diff means compare the set of methods documented .tex file with the\n"
567 "   methods declared in the header:\n"
568 "           %s diff <file.h> <files.tex...>.\n"
569 "   mode specific options are:\n"
570 "       -p          do check parameter names (not done by default)\n"
571 "\n", basename.c_str(), basename.c_str());
572 
573     exit(1);
574 }
575 
main(int argc,char ** argv)576 int main(int argc, char **argv)
577 {
578     g_argv = argv;
579 
580     wxInitializer initializer;
581     if ( !initializer )
582     {
583         fprintf(stderr, "Failed to initialize the wxWidgets library, aborting.");
584 
585         return -1;
586     }
587 
588     enum
589     {
590         Mode_None,
591         Mode_Dump,
592         Mode_Diff
593     } mode = Mode_None;
594 
595     if ( argc < 2 ) {
596         usage();
597     }
598 
599     wxArrayString filesH, filesTeX;
600     wxString directoryOut,      // directory for 'dmup' output
601              ignoreFile;        // file with classes/functions to ignore
602     bool overwrite = false,     // overwrite existing files during 'dump'?
603          paramNames = false;    // check param names during 'diff'?
604 
605     for ( int current = 1; current < argc ; current++ ) {
606         // all options have one letter
607         if ( argv[current][0] == '-' ) {
608             if ( argv[current][2] == '\0' ) {
609                 switch ( argv[current][1] ) {
610                     case 'v':
611                         // be verbose
612                         wxLog::GetActiveTarget()->SetVerbose();
613                         continue;
614 
615                     case 'q':
616                         // be quiet
617                         wxLog::GetActiveTarget()->SetVerbose(false);
618                         continue;
619 
620                     case 'H':
621                         // help requested
622                         usage();
623                         // doesn't return
624 
625                     case 'V':
626                         // version requested
627                         wxLogMessage("HelpGen version %s\n"
628                                      "(c) 1999-2001 Vadim Zeitlin\n",
629                                      GetVersionString().c_str());
630                         return 0;
631 
632                     case 'i':
633                         current++;
634                         if ( current >= argc ) {
635                             wxLogError("-i option requires an argument.");
636 
637                             break;
638                         }
639 
640                         ignoreFile = argv[current];
641                         continue;
642 
643                     case 'p':
644                         if ( mode != Mode_Diff ) {
645                             wxLogError("-p is only valid with diff.");
646 
647                             break;
648                         }
649 
650                         paramNames = true;
651                         continue;
652 
653                     case 'f':
654                         if ( mode != Mode_Dump ) {
655                             wxLogError("-f is only valid with dump.");
656 
657                             break;
658                         }
659 
660                         overwrite = true;
661                         continue;
662 
663                     case 'o':
664                         if ( mode != Mode_Dump ) {
665                             wxLogError("-o is only valid with dump.");
666 
667                             break;
668                         }
669 
670                         current++;
671                         if ( current >= argc ) {
672                             wxLogError("-o option requires an argument.");
673 
674                             break;
675                         }
676 
677                         directoryOut = argv[current];
678                         if ( !directoryOut.empty() ) {
679                             // terminate with a '/' if it doesn't have it
680                             switch ( directoryOut.Last() ) {
681                                 case '/':
682 #ifdef __WXMSW__
683                                 case '\\':
684 #endif
685                                     break;
686 
687                                 default:
688                                     directoryOut += '/';
689                             }
690                         }
691                         //else: it's empty, do nothing
692 
693                         continue;
694 
695                     default:
696                         wxLogError("unknown option '%s'", argv[current]);
697                         break;
698                 }
699             }
700             else {
701                 wxLogError("only one letter options are allowed, not '%s'.",
702                            argv[current]);
703             }
704 
705             // only get here after a break from switch or from else branch of if
706 
707             usage();
708         }
709         else {
710             if ( mode == Mode_None ) {
711                 if ( strcmp(argv[current], "diff") == 0 )
712                     mode = Mode_Diff;
713                 else if ( strcmp(argv[current], "dump") == 0 )
714                     mode = Mode_Dump;
715                 else {
716                     wxLogError("unknown mode '%s'.", argv[current]);
717 
718                     usage();
719                 }
720             }
721             else {
722                 if ( mode == Mode_Dump || filesH.IsEmpty() ) {
723                     filesH.Add(argv[current]);
724                 }
725                 else {
726                     // 2nd files and further are TeX files in diff mode
727                     wxASSERT( mode == Mode_Diff );
728 
729                     filesTeX.Add(argv[current]);
730                 }
731             }
732         }
733     }
734 
735     // create a parser object and a visitor derivation
736     CJSourceParser parser;
737     HelpGenVisitor visitor(directoryOut, overwrite);
738     if ( !ignoreFile.empty() && mode == Mode_Dump )
739         visitor.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
740 
741     spContext *ctxTop = NULL;
742 
743     // parse all header files
744     size_t nFiles = filesH.GetCount();
745     for ( size_t n = 0; n < nFiles; n++ ) {
746         wxString header = filesH[n];
747         ctxTop = parser.ParseFile(header);
748         if ( !ctxTop ) {
749             wxLogWarning("Header file '%s' couldn't be processed.",
750                          header.c_str());
751         }
752         else if ( mode == Mode_Dump ) {
753             ((spFile *)ctxTop)->m_FileName = header;
754             visitor.VisitAll(*ctxTop);
755             visitor.EndVisit();
756         }
757 
758 #ifdef __WXDEBUG__
759         if ( 0 && ctxTop )
760             ctxTop->Dump(wxEmptyString);
761 #endif // __WXDEBUG__
762     }
763 
764     // parse all TeX files
765     if ( mode == Mode_Diff ) {
766         if ( !ctxTop ) {
767             wxLogError("Can't complete diff.");
768 
769             // failure
770             return false;
771         }
772 
773         DocManager docman(paramNames);
774 
775         size_t nFiles = filesTeX.GetCount();
776         for ( size_t n = 0; n < nFiles; n++ ) {
777             wxString file = filesTeX[n];
778             if ( !docman.ParseTeXFile(file) ) {
779                 wxLogWarning("TeX file '%s' couldn't be processed.",
780                              file.c_str());
781             }
782         }
783 
784         if ( !ignoreFile.empty() )
785             docman.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
786 
787         docman.DumpDifferences(ctxTop);
788     }
789 
790     return 0;
791 }
792 
793 // -----------------------------------------------------------------------------
794 // HelpGenVisitor implementation
795 // -----------------------------------------------------------------------------
796 
HelpGenVisitor(const wxString & directoryOut,bool overwrite)797 HelpGenVisitor::HelpGenVisitor(const wxString& directoryOut,
798                                bool overwrite)
799               : m_directoryOut(directoryOut)
800 {
801     m_overwrite = overwrite;
802 
803     Reset();
804 }
805 
Reset()806 void HelpGenVisitor::Reset()
807 {
808     m_inClass =
809     m_inTypesSection =
810     m_inMethodSection = false;
811 
812     m_classname =
813     m_funcName =
814     m_textFunc =
815     m_textStoredTypedefs =
816     m_textStoredFunctionComment = wxEmptyString;
817 
818     m_arrayFuncDocs.Empty();
819 
820     m_storedEnums.Empty();
821     m_storedEnumsVerb.Empty();
822     m_headers.Empty();
823 }
824 
InsertTypedefDocs()825 void HelpGenVisitor::InsertTypedefDocs()
826 {
827     m_file.WriteTeX(m_textStoredTypedefs);
828     m_textStoredTypedefs.Empty();
829 }
830 
InsertEnumDocs()831 void HelpGenVisitor::InsertEnumDocs()
832 {
833     size_t count = m_storedEnums.GetCount();
834     for ( size_t n = 0; n < count; n++ )
835     {
836         m_file.WriteTeX(m_storedEnums[n]);
837         m_file.WriteVerbatim(m_storedEnumsVerb[n] + '\n');
838     }
839 
840     m_storedEnums.Empty();
841     m_storedEnumsVerb.Empty();
842 }
843 
InsertDataStructuresHeader()844 void HelpGenVisitor::InsertDataStructuresHeader()
845 {
846     if ( !m_inTypesSection ) {
847         m_inTypesSection = true;
848 
849         m_file.WriteVerbatim("\\wxheading{Data structures}\n\n");
850     }
851 }
852 
InsertMethodsHeader()853 void HelpGenVisitor::InsertMethodsHeader()
854 {
855     if ( !m_inMethodSection ) {
856         m_inMethodSection = true;
857 
858         m_file.WriteVerbatim( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
859     }
860 }
861 
CloseFunction()862 void HelpGenVisitor::CloseFunction()
863 {
864     if ( !m_funcName.empty() ) {
865         if ( m_isFirstParam ) {
866             // no params found
867             m_textFunc << "\\void";
868         }
869 
870         m_textFunc << "}\n\n";
871 
872         if ( !m_textStoredFunctionComment.empty() ) {
873             m_textFunc << m_textStoredFunctionComment << '\n';
874         }
875 
876         m_arrayFuncDocs.Add(new FunctionDocEntry(m_funcName, m_textFunc));
877 
878         m_funcName.clear();
879     }
880 }
881 
CloseClass()882 void HelpGenVisitor::CloseClass()
883 {
884     CloseFunction();
885 
886     if ( m_inClass )
887     {
888         size_t count = m_arrayFuncDocs.GetCount();
889         if ( count )
890         {
891             size_t n;
892             FunctionDocEntry::classname = m_classname;
893 
894             m_arrayFuncDocs.Sort(FunctionDocEntry::Compare);
895 
896             // Now examine each first line and if it's been seen, cut it
897             // off (it's a duplicate \membersection)
898             wxHashTable membersections(wxKEY_STRING);
899 
900             for ( n = 0; n < count; n++ )
901             {
902                 wxString section(m_arrayFuncDocs[n].text);
903 
904                 // Strip leading whitespace
905                 int pos = section.Find(_T("\\membersection"));
906                 if (pos > -1)
907                 {
908                     section = section.Mid(pos);
909                 }
910 
911                 wxString ms(section.BeforeFirst(wxT('\n')));
912                 if (membersections.Get(ms))
913                 {
914                     m_arrayFuncDocs[n].text = section.AfterFirst(wxT('\n'));
915                 }
916                 else
917                 {
918                     membersections.Put(ms.c_str(), & membersections);
919                 }
920             }
921 
922             for ( n = 0; n < count; n++ ) {
923                 m_file.WriteTeX(m_arrayFuncDocs[n].text);
924             }
925 
926             m_arrayFuncDocs.Empty();
927         }
928 
929         m_inClass = false;
930         m_classname.clear();
931     }
932     m_file.FlushAll();
933 }
934 
EndVisit()935 void HelpGenVisitor::EndVisit()
936 {
937     CloseFunction();
938 
939     CloseClass();
940 
941     m_fileHeader.Empty();
942 
943     m_file.FlushAll();
944     if (m_file.IsOpened())
945     {
946         m_file.Flush();
947         m_file.Close();
948     }
949 
950     wxLogVerbose("%s: finished generating for the current file.",
951                  GetCurrentTimeFormatted("%H:%M:%S"));
952 }
953 
VisitFile(spFile & file)954 void HelpGenVisitor::VisitFile( spFile& file )
955 {
956     m_fileHeader = file.m_FileName;
957     wxLogVerbose("%s: started generating docs for classes from file '%s'...",
958                  GetCurrentTimeFormatted("%H:%M:%S"), m_fileHeader.c_str());
959 }
960 
VisitClass(spClass & cl)961 void HelpGenVisitor::VisitClass( spClass& cl )
962 {
963     CloseClass();
964 
965     if (m_file.IsOpened())
966     {
967         m_file.Flush();
968         m_file.Close();
969     }
970 
971     wxString name = cl.GetName();
972 
973     if ( m_ignoreNames.IgnoreClass(name) ) {
974         wxLogVerbose("Skipping ignored class '%s'.", name.c_str());
975 
976         return;
977     }
978 
979     // the file name is built from the class name by removing the leading "wx"
980     // if any and converting it to the lower case
981     wxString filename;
982     if ( name(0, 2) == "wx" ) {
983         filename << name.c_str() + 2;
984     }
985     else {
986         filename << name;
987     }
988 
989     filename.MakeLower();
990     filename += ".tex";
991     filename.Prepend(m_directoryOut);
992 
993     if ( !m_overwrite && wxFile::Exists(filename) ) {
994         wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
995                    filename.c_str());
996 
997         return;
998     }
999 
1000     m_inClass = m_file.Open(filename, wxFile::write);
1001     if ( !m_inClass ) {
1002         wxLogError("Can't generate documentation for the class '%s'.",
1003                    name.c_str());
1004 
1005         return;
1006     }
1007 
1008     m_inMethodSection =
1009     m_inTypesSection = false;
1010 
1011     wxLogInfo("Created new file '%s' for class '%s'.",
1012               filename.c_str(), name.c_str());
1013 
1014     // write out the header
1015     wxString header;
1016     header.Printf("%%\n"
1017                   "%% automatically generated by HelpGen %s from\n"
1018                   "%% %s at %s\n"
1019                   "%%\n"
1020                   "\n"
1021                   "\n"
1022                   "\\section{\\class{%s}}\\label{%s}\n\n",
1023                   GetVersionString().c_str(),
1024                   m_fileHeader.c_str(),
1025                   GetCurrentTimeFormatted("%d/%b/%y %H:%M:%S"),
1026                   name.c_str(),
1027                   wxString(name).MakeLower().c_str());
1028 
1029     m_file.WriteVerbatim(header);
1030 
1031     // the entire text we're writing to file
1032     wxString totalText;
1033 
1034     // if the header includes other headers they must be related to it... try to
1035     // automatically generate the "See also" clause
1036     if ( !m_headers.IsEmpty() ) {
1037         // correspondence between wxWidgets headers and class names
1038         static const char *headers[] = {
1039             "object",
1040             "defs",
1041             "string",
1042             "dynarray",
1043             "file",
1044             "time",
1045         };
1046 
1047         // NULL here means not to insert anything in "See also" for the
1048         // corresponding header
1049         static const char *classes[] = {
1050             NULL,
1051             NULL,
1052             NULL,
1053             NULL,
1054             "wxFile",
1055             "wxTime",
1056         };
1057 
1058         wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes),
1059                       "arrays must be in sync!" );
1060 
1061         wxArrayInt interestingClasses;
1062 
1063         size_t count = m_headers.Count(), index;
1064         for ( size_t n = 0; n < count; n++ ) {
1065             wxString baseHeaderName = m_headers[n].Before('.');
1066             if ( baseHeaderName(0, 3) != "wx/" )
1067                 continue;
1068 
1069             baseHeaderName.erase(0, 3);
1070             for ( index = 0; index < WXSIZEOF(headers); index++ ) {
1071                 if ( Stricmp(baseHeaderName, headers[index]) == 0 )
1072                     break;
1073             }
1074 
1075             if ( (index < WXSIZEOF(headers)) && classes[index] ) {
1076                 // interesting header
1077                 interestingClasses.Add(index);
1078             }
1079         }
1080 
1081         if ( !interestingClasses.IsEmpty() ) {
1082             // do generate "See also" clause
1083             totalText << "\\wxheading{See also:}\n\n";
1084 
1085             count = interestingClasses.Count();
1086             for ( index = 0; index < count; index++ ) {
1087                 if ( index > 0 )
1088                     totalText << ", ";
1089 
1090                 totalText << MakeHelpref(classes[interestingClasses[index]]);
1091             }
1092 
1093             totalText << "\n\n";
1094         }
1095     }
1096 
1097     // the comment before the class generally explains what is it for so put it
1098     // in place of the class description
1099     if ( cl.HasComments() ) {
1100         wxString comment = GetAllComments(cl);
1101 
1102         totalText << '\n' << comment << '\n';
1103     }
1104 
1105     // derived from section
1106     wxString derived = "\\wxheading{Derived from}\n\n";
1107 
1108     const StrListT& baseClasses = cl.m_SuperClassNames;
1109     if ( baseClasses.size() == 0 ) {
1110         derived << "No base class";
1111     }
1112     else {
1113         bool first = true;
1114         for ( StrListT::const_iterator i = baseClasses.begin();
1115               i != baseClasses.end();
1116               i++ ) {
1117             if ( !first ) {
1118                 // separate from the previous one
1119                 derived << "\\\\\n";
1120             }
1121             else {
1122                 first = false;
1123             }
1124 
1125             wxString baseclass = *i;
1126             derived << "\\helpref{" << baseclass << "}";
1127             derived << "{" << baseclass.MakeLower()  << "}";
1128         }
1129     }
1130     totalText << derived << "\n\n";
1131 
1132     // include file section
1133     wxString includeFile = "\\wxheading{Include files}\n\n";
1134     includeFile << "<" << m_fileHeader << ">";
1135 
1136     totalText << includeFile << "\n\n";
1137 
1138     // write all this to file
1139     m_file.WriteTeX(totalText);
1140 
1141     // if there were any enums/typedefs before, insert their documentation now
1142     InsertDataStructuresHeader();
1143     InsertTypedefDocs();
1144     InsertEnumDocs();
1145 
1146     //m_file.Flush();
1147 }
1148 
VisitEnumeration(spEnumeration & en)1149 void HelpGenVisitor::VisitEnumeration( spEnumeration& en )
1150 {
1151     CloseFunction();
1152 
1153     if ( m_inMethodSection ) {
1154         // FIXME that's a bug, but tell the user aboit it nevertheless... we
1155         // should be smart enough to process even the enums which come after the
1156         // functions
1157         wxLogWarning("enum '%s' ignored, please put it before the class "
1158                      "methods.", en.GetName().c_str());
1159         return;
1160     }
1161 
1162     // simply copy the enum text in the docs
1163     wxString enumeration = GetAllComments(en),
1164              enumerationVerb;
1165 
1166     enumerationVerb << _T("\\begin{verbatim}\n")
1167                     << en.m_EnumContent
1168                     << _T("\n\\end{verbatim}\n");
1169 
1170     // remember for later use if we're not inside a class yet
1171     if ( !m_inClass ) {
1172         m_storedEnums.Add(enumeration);
1173         m_storedEnumsVerb.Add(enumerationVerb);
1174     }
1175     else {
1176         // write the header for this section if not done yet
1177         InsertDataStructuresHeader();
1178 
1179         m_file.WriteTeX(enumeration);
1180         m_file.WriteVerbatim(enumerationVerb);
1181         m_file.WriteVerbatim('\n');
1182     }
1183 }
1184 
VisitTypeDef(spTypeDef & td)1185 void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
1186 {
1187     CloseFunction();
1188 
1189     if ( m_inMethodSection ) {
1190         // FIXME that's a bug, but tell the user aboit it nevertheless...
1191         wxLogWarning("typedef '%s' ignored, please put it before the class "
1192                      "methods.", td.GetName().c_str());
1193         return;
1194     }
1195 
1196     wxString typedefdoc;
1197     typedefdoc << _T("{\\small \\begin{verbatim}\n")
1198                << _T("typedef ") << td.m_OriginalType << _T(' ') << td.GetName()
1199                << _T("\n\\end{verbatim}}\n")
1200                << GetAllComments(td);
1201 
1202     // remember for later use if we're not inside a class yet
1203     if ( !m_inClass ) {
1204         if ( !m_textStoredTypedefs.empty() ) {
1205             m_textStoredTypedefs << '\n';
1206         }
1207 
1208         m_textStoredTypedefs << typedefdoc;
1209     }
1210     else {
1211         // write the header for this section if not done yet
1212         InsertDataStructuresHeader();
1213 
1214         typedefdoc << '\n';
1215         m_file.WriteTeX(typedefdoc);
1216     }
1217 }
1218 
VisitPreprocessorLine(spPreprocessorLine & pd)1219 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd )
1220 {
1221     switch ( pd.GetStatementType() ) {
1222         case SP_PREP_DEF_INCLUDE_FILE:
1223             m_headers.Add(pd.CPP_GetIncludedFileNeme());
1224             break;
1225 
1226         case SP_PREP_DEF_DEFINE_SYMBOL:
1227             // TODO decide if it's a constant and document it if it is
1228             break;
1229     }
1230 }
1231 
VisitAttribute(spAttribute & attr)1232 void HelpGenVisitor::VisitAttribute( spAttribute& attr )
1233 {
1234     CloseFunction();
1235 
1236     // only document the public member variables
1237     if ( !m_inClass || !attr.IsPublic() )
1238         return;
1239 
1240     wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
1241 }
1242 
VisitOperation(spOperation & op)1243 void HelpGenVisitor::VisitOperation( spOperation& op )
1244 {
1245     CloseFunction();
1246 
1247     if ( !m_inClass ) {
1248         // we don't generate docs right now - either we ignore this class
1249         // entirely or we couldn't open the file
1250         return;
1251     }
1252 
1253     if ( !op.IsInClass() ) {
1254         // TODO document global functions
1255         wxLogWarning("skipped global function '%s'.", op.GetName().c_str());
1256 
1257         return;
1258     }
1259 
1260     if ( op.mVisibility == SP_VIS_PRIVATE ) {
1261         // FIXME should we document protected functions?
1262         return;
1263     }
1264 
1265     m_classname = op.GetClass().GetName();
1266     wxString funcname = op.GetName();
1267 
1268     if ( m_ignoreNames.IgnoreMethod(m_classname, funcname) ) {
1269         wxLogVerbose("Skipping ignored '%s::%s'.",
1270                      m_classname.c_str(), funcname.c_str());
1271 
1272         return;
1273     }
1274 
1275     InsertMethodsHeader();
1276 
1277     // save state info
1278     m_funcName = funcname;
1279     m_isFirstParam = true;
1280 
1281     m_textStoredFunctionComment = GetAllComments(op);
1282 
1283     // start function documentation
1284     wxString totalText;
1285 
1286     // check for the special case of dtor
1287     wxString dtor;
1288     if ( (funcname[0u] == '~') && (m_classname == funcname.c_str() + 1) ) {
1289         dtor.Printf("\\destruct{%s}", m_classname.c_str());
1290         funcname = dtor;
1291     }
1292 
1293     m_textFunc.Printf("\n"
1294         "\\membersection{%s::%s}\\label{%s}\n",
1295         m_classname.c_str(), funcname.c_str(),
1296         MakeLabel(m_classname, funcname).c_str());
1297 
1298     wxString constStr;
1299     if(op.mIsConstant) constStr = _T("const");
1300 
1301     wxString virtualStr;
1302     if(op.mIsVirtual) virtualStr = _T("virtual ");
1303 
1304     wxString func;
1305     func.Printf(_T("\n")
1306                 _T("\\%sfunc{%s%s}{%s}{"),
1307                 constStr.c_str(),
1308                 virtualStr.c_str(),
1309                 op.m_RetType.c_str(),
1310                 funcname.c_str());
1311     m_textFunc += func;
1312 }
1313 
VisitParameter(spParameter & param)1314 void HelpGenVisitor::VisitParameter( spParameter& param )
1315 {
1316     if ( m_funcName.empty() )
1317         return;
1318 
1319     if ( m_isFirstParam ) {
1320         m_isFirstParam = false;
1321     }
1322     else {
1323         m_textFunc << ", ";
1324     }
1325 
1326     m_textFunc << "\\param{" << param.m_Type << " }{" << param.GetName();
1327     wxString defvalue = param.m_InitVal;
1328     if ( !defvalue.empty() ) {
1329         m_textFunc << " = " << defvalue;
1330     }
1331 
1332     m_textFunc << '}';
1333 }
1334 
1335 // ---------------------------------------------------------------------------
1336 // DocManager
1337 // ---------------------------------------------------------------------------
1338 
DocManager(bool checkParamNames)1339 DocManager::DocManager(bool checkParamNames)
1340 {
1341     m_checkParamNames = checkParamNames;
1342 }
1343 
TryMatch(const char * str,const char * match)1344 size_t DocManager::TryMatch(const char *str, const char *match)
1345 {
1346     size_t lenMatch = 0;
1347     while ( str[lenMatch] == match[lenMatch] ) {
1348         lenMatch++;
1349 
1350         if ( match[lenMatch] == '\0' )
1351             return lenMatch;
1352     }
1353 
1354     return 0;
1355 }
1356 
SkipUntil(const char ** pp,char c)1357 bool DocManager::SkipUntil(const char **pp, char c)
1358 {
1359     const char *p = *pp;
1360     while ( *p != c ) {
1361         if ( *p == '\0' )
1362             break;
1363 
1364         if ( *p == '\n' )
1365             m_line++;
1366 
1367         p++;
1368     }
1369 
1370     *pp = p;
1371 
1372     return *p == c;
1373 }
1374 
SkipSpaceUntil(const char ** pp,char c)1375 bool DocManager::SkipSpaceUntil(const char **pp, char c)
1376 {
1377     const char *p = *pp;
1378     while ( *p != c ) {
1379         if ( !isspace(*p) || *p == '\0' )
1380             break;
1381 
1382         if ( *p == '\n' )
1383             m_line++;
1384 
1385         p++;
1386     }
1387 
1388     *pp = p;
1389 
1390     return *p == c;
1391 }
1392 
ExtractStringBetweenBraces(const char ** pp)1393 wxString DocManager::ExtractStringBetweenBraces(const char **pp)
1394 {
1395     wxString result;
1396 
1397     if ( !SkipSpaceUntil(pp, '{') ) {
1398         wxLogWarning("file %s(%d): '{' expected after '\\param'",
1399                      m_filename.c_str(), (int)m_line);
1400 
1401     }
1402     else {
1403         const char *startParam = ++*pp; // skip '{'
1404 
1405         if ( !SkipUntil(pp, '}') ) {
1406             wxLogWarning("file %s(%d): '}' expected after '\\param'",
1407                          m_filename.c_str(), (int)m_line);
1408         }
1409         else {
1410             result = wxString(startParam, (*pp)++ - startParam);
1411         }
1412     }
1413 
1414     return result;
1415 }
1416 
ParseTeXFile(const wxString & filename)1417 bool DocManager::ParseTeXFile(const wxString& filename)
1418 {
1419     m_filename = filename;
1420 
1421     wxFile file(m_filename, wxFile::read);
1422     if ( !file.IsOpened() )
1423         return false;
1424 
1425     off_t len = file.Length();
1426     if ( len == wxInvalidOffset )
1427         return false;
1428 
1429     char *buf = new char[len + 1];
1430     buf[len] = '\0';
1431 
1432     if ( file.Read(buf, len) == wxInvalidOffset ) {
1433         delete [] buf;
1434 
1435         return false;
1436     }
1437 
1438     // reinit everything
1439     m_line = 1;
1440 
1441     wxLogVerbose("%s: starting to parse doc file '%s'.",
1442                  GetCurrentTimeFormatted("%H:%M:%S"), m_filename.c_str());
1443 
1444     // the name of the class from the last "\membersection" command: we assume
1445     // that the following "\func" or "\constfunc" always documents a method of
1446     // this class (and it should always be like that in wxWidgets documentation)
1447     wxString classname;
1448 
1449     for ( const char *current = buf; current - buf < len; current++ ) {
1450         // FIXME parsing is awfully inefficient
1451 
1452         if ( *current == '%' ) {
1453             // comment, skip until the end of line
1454             current++;
1455             SkipUntil(&current, '\n');
1456 
1457             continue;
1458         }
1459 
1460         // all the command we're interested in start with '\\'
1461         while ( *current != '\\' && *current != '\0' ) {
1462             if ( *current++ == '\n' )
1463                 m_line++;
1464         }
1465 
1466         if ( *current == '\0' ) {
1467             // no more TeX commands left
1468             break;
1469         }
1470 
1471         current++; // skip '\\'
1472 
1473         enum
1474         {
1475             Nothing,
1476             Func,
1477             ConstFunc,
1478             MemberSect
1479         } foundCommand = Nothing;
1480 
1481         size_t lenMatch = TryMatch(current, "func");
1482         if ( lenMatch ) {
1483             foundCommand = Func;
1484         }
1485         else {
1486             lenMatch = TryMatch(current, "constfunc");
1487             if ( lenMatch )
1488                 foundCommand = ConstFunc;
1489             else {
1490                 lenMatch = TryMatch(current, "membersection");
1491 
1492                 if ( lenMatch )
1493                     foundCommand = MemberSect;
1494             }
1495         }
1496 
1497         if ( foundCommand == Nothing )
1498             continue;
1499 
1500         current += lenMatch;
1501 
1502         if ( !SkipSpaceUntil(&current, '{') ) {
1503             wxLogWarning("file %s(%d): '{' expected after \\func, "
1504                          "\\constfunc or \\membersection.",
1505                          m_filename.c_str(), (int)m_line);
1506 
1507             continue;
1508         }
1509 
1510         current++;
1511 
1512         if ( foundCommand == MemberSect ) {
1513             // what follows has the form <classname>::<funcname>
1514             const char *startClass = current;
1515             if ( !SkipUntil(&current, ':') || *(current + 1) != ':' ) {
1516                 wxLogWarning("file %s(%d): '::' expected after "
1517                              "\\membersection.", m_filename.c_str(), (int)m_line);
1518             }
1519             else {
1520                 classname = wxString(startClass, current - startClass);
1521                 TeXUnfilter(&classname);
1522             }
1523 
1524             continue;
1525         }
1526 
1527         // extract the return type
1528         const char *startRetType = current;
1529 
1530         if ( !SkipUntil(&current, '}') ) {
1531             wxLogWarning("file %s(%d): '}' expected after return type",
1532                          m_filename.c_str(), (int)m_line);
1533 
1534             continue;
1535         }
1536 
1537         wxString returnType = wxString(startRetType, current - startRetType);
1538         TeXUnfilter(&returnType);
1539 
1540         current++;
1541         if ( !SkipSpaceUntil(&current, '{') ) {
1542             wxLogWarning("file %s(%d): '{' expected after return type",
1543                          m_filename.c_str(), (int)m_line);
1544 
1545             continue;
1546         }
1547 
1548         current++;
1549         const char *funcEnd = current;
1550         if ( !SkipUntil(&funcEnd, '}') ) {
1551             wxLogWarning("file %s(%d): '}' expected after function name",
1552                          m_filename.c_str(), (int)m_line);
1553 
1554             continue;
1555         }
1556 
1557         wxString funcName = wxString(current, funcEnd - current);
1558         current = funcEnd + 1;
1559 
1560         // trim spaces from both sides
1561         funcName.Trim(false);
1562         funcName.Trim(true);
1563 
1564         // special cases: '$...$' may be used for LaTeX inline math, remove the
1565         // '$'s
1566         if ( funcName.Find('$') != wxNOT_FOUND ) {
1567             wxString name;
1568             for ( const char *p = funcName.c_str(); *p != '\0'; p++ ) {
1569                 if ( *p != '$' && !isspace(*p) )
1570                     name += *p;
1571             }
1572 
1573             funcName = name;
1574         }
1575 
1576         // \destruct{foo} is really ~foo
1577         if ( funcName[0u] == '\\' ) {
1578             size_t len = strlen("\\destruct{");
1579             if ( funcName(0, len) != "\\destruct{" ) {
1580                 wxLogWarning("file %s(%d): \\destruct expected",
1581                              m_filename.c_str(), (int)m_line);
1582 
1583                 continue;
1584             }
1585 
1586             funcName.erase(0, len);
1587             funcName.Prepend('~');
1588 
1589             if ( !SkipSpaceUntil(&current, '}') ) {
1590                 wxLogWarning("file %s(%d): '}' expected after destructor",
1591                              m_filename.c_str(), (int)m_line);
1592 
1593                 continue;
1594             }
1595 
1596             funcEnd++;  // there is an extra '}' to count
1597         }
1598 
1599         TeXUnfilter(&funcName);
1600 
1601         // extract params
1602         current = funcEnd + 1; // skip '}'
1603         if ( !SkipSpaceUntil(&current, '{') ||
1604              (current++, !SkipSpaceUntil(&current, '\\')) ) {
1605             wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1606                          m_filename.c_str(), (int)m_line);
1607 
1608             continue;
1609         }
1610 
1611         wxArrayString paramNames, paramTypes, paramValues;
1612 
1613         bool isVararg = false;
1614 
1615         current++; // skip '\\'
1616         lenMatch = TryMatch(current, "void");
1617         if ( !lenMatch ) {
1618             lenMatch = TryMatch(current, "param");
1619             while ( lenMatch && (current - buf < len) ) {
1620                 current += lenMatch;
1621 
1622                 // now come {paramtype}{paramname}
1623                 wxString paramType = ExtractStringBetweenBraces(&current);
1624                 if ( !paramType.empty() ) {
1625                     wxString paramText = ExtractStringBetweenBraces(&current);
1626                     if ( !paramText.empty() ) {
1627                         // the param declaration may contain default value
1628                         wxString paramName = paramText.BeforeFirst('='),
1629                                  paramValue = paramText.AfterFirst('=');
1630 
1631                         // sanitize all strings
1632                         TeXUnfilter(&paramValue);
1633                         TeXUnfilter(&paramName);
1634                         TeXUnfilter(&paramType);
1635 
1636                         paramValues.Add(paramValue);
1637                         paramNames.Add(paramName);
1638                         paramTypes.Add(paramType);
1639                     }
1640                 }
1641                 else {
1642                     // vararg function?
1643                     wxString paramText = ExtractStringBetweenBraces(&current);
1644                     if ( paramText == "..." ) {
1645                         isVararg = true;
1646                     }
1647                     else {
1648                         wxLogWarning("Parameters of '%s::%s' are in "
1649                                      "incorrect form.",
1650                                      classname.c_str(), funcName.c_str());
1651                     }
1652                 }
1653 
1654                 // what's next?
1655                 current = SkipSpaces(current);
1656                 if ( *current == ',' || *current == '}' ) {
1657                     current = SkipSpaces(++current);
1658 
1659                     lenMatch = TryMatch(current, "\\param");
1660                 }
1661                 else {
1662                     wxLogWarning("file %s(%d): ',' or '}' expected after "
1663                                  "'\\param'", m_filename.c_str(), (int)m_line);
1664 
1665                     continue;
1666                 }
1667             }
1668 
1669             // if we got here there was no '\\void', so must have some params
1670             if ( paramNames.IsEmpty() ) {
1671                 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1672                         m_filename.c_str(), (int)m_line);
1673 
1674                 continue;
1675             }
1676         }
1677 
1678         // verbose diagnostic output
1679         wxString paramsAll;
1680         size_t param, paramCount = paramNames.GetCount();
1681         for ( param = 0; param < paramCount; param++ ) {
1682             if ( param != 0 ) {
1683                 paramsAll << ", ";
1684             }
1685 
1686             paramsAll << paramTypes[param] << ' ' << paramNames[param];
1687         }
1688 
1689         wxString constStr;
1690         if (foundCommand == ConstFunc)
1691             constStr = _T(" const");
1692 
1693         wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1694                      m_filename.c_str(),
1695                      (int)m_line,
1696                      returnType.c_str(),
1697                      classname.c_str(),
1698                      funcName.c_str(),
1699                      paramsAll.c_str(),
1700                      constStr.c_str());
1701 
1702         // store the info about the just found function
1703         ArrayMethodInfo *methods;
1704         int index = m_classes.Index(classname);
1705         if ( index == wxNOT_FOUND ) {
1706             m_classes.Add(classname);
1707 
1708             methods = new ArrayMethodInfo;
1709             m_methods.Add(methods);
1710         }
1711         else {
1712             methods = m_methods[(size_t)index];
1713         }
1714 
1715         ArrayParamInfo params;
1716         for ( param = 0; param < paramCount; param++ ) {
1717             params.Add(new ParamInfo(paramTypes[param],
1718                                      paramNames[param],
1719                                      paramValues[param]));
1720         }
1721 
1722         MethodInfo *method = new MethodInfo(returnType, funcName, params);
1723         if ( foundCommand == ConstFunc )
1724             method->SetFlag(MethodInfo::Const);
1725         if ( isVararg )
1726             method->SetFlag(MethodInfo::Vararg);
1727 
1728         methods->Add(method);
1729     }
1730 
1731     delete [] buf;
1732 
1733     wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1734                  GetCurrentTimeFormatted("%H:%M:%S"), m_filename.c_str());
1735 
1736     return true;
1737 }
1738 
DumpDifferences(spContext * ctxTop) const1739 bool DocManager::DumpDifferences(spContext *ctxTop) const
1740 {
1741     typedef MMemberListT::const_iterator MemberIndex;
1742 
1743     bool foundDiff = false;
1744 
1745     // flag telling us whether the given class was found at all in the header
1746     size_t nClass, countClassesInDocs = m_classes.GetCount();
1747     bool *classExists = new bool[countClassesInDocs];
1748     for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1749         classExists[nClass] = false;
1750     }
1751 
1752     // ctxTop is normally an spFile
1753     wxASSERT( ctxTop->GetContextType() == SP_CTX_FILE );
1754 
1755     const MMemberListT& classes = ctxTop->GetMembers();
1756     for ( MemberIndex i = classes.begin(); i != classes.end(); i++ ) {
1757         spContext *ctx = *i;
1758         if ( ctx->GetContextType() != SP_CTX_CLASS ) {
1759             // TODO process also global functions, macros, ...
1760             continue;
1761         }
1762 
1763         spClass *ctxClass = (spClass *)ctx;
1764         const wxString& nameClass = ctxClass->m_Name;
1765         int index = m_classes.Index(nameClass);
1766         if ( index == wxNOT_FOUND ) {
1767             if ( !m_ignoreNames.IgnoreClass(nameClass) ) {
1768                 foundDiff = true;
1769 
1770                 wxLogError("Class '%s' is not documented at all.",
1771                            nameClass.c_str());
1772             }
1773 
1774             // it makes no sense to check for its functions
1775             continue;
1776         }
1777         else {
1778             classExists[index] = true;
1779         }
1780 
1781         // array of method descriptions for this class
1782         const ArrayMethodInfo& methods = *(m_methods[index]);
1783         size_t nMethod, countMethods = methods.GetCount();
1784 
1785         // flags telling if we already processed given function
1786         bool *methodExists = new bool[countMethods];
1787         for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1788             methodExists[nMethod] = false;
1789         }
1790 
1791         wxArrayString aOverloadedMethods;
1792 
1793         const MMemberListT& functions = ctxClass->GetMembers();
1794         for ( MemberIndex j = functions.begin(); j != functions.end(); j++ ) {
1795             ctx = *j;
1796             if ( ctx->GetContextType() != SP_CTX_OPERATION )
1797                 continue;
1798 
1799             spOperation *ctxMethod = (spOperation *)ctx;
1800             const wxString& nameMethod = ctxMethod->m_Name;
1801 
1802             // find all functions with the same name
1803             wxArrayInt aMethodsWithSameName;
1804             for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1805                 if ( methods[nMethod]->GetName() == nameMethod )
1806                     aMethodsWithSameName.Add(nMethod);
1807             }
1808 
1809             if ( aMethodsWithSameName.IsEmpty() && ctxMethod->IsPublic() ) {
1810                 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1811                     foundDiff = true;
1812 
1813                     wxLogError("'%s::%s' is not documented.",
1814                                nameClass.c_str(),
1815                                nameMethod.c_str());
1816                 }
1817 
1818                 // don't check params
1819                 continue;
1820             }
1821             else if ( aMethodsWithSameName.GetCount() == 1 ) {
1822                 index = (size_t)aMethodsWithSameName[0u];
1823                 methodExists[index] = true;
1824 
1825                 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1826                     continue;
1827 
1828                 if ( !ctxMethod->IsPublic() ) {
1829                     wxLogWarning("'%s::%s' is documented but not public.",
1830                                  nameClass.c_str(),
1831                                  nameMethod.c_str());
1832                 }
1833 
1834                 // check that the flags match
1835                 const MethodInfo& method = *(methods[index]);
1836 
1837                 bool isVirtual = ctxMethod->mIsVirtual;
1838                 if ( isVirtual != method.HasFlag(MethodInfo::Virtual) )
1839                 {
1840                     wxString virtualStr;
1841                     if(isVirtual)virtualStr = _T("not ");
1842 
1843                     wxLogWarning("'%s::%s' is incorrectly documented as %s"
1844                                  "virtual.",
1845                                  nameClass.c_str(),
1846                                  nameMethod.c_str(),
1847                                  virtualStr.c_str());
1848                 }
1849 
1850                 bool isConst = ctxMethod->mIsConstant;
1851                 if ( isConst != method.HasFlag(MethodInfo::Const) )
1852                 {
1853                     wxString constStr;
1854                     if(isConst)constStr = _T("not ");
1855 
1856                     wxLogWarning("'%s::%s' is incorrectly documented as %s"
1857                                  "constant.",
1858                                  nameClass.c_str(),
1859                                  nameMethod.c_str(),
1860                                  constStr.c_str());
1861                 }
1862 
1863                 // check that the params match
1864                 const MMemberListT& params = ctxMethod->GetMembers();
1865 
1866                 if ( params.size() != method.GetParamCount() ) {
1867                     wxLogError("Incorrect number of parameters for '%s::%s' "
1868                                "in the docs: should be %d instead of %d.",
1869                                nameClass.c_str(),
1870                                nameMethod.c_str(),
1871                                (int)params.size(), (int)method.GetParamCount());
1872                 }
1873                 else {
1874                     size_t nParam = 0;
1875                     for ( MemberIndex k = params.begin();
1876                           k != params.end();
1877                           k++, nParam++ ) {
1878                         ctx = *k;
1879 
1880                         // what else can a function have?
1881                         wxASSERT( ctx->GetContextType() == SP_CTX_PARAMETER );
1882 
1883                         spParameter *ctxParam = (spParameter *)ctx;
1884                         const ParamInfo& param = method.GetParam(nParam);
1885                         if ( m_checkParamNames &&
1886                              (param.GetName() != ctxParam->m_Name.c_str()) ) {
1887                             foundDiff = true;
1888 
1889                             wxLogError("Parameter #%d of '%s::%s' should be "
1890                                        "'%s' and not '%s'.",
1891                                        (int)(nParam + 1),
1892                                        nameClass.c_str(),
1893                                        nameMethod.c_str(),
1894                                        ctxParam->m_Name.c_str(),
1895                                        param.GetName().c_str());
1896 
1897                             continue;
1898                         }
1899 
1900                         if ( param.GetType() != ctxParam->m_Type ) {
1901                             foundDiff = true;
1902 
1903                             wxLogError("Type of parameter '%s' of '%s::%s' "
1904                                        "should be '%s' and not '%s'.",
1905                                        ctxParam->m_Name.c_str(),
1906                                        nameClass.c_str(),
1907                                        nameMethod.c_str(),
1908                                        ctxParam->m_Type.c_str(),
1909                                        param.GetType().GetName().c_str());
1910 
1911                             continue;
1912                         }
1913 
1914                         if ( param.GetDefValue() != ctxParam->m_InitVal.c_str() ) {
1915                             wxLogWarning("Default value of parameter '%s' of "
1916                                          "'%s::%s' should be '%s' and not "
1917                                          "'%s'.",
1918                                          ctxParam->m_Name.c_str(),
1919                                          nameClass.c_str(),
1920                                          nameMethod.c_str(),
1921                                          ctxParam->m_InitVal.c_str(),
1922                                          param.GetDefValue().c_str());
1923                         }
1924                     }
1925                 }
1926             }
1927             else {
1928                 // TODO OVER add real support for overloaded methods
1929 
1930                 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1931                     continue;
1932 
1933                 if ( aOverloadedMethods.Index(nameMethod) == wxNOT_FOUND ) {
1934                     // mark all methods with this name as existing
1935                     for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1936                         if ( methods[nMethod]->GetName() == nameMethod )
1937                             methodExists[nMethod] = true;
1938                     }
1939 
1940                     aOverloadedMethods.Add(nameMethod);
1941 
1942                     wxLogVerbose("'%s::%s' is overloaded and I'm too "
1943                                  "stupid to find the right match - skipping "
1944                                  "the param and flags checks.",
1945                                  nameClass.c_str(),
1946                                  nameMethod.c_str());
1947                 }
1948                 //else: warning already given
1949             }
1950         }
1951 
1952         for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1953             if ( !methodExists[nMethod] ) {
1954                 const wxString& nameMethod = methods[nMethod]->GetName();
1955                 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1956                     foundDiff = true;
1957 
1958                     wxLogError("'%s::%s' is documented but doesn't exist.",
1959                                nameClass.c_str(),
1960                                nameMethod.c_str());
1961                 }
1962             }
1963         }
1964 
1965         delete [] methodExists;
1966     }
1967 
1968     // check that all classes we found in the docs really exist
1969     for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1970         if ( !classExists[nClass] ) {
1971             foundDiff = true;
1972 
1973             wxLogError("Class '%s' is documented but doesn't exist.",
1974                        m_classes[nClass].c_str());
1975         }
1976     }
1977 
1978     delete [] classExists;
1979 
1980     return !foundDiff;
1981 }
1982 
~DocManager()1983 DocManager::~DocManager()
1984 {
1985     WX_CLEAR_ARRAY(m_methods);
1986 }
1987 
1988 // ---------------------------------------------------------------------------
1989 // IgnoreNamesHandler implementation
1990 // ---------------------------------------------------------------------------
1991 
CompareIgnoreListEntries(IgnoreListEntry * first,IgnoreListEntry * second)1992 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry *first,
1993                                                  IgnoreListEntry *second)
1994 {
1995     // first compare the classes
1996     int rc = first->m_classname.Cmp(second->m_classname);
1997     if ( rc == 0 )
1998         rc = first->m_funcname.Cmp(second->m_funcname);
1999 
2000     return rc;
2001 }
2002 
AddNamesFromFile(const wxString & filename)2003 bool IgnoreNamesHandler::AddNamesFromFile(const wxString& filename)
2004 {
2005     wxFile file(filename, wxFile::read);
2006     if ( !file.IsOpened() )
2007         return false;
2008 
2009     off_t len = file.Length();
2010     if ( len == wxInvalidOffset )
2011         return false;
2012 
2013     char *buf = new char[len + 1];
2014     buf[len] = '\0';
2015 
2016     if ( file.Read(buf, len) == wxInvalidOffset ) {
2017         delete [] buf;
2018 
2019         return false;
2020     }
2021 
2022     wxString line;
2023     for ( const char *current = buf; ; current++ ) {
2024 #ifdef __WXMSW__
2025         // skip DOS line separator
2026         if ( *current == '\r' )
2027             current++;
2028 #endif // wxMSW
2029 
2030         if ( *current == '\n' || *current == '\0' ) {
2031             if ( line[0u] != '#' ) {
2032                 if ( line.Find(':') != wxNOT_FOUND ) {
2033                     wxString classname = line.BeforeFirst(':'),
2034                              funcname = line.AfterLast(':');
2035                     m_ignore.Add(new IgnoreListEntry(classname, funcname));
2036                 }
2037                 else {
2038                     // entire class
2039                     m_ignore.Add(new IgnoreListEntry(line, wxEmptyString));
2040                 }
2041             }
2042             //else: comment
2043 
2044             if ( *current == '\0' )
2045                 break;
2046 
2047             line.Empty();
2048         }
2049         else {
2050             line += *current;
2051         }
2052     }
2053 
2054     delete [] buf;
2055 
2056     return true;
2057 }
2058 
2059 // -----------------------------------------------------------------------------
2060 // global function implementation
2061 // -----------------------------------------------------------------------------
2062 
MakeLabel(const char * classname,const char * funcname)2063 static wxString MakeLabel(const char *classname, const char *funcname)
2064 {
2065     wxString label(classname);
2066     if ( funcname && funcname[0] == '\\' ) {
2067         // we may have some special TeX macro - so far only \destruct exists,
2068         // but may be later others will be added
2069         static const char *macros[] = { "destruct" };
2070         static const char *replacement[] = { "dtor" };
2071 
2072         size_t n;
2073         for ( n = 0; n < WXSIZEOF(macros); n++ ) {
2074             if ( strncmp(funcname + 1, macros[n], strlen(macros[n])) == 0 ) {
2075                 // found
2076                 break;
2077             }
2078         }
2079 
2080         if ( n == WXSIZEOF(macros) ) {
2081             wxLogWarning("unknown function name '%s' - leaving as is.",
2082                          funcname);
2083         }
2084         else {
2085             funcname = replacement[n];
2086         }
2087     }
2088 
2089     if ( funcname ) {
2090         // special treatment for operatorXXX() stuff because the C operators
2091         // are not valid in LaTeX labels
2092         wxString oper;
2093         if ( wxString(funcname).StartsWith("operator", &oper) ) {
2094             label << "operator";
2095 
2096             static const struct
2097             {
2098                 const char *oper;
2099                 const char *name;
2100             } operatorNames[] =
2101             {
2102                 { "=",  "assign" },
2103                 { "==", "equal" },
2104             };
2105 
2106             size_t n;
2107             for ( n = 0; n < WXSIZEOF(operatorNames); n++ ) {
2108                 if ( oper == operatorNames[n].oper ) {
2109                     label << operatorNames[n].name;
2110 
2111                     break;
2112                 }
2113             }
2114 
2115             if ( n == WXSIZEOF(operatorNames) ) {
2116                 wxLogWarning("unknown operator '%s' - making dummy label.",
2117                              oper.c_str());
2118 
2119                 label << "unknown";
2120             }
2121         }
2122         else // simply use the func name
2123         {
2124             label << funcname;
2125         }
2126     }
2127 
2128     label.MakeLower();
2129 
2130     return label;
2131 }
2132 
MakeHelpref(const char * argument)2133 static wxString MakeHelpref(const char *argument)
2134 {
2135     wxString helpref;
2136     helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
2137 
2138     return helpref;
2139 }
2140 
TeXFilter(wxString * str)2141 static void TeXFilter(wxString* str)
2142 {
2143     // TeX special which can be quoted (don't include backslash nor braces as
2144     // we generate them
2145     static wxRegEx reNonSpecialSpecials("[#$%&_]"),
2146                    reAccents("[~^]");
2147 
2148     // just quote
2149     reNonSpecialSpecials.ReplaceAll(str, "\\\\\\0");
2150 
2151     // can't quote these ones as they produce accents when preceded by
2152     // backslash, so put them inside verb
2153     reAccents.ReplaceAll(str, "\\\\verb|\\0|");
2154 }
2155 
TeXUnfilter(wxString * str)2156 static void TeXUnfilter(wxString* str)
2157 {
2158     // FIXME may be done much more quickly
2159     str->Trim(true);
2160     str->Trim(false);
2161 
2162     // undo TeXFilter
2163     static wxRegEx reNonSpecialSpecials("\\\\([#$%&_{}])"),
2164                    reAccents("\\\\verb\\|([~^])\\|");
2165 
2166     reNonSpecialSpecials.ReplaceAll(str, "\\1");
2167     reAccents.ReplaceAll(str, "\\1");
2168 }
2169 
GetAllComments(const spContext & ctx)2170 static wxString GetAllComments(const spContext& ctx)
2171 {
2172     wxString comments;
2173     const MCommentListT& commentsList = ctx.GetCommentList();
2174     for ( MCommentListT::const_iterator i = commentsList.begin();
2175           i != commentsList.end();
2176           i++ ) {
2177         wxString comment = (*i)->GetText();
2178 
2179         // don't take comments like "// ----------" &c
2180         comment.Trim(false);
2181         if ( !comment.empty() &&
2182               comment == wxString(comment[0u], comment.length() - 1) + '\n' )
2183             comments << "\n";
2184         else
2185             comments << comment;
2186     }
2187 
2188     return comments;
2189 }
2190 
GetCurrentTimeFormatted(const char * timeFormat)2191 static const char *GetCurrentTimeFormatted(const char *timeFormat)
2192 {
2193     static char s_timeBuffer[128];
2194     time_t timeNow;
2195     struct tm *ptmNow;
2196 
2197     time(&timeNow);
2198     ptmNow = localtime(&timeNow);
2199 
2200     strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow);
2201 
2202     return s_timeBuffer;
2203 }
2204 
GetVersionString()2205 static const wxString GetVersionString()
2206 {
2207     wxString version = "$Revision: 34465 $";
2208     wxRegEx("^\\$Revision: 34465 $$").ReplaceFirst(&version, "\\1");
2209     return version;
2210 }
2211 
2212 /*
2213    $Log$
2214    Revision 1.44  2005/05/31 17:47:45  ABX
2215    More warning and error fixes (work in progress with Tinderbox).
2216 
2217    Revision 1.43  2005/05/31 15:42:43  ABX
2218    More warning and error fixes (work in progress with Tinderbox).
2219 
2220    Revision 1.42  2005/05/31 15:32:49  ABX
2221    More warning and error fixes (work in progress with Tinderbox).
2222 
2223    Revision 1.41  2005/05/30 13:06:15  ABX
2224    More warning and error fixes (work in progress with Tinderbox).
2225 
2226    Revision 1.40  2005/05/30 11:49:32  ABX
2227    More warning and error fixes (work in progress with Tinderbox).
2228 
2229    Revision 1.39  2005/05/30 09:26:42  ABX
2230    More warning and error fixes (work in progress with Tinderbox).
2231 
2232    Revision 1.38  2005/05/24 09:06:20  ABX
2233    More fixes and wxWidgets coding standards.
2234 
2235    Revision 1.37  2005/05/23 15:22:08  ABX
2236    Initial HelpGen source cleaning.
2237 
2238    Revision 1.36  2005/04/07 19:54:58  MW
2239    Workarounds to allow compilation by Sun C++ 5.5
2240 
2241    Revision 1.35  2004/12/12 11:03:31  VZ
2242    give an error message if we're built in Unicode mode (in response to bug 1079224)
2243 
2244    Revision 1.34  2004/11/23 09:53:31  JS
2245    Changed GPL to wxWindows Licence
2246 
2247    Revision 1.33  2004/11/12 03:30:07  RL
2248 
2249    Cruft cleanup from MJW, strip the tabs out of sound.cpp
2250 
2251    Revision 1.32  2004/11/10 21:02:58  VZ
2252    new set of fixes for problems due to huge files support: drop wxFileSize_t, use wxFileOffset only, make wxInvalidOffset an int (main part of the patch 1063498)
2253 
2254    Revision 1.31  2004/10/05 15:38:29  ABX
2255    Warning fixes found under hardest mode of OpenWatcom. Seems clean in Borland, MinGW and DMC.
2256 
2257    Revision 1.30  2004/06/18 19:25:50  ABX
2258    Small step in making HelpGen up to date unicode application.
2259 
2260    Revision 1.29  2004/06/17 19:00:22  ABX
2261    Warning fixes. Code cleanup. Whitespaces and tabs removed.
2262 
2263    Revision 1.28  2004/05/25 11:19:57  JS
2264    More name changes
2265 
2266    Revision 1.27  2003/10/13 17:21:30  MBN
2267      Compilation fixes.
2268 
2269    Revision 1.26  2003/09/29 15:18:35  MBN
2270      (Blind) compilation fix for Sun compiler.
2271 
2272    Revision 1.25  2003/09/03 17:39:27  MBN
2273      Compilation fixes.
2274 
2275    Revision 1.24  2003/08/13 22:59:37  VZ
2276    compilation fix
2277 
2278    Revision 1.23  2003/06/13 17:05:43  VZ
2279    quote '|' inside regexes (fixes dump mode); fixed crash due to strange HelpGenApp code
2280 
2281    Revision 1.22  2002/01/21 21:18:50  JS
2282    Now adds 'include file' heading
2283 
2284    Revision 1.21  2002/01/04 11:06:09  JS
2285    Fixed missing membersections bug and also bug with functions not being written
2286    in the right class
2287 
2288    Revision 1.20  2002/01/03 14:23:33  JS
2289    Added code to make it not duplicate membersections for overloaded functions
2290 
2291    Revision 1.19  2002/01/03 13:34:12  JS
2292    Added FlushAll to CloseClass, otherwise text was only flushed right at the end,
2293    and appeared in one file.
2294 
2295    Revision 1.18  2002/01/03 12:02:47  JS
2296    Added main() and corrected VC++ project settings
2297 
2298    Revision 1.17  2001/11/30 21:43:35  VZ
2299    now the methods are sorted in the correct order in the generated docs
2300 
2301    Revision 1.16  2001/11/28 19:27:33  VZ
2302    HelpGen doesn't work in GUI mode
2303 
2304    Revision 1.15  2001/11/22 21:59:58  GD
2305    use "..." instead of <...> for wx headers
2306 
2307    Revision 1.14  2001/07/19 13:51:29  VZ
2308    fixes to version string
2309 
2310    Revision 1.13  2001/07/19 13:44:57  VZ
2311    1. compilation fixes
2312    2. don't quote special characters inside verbatim environment
2313 
2314    Revision 1.12  2000/10/09 13:53:33  juliansmart
2315 
2316    Doc corrections; added HelpGen project files
2317 
2318    Revision 1.11  2000/07/15 19:50:42  cvsuser
2319    merged 2.2 branch
2320 
2321    Revision 1.10.2.2  2000/03/27 15:33:10  VZ
2322    don't trasnform output dir name to lower case
2323 
2324    Revision 1.10  2000/03/11 10:05:23  VS
2325    now compiles with wxBase
2326 
2327    Revision 1.9  2000/01/16 13:25:21  VS
2328    compilation fixes (gcc)
2329 
2330    Revision 1.8  1999/09/13 14:29:39  JS
2331 
2332    Made HelpGen into a wxWin app (still uses command-line args); moved includes
2333    into src for simplicity; added VC++ 5 project file
2334 
2335    Revision 1.7  1999/02/21 22:32:32  VZ
2336    1. more C++ parser fixes - now it almost parses wx/string.h
2337     a) #if/#ifdef/#else (very) limited support
2338     b) param type fix - now indirection chars are correctly handled
2339     c) class/struct/union distinction
2340     d) public/private fixes
2341     e) Dump() function added - very useful for debugging
2342 
2343    2. option to ignore parameter names during 'diff' (in fact, they're ignored
2344       by default, and this option switches it on)
2345 
2346    Revision 1.6  1999/02/20 23:00:26  VZ
2347    1. new 'diff' mode which seems to work
2348    2. output files are not overwritten in 'dmup' mode
2349    3. fixes for better handling of const functions and operators
2350     ----------------------------
2351     revision 1.5
2352     date: 1999/02/15 23:07:25;  author: VZ;  state: Exp;  lines: +106 -45
2353     1. Parser improvements
2354      a) const and virtual methods are parsed correctly (not static yet)
2355      b) "const" which is part of the return type is not swallowed
2356 
2357     2. HelpGen improvements: -o outputdir parameter added to the cmd line,
2358        "//---------" kind comments discarded now.
2359     ----------------------------
2360     revision 1.4
2361     date: 1999/01/13 14:23:31;  author: JS;  state: Exp;  lines: +4 -4
2362 
2363     some tweaks to HelpGen
2364     ----------------------------
2365     revision 1.3
2366     date: 1999/01/09 20:18:03;  author: JS;  state: Exp;  lines: +7 -2
2367 
2368     HelpGen starting to compile with VC++
2369     ----------------------------
2370     revision 1.2
2371     date: 1999/01/08 19:46:22;  author: VZ;  state: Exp;  lines: +208 -35
2372 
2373     supports typedefs, generates "See also:" and adds "virtual " for virtual
2374     functions
2375     ----------------------------
2376     revision 1.1
2377     date: 1999/01/08 17:45:55;  author: VZ;  state: Exp;
2378 
2379     HelpGen is a prototype of the tool for automatic generation of the .tex files
2380     for wxWidgets documentation from C++ headers
2381 */
2382 
2383 /* vi: set tw=80 et ts=4 sw=4: */
2384