1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Main program file for HelpGen 
   4 // Author:      Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   8 // Copyright:   (c) 1999 VZ 
  10 ///////////////////////////////////////////////////////////////////////////// 
  15    (i) small fixes in the current version 
  17    +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile) 
  19     3. Document global variables 
  23    (ii) plans for version 2 
  24     1. Use wxTextFile for direct file access to avoid one scan method problems 
  25     2. Use command line parsrer class for the options 
  29 // ============================================================================= 
  31 // ============================================================================= 
  33 // ----------------------------------------------------------------------------- 
  35 // ----------------------------------------------------------------------------- 
  38 #include "wx/wxprec.h" 
  41     #include <wx/string.h> 
  43     #include <wx/dynarray.h> 
  48 // C++ parsing classes 
  55 // ----------------------------------------------------------------------------- 
  57 // ----------------------------------------------------------------------------- 
  59 // return the label for the given function name (i.e. argument of \label) 
  60 static wxString 
MakeLabel(const char *classname
, const char *funcname 
= NULL
); 
  62 // return the whole \helpref{arg}{arg_label} string 
  63 static wxString 
MakeHelpref(const char *argument
); 
  65 // quotes special TeX characters in place 
  66 static void TeXFilter(wxString
* str
); 
  68 // get all comments associated with this context 
  69 static wxString 
GetAllComments(const spContext
& ctx
); 
  71 // get the string with current time (returns pointer to static buffer) 
  72 // timeFormat is used for the call of strftime(3) 
  77 static const char *GetCurrentTime(const char *timeFormat
); 
  79 // ----------------------------------------------------------------------------- 
  81 // ----------------------------------------------------------------------------- 
  83 // add a function which sanitazes the string before writing it to the file 
  84 class wxTeXFile 
: public wxFile
 
  89     bool WriteTeX(const wxString
& s
) 
  94         return wxFile::Write(t
); 
  98     wxTeXFile(const wxTeXFile
&); 
  99     wxTeXFile
& operator=(const wxTeXFile
&); 
 102 class HelpGenVisitor 
: public spVisitor
 
 106     HelpGenVisitor(const wxString
& directoryOut
) : m_directoryOut(directoryOut
) 
 111     virtual void VisitFile( spFile
& fl 
); 
 112     virtual void VisitClass( spClass
& cl 
); 
 113     virtual void VisitEnumeration( spEnumeration
& en 
); 
 114     virtual void VisitTypeDef( spTypeDef
& td 
); 
 115     virtual void VisitPreprocessorLine( spPreprocessorLine
& pd 
); 
 116     virtual void VisitAttribute( spAttribute
& attr 
); 
 117     virtual void VisitOperation( spOperation
& op 
); 
 118     virtual void VisitParameter( spParameter
& param 
); 
 122     // shut up g++ warning (ain't it stupid?) 
 123     virtual ~HelpGenVisitor() { } 
 126     // (re)initialize the state 
 129     // insert documentation for enums/typedefs coming immediately before the 
 130     // class declaration into the class documentation 
 131     void InsertTypedefDocs(); 
 132     void InsertEnumDocs(); 
 134     // write the headers for corresponding sections (only once) 
 135     void InsertDataStructuresHeader(); 
 136     void InsertMethodsHeader(); 
 138     // terminate the function documentation if it was started 
 139     void CloseFunction(); 
 141     wxString  m_directoryOut
;   // directory for the output 
 142     wxTeXFile m_file
;           // file we're writing to now 
 145     bool m_inClass
,         // TRUE after file successfully opened 
 146          m_inTypesSection
,  // enums & typedefs go there 
 147          m_inMethodSection
, // functions go here 
 148          m_isFirstParam
,    // first parameter of current function? 
 149          m_inFunction
;      // we're parsing a function declaration 
 151     // holders for "saved" documentation 
 152     wxString m_textStoredEnums
, 
 153              m_textStoredTypedefs
, 
 154              m_textStoredFunctionComment
; 
 156     // headers included by this file 
 157     wxArrayString m_headers
; 
 160     HelpGenVisitor(const HelpGenVisitor
&); 
 161     HelpGenVisitor
& operator=(const HelpGenVisitor
&); 
 164 // ----------------------------------------------------------------------------- 
 166 // ----------------------------------------------------------------------------- 
 168 // ============================================================================= 
 170 // ============================================================================= 
 172 // this function never returns 
 175     wxLogError("usage: HelpGen [-q|-v] [-o outdir] <header files...>\n"); 
 180 int main(int argc
, char **argv
) 
 186     wxString directoryOut
; 
 189     for ( first 
= 1; (first 
< argc
) && argv
[first
][0] == '-'; first
++ ) { 
 190         // all options have one letter 
 191         if ( argv
[first
][2] == '\0' ) { 
 192             switch ( argv
[first
][1] ) { 
 195                     wxLog::GetActiveTarget()->SetVerbose(); 
 200                     wxLog::GetActiveTarget()->SetVerbose(false); 
 205                     if ( first 
>= argc 
) { 
 206                         wxLogError("-o option requires an argument."); 
 211                     directoryOut 
= argv
[first
]; 
 212                     if ( !!directoryOut 
) { 
 213                         // terminate with a '/' if it doesn't have it 
 214                         switch ( directoryOut
.Last() ) { 
 225                     //else: it's empty, do nothing 
 234         // only get here after a break from switch or from else branch of if 
 235         wxLogError("unknown option '%s'", argv
[first
]); 
 240     // create a parser object and a visitor derivation 
 241     CJSourceParser parser
; 
 242     HelpGenVisitor 
visitor(directoryOut
); 
 245     for ( int i 
= first
; i 
< argc
; i
++ ) { 
 246         spContext 
*ctxTop 
= parser
.ParseFile(argv
[i
]); 
 248             wxLogWarning("File '%s' couldn't be processed.", argv
[i
]); 
 251             ((spFile 
*)ctxTop
)->mFileName 
= argv
[i
]; 
 252             visitor
.VisitAll(*ctxTop
); 
 260 // ----------------------------------------------------------------------------- 
 261 // HelpGenVisitor implementation 
 262 // ----------------------------------------------------------------------------- 
 264 void HelpGenVisitor::Reset() 
 269     m_inMethodSection 
= false; 
 271     m_textStoredTypedefs 
= 
 273     m_textStoredFunctionComment 
= ""; 
 277 void HelpGenVisitor::InsertTypedefDocs() 
 279     m_file
.WriteTeX(m_textStoredTypedefs
); 
 280     m_textStoredTypedefs
.Empty(); 
 283 void HelpGenVisitor::InsertEnumDocs() 
 285     m_file
.WriteTeX(m_textStoredEnums
); 
 286     m_textStoredEnums
.Empty(); 
 289 void HelpGenVisitor::InsertDataStructuresHeader() 
 291     if ( !m_inTypesSection 
) { 
 292         m_inTypesSection 
= true; 
 294         m_file
.WriteTeX("\\wxheading{Data structures}\n\n"); 
 298 void HelpGenVisitor::InsertMethodsHeader() 
 300     if ( !m_inMethodSection 
) { 
 301         m_inMethodSection 
= true; 
 303         m_file
.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n"); 
 307 void HelpGenVisitor::CloseFunction() 
 309     if ( m_inFunction 
) { 
 310         m_inFunction 
= false; 
 313         if ( m_isFirstParam 
) { 
 315             totalText 
<< "\\void"; 
 318         totalText 
<< "}\n\n"; 
 320         if ( !m_textStoredFunctionComment
.IsEmpty() ) 
 321             totalText 
<< m_textStoredFunctionComment 
<< '\n'; 
 323         m_file
.WriteTeX(totalText
); 
 327 void HelpGenVisitor::EndVisit() 
 331     wxLogInfo("%s: finished parsing the current file.", 
 332               GetCurrentTime("%H:%M:%S")); 
 335 void HelpGenVisitor::VisitFile( spFile
& file 
) 
 337     wxLogInfo("%s: started to parse classes from file '%s'...", 
 338               GetCurrentTime("%H:%M:%S"), file
.mFileName
.c_str()); 
 341 void HelpGenVisitor::VisitClass( spClass
& cl 
) 
 343     wxString name 
= cl
.GetName(); 
 345     // the file name is built from the class name by removing the leading "wx" 
 346     // if any and converting it to the lower case 
 347     wxString filename 
= m_directoryOut
; 
 348     if ( name(0, 2) == "wx" ) { 
 349         filename 
<< name
.c_str() + 2; 
 355     filename
.MakeLower(); 
 358     m_inClass 
= m_file
.Open(filename
, wxFile::write
); 
 360         wxLogError("Can't generate documentation for the class '%s'.", 
 367     m_inTypesSection 
= false; 
 369     wxLogInfo("Created new file '%s' for class '%s'.", 
 370               filename
.c_str(), name
.c_str()); 
 372     // the entire text we're writing to file 
 375     // write out the header 
 379                       "%% automatically generated by HelpGen from\n" 
 384                       "\\section{\\class{%s}}\\label{%s}\n", 
 385                       filename
.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"), 
 386                       name
.c_str(), wxString(name
).MakeLower().c_str()); 
 388         totalText 
<< header 
<< '\n'; 
 391     // if the header includes other headers they must be related to it... try to 
 392     // automatically generate the "See also" clause 
 393     if ( !m_headers
.IsEmpty() ) { 
 394         // correspondence between wxWindows headers and class names 
 395         static const char *headers
[] = { 
 404         // NULL here means not to insert anything in "See also" for the 
 405         // corresponding header 
 406         static const char *classes
[] = { 
 415         wxASSERT_MSG( WXSIZEOF(headers
) == WXSIZEOF(classes
), 
 416                       "arrays must be in sync!" ); 
 418         wxArrayInt interestingClasses
; 
 420         size_t count 
= m_headers
.Count(), index
; 
 421         for ( size_t n 
= 0; n 
< count
; n
++ ) { 
 422             wxString baseHeaderName 
= m_headers
[n
].Before('.'); 
 423             if ( baseHeaderName(0, 3) != "wx/" ) 
 426             baseHeaderName
.erase(0, 3); 
 427             for ( index 
= 0; index 
< WXSIZEOF(headers
); index
++ ) { 
 428                 if ( Stricmp(baseHeaderName
, headers
[index
]) == 0 ) 
 432             if ( (index 
< WXSIZEOF(headers
)) && classes
[index
] ) { 
 433                 // interesting header 
 434                 interestingClasses
.Add(index
); 
 438         if ( !interestingClasses
.IsEmpty() ) { 
 439             // do generate "See also" clause 
 440             totalText 
<< "\\wxheading{See also:}\n\n"; 
 442             count 
= interestingClasses
.Count(); 
 443             for ( index 
= 0; index 
< count
; index
++ ) { 
 447                 totalText 
<< MakeHelpref(classes
[interestingClasses
[index
]]); 
 454     // the comment before the class generally explains what is it for so put it 
 455     // in place of the class description 
 456     if ( cl
.HasComments() ) { 
 457         wxString comment 
= GetAllComments(cl
); 
 459         totalText 
<< '\n' << comment 
<< '\n'; 
 462     // derived from section 
 463     wxString derived 
= "\\wxheading{Derived from}\n\n"; 
 465     const StrListT
& baseClasses 
= cl
.mSuperClassNames
; 
 466     if ( baseClasses
.size() == 0 ) { 
 467         derived 
<< "No base class"; 
 471         for ( StrListT::const_iterator i 
= baseClasses
.begin(); 
 472               i 
!= baseClasses
.end(); 
 475                 // separate from the previous one 
 482             wxString baseclass 
= *i
; 
 483             derived 
<< "\\helpref{" << baseclass 
<< "}"; 
 484             derived 
<< "{" << baseclass
.MakeLower()  << "}"; 
 487     totalText 
<< derived 
<< "\n\n"; 
 489     // write all this to file 
 490     m_file
.WriteTeX(totalText
); 
 492     // if there were any enums/typedefs before, insert their documentation now 
 493     InsertDataStructuresHeader(); 
 498 void HelpGenVisitor::VisitEnumeration( spEnumeration
& en 
) 
 502     if ( m_inMethodSection 
) { 
 503         // FIXME that's a bug, but tell the user aboit it nevertheless... we 
 504         // should be smart enough to process even the enums which come after the 
 506         wxLogWarning("enum '%s' ignored, please put it before the class " 
 507                      "methods.", en
.GetName().c_str()); 
 511     // simply copy the enum text in the docs 
 512     wxString enumeration 
= GetAllComments(en
); 
 513     enumeration 
<< "{\\small \\begin{verbatim}\n" 
 515                 << "\n\\end{verbatim}}\n"; 
 517     // remember for later use if we're not inside a class yet 
 519         if ( !m_textStoredEnums
.IsEmpty() ) { 
 520             m_textStoredEnums 
<< '\n'; 
 523         m_textStoredEnums 
<< enumeration
; 
 526         // write the header for this section if not done yet 
 527         InsertDataStructuresHeader(); 
 530         m_file
.WriteTeX(enumeration
); 
 534 void HelpGenVisitor::VisitTypeDef( spTypeDef
& td 
) 
 538     if ( m_inMethodSection 
) { 
 539         // FIXME that's a bug, but tell the user aboit it nevertheless... 
 540         wxLogWarning("typedef '%s' ignored, please put it before the class " 
 541                      "methods.", td
.GetName().c_str()); 
 546     typedefdoc 
<< "{\\small \\begin{verbatim}\n" 
 547                << "typedef " << td
.mOriginalType 
<< ' ' << td
.GetName() 
 548                << "\n\\end{verbatim}}\n" 
 549                << GetAllComments(td
); 
 551     // remember for later use if we're not inside a class yet 
 553         if ( !m_textStoredTypedefs
.IsEmpty() ) { 
 554             m_textStoredTypedefs 
<< '\n'; 
 557         m_textStoredTypedefs 
<< typedefdoc
; 
 560         // write the header for this section if not done yet 
 561         InsertDataStructuresHeader(); 
 564         m_file
.WriteTeX(typedefdoc
); 
 568 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine
& pd 
) 
 570     switch ( pd
.GetStatementType() ) { 
 571         case SP_PREP_DEF_INCLUDE_FILE
: 
 572             m_headers
.Add(pd
.CPP_GetIncludedFileNeme()); 
 575         case SP_PREP_DEF_DEFINE_SYMBOL
: 
 576             // TODO decide if it's a constant and document it if it is 
 581 void HelpGenVisitor::VisitAttribute( spAttribute
& attr 
) 
 585     // only document the public member variables 
 586     if ( !m_inClass 
|| !attr
.IsPublic() ) 
 589     wxLogWarning("Ignoring member variable '%s'.", attr
.GetName().c_str()); 
 592 void HelpGenVisitor::VisitOperation( spOperation
& op 
) 
 596     if ( !m_inClass 
|| !op
.IsInClass() ) { 
 597         // FIXME that's a bug too 
 598         wxLogWarning("skipped global function '%s'.", op
.GetName().c_str()); 
 603     if ( op
.mVisibility 
== SP_VIS_PRIVATE 
) { 
 604         // FIXME should we document protected functions? 
 608     InsertMethodsHeader(); 
 612     m_isFirstParam 
= true; 
 614     m_textStoredFunctionComment 
= GetAllComments(op
); 
 616     // start function documentation 
 618     const char *funcname 
= op
.GetName().c_str(); 
 619     const char *classname 
= op
.GetClass().GetName().c_str(); 
 621     // check for the special case of dtor 
 623     if ( (funcname
[0] == '~') && (strcmp(funcname 
+ 1, classname
) == 0) ) { 
 624         dtor
.Printf("\\destruct{%s}", classname
); 
 628     totalText
.Printf("\n" 
 629                      "\\membersection{%s::%s}\\label{%s}\n" 
 631                      "\\%sfunc{%s%s}{%s}{", 
 633                      MakeLabel(classname
, funcname
).c_str(), 
 634                      op
.mIsConstant 
? "const" : "", 
 635                      op
.mIsVirtual 
? "virtual " : "", 
 639     m_file
.WriteTeX(totalText
); 
 642 void HelpGenVisitor::VisitParameter( spParameter
& param 
) 
 648     if ( m_isFirstParam 
) { 
 649         m_isFirstParam 
= false; 
 655     totalText 
<< "\\param{" << param
.mType 
<< " }{" << param
.GetName(); 
 656     wxString defvalue 
= param
.mInitVal
; 
 657     if ( !defvalue
.IsEmpty() ) { 
 658         totalText 
<< " = " << defvalue
; 
 663     m_file
.WriteTeX(totalText
); 
 666 // ----------------------------------------------------------------------------- 
 667 // global function implementation 
 668 // ----------------------------------------------------------------------------- 
 670 static wxString 
MakeLabel(const char *classname
, const char *funcname
) 
 672     wxString 
label(classname
); 
 673     if ( funcname 
&& funcname
[0] == '\\' ) { 
 674         // we may have some special TeX macro - so far only \destruct exists, 
 675         // but may be later others will be added 
 676         static const char *macros
[] = { "destruct" }; 
 677         static const char *replacement
[] = { "dtor" }; 
 680         for ( n 
= 0; n 
< WXSIZEOF(macros
); n
++ ) { 
 681             if ( strncmp(funcname 
+ 1, macros
[n
], strlen(macros
[n
])) == 0 ) { 
 687         if ( n 
== WXSIZEOF(macros
) ) { 
 688             wxLogWarning("unknown function name '%s' - leaving as is.", 
 692             funcname 
= replacement
[n
]; 
 704 static wxString 
MakeHelpref(const char *argument
) 
 707     helpref 
<< "\\helpref{" << argument 
<< "}{" << MakeLabel(argument
) << '}'; 
 712 static void TeXFilter(wxString
* str
) 
 714     // FIXME may be done much more quickly 
 715     str
->Replace("&", "\\&"); 
 716     str
->Replace("_", "\\_"); 
 719 static wxString 
GetAllComments(const spContext
& ctx
) 
 722     const MCommentListT
& commentsList 
= ctx
.GetCommentList(); 
 723     for ( MCommentListT::const_iterator i 
= commentsList
.begin(); 
 724           i 
!= commentsList
.end(); 
 726         wxString comment 
= (*i
)->GetText(); 
 728         // don't take comments like "// ----------" &c 
 731               comment 
== wxString(comment
[0u], comment
.length() - 1) + '\n' ) 
 740 static const char *GetCurrentTime(const char *timeFormat
) 
 742     static char s_timeBuffer
[128]; 
 747     ptmNow 
= localtime(&timeNow
); 
 749     strftime(s_timeBuffer
, WXSIZEOF(s_timeBuffer
), timeFormat
, ptmNow
); 
 754 /* vi: set tw=80 et ts=4 sw=4: */