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