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