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