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