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