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