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