]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/HelpGen.cpp
Moved the popup menu event handlers to dialog event handler
[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 CloseFunction();
898
899 if ( m_inClass ) {
900 size_t count = m_arrayFuncDocs.GetCount();
901 if ( count ) {
902 size_t n;
903 FunctionDocEntry::classname = m_classname;
904
905 m_arrayFuncDocs.Sort(FunctionDocEntry::Compare);
906
907 // Now examine each first line and if it's been seen, cut it
908 // off (it's a duplicate \membersection)
909 wxHashTable membersections(wxKEY_STRING);
910
911 for ( n = 0; n < count; n++ )
912 {
913 wxString section(m_arrayFuncDocs[n].text);
914
915 // Strip leading whitespace
916 int pos = section.Find("\\membersection");
917 if (pos > -1)
918 {
919 section = section.Mid(pos);
920 }
921
922 wxString ms(section.BeforeFirst(wxT('\n')));
923 if (membersections.Get(ms))
924 {
925 m_arrayFuncDocs[n].text = section.AfterFirst(wxT('\n'));
926 }
927 else
928 {
929 membersections.Put(ms, & membersections);
930 }
931 }
932
933 for ( n = 0; n < count; n++ ) {
934 m_file.WriteTeX(m_arrayFuncDocs[n].text);
935 }
936
937 m_arrayFuncDocs.Empty();
938 }
939
940 m_inClass = FALSE;
941 m_classname.clear();
942 }
943 m_file.FlushAll();
944 }
945
946 void HelpGenVisitor::EndVisit()
947 {
948 CloseFunction();
949
950 CloseClass();
951
952 m_fileHeader.Empty();
953
954 m_file.FlushAll();
955 if (m_file.IsOpened())
956 {
957 m_file.Flush();
958 m_file.Close();
959 }
960
961 wxLogVerbose("%s: finished generating for the current file.",
962 GetCurrentTime("%H:%M:%S"));
963 }
964
965 void HelpGenVisitor::VisitFile( spFile& file )
966 {
967 m_fileHeader = file.mFileName;
968 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
969 GetCurrentTime("%H:%M:%S"), m_fileHeader.c_str());
970 }
971
972 void HelpGenVisitor::VisitClass( spClass& cl )
973 {
974 CloseClass();
975
976 if (m_file.IsOpened())
977 {
978 m_file.Flush();
979 m_file.Close();
980 }
981
982 wxString name = cl.GetName();
983
984 if ( m_ignoreNames.IgnoreClass(name) ) {
985 wxLogVerbose("Skipping ignored class '%s'.", name.c_str());
986
987 return;
988 }
989
990 // the file name is built from the class name by removing the leading "wx"
991 // if any and converting it to the lower case
992 wxString filename;
993 if ( name(0, 2) == "wx" ) {
994 filename << name.c_str() + 2;
995 }
996 else {
997 filename << name;
998 }
999
1000 filename.MakeLower();
1001 filename += ".tex";
1002 filename.Prepend(m_directoryOut);
1003
1004 if ( !m_overwrite && wxFile::Exists(filename) ) {
1005 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
1006 filename.c_str());
1007
1008 return;
1009 }
1010
1011 m_inClass = m_file.Open(filename, wxFile::write);
1012 if ( !m_inClass ) {
1013 wxLogError("Can't generate documentation for the class '%s'.",
1014 name.c_str());
1015
1016 return;
1017 }
1018
1019 m_inMethodSection =
1020 m_inTypesSection = FALSE;
1021
1022 wxLogInfo("Created new file '%s' for class '%s'.",
1023 filename.c_str(), name.c_str());
1024
1025 // write out the header
1026 wxString header;
1027 header.Printf("%%\n"
1028 "%% automatically generated by HelpGen %s from\n"
1029 "%% %s at %s\n"
1030 "%%\n"
1031 "\n"
1032 "\n"
1033 "\\section{\\class{%s}}\\label{%s}\n\n",
1034 GetVersionString().c_str(),
1035 m_fileHeader.c_str(),
1036 GetCurrentTime("%d/%b/%y %H:%M:%S"),
1037 name.c_str(),
1038 wxString(name).MakeLower().c_str());
1039
1040 m_file.WriteVerbatim(header);
1041
1042 // the entire text we're writing to file
1043 wxString totalText;
1044
1045 // if the header includes other headers they must be related to it... try to
1046 // automatically generate the "See also" clause
1047 if ( !m_headers.IsEmpty() ) {
1048 // correspondence between wxWindows headers and class names
1049 static const char *headers[] = {
1050 "object",
1051 "defs",
1052 "string",
1053 "dynarray",
1054 "file",
1055 "time",
1056 };
1057
1058 // NULL here means not to insert anything in "See also" for the
1059 // corresponding header
1060 static const char *classes[] = {
1061 NULL,
1062 NULL,
1063 NULL,
1064 NULL,
1065 "wxFile",
1066 "wxTime",
1067 };
1068
1069 wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes),
1070 "arrays must be in sync!" );
1071
1072 wxArrayInt interestingClasses;
1073
1074 size_t count = m_headers.Count(), index;
1075 for ( size_t n = 0; n < count; n++ ) {
1076 wxString baseHeaderName = m_headers[n].Before('.');
1077 if ( baseHeaderName(0, 3) != "wx/" )
1078 continue;
1079
1080 baseHeaderName.erase(0, 3);
1081 for ( index = 0; index < WXSIZEOF(headers); index++ ) {
1082 if ( Stricmp(baseHeaderName, headers[index]) == 0 )
1083 break;
1084 }
1085
1086 if ( (index < WXSIZEOF(headers)) && classes[index] ) {
1087 // interesting header
1088 interestingClasses.Add(index);
1089 }
1090 }
1091
1092 if ( !interestingClasses.IsEmpty() ) {
1093 // do generate "See also" clause
1094 totalText << "\\wxheading{See also:}\n\n";
1095
1096 count = interestingClasses.Count();
1097 for ( index = 0; index < count; index++ ) {
1098 if ( index > 0 )
1099 totalText << ", ";
1100
1101 totalText << MakeHelpref(classes[interestingClasses[index]]);
1102 }
1103
1104 totalText << "\n\n";
1105 }
1106 }
1107
1108 // the comment before the class generally explains what is it for so put it
1109 // in place of the class description
1110 if ( cl.HasComments() ) {
1111 wxString comment = GetAllComments(cl);
1112
1113 totalText << '\n' << comment << '\n';
1114 }
1115
1116 // derived from section
1117 wxString derived = "\\wxheading{Derived from}\n\n";
1118
1119 const StrListT& baseClasses = cl.mSuperClassNames;
1120 if ( baseClasses.size() == 0 ) {
1121 derived << "No base class";
1122 }
1123 else {
1124 bool first = TRUE;
1125 for ( StrListT::const_iterator i = baseClasses.begin();
1126 i != baseClasses.end();
1127 i++ ) {
1128 if ( !first ) {
1129 // separate from the previous one
1130 derived << "\\\\\n";
1131 }
1132 else {
1133 first = FALSE;
1134 }
1135
1136 wxString baseclass = *i;
1137 derived << "\\helpref{" << baseclass << "}";
1138 derived << "{" << baseclass.MakeLower() << "}";
1139 }
1140 }
1141 totalText << derived << "\n\n";
1142
1143 // write all this to file
1144 m_file.WriteTeX(totalText);
1145
1146 // if there were any enums/typedefs before, insert their documentation now
1147 InsertDataStructuresHeader();
1148 InsertTypedefDocs();
1149 InsertEnumDocs();
1150
1151 //m_file.Flush();
1152 }
1153
1154 void HelpGenVisitor::VisitEnumeration( spEnumeration& en )
1155 {
1156 CloseFunction();
1157
1158 if ( m_inMethodSection ) {
1159 // FIXME that's a bug, but tell the user aboit it nevertheless... we
1160 // should be smart enough to process even the enums which come after the
1161 // functions
1162 wxLogWarning("enum '%s' ignored, please put it before the class "
1163 "methods.", en.GetName().c_str());
1164 return;
1165 }
1166
1167 // simply copy the enum text in the docs
1168 wxString enumeration = GetAllComments(en),
1169 enumerationVerb;
1170
1171 enumerationVerb << "\\begin{verbatim}\n"
1172 << en.mEnumContent
1173 << "\n\\end{verbatim}\n";
1174
1175 // remember for later use if we're not inside a class yet
1176 if ( !m_inClass ) {
1177 m_storedEnums.Add(enumeration);
1178 m_storedEnumsVerb.Add(enumerationVerb);
1179 }
1180 else {
1181 // write the header for this section if not done yet
1182 InsertDataStructuresHeader();
1183
1184 m_file.WriteTeX(enumeration);
1185 m_file.WriteVerbatim(enumerationVerb);
1186 m_file.WriteVerbatim('\n');
1187 }
1188 }
1189
1190 void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
1191 {
1192 CloseFunction();
1193
1194 if ( m_inMethodSection ) {
1195 // FIXME that's a bug, but tell the user aboit it nevertheless...
1196 wxLogWarning("typedef '%s' ignored, please put it before the class "
1197 "methods.", td.GetName().c_str());
1198 return;
1199 }
1200
1201 wxString typedefdoc;
1202 typedefdoc << "{\\small \\begin{verbatim}\n"
1203 << "typedef " << td.mOriginalType << ' ' << td.GetName()
1204 << "\n\\end{verbatim}}\n"
1205 << GetAllComments(td);
1206
1207 // remember for later use if we're not inside a class yet
1208 if ( !m_inClass ) {
1209 if ( !m_textStoredTypedefs.IsEmpty() ) {
1210 m_textStoredTypedefs << '\n';
1211 }
1212
1213 m_textStoredTypedefs << typedefdoc;
1214 }
1215 else {
1216 // write the header for this section if not done yet
1217 InsertDataStructuresHeader();
1218
1219 typedefdoc << '\n';
1220 m_file.WriteTeX(typedefdoc);
1221 }
1222 }
1223
1224 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd )
1225 {
1226 switch ( pd.GetStatementType() ) {
1227 case SP_PREP_DEF_INCLUDE_FILE:
1228 m_headers.Add(pd.CPP_GetIncludedFileNeme());
1229 break;
1230
1231 case SP_PREP_DEF_DEFINE_SYMBOL:
1232 // TODO decide if it's a constant and document it if it is
1233 break;
1234 }
1235 }
1236
1237 void HelpGenVisitor::VisitAttribute( spAttribute& attr )
1238 {
1239 CloseFunction();
1240
1241 // only document the public member variables
1242 if ( !m_inClass || !attr.IsPublic() )
1243 return;
1244
1245 wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
1246 }
1247
1248 void HelpGenVisitor::VisitOperation( spOperation& op )
1249 {
1250 CloseFunction();
1251
1252 if ( !m_inClass ) {
1253 // we don't generate docs right now - either we ignore this class
1254 // entirely or we couldn't open the file
1255 return;
1256 }
1257
1258 if ( !op.IsInClass() ) {
1259 // TODO document global functions
1260 wxLogWarning("skipped global function '%s'.", op.GetName().c_str());
1261
1262 return;
1263 }
1264
1265 if ( op.mVisibility == SP_VIS_PRIVATE ) {
1266 // FIXME should we document protected functions?
1267 return;
1268 }
1269
1270 m_classname = op.GetClass().GetName();
1271 wxString funcname = op.GetName();
1272
1273 if ( m_ignoreNames.IgnoreMethod(m_classname, funcname) ) {
1274 wxLogVerbose("Skipping ignored '%s::%s'.",
1275 m_classname.c_str(), funcname.c_str());
1276
1277 return;
1278 }
1279
1280 InsertMethodsHeader();
1281
1282 // save state info
1283 m_funcName = funcname;
1284 m_isFirstParam = TRUE;
1285
1286 m_textStoredFunctionComment = GetAllComments(op);
1287
1288 // start function documentation
1289 wxString totalText;
1290
1291 // check for the special case of dtor
1292 wxString dtor;
1293 if ( (funcname[0] == '~') && (m_classname == funcname.c_str() + 1) ) {
1294 dtor.Printf("\\destruct{%s}", m_classname.c_str());
1295 funcname = dtor;
1296 }
1297
1298 m_textFunc.Printf("\n"
1299 "\\membersection{%s::%s}\\label{%s}\n",
1300 m_classname.c_str(), funcname.c_str(),
1301 MakeLabel(m_classname, funcname).c_str());
1302
1303 wxString func;
1304 func.Printf("\n"
1305 "\\%sfunc{%s%s}{%s}{",
1306 op.mIsConstant ? "const" : "",
1307 op.mIsVirtual ? "virtual " : "",
1308 op.mRetType.c_str(),
1309 funcname.c_str());
1310 m_textFunc += func;
1311 }
1312
1313 void HelpGenVisitor::VisitParameter( spParameter& param )
1314 {
1315 if ( m_funcName.empty() )
1316 return;
1317
1318 if ( m_isFirstParam ) {
1319 m_isFirstParam = FALSE;
1320 }
1321 else {
1322 m_textFunc << ", ";
1323 }
1324
1325 m_textFunc << "\\param{" << param.mType << " }{" << param.GetName();
1326 wxString defvalue = param.mInitVal;
1327 if ( !defvalue.IsEmpty() ) {
1328 m_textFunc << " = " << defvalue;
1329 }
1330
1331 m_textFunc << '}';
1332 }
1333
1334 // ---------------------------------------------------------------------------
1335 // DocManager
1336 // ---------------------------------------------------------------------------
1337
1338 DocManager::DocManager(bool checkParamNames)
1339 {
1340 m_checkParamNames = checkParamNames;
1341 }
1342
1343 size_t DocManager::TryMatch(const char *str, const char *match)
1344 {
1345 size_t lenMatch = 0;
1346 while ( str[lenMatch] == match[lenMatch] ) {
1347 lenMatch++;
1348
1349 if ( match[lenMatch] == '\0' )
1350 return lenMatch;
1351 }
1352
1353 return 0;
1354 }
1355
1356 bool DocManager::SkipUntil(const char **pp, char c)
1357 {
1358 const char *p = *pp;
1359 while ( *p != c ) {
1360 if ( *p == '\0' )
1361 break;
1362
1363 if ( *p == '\n' )
1364 m_line++;
1365
1366 p++;
1367 }
1368
1369 *pp = p;
1370
1371 return *p == c;
1372 }
1373
1374 bool DocManager::SkipSpaceUntil(const char **pp, char c)
1375 {
1376 const char *p = *pp;
1377 while ( *p != c ) {
1378 if ( !isspace(*p) || *p == '\0' )
1379 break;
1380
1381 if ( *p == '\n' )
1382 m_line++;
1383
1384 p++;
1385 }
1386
1387 *pp = p;
1388
1389 return *p == c;
1390 }
1391
1392 wxString DocManager::ExtractStringBetweenBraces(const char **pp)
1393 {
1394 wxString result;
1395
1396 if ( !SkipSpaceUntil(pp, '{') ) {
1397 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1398 m_filename.c_str(), m_line);
1399
1400 }
1401 else {
1402 const char *startParam = ++*pp; // skip '{'
1403
1404 if ( !SkipUntil(pp, '}') ) {
1405 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1406 m_filename.c_str(), m_line);
1407 }
1408 else {
1409 result = wxString(startParam, (*pp)++ - startParam);
1410 }
1411 }
1412
1413 return result;
1414 }
1415
1416 bool DocManager::ParseTeXFile(const wxString& filename)
1417 {
1418 m_filename = filename;
1419
1420 wxFile file(m_filename, wxFile::read);
1421 if ( !file.IsOpened() )
1422 return FALSE;
1423
1424 off_t len = file.Length();
1425 if ( len == wxInvalidOffset )
1426 return FALSE;
1427
1428 char *buf = new char[len + 1];
1429 buf[len] = '\0';
1430
1431 if ( file.Read(buf, len) == wxInvalidOffset ) {
1432 delete [] buf;
1433
1434 return FALSE;
1435 }
1436
1437 // reinit everything
1438 m_line = 1;
1439
1440 wxLogVerbose("%s: starting to parse doc file '%s'.",
1441 GetCurrentTime("%H:%M:%S"), m_filename.c_str());
1442
1443 // the name of the class from the last "\membersection" command: we assume
1444 // that the following "\func" or "\constfunc" always documents a method of
1445 // this class (and it should always be like that in wxWindows documentation)
1446 wxString classname;
1447
1448 for ( const char *current = buf; current - buf < len; current++ ) {
1449 // FIXME parsing is awfully inefficient
1450
1451 if ( *current == '%' ) {
1452 // comment, skip until the end of line
1453 current++;
1454 SkipUntil(&current, '\n');
1455
1456 continue;
1457 }
1458
1459 // all the command we're interested in start with '\\'
1460 while ( *current != '\\' && *current != '\0' ) {
1461 if ( *current++ == '\n' )
1462 m_line++;
1463 }
1464
1465 if ( *current == '\0' ) {
1466 // no more TeX commands left
1467 break;
1468 }
1469
1470 current++; // skip '\\'
1471
1472 enum
1473 {
1474 Nothing,
1475 Func,
1476 ConstFunc,
1477 MemberSect
1478 } foundCommand = Nothing;
1479
1480 size_t lenMatch = TryMatch(current, "func");
1481 if ( lenMatch ) {
1482 foundCommand = Func;
1483 }
1484 else {
1485 lenMatch = TryMatch(current, "constfunc");
1486 if ( lenMatch )
1487 foundCommand = ConstFunc;
1488 else {
1489 lenMatch = TryMatch(current, "membersection");
1490
1491 if ( lenMatch )
1492 foundCommand = MemberSect;
1493 }
1494 }
1495
1496 if ( foundCommand == Nothing )
1497 continue;
1498
1499 current += lenMatch;
1500
1501 if ( !SkipSpaceUntil(&current, '{') ) {
1502 wxLogWarning("file %s(%d): '{' expected after \\func, "
1503 "\\constfunc or \\membersection.",
1504 m_filename.c_str(), m_line);
1505
1506 continue;
1507 }
1508
1509 current++;
1510
1511 if ( foundCommand == MemberSect ) {
1512 // what follows has the form <classname>::<funcname>
1513 const char *startClass = current;
1514 if ( !SkipUntil(&current, ':') || *(current + 1) != ':' ) {
1515 wxLogWarning("file %s(%d): '::' expected after "
1516 "\\membersection.", m_filename.c_str(), m_line);
1517 }
1518 else {
1519 classname = wxString(startClass, current - startClass);
1520 TeXUnfilter(&classname);
1521 }
1522
1523 continue;
1524 }
1525
1526 // extract the return type
1527 const char *startRetType = current;
1528
1529 if ( !SkipUntil(&current, '}') ) {
1530 wxLogWarning("file %s(%d): '}' expected after return type",
1531 m_filename.c_str(), m_line);
1532
1533 continue;
1534 }
1535
1536 wxString returnType = wxString(startRetType, current - startRetType);
1537 TeXUnfilter(&returnType);
1538
1539 current++;
1540 if ( !SkipSpaceUntil(&current, '{') ) {
1541 wxLogWarning("file %s(%d): '{' expected after return type",
1542 m_filename.c_str(), m_line);
1543
1544 continue;
1545 }
1546
1547 current++;
1548 const char *funcEnd = current;
1549 if ( !SkipUntil(&funcEnd, '}') ) {
1550 wxLogWarning("file %s(%d): '}' expected after function name",
1551 m_filename.c_str(), m_line);
1552
1553 continue;
1554 }
1555
1556 wxString funcName = wxString(current, funcEnd - current);
1557 current = funcEnd + 1;
1558
1559 // trim spaces from both sides
1560 funcName.Trim(FALSE);
1561 funcName.Trim(TRUE);
1562
1563 // special cases: '$...$' may be used for LaTeX inline math, remove the
1564 // '$'s
1565 if ( funcName.Find('$') != wxNOT_FOUND ) {
1566 wxString name;
1567 for ( const char *p = funcName.c_str(); *p != '\0'; p++ ) {
1568 if ( *p != '$' && !isspace(*p) )
1569 name += *p;
1570 }
1571
1572 funcName = name;
1573 }
1574
1575 // \destruct{foo} is really ~foo
1576 if ( funcName[0u] == '\\' ) {
1577 size_t len = strlen("\\destruct{");
1578 if ( funcName(0, len) != "\\destruct{" ) {
1579 wxLogWarning("file %s(%d): \\destruct expected",
1580 m_filename.c_str(), m_line);
1581
1582 continue;
1583 }
1584
1585 funcName.erase(0, len);
1586 funcName.Prepend('~');
1587
1588 if ( !SkipSpaceUntil(&current, '}') ) {
1589 wxLogWarning("file %s(%d): '}' expected after destructor",
1590 m_filename.c_str(), m_line);
1591
1592 continue;
1593 }
1594
1595 funcEnd++; // there is an extra '}' to count
1596 }
1597
1598 TeXUnfilter(&funcName);
1599
1600 // extract params
1601 current = funcEnd + 1; // skip '}'
1602 if ( !SkipSpaceUntil(&current, '{') ||
1603 (current++, !SkipSpaceUntil(&current, '\\')) ) {
1604 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1605 m_filename.c_str(), m_line);
1606
1607 continue;
1608 }
1609
1610 wxArrayString paramNames, paramTypes, paramValues;
1611
1612 bool isVararg = FALSE;
1613
1614 current++; // skip '\\'
1615 lenMatch = TryMatch(current, "void");
1616 if ( !lenMatch ) {
1617 lenMatch = TryMatch(current, "param");
1618 while ( lenMatch && (current - buf < len) ) {
1619 current += lenMatch;
1620
1621 // now come {paramtype}{paramname}
1622 wxString paramType = ExtractStringBetweenBraces(&current);
1623 if ( !!paramType ) {
1624 wxString paramText = ExtractStringBetweenBraces(&current);
1625 if ( !!paramText ) {
1626 // the param declaration may contain default value
1627 wxString paramName = paramText.BeforeFirst('='),
1628 paramValue = paramText.AfterFirst('=');
1629
1630 // sanitize all strings
1631 TeXUnfilter(&paramValue);
1632 TeXUnfilter(&paramName);
1633 TeXUnfilter(&paramType);
1634
1635 paramValues.Add(paramValue);
1636 paramNames.Add(paramName);
1637 paramTypes.Add(paramType);
1638 }
1639 }
1640 else {
1641 // vararg function?
1642 wxString paramText = ExtractStringBetweenBraces(&current);
1643 if ( paramText == "..." ) {
1644 isVararg = TRUE;
1645 }
1646 else {
1647 wxLogWarning("Parameters of '%s::%s' are in "
1648 "incorrect form.",
1649 classname.c_str(), funcName.c_str());
1650 }
1651 }
1652
1653 // what's next?
1654 current = SkipSpaces(current);
1655 if ( *current == ',' || *current == '}' ) {
1656 current = SkipSpaces(++current);
1657
1658 lenMatch = TryMatch(current, "\\param");
1659 }
1660 else {
1661 wxLogWarning("file %s(%d): ',' or '}' expected after "
1662 "'\\param'", m_filename.c_str(), m_line);
1663
1664 continue;
1665 }
1666 }
1667
1668 // if we got here there was no '\\void', so must have some params
1669 if ( paramNames.IsEmpty() ) {
1670 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1671 m_filename.c_str(), m_line);
1672
1673 continue;
1674 }
1675 }
1676
1677 // verbose diagnostic output
1678 wxString paramsAll;
1679 size_t param, paramCount = paramNames.GetCount();
1680 for ( param = 0; param < paramCount; param++ ) {
1681 if ( param != 0 ) {
1682 paramsAll << ", ";
1683 }
1684
1685 paramsAll << paramTypes[param] << ' ' << paramNames[param];
1686 }
1687
1688 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1689 m_filename.c_str(), m_line,
1690 returnType.c_str(),
1691 classname.c_str(),
1692 funcName.c_str(),
1693 paramsAll.c_str(),
1694 foundCommand == ConstFunc ? " const" : "");
1695
1696 // store the info about the just found function
1697 ArrayMethodInfo *methods;
1698 int index = m_classes.Index(classname);
1699 if ( index == wxNOT_FOUND ) {
1700 m_classes.Add(classname);
1701
1702 methods = new ArrayMethodInfo;
1703 m_methods.Add(methods);
1704 }
1705 else {
1706 methods = m_methods[(size_t)index];
1707 }
1708
1709 ArrayParamInfo params;
1710 for ( param = 0; param < paramCount; param++ ) {
1711 params.Add(new ParamInfo(paramTypes[param],
1712 paramNames[param],
1713 paramValues[param]));
1714 }
1715
1716 MethodInfo *method = new MethodInfo(returnType, funcName, params);
1717 if ( foundCommand == ConstFunc )
1718 method->SetFlag(MethodInfo::Const);
1719 if ( isVararg )
1720 method->SetFlag(MethodInfo::Vararg);
1721
1722 methods->Add(method);
1723 }
1724
1725 delete [] buf;
1726
1727 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1728 GetCurrentTime("%H:%M:%S"), m_filename.c_str());
1729
1730 return TRUE;
1731 }
1732
1733 bool DocManager::DumpDifferences(spContext *ctxTop) const
1734 {
1735 typedef MMemberListT::const_iterator MemberIndex;
1736
1737 bool foundDiff = FALSE;
1738
1739 // flag telling us whether the given class was found at all in the header
1740 size_t nClass, countClassesInDocs = m_classes.GetCount();
1741 bool *classExists = new bool[countClassesInDocs];
1742 for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1743 classExists[nClass] = FALSE;
1744 }
1745
1746 // ctxTop is normally an spFile
1747 wxASSERT( ctxTop->GetContextType() == SP_CTX_FILE );
1748
1749 const MMemberListT& classes = ctxTop->GetMembers();
1750 for ( MemberIndex i = classes.begin(); i != classes.end(); i++ ) {
1751 spContext *ctx = *i;
1752 if ( ctx->GetContextType() != SP_CTX_CLASS ) {
1753 // TODO process also global functions, macros, ...
1754 continue;
1755 }
1756
1757 spClass *ctxClass = (spClass *)ctx;
1758 const wxString& nameClass = ctxClass->mName;
1759 int index = m_classes.Index(nameClass);
1760 if ( index == wxNOT_FOUND ) {
1761 if ( !m_ignoreNames.IgnoreClass(nameClass) ) {
1762 foundDiff = TRUE;
1763
1764 wxLogError("Class '%s' is not documented at all.",
1765 nameClass.c_str());
1766 }
1767
1768 // it makes no sense to check for its functions
1769 continue;
1770 }
1771 else {
1772 classExists[index] = TRUE;
1773 }
1774
1775 // array of method descriptions for this class
1776 const ArrayMethodInfo& methods = *(m_methods[index]);
1777 size_t nMethod, countMethods = methods.GetCount();
1778
1779 // flags telling if we already processed given function
1780 bool *methodExists = new bool[countMethods];
1781 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1782 methodExists[nMethod] = FALSE;
1783 }
1784
1785 wxArrayString aOverloadedMethods;
1786
1787 const MMemberListT& functions = ctxClass->GetMembers();
1788 for ( MemberIndex j = functions.begin(); j != functions.end(); j++ ) {
1789 ctx = *j;
1790 if ( ctx->GetContextType() != SP_CTX_OPERATION )
1791 continue;
1792
1793 spOperation *ctxMethod = (spOperation *)ctx;
1794 const wxString& nameMethod = ctxMethod->mName;
1795
1796 // find all functions with the same name
1797 wxArrayInt aMethodsWithSameName;
1798 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1799 if ( methods[nMethod]->GetName() == nameMethod )
1800 aMethodsWithSameName.Add(nMethod);
1801 }
1802
1803 if ( aMethodsWithSameName.IsEmpty() && ctxMethod->IsPublic() ) {
1804 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1805 foundDiff = TRUE;
1806
1807 wxLogError("'%s::%s' is not documented.",
1808 nameClass.c_str(),
1809 nameMethod.c_str());
1810 }
1811
1812 // don't check params
1813 continue;
1814 }
1815 else if ( aMethodsWithSameName.GetCount() == 1 ) {
1816 index = (size_t)aMethodsWithSameName[0u];
1817 methodExists[index] = TRUE;
1818
1819 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1820 continue;
1821
1822 if ( !ctxMethod->IsPublic() ) {
1823 wxLogWarning("'%s::%s' is documented but not public.",
1824 nameClass.c_str(),
1825 nameMethod.c_str());
1826 }
1827
1828 // check that the flags match
1829 const MethodInfo& method = *(methods[index]);
1830
1831 bool isVirtual = ctxMethod->mIsVirtual;
1832 if ( isVirtual != method.HasFlag(MethodInfo::Virtual) ) {
1833 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1834 "virtual.",
1835 nameClass.c_str(),
1836 nameMethod.c_str(),
1837 isVirtual ? "not " : "");
1838 }
1839
1840 bool isConst = ctxMethod->mIsConstant;
1841 if ( isConst != method.HasFlag(MethodInfo::Const) ) {
1842 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1843 "constant.",
1844 nameClass.c_str(),
1845 nameMethod.c_str(),
1846 isConst ? "not " : "");
1847 }
1848
1849 // check that the params match
1850 const MMemberListT& params = ctxMethod->GetMembers();
1851
1852 if ( params.size() != method.GetParamCount() ) {
1853 wxLogError("Incorrect number of parameters for '%s::%s' "
1854 "in the docs: should be %d instead of %d.",
1855 nameClass.c_str(),
1856 nameMethod.c_str(),
1857 params.size(), method.GetParamCount());
1858 }
1859 else {
1860 size_t nParam = 0;
1861 for ( MemberIndex k = params.begin();
1862 k != params.end();
1863 k++, nParam++ ) {
1864 ctx = *k;
1865
1866 // what else can a function have?
1867 wxASSERT( ctx->GetContextType() == SP_CTX_PARAMETER );
1868
1869 spParameter *ctxParam = (spParameter *)ctx;
1870 const ParamInfo& param = method.GetParam(nParam);
1871 if ( m_checkParamNames &&
1872 (param.GetName() != ctxParam->mName) ) {
1873 foundDiff = TRUE;
1874
1875 wxLogError("Parameter #%d of '%s::%s' should be "
1876 "'%s' and not '%s'.",
1877 nParam + 1,
1878 nameClass.c_str(),
1879 nameMethod.c_str(),
1880 ctxParam->mName.c_str(),
1881 param.GetName().c_str());
1882
1883 continue;
1884 }
1885
1886 if ( param.GetType() != ctxParam->mType ) {
1887 foundDiff = TRUE;
1888
1889 wxLogError("Type of parameter '%s' of '%s::%s' "
1890 "should be '%s' and not '%s'.",
1891 ctxParam->mName.c_str(),
1892 nameClass.c_str(),
1893 nameMethod.c_str(),
1894 ctxParam->mType.c_str(),
1895 param.GetType().GetName().c_str());
1896
1897 continue;
1898 }
1899
1900 if ( param.GetDefValue() != ctxParam->mInitVal ) {
1901 wxLogWarning("Default value of parameter '%s' of "
1902 "'%s::%s' should be '%s' and not "
1903 "'%s'.",
1904 ctxParam->mName.c_str(),
1905 nameClass.c_str(),
1906 nameMethod.c_str(),
1907 ctxParam->mInitVal.c_str(),
1908 param.GetDefValue().c_str());
1909 }
1910 }
1911 }
1912 }
1913 else {
1914 // TODO OVER add real support for overloaded methods
1915
1916 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1917 continue;
1918
1919 if ( aOverloadedMethods.Index(nameMethod) == wxNOT_FOUND ) {
1920 // mark all methods with this name as existing
1921 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1922 if ( methods[nMethod]->GetName() == nameMethod )
1923 methodExists[nMethod] = TRUE;
1924 }
1925
1926 aOverloadedMethods.Add(nameMethod);
1927
1928 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1929 "stupid to find the right match - skipping "
1930 "the param and flags checks.",
1931 nameClass.c_str(),
1932 nameMethod.c_str());
1933 }
1934 //else: warning already given
1935 }
1936 }
1937
1938 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1939 if ( !methodExists[nMethod] ) {
1940 const wxString& nameMethod = methods[nMethod]->GetName();
1941 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1942 foundDiff = TRUE;
1943
1944 wxLogError("'%s::%s' is documented but doesn't exist.",
1945 nameClass.c_str(),
1946 nameMethod.c_str());
1947 }
1948 }
1949 }
1950
1951 delete [] methodExists;
1952 }
1953
1954 // check that all classes we found in the docs really exist
1955 for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1956 if ( !classExists[nClass] ) {
1957 foundDiff = TRUE;
1958
1959 wxLogError("Class '%s' is documented but doesn't exist.",
1960 m_classes[nClass].c_str());
1961 }
1962 }
1963
1964 delete [] classExists;
1965
1966 return !foundDiff;
1967 }
1968
1969 DocManager::~DocManager()
1970 {
1971 WX_CLEAR_ARRAY(m_methods);
1972 }
1973
1974 // ---------------------------------------------------------------------------
1975 // IgnoreNamesHandler implementation
1976 // ---------------------------------------------------------------------------
1977
1978 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry *first,
1979 IgnoreListEntry *second)
1980 {
1981 // first compare the classes
1982 int rc = first->m_classname.Cmp(second->m_classname);
1983 if ( rc == 0 )
1984 rc = first->m_funcname.Cmp(second->m_funcname);
1985
1986 return rc;
1987 }
1988
1989 bool IgnoreNamesHandler::AddNamesFromFile(const wxString& filename)
1990 {
1991 wxFile file(filename, wxFile::read);
1992 if ( !file.IsOpened() )
1993 return FALSE;
1994
1995 off_t len = file.Length();
1996 if ( len == wxInvalidOffset )
1997 return FALSE;
1998
1999 char *buf = new char[len + 1];
2000 buf[len] = '\0';
2001
2002 if ( file.Read(buf, len) == wxInvalidOffset ) {
2003 delete [] buf;
2004
2005 return FALSE;
2006 }
2007
2008 wxString line;
2009 for ( const char *current = buf; ; current++ ) {
2010 #ifdef __WXMSW__
2011 // skip DOS line separator
2012 if ( *current == '\r' )
2013 current++;
2014 #endif // wxMSW
2015
2016 if ( *current == '\n' || *current == '\0' ) {
2017 if ( line[0u] != '#' ) {
2018 if ( line.Find(':') != wxNOT_FOUND ) {
2019 wxString classname = line.BeforeFirst(':'),
2020 funcname = line.AfterLast(':');
2021 m_ignore.Add(new IgnoreListEntry(classname, funcname));
2022 }
2023 else {
2024 // entire class
2025 m_ignore.Add(new IgnoreListEntry(line, ""));
2026 }
2027 }
2028 //else: comment
2029
2030 if ( *current == '\0' )
2031 break;
2032
2033 line.Empty();
2034 }
2035 else {
2036 line += *current;
2037 }
2038 }
2039
2040 delete [] buf;
2041
2042 return TRUE;
2043 }
2044
2045 // -----------------------------------------------------------------------------
2046 // global function implementation
2047 // -----------------------------------------------------------------------------
2048
2049 static wxString MakeLabel(const char *classname, const char *funcname)
2050 {
2051 wxString label(classname);
2052 if ( funcname && funcname[0] == '\\' ) {
2053 // we may have some special TeX macro - so far only \destruct exists,
2054 // but may be later others will be added
2055 static const char *macros[] = { "destruct" };
2056 static const char *replacement[] = { "dtor" };
2057
2058 size_t n;
2059 for ( n = 0; n < WXSIZEOF(macros); n++ ) {
2060 if ( strncmp(funcname + 1, macros[n], strlen(macros[n])) == 0 ) {
2061 // found
2062 break;
2063 }
2064 }
2065
2066 if ( n == WXSIZEOF(macros) ) {
2067 wxLogWarning("unknown function name '%s' - leaving as is.",
2068 funcname);
2069 }
2070 else {
2071 funcname = replacement[n];
2072 }
2073 }
2074
2075 if ( funcname ) {
2076 // special treatment for operatorXXX() stuff because the C operators
2077 // are not valid in LaTeX labels
2078 wxString oper;
2079 if ( wxString(funcname).StartsWith("operator", &oper) ) {
2080 label << "operator";
2081
2082 static const struct
2083 {
2084 const char *oper;
2085 const char *name;
2086 } operatorNames[] =
2087 {
2088 { "=", "assign" },
2089 { "==", "equal" },
2090 };
2091
2092 size_t n;
2093 for ( n = 0; n < WXSIZEOF(operatorNames); n++ ) {
2094 if ( oper == operatorNames[n].oper ) {
2095 label << operatorNames[n].name;
2096
2097 break;
2098 }
2099 }
2100
2101 if ( n == WXSIZEOF(operatorNames) ) {
2102 wxLogWarning("unknown operator '%s' - making dummy label.",
2103 oper.c_str());
2104
2105 label << "unknown";
2106 }
2107 }
2108 else // simply use the func name
2109 {
2110 label << funcname;
2111 }
2112 }
2113
2114 label.MakeLower();
2115
2116 return label;
2117 }
2118
2119 static wxString MakeHelpref(const char *argument)
2120 {
2121 wxString helpref;
2122 helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
2123
2124 return helpref;
2125 }
2126
2127 static void TeXFilter(wxString* str)
2128 {
2129 // TeX special which can be quoted (don't include backslash nor braces as
2130 // we generate them
2131 static wxRegEx reNonSpecialSpecials("[#$%&_]"),
2132 reAccents("[~^]");
2133
2134 // just quote
2135 reNonSpecialSpecials.ReplaceAll(str, "\\\\\\0");
2136
2137 // can't quote these ones as they produce accents when preceded by
2138 // backslash, so put them inside verb
2139 reAccents.ReplaceAll(str, "\\\\verb|\\0|");
2140 }
2141
2142 static void TeXUnfilter(wxString* str)
2143 {
2144 // FIXME may be done much more quickly
2145 str->Trim(TRUE);
2146 str->Trim(FALSE);
2147
2148 // undo TeXFilter
2149 static wxRegEx reNonSpecialSpecials("\\\\([#$%&_{}])"),
2150 reAccents("\\\\verb|([~^])|");
2151
2152 reNonSpecialSpecials.ReplaceAll(str, "\\1");
2153 reAccents.ReplaceAll(str, "\\1");
2154 }
2155
2156 static wxString GetAllComments(const spContext& ctx)
2157 {
2158 wxString comments;
2159 const MCommentListT& commentsList = ctx.GetCommentList();
2160 for ( MCommentListT::const_iterator i = commentsList.begin();
2161 i != commentsList.end();
2162 i++ ) {
2163 wxString comment = (*i)->GetText();
2164
2165 // don't take comments like "// ----------" &c
2166 comment.Trim(FALSE);
2167 if ( !!comment &&
2168 comment == wxString(comment[0u], comment.length() - 1) + '\n' )
2169 comments << "\n";
2170 else
2171 comments << comment;
2172 }
2173
2174 return comments;
2175 }
2176
2177 static const char *GetCurrentTime(const char *timeFormat)
2178 {
2179 static char s_timeBuffer[128];
2180 time_t timeNow;
2181 struct tm *ptmNow;
2182
2183 time(&timeNow);
2184 ptmNow = localtime(&timeNow);
2185
2186 strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow);
2187
2188 return s_timeBuffer;
2189 }
2190
2191 static const wxString GetVersionString()
2192 {
2193 wxString version = "$Revision$";
2194 wxRegEx("^\\$Revision$$").ReplaceFirst(&version, "\\1");
2195 return version;
2196 }
2197
2198 /*
2199 $Log$
2200 Revision 1.21 2002/01/04 11:06:09 JS
2201 Fixed missing membersections bug and also bug with functions not being written
2202 in the right class
2203
2204 Revision 1.20 2002/01/03 14:23:33 JS
2205 Added code to make it not duplicate membersections for overloaded functions
2206
2207 Revision 1.19 2002/01/03 13:34:12 JS
2208 Added FlushAll to CloseClass, otherwise text was only flushed right at the end,
2209 and appeared in one file.
2210
2211 Revision 1.18 2002/01/03 12:02:47 JS
2212 Added main() and corrected VC++ project settings
2213
2214 Revision 1.17 2001/11/30 21:43:35 VZ
2215 now the methods are sorted in the correct order in the generated docs
2216
2217 Revision 1.16 2001/11/28 19:27:33 VZ
2218 HelpGen doesn't work in GUI mode
2219
2220 Revision 1.15 2001/11/22 21:59:58 GD
2221 use "..." instead of <...> for wx headers
2222
2223 Revision 1.14 2001/07/19 13:51:29 VZ
2224 fixes to version string
2225
2226 Revision 1.13 2001/07/19 13:44:57 VZ
2227 1. compilation fixes
2228 2. don't quote special characters inside verbatim environment
2229
2230 Revision 1.12 2000/10/09 13:53:33 juliansmart
2231
2232 Doc corrections; added HelpGen project files
2233
2234 Revision 1.11 2000/07/15 19:50:42 cvsuser
2235 merged 2.2 branch
2236
2237 Revision 1.10.2.2 2000/03/27 15:33:10 VZ
2238 don't trasnform output dir name to lower case
2239
2240 Revision 1.10 2000/03/11 10:05:23 VS
2241 now compiles with wxBase
2242
2243 Revision 1.9 2000/01/16 13:25:21 VS
2244 compilation fixes (gcc)
2245
2246 Revision 1.8 1999/09/13 14:29:39 JS
2247
2248 Made HelpGen into a wxWin app (still uses command-line args); moved includes
2249 into src for simplicity; added VC++ 5 project file
2250
2251 Revision 1.7 1999/02/21 22:32:32 VZ
2252 1. more C++ parser fixes - now it almost parses wx/string.h
2253 a) #if/#ifdef/#else (very) limited support
2254 b) param type fix - now indirection chars are correctly handled
2255 c) class/struct/union distinction
2256 d) public/private fixes
2257 e) Dump() function added - very useful for debugging
2258
2259 2. option to ignore parameter names during 'diff' (in fact, they're ignored
2260 by default, and this option switches it on)
2261
2262 Revision 1.6 1999/02/20 23:00:26 VZ
2263 1. new 'diff' mode which seems to work
2264 2. output files are not overwritten in 'dmup' mode
2265 3. fixes for better handling of const functions and operators
2266 ----------------------------
2267 revision 1.5
2268 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
2269 1. Parser improvements
2270 a) const and virtual methods are parsed correctly (not static yet)
2271 b) "const" which is part of the return type is not swallowed
2272
2273 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
2274 "//---------" kind comments discarded now.
2275 ----------------------------
2276 revision 1.4
2277 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
2278
2279 some tweaks to HelpGen
2280 ----------------------------
2281 revision 1.3
2282 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
2283
2284 HelpGen starting to compile with VC++
2285 ----------------------------
2286 revision 1.2
2287 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
2288
2289 supports typedefs, generates "See also:" and adds "virtual " for virtual
2290 functions
2291 ----------------------------
2292 revision 1.1
2293 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
2294
2295 HelpGen is a prototype of the tool for automatic generation of the .tex files
2296 for wxWindows documentation from C++ headers
2297 */
2298
2299 /* vi: set tw=80 et ts=4 sw=4: */