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