diff -r 932c358ece3e -r d8fccb2cd802 Orb/Doxygen/src/docparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Orb/Doxygen/src/docparser.cpp Fri Apr 23 20:47:58 2010 +0100 @@ -0,0 +1,6340 @@ +/****************************************************************************** + * + * + * + * + * Copyright (C) 1997-2008 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "doxygen.h" +#include "debug.h" +#include "util.h" +#include "pagedef.h" + +#include "docparser.h" +#include "doctokenizer.h" +#include "cmdmapper.h" +#include "printdocvisitor.h" +#include "message.h" +#include "section.h" +#include "searchindex.h" +#include "language.h" +#include "portable.h" + +// debug off +#define DBG(x) do {} while(0) + +// debug to stdout +//#define DBG(x) printf x + +// debug to stderr +//#define myprintf(x...) fprintf(stderr,x) +//#define DBG(x) myprintf x + +#define INTERNAL_ASSERT(x) do {} while(0) +//#define INTERNAL_ASSERT(x) if (!(x)) DBG(("INTERNAL_ASSERT(%s) failed retval=0x%x: file=%s line=%d\n",#x,retval,__FILE__,__LINE__)); + +//--------------------------------------------------------------------------- + +static const char *sectionLevelToName[] = +{ + "page", + "section", + "subsection", + "subsubsection", + "paragraph" +}; + +//--------------------------------------------------------------------------- + +// Parser state: global variables during a call to validatingParseDoc +static Definition * g_scope; +static QString g_context; +static bool g_inSeeBlock; +static bool g_insideHtmlLink; +static QStack g_nodeStack; +static QStack g_styleStack; +static QStack g_initialStyleStack; +static QList g_copyStack; +static QString g_fileName; +static QString g_relPath; + +static bool g_hasParamCommand; +static bool g_hasReturnCommand; +static QDict g_paramsFound; +static MemberDef * g_memberDef; +static bool g_isExample; +static QCString g_exampleName; +static SectionDict * g_sectionDict; +static QCString g_searchUrl; + +static QString g_includeFileText; +static uint g_includeFileOffset; +static uint g_includeFileLength; + +// parser's context to store all global variables +struct DocParserContext +{ + Definition *scope; + QString context; + bool inSeeBlock; + bool insideHtmlLink; + QStack nodeStack; + QStack styleStack; + QStack initialStyleStack; + QList copyStack; + QString fileName; + QString relPath; + + bool hasParamCommand; + bool hasReturnCommand; + MemberDef * memberDef; + QDict paramsFound; + bool isExample; + QCString exampleName; + SectionDict *sectionDict; + QCString searchUrl; + + QString includeFileText; + uint includeFileOffset; + uint includeFileLength; + + TokenInfo *token; +}; + +static QStack g_parserStack; + +//--------------------------------------------------------------------------- + +static void docParserPushContext(bool saveParamInfo=TRUE) +{ + //QCString indent; + //indent.fill(' ',g_parserStack.count()*2+2); + //printf("%sdocParserPushContext() count=%d\n",indent.data(),g_nodeStack.count()); + + doctokenizerYYpushContext(); + DocParserContext *ctx = new DocParserContext; + ctx->scope = g_scope; + ctx->context = g_context; + ctx->inSeeBlock = g_inSeeBlock; + ctx->insideHtmlLink = g_insideHtmlLink; + ctx->nodeStack = g_nodeStack; + ctx->styleStack = g_styleStack; + ctx->initialStyleStack = g_initialStyleStack; + ctx->copyStack = g_copyStack; + ctx->fileName = g_fileName; + ctx->relPath = g_relPath; + + if (saveParamInfo) + { + ctx->hasParamCommand = g_hasParamCommand; + ctx->hasReturnCommand = g_hasReturnCommand; + ctx->paramsFound = g_paramsFound; + } + + ctx->memberDef = g_memberDef; + ctx->isExample = g_isExample; + ctx->exampleName = g_exampleName; + ctx->sectionDict = g_sectionDict; + ctx->searchUrl = g_searchUrl; + + ctx->includeFileText = g_includeFileText; + ctx->includeFileOffset = g_includeFileOffset; + ctx->includeFileLength = g_includeFileLength; + + ctx->token = g_token; + g_token = new TokenInfo; + + g_parserStack.push(ctx); +} + +static void docParserPopContext(bool keepParamInfo=FALSE) +{ + DocParserContext *ctx = g_parserStack.pop(); + g_scope = ctx->scope; + g_context = ctx->context; + g_inSeeBlock = ctx->inSeeBlock; + g_insideHtmlLink = ctx->insideHtmlLink; + g_nodeStack = ctx->nodeStack; + g_styleStack = ctx->styleStack; + g_initialStyleStack = ctx->initialStyleStack; + g_copyStack = ctx->copyStack; + g_fileName = ctx->fileName; + g_relPath = ctx->relPath; + + if (!keepParamInfo) + { + g_hasParamCommand = ctx->hasParamCommand; + g_hasReturnCommand = ctx->hasReturnCommand; + g_paramsFound = ctx->paramsFound; + } + g_memberDef = ctx->memberDef; + g_isExample = ctx->isExample; + g_exampleName = ctx->exampleName; + g_sectionDict = ctx->sectionDict; + g_searchUrl = ctx->searchUrl; + + g_includeFileText = ctx->includeFileText; + g_includeFileOffset = ctx->includeFileOffset; + g_includeFileLength = ctx->includeFileLength; + + delete g_token; + g_token = ctx->token; + + delete ctx; + doctokenizerYYpopContext(); + + //QCString indent; + //indent.fill(' ',g_parserStack.count()*2+2); + //printf("%sdocParserPopContext() count=%d\n",indent.data(),g_nodeStack.count()); +} + +//--------------------------------------------------------------------------- + +/*! search for an image in the imageNameDict and if found + * copies the image to the output directory (which depends on the \a type + * parameter). + */ +static QCString findAndCopyImage(const char *fileName,DocImage::Type type) +{ + QCString result; + bool ambig; + FileDef *fd; + //printf("Search for %s\n",fileName); + if ((fd=findFileDef(Doxygen::imageNameDict,fileName,ambig))) + { + QCString inputFile = fd->absFilePath(); + QFile inImage(inputFile); + if (inImage.open(IO_ReadOnly)) + { + result = fileName; + int i; + if ((i=result.findRev('/'))!=-1 || (i=result.findRev('\\'))!=-1) + { + result = result.right(result.length()-i-1); + } + //printf("fileName=%s result=%s\n",fileName,result.data()); + QCString outputDir; + switch(type) + { + case DocImage::Html: + if (!Config_getBool("GENERATE_HTML")) return result; + outputDir = Config_getString("HTML_OUTPUT"); + break; + case DocImage::Latex: + if (!Config_getBool("GENERATE_LATEX")) return result; + outputDir = Config_getString("LATEX_OUTPUT"); + break; + case DocImage::Rtf: + if (!Config_getBool("GENERATE_RTF")) return result; + outputDir = Config_getString("RTF_OUTPUT"); + break; + } + QCString outputFile = outputDir+"/"+result; + if (outputFile!=inputFile) // prevent copying to ourself + { + QFile outImage(outputFile.data()); + if (outImage.open(IO_WriteOnly)) // copy the image + { + char *buffer = new char[inImage.size()]; + inImage.readBlock(buffer,inImage.size()); + outImage.writeBlock(buffer,inImage.size()); + outImage.flush(); + delete[] buffer; + if (type==DocImage::Html) Doxygen::indexList.addImageFile(result); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno, + "Warning: could not write output image %s",outputFile.data()); + } + } + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno, + "Warning: could not open image %s",fileName); + } + + if (type==DocImage::Latex && Config_getBool("USE_PDFLATEX") && + fd->name().right(4)==".eps" + ) + { // we have an .eps image in pdflatex mode => convert it to a pdf. + QCString outputDir = Config_getString("LATEX_OUTPUT"); + QCString baseName = fd->name().left(fd->name().length()-4); + QCString epstopdfArgs(4096); + epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"", + outputDir.data(), baseName.data(), + outputDir.data(), baseName.data()); + if (portable_system("epstopdf",epstopdfArgs)!=0) + { + err("Error: Problems running epstopdf. Check your TeX installation!\n"); + } + return baseName; + } + } + else if (ambig) + { + QCString text; + text.sprintf("Warning: image file name %s is ambigious.\n",fileName); + text+="Possible candidates:\n"; + text+=showFileDefMatches(Doxygen::imageNameDict,fileName); + warn_doc_error(g_fileName,doctokenizerYYlineno,text); + } + else + { + result=fileName; + if (result.left(5)!="http:" && result.left(6)!="https:") + { + warn_doc_error(g_fileName,doctokenizerYYlineno, + "Warning: image file %s is not found in IMAGE_PATH: " + "assuming external image.",fileName + ); + } + } + return result; +} + +/*! Collects the parameters found with \@param or \@retval commands + * in a global list g_paramsFound. If \a isParam is set to TRUE + * and the parameter is not an actual parameter of the current + * member g_memberDef, then a warning is raised (unless warnings + * are disabled altogether). + */ +static void checkArgumentName(const QString &name,bool isParam) +{ + if (!Config_getBool("WARN_IF_DOC_ERROR")) return; + if (g_memberDef==0) return; // not a member + LockingPtr al=g_memberDef->isDocsForDefinition() ? + g_memberDef->argumentList() : + g_memberDef->declArgumentList(); + //printf("isDocsForDefinition()=%d\n",g_memberDef->isDocsForDefinition()); + if (al==0) return; // no argument list + + static QRegExp re("[a-zA-Z0-9_\\x80-\\xFF]+\\.*"); + int p=0,i=0,l; + while ((i=re.match(name,p,&l))!=-1) // to handle @param x,y + { + QString aName=name.mid(i,l); + //printf("aName=`%s'\n",aName.data()); + ArgumentListIterator ali(*al); + Argument *a; + bool found=FALSE; + for (ali.toFirst();(a=ali.current());++ali) + { + QString argName = g_memberDef->isDefine() ? a->type : a->name; + argName=argName.stripWhiteSpace(); + //printf("argName=`%s'\n",argName.data()); + if (argName.right(3)=="...") argName=argName.left(argName.length()-3); + if (aName==argName) + { + //printf("adding `%s'\n",aName.data()); + g_paramsFound.insert(aName,(void *)(0x8)); + found=TRUE; + break; + } + } + if (!found && isParam) + { + //printf("member type=%d\n",memberDef->memberType()); + QString scope=g_memberDef->getScopeString(); + if (!scope.isEmpty()) scope+="::"; else scope=""; + QString inheritedFrom = ""; + QString docFile = g_memberDef->docFile(); + int docLine = g_memberDef->docLine(); + MemberDef *inheritedMd = g_memberDef->inheritsDocsFrom(); + if (inheritedMd) // documentation was inherited + { + inheritedFrom.sprintf(" inherited from member %s at line " + "%d in file %s",inheritedMd->name().data(), + inheritedMd->docLine(),inheritedMd->docFile().data()); + docFile = g_memberDef->getDefFileName(); + docLine = g_memberDef->getDefLine(); + + } + warn_doc_error(docFile,docLine, + "Warning: argument '%s' of command @param " + "is not found in the argument list of %s%s%s%s", + aName.data(),scope.data(),g_memberDef->name().data(), + argListToString(al.pointer()).data(),inheritedFrom.data()); + } + p=i+l; + } +} + +/*! Checks if the parameters that have been specified using \@param are + * indeed all paramters. + * Must be called after checkArgumentName() has been called for each + * argument. + */ +static void checkUndocumentedParams() +{ + if (g_memberDef && g_hasParamCommand && Config_getBool("WARN_IF_DOC_ERROR")) + { + LockingPtr al=g_memberDef->isDocsForDefinition() ? + g_memberDef->argumentList() : + g_memberDef->declArgumentList(); + if (al!=0) + { + ArgumentListIterator ali(*al); + Argument *a; + bool found=FALSE; + for (ali.toFirst();(a=ali.current());++ali) + { + QString argName = g_memberDef->isDefine() ? a->type : a->name; + argName=argName.stripWhiteSpace(); + if (argName.right(3)=="...") argName=argName.left(argName.length()-3); + if (getLanguageFromFileName(g_memberDef->getDefFileName())==SrcLangExt_Python && argName=="self") + { + // allow undocumented self parameter for Python + } + else if (!argName.isEmpty() && g_paramsFound.find(argName)==0 && a->docs.isEmpty()) + { + found = TRUE; + break; + } + } + if (found) + { + bool first=TRUE; + QString errMsg= + "Warning: The following parameters of "+ + QString(g_memberDef->qualifiedName()) + + QString(argListToString(al.pointer())) + + " are not documented:\n"; + for (ali.toFirst();(a=ali.current());++ali) + { + QString argName = g_memberDef->isDefine() ? a->type : a->name; + argName=argName.stripWhiteSpace(); + if (getLanguageFromFileName(g_memberDef->getDefFileName())==SrcLangExt_Python && argName=="self") + { + // allow undocumented self parameter for Python + } + else if (!argName.isEmpty() && g_paramsFound.find(argName)==0) + { + if (!first) + { + errMsg+="\n"; + } + else + { + first=FALSE; + } + errMsg+=" parameter '"+argName+"'"; + } + } + if (g_memberDef->inheritsDocsFrom()) + { + warn_doc_error(g_memberDef->getDefFileName(), + g_memberDef->getDefLine(), + substitute(errMsg,"%","%%")); + } + else + { + warn_doc_error(g_memberDef->docFile(), + g_memberDef->docLine(), + substitute(errMsg,"%","%%")); + } + } + } + } +} + +/*! Check if a member has documentation for its parameter and or return + * type, if applicable. If found this will be stored in the member, this + * is needed as a member can have brief and detailed documentation, while + * only one of these needs to document the parameters. + */ +static void detectNoDocumentedParams() +{ + if (g_memberDef && Config_getBool("WARN_NO_PARAMDOC")) + { + LockingPtr al = g_memberDef->argumentList(); + LockingPtr declAl = g_memberDef->declArgumentList(); + QString returnType = g_memberDef->typeString(); + bool isPython = getLanguageFromFileName(g_memberDef->getDefFileName())==SrcLangExt_Python; + + if (!g_memberDef->hasDocumentedParams() && + g_hasParamCommand) + { + //printf("%s->setHasDocumentedParams(TRUE);\n",g_memberDef->name().data()); + g_memberDef->setHasDocumentedParams(TRUE); + } + else if (!g_memberDef->hasDocumentedParams()) + { + bool allDoc=TRUE; // no paramater => all parameters are documented + if ( // member has parameters + al!=0 && // but the member has a parameter list + al->count()>0 // with at least one parameter (that is not void) + ) + { + ArgumentListIterator ali(*al); + Argument *a; + + // see if all parameters have documentation + for (ali.toFirst();(a=ali.current()) && allDoc;++ali) + { + if (!a->name.isEmpty() && a->type!="void" && + !(isPython && a->name=="self") + ) + { + allDoc = !a->docs.isEmpty(); + } + //printf("a->type=%s a->name=%s doc=%s\n", + // a->type.data(),a->name.data(),a->docs.data()); + } + if (!allDoc && declAl!=0) // try declaration arguments as well + { + allDoc=TRUE; + ArgumentListIterator ali(*declAl); + Argument *a; + for (ali.toFirst();(a=ali.current()) && allDoc;++ali) + { + if (!a->name.isEmpty() && a->type!="void" && + !(isPython && a->name=="self") + ) + { + allDoc = !a->docs.isEmpty(); + } + //printf("a->name=%s doc=%s\n",a->name.data(),a->docs.data()); + } + } + } + if (allDoc) + { + //printf("%s->setHasDocumentedParams(TRUE);\n",g_memberDef->name().data()); + g_memberDef->setHasDocumentedParams(TRUE); + } + } + //printf("Member %s hasReturnCommand=%d\n",g_memberDef->name().data(),g_hasReturnCommand); + if (!g_memberDef->hasDocumentedReturnType() && // docs not yet found + g_hasReturnCommand) + { + g_memberDef->setHasDocumentedReturnType(TRUE); + } + else if ( // see if return needs to documented + g_memberDef->hasDocumentedReturnType() || + returnType.isEmpty() || // empty return type + returnType.find("void")!=-1 || // void return type + !g_memberDef->isConstructor() || // a constructor + !g_memberDef->isDestructor() // or destructor + ) + { + g_memberDef->setHasDocumentedReturnType(TRUE); + } + + } +} + + +//--------------------------------------------------------------------------- + +/*! Strips known html and tex extensions from \a text. */ +static QString stripKnownExtensions(const char *text) +{ + QString result=text; + if (result.right(4)==".tex") + { + result=result.left(result.length()-4); + } + else if (result.right(Doxygen::htmlFileExtension.length())== + QString(Doxygen::htmlFileExtension)) + { + result=result.left(result.length()-Doxygen::htmlFileExtension.length()); + } + return result; +} + + +//--------------------------------------------------------------------------- + +/*! Returns TRUE iff node n is a child of a preformatted node */ +static bool insidePRE(DocNode *n) +{ + while (n) + { + if (n->isPreformatted()) return TRUE; + n=n->parent(); + } + return FALSE; +} + +//--------------------------------------------------------------------------- + +/*! Returns TRUE iff node n is a child of a html list item node */ +static bool insideLI(DocNode *n) +{ + while (n) + { + if (n->kind()==DocNode::Kind_HtmlListItem) return TRUE; + n=n->parent(); + } + return FALSE; +} + +//--------------------------------------------------------------------------- + +/*! Returns TRUE iff node n is a child of a unordered html list node */ +static bool insideUL(DocNode *n) +{ + while (n) + { + if (n->kind()==DocNode::Kind_HtmlList && + ((DocHtmlList *)n)->type()==DocHtmlList::Unordered) return TRUE; + n=n->parent(); + } + return FALSE; +} + +//--------------------------------------------------------------------------- + +/*! Returns TRUE iff node n is a child of a ordered html list node */ +static bool insideOL(DocNode *n) +{ + while (n) + { + if (n->kind()==DocNode::Kind_HtmlList && + ((DocHtmlList *)n)->type()==DocHtmlList::Ordered) return TRUE; + n=n->parent(); + } + return FALSE; +} + +//--------------------------------------------------------------------------- + +static bool insideTable(DocNode *n) +{ + while (n) + { + if (n->kind()==DocNode::Kind_HtmlTable) return TRUE; + n=n->parent(); + } + return FALSE; +} + +//--------------------------------------------------------------------------- + +///*! Returns TRUE iff node n is a child of a language node */ +//static bool insideLang(DocNode *n) +//{ +// while (n) +// { +// if (n->kind()==DocNode::Kind_Language) return TRUE; +// n=n->parent(); +// } +// return FALSE; +//} + + +//--------------------------------------------------------------------------- + +/*! Looks for a documentation block with name commandName in the current + * context (g_context). The resulting documentation string is + * put in pDoc, the definition in which the documentation was found is + * put in pDef. + * @retval TRUE if name was found. + * @retval FALSE if name was not found. + */ +static bool findDocsForMemberOrCompound(const char *commandName, + QString *pDoc, + QString *pBrief, + Definition **pDef) +{ + //printf("findDocsForMemberOrCompound(%s)\n",commandName); + *pDoc=""; + *pBrief=""; + *pDef=0; + QString cmdArg=substitute(commandName,"#","::"); + int l=cmdArg.length(); + if (l==0) return FALSE; + + int funcStart=cmdArg.find('('); + if (funcStart==-1) funcStart=l; + + QString name=removeRedundantWhiteSpace(cmdArg.left(funcStart).latin1()); + QString args=cmdArg.right(l-funcStart); + + // try if the link is to a member + MemberDef *md=0; + ClassDef *cd=0; + FileDef *fd=0; + NamespaceDef *nd=0; + GroupDef *gd=0; + PageDef *pd=0; + bool found = getDefs( + g_context.find('.')==-1?g_context.latin1():"", // `find('.') is a hack to detect files + name.latin1(), + args.isEmpty()?0:args.latin1(), + md,cd,fd,nd,gd,FALSE,0,TRUE); + //printf("found=%d context=%s name=%s\n",found,g_context.data(),name.data()); + if (found && md) + { + *pDoc=md->documentation(); + *pBrief=md->briefDescription(); + *pDef=md; + return TRUE; + } + + + int scopeOffset=g_context.length(); + do // for each scope + { + QString fullName=cmdArg; + if (scopeOffset>0) + { + fullName.prepend(g_context.left(scopeOffset)+"::"); + } + //printf("Trying fullName=`%s'\n",fullName.data()); + + // try class, namespace, group, page, file reference + cd = Doxygen::classSDict->find(fullName); + if (cd) // class + { + *pDoc=cd->documentation(); + *pBrief=cd->briefDescription(); + *pDef=cd; + return TRUE; + } + nd = Doxygen::namespaceSDict->find(fullName); + if (nd) // namespace + { + *pDoc=nd->documentation(); + *pBrief=nd->briefDescription(); + *pDef=nd; + return TRUE; + } + gd = Doxygen::groupSDict->find(cmdArg); + if (gd) // group + { + *pDoc=gd->documentation(); + *pBrief=gd->briefDescription(); + *pDef=gd; + return TRUE; + } + pd = Doxygen::pageSDict->find(cmdArg); + if (pd) // page + { + *pDoc=pd->documentation(); + *pBrief=pd->briefDescription(); + *pDef=pd; + return TRUE; + } + bool ambig; + fd = findFileDef(Doxygen::inputNameDict,cmdArg,ambig); + if (fd && !ambig) // file + { + *pDoc=fd->documentation(); + *pBrief=fd->briefDescription(); + *pDef=fd; + return TRUE; + } + + if (scopeOffset==0) + { + scopeOffset=-1; + } + else + { + scopeOffset = g_context.findRev("::",scopeOffset-1); + if (scopeOffset==-1) scopeOffset=0; + } + } while (scopeOffset>=0); + + + return FALSE; +} +//--------------------------------------------------------------------------- + +// forward declaration +static bool defaultHandleToken(DocNode *parent,int tok, + QList &children,bool + handleWord=TRUE); + + +static int handleStyleArgument(DocNode *parent,QList &children, + const QString &cmdName) +{ + DBG(("handleStyleArgument(%s)\n",cmdName.data())); + QString tokenName = g_token->name; + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return tok; + } + while ((tok=doctokenizerYYlex()) && + tok!=TK_WHITESPACE && + tok!=TK_NEWPARA && + tok!=TK_LISTITEM && + tok!=TK_ENDLIST + ) + { + static QRegExp specialChar("[.,|()\\[\\]:;\\?]"); + if (tok==TK_WORD && g_token->name.length()==1 && + g_token->name.find(specialChar)!=-1) + { + // special character that ends the markup command + return tok; + } + if (!defaultHandleToken(parent,tok,children)) + { + switch (tok) + { + case TK_COMMAND: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command \\%s as the argument of a \\%s command", + g_token->name.data(),cmdName.data()); + break; + case TK_SYMBOL: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found while handling command %s", + g_token->name.data(),cmdName.data()); + break; + case TK_HTMLTAG: + if (insideLI(parent) && Mappers::htmlTagMapper->map(g_token->name) && g_token->endTag) + { // ignore as the end of a style command + continue; + } + return tok; + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s while handling command %s", + tokToString(tok),cmdName.data()); + break; + } + break; + } + } + DBG(("handleStyleArgument(%s) end tok=%x\n",cmdName.data(),tok)); + return (tok==TK_NEWPARA || tok==TK_LISTITEM || tok==TK_ENDLIST + ) ? tok : RetVal_OK; +} + +/*! Called when a style change starts. For instance a \ command is + * encountered. + */ +static void handleStyleEnter(DocNode *parent,QList &children, + DocStyleChange::Style s,const HtmlAttribList *attribs) +{ + DBG(("HandleStyleEnter\n")); + DocStyleChange *sc= new DocStyleChange(parent,g_nodeStack.count(),s,TRUE,attribs); + children.append(sc); + g_styleStack.push(sc); +} + +/*! Called when a style change ends. For instance a \ command is + * encountered. + */ +static void handleStyleLeave(DocNode *parent,QList &children, + DocStyleChange::Style s,const char *tagName) +{ + DBG(("HandleStyleLeave\n")); + if (g_styleStack.isEmpty() || // no style change + g_styleStack.top()->style()!=s || // wrong style change + g_styleStack.top()->position()!=g_nodeStack.count() // wrong position + ) + { + if (g_styleStack.isEmpty()) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag without matching <%s>", + tagName,tagName); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag while expecting ", + tagName,g_styleStack.top()->styleString()); + } + } + else // end the section + { + DocStyleChange *sc= new DocStyleChange(parent,g_nodeStack.count(),s,FALSE); + children.append(sc); + g_styleStack.pop(); + } +} + +/*! Called at the end of a paragraph to close all open style changes + * (e.g. a without a ). The closed styles are pushed onto a stack + * and entered again at the start of a new paragraph. + */ +static void handlePendingStyleCommands(DocNode *parent,QList &children) +{ + if (!g_styleStack.isEmpty()) + { + DocStyleChange *sc = g_styleStack.top(); + while (sc && sc->position()>=g_nodeStack.count()) + { // there are unclosed style modifiers in the paragraph + children.append(new DocStyleChange(parent,g_nodeStack.count(),sc->style(),FALSE)); + g_initialStyleStack.push(sc); + g_styleStack.pop(); + sc = g_styleStack.top(); + } + } +} + +static void handleInitialStyleCommands(DocPara *parent,QList &children) +{ + DocStyleChange *sc; + while ((sc=g_initialStyleStack.pop())) + { + handleStyleEnter(parent,children,sc->style(),&sc->attribs()); + } +} + +static int handleAHref(DocNode *parent,QList &children,const HtmlAttribList &tagHtmlAttribs) +{ + HtmlAttribListIterator li(tagHtmlAttribs); + HtmlAttrib *opt; + int index=0; + int retval = RetVal_OK; + for (li.toFirst();(opt=li.current());++li,++index) + { + if (opt->name=="name") // tag + { + if (!opt->value.isEmpty()) + { + DocAnchor *anc = new DocAnchor(parent,opt->value,TRUE); + children.append(anc); + break; // stop looking for other tag attribs + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag with name option but without value!"); + } + } + else if (opt->name=="href") // .. tag + { + // copy attributes + HtmlAttribList attrList = tagHtmlAttribs; + // and remove the href attribute + bool result = attrList.remove(index); + ASSERT(result); + DocHRef *href = new DocHRef(parent,attrList,opt->value); + children.append(href); + g_insideHtmlLink=TRUE; + retval = href->parse(); + g_insideHtmlLink=FALSE; + break; + } + else // unsupported option for tag a + { + } + } + return retval; +} + +const char *DocStyleChange::styleString() const +{ + switch (m_style) + { + case DocStyleChange::Bold: return "b"; + case DocStyleChange::Italic: return "em"; + case DocStyleChange::Code: return "code"; + case DocStyleChange::Center: return "center"; + case DocStyleChange::Small: return "small"; + case DocStyleChange::Subscript: return "subscript"; + case DocStyleChange::Superscript: return "superscript"; + case DocStyleChange::Preformatted: return "pre"; + case DocStyleChange::Div: return "div"; + case DocStyleChange::Span: return "span"; + } + return ""; +} + +static void handleUnclosedStyleCommands() +{ + if (!g_initialStyleStack.isEmpty()) + { + DocStyleChange *sc = g_initialStyleStack.top(); + g_initialStyleStack.pop(); + handleUnclosedStyleCommands(); + warn_doc_error(g_fileName,doctokenizerYYlineno, + "Warning: end of comment block while expecting " + "command ",sc->styleString()); + } +} + + +static void handleLinkedWord(DocNode *parent,QList &children) +{ + Definition *compound=0; + MemberDef *member=0; + QString name = linkToText(g_token->name,TRUE); + int len = g_token->name.length(); + ClassDef *cd=0; + bool ambig; + FileDef *fd = findFileDef(Doxygen::inputNameDict,g_fileName,ambig); + //printf("handleLinkedWord(%s) g_context=%s\n",name.data(),g_context.data()); + if (!g_insideHtmlLink && + (resolveRef(g_context,g_token->name,g_inSeeBlock,&compound,&member,TRUE,fd) + || (!g_context.isEmpty() && // also try with global scope + resolveRef("",g_token->name,g_inSeeBlock,&compound,&member)) + ) + ) + { + //printf("resolveRef %s = %p (linkable?=%d)\n",g_token->name.data(),member,member ? member->isLinkable() : FALSE); + if (member && member->isLinkable()) // member link + { + if (member->isObjCMethod()) + { + bool localLink = g_memberDef ? member->getClassDef()==g_memberDef->getClassDef() : FALSE; + name = member->objCMethodName(localLink,g_inSeeBlock); + } + children.append(new + DocLinkedWord(parent,name, + member->getReference(), + member->getOutputFileBase(), + member->anchor(), + member->briefDescriptionAsTooltip() + ) + ); + } + else if (compound->isLinkable()) // compound link + { + if (compound->definitionType()==Definition::TypeFile) + { + name=g_token->name; + } + else if (compound->definitionType()==Definition::TypeGroup) + { + name=((GroupDef*)compound)->groupTitle(); + } + children.append(new + DocLinkedWord(parent,name, + compound->getReference(), + compound->getOutputFileBase(), + "", + compound->briefDescriptionAsTooltip() + ) + ); + } + else if (compound->definitionType()==Definition::TypeFile && + ((FileDef*)compound)->generateSourceFile() + ) // undocumented file that has source code we can link to + { + children.append(new + DocLinkedWord(parent,g_token->name, + compound->getReference(), + compound->getSourceFileBase(), + "", + compound->briefDescriptionAsTooltip() + ) + ); + } + else // not linkable + { + children.append(new DocWord(parent,name)); + } + } + else if (!g_insideHtmlLink && len>1 && g_token->name.at(len-1)==':') + { + // special case, where matching Foo: fails to be an Obj-C reference, + // but Foo itself might be linkable. + g_token->name=g_token->name.left(len-1); + handleLinkedWord(parent,children); + children.append(new DocWord(parent,":")); + } + else if (!g_insideHtmlLink && (cd=getClass(g_token->name+"-p"))) + { + // special case 2, where the token name is not a class, but could + // be a Obj-C protocol + children.append(new + DocLinkedWord(parent,name, + cd->getReference(), + cd->getOutputFileBase(), + "", + cd->briefDescriptionAsTooltip() + )); + } + else // normal non-linkable word + { + if (g_token->name.at(0)=='#') + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: explicit link request to '%s' could not be resolved",name.data()); + } + children.append(new DocWord(parent,name)); + } +} + +/* Helper function that deals with the most common tokens allowed in + * title like sections. + * @param parent Parent node, owner of the children list passed as + * the third argument. + * @param tok The token to process. + * @param children The list of child nodes to which the node representing + * the token can be added. + * @param handleWord Indicates if word token should be processed + * @retval TRUE The token was handled. + * @retval FALSE The token was not handled. + */ +static bool defaultHandleToken(DocNode *parent,int tok, QList &children,bool + handleWord) +{ + DBG(("token %s at %d",tokToString(tok),doctokenizerYYlineno)); + if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || + tok==TK_COMMAND || tok==TK_HTMLTAG + ) + { + DBG((" name=%s",g_token->name.data())); + } + DBG(("\n")); +reparsetoken: + QString tokenName = g_token->name; + switch (tok) + { + case TK_COMMAND: + switch (Mappers::cmdMapper->map(tokenName)) + { + case CMD_BSLASH: + children.append(new DocSymbol(parent,DocSymbol::BSlash)); + break; + case CMD_AT: + children.append(new DocSymbol(parent,DocSymbol::At)); + break; + case CMD_LESS: + children.append(new DocSymbol(parent,DocSymbol::Less)); + break; + case CMD_GREATER: + children.append(new DocSymbol(parent,DocSymbol::Greater)); + break; + case CMD_AMP: + children.append(new DocSymbol(parent,DocSymbol::Amp)); + break; + case CMD_DOLLAR: + children.append(new DocSymbol(parent,DocSymbol::Dollar)); + break; + case CMD_HASH: + children.append(new DocSymbol(parent,DocSymbol::Hash)); + break; + case CMD_PERCENT: + children.append(new DocSymbol(parent,DocSymbol::Percent)); + break; + case CMD_QUOTE: + children.append(new DocSymbol(parent,DocSymbol::Quot)); + break; + case CMD_EMPHASIS: + { + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Italic,TRUE)); + tok=handleStyleArgument(parent,children,tokenName); + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Italic,FALSE)); + if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," ")); + if (tok==TK_NEWPARA) goto handlepara; + else if (tok==TK_WORD || tok==TK_HTMLTAG) + { + DBG(("CMD_EMPHASIS: reparsing command %s\n",g_token->name.data())); + goto reparsetoken; + } + } + break; + case CMD_BOLD: + { + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Bold,TRUE)); + tok=handleStyleArgument(parent,children,tokenName); + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Bold,FALSE)); + if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," ")); + if (tok==TK_NEWPARA) goto handlepara; + else if (tok==TK_WORD || tok==TK_HTMLTAG) + { + DBG(("CMD_BOLD: reparsing command %s\n",g_token->name.data())); + goto reparsetoken; + } + } + break; + case CMD_CODE: + { + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Code,TRUE)); + tok=handleStyleArgument(parent,children,tokenName); + children.append(new DocStyleChange(parent,g_nodeStack.count(),DocStyleChange::Code,FALSE)); + if (tok!=TK_WORD) children.append(new DocWhiteSpace(parent," ")); + if (tok==TK_NEWPARA) goto handlepara; + else if (tok==TK_WORD || tok==TK_HTMLTAG) + { + DBG(("CMD_CODE: reparsing command %s\n",g_token->name.data())); + goto reparsetoken; + } + } + break; + case CMD_HTMLONLY: + { + doctokenizerYYsetStateHtmlOnly(); + tok = doctokenizerYYlex(); + children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::HtmlOnly,g_isExample,g_exampleName)); + if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: htmlonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_MANONLY: + { + doctokenizerYYsetStateManOnly(); + tok = doctokenizerYYlex(); + children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::ManOnly,g_isExample,g_exampleName)); + if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: manonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_LATEXONLY: + { + doctokenizerYYsetStateLatexOnly(); + tok = doctokenizerYYlex(); + children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::LatexOnly,g_isExample,g_exampleName)); + if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: latexonly section ended without end marker",doctokenizerYYlineno); + doctokenizerYYsetStatePara(); + } + break; + case CMD_XMLONLY: + { + doctokenizerYYsetStateXmlOnly(); + tok = doctokenizerYYlex(); + children.append(new DocVerbatim(parent,g_context,g_token->verb,DocVerbatim::XmlOnly,g_isExample,g_exampleName)); + if (tok==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: xmlonly section ended without end marker",doctokenizerYYlineno); + doctokenizerYYsetStatePara(); + } + break; + case CMD_FORMULA: + { + DocFormula *form=new DocFormula(parent,g_token->id); + children.append(form); + } + break; + case CMD_ANCHOR: + { + tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + tokenName.data()); + break; + } + tok=doctokenizerYYlex(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s",tokenName.data()); + break; + } + else if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),tokenName.data()); + break; + } + DocAnchor *anchor = new DocAnchor(parent,g_token->name,FALSE); + children.append(anchor); + } + break; + case CMD_INTERNALREF: + { + tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + tokenName.data()); + break; + } + doctokenizerYYsetStateInternalRef(); + tok=doctokenizerYYlex(); // get the reference id + DocInternalRef *ref=0; + if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),tokenName.data()); + doctokenizerYYsetStatePara(); + break; + } + ref = new DocInternalRef(parent,g_token->name); + children.append(ref); + ref->parse(); + doctokenizerYYsetStatePara(); + } + break; + default: + return FALSE; + } + break; + case TK_HTMLTAG: + { + switch (Mappers::htmlTagMapper->map(tokenName)) + { + case HTML_DIV: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found
tag in heading\n"); + break; + case HTML_PRE: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found
 tag in heading\n");
+            break;
+          case HTML_BOLD:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Bold,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Bold,tokenName);
+            }
+            break;
+          case HTML_CODE:
+          case XML_C:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Code,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Code,tokenName);
+            }
+            break;
+          case HTML_EMPHASIS:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Italic,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Italic,tokenName);
+            }
+            break;
+          case HTML_SUB:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Subscript,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Subscript,tokenName);
+            }
+            break;
+          case HTML_SUP:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Superscript,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Superscript,tokenName);
+            }
+            break;
+          case HTML_CENTER:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Center,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Center,tokenName);
+            }
+            break;
+          case HTML_SMALL:
+            if (!g_token->endTag)
+            {
+              handleStyleEnter(parent,children,DocStyleChange::Small,&g_token->attribs);
+            }
+            else
+            {
+              handleStyleLeave(parent,children,DocStyleChange::Small,tokenName);
+            }
+            break;
+          default:
+            return FALSE;
+            break;
+        }
+      }
+      break;
+    case TK_SYMBOL: 
+      {
+        char letter='\0';
+        DocSymbol::SymType s = DocSymbol::decodeSymbol(tokenName,&letter);
+        if (s!=DocSymbol::Unknown)
+        {
+          children.append(new DocSymbol(parent,s,letter));
+        }
+        else
+        {
+          return FALSE;
+        }
+      }
+      break;
+    case TK_WHITESPACE: 
+    case TK_NEWPARA: 
+handlepara:
+      if (insidePRE(parent) || !children.isEmpty())
+      {
+        children.append(new DocWhiteSpace(parent,g_token->chars));
+      }
+      break;
+    case TK_LNKWORD: 
+      if (handleWord)
+      {
+        handleLinkedWord(parent,children);
+      }
+      else
+        return FALSE;
+      break;
+    case TK_WORD: 
+      if (handleWord)
+      {
+        children.append(new DocWord(parent,g_token->name));
+      }
+      else
+        return FALSE;
+      break;
+    case TK_URL:
+      if (g_insideHtmlLink)
+      {
+        children.append(new DocWord(parent,g_token->name));
+      }
+      else
+      {
+        children.append(new DocURL(parent,g_token->name,g_token->isEMailAddr));
+      }
+      break;
+    default:
+      return FALSE;
+  }
+  return TRUE;
+}
+
+
+//---------------------------------------------------------------------------
+
+DocSymbol::SymType DocSymbol::decodeSymbol(const QString &symName,char *letter)
+{
+  int l=symName.length();
+  DBG(("decodeSymbol(%s) l=%d\n",symName.data(),l));
+  if      (symName=="©")  return DocSymbol::Copy;
+  else if (symName=="™") return DocSymbol::Tm;
+  else if (symName=="&tm;")    return DocSymbol::Tm; // alias for ™
+  else if (symName=="®")   return DocSymbol::Reg;
+  else if (symName=="<")    return DocSymbol::Less;
+  else if (symName==">")    return DocSymbol::Greater;
+  else if (symName=="&")   return DocSymbol::Amp;
+  else if (symName=="'")  return DocSymbol::Apos;
+  else if (symName==""")  return DocSymbol::Quot;
+  else if (symName=="‘") return DocSymbol::Lsquo;
+  else if (symName=="’") return DocSymbol::Rsquo;
+  else if (symName=="“") return DocSymbol::Ldquo;
+  else if (symName=="”") return DocSymbol::Rdquo;
+  else if (symName=="–") return DocSymbol::Ndash;
+  else if (symName=="—") return DocSymbol::Mdash;
+  else if (symName=="ß") return DocSymbol::Szlig;
+  else if (symName==" ")  return DocSymbol::Nbsp;
+  else if (symName=="Æ") return DocSymbol::AElig;
+  else if (symName=="æ") return DocSymbol::Aelig;
+  else if (l==6 && symName.right(4)=="uml;")  
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Uml;
+  }
+  else if (l==8 && symName.right(6)=="acute;")  
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Acute;
+  }
+  else if (l==8 && symName.right(6)=="grave;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Grave;
+  }
+  else if (l==7 && symName.right(5)=="circ;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Circ;
+  }
+  else if (l==8 && symName.right(6)=="tilde;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Tilde;
+  }
+  else if (l==8 && symName.right(6)=="cedil;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Cedil;
+  }
+  else if (l==7 && symName.right(5)=="ring;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Ring;
+  }
+  else if (l==8 && symName.right(6)=="slash;")
+  {
+    *letter=symName.at(1);
+    return DocSymbol::Slash;
+  }
+  return DocSymbol::Unknown;
+}
+
+//---------------------------------------------------------------------------
+
+static int internalValidatingParseDoc(DocNode *parent,QList &children,
+                                    const QString &doc)
+{
+  int retval = RetVal_OK;
+
+  if (doc.isEmpty()) return retval;
+
+  doctokenizerYYinit(doc,g_fileName);
+
+  // first parse any number of paragraphs
+  bool isFirst=TRUE;
+  DocPara *lastPar=0;
+  if (!children.isEmpty() && children.last()->kind()==DocNode::Kind_Para)
+  { // last child item was a paragraph
+    lastPar = (DocPara*)children.last();
+    isFirst=FALSE;
+  }
+  do
+  {
+    DocPara *par = new DocPara(parent);
+    if (isFirst) { par->markFirst(); isFirst=FALSE; }
+    retval=par->parse();
+    if (!par->isEmpty()) 
+    {
+      children.append(par);
+      if (lastPar) lastPar->markLast(FALSE);
+      lastPar=par;
+    }
+    else
+    {
+      delete par;
+    }
+  } while (retval==TK_NEWPARA);
+  if (lastPar) lastPar->markLast();
+
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+static void readTextFileByName(const QString &file,QString &text)
+{
+  bool ambig;
+  FileDef *fd;
+  if ((fd=findFileDef(Doxygen::exampleNameDict,file,ambig)))
+  {
+    text = fileToString(fd->absFilePath(),Config_getBool("FILTER_SOURCE_FILES"));
+  }
+  else if (ambig)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: included file name %s is ambigious"
+           "Possible candidates:\n%s",file.data(),
+           showFileDefMatches(Doxygen::exampleNameDict,file).data()
+          );
+  }
+  else
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: included file %s is not found. "
+           "Check your EXAMPLE_PATH",file.data());
+  }
+}
+
+//---------------------------------------------------------------------------
+
+DocWord::DocWord(DocNode *parent,const QString &word) : 
+      m_parent(parent), m_word(word) 
+{
+  //printf("new word %s url=%s\n",word.data(),g_searchUrl.data());
+  if (Doxygen::searchIndex && !g_searchUrl.isEmpty())
+  {
+    Doxygen::searchIndex->addWord(word,FALSE);
+  }
+}
+
+//---------------------------------------------------------------------------
+
+DocLinkedWord::DocLinkedWord(DocNode *parent,const QString &word,
+                  const QString &ref,const QString &file,
+                  const QString &anchor,const QString &tooltip) : 
+      m_parent(parent), m_word(word), m_ref(ref), 
+      m_file(file), m_relPath(g_relPath), m_anchor(anchor),
+      m_tooltip(tooltip)
+{
+  //printf("new word %s url=%s\n",word.data(),g_searchUrl.data());
+  if (Doxygen::searchIndex && !g_searchUrl.isEmpty())
+  {
+    Doxygen::searchIndex->addWord(word,FALSE);
+  }
+}
+
+//---------------------------------------------------------------------------
+
+DocAnchor::DocAnchor(DocNode *parent,const QString &id,bool newAnchor) 
+  : m_parent(parent)
+{
+  if (id.isEmpty())
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Empty anchor label");
+  }
+  if (newAnchor) // found 
+  {
+    m_anchor = id;
+  }
+  else // found \anchor label
+  {
+    SectionInfo *sec = Doxygen::sectionDict[id];
+    if (sec)
+    {
+      //printf("Found anchor %s\n",id.data());
+      m_file   = sec->fileName;
+      m_anchor = sec->label;
+      if (g_sectionDict && g_sectionDict->find(id)==0)
+      {
+        //printf("Inserting in dictionary!\n");
+        g_sectionDict->insert(id,sec);
+      }
+    }
+    else
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Invalid anchor id `%s'",id.data());
+      m_anchor = "invalid";
+      m_file = "invalid";
+    }
+  }
+}
+
+//---------------------------------------------------------------------------
+
+DocVerbatim::DocVerbatim(DocNode *parent,const QString &context,
+    const QString &text, Type t,bool isExample,
+    const QString &exampleFile) 
+  : m_parent(parent), m_context(context), m_text(text), m_type(t),
+    m_isExample(isExample), m_exampleFile(exampleFile), m_relPath(g_relPath) 
+{
+}
+
+
+//---------------------------------------------------------------------------
+
+void DocInclude::parse()
+{
+  DBG(("DocInclude::parse(file=%s,text=%s)\n",m_file.data(),m_text.data()));
+  switch(m_type)
+  {
+    case IncWithLines:
+      // fall through
+    case Include:
+      // fall through
+    case DontInclude:
+      readTextFileByName(m_file,m_text);
+      g_includeFileText   = m_text;
+      g_includeFileOffset = 0;
+      g_includeFileLength = m_text.length();
+      //printf("g_includeFile=<<%s>>\n",g_includeFileText.data());
+      break;
+    case VerbInclude: 
+      // fall through
+    case HtmlInclude:
+      readTextFileByName(m_file,m_text);
+      break;
+  }
+}
+
+//---------------------------------------------------------------------------
+
+void DocIncOperator::parse()
+{
+  const char *p = g_includeFileText;
+  uint l = g_includeFileLength;
+  uint o = g_includeFileOffset;
+  DBG(("DocIncOperator::parse() text=%s off=%d len=%d\n",p,o,l));
+  uint so = o,bo;
+  bool nonEmpty = FALSE;
+  switch(type())
+  {
+    case Line:
+      while (o  paramsFound      = g_paramsFound;
+      //printf("..1 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
+      //      g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
+
+      docParserPushContext(FALSE);
+      g_scope = def;
+      if (def->definitionType()==Definition::TypeMember && def->getOuterScope())
+      {
+        g_context=def->getOuterScope()->name();
+      }
+      else
+      {
+        g_context=def->name();
+      }
+      g_styleStack.clear();
+      g_nodeStack.clear();
+      g_copyStack.append(def);
+      // make sure the descriptions end with a newline, so the parser will correctly
+      // handle them in all cases.
+      //printf("doc='%s'\n",doc.data());
+      //printf("brief='%s'\n",brief.data());
+      if (m_copyBrief)
+      {
+        brief+='\n';
+        internalValidatingParseDoc(this,m_children,brief);
+
+        //printf("..2 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
+        //    g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
+        hasParamCommand  = hasParamCommand  || g_hasParamCommand;
+        hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
+        QDictIterator it(g_paramsFound);
+        void *item;
+        for (;(item=it.current());++it)
+        {
+          paramsFound.insert(it.currentKey(),it.current());
+        }
+      }
+      if (m_copyDetails)
+      {
+        doc+='\n';
+        internalValidatingParseDoc(this,m_children,doc);
+
+        //printf("..3 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
+        //    g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
+        hasParamCommand  = hasParamCommand  || g_hasParamCommand;
+        hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
+        QDictIterator it(g_paramsFound);
+        void *item;
+        for (;(item=it.current());++it)
+        {
+          paramsFound.insert(it.currentKey(),it.current());
+        }
+      }
+      g_copyStack.remove(def);
+      ASSERT(g_styleStack.isEmpty());
+      ASSERT(g_nodeStack.isEmpty());
+      docParserPopContext(TRUE);
+
+      g_hasParamCommand  = hasParamCommand;
+      g_hasReturnCommand = hasReturnCommand;
+      g_paramsFound      = paramsFound;
+
+      //printf("..4 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
+      //      g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
+    }
+    else // oops, recursion
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: recursive call chain of \\copydoc commands detected at %d\n",
+          doctokenizerYYlineno);
+    }
+  }
+  else
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: target %s of \\copydoc command not found",
+        m_link.data());
+  }
+}
+
+//---------------------------------------------------------------------------
+
+DocXRefItem::DocXRefItem(DocNode *parent,int id,const char *key) : 
+   m_parent(parent), m_id(id), m_key(key), m_relPath(g_relPath)
+{
+}
+
+bool DocXRefItem::parse()
+{
+  QString listName;
+  RefList *refList = Doxygen::xrefLists->find(m_key); 
+  if (refList && 
+      (
+       // either not a built-in list or the list is enabled
+       (m_key!="todo"       || Config_getBool("GENERATE_TODOLIST")) && 
+       (m_key!="test"       || Config_getBool("GENERATE_TESTLIST")) && 
+       (m_key!="bug"        || Config_getBool("GENERATE_BUGLIST"))  && 
+       (m_key!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
+      ) 
+     )
+  {
+    RefItem *item = refList->getRefItem(m_id);
+    ASSERT(item!=0);
+    if (item)
+    {
+      if (g_memberDef && g_memberDef->name().at(0)=='@')
+      {
+        m_file   = "@";  // can't cross reference anonymous enum
+        m_anchor = "@";
+      }
+      else
+      {
+        m_file   = refList->listName();
+        m_anchor = item->listAnchor;
+      }
+      m_title  = refList->sectionTitle();
+      //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
+      //    m_file.data(),m_anchor.data(),m_title.data());
+
+      if (!item->text.isEmpty())
+      {
+        docParserPushContext();
+        internalValidatingParseDoc(this,m_children,item->text);
+        docParserPopContext();
+      }
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+//---------------------------------------------------------------------------
+
+DocFormula::DocFormula(DocNode *parent,int id) :
+      m_parent(parent), m_relPath(g_relPath)
+{
+  QString formCmd;
+  formCmd.sprintf("\\form#%d",id);
+  Formula *formula=Doxygen::formulaNameDict[formCmd];
+  if (formula)
+  {
+    m_id = formula->getId();
+    m_name.sprintf("form_%d",m_id);
+    m_text = formula->getFormulaText();
+  }
+}
+
+//---------------------------------------------------------------------------
+
+//int DocLanguage::parse()
+//{
+//  int retval;
+//  DBG(("DocLanguage::parse() start\n"));
+//  g_nodeStack.push(this);
+//
+//  // parse one or more paragraphs
+//  bool isFirst=TRUE;
+//  DocPara *par=0;
+//  do
+//  {
+//    par = new DocPara(this);
+//    if (isFirst) { par->markFirst(); isFirst=FALSE; }
+//    m_children.append(par);
+//    retval=par->parse();
+//  }
+//  while (retval==TK_NEWPARA);
+//  if (par) par->markLast();
+//
+//  DBG(("DocLanguage::parse() end\n"));
+//  DocNode *n = g_nodeStack.pop();
+//  ASSERT(n==this);
+//  return retval;
+//}
+
+//---------------------------------------------------------------------------
+
+void DocSecRefItem::parse()
+{
+  DBG(("DocSecRefItem::parse() start\n"));
+  g_nodeStack.push(this);
+
+  doctokenizerYYsetStateTitle();
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\refitem",
+	       g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+	       tokToString(tok));
+          break;
+      }
+    }
+  }
+  doctokenizerYYsetStatePara();
+  handlePendingStyleCommands(this,m_children);
+
+  SectionInfo *sec=0;
+  if (!m_target.isEmpty())
+  {
+    sec=Doxygen::sectionDict[m_target];
+    if (sec)
+    {
+      m_file   = sec->fileName;
+      m_anchor = sec->label;
+      if (g_sectionDict && g_sectionDict->find(m_target)==0)
+      {
+        g_sectionDict->insert(m_target,sec);
+      }
+    }
+    else
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning reference to unknown section %s",
+          m_target.data());
+    }
+  } 
+  else
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning reference to empty target");
+  }
+  
+  DBG(("DocSecRefItem::parse() end\n"));
+  DocNode *n = g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+//---------------------------------------------------------------------------
+
+void DocSecRefList::parse()
+{
+  DBG(("DocSecRefList::parse() start\n"));
+  g_nodeStack.push(this);
+
+  int tok=doctokenizerYYlex();
+  // skip white space
+  while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
+  // handle items
+  while (tok)
+  {
+    if (tok==TK_COMMAND)
+    {
+      switch (Mappers::cmdMapper->map(g_token->name))
+      {
+        case CMD_SECREFITEM:
+          {
+            int tok=doctokenizerYYlex();
+            if (tok!=TK_WHITESPACE)
+            {
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after \\refitem command");
+              break;
+            }
+            tok=doctokenizerYYlex();
+            if (tok!=TK_WORD && tok!=TK_LNKWORD)
+            {
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of \\refitem",
+                  tokToString(tok));
+              break;
+            }
+
+            DocSecRefItem *item = new DocSecRefItem(this,g_token->name);
+            m_children.append(item);
+            item->parse();
+          }
+          break;
+        case CMD_ENDSECREFLIST:
+          goto endsecreflist;
+        default:
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\secreflist",
+              g_token->name.data());
+          goto endsecreflist;
+      }
+    }
+    else if (tok==TK_WHITESPACE)
+    {
+      // ignore whitespace
+    }
+    else
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s inside section reference list",
+          tokToString(tok));
+      goto endsecreflist;
+    }
+    tok=doctokenizerYYlex();
+  }
+
+endsecreflist:
+  DBG(("DocSecRefList::parse() end\n"));
+  DocNode *n = g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+//---------------------------------------------------------------------------
+
+DocInternalRef::DocInternalRef(DocNode *parent,const QString &ref) 
+  : m_parent(parent), m_relPath(g_relPath)
+{
+  int i=ref.find('#');
+  if (i!=-1)
+  {
+    m_anchor = ref.right(ref.length()-i-1);
+    m_file   = ref.left(i);
+  }
+  else
+  {
+    m_file = ref;
+  }
+}
+
+void DocInternalRef::parse()
+{
+  g_nodeStack.push(this);
+  DBG(("DocInternalRef::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\ref",
+	       g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+		tokToString(tok));
+          break;
+      }
+    }
+  }
+
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocInternalRef::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+//---------------------------------------------------------------------------
+
+DocRef::DocRef(DocNode *parent,const QString &target,const QString &context) : 
+   m_parent(parent), m_refToSection(FALSE), m_refToAnchor(FALSE)
+{
+  Definition  *compound = 0;
+  QCString     anchor;
+  //printf("DocRef::DocRef(target=%s,context=%s\n",target.data(),context.data());
+  ASSERT(!target.isEmpty());
+  m_relPath = g_relPath;
+  SectionInfo *sec = Doxygen::sectionDict[target];
+  if (sec) // ref to section or anchor
+  {
+    m_text         = sec->title;
+    if (m_text.isEmpty()) m_text = sec->label;
+
+    m_ref          = sec->ref;
+    m_file         = stripKnownExtensions(sec->fileName);
+    if (sec->type!=SectionInfo::Page) m_anchor = sec->label;
+    m_refToAnchor  = sec->type==SectionInfo::Anchor;
+    m_refToSection = sec->type!=SectionInfo::Anchor;
+    //printf("m_text=%s,m_ref=%s,m_file=%s,m_refToAnchor=%d type=%d\n",
+    //    m_text.data(),m_ref.data(),m_file.data(),m_refToAnchor,sec->type);
+    return;
+  }
+  else if (resolveLink(context,target,TRUE,&compound,anchor))
+  {
+    bool isFile = compound ? 
+                 (compound->definitionType()==Definition::TypeFile ? TRUE : FALSE) : 
+                 FALSE;
+    m_text = linkToText(target,isFile);
+    m_anchor = anchor;
+    if (compound && compound->isLinkable()) // ref to compound
+    {
+      if (anchor.isEmpty() &&                                  /* compound link */
+          compound->definitionType()==Definition::TypeGroup && /* is group */
+          ((GroupDef *)compound)->groupTitle()                 /* with title */
+         )
+      {
+        m_text=((GroupDef *)compound)->groupTitle(); // use group's title as link
+      }
+      else if (compound->definitionType()==Definition::TypeMember &&
+          ((MemberDef*)compound)->isObjCMethod())
+      {
+        // Objective C Method
+        MemberDef *member = (MemberDef*)compound;
+        bool localLink = g_memberDef ? member->getClassDef()==g_memberDef->getClassDef() : FALSE;
+        m_text = member->objCMethodName(localLink,g_inSeeBlock);
+      }
+
+      m_file = compound->getOutputFileBase();
+      m_ref  = compound->getReference();
+      return;
+    }
+    else if (compound->definitionType()==Definition::TypeFile && 
+             ((FileDef*)compound)->generateSourceFile()
+            ) // undocumented file that has source code we can link to
+    {
+      m_file = compound->getSourceFileBase();
+      m_ref  = compound->getReference();
+      return;
+    }
+  }
+  m_text = linkToText(target,FALSE);
+  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unable to resolve reference to `%s' for \\ref command",
+           target.data()); 
+  setDefinition(compound);
+}
+
+static void flattenParagraphs(QList &children)
+{
+  QListIterator li(children);
+  QList newChildren;
+  DocNode *dn;
+  for (li.toFirst();(dn=li.current());++li)
+  {
+    if (dn->kind()==DocNode::Kind_Para)
+    {
+      DocPara *para = (DocPara*)dn;
+      QList ¶Children = para->children();
+      paraChildren.setAutoDelete(FALSE); // unlink children from paragraph node
+      QListIterator li2(paraChildren);
+      DocNode *dn2;
+      for (li2.toFirst();(dn2=li2.current());++li2)
+      {
+        newChildren.append(dn2); // add them to new node
+      }
+    }
+  }
+  children.clear();
+  QListIterator li3(newChildren);
+  for (li3.toFirst();(dn=li3.current());++li3)
+  {
+    children.append(dn);
+  }
+}
+
+void DocRef::parse()
+{
+  g_nodeStack.push(this);
+  DBG(("DocRef::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\ref",
+	       g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        case TK_HTMLTAG:
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+		tokToString(tok));
+          break;
+      }
+    }
+  }
+
+  if (m_children.isEmpty() && !m_text.isEmpty())
+  {
+    g_insideHtmlLink=TRUE;
+    docParserPushContext();
+    internalValidatingParseDoc(this,m_children,m_text);
+    docParserPopContext();
+    g_insideHtmlLink=FALSE;
+    flattenParagraphs(m_children);
+  }
+
+  handlePendingStyleCommands(this,m_children);
+  
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+//---------------------------------------------------------------------------
+
+DocLink::DocLink(DocNode *parent,const QString &target) : 
+      m_parent(parent)
+{
+  Definition *compound = 0;
+  //PageInfo *page;
+  QCString anchor;
+  m_refText = target;
+  m_relPath = g_relPath;
+  if (!m_refText.isEmpty() && m_refText.at(0)=='#')
+  {
+    m_refText = m_refText.right(m_refText.length()-1);
+  }
+  if (resolveLink(g_context,stripKnownExtensions(target),g_inSeeBlock,
+                  &compound,anchor))
+  {
+    m_anchor = anchor;
+    if (compound && compound->isLinkable())
+    {
+      m_file = compound->getOutputFileBase();
+      m_ref  = compound->getReference();
+    }
+    else if (compound->definitionType()==Definition::TypeFile && 
+             ((FileDef*)compound)->generateSourceFile()
+            ) // undocumented file that has source code we can link to
+    {
+      m_file = compound->getSourceFileBase();
+      m_ref  = compound->getReference();
+    }
+    setDefinition(compound);
+    return;
+  }
+
+  setDefinition(compound);
+  // bogus link target
+  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unable to resolve link to `%s' for \\link command",
+         target.data()); 
+}
+
+
+QString DocLink::parse(bool isJavaLink,bool isXmlLink)
+{
+  QString result;
+  g_nodeStack.push(this);
+  DBG(("DocLink::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children,FALSE))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          switch (Mappers::cmdMapper->map(g_token->name))
+          {
+            case CMD_ENDLINK:
+              if (isJavaLink)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: {@link.. ended with @endlink command");
+              }
+              goto endlink;
+            default:
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\link",
+                  g_token->name.data());
+              break;
+          }
+          break;
+        case TK_SYMBOL: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+              g_token->name.data());
+          break;
+        case TK_HTMLTAG:
+          if (g_token->name!="see" || !isXmlLink)
+          {
+            warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected xml/html command %s found",
+                g_token->name.data());
+          }
+          goto endlink;
+        case TK_LNKWORD: 
+        case TK_WORD: 
+          if (isJavaLink) // special case to detect closing }
+          {
+            QString w = g_token->name;
+            int p;
+            if (w=="}")
+            {
+              goto endlink;
+            }
+            else if ((p=w.find('}'))!=-1)
+            {
+              uint l=w.length();
+              m_children.append(new DocWord(this,w.left(p)));
+              if ((uint)pname));
+          break;
+        default:
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+             tokToString(tok));
+        break;
+      }
+    }
+  }
+  if (tok==0)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected end of comment while inside"
+           " link command\n"); 
+  }
+endlink:
+
+  if (m_children.isEmpty()) // no link text
+  {
+    m_children.append(new DocWord(this,m_refText));
+  }
+
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocLink::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return result;
+}
+
+
+//---------------------------------------------------------------------------
+
+DocDotFile::DocDotFile(DocNode *parent,const QString &name,const QString &context) : 
+      m_parent(parent), m_name(name), m_relPath(g_relPath), m_context(context)
+{
+}
+
+void DocDotFile::parse()
+{
+  g_nodeStack.push(this);
+  DBG(("DocDotFile::parse() start\n"));
+
+  doctokenizerYYsetStateTitle();
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\dotfile",
+	       g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+		tokToString(tok));
+          break;
+      }
+    }
+  }
+  tok=doctokenizerYYlex();
+  while (tok==TK_WORD) // there are values following the title
+  {
+    if (g_token->name=="width") 
+    {
+      m_width=g_token->chars;
+    }
+    else if (g_token->name=="height") 
+    {
+      m_height=g_token->chars;
+    }
+    else 
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unknown option %s after image title",
+            g_token->name.data());
+    }
+    tok=doctokenizerYYlex();
+  }
+  ASSERT(tok==0);
+  doctokenizerYYsetStatePara();
+  handlePendingStyleCommands(this,m_children);
+
+  bool ambig;
+  FileDef *fd = findFileDef(Doxygen::dotFileNameDict,m_name,ambig);
+  if (fd)
+  {
+    m_file = fd->absFilePath();
+  }
+  else if (ambig)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: included dot file name %s is ambigious.\n"
+           "Possible candidates:\n%s",m_name.data(),
+           showFileDefMatches(Doxygen::exampleNameDict,m_name).data()
+          );
+  }
+  else
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: included dot file %s is not found "
+           "in any of the paths specified via DOTFILE_DIRS!",m_name.data());
+  }
+
+  DBG(("DocDotFile::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+
+//---------------------------------------------------------------------------
+
+DocImage::DocImage(DocNode *parent,const HtmlAttribList &attribs,const QString &name,Type t) : 
+      m_parent(parent), m_attribs(attribs), m_name(name), 
+      m_type(t), m_relPath(g_relPath)
+{
+}
+
+void DocImage::parse()
+{
+  g_nodeStack.push(this);
+  DBG(("DocImage::parse() start\n"));
+
+  // parse title
+  doctokenizerYYsetStateTitle();
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (tok==TK_WORD && (g_token->name=="width=" || g_token->name=="height="))
+    {
+      // special case: no title, but we do have a size indicator
+      doctokenizerYYsetStateTitleAttrValue();
+      // strip =
+      g_token->name=g_token->name.left(g_token->name.length()-1);
+      break;
+    } 
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a \\image",
+              g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+              g_token->name.data());
+          break;
+        default:
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+              tokToString(tok));
+          break;
+      }
+    }
+  }
+  // parse size attributes
+  tok=doctokenizerYYlex();
+  while (tok==TK_WORD) // there are values following the title
+  {
+    if (g_token->name=="width") 
+    {
+      m_width=g_token->chars;
+    }
+    else if (g_token->name=="height") 
+    {
+      m_height=g_token->chars;
+    }
+    else 
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unknown option %s after image title",
+          g_token->name.data());
+    }
+    tok=doctokenizerYYlex();
+  }
+  doctokenizerYYsetStatePara();
+
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocImage::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+}
+
+
+//---------------------------------------------------------------------------
+
+int DocHtmlHeader::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlHeader::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a  tag",
+	       g_token->name.data(),m_level);
+          break;
+        case TK_HTMLTAG:
+          {
+            int tagId=Mappers::htmlTagMapper->map(g_token->name);
+            if (tagId==HTML_H1 && g_token->endTag) // found  tag
+            {
+              if (m_level!=1)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_H2 && g_token->endTag) // found  tag
+            {
+              if (m_level!=2)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_H3 && g_token->endTag) // found  tag
+            {
+              if (m_level!=3)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_H4 && g_token->endTag) // found  tag
+            {
+              if (m_level!=4)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_H5 && g_token->endTag) // found  tag
+            {
+              if (m_level!=5)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_H6 && g_token->endTag) // found  tag
+            {
+              if (m_level!=6)
+              {
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning:  ended with ",
+                    m_level); 
+              }
+              goto endheader;
+            }
+            else if (tagId==HTML_A)
+            {
+              if (!g_token->endTag)
+              {
+                handleAHref(this,m_children,g_token->attribs);
+              }
+            }
+            else if (tagId==HTML_BR)
+            {
+              DocLineBreak *lb = new DocLineBreak(this);
+              m_children.append(lb);
+            }
+            else
+            {
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected html tag <%s%s> found within  context",
+                  g_token->endTag?"/":"",g_token->name.data(),m_level);
+            }
+            
+          }
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+		tokToString(tok));
+          break;
+      }
+    }
+  }
+  if (tok==0)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected end of comment while inside"
+           "  tag\n",m_level); 
+  }
+endheader:
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocHtmlHeader::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocHRef::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHRef::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a .. block",
+	       g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+               g_token->name.data());
+          break;
+        case TK_HTMLTAG:
+          {
+            int tagId=Mappers::htmlTagMapper->map(g_token->name);
+            if (tagId==HTML_A && g_token->endTag) // found  tag
+            {
+              goto endhref;
+            }
+            else
+            {
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected html tag <%s%s> found within  context",
+                  g_token->endTag?"/":"",g_token->name.data(),doctokenizerYYlineno);
+            }
+          }
+          break;
+        default:
+	  warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+		tokToString(tok),doctokenizerYYlineno);
+          break;
+      }
+    }
+  }
+  if (tok==0)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected end of comment while inside"
+           "  tag",doctokenizerYYlineno); 
+  }
+endhref:
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocHRef::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocInternal::parse(int level)
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocInternal::parse() start\n"));
+
+  // first parse any number of paragraphs
+  bool isFirst=TRUE;
+  DocPara *lastPar=0;
+  do
+  {
+    DocPara *par = new DocPara(this);
+    if (isFirst) { par->markFirst(); isFirst=FALSE; }
+    retval=par->parse();
+    if (!par->isEmpty()) 
+    {
+      m_children.append(par);
+      lastPar=par;
+    }
+    else
+    {
+      delete par;
+    }
+    if (retval==TK_LISTITEM)
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Invalid list item found",doctokenizerYYlineno);
+    }
+  } while (retval!=0 && 
+           retval!=RetVal_Section &&
+           retval!=RetVal_Subsection &&
+           retval!=RetVal_Subsubsection &&
+           retval!=RetVal_Paragraph
+          );
+  if (lastPar) lastPar->markLast();
+
+  // then parse any number of level-n sections
+  while ((level==1 && retval==RetVal_Section) || 
+         (level==2 && retval==RetVal_Subsection) ||
+         (level==3 && retval==RetVal_Subsubsection) ||
+         (level==4 && retval==RetVal_Paragraph)
+        )
+  {
+    DocSection *s=new DocSection(this,
+        QMIN(level+Doxygen::subpageNestingLevel,5),g_token->sectionId);
+    m_children.append(s);
+    retval = s->parse();
+  }
+
+  if (retval==RetVal_Internal)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: \\internal command found inside internal section");
+  }
+
+  DBG(("DocInternal::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocIndexEntry::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocIndexEntry::parse() start\n"));
+  int tok=doctokenizerYYlex();
+  if (tok!=TK_WHITESPACE)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after \\addindex command");
+    goto endindexentry;
+  }
+  doctokenizerYYsetStateTitle();
+  m_entry="";
+  while ((tok=doctokenizerYYlex()))
+  {
+    switch (tok)
+    {
+      case TK_WHITESPACE:
+        m_entry+=" ";
+        break;
+      case TK_WORD: 
+      case TK_LNKWORD: 
+        m_entry+=g_token->name;
+        break;
+      case TK_SYMBOL:
+        {
+          char letter='\0';
+          DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name,&letter);
+          switch (s)
+          {
+            case DocSymbol::BSlash:  m_entry+='\\'; break;
+            case DocSymbol::At:      m_entry+='@';  break;
+            case DocSymbol::Less:    m_entry+='<';  break;
+            case DocSymbol::Greater: m_entry+='>';  break;
+            case DocSymbol::Amp:     m_entry+='&';  break;
+            case DocSymbol::Dollar:  m_entry+='$';  break;
+            case DocSymbol::Hash:    m_entry+='#';  break;
+            case DocSymbol::Percent: m_entry+='%';  break;
+            case DocSymbol::Apos:    m_entry+='\''; break;
+            case DocSymbol::Quot:    m_entry+='"';  break;
+            case DocSymbol::Lsquo:   m_entry+='`';  break;
+            case DocSymbol::Rsquo:   m_entry+='\'';  break;
+            case DocSymbol::Ldquo:   m_entry+="``";  break;
+            case DocSymbol::Rdquo:   m_entry+="''";  break;
+            case DocSymbol::Ndash:   m_entry+="--";  break;
+            case DocSymbol::Mdash:   m_entry+="---";  break;
+            default:
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected symbol found as argument of \\addindex");
+              break;
+          }
+        }
+        break;
+    case TK_COMMAND: 
+      switch (Mappers::cmdMapper->map(g_token->name))
+      {
+        case CMD_BSLASH:  m_entry+='\\'; break;
+        case CMD_AT:      m_entry+='@';  break;
+        case CMD_LESS:    m_entry+='<';  break;
+        case CMD_GREATER: m_entry+='>';  break;
+        case CMD_AMP:     m_entry+='&';  break;
+        case CMD_DOLLAR:  m_entry+='$';  break;
+        case CMD_HASH:    m_entry+='#';  break;
+        case CMD_PERCENT: m_entry+='%';  break;
+        case CMD_QUOTE:   m_entry+='"';  break;
+        default:
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected command %s found as argument of \\addindex",
+                    g_token->name.data());
+          break;
+      }
+      break;
+      default:
+        warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+            tokToString(tok));
+        break;
+    }
+  }
+  if (tok!=0) retval=tok;
+  doctokenizerYYsetStatePara();
+  m_entry = m_entry.stripWhiteSpace();
+endindexentry:
+  DBG(("DocIndexEntry::parse() end retval=%x\n",retval));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocHtmlCaption::parse()
+{
+  int retval=0;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlCaption::parse() start\n"));
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a  tag",
+              g_token->name.data());
+          break;
+        case TK_SYMBOL: 
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found",
+              g_token->name.data());
+          break;
+        case TK_HTMLTAG:
+          {
+            int tagId=Mappers::htmlTagMapper->map(g_token->name);
+            if (tagId==HTML_CAPTION && g_token->endTag) // found  tag
+            {
+              retval = RetVal_OK;
+              goto endcaption;
+            }
+            else
+            {
+              warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected html tag <%s%s> found within  context",
+                  g_token->endTag?"/":"",g_token->name.data());
+            }
+          }
+          break;
+        default:
+          warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s",
+              tokToString(tok));
+          break;
+      }
+    }
+  }
+  if (tok==0)
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected end of comment while inside"
+           "  tag",doctokenizerYYlineno); 
+  }
+endcaption:
+  handlePendingStyleCommands(this,m_children);
+  DBG(("DocHtmlCaption::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocHtmlCell::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlCell::parse() start\n"));
+
+  // parse one or more paragraphs
+  bool isFirst=TRUE;
+  DocPara *par=0;
+  do
+  {
+    par = new DocPara(this);
+    if (isFirst) { par->markFirst(); isFirst=FALSE; }
+    m_children.append(par);
+    retval=par->parse();
+    if (retval==TK_HTMLTAG)
+    {
+      int tagId=Mappers::htmlTagMapper->map(g_token->name);
+      if (tagId==HTML_TD && g_token->endTag) // found  tag
+      {
+        retval=TK_NEWPARA; // ignore the tag
+      }
+      else if (tagId==HTML_TH && g_token->endTag) // found  tag
+      {
+        retval=TK_NEWPARA; // ignore the tag
+      }
+    }
+  }
+  while (retval==TK_NEWPARA);
+  if (par) par->markLast();
+
+  DBG(("DocHtmlCell::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+int DocHtmlCell::parseXml()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlCell::parseXml() start\n"));
+
+  // parse one or more paragraphs
+  bool isFirst=TRUE;
+  DocPara *par=0;
+  do
+  {
+    par = new DocPara(this);
+    if (isFirst) { par->markFirst(); isFirst=FALSE; }
+    m_children.append(par);
+    retval=par->parse();
+    if (retval==TK_HTMLTAG)
+    {
+      int tagId=Mappers::htmlTagMapper->map(g_token->name);
+      if (tagId==XML_ITEM && g_token->endTag) // found  tag
+      {
+        retval=TK_NEWPARA; // ignore the tag
+      }
+      else if (tagId==XML_DESCRIPTION && g_token->endTag) // found  tag
+      {
+        retval=TK_NEWPARA; // ignore the tag
+      }
+    }
+  }
+  while (retval==TK_NEWPARA);
+  if (par) par->markLast();
+
+  DBG(("DocHtmlCell::parseXml() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocHtmlRow::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlRow::parse() start\n"));
+
+  bool isHeading=FALSE;
+  bool isFirst=TRUE;
+  DocHtmlCell *cell=0;
+
+  // get next token
+  int tok=doctokenizerYYlex();
+  // skip whitespace
+  while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
+  // should find a html tag now
+  if (tok==TK_HTMLTAG)
+  {
+    int tagId=Mappers::htmlTagMapper->map(g_token->name);
+    if (tagId==HTML_TD && !g_token->endTag) // found  tag
+    {
+    }
+    else if (tagId==HTML_TH && !g_token->endTag) // found  tag
+    {
+      isHeading=TRUE;
+    }
+    else // found some other tag
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  or  tag but "
+          "found <%s> instead!",g_token->name.data());
+      doctokenizerYYpushBackHtmlTag(g_token->name);
+      goto endrow;
+    }
+  }
+  else if (tok==0) // premature end of comment
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking"
+        " for a html description title");
+    goto endrow;
+  }
+  else // token other than html token
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  or  tag but found %s token instead!",
+        tokToString(tok));
+    goto endrow;
+  }
+
+  // parse one or more cells
+  do
+  {
+    cell=new DocHtmlCell(this,g_token->attribs,isHeading);
+    cell->markFirst(isFirst);
+    isFirst=FALSE;
+    m_children.append(cell);
+    retval=cell->parse();
+    isHeading = retval==RetVal_TableHCell;
+  }
+  while (retval==RetVal_TableCell || retval==RetVal_TableHCell);
+  if (cell) cell->markLast(TRUE);
+
+endrow:
+  DBG(("DocHtmlRow::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+int DocHtmlRow::parseXml(bool isHeading)
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlRow::parseXml() start\n"));
+
+  bool isFirst=TRUE;
+  DocHtmlCell *cell=0;
+
+  // get next token
+  int tok=doctokenizerYYlex();
+  // skip whitespace
+  while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
+  // should find a html tag now
+  if (tok==TK_HTMLTAG)
+  {
+    int tagId=Mappers::htmlTagMapper->map(g_token->name);
+    if (tagId==XML_TERM && !g_token->endTag) // found  tag
+    {
+    }
+    else if (tagId==XML_DESCRIPTION && !g_token->endTag) // found  tag
+    {
+    }
+    else // found some other tag
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  or  tag but "
+          "found <%s> instead!",g_token->name.data());
+      doctokenizerYYpushBackHtmlTag(g_token->name);
+      goto endrow;
+    }
+  }
+  else if (tok==0) // premature end of comment
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking"
+        " for a html description title");
+    goto endrow;
+  }
+  else // token other than html token
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  or  tag but found %s token instead!",
+        tokToString(tok));
+    goto endrow;
+  }
+
+  do
+  {
+    cell=new DocHtmlCell(this,g_token->attribs,isHeading);
+    cell->markFirst(isFirst);
+    isFirst=FALSE;
+    m_children.append(cell);
+    retval=cell->parseXml();
+  }
+  while (retval==RetVal_TableCell || retval==RetVal_TableHCell);
+  if (cell) cell->markLast(TRUE);
+
+endrow:
+  DBG(("DocHtmlRow::parseXml() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval;
+}
+
+//---------------------------------------------------------------------------
+
+int DocHtmlTable::parse()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlTable::parse() start\n"));
+  
+getrow:
+  // get next token
+  int tok=doctokenizerYYlex();
+  // skip whitespace
+  while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
+  // should find a html tag now
+  if (tok==TK_HTMLTAG)
+  {
+    int tagId=Mappers::htmlTagMapper->map(g_token->name);
+    if (tagId==HTML_TR && !g_token->endTag) // found  tag
+    {
+      // no caption, just rows
+      retval=RetVal_TableRow;
+    }
+    else if (tagId==HTML_CAPTION && !g_token->endTag) // found  tag
+    {
+      if (m_caption)
+      {
+        warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: table already has a caption, found another one");
+      }
+      else
+      {
+        m_caption = new DocHtmlCaption(this,g_token->attribs);
+        retval=m_caption->parse();
+
+        if (retval==RetVal_OK) // caption was parsed ok
+        {
+          goto getrow;
+        }
+      }
+    }
+    else // found wrong token
+    {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  or  tag but "
+          "found <%s%s> instead!", g_token->endTag ? "/" : "", g_token->name.data());
+    }
+  }
+  else if (tok==0) // premature end of comment
+  {
+      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking"
+          " for a  or  tag");
+  }
+  else // token other than html token
+  {
+    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected  tag but found %s token instead!",
+        tokToString(tok));
+  }
+       
+  // parse one or more rows
+  while (retval==RetVal_TableRow)
+  {
+    DocHtmlRow *tr=new DocHtmlRow(this,g_token->attribs);
+    m_children.append(tr);
+    retval=tr->parse();
+  } 
+
+  DBG(("DocHtmlTable::parse() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval==RetVal_EndTable ? RetVal_OK : retval;
+}
+
+int DocHtmlTable::parseXml()
+{
+  int retval=RetVal_OK;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlTable::parseXml() start\n"));
+  
+  // get next token
+  int tok=doctokenizerYYlex();
+  // skip whitespace
+  while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex();
+  // should find a html tag now
+  int tagId=0;
+  bool isHeader=FALSE;
+  if (tok==TK_HTMLTAG)
+  {
+    tagId=Mappers::htmlTagMapper->map(g_token->name);
+    if (tagId==XML_ITEM && !g_token->endTag) // found  tag
+    {
+      retval=RetVal_TableRow;
+    }
+    if (tagId==XML_LISTHEADER && !g_token->endTag) // found  tag
+    {
+      retval=RetVal_TableRow;
+      isHeader=TRUE;
+    }
+  }
+
+  // parse one or more rows
+  while (retval==RetVal_TableRow)
+  {
+    DocHtmlRow *tr=new DocHtmlRow(this,g_token->attribs);
+    m_children.append(tr);
+    retval=tr->parseXml(isHeader);
+    isHeader=FALSE;
+  } 
+
+  DBG(("DocHtmlTable::parseXml() end\n"));
+  DocNode *n=g_nodeStack.pop();
+  ASSERT(n==this);
+  return retval==RetVal_EndTable ? RetVal_OK : retval;
+}
+
+uint DocHtmlTable::numCols() const
+{
+  uint cols=0;
+  QListIterator cli(m_children);
+  DocNode *n;
+  for (cli.toFirst();(n=cli.current());++cli)
+  {
+    ASSERT(n->kind()==DocNode::Kind_HtmlRow);
+    cols=QMAX(cols,((DocHtmlRow *)n)->numCells());
+  }
+  return cols;
+}
+
+void DocHtmlTable::accept(DocVisitor *v) 
+{ 
+  v->visitPre(this); 
+  // for HTML output we put the caption first
+  if (m_caption && v->id()==DocVisitor_Html) m_caption->accept(v);
+  QListIterator cli(m_children);
+  DocNode *n;
+  for (cli.toFirst();(n=cli.current());++cli) n->accept(v);
+  // for other output formats we put the caption last
+  if (m_caption && v->id()!=DocVisitor_Html) m_caption->accept(v);
+  v->visitPost(this); 
+}
+
+//---------------------------------------------------------------------------
+
+int DocHtmlDescTitle::parse()
+{
+  int retval=0;
+  g_nodeStack.push(this);
+  DBG(("DocHtmlDescTitle::parse() start\n"));
+
+  int tok;
+  while ((tok=doctokenizerYYlex()))
+  {
+    if (!defaultHandleToken(this,tok,m_children))
+    {
+      switch (tok)
+      {
+        case TK_COMMAND: 
+          {
+            QString cmdName=g_token->name;
+            bool isJavaLink=FALSE;
+            switch (Mappers::cmdMapper->map(cmdName))
+            {
+              case CMD_REF:
+                {
+                  int tok=doctokenizerYYlex();
+                  if (tok!=TK_WHITESPACE)
+                  {
+                    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command",
+                        g_token->name.data());
+                  }
+                  else
+                  {
+                    doctokenizerYYsetStateRef();
+                    tok=doctokenizerYYlex(); // get the reference id
+                    if (tok!=TK_WORD)
+                    {
+                      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s",
+                          tokToString(tok),cmdName.data());
+                    }
+                    else
+                    {
+                      DocRef *ref = new DocRef(this,g_token->name,g_context);
+                      m_children.append(ref);
+                      ref->parse();
+                    }
+                    doctokenizerYYsetStatePara();
+                  }
+                }
+                break;
+              case CMD_JAVALINK:
+                isJavaLink=TRUE;
+                // fall through
+              case CMD_LINK:
+                {
+                  int tok=doctokenizerYYlex();
+                  if (tok!=TK_WHITESPACE)
+                  {
+                    warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command",
+                        cmdName.data());
+                  }
+                  else
+                  {
+                    doctokenizerYYsetStateLink();
+                    tok=doctokenizerYYlex();
+                    if (tok!=TK_WORD)
+                    {
+                      warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s",
+                          tokToString(tok),cmdName.data());
+                    }
+                    else
+                    {
+                      doctokenizerYYsetStatePara();
+                      DocLink *lnk = new DocLink(this,g_token->name);
+                      m_children.append(lnk);
+                      QString leftOver = lnk->parse(isJavaLink);
+                      if (!leftOver.isEmpty())
+                      {
+                        m_children.append(new DocWord(this,leftOver));
+                      }
+                    }
+                  }
+                }
+
+                break;
+              default:
+                warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a 
tag", + g_token->name.data()); + } + } + break; + case TK_SYMBOL: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found", + g_token->name.data()); + break; + case TK_HTMLTAG: + { + int tagId=Mappers::htmlTagMapper->map(g_token->name); + if (tagId==HTML_DD && !g_token->endTag) // found
tag + { + retval = RetVal_DescData; + goto endtitle; + } + else if (tagId==HTML_DT && g_token->endTag) + { + // ignore tag. + } + else if (tagId==HTML_DT) + { + // missing
tag. + retval = RetVal_DescTitle; + goto endtitle; + } + else if (tagId==HTML_DL && g_token->endTag) + { + retval=RetVal_EndDesc; + goto endtitle; + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected html tag <%s%s> found within
context", + g_token->endTag?"/":"",g_token->name.data()); + } + } + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s", + tokToString(tok)); + break; + } + } + } + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected end of comment while inside" + "
tag"); + } +endtitle: + handlePendingStyleCommands(this,m_children); + DBG(("DocHtmlDescTitle::parse() end\n")); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//--------------------------------------------------------------------------- + +int DocHtmlDescData::parse() +{ + m_attribs = g_token->attribs; + int retval=0; + g_nodeStack.push(this); + DBG(("DocHtmlDescData::parse() start\n")); + + bool isFirst=TRUE; + DocPara *par=0; + do + { + par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + m_children.append(par); + retval=par->parse(); + } + while (retval==TK_NEWPARA); + if (par) par->markLast(); + + DBG(("DocHtmlDescData::parse() end\n")); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//--------------------------------------------------------------------------- + +int DocHtmlDescList::parse() +{ + int retval=RetVal_OK; + g_nodeStack.push(this); + DBG(("DocHtmlDescList::parse() start\n")); + + // get next token + int tok=doctokenizerYYlex(); + // skip whitespace + while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex(); + // should find a html tag now + if (tok==TK_HTMLTAG) + { + int tagId=Mappers::htmlTagMapper->map(g_token->name); + if (tagId==HTML_DT && !g_token->endTag) // found
tag + { + // continue + } + else // found some other tag + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected
tag but " + "found <%s> instead!",g_token->name.data()); + doctokenizerYYpushBackHtmlTag(g_token->name); + goto enddesclist; + } + } + else if (tok==0) // premature end of comment + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking" + " for a html description title"); + goto enddesclist; + } + else // token other than html token + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected
tag but found %s token instead!", + tokToString(tok)); + goto enddesclist; + } + + do + { + DocHtmlDescTitle *dt=new DocHtmlDescTitle(this,g_token->attribs); + m_children.append(dt); + DocHtmlDescData *dd=new DocHtmlDescData(this); + m_children.append(dd); + retval=dt->parse(); + if (retval==RetVal_DescData) + { + retval=dd->parse(); + } + else if (retval!=RetVal_DescTitle) + { + // error + break; + } + } while (retval==RetVal_DescTitle); + + if (retval==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while inside
block"); + } + +enddesclist: + + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocHtmlDescList::parse() end\n")); + return retval==RetVal_EndDesc ? RetVal_OK : retval; +} + +//--------------------------------------------------------------------------- + +int DocHtmlListItem::parse() +{ + DBG(("DocHtmlListItem::parse() start\n")); + int retval=0; + g_nodeStack.push(this); + + // parse one or more paragraphs + bool isFirst=TRUE; + DocPara *par=0; + do + { + par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + m_children.append(par); + retval=par->parse(); + } + while (retval==TK_NEWPARA); + if (par) par->markLast(); + + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocHtmlListItem::parse() end retval=%x\n",retval)); + return retval; +} + +int DocHtmlListItem::parseXml() +{ + DBG(("DocHtmlListItem::parseXml() start\n")); + int retval=0; + g_nodeStack.push(this); + + // parse one or more paragraphs + bool isFirst=TRUE; + DocPara *par=0; + do + { + par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + m_children.append(par); + retval=par->parse(); + if (retval==0) break; + + //printf("new item: retval=%x g_token->name=%s g_token->endTag=%d\n", + // retval,g_token->name.data(),g_token->endTag); + if (retval==RetVal_ListItem) + { + break; + } + } + while (retval!=RetVal_CloseXml); + + if (par) par->markLast(); + + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocHtmlListItem::parseXml() end retval=%x\n",retval)); + return retval; +} + +//--------------------------------------------------------------------------- + +int DocHtmlList::parse() +{ + DBG(("DocHtmlList::parse() start\n")); + int retval=RetVal_OK; + int num=1; + g_nodeStack.push(this); + + // get next token + int tok=doctokenizerYYlex(); + // skip whitespace and paragraph breaks + while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex(); + // should find a html tag now + if (tok==TK_HTMLTAG) + { + int tagId=Mappers::htmlTagMapper->map(g_token->name); + if (tagId==HTML_LI && !g_token->endTag) // found
  • tag + { + // ok, we can go on. + } + else // found some other tag + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected
  • tag but " + "found <%s> instead!",g_token->name.data()); + doctokenizerYYpushBackHtmlTag(g_token->name); + goto endlist; + } + } + else if (tok==0) // premature end of comment + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking" + " for a html list item"); + goto endlist; + } + else // token other than html token + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected
  • tag but found %s token instead!", + tokToString(tok)); + goto endlist; + } + + do + { + DocHtmlListItem *li=new DocHtmlListItem(this,g_token->attribs,num++); + m_children.append(li); + retval=li->parse(); + } while (retval==RetVal_ListItem); + + if (retval==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while inside <%cl> block", + m_type==Unordered ? 'u' : 'o'); + } + +endlist: + DBG(("DocHtmlList::parse() end retval=%x\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval==RetVal_EndList ? RetVal_OK : retval; +} + +int DocHtmlList::parseXml() +{ + DBG(("DocHtmlList::parseXml() start\n")); + int retval=RetVal_OK; + int num=1; + g_nodeStack.push(this); + + // get next token + int tok=doctokenizerYYlex(); + // skip whitespace and paragraph breaks + while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex(); + // should find a html tag now + if (tok==TK_HTMLTAG) + { + int tagId=Mappers::htmlTagMapper->map(g_token->name); + //printf("g_token->name=%s g_token->endTag=%d\n",g_token->name.data(),g_token->endTag); + if (tagId==XML_ITEM && !g_token->endTag) // found tag + { + // ok, we can go on. + } + else // found some other tag + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected tag but " + "found <%s> instead!",g_token->name.data()); + doctokenizerYYpushBackHtmlTag(g_token->name); + goto endlist; + } + } + else if (tok==0) // premature end of comment + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking" + " for a html list item"); + goto endlist; + } + else // token other than html token + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected tag but found %s token instead!", + tokToString(tok)); + goto endlist; + } + + do + { + DocHtmlListItem *li=new DocHtmlListItem(this,g_token->attribs,num++); + m_children.append(li); + retval=li->parseXml(); + if (retval==0) break; + //printf("retval=%x g_token->name=%s\n",retval,g_token->name.data()); + } while (retval==RetVal_ListItem); + + if (retval==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while inside block", + m_type==Unordered ? "bullet" : "number"); + } + +endlist: + DBG(("DocHtmlList::parseXml() end retval=%x\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval==RetVal_EndList || + (retval==RetVal_CloseXml || g_token->name=="list") ? + RetVal_OK : retval; +} + +//--------------------------------------------------------------------------- + +int DocSimpleListItem::parse() +{ + g_nodeStack.push(this); + int rv=m_paragraph->parse(); + m_paragraph->markFirst(); + m_paragraph->markLast(); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return rv; +} + +//-------------------------------------------------------------------------- + +int DocSimpleList::parse() +{ + g_nodeStack.push(this); + int rv; + do + { + DocSimpleListItem *li=new DocSimpleListItem(this); + m_children.append(li); + rv=li->parse(); + } while (rv==RetVal_ListItem); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return (rv!=TK_NEWPARA) ? rv : RetVal_OK; +} + +//-------------------------------------------------------------------------- + +int DocAutoListItem::parse() +{ + int retval = RetVal_OK; + g_nodeStack.push(this); + retval=m_paragraph->parse(); + m_paragraph->markFirst(); + m_paragraph->markLast(); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//-------------------------------------------------------------------------- + +int DocAutoList::parse() +{ + int retval = RetVal_OK; + int num=1; + g_nodeStack.push(this); + // first item or sub list => create new list + do + { + DocAutoListItem *li = new DocAutoListItem(this,num++); + m_children.append(li); + retval=li->parse(); + } + while (retval==TK_LISTITEM && // new list item + m_indent==g_token->indent && // at same indent level + m_isEnumList==g_token->isEnumList // of the same kind + ); + + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//-------------------------------------------------------------------------- + +void DocTitle::parse() +{ + DBG(("DocTitle::parse() start\n")); + g_nodeStack.push(this); + doctokenizerYYsetStateTitle(); + int tok; + while ((tok=doctokenizerYYlex())) + { + if (!defaultHandleToken(this,tok,m_children)) + { + switch (tok) + { + case TK_COMMAND: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal command %s as part of a title section", + g_token->name.data()); + break; + case TK_SYMBOL: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found", + g_token->name.data()); + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s", + tokToString(tok)); + break; + } + } + } + doctokenizerYYsetStatePara(); + handlePendingStyleCommands(this,m_children); + DBG(("DocTitle::parse() end\n")); + DocNode *n = g_nodeStack.pop(); + ASSERT(n==this); +} + +void DocTitle::parseFromString(const QString &text) +{ + m_children.append(new DocWord(this,text)); +} + +//-------------------------------------------------------------------------- + +DocSimpleSect::DocSimpleSect(DocNode *parent,Type t) : + m_parent(parent), m_type(t) +{ + m_title=0; +} + +DocSimpleSect::~DocSimpleSect() +{ + delete m_title; +} + +void DocSimpleSect::accept(DocVisitor *v) +{ + v->visitPre(this); + if (m_title) m_title->accept(v); + QListIterator cli(m_children); + DocNode *n; + for (cli.toFirst();(n=cli.current());++cli) n->accept(v); + v->visitPost(this); +} + +int DocSimpleSect::parse(bool userTitle,bool needsSeparator) +{ + DBG(("DocSimpleSect::parse() start\n")); + g_nodeStack.push(this); + + // handle case for user defined title + if (userTitle) + { + m_title = new DocTitle(this); + m_title->parse(); + } + + // add new paragraph as child + DocPara *par = new DocPara(this); + if (m_children.isEmpty()) + { + par->markFirst(); + } + else + { + ASSERT(m_children.last()->kind()==DocNode::Kind_Para); + ((DocPara *)m_children.last())->markLast(FALSE); + } + par->markLast(); + if (needsSeparator) m_children.append(new DocSimpleSectSep(this)); + m_children.append(par); + + // parse the contents of the paragraph + int retval = par->parse(); + + DBG(("DocSimpleSect::parse() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; // 0==EOF, TK_NEWPARA, TK_LISTITEM, TK_ENDLIST, RetVal_SimpleSec +} + +int DocSimpleSect::parseRcs() +{ + DBG(("DocSimpleSect::parseRcs() start\n")); + g_nodeStack.push(this); + + m_title = new DocTitle(this); + m_title->parseFromString(g_token->name); + + QString text = g_token->text; + docParserPushContext(); // this will create a new g_token + internalValidatingParseDoc(this,m_children,text); + docParserPopContext(); // this will restore the old g_token + + DBG(("DocSimpleSect::parseRcs()\n")); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return RetVal_OK; +} + +int DocSimpleSect::parseXml() +{ + DBG(("DocSimpleSect::parse() start\n")); + g_nodeStack.push(this); + + int retval = RetVal_OK; + for (;;) + { + // add new paragraph as child + DocPara *par = new DocPara(this); + if (m_children.isEmpty()) + { + par->markFirst(); + } + else + { + ASSERT(m_children.last()->kind()==DocNode::Kind_Para); + ((DocPara *)m_children.last())->markLast(FALSE); + } + par->markLast(); + m_children.append(par); + + // parse the contents of the paragraph + retval = par->parse(); + if (retval == 0) break; + if (retval == RetVal_CloseXml) + { + retval = RetVal_OK; + break; + } + } + + DBG(("DocSimpleSect::parseXml() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +void DocSimpleSect::appendLinkWord(const QString &word) +{ + DocPara *p; + if (m_children.isEmpty() || m_children.last()->kind()!=DocNode::Kind_Para) + { + p = new DocPara(this); + m_children.append(p); + } + else + { + p = (DocPara *)m_children.last(); + + // Comma-seperate links. + p->injectToken(TK_WORD,","); + p->injectToken(TK_WHITESPACE," "); + } + + g_inSeeBlock=TRUE; + p->injectToken(TK_LNKWORD,word); + g_inSeeBlock=FALSE; +} + +QCString DocSimpleSect::typeString() const +{ + switch (m_type) + { + case Unknown: break; + case See: return "see"; + case Return: return "return"; + case Author: // fall through + case Authors: return "author"; + case Version: return "version"; + case Since: return "since"; + case Date: return "date"; + case Note: return "note"; + case Warning: return "warning"; + case Pre: return "pre"; + case Post: return "post"; + case Invar: return "invariant"; + case Remark: return "remark"; + case Attention: return "attention"; + case User: return "user"; + case Rcs: return "rcs"; + } + return "unknown"; +} + +//-------------------------------------------------------------------------- + +int DocParamList::parse(const QString &cmdName) +{ + int retval=RetVal_OK; + DBG(("DocParamList::parse() start\n")); + g_nodeStack.push(this); + DocPara *par=0; + + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + } + doctokenizerYYsetStateParam(); + tok=doctokenizerYYlex(); + while (tok==TK_WORD) /* there is a parameter name */ + { + if (m_type==DocParamSect::Param) + { + g_hasParamCommand=TRUE; + checkArgumentName(g_token->name,TRUE); + } + else if (m_type==DocParamSect::RetVal) + { + g_hasReturnCommand=TRUE; + checkArgumentName(g_token->name,FALSE); + } + //m_params.append(g_token->name); + handleLinkedWord(this,m_params); + tok=doctokenizerYYlex(); + } + doctokenizerYYsetStatePara(); + if (tok==0) /* premature end of comment block */ + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s",cmdName.data()); + retval=0; + goto endparamlist; + } + ASSERT(tok==TK_WHITESPACE); + + par = new DocPara(this); + m_paragraphs.append(par); + retval = par->parse(); + par->markFirst(); + par->markLast(); + +endparamlist: + DBG(("DocParamList::parse() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +int DocParamList::parseXml(const QString ¶mName) +{ + int retval=RetVal_OK; + DBG(("DocParamList::parseXml() start\n")); + g_nodeStack.push(this); + + g_token->name = paramName; + if (m_type==DocParamSect::Param) + { + g_hasParamCommand=TRUE; + checkArgumentName(g_token->name,TRUE); + } + else if (m_type==DocParamSect::RetVal) + { + g_hasReturnCommand=TRUE; + checkArgumentName(g_token->name,FALSE); + } + + handleLinkedWord(this,m_params); + + do + { + DocPara *par = new DocPara(this); + retval = par->parse(); + if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace + // after and before + { + delete par; + break; + } + else // append the paragraph to the list + { + if (m_paragraphs.isEmpty()) + { + par->markFirst(); + } + else + { + m_paragraphs.last()->markLast(FALSE); + } + par->markLast(); + m_paragraphs.append(par); + } + + if (retval == 0) break; + + } while (retval==RetVal_CloseXml && + Mappers::htmlTagMapper->map(g_token->name)!=XML_PARAM && + Mappers::htmlTagMapper->map(g_token->name)!=XML_TYPEPARAM && + Mappers::htmlTagMapper->map(g_token->name)!=XML_EXCEPTION); + + + if (retval==0) /* premature end of comment block */ + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unterminated param or exception tag"); + } + else + { + retval=RetVal_OK; + } + + + DBG(("DocParamList::parse() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//-------------------------------------------------------------------------- + +int DocParamSect::parse(const QString &cmdName,bool xmlContext, Direction d) +{ + int retval=RetVal_OK; + DBG(("DocParamSect::parse() start\n")); + g_nodeStack.push(this); + + DocParamList *pl = new DocParamList(this,m_type,d); + if (m_children.isEmpty()) + { + pl->markFirst(); + pl->markLast(); + } + else + { + ASSERT(m_children.last()->kind()==DocNode::Kind_ParamList); + ((DocParamList *)m_children.last())->markLast(FALSE); + pl->markLast(); + } + m_children.append(pl); + if (xmlContext) + { + retval = pl->parseXml(cmdName); + } + else + { + retval = pl->parse(cmdName); + } + + DBG(("DocParamSect::parse() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//-------------------------------------------------------------------------- + +int DocPara::handleSimpleSection(DocSimpleSect::Type t, bool xmlContext) +{ + DocSimpleSect *ss=0; + bool needsSeparator = FALSE; + if (!m_children.isEmpty() && // previous element + m_children.last()->kind()==Kind_SimpleSect && // was a simple sect + ((DocSimpleSect *)m_children.last())->type()==t && // of same type + t!=DocSimpleSect::User) // but not user defined + { + // append to previous section + ss=(DocSimpleSect *)m_children.last(); + needsSeparator = TRUE; + } + else // start new section + { + ss=new DocSimpleSect(this,t); + m_children.append(ss); + } + int rv = RetVal_OK; + if (xmlContext) + { + return ss->parseXml(); + } + else + { + rv = ss->parse(t==DocSimpleSect::User,needsSeparator); + } + return (rv!=TK_NEWPARA) ? rv : RetVal_OK; +} + +int DocPara::handleParamSection(const QString &cmdName, + DocParamSect::Type t, + bool xmlContext=FALSE, + int direction=DocParamSect::Unspecified) +{ + DocParamSect *ps=0; + if (!m_children.isEmpty() && // previous element + m_children.last()->kind()==Kind_ParamSect && // was a param sect + ((DocParamSect *)m_children.last())->type()==t) // of same type + { + // append to previous section + ps=(DocParamSect *)m_children.last(); + } + else // start new section + { + ps=new DocParamSect(this,t); + m_children.append(ps); + } + int rv=ps->parse(cmdName,xmlContext,(DocParamSect::Direction)direction); + return (rv!=TK_NEWPARA) ? rv : RetVal_OK; +} + +int DocPara::handleXRefItem() +{ + int retval=doctokenizerYYlex(); + ASSERT(retval==TK_WHITESPACE); + doctokenizerYYsetStateXRefItem(); + retval=doctokenizerYYlex(); + if (retval==RetVal_OK) + { + DocXRefItem *ref = new DocXRefItem(this,g_token->id,g_token->name); + if (ref->parse()) + { + m_children.append(ref); + } + else + { + delete ref; + } + } + doctokenizerYYsetStatePara(); + return retval; +} + +void DocPara::handleIncludeOperator(const QString &cmdName,DocIncOperator::Type t) +{ + DBG(("handleIncludeOperator(%s)\n",cmdName.data())); + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + doctokenizerYYsetStatePattern(); + tok=doctokenizerYYlex(); + doctokenizerYYsetStatePara(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s", cmdName.data()); + return; + } + else if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + DocIncOperator *op = new DocIncOperator(this,t,g_token->name,g_context,g_isExample,g_exampleName); + DocNode *n1 = m_children.last(); + DocNode *n2 = n1!=0 ? m_children.prev() : 0; + bool isFirst = n1==0 || // no last node + (n1->kind()!=DocNode::Kind_IncOperator && + n1->kind()!=DocNode::Kind_WhiteSpace + ) || // last node is not operator or whitespace + (n1->kind()==DocNode::Kind_WhiteSpace && + n2!=0 && n2->kind()!=DocNode::Kind_IncOperator + ); // previous not is not operator + op->markFirst(isFirst); + op->markLast(TRUE); + if (n1!=0 && n1->kind()==DocNode::Kind_IncOperator) + { + ((DocIncOperator *)n1)->markLast(FALSE); + } + else if (n1!=0 && n1->kind()==DocNode::Kind_WhiteSpace && + n2!=0 && n2->kind()==DocNode::Kind_IncOperator + ) + { + ((DocIncOperator *)n2)->markLast(FALSE); + } + m_children.append(op); + op->parse(); +} + +void DocPara::handleImage(const QString &cmdName) +{ + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + tok=doctokenizerYYlex(); + if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + DocImage::Type t; + QString imgType = g_token->name.lower(); + if (imgType=="html") t=DocImage::Html; + else if (imgType=="latex") t=DocImage::Latex; + else if (imgType=="rtf") t=DocImage::Rtf; + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: image type %s specified as the first argument of " + "%s is not valid", + imgType.data(),cmdName.data()); + return; + } + doctokenizerYYsetStateFile(); + tok=doctokenizerYYlex(); + doctokenizerYYsetStatePara(); + if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + HtmlAttribList attrList; + DocImage *img = new DocImage(this,attrList,findAndCopyImage(g_token->name,t),t); + m_children.append(img); + img->parse(); +} + +void DocPara::handleDotFile(const QString &cmdName) +{ + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + doctokenizerYYsetStateFile(); + tok=doctokenizerYYlex(); + doctokenizerYYsetStatePara(); + if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + QString name = g_token->name; + DocDotFile *df = new DocDotFile(this,name,g_context); + m_children.append(df); + df->parse(); +} + +void DocPara::handleLink(const QString &cmdName,bool isJavaLink) +{ + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + doctokenizerYYsetStateLink(); + tok=doctokenizerYYlex(); + if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + doctokenizerYYsetStatePara(); + DocLink *lnk = new DocLink(this,g_token->name); + m_children.append(lnk); + QString leftOver = lnk->parse(isJavaLink); + if (!leftOver.isEmpty()) + { + m_children.append(new DocWord(this,leftOver)); + } +} + +void DocPara::handleRef(const QString &cmdName) +{ + DBG(("handleRef(%s)\n",cmdName.data())); + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + doctokenizerYYsetStateRef(); + tok=doctokenizerYYlex(); // get the reference id + DocRef *ref=0; + if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + goto endref; + } + ref = new DocRef(this,g_token->name,g_context); + m_children.append(ref); + ref->parse(); +endref: + doctokenizerYYsetStatePara(); +} + + +void DocPara::handleInclude(const QString &cmdName,DocInclude::Type t) +{ + DBG(("handleInclude(%s)\n",cmdName.data())); + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + doctokenizerYYsetStateFile(); + tok=doctokenizerYYlex(); + doctokenizerYYsetStatePara(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s",cmdName.data()); + return; + } + else if (tok!=TK_WORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + DocInclude *inc = new DocInclude(this,g_token->name,g_context,t,g_isExample,g_exampleName); + m_children.append(inc); + inc->parse(); +} + +void DocPara::handleSection(const QString &cmdName) +{ + // get the argument of the section command. + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + return; + } + tok=doctokenizerYYlex(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s\n", cmdName.data()); + return; + } + else if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + return; + } + g_token->sectionId = g_token->name; + doctokenizerYYsetStateSkipTitle(); + doctokenizerYYlex(); + doctokenizerYYsetStatePara(); +} + +int DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level) +{ + DocHtmlHeader *header = new DocHtmlHeader(this,tagHtmlAttribs,level); + m_children.append(header); + int retval = header->parse(); + return (retval==RetVal_OK) ? TK_NEWPARA : retval; +} + +// For XML tags whose content is stored in attributes rather than +// contained within the element, we need a way to inject the attribute +// text into the current paragraph. +bool DocPara::injectToken(int tok,const QString &tokText) +{ + g_token->name = tokText; + return defaultHandleToken(this,tok,m_children); +} + +int DocPara::handleStartCode() +{ + int retval = doctokenizerYYlex(); + // search for the first non-whitespace line, index is stored in li + int i=0,li=0,l=g_token->verb.length(); + while (iverb.at(i)==' ' || g_token->verb.at(i)=='\n')) + { + if (g_token->verb.at(i)=='\n') li=i+1; + i++; + } + m_children.append(new DocVerbatim(this,g_context,g_token->verb.mid(li),DocVerbatim::Code,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: code section ended without end marker"); + doctokenizerYYsetStatePara(); + return retval; +} + +void DocPara::handleInheritDoc() +{ + if (g_memberDef) // inheriting docs from a member + { + MemberDef *reMd = g_memberDef->reimplements(); + if (reMd) // member from which was inherited. + { + MemberDef *thisMd = g_memberDef; + //printf("{InheritDocs:%s=>%s}\n",g_memberDef->qualifiedName().data(),reMd->qualifiedName().data()); + docParserPushContext(); + g_scope=reMd->getOuterScope(); + g_context=g_scope->name(); + g_memberDef=reMd; + g_styleStack.clear(); + g_nodeStack.clear(); + g_copyStack.append(reMd); + internalValidatingParseDoc(this,m_children,reMd->briefDescription()); + internalValidatingParseDoc(this,m_children,reMd->documentation()); + g_copyStack.remove(reMd); + docParserPopContext(TRUE); + g_memberDef = thisMd; + } + } +} + + +int DocPara::handleCommand(const QString &cmdName) +{ + DBG(("handleCommand(%s)\n",cmdName.data())); + int retval = RetVal_OK; + int cmdId = Mappers::cmdMapper->map(cmdName); + switch (cmdId) + { + case CMD_UNKNOWN: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Found unknown command `\\%s'",cmdName.data()); + break; + case CMD_EMPHASIS: + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,TRUE)); + retval=handleStyleArgument(this,m_children,cmdName); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,FALSE)); + if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," ")); + break; + case CMD_BOLD: + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,TRUE)); + retval=handleStyleArgument(this,m_children,cmdName); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,FALSE)); + if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," ")); + break; + case CMD_CODE: + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Code,TRUE)); + retval=handleStyleArgument(this,m_children,cmdName); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Code,FALSE)); + if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," ")); + break; + case CMD_BSLASH: + m_children.append(new DocSymbol(this,DocSymbol::BSlash)); + break; + case CMD_AT: + m_children.append(new DocSymbol(this,DocSymbol::At)); + break; + case CMD_LESS: + m_children.append(new DocSymbol(this,DocSymbol::Less)); + break; + case CMD_GREATER: + m_children.append(new DocSymbol(this,DocSymbol::Greater)); + break; + case CMD_AMP: + m_children.append(new DocSymbol(this,DocSymbol::Amp)); + break; + case CMD_DOLLAR: + m_children.append(new DocSymbol(this,DocSymbol::Dollar)); + break; + case CMD_HASH: + m_children.append(new DocSymbol(this,DocSymbol::Hash)); + break; + case CMD_PERCENT: + m_children.append(new DocSymbol(this,DocSymbol::Percent)); + break; + case CMD_QUOTE: + m_children.append(new DocSymbol(this,DocSymbol::Quot)); + break; + case CMD_SA: + g_inSeeBlock=TRUE; + retval = handleSimpleSection(DocSimpleSect::See); + g_inSeeBlock=FALSE; + break; + case CMD_RETURN: + retval = handleSimpleSection(DocSimpleSect::Return); + g_hasReturnCommand=TRUE; + break; + case CMD_AUTHOR: + retval = handleSimpleSection(DocSimpleSect::Author); + break; + case CMD_AUTHORS: + retval = handleSimpleSection(DocSimpleSect::Authors); + break; + case CMD_VERSION: + retval = handleSimpleSection(DocSimpleSect::Version); + break; + case CMD_SINCE: + retval = handleSimpleSection(DocSimpleSect::Since); + break; + case CMD_DATE: + retval = handleSimpleSection(DocSimpleSect::Date); + break; + case CMD_NOTE: + retval = handleSimpleSection(DocSimpleSect::Note); + break; + case CMD_WARNING: + retval = handleSimpleSection(DocSimpleSect::Warning); + break; + case CMD_PRE: + retval = handleSimpleSection(DocSimpleSect::Pre); + break; + case CMD_POST: + retval = handleSimpleSection(DocSimpleSect::Post); + break; + case CMD_INVARIANT: + retval = handleSimpleSection(DocSimpleSect::Invar); + break; + case CMD_REMARK: + retval = handleSimpleSection(DocSimpleSect::Remark); + break; + case CMD_ATTENTION: + retval = handleSimpleSection(DocSimpleSect::Attention); + break; + case CMD_PAR: + retval = handleSimpleSection(DocSimpleSect::User); + break; + case CMD_LI: + { + DocSimpleList *sl=new DocSimpleList(this); + m_children.append(sl); + retval = sl->parse(); + } + break; + case CMD_SECTION: + { + handleSection(cmdName); + retval = RetVal_Section; + } + break; + case CMD_SUBSECTION: + { + handleSection(cmdName); + retval = RetVal_Subsection; + } + break; + case CMD_SUBSUBSECTION: + { + handleSection(cmdName); + retval = RetVal_Subsubsection; + } + break; + case CMD_PARAGRAPH: + { + handleSection(cmdName); + retval = RetVal_Paragraph; + } + break; + case CMD_STARTCODE: + { + doctokenizerYYsetStateCode(); + retval = handleStartCode(); + } + break; + case CMD_HTMLONLY: + { + doctokenizerYYsetStateHtmlOnly(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::HtmlOnly,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: htmlonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_MANONLY: + { + doctokenizerYYsetStateManOnly(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::ManOnly,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: manonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_LATEXONLY: + { + doctokenizerYYsetStateLatexOnly(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::LatexOnly,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: latexonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_XMLONLY: + { + doctokenizerYYsetStateXmlOnly(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::XmlOnly,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: xmlonly section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_VERBATIM: + { + doctokenizerYYsetStateVerbatim(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Verbatim,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: verbatim section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_DOT: + { + doctokenizerYYsetStateDot(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Dot,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: dot section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_MSC: + { + doctokenizerYYsetStateMsc(); + retval = doctokenizerYYlex(); + m_children.append(new DocVerbatim(this,g_context,g_token->verb,DocVerbatim::Msc,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: msc section ended without end marker"); + doctokenizerYYsetStatePara(); + } + break; + case CMD_ENDCODE: + case CMD_ENDHTMLONLY: + case CMD_ENDMANONLY: + case CMD_ENDLATEXONLY: + case CMD_ENDXMLONLY: + case CMD_ENDLINK: + case CMD_ENDVERBATIM: + case CMD_ENDDOT: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); + break; + case CMD_ENDMSC: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); + break; + case CMD_PARAM: + retval = handleParamSection(cmdName,DocParamSect::Param,FALSE,g_token->paramDir); + break; + case CMD_TPARAM: + retval = handleParamSection(cmdName,DocParamSect::TemplateParam,FALSE,g_token->paramDir); + break; + case CMD_RETVAL: + retval = handleParamSection(cmdName,DocParamSect::RetVal); + break; + case CMD_EXCEPTION: + retval = handleParamSection(cmdName,DocParamSect::Exception); + break; + case CMD_XREFITEM: + retval = handleXRefItem(); + break; + case CMD_LINEBREAK: + { + DocLineBreak *lb = new DocLineBreak(this); + m_children.append(lb); + } + break; + case CMD_ANCHOR: + { + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + break; + } + tok=doctokenizerYYlex(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s",cmdName.data()); + break; + } + else if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + break; + } + DocAnchor *anchor = new DocAnchor(this,g_token->name,FALSE); + m_children.append(anchor); + } + break; + case CMD_ADDINDEX: + { + DocIndexEntry *ie = new DocIndexEntry(this, + g_scope!=Doxygen::globalScope?g_scope:0, + g_memberDef); + m_children.append(ie); + retval = ie->parse(); + } + break; + case CMD_INTERNAL: + retval = RetVal_Internal; + break; + case CMD_COPYDOC: // fall through + case CMD_COPYBRIEF: // fall through + case CMD_COPYDETAILS: + { + int tok=doctokenizerYYlex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected whitespace after %s command", + cmdName.data()); + break; + } + tok=doctokenizerYYlex(); + if (tok==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment block while parsing the " + "argument of command %s\n", cmdName.data()); + break; + } + else if (tok!=TK_WORD && tok!=TK_LNKWORD) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected token %s as the argument of %s", + tokToString(tok),cmdName.data()); + break; + } + DocCopy *cpy = new DocCopy(this,g_token->name, + cmdId==CMD_COPYDOC || cmdId==CMD_COPYBRIEF, + cmdId==CMD_COPYDOC || cmdId==CMD_COPYDETAILS); + m_children.append(cpy); + cpy->parse(); + } + break; + case CMD_INCLUDE: + handleInclude(cmdName,DocInclude::Include); + break; + case CMD_INCWITHLINES: + handleInclude(cmdName,DocInclude::IncWithLines); + break; + case CMD_DONTINCLUDE: + handleInclude(cmdName,DocInclude::DontInclude); + break; + case CMD_HTMLINCLUDE: + handleInclude(cmdName,DocInclude::HtmlInclude); + break; + case CMD_VERBINCLUDE: + handleInclude(cmdName,DocInclude::VerbInclude); + break; + case CMD_SKIP: + handleIncludeOperator(cmdName,DocIncOperator::Skip); + break; + case CMD_UNTIL: + handleIncludeOperator(cmdName,DocIncOperator::Until); + break; + case CMD_SKIPLINE: + handleIncludeOperator(cmdName,DocIncOperator::SkipLine); + break; + case CMD_LINE: + handleIncludeOperator(cmdName,DocIncOperator::Line); + break; + case CMD_IMAGE: + handleImage(cmdName); + break; + case CMD_DOTFILE: + handleDotFile(cmdName); + break; + case CMD_LINK: + handleLink(cmdName,FALSE); + break; + case CMD_JAVALINK: + handleLink(cmdName,TRUE); + break; + case CMD_REF: // fall through + case CMD_SUBPAGE: + handleRef(cmdName); + break; + case CMD_SECREFLIST: + { + DocSecRefList *list = new DocSecRefList(this); + m_children.append(list); + list->parse(); + } + break; + case CMD_SECREFITEM: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); + break; + case CMD_ENDSECREFLIST: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); + break; + case CMD_FORMULA: + { + DocFormula *form=new DocFormula(this,g_token->id); + m_children.append(form); + } + break; + //case CMD_LANGSWITCH: + // retval = handleLanguageSwitch(); + // break; + case CMD_INTERNALREF: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); + break; + case CMD_INHERITDOC: + handleInheritDoc(); + break; + default: + // we should not get here! + ASSERT(0); + break; + } + INTERNAL_ASSERT(retval==0 || retval==RetVal_OK || retval==RetVal_SimpleSec || + retval==TK_LISTITEM || retval==TK_ENDLIST || retval==TK_NEWPARA || + retval==RetVal_Section || retval==RetVal_EndList || + retval==RetVal_Internal || retval==RetVal_SwitchLang + ); + DBG(("handleCommand(%s) end retval=%x\n",cmdName.data(),retval)); + return retval; +} + +static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, + const char *attrName, + QString *result) +{ + + HtmlAttribListIterator li(tagHtmlAttribs); + HtmlAttrib *opt; + for (li.toFirst();(opt=li.current());++li) + { + if (opt->name==attrName) + { + *result = opt->value; + return TRUE; + } + } + return FALSE; +} + +int DocPara::handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tagHtmlAttribs) +{ + DBG(("handleHtmlStartTag(%s,%d)\n",tagName.data(),tagHtmlAttribs.count())); + int retval=RetVal_OK; + int tagId = Mappers::htmlTagMapper->map(tagName); + if (g_token->emptyTag && !(tagId&XML_CmdMask) && tagId!=HTML_UNKNOWN) + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: HTML tags may not use the 'empty tag' XHTML syntax."); + switch (tagId) + { + case HTML_UL: + { + DocHtmlList *list = new DocHtmlList(this,tagHtmlAttribs,DocHtmlList::Unordered); + m_children.append(list); + retval=list->parse(); + } + break; + case HTML_OL: + { + DocHtmlList *list = new DocHtmlList(this,tagHtmlAttribs,DocHtmlList::Ordered); + m_children.append(list); + retval=list->parse(); + } + break; + case HTML_LI: + if (!insideUL(this) && !insideOL(this)) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: lonely
  • tag found"); + } + else + { + retval=RetVal_ListItem; + } + break; + case HTML_BOLD: + handleStyleEnter(this,m_children,DocStyleChange::Bold,&g_token->attribs); + break; + case HTML_CODE: + if (g_fileName.right(3)==".cs") + // for C# code we treat as an XML tag + { + doctokenizerYYsetStateXmlCode(); + retval = handleStartCode(); + } + else // normal HTML markup + { + handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs); + } + break; + case HTML_EMPHASIS: + handleStyleEnter(this,m_children,DocStyleChange::Italic,&g_token->attribs); + break; + case HTML_DIV: + handleStyleEnter(this,m_children,DocStyleChange::Div,&g_token->attribs); + break; + case HTML_SPAN: + handleStyleEnter(this,m_children,DocStyleChange::Span,&g_token->attribs); + break; + case HTML_SUB: + handleStyleEnter(this,m_children,DocStyleChange::Subscript,&g_token->attribs); + break; + case HTML_SUP: + handleStyleEnter(this,m_children,DocStyleChange::Superscript,&g_token->attribs); + break; + case HTML_CENTER: + handleStyleEnter(this,m_children,DocStyleChange::Center,&g_token->attribs); + break; + case HTML_SMALL: + handleStyleEnter(this,m_children,DocStyleChange::Small,&g_token->attribs); + break; + case HTML_PRE: + handleStyleEnter(this,m_children,DocStyleChange::Preformatted,&g_token->attribs); + setInsidePreformatted(TRUE); + //doctokenizerYYsetInsidePre(TRUE); + break; + case HTML_P: + retval=TK_NEWPARA; + break; + case HTML_DL: + { + DocHtmlDescList *list = new DocHtmlDescList(this,tagHtmlAttribs); + m_children.append(list); + retval=list->parse(); + } + break; + case HTML_DT: + retval = RetVal_DescTitle; + break; + case HTML_DD: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag
    found"); + break; + case HTML_TABLE: + { + DocHtmlTable *table = new DocHtmlTable(this,tagHtmlAttribs); + m_children.append(table); + retval=table->parse(); + } + break; + case HTML_TR: + retval = RetVal_TableRow; + break; + case HTML_TD: + retval = RetVal_TableCell; + break; + case HTML_TH: + retval = RetVal_TableHCell; + break; + case HTML_CAPTION: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_BR: + { + DocLineBreak *lb = new DocLineBreak(this); + m_children.append(lb); + } + break; + case HTML_HR: + { + DocHorRuler *hr = new DocHorRuler(this); + m_children.append(hr); + } + break; + case HTML_A: + retval=handleAHref(this,m_children,tagHtmlAttribs); + break; + case HTML_H1: + retval=handleHtmlHeader(tagHtmlAttribs,1); + break; + case HTML_H2: + retval=handleHtmlHeader(tagHtmlAttribs,2); + break; + case HTML_H3: + retval=handleHtmlHeader(tagHtmlAttribs,3); + break; + case HTML_H4: + retval=handleHtmlHeader(tagHtmlAttribs,4); + break; + case HTML_H5: + retval=handleHtmlHeader(tagHtmlAttribs,5); + break; + case HTML_H6: + retval=handleHtmlHeader(tagHtmlAttribs,6); + break; + case HTML_IMG: + { + HtmlAttribListIterator li(tagHtmlAttribs); + HtmlAttrib *opt; + bool found=FALSE; + int index=0; + for (li.toFirst();(opt=li.current());++li,++index) + { + //printf("option name=%s value=%s\n",opt->name.data(),opt->value.data()); + if (opt->name=="src" && !opt->value.isEmpty()) + { + // copy attributes + HtmlAttribList attrList = tagHtmlAttribs; + // and remove the href attribute + bool result = attrList.remove(index); + ASSERT(result); + DocImage *img = new DocImage(this,attrList,opt->value,DocImage::Html); + m_children.append(img); + found = TRUE; + } + } + if (!found) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: IMG tag does not have a SRC attribute!\n"); + } + } + break; + + case XML_SUMMARY: + case XML_REMARKS: + case XML_VALUE: + case XML_PARA: + if (!m_children.isEmpty()) + { + retval = TK_NEWPARA; + } + break; + case XML_EXAMPLE: + case XML_DESCRIPTION: + if (insideTable(this)) + { + retval=RetVal_TableCell; + } + break; + case XML_C: + handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs); + break; + case XML_PARAM: + case XML_TYPEPARAM: + { + QString paramName; + if (findAttribute(tagHtmlAttribs,"name",¶mName)) + { + retval = handleParamSection(paramName, + tagId==XML_PARAM ? DocParamSect::Param : DocParamSect::TemplateParam, + TRUE); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from tag."); + } + } + break; + case XML_PARAMREF: + case XML_TYPEPARAMREF: + { + QString paramName; + if (findAttribute(tagHtmlAttribs,"name",¶mName)) + { + //printf("paramName=%s\n",paramName.data()); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,TRUE)); + m_children.append(new DocWord(this,paramName)); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,FALSE)); + if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," ")); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from tag.",tagId==XML_PARAMREF?"":"type"); + } + } + break; + case XML_EXCEPTION: + { + QString exceptName; + if (findAttribute(tagHtmlAttribs,"cref",&exceptName)) + { + retval = handleParamSection(exceptName,DocParamSect::Exception,TRUE); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from tag."); + } + } + break; + case XML_ITEM: + case XML_LISTHEADER: + if (insideTable(this)) + { + retval=RetVal_TableRow; + } + else if (insideUL(this) || insideOL(this)) + { + retval=RetVal_ListItem; + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: lonely tag found"); + } + break; + case XML_RETURNS: + retval = handleSimpleSection(DocSimpleSect::Return,TRUE); + g_hasReturnCommand=TRUE; + break; + case XML_TERM: + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,TRUE)); + if (insideTable(this)) + { + retval=RetVal_TableCell; + } + break; + case XML_SEE: + // I'm not sure if is the same as or if it + // should you link a member without producing a section. The + // C# specification is extremely vague about this (but what else + // can we expect from Microsoft...) + { + QString cref; + //printf("XML_SEE: empty tag=%d\n",g_token->emptyTag); + if (findAttribute(tagHtmlAttribs,"cref",&cref)) + { + if (g_token->emptyTag) // style + { + bool inSeeBlock = g_inSeeBlock; + g_token->name = cref; + g_inSeeBlock = TRUE; + handleLinkedWord(this,m_children); + g_inSeeBlock = inSeeBlock; + } + else // ... style + { + //DocRef *ref = new DocRef(this,cref); + //m_children.append(ref); + //ref->parse(); + doctokenizerYYsetStatePara(); + DocLink *lnk = new DocLink(this,cref); + m_children.append(lnk); + QString leftOver = lnk->parse(FALSE,TRUE); + if (!leftOver.isEmpty()) + { + m_children.append(new DocWord(this,leftOver)); + } + } + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'cref' attribute from tag."); + } + } + break; + case XML_SEEALSO: + { + QString cref; + if (findAttribute(tagHtmlAttribs,"cref",&cref)) + { + // Look for an existing "see" section + DocSimpleSect *ss=0; + QListIterator cli(m_children); + DocNode *n; + for (cli.toFirst();(n=cli.current());++cli) + { + if (n->kind()==Kind_SimpleSect && ((DocSimpleSect *)n)->type()==DocSimpleSect::See) + { + ss = (DocSimpleSect *)n; + } + } + + if (!ss) // start new section + { + ss=new DocSimpleSect(this,DocSimpleSect::See); + m_children.append(ss); + } + + ss->appendLinkWord(cref); + retval = RetVal_OK; + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'cref' attribute from tag."); + } + } + break; + case XML_LIST: + { + QString type; + findAttribute(tagHtmlAttribs,"type",&type); + DocHtmlList::Type listType = DocHtmlList::Unordered; + if (type=="number") + { + listType=DocHtmlList::Ordered; + } + if (type=="table") + { + DocHtmlTable *table = new DocHtmlTable(this,tagHtmlAttribs); + m_children.append(table); + retval=table->parseXml(); + } + else + { + HtmlAttribList emptyList; + DocHtmlList *list = new DocHtmlList(this,emptyList,listType); + m_children.append(list); + retval=list->parseXml(); + } + } + break; + case XML_INCLUDE: + case XML_PERMISSION: + // These tags are defined in .Net but are currently unsupported + break; + case HTML_UNKNOWN: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported xml/html tag <%s> found", tagName.data()); + m_children.append(new DocWord(this, "<"+tagName+tagHtmlAttribs.toString()+">")); + break; + default: + // we should not get here! + ASSERT(0); + break; + } + return retval; +} + +int DocPara::handleHtmlEndTag(const QString &tagName) +{ + DBG(("handleHtmlEndTag(%s)\n",tagName.data())); + int tagId = Mappers::htmlTagMapper->map(tagName); + int retval=RetVal_OK; + switch (tagId) + { + case HTML_UL: + if (!insideUL(this)) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag without matching
      "); + } + else + { + retval=RetVal_EndList; + } + break; + case HTML_OL: + if (!insideOL(this)) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag without matching
        "); + } + else + { + retval=RetVal_EndList; + } + break; + case HTML_LI: + if (!insideLI(this)) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found tag without matching
      1. "); + } + else + { + // ignore
      2. tags + } + break; + //case HTML_PRE: + // if (!insidePRE(this)) + // { + // warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found
  • tag without matching
    ");
    +    //  }
    +    //  else
    +    //  {
    +    //    retval=RetVal_EndPre;
    +    //  }
    +    //  break;
    +    case HTML_BOLD:
    +      handleStyleLeave(this,m_children,DocStyleChange::Bold,"b");
    +      break;
    +    case HTML_CODE:
    +      handleStyleLeave(this,m_children,DocStyleChange::Code,"code");
    +      break;
    +    case HTML_EMPHASIS:
    +      handleStyleLeave(this,m_children,DocStyleChange::Italic,"em");
    +      break;
    +    case HTML_DIV:
    +      handleStyleLeave(this,m_children,DocStyleChange::Div,"div");
    +      break;
    +    case HTML_SPAN:
    +      handleStyleLeave(this,m_children,DocStyleChange::Span,"span");
    +      break;
    +    case HTML_SUB:
    +      handleStyleLeave(this,m_children,DocStyleChange::Subscript,"sub");
    +      break;
    +    case HTML_SUP:
    +      handleStyleLeave(this,m_children,DocStyleChange::Superscript,"sup");
    +      break;
    +    case HTML_CENTER:
    +      handleStyleLeave(this,m_children,DocStyleChange::Center,"center");
    +      break;
    +    case HTML_SMALL:
    +      handleStyleLeave(this,m_children,DocStyleChange::Small,"small");
    +      break;
    +    case HTML_PRE:
    +      handleStyleLeave(this,m_children,DocStyleChange::Preformatted,"pre");
    +      setInsidePreformatted(FALSE);
    +      //doctokenizerYYsetInsidePre(FALSE);
    +      break;
    +    case HTML_P:
    +      // ignore 

    tag + break; + case HTML_DL: + retval=RetVal_EndDesc; + break; + case HTML_DT: + // ignore tag + break; + case HTML_DD: + // ignore tag + break; + case HTML_TABLE: + retval=RetVal_EndTable; + break; + case HTML_TR: + // ignore tag + break; + case HTML_TD: + // ignore tag + break; + case HTML_TH: + // ignore tag + break; + case HTML_CAPTION: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_BR: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Illegal
    tag found\n"); + break; + case HTML_H1: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_H2: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_H3: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_IMG: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_HR: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag found"); + break; + case HTML_A: + //warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag
    found"); + // ignore tag (can be part of + break; + + case XML_TERM: + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Bold,FALSE)); + break; + case XML_SUMMARY: + case XML_REMARKS: + case XML_PARA: + case XML_VALUE: + case XML_LIST: + case XML_EXAMPLE: + case XML_PARAM: + case XML_TYPEPARAM: + case XML_RETURNS: + case XML_SEEALSO: + case XML_EXCEPTION: + retval = RetVal_CloseXml; + break; + case XML_C: + handleStyleLeave(this,m_children,DocStyleChange::Code,"c"); + break; + case XML_ITEM: + case XML_LISTHEADER: + case XML_INCLUDE: + case XML_PERMISSION: + case XML_DESCRIPTION: + case XML_PARAMREF: + case XML_TYPEPARAMREF: + // These tags are defined in .Net but are currently unsupported + break; + case HTML_UNKNOWN: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported xml/html tag found", tagName.data()); + m_children.append(new DocWord(this,"")); + break; + default: + // we should not get here! + warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected end tag %s\n",tagName.data()); + ASSERT(0); + break; + } + return retval; +} + +int DocPara::parse() +{ + DBG(("DocPara::parse() start\n")); + g_nodeStack.push(this); + // handle style commands "inherited" from the previous paragraph + handleInitialStyleCommands(this,m_children); + int tok; + int retval=0; + while ((tok=doctokenizerYYlex())) // get the next token + { +reparsetoken: + DBG(("token %s at %d",tokToString(tok),doctokenizerYYlineno)); + if (tok==TK_WORD || tok==TK_LNKWORD || tok==TK_SYMBOL || tok==TK_URL || + tok==TK_COMMAND || tok==TK_HTMLTAG + ) + { + DBG((" name=%s",g_token->name.data())); + } + DBG(("\n")); + switch(tok) + { + case TK_WORD: + m_children.append(new DocWord(this,g_token->name)); + break; + case TK_LNKWORD: + handleLinkedWord(this,m_children); + break; + case TK_URL: + m_children.append(new DocURL(this,g_token->name,g_token->isEMailAddr)); + break; + case TK_WHITESPACE: + { + // prevent leading whitespace and collapse multiple whitespace areas + DocNode::Kind k; + if (insidePRE(this) || // all whitespace is relevant + ( + // remove leading whitespace + !m_children.isEmpty() && + // and whitespace after certain constructs + (k=m_children.last()->kind())!=DocNode::Kind_HtmlDescList && + k!=DocNode::Kind_HtmlTable && + k!=DocNode::Kind_HtmlList && + k!=DocNode::Kind_SimpleSect && + k!=DocNode::Kind_AutoList && + k!=DocNode::Kind_SimpleList && + /*k!=DocNode::Kind_Verbatim &&*/ + k!=DocNode::Kind_HtmlHeader && + k!=DocNode::Kind_ParamSect && + k!=DocNode::Kind_XRefItem + ) + ) + { + m_children.append(new DocWhiteSpace(this,g_token->chars)); + } + } + break; + case TK_LISTITEM: + { + DBG(("found list item at %d parent=%d\n",g_token->indent,parent()->kind())); + DocNode *n=parent(); + while (n && n->kind()!=DocNode::Kind_AutoList) n=n->parent(); + if (n) // we found an auto list up in the hierarchy + { + DocAutoList *al = (DocAutoList *)n; + DBG(("previous list item at %d\n",al->indent())); + if (al->indent()>=g_token->indent) + // new item at the same or lower indent level + { + retval=TK_LISTITEM; + goto endparagraph; + } + } + + // determine list depth + int depth = 0; + n=parent(); + while(n) + { + if(n->kind() == DocNode::Kind_AutoList) ++depth; + n=n->parent(); + } + + // first item or sub list => create new list + DocAutoList *al=0; + do + { + al = new DocAutoList(this,g_token->indent,g_token->isEnumList, + depth); + m_children.append(al); + retval = al->parse(); + } while (retval==TK_LISTITEM && // new list + al->indent()==g_token->indent // at same indent level + ); + + // check the return value + if (retval==RetVal_SimpleSec) // auto list ended due to simple section command + { + // Reparse the token that ended the section at this level, + // so a new simple section will be started at this level. + // This is the same as unputting the last read token and continuing. + g_token->name = g_token->simpleSectName; + if (g_token->name.left(4)=="rcs:") // RCS section + { + g_token->name = g_token->name.mid(4); + g_token->text = g_token->simpleSectText; + tok = TK_RCSTAG; + } + else // other section + { + tok = TK_COMMAND; + } + DBG(("reparsing command %s\n",g_token->name.data())); + goto reparsetoken; + } + else if (retval==TK_ENDLIST) + { + if (al->indent()>g_token->indent) // end list + { + goto endparagraph; + } + else // continue with current paragraph + { + } + } + else // paragraph ended due to TK_NEWPARA, TK_LISTITEM, or EOF + { + goto endparagraph; + } + } + break; + case TK_ENDLIST: + DBG(("Found end of list inside of paragraph at line %d\n",doctokenizerYYlineno)); + if (parent()->kind()==DocNode::Kind_AutoListItem) + { + ASSERT(parent()->parent()->kind()==DocNode::Kind_AutoList); + DocAutoList *al = (DocAutoList *)parent()->parent(); + if (al->indent()>=g_token->indent) + { + // end of list marker ends this paragraph + retval=TK_ENDLIST; + goto endparagraph; + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: End of list marker found " + "has invalid indent level"); + } + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: End of list marker found without any preceding " + "list items"); + } + break; + case TK_COMMAND: + { + // see if we have to start a simple section + int cmd = Mappers::cmdMapper->map(g_token->name); + DocNode *n=parent(); + while (n && + n->kind()!=DocNode::Kind_SimpleSect && + n->kind()!=DocNode::Kind_ParamSect + ) + { + n=n->parent(); + } + if (cmd&SIMPLESECT_BIT) + { + if (n) // already in a simple section + { + // simple section cannot start in this paragraph, need + // to unwind the stack and remember the command. + g_token->simpleSectName = g_token->name.copy(); + retval=RetVal_SimpleSec; + goto endparagraph; + } + } + // see if we are in a simple list + n=parent(); + while (n && n->kind()!=DocNode::Kind_SimpleListItem) n=n->parent(); + if (n) + { + if (cmd==CMD_LI) + { + retval=RetVal_ListItem; + goto endparagraph; + } + } + + // handle the command + retval=handleCommand(g_token->name.copy()); + DBG(("handleCommand returns %x\n",retval)); + + // check the return value + if (retval==RetVal_SimpleSec) + { + // Reparse the token that ended the section at this level, + // so a new simple section will be started at this level. + // This is the same as unputting the last read token and continuing. + g_token->name = g_token->simpleSectName; + if (g_token->name.left(4)=="rcs:") // RCS section + { + g_token->name = g_token->name.mid(4); + g_token->text = g_token->simpleSectText; + tok = TK_RCSTAG; + } + else // other section + { + tok = TK_COMMAND; + } + DBG(("reparsing command %s\n",g_token->name.data())); + goto reparsetoken; + } + else if (retval==RetVal_OK) + { + // the command ended normally, keep scanning for new tokens. + retval = 0; + } + else if (retval>0 && retvalendTag) // found a start tag + { + retval = handleHtmlStartTag(g_token->name,g_token->attribs); + } + else // found an end tag + { + retval = handleHtmlEndTag(g_token->name); + } + if (retval==RetVal_OK) + { + // the command ended normally, keep scanner for new tokens. + retval = 0; + } + else + { + goto endparagraph; + } + } + break; + case TK_SYMBOL: + { + char letter='\0'; + DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name,&letter); + if (s!=DocSymbol::Unknown) + { + m_children.append(new DocSymbol(this,s,letter)); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found", + g_token->name.data()); + } + break; + } + case TK_NEWPARA: + retval=TK_NEWPARA; + goto endparagraph; + case TK_RCSTAG: + { + DocNode *n=parent(); + while (n && + n->kind()!=DocNode::Kind_SimpleSect && + n->kind()!=DocNode::Kind_ParamSect + ) + { + n=n->parent(); + } + if (n) // already in a simple section + { + // simple section cannot start in this paragraph, need + // to unwind the stack and remember the command. + g_token->simpleSectName = "rcs:"+g_token->name; + g_token->simpleSectText = g_token->text; + retval=RetVal_SimpleSec; + goto endparagraph; + } + + // see if we are in a simple list + DocSimpleSect *ss=new DocSimpleSect(this,DocSimpleSect::Rcs); + m_children.append(ss); + ss->parseRcs(); + } + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno, + "Warning: Found unexpected token (id=%x)\n",tok); + break; + } + } + retval=0; +endparagraph: + handlePendingStyleCommands(this,m_children); + DocNode *n = g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocPara::parse() end retval=%x\n",retval)); + INTERNAL_ASSERT(retval==0 || retval==TK_NEWPARA || retval==TK_LISTITEM || + retval==TK_ENDLIST || retval>RetVal_OK + ); + + return retval; +} + +//-------------------------------------------------------------------------- + +int DocSection::parse() +{ + DBG(("DocSection::parse() start %s level=%d\n",g_token->sectionId.data(),m_level)); + int retval=RetVal_OK; + g_nodeStack.push(this); + + SectionInfo *sec; + if (!m_id.isEmpty()) + { + sec=Doxygen::sectionDict[m_id]; + if (sec) + { + m_file = sec->fileName; + m_anchor = sec->label; + m_title = sec->title; + if (m_title.isEmpty()) m_title = sec->label; + if (g_sectionDict && g_sectionDict->find(m_id)==0) + { + g_sectionDict->insert(m_id,sec); + } + } + } + + // first parse any number of paragraphs + bool isFirst=TRUE; + DocPara *lastPar=0; + do + { + DocPara *par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + retval=par->parse(); + if (!par->isEmpty()) + { + m_children.append(par); + lastPar=par; + } + else + { + delete par; + } + if (retval==TK_LISTITEM) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Invalid list item found"); + } + } while (retval!=0 && + retval!=RetVal_Internal && + retval!=RetVal_Section && + retval!=RetVal_Subsection && + retval!=RetVal_Subsubsection && + retval!=RetVal_Paragraph + ); + + if (lastPar) lastPar->markLast(); + + //printf("m_level=%d <-> %d\n",m_level,Doxygen::subpageNestingLevel); + + if (retval==RetVal_Subsection && m_level==Doxygen::subpageNestingLevel+1) + { + // then parse any number of nested sections + while (retval==RetVal_Subsection) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(2+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + } + else if (retval==RetVal_Subsubsection && m_level==Doxygen::subpageNestingLevel+2) + { + // then parse any number of nested sections + while (retval==RetVal_Subsubsection) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(3+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + } + else if (retval==RetVal_Paragraph && m_level==QMIN(5,Doxygen::subpageNestingLevel+3)) + { + // then parse any number of nested sections + while (retval==RetVal_Paragraph) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(4+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + } + else if ((m_level<=1+Doxygen::subpageNestingLevel && retval==RetVal_Subsubsection) || + (m_level<=2+Doxygen::subpageNestingLevel && retval==RetVal_Paragraph) + ) + { + int level; + if (retval==RetVal_Subsection) level=2; + else if (retval==RetVal_Subsubsection) level=3; + else level=4; + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected %s " + "command found inside %s!", + sectionLevelToName[level],sectionLevelToName[m_level]); + retval=0; // stop parsing + + } + else if (retval==RetVal_Internal) + { + DocInternal *in = new DocInternal(this); + m_children.append(in); + retval = in->parse(m_level+1); + } + else + { + } + + INTERNAL_ASSERT(retval==0 || + retval==RetVal_Section || + retval==RetVal_Subsection || + retval==RetVal_Subsubsection || + retval==RetVal_Paragraph || + retval==RetVal_Internal + ); + + DBG(("DocSection::parse() end\n")); + DocNode *n = g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +//-------------------------------------------------------------------------- + +void DocText::parse() +{ + DBG(("DocText::parse() start\n")); + g_nodeStack.push(this); + doctokenizerYYsetStateText(); + + int tok; + while ((tok=doctokenizerYYlex())) // get the next token + { + switch(tok) + { + case TK_WORD: + m_children.append(new DocWord(this,g_token->name)); + break; + case TK_WHITESPACE: + m_children.append(new DocWhiteSpace(this,g_token->chars)); + break; + case TK_SYMBOL: + { + char letter='\0'; + DocSymbol::SymType s = DocSymbol::decodeSymbol(g_token->name,&letter); + if (s!=DocSymbol::Unknown) + { + m_children.append(new DocSymbol(this,s,letter)); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported symbol %s found", + g_token->name.data()); + } + } + break; + case TK_COMMAND: + switch (Mappers::cmdMapper->map(g_token->name)) + { + case CMD_BSLASH: + m_children.append(new DocSymbol(this,DocSymbol::BSlash)); + break; + case CMD_AT: + m_children.append(new DocSymbol(this,DocSymbol::At)); + break; + case CMD_LESS: + m_children.append(new DocSymbol(this,DocSymbol::Less)); + break; + case CMD_GREATER: + m_children.append(new DocSymbol(this,DocSymbol::Greater)); + break; + case CMD_AMP: + m_children.append(new DocSymbol(this,DocSymbol::Amp)); + break; + case CMD_DOLLAR: + m_children.append(new DocSymbol(this,DocSymbol::Dollar)); + break; + case CMD_HASH: + m_children.append(new DocSymbol(this,DocSymbol::Hash)); + break; + case CMD_PERCENT: + m_children.append(new DocSymbol(this,DocSymbol::Percent)); + break; + case CMD_QUOTE: + m_children.append(new DocSymbol(this,DocSymbol::Quot)); + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected command `%s' found", + g_token->name.data()); + break; + } + break; + default: + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected token %s", + tokToString(tok)); + break; + } + } + + handleUnclosedStyleCommands(); + + DocNode *n = g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocText::parse() end\n")); +} + + +//-------------------------------------------------------------------------- + +void DocRoot::parse() +{ + DBG(("DocRoot::parse() start\n")); + g_nodeStack.push(this); + doctokenizerYYsetStatePara(); + int retval=0; + + // first parse any number of paragraphs + bool isFirst=TRUE; + DocPara *lastPar=0; + do + { + DocPara *par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + retval=par->parse(); + if (!par->isEmpty()) + { + m_children.append(par); + lastPar=par; + } + else + { + delete par; + } + if (retval==TK_LISTITEM) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Invalid list item found"); + } + else if (retval==RetVal_Subsection) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found subsection command outside of section context!"); + } + else if (retval==RetVal_Subsubsection) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found subsubsection command outside of subsection context!"); + } + else if (retval==RetVal_Paragraph) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found paragraph command outside of subsubsection context!"); + } + } while (retval!=0 && retval!=RetVal_Section && retval!=RetVal_Internal); + if (lastPar) lastPar->markLast(); + + //printf("DocRoot::parse() retval=%d %d\n",retval,RetVal_Section); + // then parse any number of level1 sections + while (retval==RetVal_Section) + { + SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + if (sec) + { + DocSection *s=new DocSection(this, + QMIN(1+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Invalid section id `%s'; ignoring section",g_token->sectionId.data()); + retval = 0; + } + } + + if (retval==RetVal_Internal) + { + DocInternal *in = new DocInternal(this); + m_children.append(in); + retval = in->parse(1); + } + + + handleUnclosedStyleCommands(); + + DocNode *n = g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocRoot::parse() end\n")); +} + +//-------------------------------------------------------------------------- + +DocNode *validatingParseDoc(const char *fileName,int startLine, + Definition *ctx,MemberDef *md, + const char *input,bool indexWords, + bool isExample, const char *exampleName, + bool singleLine, bool linkFromIndex) +{ + //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?ctx->name().data():"", + // md?md->name().data():"", + // input); + //printf("========== validating %s at line %d\n",fileName,startLine); + //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",input); + //g_token = new TokenInfo; + + // store parser state so we can re-enter this function if needed + bool fortranOpt = Config_getBool("OPTIMIZE_FOR_FORTRAN"); + docParserPushContext(); + + if (ctx && + (ctx->definitionType()==Definition::TypeClass || + ctx->definitionType()==Definition::TypeNamespace + ) + ) + { + g_context = ctx->name(); + } + else if (ctx && ctx->definitionType()==Definition::TypePage) + { + Definition *scope = ((PageDef*)ctx)->getPageScope(); + if (scope) g_context = scope->name(); + } + else if (ctx && ctx->definitionType()==Definition::TypeGroup) + { + Definition *scope = ((GroupDef*)ctx)->getGroupScope(); + if (scope) g_context = scope->name(); + } + else + { + g_context = ""; + } + g_scope = ctx; + //printf("g_context=%s\n",g_context.data()); + + if (indexWords && md && Doxygen::searchIndex) + { + g_searchUrl=md->getOutputFileBase(); + Doxygen::searchIndex->setCurrentDoc( + (fortranOpt?theTranslator->trSubprogram(TRUE,TRUE):theTranslator->trMember(TRUE,TRUE))+" "+md->qualifiedName(), + g_searchUrl, + md->anchor()); + } + else if (indexWords && ctx && Doxygen::searchIndex) + { + g_searchUrl=ctx->getOutputFileBase(); + QCString name = ctx->qualifiedName(); + if (Config_getBool("OPTIMIZE_OUTPUT_JAVA")) + { + name = substitute(name,"::","."); + } + switch (ctx->definitionType()) + { + case Definition::TypePage: + { + PageDef *pd = (PageDef *)ctx; + if (!pd->title().isEmpty()) + { + name = theTranslator->trPage(TRUE,TRUE)+" "+pd->title(); + } + else + { + name = theTranslator->trPage(TRUE,TRUE)+" "+pd->name(); + } + } + break; + case Definition::TypeClass: + { + ClassDef *cd = (ClassDef *)ctx; + name.prepend(cd->compoundTypeString()+" "); + } + break; + case Definition::TypeNamespace: + { + if (Config_getBool("OPTIMIZE_OUTPUT_JAVA")) + { + name = theTranslator->trPackage(name); + } + else if(fortranOpt) + { + name.prepend(theTranslator->trModule(TRUE,TRUE)+" "); + } + else + { + name.prepend(theTranslator->trNamespace(TRUE,TRUE)+" "); + } + } + break; + case Definition::TypeGroup: + { + GroupDef *gd = (GroupDef *)ctx; + if (gd->groupTitle()) + { + name = theTranslator->trGroup(TRUE,TRUE)+" "+gd->groupTitle(); + } + else + { + name.prepend(theTranslator->trGroup(TRUE,TRUE)+" "); + } + } + break; + default: + break; + } + Doxygen::searchIndex->setCurrentDoc(name,g_searchUrl); + } + else + { + g_searchUrl=""; + } + + g_fileName = fileName; + g_relPath = (!linkFromIndex && ctx) ? + QString(relativePathToRoot(ctx->getOutputFileBase())) : + QString(""); + //printf("ctx->name=%s relPath=%s\n",ctx->name().data(),g_relPath.data()); + g_memberDef = md; + g_nodeStack.clear(); + g_styleStack.clear(); + g_initialStyleStack.clear(); + g_inSeeBlock = FALSE; + g_insideHtmlLink = FALSE; + g_includeFileText = ""; + g_includeFileOffset = 0; + g_includeFileLength = 0; + g_isExample = isExample; + g_exampleName = exampleName; + g_hasParamCommand = FALSE; + g_hasReturnCommand = FALSE; + g_paramsFound.setAutoDelete(FALSE); + g_paramsFound.clear(); + g_sectionDict = 0; //sections; + + //printf("Starting comment block at %s:%d\n",g_fileName.data(),startLine); + doctokenizerYYlineno=startLine; + doctokenizerYYinit(input,g_fileName); + + + // build abstract syntax tree + DocRoot *root = new DocRoot(md!=0,singleLine); + root->parse(); + + + if (Debug::isFlagSet(Debug::PrintTree)) + { + // pretty print the result + PrintDocVisitor *v = new PrintDocVisitor; + root->accept(v); + delete v; + } + + + checkUndocumentedParams(); + detectNoDocumentedParams(); + + // TODO: These should be called at the end of the program. + //doctokenizerYYcleanup(); + //Mappers::cmdMapper->freeInstance(); + //Mappers::htmlTagMapper->freeInstance(); + + // restore original parser state + docParserPopContext(); + + //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?ctx->name().data():"", + // md?md->name().data():""); + + return root; +} + +DocNode *validatingParseText(const char *input) +{ + // store parser state so we can re-enter this function if needed + docParserPushContext(); + + //printf("------------ input ---------\n%s\n" + // "------------ end input -----\n",input); + //g_token = new TokenInfo; + g_context = ""; + g_fileName = ""; + g_relPath = ""; + g_memberDef = 0; + g_nodeStack.clear(); + g_styleStack.clear(); + g_initialStyleStack.clear(); + g_inSeeBlock = FALSE; + g_insideHtmlLink = FALSE; + g_includeFileText = ""; + g_includeFileOffset = 0; + g_includeFileLength = 0; + g_isExample = FALSE; + g_exampleName = ""; + g_hasParamCommand = FALSE; + g_hasReturnCommand = FALSE; + g_paramsFound.setAutoDelete(FALSE); + g_paramsFound.clear(); + g_searchUrl=""; + + DocText *txt = new DocText; + + if (input) + { + doctokenizerYYlineno=1; + doctokenizerYYinit(input,g_fileName); + + // build abstract syntax tree + txt->parse(); + + if (Debug::isFlagSet(Debug::PrintTree)) + { + // pretty print the result + PrintDocVisitor *v = new PrintDocVisitor; + txt->accept(v); + delete v; + } + } + + // restore original parser state + docParserPopContext(); + return txt; +} + +void docFindSections(const char *input, + Definition *d, + MemberGroup *mg, + const char *fileName) +{ + doctokenizerYYFindSections(input,d,mg,fileName); +} + +void initDocParser() +{ + static bool searchEngine = Config_getBool("SEARCHENGINE"); + static bool serverBasedSearch = Config_getBool("SERVER_BASED_SEARCH"); + if (searchEngine && serverBasedSearch) + { + Doxygen::searchIndex = new SearchIndex; + } + else // no search engine or pure javascript based search function + { + Doxygen::searchIndex = 0; + } +} + +void finializeDocParser() +{ + delete Doxygen::searchIndex; +} +