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