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