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