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