home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / utils / HelpGen / src / HelpGen.cpp < prev    next >
C/C++ Source or Header  |  2002-01-21  |  71KB  |  2,309 lines

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