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