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