25d4b0cc60a86f75aeee3ab56f1808ffc91f4cd7
[platform/upstream/doxygen.git] / src / docparser.cpp
1 /******************************************************************************
2  *
3  * 
4  *
5  *
6  * Copyright (C) 1997-2015 by Dimitri van Heesch.
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation under the terms of the GNU General Public License is hereby 
10  * granted. No representations are made about the suitability of this software 
11  * for any purpose. It is provided "as is" without express or implied warranty.
12  * See the GNU General Public License for more details.
13  *
14  * Documents produced by Doxygen are derivative works derived from the
15  * input used in their production; they are not affected by this license.
16  *
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21
22 #include <qfile.h>
23 #include <qfileinfo.h>
24 #include <qcstring.h>
25 #include <qstack.h>
26 #include <qdict.h>
27 #include <qregexp.h>
28 #include <ctype.h>
29
30 #include "doxygen.h"
31 #include "debug.h"
32 #include "util.h"
33 #include "pagedef.h"
34 #include "docparser.h"
35 #include "doctokenizer.h"
36 #include "cmdmapper.h"
37 #include "printdocvisitor.h"
38 #include "message.h"
39 #include "section.h"
40 #include "searchindex.h"
41 #include "language.h"
42 #include "portable.h"
43 #include "cite.h"
44 #include "arguments.h"
45 #include "vhdldocgen.h"
46 #include "groupdef.h"
47 #include "classlist.h"
48 #include "filedef.h"
49 #include "memberdef.h"
50 #include "namespacedef.h"
51 #include "reflist.h"
52 #include "formula.h"
53 #include "config.h"
54 #include "growbuf.h"
55 #include "markdown.h"
56 #include "htmlentity.h"
57
58 // debug off
59 #define DBG(x) do {} while(0)
60
61 // debug to stdout
62 //#define DBG(x) printf x
63
64 // debug to stderr
65 //#define myprintf(x...) fprintf(stderr,x)
66 //#define DBG(x) myprintf x
67
68 #define INTERNAL_ASSERT(x) do {} while(0)
69 //#define INTERNAL_ASSERT(x) if (!(x)) DBG(("INTERNAL_ASSERT(%s) failed retval=0x%x: file=%s line=%d\n",#x,retval,__FILE__,__LINE__)); 
70
71 //---------------------------------------------------------------------------
72
73 static const char *sectionLevelToName[] = 
74 {
75   "page",
76   "section",
77   "subsection",
78   "subsubsection",
79   "paragraph",
80   "subparagraph"
81 };
82
83 //---------------------------------------------------------------------------
84
85 // Parser state: global variables during a call to validatingParseDoc
86 static Definition *           g_scope;
87 static QCString               g_context;
88 static bool                   g_inSeeBlock;
89 static bool                   g_xmlComment;
90 static bool                   g_insideHtmlLink;
91 static QStack<DocNode>        g_nodeStack;
92 static QStack<DocStyleChange> g_styleStack;
93 static QStack<DocStyleChange> g_initialStyleStack;
94 static QList<Definition>      g_copyStack;
95 static QCString               g_fileName;
96 static QCString               g_relPath;
97
98 static bool                   g_hasParamCommand;
99 static bool                   g_hasReturnCommand;
100 static QDict<void>            g_paramsFound;
101 static MemberDef *            g_memberDef;
102 static bool                   g_isExample;
103 static QCString               g_exampleName;
104 static SectionDict *          g_sectionDict;
105 static QCString               g_searchUrl;
106
107 static QCString               g_includeFileName;
108 static QCString               g_includeFileText;
109 static uint                   g_includeFileOffset;
110 static uint                   g_includeFileLength;
111
112
113 /** Parser's context to store all global variables. 
114  */
115 struct DocParserContext
116 {
117   Definition *scope;
118   QCString context;
119   bool inSeeBlock;
120   bool xmlComment;
121   bool insideHtmlLink;
122   QStack<DocNode> nodeStack;
123   QStack<DocStyleChange> styleStack;
124   QStack<DocStyleChange> initialStyleStack;
125   QList<Definition> copyStack;
126   QCString fileName;
127   int lineNo;
128   QCString relPath;
129
130   bool         hasParamCommand;
131   bool         hasReturnCommand;
132   MemberDef *  memberDef;
133   QDict<void>  paramsFound;
134   bool         isExample;
135   QCString     exampleName;
136   SectionDict *sectionDict;
137   QCString     searchUrl;
138
139   QCString  includeFileText;
140   uint     includeFileOffset;
141   uint     includeFileLength;
142
143   TokenInfo *token;
144 };
145
146 static QStack<DocParserContext> g_parserStack;
147
148 //---------------------------------------------------------------------------
149 static void docParserPushContext(bool saveParamInfo=TRUE)
150 {
151   //QCString indent;
152   //indent.fill(' ',g_parserStack.count()*2+2);
153   //printf("%sdocParserPushContext() count=%d\n",indent.data(),g_nodeStack.count());
154
155   doctokenizerYYpushContext();
156   DocParserContext *ctx   = new DocParserContext;
157   ctx->scope              = g_scope;
158   ctx->context            = g_context;
159   ctx->inSeeBlock         = g_inSeeBlock;
160   ctx->xmlComment         = g_xmlComment;
161   ctx->insideHtmlLink     = g_insideHtmlLink;
162   ctx->nodeStack          = g_nodeStack;
163   ctx->styleStack         = g_styleStack;
164   ctx->initialStyleStack  = g_initialStyleStack;
165   ctx->copyStack          = g_copyStack;
166   ctx->fileName           = g_fileName;
167   ctx->lineNo             = doctokenizerYYlineno;
168   ctx->relPath            = g_relPath;
169
170   if (saveParamInfo)
171   {
172     ctx->hasParamCommand    = g_hasParamCommand;
173     ctx->hasReturnCommand   = g_hasReturnCommand;
174     ctx->paramsFound        = g_paramsFound;
175   }
176
177   ctx->memberDef          = g_memberDef;
178   ctx->isExample          = g_isExample;
179   ctx->exampleName        = g_exampleName;
180   ctx->sectionDict        = g_sectionDict;
181   ctx->searchUrl          = g_searchUrl;
182
183   ctx->includeFileText    = g_includeFileText;
184   ctx->includeFileOffset  = g_includeFileOffset;
185   ctx->includeFileLength  = g_includeFileLength;
186   
187   ctx->token              = g_token;
188   g_token = new TokenInfo;
189
190   g_parserStack.push(ctx);
191 }
192
193 static void docParserPopContext(bool keepParamInfo=FALSE)
194 {
195   DocParserContext *ctx = g_parserStack.pop();
196   g_scope               = ctx->scope;
197   g_context             = ctx->context;
198   g_inSeeBlock          = ctx->inSeeBlock;
199   g_xmlComment          = ctx->xmlComment;
200   g_insideHtmlLink      = ctx->insideHtmlLink;
201   g_nodeStack           = ctx->nodeStack;
202   g_styleStack          = ctx->styleStack;
203   g_initialStyleStack   = ctx->initialStyleStack;
204   g_copyStack           = ctx->copyStack;
205   g_fileName            = ctx->fileName;
206   doctokenizerYYlineno  = ctx->lineNo;
207   g_relPath             = ctx->relPath;
208
209   if (!keepParamInfo)
210   {
211     g_hasParamCommand     = ctx->hasParamCommand;
212     g_hasReturnCommand    = ctx->hasReturnCommand;
213     g_paramsFound         = ctx->paramsFound;
214   }
215   g_memberDef           = ctx->memberDef;
216   g_isExample           = ctx->isExample;
217   g_exampleName         = ctx->exampleName;
218   g_sectionDict         = ctx->sectionDict;
219   g_searchUrl           = ctx->searchUrl;
220
221   g_includeFileText     = ctx->includeFileText;
222   g_includeFileOffset   = ctx->includeFileOffset;
223   g_includeFileLength   = ctx->includeFileLength;
224
225   delete g_token;
226   g_token               = ctx->token;
227
228   delete ctx;
229   doctokenizerYYpopContext();
230
231   //QCString indent;
232   //indent.fill(' ',g_parserStack.count()*2+2);
233   //printf("%sdocParserPopContext() count=%d\n",indent.data(),g_nodeStack.count());
234 }
235
236 //---------------------------------------------------------------------------
237
238 // replaces { with < and } with > and also
239 // replaces &gt; with < and &gt; with > within string s
240 static void unescapeCRef(QCString &s)
241 {
242   QCString tmp(s);
243   char *p = tmp.rawData();
244   if (p)
245   {
246     char c;
247     while ((c=*p))
248     {
249       if (c=='{') c='<'; else if (c=='}') c='>';
250       *p++=c;
251     }
252   }
253
254   tmp=substitute(tmp,"&lt;","<");
255   tmp=substitute(tmp,"&gt;",">");
256   s = tmp;
257 }
258
259 //---------------------------------------------------------------------------
260
261 /*! search for an image in the imageNameDict and if found
262  * copies the image to the output directory (which depends on the \a type
263  * parameter).
264  */
265 static QCString findAndCopyImage(const char *fileName,DocImage::Type type)
266 {
267   QCString result;
268   bool ambig;
269   FileDef *fd;
270   //printf("Search for %s\n",fileName);
271   if ((fd=findFileDef(Doxygen::imageNameDict,fileName,ambig)))
272   {
273     QCString inputFile = fd->absFilePath();
274     QFile inImage(inputFile);
275     if (inImage.open(IO_ReadOnly))
276     {
277       result = fileName;
278       int i;
279       if ((i=result.findRev('/'))!=-1 || (i=result.findRev('\\'))!=-1)
280       {
281         result = result.right(result.length()-i-1);
282       }
283       //printf("fileName=%s result=%s\n",fileName,result.data());
284       QCString outputDir;
285       switch(type)
286       {
287         case DocImage::Html:
288           if (!Config_getBool(GENERATE_HTML)) return result;
289           outputDir = Config_getString(HTML_OUTPUT);
290           break;
291         case DocImage::Latex:
292           if (!Config_getBool(GENERATE_LATEX)) return result;
293           outputDir = Config_getString(LATEX_OUTPUT);
294           break;
295         case DocImage::DocBook:
296           if (!Config_getBool(GENERATE_DOCBOOK)) return result;
297           outputDir = Config_getString(DOCBOOK_OUTPUT);
298           break;
299         case DocImage::Rtf:
300           if (!Config_getBool(GENERATE_RTF)) return result;
301           outputDir = Config_getString(RTF_OUTPUT);
302           break;
303       }
304       QCString outputFile = outputDir+"/"+result;
305       QFileInfo outfi(outputFile);
306       if (outfi.isSymLink())
307       {
308         QFile::remove(outputFile);
309         warn_doc_error(g_fileName,doctokenizerYYlineno,
310             "destination of image %s is a symlink, replacing with image",
311             qPrint(outputFile));
312       }
313       if (outputFile!=inputFile) // prevent copying to ourself
314       {
315         QFile outImage(outputFile.data());
316         if (outImage.open(IO_WriteOnly)) // copy the image
317         {
318           char *buffer = new char[inImage.size()];
319           inImage.readBlock(buffer,inImage.size());
320           outImage.writeBlock(buffer,inImage.size());
321           outImage.flush();
322           delete[] buffer;
323           if (type==DocImage::Html) Doxygen::indexList->addImageFile(result);
324         }
325         else
326         {
327           warn_doc_error(g_fileName,doctokenizerYYlineno,
328               "could not write output image %s",qPrint(outputFile));
329         }
330       }
331       else
332       {
333         printf("Source & Destination are the same!\n");
334       }
335     }
336     else
337     {
338       warn_doc_error(g_fileName,doctokenizerYYlineno,
339           "could not open image %s",qPrint(fileName));
340     }
341
342     if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) && 
343         fd->name().right(4)==".eps"
344        )
345     { // we have an .eps image in pdflatex mode => convert it to a pdf.
346       QCString outputDir = Config_getString(LATEX_OUTPUT);
347       QCString baseName  = fd->name().left(fd->name().length()-4);
348       QCString epstopdfArgs(4096);
349       epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"",
350                            outputDir.data(), baseName.data(),
351                            outputDir.data(), baseName.data());
352       portable_sysTimerStart();
353       if (portable_system("epstopdf",epstopdfArgs)!=0)
354       {
355         err("Problems running epstopdf. Check your TeX installation!\n");
356       }
357       portable_sysTimerStop();
358       return baseName;
359     }
360   }
361   else if (ambig)
362   {
363     QCString text;
364     text.sprintf("image file name %s is ambiguous.\n",qPrint(fileName));
365     text+="Possible candidates:\n";
366     text+=showFileDefMatches(Doxygen::imageNameDict,fileName);
367     warn_doc_error(g_fileName,doctokenizerYYlineno,text);
368   }
369   else
370   {
371     result=fileName;
372     if (result.left(5)!="http:" && result.left(6)!="https:")
373     {
374       warn_doc_error(g_fileName,doctokenizerYYlineno,
375            "image file %s is not found in IMAGE_PATH: "  
376            "assuming external image.",qPrint(fileName)
377           );
378     }
379   }
380   return result;
381 }
382
383 /*! Collects the parameters found with \@param or \@retval commands
384  *  in a global list g_paramsFound. If \a isParam is set to TRUE
385  *  and the parameter is not an actual parameter of the current
386  *  member g_memberDef, then a warning is raised (unless warnings
387  *  are disabled altogether).
388  */
389 static void checkArgumentName(const QCString &name,bool isParam)
390 {                
391   if (!Config_getBool(WARN_IF_DOC_ERROR)) return;
392   if (g_memberDef==0) return; // not a member
393   ArgumentList *al=g_memberDef->isDocsForDefinition() ? 
394                    g_memberDef->argumentList() :
395                    g_memberDef->declArgumentList();
396   SrcLangExt lang = g_memberDef->getLanguage();
397   //printf("isDocsForDefinition()=%d\n",g_memberDef->isDocsForDefinition());
398   if (al==0) return; // no argument list
399
400   static QRegExp re("$?[a-zA-Z0-9_\\x80-\\xFF]+\\.*");
401   int p=0,i=0,l;
402   while ((i=re.match(name,p,&l))!=-1) // to handle @param x,y
403   {
404     QCString aName=name.mid(i,l);
405     if (lang==SrcLangExt_Fortran) aName=aName.lower();
406     //printf("aName=`%s'\n",aName.data());
407     ArgumentListIterator ali(*al);
408     Argument *a;
409     bool found=FALSE;
410     for (ali.toFirst();(a=ali.current());++ali)
411     {
412       QCString argName = g_memberDef->isDefine() ? a->type : a->name;
413       if (lang==SrcLangExt_Fortran) argName=argName.lower();
414       argName=argName.stripWhiteSpace();
415       //printf("argName=`%s' aName=%s\n",argName.data(),aName.data());
416       if (argName.right(3)=="...") argName=argName.left(argName.length()-3);
417       if (aName==argName) 
418       {
419         g_paramsFound.insert(aName,(void *)(0x8));
420         found=TRUE;
421         break;
422       }
423     }
424     if (!found && isParam)
425     {
426       //printf("member type=%d\n",memberDef->memberType());
427       QCString scope=g_memberDef->getScopeString();
428       if (!scope.isEmpty()) scope+="::"; else scope="";
429       QCString inheritedFrom = "";
430       QCString docFile = g_memberDef->docFile();
431       int docLine = g_memberDef->docLine();
432       MemberDef *inheritedMd = g_memberDef->inheritsDocsFrom();
433       if (inheritedMd) // documentation was inherited
434       {
435         inheritedFrom.sprintf(" inherited from member %s at line "
436             "%d in file %s",qPrint(inheritedMd->name()),
437             inheritedMd->docLine(),qPrint(inheritedMd->docFile()));
438         docFile = g_memberDef->getDefFileName();
439         docLine = g_memberDef->getDefLine();
440         
441       }
442       QCString alStr = argListToString(al);
443       warn_doc_error(docFile,docLine,
444           "argument '%s' of command @param "
445           "is not found in the argument list of %s%s%s%s",
446           qPrint(aName), qPrint(scope), qPrint(g_memberDef->name()),
447           qPrint(alStr), qPrint(inheritedFrom));
448     }
449     p=i+l;
450   }
451 }
452
453 /*! Checks if the parameters that have been specified using \@param are
454  *  indeed all parameters.
455  *  Must be called after checkArgumentName() has been called for each
456  *  argument.
457  */
458 static void checkUndocumentedParams()
459 {
460   if (g_memberDef && g_hasParamCommand && Config_getBool(WARN_IF_DOC_ERROR))
461   {
462     ArgumentList *al=g_memberDef->isDocsForDefinition() ? 
463       g_memberDef->argumentList() :
464       g_memberDef->declArgumentList();
465     SrcLangExt lang = g_memberDef->getLanguage();
466     if (al!=0)
467     {
468       ArgumentListIterator ali(*al);
469       Argument *a;
470       bool found=FALSE;
471       for (ali.toFirst();(a=ali.current());++ali)
472       {
473         QCString argName = g_memberDef->isDefine() ? a->type : a->name;
474         if (lang==SrcLangExt_Fortran) argName = argName.lower();
475         argName=argName.stripWhiteSpace();
476         if (argName.right(3)=="...") argName=argName.left(argName.length()-3);
477         if (g_memberDef->getLanguage()==SrcLangExt_Python && (argName=="self" || argName=="cls"))
478         { 
479           // allow undocumented self / cls parameter for Python
480         }
481         else if (!argName.isEmpty() && g_paramsFound.find(argName)==0 && a->docs.isEmpty()) 
482         {
483           found = TRUE;
484           break;
485         }
486       }
487       if (found)
488       {
489         bool first=TRUE;
490         QCString errMsg=
491             "The following parameters of "+
492             QCString(g_memberDef->qualifiedName()) + 
493             QCString(argListToString(al)) +
494             " are not documented:\n";
495         for (ali.toFirst();(a=ali.current());++ali)
496         {
497           QCString argName = g_memberDef->isDefine() ? a->type : a->name;
498           if (lang==SrcLangExt_Fortran) argName = argName.lower();
499           argName=argName.stripWhiteSpace();
500           if (g_memberDef->getLanguage()==SrcLangExt_Python && (argName=="self" || argName=="cls"))
501           { 
502             // allow undocumented self / cls parameter for Python
503           }
504           else if (!argName.isEmpty() && g_paramsFound.find(argName)==0) 
505           {
506             if (!first)
507             {
508               errMsg+="\n";
509             }
510             else
511             {
512               first=FALSE;
513             }
514             errMsg+="  parameter '"+argName+"'";
515           }
516         }
517         warn_doc_error(g_memberDef->getDefFileName(),
518                        g_memberDef->getDefLine(),
519                        substitute(errMsg,"%","%%"));
520       }
521     }
522   }
523 }
524
525 /*! Check if a member has documentation for its parameter and or return
526  *  type, if applicable. If found this will be stored in the member, this
527  *  is needed as a member can have brief and detailed documentation, while
528  *  only one of these needs to document the parameters.
529  */
530 static void detectNoDocumentedParams()
531 {
532   if (g_memberDef && Config_getBool(WARN_NO_PARAMDOC))
533   {
534     ArgumentList *al     = g_memberDef->argumentList();
535     ArgumentList *declAl = g_memberDef->declArgumentList();
536     QCString returnType   = g_memberDef->typeString();
537     bool isPython = g_memberDef->getLanguage()==SrcLangExt_Python;
538
539     if (!g_memberDef->hasDocumentedParams() &&
540         g_hasParamCommand)
541     {
542       //printf("%s->setHasDocumentedParams(TRUE);\n",g_memberDef->name().data());
543       g_memberDef->setHasDocumentedParams(TRUE);
544     }
545     else if (!g_memberDef->hasDocumentedParams())
546     {
547       bool allDoc=TRUE; // no parameter => all parameters are documented
548       if ( // member has parameters
549              al!=0 &&       // but the member has a parameter list
550              al->count()>0  // with at least one parameter (that is not void)
551          )
552       {
553         ArgumentListIterator ali(*al);
554         Argument *a;
555
556         // see if all parameters have documentation
557         for (ali.toFirst();(a=ali.current()) && allDoc;++ali)
558         {
559           if (!a->name.isEmpty() && a->type!="void" &&
560               !(isPython && (a->name=="self" || a->name=="cls"))
561              )
562           {
563             allDoc = !a->docs.isEmpty();
564           }
565           //printf("a->type=%s a->name=%s doc=%s\n",
566           //        a->type.data(),a->name.data(),a->docs.data());
567         }
568         if (!allDoc && declAl!=0) // try declaration arguments as well
569         {
570           allDoc=TRUE;
571           ArgumentListIterator ali(*declAl);
572           Argument *a;
573           for (ali.toFirst();(a=ali.current()) && allDoc;++ali)
574           {
575             if (!a->name.isEmpty() && a->type!="void" &&
576                 !(isPython && (a->name=="self" || a->name=="cls"))
577                )
578             {
579               allDoc = !a->docs.isEmpty();
580             }
581             //printf("a->name=%s doc=%s\n",a->name.data(),a->docs.data());
582           }
583         }
584       }
585       if (allDoc) 
586       {
587         //printf("%s->setHasDocumentedParams(TRUE);\n",g_memberDef->name().data());
588         g_memberDef->setHasDocumentedParams(TRUE);
589       }
590     }
591     //printf("Member %s hadDocumentedReturnType()=%d hasReturnCommand=%d\n",
592     //    g_memberDef->name().data(),g_memberDef->hasDocumentedReturnType(),g_hasReturnCommand);
593     if (!g_memberDef->hasDocumentedReturnType() && // docs not yet found
594         g_hasReturnCommand)
595     {
596       g_memberDef->setHasDocumentedReturnType(TRUE);
597     }
598     else if ( // see if return needs to documented 
599         g_memberDef->hasDocumentedReturnType() ||
600         returnType.isEmpty()         || // empty return type
601         returnType.find("void")!=-1  || // void return type
602         returnType.find("subroutine")!=-1 || // fortran subroutine
603         g_memberDef->isConstructor() || // a constructor
604         g_memberDef->isDestructor()     // or destructor
605        )
606     {
607       g_memberDef->setHasDocumentedReturnType(TRUE);
608     }
609     else if ( // see if return type is documented in a function w/o return type
610         g_memberDef->hasDocumentedReturnType() &&
611         (returnType.isEmpty()              || // empty return type
612          returnType.find("void")!=-1       || // void return type
613          returnType.find("subroutine")!=-1 || // fortran subroutine
614          g_memberDef->isConstructor()      || // a constructor
615          g_memberDef->isDestructor()          // or destructor
616         )
617        )
618     {
619       warn_doc_error(g_fileName,doctokenizerYYlineno,"documented empty return type");
620     }
621   }
622 }
623
624
625 //---------------------------------------------------------------------------
626
627 /*! Strips known html and tex extensions from \a text. */
628 static QCString stripKnownExtensions(const char *text)
629 {
630   QCString result=text;
631   if (result.right(4)==".tex")
632   {
633     result=result.left(result.length()-4);
634   }
635   else if (result.right(Doxygen::htmlFileExtension.length())==
636          QCString(Doxygen::htmlFileExtension)) 
637   {
638     result=result.left(result.length()-Doxygen::htmlFileExtension.length());
639   }
640   return result;
641 }
642
643
644 //---------------------------------------------------------------------------
645
646 /*! Returns TRUE iff node n is a child of a preformatted node */
647 static bool insidePRE(DocNode *n)
648 {
649   while (n)
650   {
651     if (n->isPreformatted()) return TRUE;
652     n=n->parent();
653   }
654   return FALSE;
655 }
656
657 //---------------------------------------------------------------------------
658
659 /*! Returns TRUE iff node n is a child of a html list item node */
660 static bool insideLI(DocNode *n)
661 {
662   while (n)
663   {
664     if (n->kind()==DocNode::Kind_HtmlListItem) return TRUE;
665     n=n->parent();
666   }
667   return FALSE;
668 }
669
670 //---------------------------------------------------------------------------
671
672 /*! Returns TRUE iff node n is a child of a unordered html list node */
673 static bool insideUL(DocNode *n)
674 {
675   while (n)
676   {
677     if (n->kind()==DocNode::Kind_HtmlList && 
678         ((DocHtmlList *)n)->type()==DocHtmlList::Unordered) return TRUE;
679     n=n->parent();
680   }
681   return FALSE;
682 }
683
684 //---------------------------------------------------------------------------
685
686 /*! Returns TRUE iff node n is a child of a ordered html list node */
687 static bool insideOL(DocNode *n)
688 {
689   while (n)
690   {
691     if (n->kind()==DocNode::Kind_HtmlList && 
692         ((DocHtmlList *)n)->type()==DocHtmlList::Ordered) return TRUE;
693     n=n->parent();
694   }
695   return FALSE;
696 }
697
698 //---------------------------------------------------------------------------
699
700 static bool insideTable(DocNode *n)
701 {
702   while (n)
703   {
704     if (n->kind()==DocNode::Kind_HtmlTable) return TRUE;
705     n=n->parent();
706   }
707   return FALSE;
708 }
709
710 //---------------------------------------------------------------------------
711
712 /*! Looks for a documentation block with name commandName in the current
713  *  context (g_context). The resulting documentation string is
714  *  put in pDoc, the definition in which the documentation was found is
715  *  put in pDef.
716  *  @retval TRUE if name was found.
717  *  @retval FALSE if name was not found.
718  */
719 static bool findDocsForMemberOrCompound(const char *commandName,
720                                  QCString *pDoc,
721                                  QCString *pBrief,
722                                  Definition **pDef)
723 {
724   //printf("findDocsForMemberOrCompound(%s)\n",commandName);
725   *pDoc="";
726   *pBrief="";
727   *pDef=0;
728   QCString cmdArg=substitute(commandName,"#","::");
729   int l=cmdArg.length();
730   if (l==0) return FALSE;
731
732   int funcStart=cmdArg.find('(');
733   if (funcStart==-1) 
734   {
735     funcStart=l;
736   }
737   else
738   {
739     // Check for the case of operator() and the like.
740     // beware of scenarios like operator()((foo)bar)
741     int secondParen = cmdArg.find('(', funcStart+1);
742     int leftParen   = cmdArg.find(')', funcStart+1);
743     if (leftParen!=-1 && secondParen!=-1) 
744     {
745       if (leftParen<secondParen) 
746       {
747         funcStart=secondParen;
748       }
749     }
750   }
751
752   QCString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
753   QCString args=cmdArg.right(l-funcStart);
754
755   // try if the link is to a member
756   MemberDef    *md=0;
757   ClassDef     *cd=0;
758   FileDef      *fd=0;
759   NamespaceDef *nd=0;
760   GroupDef     *gd=0;
761   PageDef      *pd=0;
762   bool found = getDefs(
763       g_context.find('.')==-1?g_context.data():"", // `find('.') is a hack to detect files
764       name,
765       args.isEmpty()?0:args.data(),
766       md,cd,fd,nd,gd,FALSE,0,TRUE);
767   //printf("found=%d context=%s name=%s\n",found,g_context.data(),name.data());
768   if (found && md)
769   {
770     *pDoc=md->documentation();
771     *pBrief=md->briefDescription();
772     *pDef=md;
773     return TRUE;
774   }
775
776
777   int scopeOffset=g_context.length();
778   do // for each scope
779   {
780     QCString fullName=cmdArg;
781     if (scopeOffset>0)
782     {
783       fullName.prepend(g_context.left(scopeOffset)+"::");
784     }
785     //printf("Trying fullName=`%s'\n",fullName.data());
786
787     // try class, namespace, group, page, file reference
788     cd = Doxygen::classSDict->find(fullName);
789     if (cd) // class 
790     {
791       *pDoc=cd->documentation();
792       *pBrief=cd->briefDescription();
793       *pDef=cd;
794       return TRUE;
795     }
796     nd = Doxygen::namespaceSDict->find(fullName);
797     if (nd) // namespace
798     {
799       *pDoc=nd->documentation();
800       *pBrief=nd->briefDescription();
801       *pDef=nd;
802       return TRUE;
803     }
804     gd = Doxygen::groupSDict->find(cmdArg);
805     if (gd) // group
806     {
807       *pDoc=gd->documentation();
808       *pBrief=gd->briefDescription();
809       *pDef=gd;
810       return TRUE;
811     }
812     pd = Doxygen::pageSDict->find(cmdArg);
813     if (pd) // page
814     {
815       *pDoc=pd->documentation();
816       *pBrief=pd->briefDescription();
817       *pDef=pd;
818       return TRUE;
819     }
820     bool ambig;
821     fd = findFileDef(Doxygen::inputNameDict,cmdArg,ambig);
822     if (fd && !ambig) // file
823     {
824       *pDoc=fd->documentation();
825       *pBrief=fd->briefDescription();
826       *pDef=fd;
827       return TRUE;
828     }
829
830     if (scopeOffset==0)
831     {
832       scopeOffset=-1;
833     }
834     else
835     {
836       scopeOffset = g_context.findRev("::",scopeOffset-1);
837       if (scopeOffset==-1) scopeOffset=0;
838     }
839   } while (scopeOffset>=0);
840
841   
842   return FALSE;
843 }
844 //---------------------------------------------------------------------------
845
846 // forward declaration
847 static bool defaultHandleToken(DocNode *parent,int tok, 
848                                QList<DocNode> &children,bool
849                                handleWord=TRUE);
850
851 static int handleStyleArgument(DocNode *parent,QList<DocNode> &children,
852                                const QCString &cmdName)
853 {
854   DBG(("handleStyleArgument(%s)\n",qPrint(cmdName)));
855   int tok=doctokenizerYYlex();
856   if (tok!=TK_WHITESPACE)
857   {
858     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
859         qPrint(cmdName));
860     return tok;
861   }
862   while ((tok=doctokenizerYYlex()) && 
863           tok!=TK_WHITESPACE && 
864           tok!=TK_NEWPARA &&
865           tok!=TK_LISTITEM && 
866           tok!=TK_ENDLIST
867         )
868   {
869     static QRegExp specialChar("[.,|()\\[\\]:;\\?]");
870     if (tok==TK_WORD && g_token->name.length()==1 && 
871         g_token->name.find(specialChar)!=-1)
872     {
873       // special character that ends the markup command
874       return tok;
875     }
876     if (!defaultHandleToken(parent,tok,children))
877     {
878       switch (tok)
879       {
880         case TK_COMMAND: 
881           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command \\%s as the argument of a \\%s command",
882                qPrint(g_token->name),qPrint(cmdName));
883           break;
884         case TK_SYMBOL: 
885           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found while handling command %s",
886                qPrint(g_token->name),qPrint(cmdName));
887           break;
888         case TK_HTMLTAG:
889           if (insideLI(parent) && Mappers::htmlTagMapper->map(g_token->name) && g_token->endTag)
890           { // ignore </li> as the end of a style command
891             continue; 
892           }
893           return tok;
894           break;
895         default:
896           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s while handling command %s",
897                tokToString(tok),qPrint(cmdName));
898           break;
899       }
900       break;
901     }
902   }
903   DBG(("handleStyleArgument(%s) end tok=%x\n",qPrint(cmdName),tok));
904   return (tok==TK_NEWPARA || tok==TK_LISTITEM || tok==TK_ENDLIST
905          ) ? tok : RetVal_OK; 
906 }
907
908 /*! Called when a style change starts. For instance a \<b\> command is
909  *  encountered.
910  */
911 static void handleStyleEnter(DocNode *parent,QList<DocNode> &children,
912           DocStyleChange::Style s,const HtmlAttribList *attribs)
913 {
914   DBG(("HandleStyleEnter\n"));
915   DocStyleChange *sc= new DocStyleChange(parent,g_nodeStack.count(),s,TRUE,attribs);
916   children.append(sc);
917   g_styleStack.push(sc);
918 }
919
920 /*! Called when a style change ends. For instance a \</b\> command is
921  *  encountered.
922  */
923 static void handleStyleLeave(DocNode *parent,QList<DocNode> &children,
924          DocStyleChange::Style s,const char *tagName)
925 {
926   DBG(("HandleStyleLeave\n"));
927   if (g_styleStack.isEmpty() ||                           // no style change
928       g_styleStack.top()->style()!=s ||                   // wrong style change
929       g_styleStack.top()->position()!=g_nodeStack.count() // wrong position
930      )
931   {
932     if (g_styleStack.isEmpty())
933     {
934       warn_doc_error(g_fileName,doctokenizerYYlineno,"found </%s> tag without matching <%s>",
935           qPrint(tagName),qPrint(tagName));
936     }
937     else if (g_styleStack.top()->style()!=s)
938     {
939       warn_doc_error(g_fileName,doctokenizerYYlineno,"found </%s> tag while expecting </%s>",
940           qPrint(tagName),qPrint(g_styleStack.top()->styleString()));
941     }
942     else
943     {
944       warn_doc_error(g_fileName,doctokenizerYYlineno,"found </%s> at different nesting level (%d) than expected (%d)",
945           qPrint(tagName),g_nodeStack.count(),g_styleStack.top()->position());
946     }
947   }
948   else // end the section
949   {
950     DocStyleChange *sc= new DocStyleChange(parent,g_nodeStack.count(),s,FALSE);
951     children.append(sc);
952     g_styleStack.pop();
953   }
954 }
955
956 /*! Called at the end of a paragraph to close all open style changes
957  *  (e.g. a <b> without a </b>). The closed styles are pushed onto a stack
958  *  and entered again at the start of a new paragraph.
959  */
960 static void handlePendingStyleCommands(DocNode *parent,QList<DocNode> &children)
961 {
962   if (!g_styleStack.isEmpty())
963   {
964     DocStyleChange *sc = g_styleStack.top();
965     while (sc && sc->position()>=g_nodeStack.count()) 
966     { // there are unclosed style modifiers in the paragraph
967       children.append(new DocStyleChange(parent,g_nodeStack.count(),sc->style(),FALSE));
968       g_initialStyleStack.push(sc);
969       g_styleStack.pop();
970       sc = g_styleStack.top();
971     }
972   }
973 }
974
975 static void handleInitialStyleCommands(DocPara *parent,QList<DocNode> &children)
976 {
977   DocStyleChange *sc;
978   while ((sc=g_initialStyleStack.pop()))
979   {
980     handleStyleEnter(parent,children,sc->style(),&sc->attribs());
981   }
982 }
983
984 static int handleAHref(DocNode *parent,QList<DocNode> &children,const HtmlAttribList &tagHtmlAttribs)
985 {
986   HtmlAttribListIterator li(tagHtmlAttribs);
987   HtmlAttrib *opt;
988   int index=0;
989   int retval = RetVal_OK;
990   for (li.toFirst();(opt=li.current());++li,++index)
991   {
992     if (opt->name=="name") // <a name=label> tag
993     {
994       if (!opt->value.isEmpty())
995       {
996         DocAnchor *anc = new DocAnchor(parent,opt->value,TRUE);
997         children.append(anc);
998         break; // stop looking for other tag attribs
999       }
1000       else
1001       {
1002         warn_doc_error(g_fileName,doctokenizerYYlineno,"found <a> tag with name option but without value!");
1003       }
1004     }
1005     else if (opt->name=="href") // <a href=url>..</a> tag
1006     {
1007       // copy attributes
1008       HtmlAttribList attrList = tagHtmlAttribs;
1009       // and remove the href attribute
1010       bool result = attrList.remove(index);
1011       ASSERT(result);
1012       DocHRef *href = new DocHRef(parent,attrList,opt->value,g_relPath);
1013       children.append(href);
1014       g_insideHtmlLink=TRUE;
1015       retval = href->parse();
1016       g_insideHtmlLink=FALSE;
1017       break;
1018     }
1019     else // unsupported option for tag a
1020     {
1021     }
1022   }
1023   return retval;
1024 }
1025
1026 const char *DocStyleChange::styleString() const
1027 {
1028   switch (m_style)
1029   {
1030     case DocStyleChange::Bold:         return "b"; 
1031     case DocStyleChange::Italic:       return "em"; 
1032     case DocStyleChange::Code:         return "code"; 
1033     case DocStyleChange::Center:       return "center"; 
1034     case DocStyleChange::Small:        return "small"; 
1035     case DocStyleChange::Subscript:    return "subscript"; 
1036     case DocStyleChange::Superscript:  return "superscript"; 
1037     case DocStyleChange::Preformatted: return "pre"; 
1038     case DocStyleChange::Div:          return "div";
1039     case DocStyleChange::Span:         return "span";
1040   }
1041   return "<invalid>";
1042 }
1043
1044 static void handleUnclosedStyleCommands()
1045 {
1046   if (!g_initialStyleStack.isEmpty())
1047   {
1048     DocStyleChange *sc = g_initialStyleStack.top();
1049     g_initialStyleStack.pop();
1050     handleUnclosedStyleCommands();
1051     warn_doc_error(g_fileName,doctokenizerYYlineno,
1052              "end of comment block while expecting "
1053              "command </%s>",qPrint(sc->styleString()));
1054   }
1055 }
1056
1057 static void handleLinkedWord(DocNode *parent,QList<DocNode> &children,bool ignoreAutoLinkFlag=FALSE)
1058 {
1059   QCString name = linkToText(SrcLangExt_Unknown,g_token->name,TRUE);
1060   static bool autolinkSupport = Config_getBool(AUTOLINK_SUPPORT);
1061   if (!autolinkSupport && !ignoreAutoLinkFlag) // no autolinking -> add as normal word
1062   {
1063     children.append(new DocWord(parent,name));
1064     return;
1065   }
1066
1067   // ------- try to turn the word 'name' into a link
1068
1069   Definition *compound=0;
1070   MemberDef  *member=0;
1071   int len = g_token->name.length();
1072   ClassDef *cd=0;
1073   bool ambig;
1074   FileDef *fd = findFileDef(Doxygen::inputNameDict,g_fileName,ambig);
1075   //printf("handleLinkedWord(%s) g_context=%s\n",g_token->name.data(),g_context.data());
1076   if (!g_insideHtmlLink && 
1077       (resolveRef(g_context,g_token->name,g_inSeeBlock,&compound,&member,TRUE,fd,TRUE)
1078        || (!g_context.isEmpty() &&  // also try with global scope
1079            resolveRef("",g_token->name,g_inSeeBlock,&compound,&member,FALSE,0,TRUE))
1080       )
1081      )
1082   {
1083     //printf("resolveRef %s = %p (linkable?=%d)\n",qPrint(g_token->name),member,member ? member->isLinkable() : FALSE);
1084     if (member && member->isLinkable()) // member link
1085     {
1086       if (member->isObjCMethod()) 
1087       {
1088         bool localLink = g_memberDef ? member->getClassDef()==g_memberDef->getClassDef() : FALSE;
1089         name = member->objCMethodName(localLink,g_inSeeBlock);
1090       }
1091       children.append(new 
1092           DocLinkedWord(parent,name,
1093             member->getReference(),
1094             member->getOutputFileBase(),
1095             member->anchor(),
1096             member->briefDescriptionAsTooltip()
1097                        )
1098                      );
1099     }
1100     else if (compound->isLinkable()) // compound link
1101     {
1102       QCString anchor = compound->anchor();
1103       if (compound->definitionType()==Definition::TypeFile)
1104       {
1105         name=g_token->name;
1106       }
1107       else if (compound->definitionType()==Definition::TypeGroup)
1108       {
1109         name=((GroupDef*)compound)->groupTitle();
1110       }
1111       children.append(new 
1112           DocLinkedWord(parent,name,
1113                         compound->getReference(),
1114                         compound->getOutputFileBase(),
1115                         anchor,
1116                         compound->briefDescriptionAsTooltip()
1117                        )
1118                      );
1119     }
1120     else if (compound->definitionType()==Definition::TypeFile &&
1121              ((FileDef*)compound)->generateSourceFile()
1122             ) // undocumented file that has source code we can link to
1123     {
1124       children.append(new 
1125           DocLinkedWord(parent,g_token->name,
1126                          compound->getReference(),
1127                          compound->getSourceFileBase(),
1128                          "",
1129                          compound->briefDescriptionAsTooltip()
1130                        )
1131                      );
1132     }
1133     else // not linkable
1134     {
1135       children.append(new DocWord(parent,name));
1136     }
1137   }
1138   else if (!g_insideHtmlLink && len>1 && g_token->name.at(len-1)==':')
1139   {
1140     // special case, where matching Foo: fails to be an Obj-C reference, 
1141     // but Foo itself might be linkable.
1142     g_token->name=g_token->name.left(len-1);
1143     handleLinkedWord(parent,children,ignoreAutoLinkFlag);
1144     children.append(new DocWord(parent,":"));
1145   }
1146   else if (!g_insideHtmlLink && (cd=getClass(g_token->name+"-p")))
1147   {
1148     // special case 2, where the token name is not a class, but could
1149     // be a Obj-C protocol
1150     children.append(new 
1151         DocLinkedWord(parent,name,
1152           cd->getReference(),
1153           cd->getOutputFileBase(),
1154           cd->anchor(),
1155           cd->briefDescriptionAsTooltip()
1156           ));
1157   }
1158 //  else if (!g_insideHtmlLink && (cd=getClass(g_token->name+"-g")))
1159 //  {
1160 //    // special case 3, where the token name is not a class, but could
1161 //    // be a C# generic
1162 //    children.append(new 
1163 //        DocLinkedWord(parent,name,
1164 //          cd->getReference(),
1165 //          cd->getOutputFileBase(),
1166 //          cd->anchor(),
1167 //          cd->briefDescriptionAsTooltip()
1168 //          ));
1169 //  }
1170   else // normal non-linkable word
1171   {
1172     if (g_token->name.left(1)=="#" || g_token->name.left(2)=="::")
1173     {
1174       warn_doc_error(g_fileName,doctokenizerYYlineno,"explicit link request to '%s' could not be resolved",qPrint(name));
1175       children.append(new DocWord(parent,g_token->name));
1176     }
1177     else
1178     {
1179       children.append(new DocWord(parent,name));
1180     }
1181   }
1182 }
1183
1184 static void handleParameterType(DocNode *parent,QList<DocNode> &children,const QCString &paramTypes)
1185 {
1186   QCString name = g_token->name;
1187   int p=0,i;
1188   while ((i=paramTypes.find('|',p))!=-1)
1189   {
1190     g_token->name = paramTypes.mid(p,i-p);
1191     handleLinkedWord(parent,children);
1192     p=i+1;
1193   }
1194   g_token->name = paramTypes.mid(p);
1195   handleLinkedWord(parent,children);
1196   g_token->name = name;
1197 }
1198
1199 static DocInternalRef *handleInternalRef(DocNode *parent)
1200 {
1201   //printf("CMD_INTERNALREF\n");
1202   int tok=doctokenizerYYlex();
1203   QCString tokenName = g_token->name;
1204   if (tok!=TK_WHITESPACE)
1205   {
1206     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
1207         qPrint(tokenName));
1208     return 0;
1209   }
1210   doctokenizerYYsetStateInternalRef();
1211   tok=doctokenizerYYlex(); // get the reference id
1212   if (tok!=TK_WORD && tok!=TK_LNKWORD)
1213   {
1214     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
1215         tokToString(tok),qPrint(tokenName));
1216     return 0;
1217   }
1218   return new DocInternalRef(parent,g_token->name);
1219 }
1220
1221 static DocAnchor *handleAnchor(DocNode *parent)
1222 {
1223   int tok=doctokenizerYYlex();
1224   if (tok!=TK_WHITESPACE)
1225   {
1226     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
1227         qPrint(g_token->name));
1228     return 0;
1229   }
1230   doctokenizerYYsetStateAnchor();
1231   tok=doctokenizerYYlex();
1232   if (tok==0)
1233   {
1234     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
1235         "argument of command %s",qPrint(g_token->name));
1236     return 0;
1237   }
1238   else if (tok!=TK_WORD && tok!=TK_LNKWORD)
1239   {
1240     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
1241         tokToString(tok),qPrint(g_token->name));
1242     return 0;
1243   }
1244   doctokenizerYYsetStatePara();
1245   return new DocAnchor(parent,g_token->name,FALSE);
1246 }
1247
1248
1249 /* Helper function that deals with the title, width, and height arguments of various commands.
1250  * @param[in] cmd        Command id for which to extract caption and size info.
1251  * @param[in] parent     Parent node, owner of the children list passed as
1252  *                       the third argument.
1253  * @param[in] children   The list of child nodes to which the node representing
1254  *                       the token can be added.
1255  * @param[out] width     the extracted width specifier
1256  * @param[out] height    the extracted height specifier
1257  */
1258 static void defaultHandleTitleAndSize(const int cmd, DocNode *parent, QList<DocNode> &children, QCString &width,QCString &height)
1259 {
1260   g_nodeStack.push(parent);
1261
1262   // parse title
1263   doctokenizerYYsetStateTitle();
1264   int tok;
1265   while ((tok=doctokenizerYYlex()))
1266   {
1267     if (tok==TK_WORD && (g_token->name=="width=" || g_token->name=="height="))
1268     {
1269       // special case: no title, but we do have a size indicator
1270       break;
1271     }
1272     if (!defaultHandleToken(parent,tok,children))
1273     {
1274       switch (tok)
1275       {
1276         case TK_COMMAND:
1277           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\%s",
1278               qPrint(g_token->name), Mappers::cmdMapper->find(cmd).data());
1279           break;
1280         case TK_SYMBOL:
1281           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
1282               qPrint(g_token->name));
1283           break;
1284         default:
1285           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
1286               tokToString(tok));
1287           break;
1288       }
1289     }
1290   }
1291   // parse size attributes
1292   if (tok == 0)
1293   {
1294     tok=doctokenizerYYlex();
1295   }
1296   while (tok==TK_WHITESPACE || tok==TK_WORD) // there are values following the title
1297   {
1298     if(tok == TK_WORD)
1299     {
1300       if (g_token->name=="width=" || g_token->name=="height=")
1301       {
1302         doctokenizerYYsetStateTitleAttrValue();
1303         g_token->name = g_token->name.left(g_token->name.length()-1);
1304       }
1305
1306       if (g_token->name=="width")
1307       {
1308         width = g_token->chars;
1309       }
1310       else if (g_token->name=="height")
1311       {
1312         height = g_token->chars;
1313       }
1314       else
1315       {
1316         warn_doc_error(g_fileName,doctokenizerYYlineno,"Unknown option '%s' after \\%s command, expected 'width' or 'height'",
1317                        qPrint(g_token->name), Mappers::cmdMapper->find(cmd).data());
1318         break;
1319       }
1320     }
1321
1322     tok=doctokenizerYYlex();
1323   }
1324   doctokenizerYYsetStatePara();
1325
1326   handlePendingStyleCommands(parent,children);
1327   DocNode *n=g_nodeStack.pop();
1328   ASSERT(n==parent);
1329 }
1330
1331 /* Helper function that deals with the most common tokens allowed in
1332  * title like sections.
1333  * @param parent     Parent node, owner of the children list passed as
1334  *                   the third argument.
1335  * @param tok        The token to process.
1336  * @param children   The list of child nodes to which the node representing
1337  *                   the token can be added.
1338  * @param handleWord Indicates if word token should be processed
1339  * @retval TRUE      The token was handled.
1340  * @retval FALSE     The token was not handled.
1341  */
1342 static bool defaultHandleToken(DocNode *parent,int tok, QList<DocNode> &children,bool
1343     handleWord)
1344 {
1345   DBG(("token %s at %d",tokToString(tok),doctokenizerYYlineno));
1346   if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || 
1347       tok==TK_COMMAND || tok==TK_HTMLTAG
1348      )
1349   {
1350     DBG((" name=%s",qPrint(g_token->name)));
1351   }
1352   DBG(("\n"));
1353 reparsetoken:
1354   QCString tokenName = g_token->name;
1355   switch (tok)
1356   {
1357     case TK_COMMAND: 
1358       switch (Mappers::cmdMapper->map(tokenName))
1359       {
1360         case CMD_BSLASH:
1361           children.append(new DocSymbol(parent,DocSymbol::Sym_BSlash));
1362           break;
1363         case CMD_AT:
1364           children.append(new DocSymbol(parent,DocSymbol::Sym_At));
1365           break;
1366         case CMD_LESS:
1367           children.append(new DocSymbol(parent,DocSymbol::Sym_Less));
1368           break;
1369         case CMD_GREATER:
1370           children.append(new DocSymbol(parent,DocSymbol::Sym_Greater));
1371           break;
1372         case CMD_AMP:
1373           children.append(new DocSymbol(parent,DocSymbol::Sym_Amp));
1374           break;
1375         case CMD_DOLLAR:
1376           children.append(new DocSymbol(parent,DocSymbol::Sym_Dollar));
1377           break;
1378         case CMD_HASH:
1379           children.append(new DocSymbol(parent,DocSymbol::Sym_Hash));
1380           break;
1381         case CMD_DCOLON:
1382           children.append(new DocSymbol(parent,DocSymbol::Sym_DoubleColon));
1383           break;
1384         case CMD_PERCENT:
1385           children.append(new DocSymbol(parent,DocSymbol::Sym_Percent));
1386           break;
1387         case CMD_NDASH:
1388           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1389           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1390           break;
1391         case CMD_MDASH:
1392           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1393           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1394           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1395           break;
1396         case CMD_QUOTE:
1397           children.append(new DocSymbol(parent,DocSymbol::Sym_Quot));
1398           break;
1399         case CMD_PUNT:
1400           children.append(new DocSymbol(parent,DocSymbol::Sym_Dot));
1401           break;
1402         case CMD_PLUS:
1403           children.append(new DocSymbol(parent,DocSymbol::Sym_Plus));
1404           break;
1405         case CMD_MINUS:
1406           children.append(new DocSymbol(parent,DocSymbol::Sym_Minus));
1407           break;
1408         case CMD_EMPHASIS:
1409           {
1410             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Italic,TRUE));
1411             tok=handleStyleArgument(parent,children,tokenName);
1412             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Italic,FALSE));
1413             if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," "));
1414             if (tok==TK_NEWPARA) goto handlepara;
1415             else if (tok==TK_WORD || tok==TK_HTMLTAG) 
1416             {
1417               DBG(("CMD_EMPHASIS: reparsing command %s\n",qPrint(g_token->name)));
1418               goto reparsetoken;
1419             }
1420           }
1421           break;
1422         case CMD_BOLD:
1423           {
1424             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Bold,TRUE));
1425             tok=handleStyleArgument(parent,children,tokenName);
1426             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Bold,FALSE));
1427             if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," "));
1428             if (tok==TK_NEWPARA) goto handlepara;
1429             else if (tok==TK_WORD || tok==TK_HTMLTAG) 
1430             {
1431               DBG(("CMD_BOLD: reparsing command %s\n",qPrint(g_token->name)));
1432               goto reparsetoken;
1433             }
1434           }
1435           break;
1436         case CMD_CODE:
1437           {
1438             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Code,TRUE));
1439             tok=handleStyleArgument(parent,children,tokenName);
1440             children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Code,FALSE));
1441             if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," "));
1442             if (tok==TK_NEWPARA) goto handlepara;
1443             else if (tok==TK_WORD || tok==TK_HTMLTAG) 
1444             {
1445               DBG(("CMD_CODE: reparsing command %s\n",qPrint(g_token->name)));
1446               goto reparsetoken;
1447             }
1448           }
1449           break;
1450         case CMD_HTMLONLY:
1451           {
1452             doctokenizerYYsetStateHtmlOnly();
1453             tok = doctokenizerYYlex();
1454             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::HtmlOnly,g_isExample,g_exampleName,g_token->name=="block"));
1455             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"htmlonly section ended without end marker");
1456             doctokenizerYYsetStatePara();
1457           }
1458           break;
1459         case CMD_MANONLY:
1460           {
1461             doctokenizerYYsetStateManOnly();
1462             tok = doctokenizerYYlex();
1463             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::ManOnly,g_isExample,g_exampleName));
1464             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"manonly section ended without end marker");
1465             doctokenizerYYsetStatePara();
1466           }
1467           break;
1468         case CMD_RTFONLY:
1469           {
1470             doctokenizerYYsetStateRtfOnly();
1471             tok = doctokenizerYYlex();
1472             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::RtfOnly,g_isExample,g_exampleName));
1473             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"rtfonly section ended without end marker");
1474             doctokenizerYYsetStatePara();
1475           }
1476           break;
1477         case CMD_LATEXONLY:
1478           {
1479             doctokenizerYYsetStateLatexOnly();
1480             tok = doctokenizerYYlex();
1481             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::LatexOnly,g_isExample,g_exampleName));
1482             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"latexonly section ended without end marker",doctokenizerYYlineno);
1483             doctokenizerYYsetStatePara();
1484           }
1485           break;
1486         case CMD_XMLONLY:
1487           {
1488             doctokenizerYYsetStateXmlOnly();
1489             tok = doctokenizerYYlex();
1490             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::XmlOnly,g_isExample,g_exampleName));
1491             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"xmlonly section ended without end marker",doctokenizerYYlineno);
1492             doctokenizerYYsetStatePara();
1493           }
1494           break;
1495         case CMD_DBONLY:
1496           {
1497             doctokenizerYYsetStateDbOnly();
1498             tok = doctokenizerYYlex();
1499             children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::DocbookOnly,g_isExample,g_exampleName));
1500             if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"docbookonly section ended without end marker",doctokenizerYYlineno);
1501             doctokenizerYYsetStatePara();
1502           }
1503           break;
1504         case CMD_FORMULA:
1505           {
1506             DocFormula *form=new DocFormula(parent,g_token->id);
1507             children.append(form);
1508           }
1509           break;
1510         case CMD_ANCHOR:
1511           {
1512             DocAnchor *anchor = handleAnchor(parent);
1513             if (anchor)
1514             {
1515               children.append(anchor);
1516             }
1517           }
1518           break;
1519         case CMD_INTERNALREF:
1520           {
1521             DocInternalRef *ref = handleInternalRef(parent);
1522             if (ref)
1523             {
1524               children.append(ref);
1525               ref->parse();
1526             }
1527             doctokenizerYYsetStatePara();
1528           }
1529           break;
1530         case CMD_SETSCOPE:
1531           {
1532             QCString scope;
1533             doctokenizerYYsetStateSetScope();
1534             doctokenizerYYlex();
1535             scope = g_token->name;
1536             g_context = scope;
1537             //printf("Found scope='%s'\n",scope.data());
1538             doctokenizerYYsetStatePara();
1539           }
1540           break;
1541         default:
1542           return FALSE;
1543       }
1544       break;
1545     case TK_HTMLTAG:
1546       {
1547         switch (Mappers::htmlTagMapper->map(tokenName))
1548         {
1549           case HTML_DIV:
1550             warn_doc_error(g_fileName,doctokenizerYYlineno,"found <div> tag in heading\n");
1551             break;
1552           case HTML_PRE:
1553             warn_doc_error(g_fileName,doctokenizerYYlineno,"found <pre> tag in heading\n");
1554             break;
1555           case HTML_BOLD:
1556             if (!g_token->endTag)
1557             {
1558               handleStyleEnter(parent,children,DocStyleChange::Bold,&g_token->attribs);
1559             }
1560             else
1561             {
1562               handleStyleLeave(parent,children,DocStyleChange::Bold,tokenName);
1563             }
1564             break;
1565           case HTML_CODE:
1566           case XML_C:
1567             if (!g_token->endTag)
1568             {
1569               handleStyleEnter(parent,children,DocStyleChange::Code,&g_token->attribs);
1570             }
1571             else
1572             {
1573               handleStyleLeave(parent,children,DocStyleChange::Code,tokenName);
1574             }
1575             break;
1576           case HTML_EMPHASIS:
1577             if (!g_token->endTag)
1578             {
1579               handleStyleEnter(parent,children,DocStyleChange::Italic,&g_token->attribs);
1580             }
1581             else
1582             {
1583               handleStyleLeave(parent,children,DocStyleChange::Italic,tokenName);
1584             }
1585             break;
1586           case HTML_SUB:
1587             if (!g_token->endTag)
1588             {
1589               handleStyleEnter(parent,children,DocStyleChange::Subscript,&g_token->attribs);
1590             }
1591             else
1592             {
1593               handleStyleLeave(parent,children,DocStyleChange::Subscript,tokenName);
1594             }
1595             break;
1596           case HTML_SUP:
1597             if (!g_token->endTag)
1598             {
1599               handleStyleEnter(parent,children,DocStyleChange::Superscript,&g_token->attribs);
1600             }
1601             else
1602             {
1603               handleStyleLeave(parent,children,DocStyleChange::Superscript,tokenName);
1604             }
1605             break;
1606           case HTML_CENTER:
1607             if (!g_token->endTag)
1608             {
1609               handleStyleEnter(parent,children,DocStyleChange::Center,&g_token->attribs);
1610             }
1611             else
1612             {
1613               handleStyleLeave(parent,children,DocStyleChange::Center,tokenName);
1614             }
1615             break;
1616           case HTML_SMALL:
1617             if (!g_token->endTag)
1618             {
1619               handleStyleEnter(parent,children,DocStyleChange::Small,&g_token->attribs);
1620             }
1621             else
1622             {
1623               handleStyleLeave(parent,children,DocStyleChange::Small,tokenName);
1624             }
1625             break;
1626           default:
1627             return FALSE;
1628             break;
1629         }
1630       }
1631       break;
1632     case TK_SYMBOL: 
1633       {
1634         DocSymbol::SymType s = DocSymbol::decodeSymbol(tokenName);
1635         if (s!=DocSymbol::Sym_Unknown)
1636         {
1637           children.append(new DocSymbol(parent,s));
1638         }
1639         else
1640         {
1641           return FALSE;
1642         }
1643       }
1644       break;
1645     case TK_WHITESPACE: 
1646     case TK_NEWPARA: 
1647 handlepara:
1648       if (insidePRE(parent) || !children.isEmpty())
1649       {
1650         children.append(new DocWhiteSpace(parent,g_token->chars));
1651       }
1652       break;
1653     case TK_LNKWORD: 
1654       if (handleWord)
1655       {
1656         handleLinkedWord(parent,children);
1657       }
1658       else
1659         return FALSE;
1660       break;
1661     case TK_WORD: 
1662       if (handleWord)
1663       {
1664         children.append(new DocWord(parent,g_token->name));
1665       }
1666       else
1667         return FALSE;
1668       break;
1669     case TK_URL:
1670       if (g_insideHtmlLink)
1671       {
1672         children.append(new DocWord(parent,g_token->name));
1673       }
1674       else
1675       {
1676         children.append(new DocURL(parent,g_token->name,g_token->isEMailAddr));
1677       }
1678       break;
1679     default:
1680       return FALSE;
1681   }
1682   return TRUE;
1683 }
1684
1685 //---------------------------------------------------------------------------
1686
1687 static void handleImg(DocNode *parent,QList<DocNode> &children,const HtmlAttribList &tagHtmlAttribs)
1688 {
1689   HtmlAttribListIterator li(tagHtmlAttribs);
1690   HtmlAttrib *opt;
1691   bool found=FALSE;
1692   int index=0;
1693   for (li.toFirst();(opt=li.current());++li,++index)
1694   {
1695     //printf("option name=%s value=%s\n",opt->name.data(),opt->value.data());
1696     if (opt->name=="src" && !opt->value.isEmpty())
1697     {
1698       // copy attributes
1699       HtmlAttribList attrList = tagHtmlAttribs;
1700       // and remove the src attribute
1701       bool result = attrList.remove(index);
1702       ASSERT(result);
1703       DocImage *img = new DocImage(parent,attrList,opt->value,DocImage::Html,opt->value);
1704       children.append(img);
1705       found = TRUE;
1706     }
1707   }
1708   if (!found)
1709   {
1710     warn_doc_error(g_fileName,doctokenizerYYlineno,"IMG tag does not have a SRC attribute!\n");
1711   }
1712 }
1713
1714 //---------------------------------------------------------------------------
1715
1716 DocSymbol::SymType DocSymbol::decodeSymbol(const QCString &symName)
1717 {
1718   DBG(("decodeSymbol(%s)\n",qPrint(symName)));
1719   return HtmlEntityMapper::instance()->name2sym(symName);
1720 }
1721
1722 //---------------------------------------------------------------------------
1723
1724 static int internalValidatingParseDoc(DocNode *parent,QList<DocNode> &children,
1725                                     const QCString &doc)
1726 {
1727   int retval = RetVal_OK;
1728
1729   if (doc.isEmpty()) return retval;
1730
1731   doctokenizerYYinit(doc,g_fileName);
1732
1733   // first parse any number of paragraphs
1734   bool isFirst=TRUE;
1735   DocPara *lastPar=0;
1736   if (!children.isEmpty() && children.getLast()->kind()==DocNode::Kind_Para)
1737   { // last child item was a paragraph
1738     lastPar = (DocPara*)children.getLast();
1739     isFirst=FALSE;
1740   }
1741   do
1742   {
1743     DocPara *par = new DocPara(parent);
1744     if (isFirst) { par->markFirst(); isFirst=FALSE; }
1745     retval=par->parse();
1746     if (!par->isEmpty()) 
1747     {
1748       children.append(par);
1749       if (lastPar) lastPar->markLast(FALSE);
1750       lastPar=par;
1751     }
1752     else
1753     {
1754       delete par;
1755     }
1756   } while (retval==TK_NEWPARA);
1757   if (lastPar) lastPar->markLast();
1758
1759   //printf("internalValidateParsingDoc: %p: isFirst=%d isLast=%d\n",
1760   //   lastPar,lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
1761
1762   return retval;
1763 }
1764
1765 //---------------------------------------------------------------------------
1766
1767 static void readTextFileByName(const QCString &file,QCString &text)
1768 {
1769   if (portable_isAbsolutePath(file.data()))
1770   {
1771     QFileInfo fi(file);
1772     if (fi.exists())
1773     {
1774       text = fileToString(file,Config_getBool(FILTER_SOURCE_FILES));
1775       return;
1776     }
1777   }
1778   QStrList &examplePathList = Config_getList(EXAMPLE_PATH);
1779   char *s=examplePathList.first();
1780   while (s)
1781   {
1782     QCString absFileName = QCString(s)+portable_pathSeparator()+file;
1783     QFileInfo fi(absFileName);
1784     if (fi.exists())
1785     {
1786       text = fileToString(absFileName,Config_getBool(FILTER_SOURCE_FILES));
1787       return;
1788     }
1789     s=examplePathList.next(); 
1790   }
1791
1792   // as a fallback we also look in the exampleNameDict
1793   bool ambig;
1794   FileDef *fd;
1795   if ((fd=findFileDef(Doxygen::exampleNameDict,file,ambig)))
1796   {
1797     text = fileToString(fd->absFilePath(),Config_getBool(FILTER_SOURCE_FILES));
1798   }
1799   else if (ambig)
1800   {
1801     warn_doc_error(g_fileName,doctokenizerYYlineno,"included file name %s is ambiguous"
1802            "Possible candidates:\n%s",qPrint(file),
1803            qPrint(showFileDefMatches(Doxygen::exampleNameDict,file))
1804           );
1805   }
1806   else
1807   {
1808     warn_doc_error(g_fileName,doctokenizerYYlineno,"included file %s is not found. "
1809            "Check your EXAMPLE_PATH",qPrint(file));
1810   }
1811 }
1812
1813 //---------------------------------------------------------------------------
1814
1815 DocWord::DocWord(DocNode *parent,const QCString &word) : 
1816       m_word(word) 
1817 {
1818   m_parent = parent; 
1819   //printf("new word %s url=%s\n",word.data(),g_searchUrl.data());
1820   if (Doxygen::searchIndex && !g_searchUrl.isEmpty())
1821   {
1822     Doxygen::searchIndex->addWord(word,FALSE);
1823   }
1824 }
1825
1826 //---------------------------------------------------------------------------
1827
1828 DocLinkedWord::DocLinkedWord(DocNode *parent,const QCString &word,
1829                   const QCString &ref,const QCString &file,
1830                   const QCString &anchor,const QCString &tooltip) : 
1831       m_word(word), m_ref(ref), 
1832       m_file(file), m_relPath(g_relPath), m_anchor(anchor),
1833       m_tooltip(tooltip)
1834 {
1835   m_parent = parent; 
1836   //printf("DocLinkedWord: new word %s url=%s tooltip='%s'\n",
1837   //    word.data(),g_searchUrl.data(),tooltip.data());
1838   if (Doxygen::searchIndex && !g_searchUrl.isEmpty())
1839   {
1840     Doxygen::searchIndex->addWord(word,FALSE);
1841   }
1842 }
1843
1844 //---------------------------------------------------------------------------
1845
1846 DocAnchor::DocAnchor(DocNode *parent,const QCString &id,bool newAnchor) 
1847 {
1848   m_parent = parent; 
1849   if (id.isEmpty())
1850   {
1851     warn_doc_error(g_fileName,doctokenizerYYlineno,"Empty anchor label");
1852   }
1853   if (newAnchor) // found <a name="label">
1854   {
1855     m_anchor = id;
1856   }
1857   else if (id.left(CiteConsts::anchorPrefix.length()) == CiteConsts::anchorPrefix) 
1858   {
1859     CiteInfo *cite = Doxygen::citeDict->find(id.mid(CiteConsts::anchorPrefix.length()));
1860     if (cite) 
1861     {
1862       m_file = convertNameToFile(CiteConsts::fileName,FALSE,TRUE);
1863       m_anchor = id;
1864     }
1865     else 
1866     {
1867       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid cite anchor id `%s'",qPrint(id));
1868       m_anchor = "invalid";
1869       m_file = "invalid";
1870     }
1871   }
1872   else // found \anchor label
1873   {
1874     SectionInfo *sec = Doxygen::sectionDict->find(id);
1875     if (sec)
1876     {
1877       //printf("Found anchor %s\n",id.data());
1878       m_file   = sec->fileName;
1879       m_anchor = sec->label;
1880       if (g_sectionDict && g_sectionDict->find(id)==0)
1881       {
1882         //printf("Inserting in dictionary!\n");
1883         g_sectionDict->append(id,sec);
1884       }
1885     }
1886     else
1887     {
1888       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid anchor id `%s'",qPrint(id));
1889       m_anchor = "invalid";
1890       m_file = "invalid";
1891     }
1892   }
1893 }
1894
1895 //---------------------------------------------------------------------------
1896
1897 DocVerbatim::DocVerbatim(DocNode *parent,const QCString &context,
1898     const QCString &text, Type t,bool isExample,
1899     const QCString &exampleFile,bool isBlock,const QCString &lang)
1900   : m_context(context), m_text(text), m_type(t),
1901     m_isExample(isExample), m_exampleFile(exampleFile),
1902     m_relPath(g_relPath), m_lang(lang), m_isBlock(isBlock)
1903 {
1904   m_parent = parent;
1905 }
1906
1907
1908 //---------------------------------------------------------------------------
1909
1910 void DocInclude::parse()
1911 {
1912   DBG(("DocInclude::parse(file=%s,text=%s)\n",qPrint(m_file),qPrint(m_text)));
1913   switch(m_type)
1914   {
1915     case IncWithLines:
1916       // fall through
1917     case Include:
1918       // fall through
1919     case DontInclude:
1920       readTextFileByName(m_file,m_text);
1921       g_includeFileName   = m_file;
1922       g_includeFileText   = m_text;
1923       g_includeFileOffset = 0;
1924       g_includeFileLength = m_text.length();
1925       //printf("g_includeFile=<<%s>>\n",g_includeFileText.data());
1926       break;
1927     case VerbInclude: 
1928       // fall through
1929     case HtmlInclude:
1930       readTextFileByName(m_file,m_text);
1931       break;
1932     case LatexInclude:
1933       readTextFileByName(m_file,m_text);
1934       break;
1935     case Snippet:
1936     case SnipWithLines:
1937       readTextFileByName(m_file,m_text);
1938       // check here for the existence of the blockId inside the file, so we
1939       // only generate the warning once.
1940       int count;
1941       if (!m_blockId.isEmpty() && (count=m_text.contains(m_blockId.data()))!=2)
1942       {
1943         warn_doc_error(g_fileName,doctokenizerYYlineno,"block marked with %s for \\snippet should appear twice in file %s, found it %d times\n",
1944             m_blockId.data(),m_file.data(),count);
1945       }
1946       break;
1947     case DocInclude::SnippetDoc: 
1948     case DocInclude::IncludeDoc: 
1949       err("Internal inconsistency: found switch SnippetDoc / IncludeDoc in file: %s"
1950           "Please create a bug report\n",__FILE__);
1951       break;
1952   }
1953 }
1954
1955 //---------------------------------------------------------------------------
1956
1957 void DocIncOperator::parse()
1958 {
1959   m_includeFileName = g_includeFileName;
1960   const char *p = g_includeFileText;
1961   uint l = g_includeFileLength;
1962   uint o = g_includeFileOffset;
1963   DBG(("DocIncOperator::parse() text=%s off=%d len=%d\n",qPrint(p),o,l));
1964   uint so = o,bo;
1965   bool nonEmpty = FALSE;
1966   switch(type())
1967   {
1968     case Line:
1969       while (o<l)
1970       {
1971         char c = p[o];
1972         if (c=='\n') 
1973         {
1974           if (nonEmpty) break; // we have a pattern to match
1975           so=o+1; // no pattern, skip empty line
1976         }
1977         else if (!isspace((uchar)c)) // no white space char
1978         {
1979           nonEmpty=TRUE;
1980         }
1981         o++;
1982       }
1983       if (g_includeFileText.mid(so,o-so).find(m_pattern)!=-1)
1984       {
1985         m_text = g_includeFileText.mid(so,o-so);
1986         DBG(("DocIncOperator::parse() Line: %s\n",qPrint(m_text)));
1987       }
1988       g_includeFileOffset = QMIN(l,o+1); // set pointer to start of new line
1989       break;
1990     case SkipLine:
1991       while (o<l)
1992       {
1993         so=o;
1994         while (o<l)
1995         {
1996           char c = p[o];
1997           if (c=='\n')
1998           {
1999             if (nonEmpty) break; // we have a pattern to match
2000             so=o+1; // no pattern, skip empty line
2001           }
2002           else if (!isspace((uchar)c)) // no white space char
2003           {
2004             nonEmpty=TRUE;
2005           }
2006           o++;
2007         }
2008         if (g_includeFileText.mid(so,o-so).find(m_pattern)!=-1)
2009         {
2010           m_text = g_includeFileText.mid(so,o-so);
2011           DBG(("DocIncOperator::parse() SkipLine: %s\n",qPrint(m_text)));
2012           break;
2013         }
2014         o++; // skip new line
2015       }
2016       g_includeFileOffset = QMIN(l,o+1); // set pointer to start of new line
2017       break;
2018     case Skip:
2019       while (o<l)
2020       {
2021         so=o;
2022         while (o<l)
2023         {
2024           char c = p[o];
2025           if (c=='\n')
2026           {
2027             if (nonEmpty) break; // we have a pattern to match
2028             so=o+1; // no pattern, skip empty line
2029           }
2030           else if (!isspace((uchar)c)) // no white space char
2031           {
2032             nonEmpty=TRUE;
2033           }
2034           o++;
2035         }
2036         if (g_includeFileText.mid(so,o-so).find(m_pattern)!=-1)
2037         {
2038           break;
2039         }
2040         o++; // skip new line
2041       }
2042       g_includeFileOffset = so; // set pointer to start of new line
2043       break;
2044     case Until:
2045       bo=o;
2046       while (o<l)
2047       {
2048         so=o;
2049         while (o<l)
2050         {
2051           char c = p[o];
2052           if (c=='\n')
2053           {
2054             if (nonEmpty) break; // we have a pattern to match
2055             so=o+1; // no pattern, skip empty line
2056           }
2057           else if (!isspace((uchar)c)) // no white space char
2058           {
2059             nonEmpty=TRUE;
2060           }
2061           o++;
2062         }
2063         if (g_includeFileText.mid(so,o-so).find(m_pattern)!=-1)
2064         {
2065           m_text = g_includeFileText.mid(bo,o-bo);
2066           DBG(("DocIncOperator::parse() Until: %s\n",qPrint(m_text)));
2067           break;
2068         }
2069         o++; // skip new line
2070       }
2071       g_includeFileOffset = QMIN(l,o+1); // set pointer to start of new line
2072       break;
2073   }
2074 }
2075
2076 //---------------------------------------------------------------------------
2077
2078 void DocCopy::parse(QList<DocNode> &children)
2079 {
2080   QCString doc,brief;
2081   Definition *def;
2082   if (findDocsForMemberOrCompound(m_link,&doc,&brief,&def))
2083   {
2084     if (g_copyStack.findRef(def)==-1) // definition not parsed earlier
2085     {
2086       bool         hasParamCommand  = g_hasParamCommand;
2087       bool         hasReturnCommand = g_hasReturnCommand;
2088       QDict<void>  paramsFound      = g_paramsFound;
2089       //printf("..1 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
2090       //      g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
2091
2092       docParserPushContext(FALSE);
2093       g_scope = def;
2094       if (def->definitionType()==Definition::TypeMember && def->getOuterScope())
2095       {
2096         if (def->getOuterScope()!=Doxygen::globalScope)
2097         {
2098           g_context=def->getOuterScope()->name();
2099         }
2100       }
2101       else if (def!=Doxygen::globalScope)
2102       {
2103         g_context=def->name();
2104       }
2105       g_styleStack.clear();
2106       g_nodeStack.clear();
2107       g_paramsFound.clear();
2108       g_copyStack.append(def);
2109       // make sure the descriptions end with a newline, so the parser will correctly
2110       // handle them in all cases.
2111       //printf("doc='%s'\n",doc.data());
2112       //printf("brief='%s'\n",brief.data());
2113       if (m_copyBrief)
2114       {
2115         brief+='\n';
2116         internalValidatingParseDoc(m_parent,children,brief);
2117
2118         //printf("..2 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
2119         //    g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
2120         hasParamCommand  = hasParamCommand  || g_hasParamCommand;
2121         hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
2122         QDictIterator<void> it(g_paramsFound);
2123         void *item;
2124         for (;(item=it.current());++it)
2125         {
2126           paramsFound.insert(it.currentKey(),it.current());
2127         }
2128       }
2129       if (m_copyDetails)
2130       {
2131         doc+='\n';
2132         internalValidatingParseDoc(m_parent,children,doc);
2133
2134         //printf("..3 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
2135         //    g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
2136         hasParamCommand  = hasParamCommand  || g_hasParamCommand;
2137         hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
2138         QDictIterator<void> it(g_paramsFound);
2139         void *item;
2140         for (;(item=it.current());++it)
2141         {
2142           paramsFound.insert(it.currentKey(),it.current());
2143         }
2144       }
2145       g_copyStack.remove(def);
2146       ASSERT(g_styleStack.isEmpty());
2147       ASSERT(g_nodeStack.isEmpty());
2148       docParserPopContext(TRUE);
2149
2150       g_hasParamCommand  = hasParamCommand;
2151       g_hasReturnCommand = hasReturnCommand;
2152       g_paramsFound      = paramsFound;
2153
2154       //printf("..4 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
2155       //      g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
2156     }
2157     else // oops, recursion
2158     {
2159       warn_doc_error(g_fileName,doctokenizerYYlineno,"recursive call chain of \\copydoc commands detected at %d\n",
2160           doctokenizerYYlineno);
2161     }
2162   }
2163   else
2164   {
2165     warn_doc_error(g_fileName,doctokenizerYYlineno,"target %s of \\copydoc command not found",
2166         qPrint(m_link));
2167   }
2168 }
2169
2170 //---------------------------------------------------------------------------
2171
2172 DocXRefItem::DocXRefItem(DocNode *parent,int id,const char *key) : 
2173    m_id(id), m_key(key), m_relPath(g_relPath)
2174 {
2175    m_parent = parent; 
2176 }
2177
2178 bool DocXRefItem::parse()
2179 {
2180   RefList *refList = Doxygen::xrefLists->find(m_key); 
2181   if (refList && 
2182       (
2183        // either not a built-in list or the list is enabled
2184        (m_key!="todo"       || Config_getBool(GENERATE_TODOLIST)) && 
2185        (m_key!="test"       || Config_getBool(GENERATE_TESTLIST)) && 
2186        (m_key!="bug"        || Config_getBool(GENERATE_BUGLIST))  && 
2187        (m_key!="deprecated" || Config_getBool(GENERATE_DEPRECATEDLIST))
2188       ) 
2189      )
2190   {
2191     RefItem *item = refList->getRefItem(m_id);
2192     ASSERT(item!=0);
2193     if (item)
2194     {
2195       if (g_memberDef && g_memberDef->name().at(0)=='@')
2196       {
2197         m_file   = "@";  // can't cross reference anonymous enum
2198         m_anchor = "@";
2199       }
2200       else
2201       {
2202         m_file   = refList->fileName();
2203         m_anchor = item->listAnchor;
2204       }
2205       m_title  = refList->sectionTitle();
2206       //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
2207       //    m_file.data(),m_anchor.data(),m_title.data());
2208
2209       if (!item->text.isEmpty())
2210       {
2211         docParserPushContext();
2212         internalValidatingParseDoc(this,m_children,item->text);
2213         docParserPopContext();
2214       }
2215     }
2216     return TRUE;
2217   }
2218   return FALSE;
2219 }
2220
2221 //---------------------------------------------------------------------------
2222
2223 DocFormula::DocFormula(DocNode *parent,int id) :
2224       m_relPath(g_relPath)
2225 {
2226   m_parent = parent; 
2227   QCString formCmd;
2228   formCmd.sprintf("\\form#%d",id);
2229   Formula *formula=Doxygen::formulaNameDict->find(formCmd);
2230   if (formula)
2231   {
2232     m_id = formula->getId();
2233     m_name.sprintf("form_%d",m_id);
2234     m_text = formula->getFormulaText();
2235   }
2236   else // wrong \form#<n> command
2237   {
2238     warn_doc_error(g_fileName,doctokenizerYYlineno,"Wrong formula id %d",id);
2239     m_id = -1;
2240   }
2241 }
2242
2243 //---------------------------------------------------------------------------
2244
2245 //int DocLanguage::parse()
2246 //{
2247 //  int retval;
2248 //  DBG(("DocLanguage::parse() start\n"));
2249 //  g_nodeStack.push(this);
2250 //
2251 //  // parse one or more paragraphs
2252 //  bool isFirst=TRUE;
2253 //  DocPara *par=0;
2254 //  do
2255 //  {
2256 //    par = new DocPara(this);
2257 //    if (isFirst) { par->markFirst(); isFirst=FALSE; }
2258 //    m_children.append(par);
2259 //    retval=par->parse();
2260 //  }
2261 //  while (retval==TK_NEWPARA);
2262 //  if (par) par->markLast();
2263 //
2264 //  DBG(("DocLanguage::parse() end\n"));
2265 //  DocNode *n = g_nodeStack.pop();
2266 //  ASSERT(n==this);
2267 //  return retval;
2268 //}
2269
2270 //---------------------------------------------------------------------------
2271
2272 void DocSecRefItem::parse()
2273 {
2274   DBG(("DocSecRefItem::parse() start\n"));
2275   g_nodeStack.push(this);
2276
2277   doctokenizerYYsetStateTitle();
2278   int tok;
2279   while ((tok=doctokenizerYYlex()))
2280   {
2281     if (!defaultHandleToken(this,tok,m_children))
2282     {
2283       switch (tok)
2284       {
2285         case TK_COMMAND: 
2286           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\refitem",
2287                qPrint(g_token->name));
2288           break;
2289         case TK_SYMBOL: 
2290           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
2291                qPrint(g_token->name));
2292           break;
2293         default:
2294           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
2295                tokToString(tok));
2296           break;
2297       }
2298     }
2299   }
2300   doctokenizerYYsetStatePara();
2301   handlePendingStyleCommands(this,m_children);
2302
2303   SectionInfo *sec=0;
2304   if (!m_target.isEmpty())
2305   {
2306     sec=Doxygen::sectionDict->find(m_target);
2307     if (sec)
2308     {
2309       m_file   = sec->fileName;
2310       m_anchor = sec->label;
2311       if (g_sectionDict && g_sectionDict->find(m_target)==0)
2312       {
2313         g_sectionDict->append(m_target,sec);
2314       }
2315     }
2316     else
2317     {
2318       warn_doc_error(g_fileName,doctokenizerYYlineno,"reference to unknown section %s",
2319           qPrint(m_target));
2320     }
2321   } 
2322   else
2323   {
2324     warn_doc_error(g_fileName,doctokenizerYYlineno,"reference to empty target");
2325   }
2326   
2327   DBG(("DocSecRefItem::parse() end\n"));
2328   DocNode *n = g_nodeStack.pop();
2329   ASSERT(n==this);
2330 }
2331
2332 //---------------------------------------------------------------------------
2333
2334 void DocSecRefList::parse()
2335 {
2336   DBG(("DocSecRefList::parse() start\n"));
2337   g_nodeStack.push(this);
2338
2339   int tok=doctokenizerYYlex();
2340   // skip white space
2341   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
2342   // handle items
2343   while (tok)
2344   {
2345     if (tok==TK_COMMAND)
2346     {
2347       switch (Mappers::cmdMapper->map(g_token->name))
2348       {
2349         case CMD_SECREFITEM:
2350           {
2351             int tok=doctokenizerYYlex();
2352             if (tok!=TK_WHITESPACE)
2353             {
2354               warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\refitem command");
2355               break;
2356             }
2357             tok=doctokenizerYYlex();
2358             if (tok!=TK_WORD && tok!=TK_LNKWORD)
2359             {
2360               warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of \\refitem",
2361                   tokToString(tok));
2362               break;
2363             }
2364
2365             DocSecRefItem *item = new DocSecRefItem(this,g_token->name);
2366             m_children.append(item);
2367             item->parse();
2368           }
2369           break;
2370         case CMD_ENDSECREFLIST:
2371           goto endsecreflist;
2372         default:
2373           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\secreflist",
2374               qPrint(g_token->name));
2375           goto endsecreflist;
2376       }
2377     }
2378     else if (tok==TK_WHITESPACE)
2379     {
2380       // ignore whitespace
2381     }
2382     else
2383     {
2384       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s inside section reference list",
2385           tokToString(tok));
2386       goto endsecreflist;
2387     }
2388     tok=doctokenizerYYlex();
2389   }
2390
2391 endsecreflist:
2392   DBG(("DocSecRefList::parse() end\n"));
2393   DocNode *n = g_nodeStack.pop();
2394   ASSERT(n==this);
2395 }
2396
2397 //---------------------------------------------------------------------------
2398
2399 DocInternalRef::DocInternalRef(DocNode *parent,const QCString &ref) 
2400   : m_relPath(g_relPath)
2401 {
2402   m_parent = parent; 
2403   int i=ref.find('#');
2404   if (i!=-1)
2405   {
2406     m_anchor = ref.right(ref.length()-i-1);
2407     m_file   = ref.left(i);
2408   }
2409   else
2410   {
2411     m_file = ref;
2412   }
2413 }
2414
2415 void DocInternalRef::parse()
2416 {
2417   g_nodeStack.push(this);
2418   DBG(("DocInternalRef::parse() start\n"));
2419
2420   int tok;
2421   while ((tok=doctokenizerYYlex()))
2422   {
2423     if (!defaultHandleToken(this,tok,m_children))
2424     {
2425       switch (tok)
2426       {
2427         case TK_COMMAND: 
2428           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\ref",
2429                qPrint(g_token->name));
2430           break;
2431         case TK_SYMBOL: 
2432           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
2433                qPrint(g_token->name));
2434           break;
2435         default:
2436           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
2437                 tokToString(tok));
2438           break;
2439       }
2440     }
2441   }
2442
2443   handlePendingStyleCommands(this,m_children);
2444   DBG(("DocInternalRef::parse() end\n"));
2445   DocNode *n=g_nodeStack.pop();
2446   ASSERT(n==this);
2447 }
2448
2449 //---------------------------------------------------------------------------
2450
2451 DocRef::DocRef(DocNode *parent,const QCString &target,const QCString &context) : 
2452    m_refType(Unknown), m_isSubPage(FALSE)
2453 {
2454   m_parent = parent; 
2455   Definition  *compound = 0;
2456   QCString     anchor;
2457   //printf("DocRef::DocRef(target=%s,context=%s)\n",target.data(),context.data());
2458   ASSERT(!target.isEmpty());
2459   SrcLangExt lang = getLanguageFromFileName(target);
2460   m_relPath = g_relPath;
2461   SectionInfo *sec = Doxygen::sectionDict->find(target);
2462   if (sec==0 && lang==SrcLangExt_Markdown) // lookup as markdown file
2463   {
2464     sec = Doxygen::sectionDict->find(markdownFileNameToId(target));
2465   }
2466   if (sec) // ref to section or anchor
2467   {
2468     PageDef *pd = 0;
2469     if (sec->type==SectionInfo::Page)
2470     {
2471       pd = Doxygen::pageSDict->find(target);
2472     }
2473     m_text         = sec->title;
2474     if (m_text.isEmpty()) m_text = sec->label;
2475
2476     m_ref          = sec->ref;
2477     m_file         = stripKnownExtensions(sec->fileName);
2478     if (sec->type==SectionInfo::Anchor)
2479     {
2480       m_refType = Anchor;
2481     }
2482     else if (sec->type==SectionInfo::Table)
2483     {
2484       m_refType = Table;
2485     }
2486     else
2487     {
2488       m_refType = Section;
2489     }
2490     m_isSubPage    = pd && pd->hasParentPage();
2491     if (sec->type!=SectionInfo::Page || m_isSubPage) m_anchor = sec->label;
2492     //printf("m_text=%s,m_ref=%s,m_file=%s,m_refToAnchor=%d type=%d\n",
2493     //    m_text.data(),m_ref.data(),m_file.data(),m_refToAnchor,sec->type);
2494     return;
2495   }
2496   else if (resolveLink(context,target,TRUE,&compound,anchor))
2497   {
2498     bool isFile = compound ? 
2499                  (compound->definitionType()==Definition::TypeFile ||
2500                   compound->definitionType()==Definition::TypePage ? TRUE : FALSE) : 
2501                  FALSE;
2502     m_text = linkToText(compound?compound->getLanguage():SrcLangExt_Unknown,target,isFile);
2503     m_anchor = anchor;
2504     if (compound && compound->isLinkable()) // ref to compound
2505     {
2506       if (anchor.isEmpty() &&                                  /* compound link */
2507           compound->definitionType()==Definition::TypeGroup && /* is group */
2508           ((GroupDef *)compound)->groupTitle()                 /* with title */
2509          )
2510       {
2511         m_text=((GroupDef *)compound)->groupTitle(); // use group's title as link
2512       }
2513       else if (compound->definitionType()==Definition::TypeMember &&
2514           ((MemberDef*)compound)->isObjCMethod())
2515       {
2516         // Objective C Method
2517         MemberDef *member = (MemberDef*)compound;
2518         bool localLink = g_memberDef ? member->getClassDef()==g_memberDef->getClassDef() : FALSE;
2519         m_text = member->objCMethodName(localLink,g_inSeeBlock);
2520       }
2521
2522       m_file = compound->getOutputFileBase();
2523       m_ref  = compound->getReference();
2524       //printf("isFile=%d compound=%s (%d)\n",isFile,compound->name().data(),
2525       //    compound->definitionType());
2526       return;
2527     }
2528     else if (compound && compound->definitionType()==Definition::TypeFile &&
2529              ((FileDef*)compound)->generateSourceFile()
2530             ) // undocumented file that has source code we can link to
2531     {
2532       m_file = compound->getSourceFileBase();
2533       m_ref  = compound->getReference();
2534       return;
2535     }
2536   }
2537   m_text = target;
2538   warn_doc_error(g_fileName,doctokenizerYYlineno,"unable to resolve reference to `%s' for \\ref command",
2539            qPrint(target)); 
2540 }
2541
2542 static void flattenParagraphs(DocNode *root,QList<DocNode> &children)
2543 {
2544   QListIterator<DocNode> li(children);
2545   QList<DocNode> newChildren;
2546   DocNode *dn;
2547   for (li.toFirst();(dn=li.current());++li)
2548   {
2549     if (dn->kind()==DocNode::Kind_Para)
2550     {
2551       DocPara *para = (DocPara*)dn;
2552       QList<DocNode> &paraChildren = para->children();
2553       paraChildren.setAutoDelete(FALSE); // unlink children from paragraph node
2554       QListIterator<DocNode> li2(paraChildren);
2555       DocNode *dn2;
2556       for (li2.toFirst();(dn2=li2.current());++li2)
2557       {
2558         newChildren.append(dn2); // add them to new node
2559       }
2560     }
2561   }
2562   children.clear();
2563   QListIterator<DocNode> li3(newChildren);
2564   for (li3.toFirst();(dn=li3.current());++li3)
2565   {
2566     children.append(dn);
2567     dn->setParent(root);
2568   }
2569 }
2570
2571 void DocRef::parse()
2572 {
2573   g_nodeStack.push(this);
2574   DBG(("DocRef::parse() start\n"));
2575
2576   int tok;
2577   while ((tok=doctokenizerYYlex()))
2578   {
2579     if (!defaultHandleToken(this,tok,m_children))
2580     {
2581       switch (tok)
2582       {
2583         case TK_COMMAND: 
2584           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\ref",
2585                qPrint(g_token->name));
2586           break;
2587         case TK_SYMBOL: 
2588           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
2589                qPrint(g_token->name));
2590           break;
2591         case TK_HTMLTAG:
2592           break;
2593         default:
2594           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
2595                 tokToString(tok));
2596           break;
2597       }
2598     }
2599   }
2600
2601   if (m_children.isEmpty() && !m_text.isEmpty())
2602   {
2603     g_insideHtmlLink=TRUE;
2604     docParserPushContext();
2605     internalValidatingParseDoc(this,m_children,m_text);
2606     docParserPopContext();
2607     g_insideHtmlLink=FALSE;
2608     flattenParagraphs(this,m_children);
2609   }
2610
2611   handlePendingStyleCommands(this,m_children);
2612   
2613   DocNode *n=g_nodeStack.pop();
2614   ASSERT(n==this);
2615 }
2616
2617 //---------------------------------------------------------------------------
2618
2619 DocCite::DocCite(DocNode *parent,const QCString &target,const QCString &) //context)
2620 {
2621   static uint numBibFiles = Config_getList(CITE_BIB_FILES).count();
2622   m_parent = parent;
2623   //printf("DocCite::DocCite(target=%s)\n",target.data());
2624   ASSERT(!target.isEmpty());
2625   m_relPath = g_relPath;
2626   CiteInfo *cite = Doxygen::citeDict->find(target);
2627   //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?cite->text.data():"<null>",numBibFiles);
2628   if (numBibFiles>0 && cite && !cite->text.isEmpty()) // ref to citation
2629   {
2630     m_text         = cite->text;
2631     m_ref          = cite->ref;
2632     m_anchor       = CiteConsts::anchorPrefix+cite->label;
2633     m_file         = convertNameToFile(CiteConsts::fileName,FALSE,TRUE);
2634     //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
2635     //    m_text.data(),m_ref.data(),m_file.data(),m_anchor.data());
2636     return;
2637   }
2638   m_text = target;
2639   if (numBibFiles==0)
2640   {
2641     warn_doc_error(g_fileName,doctokenizerYYlineno,"\\cite command found but no bib files specified via CITE_BIB_FILES!");
2642   }
2643   else if (cite==0)
2644   {
2645     warn_doc_error(g_fileName,doctokenizerYYlineno,"unable to resolve reference to `%s' for \\cite command",
2646              qPrint(target));
2647   }
2648   else
2649   {
2650     warn_doc_error(g_fileName,doctokenizerYYlineno,"\\cite command to '%s' does not have an associated number",
2651              qPrint(target));
2652   }
2653 }
2654
2655 //---------------------------------------------------------------------------
2656
2657 DocLink::DocLink(DocNode *parent,const QCString &target) 
2658 {
2659   m_parent = parent;
2660   Definition *compound = 0;
2661   QCString anchor;
2662   m_refText = target;
2663   m_relPath = g_relPath;
2664   if (!m_refText.isEmpty() && m_refText.at(0)=='#')
2665   {
2666     m_refText = m_refText.right(m_refText.length()-1);
2667   }
2668   if (resolveLink(g_context,stripKnownExtensions(target),g_inSeeBlock,
2669                   &compound,anchor))
2670   {
2671     m_anchor = anchor;
2672     if (compound && compound->isLinkable())
2673     {
2674       m_file = compound->getOutputFileBase();
2675       m_ref  = compound->getReference();
2676     }
2677     else if (compound && compound->definitionType()==Definition::TypeFile && 
2678              ((FileDef*)compound)->generateSourceFile()
2679             ) // undocumented file that has source code we can link to
2680     {
2681       m_file = compound->getSourceFileBase();
2682       m_ref  = compound->getReference();
2683     }
2684     return;
2685   }
2686
2687   // bogus link target
2688   warn_doc_error(g_fileName,doctokenizerYYlineno,"unable to resolve link to `%s' for \\link command",
2689          qPrint(target)); 
2690 }
2691
2692
2693 QCString DocLink::parse(bool isJavaLink,bool isXmlLink)
2694 {
2695   QCString result;
2696   g_nodeStack.push(this);
2697   DBG(("DocLink::parse() start\n"));
2698
2699   int tok;
2700   while ((tok=doctokenizerYYlex()))
2701   {
2702     if (!defaultHandleToken(this,tok,m_children,FALSE))
2703     {
2704       switch (tok)
2705       {
2706         case TK_COMMAND: 
2707           switch (Mappers::cmdMapper->map(g_token->name))
2708           {
2709             case CMD_ENDLINK:
2710               if (isJavaLink)
2711               {
2712                 warn_doc_error(g_fileName,doctokenizerYYlineno,"{@link.. ended with @endlink command");
2713               }
2714               goto endlink;
2715             default:
2716               warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\link",
2717                   qPrint(g_token->name));
2718               break;
2719           }
2720           break;
2721         case TK_SYMBOL: 
2722           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
2723               qPrint(g_token->name));
2724           break;
2725         case TK_HTMLTAG:
2726           if (g_token->name!="see" || !isXmlLink)
2727           {
2728             warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected xml/html command %s found",
2729                 qPrint(g_token->name));
2730           }
2731           goto endlink;
2732         case TK_LNKWORD: 
2733         case TK_WORD: 
2734           if (isJavaLink) // special case to detect closing }
2735           {
2736             QCString w = g_token->name;
2737             int p;
2738             if (w=="}")
2739             {
2740               goto endlink;
2741             }
2742             else if ((p=w.find('}'))!=-1)
2743             {
2744               uint l=w.length();
2745               m_children.append(new DocWord(this,w.left(p)));
2746               if ((uint)p<l-1) // something left after the } (for instance a .)
2747               {
2748                 result=w.right(l-p-1);
2749               }
2750               goto endlink;
2751             }
2752           }
2753           m_children.append(new DocWord(this,g_token->name));
2754           break;
2755         default:
2756           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
2757              tokToString(tok));
2758         break;
2759       }
2760     }
2761   }
2762   if (tok==0)
2763   {
2764     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end of comment while inside"
2765            " link command\n"); 
2766   }
2767 endlink:
2768
2769   if (m_children.isEmpty()) // no link text
2770   {
2771     m_children.append(new DocWord(this,m_refText));
2772   }
2773
2774   handlePendingStyleCommands(this,m_children);
2775   DBG(("DocLink::parse() end\n"));
2776   DocNode *n=g_nodeStack.pop();
2777   ASSERT(n==this);
2778   return result;
2779 }
2780
2781
2782 //---------------------------------------------------------------------------
2783
2784 DocDotFile::DocDotFile(DocNode *parent,const QCString &name,const QCString &context) : 
2785       m_name(name), m_relPath(g_relPath), m_context(context)
2786 {
2787   m_parent = parent; 
2788 }
2789
2790 void DocDotFile::parse()
2791 {
2792   defaultHandleTitleAndSize(CMD_DOTFILE,this,m_children,m_width,m_height);
2793
2794   bool ambig;
2795   FileDef *fd = findFileDef(Doxygen::dotFileNameDict,m_name,ambig);
2796   if (fd==0 && m_name.right(4)!=".dot") // try with .dot extension as well
2797   {
2798     fd = findFileDef(Doxygen::dotFileNameDict,m_name+".dot",ambig);
2799   }
2800   if (fd)
2801   {
2802     m_file = fd->absFilePath();
2803   }
2804   else if (ambig)
2805   {
2806     warn_doc_error(g_fileName,doctokenizerYYlineno,"included dot file name %s is ambiguous.\n"
2807            "Possible candidates:\n%s",qPrint(m_name),
2808            qPrint(showFileDefMatches(Doxygen::exampleNameDict,m_name))
2809           );
2810   }
2811   else
2812   {
2813     warn_doc_error(g_fileName,doctokenizerYYlineno,"included dot file %s is not found "
2814            "in any of the paths specified via DOTFILE_DIRS!",qPrint(m_name));
2815   }
2816 }
2817
2818 DocMscFile::DocMscFile(DocNode *parent,const QCString &name,const QCString &context) : 
2819       m_name(name), m_relPath(g_relPath), m_context(context)
2820 {
2821   m_parent = parent; 
2822 }
2823
2824 void DocMscFile::parse()
2825 {
2826   defaultHandleTitleAndSize(CMD_MSCFILE,this,m_children,m_width,m_height);
2827
2828   bool ambig;
2829   FileDef *fd = findFileDef(Doxygen::mscFileNameDict,m_name,ambig);
2830   if (fd==0 && m_name.right(4)!=".msc") // try with .msc extension as well
2831   {
2832     fd = findFileDef(Doxygen::mscFileNameDict,m_name+".msc",ambig);
2833   }
2834   if (fd)
2835   {
2836     m_file = fd->absFilePath();
2837   }
2838   else if (ambig)
2839   {
2840     warn_doc_error(g_fileName,doctokenizerYYlineno,"included msc file name %s is ambiguous.\n"
2841            "Possible candidates:\n%s",qPrint(m_name),
2842            qPrint(showFileDefMatches(Doxygen::exampleNameDict,m_name))
2843           );
2844   }
2845   else
2846   {
2847     warn_doc_error(g_fileName,doctokenizerYYlineno,"included msc file %s is not found "
2848            "in any of the paths specified via MSCFILE_DIRS!",qPrint(m_name));
2849   }
2850 }
2851
2852 //---------------------------------------------------------------------------
2853
2854 DocDiaFile::DocDiaFile(DocNode *parent,const QCString &name,const QCString &context) :
2855       m_name(name), m_relPath(g_relPath), m_context(context)
2856 {
2857   m_parent = parent;
2858 }
2859
2860 void DocDiaFile::parse()
2861 {
2862   defaultHandleTitleAndSize(CMD_DIAFILE,this,m_children,m_width,m_height);
2863
2864   bool ambig;
2865   FileDef *fd = findFileDef(Doxygen::diaFileNameDict,m_name,ambig);
2866   if (fd==0 && m_name.right(4)!=".dia") // try with .dia extension as well
2867   {
2868     fd = findFileDef(Doxygen::diaFileNameDict,m_name+".dia",ambig);
2869   }
2870   if (fd)
2871   {
2872     m_file = fd->absFilePath();
2873   }
2874   else if (ambig)
2875   {
2876     warn_doc_error(g_fileName,doctokenizerYYlineno,"included dia file name %s is ambiguous.\n"
2877            "Possible candidates:\n%s",qPrint(m_name),
2878            qPrint(showFileDefMatches(Doxygen::exampleNameDict,m_name))
2879           );
2880   }
2881   else
2882   {
2883     warn_doc_error(g_fileName,doctokenizerYYlineno,"included dia file %s is not found "
2884            "in any of the paths specified via DIAFILE_DIRS!",qPrint(m_name));
2885   }
2886 }
2887
2888 //---------------------------------------------------------------------------
2889
2890 DocVhdlFlow::DocVhdlFlow(DocNode *parent)
2891 {
2892   m_parent = parent;
2893 }
2894
2895 void DocVhdlFlow::parse()
2896 {
2897   g_nodeStack.push(this);
2898   DBG(("DocVhdlFlow::parse() start\n"));
2899
2900   doctokenizerYYsetStateTitle();
2901   int tok;
2902   while ((tok=doctokenizerYYlex()))
2903   {
2904     if (!defaultHandleToken(this,tok,m_children))
2905     {
2906       switch (tok)
2907       {
2908         case TK_COMMAND: 
2909           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a \\vhdlflow",
2910                qPrint(g_token->name));
2911           break;
2912         case TK_SYMBOL: 
2913           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
2914                qPrint(g_token->name));
2915           break;
2916         default:
2917           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
2918                 tokToString(tok));
2919           break;
2920       }
2921     }
2922   }
2923   tok=doctokenizerYYlex();
2924
2925   doctokenizerYYsetStatePara();
2926   handlePendingStyleCommands(this,m_children);
2927
2928   DBG(("DocVhdlFlow::parse() end\n"));
2929   DocNode *n=g_nodeStack.pop();
2930   ASSERT(n==this);
2931   VhdlDocGen::createFlowChart(g_memberDef);
2932 }
2933
2934
2935 //---------------------------------------------------------------------------
2936
2937 DocImage::DocImage(DocNode *parent,const HtmlAttribList &attribs,const QCString &name,
2938                    Type t,const QCString &url) : 
2939       m_attribs(attribs), m_name(name), 
2940       m_type(t), m_relPath(g_relPath),
2941       m_url(url)
2942 {
2943   m_parent = parent;
2944 }
2945
2946 void DocImage::parse()
2947 {
2948   defaultHandleTitleAndSize(CMD_IMAGE,this,m_children,m_width,m_height);
2949 }
2950
2951
2952 //---------------------------------------------------------------------------
2953
2954 int DocHtmlHeader::parse()
2955 {
2956   int retval=RetVal_OK;
2957   g_nodeStack.push(this);
2958   DBG(("DocHtmlHeader::parse() start\n"));
2959
2960   int tok;
2961   while ((tok=doctokenizerYYlex()))
2962   {
2963     if (!defaultHandleToken(this,tok,m_children))
2964     {
2965       switch (tok)
2966       {
2967         case TK_COMMAND: 
2968           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a <h%d> tag",
2969                qPrint(g_token->name),m_level);
2970           break;
2971         case TK_HTMLTAG:
2972           {
2973             int tagId=Mappers::htmlTagMapper->map(g_token->name);
2974             if (tagId==HTML_H1 && g_token->endTag) // found </h1> tag
2975             {
2976               if (m_level!=1)
2977               {
2978                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h1>",
2979                     m_level); 
2980               }
2981               goto endheader;
2982             }
2983             else if (tagId==HTML_H2 && g_token->endTag) // found </h2> tag
2984             {
2985               if (m_level!=2)
2986               {
2987                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h2>",
2988                     m_level); 
2989               }
2990               goto endheader;
2991             }
2992             else if (tagId==HTML_H3 && g_token->endTag) // found </h3> tag
2993             {
2994               if (m_level!=3)
2995               {
2996                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h3>",
2997                     m_level); 
2998               }
2999               goto endheader;
3000             }
3001             else if (tagId==HTML_H4 && g_token->endTag) // found </h4> tag
3002             {
3003               if (m_level!=4)
3004               {
3005                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h4>",
3006                     m_level); 
3007               }
3008               goto endheader;
3009             }
3010             else if (tagId==HTML_H5 && g_token->endTag) // found </h5> tag
3011             {
3012               if (m_level!=5)
3013               {
3014                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h5>",
3015                     m_level); 
3016               }
3017               goto endheader;
3018             }
3019             else if (tagId==HTML_H6 && g_token->endTag) // found </h6> tag
3020             {
3021               if (m_level!=6)
3022               {
3023                 warn_doc_error(g_fileName,doctokenizerYYlineno,"<h%d> ended with </h6>",
3024                     m_level); 
3025               }
3026               goto endheader;
3027             }
3028             else if (tagId==HTML_A)
3029             {
3030               if (!g_token->endTag)
3031               {
3032                 handleAHref(this,m_children,g_token->attribs);
3033               }
3034             }
3035             else if (tagId==HTML_BR)
3036             {
3037               DocLineBreak *lb = new DocLineBreak(this);
3038               m_children.append(lb);
3039             }
3040             else
3041             {
3042               warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected html tag <%s%s> found within <h%d> context",
3043                   g_token->endTag?"/":"",qPrint(g_token->name),m_level);
3044             }
3045             
3046           }
3047           break;
3048         case TK_SYMBOL: 
3049           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
3050                qPrint(g_token->name));
3051           break;
3052         default:
3053           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
3054                 tokToString(tok));
3055           break;
3056       }
3057     }
3058   }
3059   if (tok==0)
3060   {
3061     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end of comment while inside"
3062            " <h%d> tag\n",m_level); 
3063   }
3064 endheader:
3065   handlePendingStyleCommands(this,m_children);
3066   DBG(("DocHtmlHeader::parse() end\n"));
3067   DocNode *n=g_nodeStack.pop();
3068   ASSERT(n==this);
3069   return retval;
3070 }
3071
3072 //---------------------------------------------------------------------------
3073
3074 int DocHRef::parse()
3075 {
3076   int retval=RetVal_OK;
3077   g_nodeStack.push(this);
3078   DBG(("DocHRef::parse() start\n"));
3079
3080   int tok;
3081   while ((tok=doctokenizerYYlex()))
3082   {
3083     if (!defaultHandleToken(this,tok,m_children))
3084     {
3085       switch (tok)
3086       {
3087         case TK_COMMAND: 
3088           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a <a>..</a> block",
3089                qPrint(g_token->name));
3090           break;
3091         case TK_SYMBOL: 
3092           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
3093                qPrint(g_token->name));
3094           break;
3095         case TK_HTMLTAG:
3096
3097           {
3098             int tagId=Mappers::htmlTagMapper->map(g_token->name);
3099             if (tagId==HTML_A && g_token->endTag) // found </a> tag
3100             {
3101               goto endhref;
3102             }
3103             else
3104             {
3105               warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected html tag <%s%s> found within <a href=...> context",
3106                   g_token->endTag?"/":"",qPrint(g_token->name),doctokenizerYYlineno);
3107             }
3108           }
3109           break;
3110         default:
3111           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
3112                 tokToString(tok),doctokenizerYYlineno);
3113           break;
3114       }
3115     }
3116   }
3117   if (tok==0)
3118   {
3119     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end of comment while inside"
3120            " <a href=...> tag",doctokenizerYYlineno); 
3121   }
3122 endhref:
3123   handlePendingStyleCommands(this,m_children);
3124   DBG(("DocHRef::parse() end\n"));
3125   DocNode *n=g_nodeStack.pop();
3126   ASSERT(n==this);
3127   return retval;
3128 }
3129
3130 //---------------------------------------------------------------------------
3131
3132 int DocInternal::parse(int level)
3133 {
3134   int retval=RetVal_OK;
3135   g_nodeStack.push(this);
3136   DBG(("DocInternal::parse() start\n"));
3137
3138   // first parse any number of paragraphs
3139   bool isFirst=TRUE;
3140   DocPara *lastPar=0;
3141   do
3142   {
3143     DocPara *par = new DocPara(this);
3144     if (isFirst) { par->markFirst(); isFirst=FALSE; }
3145     retval=par->parse();
3146     if (!par->isEmpty()) 
3147     {
3148       m_children.append(par);
3149       lastPar=par;
3150     }
3151     else
3152     {
3153       delete par;
3154     }
3155     if (retval==TK_LISTITEM)
3156     {
3157       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found",doctokenizerYYlineno);
3158     }
3159   } while (retval!=0 && 
3160            retval!=RetVal_Section &&
3161            retval!=RetVal_Subsection &&
3162            retval!=RetVal_Subsubsection &&
3163            retval!=RetVal_Paragraph &&
3164            retval!=RetVal_EndInternal
3165           );
3166   if (lastPar) lastPar->markLast();
3167
3168   // then parse any number of level-n sections
3169   while ((level==1 && retval==RetVal_Section) || 
3170          (level==2 && retval==RetVal_Subsection) ||
3171          (level==3 && retval==RetVal_Subsubsection) ||
3172          (level==4 && retval==RetVal_Paragraph)
3173         )
3174   {
3175     DocSection *s=new DocSection(this,
3176         QMIN(level+Doxygen::subpageNestingLevel,5),g_token->sectionId);
3177     m_children.append(s);
3178     retval = s->parse();
3179   }
3180
3181   if (retval==RetVal_Internal)
3182   {
3183     warn_doc_error(g_fileName,doctokenizerYYlineno,"\\internal command found inside internal section");
3184   }
3185
3186   DBG(("DocInternal::parse() end: retval=%x\n",retval));
3187   DocNode *n=g_nodeStack.pop();
3188   ASSERT(n==this);
3189   return retval;
3190 }
3191
3192 //---------------------------------------------------------------------------
3193
3194 int DocIndexEntry::parse()
3195 {
3196   int retval=RetVal_OK;
3197   g_nodeStack.push(this);
3198   DBG(("DocIndexEntry::parse() start\n"));
3199   int tok=doctokenizerYYlex();
3200   if (tok!=TK_WHITESPACE)
3201   {
3202     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\addindex command");
3203     goto endindexentry;
3204   }
3205   doctokenizerYYsetStateTitle();
3206   m_entry="";
3207   while ((tok=doctokenizerYYlex()))
3208   {
3209     switch (tok)
3210     {
3211       case TK_WHITESPACE:
3212         m_entry+=" ";
3213         break;
3214       case TK_WORD: 
3215       case TK_LNKWORD: 
3216         m_entry+=g_token->name;
3217         break;
3218       case TK_SYMBOL:
3219         {
3220           DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name);
3221           switch (s)
3222           {
3223             case DocSymbol::Sym_BSlash:  m_entry+='\\'; break;
3224             case DocSymbol::Sym_At:      m_entry+='@';  break;
3225             case DocSymbol::Sym_Less:    m_entry+='<';  break;
3226             case DocSymbol::Sym_Greater: m_entry+='>';  break;
3227             case DocSymbol::Sym_Amp:     m_entry+='&';  break;
3228             case DocSymbol::Sym_Dollar:  m_entry+='$';  break;
3229             case DocSymbol::Sym_Hash:    m_entry+='#';  break;
3230             case DocSymbol::Sym_Percent: m_entry+='%';  break;
3231             case DocSymbol::Sym_apos:    m_entry+='\''; break;
3232             case DocSymbol::Sym_Quot:    m_entry+='"';  break;
3233             case DocSymbol::Sym_lsquo:   m_entry+='`';  break;
3234             case DocSymbol::Sym_rsquo:   m_entry+='\'';  break;
3235             case DocSymbol::Sym_ldquo:   m_entry+="``";  break;
3236             case DocSymbol::Sym_rdquo:   m_entry+="''";  break;
3237             case DocSymbol::Sym_ndash:   m_entry+="--";  break;
3238             case DocSymbol::Sym_mdash:   m_entry+="---";  break;
3239             default:
3240               warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected symbol found as argument of \\addindex");
3241               break;
3242           }
3243         }
3244         break;
3245     case TK_COMMAND: 
3246       switch (Mappers::cmdMapper->map(g_token->name))
3247       {
3248         case CMD_BSLASH:  m_entry+='\\'; break;
3249         case CMD_AT:      m_entry+='@';  break;
3250         case CMD_LESS:    m_entry+='<';  break;
3251         case CMD_GREATER: m_entry+='>';  break;
3252         case CMD_AMP:     m_entry+='&';  break;
3253         case CMD_DOLLAR:  m_entry+='$';  break;
3254         case CMD_HASH:    m_entry+='#';  break;
3255         case CMD_DCOLON:  m_entry+="::"; break;
3256         case CMD_PERCENT: m_entry+='%';  break;
3257         case CMD_NDASH:   m_entry+="--";  break;
3258         case CMD_MDASH:   m_entry+="---";  break;
3259         case CMD_QUOTE:   m_entry+='"';  break;
3260         case CMD_PUNT:    m_entry+='.';  break;
3261         case CMD_PLUS:    m_entry+='+';  break;
3262         case CMD_MINUS:   m_entry+='-';  break;
3263         default:
3264           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected command %s found as argument of \\addindex",
3265                     qPrint(g_token->name));
3266           break;
3267       }
3268       break;
3269       default:
3270         warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
3271             tokToString(tok));
3272         break;
3273     }
3274   }
3275   doctokenizerYYsetStatePara();
3276   m_entry = m_entry.stripWhiteSpace();
3277 endindexentry:
3278   DBG(("DocIndexEntry::parse() end retval=%x\n",retval));
3279   DocNode *n=g_nodeStack.pop();
3280   ASSERT(n==this);
3281   return retval;
3282 }
3283
3284 //---------------------------------------------------------------------------
3285
3286 DocHtmlCaption::DocHtmlCaption(DocNode *parent,const HtmlAttribList &attribs)
3287 {
3288   m_hasCaptionId = FALSE;
3289   HtmlAttribListIterator li(attribs);
3290   HtmlAttrib *opt;
3291   for (li.toFirst();(opt=li.current());++li)
3292   {
3293     if (opt->name=="id") // interpret id attribute as an anchor
3294     {
3295       SectionInfo *sec = Doxygen::sectionDict->find(opt->value);
3296       if (sec)
3297       {
3298         //printf("Found anchor %s\n",id.data());
3299         m_file   = sec->fileName;
3300         m_anchor = sec->label;
3301         m_hasCaptionId = TRUE;
3302         if (g_sectionDict && g_sectionDict->find(opt->value)==0)
3303         {
3304           //printf("Inserting in dictionary!\n");
3305           g_sectionDict->append(opt->value,sec);
3306         }
3307       }
3308       else
3309       {
3310         warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid caption id `%s'",qPrint(opt->value));
3311       }
3312     }
3313     else // copy attribute
3314     {
3315       m_attribs.append(new HtmlAttrib(*opt));
3316     }
3317   }
3318   m_parent = parent;
3319 }
3320
3321 int DocHtmlCaption::parse()
3322 {
3323   int retval=0;
3324   g_nodeStack.push(this);
3325   DBG(("DocHtmlCaption::parse() start\n"));
3326   int tok;
3327   while ((tok=doctokenizerYYlex()))
3328   {
3329     if (!defaultHandleToken(this,tok,m_children))
3330     {
3331       switch (tok)
3332       {
3333         case TK_COMMAND: 
3334           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a <caption> tag",
3335               qPrint(g_token->name));
3336           break;
3337         case TK_SYMBOL: 
3338           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
3339               qPrint(g_token->name));
3340           break;
3341         case TK_HTMLTAG:
3342           {
3343             int tagId=Mappers::htmlTagMapper->map(g_token->name);
3344             if (tagId==HTML_CAPTION && g_token->endTag) // found </caption> tag
3345             {
3346               retval = RetVal_OK;
3347               goto endcaption;
3348             }
3349             else
3350             {
3351               warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected html tag <%s%s> found within <caption> context",
3352                   g_token->endTag?"/":"",qPrint(g_token->name));
3353             }
3354           }
3355           break;
3356         default:
3357           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
3358               tokToString(tok));
3359           break;
3360       }
3361     }
3362   }
3363   if (tok==0)
3364   {
3365     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end of comment while inside"
3366            " <caption> tag",doctokenizerYYlineno); 
3367   }
3368 endcaption:
3369   handlePendingStyleCommands(this,m_children);
3370   DBG(("DocHtmlCaption::parse() end\n"));
3371   DocNode *n=g_nodeStack.pop();
3372   ASSERT(n==this);
3373   return retval;
3374 }
3375
3376 //---------------------------------------------------------------------------
3377
3378 int DocHtmlCell::parse()
3379 {
3380   int retval=RetVal_OK;
3381   g_nodeStack.push(this);
3382   DBG(("DocHtmlCell::parse() start\n"));
3383
3384   // parse one or more paragraphs
3385   bool isFirst=TRUE;
3386   DocPara *par=0;
3387   do
3388   {
3389     par = new DocPara(this);
3390     if (isFirst) { par->markFirst(); isFirst=FALSE; }
3391     m_children.append(par);
3392     retval=par->parse();
3393     if (retval==TK_HTMLTAG)
3394     {
3395       int tagId=Mappers::htmlTagMapper->map(g_token->name);
3396       if (tagId==HTML_TD && g_token->endTag) // found </dt> tag
3397       {
3398         retval=TK_NEWPARA; // ignore the tag
3399       }
3400       else if (tagId==HTML_TH && g_token->endTag) // found </th> tag
3401       {
3402         retval=TK_NEWPARA; // ignore the tag
3403       }
3404     }
3405   }
3406   while (retval==TK_NEWPARA);
3407   if (par) par->markLast();
3408
3409   DBG(("DocHtmlCell::parse() end\n"));
3410   DocNode *n=g_nodeStack.pop();
3411   ASSERT(n==this);
3412   return retval;
3413 }
3414
3415 int DocHtmlCell::parseXml()
3416 {
3417   int retval=RetVal_OK;
3418   g_nodeStack.push(this);
3419   DBG(("DocHtmlCell::parseXml() start\n"));
3420
3421   // parse one or more paragraphs
3422   bool isFirst=TRUE;
3423   DocPara *par=0;
3424   do
3425   {
3426     par = new DocPara(this);
3427     if (isFirst) { par->markFirst(); isFirst=FALSE; }
3428     m_children.append(par);
3429     retval=par->parse();
3430     if (retval==TK_HTMLTAG)
3431     {
3432       int tagId=Mappers::htmlTagMapper->map(g_token->name);
3433       if (tagId==XML_ITEM && g_token->endTag) // found </item> tag
3434       {
3435         retval=TK_NEWPARA; // ignore the tag
3436       }
3437       else if (tagId==XML_DESCRIPTION && g_token->endTag) // found </description> tag
3438       {
3439         retval=TK_NEWPARA; // ignore the tag
3440       }
3441     }
3442   }
3443   while (retval==TK_NEWPARA);
3444   if (par) par->markLast();
3445
3446   DBG(("DocHtmlCell::parseXml() end\n"));
3447   DocNode *n=g_nodeStack.pop();
3448   ASSERT(n==this);
3449   return retval;
3450 }
3451
3452 int DocHtmlCell::rowSpan() const
3453 {
3454   int retval = 0;
3455   HtmlAttribList attrs = attribs();
3456   uint i;
3457   for (i=0; i<attrs.count(); ++i) 
3458   {
3459     if (attrs.at(i)->name.lower()=="rowspan")
3460     {
3461       retval = attrs.at(i)->value.toInt();
3462       break;
3463     }
3464   }
3465   return retval;
3466 }
3467
3468 int DocHtmlCell::colSpan() const
3469 {
3470   int retval = 1;
3471   HtmlAttribList attrs = attribs();
3472   uint i;
3473   for (i=0; i<attrs.count(); ++i) 
3474   {
3475     if (attrs.at(i)->name.lower()=="colspan")
3476     {
3477       retval = QMAX(1,attrs.at(i)->value.toInt());
3478       break;
3479     }
3480   }
3481   return retval;
3482 }
3483
3484 DocHtmlCell::Alignment DocHtmlCell::alignment() const
3485 {
3486   HtmlAttribList attrs = attribs();
3487   uint i;
3488   for (i=0; i<attrs.count(); ++i) 
3489   {
3490     if (attrs.at(i)->name.lower()=="align")
3491     {
3492       if (attrs.at(i)->value.lower()=="center") 
3493         return Center;
3494       else if (attrs.at(i)->value.lower()=="right") 
3495         return Right;
3496       else return Left;
3497     }
3498   }
3499   return Left;
3500 }
3501
3502
3503 //---------------------------------------------------------------------------
3504
3505 int DocHtmlRow::parse()
3506 {
3507   int retval=RetVal_OK;
3508   g_nodeStack.push(this);
3509   DBG(("DocHtmlRow::parse() start\n"));
3510
3511   bool isHeading=FALSE;
3512   bool isFirst=TRUE;
3513   DocHtmlCell *cell=0;
3514
3515   // get next token
3516   int tok=doctokenizerYYlex();
3517   // skip whitespace
3518   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
3519   // should find a html tag now
3520   if (tok==TK_HTMLTAG)
3521   {
3522     int tagId=Mappers::htmlTagMapper->map(g_token->name);
3523     if (tagId==HTML_TD && !g_token->endTag) // found <td> tag
3524     {
3525     }
3526     else if (tagId==HTML_TH && !g_token->endTag) // found <th> tag
3527     {
3528       isHeading=TRUE;
3529     }
3530     else // found some other tag
3531     {
3532       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <td> or <th> tag but "
3533           "found <%s> instead!",qPrint(g_token->name));
3534       doctokenizerYYpushBackHtmlTag(g_token->name);
3535       goto endrow;
3536     }
3537   }
3538   else if (tok==0) // premature end of comment
3539   {
3540     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
3541         " for a html description title");
3542     goto endrow;
3543   }
3544   else // token other than html token
3545   {
3546     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <td> or <th> tag but found %s token instead!",
3547         tokToString(tok));
3548     goto endrow;
3549   }
3550
3551   // parse one or more cells
3552   do
3553   {
3554     cell=new DocHtmlCell(this,g_token->attribs,isHeading);
3555     cell->markFirst(isFirst);
3556     isFirst=FALSE;
3557     m_children.append(cell);
3558     retval=cell->parse();
3559     isHeading = retval==RetVal_TableHCell;
3560   }
3561   while (retval==RetVal_TableCell || retval==RetVal_TableHCell);
3562   if (cell) cell->markLast(TRUE);
3563
3564 endrow:
3565   DBG(("DocHtmlRow::parse() end\n"));
3566   DocNode *n=g_nodeStack.pop();
3567   ASSERT(n==this);
3568   return retval;
3569 }
3570
3571 int DocHtmlRow::parseXml(bool isHeading)
3572 {
3573   int retval=RetVal_OK;
3574   g_nodeStack.push(this);
3575   DBG(("DocHtmlRow::parseXml() start\n"));
3576
3577   bool isFirst=TRUE;
3578   DocHtmlCell *cell=0;
3579
3580   // get next token
3581   int tok=doctokenizerYYlex();
3582   // skip whitespace
3583   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
3584   // should find a html tag now
3585   if (tok==TK_HTMLTAG)
3586   {
3587     int tagId=Mappers::htmlTagMapper->map(g_token->name);
3588     if (tagId==XML_TERM && !g_token->endTag) // found <term> tag
3589     {
3590     }
3591     else if (tagId==XML_DESCRIPTION && !g_token->endTag) // found <description> tag
3592     {
3593     }
3594     else // found some other tag
3595     {
3596       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <term> or <description> tag but "
3597           "found <%s> instead!",qPrint(g_token->name));
3598       doctokenizerYYpushBackHtmlTag(g_token->name);
3599       goto endrow;
3600     }
3601   }
3602   else if (tok==0) // premature end of comment
3603   {
3604     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
3605         " for a html description title");
3606     goto endrow;
3607   }
3608   else // token other than html token
3609   {
3610     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <td> or <th> tag but found %s token instead!",
3611         tokToString(tok));
3612     goto endrow;
3613   }
3614
3615   do
3616   {
3617     cell=new DocHtmlCell(this,g_token->attribs,isHeading);
3618     cell->markFirst(isFirst);
3619     isFirst=FALSE;
3620     m_children.append(cell);
3621     retval=cell->parseXml();
3622   }
3623   while (retval==RetVal_TableCell || retval==RetVal_TableHCell);
3624   if (cell) cell->markLast(TRUE);
3625
3626 endrow:
3627   DBG(("DocHtmlRow::parseXml() end\n"));
3628   DocNode *n=g_nodeStack.pop();
3629   ASSERT(n==this);
3630   return retval;
3631 }
3632
3633 //---------------------------------------------------------------------------
3634
3635 int DocHtmlTable::parse()
3636 {
3637   int retval=RetVal_OK;
3638   g_nodeStack.push(this);
3639   DBG(("DocHtmlTable::parse() start\n"));
3640   
3641 getrow:
3642   // get next token
3643   int tok=doctokenizerYYlex();
3644   // skip whitespace
3645   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
3646   // should find a html tag now
3647   if (tok==TK_HTMLTAG)
3648   {
3649     int tagId=Mappers::htmlTagMapper->map(g_token->name);
3650     if (tagId==HTML_TR && !g_token->endTag) // found <tr> tag
3651     {
3652       // no caption, just rows
3653       retval=RetVal_TableRow;
3654     }
3655     else if (tagId==HTML_CAPTION && !g_token->endTag) // found <caption> tag
3656     {
3657       if (m_caption)
3658       {
3659         warn_doc_error(g_fileName,doctokenizerYYlineno,"table already has a caption, found another one");
3660       }
3661       else
3662       {
3663         m_caption = new DocHtmlCaption(this,g_token->attribs);
3664         retval=m_caption->parse();
3665
3666         if (retval==RetVal_OK) // caption was parsed ok
3667         {
3668           goto getrow;
3669         }
3670       }
3671     }
3672     else // found wrong token
3673     {
3674       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <tr> or <caption> tag but "
3675           "found <%s%s> instead!", g_token->endTag ? "/" : "", qPrint(g_token->name));
3676     }
3677   }
3678   else if (tok==0) // premature end of comment
3679   {
3680       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
3681           " for a <tr> or <caption> tag");
3682   }
3683   else // token other than html token
3684   {
3685     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <tr> tag but found %s token instead!",
3686         tokToString(tok));
3687   }
3688        
3689   // parse one or more rows
3690   while (retval==RetVal_TableRow)
3691   {
3692     DocHtmlRow *tr=new DocHtmlRow(this,g_token->attribs);
3693     m_children.append(tr);
3694     retval=tr->parse();
3695   } 
3696
3697   computeTableGrid();
3698
3699   DBG(("DocHtmlTable::parse() end\n"));
3700   DocNode *n=g_nodeStack.pop();
3701   ASSERT(n==this);
3702   return retval==RetVal_EndTable ? RetVal_OK : retval;
3703 }
3704
3705 int DocHtmlTable::parseXml()
3706 {
3707   int retval=RetVal_OK;
3708   g_nodeStack.push(this);
3709   DBG(("DocHtmlTable::parseXml() start\n"));
3710   
3711   // get next token
3712   int tok=doctokenizerYYlex();
3713   // skip whitespace
3714   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
3715   // should find a html tag now
3716   int tagId=0;
3717   bool isHeader=FALSE;
3718   if (tok==TK_HTMLTAG)
3719   {
3720     tagId=Mappers::htmlTagMapper->map(g_token->name);
3721     if (tagId==XML_ITEM && !g_token->endTag) // found <item> tag
3722     {
3723       retval=RetVal_TableRow;
3724     }
3725     if (tagId==XML_LISTHEADER && !g_token->endTag) // found <listheader> tag
3726     {
3727       retval=RetVal_TableRow;
3728       isHeader=TRUE;
3729     }
3730   }
3731
3732   // parse one or more rows
3733   while (retval==RetVal_TableRow)
3734   {
3735     DocHtmlRow *tr=new DocHtmlRow(this,g_token->attribs);
3736     m_children.append(tr);
3737     retval=tr->parseXml(isHeader);
3738     isHeader=FALSE;
3739   } 
3740
3741   computeTableGrid();
3742
3743   DBG(("DocHtmlTable::parseXml() end\n"));
3744   DocNode *n=g_nodeStack.pop();
3745   ASSERT(n==this);
3746   tagId=Mappers::htmlTagMapper->map(g_token->name);
3747   return tagId==XML_LIST && g_token->endTag ? RetVal_OK : retval;
3748 }
3749
3750 /** Helper class to compute the grid for an HTML style table */
3751 struct ActiveRowSpan
3752 {
3753   ActiveRowSpan(int rows,int col) : rowsLeft(rows), column(col) {}
3754   int rowsLeft;
3755   int column;  
3756 };
3757
3758 /** List of ActiveRowSpan classes. */
3759 typedef QList<ActiveRowSpan> RowSpanList;
3760
3761 /** determines the location of all cells in a grid, resolving row and
3762     column spans. For each the total number of visible cells is computed,
3763     and the total number of visible columns over all rows is stored.
3764  */
3765 void DocHtmlTable::computeTableGrid()
3766 {
3767   //printf("computeTableGrid()\n");
3768   RowSpanList rowSpans;
3769   rowSpans.setAutoDelete(TRUE);
3770   int maxCols=0;
3771   int rowIdx=1;
3772   QListIterator<DocNode> li(children());
3773   DocNode *rowNode;
3774   for (li.toFirst();(rowNode=li.current());++li)
3775   {
3776     int colIdx=1;
3777     int cells=0;
3778     if (rowNode->kind()==DocNode::Kind_HtmlRow)
3779     {
3780       uint i;
3781       DocHtmlRow *row = (DocHtmlRow*)rowNode;
3782       QListIterator<DocNode> rli(row->children());
3783       DocNode *cellNode;
3784       for (rli.toFirst();(cellNode=rli.current());++rli)
3785       {
3786         if (cellNode->kind()==DocNode::Kind_HtmlCell)
3787         {
3788           DocHtmlCell *cell = (DocHtmlCell*)cellNode;
3789           int rs = cell->rowSpan();
3790           int cs = cell->colSpan();
3791
3792           for (i=0;i<rowSpans.count();i++)
3793           {
3794             if (rowSpans.at(i)->rowsLeft>0 && 
3795                 rowSpans.at(i)->column==colIdx) 
3796             {
3797               colIdx=rowSpans.at(i)->column+1;
3798               cells++;
3799             }
3800           }
3801           if (rs>0) rowSpans.append(new ActiveRowSpan(rs,colIdx));
3802           //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
3803           cell->setRowIndex(rowIdx);
3804           cell->setColumnIndex(colIdx);
3805           colIdx+=cs; 
3806           cells++;
3807         }
3808       }
3809       for (i=0;i<rowSpans.count();i++)
3810       {
3811         if (rowSpans.at(i)->rowsLeft>0) rowSpans.at(i)->rowsLeft--;
3812       }
3813       row->setVisibleCells(cells);
3814       row->setRowIndex(rowIdx);
3815       rowIdx++;
3816     }
3817     if (colIdx-1>maxCols) maxCols=colIdx-1;
3818   }
3819   m_numCols = maxCols;
3820 }
3821
3822 void DocHtmlTable::accept(DocVisitor *v) 
3823
3824   v->visitPre(this); 
3825   // for HTML output we put the caption first
3826   //if (m_caption && v->id()==DocVisitor_Html) m_caption->accept(v);
3827   // doxygen 1.8.11: always put the caption first
3828   if (m_caption) m_caption->accept(v);
3829   QListIterator<DocNode> cli(m_children);
3830   DocNode *n;
3831   for (cli.toFirst();(n=cli.current());++cli) n->accept(v);
3832   // for other output formats we put the caption last
3833   //if (m_caption && v->id()!=DocVisitor_Html) m_caption->accept(v);
3834   v->visitPost(this); 
3835 }
3836
3837 //---------------------------------------------------------------------------
3838
3839 int DocHtmlDescTitle::parse()
3840 {
3841   int retval=0;
3842   g_nodeStack.push(this);
3843   DBG(("DocHtmlDescTitle::parse() start\n"));
3844
3845   int tok;
3846   while ((tok=doctokenizerYYlex()))
3847   {
3848     if (!defaultHandleToken(this,tok,m_children))
3849     {
3850       switch (tok)
3851       {
3852         case TK_COMMAND: 
3853           {
3854             QCString cmdName=g_token->name;
3855             bool isJavaLink=FALSE;
3856             switch (Mappers::cmdMapper->map(cmdName))
3857             {
3858               case CMD_REF:
3859                 {
3860                   int tok=doctokenizerYYlex();
3861                   if (tok!=TK_WHITESPACE)
3862                   {
3863                     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
3864                         qPrint(g_token->name));
3865                   }
3866                   else
3867                   {
3868                     doctokenizerYYsetStateRef();
3869                     tok=doctokenizerYYlex(); // get the reference id
3870                     if (tok!=TK_WORD)
3871                     {
3872                       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
3873                           tokToString(tok),qPrint(cmdName));
3874                     }
3875                     else
3876                     {
3877                       DocRef *ref = new DocRef(this,g_token->name,g_context);
3878                       m_children.append(ref);
3879                       ref->parse();
3880                     }
3881                     doctokenizerYYsetStatePara();
3882                   }
3883                 }
3884                 break;
3885               case CMD_JAVALINK:
3886                 isJavaLink=TRUE;
3887                 // fall through
3888               case CMD_LINK:
3889                 {
3890                   int tok=doctokenizerYYlex();
3891                   if (tok!=TK_WHITESPACE)
3892                   {
3893                     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
3894                         qPrint(cmdName));
3895                   }
3896                   else
3897                   {
3898                     doctokenizerYYsetStateLink();
3899                     tok=doctokenizerYYlex();
3900                     if (tok!=TK_WORD)
3901                     {
3902                       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
3903                           tokToString(tok),qPrint(cmdName));
3904                     }
3905                     else
3906                     {
3907                       doctokenizerYYsetStatePara();
3908                       DocLink *lnk = new DocLink(this,g_token->name);
3909                       m_children.append(lnk);
3910                       QCString leftOver = lnk->parse(isJavaLink);
3911                       if (!leftOver.isEmpty())
3912                       {
3913                         m_children.append(new DocWord(this,leftOver));
3914                       }
3915                     }
3916                   }
3917                 }
3918
3919                 break;
3920               default:
3921                 warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a <dt> tag",
3922                                qPrint(g_token->name));
3923             }
3924           }
3925           break;
3926         case TK_SYMBOL: 
3927           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
3928               qPrint(g_token->name));
3929           break;
3930         case TK_HTMLTAG:
3931           {
3932             int tagId=Mappers::htmlTagMapper->map(g_token->name);
3933             if (tagId==HTML_DD && !g_token->endTag) // found <dd> tag
3934             {
3935               retval = RetVal_DescData;
3936               goto endtitle;
3937             }
3938             else if (tagId==HTML_DT && g_token->endTag)
3939             {
3940               // ignore </dt> tag.
3941             }
3942             else if (tagId==HTML_DT)
3943             {
3944               // missing <dt> tag.
3945               retval = RetVal_DescTitle;
3946               goto endtitle;
3947             }
3948             else if (tagId==HTML_DL && g_token->endTag)
3949             {
3950               retval=RetVal_EndDesc;
3951               goto endtitle;
3952             }
3953             else if (tagId==HTML_A)
3954             {
3955               if (!g_token->endTag)
3956               {
3957                 handleAHref(this,m_children,g_token->attribs);
3958               }
3959             }
3960             else
3961             {
3962               warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected html tag <%s%s> found within <dt> context",
3963                   g_token->endTag?"/":"",qPrint(g_token->name));
3964             }
3965           }
3966           break;
3967         default:
3968           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
3969               tokToString(tok));
3970           break;
3971       }
3972     }
3973   }
3974   if (tok==0)
3975   {
3976     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end of comment while inside"
3977         " <dt> tag"); 
3978   }
3979 endtitle:
3980   handlePendingStyleCommands(this,m_children);
3981   DBG(("DocHtmlDescTitle::parse() end\n"));
3982   DocNode *n=g_nodeStack.pop();
3983   ASSERT(n==this);
3984   return retval;
3985 }
3986
3987 //---------------------------------------------------------------------------
3988
3989 int DocHtmlDescData::parse()
3990 {
3991   m_attribs = g_token->attribs;
3992   int retval=0;
3993   g_nodeStack.push(this);
3994   DBG(("DocHtmlDescData::parse() start\n"));
3995
3996   bool isFirst=TRUE;
3997   DocPara *par=0;
3998   do
3999   {
4000     par = new DocPara(this);
4001     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4002     m_children.append(par);
4003     retval=par->parse();
4004   }
4005   while (retval==TK_NEWPARA);
4006   if (par) par->markLast();
4007   
4008   DBG(("DocHtmlDescData::parse() end\n"));
4009   DocNode *n=g_nodeStack.pop();
4010   ASSERT(n==this);
4011   return retval;
4012 }
4013
4014 //---------------------------------------------------------------------------
4015
4016 int DocHtmlDescList::parse()
4017 {
4018   int retval=RetVal_OK;
4019   g_nodeStack.push(this);
4020   DBG(("DocHtmlDescList::parse() start\n"));
4021
4022   // get next token
4023   int tok=doctokenizerYYlex();
4024   // skip whitespace
4025   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
4026   // should find a html tag now
4027   if (tok==TK_HTMLTAG)
4028   {
4029     int tagId=Mappers::htmlTagMapper->map(g_token->name);
4030     if (tagId==HTML_DT && !g_token->endTag) // found <dt> tag
4031     {
4032       // continue
4033     }
4034     else // found some other tag
4035     {
4036       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <dt> tag but "
4037           "found <%s> instead!",qPrint(g_token->name));
4038       doctokenizerYYpushBackHtmlTag(g_token->name);
4039       goto enddesclist;
4040     }
4041   }
4042   else if (tok==0) // premature end of comment
4043   {
4044     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
4045         " for a html description title");
4046     goto enddesclist;
4047   }
4048   else // token other than html token
4049   {
4050     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <dt> tag but found %s token instead!",
4051         tokToString(tok));
4052     goto enddesclist;
4053   }
4054
4055   do
4056   {
4057     DocHtmlDescTitle *dt=new DocHtmlDescTitle(this,g_token->attribs);
4058     m_children.append(dt);
4059     DocHtmlDescData *dd=new DocHtmlDescData(this);
4060     m_children.append(dd);
4061     retval=dt->parse();
4062     if (retval==RetVal_DescData)
4063     {
4064       retval=dd->parse();
4065     }
4066     else if (retval!=RetVal_DescTitle)
4067     {
4068       // error
4069       break;
4070     }
4071   } while (retval==RetVal_DescTitle);
4072
4073   if (retval==0)
4074   {
4075     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while inside <dl> block");
4076   }
4077
4078 enddesclist:
4079
4080   DocNode *n=g_nodeStack.pop();
4081   ASSERT(n==this);
4082   DBG(("DocHtmlDescList::parse() end\n"));
4083   return retval==RetVal_EndDesc ? RetVal_OK : retval;
4084 }
4085
4086 //---------------------------------------------------------------------------
4087
4088 int DocHtmlListItem::parse()
4089 {
4090   DBG(("DocHtmlListItem::parse() start\n"));
4091   int retval=0;
4092   g_nodeStack.push(this);
4093
4094   // parse one or more paragraphs
4095   bool isFirst=TRUE;
4096   DocPara *par=0;
4097   do
4098   {
4099     par = new DocPara(this);
4100     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4101     m_children.append(par);
4102     retval=par->parse();
4103   }
4104   while (retval==TK_NEWPARA);
4105   if (par) par->markLast();
4106
4107   DocNode *n=g_nodeStack.pop();
4108   ASSERT(n==this);
4109   DBG(("DocHtmlListItem::parse() end retval=%x\n",retval));
4110   return retval;
4111 }
4112
4113 int DocHtmlListItem::parseXml()
4114 {
4115   DBG(("DocHtmlListItem::parseXml() start\n"));
4116   int retval=0;
4117   g_nodeStack.push(this);
4118
4119   // parse one or more paragraphs
4120   bool isFirst=TRUE;
4121   DocPara *par=0;
4122   do
4123   {
4124     par = new DocPara(this);
4125     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4126     m_children.append(par);
4127     retval=par->parse();
4128     if (retval==0) break;
4129
4130     //printf("new item: retval=%x g_token->name=%s g_token->endTag=%d\n",
4131     //    retval,qPrint(g_token->name),g_token->endTag);
4132     if (retval==RetVal_ListItem)
4133     {
4134       break;
4135     }
4136   }
4137   while (retval!=RetVal_CloseXml);
4138
4139   if (par) par->markLast();
4140
4141   DocNode *n=g_nodeStack.pop();
4142   ASSERT(n==this);
4143   DBG(("DocHtmlListItem::parseXml() end retval=%x\n",retval));
4144   return retval;
4145 }
4146
4147 //---------------------------------------------------------------------------
4148
4149 int DocHtmlList::parse()
4150 {
4151   DBG(("DocHtmlList::parse() start\n"));
4152   int retval=RetVal_OK;
4153   int num=1;
4154   g_nodeStack.push(this);
4155
4156   // get next token
4157   int tok=doctokenizerYYlex();
4158   // skip whitespace and paragraph breaks
4159   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
4160   // should find a html tag now
4161   if (tok==TK_HTMLTAG)
4162   {
4163     int tagId=Mappers::htmlTagMapper->map(g_token->name);
4164     if (tagId==HTML_LI && !g_token->endTag) // found <li> tag
4165     {
4166       // ok, we can go on.
4167     }
4168     else if (((m_type==Unordered && tagId==HTML_UL) ||
4169               (m_type==Ordered   && tagId==HTML_OL)
4170              ) && g_token->endTag
4171             ) // found empty list
4172     {
4173       // add dummy item to obtain valid HTML
4174       m_children.append(new DocHtmlListItem(this,HtmlAttribList(),1));
4175       warn_doc_error(g_fileName,doctokenizerYYlineno,"empty list!");
4176       retval = RetVal_EndList;
4177       goto endlist;
4178     }
4179     else // found some other tag
4180     {
4181       // add dummy item to obtain valid HTML
4182       m_children.append(new DocHtmlListItem(this,HtmlAttribList(),1));
4183       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <li> tag but "
4184           "found <%s%s> instead!",g_token->endTag?"/":"",qPrint(g_token->name));
4185       doctokenizerYYpushBackHtmlTag(g_token->name);
4186       goto endlist;
4187     }
4188   }
4189   else if (tok==0) // premature end of comment
4190   {
4191     // add dummy item to obtain valid HTML
4192     m_children.append(new DocHtmlListItem(this,HtmlAttribList(),1));
4193     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
4194         " for a html list item");
4195     goto endlist;
4196   }
4197   else // token other than html token
4198   {
4199     // add dummy item to obtain valid HTML
4200     m_children.append(new DocHtmlListItem(this,HtmlAttribList(),1));
4201     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <li> tag but found %s token instead!",
4202         tokToString(tok));
4203     goto endlist;
4204   }
4205
4206   do
4207   {
4208     DocHtmlListItem *li=new DocHtmlListItem(this,g_token->attribs,num++);
4209     m_children.append(li);
4210     retval=li->parse();
4211   } while (retval==RetVal_ListItem);
4212   
4213   if (retval==0)
4214   {
4215     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while inside <%cl> block",
4216         m_type==Unordered ? 'u' : 'o');
4217   }
4218
4219 endlist:
4220   DBG(("DocHtmlList::parse() end retval=%x\n",retval));
4221   DocNode *n=g_nodeStack.pop();
4222   ASSERT(n==this);
4223   return retval==RetVal_EndList ? RetVal_OK : retval;
4224 }
4225
4226 int DocHtmlList::parseXml()
4227 {
4228   DBG(("DocHtmlList::parseXml() start\n"));
4229   int retval=RetVal_OK;
4230   int num=1;
4231   g_nodeStack.push(this);
4232
4233   // get next token
4234   int tok=doctokenizerYYlex();
4235   // skip whitespace and paragraph breaks
4236   while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
4237   // should find a html tag now
4238   if (tok==TK_HTMLTAG)
4239   {
4240     int tagId=Mappers::htmlTagMapper->map(g_token->name);
4241     //printf("g_token->name=%s g_token->endTag=%d\n",qPrint(g_token->name),g_token->endTag);
4242     if (tagId==XML_ITEM && !g_token->endTag) // found <item> tag
4243     {
4244       // ok, we can go on.
4245     }
4246     else // found some other tag
4247     {
4248       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <item> tag but "
4249           "found <%s> instead!",qPrint(g_token->name));
4250       doctokenizerYYpushBackHtmlTag(g_token->name);
4251       goto endlist;
4252     }
4253   }
4254   else if (tok==0) // premature end of comment
4255   {
4256     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while looking"
4257         " for a html list item");
4258     goto endlist;
4259   }
4260   else // token other than html token
4261   {
4262     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected <item> tag but found %s token instead!",
4263         tokToString(tok));
4264     goto endlist;
4265   }
4266
4267   do
4268   {
4269     DocHtmlListItem *li=new DocHtmlListItem(this,g_token->attribs,num++);
4270     m_children.append(li);
4271     retval=li->parseXml();
4272     if (retval==0) break;
4273     //printf("retval=%x g_token->name=%s\n",retval,qPrint(g_token->name));
4274   } while (retval==RetVal_ListItem);
4275   
4276   if (retval==0)
4277   {
4278     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment while inside <list type=\"%s\"> block",
4279         m_type==Unordered ? "bullet" : "number");
4280   }
4281
4282 endlist:
4283   DBG(("DocHtmlList::parseXml() end retval=%x\n",retval));
4284   DocNode *n=g_nodeStack.pop();
4285   ASSERT(n==this);
4286   return retval==RetVal_EndList || 
4287          (retval==RetVal_CloseXml || g_token->name=="list") ? 
4288          RetVal_OK : retval;
4289 }
4290
4291 //--------------------------------------------------------------------------
4292
4293 int DocHtmlBlockQuote::parse()
4294 {
4295   DBG(("DocHtmlBlockQuote::parse() start\n"));
4296   int retval=0;
4297   g_nodeStack.push(this);
4298
4299   // parse one or more paragraphs 
4300   bool isFirst=TRUE;
4301   DocPara *par=0;
4302   do
4303   {
4304     par = new DocPara(this);
4305     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4306     m_children.append(par);
4307     retval=par->parse();
4308   }
4309   while (retval==TK_NEWPARA);
4310   if (par) par->markLast();
4311
4312   DocNode *n=g_nodeStack.pop();
4313   ASSERT(n==this);
4314   DBG(("DocHtmlBlockQuote::parse() end retval=%x\n",retval));
4315   return (retval==RetVal_EndBlockQuote) ? RetVal_OK : retval;
4316 }
4317
4318 //---------------------------------------------------------------------------
4319
4320 int DocParBlock::parse()
4321 {
4322   DBG(("DocParBlock::parse() start\n"));
4323   int retval=0;
4324   g_nodeStack.push(this);
4325
4326   // parse one or more paragraphs 
4327   bool isFirst=TRUE;
4328   DocPara *par=0;
4329   do
4330   {
4331     par = new DocPara(this);
4332     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4333     m_children.append(par);
4334     retval=par->parse();
4335   }
4336   while (retval==TK_NEWPARA);
4337   if (par) par->markLast();
4338
4339   DocNode *n=g_nodeStack.pop();
4340   ASSERT(n==this);
4341   DBG(("DocParBlock::parse() end retval=%x\n",retval));
4342   return (retval==RetVal_EndBlockQuote) ? RetVal_OK : retval;
4343 }
4344
4345 //---------------------------------------------------------------------------
4346
4347 int DocSimpleListItem::parse()
4348 {
4349   g_nodeStack.push(this);
4350   int rv=m_paragraph->parse();
4351   m_paragraph->markFirst();
4352   m_paragraph->markLast();
4353   DocNode *n=g_nodeStack.pop();
4354   ASSERT(n==this);
4355   return rv;
4356 }
4357
4358 //--------------------------------------------------------------------------
4359
4360 int DocSimpleList::parse()
4361 {
4362   g_nodeStack.push(this);
4363   int rv;
4364   do
4365   {
4366     DocSimpleListItem *li=new DocSimpleListItem(this);
4367     m_children.append(li);
4368     rv=li->parse();
4369   } while (rv==RetVal_ListItem);
4370   DocNode *n=g_nodeStack.pop();
4371   ASSERT(n==this);
4372   return (rv!=TK_NEWPARA) ? rv : RetVal_OK;
4373 }
4374
4375 //--------------------------------------------------------------------------
4376
4377 DocAutoListItem::DocAutoListItem(DocNode *parent,int indent,int num) 
4378       : m_indent(indent), m_itemNum(num)
4379
4380   m_parent = parent; 
4381 }
4382
4383 int DocAutoListItem::parse()
4384 {
4385   int retval = RetVal_OK;
4386   g_nodeStack.push(this);
4387   
4388   // first parse any number of paragraphs
4389   bool isFirst=TRUE;
4390   DocPara *lastPar=0;
4391   do
4392   {
4393     DocPara *par = new DocPara(this);
4394     if (isFirst) { par->markFirst(); isFirst=FALSE; }
4395     retval=par->parse();
4396     if (!par->isEmpty()) 
4397     {
4398       m_children.append(par);
4399       if (lastPar) lastPar->markLast(FALSE);
4400       lastPar=par;
4401     }
4402     else
4403     {
4404       delete par;
4405     }
4406     // next paragraph should be more indented than the - marker to belong
4407     // to this item
4408   } while (retval==TK_NEWPARA && g_token->indent>m_indent);
4409   if (lastPar) lastPar->markLast();
4410
4411   DocNode *n=g_nodeStack.pop();
4412   ASSERT(n==this);
4413   //printf("DocAutoListItem: retval=%d indent=%d\n",retval,g_token->indent);
4414   return retval;
4415 }
4416
4417 //--------------------------------------------------------------------------
4418
4419 DocAutoList::DocAutoList(DocNode *parent,int indent,bool isEnumList,
4420                          int depth) : 
4421       m_indent(indent), m_isEnumList(isEnumList),
4422       m_depth(depth)
4423
4424   m_parent = parent; 
4425 }
4426
4427 int DocAutoList::parse()
4428 {
4429   int retval = RetVal_OK;
4430   int num=1;
4431   g_nodeStack.push(this);
4432   doctokenizerYYstartAutoList();
4433           // first item or sub list => create new list
4434   do
4435   {
4436     if (g_token->id!=-1) // explicitly numbered list
4437     {
4438       num=g_token->id;  // override num with real number given
4439     }
4440     DocAutoListItem *li = new DocAutoListItem(this,m_indent,num++);
4441     m_children.append(li);
4442     retval=li->parse();
4443     //printf("DocAutoList::parse(): retval=0x%x g_token->indent=%d m_indent=%d "
4444     //       "m_isEnumList=%d g_token->isEnumList=%d g_token->name=%s\n", 
4445     //       retval,g_token->indent,m_indent,m_isEnumList,g_token->isEnumList,
4446     //       g_token->name.data());
4447     //printf("num=%d g_token->id=%d\n",num,g_token->id);
4448   } 
4449   while (retval==TK_LISTITEM &&                // new list item
4450          m_indent==g_token->indent &&          // at same indent level
4451          m_isEnumList==g_token->isEnumList &&  // of the same kind
4452          (g_token->id==-1 || g_token->id>=num)  // increasing number (or no number)
4453         );
4454
4455   doctokenizerYYendAutoList();
4456   DocNode *n=g_nodeStack.pop();
4457   ASSERT(n==this);
4458   return retval;
4459 }
4460
4461 //--------------------------------------------------------------------------
4462
4463 void DocTitle::parse()
4464 {
4465   DBG(("DocTitle::parse() start\n"));
4466   g_nodeStack.push(this);
4467   doctokenizerYYsetStateTitle();
4468   int tok;
4469   while ((tok=doctokenizerYYlex()))
4470   {
4471     if (!defaultHandleToken(this,tok,m_children))
4472     {
4473       switch (tok)
4474       {
4475         case TK_COMMAND: 
4476           warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal command %s as part of a title section",
4477                qPrint(g_token->name));
4478           break;
4479         case TK_SYMBOL: 
4480           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
4481                qPrint(g_token->name));
4482           break;
4483         default:
4484           warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
4485                 tokToString(tok));
4486           break;
4487       }
4488     }
4489   }
4490   doctokenizerYYsetStatePara();
4491   handlePendingStyleCommands(this,m_children);
4492   DBG(("DocTitle::parse() end\n"));
4493   DocNode *n = g_nodeStack.pop();
4494   ASSERT(n==this);
4495 }
4496
4497 void DocTitle::parseFromString(const QCString &text)
4498 {
4499   m_children.append(new DocWord(this,text));
4500 }
4501
4502 //--------------------------------------------------------------------------
4503
4504 DocSimpleSect::DocSimpleSect(DocNode *parent,Type t) : 
4505      m_type(t)
4506
4507   m_parent = parent; 
4508   m_title=0; 
4509 }
4510
4511 DocSimpleSect::~DocSimpleSect()
4512
4513   delete m_title; 
4514 }
4515
4516 void DocSimpleSect::accept(DocVisitor *v)
4517 {
4518   v->visitPre(this);
4519   if (m_title) m_title->accept(v);
4520   QListIterator<DocNode> cli(m_children);
4521   DocNode *n;
4522   for (cli.toFirst();(n=cli.current());++cli) n->accept(v);
4523   v->visitPost(this);
4524 }
4525
4526 int DocSimpleSect::parse(bool userTitle,bool needsSeparator)
4527 {
4528   DBG(("DocSimpleSect::parse() start\n"));
4529   g_nodeStack.push(this);
4530
4531   // handle case for user defined title
4532   if (userTitle)
4533   {
4534     m_title = new DocTitle(this);
4535     m_title->parse();
4536   }
4537   
4538   // add new paragraph as child
4539   DocPara *par = new DocPara(this);
4540   if (m_children.isEmpty()) 
4541   {
4542     par->markFirst();
4543   }
4544   else
4545   {
4546     ASSERT(m_children.getLast()->kind()==DocNode::Kind_Para);
4547     ((DocPara *)m_children.getLast())->markLast(FALSE);
4548   }
4549   par->markLast();
4550   if (needsSeparator) m_children.append(new DocSimpleSectSep(this));
4551   m_children.append(par);
4552   
4553   // parse the contents of the paragraph
4554   int retval = par->parse();
4555
4556   DBG(("DocSimpleSect::parse() end retval=%d\n",retval));
4557   DocNode *n=g_nodeStack.pop();
4558   ASSERT(n==this);
4559   return retval; // 0==EOF, TK_NEWPARA, TK_LISTITEM, TK_ENDLIST, RetVal_SimpleSec
4560 }
4561
4562 int DocSimpleSect::parseRcs()
4563 {
4564   DBG(("DocSimpleSect::parseRcs() start\n"));
4565   g_nodeStack.push(this);
4566
4567   m_title = new DocTitle(this);
4568   m_title->parseFromString(g_token->name);
4569
4570   QCString text = g_token->text;
4571   docParserPushContext(); // this will create a new g_token
4572   internalValidatingParseDoc(this,m_children,text);
4573   docParserPopContext(); // this will restore the old g_token
4574
4575   DBG(("DocSimpleSect::parseRcs()\n"));
4576   DocNode *n=g_nodeStack.pop();
4577   ASSERT(n==this);
4578   return RetVal_OK; 
4579 }
4580
4581 int DocSimpleSect::parseXml()
4582 {
4583   DBG(("DocSimpleSect::parse() start\n"));
4584   g_nodeStack.push(this);
4585
4586   int retval = RetVal_OK;
4587   for (;;) 
4588   {
4589     // add new paragraph as child
4590     DocPara *par = new DocPara(this);
4591     if (m_children.isEmpty()) 
4592     {
4593       par->markFirst();
4594     }
4595     else
4596     {
4597       ASSERT(m_children.getLast()->kind()==DocNode::Kind_Para);
4598       ((DocPara *)m_children.getLast())->markLast(FALSE);
4599     }
4600     par->markLast();
4601     m_children.append(par);
4602
4603     // parse the contents of the paragraph
4604     retval = par->parse();
4605     if (retval == 0) break;
4606     if (retval == RetVal_CloseXml) 
4607     {
4608       retval = RetVal_OK;
4609       break;
4610     }
4611   }
4612   
4613   DBG(("DocSimpleSect::parseXml() end retval=%d\n",retval));
4614   DocNode *n=g_nodeStack.pop();
4615   ASSERT(n==this);
4616   return retval; 
4617 }
4618
4619 void DocSimpleSect::appendLinkWord(const QCString &word)
4620 {
4621   DocPara *p;
4622   if (m_children.isEmpty() || m_children.getLast()->kind()!=DocNode::Kind_Para)
4623   {
4624     p = new DocPara(this);
4625     m_children.append(p);
4626   }
4627   else
4628   {
4629     p = (DocPara *)m_children.getLast();
4630     
4631     // Comma-seperate <seealso> links.
4632     p->injectToken(TK_WORD,",");
4633     p->injectToken(TK_WHITESPACE," ");
4634   }
4635   
4636   g_inSeeBlock=TRUE;
4637   p->injectToken(TK_LNKWORD,word);
4638   g_inSeeBlock=FALSE;
4639 }
4640
4641 QCString DocSimpleSect::typeString() const
4642 {
4643   switch (m_type)
4644   {
4645     case Unknown:    break;
4646     case See:        return "see";
4647     case Return:     return "return";
4648     case Author:     // fall through
4649     case Authors:    return "author";
4650     case Version:    return "version";
4651     case Since:      return "since";
4652     case Date:       return "date";
4653     case Note:       return "note";
4654     case Warning:    return "warning";
4655     case Pre:        return "pre";
4656     case Post:       return "post";
4657     case Copyright:  return "copyright";
4658     case Invar:      return "invariant";
4659     case Remark:     return "remark";
4660     case Attention:  return "attention";
4661     case User:       return "user";
4662     case Rcs:        return "rcs";
4663   }
4664   return "unknown";
4665 }
4666
4667 //--------------------------------------------------------------------------
4668
4669 int DocParamList::parse(const QCString &cmdName)
4670 {
4671   int retval=RetVal_OK;
4672   DBG(("DocParamList::parse() start\n"));
4673   g_nodeStack.push(this);
4674   DocPara *par=0;
4675   QCString saveCmdName = cmdName;
4676
4677   int tok=doctokenizerYYlex();
4678   if (tok!=TK_WHITESPACE)
4679   {
4680     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
4681         qPrint(cmdName));
4682     retval=0;
4683     goto endparamlist;
4684   }
4685   doctokenizerYYsetStateParam();
4686   tok=doctokenizerYYlex();
4687   while (tok==TK_WORD) /* there is a parameter name */
4688   {
4689     if (m_type==DocParamSect::Param)
4690     {
4691       int typeSeparator = g_token->name.find('#'); // explicit type position
4692       if (typeSeparator!=-1)
4693       {
4694         handleParameterType(this,m_paramTypes,g_token->name.left(typeSeparator));
4695         g_token->name = g_token->name.mid(typeSeparator+1);
4696         g_hasParamCommand=TRUE;
4697         checkArgumentName(g_token->name,TRUE);
4698         ((DocParamSect*)parent())->m_hasTypeSpecifier=TRUE;
4699       }
4700       else
4701       {
4702         g_hasParamCommand=TRUE;
4703         checkArgumentName(g_token->name,TRUE);
4704       }
4705     }
4706     else if (m_type==DocParamSect::RetVal)
4707     {
4708       g_hasReturnCommand=TRUE;
4709       checkArgumentName(g_token->name,FALSE);
4710     }
4711     //m_params.append(g_token->name);
4712     handleLinkedWord(this,m_params);
4713     tok=doctokenizerYYlex();
4714   }
4715   doctokenizerYYsetStatePara();
4716   if (tok==0) /* premature end of comment block */
4717   {
4718     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
4719         "argument of command %s",qPrint(cmdName));
4720     retval=0;
4721     goto endparamlist;
4722   }
4723   if (tok!=TK_WHITESPACE) /* premature end of comment block */
4724   {
4725     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token in comment block while parsing the "
4726         "argument of command %s",qPrint(saveCmdName));
4727     retval=0;
4728     goto endparamlist;
4729   }
4730
4731   par = new DocPara(this);
4732   m_paragraphs.append(par);
4733   retval = par->parse();
4734   par->markFirst();
4735   par->markLast();
4736
4737 endparamlist:
4738   DBG(("DocParamList::parse() end retval=%d\n",retval));
4739   DocNode *n=g_nodeStack.pop();
4740   ASSERT(n==this);
4741   return retval;
4742 }
4743
4744 int DocParamList::parseXml(const QCString &paramName)
4745 {
4746   int retval=RetVal_OK;
4747   DBG(("DocParamList::parseXml() start\n"));
4748   g_nodeStack.push(this);
4749
4750   g_token->name = paramName;
4751   if (m_type==DocParamSect::Param)
4752   {
4753     g_hasParamCommand=TRUE;
4754     checkArgumentName(g_token->name,TRUE);
4755   }
4756   else if (m_type==DocParamSect::RetVal)
4757   {
4758     g_hasReturnCommand=TRUE;
4759     checkArgumentName(g_token->name,FALSE);
4760   }
4761   
4762   handleLinkedWord(this,m_params);
4763
4764   do
4765   {
4766     DocPara *par = new DocPara(this);
4767     retval = par->parse();
4768     if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace
4769                         // after </para> and before </param>
4770     {
4771       delete par;
4772       break;
4773     }
4774     else // append the paragraph to the list
4775     {
4776       if (m_paragraphs.isEmpty())
4777       {
4778         par->markFirst();
4779       }
4780       else
4781       {
4782         m_paragraphs.getLast()->markLast(FALSE);
4783       }
4784       par->markLast();
4785       m_paragraphs.append(par);
4786     }
4787
4788     if (retval == 0) break;
4789
4790   } while (retval==RetVal_CloseXml && 
4791            Mappers::htmlTagMapper->map(g_token->name)!=XML_PARAM &&
4792            Mappers::htmlTagMapper->map(g_token->name)!=XML_TYPEPARAM &&
4793            Mappers::htmlTagMapper->map(g_token->name)!=XML_EXCEPTION);
4794   
4795
4796   if (retval==0) /* premature end of comment block */
4797   {
4798     warn_doc_error(g_fileName,doctokenizerYYlineno,"unterminated param or exception tag");
4799   }
4800   else
4801   {
4802     retval=RetVal_OK;
4803   }
4804
4805
4806   DBG(("DocParamList::parse() end retval=%d\n",retval));
4807   DocNode *n=g_nodeStack.pop();
4808   ASSERT(n==this);
4809   return retval;
4810 }
4811
4812 //--------------------------------------------------------------------------
4813
4814 int DocParamSect::parse(const QCString &cmdName,bool xmlContext, Direction d)
4815 {
4816   int retval=RetVal_OK;
4817   DBG(("DocParamSect::parse() start\n"));
4818   g_nodeStack.push(this);
4819
4820   if (d!=Unspecified)
4821   {
4822     m_hasInOutSpecifier=TRUE;
4823   }
4824
4825   DocParamList *pl = new DocParamList(this,m_type,d);
4826   if (m_children.isEmpty())
4827   {
4828     pl->markFirst();
4829     pl->markLast();
4830   }
4831   else
4832   {
4833     ASSERT(m_children.getLast()->kind()==DocNode::Kind_ParamList);
4834     ((DocParamList *)m_children.getLast())->markLast(FALSE);
4835     pl->markLast();
4836   }
4837   m_children.append(pl);
4838   if (xmlContext)
4839   {
4840     retval = pl->parseXml(cmdName);
4841   }
4842   else
4843   {
4844     retval = pl->parse(cmdName);
4845   }
4846   if (retval==RetVal_EndParBlock)
4847   {
4848     retval = RetVal_OK;
4849   }
4850   
4851   DBG(("DocParamSect::parse() end retval=%d\n",retval));
4852   DocNode *n=g_nodeStack.pop();
4853   ASSERT(n==this);
4854   return retval;
4855 }
4856
4857 //--------------------------------------------------------------------------
4858
4859 int DocPara::handleSimpleSection(DocSimpleSect::Type t, bool xmlContext)
4860 {
4861   DocSimpleSect *ss=0;
4862   bool needsSeparator = FALSE;
4863   if (!m_children.isEmpty() &&                           // previous element
4864       m_children.getLast()->kind()==Kind_SimpleSect &&      // was a simple sect
4865       ((DocSimpleSect *)m_children.getLast())->type()==t && // of same type
4866       t!=DocSimpleSect::User)                            // but not user defined
4867   {
4868     // append to previous section
4869     ss=(DocSimpleSect *)m_children.getLast();
4870     needsSeparator = TRUE;
4871   }
4872   else // start new section
4873   {
4874     ss=new DocSimpleSect(this,t);
4875     m_children.append(ss);
4876   }
4877   int rv = RetVal_OK;
4878   if (xmlContext)
4879   {
4880     return ss->parseXml();
4881   }
4882   else
4883   {
4884     rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
4885   }
4886   return (rv!=TK_NEWPARA) ? rv : RetVal_OK;
4887 }
4888
4889 int DocPara::handleParamSection(const QCString &cmdName,
4890                                 DocParamSect::Type t,
4891                                 bool xmlContext=FALSE,
4892                                 int direction=DocParamSect::Unspecified)
4893 {
4894   DocParamSect *ps=0;
4895   if (!m_children.isEmpty() &&                        // previous element
4896       m_children.getLast()->kind()==Kind_ParamSect &&    // was a param sect
4897       ((DocParamSect *)m_children.getLast())->type()==t) // of same type
4898   {
4899     // append to previous section
4900     ps=(DocParamSect *)m_children.getLast();
4901   }
4902   else // start new section
4903   {
4904     ps=new DocParamSect(this,t);
4905     m_children.append(ps);
4906   }
4907   int rv=ps->parse(cmdName,xmlContext,(DocParamSect::Direction)direction);
4908   return (rv!=TK_NEWPARA) ? rv : RetVal_OK;
4909 }
4910
4911 void DocPara::handleCite()
4912 {
4913   // get the argument of the cite command.
4914   int tok=doctokenizerYYlex();
4915   if (tok!=TK_WHITESPACE)
4916   {
4917     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
4918         qPrint("cite"));
4919     return;
4920   }
4921   doctokenizerYYsetStateCite();
4922   tok=doctokenizerYYlex();
4923   if (tok==0)
4924   {
4925     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
4926         "argument of command %s\n", qPrint("cite"));
4927     return;
4928   }
4929   else if (tok!=TK_WORD && tok!=TK_LNKWORD)
4930   {
4931     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
4932         tokToString(tok),qPrint("cite"));
4933     return;
4934   }
4935   g_token->sectionId = g_token->name;
4936   DocCite *cite = new DocCite(this,g_token->name,g_context);
4937   m_children.append(cite);
4938   //cite->parse();
4939
4940   doctokenizerYYsetStatePara();
4941 }
4942
4943 int DocPara::handleXRefItem()
4944 {
4945   int retval=doctokenizerYYlex();
4946   ASSERT(retval==TK_WHITESPACE);
4947   doctokenizerYYsetStateXRefItem();
4948   retval=doctokenizerYYlex();
4949   if (retval==RetVal_OK)
4950   {
4951     DocXRefItem *ref = new DocXRefItem(this,g_token->id,g_token->name);
4952     if (ref->parse())
4953     {
4954       m_children.append(ref);
4955     }
4956     else 
4957     {
4958       delete ref;
4959     }
4960   }
4961   doctokenizerYYsetStatePara();
4962   return retval;
4963 }
4964
4965 void DocPara::handleIncludeOperator(const QCString &cmdName,DocIncOperator::Type t)
4966 {
4967   DBG(("handleIncludeOperator(%s)\n",qPrint(cmdName)));
4968   int tok=doctokenizerYYlex();
4969   if (tok!=TK_WHITESPACE)
4970   {
4971     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
4972         qPrint(cmdName));
4973     return;
4974   }
4975   doctokenizerYYsetStatePattern();
4976   tok=doctokenizerYYlex();
4977   doctokenizerYYsetStatePara();
4978   if (tok==0)
4979   {
4980     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
4981         "argument of command %s", qPrint(cmdName));
4982     return;
4983   }
4984   else if (tok!=TK_WORD)
4985   {
4986     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
4987         tokToString(tok),qPrint(cmdName));
4988     return;
4989   }
4990   DocIncOperator *op = new DocIncOperator(this,t,g_token->name,g_context,g_isExample,g_exampleName);
4991   QListIterator<DocNode> it(m_children);
4992   DocNode *n1 = it.toLast();
4993   --it;
4994   DocNode *n2 = n1!=0 ? it.current() : 0;
4995   bool isFirst = n1==0 || // no last node
4996                  (n1->kind()!=DocNode::Kind_IncOperator &&
4997                   n1->kind()!=DocNode::Kind_WhiteSpace
4998                  ) || // last node is not operator or whitespace
4999                  (n1->kind()==DocNode::Kind_WhiteSpace &&
5000                   n2!=0 && n2->kind()!=DocNode::Kind_IncOperator
5001                  ); // previous not is not operator
5002   op->markFirst(isFirst);
5003   op->markLast(TRUE);
5004   if (n1!=0 && n1->kind()==DocNode::Kind_IncOperator)
5005   {
5006     ((DocIncOperator *)n1)->markLast(FALSE);
5007   }
5008   else if (n1!=0 && n1->kind()==DocNode::Kind_WhiteSpace &&
5009            n2!=0 && n2->kind()==DocNode::Kind_IncOperator
5010           )
5011   {
5012     ((DocIncOperator *)n2)->markLast(FALSE);
5013   }
5014   m_children.append(op);
5015   op->parse();
5016 }
5017
5018 void DocPara::handleImage(const QCString &cmdName)
5019 {
5020   int tok=doctokenizerYYlex();
5021   if (tok!=TK_WHITESPACE)
5022   {
5023     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5024         qPrint(cmdName));
5025     return;
5026   }
5027   tok=doctokenizerYYlex();
5028   if (tok!=TK_WORD && tok!=TK_LNKWORD)
5029   {
5030     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5031         tokToString(tok),qPrint(cmdName));
5032     return;
5033   }
5034   tok=doctokenizerYYlex();
5035   if (tok!=TK_WHITESPACE)
5036   {
5037     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5038         qPrint(cmdName));
5039     return;
5040   }
5041   DocImage::Type t;
5042   QCString imgType = g_token->name.lower();
5043   if      (imgType=="html")    t=DocImage::Html;
5044   else if (imgType=="latex")   t=DocImage::Latex;
5045   else if (imgType=="docbook") t=DocImage::DocBook;
5046   else if (imgType=="rtf")     t=DocImage::Rtf;
5047   else
5048   {
5049     warn_doc_error(g_fileName,doctokenizerYYlineno,"image type %s specified as the first argument of "
5050         "%s is not valid",
5051         qPrint(imgType),qPrint(cmdName));
5052     return;
5053   }
5054   doctokenizerYYsetStateFile();
5055   tok=doctokenizerYYlex();
5056   doctokenizerYYsetStatePara();
5057   if (tok!=TK_WORD)
5058   {
5059     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5060         tokToString(tok),qPrint(cmdName));
5061     return;
5062   }
5063   HtmlAttribList attrList;
5064   DocImage *img = new DocImage(this,attrList,findAndCopyImage(g_token->name,t),t);
5065   m_children.append(img);
5066   img->parse();
5067 }
5068
5069 template<class T>
5070 void DocPara::handleFile(const QCString &cmdName)
5071 {
5072   int tok=doctokenizerYYlex();
5073   if (tok!=TK_WHITESPACE)
5074   {
5075     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5076         qPrint(cmdName));
5077     return;
5078   }
5079   doctokenizerYYsetStateFile();
5080   tok=doctokenizerYYlex();
5081   doctokenizerYYsetStatePara();
5082   if (tok!=TK_WORD)
5083   {
5084     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5085         tokToString(tok),qPrint(cmdName));
5086     return;
5087   }
5088   QCString name = g_token->name;
5089   T *df = new T(this,name,g_context);
5090   m_children.append(df);
5091   df->parse();
5092 }
5093
5094 void DocPara::handleVhdlFlow()
5095 {
5096   DocVhdlFlow *vf = new DocVhdlFlow(this);
5097   m_children.append(vf);
5098   vf->parse();
5099 }
5100
5101 void DocPara::handleLink(const QCString &cmdName,bool isJavaLink)
5102 {
5103   int tok=doctokenizerYYlex();
5104   if (tok!=TK_WHITESPACE)
5105   {
5106     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5107         qPrint(cmdName));
5108     return;
5109   }
5110   doctokenizerYYsetStateLink();
5111   tok=doctokenizerYYlex();
5112   if (tok!=TK_WORD)
5113   {
5114     warn_doc_error(g_fileName,doctokenizerYYlineno,"%s as the argument of %s",
5115         tokToString(tok),qPrint(cmdName));
5116     return;
5117   }
5118   doctokenizerYYsetStatePara();
5119   DocLink *lnk = new DocLink(this,g_token->name);
5120   m_children.append(lnk);
5121   QCString leftOver = lnk->parse(isJavaLink);
5122   if (!leftOver.isEmpty())
5123   {
5124     m_children.append(new DocWord(this,leftOver));
5125   }
5126 }
5127
5128 void DocPara::handleRef(const QCString &cmdName)
5129 {
5130   DBG(("handleRef(%s)\n",qPrint(cmdName)));
5131   int tok=doctokenizerYYlex();
5132   if (tok!=TK_WHITESPACE)
5133   {
5134     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5135         qPrint(cmdName));
5136     return;
5137   }
5138   doctokenizerYYsetStateRef();
5139   tok=doctokenizerYYlex(); // get the reference id
5140   DocRef *ref=0;
5141   if (tok!=TK_WORD)
5142   {
5143     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5144         tokToString(tok),qPrint(cmdName));
5145     goto endref;
5146   }
5147   ref = new DocRef(this,g_token->name,g_context);
5148   m_children.append(ref);
5149   ref->parse();
5150 endref:
5151   doctokenizerYYsetStatePara();
5152 }
5153
5154 void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t)
5155 {
5156   DBG(("handleInclude(%s)\n",qPrint(cmdName)));
5157   int tok=doctokenizerYYlex();
5158   if (tok!=TK_WHITESPACE)
5159   {
5160     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5161         qPrint(cmdName));
5162     return;
5163   }
5164   doctokenizerYYsetStateFile();
5165   tok=doctokenizerYYlex();
5166   doctokenizerYYsetStatePara();
5167   if (tok==0)
5168   {
5169     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
5170         "argument of command %s",qPrint(cmdName));
5171     return;
5172   }
5173   else if (tok!=TK_WORD)
5174   {
5175     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5176         tokToString(tok),qPrint(cmdName));
5177     return;
5178   }
5179   QCString fileName = g_token->name;
5180   QCString blockId;
5181   if (t==DocInclude::Snippet || t==DocInclude::SnipWithLines || t==DocInclude::SnippetDoc)
5182   {
5183     if (fileName == "this") fileName=g_fileName;
5184     doctokenizerYYsetStateSnippet();
5185     tok=doctokenizerYYlex();
5186     doctokenizerYYsetStatePara();
5187     if (tok!=TK_WORD)
5188     {
5189       warn_doc_error(g_fileName,doctokenizerYYlineno,"expected block identifier, but found token %s instead while parsing the %s command",
5190           tokToString(tok),qPrint(cmdName));
5191       return;
5192     }
5193     blockId = "["+g_token->name+"]";
5194   }
5195
5196   // This is the only place to handle the \includedoc and \snippetdoc commands,
5197   // as the content is included here as if it is really here.
5198   if (t==DocInclude::IncludeDoc || t==DocInclude::SnippetDoc)
5199   {
5200      QCString inc_text;
5201      int inc_line  = 1;
5202      readTextFileByName(fileName,inc_text);
5203      if (t==DocInclude::SnippetDoc)
5204      {
5205        inc_line = lineBlock(inc_text, blockId);
5206        inc_text = extractBlock(inc_text, blockId);
5207      }
5208      docParserPushContext();
5209      g_fileName = fileName;
5210      doctokenizerYYlineno=inc_line;
5211      internalValidatingParseDoc(this,m_children,inc_text);
5212      docParserPopContext();
5213   }
5214   else
5215   {
5216     DocInclude *inc = new DocInclude(this,fileName,g_context,t,g_isExample,g_exampleName,blockId);
5217     m_children.append(inc);
5218     inc->parse();
5219   }
5220 }
5221
5222 void DocPara::handleSection(const QCString &cmdName)
5223 {
5224   // get the argument of the section command.
5225   int tok=doctokenizerYYlex();
5226   if (tok!=TK_WHITESPACE)
5227   {
5228     warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command",
5229         qPrint(cmdName));
5230     return;
5231   }
5232   tok=doctokenizerYYlex();
5233   if (tok==0)
5234   {
5235     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the "
5236         "argument of command %s\n", qPrint(cmdName));
5237     return;
5238   }
5239   else if (tok!=TK_WORD && tok!=TK_LNKWORD)
5240   {
5241     warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s",
5242         tokToString(tok),qPrint(cmdName));
5243     return;
5244   }
5245   g_token->sectionId = g_token->name;
5246   doctokenizerYYsetStateSkipTitle();
5247   doctokenizerYYlex();
5248   doctokenizerYYsetStatePara();
5249 }
5250
5251 int DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
5252 {
5253   DocHtmlHeader *header = new DocHtmlHeader(this,tagHtmlAttribs,level);
5254   m_children.append(header);
5255   int retval = header->parse();
5256   return (retval==RetVal_OK) ? TK_NEWPARA : retval;
5257 }
5258
5259 // For XML tags whose content is stored in attributes rather than
5260 // contained within the element, we need a way to inject the attribute
5261 // text into the current paragraph.
5262 bool DocPara::injectToken(int tok,const QCString &tokText) 
5263 {
5264   g_token->name = tokText;
5265   return defaultHandleToken(this,tok,m_children);
5266 }
5267
5268 int DocPara::handleStartCode()
5269 {
5270   int retval = doctokenizerYYlex();
5271   QCString lang = g_token->name;
5272   if (!lang.isEmpty() && lang.at(0)!='.')
5273   {
5274     lang="."+lang;
5275   }
5276   if (g_xmlComment)
5277   {
5278     g_token->verb = substitute(substitute(g_token->verb,"&lt;","<"),"&gt;",">");
5279   }
5280   // search for the first non-whitespace line, index is stored in li
5281   int i=0,li=0,l=g_token->verb.length();
5282   while (i<l && (g_token->verb.at(i)==' ' || g_token->verb.at(i)=='\n'))
5283   {
5284     if (g_token->verb.at(i)=='\n') li=i+1;
5285     i++;
5286   }
5287   m_children.append(new DocVerbatim(this,g_context,stripIndentation(g_token->verb.mid(li)),DocVerbatim::Code,g_isExample,g_exampleName,FALSE,lang));
5288   if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"code section ended without end marker");
5289   doctokenizerYYsetStatePara();
5290   return retval;
5291 }
5292
5293 void DocPara::handleInheritDoc()
5294 {
5295   if (g_memberDef) // inheriting docs from a member
5296   {
5297     MemberDef *reMd = g_memberDef->reimplements();
5298     if (reMd) // member from which was inherited.
5299     {
5300       MemberDef *thisMd = g_memberDef;
5301       //printf("{InheritDocs:%s=>%s}\n",g_memberDef->qualifiedName().data(),reMd->qualifiedName().data());
5302       docParserPushContext();
5303       g_scope=reMd->getOuterScope();
5304       if (g_scope!=Doxygen::globalScope)
5305       {
5306         g_context=g_scope->name();
5307       }
5308       g_memberDef=reMd;
5309       g_styleStack.clear();
5310       g_nodeStack.clear();
5311       g_copyStack.append(reMd);
5312       internalValidatingParseDoc(this,m_children,reMd->briefDescription());
5313       internalValidatingParseDoc(this,m_children,reMd->documentation());
5314       g_copyStack.remove(reMd);
5315       docParserPopContext(TRUE);
5316       g_memberDef = thisMd;
5317     }
5318   }
5319 }
5320
5321
5322 int DocPara::handleCommand(const QCString &cmdName)
5323 {
5324   DBG(("handleCommand(%s)\n",qPrint(cmdName)));
5325   int retval = RetVal_OK;
5326   int cmdId = Mappers::cmdMapper->map(cmdName);
5327   switch (cmdId)
5328   {
5329     case CMD_UNKNOWN:
5330       warn_doc_error(g_fileName,doctokenizerYYlineno,"Found unknown command `\\%s'",qPrint(cmdName));
5331       break;
5332     case CMD_EMPHASIS:
5333       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,TRUE));
5334       retval=handleStyleArgument(this,m_children,cmdName); 
5335       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,FALSE));
5336       if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," "));
5337       break;
5338     case CMD_BOLD:
5339       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,TRUE));
5340       retval=handleStyleArgument(this,m_children,cmdName); 
5341       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,FALSE));
5342       if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," "));
5343       break;
5344     case CMD_CODE:
5345       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Code,TRUE));
5346       retval=handleStyleArgument(this,m_children,cmdName); 
5347       m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Code,FALSE));
5348       if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," "));
5349       break;
5350     case CMD_BSLASH:
5351       m_children.append(new DocSymbol(this,DocSymbol::Sym_BSlash));
5352       break;
5353     case CMD_AT:
5354       m_children.append(new DocSymbol(this,DocSymbol::Sym_At));
5355       break;
5356     case CMD_LESS:
5357       m_children.append(new DocSymbol(this,DocSymbol::Sym_Less));
5358       break;
5359     case CMD_GREATER:
5360       m_children.append(new DocSymbol(this,DocSymbol::Sym_Greater));
5361       break;
5362     case CMD_AMP:
5363       m_children.append(new DocSymbol(this,DocSymbol::Sym_Amp));
5364       break;
5365     case CMD_DOLLAR:
5366       m_children.append(new DocSymbol(this,DocSymbol::Sym_Dollar));
5367       break;
5368     case CMD_HASH:
5369       m_children.append(new DocSymbol(this,DocSymbol::Sym_Hash));
5370       break;
5371     case CMD_PIPE:
5372       m_children.append(new DocSymbol(this,DocSymbol::Sym_Pipe));
5373       break;
5374     case CMD_DCOLON:
5375       m_children.append(new DocSymbol(this,DocSymbol::Sym_DoubleColon));
5376       break;
5377     case CMD_PERCENT:
5378       m_children.append(new DocSymbol(this,DocSymbol::Sym_Percent));
5379       break;
5380     case CMD_NDASH:
5381       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5382       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5383       break;
5384     case CMD_MDASH:
5385       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5386       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5387       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5388       break;
5389     case CMD_QUOTE:
5390       m_children.append(new DocSymbol(this,DocSymbol::Sym_Quot));
5391       break;
5392     case CMD_PUNT:
5393       m_children.append(new DocSymbol(this,DocSymbol::Sym_Dot));
5394       break;
5395     case CMD_PLUS:
5396       m_children.append(new DocSymbol(this,DocSymbol::Sym_Plus));
5397       break;
5398     case CMD_MINUS:
5399       m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
5400       break;
5401     case CMD_SA:
5402       g_inSeeBlock=TRUE;
5403       retval = handleSimpleSection(DocSimpleSect::See);
5404       g_inSeeBlock=FALSE;
5405       break;
5406     case CMD_RETURN:
5407       retval = handleSimpleSection(DocSimpleSect::Return);
5408       g_hasReturnCommand=TRUE;
5409       break;
5410     case CMD_AUTHOR:
5411       retval = handleSimpleSection(DocSimpleSect::Author);
5412       break;
5413     case CMD_AUTHORS:
5414       retval = handleSimpleSection(DocSimpleSect::Authors);
5415       break;
5416     case CMD_VERSION:
5417       retval = handleSimpleSection(DocSimpleSect::Version);
5418       break;
5419     case CMD_SINCE:
5420       retval = handleSimpleSection(DocSimpleSect::Since);
5421       break;
5422     case CMD_DATE:
5423       retval = handleSimpleSection(DocSimpleSect::Date);
5424       break;
5425     case CMD_NOTE:
5426       retval = handleSimpleSection(DocSimpleSect::Note);
5427       break;
5428     case CMD_WARNING:
5429       retval = handleSimpleSection(DocSimpleSect::Warning);
5430       break;
5431     case CMD_PRE:
5432       retval = handleSimpleSection(DocSimpleSect::Pre);
5433       break;
5434     case CMD_POST:
5435       retval = handleSimpleSection(DocSimpleSect::Post);
5436       break;
5437     case CMD_COPYRIGHT:
5438       retval = handleSimpleSection(DocSimpleSect::Copyright);
5439       break;
5440     case CMD_INVARIANT:
5441       retval = handleSimpleSection(DocSimpleSect::Invar);
5442       break;
5443     case CMD_REMARK:
5444       retval = handleSimpleSection(DocSimpleSect::Remark);
5445       break;
5446     case CMD_ATTENTION:
5447       retval = handleSimpleSection(DocSimpleSect::Attention);
5448       break;
5449     case CMD_PAR:
5450       retval = handleSimpleSection(DocSimpleSect::User);
5451       break;
5452     case CMD_LI:
5453       {
5454         DocSimpleList *sl=new DocSimpleList(this);
5455         m_children.append(sl);
5456         retval = sl->parse();
5457       }
5458       break;
5459     case CMD_SECTION:
5460       {
5461         handleSection(cmdName);
5462         retval = RetVal_Section;
5463       }
5464       break;
5465     case CMD_SUBSECTION:
5466       {
5467         handleSection(cmdName);
5468         retval = RetVal_Subsection;
5469       }
5470       break;
5471     case CMD_SUBSUBSECTION:
5472       {
5473         handleSection(cmdName);
5474         retval = RetVal_Subsubsection;
5475       }
5476       break;
5477     case CMD_PARAGRAPH:
5478       {
5479         handleSection(cmdName);
5480         retval = RetVal_Paragraph;
5481       }
5482       break;
5483     case CMD_STARTCODE:
5484       {
5485         doctokenizerYYsetStateCode();
5486         retval = handleStartCode();
5487       }
5488       break;
5489     case CMD_HTMLONLY:
5490       {
5491         doctokenizerYYsetStateHtmlOnly();
5492         retval = doctokenizerYYlex();
5493         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::HtmlOnly,g_isExample,g_exampleName,g_token->name=="block"));
5494         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"htmlonly section ended without end marker");
5495         doctokenizerYYsetStatePara();
5496       }
5497       break;
5498     case CMD_MANONLY:
5499       {
5500         doctokenizerYYsetStateManOnly();
5501         retval = doctokenizerYYlex();
5502         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::ManOnly,g_isExample,g_exampleName));
5503         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"manonly section ended without end marker");
5504         doctokenizerYYsetStatePara();
5505       }
5506       break;
5507     case CMD_RTFONLY:
5508       {
5509         doctokenizerYYsetStateRtfOnly();
5510         retval = doctokenizerYYlex();
5511         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::RtfOnly,g_isExample,g_exampleName));
5512         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"rtfonly section ended without end marker");
5513         doctokenizerYYsetStatePara();
5514       }
5515       break;
5516     case CMD_LATEXONLY:
5517       {
5518         doctokenizerYYsetStateLatexOnly();
5519         retval = doctokenizerYYlex();
5520         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::LatexOnly,g_isExample,g_exampleName));
5521         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"latexonly section ended without end marker");
5522         doctokenizerYYsetStatePara();
5523       }
5524       break;
5525     case CMD_XMLONLY:
5526       {
5527         doctokenizerYYsetStateXmlOnly();
5528         retval = doctokenizerYYlex();
5529         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::XmlOnly,g_isExample,g_exampleName));
5530         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"xmlonly section ended without end marker");
5531         doctokenizerYYsetStatePara();
5532       }
5533       break;
5534     case CMD_DBONLY:
5535       {
5536         doctokenizerYYsetStateDbOnly();
5537         retval = doctokenizerYYlex();
5538         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::DocbookOnly,g_isExample,g_exampleName));
5539         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"docbookonly section ended without end marker",doctokenizerYYlineno);
5540         doctokenizerYYsetStatePara();
5541       }
5542       break;
5543     case CMD_VERBATIM:
5544       {
5545         doctokenizerYYsetStateVerbatim();
5546         retval = doctokenizerYYlex();
5547         m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Verbatim,g_isExample,g_exampleName));
5548         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"verbatim section ended without end marker");
5549         doctokenizerYYsetStatePara();
5550       }
5551       break;
5552     case CMD_DOT:
5553       {
5554         DocVerbatim *dv = new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Dot,g_isExample,g_exampleName);
5555         doctokenizerYYsetStatePara();
5556         QCString width,height;
5557         defaultHandleTitleAndSize(CMD_DOT,dv,dv->children(),width,height);
5558         doctokenizerYYsetStateDot();
5559         retval = doctokenizerYYlex();
5560         dv->setText(g_token->verb);
5561         dv->setWidth(width);
5562         dv->setHeight(height);
5563         m_children.append(dv);
5564         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"dot section ended without end marker");
5565         doctokenizerYYsetStatePara();
5566       }
5567       break;
5568     case CMD_MSC:
5569       {
5570         DocVerbatim *dv = new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Msc,g_isExample,g_exampleName);
5571         doctokenizerYYsetStatePara();
5572         QCString width,height;
5573         defaultHandleTitleAndSize(CMD_MSC,dv,dv->children(),width,height);
5574         doctokenizerYYsetStateMsc();
5575         retval = doctokenizerYYlex();
5576         dv->setText(g_token->verb);
5577         dv->setWidth(width);
5578         dv->setHeight(height);
5579         m_children.append(dv);
5580         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"msc section ended without end marker");
5581         doctokenizerYYsetStatePara();
5582       }
5583       break;
5584     case CMD_STARTUML:
5585       {
5586         static QCString jarPath = Config_getString(PLANTUML_JAR_PATH);
5587         doctokenizerYYsetStatePlantUMLOpt();
5588         retval = doctokenizerYYlex();
5589         QCString plantFile(g_token->sectionId);
5590         DocVerbatim *dv = new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::PlantUML,FALSE,plantFile);
5591         doctokenizerYYsetStatePara();
5592         QCString width,height;
5593         defaultHandleTitleAndSize(CMD_STARTUML,dv,dv->children(),width,height);
5594         doctokenizerYYsetStatePlantUML();
5595         retval = doctokenizerYYlex();
5596         dv->setText(g_token->verb);
5597         dv->setWidth(width);
5598         dv->setHeight(height);
5599         if (jarPath.isEmpty())
5600         {
5601           warn_doc_error(g_fileName,doctokenizerYYlineno,"ignoring \\startuml command because PLANTUML_JAR_PATH is not set");
5602           delete dv;
5603         }
5604         else
5605         {
5606           m_children.append(dv);
5607         }
5608         if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"startuml section ended without end marker");
5609         doctokenizerYYsetStatePara();
5610       }
5611       break;
5612     case CMD_ENDPARBLOCK:
5613       retval=RetVal_EndParBlock;
5614       break;
5615     case CMD_ENDCODE:
5616     case CMD_ENDHTMLONLY:
5617     case CMD_ENDMANONLY:
5618     case CMD_ENDRTFONLY:
5619     case CMD_ENDLATEXONLY:
5620     case CMD_ENDXMLONLY:
5621     case CMD_ENDDBONLY:
5622     case CMD_ENDLINK:
5623     case CMD_ENDVERBATIM:
5624     case CMD_ENDDOT:
5625     case CMD_ENDMSC:
5626     case CMD_ENDUML:
5627       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected command %s",qPrint(g_token->name));
5628       break; 
5629     case CMD_PARAM:
5630       retval = handleParamSection(cmdName,DocParamSect::Param,FALSE,g_token->paramDir);
5631       break;
5632     case CMD_TPARAM:
5633       retval = handleParamSection(cmdName,DocParamSect::TemplateParam,FALSE,g_token->paramDir);
5634       break;
5635     case CMD_RETVAL:
5636       retval = handleParamSection(cmdName,DocParamSect::RetVal);
5637       break;
5638     case CMD_EXCEPTION:
5639       retval = handleParamSection(cmdName,DocParamSect::Exception);
5640       break;
5641     case CMD_XREFITEM:
5642       retval = handleXRefItem();
5643       break;
5644     case CMD_LINEBREAK:
5645       {
5646         DocLineBreak *lb = new DocLineBreak(this);
5647         m_children.append(lb);
5648       }
5649       break;
5650     case CMD_ANCHOR:
5651       {
5652         DocAnchor *anchor = handleAnchor(this);
5653         if (anchor)
5654         {
5655           m_children.append(anchor);
5656         }
5657       }
5658       break;
5659     case CMD_ADDINDEX:
5660       {
5661         DocIndexEntry *ie = new DocIndexEntry(this,
5662                      g_scope!=Doxygen::globalScope?g_scope:0,
5663                      g_memberDef);
5664         m_children.append(ie);
5665         retval = ie->parse();
5666       }
5667       break;
5668     case CMD_INTERNAL:
5669       retval = RetVal_Internal;
5670       break;
5671     case CMD_ENDINTERNAL:
5672       retval = RetVal_EndInternal;
5673       break;
5674     case CMD_PARBLOCK:
5675       {
5676         DocParBlock *block = new DocParBlock(this);
5677         m_children.append(block);
5678         retval = block->parse();
5679       }
5680       break;
5681     case CMD_COPYDOC:   // fall through
5682     case CMD_COPYBRIEF: // fall through
5683     case CMD_COPYDETAILS:
5684       //retval = RetVal_CopyDoc;
5685       // these commands should already be resolved by processCopyDoc()
5686       break;
5687     case CMD_INCLUDE:
5688       handleInclude(cmdName,DocInclude::Include);
5689       break;
5690     case CMD_INCWITHLINES:
5691       handleInclude(cmdName,DocInclude::IncWithLines);
5692       break;
5693     case CMD_DONTINCLUDE:
5694       handleInclude(cmdName,DocInclude::DontInclude);
5695       break;
5696     case CMD_HTMLINCLUDE:
5697       handleInclude(cmdName,DocInclude::HtmlInclude);
5698       break;
5699     case CMD_LATEXINCLUDE:
5700       handleInclude(cmdName,DocInclude::LatexInclude);
5701       break;
5702     case CMD_VERBINCLUDE:
5703       handleInclude(cmdName,DocInclude::VerbInclude);
5704       break;
5705     case CMD_SNIPPET:
5706       handleInclude(cmdName,DocInclude::Snippet);
5707       break;
5708     case CMD_SNIPWITHLINES:
5709       handleInclude(cmdName,DocInclude::SnipWithLines);
5710       break;
5711     case CMD_INCLUDEDOC:
5712       handleInclude(cmdName,DocInclude::IncludeDoc);
5713       break;
5714     case CMD_SNIPPETDOC:
5715       handleInclude(cmdName,DocInclude::SnippetDoc);
5716       break;
5717     case CMD_SKIP:
5718       handleIncludeOperator(cmdName,DocIncOperator::Skip);
5719       break;
5720     case CMD_UNTIL:
5721       handleIncludeOperator(cmdName,DocIncOperator::Until);
5722       break;
5723     case CMD_SKIPLINE:
5724       handleIncludeOperator(cmdName,DocIncOperator::SkipLine);
5725       break;
5726     case CMD_LINE:
5727       handleIncludeOperator(cmdName,DocIncOperator::Line);
5728       break;
5729     case CMD_IMAGE:
5730       handleImage(cmdName);
5731       break;
5732     case CMD_DOTFILE:
5733       handleFile<DocDotFile>(cmdName);
5734       break;
5735     case CMD_VHDLFLOW:
5736       handleVhdlFlow();
5737       break;
5738     case CMD_MSCFILE:
5739       handleFile<DocMscFile>(cmdName);
5740       break;
5741     case CMD_DIAFILE:
5742       handleFile<DocDiaFile>(cmdName);
5743       break;
5744     case CMD_LINK:
5745       handleLink(cmdName,FALSE);
5746       break;
5747     case CMD_JAVALINK:
5748       handleLink(cmdName,TRUE);
5749       break;
5750     case CMD_CITE:
5751       handleCite();
5752       break;
5753     case CMD_REF: // fall through
5754     case CMD_SUBPAGE:
5755       handleRef(cmdName);
5756       break;
5757     case CMD_SECREFLIST:
5758       {
5759         DocSecRefList *list = new DocSecRefList(this);
5760         m_children.append(list);
5761         list->parse();
5762       }
5763       break;
5764     case CMD_SECREFITEM:
5765       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected command %s",qPrint(g_token->name));
5766       break;
5767     case CMD_ENDSECREFLIST:
5768       warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected command %s",qPrint(g_token->name));
5769       break;
5770     case CMD_FORMULA:
5771       {
5772         DocFormula *form=new DocFormula(this,g_token->id);
5773         m_children.append(form);
5774       }
5775       break;
5776     //case CMD_LANGSWITCH:
5777     //  retval = handleLanguageSwitch();
5778     //  break;
5779     case CMD_INTERNALREF:
5780       //warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected command %s",qPrint(g_token->name));
5781       {
5782         DocInternalRef *ref = handleInternalRef(this);
5783         if (ref)
5784         {
5785           m_children.append(ref);
5786           ref->parse();
5787         }
5788         doctokenizerYYsetStatePara();
5789       }
5790       break;
5791     case CMD_INHERITDOC:
5792       handleInheritDoc();
5793       break;
5794     default:
5795       // we should not get here!
5796       ASSERT(0);
5797       break;
5798   }
5799   INTERNAL_ASSERT(retval==0 || retval==RetVal_OK || retval==RetVal_SimpleSec || 
5800          retval==TK_LISTITEM || retval==TK_ENDLIST || retval==TK_NEWPARA ||
5801          retval==RetVal_Section || retval==RetVal_EndList || 
5802          retval==RetVal_Internal || retval==RetVal_SwitchLang || 
5803          retval==RetVal_EndInternal
5804         );
5805   DBG(("handleCommand(%s) end retval=%x\n",qPrint(cmdName),retval));
5806   return retval;
5807 }
5808
5809 static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, 
5810                           const char *attrName, 
5811                           QCString *result) 
5812 {
5813
5814   HtmlAttribListIterator li(tagHtmlAttribs);
5815   HtmlAttrib *opt;
5816   for (li.toFirst();(opt=li.current());++li)
5817   {
5818     if (opt->name==attrName) 
5819     {
5820       *result = opt->value;
5821       return TRUE;
5822     }
5823   }
5824   return FALSE;
5825 }
5826
5827 int DocPara::handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs)
5828 {
5829   DBG(("handleHtmlStartTag(%s,%d)\n",qPrint(tagName),tagHtmlAttribs.count()));
5830   int retval=RetVal_OK;
5831   int tagId = Mappers::htmlTagMapper->map(tagName);
5832   if (g_token->emptyTag && !(tagId&XML_CmdMask) && 
5833       tagId!=HTML_UNKNOWN && tagId!=HTML_IMG && tagId!=HTML_BR)
5834   {
5835       warn_doc_error(g_fileName,doctokenizerYYlineno,"HTML tags may not use the 'empty tag' XHTML syntax.");
5836   }
5837   switch (tagId)
5838   {
5839     case HTML_UL: 
5840       {
5841         DocHtmlList *list = new DocHtmlList(this,tagHtmlAttribs,DocHtmlList::Unordered);
5842         m_children.append(list);
5843         retval=list->parse();
5844       }
5845       break;
5846     case HTML_OL: 
5847       {
5848         DocHtmlList *list = new DocHtmlList(this,tagHtmlAttribs,DocHtmlList::Ordered);
5849         m_children.append(list);
5850         retval=list->parse();
5851       }
5852       break;
5853     case HTML_LI:
5854       if (!insideUL(this) && !insideOL(this))
5855       {
5856         warn_doc_error(g_fileName,doctokenizerYYlineno,"lonely <li> tag found");
5857       }
5858       else
5859       {
5860         retval=RetVal_ListItem;
5861       }
5862       break;
5863     case HTML_BOLD:
5864       handleStyleEnter(this,m_children,DocStyleChange::Bold,&g_token->attribs);
5865       break;
5866     case HTML_CODE:
5867       if (/*getLanguageFromFileName(g_fileName)==SrcLangExt_CSharp ||*/ g_xmlComment) 
5868         // for C# source or inside a <summary> or <remark> section we 
5869         // treat <code> as an XML tag (so similar to @code)
5870       {
5871         doctokenizerYYsetStateXmlCode();
5872         retval = handleStartCode();
5873       }
5874       else // normal HTML markup
5875       {
5876         handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs);
5877       }
5878       break;
5879     case HTML_EMPHASIS:
5880       handleStyleEnter(this,m_children,DocStyleChange::Italic,&g_token->attribs);
5881       break;
5882     case HTML_DIV:
5883       handleStyleEnter(this,m_children,DocStyleChange::Div,&g_token->attribs);
5884       break;
5885     case HTML_SPAN:
5886       handleStyleEnter(this,m_children,DocStyleChange::Span,&g_token->attribs);
5887       break;
5888     case HTML_SUB:
5889       handleStyleEnter(this,m_children,DocStyleChange::Subscript,&g_token->attribs);
5890       break;
5891     case HTML_SUP:
5892       handleStyleEnter(this,m_children,DocStyleChange::Superscript,&g_token->attribs);
5893       break;
5894     case HTML_CENTER:
5895       handleStyleEnter(this,m_children,DocStyleChange::Center,&g_token->attribs);
5896       break;
5897     case HTML_SMALL:
5898       handleStyleEnter(this,m_children,DocStyleChange::Small,&g_token->attribs);
5899       break;
5900     case HTML_PRE:
5901       handleStyleEnter(this,m_children,DocStyleChange::Preformatted,&g_token->attribs);
5902       setInsidePreformatted(TRUE);
5903       doctokenizerYYsetInsidePre(TRUE);
5904       break;
5905     case HTML_P:
5906       retval=TK_NEWPARA;
5907       break;
5908     case HTML_DL:
5909       {
5910         DocHtmlDescList *list = new DocHtmlDescList(this,tagHtmlAttribs);
5911         m_children.append(list);
5912         retval=list->parse();
5913       }
5914       break;
5915     case HTML_DT:
5916       retval = RetVal_DescTitle;
5917       break;
5918     case HTML_DD:
5919       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag <dd> found");
5920       break;
5921     case HTML_TABLE:
5922       {
5923         DocHtmlTable *table = new DocHtmlTable(this,tagHtmlAttribs);
5924         m_children.append(table);
5925         retval=table->parse();
5926       }
5927       break;
5928     case HTML_TR:
5929       retval = RetVal_TableRow;
5930       break;
5931     case HTML_TD:
5932       retval = RetVal_TableCell;
5933       break;
5934     case HTML_TH:
5935       retval = RetVal_TableHCell;
5936       break;
5937     case HTML_CAPTION:
5938       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag <caption> found");
5939       break;
5940     case HTML_BR:
5941       {
5942         DocLineBreak *lb = new DocLineBreak(this);
5943         m_children.append(lb);
5944       }
5945       break;
5946     case HTML_HR:
5947       {
5948         DocHorRuler *hr = new DocHorRuler(this);
5949         m_children.append(hr);
5950       }
5951       break;
5952     case HTML_A:
5953       retval=handleAHref(this,m_children,tagHtmlAttribs);
5954       break;
5955     case HTML_H1:
5956       retval=handleHtmlHeader(tagHtmlAttribs,1);
5957       break;
5958     case HTML_H2:
5959       retval=handleHtmlHeader(tagHtmlAttribs,2);
5960       break;
5961     case HTML_H3:
5962       retval=handleHtmlHeader(tagHtmlAttribs,3);
5963       break;
5964     case HTML_H4:
5965       retval=handleHtmlHeader(tagHtmlAttribs,4);
5966       break;
5967     case HTML_H5:
5968       retval=handleHtmlHeader(tagHtmlAttribs,5);
5969       break;
5970     case HTML_H6:
5971       retval=handleHtmlHeader(tagHtmlAttribs,6);
5972       break;
5973     case HTML_IMG:
5974       {
5975         handleImg(this,m_children,tagHtmlAttribs);
5976       }
5977       break;
5978     case HTML_BLOCKQUOTE:
5979       {
5980         DocHtmlBlockQuote *block = new DocHtmlBlockQuote(this,tagHtmlAttribs);
5981         m_children.append(block);
5982         retval = block->parse();
5983       }
5984       break;
5985
5986     case XML_SUMMARY:
5987     case XML_REMARKS:
5988     case XML_EXAMPLE:
5989       g_xmlComment=TRUE;
5990       // fall through
5991     case XML_VALUE:
5992     case XML_PARA:
5993       if (!m_children.isEmpty())
5994       {
5995         retval = TK_NEWPARA;
5996       }
5997       break;
5998     case XML_DESCRIPTION:
5999       if (insideTable(this))
6000       {
6001         retval=RetVal_TableCell;
6002       }
6003       break;
6004     case XML_C:
6005       handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs);
6006       break;
6007     case XML_PARAM:
6008     case XML_TYPEPARAM:
6009       {
6010         g_xmlComment=TRUE;
6011         QCString paramName;
6012         if (findAttribute(tagHtmlAttribs,"name",&paramName))
6013         {
6014           if (paramName.isEmpty())
6015           {
6016             if (Config_getBool(WARN_NO_PARAMDOC))
6017             {
6018               warn_doc_error(g_fileName,doctokenizerYYlineno,"empty 'name' attribute for <param%s> tag.",tagId==XML_PARAM?"":"type");
6019             }
6020           }
6021           else
6022           {
6023             retval = handleParamSection(paramName,
6024                 tagId==XML_PARAM ? DocParamSect::Param : DocParamSect::TemplateParam,
6025                 TRUE);
6026           }
6027         }
6028         else
6029         {
6030           warn_doc_error(g_fileName,doctokenizerYYlineno,"Missing 'name' attribute from <param%s> tag.",tagId==XML_PARAM?"":"type");
6031         }
6032       }
6033       break;
6034     case XML_PARAMREF:
6035     case XML_TYPEPARAMREF:
6036       {
6037         QCString paramName;
6038         if (findAttribute(tagHtmlAttribs,"name",&paramName))
6039         {
6040           //printf("paramName=%s\n",paramName.data());
6041           m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,TRUE));
6042           m_children.append(new DocWord(this,paramName)); 
6043           m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,FALSE));
6044           if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," "));
6045         }
6046         else
6047         {
6048           warn_doc_error(g_fileName,doctokenizerYYlineno,"Missing 'name' attribute from <param%sref> tag.",tagId==XML_PARAMREF?"":"type");
6049         }
6050       }
6051       break;
6052     case XML_EXCEPTION:
6053       {
6054         g_xmlComment=TRUE;
6055         QCString exceptName;
6056         if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
6057         {
6058           unescapeCRef(exceptName);
6059           retval = handleParamSection(exceptName,DocParamSect::Exception,TRUE);
6060         }
6061         else
6062         {
6063           warn_doc_error(g_fileName,doctokenizerYYlineno,"Missing 'cref' attribute from <exception> tag.");
6064         }
6065       }
6066       break;
6067     case XML_ITEM:
6068     case XML_LISTHEADER:
6069       if (insideTable(this))
6070       {
6071         retval=RetVal_TableRow;
6072       }
6073       else if (insideUL(this) || insideOL(this))
6074       {
6075         retval=RetVal_ListItem;
6076       }
6077       else
6078       {
6079         warn_doc_error(g_fileName,doctokenizerYYlineno,"lonely <item> tag found");
6080       }
6081       break;
6082     case XML_RETURNS:
6083       g_xmlComment=TRUE;
6084       retval = handleSimpleSection(DocSimpleSect::Return,TRUE);
6085       g_hasReturnCommand=TRUE;
6086       break;
6087     case XML_TERM:
6088       //m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,TRUE));
6089       if (insideTable(this))
6090       {
6091         retval=RetVal_TableCell;
6092       }
6093       break;
6094     case XML_SEE:
6095       // I'm not sure if <see> is the same as <seealso> or if it
6096       // should you link a member without producing a section. The
6097       // C# specification is extremely vague about this (but what else 
6098       // can we expect from Microsoft...)
6099       {
6100         QCString cref;
6101         //printf("XML_SEE: empty tag=%d\n",g_token->emptyTag);
6102         if (findAttribute(tagHtmlAttribs,"cref",&cref))
6103         {
6104           unescapeCRef(cref);
6105           if (g_token->emptyTag) // <see cref="..."/> style
6106           {
6107             bool inSeeBlock = g_inSeeBlock;
6108             g_token->name = cref;
6109             g_inSeeBlock = TRUE;
6110             handleLinkedWord(this,m_children,TRUE);
6111             g_inSeeBlock = inSeeBlock;
6112           }
6113           else // <see cref="...">...</see> style
6114           {
6115             //DocRef *ref = new DocRef(this,cref);
6116             //m_children.append(ref);
6117             //ref->parse();
6118             doctokenizerYYsetStatePara();
6119             DocLink *lnk = new DocLink(this,cref);
6120             m_children.append(lnk);
6121             QCString leftOver = lnk->parse(FALSE,TRUE);
6122             if (!leftOver.isEmpty())
6123             {
6124               m_children.append(new DocWord(this,leftOver));
6125             }
6126           }
6127         }
6128         else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langworld="..."></see>
6129         {
6130             doctokenizerYYsetStatePara();
6131             DocLink *lnk = new DocLink(this,cref);
6132             m_children.append(lnk);
6133             QCString leftOver = lnk->parse(FALSE,TRUE);
6134             if (!leftOver.isEmpty())
6135             {
6136               m_children.append(new DocWord(this,leftOver));
6137             }
6138         }
6139         else
6140         {
6141           warn_doc_error(g_fileName,doctokenizerYYlineno,"Missing 'cref' or 'langword' attribute from <see> tag.");
6142         }
6143       }
6144       break;
6145     case XML_SEEALSO:
6146       {
6147         g_xmlComment=TRUE;
6148         QCString cref;
6149         if (findAttribute(tagHtmlAttribs,"cref",&cref))
6150         {
6151           unescapeCRef(cref);
6152           // Look for an existing "see" section
6153           DocSimpleSect *ss=0;
6154           QListIterator<DocNode> cli(m_children);
6155           DocNode *n;
6156           for (cli.toFirst();(n=cli.current());++cli)
6157           {
6158             if (n->kind()==Kind_SimpleSect && ((DocSimpleSect *)n)->type()==DocSimpleSect::See)
6159             {
6160               ss = (DocSimpleSect *)n;
6161             }
6162           }
6163
6164           if (!ss)  // start new section
6165           {
6166             ss=new DocSimpleSect(this,DocSimpleSect::See);
6167             m_children.append(ss);
6168           }
6169
6170           ss->appendLinkWord(cref);
6171           retval = RetVal_OK;
6172         }
6173         else
6174         {
6175           warn_doc_error(g_fileName,doctokenizerYYlineno,"Missing 'cref' attribute from <seealso> tag.");
6176         }
6177       }
6178       break;
6179     case XML_LIST:
6180       {
6181         QCString type;
6182         findAttribute(tagHtmlAttribs,"type",&type);
6183         DocHtmlList::Type listType = DocHtmlList::Unordered;
6184         HtmlAttribList emptyList;
6185         if (type=="number")
6186         {
6187           listType=DocHtmlList::Ordered;
6188         }
6189         if (type=="table")
6190         {
6191           DocHtmlTable *table = new DocHtmlTable(this,emptyList);
6192           m_children.append(table);
6193           retval=table->parseXml();
6194         }
6195         else
6196         {
6197           DocHtmlList *list = new DocHtmlList(this,emptyList,listType);
6198           m_children.append(list);
6199           retval=list->parseXml();
6200         }
6201       }
6202       break;
6203     case XML_INCLUDE:
6204     case XML_PERMISSION:
6205       // These tags are defined in .Net but are currently unsupported
6206       g_xmlComment=TRUE;
6207       break;
6208     case HTML_UNKNOWN:
6209       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported xml/html tag <%s> found", qPrint(tagName));
6210       m_children.append(new DocWord(this, "<"+tagName+tagHtmlAttribs.toString()+">"));
6211       break;
6212   case XML_INHERITDOC:
6213       handleInheritDoc();
6214       break;
6215   default:
6216       // we should not get here!
6217       ASSERT(0);
6218       break;
6219   }
6220   return retval;
6221 }
6222
6223 int DocPara::handleHtmlEndTag(const QCString &tagName)
6224 {
6225   DBG(("handleHtmlEndTag(%s)\n",qPrint(tagName)));
6226   int tagId = Mappers::htmlTagMapper->map(tagName);
6227   int retval=RetVal_OK;
6228   switch (tagId)
6229   {
6230     case HTML_UL: 
6231       if (!insideUL(this))
6232       {
6233         warn_doc_error(g_fileName,doctokenizerYYlineno,"found </ul> tag without matching <ul>");
6234       }
6235       else
6236       {
6237         retval=RetVal_EndList;
6238       }
6239       break;
6240     case HTML_OL: 
6241       if (!insideOL(this))
6242       {
6243         warn_doc_error(g_fileName,doctokenizerYYlineno,"found </ol> tag without matching <ol>");
6244       }
6245       else
6246       {
6247         retval=RetVal_EndList;
6248       }
6249       break;
6250     case HTML_LI:
6251       if (!insideLI(this))
6252       {
6253         warn_doc_error(g_fileName,doctokenizerYYlineno,"found </li> tag without matching <li>");
6254       }
6255       else
6256       {
6257         // ignore </li> tags
6258       }
6259       break;
6260     case HTML_BLOCKQUOTE:
6261       retval=RetVal_EndBlockQuote;
6262       break;
6263     //case HTML_PRE:
6264     //  if (!insidePRE(this))
6265     //  {
6266     //    warn_doc_error(g_fileName,doctokenizerYYlineno,"found </pre> tag without matching <pre>");
6267     //  }
6268     //  else
6269     //  {
6270     //    retval=RetVal_EndPre;
6271     //  }
6272     //  break;
6273     case HTML_BOLD:
6274       handleStyleLeave(this,m_children,DocStyleChange::Bold,"b");
6275       break;
6276     case HTML_CODE:
6277       handleStyleLeave(this,m_children,DocStyleChange::Code,"code");
6278       break;
6279     case HTML_EMPHASIS:
6280       handleStyleLeave(this,m_children,DocStyleChange::Italic,"em");
6281       break;
6282     case HTML_DIV:
6283       handleStyleLeave(this,m_children,DocStyleChange::Div,"div");
6284       break;
6285     case HTML_SPAN:
6286       handleStyleLeave(this,m_children,DocStyleChange::Span,"span");
6287       break;
6288     case HTML_SUB:
6289       handleStyleLeave(this,m_children,DocStyleChange::Subscript,"sub");
6290       break;
6291     case HTML_SUP:
6292       handleStyleLeave(this,m_children,DocStyleChange::Superscript,"sup");
6293       break;
6294     case HTML_CENTER:
6295       handleStyleLeave(this,m_children,DocStyleChange::Center,"center");
6296       break;
6297     case HTML_SMALL:
6298       handleStyleLeave(this,m_children,DocStyleChange::Small,"small");
6299       break;
6300     case HTML_PRE:
6301       handleStyleLeave(this,m_children,DocStyleChange::Preformatted,"pre");
6302       setInsidePreformatted(FALSE);
6303       doctokenizerYYsetInsidePre(FALSE);
6304       break;
6305     case HTML_P:
6306       retval=TK_NEWPARA;
6307       break;
6308     case HTML_DL:
6309       retval=RetVal_EndDesc;
6310       break;
6311     case HTML_DT:
6312       // ignore </dt> tag
6313       break;
6314     case HTML_DD:
6315       // ignore </dd> tag
6316       break;
6317     case HTML_TABLE:
6318       retval=RetVal_EndTable;
6319       break;
6320     case HTML_TR:
6321       // ignore </tr> tag
6322       break;
6323     case HTML_TD:
6324       // ignore </td> tag
6325       break;
6326     case HTML_TH:
6327       // ignore </th> tag
6328       break;
6329     case HTML_CAPTION:
6330       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </caption> found");
6331       break;
6332     case HTML_BR:
6333       warn_doc_error(g_fileName,doctokenizerYYlineno,"Illegal </br> tag found\n");
6334       break;
6335     case HTML_H1:
6336       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h1> found");
6337       break;
6338     case HTML_H2:
6339       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h2> found");
6340       break;
6341     case HTML_H3:
6342       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h3> found");
6343       break;
6344     case HTML_H4:
6345       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h4> found");
6346       break;
6347     case HTML_H5:
6348       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h5> found");
6349       break;
6350     case HTML_H6:
6351       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </h6> found");
6352       break;
6353     case HTML_IMG:
6354       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </img> found");
6355       break;
6356     case HTML_HR:
6357       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </hr> found");
6358       break;
6359     case HTML_A:
6360       //warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected tag </a> found");
6361       // ignore </a> tag (can be part of <a name=...></a>
6362       break;
6363
6364     case XML_TERM:
6365       //m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,FALSE));
6366       break;
6367     case XML_SUMMARY:
6368     case XML_REMARKS:
6369     case XML_PARA:
6370     case XML_VALUE:
6371     case XML_EXAMPLE:
6372     case XML_PARAM:
6373     case XML_LIST:
6374     case XML_TYPEPARAM:
6375     case XML_RETURNS:
6376     case XML_SEE:
6377     case XML_SEEALSO:
6378     case XML_EXCEPTION:
6379     case XML_INHERITDOC:
6380       retval = RetVal_CloseXml;
6381       break;
6382     case XML_C:
6383       handleStyleLeave(this,m_children,DocStyleChange::Code,"c");
6384       break;
6385     case XML_ITEM:
6386     case XML_LISTHEADER:
6387     case XML_INCLUDE:
6388     case XML_PERMISSION:
6389     case XML_DESCRIPTION:
6390     case XML_PARAMREF:
6391     case XML_TYPEPARAMREF:
6392       // These tags are defined in .Net but are currently unsupported
6393       break;
6394     case HTML_UNKNOWN:
6395       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported xml/html tag </%s> found", qPrint(tagName));
6396       m_children.append(new DocWord(this,"</"+tagName+">"));
6397       break;
6398     default:
6399       // we should not get here!
6400       warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end tag %s\n",qPrint(tagName));
6401       ASSERT(0);
6402       break;
6403   }
6404   return retval;
6405 }
6406
6407 int DocPara::parse()
6408 {
6409   DBG(("DocPara::parse() start\n"));
6410   g_nodeStack.push(this);
6411   // handle style commands "inherited" from the previous paragraph
6412   handleInitialStyleCommands(this,m_children);
6413   int tok;
6414   int retval=0;
6415   while ((tok=doctokenizerYYlex())) // get the next token
6416   {
6417 reparsetoken:
6418     DBG(("token %s at %d",tokToString(tok),doctokenizerYYlineno));
6419     if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || 
6420         tok==TK_COMMAND || tok==TK_HTMLTAG
6421        )
6422     {
6423       DBG((" name=%s",qPrint(g_token->name)));
6424     }
6425     DBG(("\n"));
6426     switch(tok)
6427     {
6428       case TK_WORD:
6429         m_children.append(new DocWord(this,g_token->name));
6430         break;
6431       case TK_LNKWORD:
6432         handleLinkedWord(this,m_children);
6433         break;
6434       case TK_URL:
6435         m_children.append(new DocURL(this,g_token->name,g_token->isEMailAddr));
6436         break;
6437       case TK_WHITESPACE:
6438         {
6439           // prevent leading whitespace and collapse multiple whitespace areas
6440           DocNode::Kind k;
6441           if (insidePRE(this) || // all whitespace is relevant
6442               (
6443                // remove leading whitespace 
6444                !m_children.isEmpty()  && 
6445                // and whitespace after certain constructs
6446                (k=m_children.getLast()->kind())!=DocNode::Kind_HtmlDescList &&
6447                k!=DocNode::Kind_HtmlTable &&
6448                k!=DocNode::Kind_HtmlList &&
6449                k!=DocNode::Kind_SimpleSect &&
6450                k!=DocNode::Kind_AutoList &&
6451                k!=DocNode::Kind_SimpleList &&
6452                /*k!=DocNode::Kind_Verbatim &&*/
6453                k!=DocNode::Kind_HtmlHeader &&
6454                k!=DocNode::Kind_HtmlBlockQuote &&
6455                k!=DocNode::Kind_ParamSect &&
6456                k!=DocNode::Kind_XRefItem
6457               )
6458              )
6459           {
6460             m_children.append(new DocWhiteSpace(this,g_token->chars));
6461           }
6462         }
6463         break;
6464       case TK_LISTITEM:
6465         {
6466           DBG(("found list item at %d parent=%d\n",g_token->indent,parent()->kind()));
6467           DocNode *n=parent();
6468           while (n && n->kind()!=DocNode::Kind_AutoList) n=n->parent();
6469           if (n) // we found an auto list up in the hierarchy
6470           {
6471             DocAutoList *al = (DocAutoList *)n;
6472             DBG(("previous list item at %d\n",al->indent()));
6473             if (al->indent()>=g_token->indent) 
6474               // new item at the same or lower indent level
6475             {
6476               retval=TK_LISTITEM;
6477               goto endparagraph;
6478             }
6479           }
6480
6481           // determine list depth
6482           int depth = 0;
6483           n=parent();
6484           while(n) 
6485           {
6486             if (n->kind() == DocNode::Kind_AutoList && 
6487                 ((DocAutoList*)n)->isEnumList()) depth++;
6488             n=n->parent();
6489           }
6490
6491           // first item or sub list => create new list
6492           DocAutoList *al=0;
6493           do
6494           {
6495             al = new DocAutoList(this,g_token->indent,
6496                                  g_token->isEnumList,depth);
6497             m_children.append(al);
6498             retval = al->parse();
6499           } while (retval==TK_LISTITEM &&         // new list
6500               al->indent()==g_token->indent  // at same indent level
6501               );
6502
6503           // check the return value
6504           if (retval==RetVal_SimpleSec) // auto list ended due to simple section command
6505           {
6506             // Reparse the token that ended the section at this level,
6507             // so a new simple section will be started at this level.
6508             // This is the same as unputting the last read token and continuing.
6509             g_token->name = g_token->simpleSectName;
6510             if (g_token->name.left(4)=="rcs:") // RCS section
6511             {
6512               g_token->name = g_token->name.mid(4);
6513               g_token->text = g_token->simpleSectText;
6514               tok = TK_RCSTAG;
6515             }
6516             else // other section
6517             {
6518               tok = TK_COMMAND;
6519             }
6520             DBG(("reparsing command %s\n",qPrint(g_token->name)));
6521             goto reparsetoken;
6522           }
6523           else if (retval==TK_ENDLIST)
6524           {
6525             if (al->indent()>g_token->indent) // end list
6526             {
6527               goto endparagraph;
6528             }
6529             else // continue with current paragraph
6530             {
6531             }
6532           }
6533           else // paragraph ended due to TK_NEWPARA, TK_LISTITEM, or EOF
6534           {
6535             goto endparagraph;
6536           }
6537         }
6538         break;
6539       case TK_ENDLIST:     
6540         DBG(("Found end of list inside of paragraph at line %d\n",doctokenizerYYlineno));
6541         if (parent()->kind()==DocNode::Kind_AutoListItem)
6542         {
6543           ASSERT(parent()->parent()->kind()==DocNode::Kind_AutoList);
6544           DocAutoList *al = (DocAutoList *)parent()->parent();
6545           if (al->indent()>=g_token->indent)
6546           {
6547             // end of list marker ends this paragraph
6548             retval=TK_ENDLIST;
6549             goto endparagraph;
6550           }
6551           else
6552           {
6553             warn_doc_error(g_fileName,doctokenizerYYlineno,"End of list marker found "
6554                 "has invalid indent level");
6555           }
6556         }
6557         else
6558         {
6559           warn_doc_error(g_fileName,doctokenizerYYlineno,"End of list marker found without any preceding "
6560               "list items");
6561         }
6562         break;
6563       case TK_COMMAND:    
6564         {
6565           // see if we have to start a simple section
6566           int cmd = Mappers::cmdMapper->map(g_token->name);
6567           DocNode *n=parent();
6568           while (n && 
6569               n->kind()!=DocNode::Kind_SimpleSect && 
6570               n->kind()!=DocNode::Kind_ParamSect
6571               ) 
6572           {
6573             n=n->parent();
6574           }
6575           if (cmd&SIMPLESECT_BIT)
6576           {
6577             if (n)  // already in a simple section
6578             {
6579               // simple section cannot start in this paragraph, need
6580               // to unwind the stack and remember the command.
6581               g_token->simpleSectName = g_token->name.copy();
6582               retval=RetVal_SimpleSec;
6583               goto endparagraph;
6584             }
6585           }
6586           // see if we are in a simple list
6587           n=parent();
6588           while (n && n->kind()!=DocNode::Kind_SimpleListItem) n=n->parent();
6589           if (n)
6590           {
6591             if (cmd==CMD_LI)
6592             {
6593               retval=RetVal_ListItem;
6594               goto endparagraph;
6595             }
6596           }
6597
6598           // handle the command
6599           retval=handleCommand(g_token->name);
6600           DBG(("handleCommand returns %x\n",retval));
6601
6602           // check the return value
6603           if (retval==RetVal_SimpleSec)
6604           {
6605             // Reparse the token that ended the section at this level,
6606             // so a new simple section will be started at this level.
6607             // This is the same as unputting the last read token and continuing.
6608             g_token->name = g_token->simpleSectName;
6609             if (g_token->name.left(4)=="rcs:") // RCS section
6610             {
6611               g_token->name = g_token->name.mid(4);
6612               g_token->text = g_token->simpleSectText;
6613               tok = TK_RCSTAG;
6614             }
6615             else // other section
6616             {
6617               tok = TK_COMMAND;
6618             }
6619             DBG(("reparsing command %s\n",qPrint(g_token->name)));
6620             goto reparsetoken;
6621           }
6622           else if (retval==RetVal_OK) 
6623           {
6624             // the command ended normally, keep scanning for new tokens.
6625             retval = 0;
6626           }
6627           else if (retval>0 && retval<RetVal_OK)
6628           { 
6629             // the command ended with a new command, reparse this token
6630             tok = retval;
6631             goto reparsetoken;
6632           }
6633           else // end of file, end of paragraph, start or end of section 
6634             // or some auto list marker
6635           {
6636             goto endparagraph;
6637           }
6638         }
6639         break;
6640       case TK_HTMLTAG:    
6641         {
6642           if (!g_token->endTag) // found a start tag
6643           {
6644             retval = handleHtmlStartTag(g_token->name,g_token->attribs);
6645           }
6646           else // found an end tag
6647           {
6648             retval = handleHtmlEndTag(g_token->name);
6649           }
6650           if (retval==RetVal_OK) 
6651           {
6652             // the command ended normally, keep scanner for new tokens.
6653             retval = 0;
6654           }
6655           else
6656           {
6657             goto endparagraph;
6658           }
6659         }
6660         break;
6661       case TK_SYMBOL:     
6662         {
6663           DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name);
6664           if (s!=DocSymbol::Sym_Unknown)
6665           {
6666             m_children.append(new DocSymbol(this,s));
6667           }
6668           else
6669           {
6670             warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
6671                 qPrint(g_token->name));
6672           }
6673           break;
6674         }
6675       case TK_NEWPARA:     
6676         retval=TK_NEWPARA;
6677         goto endparagraph;
6678       case TK_RCSTAG:
6679         {
6680           DocNode *n=parent();
6681           while (n && 
6682               n->kind()!=DocNode::Kind_SimpleSect && 
6683               n->kind()!=DocNode::Kind_ParamSect
6684               ) 
6685           {
6686             n=n->parent();
6687           }
6688           if (n)  // already in a simple section
6689           {
6690             // simple section cannot start in this paragraph, need
6691             // to unwind the stack and remember the command.
6692             g_token->simpleSectName = "rcs:"+g_token->name;
6693             g_token->simpleSectText = g_token->text;
6694             retval=RetVal_SimpleSec;
6695             goto endparagraph;
6696           }
6697
6698           // see if we are in a simple list
6699           DocSimpleSect *ss=new DocSimpleSect(this,DocSimpleSect::Rcs);
6700           m_children.append(ss);
6701           ss->parseRcs();
6702         }
6703         break;
6704       default:
6705         warn_doc_error(g_fileName,doctokenizerYYlineno,
6706             "Found unexpected token (id=%x)\n",tok);
6707         break;
6708     }
6709   }
6710   retval=0;
6711 endparagraph:
6712   handlePendingStyleCommands(this,m_children);
6713   DocNode *n = g_nodeStack.pop();
6714   ASSERT(n==this);
6715   DBG(("DocPara::parse() end retval=%x\n",retval));
6716   INTERNAL_ASSERT(retval==0 || retval==TK_NEWPARA || retval==TK_LISTITEM || 
6717          retval==TK_ENDLIST || retval>RetVal_OK 
6718         );
6719
6720   return retval; 
6721 }
6722
6723 //--------------------------------------------------------------------------
6724
6725 int DocSection::parse()
6726 {
6727   DBG(("DocSection::parse() start %s level=%d\n",qPrint(g_token->sectionId),m_level));
6728   int retval=RetVal_OK;
6729   g_nodeStack.push(this);
6730
6731   SectionInfo *sec;
6732   if (!m_id.isEmpty())
6733   {
6734     sec=Doxygen::sectionDict->find(m_id);
6735     if (sec)
6736     {
6737       m_file   = sec->fileName;
6738       m_anchor = sec->label;
6739       m_title  = sec->title;
6740       if (m_title.isEmpty()) m_title = sec->label;
6741       if (g_sectionDict && g_sectionDict->find(m_id)==0)
6742       {
6743         g_sectionDict->append(m_id,sec);
6744       }
6745     }
6746   }
6747
6748   // first parse any number of paragraphs
6749   bool isFirst=TRUE;
6750   DocPara *lastPar=0;
6751   do
6752   {
6753     DocPara *par = new DocPara(this);
6754     if (isFirst) { par->markFirst(); isFirst=FALSE; }
6755     retval=par->parse();
6756     if (!par->isEmpty()) 
6757     {
6758       m_children.append(par);
6759       lastPar=par;
6760     }
6761     else
6762     {
6763       delete par;
6764     }
6765     if (retval==TK_LISTITEM)
6766     {
6767       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found");
6768     }
6769     if (retval==RetVal_Internal)
6770     {
6771       DocInternal *in = new DocInternal(this);
6772       m_children.append(in);
6773       retval = in->parse(m_level+1);
6774       if (retval==RetVal_EndInternal)
6775       {
6776         retval=RetVal_OK;
6777       }
6778     }
6779   } while (retval!=0 && 
6780            retval!=RetVal_Section       &&
6781            retval!=RetVal_Subsection    &&
6782            retval!=RetVal_Subsubsection &&
6783            retval!=RetVal_Paragraph     &&
6784            retval!=RetVal_EndInternal
6785           );
6786
6787   if (lastPar) lastPar->markLast();
6788
6789   //printf("m_level=%d <-> %d\n",m_level,Doxygen::subpageNestingLevel);
6790
6791   if (retval==RetVal_Subsection && m_level==Doxygen::subpageNestingLevel+1)
6792   {
6793     // then parse any number of nested sections
6794     while (retval==RetVal_Subsection) // more sections follow
6795     {
6796       //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId];
6797       DocSection *s=new DocSection(this,
6798           QMIN(2+Doxygen::subpageNestingLevel,5),g_token->sectionId);
6799       m_children.append(s);
6800       retval = s->parse();
6801     }
6802   }
6803   else if (retval==RetVal_Subsubsection && m_level==Doxygen::subpageNestingLevel+2)
6804   {
6805     // then parse any number of nested sections
6806     while (retval==RetVal_Subsubsection) // more sections follow
6807     {
6808       //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId];
6809       DocSection *s=new DocSection(this,
6810           QMIN(3+Doxygen::subpageNestingLevel,5),g_token->sectionId);
6811       m_children.append(s);
6812       retval = s->parse();
6813     }
6814   }
6815   else if (retval==RetVal_Paragraph && m_level==QMIN(5,Doxygen::subpageNestingLevel+3))
6816   {
6817     // then parse any number of nested sections
6818     while (retval==RetVal_Paragraph) // more sections follow
6819     {
6820       //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId];
6821       DocSection *s=new DocSection(this,
6822           QMIN(4+Doxygen::subpageNestingLevel,5),g_token->sectionId);
6823       m_children.append(s);
6824       retval = s->parse();
6825     }
6826   }
6827   else if ((m_level<=1+Doxygen::subpageNestingLevel && retval==RetVal_Subsubsection) ||
6828            (m_level<=2+Doxygen::subpageNestingLevel && retval==RetVal_Paragraph)
6829           )
6830   {
6831     int level = (retval==RetVal_Subsubsection) ? 3 : 4;
6832     warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected %s "
6833             "command found inside %s!",
6834             sectionLevelToName[level],sectionLevelToName[m_level]);
6835     retval=0; // stop parsing
6836   }
6837   else
6838   {
6839   }
6840
6841   INTERNAL_ASSERT(retval==0 || 
6842                   retval==RetVal_Section || 
6843                   retval==RetVal_Subsection || 
6844                   retval==RetVal_Subsubsection || 
6845                   retval==RetVal_Paragraph || 
6846                   retval==RetVal_Internal ||
6847                   retval==RetVal_EndInternal
6848                  );
6849
6850   DBG(("DocSection::parse() end: retval=%x\n",retval));
6851   DocNode *n = g_nodeStack.pop();
6852   ASSERT(n==this);
6853   return retval;
6854 }
6855
6856 //--------------------------------------------------------------------------
6857
6858 void DocText::parse()
6859 {
6860   DBG(("DocText::parse() start\n"));
6861   g_nodeStack.push(this);
6862   doctokenizerYYsetStateText();
6863   
6864   int tok;
6865   while ((tok=doctokenizerYYlex())) // get the next token
6866   {
6867     switch(tok)
6868     {
6869       case TK_WORD:        
6870         m_children.append(new DocWord(this,g_token->name));
6871         break;
6872       case TK_WHITESPACE:  
6873         m_children.append(new DocWhiteSpace(this,g_token->chars));
6874         break;
6875       case TK_SYMBOL:     
6876         {
6877           DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name);
6878           if (s!=DocSymbol::Sym_Unknown)
6879           {
6880             m_children.append(new DocSymbol(this,s));
6881           }
6882           else
6883           {
6884             warn_doc_error(g_fileName,doctokenizerYYlineno,"Unsupported symbol %s found",
6885                 qPrint(g_token->name));
6886           }
6887         }
6888         break;
6889       case TK_COMMAND: 
6890         switch (Mappers::cmdMapper->map(g_token->name))
6891         {
6892           case CMD_BSLASH:
6893             m_children.append(new DocSymbol(this,DocSymbol::Sym_BSlash));
6894             break;
6895           case CMD_AT:
6896             m_children.append(new DocSymbol(this,DocSymbol::Sym_At));
6897             break;
6898           case CMD_LESS:
6899             m_children.append(new DocSymbol(this,DocSymbol::Sym_Less));
6900             break;
6901           case CMD_GREATER:
6902             m_children.append(new DocSymbol(this,DocSymbol::Sym_Greater));
6903             break;
6904           case CMD_AMP:
6905             m_children.append(new DocSymbol(this,DocSymbol::Sym_Amp));
6906             break;
6907           case CMD_DOLLAR:
6908             m_children.append(new DocSymbol(this,DocSymbol::Sym_Dollar));
6909             break;
6910           case CMD_HASH:
6911             m_children.append(new DocSymbol(this,DocSymbol::Sym_Hash));
6912             break;
6913           case CMD_DCOLON:
6914             m_children.append(new DocSymbol(this,DocSymbol::Sym_DoubleColon));
6915             break;
6916           case CMD_PERCENT:
6917             m_children.append(new DocSymbol(this,DocSymbol::Sym_Percent));
6918             break;
6919           case CMD_NDASH:
6920             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6921             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6922             break;
6923           case CMD_MDASH:
6924             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6925             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6926             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6927             break;
6928           case CMD_QUOTE:
6929             m_children.append(new DocSymbol(this,DocSymbol::Sym_Quot));
6930             break;
6931           case CMD_PUNT:
6932             m_children.append(new DocSymbol(this,DocSymbol::Sym_Dot));
6933             break;
6934           case CMD_PLUS:
6935             m_children.append(new DocSymbol(this,DocSymbol::Sym_Plus));
6936             break;
6937           case CMD_MINUS:
6938             m_children.append(new DocSymbol(this,DocSymbol::Sym_Minus));
6939             break;
6940           default:
6941             warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected command `%s' found",
6942                       qPrint(g_token->name));
6943             break;
6944         }
6945         break;
6946       default:
6947         warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected token %s",
6948             tokToString(tok));
6949         break;
6950     }
6951   }
6952
6953   handleUnclosedStyleCommands();
6954
6955   DocNode *n = g_nodeStack.pop();
6956   ASSERT(n==this);
6957   DBG(("DocText::parse() end\n"));
6958 }
6959
6960
6961 //--------------------------------------------------------------------------
6962
6963 void DocRoot::parse()
6964 {
6965   DBG(("DocRoot::parse() start\n"));
6966   g_nodeStack.push(this);
6967   doctokenizerYYsetStatePara();
6968   int retval=0;
6969
6970   // first parse any number of paragraphs
6971   bool isFirst=TRUE;
6972   DocPara *lastPar=0;
6973   do
6974   {
6975     DocPara *par = new DocPara(this);
6976     if (isFirst) { par->markFirst(); isFirst=FALSE; }
6977     retval=par->parse();
6978     if (!par->isEmpty()) 
6979     {
6980       m_children.append(par);
6981       lastPar=par;
6982     }
6983     else
6984     {
6985       delete par;
6986     }
6987     if (retval==TK_LISTITEM)
6988     {
6989       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found");
6990     }
6991     else if (retval==RetVal_Subsection)
6992     {
6993       warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsection command outside of section context!");
6994     }
6995     else if (retval==RetVal_Subsubsection)
6996     {
6997       warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsubsection command outside of subsection context!");
6998     }
6999     else if (retval==RetVal_Paragraph)
7000     {
7001       warn_doc_error(g_fileName,doctokenizerYYlineno,"found paragraph command outside of subsubsection context!");
7002     }
7003     if (retval==RetVal_Internal)
7004     {
7005       DocInternal *in = new DocInternal(this);
7006       m_children.append(in);
7007       retval = in->parse(1);
7008     }
7009   } while (retval!=0 && retval!=RetVal_Section);
7010   if (lastPar) lastPar->markLast();
7011
7012   //printf("DocRoot::parse() retval=%d %d\n",retval,RetVal_Section);
7013   // then parse any number of level1 sections
7014   while (retval==RetVal_Section)
7015   {
7016     SectionInfo *sec=Doxygen::sectionDict->find(g_token->sectionId);
7017     if (sec)
7018     {
7019       DocSection *s=new DocSection(this,
7020           QMIN(1+Doxygen::subpageNestingLevel,5),g_token->sectionId);
7021       m_children.append(s);
7022       retval = s->parse();
7023     }
7024     else
7025     {
7026       warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid section id `%s'; ignoring section",qPrint(g_token->sectionId));
7027       retval = 0;
7028     }
7029   }
7030
7031   handleUnclosedStyleCommands();
7032
7033   DocNode *n = g_nodeStack.pop();
7034   ASSERT(n==this);
7035   DBG(("DocRoot::parse() end\n"));
7036 }
7037
7038 static QCString extractCopyDocId(const char *data, uint &j, uint len)
7039 {
7040   uint s=j;
7041   uint e=j;
7042   int round=0;
7043   bool insideDQuote=FALSE;
7044   bool insideSQuote=FALSE;
7045   bool found=FALSE;
7046   while (j<len && !found)
7047   {
7048     if (!insideSQuote && !insideDQuote)
7049     {
7050       switch (data[j])
7051       {
7052         case '(': round++; break;
7053         case ')': round--; break;
7054         case '"': insideDQuote=TRUE; break;
7055         case '\'': insideSQuote=TRUE; break;
7056         case ' ':  // fall through
7057         case '\t': // fall through
7058         case '\n': 
7059           found=(round==0);
7060           break;
7061       }
7062     }
7063     else if (insideSQuote) // look for single quote end
7064     {
7065       if (data[j]=='\'' && (j==0 || data[j]!='\\'))
7066       {
7067         insideSQuote=FALSE;
7068       }
7069     }
7070     else if (insideDQuote) // look for double quote end
7071     {
7072       if (data[j]=='"' && (j==0 || data[j]!='\\'))
7073       {
7074         insideDQuote=FALSE;
7075       }
7076     }
7077     if (!found) j++;
7078   }
7079   if (qstrncmp(data+j," const",6)==0)
7080   {
7081     j+=6;
7082   }
7083   else if (qstrncmp(data+j," volatile",9)==0)
7084   {
7085     j+=9;
7086   }
7087   e=j;
7088   QCString id(e-s+1);
7089   if (e>s) memcpy(id.rawData(),data+s,e-s);
7090   id.at(e-s)='\0';
7091   //printf("extractCopyDocId='%s' input='%s'\n",id.data(),&data[s]);
7092   return id;
7093 }
7094
7095 // macro to check if the input starts with a specific command.
7096 // note that data[i] should point to the start of the command (\ or @ character)
7097 // and the sizeof(str) returns the size of str including the '\0' terminator;
7098 // a fact we abuse to skip over the start of the command character.
7099 #define CHECK_FOR_COMMAND(str,action) \
7100    do if ((i+sizeof(str)<len) && qstrncmp(data+i+1,str,sizeof(str)-1)==0) \
7101    { j=i+sizeof(str); action; } while(0)
7102
7103 static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brief)
7104 {
7105   int j=0;
7106   if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
7107   {
7108     CHECK_FOR_COMMAND("copybrief",brief=TRUE);    // @copybrief or \copybrief
7109     CHECK_FOR_COMMAND("copydetails",brief=FALSE); // @copydetails or \copydetails
7110   }
7111   return j;
7112 }
7113
7114 static uint isVerbatimSection(const char *data,uint i,uint len,QCString &endMarker)
7115 {
7116   int j=0;
7117   if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
7118   {
7119     CHECK_FOR_COMMAND("dot",endMarker="enddot");
7120     CHECK_FOR_COMMAND("code",endMarker="endcode");
7121     CHECK_FOR_COMMAND("msc",endMarker="endmsc");
7122     CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim");
7123     CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly");
7124     CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly");
7125     CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly");
7126     CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly");
7127     CHECK_FOR_COMMAND("manonly",endMarker="endmanonly");
7128     CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly");
7129     CHECK_FOR_COMMAND("startuml",endMarker="enduml");
7130   }
7131   //printf("isVerbatimSection(%s)=%d)\n",QCString(&data[i]).left(10).data(),j);
7132   return j;
7133 }
7134
7135 static uint skipToEndMarker(const char *data,uint i,uint len,const QCString &endMarker)
7136 {
7137   while (i<len)
7138   {
7139     if ((data[i]=='@' || data[i]=='\\') &&  // start of command character
7140         (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped
7141     {
7142       if (i+endMarker.length()+1<=len && qstrncmp(data+i+1,endMarker,endMarker.length())==0)
7143       {
7144         return i+endMarker.length()+1;
7145       }
7146     }
7147     i++;
7148   }
7149   // oops no endmarker found...
7150   return i<len ? i+1 : len;
7151 }
7152
7153 static QCString processCopyDoc(const char *data,uint &len)
7154 {
7155   //printf("processCopyDoc start '%s'\n",data);
7156   GrowBuf buf;
7157   uint i=0;
7158   while (i<len)
7159   {
7160     char c = data[i];
7161     if (c=='@' || c=='\\') // look for a command
7162     {
7163       bool isBrief=TRUE;
7164       uint j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
7165       if (j>0)
7166       {
7167         // skip whitespace
7168         while (j<len && (data[j]==' ' || data[j]=='\t')) j++;
7169         // extract the argument
7170         QCString id = extractCopyDocId(data,j,len);
7171         Definition *def;
7172         QCString doc,brief;
7173         //printf("resolving docs='%s'\n",id.data());
7174         if (findDocsForMemberOrCompound(id,&doc,&brief,&def))
7175         {
7176           //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,brief.data(),doc.data(),isBrief);
7177           if (g_copyStack.findRef(def)==-1) // definition not parsed earlier
7178           {
7179             g_copyStack.append(def);
7180             if (isBrief)
7181             {
7182               uint l=brief.length();
7183               buf.addStr(processCopyDoc(brief,l));
7184             }
7185             else
7186             {
7187               uint l=doc.length();
7188               buf.addStr(processCopyDoc(doc,l));
7189             }
7190             g_copyStack.remove(def);
7191           }
7192           else
7193           {
7194             warn_doc_error(g_fileName,doctokenizerYYlineno,
7195                  "Found recursive @copy%s or @copydoc relation for argument '%s'.\n",
7196                  isBrief?"brief":"details",id.data());
7197           }
7198         }
7199         else
7200         {
7201           warn_doc_error(g_fileName,doctokenizerYYlineno,
7202                "@copy%s or @copydoc target '%s' not found", isBrief?"brief":"details",
7203                id.data());
7204         }
7205         // skip over command
7206         i=j;
7207       }
7208       else
7209       {
7210         QCString endMarker;
7211         uint k = isVerbatimSection(data,i,len,endMarker);
7212         if (k>0)
7213         {
7214           int orgPos = i;
7215           i=skipToEndMarker(data,k,len,endMarker);
7216           buf.addStr(data+orgPos,i-orgPos);
7217         }
7218         else
7219         {
7220           buf.addChar(c);
7221           i++;
7222         }
7223       }
7224     }
7225     else // not a command, just copy
7226     {
7227       buf.addChar(c);
7228       i++;
7229     }
7230   }
7231   len = buf.getPos();
7232   buf.addChar(0);
7233   return buf.get();
7234 }
7235
7236 //--------------------------------------------------------------------------
7237
7238 DocRoot *validatingParseDoc(const char *fileName,int startLine,
7239                             Definition *ctx,MemberDef *md,
7240                             const char *input,bool indexWords,
7241                             bool isExample, const char *exampleName,
7242                             bool singleLine, bool linkFromIndex)
7243 {
7244   //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?ctx->name().data():"<none>",
7245   //                                     md?md->name().data():"<none>",
7246   //                                     input);
7247   //printf("========== validating %s at line %d\n",fileName,startLine);
7248   //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",input);
7249   //g_token = new TokenInfo;
7250
7251   // store parser state so we can re-enter this function if needed
7252   //bool fortranOpt = Config_getBool(OPTIMIZE_FOR_FORTRAN);
7253   docParserPushContext();
7254
7255   if (ctx && ctx!=Doxygen::globalScope &&
7256       (ctx->definitionType()==Definition::TypeClass || 
7257        ctx->definitionType()==Definition::TypeNamespace
7258       ) 
7259      ) 
7260   {
7261     g_context = ctx->name();
7262   }
7263   else if (ctx && ctx->definitionType()==Definition::TypePage)
7264   {
7265     Definition *scope = ((PageDef*)ctx)->getPageScope();
7266     if (scope && scope!=Doxygen::globalScope) g_context = scope->name();
7267   }
7268   else if (ctx && ctx->definitionType()==Definition::TypeGroup)
7269   {
7270     Definition *scope = ((GroupDef*)ctx)->getGroupScope();
7271     if (scope && scope!=Doxygen::globalScope) g_context = scope->name();
7272   }
7273   else
7274   {
7275     g_context = "";
7276   }
7277   g_scope = ctx;
7278
7279   if (indexWords && Doxygen::searchIndex)
7280   {
7281     if (md)
7282     {
7283       g_searchUrl=md->getOutputFileBase();
7284       Doxygen::searchIndex->setCurrentDoc(md,md->anchor(),FALSE);
7285     }
7286     else if (ctx)
7287     {
7288       g_searchUrl=ctx->getOutputFileBase();
7289       Doxygen::searchIndex->setCurrentDoc(ctx,ctx->anchor(),FALSE);
7290     }
7291   }
7292 #if 0
7293   if (indexWords && md && Doxygen::searchIndex)
7294   {
7295     g_searchUrl=md->getOutputFileBase();
7296     Doxygen::searchIndex->setCurrentDoc(
7297         (md->getLanguage()==SrcLangExt_Fortran ? 
7298          theTranslator->trSubprogram(TRUE,TRUE):
7299          theTranslator->trMember(TRUE,TRUE))+" "+md->qualifiedName(),
7300         g_searchUrl,
7301         md->anchor());
7302   }
7303   else if (indexWords && ctx && Doxygen::searchIndex)
7304   {
7305     g_searchUrl=ctx->getOutputFileBase();
7306     QCString name = ctx->qualifiedName();
7307
7308     SrcLangExt lang = ctx->getLanguage();
7309     QCString sep = getLanguageSpecificSeparator(lang);
7310     if (sep!="::")
7311     {
7312       name = substitute(name,"::",sep);
7313     }
7314
7315     switch (ctx->definitionType())
7316     {
7317       case Definition::TypePage:
7318         {
7319           PageDef *pd = (PageDef *)ctx;
7320           if (!pd->title().isEmpty())
7321           {
7322             name = theTranslator->trPage(TRUE,TRUE)+" "+pd->title();
7323           }
7324           else
7325           {
7326             name = theTranslator->trPage(TRUE,TRUE)+" "+pd->name();
7327           }
7328         }
7329         break;
7330       case Definition::TypeClass:
7331         {
7332           ClassDef *cd = (ClassDef *)ctx;
7333           name.prepend(cd->compoundTypeString()+" ");
7334         }
7335         break;
7336       case Definition::TypeNamespace:
7337         {
7338           if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
7339           {
7340             name = theTranslator->trPackage(name);
7341           }
7342           else if (lang==SrcLangExt_Fortran)
7343           {
7344             name.prepend(theTranslator->trModule(TRUE,TRUE)+" ");
7345           }
7346           else
7347           {
7348             name.prepend(theTranslator->trNamespace(TRUE,TRUE)+" ");
7349           }
7350         }
7351         break;
7352       case Definition::TypeGroup:
7353         {
7354           GroupDef *gd = (GroupDef *)ctx;
7355           if (gd->groupTitle())
7356           {
7357             name = theTranslator->trGroup(TRUE,TRUE)+" "+gd->groupTitle();
7358           }
7359           else
7360           {
7361             name.prepend(theTranslator->trGroup(TRUE,TRUE)+" ");
7362           }
7363         }
7364         break;
7365       default:
7366         break;
7367     }
7368     Doxygen::searchIndex->setCurrentDoc(name,g_searchUrl);
7369   }
7370 #endif
7371   else
7372   {
7373     g_searchUrl="";
7374   }
7375
7376   g_fileName = fileName;
7377   g_relPath = (!linkFromIndex && ctx) ? 
7378                QCString(relativePathToRoot(ctx->getOutputFileBase())) : 
7379                QCString("");
7380   //printf("ctx->name=%s relPath=%s\n",ctx->name().data(),g_relPath.data());
7381   g_memberDef = md;
7382   g_nodeStack.clear();
7383   g_styleStack.clear();
7384   g_initialStyleStack.clear();
7385   g_inSeeBlock = FALSE;
7386   g_xmlComment = FALSE;
7387   g_insideHtmlLink = FALSE;
7388   g_includeFileText = "";
7389   g_includeFileOffset = 0;
7390   g_includeFileLength = 0;
7391   g_isExample = isExample;
7392   g_exampleName = exampleName;
7393   g_hasParamCommand = FALSE;
7394   g_hasReturnCommand = FALSE;
7395   g_paramsFound.setAutoDelete(FALSE);
7396   g_paramsFound.clear();
7397   g_sectionDict = 0; //sections;
7398   
7399   //printf("Starting comment block at %s:%d\n",g_fileName.data(),startLine);
7400   doctokenizerYYlineno=startLine;
7401   uint inpLen=qstrlen(input);
7402   QCString inpStr = processCopyDoc(input,inpLen);
7403   if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
7404   {
7405     inpStr+='\n';
7406   }
7407   //printf("processCopyDoc(in='%s' out='%s')\n",input,inpStr.data());
7408   doctokenizerYYinit(inpStr,g_fileName);
7409
7410   // build abstract syntax tree
7411   DocRoot *root = new DocRoot(md!=0,singleLine);
7412   root->parse();
7413
7414
7415   if (Debug::isFlagSet(Debug::PrintTree))
7416   {
7417     // pretty print the result
7418     PrintDocVisitor *v = new PrintDocVisitor;
7419     root->accept(v);
7420     delete v;
7421   }
7422
7423   checkUndocumentedParams();
7424   detectNoDocumentedParams();
7425
7426   // TODO: These should be called at the end of the program.
7427   //doctokenizerYYcleanup();
7428   //Mappers::cmdMapper->freeInstance();
7429   //Mappers::htmlTagMapper->freeInstance();
7430
7431   // restore original parser state
7432   docParserPopContext();
7433
7434   //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?ctx->name().data():"<none>",
7435   //                                     md?md->name().data():"<none>");
7436   
7437   return root;
7438 }
7439
7440 DocText *validatingParseText(const char *input)
7441 {
7442   // store parser state so we can re-enter this function if needed
7443   docParserPushContext();
7444
7445   //printf("------------ input ---------\n%s\n"
7446   //       "------------ end input -----\n",input);
7447   //g_token = new TokenInfo;
7448   g_context = "";
7449   g_fileName = "<parseText>";
7450   g_relPath = "";
7451   g_memberDef = 0;
7452   g_nodeStack.clear();
7453   g_styleStack.clear();
7454   g_initialStyleStack.clear();
7455   g_inSeeBlock = FALSE;
7456   g_xmlComment = FALSE;
7457   g_insideHtmlLink = FALSE;
7458   g_includeFileText = "";
7459   g_includeFileOffset = 0;
7460   g_includeFileLength = 0;
7461   g_isExample = FALSE;
7462   g_exampleName = "";
7463   g_hasParamCommand = FALSE;
7464   g_hasReturnCommand = FALSE;
7465   g_paramsFound.setAutoDelete(FALSE);
7466   g_paramsFound.clear();
7467   g_searchUrl="";
7468
7469   DocText *txt = new DocText;
7470
7471   if (input)
7472   {
7473     doctokenizerYYlineno=1;
7474     doctokenizerYYinit(input,g_fileName);
7475
7476     // build abstract syntax tree
7477     txt->parse();
7478
7479     if (Debug::isFlagSet(Debug::PrintTree))
7480     {
7481       // pretty print the result
7482       PrintDocVisitor *v = new PrintDocVisitor;
7483       txt->accept(v);
7484       delete v;
7485     }
7486   }
7487
7488   // restore original parser state
7489   docParserPopContext();
7490   return txt;
7491 }
7492
7493 void docFindSections(const char *input,
7494                      Definition *d,
7495                      MemberGroup *mg,
7496                      const char *fileName)
7497 {
7498   doctokenizerYYFindSections(input,d,mg,fileName);
7499 }
7500