]> git.saurik.com Git - wxWidgets.git/blame - 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
cecfc5e7
VZ
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/*
5f7cf62f
VZ
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.
cecfc5e7
VZ
22
23 (i) small fixes in the current version
59734eb5 24
cecfc5e7
VZ
25 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
26 2. Document typedefs
27 3. Document global variables
28 4. Document #defines
ed38ec7e 29 +5. Program options
5f7cf62f
VZ
30 6. Include file name/line number in the "diff" messages?
31 +7. Support for vararg functions
cecfc5e7
VZ
32
33 (ii) plans for version 2
34 1. Use wxTextFile for direct file access to avoid one scan method problems
5f7cf62f 35 2. Use command line parser class for the options
d12e3536 36 3. support for overloaded functions in diff mode (search for OVER)
59734eb5 37
5f7cf62f
VZ
38 (iii) plans for version 3
39 1. Merging with existing files
40 2. GUI
cecfc5e7
VZ
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>
ed38ec7e 57 #include <wx/dynarray.h>
b136d1fe 58 #include <wx/wx.h>
cecfc5e7
VZ
59#endif // WX_PRECOMP
60
3d05544e 61#include <wx/file.h>
2f919f99 62#include <wx/regex.h>
3d05544e 63
cecfc5e7
VZ
64// C++ parsing classes
65#include "cjparser.h"
66
67// standard headers
68#include <stdio.h>
69#include <time.h>
70
a7adaeda
VZ
71// argh, Windows defines this
72#ifdef GetCurrentTime
73#undef GetCurrentTime
74#endif
75
5f7cf62f
VZ
76// -----------------------------------------------------------------------------
77// global vars
78// -----------------------------------------------------------------------------
79
28468136
JS
80class HelpGenApp: public wxApp
81{
82public:
83 HelpGenApp() {};
84
a7adaeda
VZ
85 // don't let wxWin parse our cmd line, we do it ourselves
86 virtual bool OnInit() { return TRUE; }
87
88 virtual int OnRun();
28468136
JS
89};
90
91IMPLEMENT_APP(HelpGenApp);
92
cecfc5e7
VZ
93// -----------------------------------------------------------------------------
94// private functions
95// -----------------------------------------------------------------------------
96
ed38ec7e
VZ
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
5f7cf62f 103// [un]quote special TeX characters (in place)
cecfc5e7 104static void TeXFilter(wxString* str);
5f7cf62f 105static void TeXUnfilter(wxString* str); // also trims spaces
cecfc5e7 106
ed38ec7e
VZ
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
2f919f99
VZ
114// get the string containing the program version
115static const wxString GetVersionString();
116
cecfc5e7
VZ
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:
59734eb5 125 wxTeXFile() { }
cecfc5e7 126
a7adaeda
VZ
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
cecfc5e7
VZ
135 bool WriteTeX(const wxString& s)
136 {
137 wxString t(s);
138 TeXFilter(&t);
139
140 return wxFile::Write(t);
141 }
59734eb5
VZ
142
143private:
144 wxTeXFile(const wxTeXFile&);
145 wxTeXFile& operator=(const wxTeXFile&);
cecfc5e7
VZ
146};
147
d12e3536
VZ
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
5f7cf62f 206// visitor implementation which writes all collected data to a .tex file
cecfc5e7
VZ
207class HelpGenVisitor : public spVisitor
208{
209public:
210 // ctor
d12e3536 211 HelpGenVisitor(const wxString& directoryOut, bool overwrite);
cecfc5e7
VZ
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 );
59734eb5 217 virtual void VisitPreprocessorLine( spPreprocessorLine& pd );
cecfc5e7
VZ
218 virtual void VisitAttribute( spAttribute& attr );
219 virtual void VisitOperation( spOperation& op );
220 virtual void VisitParameter( spParameter& param );
221
222 void EndVisit();
223
d12e3536
VZ
224 // get our `ignore' object
225 IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
226
cecfc5e7
VZ
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();
59734eb5 242
cecfc5e7
VZ
243 // terminate the function documentation if it was started
244 void CloseFunction();
245
d12e3536
VZ
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?
59734eb5 249 wxTeXFile m_file; // file we're writing to now
cecfc5e7
VZ
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
a7adaeda 259 wxString m_textStoredTypedefs,
cecfc5e7 260 m_textStoredFunctionComment;
ed38ec7e 261
a7adaeda
VZ
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
59734eb5 267 // headers included by this file
ed38ec7e 268 wxArrayString m_headers;
59734eb5 269
d12e3536
VZ
270 // ignore handler: tells us which classes to ignore for doc generation
271 // purposes
272 IgnoreNamesHandler m_ignoreNames;
273
59734eb5
VZ
274private:
275 HelpGenVisitor(const HelpGenVisitor&);
276 HelpGenVisitor& operator=(const HelpGenVisitor&);
cecfc5e7
VZ
277};
278
5f7cf62f
VZ
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:
d12e3536 285 DocManager(bool checkParamNames);
5f7cf62f
VZ
286 ~DocManager();
287
5f7cf62f
VZ
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
d12e3536
VZ
294 // get our `ignore' object
295 IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
296
5f7cf62f
VZ
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 // -------------------------------------------
5f7cf62f 338
d12e3536 339 IgnoreNamesHandler m_ignoreNames;
5f7cf62f
VZ
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;
d12e3536
VZ
431
432 // are we checking parameter names?
433 bool m_checkParamNames;
434
435private:
436 DocManager(const DocManager&);
437 DocManager& operator=(const DocManager&);
5f7cf62f
VZ
438};
439
cecfc5e7
VZ
440// -----------------------------------------------------------------------------
441// private functions
442// -----------------------------------------------------------------------------
443
444// =============================================================================
445// implementation
446// =============================================================================
447
ed38ec7e
VZ
448// this function never returns
449static void usage()
450{
a7adaeda 451 wxString prog = wxTheApp->argv[0];
f6bcfd97 452 wxString basename = prog.AfterLast('/');
5f7cf62f
VZ
453#ifdef __WXMSW__
454 if ( !basename )
f6bcfd97 455 basename = prog.AfterLast('\\');
5f7cf62f
VZ
456#endif
457 if ( !basename )
458 basename = prog;
459
a7adaeda 460 wxLogMessage(
5f7cf62f
VZ
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"
d12e3536 468" -i file file with classes/function to ignore\n"
5f7cf62f
VZ
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"
d12e3536 474" -f overwrite existing files\n"
5f7cf62f
VZ
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"
d12e3536
VZ
480" mode specific options are:\n"
481" -p do check parameter names (not done by default)\n"
5f7cf62f 482"\n", basename.c_str(), basename.c_str());
a7adaeda 483
ed38ec7e
VZ
484 exit(1);
485}
486
de528224 487int HelpGenApp::OnRun()
cecfc5e7 488{
5f7cf62f
VZ
489 enum
490 {
491 Mode_None,
492 Mode_Dump,
493 Mode_Diff
494 } mode = Mode_None;
495
cecfc5e7 496 if ( argc < 2 ) {
ed38ec7e 497 usage();
cecfc5e7
VZ
498 }
499
5f7cf62f 500 wxArrayString filesH, filesTeX;
d12e3536
VZ
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'?
59734eb5 505
5f7cf62f 506 for ( int current = 1; current < argc ; current++ ) {
59734eb5 507 // all options have one letter
5f7cf62f
VZ
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();
a7adaeda
VZ
524 // doesn't return
525
526 case 'V':
527 // version requested
528 wxLogMessage("HelpGen version %s\n"
529 "(c) 1999-2001 Vadim Zeitlin\n",
2f919f99 530 GetVersionString().c_str());
a7adaeda 531 return 0;
5f7cf62f
VZ
532
533 case 'i':
d12e3536
VZ
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':
5f7cf62f 545 if ( mode != Mode_Diff ) {
d12e3536 546 wxLogError("-p is only valid with diff.");
5f7cf62f
VZ
547
548 break;
549 }
59734eb5 550
d12e3536
VZ
551 paramNames = TRUE;
552 continue;
553
554 case 'f':
555 if ( mode != Mode_Dump ) {
556 wxLogError("-f is only valid with dump.");
59734eb5 557
5f7cf62f
VZ
558 break;
559 }
59734eb5 560
d12e3536 561 overwrite = TRUE;
5f7cf62f
VZ
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 }
59734eb5 577
5f7cf62f
VZ
578 directoryOut = argv[current];
579 if ( !!directoryOut ) {
580 // terminate with a '/' if it doesn't have it
581 switch ( directoryOut.Last() ) {
582 case '/':
59734eb5 583#ifdef __WXMSW__
5f7cf62f 584 case '\\':
59734eb5 585#endif
5f7cf62f 586 break;
ed38ec7e 587
5f7cf62f
VZ
588 default:
589 directoryOut += '/';
590 }
59734eb5 591 }
5f7cf62f 592 //else: it's empty, do nothing
59734eb5 593
5f7cf62f 594 continue;
ed38ec7e 595
5f7cf62f 596 default:
d12e3536 597 wxLogError("unknown option '%s'", argv[current]);
5f7cf62f
VZ
598 break;
599 }
59734eb5 600 }
d12e3536
VZ
601 else {
602 wxLogError("only one letter options are allowed, not '%s'.",
603 argv[current]);
604 }
59734eb5 605
5f7cf62f 606 // only get here after a break from switch or from else branch of if
59734eb5 607
5f7cf62f
VZ
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 {
d12e3536 617 wxLogError("unknown mode '%s'.", argv[current]);
5f7cf62f
VZ
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 }
ed38ec7e 634 }
cecfc5e7
VZ
635
636 // create a parser object and a visitor derivation
637 CJSourceParser parser;
d12e3536
VZ
638 HelpGenVisitor visitor(directoryOut, overwrite);
639 if ( !!ignoreFile && mode == Mode_Dump )
640 visitor.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
641
5f7cf62f 642 spContext *ctxTop = NULL;
cecfc5e7 643
5f7cf62f
VZ
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);
cecfc5e7 649 if ( !ctxTop ) {
5f7cf62f
VZ
650 wxLogWarning("Header file '%s' couldn't be processed.",
651 header.c_str());
cecfc5e7 652 }
5f7cf62f
VZ
653 else if ( mode == Mode_Dump ) {
654 ((spFile *)ctxTop)->mFileName = header;
cecfc5e7
VZ
655 visitor.VisitAll(*ctxTop);
656 visitor.EndVisit();
657 }
d12e3536
VZ
658
659#ifdef __WXDEBUG__
660 if ( 0 && ctxTop )
661 ctxTop->Dump("");
662#endif // __WXDEBUG__
cecfc5e7
VZ
663 }
664
5f7cf62f
VZ
665 // parse all TeX files
666 if ( mode == Mode_Diff ) {
667 if ( !ctxTop ) {
668 wxLogError("Can't complete diff.");
669
670 // failure
a7adaeda 671 return FALSE;
5f7cf62f
VZ
672 }
673
d12e3536 674 DocManager docman(paramNames);
5f7cf62f
VZ
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 )
d12e3536 686 docman.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
5f7cf62f
VZ
687
688 docman.DumpDifferences(ctxTop);
689 }
690
a7adaeda 691 return 0;
cecfc5e7
VZ
692}
693
694// -----------------------------------------------------------------------------
695// HelpGenVisitor implementation
696// -----------------------------------------------------------------------------
697
d12e3536
VZ
698HelpGenVisitor::HelpGenVisitor(const wxString& directoryOut,
699 bool overwrite)
700 : m_directoryOut(directoryOut)
701{
702 m_overwrite = overwrite;
703
704 Reset();
705}
706
cecfc5e7
VZ
707void HelpGenVisitor::Reset()
708{
709 m_inClass =
710 m_inFunction =
711 m_inTypesSection =
5f7cf62f 712 m_inMethodSection = FALSE;
ed38ec7e
VZ
713
714 m_textStoredTypedefs =
ed38ec7e 715 m_textStoredFunctionComment = "";
a7adaeda
VZ
716
717 m_storedEnums.Empty();
718 m_storedEnumsVerb.Empty();
ed38ec7e 719 m_headers.Empty();
cecfc5e7
VZ
720}
721
722void HelpGenVisitor::InsertTypedefDocs()
723{
724 m_file.WriteTeX(m_textStoredTypedefs);
725 m_textStoredTypedefs.Empty();
726}
727
728void HelpGenVisitor::InsertEnumDocs()
729{
a7adaeda
VZ
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();
cecfc5e7
VZ
739}
740
741void HelpGenVisitor::InsertDataStructuresHeader()
742{
743 if ( !m_inTypesSection ) {
5f7cf62f 744 m_inTypesSection = TRUE;
cecfc5e7 745
a7adaeda 746 m_file.Write("\\wxheading{Data structures}\n\n");
cecfc5e7
VZ
747 }
748}
749
750void HelpGenVisitor::InsertMethodsHeader()
751{
752 if ( !m_inMethodSection ) {
5f7cf62f 753 m_inMethodSection = TRUE;
cecfc5e7 754
a7adaeda 755 m_file.Write( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
cecfc5e7
VZ
756 }
757}
758
759void HelpGenVisitor::CloseFunction()
760{
761 if ( m_inFunction ) {
5f7cf62f 762 m_inFunction = FALSE;
cecfc5e7
VZ
763
764 wxString totalText;
765 if ( m_isFirstParam ) {
766 // no params found
767 totalText << "\\void";
768 }
769
770 totalText << "}\n\n";
a7adaeda 771 m_file.Write(totalText);
cecfc5e7
VZ
772
773 if ( !m_textStoredFunctionComment.IsEmpty() )
a7adaeda 774 m_file.WriteTeX(m_textStoredFunctionComment + '\n');
cecfc5e7
VZ
775 }
776}
777
778void HelpGenVisitor::EndVisit()
779{
780 CloseFunction();
ed38ec7e 781
d12e3536
VZ
782 m_fileHeader.Empty();
783
5f7cf62f
VZ
784 wxLogVerbose("%s: finished generating for the current file.",
785 GetCurrentTime("%H:%M:%S"));
cecfc5e7
VZ
786}
787
788void HelpGenVisitor::VisitFile( spFile& file )
789{
d12e3536 790 m_fileHeader = file.mFileName;
5f7cf62f 791 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
d12e3536 792 GetCurrentTime("%H:%M:%S"), m_fileHeader.c_str());
cecfc5e7
VZ
793}
794
795void HelpGenVisitor::VisitClass( spClass& cl )
796{
d12e3536
VZ
797 m_inClass = FALSE; // will be left FALSE on error
798
cecfc5e7
VZ
799 wxString name = cl.GetName();
800
d12e3536
VZ
801 if ( m_ignoreNames.IgnoreClass(name) ) {
802 wxLogVerbose("Skipping ignored class '%s'.", name.c_str());
803
804 return;
805 }
806
cecfc5e7
VZ
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
f6bcfd97 809 wxString filename;
59734eb5
VZ
810 if ( name(0, 2) == "wx" ) {
811 filename << name.c_str() + 2;
812 }
813 else {
814 filename << name;
cecfc5e7
VZ
815 }
816
817 filename.MakeLower();
818 filename += ".tex";
f6bcfd97 819 filename.Prepend(m_directoryOut);
cecfc5e7 820
d12e3536
VZ
821 if ( !m_overwrite && wxFile::Exists(filename) ) {
822 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
5f7cf62f
VZ
823 filename.c_str());
824
5f7cf62f
VZ
825 return;
826 }
827
cecfc5e7
VZ
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 =
5f7cf62f 837 m_inTypesSection = FALSE;
cecfc5e7
VZ
838
839 wxLogInfo("Created new file '%s' for class '%s'.",
840 filename.c_str(), name.c_str());
841
a7adaeda
VZ
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",
2f919f99 851 GetVersionString().c_str(),
a7adaeda
VZ
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
cecfc5e7
VZ
859 // the entire text we're writing to file
860 wxString totalText;
861
ed38ec7e
VZ
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",
59734eb5 871 "file",
ed38ec7e
VZ
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
cecfc5e7
VZ
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() ) {
ed38ec7e 928 wxString comment = GetAllComments(cl);
cecfc5e7
VZ
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 {
5f7cf62f 941 bool first = TRUE;
cecfc5e7
VZ
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 {
5f7cf62f 950 first = FALSE;
cecfc5e7
VZ
951 }
952
953 wxString baseclass = *i;
dface61c 954 derived << "\\helpref{" << baseclass << "}";
59734eb5 955 derived << "{" << baseclass.MakeLower() << "}";
cecfc5e7
VZ
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
a7adaeda
VZ
983 wxString enumeration = GetAllComments(en),
984 enumerationVerb;
985
986 enumerationVerb << "\\begin{verbatim}\n"
987 << en.mEnumContent
988 << "\n\\end{verbatim}\n";
cecfc5e7
VZ
989
990 // remember for later use if we're not inside a class yet
991 if ( !m_inClass ) {
a7adaeda
VZ
992 m_storedEnums.Add(enumeration);
993 m_storedEnumsVerb.Add(enumerationVerb);
cecfc5e7
VZ
994 }
995 else {
996 // write the header for this section if not done yet
997 InsertDataStructuresHeader();
998
cecfc5e7 999 m_file.WriteTeX(enumeration);
a7adaeda
VZ
1000 m_file.WriteVerbatim(enumerationVerb);
1001 m_file.Write('\n');
cecfc5e7
VZ
1002 }
1003}
1004
1005void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
1006{
1007 CloseFunction();
1008
ed38ec7e
VZ
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 }
cecfc5e7
VZ
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
ed38ec7e 1060 wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
cecfc5e7
VZ
1061}
1062
1063void HelpGenVisitor::VisitOperation( spOperation& op )
1064{
1065 CloseFunction();
1066
d12e3536
VZ
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
cecfc5e7
VZ
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
d12e3536
VZ
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
cecfc5e7
VZ
1094 InsertMethodsHeader();
1095
1096 // save state info
1097 m_inFunction =
5f7cf62f 1098 m_isFirstParam = TRUE;
cecfc5e7 1099
ed38ec7e 1100 m_textStoredFunctionComment = GetAllComments(op);
cecfc5e7
VZ
1101
1102 // start function documentation
1103 wxString totalText;
59734eb5 1104
cecfc5e7
VZ
1105 // check for the special case of dtor
1106 wxString dtor;
d12e3536
VZ
1107 if ( (funcname[0] == '~') && (classname == funcname.c_str() + 1) ) {
1108 dtor.Printf("\\destruct{%s}", classname.c_str());
cecfc5e7
VZ
1109 funcname = dtor;
1110 }
1111
59734eb5
VZ
1112 totalText.Printf("\n"
1113 "\\membersection{%s::%s}\\label{%s}\n"
1114 "\n"
ed38ec7e 1115 "\\%sfunc{%s%s}{%s}{",
d12e3536 1116 classname.c_str(), funcname.c_str(),
cecfc5e7
VZ
1117 MakeLabel(classname, funcname).c_str(),
1118 op.mIsConstant ? "const" : "",
ed38ec7e 1119 op.mIsVirtual ? "virtual " : "",
cecfc5e7 1120 op.mRetType.c_str(),
d12e3536 1121 funcname.c_str());
cecfc5e7
VZ
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 ) {
5f7cf62f 1133 m_isFirstParam = FALSE;
cecfc5e7
VZ
1134 }
1135 else {
1136 totalText << ", ";
1137 }
59734eb5 1138
cecfc5e7
VZ
1139 totalText << "\\param{" << param.mType << " }{" << param.GetName();
1140 wxString defvalue = param.mInitVal;
1141 if ( !defvalue.IsEmpty() ) {
1142 totalText << " = " << defvalue;
1143 }
59734eb5 1144
cecfc5e7
VZ
1145 totalText << '}';
1146
1147 m_file.WriteTeX(totalText);
1148}
1149
5f7cf62f
VZ
1150// ---------------------------------------------------------------------------
1151// DocManager
1152// ---------------------------------------------------------------------------
1153
d12e3536
VZ
1154DocManager::DocManager(bool checkParamNames)
1155{
1156 m_checkParamNames = checkParamNames;
1157}
1158
5f7cf62f
VZ
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");
d12e3536 1434 while ( lenMatch && (current - buf < len) ) {
5f7cf62f
VZ
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 ) {
d12e3536 1577 if ( !m_ignoreNames.IgnoreClass(nameClass) ) {
5f7cf62f
VZ
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() ) {
d12e3536 1620 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
5f7cf62f
VZ
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
d12e3536 1635 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
5f7cf62f
VZ
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);
d12e3536
VZ
1687 if ( m_checkParamNames &&
1688 (param.GetName() != ctxParam->mName) ) {
5f7cf62f
VZ
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 {
d12e3536 1730 // TODO OVER add real support for overloaded methods
5f7cf62f 1731
d12e3536 1732 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
5f7cf62f
VZ
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();
d12e3536 1757 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
5f7cf62f
VZ
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);
5f7cf62f
VZ
1788}
1789
d12e3536
VZ
1790// ---------------------------------------------------------------------------
1791// IgnoreNamesHandler implementation
1792// ---------------------------------------------------------------------------
1793
1794int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry *first,
1795 IgnoreListEntry *second)
5f7cf62f
VZ
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
d12e3536 1805bool IgnoreNamesHandler::AddNamesFromFile(const wxString& filename)
5f7cf62f
VZ
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
cecfc5e7
VZ
1861// -----------------------------------------------------------------------------
1862// global function implementation
1863// -----------------------------------------------------------------------------
1864
1865static wxString MakeLabel(const char *classname, const char *funcname)
1866{
1867 wxString label(classname);
ed38ec7e 1868 if ( funcname && funcname[0] == '\\' ) {
cecfc5e7
VZ
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" };
59734eb5 1873
cecfc5e7
VZ
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
ed38ec7e
VZ
1891 if ( funcname )
1892 label << funcname;
cecfc5e7
VZ
1893
1894 label.MakeLower();
1895
1896 return label;
1897}
1898
ed38ec7e
VZ
1899static wxString MakeHelpref(const char *argument)
1900{
1901 wxString helpref;
1902 helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
1903
1904 return helpref;
1905}
1906
a7adaeda
VZ
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
5f7cf62f
VZ
1922static void TeXUnfilter(wxString* str)
1923{
1924 // FIXME may be done much more quickly
1925 str->Trim(TRUE);
1926 str->Trim(FALSE);
1927
a7adaeda
VZ
1928 // undo TeXFilter
1929 static wxRegEx reNonSpecialSpecials("\\\\([#$%&_{}])"),
1930 reAccents("\\\\verb|([~^])|");
5f7cf62f 1931
a7adaeda
VZ
1932 reNonSpecialSpecials.ReplaceAll(str, "\\1");
1933 reAccents.ReplaceAll(str, "\\1");
cecfc5e7
VZ
1934}
1935
ed38ec7e
VZ
1936static wxString GetAllComments(const spContext& ctx)
1937{
59734eb5
VZ
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;
ed38ec7e
VZ
1952 }
1953
59734eb5 1954 return comments;
ed38ec7e
VZ
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
2f919f99
VZ
1971static const wxString GetVersionString()
1972{
1973 wxString version = "$Revision$";
1974 wxRegEx("^\\$Revision$$").ReplaceFirst(&version, "\\1");
1975 return version;
1976}
1977
5f7cf62f
VZ
1978/*
1979 $Log$
2f919f99
VZ
1980 Revision 1.14 2001/07/19 13:51:29 VZ
1981 fixes to version string
1982
a7adaeda
VZ
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
4e28924c 1987 Revision 1.12 2000/10/09 13:53:33 juliansmart
a7adaeda 1988
4e28924c
JS
1989 Doc corrections; added HelpGen project files
1990
f6bcfd97
BP
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
de528224
VS
1997 Revision 1.10 2000/03/11 10:05:23 VS
1998 now compiles with wxBase
1999
b136d1fe
VS
2000 Revision 1.9 2000/01/16 13:25:21 VS
2001 compilation fixes (gcc)
2002
28468136 2003 Revision 1.8 1999/09/13 14:29:39 JS
b136d1fe 2004
28468136
JS
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
d12e3536
VZ
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
5f7cf62f
VZ
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
d12e3536
VZ
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
5f7cf62f
VZ
2054*/
2055
cecfc5e7 2056/* vi: set tw=80 et ts=4 sw=4: */