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