Doxygen
Loading...
Searching...
No Matches
docparser.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2022 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <cassert>
19
20#include <ctype.h>
21
22#include "classlist.h"
23#include "cmdmapper.h"
24#include "config.h"
25#include "debug.h"
26#include "dir.h"
27#include "docparser.h"
28#include "docparser_p.h"
29#include "doxygen.h"
30#include "filedef.h"
31#include "fileinfo.h"
32#include "groupdef.h"
33#include "namespacedef.h"
34#include "message.h"
35#include "pagedef.h"
36#include "portable.h"
37#include "printdocvisitor.h"
38#include "util.h"
39#include "indexlist.h"
40#include "trace.h"
41#include "stringutil.h"
42
43#if !ENABLE_DOCPARSER_TRACING
44#undef AUTO_TRACE
45#undef AUTO_TRACE_ADD
46#undef AUTO_TRACE_EXIT
47#define AUTO_TRACE(...) (void)0
48#define AUTO_TRACE_ADD(...) (void)0
49#define AUTO_TRACE_EXIT(...) (void)0
50#endif
51
52
53//---------------------------------------------------------------------------
54
56{
57 return std::make_unique<DocParser>();
58}
59
61{
62 //QCString indent;
63 //indent.fill(' ',contextStack.size()*2+2);
64 //printf("%sdocParserPushContext() count=%zu\n",qPrint(indent),context.nodeStack.size());
65
66 tokenizer.pushContext();
67 contextStack.emplace();
68 auto &ctx = contextStack.top();
69 ctx = context;
70 ctx.fileName = tokenizer.getFileName();
71 ctx.lineNo = tokenizer.getLineNr();
72 context.token = tokenizer.token();
73}
74
76{
77 auto &ctx = contextStack.top();
78 context = ctx;
79 tokenizer.setFileName(ctx.fileName);
80 tokenizer.setLineNr(ctx.lineNo);
81 contextStack.pop();
82 tokenizer.popContext();
83 context.token = tokenizer.token();
84
85 //QCString indent;
86 //indent.fill(' ',contextStack.size()*2+2);
87 //printf("%sdocParserPopContext() count=%zu\n",qPrint(indent),context.nodeStack.size());
88}
89
90//---------------------------------------------------------------------------
91
92/*! search for an image in the imageNameDict and if found
93 * copies the image to the output directory (which depends on the \a type
94 * parameter).
95 */
97{
98 QCString result;
99 bool ambig = false;
100 FileDef *fd = findFileDef(Doxygen::imageNameLinkedMap,fileName,ambig);
101 //printf("Search for %s\n",fileName);
102 if (fd)
103 {
104 if (ambig & doWarn)
105 {
106 warn_doc_error(context.fileName,tokenizer.getLineNr(),
107 "image file name '{}' is ambiguous.\n"
108 "Possible candidates:\n{}", fileName,showFileDefMatches(Doxygen::imageNameLinkedMap,fileName));
109 }
110
111 QCString inputFile = fd->absFilePath();
112 FileInfo infi(inputFile.str());
113 if (infi.exists())
114 {
115 result = fileName;
116 int i = result.findRev('/');
117 if (i!=-1 || (i=result.findRev('\\'))!=-1)
118 {
119 result = result.right(static_cast<int>(result.length())-i-1);
120 }
121 //printf("fileName=%s result=%s\n",fileName,qPrint(result));
122 QCString outputDir;
123 switch(type)
124 {
125 case DocImage::Html:
126 if (!Config_getBool(GENERATE_HTML)) return result;
127 outputDir = Config_getString(HTML_OUTPUT);
128 break;
129 case DocImage::Latex:
130 if (!Config_getBool(GENERATE_LATEX)) return result;
131 outputDir = Config_getString(LATEX_OUTPUT);
132 break;
134 if (!Config_getBool(GENERATE_DOCBOOK)) return result;
135 outputDir = Config_getString(DOCBOOK_OUTPUT);
136 break;
137 case DocImage::Rtf:
138 if (!Config_getBool(GENERATE_RTF)) return result;
139 outputDir = Config_getString(RTF_OUTPUT);
140 break;
141 case DocImage::Xml:
142 if (!Config_getBool(GENERATE_XML)) return result;
143 outputDir = Config_getString(XML_OUTPUT);
144 break;
145 }
146 QCString outputFile = outputDir+"/"+result;
147 FileInfo outfi(outputFile.str());
148 if (outfi.isSymLink())
149 {
150 Dir().remove(outputFile.str());
151 warn_doc_error(context.fileName,tokenizer.getLineNr(),
152 "destination of image {} is a symlink, replacing with image",
153 outputFile);
154 }
155 if (outputFile!=inputFile) // prevent copying to ourself
156 {
157 if (copyFile(inputFile,outputFile) && type==DocImage::Html)
158 {
159 Doxygen::indexList->addImageFile(result);
160 }
161 }
162 }
163 else
164 {
165 warn_doc_error(context.fileName,tokenizer.getLineNr(),
166 "could not open image {}",fileName);
167 }
168
169 if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) &&
170 fd->name().endsWith(".eps")
171 )
172 { // we have an .eps image in pdflatex mode => convert it to a pdf.
173 QCString outputDir = Config_getString(LATEX_OUTPUT);
174 QCString baseName = fd->name().left(fd->name().length()-4);
175 QCString epstopdfArgs(4096, QCString::ExplicitSize);
176 epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"",
177 qPrint(outputDir), qPrint(baseName),
178 qPrint(outputDir), qPrint(baseName));
179 if (Portable::system("epstopdf",epstopdfArgs)!=0)
180 {
181 err("Problems running epstopdf. Check your TeX installation!\n");
182 }
183 else
184 {
185 Dir().remove(outputDir.str()+"/"+baseName.str()+".eps");
186 }
187 return baseName;
188 }
189 }
190 else
191 {
192 result=fileName;
193 if (!result.startsWith("http:") && !result.startsWith("https:") && doWarn)
194 {
195 warn_doc_error(context.fileName,tokenizer.getLineNr(),
196 "image file {} is not found in IMAGE_PATH: "
197 "assuming external image.",fileName
198 );
199 }
200 }
201 return result;
202}
203
204/*! Collects the parameters found with \@param command
205 * in a list context.paramsFound. If
206 * the parameter is not an actual parameter of the current
207 * member context.memberDef, then a warning is raised (unless warnings
208 * are disabled altogether).
209 */
211{
212 if (!(Config_getBool(WARN_IF_DOC_ERROR) || Config_getBool(WARN_IF_INCOMPLETE_DOC))) return;
213 if (context.memberDef==nullptr) return; // not a member
214 std::string name = context.token->name.str();
215 const ArgumentList &al=context.memberDef->isDocsForDefinition() ?
216 context.memberDef->argumentList() :
217 context.memberDef->declArgumentList();
218 SrcLangExt lang = context.memberDef->getLanguage();
219 //printf("isDocsForDefinition()=%d\n",context.memberDef->isDocsForDefinition());
220 if (al.empty()) return; // no argument list
221
222 static const reg::Ex re(R"(\$?\w+\.*)");
223 static const reg::Ex re_digits(R"(\d+)");
224 reg::Iterator it(name,re);
226 for (; it!=end ; ++it)
227 {
228 const auto &match = *it;
229 QCString aName=match.str();
230 int number = reg::match(aName.view(),re_digits) ? std::atoi(aName.data()) : 0;
231 if (lang==SrcLangExt::Fortran) aName=aName.lower();
232 //printf("aName='%s'\n",qPrint(aName));
233 bool found=FALSE;
234 for (const Argument &a : al)
235 {
236 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
237 if (lang==SrcLangExt::Fortran) argName=argName.lower();
238 argName=argName.stripWhiteSpace();
239 //printf("argName='%s' aName=%s\n",qPrint(argName),qPrint(aName));
240 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
241 bool sameName = aName==argName;
242 bool samePosition = context.paramPosition==number;
243 if (samePosition || sameName) // @param <number> or @param name
244 {
245 if (!sameName) // replace positional argument with real name or -
246 {
247 context.token->name = argName.isEmpty() ? "-" : argName;
248 }
249 context.paramsFound.insert(aName.str());
250 found=true;
251 break;
252 }
253 else if (aName==".") // replace . by - in the output
254 {
255 context.token->name = "-";
256 }
257 context.paramPosition++;
258 }
259 if (!found)
260 {
261 //printf("member type=%d\n",context.memberDef->memberType());
262 QCString scope=context.memberDef->getScopeString();
263 if (!scope.isEmpty()) scope+="::"; else scope="";
264 QCString inheritedFrom = "";
265 QCString docFile = context.memberDef->docFile();
266 int docLine = context.memberDef->docLine();
267 const MemberDef *inheritedMd = context.memberDef->inheritsDocsFrom();
268 if (inheritedMd) // documentation was inherited
269 {
270 inheritedFrom.sprintf(" inherited from member %s at line "
271 "%d in file %s",qPrint(inheritedMd->name()),
272 inheritedMd->docLine(),qPrint(inheritedMd->docFile()));
273 docFile = context.memberDef->getDefFileName();
274 docLine = context.memberDef->getDefLine();
275 }
276 QCString alStr = argListToString(al);
277 if (number>0)
278 {
279 warn_doc_error(docFile,docLine,
280 "positional argument '{}' of command @param "
281 "is outside of range 1..{} for {}{}{}{}",
282 number, al.size(), scope, context.memberDef->name(),
283 alStr, inheritedFrom);
284 context.token->name = "-";
285 }
286 else
287 {
288 warn_doc_error(docFile,docLine,
289 "argument '{}' of command @param "
290 "is not found in the argument list of {}{}{}{}",
291 aName, scope, context.memberDef->name(),
292 alStr, inheritedFrom);
293 }
294 }
295 }
296}
297/*! Collects the return values found with \@retval command
298 * in a global list g_parserContext.retvalsFound.
299 */
301{
302 QCString name = context.token->name;
303 if (!Config_getBool(WARN_IF_DOC_ERROR)) return;
304 if (context.memberDef==nullptr || name.isEmpty()) return; // not a member or no valid name
305 if (context.retvalsFound.count(name.str())==1) // only report the first double entry
306 {
307 warn_doc_error(context.memberDef->getDefFileName(),
308 context.memberDef->getDefLine(),
309 "return value '{}' of {} has multiple documentation sections",
310 name, context.memberDef->qualifiedName());
311 }
312 context.retvalsFound.insert(name.str());
313}
314
315/*! Checks if the parameters that have been specified using \@param are
316 * indeed all parameters and that a parameter does not have multiple
317 * \@param blocks.
318 * Must be called after checkArgumentName() has been called for each
319 * argument.
320 */
322{
323 if (context.memberDef && context.hasParamCommand)
324 {
325 const ArgumentList &al=context.memberDef->isDocsForDefinition() ?
326 context.memberDef->argumentList() :
327 context.memberDef->declArgumentList();
328 SrcLangExt lang = context.memberDef->getLanguage();
329 if (!al.empty())
330 {
331 ArgumentList undocParams;
332 int position = 1;
333 for (const Argument &a: al)
334 {
335 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
336 if (lang==SrcLangExt::Fortran) argName = argName.lower();
337 argName=argName.stripWhiteSpace();
338 QCString aName = argName;
339 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
340 if (lang==SrcLangExt::Python && (argName=="self" || argName=="cls"))
341 {
342 // allow undocumented self / cls parameter for Python
343 }
344 else if (lang==SrcLangExt::Cpp && (a.type=="this" || a.type.startsWith("this ")))
345 {
346 // allow undocumented this (for C++23 deducing this), see issue #11123
347 }
348 else if (!argName.isEmpty())
349 {
350 size_t count = context.paramsFound.count(argName.str());
351 if (count==0)
352 {
353 count = context.paramsFound.count(std::to_string(position));
354 }
355 if (count==0 && a.docs.isEmpty())
356 {
357 undocParams.push_back(a);
358 }
359 else if (count>1 && Config_getBool(WARN_IF_DOC_ERROR))
360 {
361 warn_doc_error(context.memberDef->docFile(),
362 context.memberDef->docLine(),
363 "argument {} from the argument list of {} has multiple @param documentation sections",
364 aName, context.memberDef->qualifiedName());
365 }
366 }
367 position++;
368 }
369 if (!undocParams.empty() && Config_getBool(WARN_IF_INCOMPLETE_DOC))
370 {
371 bool first=TRUE;
372 QCString errMsg = "The following parameter";
373 if (undocParams.size()>1) errMsg+="s";
374 errMsg+=QCString(" of ")+
375 context.memberDef->qualifiedName() +
376 argListToString(al) +
377 (undocParams.size()>1 ? " are" : " is") + " not documented:\n";
378 for (const Argument &a : undocParams)
379 {
380 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
381 if (lang==SrcLangExt::Fortran) argName = argName.lower();
382 argName=argName.stripWhiteSpace();
383 if (!first) errMsg+="\n";
384 first=FALSE;
385 errMsg+=" parameter '"+argName+"'";
386 }
387 warn_incomplete_doc(context.memberDef->docFile(), context.memberDef->docLine(), "{}", errMsg);
388 }
389 }
390 else
391 {
392 if (context.paramsFound.empty() && Config_getBool(WARN_IF_DOC_ERROR))
393 {
394 warn_doc_error(context.memberDef->docFile(),
395 context.memberDef->docLine(),
396 "{} has @param documentation sections but no arguments",
397 context.memberDef->qualifiedName());
398 }
399 }
400 }
401}
402
403
404//---------------------------------------------------------------------------
405
406//---------------------------------------------------------------------------
407
408//---------------------------------------------------------------------------
409/*! Looks for a documentation block with name commandName in the current
410 * context (g_parserContext.context). The resulting documentation string is
411 * put in pDoc, the definition in which the documentation was found is
412 * put in pDef.
413 * @retval TRUE if name was found.
414 * @retval FALSE if name was not found.
415 */
417 QCString *pDoc,
418 QCString *pBrief,
419 const Definition **pDef)
420{
421 AUTO_TRACE("commandName={}",commandName);
422 *pDoc="";
423 *pBrief="";
424 *pDef=nullptr;
425 QCString cmdArg=commandName;
426 if (cmdArg.isEmpty())
427 {
428 AUTO_TRACE_EXIT("empty");
429 return false;
430 }
431
432 const FileDef *fd=nullptr;
433 const GroupDef *gd=nullptr;
434 const PageDef *pd=nullptr;
435 gd = Doxygen::groupLinkedMap->find(cmdArg);
436 if (gd) // group
437 {
438 *pDoc=gd->documentation();
439 *pBrief=gd->briefDescription();
440 *pDef=gd;
441 AUTO_TRACE_EXIT("group");
442 return true;
443 }
444 pd = Doxygen::pageLinkedMap->find(cmdArg);
445 if (pd) // page
446 {
447 *pDoc=pd->documentation();
448 *pBrief=pd->briefDescription();
449 *pDef=pd;
450 AUTO_TRACE_EXIT("page");
451 return true;
452 }
453 bool ambig = false;
454 fd = findFileDef(Doxygen::inputNameLinkedMap,cmdArg,ambig);
455 if (fd && !ambig) // file
456 {
457 *pDoc=fd->documentation();
458 *pBrief=fd->briefDescription();
459 *pDef=fd;
460 AUTO_TRACE_EXIT("file");
461 return true;
462 }
463
464 // for symbols we need to normalize the separator, so A#B, or A\B, or A.B becomes A::B
465 cmdArg = substitute(cmdArg,"#","::");
466 cmdArg = substitute(cmdArg,"\\","::");
467 bool extractAnonNs = Config_getBool(EXTRACT_ANON_NSPACES);
468 if (extractAnonNs &&
469 cmdArg.startsWith("anonymous_namespace{")
470 )
471 {
472 size_t rightBracePos = cmdArg.find("}", static_cast<int>(qstrlen("anonymous_namespace{")));
473 QCString leftPart = cmdArg.left(rightBracePos + 1);
474 QCString rightPart = cmdArg.right(cmdArg.size() - rightBracePos - 1);
475 rightPart = substitute(rightPart, ".", "::");
476 cmdArg = leftPart + rightPart;
477 }
478 else
479 {
480 cmdArg = substitute(cmdArg,".","::");
481 }
482
483 int l=static_cast<int>(cmdArg.length());
484
485 int funcStart=cmdArg.find('(');
486 if (funcStart==-1)
487 {
488 funcStart=l;
489 }
490 else
491 {
492 // Check for the case of operator() and the like.
493 // beware of scenarios like operator()((foo)bar)
494 int secondParen = cmdArg.find('(', funcStart+1);
495 int leftParen = cmdArg.find(')', funcStart+1);
496 if (leftParen!=-1 && secondParen!=-1)
497 {
498 if (leftParen<secondParen)
499 {
500 funcStart=secondParen;
501 }
502 }
503 }
504
505 QCString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
506 QCString args=cmdArg.right(l-funcStart);
507 // try if the link is to a member
508 GetDefInput input(
509 context.context.find('.')==-1 ? context.context : QCString(), // find('.') is a hack to detect files
510 name,
511 args);
512 input.checkCV=true;
513 GetDefResult result = getDefs(input);
514 //printf("found=%d context=%s name=%s\n",result.found,qPrint(context.context),qPrint(name));
515 if (result.found && result.md)
516 {
517 *pDoc=result.md->documentation();
518 *pBrief=result.md->briefDescription();
519 *pDef=result.md;
520 AUTO_TRACE_EXIT("member");
521 return true;
522 }
523
524 int scopeOffset=static_cast<int>(context.context.length());
525 do // for each scope
526 {
527 QCString fullName=cmdArg;
528 if (scopeOffset>0)
529 {
530 fullName.prepend(context.context.left(scopeOffset)+"::");
531 }
532 //printf("Trying fullName='%s'\n",qPrint(fullName));
533
534 // try class, namespace, group, page, file reference
535 const ClassDef *cd = Doxygen::classLinkedMap->find(fullName);
536 if (cd) // class
537 {
538 *pDoc=cd->documentation();
539 *pBrief=cd->briefDescription();
540 *pDef=cd;
541 AUTO_TRACE_EXIT("class");
542 return true;
543 }
544 const NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(fullName);
545 if (nd) // namespace
546 {
547 *pDoc=nd->documentation();
548 *pBrief=nd->briefDescription();
549 *pDef=nd;
550 AUTO_TRACE_EXIT("namespace");
551 return true;
552 }
553 if (scopeOffset==0)
554 {
555 scopeOffset=-1;
556 }
557 else
558 {
559 scopeOffset = context.context.findRev("::",scopeOffset-1);
560 if (scopeOffset==-1) scopeOffset=0;
561 }
562 } while (scopeOffset>=0);
563
564 AUTO_TRACE_EXIT("not found");
565 return FALSE;
566}
567
568//---------------------------------------------------------------------------
570 DocNodeList &children,const QCString &txt)
571{
572 switch (tok.value())
573 {
574 case TokenRetval::TK_COMMAND_AT:
575 // fall through
576 case TokenRetval::TK_COMMAND_BS:
577 {
578 char cs[2] = { tok.command_to_char(), 0 };
579 children.append<DocWord>(this,parent,cs + context.token->name);
580 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a {}",
581 tok.command_to_char(),context.token->name,txt);
582 }
583 break;
584 case TokenRetval::TK_SYMBOL:
585 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a {}",
586 qPrint(context.token->name), qPrint(txt));
587 break;
588 case TokenRetval::TK_HTMLTAG:
589 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <{}{}> found as part of a {}",
590 context.token->endTag ? "/" : "",context.token->name, txt);
591 break;
592 default:
593 children.append<DocWord>(this,parent,context.token->name);
594 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token {} found as part of a {}",
595 tok.to_string(), txt);
596 break;
597 }
598}
599
600//---------------------------------------------------------------------------
601
603{
604 AUTO_TRACE("cmdName={}",cmdName);
605 QCString saveCmdName = cmdName;
606 Token tok=tokenizer.lex();
607 if (!tok.is(TokenRetval::TK_WHITESPACE))
608 {
609 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
610 saveCmdName);
611 return tok;
612 }
613 tok = tokenizer.lex();
614 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::TK_WHITESPACE,
615 TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST)
616 )
617 {
618 static const reg::Ex specialChar(R"([.,|()\‍[\‍]:;?])");
619 if (tok.is(TokenRetval::TK_WORD) && context.token->name.length()==1 &&
620 reg::match(context.token->name.str(),specialChar))
621 {
622 // special character that ends the markup command
623 return tok;
624 }
625 if (!defaultHandleToken(parent,tok,children))
626 {
627 switch (tok.value())
628 {
629 case TokenRetval::TK_HTMLTAG:
630 if (insideLI(parent) && Mappers::htmlTagMapper->map(context.token->name)!=HtmlTagType::UNKNOWN && context.token->endTag)
631 {
632 // ignore </li> as the end of a style command
633 }
634 else
635 {
636 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
637 return tok;
638 }
639 break;
640 default:
641 errorHandleDefaultToken(parent,tok,children,"\\" + saveCmdName + " command");
642 break;
643 }
644 break;
645 }
646 tok = tokenizer.lex();
647 }
648 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
649 return (tok.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST)) ? tok : Token::make_RetVal_OK();
650}
651
652/*! Called when a style change starts. For instance a <b> command is
653 * encountered.
654 */
656 DocStyleChange::Style s,const QCString &tagName,const HtmlAttribList *attribs)
657{
658 AUTO_TRACE("tagName={}",tagName);
659 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),s,tagName,TRUE,
660 context.fileName,tokenizer.getLineNr(),attribs);
661 context.styleStack.push(&children.back());
663}
664
665/*! Called when a style change ends. For instance a </b> command is
666 * encountered.
667 */
669 DocStyleChange::Style s,const QCString &tagName)
670{
671 AUTO_TRACE("tagName={}",tagName);
672 QCString tagNameLower = QCString(tagName).lower();
673
674 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
675 {
676 return std::get<DocStyleChange>(*stack.top());
677 };
678
679 if (context.styleStack.empty() || // no style change
680 topStyleChange(context.styleStack).style()!=s || // wrong style change
681 topStyleChange(context.styleStack).tagName()!=tagNameLower || // wrong style change
682 topStyleChange(context.styleStack).position()!=context.nodeStack.size() // wrong position
683 )
684 {
685 if (context.styleStack.empty())
686 {
687 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{0}> tag without matching <{0}>",tagName);
688 }
689 else if (topStyleChange(context.styleStack).tagName()!=tagNameLower ||
690 topStyleChange(context.styleStack).style()!=s)
691 {
692 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> tag while expecting </{}>",
693 tagName,topStyleChange(context.styleStack).tagName());
694 }
695 else
696 {
697 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> at different nesting level ({}) than expected ({})",
698 tagName,context.nodeStack.size(),topStyleChange(context.styleStack).position());
699 }
700 }
701 else // end the section
702 {
703 children.append<DocStyleChange>(
704 this,parent,context.nodeStack.size(),s,
705 topStyleChange(context.styleStack).tagName(),FALSE);
706 context.styleStack.pop();
707 }
709 {
710 context.inCodeStyle = false;
711 }
712}
713
714/*! Called at the end of a paragraph to close all open style changes
715 * (e.g. a <b> without a </b>). The closed styles are pushed onto a stack
716 * and entered again at the start of a new paragraph.
717 */
719{
720 AUTO_TRACE();
721 if (!context.styleStack.empty())
722 {
723 const DocStyleChange *sc = &std::get<DocStyleChange>(*context.styleStack.top());
724 while (sc && sc->position()>=context.nodeStack.size())
725 { // there are unclosed style modifiers in the paragraph
726 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),
727 sc->style(),sc->tagName(),FALSE);
728 context.initialStyleStack.push(context.styleStack.top());
729 context.styleStack.pop();
730 sc = !context.styleStack.empty() ? &std::get<DocStyleChange>(*context.styleStack.top()) : nullptr;
731 }
732 }
733}
734
736{
737 AUTO_TRACE();
738 while (!context.initialStyleStack.empty())
739 {
740 const DocStyleChange &sc = std::get<DocStyleChange>(*context.initialStyleStack.top());
741 handleStyleEnter(parent,children,sc.style(),sc.tagName(),&sc.attribs());
742 context.initialStyleStack.pop();
743 }
744}
745
747 const HtmlAttribList &tagHtmlAttribs)
748{
749 AUTO_TRACE();
750 size_t index=0;
751 Token retval = Token::make_RetVal_OK();
752 for (const auto &opt : tagHtmlAttribs)
753 {
754 if (opt.name=="name" || opt.name=="id") // <a name=label> or <a id=label> tag
755 {
756 if (!opt.value.isEmpty())
757 {
758 children.append<DocAnchor>(this,parent,opt.value,TRUE);
759 break; // stop looking for other tag attribs
760 }
761 else
762 {
763 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <a> tag with name option but without value!");
764 }
765 }
766 else if (opt.name=="href") // <a href=url>..</a> tag
767 {
768 // copy attributes
769 HtmlAttribList attrList = tagHtmlAttribs;
770 // and remove the href attribute
771 attrList.erase(attrList.begin()+index);
772 QCString relPath;
773 if (opt.value.at(0) != '#') relPath = context.relPath;
774 children.append<DocHRef>(this, parent, attrList,
775 opt.value, relPath,
776 convertNameToFile(context.fileName, FALSE, TRUE));
777 context.insideHtmlLink=TRUE;
778 retval = children.get_last<DocHRef>()->parse();
779 context.insideHtmlLink=FALSE;
780 tokenizer.setStatePara();
781 break;
782 }
783 else // unsupported option for tag a
784 {
785 }
786 ++index;
787 }
788 return retval;
789}
790
792{
793 AUTO_TRACE();
794 if (!context.initialStyleStack.empty())
795 {
796 QCString tagName = std::get<DocStyleChange>(*context.initialStyleStack.top()).tagName();
797 QCString fileName = std::get<DocStyleChange>(*context.initialStyleStack.top()).fileName();
798 int lineNr = std::get<DocStyleChange>(*context.initialStyleStack.top()).lineNr();
799 context.initialStyleStack.pop();
801 if (lineNr != -1)
802 {
803 warn_doc_error(context.fileName,tokenizer.getLineNr(),
804 "end of comment block while expecting "
805 "command </{}> (Probable start '{}' at line {})",tagName, fileName, lineNr);
806 }
807 else
808 {
809 warn_doc_error(context.fileName,tokenizer.getLineNr(),
810 "end of comment block while expecting command </{}>",tagName);
811 }
812 }
813}
814
815void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag)
816{
817 // helper to check if word w starts with any of the words in AUTOLINK_IGNORE_WORDS
818 auto ignoreWord = [](const QCString &w) -> bool {
819 const auto &list = Config_getList(AUTOLINK_IGNORE_WORDS);
820 return std::find_if(list.begin(), list.end(),
821 [&w](const auto &ignore) { return w.startsWith(ignore); }
822 )!=list.end();
823 };
824 QCString name = linkToText(context.lang,context.token->name,TRUE);
825 AUTO_TRACE("word={}",name);
826 if (!context.autolinkSupport || ignoreAutoLinkFlag || ignoreWord(context.token->name)) // no autolinking -> add as normal word
827 {
828 children.append<DocWord>(this,parent,name);
829 return;
830 }
831
832 // ------- try to turn the word 'name' into a link
833
834 const Definition *compound=nullptr;
835 const MemberDef *member=nullptr;
836 size_t len = context.token->name.length();
837 ClassDef *cd=nullptr;
838 bool ambig = false;
840 auto lang = context.lang;
841 bool inSeeBlock = context.inSeeBlock || context.inCodeStyle;
842 //printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context));
843 if (!context.insideHtmlLink &&
844 (resolveRef(context.context,context.token->name,inSeeBlock,&compound,&member,lang,TRUE,fd,TRUE)
845 || (!context.context.isEmpty() && // also try with global scope
846 resolveRef(QCString(),context.token->name,inSeeBlock,&compound,&member,lang,FALSE,nullptr,TRUE))
847 )
848 )
849 {
850 //printf("ADD %s = %p (linkable?=%d)\n",qPrint(context.token->name),(void*)member,member ? member->isLinkable() : FALSE);
851 if (member && member->isLinkable()) // member link
852 {
853 AUTO_TRACE_ADD("resolved reference as member link");
854 if (member->isObjCMethod())
855 {
856 bool localLink = context.memberDef ? member->getClassDef()==context.memberDef->getClassDef() : FALSE;
857 name = member->objCMethodName(localLink,inSeeBlock);
858 }
859 children.append<DocLinkedWord>(
860 this,parent,name,
861 member->getReference(),
862 member->getOutputFileBase(),
863 member->anchor(),
864 member->briefDescriptionAsTooltip());
865 }
866 else if (compound->isLinkable()) // compound link
867 {
868 AUTO_TRACE_ADD("resolved reference as compound link");
869 QCString anchor = compound->anchor();
870 if (compound->definitionType()==Definition::TypeFile)
871 {
872 name=context.token->name;
873 }
874 else if (compound->definitionType()==Definition::TypeGroup)
875 {
876 name=toGroupDef(compound)->groupTitle();
877 }
878 children.append<DocLinkedWord>(
879 this,parent,name,
880 compound->getReference(),
881 compound->getOutputFileBase(),
882 anchor,
883 compound->briefDescriptionAsTooltip());
884 }
885 else if (compound->definitionType()==Definition::TypeFile &&
886 (toFileDef(compound))->generateSourceFile()
887 ) // undocumented file that has source code we can link to
888 {
889 AUTO_TRACE_ADD("resolved reference as source link");
890 children.append<DocLinkedWord>(
891 this,parent,context.token->name,
892 compound->getReference(),
893 compound->getSourceFileBase(),
894 "",
895 compound->briefDescriptionAsTooltip());
896 }
897 else // not linkable
898 {
899 AUTO_TRACE_ADD("resolved reference as unlinkable compound={} (linkable={}) member={} (linkable={})",
900 compound ? compound->name() : "<none>", compound ? (int)compound->isLinkable() : -1,
901 member ? member->name() : "<none>", member ? (int)member->isLinkable() : -1);
902 children.append<DocWord>(this,parent,name);
903 }
904 }
905 else if (!context.insideHtmlLink && len>1 && context.token->name.at(len-1)==':')
906 {
907 // special case, where matching Foo: fails to be an Obj-C reference,
908 // but Foo itself might be linkable.
909 context.token->name=context.token->name.left(len-1);
910 handleLinkedWord(parent,children,ignoreAutoLinkFlag);
911 children.append<DocWord>(this,parent,":");
912 }
913 else if (!context.insideHtmlLink && (cd=getClass(context.token->name+"-p")))
914 {
915 // special case 2, where the token name is not a class, but could
916 // be a Obj-C protocol
917 children.append<DocLinkedWord>(
918 this,parent,name,
919 cd->getReference(),
920 cd->getOutputFileBase(),
921 cd->anchor(),
923 }
924 else if (const RequirementIntf *req = RequirementManager::instance().find(name); req!=nullptr) // link to requirement
925 {
926 if (Config_getBool(GENERATE_REQUIREMENTS))
927 {
928 children.append<DocLinkedWord>(
929 this,parent,name,
930 QCString(), // link to local requirements overview also for external references
931 req->getOutputFileBase(),
932 req->id(),
933 req->title()
934 );
935 }
936 else // cannot link to a page that does not exist
937 {
938 children.append<DocWord>(this,parent,name);
939 }
940 }
941 else // normal non-linkable word
942 {
943 AUTO_TRACE_ADD("non-linkable");
944 if (context.token->name.startsWith("#"))
945 {
946 warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '{}' could not be resolved",name);
947 }
948 children.append<DocWord>(this,parent,context.token->name);
949 }
950}
951
953{
954 QCString name = context.token->name; // save token name
955 AUTO_TRACE("name={}",name);
956 QCString name1;
957 int p=0, i=0, ii=0;
958 while ((i=paramTypes.find('|',p))!=-1)
959 {
960 name1 = paramTypes.mid(p,i-p);
961 ii=name1.find('[');
962 context.token->name=ii!=-1 ? name1.mid(0,ii) : name1; // take part without []
963 handleLinkedWord(parent,children);
964 if (ii!=-1) children.append<DocWord>(this,parent,name1.mid(ii)); // add [] part
965 p=i+1;
966 children.append<DocSeparator>(this,parent,"|");
967 }
968 name1 = paramTypes.mid(p);
969 ii=name1.find('[');
970 context.token->name=ii!=-1 ? name1.mid(0,ii) : name1;
971 handleLinkedWord(parent,children);
972 if (ii!=-1) children.append<DocWord>(this,parent,name1.mid(ii));
973 context.token->name = name; // restore original token name
974}
975
977{
978 Token tok=tokenizer.lex();
979 QCString tokenName = context.token->name;
980 AUTO_TRACE("name={}",tokenName);
981 if (!tok.is(TokenRetval::TK_WHITESPACE))
982 {
983 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", tokenName);
984 return;
985 }
986 tokenizer.setStateInternalRef();
987 tok=tokenizer.lex(); // get the reference id
988 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
989 {
990 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
991 tok.to_string(),tokenName);
992 return;
993 }
994 children.append<DocInternalRef>(this,parent,context.token->name);
995 children.get_last<DocInternalRef>()->parse();
996}
997
999{
1000 AUTO_TRACE();
1001 Token tok=tokenizer.lex();
1002 if (!tok.is(TokenRetval::TK_WHITESPACE))
1003 {
1004 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1005 context.token->name);
1006 return;
1007 }
1008 tokenizer.setStateAnchor();
1009 tok=tokenizer.lex();
1010 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1011 {
1012 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1013 "argument of command {}",context.token->name);
1014 return;
1015 }
1016 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1017 {
1018 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1019 tok.to_string(),context.token->name);
1020 return;
1021 }
1022 tokenizer.setStatePara();
1023 children.append<DocAnchor>(this,parent,context.token->name,FALSE);
1024}
1025
1027{
1028 AUTO_TRACE();
1029 Token tok=tokenizer.lex();
1030 if (!tok.is(TokenRetval::TK_WHITESPACE))
1031 {
1032 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", context.token->name);
1033 return;
1034 }
1035 tokenizer.setStatePrefix();
1036 tok=tokenizer.lex();
1037 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1038 {
1039 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1040 "argument of command {}",context.token->name);
1041 return;
1042 }
1043 else if (!tok.is(TokenRetval::TK_WORD))
1044 {
1045 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1046 tok.to_string(),context.token->name);
1047 return;
1048 }
1049 context.prefix = context.token->name;
1050 tokenizer.setStatePara();
1051}
1052
1053/* Helper function that deals with the title, width, and height arguments of various commands.
1054 * @param[in] cmd Command id for which to extract caption and size info.
1055 * @param[in] parent Parent node, owner of the children list passed as
1056 * the third argument.
1057 * @param[in] children The list of child nodes to which the node representing
1058 * the token can be added.
1059 * @param[out] width the extracted width specifier
1060 * @param[out] height the extracted height specifier
1061 */
1063{
1064 AUTO_TRACE();
1065 auto ns = AutoNodeStack(this,parent);
1066
1067 // parse title
1068 tokenizer.setStateTitle();
1069 Token tok = tokenizer.lex();
1070 while (!tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1071 {
1072 if (tok.is(TokenRetval::TK_WORD) && (context.token->name=="width=" || context.token->name=="height="))
1073 {
1074 // special case: no title, but we do have a size indicator
1075 break;
1076 }
1077 else if (tok.is(TokenRetval::TK_HTMLTAG))
1078 {
1079 tokenizer.unputString(context.token->text);
1080 break;
1081 }
1082 if (!defaultHandleToken(parent,tok,children))
1083 {
1084 errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd));
1085 }
1086 tok = tokenizer.lex();
1087 }
1088 // parse size attributes
1089 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1090 {
1091 tok=tokenizer.lex();
1092 }
1093 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) // there are values following the title
1094 {
1095 if (tok.is(TokenRetval::TK_WORD))
1096 {
1097 if (context.token->name=="width=" || context.token->name=="height=")
1098 {
1099 tokenizer.setStateTitleAttrValue();
1100 context.token->name = context.token->name.left(context.token->name.length()-1);
1101 }
1102
1103 if (context.token->name=="width")
1104 {
1105 width = context.token->chars;
1106 }
1107 else if (context.token->name=="height")
1108 {
1109 height = context.token->chars;
1110 }
1111 else // other text after the title -> treat as normal text
1112 {
1113 tokenizer.unputString(context.token->name);
1114 //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '{}' after \\{} command, expected 'width' or 'height'",
1115 // context.token->name, Mappers::cmdMapper->find(cmd));
1116 break;
1117 }
1118 }
1119
1120 tok=tokenizer.lex();
1121 // if we found something we did not expect, push it back to the stream
1122 // so it can still be processed
1123 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
1124 {
1125 tokenizer.unputString(context.token->name);
1126 tokenizer.unputString(tok.is(TokenRetval::TK_COMMAND_AT) ? "@" : "\\");
1127 break;
1128 }
1129 else if (tok.is(TokenRetval::TK_SYMBOL))
1130 {
1131 tokenizer.unputString(context.token->name);
1132 break;
1133 }
1134 else if (tok.is(TokenRetval::TK_HTMLTAG))
1135 {
1136 tokenizer.unputString(context.token->text);
1137 break;
1138 }
1139 }
1140 tokenizer.setStatePara();
1141
1143 AUTO_TRACE_EXIT("width={} height={}",width,height);
1144}
1145
1147{
1148 AUTO_TRACE();
1149 bool inlineImage = false;
1150 QCString anchorStr;
1151
1152 Token tok=tokenizer.lex();
1153 if (!tok.is(TokenRetval::TK_WHITESPACE))
1154 {
1155 if (tok.is(TokenRetval::TK_WORD))
1156 {
1157 if (context.token->name == "{")
1158 {
1159 tokenizer.setStateOptions();
1160 tokenizer.lex();
1161 tokenizer.setStatePara();
1162 StringVector optList=split(context.token->name.str(),",");
1163 for (const auto &opt : optList)
1164 {
1165 if (opt.empty()) continue;
1166 QCString locOpt(opt);
1167 QCString locOptLow;
1168 locOpt = locOpt.stripWhiteSpace();
1169 locOptLow = locOpt.lower();
1170 if (locOptLow == "inline")
1171 {
1172 inlineImage = true;
1173 }
1174 else if (locOptLow.startsWith("anchor:"))
1175 {
1176 if (!anchorStr.isEmpty())
1177 {
1178 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1179 "multiple use of option 'anchor' for 'image' command, ignoring: '{}'",
1180 locOpt.mid(7));
1181 }
1182 else
1183 {
1184 anchorStr = locOpt.mid(7);
1185 }
1186 }
1187 else
1188 {
1189 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1190 "unknown option '{}' for 'image' command specified",
1191 locOpt);
1192 }
1193 }
1194 tok=tokenizer.lex();
1195 if (!tok.is(TokenRetval::TK_WHITESPACE))
1196 {
1197 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1198 return;
1199 }
1200 }
1201 }
1202 else
1203 {
1204 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1205 return;
1206 }
1207 }
1208 tok=tokenizer.lex();
1209 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1210 {
1211 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image",
1212 tok.to_string());
1213 return;
1214 }
1215 tok=tokenizer.lex();
1216 if (!tok.is(TokenRetval::TK_WHITESPACE))
1217 {
1218 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1219 return;
1220 }
1222 QCString imgType = context.token->name.lower();
1223 if (imgType=="html") t=DocImage::Html;
1224 else if (imgType=="latex") t=DocImage::Latex;
1225 else if (imgType=="docbook") t=DocImage::DocBook;
1226 else if (imgType=="rtf") t=DocImage::Rtf;
1227 else if (imgType=="xml") t=DocImage::Xml;
1228 else
1229 {
1230 warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `{}` specified as the first argument of "
1231 "\\image command is not valid", imgType);
1232 return;
1233 }
1234 tokenizer.setStateFile();
1235 tok=tokenizer.lex();
1236 tokenizer.setStatePara();
1237 if (!tok.is(TokenRetval::TK_WORD))
1238 {
1239 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", tok.to_string());
1240 return;
1241 }
1242 if (!anchorStr.isEmpty())
1243 {
1244 children.append<DocAnchor>(this,parent,anchorStr,true);
1245 }
1246 HtmlAttribList attrList;
1247 children.append<DocImage>(this,parent,attrList,
1248 findAndCopyImage(context.token->name,t),t,"",inlineImage);
1249 children.get_last<DocImage>()->parse();
1250}
1251
1252void DocParser::handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const QCString &cmdName)
1253{
1254 AUTO_TRACE("cmdName={}",cmdName);
1255 QCString saveCmdName = cmdName;
1256 tokenizer.pushState();
1257 Token tok=tokenizer.lex();
1258 if (!tok.is(TokenRetval::TK_WHITESPACE))
1259 {
1260 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1261 cmdChar,qPrint(saveCmdName));
1262 goto endref;
1263 }
1264 tokenizer.setStateRef();
1265 tok=tokenizer.lex(); // get the reference id
1266 if (!tok.is(TokenRetval::TK_WORD))
1267 {
1268 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1269 tok.to_string(),cmdChar,saveCmdName);
1270 goto endref;
1271 }
1272 children.append<DocRef>(this,parent,
1273 context.token->name,
1274 context.context);
1275 children.get_last<DocRef>()->parse(cmdChar,saveCmdName);
1276endref:
1277 tokenizer.popState();
1278}
1279
1280void DocParser::handleIFile(char cmdChar,const QCString &cmdName)
1281{
1282 AUTO_TRACE();
1283 Token tok=tokenizer.lex();
1284 if (!tok.is(TokenRetval::TK_WHITESPACE))
1285 {
1286 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1287 cmdChar,cmdName);
1288 return;
1289 }
1290 tokenizer.setStateFile();
1291 tok=tokenizer.lex();
1292 tokenizer.setStatePara();
1293 if (!tok.is(TokenRetval::TK_WORD))
1294 {
1295 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1296 tok.to_string(),cmdChar,cmdName);
1297 return;
1298 }
1299 context.fileName = context.token->name;
1300 tokenizer.setStatePara();
1301}
1302
1303void DocParser::handleILine(char cmdChar,const QCString &cmdName)
1304{
1305 AUTO_TRACE();
1306 tokenizer.setStateILine();
1307 Token tok = tokenizer.lex();
1308 if (!tok.is(TokenRetval::TK_WORD))
1309 {
1310 warn_doc_error(context.fileName,tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
1311 cmdChar,cmdName);
1312 return;
1313 }
1314 tokenizer.setStatePara();
1315}
1316
1317/* Helper function that deals with the most common tokens allowed in
1318 * title like sections.
1319 * @param parent Parent node, owner of the children list passed as
1320 * the third argument.
1321 * @param tok The token to process.
1322 * @param children The list of child nodes to which the node representing
1323 * the token can be added.
1324 * @param handleWord Indicates if word token should be processed
1325 * @retval TRUE The token was handled.
1326 * @retval FALSE The token was not handled.
1327 */
1329{
1330 AUTO_TRACE("token={} handleWord={}",tok.to_string(),handleWord);
1331 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
1332 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
1333 )
1334 {
1335 }
1336reparsetoken:
1337 QCString tokenName = context.token->name;
1338 AUTO_TRACE_ADD("tokenName={}",tokenName);
1339 switch (tok.value())
1340 {
1341 case TokenRetval::TK_COMMAND_AT:
1342 // fall through
1343 case TokenRetval::TK_COMMAND_BS:
1344 switch (Mappers::cmdMapper->map(tokenName))
1345 {
1348 break;
1351 break;
1354 break;
1357 break;
1360 break;
1363 break;
1366 break;
1369 break;
1372 break;
1376 break;
1381 break;
1384 break;
1387 break;
1390 break;
1393 break;
1396 break;
1399 break;
1402 break;
1404 {
1405 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,TRUE);
1406 tok=handleStyleArgument(parent,children,tokenName);
1407 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,FALSE);
1408 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1409 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1410 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1411 {
1412 AUTO_TRACE_ADD("CommandType::CMD_EMPHASIS: reparsing");
1413 goto reparsetoken;
1414 }
1415 }
1416 break;
1418 {
1419 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,TRUE);
1420 tok=handleStyleArgument(parent,children,tokenName);
1421 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,FALSE);
1422 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1423 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1424 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1425 {
1426 AUTO_TRACE_ADD("CommandType::CMD_BOLD: reparsing");
1427 goto reparsetoken;
1428 }
1429 }
1430 break;
1432 {
1433 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,TRUE);
1434 tok=handleStyleArgument(parent,children,tokenName);
1435 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,FALSE);
1436 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1437 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1438 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1439 {
1440 AUTO_TRACE_ADD("CommandType::CMD_CODE: reparsing");
1441 goto reparsetoken;
1442 }
1443 }
1444 break;
1446 {
1447 tokenizer.setStateHtmlOnly();
1448 tok = tokenizer.lex();
1449 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::HtmlOnly,context.isExample,context.exampleName,context.token->name=="block");
1450 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1451 {
1452 warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker");
1453 }
1454 tokenizer.setStatePara();
1455 }
1456 break;
1458 {
1459 tokenizer.setStateManOnly();
1460 tok = tokenizer.lex();
1461 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::ManOnly,context.isExample,context.exampleName);
1462 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1463 {
1464 warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker");
1465 }
1466 tokenizer.setStatePara();
1467 }
1468 break;
1470 {
1471 tokenizer.setStateRtfOnly();
1472 tok = tokenizer.lex();
1473 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::RtfOnly,context.isExample,context.exampleName);
1474 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1475 {
1476 warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker");
1477 }
1478 tokenizer.setStatePara();
1479 }
1480 break;
1482 {
1483 tokenizer.setStateLatexOnly();
1484 tok = tokenizer.lex();
1485 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::LatexOnly,context.isExample,context.exampleName);
1486 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1487 {
1488 warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker");
1489 }
1490 tokenizer.setStatePara();
1491 }
1492 break;
1494 {
1495 tokenizer.setStateXmlOnly();
1496 tok = tokenizer.lex();
1497 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::XmlOnly,context.isExample,context.exampleName);
1498 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1499 {
1500 warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker");
1501 }
1502 tokenizer.setStatePara();
1503 }
1504 break;
1506 {
1507 tokenizer.setStateDbOnly();
1508 tok = tokenizer.lex();
1509 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::DocbookOnly,context.isExample,context.exampleName);
1510 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1511 {
1512 warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker");
1513 }
1514 tokenizer.setStatePara();
1515 }
1516 break;
1518 {
1519 children.append<DocFormula>(this,parent,context.token->id);
1520 }
1521 break;
1524 {
1525 handleAnchor(parent,children);
1526 }
1527 break;
1529 {
1530 handlePrefix(parent,children);
1531 }
1532 break;
1534 {
1535 handleInternalRef(parent,children);
1536 tokenizer.setStatePara();
1537 }
1538 break;
1540 {
1541 tokenizer.setStateSetScope();
1542 (void)tokenizer.lex();
1543 context.context = context.token->name;
1544 //printf("Found scope='%s'\n",qPrint(context.context));
1545 tokenizer.setStatePara();
1546 }
1547 break;
1549 handleImage(parent,children);
1550 break;
1552 handleILine(tok.command_to_char(),tokenName);
1553 break;
1555 handleIFile(tok.command_to_char(),tokenName);
1556 break;
1557 default:
1558 return FALSE;
1559 }
1560 break;
1561 case TokenRetval::TK_HTMLTAG:
1562 {
1563 auto handleEnterLeaveStyle = [this,&parent,&children,&tokenName](DocStyleChange::Style style) {
1564 if (!context.token->endTag)
1565 {
1566 handleStyleEnter(parent,children,style,tokenName,&context.token->attribs);
1567 }
1568 else
1569 {
1570 handleStyleLeave(parent,children,style,tokenName);
1571 }
1572 };
1573 switch (Mappers::htmlTagMapper->map(tokenName))
1574 {
1576 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading");
1577 break;
1579 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading");
1580 break;
1582 handleEnterLeaveStyle(DocStyleChange::Span);
1583 break;
1585 handleEnterLeaveStyle(DocStyleChange::Bold);
1586 break;
1588 handleEnterLeaveStyle(DocStyleChange::S);
1589 break;
1591 handleEnterLeaveStyle(DocStyleChange::Strike);
1592 break;
1594 handleEnterLeaveStyle(DocStyleChange::Del);
1595 break;
1597 handleEnterLeaveStyle(DocStyleChange::Underline);
1598 break;
1600 handleEnterLeaveStyle(DocStyleChange::Ins);
1601 break;
1603 case HtmlTagType::XML_C:
1604 handleEnterLeaveStyle(DocStyleChange::Code);
1605 break;
1607 handleEnterLeaveStyle(DocStyleChange::Kbd);
1608 break;
1610 handleEnterLeaveStyle(DocStyleChange::Typewriter);
1611 break;
1613 handleEnterLeaveStyle(DocStyleChange::Italic);
1614 break;
1616 handleEnterLeaveStyle(DocStyleChange::Subscript);
1617 break;
1619 handleEnterLeaveStyle(DocStyleChange::Superscript);
1620 break;
1622 handleEnterLeaveStyle(DocStyleChange::Center);
1623 break;
1625 handleEnterLeaveStyle(DocStyleChange::Small);
1626 break;
1628 handleEnterLeaveStyle(DocStyleChange::Cite);
1629 break;
1631 if (!context.token->endTag)
1632 {
1633 handleImg(parent,children,context.token->attribs);
1634 }
1635 break;
1636 default:
1637 return FALSE;
1638 break;
1639 }
1640 }
1641 break;
1642 case TokenRetval::TK_SYMBOL:
1643 {
1646 {
1647 children.append<DocSymbol>(this,parent,s);
1648 }
1649 else
1650 {
1651 return FALSE;
1652 }
1653 }
1654 break;
1655 case TokenRetval::TK_WHITESPACE:
1656 case TokenRetval::TK_NEWPARA:
1657handlepara:
1658 if (insidePRE(parent) || !children.empty())
1659 {
1660 children.append<DocWhiteSpace>(this,parent,context.token->chars);
1661 }
1662 break;
1663 case TokenRetval::TK_LNKWORD:
1664 if (handleWord)
1665 {
1666 handleLinkedWord(parent,children);
1667 }
1668 else
1669 return FALSE;
1670 break;
1671 case TokenRetval::TK_WORD:
1672 if (handleWord)
1673 {
1674 children.append<DocWord>(this,parent,context.token->name);
1675 }
1676 else
1677 return FALSE;
1678 break;
1679 case TokenRetval::TK_URL:
1680 if (context.insideHtmlLink)
1681 {
1682 children.append<DocWord>(this,parent,context.token->name);
1683 }
1684 else
1685 {
1686 children.append<DocURL>(this,parent,context.token->name,context.token->isEMailAddr);
1687 }
1688 break;
1689 default:
1690 return FALSE;
1691 }
1692 return TRUE;
1693}
1694
1695//---------------------------------------------------------------------------
1696
1698{
1699 AUTO_TRACE();
1700 bool found=FALSE;
1701 size_t index=0;
1702 for (const auto &opt : tagHtmlAttribs)
1703 {
1704 AUTO_TRACE_ADD("option name={} value='{}'",opt.name,opt.value);
1705 if (opt.name=="src" && !opt.value.isEmpty())
1706 {
1707 // copy attributes
1708 HtmlAttribList attrList = tagHtmlAttribs;
1709 // and remove the src attribute
1710 attrList.erase(attrList.begin()+index);
1712 children.append<DocImage>(
1713 this,parent,attrList,
1714 findAndCopyImage(opt.value,t,false),
1715 t,opt.value);
1716 found = TRUE;
1717 }
1718 ++index;
1719 }
1720 if (!found)
1721 {
1722 warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!");
1723 }
1724}
1725
1726//---------------------------------------------------------------------------
1727
1729 const QCString &doc)
1730{
1731 AUTO_TRACE();
1732 Token retval = Token::make_RetVal_OK();
1733
1734 if (doc.isEmpty()) return retval;
1735
1736 tokenizer.init(doc.data(),context.fileName,context.markdownSupport,context.insideHtmlLink);
1737
1738 // first parse any number of paragraphs
1739 bool isFirst=TRUE;
1740 DocPara *lastPar=!children.empty() ? std::get_if<DocPara>(&children.back()): nullptr;
1741 if (lastPar)
1742 { // last child item was a paragraph
1743 isFirst=FALSE;
1744 }
1745 do
1746 {
1747 children.append<DocPara>(this,parent);
1748 DocPara *par = children.get_last<DocPara>();
1749 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1750 retval=par->parse();
1751 if (!par->isEmpty())
1752 {
1753 if (lastPar) lastPar->markLast(FALSE);
1754 lastPar=par;
1755 }
1756 else
1757 {
1758 children.pop_back();
1759 }
1760 } while (retval.is(TokenRetval::TK_NEWPARA));
1761 if (lastPar) lastPar->markLast();
1762
1763 AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
1764 return retval;
1765}
1766
1767//---------------------------------------------------------------------------
1768
1770{
1771 AUTO_TRACE("file={} text={}",file,text);
1772 bool ambig = false;
1773 QCString filePath = findFilePath(file,ambig);
1774 if (!filePath.isEmpty())
1775 {
1776 size_t indent = 0;
1777 text = detab(fileToString(filePath,Config_getBool(FILTER_SOURCE_FILES)),indent);
1778 if (ambig)
1779 {
1780 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '{}' is ambiguous"
1781 "Possible candidates:\n{}",file, showFileDefMatches(Doxygen::exampleNameLinkedMap,file));
1782 }
1783 }
1784 else
1785 {
1786 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '{}' is not found. "
1787 "Check your EXAMPLE_PATH",file);
1788 }
1789}
1790
1791//---------------------------------------------------------------------------
1792
1793static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
1794{
1795 size_t s=j;
1796 int round=0;
1797 bool insideDQuote=FALSE;
1798 bool insideSQuote=FALSE;
1799 bool found=FALSE;
1800 while (j<len && !found)
1801 {
1802 if (!insideSQuote && !insideDQuote)
1803 {
1804 switch (data[j])
1805 {
1806 case '(': round++; break;
1807 case ')': round--; break;
1808 case '"': insideDQuote=TRUE; break;
1809 case '\'': insideSQuote=TRUE; break;
1810 case '\\': // fall through, begin of command
1811 case '@': // fall through, begin of command
1812 case '\t': // fall through
1813 case '\n':
1814 found=(round==0);
1815 break;
1816 case ' ': // allow spaces in cast operator (see issue #11169)
1817 found=(round==0) && (j<8 || !literal_at(data+j-8,"operator"));
1818 break;
1819 }
1820 }
1821 else if (insideSQuote) // look for single quote end
1822 {
1823 if (data[j]=='\'' && (j==0 || data[j]!='\\'))
1824 {
1825 insideSQuote=FALSE;
1826 }
1827 }
1828 else if (insideDQuote) // look for double quote end
1829 {
1830 if (data[j]=='"' && (j==0 || data[j]!='\\'))
1831 {
1832 insideDQuote=FALSE;
1833 }
1834 }
1835 if (!found) j++;
1836 }
1837
1838 // include const and volatile
1839 if (literal_at(data+j," const"))
1840 {
1841 j+=6;
1842 }
1843 else if (literal_at(data+j," volatile"))
1844 {
1845 j+=9;
1846 }
1847
1848 // allow '&' or '&&' or ' &' or ' &&' at the end
1849 size_t k=j;
1850 while (k<len && data[k]==' ') k++;
1851 if (k<len-1 && data[k]=='&' && data[k+1]=='&') j=k+2;
1852 else if (k<len && data[k]=='&' ) j=k+1;
1853
1854 // do not include punctuation added by Definition::_setBriefDescription()
1855 size_t e=j;
1856 if (j>0 && data[j-1]=='.') { e--; }
1857 QCString id(data+s,e-s);
1858 //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
1859 return id;
1860}
1861
1862// macro to check if the input starts with a specific command.
1863// note that data[i] should point to the start of the command (\ or @ character)
1864// and the sizeof(str) returns the size of str including the '\0' terminator;
1865// a fact we abuse to skip over the start of the command character.
1866#define CHECK_FOR_COMMAND(str,action) \
1867 do if ((i+sizeof(str)<len) && literal_at(data+i+1,str)) \
1868 { j=i+sizeof(str); action; } while(0)
1869
1870static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i,size_t len,bool &brief)
1871{
1872 size_t j=0;
1873 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
1874 {
1875 CHECK_FOR_COMMAND("copybrief",brief=TRUE); // @copybrief or \copybrief
1876 CHECK_FOR_COMMAND("copydetails",brief=FALSE); // @copydetails or \copydetails
1877 }
1878 return j;
1879}
1880
1881static size_t isVerbatimSection(const char *data,size_t i,size_t len,QCString &endMarker)
1882{
1883 size_t j=0;
1884 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
1885 {
1886 CHECK_FOR_COMMAND("dot",endMarker="enddot");
1887 CHECK_FOR_COMMAND("icode",endMarker="endicode");
1888 CHECK_FOR_COMMAND("code",endMarker="endcode");
1889 CHECK_FOR_COMMAND("msc",endMarker="endmsc");
1890 CHECK_FOR_COMMAND("iverbatim",endMarker="endiverbatim");
1891 CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim");
1892 CHECK_FOR_COMMAND("iliteral",endMarker="endiliteral");
1893 CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly");
1894 CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly");
1895 CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly");
1896 CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly");
1897 CHECK_FOR_COMMAND("manonly",endMarker="endmanonly");
1898 CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly");
1899 CHECK_FOR_COMMAND("startuml",endMarker="enduml");
1900 }
1901 //printf("isVerbatimSection(%s)=%d)\n",qPrint(QCString(&data[i]).left(10)),j);
1902 return j;
1903}
1904
1905static size_t skipToEndMarker(const char *data,size_t i,size_t len,const QCString &endMarker)
1906{
1907 while (i<len)
1908 {
1909 if ((data[i]=='@' || data[i]=='\\') && // start of command character
1910 (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped
1911 {
1912 if (i+endMarker.length()+1<=len && qstrncmp(data+i+1,endMarker.data(),endMarker.length())==0)
1913 {
1914 return i+endMarker.length()+1;
1915 }
1916 }
1917 i++;
1918 }
1919 // oops no endmarker found...
1920 return i<len ? i+1 : len;
1921}
1922
1923
1924QCString DocParser::processCopyDoc(const char *data,size_t &len)
1925{
1926 AUTO_TRACE("data={} len={}",Trace::trunc(data),len);
1927 QCString result;
1928 result.reserve(len+32);
1929 size_t i=0;
1930 int lineNr = tokenizer.getLineNr();
1931 while (i<len)
1932 {
1933 char c = data[i];
1934 if (c=='@' || c=='\\') // look for a command
1935 {
1936 bool isBrief=TRUE;
1937 size_t j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
1938 if (j>0)
1939 {
1940 // skip whitespace
1941 while (j<len && (data[j]==' ' || data[j]=='\t')) j++;
1942 // extract the argument
1943 QCString id = extractCopyDocId(data,j,len);
1944 const Definition *def = nullptr;
1945 QCString doc,brief;
1946 //printf("resolving docs='%s'\n",qPrint(id));
1947 bool found = findDocsForMemberOrCompound(id,&doc,&brief,&def);
1948 if (found && def->isReference())
1949 {
1950 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1951 "@copy{} or @copydoc target '{}' found but is from a tag file, skipped",
1952 isBrief?"brief":"details", id);
1953 }
1954 else if (found)
1955 {
1956 //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,qPrint(brief),qPrint(doc),isBrief);
1957 auto it = std::find(context.copyStack.begin(),context.copyStack.end(),def);
1958 if (it==context.copyStack.end()) // definition not parsed earlier
1959 {
1960 QCString orgFileName = context.fileName;
1961 context.copyStack.push_back(def);
1962 auto addDocs = [&](const QCString &file_,int line_,const QCString &doc_)
1963 {
1964 result+=" \\ifile \""+file_+"\" ";
1965 result+="\\iline "+QCString().setNum(line_)+" \\ilinebr ";
1966 size_t len_ = doc_.length();
1967 result+=processCopyDoc(doc_.data(),len_);
1968 };
1969 if (isBrief)
1970 {
1971 addDocs(def->briefFile(),def->briefLine(),brief);
1972 }
1973 else
1974 {
1975 addDocs(def->docFile(),def->docLine(),doc);
1977 {
1978 const MemberDef *md = toMemberDef(def);
1979 const ArgumentList &docArgList = md->templateMaster() ?
1980 md->templateMaster()->argumentList() :
1981 md->argumentList();
1982 result+=inlineArgListToDoc(docArgList);
1983 }
1984 }
1985 context.copyStack.pop_back();
1986 result+=" \\ilinebr \\ifile \""+context.fileName+"\" ";
1987 result+="\\iline "+QCString().setNum(lineNr)+" ";
1988 }
1989 else
1990 {
1991 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1992 "Found recursive @copy{} or @copydoc relation for argument '{}'.",
1993 isBrief?"brief":"details",id);
1994 }
1995 }
1996 else
1997 {
1998 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1999 "@copy{} or @copydoc target '{}' not found", isBrief?"brief":"details",id);
2000 }
2001 // skip over command
2002 i=j;
2003 }
2004 else
2005 {
2006 QCString endMarker;
2007 size_t k = isVerbatimSection(data,i,len,endMarker);
2008 if (k>0)
2009 {
2010 size_t orgPos = i;
2011 i=skipToEndMarker(data,k,len,endMarker);
2012 result+=QCString(data+orgPos,i-orgPos);
2013 // TODO: adjust lineNr
2014 }
2015 else
2016 {
2017 result+=c;
2018 i++;
2019 }
2020 }
2021 }
2022 else // not a command, just copy
2023 {
2024 result+=c;
2025 i++;
2026 lineNr += (c=='\n') ? 1 : 0;
2027 }
2028 }
2029 len = result.length();
2030 AUTO_TRACE_EXIT("result={}",Trace::trunc(result));
2031 return result;
2032}
2033
2034
2035//---------------------------------------------------------------------------
2036
2038 const QCString &fileName,
2039 int startLine,
2040 const Definition *ctx,
2041 const MemberDef *md,
2042 const QCString &input,
2043 const DocOptions &options)
2044{
2045 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2046 assert(parser!=nullptr);
2047 if (parser==nullptr) return nullptr;
2048 //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?qPrint(ctx->name()):"<none>",
2049 // md?qPrint(md->name()):"<none>",
2050 // qPrint(input));
2051 //printf("========== validating %s at line %d\n",qPrint(fileName),startLine);
2052 //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",qPrint(input));
2053
2054 // set initial token
2055 parser->context.token = parser->tokenizer.resetToken();
2056
2057 if (ctx && ctx!=Doxygen::globalScope &&
2060 )
2061 )
2062 {
2064 }
2065 else if (ctx && ctx->definitionType()==Definition::TypePage)
2066 {
2067 const Definition *scope = (toPageDef(ctx))->getPageScope();
2068 if (scope && scope!=Doxygen::globalScope)
2069 {
2070 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2071 }
2072 }
2073 else if (ctx && ctx->definitionType()==Definition::TypeGroup)
2074 {
2075 const Definition *scope = (toGroupDef(ctx))->getGroupScope();
2076 if (scope && scope!=Doxygen::globalScope)
2077 {
2078 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2079 }
2080 }
2081 else
2082 {
2083 parser->context.context = "";
2084 }
2085 parser->context.scope = ctx;
2086 parser->context.lang = getLanguageFromFileName(fileName);
2087
2088 if (options.indexWords() && Doxygen::searchIndex.enabled())
2089 {
2090 if (md)
2091 {
2092 parser->context.searchUrl=md->getOutputFileBase();
2093 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
2094 }
2095 else if (ctx)
2096 {
2097 parser->context.searchUrl=ctx->getOutputFileBase();
2098 Doxygen::searchIndex.setCurrentDoc(ctx,ctx->anchor(),false);
2099 }
2100 }
2101 else
2102 {
2103 parser->context.searchUrl="";
2104 }
2105
2106 parser->context.fileName = fileName;
2107 parser->context.relPath = (!options.linkFromIndex() && ctx) ?
2109 QCString("");
2110 //printf("ctx->name=%s relPath=%s\n",qPrint(ctx->name()),qPrint(parser->context.relPath));
2111 parser->context.memberDef = md;
2112 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2113 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2114 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2115 parser->context.inSeeBlock = FALSE;
2116 parser->context.inCodeStyle = FALSE;
2117 parser->context.xmlComment = FALSE;
2118 parser->context.insideHtmlLink = FALSE;
2119 parser->context.includeFileText = "";
2120 parser->context.includeFileOffset = 0;
2121 parser->context.includeFileLength = 0;
2122 parser->context.isExample = options.isExample();
2123 parser->context.exampleName = options.exampleName();
2124 parser->context.hasParamCommand = FALSE;
2125 parser->context.hasReturnCommand = FALSE;
2126 parser->context.retvalsFound.clear();
2127 parser->context.paramsFound.clear();
2128 parser->context.markdownSupport = options.markdownSupport();
2129 parser->context.autolinkSupport = options.autolinkSupport();
2130
2131 //printf("Starting comment block at %s:%d\n",qPrint(parser->context.fileName),startLine);
2132 parser->tokenizer.setFileName(fileName);
2133 parser->tokenizer.setLineNr(startLine);
2134 size_t ioLen = input.length();
2135 QCString inpStr = parser->processCopyDoc(input.data(),ioLen);
2136 if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
2137 {
2138 inpStr+='\n';
2139 }
2140 //printf("processCopyDoc(in='%s' out='%s')\n",qPrint(input),qPrint(inpStr));
2141 parser->tokenizer.init(inpStr.data(),parser->context.fileName,
2143
2144 // build abstract syntax tree
2145 auto ast = std::make_unique<DocNodeAST>(DocRoot(parser,md!=nullptr,options.singleLine()));
2146 std::get<DocRoot>(ast->root).parse();
2147
2149 {
2150 // pretty print the result
2151 std::visit(PrintDocVisitor{},ast->root);
2152 }
2153
2154 if (md && md->isFunction())
2155 {
2157 }
2159
2160 // reset token
2161 parser->tokenizer.resetToken();
2162
2163 //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?qPrint(ctx->name()):"<none>",
2164 // md?qPrint(md->name()):"<none>");
2165
2166 return ast;
2167}
2168
2169IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf,const QCString &fileName,int lineNr,const QCString &input)
2170{
2171 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2172 assert(parser!=nullptr);
2173 if (parser==nullptr) return nullptr;
2174
2175 // set initial token
2176 parser->context.token = parser->tokenizer.resetToken();
2177
2178 //printf("------------ input ---------\n%s\n"
2179 // "------------ end input -----\n",input);
2180 parser->context.context = "";
2181 parser->context.fileName = fileName;
2182 parser->context.relPath = "";
2183 parser->context.memberDef = nullptr;
2184 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2185 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2186 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2187 parser->context.inSeeBlock = FALSE;
2188 parser->context.inCodeStyle = FALSE;
2189 parser->context.xmlComment = FALSE;
2190 parser->context.insideHtmlLink = FALSE;
2191 parser->context.includeFileText = "";
2192 parser->context.includeFileOffset = 0;
2193 parser->context.includeFileLength = 0;
2194 parser->context.isExample = FALSE;
2195 parser->context.exampleName = "";
2196 parser->context.hasParamCommand = FALSE;
2197 parser->context.hasReturnCommand = FALSE;
2198 parser->context.retvalsFound.clear();
2199 parser->context.paramsFound.clear();
2200 parser->context.searchUrl="";
2201 parser->context.lang = SrcLangExt::Unknown;
2202 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2203 parser->context.autolinkSupport = false;
2204
2205 auto ast = std::make_unique<DocNodeAST>(DocTitle(parser,nullptr));
2206
2207 if (!input.isEmpty())
2208 {
2209 // build abstract syntax tree from title string
2210 std::get<DocTitle>(ast->root).parseFromString(nullptr,input);
2211
2213 {
2214 // pretty print the result
2215 std::visit(PrintDocVisitor{},ast->root);
2216 }
2217 }
2218
2219 return ast;
2220}
2221
2223{
2224 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2225 assert(parser!=nullptr);
2226 if (parser==nullptr) return nullptr;
2227
2228 // set initial token
2229 parser->context.token = parser->tokenizer.resetToken();
2230
2231 //printf("------------ input ---------\n%s\n"
2232 // "------------ end input -----\n",input);
2233 //parser->context.token = new TokenInfo;
2234 parser->context.context = "";
2235 parser->context.fileName = "<parseText>";
2236 parser->context.relPath = "";
2237 parser->context.memberDef = nullptr;
2238 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2239 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2240 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2241 parser->context.inSeeBlock = FALSE;
2242 parser->context.inCodeStyle = FALSE;
2243 parser->context.xmlComment = FALSE;
2244 parser->context.insideHtmlLink = FALSE;
2245 parser->context.includeFileText = "";
2246 parser->context.includeFileOffset = 0;
2247 parser->context.includeFileLength = 0;
2248 parser->context.isExample = FALSE;
2249 parser->context.exampleName = "";
2250 parser->context.hasParamCommand = FALSE;
2251 parser->context.hasReturnCommand = FALSE;
2252 parser->context.retvalsFound.clear();
2253 parser->context.paramsFound.clear();
2254 parser->context.searchUrl="";
2255 parser->context.lang = SrcLangExt::Unknown;
2256 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2257 parser->context.autolinkSupport = FALSE;
2258
2259
2260 auto ast = std::make_unique<DocNodeAST>(DocText(parser));
2261
2262 if (!input.isEmpty())
2263 {
2264 parser->tokenizer.setLineNr(1);
2265 parser->tokenizer.init(input.data(),parser->context.fileName,
2267
2268 // build abstract syntax tree
2269 std::get<DocText>(ast->root).parse();
2270
2272 {
2273 // pretty print the result
2274 std::visit(PrintDocVisitor{},ast->root);
2275 }
2276 }
2277
2278 return ast;
2279}
2280
2281IDocNodeASTPtr createRef(IDocParser &parserIntf,const QCString &target,const QCString &context, const QCString &srcFile, int srcLine )
2282{
2283 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2284 assert(parser!=nullptr);
2285 if (parser==nullptr) return nullptr;
2286 if (!srcFile.isEmpty())
2287 {
2288 parser->context.fileName = srcFile;
2289 parser->tokenizer.setFileName(srcFile);
2290 parser->tokenizer.setLineNr(srcLine);
2291 }
2292 return std::make_unique<DocNodeAST>(DocRef(parser,nullptr,target,context));
2293}
2294
2295void docFindSections(const QCString &input,
2296 const Definition *d,
2297 const QCString &fileName)
2298{
2299 DocParser parser;
2300 parser.tokenizer.findSections(input,d,fileName);
2301}
2302
This class represents an function or template argument list.
Definition arguments.h:65
size_t size() const
Definition arguments.h:100
void push_back(const Argument &a)
Definition arguments.h:102
bool empty() const
Definition arguments.h:99
A abstract class representing of a compound symbol.
Definition classdef.h:104
@ PrintTree
Definition debug.h:34
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual QCString docFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual QCString anchor() const =0
virtual QCString briefDescriptionAsTooltip() const =0
virtual int briefLine() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual QCString getReference() const =0
virtual QCString getSourceFileBase() const =0
virtual QCString documentation() const =0
virtual QCString qualifiedName() const =0
virtual QCString briefFile() const =0
virtual QCString getOutputFileBase() const =0
virtual bool isReference() const =0
virtual const QCString & name() const =0
Class representing a directory in the file system.
Definition dir.h:75
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
Node representing an anchor.
Definition docnode.h:229
Node representing an item of a cross-referenced list.
Definition docnode.h:529
Node representing a Hypertext reference.
Definition docnode.h:823
Node representing an image.
Definition docnode.h:642
@ DocBook
Definition docnode.h:644
Node representing an internal reference to some item.
Definition docnode.h:807
Node representing a word that can be linked to something.
Definition docnode.h:165
Node representing a paragraph in the documentation tree.
Definition docnode.h:1080
bool isLast() const
Definition docnode.h:1088
void markLast(bool v=TRUE)
Definition docnode.h:1086
bool isFirst() const
Definition docnode.h:1087
bool defaultHandleToken(DocNodeVariant *parent, Token tok, DocNodeList &children, bool handleWord=TRUE)
std::stack< DocParserContext > contextStack
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=FALSE)
DocTokenizer tokenizer
void handleInternalRef(DocNodeVariant *parent, DocNodeList &children)
void handleParameterType(DocNodeVariant *parent, DocNodeList &children, const QCString &paramTypes)
QCString findAndCopyImage(const QCString &fileName, DocImage::Type type, bool doWarn=true)
Definition docparser.cpp:96
void checkRetvalName()
QCString processCopyDoc(const char *data, size_t &len)
void readTextFileByName(const QCString &file, QCString &text)
void handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const QCString &cmdName)
Token handleAHref(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
Token internalValidatingParseDoc(DocNodeVariant *parent, DocNodeList &children, const QCString &doc)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName)
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children)
void checkUnOrMultipleDocumentedParams()
void popContext()
Definition docparser.cpp:75
bool findDocsForMemberOrCompound(const QCString &commandName, QCString *pDoc, QCString *pBrief, const Definition **pDef)
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName, const HtmlAttribList *attribs)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const QCString &cmdName)
void handleIFile(char cmdChar, const QCString &cmdName)
void handleILine(char cmdChar, const QCString &cmdName)
void checkArgumentName()
DocParserContext context
void handleAnchor(DocNodeVariant *parent, DocNodeList &children)
void handleImg(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, QCString &width, QCString &height)
void handleUnclosedStyleCommands()
void pushContext()
Definition docparser.cpp:60
void errorHandleDefaultToken(DocNodeVariant *parent, Token tok, DocNodeList &children, const QCString &txt)
Node representing a reference to some item.
Definition docnode.h:778
Root node of documentation tree.
Definition docnode.h:1310
Node representing a separator.
Definition docnode.h:365
Node representing a style change.
Definition docnode.h:268
const HtmlAttribList & attribs() const
Definition docnode.h:311
QCString tagName() const
Definition docnode.h:312
Style style() const
Definition docnode.h:307
size_t position() const
Definition docnode.h:310
Node representing a special symbol.
Definition docnode.h:328
static HtmlEntityMapper::SymType decodeSymbol(const QCString &symName)
Definition docnode.cpp:153
Root node of a text fragment.
Definition docnode.h:1301
Node representing a simple section title.
Definition docnode.h:608
void init(const char *input, const QCString &fileName, bool markdownSupport, bool insideHtmlLink)
void setLineNr(int lineno)
void setFileName(const QCString &fileName)
void findSections(const QCString &input, const Definition *d, const QCString &fileName)
TokenInfo * resetToken()
Node representing a URL (or email address).
Definition docnode.h:188
Node representing a verbatim, unparsed text fragment.
Definition docnode.h:376
Node representing some amount of white space.
Definition docnode.h:354
Node representing a word.
Definition docnode.h:153
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:114
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static NamespaceDefMutable * globalScope
Definition doxygen.h:120
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static IndexList * indexList
Definition doxygen.h:131
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:123
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:113
A model of a file symbol.
Definition filedef.h:99
virtual QCString absFilePath() const =0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool isSymLink() const
Definition fileinfo.cpp:77
bool exists() const
Definition fileinfo.cpp:30
A model of a group of symbols.
Definition groupdef.h:52
virtual QCString groupTitle() const =0
T & back()
access the last element
Definition growvector.h:135
void pop_back()
removes the last element
Definition growvector.h:115
bool empty() const
checks whether the container is empty
Definition growvector.h:140
Class representing a list of HTML attributes.
Definition htmlattrib.h:33
opaque parser interface
Definition docparser.h:35
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual bool isObjCMethod() const =0
virtual const ClassDef * getClassDef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isFunction() const =0
virtual QCString objCMethodName(bool localLink, bool showStatic) const =0
virtual const MemberDef * templateMaster() const =0
virtual void detectUndocumentedParams(bool hasParamCommand, bool hasReturnCommand) const =0
An abstract interface of a namespace symbol.
A model of a page symbol.
Definition pagedef.h:26
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:169
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:185
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
std::string_view view() const
Definition qcstring.h:174
QCString left(size_t len) const
Definition qcstring.h:229
static RequirementManager & instance()
bool is(TokenRetval rv) const
TOKEN_SPECIFICATIONS RETVAL_SPECIFICATIONS const char * to_string() const
TokenRetval value() const
bool is_any_of(ARGS... args) const
char command_to_char() const
ClassDef * getClass(const QCString &n)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:230
CommandType
Definition cmdmapper.h:29
@ CMD_INTERNALREF
Definition cmdmapper.h:65
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::vector< std::string > StringVector
Definition containers.h:33
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:48
#define AUTO_TRACE(...)
Definition docnode.cpp:47
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:49
std::variant< DocWord, DocLinkedWord, DocURL, DocLineBreak, DocHorRuler, DocAnchor, DocCite, DocStyleChange, DocSymbol, DocEmoji, DocWhiteSpace, DocSeparator, DocVerbatim, DocInclude, DocIncOperator, DocFormula, DocIndexEntry, DocAutoList, DocAutoListItem, DocTitle, DocXRefItem, DocImage, DocDotFile, DocMscFile, DocDiaFile, DocVhdlFlow, DocLink, DocRef, DocInternalRef, DocHRef, DocHtmlHeader, DocHtmlDescTitle, DocHtmlDescList, DocSection, DocSecRefItem, DocSecRefList, DocInternal, DocParBlock, DocSimpleList, DocHtmlList, DocSimpleSect, DocSimpleSectSep, DocParamSect, DocPara, DocParamList, DocSimpleListItem, DocHtmlListItem, DocHtmlDescData, DocHtmlCell, DocHtmlCaption, DocHtmlRow, DocHtmlTable, DocHtmlBlockQuote, DocText, DocRoot, DocHtmlDetails, DocHtmlSummary, DocPlantUmlFile > DocNodeVariant
Definition docnode.h:67
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1327
void docFindSections(const QCString &input, const Definition *d, const QCString &fileName)
static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf, const QCString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const QCString &input, const DocOptions &options)
static size_t skipToEndMarker(const char *data, size_t i, size_t len, const QCString &endMarker)
IDocNodeASTPtr validatingParseText(IDocParser &parserIntf, const QCString &input)
IDocParserPtr createDocParser()
factory function to create a parser
Definition docparser.cpp:55
IDocNodeASTPtr createRef(IDocParser &parserIntf, const QCString &target, const QCString &context, const QCString &srcFile, int srcLine)
static size_t isVerbatimSection(const char *data, size_t i, size_t len, QCString &endMarker)
IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf, const QCString &fileName, int lineNr, const QCString &input)
static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i, size_t len, bool &brief)
#define CHECK_FOR_COMMAND(str, action)
std::unique_ptr< IDocNodeAST > IDocNodeASTPtr
Definition docparser.h:57
std::unique_ptr< IDocParser > IDocParserPtr
pointer to parser interface
Definition docparser.h:41
Private header shared between docparser.cpp and docnode.cpp.
IterableStack< const DocNodeVariant * > DocStyleChangeStack
Definition docparser_p.h:55
bool insidePRE(const DocNodeVariant *n)
bool insideLI(const DocNodeVariant *n)
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
GroupDef * toGroupDef(Definition *d)
MemberDef * toMemberDef(Definition *d)
#define warn_incomplete_doc(file, line, fmt,...)
Definition message.h:107
#define err(fmt,...)
Definition message.h:127
#define warn_doc_error(file, line, fmt,...)
Definition message.h:112
const Mapper< HtmlTagType > * htmlTagMapper
const Mapper< CommandType > * cmdMapper
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:855
PageDef * toPageDef(Definition *d)
Definition pagedef.cpp:502
Portable versions of functions that are platform dependent.
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
int qstrncmp(const char *str1, const char *str2, size_t len)
Definition qcstring.h:75
const char * qPrint(const char *s)
Definition qcstring.h:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns TRUE iff data points to a substring that matches string literal str
Definition stringutil.h:98
This class contains the information about the argument of a function or template.
Definition arguments.h:27
void append(Args &&... args)
Append a new DocNodeVariant to the list by constructing it with type T and parameters Args.
Definition docnode.h:1396
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1407
bool autolinkSupport() const
Definition docoptions.h:32
bool linkFromIndex() const
Definition docoptions.h:30
bool markdownSupport() const
Definition docoptions.h:31
QCString exampleName() const
Definition docoptions.h:28
bool indexWords() const
Definition docoptions.h:26
bool singleLine() const
Definition docoptions.h:29
bool isExample() const
Definition docoptions.h:27
StringMultiSet retvalsFound
Definition docparser_p.h:76
DocStyleChangeStack styleStack
Definition docparser_p.h:68
size_t includeFileLength
Definition docparser_p.h:89
QCString fileName
Definition docparser_p.h:71
DocNodeStack nodeStack
Definition docparser_p.h:67
StringMultiSet paramsFound
Definition docparser_p.h:77
QCString exampleName
Definition docparser_p.h:81
const Definition * scope
Definition docparser_p.h:61
QCString includeFileText
Definition docparser_p.h:87
TokenInfo * token
Definition docparser_p.h:94
DocStyleChangeStack initialStyleStack
Definition docparser_p.h:69
SrcLangExt lang
Definition docparser_p.h:84
QCString searchUrl
Definition docparser_p.h:82
size_t includeFileOffset
Definition docparser_p.h:88
const MemberDef * memberDef
Definition docparser_p.h:79
bool checkCV
Definition util.h:166
const MemberDef * md
Definition util.h:173
bool found
Definition util.h:172
SrcLangExt
Definition types.h:207
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:567
QCString findFilePath(const QCString &file, bool &ambig)
Definition util.cpp:2979
QCString linkToText(SrcLangExt lang, const QCString &link, bool isFileName)
Definition util.cpp:2698
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5214
bool resolveRef(const QCString &scName, const QCString &name, bool inSeeBlock, const Definition **resContext, const MemberDef **resMember, SrcLangExt lang, bool lookForSpecialization, const FileDef *currentFile, bool checkScope)
Definition util.cpp:2442
QCString relativePathToRoot(const QCString &name)
Definition util.cpp:3583
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3022
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3508
QCString detab(const QCString &s, size_t &refIndent)
Definition util.cpp:6743
QCString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:1254
QCString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5920
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:2298
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6641
bool copyFile(const QCString &src, const QCString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:5880
QCString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:1204
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2894
A bunch of utility functions.