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