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 context.numParameters = static_cast<int>(al.size());
220 //printf("isDocsForDefinition()=%d\n",context.memberDef->isDocsForDefinition());
221 if (al.empty()) return; // no argument list
222
223 static const reg::Ex re(R"(\$?\w+\.*)");
224 static const reg::Ex re_digits(R"(\d+)");
225 reg::Iterator it(name,re);
227 for (; it!=end ; ++it)
228 {
229 const auto &match = *it;
230 QCString aName=match.str();
231 int number = reg::match(aName.view(),re_digits) ? std::atoi(aName.data()) : -1;
232 if (lang==SrcLangExt::Fortran) aName=aName.lower();
233 //printf("aName='%s'\n",qPrint(aName));
234 bool found=FALSE;
235 int position=1;
236 for (const Argument &a : al)
237 {
238 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
239 if (lang==SrcLangExt::Fortran) argName=argName.lower();
240 argName=argName.stripWhiteSpace();
241 //printf("argName='%s' aName=%s\n",qPrint(argName),qPrint(aName));
242 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
243 bool sameName = aName==argName;
244 bool samePosition = position==number;
245 if (samePosition || sameName) // @param <number> or @param name
246 {
247 if (!sameName) // replace positional argument with real name or -
248 {
249 context.token->name = argName.isEmpty() ? "-" : argName;
250 }
251 context.paramsFound.insert(aName.str());
252 found=true;
253 break;
254 }
255 else if (aName==".") // replace . by - in the output
256 {
257 context.token->name = "-";
258 }
259 position++;
260 }
261 if (!found)
262 {
263 //printf("member type=%d\n",context.memberDef->memberType());
264 QCString scope=context.memberDef->getScopeString();
265 if (!scope.isEmpty()) scope+="::"; else scope="";
266 QCString inheritedFrom = "";
267 QCString docFile = context.memberDef->docFile();
268 int docLine = context.memberDef->docLine();
269 const MemberDef *inheritedMd = context.memberDef->inheritsDocsFrom();
270 if (inheritedMd) // documentation was inherited
271 {
272 inheritedFrom.sprintf(" inherited from member %s at line "
273 "%d in file %s",qPrint(inheritedMd->name()),
274 inheritedMd->docLine(),qPrint(inheritedMd->docFile()));
275 docFile = context.memberDef->getDefFileName();
276 docLine = context.memberDef->getDefLine();
277 }
278 QCString alStr = argListToString(al);
279 if (number==0)
280 {
281 warn_doc_error(docFile,docLine,
282 "positional argument with value '0' of command @param "
283 "is invalid, first parameter has index '1' for {}{}{}{}",
284 scope, context.memberDef->name(),
285 alStr, inheritedFrom);
286 context.token->name = "-";
287 }
288 else if (number>0)
289 {
290 warn_doc_error(docFile,docLine,
291 "positional argument '{}' of command @param "
292 "is larger than the number of parameters ({}) for {}{}{}{}",
293 number, al.size(), scope, context.memberDef->name(),
294 alStr, inheritedFrom);
295 context.token->name = "-";
296 }
297 else
298 {
299 warn_doc_error(docFile,docLine,
300 "argument '{}' of command @param "
301 "is not found in the argument list of {}{}{}{}",
302 aName, scope, context.memberDef->name(),
303 alStr, inheritedFrom);
304 }
305 }
306 }
307}
308/*! Collects the return values found with \@retval command
309 * in a global list g_parserContext.retvalsFound.
310 */
312{
313 QCString name = context.token->name;
314 if (!Config_getBool(WARN_IF_DOC_ERROR)) return;
315 if (context.memberDef==nullptr || name.isEmpty()) return; // not a member or no valid name
316 if (context.retvalsFound.count(name.str())==1) // only report the first double entry
317 {
318 warn_doc_error(context.memberDef->getDefFileName(),
319 context.memberDef->getDefLine(),
320 "return value '{}' of {} has multiple documentation sections",
321 name, context.memberDef->qualifiedName());
322 }
323 context.retvalsFound.insert(name.str());
324}
325
326/*! Checks if the parameters that have been specified using \@param are
327 * indeed all parameters and that a parameter does not have multiple
328 * \@param blocks.
329 * Must be called after checkArgumentName() has been called for each
330 * argument.
331 */
333{
334 if (context.memberDef && context.hasParamCommand)
335 {
336 const ArgumentList &al=context.memberDef->isDocsForDefinition() ?
337 context.memberDef->argumentList() :
338 context.memberDef->declArgumentList();
339 SrcLangExt lang = context.memberDef->getLanguage();
340 if (!al.empty())
341 {
342 ArgumentList undocParams;
343 int position = 1;
344 for (const Argument &a: al)
345 {
346 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
347 if (lang==SrcLangExt::Fortran) argName = argName.lower();
348 argName=argName.stripWhiteSpace();
349 QCString aName = argName;
350 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
351 if (lang==SrcLangExt::Python && (argName=="self" || argName=="cls"))
352 {
353 // allow undocumented self / cls parameter for Python
354 }
355 else if (lang==SrcLangExt::Cpp && (a.type=="this" || a.type.startsWith("this ")))
356 {
357 // allow undocumented this (for C++23 deducing this), see issue #11123
358 }
359 else if (!argName.isEmpty())
360 {
361 size_t count_named = context.paramsFound.count(argName.str());
362 size_t count_positional = context.paramsFound.count(std::to_string(position));
363 if (count_named==0 && count_positional==0 && a.docs.isEmpty())
364 {
365 undocParams.push_back(a);
366 }
367 else if (count_named==1 && count_positional==1 && Config_getBool(WARN_IF_DOC_ERROR))
368 {
369 warn_doc_error(context.memberDef->docFile(),
370 context.memberDef->docLine(),
371 "argument {} from the argument list of {} has both named and positional @param documentation sections",
372 aName, context.memberDef->qualifiedName());
373 }
374 else if (count_named+count_positional>1 && Config_getBool(WARN_IF_DOC_ERROR))
375 {
376 warn_doc_error(context.memberDef->docFile(),
377 context.memberDef->docLine(),
378 "argument {} from the argument list of {} has multiple @param documentation sections",
379 aName, context.memberDef->qualifiedName());
380 }
381 }
382 position++;
383 }
384 if (!undocParams.empty() && Config_getBool(WARN_IF_INCOMPLETE_DOC))
385 {
386 bool first=TRUE;
387 QCString errMsg = "The following parameter";
388 if (undocParams.size()>1) errMsg+="s";
389 errMsg+=QCString(" of ")+
390 context.memberDef->qualifiedName() +
391 argListToString(al) +
392 (undocParams.size()>1 ? " are" : " is") + " not documented:\n";
393 for (const Argument &a : undocParams)
394 {
395 QCString argName = context.memberDef->isDefine() ? a.type : a.name;
396 if (lang==SrcLangExt::Fortran) argName = argName.lower();
397 argName=argName.stripWhiteSpace();
398 if (!first) errMsg+="\n";
399 first=FALSE;
400 errMsg+=" parameter '"+argName+"'";
401 }
402 warn_incomplete_doc(context.memberDef->docFile(), context.memberDef->docLine(), "{}", errMsg);
403 }
404
405 if (Config_getBool(WARN_IF_DOC_ERROR) && context.paramPosition-1 > context.numParameters)
406 {
407 warn_doc_error(context.memberDef->docFile(),
408 context.memberDef->docLine(),
409 "too many @param commands for function {}. Found {} while function has {} parameter{}",
410 context.memberDef->qualifiedName(), context.paramPosition-1, context.numParameters, context.numParameters!=1?"s":"");
411 }
412 }
413 else
414 {
415 if (context.paramsFound.empty() && Config_getBool(WARN_IF_DOC_ERROR))
416 {
417 warn_doc_error(context.memberDef->docFile(),
418 context.memberDef->docLine(),
419 "{} has @param documentation sections but no arguments",
420 context.memberDef->qualifiedName());
421 }
422 }
423 }
424}
425
426
427//---------------------------------------------------------------------------
428
429//---------------------------------------------------------------------------
430
431//---------------------------------------------------------------------------
432/*! Looks for a documentation block with name commandName in the current
433 * context (g_parserContext.context). The resulting documentation string is
434 * put in pDoc, the definition in which the documentation was found is
435 * put in pDef.
436 * @retval TRUE if name was found.
437 * @retval FALSE if name was not found.
438 */
440 QCString *pDoc,
441 QCString *pBrief,
442 const Definition **pDef)
443{
444 AUTO_TRACE("commandName={}",commandName);
445 *pDoc="";
446 *pBrief="";
447 *pDef=nullptr;
448 QCString cmdArg=commandName;
449 if (cmdArg.isEmpty())
450 {
451 AUTO_TRACE_EXIT("empty");
452 return false;
453 }
454
455 const FileDef *fd=nullptr;
456 const GroupDef *gd=nullptr;
457 const PageDef *pd=nullptr;
458 gd = Doxygen::groupLinkedMap->find(cmdArg);
459 if (gd) // group
460 {
461 *pDoc=gd->documentation();
462 *pBrief=gd->briefDescription();
463 *pDef=gd;
464 AUTO_TRACE_EXIT("group");
465 return true;
466 }
467 pd = Doxygen::pageLinkedMap->find(cmdArg);
468 if (pd) // page
469 {
470 *pDoc=pd->documentation();
471 *pBrief=pd->briefDescription();
472 *pDef=pd;
473 AUTO_TRACE_EXIT("page");
474 return true;
475 }
476 bool ambig = false;
477 fd = findFileDef(Doxygen::inputNameLinkedMap,cmdArg,ambig);
478 if (fd && !ambig) // file
479 {
480 *pDoc=fd->documentation();
481 *pBrief=fd->briefDescription();
482 *pDef=fd;
483 AUTO_TRACE_EXIT("file");
484 return true;
485 }
486
487 // for symbols we need to normalize the separator, so A#B, or A\B, or A.B becomes A::B
488 cmdArg = substitute(cmdArg,"#","::");
489 cmdArg = substitute(cmdArg,"\\","::");
490 bool extractAnonNs = Config_getBool(EXTRACT_ANON_NSPACES);
491 if (extractAnonNs &&
492 cmdArg.startsWith("anonymous_namespace{")
493 )
494 {
495 size_t rightBracePos = cmdArg.find("}", static_cast<int>(qstrlen("anonymous_namespace{")));
496 QCString leftPart = cmdArg.left(rightBracePos + 1);
497 QCString rightPart = cmdArg.right(cmdArg.size() - rightBracePos - 1);
498 rightPart = substitute(rightPart, ".", "::");
499 cmdArg = leftPart + rightPart;
500 }
501 else
502 {
503 cmdArg = substitute(cmdArg,".","::");
504 }
505
506 int l=static_cast<int>(cmdArg.length());
507
508 int funcStart=cmdArg.find('(');
509 if (funcStart==-1)
510 {
511 funcStart=l;
512 }
513 else
514 {
515 // Check for the case of operator() and the like.
516 // beware of scenarios like operator()((foo)bar)
517 int secondParen = cmdArg.find('(', funcStart+1);
518 int leftParen = cmdArg.find(')', funcStart+1);
519 if (leftParen!=-1 && secondParen!=-1)
520 {
521 if (leftParen<secondParen)
522 {
523 funcStart=secondParen;
524 }
525 }
526 }
527
528 QCString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
529 QCString args=cmdArg.right(l-funcStart);
530 // try if the link is to a member
531 GetDefInput input(
532 context.context.find('.')==-1 ? context.context : QCString(), // find('.') is a hack to detect files
533 name,
534 args);
535 input.checkCV=true;
536 GetDefResult result = getDefs(input);
537 //printf("found=%d context=%s name=%s\n",result.found,qPrint(context.context),qPrint(name));
538 if (result.found && result.md)
539 {
540 *pDoc=result.md->documentation();
541 *pBrief=result.md->briefDescription();
542 *pDef=result.md;
543 AUTO_TRACE_EXIT("member");
544 return true;
545 }
546
547 int scopeOffset=static_cast<int>(context.context.length());
548 do // for each scope
549 {
550 QCString fullName=cmdArg;
551 if (scopeOffset>0)
552 {
553 fullName.prepend(context.context.left(scopeOffset)+"::");
554 }
555 //printf("Trying fullName='%s'\n",qPrint(fullName));
556
557 // try class, namespace, group, page, file reference
558 const ClassDef *cd = Doxygen::classLinkedMap->find(fullName);
559 if (cd) // class
560 {
561 *pDoc=cd->documentation();
562 *pBrief=cd->briefDescription();
563 *pDef=cd;
564 AUTO_TRACE_EXIT("class");
565 return true;
566 }
567 const NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(fullName);
568 if (nd) // namespace
569 {
570 *pDoc=nd->documentation();
571 *pBrief=nd->briefDescription();
572 *pDef=nd;
573 AUTO_TRACE_EXIT("namespace");
574 return true;
575 }
576 if (scopeOffset==0)
577 {
578 scopeOffset=-1;
579 }
580 else
581 {
582 scopeOffset = context.context.findRev("::",scopeOffset-1);
583 if (scopeOffset==-1) scopeOffset=0;
584 }
585 } while (scopeOffset>=0);
586
587 AUTO_TRACE_EXIT("not found");
588 return FALSE;
589}
590
591//---------------------------------------------------------------------------
593 DocNodeList &children,const QCString &txt)
594{
595 switch (tok.value())
596 {
597 case TokenRetval::TK_COMMAND_AT:
598 // fall through
599 case TokenRetval::TK_COMMAND_BS:
600 {
601 char cs[2] = { tok.command_to_char(), 0 };
602 children.append<DocWord>(this,parent,cs + context.token->name);
603 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a {}",
604 tok.command_to_char(),context.token->name,txt);
605 }
606 break;
607 case TokenRetval::TK_SYMBOL:
608 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a {}",
609 qPrint(context.token->name), qPrint(txt));
610 break;
611 case TokenRetval::TK_HTMLTAG:
612 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <{}{}> found as part of a {}",
613 context.token->endTag ? "/" : "",context.token->name, txt);
614 break;
615 default:
616 children.append<DocWord>(this,parent,context.token->name);
617 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token {} found as part of a {}",
618 tok.to_string(), txt);
619 break;
620 }
621}
622
623//---------------------------------------------------------------------------
624
626{
627 AUTO_TRACE("cmdName={}",cmdName);
628 QCString saveCmdName = cmdName;
629 Token tok=tokenizer.lex();
630 size_t styleStackSizeAtStart = context.styleStack.size();
631 if (!tok.is(TokenRetval::TK_WHITESPACE))
632 {
633 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
634 saveCmdName);
635 return tok;
636 }
637 tok = tokenizer.lex();
638 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::TK_WHITESPACE,
639 TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST)
640 )
641 {
642 static const reg::Ex specialChar(R"([.,|()\‍[\‍]:;?])");
643 if (tok.is(TokenRetval::TK_WORD) && context.token->name.length()==1 &&
644 reg::match(context.token->name.str(),specialChar))
645 {
646 // special character that ends the markup command
647 AUTO_TRACE_ADD("special character ending style argument: '{}' styleStackSize {}->{}",context.token->name,styleStackSizeAtStart,context.styleStack.size());
648 if (context.styleStack.size() > styleStackSizeAtStart) // new styles opened inside command, but not closed
649 {
650 handlePendingStyleCommands(parent,children,context.styleStack.size()-styleStackSizeAtStart);
651 }
652 return tok;
653 }
654 if (!defaultHandleToken(parent,tok,children))
655 {
656 switch (tok.value())
657 {
658 case TokenRetval::TK_HTMLTAG:
659 if (insideLI(parent) && Mappers::htmlTagMapper->map(context.token->name)!=HtmlTagType::UNKNOWN && context.token->endTag)
660 {
661 // ignore </li> as the end of a style command
662 }
663 else
664 {
665 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
666 return tok;
667 }
668 break;
669 default:
670 errorHandleDefaultToken(parent,tok,children,"\\" + saveCmdName + " command");
671 break;
672 }
673 break;
674 }
675 tok = tokenizer.lex();
676 }
677 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
678 return (tok.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST)) ? tok : Token::make_RetVal_OK();
679}
680
681/*! Called when a style change starts. For instance a <b> command is
682 * encountered.
683 */
685 DocStyleChange::Style s,const QCString &tagName,const HtmlAttribList *attribs)
686{
687 AUTO_TRACE("tagName={}",tagName);
688 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),s,tagName,TRUE,
689 context.fileName,tokenizer.getLineNr(),attribs);
690 context.styleStack.push(&children.back());
692}
693
694/*! Called when a style change ends. For instance a </b> command is
695 * encountered.
696 */
698 DocStyleChange::Style s,const QCString &tagName)
699{
700 AUTO_TRACE("tagName={}",tagName);
701 QCString tagNameLower = QCString(tagName).lower();
702
703 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
704 {
705 return std::get<DocStyleChange>(*stack.top());
706 };
707
708 if (context.styleStack.empty() || // no style change
709 topStyleChange(context.styleStack).style()!=s || // wrong style change
710 topStyleChange(context.styleStack).tagName()!=tagNameLower || // wrong style change
711 topStyleChange(context.styleStack).position()!=context.nodeStack.size() // wrong position
712 )
713 {
714 if (context.styleStack.empty())
715 {
716 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{0}> tag without matching <{0}>",tagName);
717 }
718 else if (topStyleChange(context.styleStack).tagName()!=tagNameLower ||
719 topStyleChange(context.styleStack).style()!=s)
720 {
721 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> tag while expecting </{}>",
722 tagName,topStyleChange(context.styleStack).tagName());
723 }
724 else
725 {
726 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> at different nesting level ({}) than expected ({})",
727 tagName,context.nodeStack.size(),topStyleChange(context.styleStack).position());
728 }
729 }
730 else // end the section
731 {
732 children.append<DocStyleChange>(
733 this,parent,context.nodeStack.size(),s,
734 topStyleChange(context.styleStack).tagName(),FALSE);
735 context.styleStack.pop();
736 }
738 {
739 context.inCodeStyle = false;
740 }
741}
742
743/*! Called at the end of a paragraph to close all open style changes
744 * (e.g. a <b> without a </b>). The closed styles are pushed onto a stack
745 * and entered again at the start of a new paragraph.
746 */
747void DocParser::handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &children, size_t numberOfElementsToClose)
748{
749 AUTO_TRACE("context.styleStack.size()={} numberOfElementsToClose={}",context.styleStack.size(),numberOfElementsToClose);
750 if (!context.styleStack.empty())
751 {
752 if (numberOfElementsToClose==0) numberOfElementsToClose = context.styleStack.size(); // 0 is special value for "close all"
753 const DocStyleChange *sc = &std::get<DocStyleChange>(*context.styleStack.top());
754 while (sc && sc->position()>=context.nodeStack.size() && numberOfElementsToClose>0)
755 { // there are unclosed style modifiers in the paragraph
756 AUTO_TRACE_ADD("unclosed style {} at position {}",sc->styleString(),sc->position());
757 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),
758 sc->style(),sc->tagName(),FALSE);
759 context.initialStyleStack.push(context.styleStack.top());
760 context.styleStack.pop();
761 sc = !context.styleStack.empty() ? &std::get<DocStyleChange>(*context.styleStack.top()) : nullptr;
762 numberOfElementsToClose--;
763 }
764 }
765}
766
768{
769 AUTO_TRACE();
770 while (!context.initialStyleStack.empty())
771 {
772 const DocStyleChange &sc = std::get<DocStyleChange>(*context.initialStyleStack.top());
773 handleStyleEnter(parent,children,sc.style(),sc.tagName(),&sc.attribs());
774 context.initialStyleStack.pop();
775 }
776}
777
779 const HtmlAttribList &tagHtmlAttribs)
780{
781 AUTO_TRACE();
782 size_t index=0;
783 Token retval = Token::make_RetVal_OK();
784 for (const auto &opt : tagHtmlAttribs)
785 {
786 if (opt.name=="name" || opt.name=="id") // <a name=label> or <a id=label> tag
787 {
788 if (!opt.value.isEmpty())
789 {
790 children.append<DocAnchor>(this,parent,opt.value,TRUE);
791 break; // stop looking for other tag attribs
792 }
793 else
794 {
795 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <a> tag with name option but without value!");
796 }
797 }
798 else if (opt.name=="href") // <a href=url>..</a> tag
799 {
800 // copy attributes
801 HtmlAttribList attrList = tagHtmlAttribs;
802 // and remove the href attribute
803 attrList.erase(attrList.begin()+index);
804 QCString relPath;
805 if (opt.value.at(0) != '#') relPath = context.relPath;
806 children.append<DocHRef>(this, parent, attrList,
807 opt.value, relPath,
808 convertNameToFile(context.fileName, FALSE, TRUE));
809 context.insideHtmlLink=TRUE;
810 retval = children.get_last<DocHRef>()->parse();
811 context.insideHtmlLink=FALSE;
812 tokenizer.setStatePara();
813 break;
814 }
815 else // unsupported option for tag a
816 {
817 }
818 ++index;
819 }
820 return retval;
821}
822
824{
825 AUTO_TRACE("content.initialStyleStack.size()={}",context.initialStyleStack.size());
826 if (!context.initialStyleStack.empty())
827 {
828 QCString tagName = std::get<DocStyleChange>(*context.initialStyleStack.top()).tagName();
829 QCString fileName = std::get<DocStyleChange>(*context.initialStyleStack.top()).fileName();
830 int lineNr = std::get<DocStyleChange>(*context.initialStyleStack.top()).lineNr();
831 context.initialStyleStack.pop();
833 if (lineNr != -1)
834 {
835 warn_doc_error(context.fileName,tokenizer.getLineNr(),
836 "end of comment block while expecting "
837 "command </{}> (Probable start '{}' at line {})",tagName, fileName, lineNr);
838 }
839 else
840 {
841 warn_doc_error(context.fileName,tokenizer.getLineNr(),
842 "end of comment block while expecting command </{}>",tagName);
843 }
844 }
845}
846
847void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag,bool typeLinkOnly)
848{
849 // helper to check if word w starts with any of the words in AUTOLINK_IGNORE_WORDS
850 auto ignoreWord = [](const QCString &w) -> bool {
851 const auto &list = Config_getList(AUTOLINK_IGNORE_WORDS);
852 return std::find_if(list.begin(), list.end(),
853 [&w](const auto &ignore) { return w.startsWith(ignore); }
854 )!=list.end();
855 };
856 QCString name = linkToText(context.lang,context.token->name,TRUE);
857 AUTO_TRACE("word={}",name);
858 if (!context.autolinkSupport || ignoreAutoLinkFlag || ignoreWord(context.token->name)) // no autolinking -> add as normal word
859 {
860 children.append<DocWord>(this,parent,name);
861 return;
862 }
863
864 // ------- try to turn the word 'name' into a link
865
866 const Definition *compound=nullptr;
867 const MemberDef *member=nullptr;
868 size_t len = context.token->name.length();
869 ClassDef *cd=nullptr;
870 bool ambig = false;
872 auto lang = context.lang;
873 bool inSeeBlock = context.inSeeBlock || context.inCodeStyle;
874 //printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context));
875 if (!context.insideHtmlLink &&
876 (resolveRef(context.context,context.token->name,inSeeBlock,&compound,&member,lang,TRUE,fd,TRUE)
877 || (!context.context.isEmpty() && // also try with global scope
878 resolveRef(QCString(),context.token->name,inSeeBlock,&compound,&member,lang,FALSE,nullptr,TRUE))
879 )
880 )
881 {
882 //printf("ADD %s = %p (linkable?=%d typeLinkOnly=%d)\n",
883 // qPrint(context.token->name),(void*)member,member ? member->isLinkable() : FALSE, typeLinkOnly);
884 if (member && member->isLinkable())
885 {
886 if (!typeLinkOnly || context.token->name.startsWith("#") ||
887 member->isTypedef() || member->isEnumerate() || member->isEnumValue()) // filter on type links
888 {
889 AUTO_TRACE_ADD("resolved reference as member link");
890 if (member->isObjCMethod())
891 {
892 bool localLink = context.memberDef ? member->getClassDef()==context.memberDef->getClassDef() : FALSE;
893 name = member->objCMethodName(localLink,inSeeBlock);
894 }
895 children.append<DocLinkedWord>(
896 this,parent,name,
897 member->getReference(),
898 member->getOutputFileBase(),
899 member->anchor(),
900 member->briefDescriptionAsTooltip());
901 }
902 else // explicit type link, but not a type
903 {
904 AUTO_TRACE_ADD("no link as request is type but member is not a type or explicit link");
905 children.append<DocWord>(this,parent,context.token->name);
906 }
907 }
908 else if (compound->isLinkable()) // compound link
909 {
910 AUTO_TRACE_ADD("resolved reference as compound link");
911 QCString anchor = compound->anchor();
912 if (compound->definitionType()==Definition::TypeFile)
913 {
914 name=context.token->name;
915 }
916 else if (compound->definitionType()==Definition::TypeGroup)
917 {
918 name=toGroupDef(compound)->groupTitle();
919 }
920 children.append<DocLinkedWord>(
921 this,parent,name,
922 compound->getReference(),
923 compound->getOutputFileBase(),
924 anchor,
925 compound->briefDescriptionAsTooltip());
926 }
927 else if (compound->definitionType()==Definition::TypeFile &&
928 (toFileDef(compound))->generateSourceFile()
929 ) // undocumented file that has source code we can link to
930 {
931 AUTO_TRACE_ADD("resolved reference as source link");
932 children.append<DocLinkedWord>(
933 this,parent,context.token->name,
934 compound->getReference(),
935 compound->getSourceFileBase(),
936 "",
937 compound->briefDescriptionAsTooltip());
938 }
939 else // not linkable
940 {
941 AUTO_TRACE_ADD("resolved reference as unlinkable compound={} (linkable={}) member={} (linkable={})",
942 compound ? compound->name() : "<none>", compound ? (int)compound->isLinkable() : -1,
943 member ? member->name() : "<none>", member ? (int)member->isLinkable() : -1);
944 children.append<DocWord>(this,parent,name);
945 }
946 }
947 else if (!context.insideHtmlLink && len>1 && context.token->name.at(len-1)==':')
948 {
949 // special case, where matching Foo: fails to be an Obj-C reference,
950 // but Foo itself might be linkable.
951 context.token->name=context.token->name.left(len-1);
952 handleLinkedWord(parent,children,ignoreAutoLinkFlag);
953 children.append<DocWord>(this,parent,":");
954 }
955 else if (!context.insideHtmlLink && (cd=getClass(context.token->name+"-p")))
956 {
957 // special case 2, where the token name is not a class, but could
958 // be a Obj-C protocol
959 children.append<DocLinkedWord>(
960 this,parent,name,
961 cd->getReference(),
962 cd->getOutputFileBase(),
963 cd->anchor(),
965 }
966 else if (const RequirementIntf *req = RequirementManager::instance().find(name); req!=nullptr) // link to requirement
967 {
968 if (Config_getBool(GENERATE_REQUIREMENTS))
969 {
970 children.append<DocLinkedWord>(
971 this,parent,name,
972 QCString(), // link to local requirements overview also for external references
973 req->getOutputFileBase(),
974 req->id(),
975 req->title()
976 );
977 }
978 else // cannot link to a page that does not exist
979 {
980 children.append<DocWord>(this,parent,name);
981 }
982 }
983 else // normal non-linkable word
984 {
985 AUTO_TRACE_ADD("non-linkable");
986 if (context.token->name.startsWith("#"))
987 {
988 warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '{}' could not be resolved",name);
989 }
990 children.append<DocWord>(this,parent,context.token->name);
991 }
992}
993
995{
996 QCString name = context.token->name; // save token name
997 AUTO_TRACE("name={}",name);
998 QCString name1;
999 int p=0, i=0, ii=0;
1000 while ((i=paramTypes.find('|',p))!=-1)
1001 {
1002 name1 = paramTypes.mid(p,i-p);
1003 ii=name1.find('[');
1004 context.token->name=ii!=-1 ? name1.mid(0,ii) : name1; // take part without []
1005 handleLinkedWord(parent,children);
1006 if (ii!=-1) children.append<DocWord>(this,parent,name1.mid(ii)); // add [] part
1007 p=i+1;
1008 children.append<DocSeparator>(this,parent,"|");
1009 }
1010 name1 = paramTypes.mid(p);
1011 ii=name1.find('[');
1012 context.token->name=ii!=-1 ? name1.mid(0,ii) : name1;
1013 handleLinkedWord(parent,children);
1014 if (ii!=-1) children.append<DocWord>(this,parent,name1.mid(ii));
1015 context.token->name = name; // restore original token name
1016}
1017
1019{
1020 Token tok=tokenizer.lex();
1021 QCString tokenName = context.token->name;
1022 AUTO_TRACE("name={}",tokenName);
1023 if (!tok.is(TokenRetval::TK_WHITESPACE))
1024 {
1025 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", tokenName);
1026 return;
1027 }
1028 tokenizer.setStateInternalRef();
1029 tok=tokenizer.lex(); // get the reference id
1030 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1031 {
1032 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1033 tok.to_string(),tokenName);
1034 return;
1035 }
1036 children.append<DocInternalRef>(this,parent,context.token->name);
1037 children.get_last<DocInternalRef>()->parse();
1038}
1039
1041{
1042 AUTO_TRACE();
1043 Token tok=tokenizer.lex();
1044 if (!tok.is(TokenRetval::TK_WHITESPACE))
1045 {
1046 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1047 context.token->name);
1048 return;
1049 }
1050
1052 tokenizer.setStateAnchor();
1053 tok=tokenizer.lex();
1054 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1055 {
1056 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1057 "argument of command {}",context.token->name);
1058 return;
1059 }
1060 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1061 {
1062 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1063 tok.to_string(),context.token->name);
1064 return;
1065 }
1066 }
1067 children.append<DocAnchor>(this,parent,context.token->name,FALSE);
1068}
1069
1071{
1072 AUTO_TRACE();
1073 // get the argument of the cite command.
1074 Token tok=tokenizer.lex();
1075
1076 CiteInfoOption option;
1077 if (tok.is(TokenRetval::TK_WORD) && context.token->name=="{")
1078 {
1079 tokenizer.setStateOptions();
1080 tokenizer.lex();
1081 StringVector optList=split(context.token->name.str(),",");
1082 for (auto const &opt : optList)
1083 {
1084 if (opt == "number")
1085 {
1086 if (!option.isUnknown())
1087 {
1088 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1089 }
1090 else
1091 {
1092 option = CiteInfoOption::makeNumber();
1093 }
1094 }
1095 else if (opt == "year")
1096 {
1097 if (!option.isUnknown())
1098 {
1099 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1100 }
1101 else
1102 {
1103 option = CiteInfoOption::makeYear();
1104 }
1105 }
1106 else if (opt == "shortauthor")
1107 {
1108 if (!option.isUnknown())
1109 {
1110 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1111 }
1112 else
1113 {
1115 }
1116 }
1117 else if (opt == "nopar")
1118 {
1119 option.setNoPar();
1120 }
1121 else if (opt == "nocite")
1122 {
1123 option.setNoCite();
1124 }
1125 else
1126 {
1127 warn(context.fileName,tokenizer.getLineNr(),"Unknown option specified with \\{}, discarding '{}'", context.token->name, opt);
1128 }
1129 }
1130
1131 if (option.isUnknown()) option.changeToNumber();
1132
1133 tokenizer.setStatePara();
1134 tok=tokenizer.lex();
1135 if (!tok.is(TokenRetval::TK_WHITESPACE))
1136 {
1137 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1138 context.token->name);
1139 return;
1140 }
1141 }
1142 else if (!tok.is(TokenRetval::TK_WHITESPACE))
1143 {
1144 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '\\{}' command",
1145 context.token->name);
1146 return;
1147 }
1148 else
1149 {
1150 option = CiteInfoOption::makeNumber();
1151 }
1152
1154 tokenizer.setStateCite();
1155 tok=tokenizer.lex();
1156 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1157 {
1158 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1159 "argument of command '\\{}'",context.token->name);
1160 return;
1161 }
1162 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1163 {
1164 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '\\{}'",
1165 tok.to_string(),context.token->name);
1166 return;
1167 }
1168 context.token->sectionId = context.token->name;
1169 children.append<DocCite>(this,parent,context.token->name,context.context,option);
1170 }
1171
1172}
1173
1175{
1176 AUTO_TRACE();
1177 Token tok=tokenizer.lex();
1178 if (!tok.is(TokenRetval::TK_WHITESPACE))
1179 {
1180 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", context.token->name);
1181 return;
1182 }
1183 tokenizer.setStatePrefix();
1184 tok=tokenizer.lex();
1185 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1186 {
1187 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1188 "argument of command {}",context.token->name);
1189 return;
1190 }
1191 else if (!tok.is(TokenRetval::TK_WORD))
1192 {
1193 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1194 tok.to_string(),context.token->name);
1195 return;
1196 }
1197 context.prefix = context.token->name;
1198 tokenizer.setStatePara();
1199}
1200
1201/* Helper function that deals with the title, width, and height arguments of various commands.
1202 * @param[in] cmd Command id for which to extract caption and size info.
1203 * @param[in] parent Parent node, owner of the children list passed as
1204 * the third argument.
1205 * @param[in] children The list of child nodes to which the node representing
1206 * the token can be added.
1207 * @param[out] width the extracted width specifier
1208 * @param[out] height the extracted height specifier
1209 */
1211{
1212 AUTO_TRACE();
1213 auto ns = AutoNodeStack(this,parent);
1214
1215 // parse title
1216 tokenizer.setStateTitle();
1217 Token tok = tokenizer.lex();
1218 while (!tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1219 {
1220 if (tok.is(TokenRetval::TK_WORD) && (context.token->name=="width=" || context.token->name=="height="))
1221 {
1222 // special case: no title, but we do have a size indicator
1223 break;
1224 }
1225 else if (tok.is(TokenRetval::TK_HTMLTAG))
1226 {
1227 tokenizer.unputString(context.token->text);
1228 break;
1229 }
1230 if (!defaultHandleToken(parent,tok,children))
1231 {
1232 errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd));
1233 }
1234 tok = tokenizer.lex();
1235 }
1236 // parse size attributes
1237 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1238 {
1239 tok=tokenizer.lex();
1240 }
1241 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) // there are values following the title
1242 {
1243 if (tok.is(TokenRetval::TK_WORD))
1244 {
1245 if (context.token->name=="width=" || context.token->name=="height=")
1246 {
1247 tokenizer.setStateTitleAttrValue();
1248 context.token->name = context.token->name.left(context.token->name.length()-1);
1249 }
1250
1251 if (context.token->name=="width")
1252 {
1253 width = context.token->chars;
1254 }
1255 else if (context.token->name=="height")
1256 {
1257 height = context.token->chars;
1258 }
1259 else // other text after the title -> treat as normal text
1260 {
1261 tokenizer.unputString(context.token->name);
1262 //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '{}' after \\{} command, expected 'width' or 'height'",
1263 // context.token->name, Mappers::cmdMapper->find(cmd));
1264 break;
1265 }
1266 }
1267
1268 tok=tokenizer.lex();
1269 // if we found something we did not expect, push it back to the stream
1270 // so it can still be processed
1271 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
1272 {
1273 tokenizer.unputString(context.token->name);
1274 tokenizer.unputString(tok.is(TokenRetval::TK_COMMAND_AT) ? "@" : "\\");
1275 break;
1276 }
1277 else if (tok.is(TokenRetval::TK_SYMBOL))
1278 {
1279 tokenizer.unputString(context.token->name);
1280 break;
1281 }
1282 else if (tok.is(TokenRetval::TK_HTMLTAG))
1283 {
1284 tokenizer.unputString(context.token->text);
1285 break;
1286 }
1287 }
1288 tokenizer.setStatePara();
1289
1291 AUTO_TRACE_EXIT("width={} height={}",width,height);
1292}
1293
1295{
1296 AUTO_TRACE();
1297 bool inlineImage = false;
1298 QCString anchorStr;
1299
1300 Token tok=tokenizer.lex();
1301 if (!tok.is(TokenRetval::TK_WHITESPACE))
1302 {
1303 if (tok.is(TokenRetval::TK_WORD))
1304 {
1305 if (context.token->name == "{")
1306 {
1307 tokenizer.setStateOptions();
1308 tokenizer.lex();
1309 tokenizer.setStatePara();
1310 StringVector optList=split(context.token->name.str(),",");
1311 for (const auto &opt : optList)
1312 {
1313 if (opt.empty()) continue;
1314 QCString locOpt(opt);
1315 QCString locOptLow;
1316 locOpt = locOpt.stripWhiteSpace();
1317 locOptLow = locOpt.lower();
1318 if (locOptLow == "inline")
1319 {
1320 inlineImage = true;
1321 }
1322 else if (locOptLow.startsWith("anchor:"))
1323 {
1324 if (!anchorStr.isEmpty())
1325 {
1326 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1327 "multiple use of option 'anchor' for 'image' command, ignoring: '{}'",
1328 locOpt.mid(7));
1329 }
1330 else
1331 {
1332 anchorStr = locOpt.mid(7);
1333 }
1334 }
1335 else
1336 {
1337 warn_doc_error(context.fileName,tokenizer.getLineNr(),
1338 "unknown option '{}' for 'image' command specified",
1339 locOpt);
1340 }
1341 }
1342 tok=tokenizer.lex();
1343 if (!tok.is(TokenRetval::TK_WHITESPACE))
1344 {
1345 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1346 return;
1347 }
1348 }
1349 }
1350 else
1351 {
1352 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1353 return;
1354 }
1355 }
1356 tok=tokenizer.lex();
1357 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1358 {
1359 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image",
1360 tok.to_string());
1361 return;
1362 }
1363 tok=tokenizer.lex();
1364 if (!tok.is(TokenRetval::TK_WHITESPACE))
1365 {
1366 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1367 return;
1368 }
1370 QCString imgType = context.token->name.lower();
1371 if (imgType=="html") t=DocImage::Html;
1372 else if (imgType=="latex") t=DocImage::Latex;
1373 else if (imgType=="docbook") t=DocImage::DocBook;
1374 else if (imgType=="rtf") t=DocImage::Rtf;
1375 else if (imgType=="xml") t=DocImage::Xml;
1376 else
1377 {
1378 warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `{}` specified as the first argument of "
1379 "\\image command is not valid", imgType);
1380 return;
1381 }
1382 tokenizer.setStateFile();
1383 tok=tokenizer.lex();
1384 tokenizer.setStatePara();
1385 if (!tok.is(TokenRetval::TK_WORD))
1386 {
1387 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", tok.to_string());
1388 return;
1389 }
1390 if (!anchorStr.isEmpty())
1391 {
1392 children.append<DocAnchor>(this,parent,anchorStr,true);
1393 }
1394 HtmlAttribList attrList;
1395 children.append<DocImage>(this,parent,attrList,
1396 findAndCopyImage(context.token->name,t),t,"",inlineImage);
1397 children.get_last<DocImage>()->parse();
1398}
1399
1400void DocParser::handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const QCString &cmdName)
1401{
1402 AUTO_TRACE("cmdName={}",cmdName);
1403 QCString saveCmdName = cmdName;
1405 Token tok=tokenizer.lex();
1406 if (!tok.is(TokenRetval::TK_WHITESPACE))
1407 {
1408 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1409 cmdChar,qPrint(saveCmdName));
1410 return;
1411 }
1412 tokenizer.setStateRef();
1413 tok=tokenizer.lex(); // get the reference id
1414 if (!tok.is(TokenRetval::TK_WORD))
1415 {
1416 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1417 tok.to_string(),cmdChar,saveCmdName);
1418 return;
1419 }
1420 children.append<DocRef>(this,parent,
1421 context.token->name,
1422 context.context);
1423 children.get_last<DocRef>()->parse(cmdChar,saveCmdName);
1424}
1425
1426void DocParser::handleIFile(char cmdChar,const QCString &cmdName)
1427{
1428 AUTO_TRACE();
1429 Token tok=tokenizer.lex();
1430 if (!tok.is(TokenRetval::TK_WHITESPACE))
1431 {
1432 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1433 cmdChar,cmdName);
1434 return;
1435 }
1436 tokenizer.setStateIFile();
1437 tok=tokenizer.lex();
1438 tokenizer.setStatePara();
1439 if (!tok.is(TokenRetval::TK_WORD))
1440 {
1441 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1442 tok.to_string(),cmdChar,cmdName);
1443 return;
1444 }
1445 context.fileName = context.token->name;
1446}
1447
1448void DocParser::handleILine(char cmdChar,const QCString &cmdName)
1449{
1450 AUTO_TRACE();
1451 tokenizer.setStateILine();
1452 Token tok = tokenizer.lex();
1453 tokenizer.setStatePara();
1454 if (!tok.is(TokenRetval::TK_WORD))
1455 {
1456 warn_doc_error(context.fileName,tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
1457 cmdChar,cmdName);
1458 return;
1459 }
1460}
1461
1462/* Helper function that deals with the most common tokens allowed in
1463 * title like sections.
1464 * @param parent Parent node, owner of the children list passed as
1465 * the third argument.
1466 * @param tok The token to process.
1467 * @param children The list of child nodes to which the node representing
1468 * the token can be added.
1469 * @param handleWord Indicates if word token should be processed
1470 * @retval TRUE The token was handled.
1471 * @retval FALSE The token was not handled.
1472 */
1474{
1475 AUTO_TRACE("token={} handleWord={}",tok.to_string(),handleWord);
1476 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
1477 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
1478 )
1479 {
1480 }
1481reparsetoken:
1482 QCString tokenName = context.token->name;
1483 AUTO_TRACE_ADD("tokenName={}",tokenName);
1484 switch (tok.value())
1485 {
1486 case TokenRetval::TK_COMMAND_AT:
1487 // fall through
1488 case TokenRetval::TK_COMMAND_BS:
1489 switch (Mappers::cmdMapper->map(tokenName))
1490 {
1493 break;
1496 break;
1499 break;
1502 break;
1505 break;
1508 break;
1511 break;
1514 break;
1517 break;
1521 break;
1526 break;
1529 break;
1532 break;
1535 break;
1538 break;
1541 break;
1544 break;
1547 break;
1549 {
1550 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,TRUE);
1551 tok=handleStyleArgument(parent,children,tokenName);
1552 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,FALSE);
1553 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1554 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1555 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1556 {
1557 AUTO_TRACE_ADD("CommandType::CMD_EMPHASIS: reparsing");
1558 goto reparsetoken;
1559 }
1560 }
1561 break;
1563 {
1564 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,TRUE);
1565 tok=handleStyleArgument(parent,children,tokenName);
1566 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,FALSE);
1567 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1568 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1569 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1570 {
1571 AUTO_TRACE_ADD("CommandType::CMD_BOLD: reparsing");
1572 goto reparsetoken;
1573 }
1574 }
1575 break;
1577 {
1578 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,TRUE);
1579 tok=handleStyleArgument(parent,children,tokenName);
1580 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,FALSE);
1581 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1582 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1583 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1584 {
1585 AUTO_TRACE_ADD("CommandType::CMD_CODE: reparsing");
1586 goto reparsetoken;
1587 }
1588 }
1589 break;
1591 {
1592 tokenizer.setStateHtmlOnly();
1593 tok = tokenizer.lex();
1594 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::HtmlOnly,context.isExample,context.exampleName,context.token->name=="block");
1595 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1596 {
1597 warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker");
1598 }
1599 tokenizer.setStatePara();
1600 }
1601 break;
1603 {
1604 tokenizer.setStateManOnly();
1605 tok = tokenizer.lex();
1606 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::ManOnly,context.isExample,context.exampleName);
1607 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1608 {
1609 warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker");
1610 }
1611 tokenizer.setStatePara();
1612 }
1613 break;
1615 {
1616 tokenizer.setStateRtfOnly();
1617 tok = tokenizer.lex();
1618 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::RtfOnly,context.isExample,context.exampleName);
1619 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1620 {
1621 warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker");
1622 }
1623 tokenizer.setStatePara();
1624 }
1625 break;
1627 {
1628 tokenizer.setStateLatexOnly();
1629 tok = tokenizer.lex();
1630 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::LatexOnly,context.isExample,context.exampleName);
1631 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1632 {
1633 warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker");
1634 }
1635 tokenizer.setStatePara();
1636 }
1637 break;
1639 {
1640 tokenizer.setStateXmlOnly();
1641 tok = tokenizer.lex();
1642 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::XmlOnly,context.isExample,context.exampleName);
1643 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1644 {
1645 warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker");
1646 }
1647 tokenizer.setStatePara();
1648 }
1649 break;
1651 {
1652 tokenizer.setStateDbOnly();
1653 tok = tokenizer.lex();
1654 children.append<DocVerbatim>(this,parent,context.context,context.token->verb,DocVerbatim::DocbookOnly,context.isExample,context.exampleName);
1655 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1656 {
1657 warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker");
1658 }
1659 tokenizer.setStatePara();
1660 }
1661 break;
1663 {
1664 children.append<DocFormula>(this,parent,context.token->id);
1665 }
1666 break;
1669 {
1670 handleAnchor(parent,children);
1671 }
1672 break;
1674 {
1675 handleCite(parent,children);
1676 }
1677 break;
1679 {
1680 handlePrefix(parent,children);
1681 }
1682 break;
1684 {
1685 handleInternalRef(parent,children);
1686 tokenizer.setStatePara();
1687 }
1688 break;
1690 {
1691 tokenizer.setStateSetScope();
1692 (void)tokenizer.lex();
1693 context.context = context.token->name;
1694 //printf("Found scope='%s'\n",qPrint(context.context));
1695 tokenizer.setStatePara();
1696 }
1697 break;
1699 handleImage(parent,children);
1700 break;
1702 handleILine(tok.command_to_char(),tokenName);
1703 break;
1705 handleIFile(tok.command_to_char(),tokenName);
1706 break;
1707 default:
1708 return FALSE;
1709 }
1710 break;
1711 case TokenRetval::TK_HTMLTAG:
1712 {
1713 auto handleEnterLeaveStyle = [this,&parent,&children,&tokenName](DocStyleChange::Style style) {
1714 if (!context.token->endTag)
1715 {
1716 handleStyleEnter(parent,children,style,tokenName,&context.token->attribs);
1717 }
1718 else
1719 {
1720 handleStyleLeave(parent,children,style,tokenName);
1721 }
1722 };
1723 switch (Mappers::htmlTagMapper->map(tokenName))
1724 {
1726 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading");
1727 break;
1729 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading");
1730 break;
1732 handleEnterLeaveStyle(DocStyleChange::Span);
1733 break;
1735 handleEnterLeaveStyle(DocStyleChange::Bold);
1736 break;
1738 handleEnterLeaveStyle(DocStyleChange::S);
1739 break;
1741 handleEnterLeaveStyle(DocStyleChange::Strike);
1742 break;
1744 handleEnterLeaveStyle(DocStyleChange::Del);
1745 break;
1747 handleEnterLeaveStyle(DocStyleChange::Underline);
1748 break;
1750 handleEnterLeaveStyle(DocStyleChange::Ins);
1751 break;
1753 case HtmlTagType::XML_C:
1754 handleEnterLeaveStyle(DocStyleChange::Code);
1755 break;
1757 handleEnterLeaveStyle(DocStyleChange::Kbd);
1758 break;
1760 handleEnterLeaveStyle(DocStyleChange::Typewriter);
1761 break;
1763 handleEnterLeaveStyle(DocStyleChange::Italic);
1764 break;
1766 handleEnterLeaveStyle(DocStyleChange::Subscript);
1767 break;
1769 handleEnterLeaveStyle(DocStyleChange::Superscript);
1770 break;
1772 handleEnterLeaveStyle(DocStyleChange::Center);
1773 break;
1775 handleEnterLeaveStyle(DocStyleChange::Small);
1776 break;
1778 handleEnterLeaveStyle(DocStyleChange::Cite);
1779 break;
1781 if (!context.token->endTag)
1782 {
1783 handleImg(parent,children,context.token->attribs);
1784 }
1785 break;
1786 default:
1787 return FALSE;
1788 break;
1789 }
1790 }
1791 break;
1792 case TokenRetval::TK_SYMBOL:
1793 {
1796 {
1797 children.append<DocSymbol>(this,parent,s);
1798 }
1799 else
1800 {
1801 return FALSE;
1802 }
1803 }
1804 break;
1805 case TokenRetval::TK_WHITESPACE:
1806 case TokenRetval::TK_NEWPARA:
1807handlepara:
1808 if (insidePRE(parent) || !children.empty())
1809 {
1810 children.append<DocWhiteSpace>(this,parent,context.token->chars);
1811 }
1812 break;
1813 case TokenRetval::TK_LNKWORD:
1814 if (handleWord)
1815 {
1816 handleLinkedWord(parent,children);
1817 }
1818 else
1819 return FALSE;
1820 break;
1821 case TokenRetval::TK_WORD:
1822 if (handleWord)
1823 {
1824 children.append<DocWord>(this,parent,context.token->name);
1825 }
1826 else
1827 return FALSE;
1828 break;
1829 case TokenRetval::TK_URL:
1830 if (context.insideHtmlLink)
1831 {
1832 children.append<DocWord>(this,parent,context.token->name);
1833 }
1834 else
1835 {
1836 children.append<DocURL>(this,parent,context.token->name,context.token->isEMailAddr);
1837 }
1838 break;
1839 default:
1840 return FALSE;
1841 }
1842 return TRUE;
1843}
1844
1845//---------------------------------------------------------------------------
1846
1848{
1849 AUTO_TRACE();
1850 bool found=FALSE;
1851 size_t index=0;
1852 for (const auto &opt : tagHtmlAttribs)
1853 {
1854 AUTO_TRACE_ADD("option name={} value='{}'",opt.name,opt.value);
1855 if (opt.name=="src" && !opt.value.isEmpty())
1856 {
1857 // copy attributes
1858 HtmlAttribList attrList = tagHtmlAttribs;
1859 // and remove the src attribute
1860 attrList.erase(attrList.begin()+index);
1862 children.append<DocImage>(
1863 this,parent,attrList,
1864 findAndCopyImage(opt.value,t,false),
1865 t,opt.value);
1866 found = TRUE;
1867 }
1868 ++index;
1869 }
1870 if (!found)
1871 {
1872 warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!");
1873 }
1874}
1875
1876//---------------------------------------------------------------------------
1877
1879 const QCString &doc)
1880{
1881 AUTO_TRACE();
1882 Token retval = Token::make_RetVal_OK();
1883
1884 if (doc.isEmpty()) return retval;
1885
1886 tokenizer.init(doc.data(),context.fileName,context.markdownSupport,context.insideHtmlLink);
1887
1888 // first parse any number of paragraphs
1889 bool isFirst=TRUE;
1890 DocPara *lastPar=!children.empty() ? std::get_if<DocPara>(&children.back()): nullptr;
1891 if (lastPar)
1892 { // last child item was a paragraph
1893 isFirst=FALSE;
1894 }
1895 do
1896 {
1897 children.append<DocPara>(this,parent);
1898 DocPara *par = children.get_last<DocPara>();
1899 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1900 retval=par->parse();
1901 if (!par->isEmpty())
1902 {
1903 if (lastPar) lastPar->markLast(FALSE);
1904 lastPar=par;
1905 }
1906 else
1907 {
1908 children.pop_back();
1909 }
1910 } while (retval.is(TokenRetval::TK_NEWPARA));
1911 if (lastPar) lastPar->markLast();
1912
1913 AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
1914 return retval;
1915}
1916
1917//---------------------------------------------------------------------------
1918
1920{
1921 AUTO_TRACE("file={} text={}",file,text);
1922 bool ambig = false;
1923 QCString filePath = findFilePath(file,ambig);
1924 if (!filePath.isEmpty())
1925 {
1926 size_t indent = 0;
1927 text = detab(fileToString(filePath,Config_getBool(FILTER_SOURCE_FILES)),indent);
1928 if (ambig)
1929 {
1930 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '{}' is ambiguous"
1931 "Possible candidates:\n{}",file, showFileDefMatches(Doxygen::exampleNameLinkedMap,file));
1932 }
1933 }
1934 else
1935 {
1936 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '{}' is not found. "
1937 "Check your EXAMPLE_PATH",file);
1938 }
1939}
1940
1941//---------------------------------------------------------------------------
1942
1943static QCString extractCopyDocId(const char *data, size_t &j, size_t len)
1944{
1945 size_t s=j;
1946 int round=0;
1947 bool insideDQuote=FALSE;
1948 bool insideSQuote=FALSE;
1949 bool found=FALSE;
1950 while (j<len && !found)
1951 {
1952 if (!insideSQuote && !insideDQuote)
1953 {
1954 switch (data[j])
1955 {
1956 case '(': round++; break;
1957 case ')': round--; break;
1958 case '"': insideDQuote=TRUE; break;
1959 case '\'': insideSQuote=TRUE; break;
1960 case '\\': // fall through, begin of command
1961 case '@': // fall through, begin of command
1962 case '\t': // fall through
1963 case '\n':
1964 found=(round==0);
1965 break;
1966 case ' ': // allow spaces in cast operator (see issue #11169)
1967 found=(round==0) && (j<8 || !literal_at(data+j-8,"operator"));
1968 break;
1969 }
1970 }
1971 else if (insideSQuote) // look for single quote end
1972 {
1973 if (data[j]=='\'' && (j==0 || data[j]!='\\'))
1974 {
1975 insideSQuote=FALSE;
1976 }
1977 }
1978 else if (insideDQuote) // look for double quote end
1979 {
1980 if (data[j]=='"' && (j==0 || data[j]!='\\'))
1981 {
1982 insideDQuote=FALSE;
1983 }
1984 }
1985 if (!found) j++;
1986 }
1987
1988 // include const and volatile
1989 if (literal_at(data+j," const"))
1990 {
1991 j+=6;
1992 }
1993 else if (literal_at(data+j," volatile"))
1994 {
1995 j+=9;
1996 }
1997
1998 // allow '&' or '&&' or ' &' or ' &&' at the end
1999 size_t k=j;
2000 while (k<len && data[k]==' ') k++;
2001 if (k<len-1 && data[k]=='&' && data[k+1]=='&') j=k+2;
2002 else if (k<len && data[k]=='&' ) j=k+1;
2003
2004 // do not include punctuation added by Definition::_setBriefDescription()
2005 size_t e=j;
2006 if (j>0 && data[j-1]=='.') { e--; }
2007 QCString id(data+s,e-s);
2008 //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
2009 return id;
2010}
2011
2012// macro to check if the input starts with a specific command.
2013// note that data[i] should point to the start of the command (\ or @ character)
2014// and the sizeof(str) returns the size of str including the '\0' terminator;
2015// a fact we abuse to skip over the start of the command character.
2016#define CHECK_FOR_COMMAND(str,action) \
2017 do if ((i+sizeof(str)<len) && literal_at(data+i+1,str)) \
2018 { j=i+sizeof(str); action; } while(0)
2019
2020static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i,size_t len,bool &brief)
2021{
2022 size_t j=0;
2023 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2024 {
2025 CHECK_FOR_COMMAND("copybrief",brief=TRUE); // @copybrief or \copybrief
2026 CHECK_FOR_COMMAND("copydetails",brief=FALSE); // @copydetails or \copydetails
2027 }
2028 return j;
2029}
2030
2031static size_t isVerbatimSection(const char *data,size_t i,size_t len,QCString &endMarker)
2032{
2033 size_t j=0;
2034 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2035 {
2036 CHECK_FOR_COMMAND("dot",endMarker="enddot");
2037 CHECK_FOR_COMMAND("icode",endMarker="endicode");
2038 CHECK_FOR_COMMAND("code",endMarker="endcode");
2039 CHECK_FOR_COMMAND("msc",endMarker="endmsc");
2040 CHECK_FOR_COMMAND("iverbatim",endMarker="endiverbatim");
2041 CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim");
2042 CHECK_FOR_COMMAND("iliteral",endMarker="endiliteral");
2043 CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly");
2044 CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly");
2045 CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly");
2046 CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly");
2047 CHECK_FOR_COMMAND("manonly",endMarker="endmanonly");
2048 CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly");
2049 CHECK_FOR_COMMAND("startuml",endMarker="enduml");
2050 }
2051 //printf("isVerbatimSection(%s)=%d)\n",qPrint(QCString(&data[i]).left(10)),j);
2052 return j;
2053}
2054
2055static size_t skipToEndMarker(const char *data,size_t i,size_t len,const QCString &endMarker)
2056{
2057 while (i<len)
2058 {
2059 if ((data[i]=='@' || data[i]=='\\') && // start of command character
2060 (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped
2061 {
2062 if (i+endMarker.length()+1<=len && qstrncmp(data+i+1,endMarker.data(),endMarker.length())==0)
2063 {
2064 return i+endMarker.length()+1;
2065 }
2066 }
2067 i++;
2068 }
2069 // oops no endmarker found...
2070 return i<len ? i+1 : len;
2071}
2072
2073
2074QCString DocParser::processCopyDoc(const char *data,size_t &len)
2075{
2076 AUTO_TRACE("data={} len={}",Trace::trunc(data),len);
2077 QCString result;
2078 result.reserve(len+32);
2079 size_t i=0;
2080 int lineNr = tokenizer.getLineNr();
2081 while (i<len)
2082 {
2083 char c = data[i];
2084 if (c=='@' || c=='\\') // look for a command
2085 {
2086 bool isBrief=TRUE;
2087 size_t j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
2088 if (j>0)
2089 {
2090 // skip whitespace
2091 while (j<len && (data[j]==' ' || data[j]=='\t')) j++;
2092 // extract the argument
2093 QCString id = extractCopyDocId(data,j,len);
2094 const Definition *def = nullptr;
2095 QCString doc,brief;
2096 //printf("resolving docs='%s'\n",qPrint(id));
2097 bool found = findDocsForMemberOrCompound(id,&doc,&brief,&def);
2098 if (found && def->isReference())
2099 {
2100 warn_doc_error(context.fileName,tokenizer.getLineNr(),
2101 "@copy{} or @copydoc target '{}' found but is from a tag file, skipped",
2102 isBrief?"brief":"details", id);
2103 }
2104 else if (found)
2105 {
2106 //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,qPrint(brief),qPrint(doc),isBrief);
2107 auto it = std::find(context.copyStack.begin(),context.copyStack.end(),def);
2108 if (it==context.copyStack.end()) // definition not parsed earlier
2109 {
2110 QCString orgFileName = context.fileName;
2111 context.copyStack.push_back(def);
2112 auto addDocs = [&](const QCString &file_,int line_,const QCString &doc_)
2113 {
2114 result+=" \\ifile \""+file_+"\" ";
2115 result+="\\iline "+QCString().setNum(line_)+" \\ilinebr ";
2116 size_t len_ = doc_.length();
2117 result+=processCopyDoc(doc_.data(),len_);
2118 };
2119 if (isBrief)
2120 {
2121 addDocs(def->briefFile(),def->briefLine(),brief);
2122 }
2123 else
2124 {
2125 addDocs(def->docFile(),def->docLine(),doc);
2127 {
2128 const MemberDef *md = toMemberDef(def);
2129 const ArgumentList &docArgList = md->templateMaster() ?
2130 md->templateMaster()->argumentList() :
2131 md->argumentList();
2132 result+=inlineArgListToDoc(docArgList);
2133 }
2134 }
2135 context.copyStack.pop_back();
2136 result+=" \\ilinebr \\ifile \""+context.fileName+"\" ";
2137 result+="\\iline "+QCString().setNum(lineNr)+" ";
2138 }
2139 else
2140 {
2141 warn_doc_error(context.fileName,tokenizer.getLineNr(),
2142 "Found recursive @copy{} or @copydoc relation for argument '{}'.",
2143 isBrief?"brief":"details",id);
2144 }
2145 }
2146 else
2147 {
2148 warn_doc_error(context.fileName,tokenizer.getLineNr(),
2149 "@copy{} or @copydoc target '{}' not found", isBrief?"brief":"details",id);
2150 }
2151 // skip over command
2152 i=j;
2153 }
2154 else
2155 {
2156 QCString endMarker;
2157 size_t k = isVerbatimSection(data,i,len,endMarker);
2158 if (k>0)
2159 {
2160 size_t orgPos = i;
2161 i=skipToEndMarker(data,k,len,endMarker);
2162 result+=QCString(data+orgPos,i-orgPos);
2163 // TODO: adjust lineNr
2164 }
2165 else
2166 {
2167 result+=c;
2168 i++;
2169 }
2170 }
2171 }
2172 else // not a command, just copy
2173 {
2174 result+=c;
2175 i++;
2176 lineNr += (c=='\n') ? 1 : 0;
2177 }
2178 }
2179 len = result.length();
2180 AUTO_TRACE_EXIT("result={}",Trace::trunc(result));
2181 return result;
2182}
2183
2184
2185//---------------------------------------------------------------------------
2186
2188 const QCString &fileName,
2189 int startLine,
2190 const Definition *ctx,
2191 const MemberDef *md,
2192 const QCString &input,
2193 const DocOptions &options)
2194{
2195 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2196 assert(parser!=nullptr);
2197 if (parser==nullptr) return nullptr;
2198 //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?qPrint(ctx->name()):"<none>",
2199 // md?qPrint(md->name()):"<none>",
2200 // qPrint(input));
2201 //printf("========== validating %s at line %d\n",qPrint(fileName),startLine);
2202 //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",qPrint(input));
2203
2204 // set initial token
2205 parser->context.token = parser->tokenizer.resetToken();
2206
2207 if (ctx && ctx!=Doxygen::globalScope &&
2210 )
2211 )
2212 {
2214 }
2215 else if (ctx && ctx->definitionType()==Definition::TypePage)
2216 {
2217 const Definition *scope = (toPageDef(ctx))->getPageScope();
2218 if (scope && scope!=Doxygen::globalScope)
2219 {
2220 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2221 }
2222 }
2223 else if (ctx && ctx->definitionType()==Definition::TypeGroup)
2224 {
2225 const Definition *scope = (toGroupDef(ctx))->getGroupScope();
2226 if (scope && scope!=Doxygen::globalScope)
2227 {
2228 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2229 }
2230 }
2231 else
2232 {
2233 parser->context.context = "";
2234 }
2235 parser->context.scope = ctx;
2236 parser->context.lang = getLanguageFromFileName(fileName);
2237
2238 if (options.indexWords() && Doxygen::searchIndex.enabled())
2239 {
2240 if (md)
2241 {
2242 parser->context.searchUrl=md->getOutputFileBase();
2243 Doxygen::searchIndex.setCurrentDoc(md,md->anchor(),false);
2244 }
2245 else if (ctx)
2246 {
2247 parser->context.searchUrl=ctx->getOutputFileBase();
2248 Doxygen::searchIndex.setCurrentDoc(ctx,ctx->anchor(),false);
2249 }
2250 }
2251 else
2252 {
2253 parser->context.searchUrl="";
2254 }
2255
2256 parser->context.fileName = fileName;
2257 parser->context.relPath = (!options.linkFromIndex() && ctx) ?
2259 QCString("");
2260 //printf("ctx->name=%s relPath=%s\n",qPrint(ctx->name()),qPrint(parser->context.relPath));
2261 parser->context.memberDef = md;
2262 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2263 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2264 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2265 parser->context.inSeeBlock = FALSE;
2266 parser->context.inCodeStyle = FALSE;
2267 parser->context.xmlComment = FALSE;
2268 parser->context.insideHtmlLink = FALSE;
2269 parser->context.includeFileText = "";
2270 parser->context.includeFileOffset = 0;
2271 parser->context.includeFileLength = 0;
2272 parser->context.isExample = options.isExample();
2273 parser->context.exampleName = options.exampleName();
2274 parser->context.hasParamCommand = FALSE;
2275 parser->context.hasReturnCommand = FALSE;
2276 parser->context.retvalsFound.clear();
2277 parser->context.paramsFound.clear();
2278 parser->context.markdownSupport = options.markdownSupport();
2279 parser->context.autolinkSupport = options.autolinkSupport();
2280 if (md)
2281 {
2282 const ArgumentList &al=md->isDocsForDefinition() ? md->argumentList() : md->declArgumentList();
2283 parser->context.numParameters = static_cast<int>(al.size());
2284 }
2285 else
2286 {
2287 parser->context.numParameters = 0;
2288 }
2289 parser->context.paramPosition = 1;
2290
2291 //printf("Starting comment block at %s:%d\n",qPrint(parser->context.fileName),startLine);
2292 parser->tokenizer.setFileName(fileName);
2293 parser->tokenizer.setLineNr(startLine);
2294 size_t ioLen = input.length();
2295 QCString inpStr = parser->processCopyDoc(input.data(),ioLen);
2296 if (inpStr.isEmpty() || inpStr.at(inpStr.length()-1)!='\n')
2297 {
2298 inpStr+='\n';
2299 }
2300 //printf("processCopyDoc(in='%s' out='%s')\n",qPrint(input),qPrint(inpStr));
2301 parser->tokenizer.init(inpStr.data(),parser->context.fileName,
2303
2304 // build abstract syntax tree
2305 auto ast = std::make_unique<DocNodeAST>(DocRoot(parser,md!=nullptr,options.singleLine()));
2306 std::get<DocRoot>(ast->root).parse();
2307
2309 {
2310 // pretty print the result
2311 std::visit(PrintDocVisitor{},ast->root);
2312 }
2313
2314 if (md && md->isFunction())
2315 {
2317 }
2319
2320 // reset token
2321 parser->tokenizer.resetToken();
2322
2323 //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?qPrint(ctx->name()):"<none>",
2324 // md?qPrint(md->name()):"<none>");
2325
2326 return ast;
2327}
2328
2329IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf,const QCString &fileName,int lineNr,const QCString &input)
2330{
2331 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2332 assert(parser!=nullptr);
2333 if (parser==nullptr) return nullptr;
2334
2335 // set initial token
2336 parser->context.token = parser->tokenizer.resetToken();
2337
2338 //printf("------------ input ---------\n%s\n"
2339 // "------------ end input -----\n",input);
2340 parser->context.context = "";
2341 parser->context.fileName = fileName;
2342 parser->context.relPath = "";
2343 parser->context.memberDef = nullptr;
2344 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2345 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2346 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2347 parser->context.inSeeBlock = FALSE;
2348 parser->context.inCodeStyle = FALSE;
2349 parser->context.xmlComment = FALSE;
2350 parser->context.insideHtmlLink = FALSE;
2351 parser->context.includeFileText = "";
2352 parser->context.includeFileOffset = 0;
2353 parser->context.includeFileLength = 0;
2354 parser->context.isExample = FALSE;
2355 parser->context.exampleName = "";
2356 parser->context.hasParamCommand = FALSE;
2357 parser->context.hasReturnCommand = FALSE;
2358 parser->context.retvalsFound.clear();
2359 parser->context.paramsFound.clear();
2360 parser->context.searchUrl="";
2361 parser->context.lang = SrcLangExt::Unknown;
2362 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2363 parser->context.autolinkSupport = false;
2364
2365 auto ast = std::make_unique<DocNodeAST>(DocTitle(parser,nullptr));
2366
2367 if (!input.isEmpty())
2368 {
2369 // build abstract syntax tree from title string
2370 std::get<DocTitle>(ast->root).parseFromString(nullptr,input);
2371
2373 {
2374 // pretty print the result
2375 std::visit(PrintDocVisitor{},ast->root);
2376 }
2377 }
2378
2379 return ast;
2380}
2381
2383{
2384 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2385 assert(parser!=nullptr);
2386 if (parser==nullptr) return nullptr;
2387
2388 // set initial token
2389 parser->context.token = parser->tokenizer.resetToken();
2390
2391 //printf("------------ input ---------\n%s\n"
2392 // "------------ end input -----\n",input);
2393 //parser->context.token = new TokenInfo;
2394 parser->context.context = "";
2395 parser->context.fileName = "<parseText>";
2396 parser->context.relPath = "";
2397 parser->context.memberDef = nullptr;
2398 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2399 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2400 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2401 parser->context.inSeeBlock = FALSE;
2402 parser->context.inCodeStyle = FALSE;
2403 parser->context.xmlComment = FALSE;
2404 parser->context.insideHtmlLink = FALSE;
2405 parser->context.includeFileText = "";
2406 parser->context.includeFileOffset = 0;
2407 parser->context.includeFileLength = 0;
2408 parser->context.isExample = FALSE;
2409 parser->context.exampleName = "";
2410 parser->context.hasParamCommand = FALSE;
2411 parser->context.hasReturnCommand = FALSE;
2412 parser->context.retvalsFound.clear();
2413 parser->context.paramsFound.clear();
2414 parser->context.searchUrl="";
2415 parser->context.lang = SrcLangExt::Unknown;
2416 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2417 parser->context.autolinkSupport = FALSE;
2418
2419
2420 auto ast = std::make_unique<DocNodeAST>(DocText(parser));
2421
2422 if (!input.isEmpty())
2423 {
2424 parser->tokenizer.setLineNr(1);
2425 parser->tokenizer.init(input.data(),parser->context.fileName,
2427
2428 // build abstract syntax tree
2429 std::get<DocText>(ast->root).parse();
2430
2432 {
2433 // pretty print the result
2434 std::visit(PrintDocVisitor{},ast->root);
2435 }
2436 }
2437
2438 return ast;
2439}
2440
2441IDocNodeASTPtr createRef(IDocParser &parserIntf,const QCString &target,const QCString &context, const QCString &srcFile, int srcLine )
2442{
2443 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2444 assert(parser!=nullptr);
2445 if (parser==nullptr) return nullptr;
2446 if (!srcFile.isEmpty())
2447 {
2448 parser->context.fileName = srcFile;
2449 parser->tokenizer.setFileName(srcFile);
2450 parser->tokenizer.setLineNr(srcLine);
2451 }
2452 return std::make_unique<DocNodeAST>(DocRef(parser,nullptr,target,context));
2453}
2454
2455void docFindSections(const QCString &input,
2456 const Definition *d,
2457 const QCString &fileName)
2458{
2459 DocParser parser;
2460 parser.tokenizer.findSections(input,d,fileName);
2461}
2462
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
constexpr void setNoCite() noexcept
Definition cite.h:35
static constexpr CiteInfoOption makeNumber()
Definition cite.h:29
constexpr void changeToNumber() noexcept
Definition cite.h:33
constexpr void setNoPar() noexcept
Definition cite.h:34
constexpr bool isUnknown() const noexcept
Definition cite.h:37
static constexpr CiteInfoOption makeYear()
Definition cite.h:31
static constexpr CiteInfoOption makeShortAuthor()
Definition cite.h:30
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:133
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 a citation of some bibliographic reference.
Definition docnode.h:245
Node representing an item of a cross-referenced list.
Definition docnode.h:529
Node representing a Hypertext reference.
Definition docnode.h:832
Node representing an image.
Definition docnode.h:642
@ DocBook
Definition docnode.h:644
Node representing an internal reference to some item.
Definition docnode.h:816
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:1089
bool isLast() const
Definition docnode.h:1097
void markLast(bool v=TRUE)
Definition docnode.h:1095
bool isFirst() const
Definition docnode.h:1096
std::stack< DocParserContext > contextStack
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children, size_t numberOfElementsToClose=0)
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)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=TRUE)
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 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 handleCite(DocNodeVariant *parent, DocNodeList &children)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const QCString &cmdName)
void handleIFile(char cmdChar, const QCString &cmdName)
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=FALSE, bool typeLinkOnly=false)
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:787
Root node of documentation tree.
Definition docnode.h:1318
Node representing a separator.
Definition docnode.h:365
Node representing a style change.
Definition docnode.h:268
const char * styleString() const
Definition docnode.cpp:127
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:155
Root node of a text fragment.
Definition docnode.h:1309
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:115
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:104
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:95
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:105
static IndexList * indexList
Definition doxygen.h:132
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:102
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:114
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 bool isTypedef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isFunction() const =0
virtual bool isDocsForDefinition() const =0
virtual QCString objCMethodName(bool localLink, bool showStatic) const =0
virtual const MemberDef * templateMaster() const =0
virtual bool isEnumerate() const =0
virtual void detectUndocumentedParams(bool hasParamCommand, bool hasReturnCommand) const =0
virtual const ArgumentList & declArgumentList() const =0
virtual bool isEnumValue() 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:103
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:426
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:170
bool startsWith(const char *s) const
Definition qcstring.h:511
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:245
QCString lower() const
Definition qcstring.h:253
bool endsWith(const char *s) const
Definition qcstring.h:528
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:597
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:167
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:264
const std::string & str() const
Definition qcstring.h:556
QCString & setNum(short n)
Definition qcstring.h:463
QCString right(size_t len) const
Definition qcstring.h:238
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:173
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:189
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:150
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:176
std::string_view view() const
Definition qcstring.h:178
QCString left(size_t len) const
Definition qcstring.h:233
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:49
#define AUTO_TRACE(...)
Definition docnode.cpp:48
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
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, DocMermaidFile > 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:1335
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 warn(file, line, fmt,...)
Definition message.h:97
#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:691
#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:1404
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1415
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:90
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:82
const Definition * scope
Definition docparser_p.h:61
QCString includeFileText
Definition docparser_p.h:88
TokenInfo * token
Definition docparser_p.h:95
DocStyleChangeStack initialStyleStack
Definition docparser_p.h:69
SrcLangExt lang
Definition docparser_p.h:85
QCString searchUrl
Definition docparser_p.h:83
size_t includeFileOffset
Definition docparser_p.h:89
const MemberDef * memberDef
Definition docparser_p.h:80
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:2996
QCString linkToText(SrcLangExt lang, const QCString &link, bool isFileName)
Definition util.cpp:2708
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5231
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:2452
QCString relativePathToRoot(const QCString &name)
Definition util.cpp:3600
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3039
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:3525
QCString detab(const QCString &s, size_t &refIndent)
Definition util.cpp:6738
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:5915
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:2308
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6636
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:5875
QCString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:1204
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2904
A bunch of utility functions.