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