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