Doxygen
Loading...
Searching...
No Matches
util.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 *
3 * Copyright (C) 1997-2026 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// own include
17#include "util.h"
18
19// std includes (sorted)
20#include <algorithm>
21#include <cctype>
22#include <cinttypes>
23#include <cstdlib>
24#include <cstring>
25#include <ctime>
26#include <mutex>
27#include <unordered_set>
28
29// other includes (sorted)
30#include "arguments.h"
31#include "classdef.h"
32#include "classlist.h"
33#include "conceptdef.h"
34#include "config.h"
35#include "datetime.h"
36#include "debug.h"
37#include "defargs.h"
38#include "dir.h"
39#include "dirdef.h"
40#include "doxygen.h"
41#include "example.h"
42#include "filedef.h"
43#include "fileinfo.h"
44#include "filename.h"
45#include "groupdef.h"
46#include "htmlentity.h"
47#include "image.h"
48#include "language.h"
49#include "md5hash.h"
50#include "membername.h"
51#include "message.h"
52#include "moduledef.h"
53#include "namespacedef.h"
54#include "outputlist.h"
55#include "pagedef.h"
56#include "parserintf.h"
57#include "portable.h"
58#include "regex.h"
59#include "stringutil.h"
60#include "symbolresolver.h"
61#include "textstream.h"
62#include "trace.h"
63#include "utf8.h"
64
65#define ENABLE_TRACINGSUPPORT 0
66
67#if defined(__APPLE__) && ENABLE_TRACINGSUPPORT
68#define TRACINGSUPPORT
69#endif
70
71#ifdef TRACINGSUPPORT
72#include <execinfo.h>
73#include <unistd.h>
74#endif
75
76//------------------------------------------------------------------------
77
78#define REL_PATH_TO_ROOT "../../"
79
80static const char *hex = "0123456789ABCDEF";
81
82//------------------------------------------------------------------------
83
84/*!
85 Removes all anonymous scopes from string s
86 Possible examples:
87\verbatim
88 "bla::@10::blep" => "bla::blep"
89 "bla::@10::@11::blep" => "bla::blep"
90 "@10::blep" => "blep"
91 " @10::blep" => "blep"
92 "@9::@10::blep" => "blep"
93 "bla::@1" => "bla"
94 "bla::@1::@2" => "bla"
95 "bla @1" => "bla"
96\endverbatim
97 */
99{
100 std::string result;
101 if (str.empty()) return result;
102
103 // helper to check if the found delimiter starts with a colon
104 auto startsWithColon = [](const std::string &del)
105 {
106 for (size_t i=0;i<del.size();i++)
107 {
108 if (del[i]=='@') return false;
109 else if (del[i]==':') return true;
110 }
111 return false;
112 };
113
114 // helper to check if the found delimiter ends with a colon
115 auto endsWithColon = [](const std::string &del)
116 {
117 for (int i=static_cast<int>(del.size())-1;i>=0;i--)
118 {
119 if (del[i]=='@') return false;
120 else if (del[i]==':') return true;
121 }
122 return false;
123 };
124
125 static const reg::Ex re(R"([\s:]*@\d+[\s:]*)");
126 std::string s = str.str();
127 reg::Iterator iter(s,re);
129 size_t p=0;
130 size_t sl=s.length();
131 bool needsSeparator=false;
132 for ( ; iter!=end ; ++iter)
133 {
134 const auto &match = *iter;
135 size_t i = match.position();
136 if (i>p) // add non-matching prefix
137 {
138 if (needsSeparator) result+="::";
139 needsSeparator=false;
140 result+=s.substr(p,i-p);
141 }
142 std::string delim = match.str();
143 needsSeparator = needsSeparator || (startsWithColon(delim) && endsWithColon(delim));
144 p = match.position()+match.length();
145 }
146 if (p<sl) // add trailing remainder
147 {
148 if (needsSeparator) result+="::";
149 result+=s.substr(p);
150 }
151 return result;
152}
153
154// replace anonymous scopes with __anonymous__ or replacement if provided
155DString replaceAnonymousScopes(const DString &s,const DString &replacement)
156{
157 if (s.empty()) return s;
158 static const reg::Ex marker(R"(@\d+)");
159 std::string result = reg::replace(s.str(),marker,
160 !replacement.empty() ? replacement.data() : "__anonymous__");
161 //printf("replaceAnonymousScopes('%s')='%s'\n",qPrint(s),qPrint(result));
162 return result;
163}
164
165
166// strip anonymous left hand side part of the scope
168{
169 int i=0,p=0,l=0;
170 DString newScope;
171 int sl = static_cast<int>(s.length());
172 while ((i=getScopeFragment(s,p,&l))!=-1)
173 {
174 //printf("Scope fragment %s\n",qPrint(s.mid(i,l)));
175 if (Doxygen::namespaceLinkedMap->find(s.left(i+l))!=nullptr)
176 {
177 if (s.at(i)!='@')
178 {
179 if (!newScope.empty()) newScope+="::";
180 newScope+=s.mid(i,l);
181 }
182 }
183 else if (i<sl)
184 {
185 if (!newScope.empty()) newScope+="::";
186 newScope+=s.right(sl-i);
187 goto done;
188 }
189 p=i+l;
190 }
191done:
192 //printf("stripAnonymousNamespaceScope('%s')='%s'\n",qPrint(s),qPrint(newScope));
193 return newScope;
194}
195
196static inline DString stripFromPath(const DString &p,StringVector l)
197{
198 // look at all the strings in the list and strip the longest match
199 DString potential;
201 size_t length = 0;
202 for (const auto &s : l)
203 {
204 DString prefix = s;
205 if (prefix.length() > length &&
206 dstricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
207 {
208 length = prefix.length();
209 potential = path.mid(prefix.length());
210 }
211 }
212 if (length>0) return potential;
213 return path;
214}
215
216/*! strip part of \a path if it matches
217 * one of the paths in the Config_getList(STRIP_FROM_PATH) list
218 */
220{
221 return stripFromPath(path,Config_getList(STRIP_FROM_PATH));
222}
223
224/*! strip part of \a path if it matches
225 * one of the paths in the Config_getList(INCLUDE_PATH) list
226 */
228{
229 return stripFromPath(path,Config_getList(STRIP_FROM_INC_PATH));
230}
231
232DString resolveTypeDef(const Definition *context,const DString &qualifiedName,
233 const Definition **typedefContext)
234{
235 AUTO_TRACE("context='{}' qualifiedName='{}'",context?context->name():"",qualifiedName);
236 DString result;
237 if (qualifiedName.empty())
238 {
239 AUTO_TRACE_EXIT("empty name");
240 return result;
241 }
242
243 const Definition *mContext=context;
244 if (typedefContext) *typedefContext=context;
245
246 // see if the qualified name has a scope part
247 if (qualifiedName.find('<')!=DString::npos)
248 {
249 AUTO_TRACE_EXIT("template");
250 return result;
251 }
252 size_t scopeIndex = qualifiedName.rfind("::");
253 DString resName=qualifiedName;
254 if (scopeIndex!=DString::npos) // strip scope part for the name
255 {
256 resName=qualifiedName.mid(scopeIndex+2);
257 if (resName.empty())
258 {
259 AUTO_TRACE_EXIT("invalid format");
260 return result;
261 }
262 }
263 const MemberDef *md=nullptr;
264 while (mContext && md==nullptr)
265 {
266 // step 1: get the right scope
267 const Definition *resScope=mContext;
268 if (scopeIndex!=DString::npos)
269 {
270 // split-off scope part
271 DString resScopeName = qualifiedName.left(scopeIndex);
272 //printf("resScopeName='%s'\n",qPrint(resScopeName));
273
274 // look-up scope in context
275 int is=0,ps=0,l=0;
276 while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
277 {
278 DString qualScopePart = resScopeName.mid(is,l);
279 DString tmp = resolveTypeDef(mContext,qualScopePart);
280 if (!tmp.empty()) qualScopePart=tmp;
281 resScope = resScope->findInnerCompound(qualScopePart);
282 //printf("qualScopePart='%s' resScope=%p\n",qPrint(qualScopePart),resScope);
283 if (resScope==nullptr) break;
284 ps=is+l;
285 }
286 }
287 AUTO_TRACE_ADD("resScope='{}' resName='{}'",resScope?resScope->name():"",resName);
288
289 // step 2: get the member
290 if (resScope) // no scope or scope found in the current context
291 {
292 //printf("scope found: %s, look for typedef %s\n",
293 // qPrint(resScope->qualifiedName()),qPrint(resName));
294 MemberNameLinkedMap *mnd=nullptr;
295 bool searchRelated=false;
296 bool mustBeRelated=false;
297 if (resScope->definitionType()==Definition::TypeClass)
298 {
300 }
301 else
302 {
304 searchRelated=true;
305 }
306 MemberName *mn=mnd->find(resName);
307 if (mn==0 && searchRelated)
308 {
310 mustBeRelated=true;
311 }
312 if (mn)
313 {
314 int minDist=-1;
315 for (const auto &tmd_p : *mn)
316 {
317 const MemberDef *tmd = tmd_p.get();
318 AUTO_TRACE_ADD("found candidate member '{}' isTypeDef={}' isRelated={} mustBeRelated={}",
319 tmd->name(),tmd->isTypedef(),tmd->isRelated(),mustBeRelated);
320 //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
321 // qPrint(tmd->name()),qPrint( resScope->name()),
322 // qPrint(tmd->getOuterScope()->name()), mContext);
323 if (tmd->isTypedef())
324 {
325 if (resScope==Doxygen::globalScope && tmd->isRelated() && mustBeRelated)
326 {
327 md = tmd;
328 }
329 else
330 {
331 SymbolResolver resolver;
332 int dist=resolver.isAccessibleFrom(resScope,tmd);
333 if (dist!=-1 && (md==nullptr || dist<minDist))
334 {
335 md = tmd;
336 minDist = dist;
337 }
338 }
339 }
340 }
341 }
342 }
343 mContext=mContext->getOuterScope();
344 }
345
346 AUTO_TRACE_ADD("md='{}'",md?md->name():"");
347 // step 3: get the member's type
348 if (md)
349 {
350 //printf(">>resolveTypeDef: Found typedef name '%s' in scope '%s' value='%s' args='%s'\n",
351 // qPrint(qualifiedName),qPrint(context->name()),qPrint(md->typeString()),qPrint(md->argsString())
352 // );
353 result=md->typeString();
354 DString args = md->argsString();
355 if (args.find(")(")!=DString::npos) // typedef of a function/member pointer
356 {
357 result+=args;
358 }
359 else if (args.find('[')!=DString::npos) // typedef of an array
360 {
361 result+=args;
362 }
363 if (typedefContext) *typedefContext=md->getOuterScope();
364 }
365 else
366 {
367 //printf(">>resolveTypeDef: Typedef '%s' not found in scope '%s'!\n",
368 // qPrint(qualifiedName),context ? qPrint(context->name()) : "<global>");
369 }
370 AUTO_TRACE_EXIT("result='{}'",result);
371 return result;
372}
373
374//-------------------------------------------------------------------------
375//-------------------------------------------------------------------------
376//-------------------------------------------------------------------------
377//-------------------------------------------------------------------------
378
379static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
380static const char volatileScope[] = { 'v', 'o', 'l', 'a', 't', 'i', 'l', 'e', ':' };
381static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
382static const char operatorScope[] = { 'o', 'p', 'e', 'r', 'a', 't', 'o', 'r', '?', '?', '?' };
383
385{
387 {
388 charMap[static_cast<int>('(')].before=false;
389 charMap[static_cast<int>('=')].before=false;
390 charMap[static_cast<int>('&')].before=false;
391 charMap[static_cast<int>('*')].before=false;
392 charMap[static_cast<int>('[')].before=false;
393 charMap[static_cast<int>('|')].before=false;
394 charMap[static_cast<int>('+')].before=false;
395 charMap[static_cast<int>(';')].before=false;
396 charMap[static_cast<int>(':')].before=false;
397 charMap[static_cast<int>('/')].before=false;
398
399 charMap[static_cast<int>('=')].after=false;
400 charMap[static_cast<int>(' ')].after=false;
401 charMap[static_cast<int>('[')].after=false;
402 charMap[static_cast<int>(']')].after=false;
403 charMap[static_cast<int>('\t')].after=false;
404 charMap[static_cast<int>('\n')].after=false;
405 charMap[static_cast<int>(')')].after=false;
406 charMap[static_cast<int>(',')].after=false;
407 charMap[static_cast<int>('<')].after=false;
408 charMap[static_cast<int>('|')].after=false;
409 charMap[static_cast<int>('+')].after=false;
410 charMap[static_cast<int>('(')].after=false;
411 charMap[static_cast<int>('/')].after=false;
412 }
413 struct CharElem
414 {
415 CharElem() : before(true), after(true) {}
416 bool before;
417 bool after;
418 };
419
421};
422
424
425// Note: this function is not reentrant due to the use of static buffer!
427{
428 bool cliSupport = Config_getBool(CPP_CLI_SUPPORT);
429 bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
430
431 if (s.empty() || vhdl) return s;
432
433 // We use a static character array to
434 // improve the performance of this function
435 // and thread_local is needed to make it multi-thread safe
436 static THREAD_LOCAL char *growBuf = nullptr;
437 static THREAD_LOCAL size_t growBufLen = 0;
438 if (s.length()*3>growBufLen) // For input character we produce at most 3 output characters,
439 {
440 growBufLen = s.length()*3;
441 growBuf = static_cast<char *>(realloc(growBuf,growBufLen+1)); // add 1 for 0-terminator
442 }
443 if (growBuf==nullptr) return s; // should not happen, only we run out of memory
444
445 const char *src=s.data();
446 char *dst=growBuf;
447
448 size_t i=0;
449 size_t l=s.length();
450 size_t csp=0;
451 size_t vosp=0;
452 size_t vsp=0;
453 size_t osp=0;
454 char pc=0;
455 // skip leading whitespace
456 while (i<l && isspace(static_cast<uint8_t>(src[i])))
457 {
458 i++;
459 }
460 for (;i<l;i++)
461 {
462 char c=src[i];
463 char nc=i+1<l ? src[i+1] : ' ';
464
465 auto searchForKeyword = [&](const char *kw,size_t &matchLen,size_t totalLen)
466 {
467 if (matchLen<=totalLen && c==kw[matchLen] && // character matches substring kw
468 (matchLen>0 || // inside search string
469 i==0 || // if it is the first character
470 !isId(pc) // the previous may not be a digit
471 )
472 )
473 matchLen++;
474 else // reset counter
475 matchLen=0;
476 };
477 searchForKeyword(constScope, csp, 5); // keyword: const
478 searchForKeyword(volatileScope, vosp, 8); // keyword: volatile
479 searchForKeyword(virtualScope, vsp, 7); // keyword: virtual
480
481 // search for "operator"
482 if (osp<11 && (osp>=8 || c==operatorScope[osp]) && // character matches substring "operator" followed by 3 arbitrary characters
483 (osp>0 || // inside search string
484 i==0 || // if it is the first character
485 !isId(pc) // the previous may not be a digit
486 )
487 )
488 osp++;
489 else // reset counter
490 osp=0;
491
492 switch(c)
493 {
494 case '"': // quoted string
495 {
496 *dst++=c;
497 i++;
498 for (;i<l;i++) // find end of string
499 {
500 c = src[i];
501 *dst++=c;
502 if (c=='\\' && i+1<l)
503 {
504 i++;
505 c = src[i];
506 *dst++=c;
507 }
508 else if (c=='"')
509 {
510 break;
511 }
512 }
513 }
514 break;
515 case '<': // current char is a <
516 *dst++=c;
517 if (i+1<l &&
518 (isId(nc)) && // next char is an id char
519 (osp<8) // string in front is not "operator"
520 )
521 {
522 *dst++=' '; // add extra space
523 }
524 break;
525 case '>': // current char is a >
526 if (i>0 && !isspace(static_cast<uint8_t>(pc)) &&
527 (isId(pc) || pc=='*' || pc=='&' || pc=='.' || pc=='>') && // prev char is an id char or space or *&.
528 (osp<8 || (osp==8 && pc!='-')) // string in front is not "operator>" or "operator->"
529 )
530 {
531 *dst++=' '; // add extra space in front
532 }
533 *dst++=c;
534 if (i+1<l && (nc=='-' || nc=='&')) // '>-' -> '> -'
535 {
536 *dst++=' '; // add extra space after
537 }
538 break;
539 case ',': // current char is a ,
540 *dst++=c;
541 if (i>0 && !isspace(static_cast<uint8_t>(pc)) &&
542 ((i+1<l && (isId(nc) || nc=='[')) || // the [ is for attributes (see bug702170)
543 (i+2<l && nc=='$' && isId(src[i+2])) || // for PHP: ',$name' -> ', $name'
544 (i+3<l && nc=='&' && src[i+2]=='$' && isId(src[i+3])) // for PHP: ',&$name' -> ', &$name'
545 )
546 )
547 {
548 *dst++=' '; // add extra space after
549 }
550 break;
551 case '^': // CLI 'Type^name' -> 'Type^ name'
552 case '%': // CLI 'Type%name' -> 'Type% name'
553 *dst++=c;
554 if (cliSupport && i+1<l && (isId(nc) || nc=='-'))
555 {
556 *dst++=' '; // add extra space after
557 }
558 break;
559 case ')': // current char is a ) -> ')name' -> ') name'
560 *dst++=c;
561 if (i+1<l && (isId(nc) || nc=='-'))
562 {
563 *dst++=' '; // add extra space after
564 }
565 break;
566 case '*':
567 if (i>0 && pc!=' ' && pc!='\t' && pc!=':' &&
568 pc!='*' && pc!='&' && pc!='(' && pc!='/' && pc!='[' &&
569 pc!='.' && osp<9
570 )
571 // avoid splitting &&, **, .*, operator*, operator->*
572 {
573 *dst++=' ';
574 }
575 *dst++=c;
576 break;
577 case '&':
578 if (i>0 && isId(pc) && osp<9)
579 {
580 if (nc != '=')
581 // avoid splitting operator&=
582 {
583 *dst++=' ';
584 }
585 }
586 *dst++=c;
587 break;
588 case '$': // '$name' -> ' $name'
589 // 'name$name' -> 'name$name'
590 if (isId(pc))
591 {
592 *dst++=c;
593 break;
594 }
595 // else fallthrough
596 case '@': // '@name' -> ' @name'
597 case '\'': // ''name' -> '' name'
598 if (i>0 && i+1<l && pc!='=' && pc!=':' && !isspace(static_cast<uint8_t>(pc)) &&
599 isId(nc) && osp<8) // ")id" -> ") id"
600 {
601 *dst++=' ';
602 }
603 *dst++=c;
604 break;
605 case ':': // current char is a :
606 if (csp==6) // replace const::A by const ::A
607 {
608 *dst++=' ';
609 csp=0;
610 }
611 else if (vosp==9) // replace volatile::A by volatile ::A
612 {
613 *dst++=' ';
614 vosp=0;
615 }
616 else if (vsp==8) // replace virtual::A by virtual ::A
617 {
618 *dst++=' ';
619 vsp=0;
620 }
621 *dst++=c;
622 break;
623 case ' ': // fallthrough
624 case '\n': // fallthrough
625 case '\t':
626 {
627 if (g_charAroundSpace.charMap[static_cast<uint8_t>(pc)].before &&
628 g_charAroundSpace.charMap[static_cast<uint8_t>(nc)].after &&
629 !(pc==',' && nc=='.') &&
630 (osp<8 || (osp>=8 && isId(pc) && isId(nc)))
631 // e.g. 'operator >>' -> 'operator>>',
632 // 'operator "" _x' -> 'operator""_x',
633 // but not 'operator int' -> 'operatorint'
634 )
635 { // keep space
636 *dst++=' ';
637 }
638 else if ((pc=='*' || pc=='&' || pc=='.') && nc=='>')
639 {
640 *dst++=' ';
641 }
642 }
643 break;
644 default:
645 *dst++=c;
646 auto correctKeywordAllowedInsideScope = [&](char cc,size_t &matchLen,size_t totalLen) {
647 if (c==cc && matchLen==totalLen)
648 {
649 if ((i+2<l && src[i+1] == ':' && src[i+2] == ':') || // keyword::
650 ((i>matchLen && src[i-matchLen] == ':' && src[i-matchLen-1] == ':')) // ::keyword
651 ) matchLen = 0;
652 };
653 };
654 correctKeywordAllowedInsideScope('t',csp, 5); // keyword: const
655 correctKeywordAllowedInsideScope('e',vosp,8); // keyword: volatile
656 correctKeywordAllowedInsideScope('l',vsp, 7); // keyword: virtual
657
658 auto correctKeywordNotPartOfScope = [&](char cc,size_t &matchLen,size_t totalLen)
659 {
660 if (c==cc && matchLen==totalLen && i+1<l && // found matching keyword
661 !(isId(nc) || nc==')' || nc==',' || disspace(nc))
662 ) // prevent keyword ::A from being converted to keyword::A
663 {
664 *dst++=' ';
665 matchLen=0;
666 }
667 };
668 correctKeywordNotPartOfScope('t',csp, 5); // keyword: const
669 correctKeywordNotPartOfScope('e',vosp,8); // keyword: volatile
670 correctKeywordNotPartOfScope('l',vsp, 7); // keyword: virtual
671 break;
672 }
673 pc=c;
674 }
675 *dst++='\0';
676 //printf("removeRedundantWhitespace(%s)->%s\n",qPrint(s),growBuf);
677 return growBuf;
678}
679
680/**
681 * Returns the position in the string where a function parameter list
682 * begins, or DString::npos if one is not found.
683 */
684static size_t findParameterList(const DString &name)
685{
686 size_t pos=DString::npos;
687 int templateDepth=0;
688 do
689 {
690 if (templateDepth > 0)
691 {
692 size_t nextOpenPos = name.rfind('>', pos);
693 size_t nextClosePos = name.rfind('<', pos);
694 if (nextOpenPos!=DString::npos && nextClosePos!=DString::npos && nextOpenPos>nextClosePos)
695 {
696 ++templateDepth;
697 pos=nextOpenPos-1;
698 }
699 else if (nextClosePos!=DString::npos)
700 {
701 --templateDepth;
702 pos=nextClosePos-1;
703 }
704 else // more >'s than <'s, see bug701295
705 {
706 return -1;
707 }
708 }
709 else
710 {
711 size_t lastAnglePos = name.rfind('>', pos);
712 size_t bracePos = name.rfind('(', pos);
713 if (lastAnglePos!=DString::npos && bracePos!=DString::npos && lastAnglePos>bracePos)
714 {
715 ++templateDepth;
716 pos=lastAnglePos-1;
717 }
718 else
719 {
720 size_t bp = bracePos>0 ? name.rfind('(',bracePos-1) : DString::npos;
721 // bp test is to allow foo(int(&)[10]), but we need to make an exception for operator()
722 return bp==DString::npos || (bp>=8 && name.mid(bp-8,10)=="operator()") ? bracePos : bp;
723 }
724 }
725 } while (pos!=DString::npos);
726 return DString::npos;
727}
728
729bool rightScopeMatch(const DString &scope, const DString &name)
730{
731 size_t sl=scope.length();
732 size_t nl=name.length();
733 return (name==scope || // equal
734 (scope.right(nl)==name && // substring
735 sl>1+nl && scope.at(sl-nl-1)==':' && scope.at(sl-nl-2)==':' // scope
736 )
737 );
738}
739
740bool leftScopeMatch(const DString &scope, const DString &name)
741{
742 size_t sl=scope.length();
743 size_t nl=name.length();
744 return (name==scope || // equal
745 (name.left(sl)==scope && // substring
746 nl>sl+1 && name.at(sl)==':' && name.at(sl+1)==':' // scope
747 )
748 );
749}
750
751
752
753void writeMarkerList(OutputList &ol,const std::string &markerText,size_t numMarkers,
754 std::function<void(size_t)> replaceFunc)
755{
756 static const reg::Ex marker(R"(@(\d+))");
757 reg::Iterator it(markerText,marker);
759 size_t index=0;
760 for ( ; it!=end ; ++it)
761 {
762 const auto &match = *it;
763 size_t newIndex = match.position();
764 size_t matchLen = match.length();
765 ol.parseText(markerText.substr(index,newIndex-index));
766 unsigned long entryIndex = std::stoul(match[1].str());
767 if (entryIndex<static_cast<unsigned long>(numMarkers))
768 {
769 replaceFunc(entryIndex);
770 }
771 index=newIndex+matchLen;
772 }
773 ol.parseText(markerText.substr(index));
774}
775
776DString writeMarkerList(const std::string &markerText,size_t numMarkers,
777 std::function<DString(size_t)> replaceFunc)
778{
779 DString result;
780 static const reg::Ex marker(R"(@(\d+))");
781 reg::Iterator it(markerText,marker);
783 size_t index=0;
784 for ( ; it!=end ; ++it)
785 {
786 const auto &match = *it;
787 size_t newIndex = match.position();
788 size_t matchLen = match.length();
789 result += markerText.substr(index,newIndex-index);
790 unsigned long entryIndex = std::stoul(match[1].str());
791 if (entryIndex<static_cast<unsigned long>(numMarkers))
792 {
793 result+=replaceFunc(entryIndex);
794 }
795 index=newIndex+matchLen;
796 }
797 if (index<markerText.size())
798 {
799 result += markerText.substr(index);
800 }
801 return result;
802}
803
805{
806 auto replaceFunc = [&list,&ol](size_t entryIndex)
807 {
808 const auto &e = list[entryIndex];
813 // link for Html / man
814 //printf("writeObjectLink(file=%s)\n",qPrint(e->file));
815 ol.writeObjectLink(DString(),e.file,e.anchor,e.name);
817
821 // link for Latex / pdf with anchor because the sources
822 // are not hyperlinked (not possible with a verbatim environment).
823 ol.writeObjectLink(DString(),e.file,DString(),e.name);
825 };
826
827 writeMarkerList(ol, theTranslator->trWriteList(static_cast<int>(list.size())).str(), list.size(), replaceFunc);
828
829 ol.writeString(".");
830}
831
833{
834 DString paramDocs;
836 {
837 for (const Argument &a : al)
838 {
839 if (!a.docs.empty())
840 {
841 if (!a.name.empty())
842 {
843 paramDocs+=" \\ilinebr @tparam "+a.name+" "+a.docs;
844 }
845 else if (!a.type.empty())
846 {
847 DString type = a.type;
848 type.stripPrefix("class ");
849 type.stripPrefix("typename ");
850 type = type.stripWhiteSpace();
851 paramDocs+=" \\ilinebr @tparam "+type+" "+a.docs;
852 }
853 }
854 }
855 }
856 return paramDocs;
857}
858
859DString argListToString(const ArgumentList &al,bool useCanonicalType,bool showDefVals)
860{
861 DString result;
862 if (!al.hasParameters()) return result;
863 result+="(";
864 for (auto it = al.begin() ; it!=al.end() ;)
865 {
866 Argument a = *it;
867 DString type1 = useCanonicalType && !a.canType.empty() ? a.canType : a.type;
868 DString type2;
869 if (size_t i=type1.find(")("); i!=DString::npos) // hack to deal with function pointers
870 {
871 type2=type1.mid(i);
872 type1=type1.left(i);
873 }
874 if (!a.attrib.empty())
875 {
876 result+=a.attrib+" ";
877 }
878 if (!a.name.empty() || !a.array.empty())
879 {
880 result+= type1+" "+a.name+type2+a.array;
881 }
882 else
883 {
884 result+= type1+type2;
885 }
886 if (!a.defval.empty() && showDefVals)
887 {
888 result+="="+a.defval;
889 }
890 ++it;
891 if (it!=al.end()) result+=", ";
892 }
893 result+=")";
894 if (al.constSpecifier()) result+=" const";
895 if (al.volatileSpecifier()) result+=" volatile";
896 if (al.refQualifier()==RefQualifierType::LValue) result+=" &";
897 else if (al.refQualifier()==RefQualifierType::RValue) result+=" &&";
898 if (!al.trailingReturnType().empty()) result+=al.trailingReturnType();
899 if (al.pureSpecifier()) result+=" =0";
900 return removeRedundantWhiteSpace(result);
901}
902
903DString tempArgListToString(const ArgumentList &al,SrcLangExt lang,bool includeDefault)
904{
905 DString result;
906 if (al.empty()) return result;
907 result="<";
908 bool first=true;
909 for (const auto &a : al)
910 {
911 if (a.defval.empty() || includeDefault)
912 {
913 if (!first) result+=", ";
914 if (!a.name.empty()) // add template argument name
915 {
916 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp)
917 {
918 result+=a.type+" ";
919 }
920 result+=a.name;
921 }
922 else // extract name from type
923 {
924 int i = static_cast<int>(a.type.length())-1;
925 while (i>=0 && isId(a.type.at(i))) i--;
926 if (i>0)
927 {
928 result+=a.type.right(a.type.length()-i-1);
929 if (a.type.find("...")!=DString::npos)
930 {
931 result+="...";
932 }
933 }
934 else // nothing found -> take whole name
935 {
936 result+=a.type;
937 }
938 }
939 if (!a.typeConstraint.empty() && lang==SrcLangExt::Java)
940 {
941 result+=" extends "; // TODO: now Java specific, C# has where...
942 result+=a.typeConstraint;
943 }
944 first=false;
945 }
946 }
947 result+=">";
948 return removeRedundantWhiteSpace(result);
949}
950
951
952//----------------------------------------------------------------------------
953
954/*! takes the \a buf of the given length \a len and converts CR LF (DOS)
955 * or CR (MAC) line ending to LF (Unix). Returns the length of the
956 * converted content (i.e. the same as \a len (Unix, MAC) or
957 * smaller (DOS)).
958 */
959static void filterCRLF(std::string &contents)
960{
961 size_t src = 0; // source index
962 size_t dest = 0; // destination index
963 size_t len = contents.length();
964
965 while (src<len)
966 {
967 char c = contents[src++]; // Remember the processed character.
968 if (c == '\r') // CR to be solved (MAC, DOS)
969 {
970 c = '\n'; // each CR to LF
971 if (src<len && contents[src] == '\n')
972 {
973 ++src; // skip LF just after CR (DOS)
974 }
975 }
976 else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
977 {
978 c = ' '; // turn into a space
979 }
980 contents[dest++] = c; // copy the (modified) character to dest
981 }
982 contents.resize(dest);
983}
984
985static DString getFilterFromList(const DString &name,const StringVector &filterList,bool &found)
986{
987 found=false;
988 // compare the file name to the filter pattern list
989 for (const auto &filterStr : filterList)
990 {
991 DString fs = filterStr;
992 if (size_t i_equals=fs.find('='); i_equals!=DString::npos)
993 {
994 DString filterPattern = fs.left(i_equals);
995 DString input = name;
997 {
998 filterPattern = filterPattern.lower();
999 input = input.lower();
1000 }
1001 reg::Ex re(filterPattern.str(),reg::Ex::Mode::Wildcard);
1002 if (re.isValid() && reg::match(input.str(),re))
1003 {
1004 // found a match!
1005 DString filterName = fs.mid(i_equals+1);
1006 if (filterName.find(' ')!=DString::npos)
1007 { // add quotes if the name has spaces
1008 filterName="\""+filterName+"\"";
1009 }
1010 found=true;
1011 return filterName;
1012 }
1013 }
1014 }
1015
1016 // no match
1017 return "";
1018}
1019
1020DString getFileFilter(const DString &name,bool isSourceCode)
1021{
1022 // sanity check
1023 if (name.empty()) return "";
1024
1025 StringVector filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS);
1026 StringVector filterList = Config_getList(FILTER_PATTERNS);
1027
1028 DString filterName;
1029 bool found=false;
1030 if (isSourceCode && !filterSrcList.empty())
1031 { // first look for source filter pattern list
1032 filterName = getFilterFromList(name,filterSrcList,found);
1033 }
1034 if (!found && filterName.empty())
1035 { // then look for filter pattern list
1036 filterName = getFilterFromList(name,filterList,found);
1037 }
1038 if (!found)
1039 { // then use the generic input filter
1040 return Config_getString(INPUT_FILTER);
1041 }
1042 else
1043 {
1044 /* remove surrounding double quotes */
1045 if (filterName.length()>=2 && filterName[0]=='"' && filterName[static_cast<int>(filterName.length())-1]=='"')
1046 {
1047 filterName = filterName.mid(1,filterName.length()-2);
1048 }
1049 return filterName;
1050 }
1051}
1052
1053DString fileToString(const DString &name,bool filter,bool isSourceCode)
1054{
1055 if (name.empty()) return DString();
1056 bool fileOpened=false;
1057 if (name[0]=='-' && name[1]==0) // read from stdin
1058 {
1059 std::string contents;
1060 std::string line;
1061 while (getline(std::cin,line))
1062 {
1063 contents+=line+'\n';
1064 }
1065 return contents;
1066 }
1067 else // read from file
1068 {
1069 FileInfo fi(name.str());
1070 if (!fi.exists() || !fi.isFile())
1071 {
1072 err("file '{}' not found\n",name);
1073 return "";
1074 }
1075 std::string buf;
1076 fileOpened=readInputFile(name,buf,filter,isSourceCode);
1077 if (fileOpened)
1078 {
1079 addTerminalCharIfMissing(buf,'\n');
1080 return buf;
1081 }
1082 }
1083 if (!fileOpened)
1084 {
1085 err("cannot open file '{}' for reading\n",name);
1086 }
1087 return "";
1088}
1089
1090void trimBaseClassScope(const BaseClassList &bcl,DString &s,int level=0)
1091{
1092 //printf("trimBaseClassScope level=%d '%s'\n",level,qPrint(s));
1093 for (const auto &bcd : bcl)
1094 {
1095 ClassDef *cd=bcd.classDef;
1096 //printf("Trying class %s\n",qPrint(cd->name()));
1097 if (size_t spos=s.find(cd->name()+"::"); spos!=DString::npos)
1098 {
1099 s = s.left(spos)+s.right(
1100 s.length()-spos-cd->name().length()-2
1101 );
1102 }
1103 //printf("base class '%s'\n",qPrint(cd->name()));
1104 if (!cd->baseClasses().empty())
1105 {
1106 trimBaseClassScope(cd->baseClasses(),s,level+1);
1107 }
1108 }
1109}
1110
1111static void stripIrrelevantString(DString &target,const DString &str,bool insideTemplate)
1112{
1113 AUTO_TRACE("target='{}' str='{}'",target,str);
1114 if (target==str) { target.clear(); return; }
1115 size_t i=0,p=0;
1116 size_t l=str.length();
1117 bool changed=false;
1118 int sharpCount=0;
1119 while ((i=target.find(str,p))!=DString::npos)
1120 {
1121 for (size_t q=p;q<i;q++)
1122 {
1123 if (target[q]=='<') sharpCount++;
1124 else if (target[q]=='>' && sharpCount>0) sharpCount--;
1125 }
1126 bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
1127 (i+l==target.length() || !isId(target.at(i+l))) && // not a character after str
1128 !insideTemplate && sharpCount==0; // not inside template, because e.g. <const A> is different than <A>, see issue #11663
1129 if (isMatch)
1130 {
1131 size_t i1=target.find('*',i+l);
1132 size_t i2=target.find('&',i+l);
1133 if (i1==DString::npos && i2==DString::npos)
1134 {
1135 // strip str from target at index i
1136 target=target.left(i)+target.mid(i+l);
1137 changed=true;
1138 i-=l;
1139 }
1140 else if ((i1!=DString::npos && i<i1) || (i2!=DString::npos && i<i2)) // str before * or &
1141 {
1142 // move str to front
1143 target=str+" "+target.left(i)+target.mid(i+l);
1144 changed=true;
1145 i++;
1146 }
1147 }
1148 p = i+l;
1149 }
1150 if (changed) target=target.stripWhiteSpace();
1151 AUTO_TRACE_EXIT("target='{}'",target,str);
1152}
1153
1154/*! According to the C++ spec and Ivan Vecerina:
1155
1156 Parameter declarations that differ only in the presence or absence
1157 of const and/or volatile are equivalent.
1158
1159 So the following example, show what is stripped by this routine
1160 for const. The same is done for volatile.
1161
1162 For Java code we also strip the "final" keyword, see bug 765070.
1163
1164 \code
1165 const T param -> T param // not relevant
1166 const T& param -> const T& param // const needed
1167 T* const param -> T* param // not relevant
1168 const T* param -> const T* param // const needed
1169 \endcode
1170 */
1171void stripIrrelevantConstVolatile(DString &s,bool insideTemplate)
1172{
1173 //printf("stripIrrelevantConstVolatile(%s)=",qPrint(s));
1174 stripIrrelevantString(s,"const",insideTemplate);
1175 stripIrrelevantString(s,"volatile",insideTemplate);
1176 stripIrrelevantString(s,"final",insideTemplate);
1177 //printf("%s\n",qPrint(s));
1178}
1179
1180
1182{
1183 size_t i=s.find(" class ");
1184 if (i!=DString::npos) return s.left(i)+s.mid(i+6);
1185 i=s.find(" typename ");
1186 if (i!=DString::npos) return s.left(i)+s.mid(i+9);
1187 i=s.find(" union ");
1188 if (i!=DString::npos) return s.left(i)+s.mid(i+6);
1189 i=s.find(" struct ");
1190 if (i!=DString::npos) return s.left(i)+s.mid(i+7);
1191 return s;
1192}
1193
1194// forward decl for circular dependencies
1195static DString extractCanonicalType(const Definition *d,const FileDef *fs,DString type,SrcLangExt lang,bool insideTemplate);
1196
1197static DString getCanonicalTemplateSpec(const Definition *d,const FileDef *fs,const DString& spec,SrcLangExt lang)
1198{
1199 AUTO_TRACE("spec={}",spec);
1200 DString templSpec = spec.stripWhiteSpace();
1201 // this part had been commented out before... but it is needed to match for instance
1202 // std::list<std::string> against list<string> so it is now back again!
1203 if (!templSpec.empty() && templSpec.at(0) == '<')
1204 {
1205 templSpec = "< " + extractCanonicalType(d,fs,templSpec.mid(1).stripWhiteSpace(),lang,true);
1206 }
1207 DString resolvedType = lang==SrcLangExt::Java ? templSpec : resolveTypeDef(d,templSpec);
1208 if (!resolvedType.empty()) // not known as a typedef either
1209 {
1210 templSpec = resolvedType;
1211 }
1212 //printf("getCanonicalTemplateSpec(%s)=%s\n",qPrint(spec),qPrint(templSpec));
1213 AUTO_TRACE_EXIT("result={}",templSpec);
1214 return templSpec;
1215}
1216
1217
1219 const Definition *d,const FileDef *fs,const DString &word,SrcLangExt lang,
1220 DString *tSpec,int count=0)
1221{
1222 if (count>10) return word; // oops recursion
1223
1224 DString symName,result,templSpec,tmpName;
1225 if (tSpec && !tSpec->empty())
1226 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec,lang));
1227
1228 AUTO_TRACE("d='{}' fs='{}' word='{}' templSpec='{}'",d?d->name():"",fs?fs->name():"",word,templSpec);
1229
1230 if (word.rfind("::")!=DString::npos && !(tmpName=stripScope(word)).empty())
1231 {
1232 symName=tmpName; // name without scope
1233 }
1234 else
1235 {
1236 symName=word;
1237 }
1238
1239 // lookup class / class template instance
1240 SymbolResolver resolver(fs);
1241 const ClassDef *cd = resolver.resolveClass(d,word+templSpec,true,true);
1242 const MemberDef *mType = resolver.getTypedef();
1243 DString ts = resolver.getTemplateSpec();
1244 DString resolvedType = resolver.getResolvedType();
1245
1246 bool isTemplInst = cd && !templSpec.empty();
1247 if (!cd && !templSpec.empty())
1248 {
1249 // class template specialization not known, look up class template
1250 cd = resolver.resolveClass(d,word,true,true);
1251 mType = resolver.getTypedef();
1252 ts = resolver.getTemplateSpec();
1253 resolvedType = resolver.getResolvedType();
1254 }
1255 if (cd && cd->isUsedOnly()) cd=nullptr; // ignore types introduced by usage relations
1256
1257 AUTO_TRACE_ADD("cd='{}' mType='{}' ts='{}' resolvedType='{}'",
1258 cd?cd->name():"",mType?mType->name():"",ts,resolvedType);
1259 //printf("cd=%p mtype=%p\n",cd,mType);
1260 //printf(" getCanonicalTypeForIdentifier: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
1261 // qPrint(symName),
1262 // qPrint(word),
1263 // cd ? qPrint(cd->name()) : "<none>",
1264 // d ? qPrint( d->name()) : "<none>",
1265 // fs ? qPrint(fs->name()) : "<none>",
1266 // cd ? cd->isTemplate():-1
1267 // );
1268
1269 //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
1270 // qPrint((word+templSpec)),
1271 // cd ? qPrint(cd->qualifiedName()) : "<none>",
1272 // qPrint(templSpec), qPrint(ts),
1273 // tSpec ? qPrint(tSpec) : "<null>",
1274 // cd ? cd->isTemplate():false,
1275 // qPrint(resolvedType));
1276
1277 //printf(" mtype=%s\n",mType ? qPrint(mType->name()) : "<none>");
1278
1279 if (cd) // resolves to a known class type
1280 {
1281 if (cd==d && tSpec) *tSpec="";
1282
1283 if (mType && mType->isTypedef()) // but via a typedef
1284 {
1285 result = resolvedType+ts; // the +ts was added for bug 685125
1286 }
1287 else
1288 {
1289 if (isTemplInst)
1290 {
1291 // spec is already part of class type
1292 templSpec="";
1293 if (tSpec) *tSpec="";
1294 }
1295 else if (!ts.empty() && templSpec.empty())
1296 {
1297 // use formal template args for spec
1298 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts,lang));
1299 }
1300
1301 result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
1302
1303 if (cd->isTemplate() && tSpec) //
1304 {
1305 if (!templSpec.empty()) // specific instance
1306 {
1307 result=cd->name()+templSpec;
1308 }
1309 else // use template type
1310 {
1312 }
1313 // template class, so remove the template part (it is part of the class name)
1314 *tSpec="";
1315 }
1316 else if (ts.empty() && !templSpec.empty() && cd && !cd->isTemplate() && tSpec)
1317 {
1318 // obscure case, where a class is used as a template, but doxygen think it is
1319 // not (could happen when loading the class from a tag file).
1320 *tSpec="";
1321 }
1322 }
1323 }
1324 else if (mType && mType->isEnumerate()) // an enum
1325 {
1326 result = mType->qualifiedName();
1327 }
1328 else if (mType && mType->isTypedef()) // a typedef
1329 {
1330 //result = mType->qualifiedName(); // changed after 1.7.2
1331 //result = mType->typeString();
1332 //printf("word=%s typeString=%s\n",qPrint(word),mType->typeString());
1333 if (word!=mType->typeString())
1334 {
1335 DString type = mType->typeString();
1336 if (type.startsWith("typename "))
1337 {
1338 type.stripPrefix("typename ");
1339 type = stripTemplateSpecifiersFromScope(type,false);
1340 }
1341 if (!type.empty()) // see issue #11065
1342 {
1343 result = getCanonicalTypeForIdentifier(d,fs,type,mType->getLanguage(),tSpec,count+1);
1344 }
1345 else
1346 {
1347 result = word;
1348 }
1349 }
1350 else
1351 {
1352 result = mType->typeString();
1353 }
1354 }
1355 else // fallback
1356 {
1357 resolvedType = lang==SrcLangExt::Java ? word : resolveTypeDef(d,word);
1358 AUTO_TRACE_ADD("fallback resolvedType='{}'",resolvedType);
1359 if (resolvedType.empty()) // not known as a typedef either
1360 {
1361 result = word;
1362 }
1363 else
1364 {
1365 result = resolvedType;
1366 }
1367 }
1368 AUTO_TRACE_EXIT("result='{}'",result);
1369 return result;
1370}
1371
1372static DString extractCanonicalType(const Definition *d,const FileDef *fs,DString type,SrcLangExt lang,bool insideTemplate)
1373{
1374 AUTO_TRACE("d={} fs={} type='{}'",d?d->name():"",fs?fs->name():"",type);
1375 type = type.stripWhiteSpace();
1376
1377 // strip const and volatile keywords that are not relevant for the type
1378 stripIrrelevantConstVolatile(type,insideTemplate);
1379
1380 // strip leading keywords
1381 type.stripPrefix("class ");
1382 type.stripPrefix("struct ");
1383 type.stripPrefix("union ");
1384 type.stripPrefix("enum ");
1385 type.stripPrefix("typename ");
1386
1387 type = removeRedundantWhiteSpace(type);
1388 //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",qPrint(type),
1389 // d ? qPrint(d->name()) : "<null>", fs ? qPrint(fs->name()) : "<null>");
1390
1391 DString canType;
1392 DString templSpec,word;
1393 int i=0,p=0,pp=0;
1394 while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
1395 // foreach identifier in the type
1396 {
1397 //printf(" i=%d p=%d\n",i,p);
1398 if (i>pp)
1399 {
1400 if (i-pp>=2 && type[i-2]==':' && type[i-1]==':') // skip over leading ::, see issue #12021
1401 {
1402 canType += type.mid(pp,i-pp-2);
1403 }
1404 else
1405 {
1406 canType += type.mid(pp,i-pp);
1407 }
1408 }
1409
1410 DString ct = getCanonicalTypeForIdentifier(d,fs,word,lang,&templSpec);
1411
1412 // in case the ct is empty it means that "word" represents scope "d"
1413 // and this does not need to be added to the canonical
1414 // type (it is redundant), so/ we skip it. This solves problem 589616.
1415 if (ct.empty() && type.mid(p,2)=="::")
1416 {
1417 p+=2;
1418 }
1419 else
1420 {
1421 canType += ct;
1422 }
1423 //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
1424 // qPrint(word), qPrint(templSpec), qPrint(canType), qPrint(ct));
1425 if (!templSpec.empty()) // if we didn't use up the templSpec already
1426 // (i.e. type is not a template specialization)
1427 // then resolve any identifiers inside.
1428 {
1429 std::string ts = templSpec.str();
1430 static const reg::Ex re(R"(\a\w*)");
1431 reg::Iterator it(ts,re);
1433
1434 size_t tp=0;
1435 // for each identifier template specifier
1436 //printf("adding resolved %s to %s\n",qPrint(templSpec),qPrint(canType));
1437 for (; it!=end ; ++it)
1438 {
1439 const auto &match = *it;
1440 size_t ti = match.position();
1441 size_t tl = match.length();
1442 std::string matchStr = match.str();
1443 canType += ts.substr(tp,ti-tp);
1444 canType += getCanonicalTypeForIdentifier(d,fs,matchStr,lang,nullptr);
1445 tp=ti+tl;
1446 }
1447 canType+=ts.substr(tp);
1448 }
1449
1450 pp=p;
1451 }
1452 canType += type.mid(pp);
1453 AUTO_TRACE_EXIT("canType='{}'",canType);
1454
1455 return removeRedundantWhiteSpace(canType);
1456}
1457
1458static DString extractCanonicalArgType(const Definition *d,const FileDef *fs,const Argument &arg,SrcLangExt lang)
1459{
1460 DString type = arg.type.stripWhiteSpace();
1461 DString name = arg.name;
1462 //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",qPrint(type),qPrint(name));
1463 if ((type=="const" || type=="volatile") && !name.empty())
1464 { // name is part of type => correct
1465 type+=" ";
1466 type+=name;
1467 }
1468 if (name=="const" || name=="volatile")
1469 { // name is part of type => correct
1470 if (!type.empty()) type+=" ";
1471 type+=name;
1472 }
1473 if (!arg.array.empty())
1474 {
1475 type+=arg.array;
1476 }
1477
1478 return extractCanonicalType(d,fs,type,lang,false);
1479}
1480
1481static std::mutex g_matchArgsMutex;
1482
1483// a bit of debug support for matchArguments
1484//#define MATCH
1485//#define NOMATCH
1486//#define MATCH printf("Match at line %d\n",__LINE__);
1487//#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
1488#define MATCH AUTO_TRACE_EXIT("match at line {}",__LINE__);
1489#define NOMATCH AUTO_TRACE_EXIT("no match at line {}",__LINE__);
1490
1492 const Definition *srcScope,const FileDef *srcFileScope,const DString &srcType,
1493 const Definition *dstScope,const FileDef *dstFileScope,const DString &dstType,
1494 SrcLangExt lang)
1495{
1496 AUTO_TRACE("srcType='{}' dstType='{}'",srcType,dstType);
1497 if (srcType==dstType) return true;
1498
1499 // check if the types are function pointers
1500 size_t i1=srcType.find(")(");
1501 if (i1==DString::npos) return false;
1502 size_t i2=dstType.find(")(");
1503 if (i1!=i2) return false;
1504
1505 // check if the result part of the function pointer types matches
1506 size_t j1=srcType.find("(");
1507 if (j1==DString::npos || j1>i1) return false;
1508 size_t j2=dstType.find("(");
1509 if (j2!=j1) return false;
1510 if (srcType.left(j1)!=dstType.left(j2)) return false; // different return types
1511
1512 // if srcType and dstType are both function pointers with the same return type,
1513 // then match against the parameter lists.
1514 // This way srcType='void (*fptr)(int x)' will match against `void (*fptr)(int y)' because
1515 // 'int x' matches 'int y'. A simple literal string match would treat these as different.
1516 auto srcAl = stringToArgumentList(lang,srcType.mid(i1+1));
1517 auto dstAl = stringToArgumentList(lang,dstType.mid(i2+1));
1518 return matchArguments2(srcScope,srcFileScope,srcType.left(j1),srcAl.get(),
1519 dstScope,dstFileScope,dstType.left(j2),dstAl.get(),
1520 true,lang);
1521}
1522
1523static bool matchArgument2(
1524 const Definition *srcScope,const FileDef *srcFileScope,Argument &srcA,
1525 const Definition *dstScope,const FileDef *dstFileScope,Argument &dstA,
1526 SrcLangExt lang
1527 )
1528{
1529 AUTO_TRACE("src: scope={} type={} name={} canType={}, dst: scope={} type={} name={} canType={}",
1530 srcScope?srcScope->name():"",srcA.type,srcA.name,srcA.canType,
1531 dstScope?dstScope->name():"",dstA.type,dstA.name,dstA.canType);
1532 //printf(">> match argument: %s::'%s|%s' (%s) <-> %s::'%s|%s' (%s)\n",
1533 // srcScope ? qPrint(srcScope->name()) : "",
1534 // qPrint(srcA.type), qPrint(srcA.name), qPrint(srcA.canType),
1535 // dstScope ? qPrint(dstScope->name()) : "",
1536 // qPrint(dstA.type), qPrint(dstA.name), qPrint(dstA.canType));
1537
1538 DString sSrcName = " "+srcA.name;
1539 DString sDstName = " "+dstA.name;
1540 DString srcType = srcA.type;
1541 DString dstType = dstA.type;
1542 stripIrrelevantConstVolatile(srcType,false);
1543 stripIrrelevantConstVolatile(dstType,false);
1544 //printf("'%s'<->'%s'\n",qPrint(sSrcName),qPrint(dstType.right(sSrcName.length())));
1545 //printf("'%s'<->'%s'\n",qPrint(sDstName),qPrint(srcType.right(sDstName.length())));
1546 if (sSrcName==dstType.right(sSrcName.length()))
1547 { // case "unsigned int" <-> "unsigned int i"
1548 srcA.type+=sSrcName;
1549 srcA.name="";
1550 srcA.canType=""; // invalidate cached type value
1551 }
1552 else if (sDstName==srcType.right(sDstName.length()))
1553 { // case "unsigned int i" <-> "unsigned int"
1554 dstA.type+=sDstName;
1555 dstA.name="";
1556 dstA.canType=""; // invalidate cached type value
1557 }
1558
1559 {
1560 std::lock_guard lock(g_matchArgsMutex);
1561 if (srcA.canType.empty() || dstA.canType.empty())
1562 {
1563 // need to re-evaluate both see issue #8370
1564 srcA.canType = extractCanonicalArgType(srcScope,srcFileScope,srcA,lang);
1565 dstA.canType = extractCanonicalArgType(dstScope,dstFileScope,dstA,lang);
1566 }
1567 }
1568
1569 if (matchCanonicalTypes(srcScope,srcFileScope,srcA.canType,
1570 dstScope,dstFileScope,dstA.canType,
1571 lang))
1572 {
1573 MATCH
1574 AUTO_TRACE_EXIT("true");
1575 return true;
1576 }
1577 else
1578 {
1579 //printf(" Canonical types do not match [%s]<->[%s]\n",
1580 // qPrint(srcA->canType),qPrint(dstA->canType));
1581 NOMATCH
1582 AUTO_TRACE_EXIT("false");
1583 return false;
1584 }
1585}
1586
1587
1588// new algorithm for argument matching
1589bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const DString &srcReturnType,const ArgumentList *srcAl,
1590 const Definition *dstScope,const FileDef *dstFileScope,const DString &dstReturnType,const ArgumentList *dstAl,
1591 bool checkCV,SrcLangExt lang)
1592{
1593 ASSERT(srcScope!=nullptr && dstScope!=nullptr);
1594
1595 AUTO_TRACE("srcScope='{}' dstScope='{}' srcArgs='{}' dstArgs='{}' checkCV={} lang={}",
1596 srcScope->name(),dstScope->name(),srcAl?argListToString(*srcAl):"",dstAl?argListToString(*dstAl):"",checkCV,lang);
1597
1598 if (srcAl==nullptr || dstAl==nullptr)
1599 {
1600 bool match = srcAl==dstAl;
1601 if (match)
1602 {
1603 MATCH
1604 return true;
1605 }
1606 else
1607 {
1608 NOMATCH
1609 return false;
1610 }
1611 }
1612
1613 // handle special case with void argument
1614 if ( srcAl->empty() && dstAl->size()==1 && dstAl->front().type=="void" )
1615 { // special case for finding match between func() and func(void)
1616 Argument a;
1617 a.type = "void";
1618 const_cast<ArgumentList*>(srcAl)->push_back(a);
1619 MATCH
1620 return true;
1621 }
1622 if ( dstAl->empty() && srcAl->size()==1 && srcAl->front().type=="void" )
1623 { // special case for finding match between func(void) and func()
1624 Argument a;
1625 a.type = "void";
1626 const_cast<ArgumentList*>(dstAl)->push_back(a);
1627 MATCH
1628 return true;
1629 }
1630
1631 if (srcAl->size() != dstAl->size())
1632 {
1633 NOMATCH
1634 return false; // different number of arguments -> no match
1635 }
1636
1637 if (checkCV)
1638 {
1639 if (srcAl->constSpecifier() != dstAl->constSpecifier())
1640 {
1641 NOMATCH
1642 return false; // one member is const, the other not -> no match
1643 }
1644 if (srcAl->volatileSpecifier() != dstAl->volatileSpecifier())
1645 {
1646 NOMATCH
1647 return false; // one member is volatile, the other not -> no match
1648 }
1649 }
1650
1651 if (srcAl->refQualifier() != dstAl->refQualifier())
1652 {
1653 NOMATCH
1654 return false; // one member is has a different ref-qualifier than the other
1655 }
1656
1657 if (srcReturnType=="auto" && dstReturnType=="auto" && srcAl->trailingReturnType()!=dstAl->trailingReturnType())
1658 {
1659 NOMATCH
1660 return false; // one member is has a different return type than the other
1661 }
1662
1663 // so far the argument list could match, so we need to compare the types of
1664 // all arguments.
1665 auto srcIt = srcAl->begin();
1666 auto dstIt = dstAl->begin();
1667 for (;srcIt!=srcAl->end() && dstIt!=dstAl->end();++srcIt,++dstIt)
1668 {
1669 Argument &srcA = const_cast<Argument&>(*srcIt);
1670 Argument &dstA = const_cast<Argument&>(*dstIt);
1671 if (!matchArgument2(srcScope,srcFileScope,srcA,
1672 dstScope,dstFileScope,dstA,
1673 lang)
1674 )
1675 {
1676 NOMATCH
1677 return false;
1678 }
1679 }
1680 MATCH
1681 return true; // all arguments match
1682}
1683
1684#undef MATCH
1685#undef NOMATCH
1686
1687// merges the initializer of two argument lists
1688// pre: the types of the arguments in the list should match.
1689void mergeArguments(ArgumentList &srcAl,ArgumentList &dstAl,bool forceNameOverwrite)
1690{
1691 AUTO_TRACE("srcAl='{}',dstAl='{}',forceNameOverwrite={}",
1692 qPrint(argListToString(srcAl)),qPrint(argListToString(dstAl)),forceNameOverwrite);
1693
1694 if (srcAl.size()!=dstAl.size())
1695 {
1696 return; // invalid argument lists -> do not merge
1697 }
1698
1699 auto srcIt=srcAl.begin();
1700 auto dstIt=dstAl.begin();
1701 while (srcIt!=srcAl.end() && dstIt!=dstAl.end())
1702 {
1703 Argument &srcA = *srcIt;
1704 Argument &dstA = *dstIt;
1705
1706 AUTO_TRACE_ADD("before merge: src=[type='{}',name='{}',def='{}'] dst=[type='{}',name='{}',def='{}']",
1707 srcA.type,srcA.name,srcA.defval,
1708 dstA.type,dstA.name,dstA.defval);
1709 if (srcA.defval.empty() && !dstA.defval.empty())
1710 {
1711 //printf("Defval changing '%s'->'%s'\n",qPrint(srcA.defval),qPrint(dstA.defval));
1712 srcA.defval=dstA.defval;
1713 }
1714 else if (!srcA.defval.empty() && dstA.defval.empty())
1715 {
1716 //printf("Defval changing '%s'->'%s'\n",qPrint(dstA.defval),qPrint(srcA.defval));
1717 dstA.defval=srcA.defval;
1718 }
1719
1720 // fix wrongly detected const or volatile specifiers before merging.
1721 // example: "const A *const" is detected as type="const A *" name="const"
1722 if (srcA.name=="const" || srcA.name=="volatile")
1723 {
1724 srcA.type+=" "+srcA.name;
1725 srcA.name.clear();
1726 }
1727 if (dstA.name=="const" || dstA.name=="volatile")
1728 {
1729 dstA.type+=" "+dstA.name;
1730 dstA.name.clear();
1731 }
1732
1733 if (srcA.type==dstA.type)
1734 {
1735 //printf("1. merging %s:%s <-> %s:%s\n",qPrint(srcA.type),qPrint(srcA.name),qPrint(dstA.type),qPrint(dstA.name));
1736 if (srcA.name.empty() && !dstA.name.empty())
1737 {
1738 //printf("type: '%s':='%s'\n",qPrint(srcA.type),qPrint(dstA.type));
1739 //printf("name: '%s':='%s'\n",qPrint(srcA.name),qPrint(dstA.name));
1740 srcA.type = dstA.type;
1741 srcA.name = dstA.name;
1742 }
1743 else if (!srcA.name.empty() && dstA.name.empty())
1744 {
1745 //printf("type: '%s':='%s'\n",qPrint(dstA.type),qPrint(srcA.type));
1746 //printf("name: '%s':='%s'\n",qPrint(dstA.name),qPrint(srcA.name));
1747 dstA.type = srcA.type;
1748 dstA.name = srcA.name;
1749 }
1750 else if (!srcA.name.empty() && !dstA.name.empty())
1751 {
1752 //printf("srcA.name=%s dstA.name=%s\n",qPrint(srcA.name),qPrint(dstA.name));
1753 if (forceNameOverwrite)
1754 {
1755 srcA.name = dstA.name;
1756 }
1757 else
1758 {
1759 if (srcA.docs.empty() && !dstA.docs.empty())
1760 {
1761 srcA.name = dstA.name;
1762 }
1763 else if (!srcA.docs.empty() && dstA.docs.empty())
1764 {
1765 dstA.name = srcA.name;
1766 }
1767 }
1768 }
1769 }
1770 else
1771 {
1772 //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",qPrint(srcA.type),qPrint(srcA.name),qPrint(dstA.type),qPrint(dstA.name));
1773 srcA.type=srcA.type.stripWhiteSpace();
1774 dstA.type=dstA.type.stripWhiteSpace();
1775 if (srcA.type+" "+srcA.name==dstA.type) // "unsigned long:int" <-> "unsigned long int:bla"
1776 {
1777 srcA.type+=" "+srcA.name;
1778 srcA.name=dstA.name;
1779 }
1780 else if (dstA.type+" "+dstA.name==srcA.type) // "unsigned long int bla" <-> "unsigned long int"
1781 {
1782 dstA.type+=" "+dstA.name;
1783 dstA.name=srcA.name;
1784 }
1785 else if (srcA.name.empty() && !dstA.name.empty())
1786 {
1787 srcA.name = dstA.name;
1788 }
1789 else if (dstA.name.empty() && !srcA.name.empty())
1790 {
1791 dstA.name = srcA.name;
1792 }
1793 }
1794 size_t i1=srcA.type.find("::"),
1795 i2=dstA.type.find("::"),
1796 j1=srcA.type.length()-i1-2,
1797 j2=dstA.type.length()-i2-2;
1798 if (i1!=DString::npos && i2==DString::npos && srcA.type.right(j1)==dstA.type)
1799 {
1800 //printf("type: '%s':='%s'\n",qPrint(dstA.type),qPrint(srcA.type));
1801 //printf("name: '%s':='%s'\n",qPrint(dstA.name),qPrint(srcA.name));
1802 dstA.type = srcA.type.left(i1+2)+dstA.type;
1803 dstA.name = srcA.name;
1804 }
1805 else if (i1==DString::npos && i2!=DString::npos && dstA.type.right(j2)==srcA.type)
1806 {
1807 //printf("type: '%s':='%s'\n",qPrint(srcA.type),qPrint(dstA.type));
1808 //printf("name: '%s':='%s'\n",qPrint(dstA.name),qPrint(srcA.name));
1809 srcA.type = dstA.type.left(i2+2)+srcA.type;
1810 srcA.name = dstA.name;
1811 }
1812 if (srcA.docs.empty() && !dstA.docs.empty())
1813 {
1814 srcA.docs = dstA.docs;
1815 }
1816 else if (dstA.docs.empty() && !srcA.docs.empty())
1817 {
1818 dstA.docs = srcA.docs;
1819 }
1820 //printf("Merge argument '%s|%s' '%s|%s'\n",
1821 // qPrint(srcA.type), qPrint(srcA.name),
1822 // qPrint(dstA.type), qPrint(dstA.name));
1823 ++srcIt;
1824 ++dstIt;
1825 AUTO_TRACE_ADD("after merge: src=[type='{}',name='{}',def='{}'] dst=[type='{}',name='{}',def='{}']",
1826 srcA.type,srcA.name,srcA.defval,
1827 dstA.type,dstA.name,dstA.defval);
1828 }
1829}
1830
1831//---------------------------------------------------------------------------------------
1832
1833bool matchTemplateArguments(const ArgumentList &srcAl,const ArgumentList &dstAl)
1834{
1835 AUTO_TRACE("srcAl={} dstAl={}",argListToString(srcAl),argListToString(dstAl));
1836 if (srcAl.size()!=dstAl.size()) // different number of template parameters -> overload
1837 {
1838 AUTO_TRACE_EXIT("different number of parameters");
1839 return false;
1840 }
1841 auto isUnconstraintTemplate = [](const DString &type)
1842 {
1843 return type=="typename" || type=="class" || type.startsWith("typename ") || type.startsWith("class ");
1844 };
1845 auto srcIt = srcAl.begin();
1846 auto dstIt = dstAl.begin();
1847 while (srcIt!=srcAl.end() && dstIt!=dstAl.end())
1848 {
1849 const Argument &srcA = *srcIt;
1850 const Argument &dstA = *dstIt;
1851 if ((!isUnconstraintTemplate(srcA.type) || !isUnconstraintTemplate(dstA.type)) && srcA.type!=dstA.type) // different constraints -> overload
1852 {
1853 AUTO_TRACE_EXIT("different constraints");
1854 return false;
1855 }
1856 ++srcIt;
1857 ++dstIt;
1858 }
1859 AUTO_TRACE_EXIT("same");
1860 // no overload with respect to the template parameters
1861 return true;
1862}
1863
1864//---------------------------------------------------------------------------------------
1865
1867{
1868 GetDefResult result;
1869 if (input.memberName.empty()) return result;
1870 AUTO_TRACE("scopeName={},memberName={},forceEmptyScope={}",
1871 input.scopeName,input.memberName,input.forceEmptyScope);
1872
1873 //printf("@@ --- getDefsNew(%s,%s)-----------\n",qPrint(scName),qPrint(mbName));
1874 const Definition *scope = Doxygen::globalScope;
1875 SymbolResolver resolver;
1876 if (input.currentFile) resolver.setFileScope(input.currentFile);
1877 if (!input.scopeName.empty() && !input.forceEmptyScope)
1878 {
1879 scope = resolver.resolveSymbol(scope,input.scopeName);
1880 }
1881 if (scope==Doxygen::globalScope)
1882 {
1883 scope = input.currentFile;
1884 }
1885 //printf("@@ -> found scope scope=%s member=%s out=%s\n",qPrint(input.scopeName),qPrint(input.memberName),qPrint(scope?scope->name():""));
1886 //
1887 const Definition *symbol = resolver.resolveSymbol(scope,input.memberName,input.args,input.checkCV,input.insideCode,true);
1888 //printf("@@ -> found symbol in=%s out=%s\n",qPrint(input.memberName),qPrint(symbol?symbol->qualifiedName():DString()));
1889 if (symbol && symbol->definitionType()==Definition::TypeMember)
1890 {
1891 result.md = toMemberDef(symbol);
1892 result.cd = result.md->getClassDef();
1893 if (result.cd==nullptr) result.nd = result.md->getNamespaceDef();
1894 if (result.cd==nullptr && result.nd==nullptr) result.fd = result.md->getFileDef();
1895 result.gd = result.md->getGroupDef();
1896 result.found = true;
1897 }
1898 else if (symbol && symbol->definitionType()==Definition::TypeClass)
1899 {
1900 result.cd = toClassDef(symbol);
1901 result.found = true;
1902 }
1903 else if (symbol && symbol->definitionType()==Definition::TypeNamespace)
1904 {
1905 result.nd = toNamespaceDef(symbol);
1906 result.found = true;
1907 }
1908 else if (symbol && symbol->definitionType()==Definition::TypeConcept)
1909 {
1910 result.cnd = toConceptDef(symbol);
1911 result.found = true;
1912 }
1913 else if (symbol && symbol->definitionType()==Definition::TypeModule)
1914 {
1915 result.modd = toModuleDef(symbol);
1916 result.found = true;
1917 }
1918 return result;
1919}
1920
1921
1922/*!
1923 * Searches for a scope definition given its name as a string via parameter
1924 * `scope`.
1925 *
1926 * The parameter `docScope` is a string representing the name of the scope in
1927 * which the `scope` string was found.
1928 *
1929 * The function returns true if the scope is known and documented or
1930 * false if it is not.
1931 * If true is returned exactly one of the parameter `cd`, `nd`
1932 * will be non-zero:
1933 * - if `cd` is non zero, the scope was a class pointed to by cd.
1934 * - if `nd` is non zero, the scope was a namespace pointed to by nd.
1935 */
1936static bool getScopeDefs(const DString &docScope,const DString &scope,
1937 ClassDef *&cd, ConceptDef *&cnd, NamespaceDef *&nd,ModuleDef *&modd)
1938{
1939 cd=nullptr;
1940 cnd=nullptr;
1941 nd=nullptr;
1942 modd=nullptr;
1943
1944 DString scopeName=scope;
1945 //printf("getScopeDefs: docScope='%s' scope='%s'\n",qPrint(docScope),qPrint(scope));
1946 if (scopeName.empty()) return false;
1947
1948 bool explicitGlobalScope=false;
1949 if (scopeName.at(0)==':' && scopeName.at(1)==':')
1950 {
1951 scopeName=scopeName.mid(2);
1952 explicitGlobalScope=true;
1953 }
1954 if (scopeName.empty())
1955 {
1956 return false;
1957 }
1958
1959 DString docScopeName=docScope;
1960 int scopeOffset=explicitGlobalScope ? 0 : static_cast<int>(docScopeName.length());
1961
1962 do // for each possible docScope (from largest to and including empty)
1963 {
1964 DString fullName=scopeName;
1965 if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
1966
1967 if (((cd=getClass(fullName)) || // normal class
1968 (cd=getClass(fullName+"-p")) // ObjC protocol
1969 ) && cd->isLinkable())
1970 {
1971 return true; // class link written => quit
1972 }
1973 else if ((nd=Doxygen::namespaceLinkedMap->find(fullName)) && nd->isLinkable())
1974 {
1975 return true; // namespace link written => quit
1976 }
1977 else if ((cnd=Doxygen::conceptLinkedMap->find(fullName)) && cnd->isLinkable())
1978 {
1979 return true; // concept link written => quit
1980 }
1981 else if ((modd=ModuleManager::instance().modules().find(fullName)) && modd->isLinkable())
1982 {
1983 return true; // module link written => quit
1984 }
1985 if (scopeOffset==0)
1986 {
1987 scopeOffset=-1;
1988 }
1989 else
1990 {
1991 size_t o = docScopeName.rfind("::",scopeOffset-1);
1992 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
1993 }
1994 } while (scopeOffset>=0);
1995
1996 return false;
1997}
1998
1999static bool isLowerCase(DString &s)
2000{
2001 if (s.empty()) return true;
2002 const char *p=s.data();
2003 int c=0;
2004 while ((c=static_cast<uint8_t>(*p++))) if (!islower(c)) return false;
2005 return true;
2006}
2007
2008bool resolveRef(/* in */ const DString &scName,
2009 /* in */ const DString &name,
2010 /* in */ bool inSeeBlock,
2011 /* out */ const Definition **resContext,
2012 /* out */ const MemberDef **resMember,
2013 /* in */ SrcLangExt lang,
2014 bool lookForSpecialization,
2015 const FileDef *currentFile,
2016 bool checkScope
2017 )
2018{
2019 AUTO_TRACE("scope={} name={} inSeeBlock={} lang={} lookForSpecialization={} currentFile={} checkScope={}",
2020 scName,name,inSeeBlock,lang,lookForSpecialization,currentFile ? currentFile->name() : "", checkScope);
2021 //printf("resolveRef(scope=%s,name=%s,inSeeBlock=%d)\n",qPrint(scName),qPrint(name),inSeeBlock);
2022 DString tsName = name;
2023 //bool memberScopeFirst = tsName.find('#')!=-1;
2024 DString fullName = substitute(tsName,"#","::");
2025 if (fullName.find("anonymous_namespace{")==DString::npos)
2026 {
2027 fullName = removeRedundantWhiteSpace(substitute(fullName,".","::",3));
2028 }
2029 else
2030 {
2031 fullName = removeRedundantWhiteSpace(fullName);
2032 }
2033
2034 size_t templStartPos;
2035 if (lang==SrcLangExt::CSharp && (templStartPos=fullName.find('<'))!=DString::npos)
2036 {
2037 size_t templEndPos = fullName.rfind('>');
2038 if (templEndPos!=DString::npos)
2039 {
2040 fullName = mangleCSharpGenericName(fullName.left(templEndPos+1))+fullName.mid(templEndPos+1);
2041 AUTO_TRACE_ADD("C# mangled name='{}'",fullName);
2042 }
2043 }
2044
2045 size_t bracePos = findParameterList(fullName);
2046 size_t endNamePos = bracePos!=DString::npos ? bracePos : fullName.length();
2047 size_t scopePos = fullName.rfind("::",endNamePos);
2048 bool explicitScope = fullName.startsWith("::") && // ::scope or #scope
2049 ((scopePos!=DString::npos && scopePos>2) || // ::N::A
2050 tsName.startsWith("::") || // ::foo in local scope
2051 scName==nullptr // #foo in global scope
2052 );
2053 bool allowTypeOnly=false;
2054
2055 // default result values
2056 *resContext=nullptr;
2057 *resMember=nullptr;
2058
2059 if (bracePos==DString::npos) // simple name
2060 {
2061 // the following if() was commented out for releases in the range
2062 // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
2063 if (!inSeeBlock && scopePos==DString::npos && isLowerCase(tsName))
2064 { // link to lower case only name => do not try to autolink
2065 AUTO_TRACE_ADD("false");
2066 return false;
2067 }
2068
2069 ClassDef *cd=nullptr;
2070 NamespaceDef *nd=nullptr;
2071 ConceptDef *cnd=nullptr;
2072 ModuleDef *modd=nullptr;
2073
2074 //printf("scName=%s fullName=%s\n",qPrint(scName),qPrint(fullName));
2075
2076 // check if this is a class or namespace reference
2077 if (scName!=fullName && getScopeDefs(scName,fullName,cd,cnd,nd,modd))
2078 {
2079 //printf("found scopeDef\n");
2080 if (cd) // scope matches that of a class
2081 {
2082 *resContext = cd;
2083 }
2084 else if (cnd)
2085 {
2086 *resContext = cnd;
2087 }
2088 else if (modd)
2089 {
2090 *resContext = modd;
2091 }
2092 else // scope matches that of a namespace
2093 {
2094 ASSERT(nd!=nullptr);
2095 *resContext = nd;
2096 }
2097 AUTO_TRACE_ADD("true");
2098 return true;
2099 }
2100 else if (scName==fullName || (!inSeeBlock && scopePos==DString::npos))
2101 // nothing to link => output plain text
2102 {
2103 //printf("found scName=%s fullName=%s scName==fullName=%d "
2104 // "inSeeBlock=%d scopePos=%d!\n",
2105 // qPrint(scName),qPrint(fullName),scName==fullName,inSeeBlock,scopePos);
2106
2107 // at this point we have a bare word that is not a class or namespace
2108 // we should also allow typedefs or enums to be linked, but not for instance member
2109 // functions, otherwise 'Foo' would always link to the 'Foo()' constructor instead of the
2110 // 'Foo' class. So we use this flag as a filter.
2111 allowTypeOnly=true;
2112 }
2113
2114 // continue search...
2115 }
2116
2117 // extract userscope+name
2118 DString nameStr=fullName.left(endNamePos);
2119 if (explicitScope) nameStr=nameStr.mid(2);
2120
2121
2122 // extract arguments
2123 DString argsStr;
2124 if (bracePos!=DString::npos) argsStr=fullName.mid(bracePos);
2125
2126 // strip template specifier
2127 // TODO: match against the correct partial template instantiation
2128 size_t templPos = nameStr.find('<');
2129 bool tryUnspecializedVersion = false;
2130 if (templPos!=DString::npos && nameStr.find("operator")==DString::npos)
2131 {
2132 size_t endTemplPos=nameStr.rfind('>');
2133 if (endTemplPos!=DString::npos)
2134 {
2135 if (!lookForSpecialization)
2136 {
2137 nameStr=nameStr.left(templPos)+nameStr.mid(endTemplPos+1);
2138 }
2139 else
2140 {
2141 tryUnspecializedVersion = true;
2142 }
2143 }
2144 }
2145
2146 DString scopeStr=scName;
2147 if (!explicitScope && nameStr.length()>scopeStr.length() && leftScopeMatch(scopeStr,nameStr))
2148 {
2149 nameStr=nameStr.mid(scopeStr.length()+2);
2150 }
2151
2152 const GroupDef *gd = nullptr;
2153 const ConceptDef *cnd = nullptr;
2154 const ModuleDef *modd = nullptr;
2155
2156 // check if nameStr is a member or global.
2157 //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
2158 // qPrint(scopeStr), qPrint(nameStr), qPrint(argsStr),checkScope);
2159 GetDefInput input(scopeStr,nameStr,argsStr);
2160 input.forceEmptyScope = explicitScope;
2161 input.currentFile = currentFile;
2162 input.checkCV = true;
2163 GetDefResult result = getDefs(input);
2164 if (result.found)
2165 {
2166 //printf("after getDefs checkScope=%d nameStr=%s\n",checkScope,qPrint(nameStr));
2167 size_t np = nameStr.find("::");
2168 if (checkScope && result.md && result.md->getOuterScope()==Doxygen::globalScope &&
2169 !result.md->isStrongEnumValue() &&
2170 (!scopeStr.empty() || (np!=DString::npos && np>0)))
2171 {
2172 // we did find a member, but it is a global one while we were explicitly
2173 // looking for a scoped variable. See bug 616387 for an example why this check is needed.
2174 // note we do need to support autolinking to "::symbol" hence the >0
2175 //printf("not global member!\n");
2176 *resContext=nullptr;
2177 *resMember=nullptr;
2178 AUTO_TRACE_ADD("false");
2179 return false;
2180 }
2181 //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
2182 if (result.md)
2183 {
2184 if (!allowTypeOnly || result.md->isTypedef() || result.md->isEnumerate())
2185 {
2186 *resMember=result.md;
2187 *resContext=result.md;
2188 }
2189 else // md is not a type, but we explicitly expect one
2190 {
2191 *resContext=nullptr;
2192 *resMember=nullptr;
2193 AUTO_TRACE_ADD("false");
2194 return false;
2195 }
2196 }
2197 else if (result.cd) *resContext=result.cd;
2198 else if (result.nd) *resContext=result.nd;
2199 else if (result.fd) *resContext=result.fd;
2200 else if (result.gd) *resContext=result.gd;
2201 else if (result.cnd) *resContext=result.cnd;
2202 else if (result.modd) *resContext=result.modd;
2203 else
2204 {
2205 *resContext=nullptr; *resMember=nullptr;
2206 AUTO_TRACE_ADD("false");
2207 return false;
2208 }
2209 //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
2210 // qPrint(md->name()), md, qPrint(md->anchor()), md->isLinkable(), qPrint((*resContext)->name()));
2211 AUTO_TRACE_ADD("true");
2212 return true;
2213 }
2214 else if (inSeeBlock && !nameStr.empty() && (gd=Doxygen::groupLinkedMap->find(nameStr)))
2215 { // group link
2216 *resContext=gd;
2217 AUTO_TRACE_ADD("true");
2218 return true;
2219 }
2220 else if ((cnd=Doxygen::conceptLinkedMap->find(nameStr)))
2221 {
2222 *resContext=cnd;
2223 AUTO_TRACE_ADD("true");
2224 return true;
2225 }
2226 else if ((modd=ModuleManager::instance().modules().find(nameStr)))
2227 {
2228 *resContext=modd;
2229 AUTO_TRACE_ADD("true");
2230 return true;
2231 }
2232 else if (tsName.find('.')!=DString::npos) // maybe a link to a file
2233 {
2234 bool ambig = false;
2235 const FileDef *fd=Doxygen::inputNameLinkedMap->findFileDef(tsName,ambig);
2236 if (fd && !ambig)
2237 {
2238 *resContext=fd;
2239 AUTO_TRACE_ADD("true");
2240 return true;
2241 }
2242 }
2243
2244 if (tryUnspecializedVersion)
2245 {
2246 bool b = resolveRef(scName,name,inSeeBlock,resContext,resMember,lang,false,nullptr,checkScope);
2247 AUTO_TRACE_ADD("{}",b);
2248 return b;
2249 }
2250 if (bracePos!=DString::npos) // Try without parameters as well, could be a constructor invocation
2251 {
2252 *resContext=getClass(fullName.left(bracePos));
2253 if (*resContext)
2254 {
2255 AUTO_TRACE_ADD("true");
2256 return true;
2257 }
2258 }
2259 //printf("resolveRef: %s not found!\n",qPrint(name));
2260
2261 AUTO_TRACE_ADD("false");
2262 return false;
2263}
2264
2265DString linkToText(SrcLangExt lang,const DString &link,bool ignoreDots)
2266{
2267 //bool optimizeOutputJava = Config_getBool(OPTIMIZE_OUTPUT_JAVA);
2268 DString result=link;
2269 if (!result.empty())
2270 {
2271 // replace # by ::
2272 result=substitute(result,"#","::");
2273 // replace . by ::
2274 if (!ignoreDots && result.find('<')==DString::npos) result=substitute(result,".","::",3);
2275 // strip leading :: prefix if present
2276 if (result.at(0)==':' && result.at(1)==':')
2277 {
2278 result=result.mid(2);
2279 }
2281 if (sep!="::")
2282 {
2283 result=substitute(result,"::",sep);
2284 }
2285 }
2286 //printf("linkToText(%s,lang=%d)=%s\n",qPrint(link),lang,qPrint(result));
2287 return result;
2288}
2289
2290static const DirDef *resolveDirLink(const DString &linkRef)
2291{
2292 const DirDef *dd = Doxygen::dirLinkedMap->find(FileInfo(linkRef.str()).absFilePath()+"/");
2293 //printf("resolveDirLink(%s) -> %s\n",qPrint(linkRef),dd?qPrint(dd->name()):"<none>");
2294 if (dd==nullptr)
2295 {
2296 StringVector stripPaths = Config_getList(STRIP_FROM_PATH);
2297 for (const auto &path : stripPaths)
2298 {
2299 FileInfo fi(path+linkRef.str());
2300 //printf(" trying to strip path '%s' from linkRef '%s' fi='%s'\n",qPrint(path),qPrint(linkRef),qPrint(fi.absFilePath()));
2301 dd = Doxygen::dirLinkedMap->find(fi.absFilePath()+"/");
2302 if (dd) break;
2303 }
2304 }
2305 return dd;
2306}
2307
2308bool resolveLink(/* in */ const DString &scName,
2309 /* in */ const DString &lr,
2310 /* in */ bool /*inSeeBlock*/,
2311 /* out */ const Definition **resContext,
2312 /* out */ DString &resAnchor,
2313 /* in */ SrcLangExt lang,
2314 /* in */ const DString &prefix,
2315 /* in */ const FileDef *currentFile
2316 )
2317{
2318 *resContext=nullptr;
2319
2320 DString linkRef=lr;
2321 if (lang==SrcLangExt::CSharp)
2322 {
2323 linkRef = mangleCSharpGenericName(linkRef);
2324 }
2325 DString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,false);
2326 AUTO_TRACE("scName='{}',ref='{}'",scName,lr);
2327 const FileDef *fd = nullptr;
2328 const GroupDef *gd = nullptr;
2329 const PageDef *pd = nullptr;
2330 const ClassDef *cd = nullptr;
2331 const DirDef *dir = nullptr;
2332 const ConceptDef *cnd = nullptr;
2333 const ModuleDef *modd = nullptr;
2334 const NamespaceDef *nd = nullptr;
2335 const SectionInfo *si = nullptr;
2336 bool ambig = false;
2337 if (linkRef.empty()) // no reference name!
2338 {
2339 AUTO_TRACE_EXIT("no_ref");
2340 return false;
2341 }
2342 else if ((pd=Doxygen::pageLinkedMap->find(linkRef))) // link to a page
2343 {
2344 gd = pd->getGroupDef();
2345 if (gd)
2346 {
2347 if (!pd->name().empty()) si=SectionManager::instance().find(pd->name());
2348 *resContext=gd;
2349 if (si) resAnchor = si->label();
2350 }
2351 else
2352 {
2353 *resContext=pd;
2354 }
2355 AUTO_TRACE_EXIT("page");
2356 return true;
2357 }
2358 else if ((si=SectionManager::instance().find(prefix+linkRef)))
2359 {
2360 *resContext=si->definition();
2361 resAnchor = si->label();
2362 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2363 return true;
2364 }
2365 else if (!prefix.empty() && (si=SectionManager::instance().find(linkRef)))
2366 {
2367 *resContext=si->definition();
2368 resAnchor = si->label();
2369 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2370 return true;
2371 }
2372 else if ((pd=Doxygen::exampleLinkedMap->find(linkRef))) // link to an example
2373 {
2374 *resContext=pd;
2375 AUTO_TRACE_EXIT("example");
2376 return true;
2377 }
2378 else if ((gd=Doxygen::groupLinkedMap->find(linkRef))) // link to a group
2379 {
2380 *resContext=gd;
2381 AUTO_TRACE_EXIT("group");
2382 return true;
2383 }
2384 else if ((fd=Doxygen::inputNameLinkedMap->findFileDef(linkRef,ambig)) // file link
2385 && fd->isLinkable())
2386 {
2387 *resContext=fd;
2388 AUTO_TRACE_EXIT("file");
2389 return true;
2390 }
2391 else if ((cd=getClass(linkRef))) // class link
2392 {
2393 *resContext=cd;
2394 resAnchor=cd->anchor();
2395 AUTO_TRACE_EXIT("class");
2396 return true;
2397 }
2398 else if (lang==SrcLangExt::Java &&
2399 (cd=getClass(linkRefWithoutTemplates))) // Java generic class link
2400 {
2401 *resContext=cd;
2402 resAnchor=cd->anchor();
2403 AUTO_TRACE_EXIT("generic");
2404 return true;
2405 }
2406 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
2407 {
2408 *resContext=cd;
2409 resAnchor=cd->anchor();
2410 AUTO_TRACE_EXIT("protocol");
2411 return true;
2412 }
2413 else if ((cnd=getConcept(linkRef))) // C++20 concept definition
2414 {
2415 *resContext=cnd;
2416 resAnchor=cnd->anchor();
2417 AUTO_TRACE_EXIT("concept");
2418 return true;
2419 }
2420 else if ((modd=ModuleManager::instance().modules().find(linkRef)))
2421 {
2422 *resContext=modd;
2423 resAnchor=modd->anchor();
2424 AUTO_TRACE_EXIT("module");
2425 return true;
2426 }
2427 else if ((nd=Doxygen::namespaceLinkedMap->find(linkRef)))
2428 {
2429 *resContext=nd;
2430 AUTO_TRACE_EXIT("namespace");
2431 return true;
2432 }
2433 else if ((dir=resolveDirLink(linkRef)) && dir->isLinkable())
2434 {
2435 *resContext=dir;
2436 AUTO_TRACE_EXIT("directory");
2437 return true;
2438 }
2439 else // probably a member reference
2440 {
2441 const MemberDef *md = nullptr;
2442 bool res = resolveRef(scName,lr,true,resContext,&md,lang,true,currentFile);
2443 if (md) resAnchor=md->anchor();
2444 AUTO_TRACE_EXIT("member? res={}",res);
2445 return res;
2446 }
2447}
2448
2449//----------------------------------------------------------------------
2450
2451
2452DString findExampleFilePath(const DString &file,bool &ambig)
2453{
2454 ambig=false;
2455 DString result;
2456 bool found=false;
2457 if (!found)
2458 {
2459 FileInfo fi(file.str());
2460 if (fi.exists())
2461 {
2462 result=fi.absFilePath();
2463 found=true;
2464 }
2465 }
2466 if (!found)
2467 {
2468 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
2469 for (const auto &s : examplePathList)
2470 {
2471 std::string absFileName = s+(Portable::pathSeparator()+file).str();
2472 FileInfo fi(absFileName);
2473 if (fi.exists())
2474 {
2475 result=fi.absFilePath();
2476 found=true;
2477 }
2478 }
2479 }
2480
2481 if (!found)
2482 {
2483 // as a fallback we also look in the exampleNameDict
2485 if (fd && !ambig)
2486 {
2487 result=fd->absFilePath();
2488 }
2489 }
2490 return result;
2491}
2492
2493//----------------------------------------------------------------------
2494
2496{
2497 std::string substRes;
2498 int line = 1;
2499 const char *p = s.data();
2500 if (p)
2501 {
2502 // reserve some room for expansion
2503 substRes.reserve(s.length()+1024);
2504 char c = 0;
2505 while ((c=*p))
2506 {
2507 bool found = false;
2508 if (c=='$')
2509 {
2510 for (const auto &kw : keywords)
2511 {
2512 size_t keyLen = dstrlen(kw.keyword);
2513 if (dstrncmp(p,kw.keyword,keyLen)==0)
2514 {
2515 const char *startArg = p+keyLen;
2516 bool expectParam = std::holds_alternative<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
2517 //printf("%s: expectParam=%d *startArg=%c\n",kw.keyword,expectParam,*startArg);
2518 if (expectParam && *startArg=='(') // $key(value)
2519 {
2520 size_t j=1;
2521 const char *endArg = nullptr;
2522 while ((c=*(startArg+j)) && c!=')' && c!='\n' && c!=0) j++;
2523 if (c==')') endArg=startArg+j;
2524 if (endArg)
2525 {
2526 DString value = DString(startArg+1).left(endArg-startArg-1);
2527 auto &&getValue = std::get<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
2528 substRes+=getValue(value).str();
2529 p=endArg+1;
2530 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue(value)));
2531 }
2532 else
2533 {
2534 //printf("missing argument\n");
2535 warn(file,line,"Missing argument for '{}'",kw.keyword);
2536 p+=keyLen;
2537 }
2538 }
2539 else if (!expectParam) // $key
2540 {
2541 auto &&getValue = std::get<KeywordSubstitution::GetValue>(kw.getValueVariant);
2542 substRes+=getValue().str();
2543 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue()));
2544 p+=keyLen;
2545 }
2546 else
2547 {
2548 //printf("%s %d Expected arguments, none specified '%s'\n",qPrint(file), line, qPrint(kw.keyword));
2549 warn(file,line,"Expected arguments for '{}' but none were specified",kw.keyword);
2550 p+=keyLen;
2551 }
2552 found = true;
2553 break;
2554 }
2555 }
2556 }
2557 if (!found) // copy
2558 {
2559 if (c=='\n') line++;
2560 substRes+=c;
2561 p++;
2562 }
2563 }
2564 }
2565 return substRes;
2566}
2567
2569{
2570 // get the current date and time
2571 std::tm dat{};
2572 int specFormat=0;
2573 DString specDate = "";
2574 DString err = dateTimeFromString(specDate,dat,specFormat);
2575
2576 // do the conversion
2577 int usedFormat=0;
2578 return formatDateTime(fmt,dat,usedFormat);
2579}
2580
2582{
2583 DString projectLogo = Config_getString(PROJECT_LOGO);
2584 if (!projectLogo.empty())
2585 {
2586 // check for optional width= and height= specifier
2587 if (size_t wi = projectLogo.find(" width="); wi!=DString::npos) // and strip them
2588 {
2589 projectLogo = projectLogo.left(wi);
2590 }
2591 if (size_t hi = projectLogo.find(" height="); hi!=DString::npos)
2592 {
2593 projectLogo = projectLogo.left(hi);
2594 }
2595 }
2596 //printf("projectlogo='%s'\n",qPrint(projectLogo));
2597 return projectLogo;
2598}
2599
2601{
2602 DString sizeVal;
2603 DString projectLogo = Config_getString(PROJECT_LOGO);
2604 if (!projectLogo.empty())
2605 {
2606 auto extractDimension = [&projectLogo](const char *startMarker,size_t startPos,size_t endPos) -> DString
2607 {
2608 DString result = projectLogo.mid(startPos,endPos-startPos).stripWhiteSpace().quoted();
2609 if (result.length()>=2 && result.at(0)!='"' && result.at(result.length()-1)!='"')
2610 {
2611 result="\""+result+"\"";
2612 }
2613 result.prepend(startMarker);
2614 return result;
2615 };
2616 // check for optional width= and height= specifier
2617 size_t wi = projectLogo.find(" width=");
2618 size_t hi = projectLogo.find(" height=");
2619 if (wi!=DString::npos && hi!=DString::npos)
2620 {
2621 if (wi<hi) // "... width=x height=y..."
2622 {
2623 sizeVal = extractDimension(" width=", wi+7, hi) + " "
2624 + extractDimension(" height=", hi+8, projectLogo.length());
2625 }
2626 else // "... height=y width=x..."
2627 {
2628 sizeVal = extractDimension(" height=", hi+8, wi) + " "
2629 + extractDimension(" width=", wi+7, projectLogo.length());
2630 }
2631 }
2632 else if (wi!=DString::npos) // ... width=x..."
2633 {
2634 sizeVal = extractDimension(" width=", wi+7, projectLogo.length());
2635 }
2636 else if (hi!=DString::npos) // ... height=x..."
2637 {
2638 sizeVal = extractDimension(" height=", hi+8, projectLogo.length());
2639 }
2640 }
2641 //printf("projectsize='%s'\n",qPrint(sizeVal));
2642 return sizeVal;
2643}
2644
2645
2646//----------------------------------------------------------------------
2647
2648/*! Returns the character index within \a name of the first prefix
2649 * in Config_getList(IGNORE_PREFIX) that matches \a name at the left hand side,
2650 * or zero if no match was found
2651 */
2652int getPrefixIndex(const DString &name)
2653{
2654 if (name.empty()) return 0;
2655 int result=0;
2656 StringVector sl = Config_getList(IGNORE_PREFIX);
2657 for (const auto &s : sl)
2658 {
2659 const char *ps=s.c_str();
2660 const char *pd=name.data();
2661 int i=0;
2662 while (*ps!=0 && *pd!=0 && *ps==*pd)
2663 {
2664 ps++;
2665 pd++;
2666 i++;
2667 }
2668 if (*ps==0 && *pd!=0)
2669 {
2670 result=i;
2671 break;
2672 }
2673 }
2674 if (result<static_cast<int>(name.length())-1 && name.at(result)=='[') result++; // for e.g. [union] return u
2675 return result;
2676}
2677
2678//----------------------------------------------------------------------------
2679
2681{
2682 auto caseSenseNames = Config_getEnum(CASE_SENSE_NAMES);
2683
2684 if (caseSenseNames == CASE_SENSE_NAMES_t::YES) return true;
2685 else if (caseSenseNames == CASE_SENSE_NAMES_t::NO) return false;
2687}
2688
2689DString escapeCharsInString(const DString &name,bool allowDots,bool allowUnderscore)
2690{
2691 if (name.empty()) return name;
2692 bool caseSenseNames = useCaseSenseNames();
2693 bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
2694 DString result;
2695 result.reserve(name.length()+8);
2696 signed char c = 0;
2697 const char *p=name.data();
2698 while ((c=*p++)!=0)
2699 {
2700 switch(c)
2701 {
2702 case '_': if (allowUnderscore) result+='_'; else result+="__"; break;
2703 case '-': result+='-'; break;
2704 case ':': result+="_1"; break;
2705 case '/': result+="_2"; break;
2706 case '<': result+="_3"; break;
2707 case '>': result+="_4"; break;
2708 case '*': result+="_5"; break;
2709 case '&': result+="_6"; break;
2710 case '|': result+="_7"; break;
2711 case '.': if (allowDots) result+='.'; else result+="_8"; break;
2712 case '!': result+="_9"; break;
2713 case ',': result+="_00"; break;
2714 case ' ': result+="_01"; break;
2715 case '{': result+="_02"; break;
2716 case '}': result+="_03"; break;
2717 case '?': result+="_04"; break;
2718 case '^': result+="_05"; break;
2719 case '%': result+="_06"; break;
2720 case '(': result+="_07"; break;
2721 case ')': result+="_08"; break;
2722 case '+': result+="_09"; break;
2723 case '=': result+="_0a"; break;
2724 case '$': result+="_0b"; break;
2725 case '\\': result+="_0c"; break;
2726 case '@': result+="_0d"; break;
2727 case ']': result+="_0e"; break;
2728 case '[': result+="_0f"; break;
2729 case '#': result+="_0g"; break;
2730 case '"': result+="_0h"; break;
2731 case '~': result+="_0i"; break;
2732 case '\'': result+="_0j"; break;
2733 case ';': result+="_0k"; break;
2734 case '`': result+="_0l"; break;
2735 default:
2736 if (c<0)
2737 {
2738 bool doEscape = true;
2739 if (allowUnicodeNames)
2740 {
2741 int charLen = getUTF8CharNumBytes(c);
2742 if (charLen>0)
2743 {
2744 result+=DString(p-1,charLen);
2745 p+=charLen;
2746 doEscape = false;
2747 }
2748 }
2749 if (doEscape) // not a valid unicode char or escaping needed
2750 {
2751 char ids[5];
2752 unsigned char id = static_cast<unsigned char>(c);
2753 ids[0]='_';
2754 ids[1]='x';
2755 ids[2]=hex[id>>4];
2756 ids[3]=hex[id&0xF];
2757 ids[4]=0;
2758 result+=ids;
2759 }
2760 }
2761 else if (caseSenseNames || !isupper(c))
2762 {
2763 result+=c;
2764 }
2765 else
2766 {
2767 result+='_';
2768 result+=static_cast<char>(tolower(c));
2769 }
2770 break;
2771 }
2772 }
2773 return result;
2774}
2775
2777{
2778 if (s.empty()) return s;
2779 bool caseSenseNames = useCaseSenseNames();
2780 DString result;
2781 result.reserve(s.length());
2782 const char *p = s.data();
2783 if (p)
2784 {
2785 char c = 0;
2786 while ((c=*p++))
2787 {
2788 if (c=='_') // 2 or 3 character escape
2789 {
2790 switch (*p)
2791 {
2792 case '_': result+=c; p++; break; // __ -> '_'
2793 case '1': result+=':'; p++; break; // _1 -> ':'
2794 case '2': result+='/'; p++; break; // _2 -> '/'
2795 case '3': result+='<'; p++; break; // _3 -> '<'
2796 case '4': result+='>'; p++; break; // _4 -> '>'
2797 case '5': result+='*'; p++; break; // _5 -> '*'
2798 case '6': result+='&'; p++; break; // _6 -> '&'
2799 case '7': result+='|'; p++; break; // _7 -> '|'
2800 case '8': result+='.'; p++; break; // _8 -> '.'
2801 case '9': result+='!'; p++; break; // _9 -> '!'
2802 case '0': // 3 character escape
2803 switch (*(p+1))
2804 {
2805 case '0': result+=','; p+=2; break; // _00 -> ','
2806 case '1': result+=' '; p+=2; break; // _01 -> ' '
2807 case '2': result+='{'; p+=2; break; // _02 -> '{'
2808 case '3': result+='}'; p+=2; break; // _03 -> '}'
2809 case '4': result+='?'; p+=2; break; // _04 -> '?'
2810 case '5': result+='^'; p+=2; break; // _05 -> '^'
2811 case '6': result+='%'; p+=2; break; // _06 -> '%'
2812 case '7': result+='('; p+=2; break; // _07 -> '('
2813 case '8': result+=')'; p+=2; break; // _08 -> ')'
2814 case '9': result+='+'; p+=2; break; // _09 -> '+'
2815 case 'a': result+='='; p+=2; break; // _0a -> '='
2816 case 'b': result+='$'; p+=2; break; // _0b -> '$'
2817 case 'c': result+='\\'; p+=2; break;// _0c -> '\'
2818 case 'd': result+='@'; p+=2; break; // _0d -> '@'
2819 case 'e': result+=']'; p+=2; break; // _0e -> ']'
2820 case 'f': result+='['; p+=2; break; // _0f -> '['
2821 case 'g': result+='#'; p+=2; break; // _0g -> '#'
2822 case 'h': result+='"'; p+=2; break; // _0h -> '"'
2823 case 'i': result+='~'; p+=2; break; // _0i -> '~'
2824 case 'j': result+='\''; p+=2; break;// _0j -> '\'
2825 case 'k': result+=';'; p+=2; break; // _0k -> ';'
2826 case 'l': result+='`'; p+=2; break; // _0l -> '`'
2827 default: // unknown escape, just pass underscore character as-is
2828 result+=c;
2829 break;
2830 }
2831 break;
2832 default:
2833 if (!caseSenseNames && c>='a' && c<='z') // lower to upper case escape, _a -> 'A'
2834 {
2835 result+=static_cast<char>(toupper(*p));
2836 p++;
2837 }
2838 else // unknown escape, pass underscore character as-is
2839 {
2840 result+=c;
2841 }
2842 break;
2843 }
2844 }
2845 else // normal character; pass as is
2846 {
2847 result+=c;
2848 }
2849 }
2850 }
2851 return result;
2852}
2853
2854static std::unordered_map<std::string,int> g_usedNames;
2855static std::mutex g_usedNamesMutex;
2856static int g_usedNamesCount=1;
2857
2858
2859
2860/*! This function determines the file name on disk of an item
2861 * given its name, which could be a class name with template
2862 * arguments, so special characters need to be escaped.
2863 */
2864DString convertNameToFile(const DString &name,bool allowDots,bool allowUnderscore)
2865{
2866 if (name.empty()) return name;
2867 bool shortNames = Config_getBool(SHORT_NAMES);
2868 bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
2869 DString result;
2870 if (shortNames) // use short names only
2871 {
2872 std::lock_guard<std::mutex> lock(g_usedNamesMutex);
2873 auto kv = g_usedNames.find(name.str());
2874 uint32_t num=0;
2875 if (kv!=g_usedNames.end())
2876 {
2877 num = kv->second;
2878 }
2879 else
2880 {
2881 num = g_usedNamesCount;
2882 g_usedNames.emplace(name.str(),g_usedNamesCount++);
2883 }
2884 result.sprintf("a%05d",num);
2885 }
2886 else // long names
2887 {
2888 result=escapeCharsInString(name,allowDots,allowUnderscore);
2889 size_t resultLen = result.length();
2890 if (resultLen>=128) // prevent names that cannot be created!
2891 {
2892 // third algorithm based on MD5 hash
2893 result=result.left(128-32)+md5str(result.view());
2894 }
2895 }
2896 if (createSubdirs)
2897 {
2898 int l1Dir=0,l2Dir=0;
2899 int createSubdirsLevel = Config_getInt(CREATE_SUBDIRS_LEVEL);
2900 int createSubdirsBitmaskL2 = (1<<createSubdirsLevel)-1;
2901
2902 // compute md5 hash to determine sub directory to use
2903 auto md5_sig = md5hash(result.view());
2904 l1Dir = md5_sig[14] & 0xf;
2905 l2Dir = md5_sig[15] & createSubdirsBitmaskL2;
2906
2907 result.prepend(DString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
2908 }
2909 //printf("*** convertNameToFile(%s)->%s\n",qPrint(name),qPrint(result));
2910 return result;
2911}
2912
2914{
2915 DString result;
2916 if (Config_getBool(CREATE_SUBDIRS))
2917 {
2918 if (name.empty())
2919 {
2920 return REL_PATH_TO_ROOT;
2921 }
2922 else if (size_t i = name.rfind('/'); i!=DString::npos)
2923 {
2924 result=REL_PATH_TO_ROOT;
2925 }
2926 }
2927 return result;
2928}
2929
2930DString determineAbsoluteIncludeName(const DString &curFile,const DString &incFileName)
2931{
2932 bool searchIncludes = Config_getBool(SEARCH_INCLUDES);
2933 DString absIncFileName = incFileName;
2934 FileInfo fi(curFile.str());
2935 if (fi.exists())
2936 {
2937 DString absName = fi.dirPath(true)+"/"+incFileName;
2938 FileInfo fi2(absName.str());
2939 if (fi2.exists())
2940 {
2941 absIncFileName=fi2.absFilePath();
2942 }
2943 else if (searchIncludes) // search in INCLUDE_PATH as well
2944 {
2945 StringVector includePath = Config_getList(INCLUDE_PATH);
2946 for (const auto &incPath : includePath)
2947 {
2948 FileInfo fi3(incPath);
2949 if (fi3.exists() && fi3.isDir())
2950 {
2951 absName = fi3.absFilePath()+"/"+incFileName;
2952 //printf("trying absName=%s\n",qPrint(absName));
2953 FileInfo fi4(absName.str());
2954 if (fi4.exists())
2955 {
2956 absIncFileName=fi4.absFilePath();
2957 break;
2958 }
2959 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
2960 }
2961 }
2962 }
2963 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
2964 }
2965 return absIncFileName;
2966}
2967
2968
2969
2970void createSubDirs(const Dir &d)
2971{
2972 if (Config_getBool(CREATE_SUBDIRS))
2973 {
2974 // create up to 4096 subdirectories
2975 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
2976 for (int l1=0; l1<16; l1++)
2977 {
2978 DString subdir;
2979 subdir.sprintf("d%x",l1);
2980 if (!d.exists(subdir.str()) && !d.mkdir(subdir.str()))
2981 {
2982 term("Failed to create output directory '{}'\n",subdir);
2983 }
2984 for (int l2=0; l2<createSubdirsLevelPow2; l2++)
2985 {
2986 DString subsubdir;
2987 subsubdir.sprintf("d%x/d%02x",l1,l2);
2988 if (!d.exists(subsubdir.str()) && !d.mkdir(subsubdir.str()))
2989 {
2990 term("Failed to create output directory '{}'\n",subsubdir);
2991 }
2992 }
2993 }
2994 }
2995}
2996
2997void clearSubDirs(const Dir &d)
2998{
2999 if (Config_getBool(CREATE_SUBDIRS))
3000 {
3001 // remove empty subdirectories
3002 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
3003 for (int l1=0;l1<16;l1++)
3004 {
3005 DString subdir;
3006 subdir.sprintf("d%x",l1);
3007 for (int l2=0; l2 < createSubdirsLevelPow2; l2++)
3008 {
3009 DString subsubdir;
3010 subsubdir.sprintf("d%x/d%02x",l1,l2);
3011 if (d.exists(subsubdir.str()) && d.empty(subsubdir.str()))
3012 {
3013 d.rmdir(subsubdir.str());
3014 }
3015 }
3016 if (d.exists(subdir.str()) && d.empty(subdir.str()))
3017 {
3018 d.rmdir(subdir.str());
3019 }
3020 }
3021 }
3022}
3023
3024/*! Input is a scopeName, output is the scopename split into a
3025 * namespace part (as large as possible) and a classname part.
3026 */
3027void extractNamespaceName(const DString &scopeName,
3028 DString &className,DString &namespaceName,
3029 bool allowEmptyClass)
3030{
3031 DString clName=scopeName;
3032 NamespaceDef *nd = nullptr;
3033 size_t i=0;
3034 int p=0;
3035 if (!clName.empty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==nullptr)
3036 { // the whole name is a namespace (and not a class)
3037 namespaceName=nd->name();
3038 className.clear();
3039 goto done;
3040 }
3041 p=static_cast<int>(clName.length())-2;
3042 while (p>=0 && (i=clName.rfind("::",p))!=DString::npos)
3043 // see if the first part is a namespace (and not a class)
3044 {
3045 //printf("Trying %s\n",qPrint(clName.left(i)));
3046 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==nullptr)
3047 {
3048 //printf("found!\n");
3049 namespaceName=nd->name();
3050 className=clName.mid(i+2);
3051 goto done;
3052 }
3053 p=static_cast<int>(i)-2; // try a smaller piece of the scope
3054 }
3055 //printf("not found!\n");
3056
3057 // not found, so we just have to guess.
3058 className=scopeName;
3059 namespaceName.clear();
3060
3061done:
3062 if (className.empty() && !namespaceName.empty() && !allowEmptyClass)
3063 {
3064 // class and namespace with the same name, correct to return the class.
3065 className=namespaceName;
3066 namespaceName.clear();
3067 }
3068 //printf("extractNamespace '%s' => '%s|%s'\n",qPrint(scopeName),
3069 // qPrint(className),qPrint(namespaceName));
3070 if (className.endsWith("-p"))
3071 {
3072 className = className.left(className.length()-2);
3073 }
3074 return;
3075}
3076
3078{
3079 DString result=scope;
3080 if (!templ.empty() && scope.find('<')==DString::npos)
3081 {
3082 size_t si=0, pi=0;
3083 ClassDef *cd=nullptr;
3084 while (
3085 (si=scope.find("::",pi))!=DString::npos && !getClass(scope.left(si)+templ) &&
3086 ((cd=getClass(scope.left(si)))==nullptr || cd->templateArguments().empty())
3087 )
3088 {
3089 //printf("Tried '%s'\n",qPrint((scope.left(si)+templ)));
3090 pi=si+2;
3091 }
3092 if (si==DString::npos) // not nested => append template specifier
3093 {
3094 result+=templ;
3095 }
3096 else // nested => insert template specifier before after first class name
3097 {
3098 result=scope.left(si) + templ + scope.mid(si);
3099 }
3100 }
3101 //printf("insertTemplateSpecifierInScope('%s','%s')=%s\n",
3102 // qPrint(scope),qPrint(templ),qPrint(result));
3103 return result;
3104}
3105
3106
3107/*! Strips the scope from a name. Examples: A::B will return A
3108 * and A<T>::B<N::C<D> > will return A<T>.
3109 */
3111{
3112 DString result = name;
3113 int l = static_cast<int>(result.length());
3114 int p = 0;
3115 bool done = false;
3116 bool skipBracket=false; // if brackets do not match properly, ignore them altogether
3117 int count=0;
3118 int round=0;
3119
3120 do
3121 {
3122 p=l-1; // start at the end of the string
3123 while (p>=0 && count>=0)
3124 {
3125 char c=result.at(p);
3126 switch (c)
3127 {
3128 case ':':
3129 // only exit in the case of ::
3130 //printf("stripScope(%s)=%s\n",name,qPrint(result.right(l-p-1)));
3131 if (p>0 && result.at(p-1)==':' && (count==0 || skipBracket))
3132 {
3133 return result.right(l-p-1);
3134 }
3135 p--;
3136 break;
3137 case '>':
3138 if (skipBracket) // we don't care about brackets
3139 {
3140 p--;
3141 }
3142 else // count open/close brackets
3143 {
3144 if (p>0 && result.at(p-1)=='>') // skip >> operator
3145 {
3146 p-=2;
3147 break;
3148 }
3149 count=1;
3150 //printf("pos < = %d\n",p);
3151 p--;
3152 bool foundMatch=false;
3153 while (p>=0 && !foundMatch)
3154 {
3155 c=result.at(p--);
3156 switch (c)
3157 {
3158 case ')':
3159 round++;
3160 break;
3161 case '(':
3162 round--;
3163 break;
3164 case '>': // ignore > inside (...) to support e.g. (sizeof(T)>0) inside template parameters
3165 if (round==0) count++;
3166 break;
3167 case '<':
3168 if (round==0)
3169 {
3170 if (p>0)
3171 {
3172 if (result.at(p-1) == '<') // skip << operator
3173 {
3174 p--;
3175 break;
3176 }
3177 }
3178 count--;
3179 foundMatch = count==0;
3180 }
3181 break;
3182 default:
3183 //printf("c=%c count=%d\n",c,count);
3184 break;
3185 }
3186 }
3187 }
3188 //printf("pos > = %d\n",p+1);
3189 break;
3190 default:
3191 p--;
3192 }
3193 }
3194 done = count==0 || skipBracket; // reparse if brackets do not match
3195 skipBracket=true;
3196 }
3197 while (!done); // if < > unbalanced repeat ignoring them
3198 //printf("stripScope(%s)=%s\n",name,name);
3199 return name;
3200}
3201
3202/*! Converts a string to a HTML id string */
3204{
3205 if (s.empty()) return s;
3206 DString result;
3207 result.reserve(s.length()+8);
3208 const char *p = s.data();
3209 char c = 0;
3210 bool first = true;
3211 while ((c=*p++))
3212 {
3213 char encChar[4];
3214 if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-')
3215 { // any permissive character except _
3216 if (first && c>='0' && c<='9') result+='a'; // don't start with a digit
3217 result+=c;
3218 }
3219 else
3220 {
3221 encChar[0]='_';
3222 encChar[1]=hex[static_cast<unsigned char>(c)>>4];
3223 encChar[2]=hex[static_cast<unsigned char>(c)&0xF];
3224 encChar[3]=0;
3225 result+=encChar;
3226 }
3227 first=false;
3228 }
3229 return result;
3230}
3231
3232/*! Converts a string to an XML-encoded string */
3233DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
3234{
3235 if (s.empty()) return s;
3236 DString result;
3237 result.reserve(s.length()+32);
3238 const char *p = s.data();
3239 char c = 0;
3240 while ((c=*p++))
3241 {
3242 switch (c)
3243 {
3244 case '<': result+="&lt;"; break;
3245 case '>': result+="&gt;"; break;
3246 case '&': if (keepEntities)
3247 {
3248 const char *e=p;
3249 char ce = 0;
3250 while ((ce=*e++))
3251 {
3252 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
3253 }
3254 if (ce==';') // found end of an entity
3255 {
3256 // copy entry verbatim
3257 result+=c;
3258 while (p<e) result+=*p++;
3259 }
3260 else
3261 {
3262 result+="&amp;";
3263 }
3264 }
3265 else if (citeEntry)
3266 {
3268 result,
3269 p-1,
3270 [](HtmlEntityMapper::SymType symType) { return HtmlEntityMapper::instance().xml(symType); },
3271 "&amp;");
3272 }
3273 else
3274 {
3275 result+="&amp;";
3276 }
3277 break;
3278 case '\'': result+="&apos;"; break;
3279 case '"': result+="&quot;"; break;
3280 case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
3281 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
3282 case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
3283 case 27: case 28: case 29: case 30: case 31:
3284 break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
3285 default: result+=c; break;
3286 }
3287 }
3288 return result;
3289}
3290
3291/*! Converts a string to a HTML-encoded string */
3292DString convertToHtml(const DString &s,bool keepEntities)
3293{
3294 if (s.empty()) return s;
3295 DString result;
3296 result.reserve(s.length()+32);
3297 const char *p=s.data();
3298 char c = 0;
3299 while ((c=*p++))
3300 {
3301 switch (c)
3302 {
3303 case '<': result+="&lt;"; break;
3304 case '>': result+="&gt;"; break;
3305 case '&': if (keepEntities)
3306 {
3307 const char *e=p;
3308 char ce = 0;
3309 while ((ce=*e++))
3310 {
3311 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
3312 }
3313 if (ce==';') // found end of an entity
3314 {
3315 // copy entry verbatim
3316 result+=c;
3317 while (p<e) result+=*p++;
3318 }
3319 else
3320 {
3321 result+="&amp;";
3322 }
3323 }
3324 else
3325 {
3326 result+="&amp;";
3327 }
3328 break;
3329 case '\'': result+="&#39;"; break;
3330 case '"': result+="&quot;"; break;
3331 default:
3332 {
3333 uint8_t uc = static_cast<uint8_t>(c);
3334 if (uc<32 && !isspace(c))
3335 {
3336 result+="&#x24";
3337 result+=hex[uc>>4];
3338 result+=hex[uc&0xF];
3339 result+=';';
3340 }
3341 else
3342 {
3343 result+=c;
3344 }
3345 }
3346 break;
3347 }
3348 }
3349 return result;
3350}
3351
3352DString convertToJSString(const DString &s,bool keepEntities,bool singleQuotes)
3353{
3354 if (s.empty()) return s;
3355 DString result;
3356 result.reserve(s.length()+32);
3357 const char *p=s.data();
3358 char c = 0;
3359 while ((c=*p++))
3360 {
3361 switch (c)
3362 {
3363 case '"': if (!singleQuotes) result+="\\\""; else result+=c;
3364 break;
3365 case '\'': if (singleQuotes) result+="\\\'"; else result+=c;
3366 break;
3367 case '\\': if (*p=='u' && *(p+1)=='{') result+="\\"; // keep \u{..} unicode escapes
3368 else result+="\\\\";
3369 break;
3370 default: result+=c; break;
3371 }
3372 }
3373 return keepEntities ? result : HtmlEntityMapper::instance().convertCharEntitiesToUTF8(result);
3374}
3375
3376/*! Extracts a (sub-)string from \a type starting at \a pos that
3377 * could form a class. The index of the match is returned and the found
3378 * class \a name and a template argument list \a templSpec. If -1 is returned
3379 * there are no more matches.
3380 */
3381int extractClassNameFromType(const DString &type,int &pos,DString &name,DString &templSpec,SrcLangExt lang)
3382{
3383 AUTO_TRACE("type='{}' pos={} name='{}' lang={}",type,pos,name,lang);
3384 static const reg::Ex re_norm(R"(\a[\w:]*)");
3385 static const reg::Ex re_fortran(R"(\a[\w:()=]*)");
3386 const reg::Ex *re = &re_norm;
3387
3388 name.clear();
3389 templSpec.clear();
3390 if (type.empty())
3391 {
3392 AUTO_TRACE_EXIT("empty type");
3393 return -1;
3394 }
3395 size_t typeLen=type.length();
3396 if (typeLen>0)
3397 {
3398 if (lang == SrcLangExt::Fortran)
3399 {
3400 if (type[pos]==',')
3401 {
3402 AUTO_TRACE_EXIT("comma");
3403 return -1;
3404 }
3405 if (!type.lower().startsWith("type"))
3406 {
3407 re = &re_fortran;
3408 }
3409 }
3410 std::string s = type.str();
3411 reg::Iterator it(s,*re,static_cast<int>(pos));
3413
3414 if (it!=end)
3415 {
3416 const auto &match = *it;
3417 size_t i = match.position();
3418 size_t l = match.length();
3419 size_t ts = i+l;
3420 size_t te = ts;
3421 size_t tl = 0;
3422
3423 while (ts<typeLen && type[static_cast<uint32_t>(ts)]==' ') { ts++; tl++; } // skip any whitespace
3424 if (ts<typeLen && type[static_cast<uint32_t>(ts)]=='<') // assume template instance
3425 {
3426 // locate end of template
3427 te=ts+1;
3428 int brCount=1;
3429 while (te<typeLen && brCount!=0)
3430 {
3431 if (type[static_cast<uint32_t>(te)]=='<')
3432 {
3433 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='<') te++; else brCount++;
3434 }
3435 if (type[static_cast<uint32_t>(te)]=='>')
3436 {
3437 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='>') te++; else brCount--;
3438 }
3439 te++;
3440 }
3441 }
3442 name = match.str();
3443 if (te>ts)
3444 {
3445 templSpec = DString(type).mid(ts,te-ts);
3446 tl+=te-ts;
3447 pos=static_cast<int>(i+l+tl);
3448 }
3449 else // no template part
3450 {
3451 pos=static_cast<int>(i+l);
3452 }
3453 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=true i=%d\n",
3454 // qPrint(type),pos,qPrint(name),qPrint(templSpec),i);
3455 AUTO_TRACE_EXIT("pos={} templSpec='{}' return={}",pos,templSpec,i);
3456 return static_cast<int>(i);
3457 }
3458 }
3459 pos = static_cast<int>(typeLen);
3460 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=false\n",
3461 // qPrint(type),pos,qPrint(name),qPrint(templSpec));
3462 AUTO_TRACE_EXIT("not found");
3463 return -1;
3464}
3465
3467 const DString &name,
3468 const Definition *context,
3469 const ArgumentList &formalArgs)
3470{
3471 // skip until <
3472 size_t p=name.find('<');
3473 if (p==DString::npos) return name;
3474 p++;
3475 DString result = name.left(p);
3476
3477 std::string s = name.mid(p).str();
3478 static const reg::Ex re(R"([\a:][\w:]*)");
3479 reg::Iterator it(s,re);
3481 size_t pi=0;
3482 // for each identifier in the template part (e.g. B<T> -> T)
3483 for (; it!=end ; ++it)
3484 {
3485 const auto &match = *it;
3486 size_t i = match.position();
3487 size_t l = match.length();
3488 result += s.substr(pi,i-pi);
3489 DString n(match.str());
3490 bool found=false;
3491 for (const Argument &formArg : formalArgs)
3492 {
3493 if (formArg.name == n)
3494 {
3495 found=true;
3496 break;
3497 }
3498 }
3499 if (!found)
3500 {
3501 // try to resolve the type
3502 SymbolResolver resolver;
3503 const ClassDef *cd = resolver.resolveClass(context,n);
3504 if (cd)
3505 {
3506 result+=cd->name();
3507 }
3508 else
3509 {
3510 result+=n;
3511 }
3512 }
3513 else
3514 {
3515 result+=n;
3516 }
3517 pi=i+l;
3518 }
3519 result+=s.substr(pi);
3520 //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",qPrint(name),qPrint(result));
3521 return removeRedundantWhiteSpace(result);
3522}
3523
3524
3526 const DString &nm,
3527 const ArgumentList &formalArgs,
3528 const ArgumentList *actualArgs)
3529{
3530 AUTO_TRACE("name={} formalArgs={} actualArgs={}",nm,argListToString(formalArgs),actualArgs ? argListToString(*actualArgs) : DString());
3531 if (formalArgs.empty()) return nm;
3532 DString result;
3533
3534 static const reg::Ex re(R"(\a\w*)");
3535 std::string name = nm.str();
3536 reg::Iterator it(name,re);
3538 size_t p=0;
3539
3540 for (; it!=end ; ++it)
3541 {
3542 const auto &match = *it;
3543 size_t i = match.position();
3544 size_t l = match.length();
3545 if (i>p) result += name.substr(p,i-p);
3546 DString n(match.str());
3548 if (actualArgs)
3549 {
3550 actIt = actualArgs->begin();
3551 }
3552 //printf(": name=%s\n",qPrint(name));
3553
3554 // if n is a template argument, then we substitute it
3555 // for its template instance argument.
3556 bool found=false;
3557 for (auto formIt = formalArgs.begin();
3558 formIt!=formalArgs.end() && !found;
3559 ++formIt
3560 )
3561 {
3562 Argument formArg = *formIt;
3563 Argument actArg;
3564 if (actualArgs && actIt!=actualArgs->end())
3565 {
3566 actArg = *actIt;
3567 }
3568 if (formArg.type.startsWith("class ") && formArg.name.empty())
3569 {
3570 formArg.name = formArg.type.mid(6);
3571 formArg.type = "class";
3572 }
3573 else if (formArg.type.startsWith("typename ") && formArg.name.empty())
3574 {
3575 formArg.name = formArg.type.mid(9);
3576 formArg.type = "typename";
3577 }
3578 else if (formArg.type.startsWith("class...")) // match 'class... name' to 'name...'
3579 {
3580 formArg.name += "...";
3581 formArg.type = formArg.type.left(5)+formArg.type.mid(8);
3582 }
3583 else if (formArg.type.startsWith("typename...")) // match 'typename... name' to 'name...'
3584 {
3585 formArg.name += "...";
3586 formArg.type = formArg.type.left(8)+formArg.type.mid(11);
3587 }
3588 //printf(": n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s' actArg->type='%s' actArg->name='%s' \n",
3589 // qPrint(n),qPrint(formArg.type),qPrint(formArg.name),qPrint(formArg.defval),qPrint(actArg.type),qPrint(actArg.name));
3590 if (formArg.type=="class" || formArg.type=="typename" || formArg.type.startsWith("template"))
3591 {
3592 if (formArg.name==n && actualArgs && actIt!=actualArgs->end() && !actArg.type.empty()) // base class is a template argument
3593 {
3594 static constexpr auto hasRecursion = [](const DString &prefix,const DString &nameArg,const DString &subst) -> bool
3595 {
3596 size_t ii=0;
3597 size_t pp=0;
3598
3599 ii = subst.find('<');
3600 //printf("prefix='%s' subst='%s'\n",qPrint(prefix.mid(prefix.length()-ii-2,ii+1)),qPrint(subst.left(ii+1)));
3601 if (ii!=DString::npos && prefix.length()>=ii+2 && prefix.mid(prefix.length()-ii-2,ii+1)==subst.left(ii+1))
3602 {
3603 return true; // don't replace 'A< ' with 'A< A<...', see issue #10951
3604 }
3605
3606 while ((ii=subst.find(nameArg,pp))!=DString::npos)
3607 {
3608 bool beforeNonWord = ii==0 || !isId(subst.at(ii-1));
3609 bool afterNonWord = subst.length()==ii+nameArg.length() || !isId(subst.at(ii+nameArg.length()));
3610 if (beforeNonWord && afterNonWord)
3611 {
3612 return true; // if nameArg=='A' then subst=='A::Z' or 'S<A>' or 'Z::A' should return true, but 'AA::ZZ' or 'BAH' should not match
3613 }
3614 pp=ii+nameArg.length();
3615 }
3616 return false;
3617 };
3618 // replace formal argument with the actual argument of the instance
3619 AUTO_TRACE_ADD("result={} n={} type={} hasRecursion={}",result,n,actArg.type,hasRecursion(result,n,actArg.type));
3620 if (!hasRecursion(result,n,actArg.type))
3621 // the scope guard is to prevent recursive lockup for
3622 // template<class A> class C : public<A::T>,
3623 // where A::T would become A::T::T here,
3624 // since n==A and actArg->type==A::T
3625 // see bug595833 for an example
3626 //
3627 // Also prevent recursive substitution if n is part of actArg.type, i.e.
3628 // n='A' in argType='S< A >' would produce 'S< S< A > >'
3629 {
3630 if (actArg.name.empty())
3631 {
3632 result += actArg.type;
3633 }
3634 else
3635 // for case where the actual arg is something like "unsigned int"
3636 // the "int" part is in actArg->name.
3637 {
3638 result += actArg.type+" "+actArg.name;
3639 }
3640 found=true;
3641 }
3642 }
3643 else if (formArg.name==n &&
3644 (actualArgs==nullptr || actIt==actualArgs->end()) &&
3645 !formArg.defval.empty() &&
3646 formArg.defval!=nm /* to prevent recursion */
3647 )
3648 {
3649 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
3650 found=true;
3651 }
3652 }
3653 else if (formArg.name==n &&
3654 (actualArgs==nullptr || actIt==actualArgs->end()) &&
3655 !formArg.defval.empty() &&
3656 formArg.defval!=nm /* to prevent recursion */
3657 )
3658 {
3659 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
3660 found=true;
3661 }
3662 if (actualArgs && actIt!=actualArgs->end())
3663 {
3664 actIt++;
3665 }
3666 }
3667 if (!found)
3668 {
3669 result += n;
3670 }
3671 p=i+l;
3672 }
3673 result+=name.substr(p);
3674 result=result.simplifyWhiteSpace();
3675 AUTO_TRACE_EXIT("result={}",result);
3676 return result.stripWhiteSpace();
3677}
3678
3679
3680/*! Strips template specifiers from scope \a fullName, except those
3681 * that make up specialized classes. The switch \a parentOnly
3682 * determines whether or not a template "at the end" of a scope
3683 * should be considered, e.g. with \a parentOnly is \c true, `A<T>::B<S>` will
3684 * try to strip `<T>` and not `<S>`, while \a parentOnly is \c false will
3685 * strip both unless `A<T>` or `B<S>` are specialized template classes.
3686 */
3688 bool parentOnly,
3689 DString *pLastScopeStripped,
3690 DString scopeName,
3691 bool allowArtificial)
3692{
3693 //printf("stripTemplateSpecifiersFromScope(name=%s,scopeName=%s)\n",qPrint(fullName),qPrint(scopeName));
3694 size_t i=fullName.find('<');
3695 if (i==DString::npos) return fullName;
3696 DString result;
3697 size_t p=0;
3698 size_t l=fullName.length();
3699 while (i!=DString::npos)
3700 {
3701 //printf("1:result+=%s\n",qPrint(fullName.mid(p,i-p)));
3702 size_t e=i+1;
3703 int count=1;
3704 int round=0;
3705 while (e<l && count>0)
3706 {
3707 char c=fullName.at(e++);
3708 switch (c)
3709 {
3710 case '(': round++; break;
3711 case ')': if (round>0) round--; break;
3712 case '<': if (round==0) count++; break;
3713 case '>': if (round==0) count--; break;
3714 default:
3715 break;
3716 }
3717 }
3718 size_t si = fullName.find("::",e);
3719
3720 if (parentOnly && si==DString::npos) break;
3721 // we only do the parent scope, so we stop here if needed
3722
3723 result+=fullName.mid(p,i-p);
3724 //printf(" trying %s\n",qPrint(mergeScopes(scopeName,result+fullName.mid(i,e-i))));
3725 ClassDef *cd = getClass(mergeScopes(scopeName,result+fullName.mid(i,e-i)));
3726 if (cd!=nullptr && (allowArtificial || !cd->isArtificial()))
3727 {
3728 result+=fullName.mid(i,e-i);
3729 //printf(" 2:result+=%s\n",qPrint(fullName.mid(i,e-i-1)));
3730 }
3731 else if (pLastScopeStripped)
3732 {
3733 //printf(" last stripped scope '%s'\n",qPrint(fullName.mid(i,e-i)));
3734 *pLastScopeStripped=fullName.mid(i,e-i);
3735 }
3736 p=e;
3737 i=fullName.find('<',p);
3738 }
3739 result+=fullName.right(l-p);
3740 //printf("3:result+=%s\n",qPrint(fullName.right(l-p)));
3741 //printf("end result=%s\n",qPrint(result));
3742 return result;
3743}
3744
3745/*! Merges two scope parts together. The parts may (partially) overlap.
3746 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
3747 * Example2: \c A and \c B will be \c A::B <br>
3748 * Example3: \c A::B and B will be \c A::B
3749 *
3750 * @param leftScope the left hand part of the scope.
3751 * @param rightScope the right hand part of the scope.
3752 * @returns the merged scope.
3753 */
3754DString mergeScopes(const DString &leftScope,const DString &rightScope)
3755{
3756 AUTO_TRACE("leftScope='{}' rightScope='{}'",leftScope,rightScope);
3757 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
3758 if (leftScopeMatch(leftScope,rightScope))
3759 {
3760 AUTO_TRACE_EXIT("case1={}",rightScope);
3761 return rightScope;
3762 }
3763 DString result;
3764 size_t i=0,p=leftScope.length();
3765
3766 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
3767 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
3768 bool found=false;
3769 while ((i=leftScope.rfind("::",p))!=DString::npos && i>0)
3770 {
3771 if (leftScopeMatch(rightScope,leftScope.mid(i+2)))
3772 {
3773 result = leftScope.left(i+2)+rightScope;
3774 found=true;
3775 }
3776 p=i-1;
3777 }
3778 if (found)
3779 {
3780 AUTO_TRACE_EXIT("case2={}",result);
3781 return result;
3782 }
3783
3784 // case leftScope=="A" rightScope=="B" => result = "A::B"
3785 result=leftScope;
3786 if (!result.empty() && !rightScope.empty()) result+="::";
3787 result+=rightScope;
3788 AUTO_TRACE_EXIT("case3={}",result);
3789 return result;
3790}
3791
3792/*! Returns a fragment from scope \a s, starting at position \a p.
3793 *
3794 * @param s the scope name as a string.
3795 * @param p the start position (0 is the first).
3796 * @param l the resulting length of the fragment.
3797 * @returns the location of the fragment, or -1 if non is found.
3798 */
3799int getScopeFragment(const DString &s,int p,int *l)
3800{
3801 int sl=static_cast<int>(s.length());
3802 int sp=p;
3803 int count=0;
3804 bool done=false;
3805 if (sp>=sl) return -1;
3806 while (sp<sl)
3807 {
3808 char c=s.at(sp);
3809 if (c==':')
3810 {
3811 sp++;
3812 p++;
3813 }
3814 else
3815 {
3816 break;
3817 }
3818 }
3819 while (sp<sl)
3820 {
3821 char c=s.at(sp);
3822 switch (c)
3823 {
3824 case ':': // found next part
3825 goto found;
3826 case '<': // skip template specifier
3827 count=1;sp++;
3828 done=false;
3829 while (sp<sl && !done)
3830 {
3831 // TODO: deal with << and >> operators!
3832 c=s.at(sp++);
3833 switch(c)
3834 {
3835 case '<': count++; break;
3836 case '>': count--; if (count==0) done=true; break;
3837 default: break;
3838 }
3839 }
3840 break;
3841 default:
3842 sp++;
3843 break;
3844 }
3845 }
3846found:
3847 *l=sp-p;
3848 //printf("getScopeFragment(%s,%d)=%s\n",qPrint(s),p,qPrint(s.mid(p,*l)));
3849 return p;
3850}
3851
3852//----------------------------------------------------------------------------
3853
3855{
3856 ModuleDef *mod = nullptr;
3858 {
3859 const FileDef *fd = toFileDef(d);
3860 if (fd) mod = fd->getModuleDef();
3861 }
3863 {
3864 const ClassDef *cd = toClassDef(d);
3865 if (cd)
3866 {
3867 const FileDef *fd = cd->getFileDef();
3868 if (fd) mod = fd->getModuleDef();
3869 }
3870 }
3872 {
3873 const ConceptDef *cd = toConceptDef(d);
3874 if (cd)
3875 {
3876 const FileDef *fd = cd->getFileDef();
3877 if (fd) mod = fd->getModuleDef();
3878 }
3879 }
3880 return mod;
3881}
3882
3883static bool recursivelyAddGroupListToTitle(OutputList &ol,const Definition *d,bool root)
3884{
3885 ModuleDef *mod = root ? findModuleDef(d) : nullptr;
3886 if (!d->partOfGroups().empty() || mod!=nullptr) // write list of group to which this definition belongs
3887 {
3888 if (root)
3889 {
3890 ol.pushGeneratorState();
3892 ol.writeString("<div class=\"ingroups\">");
3893 }
3894 bool first=true;
3895 for (const auto &gd : d->partOfGroups())
3896 {
3897 if (!first) { ol.writeString(" &#124; "); } else first=false;
3898 if (recursivelyAddGroupListToTitle(ol, gd, false))
3899 {
3900 ol.writeString(" &raquo; ");
3901 }
3902 ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),DString(),gd->groupTitle());
3903 }
3904 if (root)
3905 {
3906 // add module as a group to the file as well
3907 if (mod)
3908 {
3909 if (!first) { ol.writeString(" &#124; "); } else first=false;
3910 ol.writeString(theTranslator->trModule(false,true)+" ");
3912 mod->displayName());
3913 }
3914 ol.writeString("</div>");
3915 ol.popGeneratorState();
3916 }
3917 return true;
3918 }
3919 return false;
3920}
3921
3923{
3925}
3926
3927bool checkExtension(const DString &fName, const DString &ext)
3928{
3929 return fName.right(ext.length())==ext;
3930}
3931
3933{
3934 if (fName.empty()) return;
3935 size_t i_fs = fName.rfind('/');
3936 size_t i_bs = fName.rfind('\\');
3937 size_t p = i_fs!=DString::npos && i_bs!=DString::npos ? std::max(i_fs, i_bs) :
3938 i_fs!=DString::npos ? i_fs : i_bs!=DString::npos ? i_bs : 0;
3939 size_t i = fName.find('.',p); // search for . after path part
3940 if (i==DString::npos)
3941 {
3943 }
3944}
3945
3947{
3948 DString result=fName;
3949 if (result.right(ext.length())==ext)
3950 {
3951 result=result.left(result.length()-ext.length());
3952 }
3953 return result;
3954}
3955
3960
3962{
3963 DString result=s;
3964 if (size_t i=result.rfind('/'); i!=DString::npos)
3965 {
3966 result=result.mid(i+1);
3967 }
3968 if (size_t i=result.rfind('\\'); i!=DString::npos)
3969 {
3970 result=result.mid(i+1);
3971 }
3972 return result;
3973}
3974
3975DString makeBaseName(const DString &name, const DString &ext)
3976{
3977 return stripExtensionGeneral(stripPath(name), ext);
3978}
3979
3980/** Special version of DString::stripWhiteSpace() that only strips
3981 * completely blank lines.
3982 * @param s the string to be stripped
3983 * @param docLine the line number corresponding to the start of the
3984 * string. This will be adjusted based on the number of lines stripped
3985 * from the start.
3986 * @returns The stripped string.
3987 */
3989{
3990 if (s.empty()) return DString();
3991 const char *p = s.data();
3992
3993 // search for leading empty lines
3994 int i=0,li=-1,l=static_cast<int>(s.length());
3995 char c = 0;
3996 while ((c=*p))
3997 {
3998 if (c==' ' || c=='\t' || c=='\r') { i++; p++; }
3999 else if (c=='\\' && literal_at(p,"\\ilinebr")) { i+=8; li=i; p+=8; }
4000 else if (c=='\n') { i++; li=i; docLine++; p++; }
4001 else break;
4002 }
4003
4004 // search for trailing empty lines
4005 int b=l-1,bi=-1;
4006 p=s.data()+b;
4007 while (b>=0)
4008 {
4009 c=*p;
4010 if (c==' ' || c=='\t' || c=='\r') { b--; p--; }
4011 else if (c=='r' && b>=7 && literal_at(p-7,"\\ilinebr")) { bi=b-7; b-=8; p-=8; }
4012 else if (c=='>' && b>=11 && literal_at(p-11,"\\ilinebr<br>")) { bi=b-11; b-=12; p-=12; }
4013 else if (c=='\n') { bi=b; b--; p--; }
4014 else break;
4015 }
4016
4017 // return whole string if no leading or trailing lines where found
4018 if (li==-1 && bi==-1) return s;
4019
4020 // return substring
4021 if (bi==-1) bi=l;
4022 if (li==-1) li=0;
4023 if (bi<=li) return DString(); // only empty lines
4024 //printf("docLine='%s' len=%d li=%d bi=%d\n",qPrint(s),s.length(),li,bi);
4025 return s.mid(li,bi-li);
4026}
4027
4028//--------------------------------------------------------------------------
4029
4030static std::unordered_map<std::string,SrcLangExt> g_extLookup;
4031
4033{
4034 const char *langName;
4035 const char *parserName;
4037 const char *defExt;
4038};
4039
4040static std::vector<Lang2ExtMap> g_lang2extMap =
4041{
4042// language parser parser option
4043 { "idl", "c", SrcLangExt::IDL, ".idl" },
4044 { "java", "c", SrcLangExt::Java, ".java"},
4045 { "javascript", "c", SrcLangExt::JS, ".js" },
4046 { "csharp", "c", SrcLangExt::CSharp, ".cs" },
4047 { "d", "c", SrcLangExt::D, ".d" },
4048 { "php", "c", SrcLangExt::PHP, ".php" },
4049 { "objective-c", "c", SrcLangExt::ObjC, ".m" },
4050 { "c", "c", SrcLangExt::Cpp, ".c" },
4051 { "c++", "c", SrcLangExt::Cpp, ".cpp" },
4052 { "slice", "c", SrcLangExt::Slice, ".ice" },
4053 { "python", "python", SrcLangExt::Python, ".py" },
4054 { "fortran", "fortran", SrcLangExt::Fortran, ".f" },
4055 { "fortranfree", "fortranfree", SrcLangExt::Fortran, ".f90" },
4056 { "fortranfixed", "fortranfixed", SrcLangExt::Fortran, ".f" },
4057 { "vhdl", "vhdl", SrcLangExt::VHDL, ".vhdl"},
4058 { "xml", "xml", SrcLangExt::XML, ".xml" },
4059 { "sql", "sql", SrcLangExt::SQL, ".sql" },
4060 { "md", "md", SrcLangExt::Markdown, ".md" },
4061 { "lex", "lex", SrcLangExt::Lex, ".l" },
4062};
4063
4064bool updateLanguageMapping(const DString &extension,const DString &language)
4065{
4066 DString langName = language.lower();
4067 auto it1 = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
4068 [&langName](const auto &info) { return info.langName==langName; });
4069 if (it1 == g_lang2extMap.end()) return false;
4070
4071 // found the language
4072 SrcLangExt parserId = it1->parserId;
4073 DString extName = extension.lower();
4074 if (extName.empty()) return false;
4075 if (extName.at(0)!='.') extName.prepend(".");
4076 auto it2 = g_extLookup.find(extName.str());
4077 if (it2!=g_extLookup.end())
4078 {
4079 g_extLookup.erase(it2); // language was already register for this ext
4080 }
4081 //printf("registering extension %s\n",qPrint(extName));
4082 g_extLookup.emplace(extName.str(),parserId);
4083 if (!Doxygen::parserManager->registerExtension(extName,it1->parserName))
4084 {
4085 err("Failed to assign extension {} to parser {} for language {}\n",
4086 extName.data(),it1->parserName,language);
4087 }
4088 else
4089 {
4090 //msg("Registered extension {} to language parser {}...\n",
4091 // extName,language);
4092 }
4093 return true;
4094}
4095
4097{
4098 // NOTE: when adding an extension, also add the extension in config.xml
4099 // extension parser id
4100 updateLanguageMapping(".dox", "c");
4101 updateLanguageMapping(".txt", "c"); // see bug 760836
4102 updateLanguageMapping(".doc", "c");
4103 updateLanguageMapping(".c", "c");
4104 updateLanguageMapping(".C", "c");
4105 updateLanguageMapping(".cc", "c");
4106 updateLanguageMapping(".CC", "c");
4107 updateLanguageMapping(".cxx", "c");
4108 updateLanguageMapping(".cpp", "c");
4109 updateLanguageMapping(".c++", "c");
4110 updateLanguageMapping(".cxxm", "c"); // C++20 modules
4111 updateLanguageMapping(".cppm", "c"); // C++20 modules
4112 updateLanguageMapping(".ccm", "c"); // C++20 modules
4113 updateLanguageMapping(".c++m", "c"); // C++20 modules
4114 updateLanguageMapping(".ii", "c");
4115 updateLanguageMapping(".ixx", "c");
4116 updateLanguageMapping(".ipp", "c");
4117 updateLanguageMapping(".i++", "c");
4118 updateLanguageMapping(".inl", "c");
4119 updateLanguageMapping(".h", "c");
4120 updateLanguageMapping(".H", "c");
4121 updateLanguageMapping(".hh", "c");
4122 updateLanguageMapping(".HH", "c");
4123 updateLanguageMapping(".hxx", "c");
4124 updateLanguageMapping(".hpp", "c");
4125 updateLanguageMapping(".h++", "c");
4126 updateLanguageMapping(".idl", "idl");
4127 updateLanguageMapping(".ddl", "idl");
4128 updateLanguageMapping(".odl", "idl");
4129 updateLanguageMapping(".java", "java");
4130 //updateLanguageMapping(".as", "javascript"); // not officially supported
4131 //updateLanguageMapping(".js", "javascript"); // not officially supported
4132 updateLanguageMapping(".cs", "csharp");
4133 updateLanguageMapping(".d", "d");
4134 updateLanguageMapping(".php", "php");
4135 updateLanguageMapping(".php4", "php");
4136 updateLanguageMapping(".php5", "php");
4137 updateLanguageMapping(".inc", "php");
4138 updateLanguageMapping(".phtml", "php");
4139 updateLanguageMapping(".m", "objective-c");
4140 updateLanguageMapping(".M", "objective-c");
4141 updateLanguageMapping(".mm", "c"); // see bug746361
4142 updateLanguageMapping(".py", "python");
4143 updateLanguageMapping(".pyw", "python");
4144 updateLanguageMapping(".f", "fortran");
4145 updateLanguageMapping(".for", "fortran");
4146 updateLanguageMapping(".f90", "fortran");
4147 updateLanguageMapping(".f95", "fortran");
4148 updateLanguageMapping(".f03", "fortran");
4149 updateLanguageMapping(".f08", "fortran");
4150 updateLanguageMapping(".f18", "fortran");
4151 updateLanguageMapping(".vhd", "vhdl");
4152 updateLanguageMapping(".vhdl", "vhdl");
4153 updateLanguageMapping(".ucf", "vhdl");
4154 updateLanguageMapping(".qsf", "vhdl");
4155 updateLanguageMapping(".md", "md");
4156 updateLanguageMapping(".markdown", "md");
4157 updateLanguageMapping(".ice", "slice");
4158 updateLanguageMapping(".l", "lex");
4159 updateLanguageMapping(".doxygen_lex_c", "c"); // this is a placeholder so we can map initializations
4160 // in the lex scanning to cpp
4161}
4162
4164{
4165 updateLanguageMapping(".xml", "xml");
4166 updateLanguageMapping(".sql", "sql");
4167}
4168
4170{
4171 FileInfo fi(fileName.str());
4172 // we need only the part after the last ".", newer implementations of FileInfo have 'suffix()' for this.
4173 DString extName = DString(fi.extension(false)).lower();
4174 if (extName.empty()) extName=".no_extension";
4175 if (extName.at(0)!='.') extName.prepend(".");
4176 auto it = g_extLookup.find(extName.str());
4177 if (it!=g_extLookup.end()) // listed extension
4178 {
4179 //printf("getLanguageFromFileName(%s)=%x\n",qPrint(fi.extension()),*pVal);
4180 return it->second;
4181 }
4182 //printf("getLanguageFromFileName(%s) not found!\n",qPrint(fileName));
4183 return defLang; // not listed => assume C-ish language.
4184}
4185
4186/// Routine to handle the language attribute of the `\code` command
4188{
4189 // try the extension
4190 auto lang = getLanguageFromFileName(fileName, SrcLangExt::Unknown);
4191 if (lang == SrcLangExt::Unknown)
4192 {
4193 // try the language names
4194 DString langName = fileName.lower();
4195 if (langName.at(0)=='.') langName = langName.mid(1);
4196 auto it = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
4197 [&langName](const auto &info) { return info.langName==langName; });
4198 if (it != g_lang2extMap.end())
4199 {
4200 lang = it->parserId;
4201 fileName = it->defExt;
4202 }
4203 else // default to C++
4204 {
4205 return SrcLangExt::Cpp;
4206 }
4207 }
4208 return lang;
4209}
4210
4212{
4213 if (fn.empty()) return "";
4214 if (size_t lastDot = fn.rfind('.'); lastDot!=DString::npos) return fn.mid(lastDot);
4215 return "";
4216}
4217
4218//--------------------------------------------------------------------------
4219
4220static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
4221 const DString &n)
4222{
4223 if (scope==nullptr ||
4226 )
4227 )
4228 {
4230 }
4231
4232 DString name = n;
4233 if (name.empty())
4234 return nullptr; // no name was given
4235
4236 auto &range = Doxygen::symbolMap->find(name);
4237 if (range.empty())
4238 return nullptr; // could not find any matching symbols
4239
4240 // mostly copied from getResolvedClassRec()
4241 DString explicitScopePart;
4242 int qualifierIndex = computeQualifiedIndex(name);
4243 if (qualifierIndex!=-1)
4244 {
4245 explicitScopePart = name.left(qualifierIndex);
4246 replaceNamespaceAliases(explicitScopePart);
4247 name = name.mid(qualifierIndex+2);
4248 }
4249 //printf("explicitScopePart=%s\n",qPrint(explicitScopePart));
4250
4251 int minDistance = 10000;
4252 MemberDef *bestMatch = nullptr;
4253
4254 for (Definition *d : range)
4255 {
4256 if (d->definitionType()==Definition::TypeMember)
4257 {
4258 SymbolResolver resolver(fileScope);
4259 int distance = resolver.isAccessibleFromWithExpScope(scope,d,explicitScopePart);
4260 if (distance!=-1 && distance<minDistance)
4261 {
4262 minDistance = distance;
4263 bestMatch = toMemberDef(d);
4264 //printf("new best match %s distance=%d\n",qPrint(bestMatch->qualifiedName()),distance);
4265 }
4266 }
4267 }
4268 return bestMatch;
4269}
4270
4271/*! Returns true iff the given name string appears to be a typedef in scope. */
4272bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const DString &n)
4273{
4274 MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
4275
4276 if (bestMatch && bestMatch->isTypedef())
4277 return true; // closest matching symbol is a typedef
4278 else
4279 return false;
4280}
4281
4282//--------------------------------------------------------------------------------------
4283
4285{
4286 if (al.empty()) return;
4288 for (const Argument &a : al)
4289 {
4291 ol.parseText(a.name);
4292 ol.endConstraintParam();
4294 linkifyText(TextGeneratorOLImpl(ol),a.type,LinkifyTextOptions().setScope(d));
4295 ol.endConstraintType();
4297 ol.generateDoc(d->docFile(),
4298 d->docLine(),
4299 d,
4300 nullptr,
4301 a.docs,
4302 DocOptions()
4303 .setIndexWords(true));
4304 ol.endConstraintDocs();
4305 }
4306 ol.endConstraintList();
4307}
4308
4309//----------------------------------------------------------------------------
4310
4312{
4313#ifdef TRACINGSUPPORT
4314 void *backtraceFrames[128];
4315 int frameCount = backtrace(backtraceFrames, 128);
4316 const size_t cmdLen = 40960;
4317 static char cmd[cmdLen];
4318 char *p = cmd;
4319 p += snprintf(p,cmdLen,"/usr/bin/atos -p %d ", (int)getpid());
4320 for (int x = 0; x < frameCount; x++)
4321 {
4322 p += snprintf(p,cmdLen,"%p ", backtraceFrames[x]);
4323 }
4324 fprintf(stderr,"========== STACKTRACE START ==============\n");
4325 if (FILE *fp = Portable::popen(cmd, "r"))
4326 {
4327 char resBuf[512];
4328 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
4329 {
4330 fwrite(resBuf, 1, len, stderr);
4331 }
4332 Portable::pclose(fp);
4333 }
4334 fprintf(stderr,"============ STACKTRACE END ==============\n");
4335 //fprintf(stderr,"%s\n", frameStrings[x]);
4336#endif
4337}
4338
4339static void transcodeCharacterBuffer(const DString &fileName,std::string &contents,
4340 const DString &inputEncoding,const DString &outputEncoding)
4341{
4342 if (inputEncoding.empty() || outputEncoding.empty()) return; // no encoding specified
4343 if (dstricmp(inputEncoding,outputEncoding)==0) return; // input encoding same as output encoding
4344 void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data());
4345 if (cd==reinterpret_cast<void *>(-1))
4346 {
4347 term("unsupported character conversion: '{}'->'{}': {}\n"
4348 "Check the INPUT_ENCODING setting in the config file!\n",
4349 inputEncoding,outputEncoding,strerror(errno));
4350 }
4351 size_t iLeft = contents.size();
4352 const char *srcPtr = contents.data();
4353 size_t tmpBufSize = contents.size()*4+1;
4354 size_t oLeft = tmpBufSize;
4355 std::string tmpBuf;
4356 tmpBuf.resize(tmpBufSize);
4357 char *dstPtr = tmpBuf.data();
4358 size_t newSize=0;
4359 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
4360 {
4361 newSize = tmpBufSize-oLeft;
4362 tmpBuf.resize(newSize);
4363 std::swap(contents,tmpBuf);
4364 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,qPrint(srcBuf));
4365 }
4366 else
4367 {
4368 term("{}: failed to translate characters from {} to {}: check INPUT_ENCODING\n",
4369 fileName,inputEncoding,outputEncoding);
4370 }
4372}
4373
4374//! read a file name \a fileName and optionally filter and transcode it
4375bool readInputFile(const DString &fileName,std::string &contents,bool filter,bool isSourceCode)
4376{
4377 // try to open file
4378 FileInfo fi(fileName.str());
4379 if (!fi.exists()) return false;
4380 DString filterName = getFileFilter(fileName,isSourceCode);
4381 if (filterName.empty() || !filter)
4382 {
4383 std::ifstream f = Portable::openInputStream(fileName,true);
4384 if (!f.is_open())
4385 {
4386 err("could not open file {}\n",fileName);
4387 return false;
4388 }
4389 // read the file
4390 auto fileSize = fi.size();
4391 contents.resize(fileSize);
4392 f.read(contents.data(),fileSize);
4393 if (f.fail())
4394 {
4395 err("problems while reading file {}\n",fileName);
4396 return false;
4397 }
4398 }
4399 else
4400 {
4401 DString cmd=filterName+" \""+fileName+"\"";
4402 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
4403 FILE *f=Portable::popen(cmd,"r");
4404 if (!f)
4405 {
4406 err("could not execute filter {}\n",filterName);
4407 return false;
4408 }
4409 const int bufSize=4096;
4410 char buf[bufSize];
4411 int numRead = 0;
4412 while ((numRead=static_cast<int>(fread(buf,1,bufSize,f)))>0)
4413 {
4414 //printf(">>>>>>>>Reading %d bytes\n",numRead);
4415 contents.append(buf,numRead);
4416 }
4418 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
4419 Debug::print(Debug::FilterOutput,0,"-------------\n{}\n-------------\n",contents);
4420 }
4421
4422 if (contents.size()>=2 &&
4423 static_cast<uint8_t>(contents[0])==0xFF &&
4424 static_cast<uint8_t>(contents[1])==0xFE // Little endian BOM
4425 ) // UCS-2LE encoded file
4426 {
4427 transcodeCharacterBuffer(fileName,contents,"UCS-2LE","UTF-8");
4428 }
4429 else if (contents.size()>=2 &&
4430 static_cast<uint8_t>(contents[0])==0xFE &&
4431 static_cast<uint8_t>(contents[1])==0xFF // big endian BOM
4432 ) // UCS-2BE encoded file
4433 {
4434 transcodeCharacterBuffer(fileName,contents,"UCS-2BE","UTF-8");
4435 }
4436 else if (contents.size()>=3 &&
4437 static_cast<uint8_t>(contents[0])==0xEF &&
4438 static_cast<uint8_t>(contents[1])==0xBB &&
4439 static_cast<uint8_t>(contents[2])==0xBF
4440 ) // UTF-8 encoded file
4441 {
4442 contents.erase(0,3); // remove UTF-8 BOM: no translation needed
4443 }
4444 else // transcode according to the INPUT_ENCODING setting
4445 {
4446 // do character transcoding if needed.
4447 transcodeCharacterBuffer(fileName,contents,getEncoding(fi),"UTF-8");
4448 }
4449
4450 filterCRLF(contents);
4451 return true;
4452}
4453
4454// Replace %word by word in title
4456{
4457 std::string tf;
4458 std::string t = title.str();
4459 static const reg::Ex re(R"(%[a-z_A-Z]+)");
4460 reg::Iterator it(t,re);
4462 size_t p = 0;
4463 for (; it!=end ; ++it)
4464 {
4465 const auto &match = *it;
4466 size_t i = match.position();
4467 size_t l = match.length();
4468 if (i>p) tf+=t.substr(p,i-p);
4469 tf+=match.str().substr(1); // skip %
4470 p=i+l;
4471 }
4472 tf+=t.substr(p);
4473 return tf;
4474}
4475
4476//----------------------------------------------------------------------------
4477
4479{
4480 InputFileEncoding elem;
4481 auto getter = [](const InputFileEncoding &e) { return e.pattern.str(); };
4482 if (fi.match(Doxygen::inputFileEncodingList,useCaseSenseNames(),&elem,getter)) // check for file specific encoding
4483 {
4484 return elem.encoding;
4485 }
4486 else // fall back to default encoding
4487 {
4488 return Config_getString(INPUT_ENCODING);
4489 }
4490}
4491
4493{
4494 bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW);
4495 if (extLinksInWindow)
4496 return "target=\"_blank\" ";
4497 else if (parent)
4498 return "target=\"_parent\" ";
4499 else
4500 return "";
4501}
4502
4504 const DString &ref,
4505 bool isLocalFile,
4506 const DString &targetFileName,
4507 const DString &anchor)
4508{
4509 DString url;
4510 if (!ref.empty())
4511 {
4512 url = externalRef(relPath,ref);
4513 }
4514 if (!targetFileName.empty())
4515 {
4516 DString fn = targetFileName;
4517 if (ref.empty())
4518 {
4519 if (!anchor.empty() && isLocalFile)
4520 {
4521 fn=""; // omit file name for local links
4522 }
4523 else
4524 {
4525 url = relPath;
4526 }
4527 }
4528 url+=fn;
4529 }
4530 if (!anchor.empty())
4531 {
4532 if (!url.endsWith("=")) url+="#";
4533 url+=anchor;
4534 }
4535 //printf("createHtmlUrl(relPath=%s,local=%d,target=%s,anchor=%s)=%s\n",qPrint(relPath),isLocalFile,qPrint(targetFileName),qPrint(anchor),qPrint(url));
4536 return url;
4537}
4538
4539DString externalRef(const DString &relPath,const DString &ref)
4540{
4541 DString result;
4542 if (!ref.empty())
4543 {
4544 auto it = Doxygen::tagDestinationMap.find(ref.str());
4546 {
4547 result = it->second;
4548 size_t l = result.length();
4549 if (!relPath.empty() && l>0 && result.at(0)=='.')
4550 { // relative path -> prepend relPath.
4551 result.prepend(relPath);
4552 l+=relPath.length();
4553 }
4554 if (l>0 && result.at(l-1)!='/') result+='/';
4555 }
4556 }
4557 else
4558 {
4559 result = relPath;
4560 }
4561 return result;
4562}
4563
4564/** Replaces any markers of the form \#\#AA in input string \a str
4565 * by new markers of the form \#AABBCC, where \#AABBCC represents a
4566 * valid color, based on the intensity represented by hex number AA
4567 * and the current HTML_COLORSTYLE_* settings.
4568 */
4570{
4571 if (str.empty()) return DString();
4572 std::string result;
4573 std::string s=str.str();
4574 static const reg::Ex re(R"(##[0-9A-Fa-f][0-9A-Fa-f])");
4575 reg::Iterator it(s,re);
4577 int hue = Config_getInt(HTML_COLORSTYLE_HUE);
4578 int sat = Config_getInt(HTML_COLORSTYLE_SAT);
4579 int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
4580 size_t sl=s.length();
4581 size_t p=0;
4582 for (; it!=end ; ++it)
4583 {
4584 const auto &match = *it;
4585 size_t i = match.position();
4586 size_t l = match.length();
4587 if (i>p) result+=s.substr(p,i-p);
4588 std::string lumStr = match.str().substr(2);
4589#define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
4590 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
4591 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
4592
4593 double r = 0,g = 0,b = 0;
4594 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
4595 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
4596 pow(level/255.0,gamma/100.0),&r,&g,&b);
4597 int red = static_cast<int>(r*255.0);
4598 int green = static_cast<int>(g*255.0);
4599 int blue = static_cast<int>(b*255.0);
4600 char colStr[8];
4601 colStr[0]='#';
4602 colStr[1]=hex[red>>4];
4603 colStr[2]=hex[red&0xf];
4604 colStr[3]=hex[green>>4];
4605 colStr[4]=hex[green&0xf];
4606 colStr[5]=hex[blue>>4];
4607 colStr[6]=hex[blue&0xf];
4608 colStr[7]=0;
4609 //printf("replacing %s->%s (level=%d)\n",qPrint(lumStr),colStr,level);
4610 result+=colStr;
4611 p=i+l;
4612 }
4613 if (p<sl) result+=s.substr(p);
4614 return result;
4615}
4616
4617/** Copies the contents of file with name \a src to the newly created
4618 * file with name \a dest. Returns true if successful.
4619 */
4620bool copyFile(const DString &src,const DString &dest)
4621{
4622 if (!Dir().copy(src.str(),dest.str()))
4623 {
4624 err("could not copy file {} to {}\n",src,dest);
4625 return false;
4626 }
4627 return true;
4628}
4629
4631{
4632 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp || lang==SrcLangExt::VHDL || lang==SrcLangExt::Python)
4633 {
4634 return ".";
4635 }
4636 else if (lang==SrcLangExt::PHP && !classScope)
4637 {
4638 return "\\";
4639 }
4640 else
4641 {
4642 return "::";
4643 }
4644}
4645
4646/** Checks whether the given url starts with a supported protocol */
4647bool isURL(const DString &url)
4648{
4649 static const std::unordered_set<std::string> schemes = {
4650 "http", "https", "ftp", "ftps", "sftp", "file", "news", "irc", "ircs"
4651 };
4652 DString loc_url = url.stripWhiteSpace();
4653 size_t colonPos = loc_url.find(':');
4654 return colonPos!=DString::npos && schemes.find(loc_url.left(colonPos).str())!=schemes.end();
4655}
4656
4657/** Corrects URL \a url according to the relative path \a relPath.
4658 * Returns the corrected URL. For absolute URLs no correction will be done.
4659 */
4660DString correctURL(const DString &url,const DString &relPath)
4661{
4662 DString result = url;
4663 if (!relPath.empty() && !isURL(url))
4664 {
4665 result.prepend(relPath);
4666 }
4667 return result;
4668}
4669
4670//---------------------------------------------------------------------------
4671
4673{
4674 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
4675 bool extractPackage = Config_getBool(EXTRACT_PACKAGE);
4676
4677 return (prot!=Protection::Private && prot!=Protection::Package) ||
4678 (prot==Protection::Private && extractPrivate) ||
4679 (prot==Protection::Package && extractPackage);
4680}
4681
4682//---------------------------------------------------------------------------
4683
4684DString stripIndentation(const DString &s,bool skipFirstLine)
4685{
4686 if (s.empty()) return s; // empty string -> we're done
4687
4688 //printf("stripIndentation:\n%s\n------\n",qPrint(s));
4689 // compute minimum indentation over all lines
4690 const char *p=s.data();
4691 char c=0;
4692 int indent=0;
4693 int minIndent=1000000; // "infinite"
4694 bool searchIndent=true;
4695 int tabSize=Config_getInt(TAB_SIZE);
4696 bool skipFirst = skipFirstLine;
4697 while ((c=*p++))
4698 {
4699 if (c=='\t') { indent+=tabSize - (indent%tabSize); }
4700 else if (c=='\n') { indent=0; searchIndent=true; skipFirst=false; }
4701 else if (c==' ') { indent++; }
4702 else if (searchIndent && !skipFirst)
4703 {
4704 searchIndent=false;
4705 if (indent<minIndent) minIndent=indent;
4706 }
4707 }
4708
4709 // no indent to remove -> we're done
4710 if (minIndent==0) return substitute(s,"@ilinebr","\\ilinebr");
4711
4712 // remove minimum indentation for each line
4713 TextStream result;
4714 p=s.data();
4715 indent=0;
4716 skipFirst=skipFirstLine;
4717 while ((c=*p++))
4718 {
4719 if (c=='\n') // start of new line
4720 {
4721 indent=0;
4722 result << c;
4723 skipFirst=false;
4724 }
4725 else if (indent<minIndent && !skipFirst) // skip until we reach minIndent
4726 {
4727 if (c=='\t')
4728 {
4729 int newIndent = indent+tabSize-(indent%tabSize);
4730 int i=newIndent;
4731 while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
4732 {
4733 result << ' ';
4734 i--;
4735 }
4736 indent=newIndent;
4737 }
4738 else // space
4739 {
4740 indent++;
4741 }
4742 }
4743 else if (c=='\\' && literal_at(p,"ilinebr "))
4744 // we also need to remove the indentation after a \ilinebr command at the end of a line
4745 {
4746 result << "\\ilinebr ";
4747 p+=8;
4748 int skipAmount=0;
4749 for (int j=0;j<minIndent;j++) if (*(p+j)==' ') skipAmount++; // test to see if we have the indent
4750 if (skipAmount==minIndent)
4751 {
4752 p+=skipAmount; // remove the indent
4753 }
4754 }
4755 else if (c=='@' && literal_at(p,"ilinebr"))
4756 {
4757 result << "\\ilinebr";
4758 p+=7;
4759 }
4760 else // copy anything until the end of the line
4761 {
4762 result << c;
4763 }
4764 }
4765
4766 //printf("stripIndentation: result=\n%s\n------\n",qPrint(result.str()));
4767
4768 return result.str();
4769}
4770
4771// strip up to \a indentationLevel spaces from each line in \a doc (excluding the first line
4772// when skipFirstLine is set to true)
4773void stripIndentationVerbatim(DString &doc,size_t indentationLevel, bool skipFirstLine)
4774{
4775 //printf("stripIndentationVerbatim(level=%d):\n%s\n------\n",indentationLevel,qPrint(doc));
4776 if (indentationLevel <= 0 || doc.empty()) return; // nothing to strip
4777
4778 // by stripping content the string will only become shorter so we write the results
4779 // back into the input string and then resize it at the end.
4780 char c = 0;
4781 const char *src = doc.data();
4782 char *dst = doc.rawData();
4783 bool insideIndent = !skipFirstLine; // skip the initial line from stripping
4784 size_t cnt = 0;
4785 if (!skipFirstLine) cnt = indentationLevel;
4786 while ((c=*src++))
4787 {
4788 // invariant: dst<=src
4789 switch(c)
4790 {
4791 case '\n':
4792 *dst++ = c;
4793 insideIndent = true;
4794 cnt = indentationLevel;
4795 break;
4796 case ' ':
4797 if (insideIndent)
4798 {
4799 if (cnt>0) // count down the spacing until the end of the indent
4800 {
4801 cnt--;
4802 }
4803 else // reached the end of the indent, start of the part of the line to keep
4804 {
4805 insideIndent = false;
4806 *dst++ = c;
4807 }
4808 }
4809 else // part after indent, copy to the output
4810 {
4811 *dst++ = c;
4812 }
4813 break;
4814 default:
4815 insideIndent = false;
4816 *dst++ = c;
4817 break;
4818 }
4819 }
4820 doc.resize(static_cast<uint32_t>(dst-doc.data()));
4821 //printf("stripIndentationVerbatim: result=\n%s\n------\n",qPrint(doc));
4822}
4823
4824//----------------------------------------------------------------------------
4825
4826/** Strip the direction part from docs and return it as a string in canonical form.
4827 * The input \a docs string can start with e.g. "[in]", "[in, out]", "[inout]", "[out,in]"...
4828 * @returns either "[in,out]", "[in]", or "[out]" or the empty string.
4829 */
4831{
4832 std::string s = docs.str();
4833 static const reg::Ex re(R"(\‍[([ inout,]+)\‍])");
4834 reg::Iterator it(s,re);
4836 if (it!=end)
4837 {
4838 const auto &match = *it;
4839 size_t p = match.position();
4840 size_t l = match.length();
4841 if (p==0 && l>2)
4842 {
4843 // make dir the part inside [...] without separators
4844 std::string dir = match[1].str();
4845 // strip , and ' ' from dir
4846 dir.erase(std::remove_if(dir.begin(),dir.end(),
4847 [](const char c) { return c==' ' || c==','; }
4848 ),dir.end());
4849 unsigned char ioMask=0;
4850 size_t inIndex = dir.find( "in");
4851 if ( inIndex!=std::string::npos) { dir.erase( inIndex,2); ioMask|=(1<<0); }
4852 size_t outIndex = dir.find("out");
4853 if (outIndex!=std::string::npos) { dir.erase(outIndex,3); ioMask|=(1<<1); }
4854 if (dir.empty() && ioMask!=0) // only in and/or out attributes found
4855 {
4856 docs = s.substr(l); // strip attributes
4857 if (ioMask==((1<<0)|(1<<1))) return "[in,out]";
4858 else if (ioMask==(1<<0)) return "[in]";
4859 else if (ioMask==(1<<1)) return "[out]";
4860 }
4861 }
4862 }
4863 return "";
4864}
4865
4867{
4868 DString paramDocs;
4869 if (al.hasDocumentation(true))
4870 {
4871 for (const Argument &a : al)
4872 {
4873 if (a.hasDocumentation(true))
4874 {
4875 DString docsWithoutDir = a.docs;
4876 DString direction = extractDirection(docsWithoutDir);
4877 DString name = a.name;
4878 if (name.empty())
4879 {
4880 name = "-";
4881 }
4882 paramDocs+=" \\ilinebr @param"+direction+" "+name+" "+docsWithoutDir;
4883 }
4884 }
4885 }
4886 return paramDocs;
4887}
4888
4889
4890//-----------------------------------------------------------
4891
4892/** Computes for a given list type \a inListType, which are the
4893 * the corresponding list type(s) in the base class that are to be
4894 * added to this list.
4895 *
4896 * So for public inheritance, the mapping is 1-1, so outListType1=inListType
4897 * Private members are to be hidden completely.
4898 *
4899 * For protected inheritance, both protected and public members of the
4900 * base class should be joined in the protected member section.
4901 *
4902 * For private inheritance, both protected and public members of the
4903 * base class should be joined in the private member section.
4904 */
4906 MemberListType inListType,
4907 Protection inProt,
4908 MemberListType *outListType1,
4909 MemberListType *outListType2
4910 )
4911{
4912 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
4913
4914 // default representing 1-1 mapping
4915 *outListType1=inListType;
4916 *outListType2=MemberListType::Invalid();
4917
4918 if (inProt==Protection::Public)
4919 {
4920 if (inListType.isPrivate())
4921 {
4922 *outListType1=MemberListType::Invalid();
4923 }
4924 }
4925 else if (inProt==Protection::Protected)
4926 {
4927 if (inListType.isPrivate() || inListType.isPublic())
4928 {
4929 *outListType1=MemberListType::Invalid();
4930 }
4931 else if (inListType.isProtected())
4932 {
4933 *outListType2=inListType.toPublic();
4934 }
4935 }
4936 else if (inProt==Protection::Private)
4937 {
4938 if (inListType.isPublic() || inListType.isProtected())
4939 {
4940 *outListType1=MemberListType::Invalid();
4941 }
4942 else if (inListType.isPrivate())
4943 {
4944 if (extractPrivate)
4945 {
4946 *outListType1=inListType.toPublic();
4947 *outListType2=inListType.toProtected();
4948 }
4949 else
4950 {
4951 *outListType1=MemberListType::Invalid();
4952 }
4953 }
4954 }
4955
4956 //printf("convertProtectionLevel(type=%s prot=%d): %s,%s\n",
4957 // qPrint(inListType.to_string()),inProt,qPrint(outListType1->to_string()),qPrint(outListType2->to_string()));
4958}
4959
4961{
4962 return Doxygen::mainPage!=nullptr && Doxygen::mainPage->hasTitle();
4963}
4964
4966{
4967 return getDotImageExtension(Config_getEnumAsString(DOT_IMAGE_FORMAT));
4968}
4970{
4971 size_t i= format.find(':'); // strip renderer part when using e.g. 'png:cairo:gd' as format
4972 return i==DString::npos ? format : format.left(i);
4973}
4974
4976{
4977 StringVector splitExt = split(format.str(),":");
4978 DString extGen;
4979 for (size_t i = splitExt.size(); i > 0; )
4980 {
4981 --i;
4982 extGen += splitExt[i] + (i!=0 ? "." : "");
4983 }
4984 return extGen;
4985}
4986
4987bool openOutputFile(const DString &outFile,std::ofstream &f)
4988{
4989 ASSERT(!f.is_open());
4990 bool fileOpened=false;
4991 bool writeToStdout=outFile=="-";
4992 if (writeToStdout) // write to stdout
4993 {
4994 f.basic_ios<char>::rdbuf(std::cout.rdbuf());
4995 fileOpened = true;
4996 }
4997 else // write to file
4998 {
4999 FileInfo fi(outFile.str());
5000 if (fi.exists()) // create a backup
5001 {
5002 Dir dir;
5003 FileInfo backup(fi.filePath()+".bak");
5004 if (backup.exists()) // remove existing backup
5005 dir.remove(backup.filePath());
5006 dir.rename(fi.filePath(),fi.filePath()+".bak");
5007 }
5008 f = Portable::openOutputStream(outFile);
5009 fileOpened = f.is_open();
5010 }
5011 return fileOpened;
5012}
5013
5014//------------------------------------------------------
5015
5016//! remove disabled blocks and all block markers from \a s and return the result as a string
5017DString selectBlocks(const DString &s,const SelectionBlockList &blockList,const SelectionMarkerInfo &markerInfo)
5018{
5019 if (s.empty()) return s;
5020
5021 // helper to find the end of a block
5022 auto skipBlock = [&markerInfo](const char *p,const SelectionBlock &blk)
5023 {
5024 char c = 0;
5025 while ((c=*p))
5026 {
5027 if (c==markerInfo.markerChar && dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // end marker
5028 {
5029 size_t len = markerInfo.endLen;
5030 bool negate = *(p+markerInfo.endLen)=='!';
5031 if (negate) len++;
5032 size_t blkNameLen = dstrlen(blk.name);
5033 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5034 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5035 {
5036 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5037 return p+len+blkNameLen+markerInfo.closeLen;
5038 }
5039 else // not the right marker id
5040 {
5041 p++;
5042 }
5043 }
5044 else // not and end marker
5045 {
5046 p++;
5047 }
5048 }
5049 return p;
5050 };
5051
5052 DString result;
5053 result.reserve(s.length());
5054 const char *p = s.data();
5055 char c = 0;
5056 while ((c=*p))
5057 {
5058 if (c==markerInfo.markerChar) // potential start of marker
5059 {
5060 if (dstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
5061 {
5062 bool found = false;
5063 size_t len = markerInfo.beginLen;
5064 bool negate = *(p+len)=='!';
5065 if (negate) len++;
5066 for (const auto &blk : blockList)
5067 {
5068 size_t blkNameLen = dstrlen(blk.name);
5069 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5070 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5071 {
5072 bool blockEnabled = blk.enabled!=negate;
5073 //printf("Found start marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5074 p+=len+blkNameLen+markerInfo.closeLen;
5075 if (!blockEnabled) // skip until the end of the block
5076 {
5077 //printf("skipping block\n");
5078 p=skipBlock(p,blk);
5079 }
5080 found=true;
5081 break;
5082 }
5083 }
5084 if (!found) // unknown marker id
5085 {
5086 result+=c;
5087 p++;
5088 }
5089 }
5090 else if (dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
5091 {
5092 bool found = false;
5093 size_t len = markerInfo.endLen;
5094 bool negate = *(p+len)=='!';
5095 if (negate) len++;
5096 for (const auto &blk : blockList)
5097 {
5098 size_t blkNameLen = dstrlen(blk.name);
5099 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5100 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5101 {
5102 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5103 p+=len+blkNameLen+markerInfo.closeLen;
5104 found=true;
5105 break;
5106 }
5107 }
5108 if (!found) // unknown marker id
5109 {
5110 result+=c;
5111 p++;
5112 }
5113 }
5114 else // not a start or end marker
5115 {
5116 result+=c;
5117 p++;
5118 }
5119 }
5120 else // not a marker character
5121 {
5122 result+=c;
5123 p++;
5124 }
5125 }
5126 //printf("====\n%s\n-----\n%s\n~~~~\n",qPrint(s),qPrint(result));
5127 return result;
5128}
5129
5130void checkBlocks(const DString &s, const DString fileName,const SelectionMarkerInfo &markerInfo)
5131{
5132 if (s.empty()) return;
5133
5134 const char *p = s.data();
5135 char c = 0;
5136 while ((c=*p))
5137 {
5138 if (c==markerInfo.markerChar) // potential start of marker
5139 {
5140 if (dstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
5141 {
5142 size_t len = markerInfo.beginLen;
5143 bool negate = *(p+len)=='!';
5144 if (negate) len++;
5145 p += len;
5146 DString marker;
5147 while (*p)
5148 {
5149 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
5150 {
5151 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
5152 break;
5153 }
5154 else if (markerInfo.closeLen!= 0 && dstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5155 {
5156 p += markerInfo.closeLen;
5157 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
5158 break;
5159 }
5160 marker += *p;
5161 p++;
5162 }
5163 }
5164 else if (dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
5165 {
5166 size_t len = markerInfo.endLen;
5167 bool negate = *(p+len)=='!';
5168 if (negate) len++;
5169 p += len;
5170 DString marker;
5171 while (*p)
5172 {
5173 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
5174 {
5175 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
5176 break;
5177 }
5178 else if (markerInfo.closeLen!= 0 && dstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5179 {
5180 p += markerInfo.closeLen;
5181 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
5182 break;
5183 }
5184 marker += *p;
5185 p++;
5186 }
5187 }
5188 }
5189 p++;
5190 }
5191}
5192
5193
5194
5195DString detab(const DString &s,size_t &refIndent)
5196{
5197 int tabSize = Config_getInt(TAB_SIZE);
5198 size_t size = s.length();
5199 DString result;
5200 result.reserve(size+256);
5201 const char *data = s.data();
5202 size_t i=0;
5203 int col=0;
5204 constexpr auto doxy_nbsp = "&_doxy_nbsp;"; // doxygen escape command for UTF-8 nbsp
5205 const int maxIndent=1000000; // value representing infinity
5206 int minIndent=maxIndent;
5207 bool skip = false;
5208 while (i<size)
5209 {
5210 char c = data[i++];
5211 switch(c)
5212 {
5213 case '\t': // expand tab
5214 {
5215 int stop = tabSize - (col%tabSize);
5216 //printf("expand at %d stop=%d\n",col,stop);
5217 col+=stop;
5218 while (stop--) result+=' ';
5219 }
5220 break;
5221 case '\\':
5222 if (skip && data[i] == '\\' && i+8<size && literal_at(data+i+1,"endiskip"))
5223 {
5224 // literal backslash directly followed by , e.g. `\` in a code span
5225 result+=c;
5226 col++;
5227 }
5228 else if (data[i] == '\\') // escaped command -> ignore
5229 {
5230 result+=c;
5231 result+=data[i++];
5232 col+=2;
5233 }
5234 else if (i+5<size && literal_at(data+i,"iskip")) // command
5235 {
5236 i+=5;
5237 skip = true;
5238 }
5239 else if (i+8<size && literal_at(data+i,"endiskip")) // command
5240 {
5241 i+=8;
5242 skip = false;
5243 }
5244 else // some other command
5245 {
5246 result+=c;
5247 col++;
5248 }
5249 break;
5250 case '\n': // reset column counter
5251 result+=c;
5252 col=0;
5253 break;
5254 case ' ': // increment column counter
5255 result+=c;
5256 col++;
5257 break;
5258 default: // non-whitespace => update minIndent
5259 if (static_cast<signed char>(c)<0 && i<size) // multibyte sequence
5260 {
5261 // special handling of the UTF-8 nbsp character 0xC2 0xA0
5262 int nb = isUTF8NonBreakableSpace(data);
5263 if (nb>0)
5264 {
5265 result+=doxy_nbsp;
5266 i+=nb-1;
5267 }
5268 else
5269 {
5270 int bytes = getUTF8CharNumBytes(c);
5271 for (int j=0;j<bytes-1 && c;j++)
5272 {
5273 result+=c;
5274 c = data[i++];
5275 }
5276 result+=c;
5277 }
5278 }
5279 else
5280 {
5281 result+=c;
5282 }
5283 if (!skip && col<minIndent) minIndent=col;
5284 col++;
5285 }
5286 }
5287 if (minIndent!=maxIndent) refIndent=minIndent; else refIndent=0;
5288 //printf("detab(\n%s\n)=[\n%s\n]\n",qPrint(s),qPrint(out.get()));
5289 return result;
5290}
5291
5293{
5294 DString projectCookie = Config_getString(HTML_PROJECT_COOKIE);
5295 if (projectCookie.empty()) return DString();
5296 return md5str(projectCookie.view())+"_";
5297}
5298
5299//! Return the index of the last :: in the string \a name that is still before the first <
5301{
5302 int l = static_cast<int>(name.length());
5303 int lastSepPos = -1;
5304 const char *p = name.data();
5305 int i=l-2;
5306 int sharpCount=0;
5307 // --- begin optimized version of ts=name.findRev(">::");
5308 int ts = -1;
5309 while (i>=0)
5310 {
5311 if (p[i]=='>')
5312 {
5313 if (sharpCount==0 && p[i+1]==':' && p[i+2]==':')
5314 {
5315 ts=i;
5316 break;
5317 }
5318 sharpCount++;
5319 }
5320 else if (p[i]=='<')
5321 {
5322 sharpCount--;
5323 }
5324 i--;
5325 }
5326 // --- end optimized version
5327 if (ts==-1) ts=0; else p+=++ts;
5328 for (i=ts;i<l-1;i++)
5329 {
5330 char c=*p++;
5331 if (c==':' && *p==':') lastSepPos=i;
5332 if (c=='<') break;
5333 }
5334 return lastSepPos;
5335}
5336
5337// in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
5338// A -> A
5339// A<T> -> A-1-g
5340// A<T,S> -> A-2-g
5342{
5343 if (size_t idx = name.find('<'); idx!=DString::npos)
5344 {
5345 return name.left(idx)+"-"+DString().setNum(name.contains(",")+1)+"-g";
5346 }
5347 return name;
5348}
5349
5351{
5352 DString result=name;
5353 if (result.endsWith("-g"))
5354 {
5355 size_t idx = result.find('-');
5356 result = result.left(idx)+templArgs;
5357 }
5358 return result;
5359}
5360
5362{
5363 DString text=rawStart;
5364 size_t i = text.find('"');
5366 return text.mid(i+1,text.length()-i-2); // text=...R"xyz( -> delimiter=xyz
5367}
5368
5370{
5371 DString text=rawEnd;
5372 return text.mid(1,text.length()-2); // text=)xyz" -> delimiter=xyz
5373}
5374
5375//----------------------------------------------------------------------------------------------------------
5376
5377static std::mutex writeFileContents_lock;
5379
5380DString writeInlineGraph(const DString &baseName,const DString &extension,const DString &content,bool &exists)
5381{
5382 DString fileName = baseName + md5str(content.view()) + extension;
5383 { // ==== start atomic section
5384 std::lock_guard lock(writeFileContents_lock);
5385 auto it=writeFileContents_set.find(fileName.str());
5386 exists = it!=writeFileContents_set.end();
5387 if (!exists)
5388 {
5389 writeFileContents_set.insert(fileName.str());
5390 if (auto file = Portable::openOutputStream(fileName); file.is_open())
5391 {
5392 file.write( content.data(), content.length() );
5393 file.close();
5394 }
5395 else
5396 {
5397 err("Could not open file {} for writing\n",fileName);
5398 return DString();
5399 }
5400 }
5401 } // ==== end atomic section
5402 return fileName;
5403}
5404
5406{
5407 if (Config_getBool(DOT_CLEANUP))
5408 {
5409 for (const auto& fileName: writeFileContents_set)
5410 {
5411 Dir().remove(qPrint(fileName));
5412 }
5413 }
5414}
5415
constexpr auto prefix
Definition anchor.cpp:47
This class contains the information about the argument of a function or template.
Definition arguments.h:27
DString docs
Definition arguments.h:48
DString attrib
Definition arguments.h:42
DString defval
Definition arguments.h:47
DString array
Definition arguments.h:46
DString name
Definition arguments.h:45
DString type
Definition arguments.h:43
DString canType
Definition arguments.h:44
This class represents an function or template argument list.
Definition arguments.h:66
RefQualifierType refQualifier() const
Definition arguments.h:117
bool pureSpecifier() const
Definition arguments.h:114
iterator end()
Definition arguments.h:95
bool hasParameters() const
Definition arguments.h:77
Argument & front()
Definition arguments.h:106
DString trailingReturnType() const
Definition arguments.h:115
size_t size() const
Definition arguments.h:101
typename Vec::const_iterator const_iterator
Definition arguments.h:70
bool constSpecifier() const
Definition arguments.h:112
bool hasDocumentation(bool allowEmptyNames=false) const
Definition arguments.cpp:26
bool empty() const
Definition arguments.h:100
bool hasTemplateDocumentation() const
Definition arguments.cpp:34
iterator begin()
Definition arguments.h:94
bool volatileSpecifier() const
Definition arguments.h:113
A abstract class representing of a compound symbol.
Definition classdef.h:100
virtual const ArgumentList & templateArguments() const =0
Returns the template arguments of this class.
virtual bool isTemplate() const =0
Returns true if this class is a template.
virtual const BaseClassList & baseClasses() const =0
Returns the list of base classes from which this class directly inherits.
virtual DString qualifiedNameWithTemplateParameters(const ArgumentLists *actualParams=nullptr, uint32_t *actualParamIndex=nullptr) const =0
virtual FileDef * getFileDef() const =0
Returns the file in which this compound's definition can be found.
virtual bool isUsedOnly() const =0
static void hsl2rgb(double h, double s, double l, double *pRed, double *pGreen, double *pBlue)
Definition image.cpp:373
virtual const FileDef * getFileDef() const =0
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString & setNum(short n)
Definition dstring.h:552
void resize(size_t newlen)
Definition dstring.h:209
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
DString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple internal white...
Definition dstring.cpp:127
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
char * rawData()
Returns a writable pointer to the data.
Definition dstring.h:166
std::string_view view() const
Definition dstring.h:162
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:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
DString quoted() const
Definition dstring.h:352
DString right(size_t len) const
Definition dstring.h:311
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition dstring.h:217
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
bool stripPrefix(const DString &prefix)
Definition dstring.h:290
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:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ FilterOutput
Definition debug.h:39
@ ExtCmd
Definition debug.h:37
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
The common base class of all entity definitions found in the sources.
Definition definition.h:77
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 const DString & name() const =0
virtual DString displayName(bool includeScope=true) const =0
virtual DString qualifiedName() const =0
virtual DString anchor() const =0
virtual DString getReference() const =0
virtual const GroupList & partOfGroups() const =0
virtual bool isArtificial() const =0
virtual Definition * getOuterScope() const =0
virtual DString docFile() const =0
virtual const Definition * findInnerCompound(const DString &name) const =0
virtual DString getOutputFileBase() const =0
A model of a directory symbol.
Definition dirdef.h:108
Class representing a directory in the file system.
Definition dir.h:73
bool mkdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:301
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
bool empty(const std::string &subdir) const
Definition dir.cpp:269
bool rmdir(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:315
bool rename(const std::string &orgName, const std::string &newName, bool acceptsAbsPath=true) const
Definition dir.cpp:327
bool exists() const
Definition dir.cpp:263
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static ConceptLinkedMap * conceptLinkedMap
Definition doxygen.h:90
static std::unique_ptr< PageDef > mainPage
Definition doxygen.h:93
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static ParserManager * parserManager
Definition doxygen.h:122
static InputFileEncodingList inputFileEncodingList
Definition doxygen.h:131
static MemberNameLinkedMap * functionNameLinkedMap
Definition doxygen.h:105
static PageLinkedMap * exampleLinkedMap
Definition doxygen.h:91
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static StringMap tagDestinationMap
Definition doxygen.h:109
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static DString htmlFileExtension
Definition doxygen.h:115
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static MemberNameLinkedMap * memberNameLinkedMap
Definition doxygen.h:104
static SymbolMap< Definition > * symbolMap
Definition doxygen.h:118
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
A model of a file symbol.
Definition filedef.h:97
virtual ModuleDef * getModuleDef() const =0
virtual DString absFilePath() const =0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
bool exists() const
Definition fileinfo.cpp:34
size_t size() const
Definition fileinfo.cpp:27
std::string extension(bool complete) const
Definition fileinfo.cpp:134
bool match(const PatternList &patList, bool caseSenseNames, PatternElem *elem=nullptr, PatternGet getter=[](const std::string &s){ return s;}) const
Match the file name against a list of patterns.
Definition fileinfo.h:60
bool isDir() const
Definition fileinfo.cpp:74
bool isFile() const
Definition fileinfo.cpp:67
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:141
std::string filePath() const
Definition fileinfo.cpp:95
std::string absFilePath() const
Definition fileinfo.cpp:105
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
A model of a group of symbols.
Definition groupdef.h:48
const char * writeHtmlEntity(T &result, const char *s, HtmlEntityMapperFunc &&mapper, const char *fallback)
Definition htmlentity.h:127
const char * xml(SymType symb) const
Access routine to the XML code of the HTML entity.
static HtmlEntityMapper & instance()
Returns the one and only instance of the HTML entity mapper.
DString convertCharEntitiesToUTF8(const DString &s) const
const T * find(const std::string &key) const
Definition linkedmap.h:47
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString argsString() const =0
virtual bool isRelated() const =0
virtual const ClassDef * getClassDef() const =0
virtual GroupDef * getGroupDef()=0
virtual bool isTypedef() const =0
virtual const FileDef * getFileDef() const =0
virtual bool isStrongEnumValue() const =0
virtual const NamespaceDef * getNamespaceDef() const =0
virtual bool isEnumerate() const =0
virtual DString typeString() const =0
Wrapper class for the MemberListType type.
Definition types.h:346
constexpr bool isPrivate() const noexcept
Definition types.h:382
constexpr MemberListType toPublic() const noexcept
Definition types.h:426
static constexpr MemberListType Invalid() noexcept
Definition types.h:371
constexpr MemberListType toProtected() const noexcept
Definition types.h:438
constexpr bool isProtected() const noexcept
Definition types.h:380
ML_TYPES constexpr bool isPublic() const noexcept
Definition types.h:378
Ordered dictionary of MemberName objects.
Definition membername.h:61
static ModuleManager & instance()
An abstract interface of a namespace symbol.
Class representing a list of output generators that are written to in parallel.
Definition outputlist.h:311
void parseText(const DString &textStr)
void endConstraintType()
Definition outputlist.h:710
void disable(OutputType o)
void writeObjectLink(const DString &ref, const DString &file, const DString &anchor, const DString &name)
Definition outputlist.h:435
void endConstraintList()
Definition outputlist.h:716
void startConstraintList(const DString &header)
Definition outputlist.h:702
void startConstraintParam()
Definition outputlist.h:704
void writeString(const DString &text)
Definition outputlist.h:407
void startConstraintDocs()
Definition outputlist.h:712
void startConstraintType()
Definition outputlist.h:708
void endConstraintDocs()
Definition outputlist.h:714
void pushGeneratorState()
void disableAllBut(OutputType o)
void popGeneratorState()
void endConstraintParam()
Definition outputlist.h:706
void generateDoc(const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &docStr, const DocOptions &options)
A model of a page symbol.
Definition pagedef.h:27
virtual const GroupDef * getGroupDef() const =0
class that provide information about a section.
Definition section.h:58
Definition * definition() const
Definition section.h:77
DString label() const
Definition section.h:69
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
const VectorPtr & find(const DString &name)
Definition symbolmap.h:74
int isAccessibleFrom(const Definition *scope, const Definition *item)
Checks if symbol item is accessible from within scope.
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
DString getResolvedType() const
In case a call to resolveClass() points to a typedef or using declaration.
int isAccessibleFromWithExpScope(const Definition *scope, const Definition *item, const DString &explicitScopePart)
Check if symbol item is accessible from within scope, where it has to match the explicitScopePart.
DString getTemplateSpec() const
In case a call to resolveClass() points to a template specialization, the template part is return via...
void setFileScope(const FileDef *fd)
Sets or updates the file scope using when resolving symbols.
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
Implements TextGeneratorIntf for an OutputDocInterface stream.
Definition outputlist.h:786
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
virtual DString trTypeConstraints()=0
virtual DString trModule(bool first_capital, bool singular)=0
virtual DString trWriteList(int numEntries)=0
ClassDef * getClass(const DString &n)
ClassDef * toClassDef(Definition *d)
std::vector< BaseClassDef > BaseClassList
Definition classdef.h:77
Class representing a regular expression.
Definition regex.h:39
@ Wildcard
simple globbing pattern.
Definition regex.h:45
bool isValid() const
Definition regex.cpp:843
Class to iterate through matches.
Definition regex.h:239
ConceptDef * getConcept(const DString &n)
ConceptDef * toConceptDef(Definition *d)
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_getEnumAsString(name)
Definition config.h:36
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_getEnum(name)
Definition config.h:35
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::vector< std::string > StringVector
Definition containers.h:33
DString formatDateTime(const DString &format, const std::tm &dt, int &formatUsed)
Return a string representation for a given std::tm value that is formatted according to the pattern g...
Definition datetime.cpp:176
DString dateTimeFromString(const DString &spec, std::tm &dt, int &format)
Returns the filled in std::tm for a given string representing a date and/or time.
Definition datetime.cpp:135
std::unique_ptr< ArgumentList > stringToArgumentList(SrcLangExt lang, const DString &argsString, DString *extraTypeChars=nullptr)
Definition defargs.l:821
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
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
#define THREAD_LOCAL
Definition doxygen.h:29
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:444
bool isId(char c)
Returns true if c is a valid character for an identifier.
Definition dstring.h:895
int dstrncmp(const char *str1, const char *str2, size_t len)
Definition dstring.h:56
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
const char * qPrint(const char *s)
Definition dstring.h:783
bool disspace(char c)
Definition dstring.h:62
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:2033
Translator * theTranslator
Definition language.cpp:76
void linkifyText(const TextGeneratorIntf &out, const DString &text, const LinkifyTextOptions &options)
DString md5str(const std::string_view &str)
Definition md5hash.h:33
std::array< uint8_t, 16 > md5hash(const std::string_view &str)
Definition md5hash.h:26
MemberDef * toMemberDef(Definition *d)
#define warn(file, line, fmt,...)
Definition message.h:97
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
#define term(fmt,...)
Definition message.h:137
ModuleDef * toModuleDef(Definition *d)
FILE * popen(const DString &name, const DString &type)
Definition portable.cpp:495
std::ifstream openInputStream(const DString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:692
int pclose(FILE *stream)
Definition portable.cpp:504
DString removeLongPathMarker(const DString &path)
Definition portable.cpp:656
bool fileSystemIsCaseSensitive()
Definition portable.cpp:486
DString pathSeparator()
Definition portable.cpp:390
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
void replaceNamespaceAliases(DString &name)
NamespaceDef * getResolvedNamespace(const DString &name)
NamespaceDef * toNamespaceDef(Definition *d)
Definition message.h:146
std::string replace(std::string_view str, const Ex &re, std::string_view replacement)
Searching in a given input string for parts that match regular expression re and replaces those parts...
Definition regex.cpp:872
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:861
Token literal values and constants.
Definition CharStream.h:12
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
size_t portable_iconv(void *cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
void * portable_iconv_open(const char *tocode, const char *fromcode)
Some helper functions for std::string.
void addTerminalCharIfMissing(std::string &s, char c)
Definition stringutil.h:87
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:101
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition stringutil.h:117
CharElem charMap[256]
Definition util.cpp:420
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
bool forceEmptyScope
Definition util.h:85
const FileDef * currentFile
Definition util.h:86
bool insideCode
Definition util.h:88
DString args
Definition util.h:84
DString memberName
Definition util.h:83
bool checkCV
Definition util.h:87
DString scopeName
Definition util.h:82
const MemberDef * md
Definition util.h:95
const ConceptDef * cnd
Definition util.h:100
const FileDef * fd
Definition util.h:97
const ModuleDef * modd
Definition util.h:101
const GroupDef * gd
Definition util.h:99
bool found
Definition util.h:94
const ClassDef * cd
Definition util.h:96
const NamespaceDef * nd
Definition util.h:98
DString encoding
Definition doxygen.h:63
SrcLangExt parserId
Definition util.cpp:4036
const char * langName
Definition util.cpp:4034
const char * parserName
Definition util.cpp:4035
const char * defExt
Definition util.cpp:4037
size_t beginLen
Definition util.h:220
const char * closeStr
Definition util.h:223
const char * beginStr
Definition util.h:219
size_t closeLen
Definition util.h:224
const char * endStr
Definition util.h:221
Protection
Definition types.h:32
SrcLangExt
Definition types.h:207
int isUTF8NonBreakableSpace(const char *input)
Check if the first character pointed at by input is a non-breakable whitespace character.
Definition utf8.cpp:232
uint8_t getUTF8CharNumBytes(char c)
Returns the number of bytes making up a single UTF8 character given the first byte in the sequence.
Definition utf8.cpp:27
Various UTF8 related helper functions.
DString stripExtension(const DString &fName)
Definition util.cpp:3956
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2680
bool isURL(const DString &url)
Checks whether the given url starts with a supported protocol.
Definition util.cpp:4647
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3525
static DString extractDirection(DString &docs)
Strip the direction part from docs and return it as a string in canonical form.
Definition util.cpp:4830
static size_t findParameterList(const DString &name)
Returns the position in the string where a function parameter list begins, or DString::npos if one is...
Definition util.cpp:684
static std::mutex writeFileContents_lock
Definition util.cpp:5377
DString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:4866
DString stripLeadingAndTrailingEmptyLines(const DString &s, int &docLine)
Special version of DString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:3988
bool mainPageHasTitle()
Definition util.cpp:4960
DString replaceColorMarkers(const DString &str)
Replaces any markers of the form ##AA in input string str by new markers of the form #AABBCC,...
Definition util.cpp:4569
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4672
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3754
DString filterTitle(const DString &title)
Definition util.cpp:4455
DString detab(const DString &s, size_t &refIndent)
Definition util.cpp:5195
DString selectBlocks(const DString &s, const SelectionBlockList &blockList, const SelectionMarkerInfo &markerInfo)
remove disabled blocks and all block markers from s and return the result as a string
Definition util.cpp:5017
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void stripIndentationVerbatim(DString &doc, size_t indentationLevel, bool skipFirstLine)
Definition util.cpp:4773
void addCodeOnlyMappings()
Definition util.cpp:4163
static int g_usedNamesCount
Definition util.cpp:2856
static void filterCRLF(std::string &contents)
Definition util.cpp:959
static void stripIrrelevantString(DString &target, const DString &str, bool insideTemplate)
Definition util.cpp:1111
static DString stripDeclKeywords(const DString &s)
Definition util.cpp:1181
void writeTypeConstraints(OutputList &ol, const Definition *d, const ArgumentList &al)
Definition util.cpp:4284
DString extractBeginRawStringDelimiter(const char *rawStart)
Definition util.cpp:5361
void stripIrrelevantConstVolatile(DString &s, bool insideTemplate)
Definition util.cpp:1171
static DString getFilterFromList(const DString &name, const StringVector &filterList, bool &found)
Definition util.cpp:985
DString getProjectId()
Definition util.cpp:5292
#define REL_PATH_TO_ROOT
Definition util.cpp:78
static DString extractCanonicalArgType(const Definition *d, const FileDef *fs, const Argument &arg, SrcLangExt lang)
Definition util.cpp:1458
static DString getCanonicalTemplateSpec(const Definition *d, const FileDef *fs, const DString &spec, SrcLangExt lang)
Definition util.cpp:1197
DString writeInlineGraph(const DString &baseName, const DString &extension, const DString &content, bool &exists)
Definition util.cpp:5380
bool resolveLink(const DString &scName, const DString &lr, bool, const Definition **resContext, DString &resAnchor, SrcLangExt lang, const DString &prefix, const FileDef *currentFile)
Definition util.cpp:2308
bool rightScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:729
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4272
static const char constScope[]
Definition util.cpp:379
static bool recursivelyAddGroupListToTitle(OutputList &ol, const Definition *d, bool root)
Definition util.cpp:3883
DString unescapeCharsInString(const DString &s)
Definition util.cpp:2776
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:2008
DString externalLinkTarget(const bool parent)
Definition util.cpp:4492
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5405
DString correctURL(const DString &url, const DString &relPath)
Corrects URL url according to the relative path relPath.
Definition util.cpp:4660
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5300
DString convertToJSString(const DString &s, bool keepEntities, bool singleQuotes)
Definition util.cpp:3352
DString convertToId(const DString &s)
Definition util.cpp:3203
void trimBaseClassScope(const BaseClassList &bcl, DString &s, int level=0)
Definition util.cpp:1090
DString insertTemplateSpecifierInScope(const DString &scope, const DString &templ)
Definition util.cpp:3077
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
static std::unordered_map< std::string, SrcLangExt > g_extLookup
Definition util.cpp:4030
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3927
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2864
DString showDate(const DString &fmt)
Definition util.cpp:2568
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString stripExtensionGeneral(const DString &fName, const DString &ext)
Definition util.cpp:3946
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
static ModuleDef * findModuleDef(const Definition *d)
Definition util.cpp:3854
void addGroupListToTitle(OutputList &ol, const Definition *d)
Definition util.cpp:3922
DString extractEndRawStringDelimiter(const char *rawEnd)
Definition util.cpp:5369
static DString extractCanonicalType(const Definition *d, const FileDef *fs, DString type, SrcLangExt lang, bool insideTemplate)
Definition util.cpp:1372
#define MATCH
Definition util.cpp:1488
void clearSubDirs(const Dir &d)
Definition util.cpp:2997
DString stripScope(const DString &name)
Definition util.cpp:3110
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4211
void createSubDirs(const Dir &d)
Definition util.cpp:2970
DString convertToHtml(const DString &s, bool keepEntities)
Definition util.cpp:3292
DString resolveTypeDef(const Definition *context, const DString &qualifiedName, const Definition **typedefContext)
Definition util.cpp:232
bool readInputFile(const DString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:4375
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3466
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4169
DString getFileFilter(const DString &name, bool isSourceCode)
Definition util.cpp:1020
bool matchArguments2(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcReturnType, const ArgumentList *srcAl, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstReturnType, const ArgumentList *dstAl, bool checkCV, SrcLangExt lang)
Definition util.cpp:1589
static MemberDef * getMemberFromSymbol(const Definition *scope, const FileDef *fileScope, const DString &n)
Definition util.cpp:4220
void initDefaultExtensionMapping()
Definition util.cpp:4096
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3932
void checkBlocks(const DString &s, const DString fileName, const SelectionMarkerInfo &markerInfo)
Definition util.cpp:5130
static void transcodeCharacterBuffer(const DString &fileName, std::string &contents, const DString &inputEncoding, const DString &outputEncoding)
Definition util.cpp:4339
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1053
static const char virtualScope[]
Definition util.cpp:381
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2265
DString substituteKeywords(const DString &file, const DString &s, const KeywordSubstitutionList &keywords)
Definition util.cpp:2495
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3027
DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
Definition util.cpp:3233
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3687
DString relativePathToRoot(const DString &name)
Definition util.cpp:2913
static const DirDef * resolveDirLink(const DString &linkRef)
Definition util.cpp:2290
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:859
DString projectLogoFile()
Definition util.cpp:2581
static StringUnorderedSet writeFileContents_set
Definition util.cpp:5378
void convertProtectionLevel(MemberListType inListType, Protection inProt, MemberListType *outListType1, MemberListType *outListType2)
Computes for a given list type inListType, which are the the corresponding list type(s) in the base c...
Definition util.cpp:4905
static const char volatileScope[]
Definition util.cpp:380
DString getEncoding(const FileInfo &fi)
Definition util.cpp:4478
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:4620
int getPrefixIndex(const DString &name)
Definition util.cpp:2652
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4630
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4064
void stackTrace()
Definition util.cpp:4311
DString demangleCSharpGenericName(const DString &name, const DString &templArgs)
Definition util.cpp:5350
DString createHtmlUrl(const DString &relPath, const DString &ref, bool isLocalFile, const DString &targetFileName, const DString &anchor)
Definition util.cpp:4503
DString projectLogoSize()
Definition util.cpp:2600
static DString getCanonicalTypeForIdentifier(const Definition *d, const FileDef *fs, const DString &word, SrcLangExt lang, DString *tSpec, int count=0)
Definition util.cpp:1218
DString mangleCSharpGenericName(const DString &name)
Definition util.cpp:5341
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:1866
DString getDotImageExtension()
Definition util.cpp:4965
#define NOMATCH
Definition util.cpp:1489
DString escapeCharsInString(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2689
static std::mutex g_usedNamesMutex
Definition util.cpp:2855
static bool matchArgument2(const Definition *srcScope, const FileDef *srcFileScope, Argument &srcA, const Definition *dstScope, const FileDef *dstFileScope, Argument &dstA, SrcLangExt lang)
Definition util.cpp:1523
void mergeArguments(ArgumentList &srcAl, ArgumentList &dstAl, bool forceNameOverwrite)
Definition util.cpp:1689
int getScopeFragment(const DString &s, int p, int *l)
Definition util.cpp:3799
static bool getScopeDefs(const DString &docScope, const DString &scope, ClassDef *&cd, ConceptDef *&cnd, NamespaceDef *&nd, ModuleDef *&modd)
Definition util.cpp:1936
DString stripIndentation(const DString &s, bool skipFirstLine)
Definition util.cpp:4684
static bool matchCanonicalTypes(const Definition *srcScope, const FileDef *srcFileScope, const DString &srcType, const Definition *dstScope, const FileDef *dstFileScope, const DString &dstType, SrcLangExt lang)
Definition util.cpp:1491
int extractClassNameFromType(const DString &type, int &pos, DString &name, DString &templSpec, SrcLangExt lang)
Definition util.cpp:3381
DString determineAbsoluteIncludeName(const DString &curFile, const DString &incFileName)
Definition util.cpp:2930
static DString stripFromPath(const DString &p, StringVector l)
Definition util.cpp:196
DString removeAnonymousScopes(const DString &str)
Definition util.cpp:98
DString makeBaseName(const DString &name, const DString &ext)
Definition util.cpp:3975
DString getDotImageExtensionGenerated(const DString &format)
Definition util.cpp:4975
void writeMarkerList(OutputList &ol, const std::string &markerText, size_t numMarkers, std::function< void(size_t)> replaceFunc)
Definition util.cpp:753
DString externalRef(const DString &relPath, const DString &ref)
Definition util.cpp:4539
static bool isLowerCase(DString &s)
Definition util.cpp:1999
DString stripPath(const DString &s)
Definition util.cpp:3961
static std::unordered_map< std::string, int > g_usedNames
Definition util.cpp:2854
static CharAroundSpace g_charAroundSpace
Definition util.cpp:423
DString inlineTemplateArgListToDoc(const ArgumentList &al)
Definition util.cpp:832
#define HEXTONUM(x)
void writeExamples(OutputList &ol, const ExampleList &list)
Definition util.cpp:804
static std::mutex g_matchArgsMutex
Definition util.cpp:1481
static const char * hex
Definition util.cpp:80
static const char operatorScope[]
Definition util.cpp:382
bool openOutputFile(const DString &outFile, std::ofstream &f)
Definition util.cpp:4987
static std::vector< Lang2ExtMap > g_lang2extMap
Definition util.cpp:4040
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
DString findExampleFilePath(const DString &file, bool &ambig)
Definition util.cpp:2452
SrcLangExt getLanguageFromCodeLang(DString &fileName)
Routine to handle the language attribute of the \code command.
Definition util.cpp:4187
A bunch of utility functions.
std::vector< KeywordSubstitution > KeywordSubstitutionList
Definition util.h:266
std::vector< SelectionBlock > SelectionBlockList
Definition util.h:214