]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/HelpGen.cpp
More SC++ fixes; HelpGen starting to compile with VC++; image sample now compiles...
[wxWidgets.git] / utils / HelpGen / src / HelpGen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: HelpGen.cpp
3 // Purpose: Main program file for HelpGen
4 // Author: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
5 // Modified by:
6 // Created: 06/01/99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 VZ
9 // Licence: GPL
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 TODO (+ means fixed)
14
15 (i) small fixes in the current version
16
17 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
18 2. Document typedefs
19 3. Document global variables
20 4. Document #defines
21 +5. Program options
22
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
26
27 */
28
29 // =============================================================================
30 // declarations
31 // =============================================================================
32
33 // -----------------------------------------------------------------------------
34 // headers
35 // -----------------------------------------------------------------------------
36
37 // wxWindows
38 #include "wx/wxprec.h"
39
40 #ifndef WX_PRECOMP
41 #include <wx/string.h>
42 #include <wx/log.h>
43 #include <wx/dynarray.h>
44 #endif // WX_PRECOMP
45
46 #include <wx/file.h>
47
48 // C++ parsing classes
49 #include "cjparser.h"
50
51 // standard headers
52 #include <stdio.h>
53 #include <time.h>
54
55 // -----------------------------------------------------------------------------
56 // private functions
57 // -----------------------------------------------------------------------------
58
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);
61
62 // return the whole \helpref{arg}{arg_label} string
63 static wxString MakeHelpref(const char *argument);
64
65 // quotes special TeX characters in place
66 static void TeXFilter(wxString* str);
67
68 // get all comments associated with this context
69 static wxString GetAllComments(const spContext& ctx);
70
71 // get the string with current time (returns pointer to static buffer)
72 // timeFormat is used for the call of strftime(3)
73 #ifdef GetCurrentTime
74 #undef GetCurrentTime
75 #endif
76
77 static const char *GetCurrentTime(const char *timeFormat);
78
79 // -----------------------------------------------------------------------------
80 // private classes
81 // -----------------------------------------------------------------------------
82
83 // add a function which sanitazes the string before writing it to the file
84 class wxTeXFile : public wxFile
85 {
86 public:
87 wxTeXFile() : wxFile() { }
88
89 bool WriteTeX(const wxString& s)
90 {
91 wxString t(s);
92 TeXFilter(&t);
93
94 return wxFile::Write(t);
95 }
96 };
97
98 class HelpGenVisitor : public spVisitor
99 {
100 public:
101 // ctor
102 HelpGenVisitor();
103
104 virtual void VisitFile( spFile& fl );
105 virtual void VisitClass( spClass& cl );
106 virtual void VisitEnumeration( spEnumeration& en );
107 virtual void VisitTypeDef( spTypeDef& td );
108 virtual void VisitPreprocessorLine( spPreprocessorLine& pd );
109 virtual void VisitAttribute( spAttribute& attr );
110 virtual void VisitOperation( spOperation& op );
111 virtual void VisitParameter( spParameter& param );
112
113 void EndVisit();
114
115 // shut up g++ warning (ain't it stupid?)
116 virtual ~HelpGenVisitor() { }
117
118 protected:
119 // (re)initialize the state
120 void Reset();
121
122 // insert documentation for enums/typedefs coming immediately before the
123 // class declaration into the class documentation
124 void InsertTypedefDocs();
125 void InsertEnumDocs();
126
127 // write the headers for corresponding sections (only once)
128 void InsertDataStructuresHeader();
129 void InsertMethodsHeader();
130
131 // terminate the function documentation if it was started
132 void CloseFunction();
133
134 wxTeXFile m_file; // file we're writing to now
135
136 // state variables
137 bool m_inClass, // TRUE after file successfully opened
138 m_inTypesSection, // enums & typedefs go there
139 m_inMethodSection, // functions go here
140 m_isFirstParam, // first parameter of current function?
141 m_inFunction; // we're parsing a function declaration
142
143 // holders for "saved" documentation
144 wxString m_textStoredEnums,
145 m_textStoredTypedefs,
146 m_textStoredFunctionComment;
147
148 // headers included by this file
149 wxArrayString m_headers;
150 };
151
152 // -----------------------------------------------------------------------------
153 // private functions
154 // -----------------------------------------------------------------------------
155
156 // =============================================================================
157 // implementation
158 // =============================================================================
159
160 // this function never returns
161 static void usage()
162 {
163 wxLogError("usage: HelpGen [-q|-v] <header files...>\n");
164
165 exit(1);
166 }
167
168 int main(int argc, char **argv)
169 {
170 if ( argc < 2 ) {
171 usage();
172 }
173
174 int first;
175 for ( first = 1; (first < argc) && argv[first][0] == '-'; first++ ) {
176 switch ( argv[first][1] ) {
177 case 'v':
178 // be verbose
179 wxLog::GetActiveTarget()->SetVerbose();
180 break;
181
182 case 'q':
183 // be quiet
184 wxLog::GetActiveTarget()->SetVerbose(false);
185 break;
186
187 default:
188 usage();
189 }
190 }
191
192 // create a parser object and a visitor derivation
193 CJSourceParser parser;
194 HelpGenVisitor visitor;
195
196 // parse all files
197 for ( int i = first; i < argc; i++ ) {
198 spContext *ctxTop = parser.ParseFile(argv[i]);
199 if ( !ctxTop ) {
200 wxLogWarning("File '%s' couldn't be processed.", argv[i]);
201 }
202 else {
203 ((spFile *)ctxTop)->mFileName = argv[i];
204 visitor.VisitAll(*ctxTop);
205 visitor.EndVisit();
206 }
207 }
208
209 return 0;
210 }
211
212 // -----------------------------------------------------------------------------
213 // HelpGenVisitor implementation
214 // -----------------------------------------------------------------------------
215
216 HelpGenVisitor::HelpGenVisitor()
217 {
218 Reset();
219 }
220
221 void HelpGenVisitor::Reset()
222 {
223 m_inClass =
224 m_inFunction =
225 m_inTypesSection =
226 m_inMethodSection = false;
227
228 m_textStoredTypedefs =
229 m_textStoredEnums =
230 m_textStoredFunctionComment = "";
231 m_headers.Empty();
232 }
233
234 void HelpGenVisitor::InsertTypedefDocs()
235 {
236 m_file.WriteTeX(m_textStoredTypedefs);
237 m_textStoredTypedefs.Empty();
238 }
239
240 void HelpGenVisitor::InsertEnumDocs()
241 {
242 m_file.WriteTeX(m_textStoredEnums);
243 m_textStoredEnums.Empty();
244 }
245
246 void HelpGenVisitor::InsertDataStructuresHeader()
247 {
248 if ( !m_inTypesSection ) {
249 m_inTypesSection = true;
250
251 m_file.WriteTeX("\\wxheading{Data structures}\n\n");
252 }
253 }
254
255 void HelpGenVisitor::InsertMethodsHeader()
256 {
257 if ( !m_inMethodSection ) {
258 m_inMethodSection = true;
259
260 m_file.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
261 }
262 }
263
264 void HelpGenVisitor::CloseFunction()
265 {
266 if ( m_inFunction ) {
267 m_inFunction = false;
268
269 wxString totalText;
270 if ( m_isFirstParam ) {
271 // no params found
272 totalText << "\\void";
273 }
274
275 totalText << "}\n\n";
276
277 if ( !m_textStoredFunctionComment.IsEmpty() )
278 totalText << m_textStoredFunctionComment << '\n';
279
280 m_file.WriteTeX(totalText);
281 }
282 }
283
284 void HelpGenVisitor::EndVisit()
285 {
286 CloseFunction();
287
288 wxLogInfo("%s: finished parsing the current file.",
289 GetCurrentTime("%H:%M:%S"));
290 }
291
292 void HelpGenVisitor::VisitFile( spFile& file )
293 {
294 wxLogInfo("%s: started to parse classes from file '%s'...",
295 GetCurrentTime("%H:%M:%S"), file.mFileName.c_str());
296 }
297
298 void HelpGenVisitor::VisitClass( spClass& cl )
299 {
300 wxString name = cl.GetName();
301
302 // the file name is built from the class name by removing the leading "wx"
303 // if any and converting it to the lower case
304 wxString filename = name;
305 if ( filename(0, 2) == "wx" ) {
306 filename.erase(0, 2);
307 }
308
309 filename.MakeLower();
310 filename += ".tex";
311
312 m_inClass = m_file.Open(filename, wxFile::write);
313 if ( !m_inClass ) {
314 wxLogError("Can't generate documentation for the class '%s'.",
315 name.c_str());
316
317 return;
318 }
319
320 m_inMethodSection =
321 m_inTypesSection = false;
322
323 wxLogInfo("Created new file '%s' for class '%s'.",
324 filename.c_str(), name.c_str());
325
326 // the entire text we're writing to file
327 wxString totalText;
328
329 // write out the header
330 {
331 wxString header;
332 header.Printf("% automatically generated by HelpGen from %s at %s\n"
333 "\\section{\\class{%s}}\\label{%s}\n",
334 filename.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
335 name.c_str(), wxString(name).MakeLower().c_str());
336
337 totalText << header << '\n';
338 }
339
340 // if the header includes other headers they must be related to it... try to
341 // automatically generate the "See also" clause
342 if ( !m_headers.IsEmpty() ) {
343 // correspondence between wxWindows headers and class names
344 static const char *headers[] = {
345 "object",
346 "defs",
347 "string",
348 "dynarray",
349 "file",
350 "time",
351 };
352
353 // NULL here means not to insert anything in "See also" for the
354 // corresponding header
355 static const char *classes[] = {
356 NULL,
357 NULL,
358 NULL,
359 NULL,
360 "wxFile",
361 "wxTime",
362 };
363
364 wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes),
365 "arrays must be in sync!" );
366
367 wxArrayInt interestingClasses;
368
369 size_t count = m_headers.Count(), index;
370 for ( size_t n = 0; n < count; n++ ) {
371 wxString baseHeaderName = m_headers[n].Before('.');
372 if ( baseHeaderName(0, 3) != "wx/" )
373 continue;
374
375 baseHeaderName.erase(0, 3);
376 for ( index = 0; index < WXSIZEOF(headers); index++ ) {
377 if ( Stricmp(baseHeaderName, headers[index]) == 0 )
378 break;
379 }
380
381 if ( (index < WXSIZEOF(headers)) && classes[index] ) {
382 // interesting header
383 interestingClasses.Add(index);
384 }
385 }
386
387 if ( !interestingClasses.IsEmpty() ) {
388 // do generate "See also" clause
389 totalText << "\\wxheading{See also:}\n\n";
390
391 count = interestingClasses.Count();
392 for ( index = 0; index < count; index++ ) {
393 if ( index > 0 )
394 totalText << ", ";
395
396 totalText << MakeHelpref(classes[interestingClasses[index]]);
397 }
398
399 totalText << "\n\n";
400 }
401 }
402
403 // the comment before the class generally explains what is it for so put it
404 // in place of the class description
405 if ( cl.HasComments() ) {
406 wxString comment = GetAllComments(cl);
407
408 totalText << '\n' << comment << '\n';
409 }
410
411 // derived from section
412 wxString derived = "\\wxheading{Derived from}\n\n";
413
414 const StrListT& baseClasses = cl.mSuperClassNames;
415 if ( baseClasses.size() == 0 ) {
416 derived << "No base class";
417 }
418 else {
419 bool first = true;
420 for ( StrListT::const_iterator i = baseClasses.begin();
421 i != baseClasses.end();
422 i++ ) {
423 if ( !first ) {
424 // separate from the previous one
425 derived << "\\\\\n";
426 }
427 else {
428 first = false;
429 }
430
431 wxString baseclass = *i;
432 derived << "\\helpref{" << baseclass << "}"
433 "{ " << baseclass.MakeLower() << "}";
434 }
435 }
436 totalText << derived << "\n\n";
437
438 // write all this to file
439 m_file.WriteTeX(totalText);
440
441 // if there were any enums/typedefs before, insert their documentation now
442 InsertDataStructuresHeader();
443 InsertTypedefDocs();
444 InsertEnumDocs();
445 }
446
447 void HelpGenVisitor::VisitEnumeration( spEnumeration& en )
448 {
449 CloseFunction();
450
451 if ( m_inMethodSection ) {
452 // FIXME that's a bug, but tell the user aboit it nevertheless... we
453 // should be smart enough to process even the enums which come after the
454 // functions
455 wxLogWarning("enum '%s' ignored, please put it before the class "
456 "methods.", en.GetName().c_str());
457 return;
458 }
459
460 // simply copy the enum text in the docs
461 wxString enumeration = GetAllComments(en);
462 enumeration << "{\\small \\begin{verbatim}\n"
463 << en.mEnumContent
464 << "\n\\end{verbatim}}\n";
465
466 // remember for later use if we're not inside a class yet
467 if ( !m_inClass ) {
468 if ( !m_textStoredEnums.IsEmpty() ) {
469 m_textStoredEnums << '\n';
470 }
471
472 m_textStoredEnums << enumeration;
473 }
474 else {
475 // write the header for this section if not done yet
476 InsertDataStructuresHeader();
477
478 enumeration << '\n';
479 m_file.WriteTeX(enumeration);
480 }
481 }
482
483 void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
484 {
485 CloseFunction();
486
487 if ( m_inMethodSection ) {
488 // FIXME that's a bug, but tell the user aboit it nevertheless...
489 wxLogWarning("typedef '%s' ignored, please put it before the class "
490 "methods.", td.GetName().c_str());
491 return;
492 }
493
494 wxString typedefdoc;
495 typedefdoc << "{\\small \\begin{verbatim}\n"
496 << "typedef " << td.mOriginalType << ' ' << td.GetName()
497 << "\n\\end{verbatim}}\n"
498 << GetAllComments(td);
499
500 // remember for later use if we're not inside a class yet
501 if ( !m_inClass ) {
502 if ( !m_textStoredTypedefs.IsEmpty() ) {
503 m_textStoredTypedefs << '\n';
504 }
505
506 m_textStoredTypedefs << typedefdoc;
507 }
508 else {
509 // write the header for this section if not done yet
510 InsertDataStructuresHeader();
511
512 typedefdoc << '\n';
513 m_file.WriteTeX(typedefdoc);
514 }
515 }
516
517 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd )
518 {
519 switch ( pd.GetStatementType() ) {
520 case SP_PREP_DEF_INCLUDE_FILE:
521 m_headers.Add(pd.CPP_GetIncludedFileNeme());
522 break;
523
524 case SP_PREP_DEF_DEFINE_SYMBOL:
525 // TODO decide if it's a constant and document it if it is
526 break;
527 }
528 }
529
530 void HelpGenVisitor::VisitAttribute( spAttribute& attr )
531 {
532 CloseFunction();
533
534 // only document the public member variables
535 if ( !m_inClass || !attr.IsPublic() )
536 return;
537
538 wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
539 }
540
541 void HelpGenVisitor::VisitOperation( spOperation& op )
542 {
543 CloseFunction();
544
545 if ( !m_inClass || !op.IsInClass() ) {
546 // FIXME that's a bug too
547 wxLogWarning("skipped global function '%s'.", op.GetName().c_str());
548
549 return;
550 }
551
552 if ( op.mVisibility == SP_VIS_PRIVATE ) {
553 // FIXME should we document protected functions?
554 return;
555 }
556
557 InsertMethodsHeader();
558
559 // save state info
560 m_inFunction =
561 m_isFirstParam = true;
562
563 m_textStoredFunctionComment = GetAllComments(op);
564
565 // start function documentation
566 wxString totalText;
567 const char *funcname = op.GetName().c_str();
568 const char *classname = op.GetClass().GetName().c_str();
569
570 // check for the special case of dtor
571 wxString dtor;
572 if ( (funcname[0] == '~') && (strcmp(funcname + 1, classname) == 0) ) {
573 dtor.Printf("\\destruct{%s}", classname);
574 funcname = dtor;
575 }
576
577 totalText.Printf("\\membersection{%s::%s}\\label{%s}\n"
578 "\\%sfunc{%s%s}{%s}{",
579 classname, funcname,
580 MakeLabel(classname, funcname).c_str(),
581 op.mIsConstant ? "const" : "",
582 op.mIsVirtual ? "virtual " : "",
583 op.mRetType.c_str(),
584 funcname);
585
586 m_file.WriteTeX(totalText);
587 }
588
589 void HelpGenVisitor::VisitParameter( spParameter& param )
590 {
591 if ( !m_inFunction )
592 return;
593
594 wxString totalText;
595 if ( m_isFirstParam ) {
596 m_isFirstParam = false;
597 }
598 else {
599 totalText << ", ";
600 }
601
602 totalText << "\\param{" << param.mType << " }{" << param.GetName();
603 wxString defvalue = param.mInitVal;
604 if ( !defvalue.IsEmpty() ) {
605 totalText << " = " << defvalue;
606 }
607
608 totalText << '}';
609
610 m_file.WriteTeX(totalText);
611 }
612
613 // -----------------------------------------------------------------------------
614 // global function implementation
615 // -----------------------------------------------------------------------------
616
617 static wxString MakeLabel(const char *classname, const char *funcname)
618 {
619 wxString label(classname);
620 if ( funcname && funcname[0] == '\\' ) {
621 // we may have some special TeX macro - so far only \destruct exists,
622 // but may be later others will be added
623 static const char *macros[] = { "destruct" };
624 static const char *replacement[] = { "dtor" };
625
626 size_t n;
627 for ( n = 0; n < WXSIZEOF(macros); n++ ) {
628 if ( strncmp(funcname + 1, macros[n], strlen(macros[n])) == 0 ) {
629 // found
630 break;
631 }
632 }
633
634 if ( n == WXSIZEOF(macros) ) {
635 wxLogWarning("unknown function name '%s' - leaving as is.",
636 funcname);
637 }
638 else {
639 funcname = replacement[n];
640 }
641 }
642
643 if ( funcname )
644 label << funcname;
645
646 label.MakeLower();
647
648 return label;
649 }
650
651 static wxString MakeHelpref(const char *argument)
652 {
653 wxString helpref;
654 helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
655
656 return helpref;
657 }
658
659 static void TeXFilter(wxString* str)
660 {
661 // FIXME may be done much more quickly
662 str->Replace("&", "\\&");
663 str->Replace("_", "\\_");
664 }
665
666 static wxString GetAllComments(const spContext& ctx)
667 {
668 wxString comment;
669 const MCommentListT& comments = ctx.GetCommentList();
670 for ( MCommentListT::const_iterator i = comments.begin();
671 i != comments.end();
672 i++ ) {
673 comment << (*i)->GetText();
674 }
675
676 return comment;
677 }
678
679 static const char *GetCurrentTime(const char *timeFormat)
680 {
681 static char s_timeBuffer[128];
682 time_t timeNow;
683 struct tm *ptmNow;
684
685 time(&timeNow);
686 ptmNow = localtime(&timeNow);
687
688 strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow);
689
690 return s_timeBuffer;
691 }
692
693 /* vi: set tw=80 et ts=4 sw=4: */