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