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