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