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