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