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 //DString indent;
63 //indent.fill(' ',contextStack.size()*2+2);
64 //printf("%sdocParserPushContext() count=%zu\n",qPrint(indent),context.nodeStack.size());
65
67 contextStack.emplace();
68 auto &ctx = contextStack.top();
69 ctx = context;
71 ctx.lineNo = tokenizer.getLineNr();
73}
74
76{
77 auto &ctx = contextStack.top();
78 context = ctx;
79 tokenizer.setFileName(ctx.fileName);
80 tokenizer.setLineNr(ctx.lineNo);
81 contextStack.pop();
84
85 //DString 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 */
96DString DocParser::findAndCopyImage(const DString &fileName, DocImage::Type type, bool doWarn)
97{
98 DString 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 {
107 "image file name '{}' is ambiguous.\n"
108 "Possible candidates:\n{}", fileName,showFileDefMatches(Doxygen::imageNameLinkedMap,fileName));
109 }
110
111 DString inputFile = fd->absFilePath();
112 FileInfo infi(inputFile.str());
113 if (infi.exists())
114 {
115 result = fileName;
116 if (size_t i = result.rfind('/') ; i!=DString::npos || (i=result.rfind('\\'))!=DString::npos)
117 {
118 result = result.mid(i+1);
119 }
120 //printf("fileName=%s result=%s\n",fileName,qPrint(result));
121 DString outputDir;
122 switch(type)
123 {
124 case DocImage::Html:
125 if (!Config_getBool(GENERATE_HTML)) return result;
126 outputDir = Config_getString(HTML_OUTPUT);
127 break;
128 case DocImage::Latex:
129 if (!Config_getBool(GENERATE_LATEX)) return result;
130 outputDir = Config_getString(LATEX_OUTPUT);
131 break;
133 if (!Config_getBool(GENERATE_DOCBOOK)) return result;
134 outputDir = Config_getString(DOCBOOK_OUTPUT);
135 break;
136 case DocImage::Rtf:
137 if (!Config_getBool(GENERATE_RTF)) return result;
138 outputDir = Config_getString(RTF_OUTPUT);
139 break;
140 case DocImage::Xml:
141 if (!Config_getBool(GENERATE_XML)) return result;
142 outputDir = Config_getString(XML_OUTPUT);
143 break;
144 }
145 DString outputFile = outputDir+"/"+result;
146 FileInfo outfi(outputFile.str());
147 if (outfi.isSymLink())
148 {
149 Dir().remove(outputFile.str());
151 "destination of image {} is a symlink, replacing with image",
152 outputFile);
153 }
154 if (outputFile!=inputFile) // prevent copying to ourself
155 {
156 if (copyFile(inputFile,outputFile) && type==DocImage::Html)
157 {
159 }
160 }
161 }
162 else
163 {
165 "could not open image {}",fileName);
166 }
167
168 if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) &&
169 fd->name().endsWith(".eps")
170 )
171 { // we have an .eps image in pdflatex mode => convert it to a pdf.
172 DString outputDir = Config_getString(LATEX_OUTPUT);
173 DString baseName = fd->name().left(fd->name().length()-4);
174 DString epstopdfArgs(4096, DString::ExplicitSize);
175 epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"",
176 qPrint(outputDir), qPrint(baseName),
177 qPrint(outputDir), qPrint(baseName));
178 if (Portable::system("epstopdf",epstopdfArgs)!=0)
179 {
180 err("Problems running epstopdf. Check your TeX installation!\n");
181 }
182 else
183 {
184 Dir().remove(outputDir.str()+"/"+baseName.str()+".eps");
185 }
186 return baseName;
187 }
188 }
189 else
190 {
191 result=fileName;
192 if (!result.startsWith("http:") && !result.startsWith("https:") && doWarn)
193 {
195 "image file {} is not found in IMAGE_PATH: "
196 "assuming external image.",fileName
197 );
198 }
199 }
200 return result;
201}
202
203/*! Collects the parameters found with \@param command
204 * in a list context.paramsFound. If
205 * the parameter is not an actual parameter of the current
206 * member context.memberDef, then a warning is raised (unless warnings
207 * are disabled altogether).
208 */
210{
211 if (!(Config_getBool(WARN_IF_DOC_ERROR) || Config_getBool(WARN_IF_INCOMPLETE_DOC))) return;
212 if (context.memberDef==nullptr) return; // not a member
213 std::string name = context.token->name.str();
218 context.numParameters = static_cast<int>(al.size());
219 //printf("isDocsForDefinition()=%d\n",context.memberDef->isDocsForDefinition());
220 if (al.empty()) return; // no argument list
221
222 static const reg::Ex re(R"(\$?\w+\.*)");
223 static const reg::Ex re_digits(R"(\d+)");
224 reg::Iterator it(name,re);
226 for (; it!=end ; ++it)
227 {
228 const auto &match = *it;
229 DString aName=match.str();
230 int number = reg::match(aName.view(),re_digits) ? std::atoi(aName.data()) : -1;
231 if (lang==SrcLangExt::Fortran) aName=aName.lower();
232 //printf("aName='%s'\n",qPrint(aName));
233 bool found=false;
234 int position=1;
235 for (const Argument &a : al)
236 {
237 DString argName = context.memberDef->isDefine() ? a.type : a.name;
238 if (lang==SrcLangExt::Fortran) argName=argName.lower();
239 argName=argName.stripWhiteSpace();
240 //printf("argName='%s' aName=%s\n",qPrint(argName),qPrint(aName));
241 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
242 bool sameName = aName==argName;
243 bool samePosition = position==number;
244 if (samePosition || sameName) // @param <number> or @param name
245 {
246 if (!sameName) // replace positional argument with real name or -
247 {
248 context.token->name = argName.empty() ? "-" : argName;
249 }
250 context.paramsFound.insert(aName.str());
251 found=true;
252 break;
253 }
254 else if (aName==".") // replace . by - in the output
255 {
256 context.token->name = "-";
257 }
258 position++;
259 }
260 if (!found)
261 {
262 //printf("member type=%d\n",context.memberDef->memberType());
264 if (!scope.empty()) scope+="::"; else scope="";
265 DString inheritedFrom = "";
266 DString docFile = context.memberDef->docFile();
267 int docLine = context.memberDef->docLine();
268 const MemberDef *inheritedMd = context.memberDef->inheritsDocsFrom();
269 if (inheritedMd) // documentation was inherited
270 {
271 inheritedFrom.sprintf(" inherited from member %s at line "
272 "%d in file %s",qPrint(inheritedMd->name()),
273 inheritedMd->docLine(),qPrint(inheritedMd->docFile()));
274 docFile = context.memberDef->getDefFileName();
275 docLine = context.memberDef->getDefLine();
276 }
277 DString alStr = argListToString(al);
278 if (number==0)
279 {
280 warn_doc_error(docFile,docLine,
281 "positional argument with value '0' of command @param "
282 "is invalid, first parameter has index '1' for {}{}{}{}",
283 scope, context.memberDef->name(),
284 alStr, inheritedFrom);
285 context.token->name = "-";
286 }
287 else if (number>0)
288 {
289 warn_doc_error(docFile,docLine,
290 "positional argument '{}' of command @param "
291 "is larger than the number of parameters ({}) for {}{}{}{}",
292 number, al.size(), scope, context.memberDef->name(),
293 alStr, inheritedFrom);
294 context.token->name = "-";
295 }
296 else
297 {
298 warn_doc_error(docFile,docLine,
299 "argument '{}' of command @param "
300 "is not found in the argument list of {}{}{}{}",
301 aName, scope, context.memberDef->name(),
302 alStr, inheritedFrom);
303 }
304 }
305 }
306}
307/*! Collects the return values found with \@retval command
308 * in a global list g_parserContext.retvalsFound.
309 */
311{
312 DString name = context.token->name;
313 if (!Config_getBool(WARN_IF_DOC_ERROR)) return;
314 if (context.memberDef==nullptr || name.empty()) return; // not a member or no valid name
315 if (context.retvalsFound.count(name.str())==1) // only report the first double entry
316 {
319 "return value '{}' of {} has multiple documentation sections",
321 }
322 context.retvalsFound.insert(name.str());
323}
324
325/*! Checks if the parameters that have been specified using \@param are
326 * indeed all parameters and that a parameter does not have multiple
327 * \@param blocks.
328 * Must be called after checkArgumentName() has been called for each
329 * argument.
330 */
332{
334 {
339 if (!al.empty())
340 {
341 ArgumentList undocParams;
342 int position = 1;
343 for (const Argument &a: al)
344 {
345 DString argName = context.memberDef->isDefine() ? a.type : a.name;
346 if (lang==SrcLangExt::Fortran) argName = argName.lower();
347 argName=argName.stripWhiteSpace();
348 DString aName = argName;
349 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
350 if (lang==SrcLangExt::Python && (argName=="self" || argName=="cls"))
351 {
352 // allow undocumented self / cls parameter for Python
353 }
354 else if (lang==SrcLangExt::Cpp && (a.type=="this" || a.type.startsWith("this ")))
355 {
356 // allow undocumented this (for C++23 deducing this), see issue #11123
357 }
358 else if (!argName.empty())
359 {
360 size_t count_named = context.paramsFound.count(argName.str());
361 size_t count_positional = context.paramsFound.count(std::to_string(position));
362 if (count_named==0 && count_positional==0 && a.docs.empty())
363 {
364 undocParams.push_back(a);
365 }
366 else if (count_named==1 && count_positional==1 && Config_getBool(WARN_IF_DOC_ERROR))
367 {
370 "argument {} from the argument list of {} has both named and positional @param documentation sections",
372 }
373 else if (count_named+count_positional>1 && Config_getBool(WARN_IF_DOC_ERROR))
374 {
377 "argument {} from the argument list of {} has multiple @param documentation sections",
379 }
380 }
381 position++;
382 }
383 if (!undocParams.empty() && Config_getBool(WARN_IF_INCOMPLETE_DOC))
384 {
385 bool first=true;
386 DString errMsg = "The following parameter";
387 if (undocParams.size()>1) errMsg+="s";
388 errMsg+=DString(" of ")+
390 argListToString(al) +
391 (undocParams.size()>1 ? " are" : " is") + " not documented:\n";
392 for (const Argument &a : undocParams)
393 {
394 DString argName = context.memberDef->isDefine() ? a.type : a.name;
395 if (lang==SrcLangExt::Fortran) argName = argName.lower();
396 argName=argName.stripWhiteSpace();
397 if (!first) errMsg+="\n";
398 first=false;
399 errMsg+=" parameter '"+argName+"'";
400 }
402 }
403
404 if (Config_getBool(WARN_IF_DOC_ERROR) && context.paramPosition-1 > context.numParameters)
405 {
408 "too many @param commands for function {}. Found {} while function has {} parameter{}",
410 }
411 }
412 else
413 {
414 if (context.paramsFound.empty() && Config_getBool(WARN_IF_DOC_ERROR))
415 {
418 "{} has @param documentation sections but no arguments",
420 }
421 }
422 }
423}
424
425
426//---------------------------------------------------------------------------
427
428//---------------------------------------------------------------------------
429
430//---------------------------------------------------------------------------
431/*! Looks for a documentation block with name commandName in the current
432 * context (g_parserContext.context). The resulting documentation string is
433 * put in pDoc, the definition in which the documentation was found is
434 * put in pDef.
435 * @retval true if name was found.
436 * @retval false if name was not found.
437 */
439 DString *pDoc,
440 DString *pBrief,
441 const Definition **pDef)
442{
443 AUTO_TRACE("commandName={}",commandName);
444 *pDoc="";
445 *pBrief="";
446 *pDef=nullptr;
447 DString cmdArg=commandName;
448 if (cmdArg.empty())
449 {
450 AUTO_TRACE_EXIT("empty");
451 return false;
452 }
453
454 const FileDef *fd=nullptr;
455 const GroupDef *gd=nullptr;
456 const PageDef *pd=nullptr;
457 gd = Doxygen::groupLinkedMap->find(cmdArg);
458 if (gd) // group
459 {
460 *pDoc=gd->documentation();
461 *pBrief=gd->briefDescription();
462 *pDef=gd;
463 AUTO_TRACE_EXIT("group");
464 return true;
465 }
466 pd = Doxygen::pageLinkedMap->find(cmdArg);
467 if (pd) // page
468 {
469 *pDoc=pd->documentation();
470 *pBrief=pd->briefDescription();
471 *pDef=pd;
472 AUTO_TRACE_EXIT("page");
473 return true;
474 }
475 bool ambig = false;
476 fd = findFileDef(Doxygen::inputNameLinkedMap,cmdArg,ambig);
477 if (fd && !ambig) // file
478 {
479 *pDoc=fd->documentation();
480 *pBrief=fd->briefDescription();
481 *pDef=fd;
482 AUTO_TRACE_EXIT("file");
483 return true;
484 }
485
486 // for symbols we need to normalize the separator, so A#B, or A\B, or A.B becomes A::B
487 cmdArg = substitute(cmdArg,"#","::");
488 cmdArg = substitute(cmdArg,"\\","::");
489 bool extractAnonNs = Config_getBool(EXTRACT_ANON_NSPACES);
490 if (extractAnonNs &&
491 cmdArg.startsWith("anonymous_namespace{")
492 )
493 {
494 size_t rightBracePos = cmdArg.find("}", dstrlen("anonymous_namespace{"));
495 DString leftPart = cmdArg.left(rightBracePos + 1);
496 DString rightPart = cmdArg.mid(rightBracePos + 1);
497 rightPart = substitute(rightPart, ".", "::");
498 cmdArg = leftPart + rightPart;
499 }
500 else
501 {
502 cmdArg = substitute(cmdArg,".","::");
503 }
504
505 int l=static_cast<int>(cmdArg.length());
506
507 size_t funcStart=cmdArg.find('(');
508 if (funcStart==DString::npos)
509 {
510 funcStart=l;
511 }
512 else
513 {
514 // Check for the case of operator() and the like.
515 // beware of scenarios like operator()((foo)bar)
516 size_t secondParen = cmdArg.find('(', funcStart+1);
517 size_t leftParen = cmdArg.find(')', funcStart+1);
518 if (leftParen!=DString::npos && secondParen!=DString::npos)
519 {
520 if (leftParen<secondParen)
521 {
522 funcStart=secondParen;
523 }
524 }
525 }
526
527 DString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
528 DString args=cmdArg.right(l-funcStart);
529 // try if the link is to a member
530 GetDefInput input(
531 context.context.find('.')==DString::npos ? context.context : DString(), // find('.') is a hack to detect files
532 name,
533 args);
534 input.checkCV=true;
535 GetDefResult result = getDefs(input);
536 //printf("found=%d context=%s name=%s\n",result.found,qPrint(context.context),qPrint(name));
537 if (result.found && result.md)
538 {
539 *pDoc=result.md->documentation();
540 *pBrief=result.md->briefDescription();
541 *pDef=result.md;
542 AUTO_TRACE_EXIT("member");
543 return true;
544 }
545
546 int scopeOffset=static_cast<int>(context.context.length());
547 do // for each scope
548 {
549 DString fullName=cmdArg;
550 if (scopeOffset>0)
551 {
552 fullName.prepend(context.context.left(scopeOffset)+"::");
553 }
554 //printf("Trying fullName='%s'\n",qPrint(fullName));
555
556 // try class, namespace, group, page, file reference
557 const ClassDef *cd = Doxygen::classLinkedMap->find(fullName);
558 if (cd) // class
559 {
560 *pDoc=cd->documentation();
561 *pBrief=cd->briefDescription();
562 *pDef=cd;
563 AUTO_TRACE_EXIT("class");
564 return true;
565 }
566 const NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(fullName);
567 if (nd) // namespace
568 {
569 *pDoc=nd->documentation();
570 *pBrief=nd->briefDescription();
571 *pDef=nd;
572 AUTO_TRACE_EXIT("namespace");
573 return true;
574 }
575 if (scopeOffset==0)
576 {
577 scopeOffset=-1;
578 }
579 else
580 {
581 size_t o = context.context.rfind("::",scopeOffset-1);
582 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
583 }
584 } while (scopeOffset>=0);
585
586 AUTO_TRACE_EXIT("not found");
587 return false;
588}
589
590//---------------------------------------------------------------------------
592 DocNodeList &children,const DString &txt)
593{
594 switch (tok.value())
595 {
596 case TokenRetval::TK_COMMAND_AT:
597 // fall through
598 case TokenRetval::TK_COMMAND_BS:
599 {
600 char cs[2] = { tok.command_to_char(), 0 };
601 children.append<DocWord>(this,parent,cs + context.token->name);
602 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a {}",
603 tok.command_to_char(),context.token->name,txt);
604 }
605 break;
606 case TokenRetval::TK_SYMBOL:
607 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a {}",
608 qPrint(context.token->name), qPrint(txt));
609 break;
610 case TokenRetval::TK_HTMLTAG:
611 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <{}{}> found as part of a {}",
612 context.token->endTag ? "/" : "",context.token->name, txt);
613 break;
614 default:
615 children.append<DocWord>(this,parent,context.token->name);
616 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token {} found as part of a {}",
617 tok.to_string(), txt);
618 break;
619 }
620}
621
622//---------------------------------------------------------------------------
623
625{
626 AUTO_TRACE("cmdName={}",cmdName);
627 DString saveCmdName = cmdName;
628 Token tok=tokenizer.lex();
629 size_t styleStackSizeAtStart = context.styleStack.size();
630 if (!tok.is(TokenRetval::TK_WHITESPACE))
631 {
632 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
633 saveCmdName);
634 return tok;
635 }
636 tok = tokenizer.lex();
637 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::TK_WHITESPACE,
638 TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST)
639 )
640 {
641 static const reg::Ex specialChar(R"([.,|()\‍[\‍]:;?])");
642 if (tok.is(TokenRetval::TK_WORD) && context.token->name.length()==1 &&
643 reg::match(context.token->name.str(),specialChar))
644 {
645 // special character that ends the markup command
646 AUTO_TRACE_ADD("special character ending style argument: '{}' styleStackSize {}->{}",context.token->name,styleStackSizeAtStart,context.styleStack.size());
647 if (context.styleStack.size() > styleStackSizeAtStart) // new styles opened inside command, but not closed
648 {
649 handlePendingStyleCommands(parent,children,context.styleStack.size()-styleStackSizeAtStart);
650 }
651 return tok;
652 }
653 if (!defaultHandleToken(parent,tok,children))
654 {
655 switch (tok.value())
656 {
657 case TokenRetval::TK_HTMLTAG:
659 {
660 // ignore </li> as the end of a style command
661 }
662 else
663 {
664 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
665 return tok;
666 }
667 break;
668 default:
669 errorHandleDefaultToken(parent,tok,children,"\\" + saveCmdName + " command");
670 break;
671 }
672 break;
673 }
674 tok = tokenizer.lex();
675 }
676 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
677 return (tok.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST)) ? tok : Token::make_RetVal_OK();
678}
679
680/*! Called when a style change starts. For instance a <b> command is
681 * encountered.
682 */
684 DocStyleChange::Style s,const DString &tagName,const HtmlAttribList *attribs)
685{
686 AUTO_TRACE("tagName={}",tagName);
687 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),s,tagName,true,
689 context.styleStack.push(&children.back());
691}
692
693/*! Called when a style change ends. For instance a </b> command is
694 * encountered.
695 */
697 DocStyleChange::Style s,const DString &tagName)
698{
699 AUTO_TRACE("tagName={}",tagName);
700 DString tagNameLower = DString(tagName).lower();
701
702 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
703 {
704 return std::get<DocStyleChange>(*stack.top());
705 };
706
707 if (context.styleStack.empty() || // no style change
708 topStyleChange(context.styleStack).style()!=s || // wrong style change
709 topStyleChange(context.styleStack).tagName()!=tagNameLower || // wrong style change
710 topStyleChange(context.styleStack).position()!=context.nodeStack.size() // wrong position
711 )
712 {
713 if (context.styleStack.empty())
714 {
715 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{0}> tag without matching <{0}>",tagName);
716 }
717 else if (topStyleChange(context.styleStack).tagName()!=tagNameLower ||
718 topStyleChange(context.styleStack).style()!=s)
719 {
720 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> tag while expecting </{}>",
721 tagName,topStyleChange(context.styleStack).tagName());
722 }
723 else
724 {
725 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> at different nesting level ({}) than expected ({})",
726 tagName,context.nodeStack.size(),topStyleChange(context.styleStack).position());
727 }
728 }
729 else // end the section
730 {
731 children.append<DocStyleChange>(
732 this,parent,context.nodeStack.size(),s,
733 topStyleChange(context.styleStack).tagName(),false);
734 context.styleStack.pop();
735 }
737 {
738 context.inCodeStyle = false;
739 }
740}
741
742/*! Called at the end of a paragraph to close all open style changes
743 * (e.g. a <b> without a </b>). The closed styles are pushed onto a stack
744 * and entered again at the start of a new paragraph.
745 */
746void DocParser::handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &children, size_t numberOfElementsToClose)
747{
748 AUTO_TRACE("context.styleStack.size()={} numberOfElementsToClose={}",context.styleStack.size(),numberOfElementsToClose);
749 if (!context.styleStack.empty())
750 {
751 if (numberOfElementsToClose==0) numberOfElementsToClose = context.styleStack.size(); // 0 is special value for "close all"
752 const DocStyleChange *sc = &std::get<DocStyleChange>(*context.styleStack.top());
753 while (sc && sc->position()>=context.nodeStack.size() && numberOfElementsToClose>0)
754 { // there are unclosed style modifiers in the paragraph
755 AUTO_TRACE_ADD("unclosed style {} at position {}",sc->styleString(),sc->position());
756 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),
757 sc->style(),sc->tagName(),false);
759 context.styleStack.pop();
760 sc = !context.styleStack.empty() ? &std::get<DocStyleChange>(*context.styleStack.top()) : nullptr;
761 numberOfElementsToClose--;
762 }
763 }
764}
765
767{
768 AUTO_TRACE();
769 while (!context.initialStyleStack.empty())
770 {
771 const DocStyleChange &sc = std::get<DocStyleChange>(*context.initialStyleStack.top());
772 handleStyleEnter(parent,children,sc.style(),sc.tagName(),&sc.attribs());
774 }
775}
776
778 const HtmlAttribList &tagHtmlAttribs)
779{
780 AUTO_TRACE();
781 size_t index=0;
782 Token retval = Token::make_RetVal_OK();
783 for (const auto &opt : tagHtmlAttribs)
784 {
785 if (opt.name=="name" || opt.name=="id") // <a name=label> or <a id=label> tag
786 {
787 if (!opt.value.empty())
788 {
789 children.append<DocAnchor>(this,parent,opt.value,true);
790 break; // stop looking for other tag attribs
791 }
792 else
793 {
794 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <a> tag with name option but without value!");
795 }
796 }
797 else if (opt.name=="href") // <a href=url>..</a> tag
798 {
799 // copy attributes
800 HtmlAttribList attrList = tagHtmlAttribs;
801 // and remove the href attribute
802 attrList.erase(attrList.begin()+index);
803 DString relPath;
804 if (opt.value.at(0) != '#') relPath = context.relPath;
805 children.append<DocHRef>(this, parent, attrList,
806 opt.value, relPath,
807 convertNameToFile(context.fileName, false, true));
809 retval = children.get_last<DocHRef>()->parse();
812 break;
813 }
814 else // unsupported option for tag a
815 {
816 }
817 ++index;
818 }
819 return retval;
820}
821
823{
824 AUTO_TRACE("content.initialStyleStack.size()={}",context.initialStyleStack.size());
825 if (!context.initialStyleStack.empty())
826 {
827 DString tagName = std::get<DocStyleChange>(*context.initialStyleStack.top()).tagName();
828 DString fileName = std::get<DocStyleChange>(*context.initialStyleStack.top()).fileName();
829 int lineNr = std::get<DocStyleChange>(*context.initialStyleStack.top()).lineNr();
832 if (lineNr != -1)
833 {
835 "end of comment block while expecting "
836 "command </{}> (Probable start '{}' at line {})",tagName, fileName, lineNr);
837 }
838 else
839 {
841 "end of comment block while expecting command </{}>",tagName);
842 }
843 }
844}
845
846void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag,bool typeLinkOnly)
847{
848 // helper to check if word w starts with any of the words in AUTOLINK_IGNORE_WORDS
849 auto ignoreWord = [](const DString &w) -> bool {
850 auto list = Config_getList(AUTOLINK_IGNORE_WORDS);
851 return std::find_if(list.begin(), list.end(),
852 [&w](const auto &ignore) { return w.startsWith(ignore); }
853 )!=list.end();
854 };
856 AUTO_TRACE("word={}",name);
857 if (!context.autolinkSupport || ignoreAutoLinkFlag || ignoreWord(context.token->name)) // no autolinking -> add as normal word
858 {
859 children.append<DocWord>(this,parent,name);
860 return;
861 }
862
863 // ------- try to turn the word 'name' into a link
864
865 const Definition *compound=nullptr;
866 const MemberDef *member=nullptr;
867 size_t len = context.token->name.length();
868 ClassDef *cd=nullptr;
869 bool ambig = false;
871 auto lang = context.lang;
872 bool inSeeBlock = context.inSeeBlock || context.inCodeStyle;
873 //printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context));
874 if (!context.insideHtmlLink &&
875 (resolveRef(context.context,context.token->name,inSeeBlock,&compound,&member,lang,true,fd,true)
876 || (!context.context.empty() && // also try with global scope
877 resolveRef(DString(),context.token->name,inSeeBlock,&compound,&member,lang,false,nullptr,true))
878 )
879 )
880 {
881 //printf("ADD %s = %p (linkable?=%d typeLinkOnly=%d)\n",
882 // qPrint(context.token->name),(void*)member,member ? member->isLinkable() : false, typeLinkOnly);
883 if (member && member->isLinkable())
884 {
885 if (!typeLinkOnly || context.token->name.startsWith("#") ||
886 member->isTypedef() || member->isEnumerate() || member->isEnumValue()) // filter on type links
887 {
888 AUTO_TRACE_ADD("resolved reference as member link");
889 if (member->isObjCMethod())
890 {
891 bool localLink = context.memberDef ? member->getClassDef()==context.memberDef->getClassDef() : false;
892 name = member->objCMethodName(localLink,inSeeBlock);
893 }
894 children.append<DocLinkedWord>(
895 this,parent,name,
896 member->getReference(),
897 member->getOutputFileBase(),
898 member->anchor(),
899 member->briefDescriptionAsTooltip());
900 }
901 else // explicit type link, but not a type
902 {
903 AUTO_TRACE_ADD("no link as request is type but member is not a type or explicit link");
904 children.append<DocWord>(this,parent,context.token->name);
905 }
906 }
907 else if (compound->isLinkable()) // compound link
908 {
909 AUTO_TRACE_ADD("resolved reference as compound link");
910 DString anchor = compound->anchor();
911 if (compound->definitionType()==Definition::TypeFile)
912 {
913 name=context.token->name;
914 }
915 else if (compound->definitionType()==Definition::TypeGroup)
916 {
917 name=toGroupDef(compound)->groupTitle();
918 }
919 children.append<DocLinkedWord>(
920 this,parent,name,
921 compound->getReference(),
922 compound->getOutputFileBase(),
923 anchor,
924 compound->briefDescriptionAsTooltip());
925 }
926 else if (compound->definitionType()==Definition::TypeFile &&
927 (toFileDef(compound))->generateSourceFile()
928 ) // undocumented file that has source code we can link to
929 {
930 AUTO_TRACE_ADD("resolved reference as source link");
931 children.append<DocLinkedWord>(
932 this,parent,context.token->name,
933 compound->getReference(),
934 compound->getSourceFileBase(),
935 "",
936 compound->briefDescriptionAsTooltip());
937 }
938 else // not linkable
939 {
940 AUTO_TRACE_ADD("resolved reference as unlinkable compound={} (linkable={}) member={} (linkable={})",
941 compound ? compound->name() : "<none>", compound ? (int)compound->isLinkable() : -1,
942 member ? member->name() : "<none>", member ? (int)member->isLinkable() : -1);
943 children.append<DocWord>(this,parent,name);
944 }
945 }
946 else if (!context.insideHtmlLink && len>1 && context.token->name.at(len-1)==':')
947 {
948 // special case, where matching Foo: fails to be an Obj-C reference,
949 // but Foo itself might be linkable.
951 handleLinkedWord(parent,children,ignoreAutoLinkFlag);
952 children.append<DocWord>(this,parent,":");
953 }
954 else if (!context.insideHtmlLink && (cd=getClass(context.token->name+"-p")))
955 {
956 // special case 2, where the token name is not a class, but could
957 // be a Obj-C protocol
958 children.append<DocLinkedWord>(
959 this,parent,name,
960 cd->getReference(),
961 cd->getOutputFileBase(),
962 cd->anchor(),
964 }
965 else if (const RequirementIntf *req = RequirementManager::instance().find(name); req!=nullptr) // link to requirement
966 {
967 if (Config_getBool(GENERATE_REQUIREMENTS))
968 {
969 children.append<DocLinkedWord>(
970 this,parent,name,
971 DString(), // link to local requirements overview also for external references
972 req->getOutputFileBase(),
973 req->id(),
974 req->title()
975 );
976 }
977 else // cannot link to a page that does not exist
978 {
979 children.append<DocWord>(this,parent,name);
980 }
981 }
982 else // normal non-linkable word
983 {
984 AUTO_TRACE_ADD("non-linkable");
985 if (context.token->name.startsWith("#"))
986 {
987 warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '{}' could not be resolved",name);
988 }
989 children.append<DocWord>(this,parent,context.token->name);
990 }
991}
992
994{
995 DString name = context.token->name; // save token name
996 AUTO_TRACE("name={}",name);
997 DString name1;
998 size_t p=0, i=0, ii=0;
999 while ((i=paramTypes.find('|',p))!=DString::npos)
1000 {
1001 name1 = paramTypes.mid(p,i-p);
1002 ii=name1.find('[');
1003 context.token->name=ii!=DString::npos ? name1.mid(0,ii) : name1; // take part without []
1004 handleLinkedWord(parent,children);
1005 if (ii!=DString::npos) children.append<DocWord>(this,parent,name1.mid(ii)); // add [] part
1006 p=i+1;
1007 children.append<DocSeparator>(this,parent,"|");
1008 }
1009 name1 = paramTypes.mid(p);
1010 ii=name1.find('[');
1011 context.token->name=ii!=DString::npos ? name1.mid(0,ii) : name1;
1012 handleLinkedWord(parent,children);
1013 if (ii!=DString::npos) children.append<DocWord>(this,parent,name1.mid(ii));
1014 context.token->name = name; // restore original token name
1015}
1016
1018{
1019 Token tok=tokenizer.lex();
1020 DString tokenName = context.token->name;
1021 AUTO_TRACE("name={}",tokenName);
1022 if (!tok.is(TokenRetval::TK_WHITESPACE))
1023 {
1024 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", tokenName);
1025 return;
1026 }
1028 tok=tokenizer.lex(); // get the reference id
1029 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1030 {
1031 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1032 tok.to_string(),tokenName);
1033 return;
1034 }
1035 children.append<DocInternalRef>(this,parent,context.token->name);
1036 children.get_last<DocInternalRef>()->parse();
1037}
1038
1040{
1041 AUTO_TRACE();
1042 Token tok=tokenizer.lex();
1043 if (!tok.is(TokenRetval::TK_WHITESPACE))
1044 {
1045 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1046 context.token->name);
1047 return;
1048 }
1049
1052 tok=tokenizer.lex();
1053 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1054 {
1055 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1056 "argument of command {}",context.token->name);
1057 return;
1058 }
1059 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1060 {
1061 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1062 tok.to_string(),context.token->name);
1063 return;
1064 }
1065 }
1066 children.append<DocAnchor>(this,parent,context.token->name,false);
1067}
1068
1070{
1071 AUTO_TRACE();
1072 // get the argument of the cite command.
1073 Token tok=tokenizer.lex();
1074
1075 CiteInfoOption option;
1076 if (tok.is(TokenRetval::TK_WORD) && context.token->name=="{")
1077 {
1079 tokenizer.lex();
1080 StringVector optList=split(context.token->name.str(),",");
1081 for (auto const &opt : optList)
1082 {
1083 if (opt == "number")
1084 {
1085 if (!option.isUnknown())
1086 {
1087 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1088 }
1089 else
1090 {
1091 option = CiteInfoOption::makeNumber();
1092 }
1093 }
1094 else if (opt == "year")
1095 {
1096 if (!option.isUnknown())
1097 {
1098 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1099 }
1100 else
1101 {
1102 option = CiteInfoOption::makeYear();
1103 }
1104 }
1105 else if (opt == "shortauthor")
1106 {
1107 if (!option.isUnknown())
1108 {
1109 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1110 }
1111 else
1112 {
1114 }
1115 }
1116 else if (opt == "nopar")
1117 {
1118 option.setNoPar();
1119 }
1120 else if (opt == "nocite")
1121 {
1122 option.setNoCite();
1123 }
1124 else
1125 {
1126 warn(context.fileName,tokenizer.getLineNr(),"Unknown option specified with \\{}, discarding '{}'", context.token->name, opt);
1127 }
1128 }
1129
1130 if (option.isUnknown()) option.changeToNumber();
1131
1133 tok=tokenizer.lex();
1134 if (!tok.is(TokenRetval::TK_WHITESPACE))
1135 {
1136 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1137 context.token->name);
1138 return;
1139 }
1140 }
1141 else if (!tok.is(TokenRetval::TK_WHITESPACE))
1142 {
1143 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '\\{}' command",
1144 context.token->name);
1145 return;
1146 }
1147 else
1148 {
1149 option = CiteInfoOption::makeNumber();
1150 }
1151
1154 tok=tokenizer.lex();
1155 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1156 {
1157 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1158 "argument of command '\\{}'",context.token->name);
1159 return;
1160 }
1161 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1162 {
1163 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '\\{}'",
1164 tok.to_string(),context.token->name);
1165 return;
1166 }
1168 children.append<DocCite>(this,parent,context.token->name,context.context,option);
1169 }
1170
1171}
1172
1174{
1175 AUTO_TRACE();
1176 Token tok=tokenizer.lex();
1177 if (!tok.is(TokenRetval::TK_WHITESPACE))
1178 {
1179 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", context.token->name);
1180 return;
1181 }
1183 tok=tokenizer.lex();
1184 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1185 {
1186 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1187 "argument of command {}",context.token->name);
1188 return;
1189 }
1190 else if (!tok.is(TokenRetval::TK_WORD))
1191 {
1192 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1193 tok.to_string(),context.token->name);
1194 return;
1195 }
1198}
1199
1200/* Helper function that deals with the title, width, and height arguments of various commands.
1201 * @param[in] cmd Command id for which to extract caption and size info.
1202 * @param[in] parent Parent node, owner of the children list passed as
1203 * the third argument.
1204 * @param[in] children The list of child nodes to which the node representing
1205 * the token can be added.
1206 * @param[out] width the extracted width specifier
1207 * @param[out] height the extracted height specifier
1208 */
1210{
1211 AUTO_TRACE();
1212 auto ns = AutoNodeStack(this,parent);
1213
1214 // parse title
1216 Token tok = tokenizer.lex();
1217 while (!tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1218 {
1219 if (tok.is(TokenRetval::TK_WORD) && (context.token->name=="width=" || context.token->name=="height="))
1220 {
1221 // special case: no title, but we do have a size indicator
1222 break;
1223 }
1224 else if (tok.is(TokenRetval::TK_HTMLTAG))
1225 {
1227 break;
1228 }
1229 if (!defaultHandleToken(parent,tok,children))
1230 {
1231 errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd));
1232 }
1233 tok = tokenizer.lex();
1234 }
1235 // parse size attributes
1236 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1237 {
1238 tok=tokenizer.lex();
1239 }
1240 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) // there are values following the title
1241 {
1242 if (tok.is(TokenRetval::TK_WORD))
1243 {
1244 if (context.token->name=="width=" || context.token->name=="height=")
1245 {
1248 }
1249
1250 if (context.token->name=="width")
1251 {
1252 width = context.token->chars;
1253 }
1254 else if (context.token->name=="height")
1255 {
1256 height = context.token->chars;
1257 }
1258 else // other text after the title -> treat as normal text
1259 {
1261 //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '{}' after \\{} command, expected 'width' or 'height'",
1262 // context.token->name, Mappers::cmdMapper->find(cmd));
1263 break;
1264 }
1265 }
1266
1267 tok=tokenizer.lex();
1268 // if we found something we did not expect, push it back to the stream
1269 // so it can still be processed
1270 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
1271 {
1273 tokenizer.unputString(tok.is(TokenRetval::TK_COMMAND_AT) ? "@" : "\\");
1274 break;
1275 }
1276 else if (tok.is(TokenRetval::TK_SYMBOL))
1277 {
1279 break;
1280 }
1281 else if (tok.is(TokenRetval::TK_HTMLTAG))
1282 {
1284 break;
1285 }
1286 }
1288
1290 AUTO_TRACE_EXIT("width={} height={}",width,height);
1291}
1292
1294{
1295 AUTO_TRACE();
1296 bool inlineImage = false;
1297 DString anchorStr;
1298
1299 Token tok=tokenizer.lex();
1300 if (!tok.is(TokenRetval::TK_WHITESPACE))
1301 {
1302 if (tok.is(TokenRetval::TK_WORD))
1303 {
1304 if (context.token->name == "{")
1305 {
1307 tokenizer.lex();
1309 StringVector optList=split(context.token->name.str(),",");
1310 for (const auto &opt : optList)
1311 {
1312 if (opt.empty()) continue;
1313 DString locOpt(opt);
1314 DString locOptLow;
1315 locOpt = locOpt.stripWhiteSpace();
1316 locOptLow = locOpt.lower();
1317 if (locOptLow == "inline")
1318 {
1319 inlineImage = true;
1320 }
1321 else if (locOptLow.startsWith("anchor:"))
1322 {
1323 if (!anchorStr.empty())
1324 {
1326 "multiple use of option 'anchor' for 'image' command, ignoring: '{}'",
1327 locOpt.mid(7));
1328 }
1329 else
1330 {
1331 anchorStr = locOpt.mid(7);
1332 }
1333 }
1334 else
1335 {
1337 "unknown option '{}' for 'image' command specified",
1338 locOpt);
1339 }
1340 }
1341 tok=tokenizer.lex();
1342 if (!tok.is(TokenRetval::TK_WHITESPACE))
1343 {
1344 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1345 return;
1346 }
1347 }
1348 }
1349 else
1350 {
1351 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1352 return;
1353 }
1354 }
1355 tok=tokenizer.lex();
1356 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1357 {
1358 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image",
1359 tok.to_string());
1360 return;
1361 }
1362 tok=tokenizer.lex();
1363 if (!tok.is(TokenRetval::TK_WHITESPACE))
1364 {
1365 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1366 return;
1367 }
1369 DString imgType = context.token->name.lower();
1370 if (imgType=="html") t=DocImage::Html;
1371 else if (imgType=="latex") t=DocImage::Latex;
1372 else if (imgType=="docbook") t=DocImage::DocBook;
1373 else if (imgType=="rtf") t=DocImage::Rtf;
1374 else if (imgType=="xml") t=DocImage::Xml;
1375 else
1376 {
1377 warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `{}` specified as the first argument of "
1378 "\\image command is not valid", imgType);
1379 return;
1380 }
1382 tok=tokenizer.lex();
1384 if (!tok.is(TokenRetval::TK_WORD))
1385 {
1386 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", tok.to_string());
1387 return;
1388 }
1389 if (!anchorStr.empty())
1390 {
1391 children.append<DocAnchor>(this,parent,anchorStr,true);
1392 }
1393 HtmlAttribList attrList;
1394 children.append<DocImage>(this,parent,attrList,
1395 findAndCopyImage(context.token->name,t),t,"",inlineImage);
1396 children.get_last<DocImage>()->parse();
1397}
1398
1399void DocParser::handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const DString &cmdName)
1400{
1401 AUTO_TRACE("cmdName={}",cmdName);
1402 DString saveCmdName = cmdName;
1404 Token tok=tokenizer.lex();
1405 if (!tok.is(TokenRetval::TK_WHITESPACE))
1406 {
1407 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1408 cmdChar,qPrint(saveCmdName));
1409 return;
1410 }
1412 tok=tokenizer.lex(); // get the reference id
1413 if (!tok.is(TokenRetval::TK_WORD))
1414 {
1415 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1416 tok.to_string(),cmdChar,saveCmdName);
1417 return;
1418 }
1419 children.append<DocRef>(this,parent,
1422 children.get_last<DocRef>()->parse(cmdChar,saveCmdName);
1423}
1424
1425void DocParser::handleIFile(char cmdChar,const DString &cmdName)
1426{
1427 AUTO_TRACE();
1428 Token tok=tokenizer.lex();
1429 if (!tok.is(TokenRetval::TK_WHITESPACE))
1430 {
1431 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1432 cmdChar,cmdName);
1433 return;
1434 }
1436 tok=tokenizer.lex();
1438 if (!tok.is(TokenRetval::TK_WORD))
1439 {
1440 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1441 tok.to_string(),cmdChar,cmdName);
1442 return;
1443 }
1445}
1446
1447void DocParser::handleILine(char cmdChar,const DString &cmdName)
1448{
1449 AUTO_TRACE();
1451 Token tok = tokenizer.lex();
1453 if (!tok.is(TokenRetval::TK_WORD))
1454 {
1455 warn_doc_error(context.fileName,tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
1456 cmdChar,cmdName);
1457 return;
1458 }
1459}
1460
1461/* Helper function that deals with the most common tokens allowed in
1462 * title like sections.
1463 * @param parent Parent node, owner of the children list passed as
1464 * the third argument.
1465 * @param tok The token to process.
1466 * @param children The list of child nodes to which the node representing
1467 * the token can be added.
1468 * @param handleWord Indicates if word token should be processed
1469 * @retval true The token was handled.
1470 * @retval false The token was not handled.
1471 */
1473{
1474 AUTO_TRACE("token={} handleWord={}",tok.to_string(),handleWord);
1475 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
1476 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
1477 )
1478 {
1479 }
1480reparsetoken:
1481 DString tokenName = context.token->name;
1482 AUTO_TRACE_ADD("tokenName={}",tokenName);
1483 switch (tok.value())
1484 {
1485 case TokenRetval::TK_COMMAND_AT:
1486 // fall through
1487 case TokenRetval::TK_COMMAND_BS:
1488 switch (Mappers::cmdMapper->map(tokenName))
1489 {
1492 break;
1495 break;
1498 break;
1501 break;
1504 break;
1507 break;
1510 break;
1513 break;
1516 break;
1520 break;
1525 break;
1528 break;
1531 break;
1534 break;
1537 break;
1540 break;
1543 break;
1546 break;
1548 {
1549 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,true);
1550 tok=handleStyleArgument(parent,children,tokenName);
1551 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,false);
1552 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1553 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1554 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1555 {
1556 AUTO_TRACE_ADD("CommandType::CMD_EMPHASIS: reparsing");
1557 goto reparsetoken;
1558 }
1559 }
1560 break;
1562 {
1563 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,true);
1564 tok=handleStyleArgument(parent,children,tokenName);
1565 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,false);
1566 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1567 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1568 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1569 {
1570 AUTO_TRACE_ADD("CommandType::CMD_BOLD: reparsing");
1571 goto reparsetoken;
1572 }
1573 }
1574 break;
1576 {
1577 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,true);
1578 tok=handleStyleArgument(parent,children,tokenName);
1579 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,false);
1580 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1581 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1582 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1583 {
1584 AUTO_TRACE_ADD("CommandType::CMD_CODE: reparsing");
1585 goto reparsetoken;
1586 }
1587 }
1588 break;
1590 {
1592 tok = tokenizer.lex();
1594 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1595 {
1596 warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker");
1597 }
1599 }
1600 break;
1602 {
1604 tok = tokenizer.lex();
1606 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1607 {
1608 warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker");
1609 }
1611 }
1612 break;
1614 {
1616 tok = tokenizer.lex();
1618 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1619 {
1620 warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker");
1621 }
1623 }
1624 break;
1626 {
1628 tok = tokenizer.lex();
1630 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1631 {
1632 warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker");
1633 }
1635 }
1636 break;
1638 {
1640 tok = tokenizer.lex();
1642 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1643 {
1644 warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker");
1645 }
1647 }
1648 break;
1650 {
1652 tok = tokenizer.lex();
1654 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1655 {
1656 warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker");
1657 }
1659 }
1660 break;
1662 {
1663 children.append<DocFormula>(this,parent,context.token->id);
1664 }
1665 break;
1668 {
1669 handleAnchor(parent,children);
1670 }
1671 break;
1673 {
1674 handleCite(parent,children);
1675 }
1676 break;
1678 {
1679 handlePrefix(parent,children);
1680 }
1681 break;
1683 {
1684 handleInternalRef(parent,children);
1686 }
1687 break;
1689 {
1691 (void)tokenizer.lex();
1693 //printf("Found scope='%s'\n",qPrint(context.context));
1695 }
1696 break;
1698 handleImage(parent,children);
1699 break;
1701 handleILine(tok.command_to_char(),tokenName);
1702 break;
1704 handleIFile(tok.command_to_char(),tokenName);
1705 break;
1706 default:
1707 return false;
1708 }
1709 break;
1710 case TokenRetval::TK_HTMLTAG:
1711 {
1712 auto handleEnterLeaveStyle = [this,&parent,&children,&tokenName](DocStyleChange::Style style) {
1713 if (!context.token->endTag)
1714 {
1715 handleStyleEnter(parent,children,style,tokenName,&context.token->attribs);
1716 }
1717 else
1718 {
1719 handleStyleLeave(parent,children,style,tokenName);
1720 }
1721 };
1722 switch (Mappers::htmlTagMapper->map(tokenName))
1723 {
1725 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading");
1726 break;
1728 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading");
1729 break;
1731 handleEnterLeaveStyle(DocStyleChange::Span);
1732 break;
1734 handleEnterLeaveStyle(DocStyleChange::Bold);
1735 break;
1737 handleEnterLeaveStyle(DocStyleChange::S);
1738 break;
1740 handleEnterLeaveStyle(DocStyleChange::Strike);
1741 break;
1743 handleEnterLeaveStyle(DocStyleChange::Del);
1744 break;
1746 handleEnterLeaveStyle(DocStyleChange::Underline);
1747 break;
1749 handleEnterLeaveStyle(DocStyleChange::Ins);
1750 break;
1752 case HtmlTagType::XML_C:
1753 handleEnterLeaveStyle(DocStyleChange::Code);
1754 break;
1756 handleEnterLeaveStyle(DocStyleChange::Kbd);
1757 break;
1759 handleEnterLeaveStyle(DocStyleChange::Typewriter);
1760 break;
1762 handleEnterLeaveStyle(DocStyleChange::Italic);
1763 break;
1765 handleEnterLeaveStyle(DocStyleChange::Subscript);
1766 break;
1768 handleEnterLeaveStyle(DocStyleChange::Superscript);
1769 break;
1771 handleEnterLeaveStyle(DocStyleChange::Center);
1772 break;
1774 handleEnterLeaveStyle(DocStyleChange::Small);
1775 break;
1777 handleEnterLeaveStyle(DocStyleChange::Cite);
1778 break;
1780 if (!context.token->endTag)
1781 {
1783 }
1784 break;
1785 default:
1786 return false;
1787 break;
1788 }
1789 }
1790 break;
1791 case TokenRetval::TK_SYMBOL:
1792 {
1795 {
1796 children.append<DocSymbol>(this,parent,s);
1797 }
1798 else
1799 {
1800 return false;
1801 }
1802 }
1803 break;
1804 case TokenRetval::TK_WHITESPACE:
1805 case TokenRetval::TK_NEWPARA:
1806handlepara:
1807 if (insidePRE(parent) || !children.empty())
1808 {
1809 children.append<DocWhiteSpace>(this,parent,context.token->chars);
1810 }
1811 break;
1812 case TokenRetval::TK_LNKWORD:
1813 if (handleWord)
1814 {
1815 handleLinkedWord(parent,children);
1816 }
1817 else
1818 return false;
1819 break;
1820 case TokenRetval::TK_WORD:
1821 if (handleWord)
1822 {
1823 children.append<DocWord>(this,parent,context.token->name);
1824 }
1825 else
1826 return false;
1827 break;
1828 case TokenRetval::TK_URL:
1830 {
1831 children.append<DocWord>(this,parent,context.token->name);
1832 }
1833 else
1834 {
1836 }
1837 break;
1838 default:
1839 return false;
1840 }
1841 return true;
1842}
1843
1844//---------------------------------------------------------------------------
1845
1847{
1848 AUTO_TRACE();
1849 bool found=false;
1850 size_t index=0;
1851 for (const auto &opt : tagHtmlAttribs)
1852 {
1853 AUTO_TRACE_ADD("option name={} value='{}'",opt.name,opt.value);
1854 if (opt.name=="src" && !opt.value.empty())
1855 {
1856 // copy attributes
1857 HtmlAttribList attrList = tagHtmlAttribs;
1858 // and remove the src attribute
1859 attrList.erase(attrList.begin()+index);
1861 children.append<DocImage>(
1862 this,parent,attrList,
1863 findAndCopyImage(opt.value,t,false),
1864 t,opt.value);
1865 found = true;
1866 }
1867 ++index;
1868 }
1869 if (!found)
1870 {
1871 warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!");
1872 }
1873}
1874
1875//---------------------------------------------------------------------------
1876
1878 const DString &doc)
1879{
1880 AUTO_TRACE();
1881 Token retval = Token::make_RetVal_OK();
1882
1883 if (doc.empty()) return retval;
1884
1886
1887 // first parse any number of paragraphs
1888 bool isFirst=true;
1889 DocPara *lastPar=!children.empty() ? std::get_if<DocPara>(&children.back()): nullptr;
1890 if (lastPar)
1891 { // last child item was a paragraph
1892 isFirst=false;
1893 }
1894 do
1895 {
1896 children.append<DocPara>(this,parent);
1897 DocPara *par = children.get_last<DocPara>();
1898 if (isFirst) { par->markFirst(); isFirst=false; }
1899 retval=par->parse();
1900 if (!par->empty())
1901 {
1902 if (lastPar) lastPar->markLast(false);
1903 lastPar=par;
1904 }
1905 else
1906 {
1907 children.pop_back();
1908 }
1909 } while (retval.is(TokenRetval::TK_NEWPARA));
1910 if (lastPar) lastPar->markLast();
1911
1912 AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
1913 return retval;
1914}
1915
1916//---------------------------------------------------------------------------
1917
1919{
1920 AUTO_TRACE("file={} text={}",file,text);
1921 bool ambig = false;
1922 DString filePath = findFilePath(file,ambig);
1923 if (!filePath.empty())
1924 {
1925 size_t indent = 0;
1926 text = detab(fileToString(filePath,Config_getBool(FILTER_SOURCE_FILES)),indent);
1927 if (ambig)
1928 {
1929 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '{}' is ambiguous"
1930 "Possible candidates:\n{}",file, showFileDefMatches(Doxygen::exampleNameLinkedMap,file));
1931 }
1932 }
1933 else
1934 {
1935 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '{}' is not found. "
1936 "Check your EXAMPLE_PATH",file);
1937 }
1938}
1939
1940//---------------------------------------------------------------------------
1941
1942static DString extractCopyDocId(const char *data, size_t &j, size_t len)
1943{
1944 size_t s=j;
1945 int round=0;
1946 bool insideDQuote=false;
1947 bool insideSQuote=false;
1948 bool found=false;
1949 while (j<len && !found)
1950 {
1951 if (!insideSQuote && !insideDQuote)
1952 {
1953 switch (data[j])
1954 {
1955 case '(': round++; break;
1956 case ')': round--; break;
1957 case '"': insideDQuote=true; break;
1958 case '\'': insideSQuote=true; break;
1959 case '\\': // fall through, begin of command
1960 case '@': // fall through, begin of command
1961 case '\t': // fall through
1962 case '\n':
1963 found=(round==0);
1964 break;
1965 case ' ': // allow spaces in cast operator (see issue #11169)
1966 found=(round==0) && (j<8 || !literal_at(data+j-8,"operator"));
1967 break;
1968 }
1969 }
1970 else if (insideSQuote) // look for single quote end
1971 {
1972 if (data[j]=='\'' && (j==0 || data[j]!='\\'))
1973 {
1974 insideSQuote=false;
1975 }
1976 }
1977 else if (insideDQuote) // look for double quote end
1978 {
1979 if (data[j]=='"' && (j==0 || data[j]!='\\'))
1980 {
1981 insideDQuote=false;
1982 }
1983 }
1984 if (!found) j++;
1985 }
1986
1987 // include const and volatile
1988 if (literal_at(data+j," const"))
1989 {
1990 j+=6;
1991 }
1992 else if (literal_at(data+j," volatile"))
1993 {
1994 j+=9;
1995 }
1996
1997 // allow '&' or '&&' or ' &' or ' &&' at the end
1998 size_t k=j;
1999 while (k<len && data[k]==' ') k++;
2000 if (k<len-1 && data[k]=='&' && data[k+1]=='&') j=k+2;
2001 else if (k<len && data[k]=='&' ) j=k+1;
2002
2003 // do not include punctuation added by Definition::_setBriefDescription()
2004 size_t e=j;
2005 if (j>0 && data[j-1]=='.') { e--; }
2006 DString id(data+s,e-s);
2007 //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
2008 return id;
2009}
2010
2011// macro to check if the input starts with a specific command.
2012// note that data[i] should point to the start of the command (\ or @ character)
2013// and the sizeof(str) returns the size of str including the '\0' terminator;
2014// a fact we abuse to skip over the start of the command character.
2015#define CHECK_FOR_COMMAND(str,action) \
2016 do if ((i+sizeof(str)<len) && literal_at(data+i+1,str)) \
2017 { j=i+sizeof(str); action; } while(0)
2018
2019static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i,size_t len,bool &brief)
2020{
2021 size_t j=0;
2022 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2023 {
2024 CHECK_FOR_COMMAND("copybrief",brief=true); // @copybrief or \copybrief
2025 CHECK_FOR_COMMAND("copydetails",brief=false); // @copydetails or \copydetails
2026 }
2027 return j;
2028}
2029
2030static size_t isVerbatimSection(const char *data,size_t i,size_t len,DString &endMarker)
2031{
2032 size_t j=0;
2033 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2034 {
2035 CHECK_FOR_COMMAND("dot",endMarker="enddot");
2036 CHECK_FOR_COMMAND("icode",endMarker="endicode");
2037 CHECK_FOR_COMMAND("code",endMarker="endcode");
2038 CHECK_FOR_COMMAND("msc",endMarker="endmsc");
2039 CHECK_FOR_COMMAND("mermaid",endMarker="endmermaid");
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(DString(&data[i]).left(10)),j);
2052 return j;
2053}
2054
2055static size_t skipToEndMarker(const char *data,size_t i,size_t len,const DString &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 && dstrncmp(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
2074DString DocParser::processCopyDoc(const char *data,size_t &len)
2075{
2076 AUTO_TRACE("data={} len={}",Trace::trunc(data),len);
2077 DString 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 DString id = extractCopyDocId(data,j,len);
2094 const Definition *def = nullptr;
2095 DString doc,brief;
2096 //printf("resolving docs='%s'\n",qPrint(id));
2097 bool found = findDocsForMemberOrCompound(id,&doc,&brief,&def);
2098 if (found && def->isReference())
2099 {
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 DString orgFileName = context.fileName;
2111 context.copyStack.push_back(def);
2112 auto addDocs = [&](const DString &file_,int line_,const DString &doc_)
2113 {
2114 result+=" \\ifile \""+file_+"\" ";
2115 result+="\\iline "+DString().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 "+DString().setNum(lineNr)+" ";
2138 }
2139 else
2140 {
2142 "Found recursive @copy{} or @copydoc relation for argument '{}'.",
2143 isBrief?"brief":"details",id);
2144 }
2145 }
2146 else
2147 {
2149 "@copy{} or @copydoc target '{}' not found", isBrief?"brief":"details",id);
2150 }
2151 // skip over command
2152 i=j;
2153 }
2154 else
2155 {
2156 DString 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+=DString(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 DString &fileName,
2189 int startLine,
2190 const Definition *ctx,
2191 const MemberDef *md,
2192 const DString &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();
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 DString("");
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 DString inpStr = parser->processCopyDoc(input.data(),ioLen);
2296 if (inpStr.empty() || 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 DString &fileName,int lineNr,const DString &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.empty())
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.empty())
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 DString &target,const DString &context, const DString &srcFile, int srcLine )
2442{
2443 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2444 assert(parser!=nullptr);
2445 if (parser==nullptr) return nullptr;
2446 if (!srcFile.empty())
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 DString &input,
2456 const Definition *d,
2457 const DString &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
AutoNodeStack(DocParser *parser, DocNodeVariant *node)
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
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
DString & setNum(short n)
Definition dstring.h:541
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
DString lower() const
Definition dstring.h:331
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
std::string_view view() const
Definition dstring.h:167
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:675
DString right(size_t len) const
Definition dstring.h:316
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:159
DString & prepend(const char *s)
Definition dstring.h:504
size_t find(char c, size_t pos=0) const
Definition dstring.h:244
DString & sprintf(const char *format,...)
Definition dstring.cpp:29
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition dstring.h:222
@ ExplicitSize
Definition dstring.h:136
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:342
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:634
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
bool startsWith(const char *s) const
Definition dstring.h:589
bool endsWith(const char *s) const
Definition dstring.h:606
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
@ 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 DString briefDescription(bool abbreviate=false) const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual DString briefDescriptionAsTooltip() const =0
virtual int briefLine() const =0
virtual DString qualifiedName() const =0
virtual DString anchor() const =0
virtual DString getReference() const =0
virtual DString docFile() const =0
virtual DString getSourceFileBase() const =0
virtual bool isReference() const =0
virtual DString getOutputFileBase() const =0
Dir()
Definition dir.cpp:189
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
bool isFirst() const
Definition docnode.h:1096
void markLast(bool v=true)
Definition docnode.h:1095
void handleIFile(char cmdChar, const DString &cmdName)
std::stack< DocParserContext > contextStack
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children, size_t numberOfElementsToClose=0)
DocTokenizer tokenizer
void handleInternalRef(DocNodeVariant *parent, DocNodeList &children)
void readTextFileByName(const DString &file, DString &text)
void checkRetvalName()
void errorHandleDefaultToken(DocNodeVariant *parent, Token tok, DocNodeList &children, const DString &txt)
void handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const DString &cmdName)
Token handleAHref(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName, const HtmlAttribList *attribs)
void handleParameterType(DocNodeVariant *parent, DocNodeList &children, const DString &paramTypes)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=true)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
bool findDocsForMemberOrCompound(const DString &commandName, DString *pDoc, DString *pBrief, const Definition **pDef)
DString processCopyDoc(const char *data, size_t &len)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const DString &cmdName)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName)
void checkUnOrMultipleDocumentedParams()
void popContext()
Definition docparser.cpp:75
void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, DString &width, DString &height)
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=false, bool typeLinkOnly=false)
void handleCite(DocNodeVariant *parent, DocNodeList &children)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
void handleILine(char cmdChar, const DString &cmdName)
void checkArgumentName()
DocParserContext context
Token internalValidatingParseDoc(DocNodeVariant *parent, DocNodeList &children, const DString &doc)
void handleAnchor(DocNodeVariant *parent, DocNodeList &children)
void handleImg(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleUnclosedStyleCommands()
void pushContext()
Definition docparser.cpp:60
DString findAndCopyImage(const DString &fileName, DocImage::Type type, bool doWarn=true)
Definition docparser.cpp:96
Node representing a reference to some item.
Definition docnode.h:787
DocRef(DocParser *parser, DocNodeVariant *parent, const DString &target, const DString &context)
Definition docnode.cpp:709
DocRoot(DocParser *parser, bool indent, bool sl)
Definition docnode.h:1320
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
Style style() const
Definition docnode.h:307
DString tagName() const
Definition docnode.h:312
size_t position() const
Definition docnode.h:310
Node representing a special symbol.
Definition docnode.h:328
static HtmlEntityMapper::SymType decodeSymbol(const DString &symName)
Definition docnode.cpp:155
DocText(DocParser *parser)
Definition docnode.h:1311
DocTitle(DocParser *parser, DocNodeVariant *parent)
Definition docnode.h:610
TokenInfo * token()
DString getFileName() const
void setStateTitleAttrValue()
void unputString(const DString &tag)
void setStateCite()
void setStatePrefix()
void setLineNr(int lineno)
void setStateIFile()
void setStateAnchor()
void setStateRtfOnly()
void setStateTitle()
void setStateFile()
void setStateLatexOnly()
void setStateManOnly()
void findSections(const DString &input, const Definition *d, const DString &fileName)
int getLineNr() const
void setStateDbOnly()
void setStateHtmlOnly()
void setStateInternalRef()
void init(const char *input, const DString &fileName, bool markdownSupport, bool insideHtmlLink)
void setStateILine()
void setFileName(const DString &fileName)
void setStateOptions()
void setStatePara()
TokenInfo * resetToken()
void setStateXmlOnly()
void setStateSetScope()
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 DString 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 DString 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
void addImageFile(const DString &name)
Definition indexlist.h:123
const T * find(const std::string &key) const
Definition linkedmap.h:47
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual bool isObjCMethod() const =0
virtual const MemberDef * inheritsDocsFrom() const =0
virtual DString objCMethodName(bool localLink, bool showStatic) 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 bool isDefine() const =0
virtual DString getScopeString() 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
static RequirementManager & instance()
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
bool enabled() const
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 DString &n)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
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
IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf, const DString &fileName, int lineNr, const DString &input)
IDocParserPtr createDocParser()
factory function to create a parser
Definition docparser.cpp:55
IDocNodeASTPtr createRef(IDocParser &parserIntf, const DString &target, const DString &context, const DString &srcFile, int srcLine)
IDocNodeASTPtr validatingParseText(IDocParser &parserIntf, const DString &input)
static DString extractCopyDocId(const char *data, size_t &j, size_t len)
static size_t isVerbatimSection(const char *data, size_t i, size_t len, DString &endMarker)
void docFindSections(const DString &input, const Definition *d, const DString &fileName)
static size_t skipToEndMarker(const char *data, size_t i, size_t len, const DString &endMarker)
static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i, size_t len, bool &brief)
IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf, const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &input, const DocOptions &options)
#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)
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:476
int dstrncmp(const char *str1, const char *str2, size_t len)
Definition dstring.h:61
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:44
const char * qPrint(const char *s)
Definition dstring.h:769
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 DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:105
DString trunc(const DString &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:858
PageDef * toPageDef(Definition *d)
Definition pagedef.cpp:519
Portable versions of functions that are platform dependent.
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
DString exampleName() const
Definition docoptions.h:28
bool linkFromIndex() const
Definition docoptions.h:30
bool markdownSupport() const
Definition docoptions.h:31
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
DString includeFileText
Definition docparser_p.h:88
DocNodeStack nodeStack
Definition docparser_p.h:67
StringMultiSet paramsFound
Definition docparser_p.h:77
DefinitionStack copyStack
Definition docparser_p.h:70
const Definition * scope
Definition docparser_p.h:61
TokenInfo * token
Definition docparser_p.h:95
DocStyleChangeStack initialStyleStack
Definition docparser_p.h:69
SrcLangExt lang
Definition docparser_p.h:85
size_t includeFileOffset
Definition docparser_p.h:89
const MemberDef * memberDef
Definition docparser_p.h:80
DString exampleName
Definition docparser_p.h:82
bool checkCV
Definition util.h:75
const MemberDef * md
Definition util.h:82
bool found
Definition util.h:81
DString verb
DString sectionId
DString chars
HtmlAttribList attribs
bool isEMailAddr
DString text
DString name
SrcLangExt
Definition types.h:207
DString findFilePath(const DString &file, bool &ambig)
Definition util.cpp:2742
DString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:932
DString detab(const DString &s, size_t &refIndent)
Definition util.cpp:6481
bool resolveRef(const DString &scName, const DString &name, bool inSeeBlock, const Definition **resContext, const MemberDef **resMember, SrcLangExt lang, bool lookForSpecialization, const FileDef *currentFile, bool checkScope)
Definition util.cpp:2178
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:525
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3275
DString showFileDefMatches(const FileNameLinkedMap *fnMap, const DString &n)
Definition util.cpp:2785
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const DString &n, bool &ambig)
Definition util.cpp:2647
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4975
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1220
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2435
DString relativePathToRoot(const DString &name)
Definition util.cpp:3343
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:982
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:5618
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Returns the scope separator to use given the programming language lang.
Definition util.cpp:5658
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:2033
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6379
A bunch of utility functions.