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