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