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