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