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