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