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