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 )
2316{
2317 *resContext=nullptr;
2318
2319 DString linkRef=lr;
2320 if (lang==SrcLangExt::CSharp)
2321 {
2322 linkRef = mangleCSharpGenericName(linkRef);
2323 }
2324 DString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,false);
2325 AUTO_TRACE("scName='{}',ref='{}'",scName,lr);
2326 const FileDef *fd = nullptr;
2327 const GroupDef *gd = nullptr;
2328 const PageDef *pd = nullptr;
2329 const ClassDef *cd = nullptr;
2330 const DirDef *dir = nullptr;
2331 const ConceptDef *cnd = nullptr;
2332 const ModuleDef *modd = nullptr;
2333 const NamespaceDef *nd = nullptr;
2334 const SectionInfo *si = nullptr;
2335 bool ambig = false;
2336 if (linkRef.empty()) // no reference name!
2337 {
2338 AUTO_TRACE_EXIT("no_ref");
2339 return false;
2340 }
2341 else if ((pd=Doxygen::pageLinkedMap->find(linkRef))) // link to a page
2342 {
2343 gd = pd->getGroupDef();
2344 if (gd)
2345 {
2346 if (!pd->name().empty()) si=SectionManager::instance().find(pd->name());
2347 *resContext=gd;
2348 if (si) resAnchor = si->label();
2349 }
2350 else
2351 {
2352 *resContext=pd;
2353 }
2354 AUTO_TRACE_EXIT("page");
2355 return true;
2356 }
2357 else if ((si=SectionManager::instance().find(prefix+linkRef)))
2358 {
2359 *resContext=si->definition();
2360 resAnchor = si->label();
2361 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2362 return true;
2363 }
2364 else if (!prefix.empty() && (si=SectionManager::instance().find(linkRef)))
2365 {
2366 *resContext=si->definition();
2367 resAnchor = si->label();
2368 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2369 return true;
2370 }
2371 else if ((pd=Doxygen::exampleLinkedMap->find(linkRef))) // link to an example
2372 {
2373 *resContext=pd;
2374 AUTO_TRACE_EXIT("example");
2375 return true;
2376 }
2377 else if ((gd=Doxygen::groupLinkedMap->find(linkRef))) // link to a group
2378 {
2379 *resContext=gd;
2380 AUTO_TRACE_EXIT("group");
2381 return true;
2382 }
2383 else if ((fd=Doxygen::inputNameLinkedMap->findFileDef(linkRef,ambig)) // file link
2384 && fd->isLinkable())
2385 {
2386 *resContext=fd;
2387 AUTO_TRACE_EXIT("file");
2388 return true;
2389 }
2390 else if ((cd=getClass(linkRef))) // class link
2391 {
2392 *resContext=cd;
2393 resAnchor=cd->anchor();
2394 AUTO_TRACE_EXIT("class");
2395 return true;
2396 }
2397 else if (lang==SrcLangExt::Java &&
2398 (cd=getClass(linkRefWithoutTemplates))) // Java generic class link
2399 {
2400 *resContext=cd;
2401 resAnchor=cd->anchor();
2402 AUTO_TRACE_EXIT("generic");
2403 return true;
2404 }
2405 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
2406 {
2407 *resContext=cd;
2408 resAnchor=cd->anchor();
2409 AUTO_TRACE_EXIT("protocol");
2410 return true;
2411 }
2412 else if ((cnd=getConcept(linkRef))) // C++20 concept definition
2413 {
2414 *resContext=cnd;
2415 resAnchor=cnd->anchor();
2416 AUTO_TRACE_EXIT("concept");
2417 return true;
2418 }
2419 else if ((modd=ModuleManager::instance().modules().find(linkRef)))
2420 {
2421 *resContext=modd;
2422 resAnchor=modd->anchor();
2423 AUTO_TRACE_EXIT("module");
2424 return true;
2425 }
2426 else if ((nd=Doxygen::namespaceLinkedMap->find(linkRef)))
2427 {
2428 *resContext=nd;
2429 AUTO_TRACE_EXIT("namespace");
2430 return true;
2431 }
2432 else if ((dir=resolveDirLink(linkRef)) && dir->isLinkable())
2433 {
2434 *resContext=dir;
2435 AUTO_TRACE_EXIT("directory");
2436 return true;
2437 }
2438 else // probably a member reference
2439 {
2440 const MemberDef *md = nullptr;
2441 bool res = resolveRef(scName,lr,true,resContext,&md,lang);
2442 if (md) resAnchor=md->anchor();
2443 AUTO_TRACE_EXIT("member? res={}",res);
2444 return res;
2445 }
2446}
2447
2448//----------------------------------------------------------------------
2449
2450
2451DString findExampleFilePath(const DString &file,bool &ambig)
2452{
2453 ambig=false;
2454 DString result;
2455 bool found=false;
2456 if (!found)
2457 {
2458 FileInfo fi(file.str());
2459 if (fi.exists())
2460 {
2461 result=fi.absFilePath();
2462 found=true;
2463 }
2464 }
2465 if (!found)
2466 {
2467 StringVector examplePathList = Config_getList(EXAMPLE_PATH);
2468 for (const auto &s : examplePathList)
2469 {
2470 std::string absFileName = s+(Portable::pathSeparator()+file).str();
2471 FileInfo fi(absFileName);
2472 if (fi.exists())
2473 {
2474 result=fi.absFilePath();
2475 found=true;
2476 }
2477 }
2478 }
2479
2480 if (!found)
2481 {
2482 // as a fallback we also look in the exampleNameDict
2484 if (fd && !ambig)
2485 {
2486 result=fd->absFilePath();
2487 }
2488 }
2489 return result;
2490}
2491
2492//----------------------------------------------------------------------
2493
2495{
2496 std::string substRes;
2497 int line = 1;
2498 const char *p = s.data();
2499 if (p)
2500 {
2501 // reserve some room for expansion
2502 substRes.reserve(s.length()+1024);
2503 char c = 0;
2504 while ((c=*p))
2505 {
2506 bool found = false;
2507 if (c=='$')
2508 {
2509 for (const auto &kw : keywords)
2510 {
2511 size_t keyLen = dstrlen(kw.keyword);
2512 if (dstrncmp(p,kw.keyword,keyLen)==0)
2513 {
2514 const char *startArg = p+keyLen;
2515 bool expectParam = std::holds_alternative<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
2516 //printf("%s: expectParam=%d *startArg=%c\n",kw.keyword,expectParam,*startArg);
2517 if (expectParam && *startArg=='(') // $key(value)
2518 {
2519 size_t j=1;
2520 const char *endArg = nullptr;
2521 while ((c=*(startArg+j)) && c!=')' && c!='\n' && c!=0) j++;
2522 if (c==')') endArg=startArg+j;
2523 if (endArg)
2524 {
2525 DString value = DString(startArg+1).left(endArg-startArg-1);
2526 auto &&getValue = std::get<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
2527 substRes+=getValue(value).str();
2528 p=endArg+1;
2529 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue(value)));
2530 }
2531 else
2532 {
2533 //printf("missing argument\n");
2534 warn(file,line,"Missing argument for '{}'",kw.keyword);
2535 p+=keyLen;
2536 }
2537 }
2538 else if (!expectParam) // $key
2539 {
2540 auto &&getValue = std::get<KeywordSubstitution::GetValue>(kw.getValueVariant);
2541 substRes+=getValue().str();
2542 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue()));
2543 p+=keyLen;
2544 }
2545 else
2546 {
2547 //printf("%s %d Expected arguments, none specified '%s'\n",qPrint(file), line, qPrint(kw.keyword));
2548 warn(file,line,"Expected arguments for '{}' but none were specified",kw.keyword);
2549 p+=keyLen;
2550 }
2551 found = true;
2552 break;
2553 }
2554 }
2555 }
2556 if (!found) // copy
2557 {
2558 if (c=='\n') line++;
2559 substRes+=c;
2560 p++;
2561 }
2562 }
2563 }
2564 return substRes;
2565}
2566
2568{
2569 // get the current date and time
2570 std::tm dat{};
2571 int specFormat=0;
2572 DString specDate = "";
2573 DString err = dateTimeFromString(specDate,dat,specFormat);
2574
2575 // do the conversion
2576 int usedFormat=0;
2577 return formatDateTime(fmt,dat,usedFormat);
2578}
2579
2581{
2582 DString projectLogo = Config_getString(PROJECT_LOGO);
2583 if (!projectLogo.empty())
2584 {
2585 // check for optional width= and height= specifier
2586 if (size_t wi = projectLogo.find(" width="); wi!=DString::npos) // and strip them
2587 {
2588 projectLogo = projectLogo.left(wi);
2589 }
2590 if (size_t hi = projectLogo.find(" height="); hi!=DString::npos)
2591 {
2592 projectLogo = projectLogo.left(hi);
2593 }
2594 }
2595 //printf("projectlogo='%s'\n",qPrint(projectLogo));
2596 return projectLogo;
2597}
2598
2600{
2601 DString sizeVal;
2602 DString projectLogo = Config_getString(PROJECT_LOGO);
2603 if (!projectLogo.empty())
2604 {
2605 auto extractDimension = [&projectLogo](const char *startMarker,size_t startPos,size_t endPos) -> DString
2606 {
2607 DString result = projectLogo.mid(startPos,endPos-startPos).stripWhiteSpace().quoted();
2608 if (result.length()>=2 && result.at(0)!='"' && result.at(result.length()-1)!='"')
2609 {
2610 result="\""+result+"\"";
2611 }
2612 result.prepend(startMarker);
2613 return result;
2614 };
2615 // check for optional width= and height= specifier
2616 size_t wi = projectLogo.find(" width=");
2617 size_t hi = projectLogo.find(" height=");
2618 if (wi!=DString::npos && hi!=DString::npos)
2619 {
2620 if (wi<hi) // "... width=x height=y..."
2621 {
2622 sizeVal = extractDimension(" width=", wi+7, hi) + " "
2623 + extractDimension(" height=", hi+8, projectLogo.length());
2624 }
2625 else // "... height=y width=x..."
2626 {
2627 sizeVal = extractDimension(" height=", hi+8, wi) + " "
2628 + extractDimension(" width=", wi+7, projectLogo.length());
2629 }
2630 }
2631 else if (wi!=DString::npos) // ... width=x..."
2632 {
2633 sizeVal = extractDimension(" width=", wi+7, projectLogo.length());
2634 }
2635 else if (hi!=DString::npos) // ... height=x..."
2636 {
2637 sizeVal = extractDimension(" height=", hi+8, projectLogo.length());
2638 }
2639 }
2640 //printf("projectsize='%s'\n",qPrint(sizeVal));
2641 return sizeVal;
2642}
2643
2644
2645//----------------------------------------------------------------------
2646
2647/*! Returns the character index within \a name of the first prefix
2648 * in Config_getList(IGNORE_PREFIX) that matches \a name at the left hand side,
2649 * or zero if no match was found
2650 */
2651int getPrefixIndex(const DString &name)
2652{
2653 if (name.empty()) return 0;
2654 int result=0;
2655 StringVector sl = Config_getList(IGNORE_PREFIX);
2656 for (const auto &s : sl)
2657 {
2658 const char *ps=s.c_str();
2659 const char *pd=name.data();
2660 int i=0;
2661 while (*ps!=0 && *pd!=0 && *ps==*pd)
2662 {
2663 ps++;
2664 pd++;
2665 i++;
2666 }
2667 if (*ps==0 && *pd!=0)
2668 {
2669 result=i;
2670 break;
2671 }
2672 }
2673 if (result<static_cast<int>(name.length())-1 && name.at(result)=='[') result++; // for e.g. [union] return u
2674 return result;
2675}
2676
2677//----------------------------------------------------------------------------
2678
2680{
2681 auto caseSenseNames = Config_getEnum(CASE_SENSE_NAMES);
2682
2683 if (caseSenseNames == CASE_SENSE_NAMES_t::YES) return true;
2684 else if (caseSenseNames == CASE_SENSE_NAMES_t::NO) return false;
2686}
2687
2688DString escapeCharsInString(const DString &name,bool allowDots,bool allowUnderscore)
2689{
2690 if (name.empty()) return name;
2691 bool caseSenseNames = useCaseSenseNames();
2692 bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
2693 DString result;
2694 result.reserve(name.length()+8);
2695 signed char c = 0;
2696 const char *p=name.data();
2697 while ((c=*p++)!=0)
2698 {
2699 switch(c)
2700 {
2701 case '_': if (allowUnderscore) result+='_'; else result+="__"; break;
2702 case '-': result+='-'; break;
2703 case ':': result+="_1"; break;
2704 case '/': result+="_2"; break;
2705 case '<': result+="_3"; break;
2706 case '>': result+="_4"; break;
2707 case '*': result+="_5"; break;
2708 case '&': result+="_6"; break;
2709 case '|': result+="_7"; break;
2710 case '.': if (allowDots) result+='.'; else result+="_8"; break;
2711 case '!': result+="_9"; break;
2712 case ',': result+="_00"; break;
2713 case ' ': result+="_01"; break;
2714 case '{': result+="_02"; break;
2715 case '}': result+="_03"; break;
2716 case '?': result+="_04"; break;
2717 case '^': result+="_05"; break;
2718 case '%': result+="_06"; break;
2719 case '(': result+="_07"; break;
2720 case ')': result+="_08"; break;
2721 case '+': result+="_09"; break;
2722 case '=': result+="_0a"; break;
2723 case '$': result+="_0b"; break;
2724 case '\\': result+="_0c"; break;
2725 case '@': result+="_0d"; break;
2726 case ']': result+="_0e"; break;
2727 case '[': result+="_0f"; break;
2728 case '#': result+="_0g"; break;
2729 case '"': result+="_0h"; break;
2730 case '~': result+="_0i"; break;
2731 case '\'': result+="_0j"; break;
2732 case ';': result+="_0k"; break;
2733 case '`': result+="_0l"; break;
2734 default:
2735 if (c<0)
2736 {
2737 bool doEscape = true;
2738 if (allowUnicodeNames)
2739 {
2740 int charLen = getUTF8CharNumBytes(c);
2741 if (charLen>0)
2742 {
2743 result+=DString(p-1,charLen);
2744 p+=charLen;
2745 doEscape = false;
2746 }
2747 }
2748 if (doEscape) // not a valid unicode char or escaping needed
2749 {
2750 char ids[5];
2751 unsigned char id = static_cast<unsigned char>(c);
2752 ids[0]='_';
2753 ids[1]='x';
2754 ids[2]=hex[id>>4];
2755 ids[3]=hex[id&0xF];
2756 ids[4]=0;
2757 result+=ids;
2758 }
2759 }
2760 else if (caseSenseNames || !isupper(c))
2761 {
2762 result+=c;
2763 }
2764 else
2765 {
2766 result+='_';
2767 result+=static_cast<char>(tolower(c));
2768 }
2769 break;
2770 }
2771 }
2772 return result;
2773}
2774
2776{
2777 if (s.empty()) return s;
2778 bool caseSenseNames = useCaseSenseNames();
2779 DString result;
2780 result.reserve(s.length());
2781 const char *p = s.data();
2782 if (p)
2783 {
2784 char c = 0;
2785 while ((c=*p++))
2786 {
2787 if (c=='_') // 2 or 3 character escape
2788 {
2789 switch (*p)
2790 {
2791 case '_': result+=c; p++; break; // __ -> '_'
2792 case '1': result+=':'; p++; break; // _1 -> ':'
2793 case '2': result+='/'; p++; break; // _2 -> '/'
2794 case '3': result+='<'; p++; break; // _3 -> '<'
2795 case '4': result+='>'; p++; break; // _4 -> '>'
2796 case '5': result+='*'; p++; break; // _5 -> '*'
2797 case '6': result+='&'; p++; break; // _6 -> '&'
2798 case '7': result+='|'; p++; break; // _7 -> '|'
2799 case '8': result+='.'; p++; break; // _8 -> '.'
2800 case '9': result+='!'; p++; break; // _9 -> '!'
2801 case '0': // 3 character escape
2802 switch (*(p+1))
2803 {
2804 case '0': result+=','; p+=2; break; // _00 -> ','
2805 case '1': result+=' '; p+=2; break; // _01 -> ' '
2806 case '2': result+='{'; p+=2; break; // _02 -> '{'
2807 case '3': result+='}'; p+=2; break; // _03 -> '}'
2808 case '4': result+='?'; p+=2; break; // _04 -> '?'
2809 case '5': result+='^'; p+=2; break; // _05 -> '^'
2810 case '6': result+='%'; p+=2; break; // _06 -> '%'
2811 case '7': result+='('; p+=2; break; // _07 -> '('
2812 case '8': result+=')'; p+=2; break; // _08 -> ')'
2813 case '9': result+='+'; p+=2; break; // _09 -> '+'
2814 case 'a': result+='='; p+=2; break; // _0a -> '='
2815 case 'b': result+='$'; p+=2; break; // _0b -> '$'
2816 case 'c': result+='\\'; p+=2; break;// _0c -> '\'
2817 case 'd': result+='@'; p+=2; break; // _0d -> '@'
2818 case 'e': result+=']'; p+=2; break; // _0e -> ']'
2819 case 'f': result+='['; p+=2; break; // _0f -> '['
2820 case 'g': result+='#'; p+=2; break; // _0g -> '#'
2821 case 'h': result+='"'; p+=2; break; // _0h -> '"'
2822 case 'i': result+='~'; p+=2; break; // _0i -> '~'
2823 case 'j': result+='\''; p+=2; break;// _0j -> '\'
2824 case 'k': result+=';'; p+=2; break; // _0k -> ';'
2825 case 'l': result+='`'; p+=2; break; // _0l -> '`'
2826 default: // unknown escape, just pass underscore character as-is
2827 result+=c;
2828 break;
2829 }
2830 break;
2831 default:
2832 if (!caseSenseNames && c>='a' && c<='z') // lower to upper case escape, _a -> 'A'
2833 {
2834 result+=static_cast<char>(toupper(*p));
2835 p++;
2836 }
2837 else // unknown escape, pass underscore character as-is
2838 {
2839 result+=c;
2840 }
2841 break;
2842 }
2843 }
2844 else // normal character; pass as is
2845 {
2846 result+=c;
2847 }
2848 }
2849 }
2850 return result;
2851}
2852
2853static std::unordered_map<std::string,int> g_usedNames;
2854static std::mutex g_usedNamesMutex;
2855static int g_usedNamesCount=1;
2856
2857
2858
2859/*! This function determines the file name on disk of an item
2860 * given its name, which could be a class name with template
2861 * arguments, so special characters need to be escaped.
2862 */
2863DString convertNameToFile(const DString &name,bool allowDots,bool allowUnderscore)
2864{
2865 if (name.empty()) return name;
2866 bool shortNames = Config_getBool(SHORT_NAMES);
2867 bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
2868 DString result;
2869 if (shortNames) // use short names only
2870 {
2871 std::lock_guard<std::mutex> lock(g_usedNamesMutex);
2872 auto kv = g_usedNames.find(name.str());
2873 uint32_t num=0;
2874 if (kv!=g_usedNames.end())
2875 {
2876 num = kv->second;
2877 }
2878 else
2879 {
2880 num = g_usedNamesCount;
2881 g_usedNames.emplace(name.str(),g_usedNamesCount++);
2882 }
2883 result.sprintf("a%05d",num);
2884 }
2885 else // long names
2886 {
2887 result=escapeCharsInString(name,allowDots,allowUnderscore);
2888 size_t resultLen = result.length();
2889 if (resultLen>=128) // prevent names that cannot be created!
2890 {
2891 // third algorithm based on MD5 hash
2892 result=result.left(128-32)+md5str(result.view());
2893 }
2894 }
2895 if (createSubdirs)
2896 {
2897 int l1Dir=0,l2Dir=0;
2898 int createSubdirsLevel = Config_getInt(CREATE_SUBDIRS_LEVEL);
2899 int createSubdirsBitmaskL2 = (1<<createSubdirsLevel)-1;
2900
2901 // compute md5 hash to determine sub directory to use
2902 auto md5_sig = md5hash(result.view());
2903 l1Dir = md5_sig[14] & 0xf;
2904 l2Dir = md5_sig[15] & createSubdirsBitmaskL2;
2905
2906 result.prepend(DString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
2907 }
2908 //printf("*** convertNameToFile(%s)->%s\n",qPrint(name),qPrint(result));
2909 return result;
2910}
2911
2913{
2914 DString result;
2915 if (Config_getBool(CREATE_SUBDIRS))
2916 {
2917 if (name.empty())
2918 {
2919 return REL_PATH_TO_ROOT;
2920 }
2921 else if (size_t i = name.rfind('/'); i!=DString::npos)
2922 {
2923 result=REL_PATH_TO_ROOT;
2924 }
2925 }
2926 return result;
2927}
2928
2929DString determineAbsoluteIncludeName(const DString &curFile,const DString &incFileName)
2930{
2931 bool searchIncludes = Config_getBool(SEARCH_INCLUDES);
2932 DString absIncFileName = incFileName;
2933 FileInfo fi(curFile.str());
2934 if (fi.exists())
2935 {
2936 DString absName = fi.dirPath(true)+"/"+incFileName;
2937 FileInfo fi2(absName.str());
2938 if (fi2.exists())
2939 {
2940 absIncFileName=fi2.absFilePath();
2941 }
2942 else if (searchIncludes) // search in INCLUDE_PATH as well
2943 {
2944 StringVector includePath = Config_getList(INCLUDE_PATH);
2945 for (const auto &incPath : includePath)
2946 {
2947 FileInfo fi3(incPath);
2948 if (fi3.exists() && fi3.isDir())
2949 {
2950 absName = fi3.absFilePath()+"/"+incFileName;
2951 //printf("trying absName=%s\n",qPrint(absName));
2952 FileInfo fi4(absName.str());
2953 if (fi4.exists())
2954 {
2955 absIncFileName=fi4.absFilePath();
2956 break;
2957 }
2958 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
2959 }
2960 }
2961 }
2962 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
2963 }
2964 return absIncFileName;
2965}
2966
2967
2968
2969void createSubDirs(const Dir &d)
2970{
2971 if (Config_getBool(CREATE_SUBDIRS))
2972 {
2973 // create up to 4096 subdirectories
2974 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
2975 for (int l1=0; l1<16; l1++)
2976 {
2977 DString subdir;
2978 subdir.sprintf("d%x",l1);
2979 if (!d.exists(subdir.str()) && !d.mkdir(subdir.str()))
2980 {
2981 term("Failed to create output directory '{}'\n",subdir);
2982 }
2983 for (int l2=0; l2<createSubdirsLevelPow2; l2++)
2984 {
2985 DString subsubdir;
2986 subsubdir.sprintf("d%x/d%02x",l1,l2);
2987 if (!d.exists(subsubdir.str()) && !d.mkdir(subsubdir.str()))
2988 {
2989 term("Failed to create output directory '{}'\n",subsubdir);
2990 }
2991 }
2992 }
2993 }
2994}
2995
2996void clearSubDirs(const Dir &d)
2997{
2998 if (Config_getBool(CREATE_SUBDIRS))
2999 {
3000 // remove empty subdirectories
3001 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
3002 for (int l1=0;l1<16;l1++)
3003 {
3004 DString subdir;
3005 subdir.sprintf("d%x",l1);
3006 for (int l2=0; l2 < createSubdirsLevelPow2; l2++)
3007 {
3008 DString subsubdir;
3009 subsubdir.sprintf("d%x/d%02x",l1,l2);
3010 if (d.exists(subsubdir.str()) && d.empty(subsubdir.str()))
3011 {
3012 d.rmdir(subsubdir.str());
3013 }
3014 }
3015 if (d.exists(subdir.str()) && d.empty(subdir.str()))
3016 {
3017 d.rmdir(subdir.str());
3018 }
3019 }
3020 }
3021}
3022
3023/*! Input is a scopeName, output is the scopename split into a
3024 * namespace part (as large as possible) and a classname part.
3025 */
3026void extractNamespaceName(const DString &scopeName,
3027 DString &className,DString &namespaceName,
3028 bool allowEmptyClass)
3029{
3030 DString clName=scopeName;
3031 NamespaceDef *nd = nullptr;
3032 size_t i=0;
3033 int p=0;
3034 if (!clName.empty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==nullptr)
3035 { // the whole name is a namespace (and not a class)
3036 namespaceName=nd->name();
3037 className.clear();
3038 goto done;
3039 }
3040 p=static_cast<int>(clName.length())-2;
3041 while (p>=0 && (i=clName.rfind("::",p))!=DString::npos)
3042 // see if the first part is a namespace (and not a class)
3043 {
3044 //printf("Trying %s\n",qPrint(clName.left(i)));
3045 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==nullptr)
3046 {
3047 //printf("found!\n");
3048 namespaceName=nd->name();
3049 className=clName.mid(i+2);
3050 goto done;
3051 }
3052 p=static_cast<int>(i)-2; // try a smaller piece of the scope
3053 }
3054 //printf("not found!\n");
3055
3056 // not found, so we just have to guess.
3057 className=scopeName;
3058 namespaceName.clear();
3059
3060done:
3061 if (className.empty() && !namespaceName.empty() && !allowEmptyClass)
3062 {
3063 // class and namespace with the same name, correct to return the class.
3064 className=namespaceName;
3065 namespaceName.clear();
3066 }
3067 //printf("extractNamespace '%s' => '%s|%s'\n",qPrint(scopeName),
3068 // qPrint(className),qPrint(namespaceName));
3069 if (className.endsWith("-p"))
3070 {
3071 className = className.left(className.length()-2);
3072 }
3073 return;
3074}
3075
3077{
3078 DString result=scope;
3079 if (!templ.empty() && scope.find('<')==DString::npos)
3080 {
3081 size_t si=0, pi=0;
3082 ClassDef *cd=nullptr;
3083 while (
3084 (si=scope.find("::",pi))!=DString::npos && !getClass(scope.left(si)+templ) &&
3085 ((cd=getClass(scope.left(si)))==nullptr || cd->templateArguments().empty())
3086 )
3087 {
3088 //printf("Tried '%s'\n",qPrint((scope.left(si)+templ)));
3089 pi=si+2;
3090 }
3091 if (si==DString::npos) // not nested => append template specifier
3092 {
3093 result+=templ;
3094 }
3095 else // nested => insert template specifier before after first class name
3096 {
3097 result=scope.left(si) + templ + scope.mid(si);
3098 }
3099 }
3100 //printf("insertTemplateSpecifierInScope('%s','%s')=%s\n",
3101 // qPrint(scope),qPrint(templ),qPrint(result));
3102 return result;
3103}
3104
3105
3106/*! Strips the scope from a name. Examples: A::B will return A
3107 * and A<T>::B<N::C<D> > will return A<T>.
3108 */
3110{
3111 DString result = name;
3112 int l = static_cast<int>(result.length());
3113 int p = 0;
3114 bool done = false;
3115 bool skipBracket=false; // if brackets do not match properly, ignore them altogether
3116 int count=0;
3117 int round=0;
3118
3119 do
3120 {
3121 p=l-1; // start at the end of the string
3122 while (p>=0 && count>=0)
3123 {
3124 char c=result.at(p);
3125 switch (c)
3126 {
3127 case ':':
3128 // only exit in the case of ::
3129 //printf("stripScope(%s)=%s\n",name,qPrint(result.right(l-p-1)));
3130 if (p>0 && result.at(p-1)==':' && (count==0 || skipBracket))
3131 {
3132 return result.right(l-p-1);
3133 }
3134 p--;
3135 break;
3136 case '>':
3137 if (skipBracket) // we don't care about brackets
3138 {
3139 p--;
3140 }
3141 else // count open/close brackets
3142 {
3143 if (p>0 && result.at(p-1)=='>') // skip >> operator
3144 {
3145 p-=2;
3146 break;
3147 }
3148 count=1;
3149 //printf("pos < = %d\n",p);
3150 p--;
3151 bool foundMatch=false;
3152 while (p>=0 && !foundMatch)
3153 {
3154 c=result.at(p--);
3155 switch (c)
3156 {
3157 case ')':
3158 round++;
3159 break;
3160 case '(':
3161 round--;
3162 break;
3163 case '>': // ignore > inside (...) to support e.g. (sizeof(T)>0) inside template parameters
3164 if (round==0) count++;
3165 break;
3166 case '<':
3167 if (round==0)
3168 {
3169 if (p>0)
3170 {
3171 if (result.at(p-1) == '<') // skip << operator
3172 {
3173 p--;
3174 break;
3175 }
3176 }
3177 count--;
3178 foundMatch = count==0;
3179 }
3180 break;
3181 default:
3182 //printf("c=%c count=%d\n",c,count);
3183 break;
3184 }
3185 }
3186 }
3187 //printf("pos > = %d\n",p+1);
3188 break;
3189 default:
3190 p--;
3191 }
3192 }
3193 done = count==0 || skipBracket; // reparse if brackets do not match
3194 skipBracket=true;
3195 }
3196 while (!done); // if < > unbalanced repeat ignoring them
3197 //printf("stripScope(%s)=%s\n",name,name);
3198 return name;
3199}
3200
3201/*! Converts a string to a HTML id string */
3203{
3204 if (s.empty()) return s;
3205 DString result;
3206 result.reserve(s.length()+8);
3207 const char *p = s.data();
3208 char c = 0;
3209 bool first = true;
3210 while ((c=*p++))
3211 {
3212 char encChar[4];
3213 if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-')
3214 { // any permissive character except _
3215 if (first && c>='0' && c<='9') result+='a'; // don't start with a digit
3216 result+=c;
3217 }
3218 else
3219 {
3220 encChar[0]='_';
3221 encChar[1]=hex[static_cast<unsigned char>(c)>>4];
3222 encChar[2]=hex[static_cast<unsigned char>(c)&0xF];
3223 encChar[3]=0;
3224 result+=encChar;
3225 }
3226 first=false;
3227 }
3228 return result;
3229}
3230
3231/*! Converts a string to an XML-encoded string */
3232DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
3233{
3234 if (s.empty()) return s;
3235 DString result;
3236 result.reserve(s.length()+32);
3237 const char *p = s.data();
3238 char c = 0;
3239 while ((c=*p++))
3240 {
3241 switch (c)
3242 {
3243 case '<': result+="&lt;"; break;
3244 case '>': result+="&gt;"; break;
3245 case '&': if (keepEntities)
3246 {
3247 const char *e=p;
3248 char ce = 0;
3249 while ((ce=*e++))
3250 {
3251 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
3252 }
3253 if (ce==';') // found end of an entity
3254 {
3255 // copy entry verbatim
3256 result+=c;
3257 while (p<e) result+=*p++;
3258 }
3259 else
3260 {
3261 result+="&amp;";
3262 }
3263 }
3264 else if (citeEntry)
3265 {
3267 result,
3268 p-1,
3269 [](HtmlEntityMapper::SymType symType) { return HtmlEntityMapper::instance().xml(symType); },
3270 "&amp;");
3271 }
3272 else
3273 {
3274 result+="&amp;";
3275 }
3276 break;
3277 case '\'': result+="&apos;"; break;
3278 case '"': result+="&quot;"; break;
3279 case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
3280 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
3281 case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
3282 case 27: case 28: case 29: case 30: case 31:
3283 break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
3284 default: result+=c; break;
3285 }
3286 }
3287 return result;
3288}
3289
3290/*! Converts a string to a HTML-encoded string */
3291DString convertToHtml(const DString &s,bool keepEntities)
3292{
3293 if (s.empty()) return s;
3294 DString result;
3295 result.reserve(s.length()+32);
3296 const char *p=s.data();
3297 char c = 0;
3298 while ((c=*p++))
3299 {
3300 switch (c)
3301 {
3302 case '<': result+="&lt;"; break;
3303 case '>': result+="&gt;"; break;
3304 case '&': if (keepEntities)
3305 {
3306 const char *e=p;
3307 char ce = 0;
3308 while ((ce=*e++))
3309 {
3310 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
3311 }
3312 if (ce==';') // found end of an entity
3313 {
3314 // copy entry verbatim
3315 result+=c;
3316 while (p<e) result+=*p++;
3317 }
3318 else
3319 {
3320 result+="&amp;";
3321 }
3322 }
3323 else
3324 {
3325 result+="&amp;";
3326 }
3327 break;
3328 case '\'': result+="&#39;"; break;
3329 case '"': result+="&quot;"; break;
3330 default:
3331 {
3332 uint8_t uc = static_cast<uint8_t>(c);
3333 if (uc<32 && !isspace(c))
3334 {
3335 result+="&#x24";
3336 result+=hex[uc>>4];
3337 result+=hex[uc&0xF];
3338 result+=';';
3339 }
3340 else
3341 {
3342 result+=c;
3343 }
3344 }
3345 break;
3346 }
3347 }
3348 return result;
3349}
3350
3351DString convertToJSString(const DString &s,bool keepEntities,bool singleQuotes)
3352{
3353 if (s.empty()) return s;
3354 DString result;
3355 result.reserve(s.length()+32);
3356 const char *p=s.data();
3357 char c = 0;
3358 while ((c=*p++))
3359 {
3360 switch (c)
3361 {
3362 case '"': if (!singleQuotes) result+="\\\""; else result+=c;
3363 break;
3364 case '\'': if (singleQuotes) result+="\\\'"; else result+=c;
3365 break;
3366 case '\\': if (*p=='u' && *(p+1)=='{') result+="\\"; // keep \u{..} unicode escapes
3367 else result+="\\\\";
3368 break;
3369 default: result+=c; break;
3370 }
3371 }
3372 return keepEntities ? result : HtmlEntityMapper::instance().convertCharEntitiesToUTF8(result);
3373}
3374
3375/*! Extracts a (sub-)string from \a type starting at \a pos that
3376 * could form a class. The index of the match is returned and the found
3377 * class \a name and a template argument list \a templSpec. If -1 is returned
3378 * there are no more matches.
3379 */
3380int extractClassNameFromType(const DString &type,int &pos,DString &name,DString &templSpec,SrcLangExt lang)
3381{
3382 AUTO_TRACE("type='{}' pos={} name='{}' lang={}",type,pos,name,lang);
3383 static const reg::Ex re_norm(R"(\a[\w:]*)");
3384 static const reg::Ex re_fortran(R"(\a[\w:()=]*)");
3385 const reg::Ex *re = &re_norm;
3386
3387 name.clear();
3388 templSpec.clear();
3389 if (type.empty())
3390 {
3391 AUTO_TRACE_EXIT("empty type");
3392 return -1;
3393 }
3394 size_t typeLen=type.length();
3395 if (typeLen>0)
3396 {
3397 if (lang == SrcLangExt::Fortran)
3398 {
3399 if (type[pos]==',')
3400 {
3401 AUTO_TRACE_EXIT("comma");
3402 return -1;
3403 }
3404 if (!type.lower().startsWith("type"))
3405 {
3406 re = &re_fortran;
3407 }
3408 }
3409 std::string s = type.str();
3410 reg::Iterator it(s,*re,static_cast<int>(pos));
3412
3413 if (it!=end)
3414 {
3415 const auto &match = *it;
3416 size_t i = match.position();
3417 size_t l = match.length();
3418 size_t ts = i+l;
3419 size_t te = ts;
3420 size_t tl = 0;
3421
3422 while (ts<typeLen && type[static_cast<uint32_t>(ts)]==' ') { ts++; tl++; } // skip any whitespace
3423 if (ts<typeLen && type[static_cast<uint32_t>(ts)]=='<') // assume template instance
3424 {
3425 // locate end of template
3426 te=ts+1;
3427 int brCount=1;
3428 while (te<typeLen && brCount!=0)
3429 {
3430 if (type[static_cast<uint32_t>(te)]=='<')
3431 {
3432 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='<') te++; else brCount++;
3433 }
3434 if (type[static_cast<uint32_t>(te)]=='>')
3435 {
3436 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='>') te++; else brCount--;
3437 }
3438 te++;
3439 }
3440 }
3441 name = match.str();
3442 if (te>ts)
3443 {
3444 templSpec = DString(type).mid(ts,te-ts);
3445 tl+=te-ts;
3446 pos=static_cast<int>(i+l+tl);
3447 }
3448 else // no template part
3449 {
3450 pos=static_cast<int>(i+l);
3451 }
3452 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=true i=%d\n",
3453 // qPrint(type),pos,qPrint(name),qPrint(templSpec),i);
3454 AUTO_TRACE_EXIT("pos={} templSpec='{}' return={}",pos,templSpec,i);
3455 return static_cast<int>(i);
3456 }
3457 }
3458 pos = static_cast<int>(typeLen);
3459 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=false\n",
3460 // qPrint(type),pos,qPrint(name),qPrint(templSpec));
3461 AUTO_TRACE_EXIT("not found");
3462 return -1;
3463}
3464
3466 const DString &name,
3467 const Definition *context,
3468 const ArgumentList &formalArgs)
3469{
3470 // skip until <
3471 size_t p=name.find('<');
3472 if (p==DString::npos) return name;
3473 p++;
3474 DString result = name.left(p);
3475
3476 std::string s = name.mid(p).str();
3477 static const reg::Ex re(R"([\a:][\w:]*)");
3478 reg::Iterator it(s,re);
3480 size_t pi=0;
3481 // for each identifier in the template part (e.g. B<T> -> T)
3482 for (; it!=end ; ++it)
3483 {
3484 const auto &match = *it;
3485 size_t i = match.position();
3486 size_t l = match.length();
3487 result += s.substr(pi,i-pi);
3488 DString n(match.str());
3489 bool found=false;
3490 for (const Argument &formArg : formalArgs)
3491 {
3492 if (formArg.name == n)
3493 {
3494 found=true;
3495 break;
3496 }
3497 }
3498 if (!found)
3499 {
3500 // try to resolve the type
3501 SymbolResolver resolver;
3502 const ClassDef *cd = resolver.resolveClass(context,n);
3503 if (cd)
3504 {
3505 result+=cd->name();
3506 }
3507 else
3508 {
3509 result+=n;
3510 }
3511 }
3512 else
3513 {
3514 result+=n;
3515 }
3516 pi=i+l;
3517 }
3518 result+=s.substr(pi);
3519 //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",qPrint(name),qPrint(result));
3520 return removeRedundantWhiteSpace(result);
3521}
3522
3523
3525 const DString &nm,
3526 const ArgumentList &formalArgs,
3527 const ArgumentList *actualArgs)
3528{
3529 AUTO_TRACE("name={} formalArgs={} actualArgs={}",nm,argListToString(formalArgs),actualArgs ? argListToString(*actualArgs) : DString());
3530 if (formalArgs.empty()) return nm;
3531 DString result;
3532
3533 static const reg::Ex re(R"(\a\w*)");
3534 std::string name = nm.str();
3535 reg::Iterator it(name,re);
3537 size_t p=0;
3538
3539 for (; it!=end ; ++it)
3540 {
3541 const auto &match = *it;
3542 size_t i = match.position();
3543 size_t l = match.length();
3544 if (i>p) result += name.substr(p,i-p);
3545 DString n(match.str());
3547 if (actualArgs)
3548 {
3549 actIt = actualArgs->begin();
3550 }
3551 //printf(": name=%s\n",qPrint(name));
3552
3553 // if n is a template argument, then we substitute it
3554 // for its template instance argument.
3555 bool found=false;
3556 for (auto formIt = formalArgs.begin();
3557 formIt!=formalArgs.end() && !found;
3558 ++formIt
3559 )
3560 {
3561 Argument formArg = *formIt;
3562 Argument actArg;
3563 if (actualArgs && actIt!=actualArgs->end())
3564 {
3565 actArg = *actIt;
3566 }
3567 if (formArg.type.startsWith("class ") && formArg.name.empty())
3568 {
3569 formArg.name = formArg.type.mid(6);
3570 formArg.type = "class";
3571 }
3572 else if (formArg.type.startsWith("typename ") && formArg.name.empty())
3573 {
3574 formArg.name = formArg.type.mid(9);
3575 formArg.type = "typename";
3576 }
3577 else if (formArg.type.startsWith("class...")) // match 'class... name' to 'name...'
3578 {
3579 formArg.name += "...";
3580 formArg.type = formArg.type.left(5)+formArg.type.mid(8);
3581 }
3582 else if (formArg.type.startsWith("typename...")) // match 'typename... name' to 'name...'
3583 {
3584 formArg.name += "...";
3585 formArg.type = formArg.type.left(8)+formArg.type.mid(11);
3586 }
3587 //printf(": n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s' actArg->type='%s' actArg->name='%s' \n",
3588 // qPrint(n),qPrint(formArg.type),qPrint(formArg.name),qPrint(formArg.defval),qPrint(actArg.type),qPrint(actArg.name));
3589 if (formArg.type=="class" || formArg.type=="typename" || formArg.type.startsWith("template"))
3590 {
3591 if (formArg.name==n && actualArgs && actIt!=actualArgs->end() && !actArg.type.empty()) // base class is a template argument
3592 {
3593 static constexpr auto hasRecursion = [](const DString &prefix,const DString &nameArg,const DString &subst) -> bool
3594 {
3595 size_t ii=0;
3596 size_t pp=0;
3597
3598 ii = subst.find('<');
3599 //printf("prefix='%s' subst='%s'\n",qPrint(prefix.mid(prefix.length()-ii-2,ii+1)),qPrint(subst.left(ii+1)));
3600 if (ii!=DString::npos && prefix.length()>=ii+2 && prefix.mid(prefix.length()-ii-2,ii+1)==subst.left(ii+1))
3601 {
3602 return true; // don't replace 'A< ' with 'A< A<...', see issue #10951
3603 }
3604
3605 while ((ii=subst.find(nameArg,pp))!=DString::npos)
3606 {
3607 bool beforeNonWord = ii==0 || !isId(subst.at(ii-1));
3608 bool afterNonWord = subst.length()==ii+nameArg.length() || !isId(subst.at(ii+nameArg.length()));
3609 if (beforeNonWord && afterNonWord)
3610 {
3611 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
3612 }
3613 pp=ii+nameArg.length();
3614 }
3615 return false;
3616 };
3617 // replace formal argument with the actual argument of the instance
3618 AUTO_TRACE_ADD("result={} n={} type={} hasRecursion={}",result,n,actArg.type,hasRecursion(result,n,actArg.type));
3619 if (!hasRecursion(result,n,actArg.type))
3620 // the scope guard is to prevent recursive lockup for
3621 // template<class A> class C : public<A::T>,
3622 // where A::T would become A::T::T here,
3623 // since n==A and actArg->type==A::T
3624 // see bug595833 for an example
3625 //
3626 // Also prevent recursive substitution if n is part of actArg.type, i.e.
3627 // n='A' in argType='S< A >' would produce 'S< S< A > >'
3628 {
3629 if (actArg.name.empty())
3630 {
3631 result += actArg.type;
3632 }
3633 else
3634 // for case where the actual arg is something like "unsigned int"
3635 // the "int" part is in actArg->name.
3636 {
3637 result += actArg.type+" "+actArg.name;
3638 }
3639 found=true;
3640 }
3641 }
3642 else if (formArg.name==n &&
3643 (actualArgs==nullptr || actIt==actualArgs->end()) &&
3644 !formArg.defval.empty() &&
3645 formArg.defval!=nm /* to prevent recursion */
3646 )
3647 {
3648 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
3649 found=true;
3650 }
3651 }
3652 else if (formArg.name==n &&
3653 (actualArgs==nullptr || actIt==actualArgs->end()) &&
3654 !formArg.defval.empty() &&
3655 formArg.defval!=nm /* to prevent recursion */
3656 )
3657 {
3658 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
3659 found=true;
3660 }
3661 if (actualArgs && actIt!=actualArgs->end())
3662 {
3663 actIt++;
3664 }
3665 }
3666 if (!found)
3667 {
3668 result += n;
3669 }
3670 p=i+l;
3671 }
3672 result+=name.substr(p);
3673 result=result.simplifyWhiteSpace();
3674 AUTO_TRACE_EXIT("result={}",result);
3675 return result.stripWhiteSpace();
3676}
3677
3678
3679/*! Strips template specifiers from scope \a fullName, except those
3680 * that make up specialized classes. The switch \a parentOnly
3681 * determines whether or not a template "at the end" of a scope
3682 * should be considered, e.g. with \a parentOnly is \c true, `A<T>::B<S>` will
3683 * try to strip `<T>` and not `<S>`, while \a parentOnly is \c false will
3684 * strip both unless `A<T>` or `B<S>` are specialized template classes.
3685 */
3687 bool parentOnly,
3688 DString *pLastScopeStripped,
3689 DString scopeName,
3690 bool allowArtificial)
3691{
3692 //printf("stripTemplateSpecifiersFromScope(name=%s,scopeName=%s)\n",qPrint(fullName),qPrint(scopeName));
3693 size_t i=fullName.find('<');
3694 if (i==DString::npos) return fullName;
3695 DString result;
3696 size_t p=0;
3697 size_t l=fullName.length();
3698 while (i!=DString::npos)
3699 {
3700 //printf("1:result+=%s\n",qPrint(fullName.mid(p,i-p)));
3701 size_t e=i+1;
3702 int count=1;
3703 int round=0;
3704 while (e<l && count>0)
3705 {
3706 char c=fullName.at(e++);
3707 switch (c)
3708 {
3709 case '(': round++; break;
3710 case ')': if (round>0) round--; break;
3711 case '<': if (round==0) count++; break;
3712 case '>': if (round==0) count--; break;
3713 default:
3714 break;
3715 }
3716 }
3717 size_t si = fullName.find("::",e);
3718
3719 if (parentOnly && si==DString::npos) break;
3720 // we only do the parent scope, so we stop here if needed
3721
3722 result+=fullName.mid(p,i-p);
3723 //printf(" trying %s\n",qPrint(mergeScopes(scopeName,result+fullName.mid(i,e-i))));
3724 ClassDef *cd = getClass(mergeScopes(scopeName,result+fullName.mid(i,e-i)));
3725 if (cd!=nullptr && (allowArtificial || !cd->isArtificial()))
3726 {
3727 result+=fullName.mid(i,e-i);
3728 //printf(" 2:result+=%s\n",qPrint(fullName.mid(i,e-i-1)));
3729 }
3730 else if (pLastScopeStripped)
3731 {
3732 //printf(" last stripped scope '%s'\n",qPrint(fullName.mid(i,e-i)));
3733 *pLastScopeStripped=fullName.mid(i,e-i);
3734 }
3735 p=e;
3736 i=fullName.find('<',p);
3737 }
3738 result+=fullName.right(l-p);
3739 //printf("3:result+=%s\n",qPrint(fullName.right(l-p)));
3740 //printf("end result=%s\n",qPrint(result));
3741 return result;
3742}
3743
3744/*! Merges two scope parts together. The parts may (partially) overlap.
3745 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
3746 * Example2: \c A and \c B will be \c A::B <br>
3747 * Example3: \c A::B and B will be \c A::B
3748 *
3749 * @param leftScope the left hand part of the scope.
3750 * @param rightScope the right hand part of the scope.
3751 * @returns the merged scope.
3752 */
3753DString mergeScopes(const DString &leftScope,const DString &rightScope)
3754{
3755 AUTO_TRACE("leftScope='{}' rightScope='{}'",leftScope,rightScope);
3756 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
3757 if (leftScopeMatch(leftScope,rightScope))
3758 {
3759 AUTO_TRACE_EXIT("case1={}",rightScope);
3760 return rightScope;
3761 }
3762 DString result;
3763 size_t i=0,p=leftScope.length();
3764
3765 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
3766 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
3767 bool found=false;
3768 while ((i=leftScope.rfind("::",p))!=DString::npos && i>0)
3769 {
3770 if (leftScopeMatch(rightScope,leftScope.mid(i+2)))
3771 {
3772 result = leftScope.left(i+2)+rightScope;
3773 found=true;
3774 }
3775 p=i-1;
3776 }
3777 if (found)
3778 {
3779 AUTO_TRACE_EXIT("case2={}",result);
3780 return result;
3781 }
3782
3783 // case leftScope=="A" rightScope=="B" => result = "A::B"
3784 result=leftScope;
3785 if (!result.empty() && !rightScope.empty()) result+="::";
3786 result+=rightScope;
3787 AUTO_TRACE_EXIT("case3={}",result);
3788 return result;
3789}
3790
3791/*! Returns a fragment from scope \a s, starting at position \a p.
3792 *
3793 * @param s the scope name as a string.
3794 * @param p the start position (0 is the first).
3795 * @param l the resulting length of the fragment.
3796 * @returns the location of the fragment, or -1 if non is found.
3797 */
3798int getScopeFragment(const DString &s,int p,int *l)
3799{
3800 int sl=static_cast<int>(s.length());
3801 int sp=p;
3802 int count=0;
3803 bool done=false;
3804 if (sp>=sl) return -1;
3805 while (sp<sl)
3806 {
3807 char c=s.at(sp);
3808 if (c==':')
3809 {
3810 sp++;
3811 p++;
3812 }
3813 else
3814 {
3815 break;
3816 }
3817 }
3818 while (sp<sl)
3819 {
3820 char c=s.at(sp);
3821 switch (c)
3822 {
3823 case ':': // found next part
3824 goto found;
3825 case '<': // skip template specifier
3826 count=1;sp++;
3827 done=false;
3828 while (sp<sl && !done)
3829 {
3830 // TODO: deal with << and >> operators!
3831 c=s.at(sp++);
3832 switch(c)
3833 {
3834 case '<': count++; break;
3835 case '>': count--; if (count==0) done=true; break;
3836 default: break;
3837 }
3838 }
3839 break;
3840 default:
3841 sp++;
3842 break;
3843 }
3844 }
3845found:
3846 *l=sp-p;
3847 //printf("getScopeFragment(%s,%d)=%s\n",qPrint(s),p,qPrint(s.mid(p,*l)));
3848 return p;
3849}
3850
3851//----------------------------------------------------------------------------
3852
3854{
3855 ModuleDef *mod = nullptr;
3857 {
3858 const FileDef *fd = toFileDef(d);
3859 if (fd) mod = fd->getModuleDef();
3860 }
3862 {
3863 const ClassDef *cd = toClassDef(d);
3864 if (cd)
3865 {
3866 const FileDef *fd = cd->getFileDef();
3867 if (fd) mod = fd->getModuleDef();
3868 }
3869 }
3871 {
3872 const ConceptDef *cd = toConceptDef(d);
3873 if (cd)
3874 {
3875 const FileDef *fd = cd->getFileDef();
3876 if (fd) mod = fd->getModuleDef();
3877 }
3878 }
3879 return mod;
3880}
3881
3882static bool recursivelyAddGroupListToTitle(OutputList &ol,const Definition *d,bool root)
3883{
3884 ModuleDef *mod = root ? findModuleDef(d) : nullptr;
3885 if (!d->partOfGroups().empty() || mod!=nullptr) // write list of group to which this definition belongs
3886 {
3887 if (root)
3888 {
3889 ol.pushGeneratorState();
3891 ol.writeString("<div class=\"ingroups\">");
3892 }
3893 bool first=true;
3894 for (const auto &gd : d->partOfGroups())
3895 {
3896 if (!first) { ol.writeString(" &#124; "); } else first=false;
3897 if (recursivelyAddGroupListToTitle(ol, gd, false))
3898 {
3899 ol.writeString(" &raquo; ");
3900 }
3901 ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),DString(),gd->groupTitle());
3902 }
3903 if (root)
3904 {
3905 // add module as a group to the file as well
3906 if (mod)
3907 {
3908 if (!first) { ol.writeString(" &#124; "); } else first=false;
3909 ol.writeString(theTranslator->trModule(false,true)+" ");
3911 mod->displayName());
3912 }
3913 ol.writeString("</div>");
3914 ol.popGeneratorState();
3915 }
3916 return true;
3917 }
3918 return false;
3919}
3920
3922{
3924}
3925
3926bool checkExtension(const DString &fName, const DString &ext)
3927{
3928 return fName.right(ext.length())==ext;
3929}
3930
3932{
3933 if (fName.empty()) return;
3934 size_t i_fs = fName.rfind('/');
3935 size_t i_bs = fName.rfind('\\');
3936 size_t p = i_fs!=DString::npos && i_bs!=DString::npos ? std::max(i_fs, i_bs) :
3937 i_fs!=DString::npos ? i_fs : i_bs!=DString::npos ? i_bs : 0;
3938 size_t i = fName.find('.',p); // search for . after path part
3939 if (i==DString::npos)
3940 {
3942 }
3943}
3944
3946{
3947 DString result=fName;
3948 if (result.right(ext.length())==ext)
3949 {
3950 result=result.left(result.length()-ext.length());
3951 }
3952 return result;
3953}
3954
3959
3961{
3962 DString result=s;
3963 if (size_t i=result.rfind('/'); i!=DString::npos)
3964 {
3965 result=result.mid(i+1);
3966 }
3967 if (size_t i=result.rfind('\\'); i!=DString::npos)
3968 {
3969 result=result.mid(i+1);
3970 }
3971 return result;
3972}
3973
3974DString makeBaseName(const DString &name, const DString &ext)
3975{
3976 return stripExtensionGeneral(stripPath(name), ext);
3977}
3978
3979/** Special version of DString::stripWhiteSpace() that only strips
3980 * completely blank lines.
3981 * @param s the string to be stripped
3982 * @param docLine the line number corresponding to the start of the
3983 * string. This will be adjusted based on the number of lines stripped
3984 * from the start.
3985 * @returns The stripped string.
3986 */
3988{
3989 if (s.empty()) return DString();
3990 const char *p = s.data();
3991
3992 // search for leading empty lines
3993 int i=0,li=-1,l=static_cast<int>(s.length());
3994 char c = 0;
3995 while ((c=*p))
3996 {
3997 if (c==' ' || c=='\t' || c=='\r') { i++; p++; }
3998 else if (c=='\\' && literal_at(p,"\\ilinebr")) { i+=8; li=i; p+=8; }
3999 else if (c=='\n') { i++; li=i; docLine++; p++; }
4000 else break;
4001 }
4002
4003 // search for trailing empty lines
4004 int b=l-1,bi=-1;
4005 p=s.data()+b;
4006 while (b>=0)
4007 {
4008 c=*p;
4009 if (c==' ' || c=='\t' || c=='\r') { b--; p--; }
4010 else if (c=='r' && b>=7 && literal_at(p-7,"\\ilinebr")) { bi=b-7; b-=8; p-=8; }
4011 else if (c=='>' && b>=11 && literal_at(p-11,"\\ilinebr<br>")) { bi=b-11; b-=12; p-=12; }
4012 else if (c=='\n') { bi=b; b--; p--; }
4013 else break;
4014 }
4015
4016 // return whole string if no leading or trailing lines where found
4017 if (li==-1 && bi==-1) return s;
4018
4019 // return substring
4020 if (bi==-1) bi=l;
4021 if (li==-1) li=0;
4022 if (bi<=li) return DString(); // only empty lines
4023 //printf("docLine='%s' len=%d li=%d bi=%d\n",qPrint(s),s.length(),li,bi);
4024 return s.mid(li,bi-li);
4025}
4026
4027//--------------------------------------------------------------------------
4028
4029static std::unordered_map<std::string,SrcLangExt> g_extLookup;
4030
4032{
4033 const char *langName;
4034 const char *parserName;
4036 const char *defExt;
4037};
4038
4039static std::vector<Lang2ExtMap> g_lang2extMap =
4040{
4041// language parser parser option
4042 { "idl", "c", SrcLangExt::IDL, ".idl" },
4043 { "java", "c", SrcLangExt::Java, ".java"},
4044 { "javascript", "c", SrcLangExt::JS, ".js" },
4045 { "csharp", "c", SrcLangExt::CSharp, ".cs" },
4046 { "d", "c", SrcLangExt::D, ".d" },
4047 { "php", "c", SrcLangExt::PHP, ".php" },
4048 { "objective-c", "c", SrcLangExt::ObjC, ".m" },
4049 { "c", "c", SrcLangExt::Cpp, ".c" },
4050 { "c++", "c", SrcLangExt::Cpp, ".cpp" },
4051 { "slice", "c", SrcLangExt::Slice, ".ice" },
4052 { "python", "python", SrcLangExt::Python, ".py" },
4053 { "fortran", "fortran", SrcLangExt::Fortran, ".f" },
4054 { "fortranfree", "fortranfree", SrcLangExt::Fortran, ".f90" },
4055 { "fortranfixed", "fortranfixed", SrcLangExt::Fortran, ".f" },
4056 { "vhdl", "vhdl", SrcLangExt::VHDL, ".vhdl"},
4057 { "xml", "xml", SrcLangExt::XML, ".xml" },
4058 { "sql", "sql", SrcLangExt::SQL, ".sql" },
4059 { "md", "md", SrcLangExt::Markdown, ".md" },
4060 { "lex", "lex", SrcLangExt::Lex, ".l" },
4061};
4062
4063bool updateLanguageMapping(const DString &extension,const DString &language)
4064{
4065 DString langName = language.lower();
4066 auto it1 = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
4067 [&langName](const auto &info) { return info.langName==langName; });
4068 if (it1 == g_lang2extMap.end()) return false;
4069
4070 // found the language
4071 SrcLangExt parserId = it1->parserId;
4072 DString extName = extension.lower();
4073 if (extName.empty()) return false;
4074 if (extName.at(0)!='.') extName.prepend(".");
4075 auto it2 = g_extLookup.find(extName.str());
4076 if (it2!=g_extLookup.end())
4077 {
4078 g_extLookup.erase(it2); // language was already register for this ext
4079 }
4080 //printf("registering extension %s\n",qPrint(extName));
4081 g_extLookup.emplace(extName.str(),parserId);
4082 if (!Doxygen::parserManager->registerExtension(extName,it1->parserName))
4083 {
4084 err("Failed to assign extension {} to parser {} for language {}\n",
4085 extName.data(),it1->parserName,language);
4086 }
4087 else
4088 {
4089 //msg("Registered extension {} to language parser {}...\n",
4090 // extName,language);
4091 }
4092 return true;
4093}
4094
4096{
4097 // NOTE: when adding an extension, also add the extension in config.xml
4098 // extension parser id
4099 updateLanguageMapping(".dox", "c");
4100 updateLanguageMapping(".txt", "c"); // see bug 760836
4101 updateLanguageMapping(".doc", "c");
4102 updateLanguageMapping(".c", "c");
4103 updateLanguageMapping(".C", "c");
4104 updateLanguageMapping(".cc", "c");
4105 updateLanguageMapping(".CC", "c");
4106 updateLanguageMapping(".cxx", "c");
4107 updateLanguageMapping(".cpp", "c");
4108 updateLanguageMapping(".c++", "c");
4109 updateLanguageMapping(".cxxm", "c"); // C++20 modules
4110 updateLanguageMapping(".cppm", "c"); // C++20 modules
4111 updateLanguageMapping(".ccm", "c"); // C++20 modules
4112 updateLanguageMapping(".c++m", "c"); // C++20 modules
4113 updateLanguageMapping(".ii", "c");
4114 updateLanguageMapping(".ixx", "c");
4115 updateLanguageMapping(".ipp", "c");
4116 updateLanguageMapping(".i++", "c");
4117 updateLanguageMapping(".inl", "c");
4118 updateLanguageMapping(".h", "c");
4119 updateLanguageMapping(".H", "c");
4120 updateLanguageMapping(".hh", "c");
4121 updateLanguageMapping(".HH", "c");
4122 updateLanguageMapping(".hxx", "c");
4123 updateLanguageMapping(".hpp", "c");
4124 updateLanguageMapping(".h++", "c");
4125 updateLanguageMapping(".idl", "idl");
4126 updateLanguageMapping(".ddl", "idl");
4127 updateLanguageMapping(".odl", "idl");
4128 updateLanguageMapping(".java", "java");
4129 //updateLanguageMapping(".as", "javascript"); // not officially supported
4130 //updateLanguageMapping(".js", "javascript"); // not officially supported
4131 updateLanguageMapping(".cs", "csharp");
4132 updateLanguageMapping(".d", "d");
4133 updateLanguageMapping(".php", "php");
4134 updateLanguageMapping(".php4", "php");
4135 updateLanguageMapping(".php5", "php");
4136 updateLanguageMapping(".inc", "php");
4137 updateLanguageMapping(".phtml", "php");
4138 updateLanguageMapping(".m", "objective-c");
4139 updateLanguageMapping(".M", "objective-c");
4140 updateLanguageMapping(".mm", "c"); // see bug746361
4141 updateLanguageMapping(".py", "python");
4142 updateLanguageMapping(".pyw", "python");
4143 updateLanguageMapping(".f", "fortran");
4144 updateLanguageMapping(".for", "fortran");
4145 updateLanguageMapping(".f90", "fortran");
4146 updateLanguageMapping(".f95", "fortran");
4147 updateLanguageMapping(".f03", "fortran");
4148 updateLanguageMapping(".f08", "fortran");
4149 updateLanguageMapping(".f18", "fortran");
4150 updateLanguageMapping(".vhd", "vhdl");
4151 updateLanguageMapping(".vhdl", "vhdl");
4152 updateLanguageMapping(".ucf", "vhdl");
4153 updateLanguageMapping(".qsf", "vhdl");
4154 updateLanguageMapping(".md", "md");
4155 updateLanguageMapping(".markdown", "md");
4156 updateLanguageMapping(".ice", "slice");
4157 updateLanguageMapping(".l", "lex");
4158 updateLanguageMapping(".doxygen_lex_c", "c"); // this is a placeholder so we can map initializations
4159 // in the lex scanning to cpp
4160}
4161
4163{
4164 updateLanguageMapping(".xml", "xml");
4165 updateLanguageMapping(".sql", "sql");
4166}
4167
4169{
4170 FileInfo fi(fileName.str());
4171 // we need only the part after the last ".", newer implementations of FileInfo have 'suffix()' for this.
4172 DString extName = DString(fi.extension(false)).lower();
4173 if (extName.empty()) extName=".no_extension";
4174 if (extName.at(0)!='.') extName.prepend(".");
4175 auto it = g_extLookup.find(extName.str());
4176 if (it!=g_extLookup.end()) // listed extension
4177 {
4178 //printf("getLanguageFromFileName(%s)=%x\n",qPrint(fi.extension()),*pVal);
4179 return it->second;
4180 }
4181 //printf("getLanguageFromFileName(%s) not found!\n",qPrint(fileName));
4182 return defLang; // not listed => assume C-ish language.
4183}
4184
4185/// Routine to handle the language attribute of the `\code` command
4187{
4188 // try the extension
4189 auto lang = getLanguageFromFileName(fileName, SrcLangExt::Unknown);
4190 if (lang == SrcLangExt::Unknown)
4191 {
4192 // try the language names
4193 DString langName = fileName.lower();
4194 if (langName.at(0)=='.') langName = langName.mid(1);
4195 auto it = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
4196 [&langName](const auto &info) { return info.langName==langName; });
4197 if (it != g_lang2extMap.end())
4198 {
4199 lang = it->parserId;
4200 fileName = it->defExt;
4201 }
4202 else // default to C++
4203 {
4204 return SrcLangExt::Cpp;
4205 }
4206 }
4207 return lang;
4208}
4209
4211{
4212 if (fn.empty()) return "";
4213 if (size_t lastDot = fn.rfind('.'); lastDot!=DString::npos) return fn.mid(lastDot);
4214 return "";
4215}
4216
4217//--------------------------------------------------------------------------
4218
4219static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
4220 const DString &n)
4221{
4222 if (scope==nullptr ||
4225 )
4226 )
4227 {
4229 }
4230
4231 DString name = n;
4232 if (name.empty())
4233 return nullptr; // no name was given
4234
4235 auto &range = Doxygen::symbolMap->find(name);
4236 if (range.empty())
4237 return nullptr; // could not find any matching symbols
4238
4239 // mostly copied from getResolvedClassRec()
4240 DString explicitScopePart;
4241 int qualifierIndex = computeQualifiedIndex(name);
4242 if (qualifierIndex!=-1)
4243 {
4244 explicitScopePart = name.left(qualifierIndex);
4245 replaceNamespaceAliases(explicitScopePart);
4246 name = name.mid(qualifierIndex+2);
4247 }
4248 //printf("explicitScopePart=%s\n",qPrint(explicitScopePart));
4249
4250 int minDistance = 10000;
4251 MemberDef *bestMatch = nullptr;
4252
4253 for (Definition *d : range)
4254 {
4255 if (d->definitionType()==Definition::TypeMember)
4256 {
4257 SymbolResolver resolver(fileScope);
4258 int distance = resolver.isAccessibleFromWithExpScope(scope,d,explicitScopePart);
4259 if (distance!=-1 && distance<minDistance)
4260 {
4261 minDistance = distance;
4262 bestMatch = toMemberDef(d);
4263 //printf("new best match %s distance=%d\n",qPrint(bestMatch->qualifiedName()),distance);
4264 }
4265 }
4266 }
4267 return bestMatch;
4268}
4269
4270/*! Returns true iff the given name string appears to be a typedef in scope. */
4271bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const DString &n)
4272{
4273 MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
4274
4275 if (bestMatch && bestMatch->isTypedef())
4276 return true; // closest matching symbol is a typedef
4277 else
4278 return false;
4279}
4280
4281//--------------------------------------------------------------------------------------
4282
4284{
4285 if (al.empty()) return;
4287 for (const Argument &a : al)
4288 {
4290 ol.parseText(a.name);
4291 ol.endConstraintParam();
4293 linkifyText(TextGeneratorOLImpl(ol),a.type,LinkifyTextOptions().setScope(d));
4294 ol.endConstraintType();
4296 ol.generateDoc(d->docFile(),
4297 d->docLine(),
4298 d,
4299 nullptr,
4300 a.docs,
4301 DocOptions()
4302 .setIndexWords(true));
4303 ol.endConstraintDocs();
4304 }
4305 ol.endConstraintList();
4306}
4307
4308//----------------------------------------------------------------------------
4309
4311{
4312#ifdef TRACINGSUPPORT
4313 void *backtraceFrames[128];
4314 int frameCount = backtrace(backtraceFrames, 128);
4315 const size_t cmdLen = 40960;
4316 static char cmd[cmdLen];
4317 char *p = cmd;
4318 p += snprintf(p,cmdLen,"/usr/bin/atos -p %d ", (int)getpid());
4319 for (int x = 0; x < frameCount; x++)
4320 {
4321 p += snprintf(p,cmdLen,"%p ", backtraceFrames[x]);
4322 }
4323 fprintf(stderr,"========== STACKTRACE START ==============\n");
4324 if (FILE *fp = Portable::popen(cmd, "r"))
4325 {
4326 char resBuf[512];
4327 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
4328 {
4329 fwrite(resBuf, 1, len, stderr);
4330 }
4331 Portable::pclose(fp);
4332 }
4333 fprintf(stderr,"============ STACKTRACE END ==============\n");
4334 //fprintf(stderr,"%s\n", frameStrings[x]);
4335#endif
4336}
4337
4338static void transcodeCharacterBuffer(const DString &fileName,std::string &contents,
4339 const DString &inputEncoding,const DString &outputEncoding)
4340{
4341 if (inputEncoding.empty() || outputEncoding.empty()) return; // no encoding specified
4342 if (dstricmp(inputEncoding,outputEncoding)==0) return; // input encoding same as output encoding
4343 void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data());
4344 if (cd==reinterpret_cast<void *>(-1))
4345 {
4346 term("unsupported character conversion: '{}'->'{}': {}\n"
4347 "Check the INPUT_ENCODING setting in the config file!\n",
4348 inputEncoding,outputEncoding,strerror(errno));
4349 }
4350 size_t iLeft = contents.size();
4351 const char *srcPtr = contents.data();
4352 size_t tmpBufSize = contents.size()*4+1;
4353 size_t oLeft = tmpBufSize;
4354 std::string tmpBuf;
4355 tmpBuf.resize(tmpBufSize);
4356 char *dstPtr = tmpBuf.data();
4357 size_t newSize=0;
4358 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
4359 {
4360 newSize = tmpBufSize-oLeft;
4361 tmpBuf.resize(newSize);
4362 std::swap(contents,tmpBuf);
4363 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,qPrint(srcBuf));
4364 }
4365 else
4366 {
4367 term("{}: failed to translate characters from {} to {}: check INPUT_ENCODING\n",
4368 fileName,inputEncoding,outputEncoding);
4369 }
4371}
4372
4373//! read a file name \a fileName and optionally filter and transcode it
4374bool readInputFile(const DString &fileName,std::string &contents,bool filter,bool isSourceCode)
4375{
4376 // try to open file
4377 FileInfo fi(fileName.str());
4378 if (!fi.exists()) return false;
4379 DString filterName = getFileFilter(fileName,isSourceCode);
4380 if (filterName.empty() || !filter)
4381 {
4382 std::ifstream f = Portable::openInputStream(fileName,true);
4383 if (!f.is_open())
4384 {
4385 err("could not open file {}\n",fileName);
4386 return false;
4387 }
4388 // read the file
4389 auto fileSize = fi.size();
4390 contents.resize(fileSize);
4391 f.read(contents.data(),fileSize);
4392 if (f.fail())
4393 {
4394 err("problems while reading file {}\n",fileName);
4395 return false;
4396 }
4397 }
4398 else
4399 {
4400 DString cmd=filterName+" \""+fileName+"\"";
4401 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
4402 FILE *f=Portable::popen(cmd,"r");
4403 if (!f)
4404 {
4405 err("could not execute filter {}\n",filterName);
4406 return false;
4407 }
4408 const int bufSize=4096;
4409 char buf[bufSize];
4410 int numRead = 0;
4411 while ((numRead=static_cast<int>(fread(buf,1,bufSize,f)))>0)
4412 {
4413 //printf(">>>>>>>>Reading %d bytes\n",numRead);
4414 contents.append(buf,numRead);
4415 }
4417 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
4418 Debug::print(Debug::FilterOutput,0,"-------------\n{}\n-------------\n",contents);
4419 }
4420
4421 if (contents.size()>=2 &&
4422 static_cast<uint8_t>(contents[0])==0xFF &&
4423 static_cast<uint8_t>(contents[1])==0xFE // Little endian BOM
4424 ) // UCS-2LE encoded file
4425 {
4426 transcodeCharacterBuffer(fileName,contents,"UCS-2LE","UTF-8");
4427 }
4428 else if (contents.size()>=2 &&
4429 static_cast<uint8_t>(contents[0])==0xFE &&
4430 static_cast<uint8_t>(contents[1])==0xFF // big endian BOM
4431 ) // UCS-2BE encoded file
4432 {
4433 transcodeCharacterBuffer(fileName,contents,"UCS-2BE","UTF-8");
4434 }
4435 else if (contents.size()>=3 &&
4436 static_cast<uint8_t>(contents[0])==0xEF &&
4437 static_cast<uint8_t>(contents[1])==0xBB &&
4438 static_cast<uint8_t>(contents[2])==0xBF
4439 ) // UTF-8 encoded file
4440 {
4441 contents.erase(0,3); // remove UTF-8 BOM: no translation needed
4442 }
4443 else // transcode according to the INPUT_ENCODING setting
4444 {
4445 // do character transcoding if needed.
4446 transcodeCharacterBuffer(fileName,contents,getEncoding(fi),"UTF-8");
4447 }
4448
4449 filterCRLF(contents);
4450 return true;
4451}
4452
4453// Replace %word by word in title
4455{
4456 std::string tf;
4457 std::string t = title.str();
4458 static const reg::Ex re(R"(%[a-z_A-Z]+)");
4459 reg::Iterator it(t,re);
4461 size_t p = 0;
4462 for (; it!=end ; ++it)
4463 {
4464 const auto &match = *it;
4465 size_t i = match.position();
4466 size_t l = match.length();
4467 if (i>p) tf+=t.substr(p,i-p);
4468 tf+=match.str().substr(1); // skip %
4469 p=i+l;
4470 }
4471 tf+=t.substr(p);
4472 return tf;
4473}
4474
4475//----------------------------------------------------------------------------
4476
4478{
4479 InputFileEncoding elem;
4480 auto getter = [](const InputFileEncoding &e) { return e.pattern.str(); };
4481 if (fi.match(Doxygen::inputFileEncodingList,useCaseSenseNames(),&elem,getter)) // check for file specific encoding
4482 {
4483 return elem.encoding;
4484 }
4485 else // fall back to default encoding
4486 {
4487 return Config_getString(INPUT_ENCODING);
4488 }
4489}
4490
4492{
4493 bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW);
4494 if (extLinksInWindow)
4495 return "target=\"_blank\" ";
4496 else if (parent)
4497 return "target=\"_parent\" ";
4498 else
4499 return "";
4500}
4501
4503 const DString &ref,
4504 bool isLocalFile,
4505 const DString &targetFileName,
4506 const DString &anchor)
4507{
4508 DString url;
4509 if (!ref.empty())
4510 {
4511 url = externalRef(relPath,ref);
4512 }
4513 if (!targetFileName.empty())
4514 {
4515 DString fn = targetFileName;
4516 if (ref.empty())
4517 {
4518 if (!anchor.empty() && isLocalFile)
4519 {
4520 fn=""; // omit file name for local links
4521 }
4522 else
4523 {
4524 url = relPath;
4525 }
4526 }
4527 url+=fn;
4528 }
4529 if (!anchor.empty())
4530 {
4531 if (!url.endsWith("=")) url+="#";
4532 url+=anchor;
4533 }
4534 //printf("createHtmlUrl(relPath=%s,local=%d,target=%s,anchor=%s)=%s\n",qPrint(relPath),isLocalFile,qPrint(targetFileName),qPrint(anchor),qPrint(url));
4535 return url;
4536}
4537
4538DString externalRef(const DString &relPath,const DString &ref)
4539{
4540 DString result;
4541 if (!ref.empty())
4542 {
4543 auto it = Doxygen::tagDestinationMap.find(ref.str());
4545 {
4546 result = it->second;
4547 size_t l = result.length();
4548 if (!relPath.empty() && l>0 && result.at(0)=='.')
4549 { // relative path -> prepend relPath.
4550 result.prepend(relPath);
4551 l+=relPath.length();
4552 }
4553 if (l>0 && result.at(l-1)!='/') result+='/';
4554 }
4555 }
4556 else
4557 {
4558 result = relPath;
4559 }
4560 return result;
4561}
4562
4563/** Replaces any markers of the form \#\#AA in input string \a str
4564 * by new markers of the form \#AABBCC, where \#AABBCC represents a
4565 * valid color, based on the intensity represented by hex number AA
4566 * and the current HTML_COLORSTYLE_* settings.
4567 */
4569{
4570 if (str.empty()) return DString();
4571 std::string result;
4572 std::string s=str.str();
4573 static const reg::Ex re(R"(##[0-9A-Fa-f][0-9A-Fa-f])");
4574 reg::Iterator it(s,re);
4576 int hue = Config_getInt(HTML_COLORSTYLE_HUE);
4577 int sat = Config_getInt(HTML_COLORSTYLE_SAT);
4578 int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
4579 size_t sl=s.length();
4580 size_t p=0;
4581 for (; it!=end ; ++it)
4582 {
4583 const auto &match = *it;
4584 size_t i = match.position();
4585 size_t l = match.length();
4586 if (i>p) result+=s.substr(p,i-p);
4587 std::string lumStr = match.str().substr(2);
4588#define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
4589 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
4590 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
4591
4592 double r = 0,g = 0,b = 0;
4593 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
4594 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
4595 pow(level/255.0,gamma/100.0),&r,&g,&b);
4596 int red = static_cast<int>(r*255.0);
4597 int green = static_cast<int>(g*255.0);
4598 int blue = static_cast<int>(b*255.0);
4599 char colStr[8];
4600 colStr[0]='#';
4601 colStr[1]=hex[red>>4];
4602 colStr[2]=hex[red&0xf];
4603 colStr[3]=hex[green>>4];
4604 colStr[4]=hex[green&0xf];
4605 colStr[5]=hex[blue>>4];
4606 colStr[6]=hex[blue&0xf];
4607 colStr[7]=0;
4608 //printf("replacing %s->%s (level=%d)\n",qPrint(lumStr),colStr,level);
4609 result+=colStr;
4610 p=i+l;
4611 }
4612 if (p<sl) result+=s.substr(p);
4613 return result;
4614}
4615
4616/** Copies the contents of file with name \a src to the newly created
4617 * file with name \a dest. Returns true if successful.
4618 */
4619bool copyFile(const DString &src,const DString &dest)
4620{
4621 if (!Dir().copy(src.str(),dest.str()))
4622 {
4623 err("could not copy file {} to {}\n",src,dest);
4624 return false;
4625 }
4626 return true;
4627}
4628
4630{
4631 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp || lang==SrcLangExt::VHDL || lang==SrcLangExt::Python)
4632 {
4633 return ".";
4634 }
4635 else if (lang==SrcLangExt::PHP && !classScope)
4636 {
4637 return "\\";
4638 }
4639 else
4640 {
4641 return "::";
4642 }
4643}
4644
4645/** Checks whether the given url starts with a supported protocol */
4646bool isURL(const DString &url)
4647{
4648 static const std::unordered_set<std::string> schemes = {
4649 "http", "https", "ftp", "ftps", "sftp", "file", "news", "irc", "ircs"
4650 };
4651 DString loc_url = url.stripWhiteSpace();
4652 size_t colonPos = loc_url.find(':');
4653 return colonPos!=DString::npos && schemes.find(loc_url.left(colonPos).str())!=schemes.end();
4654}
4655
4656/** Corrects URL \a url according to the relative path \a relPath.
4657 * Returns the corrected URL. For absolute URLs no correction will be done.
4658 */
4659DString correctURL(const DString &url,const DString &relPath)
4660{
4661 DString result = url;
4662 if (!relPath.empty() && !isURL(url))
4663 {
4664 result.prepend(relPath);
4665 }
4666 return result;
4667}
4668
4669//---------------------------------------------------------------------------
4670
4672{
4673 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
4674 bool extractPackage = Config_getBool(EXTRACT_PACKAGE);
4675
4676 return (prot!=Protection::Private && prot!=Protection::Package) ||
4677 (prot==Protection::Private && extractPrivate) ||
4678 (prot==Protection::Package && extractPackage);
4679}
4680
4681//---------------------------------------------------------------------------
4682
4683DString stripIndentation(const DString &s,bool skipFirstLine)
4684{
4685 if (s.empty()) return s; // empty string -> we're done
4686
4687 //printf("stripIndentation:\n%s\n------\n",qPrint(s));
4688 // compute minimum indentation over all lines
4689 const char *p=s.data();
4690 char c=0;
4691 int indent=0;
4692 int minIndent=1000000; // "infinite"
4693 bool searchIndent=true;
4694 int tabSize=Config_getInt(TAB_SIZE);
4695 bool skipFirst = skipFirstLine;
4696 while ((c=*p++))
4697 {
4698 if (c=='\t') { indent+=tabSize - (indent%tabSize); }
4699 else if (c=='\n') { indent=0; searchIndent=true; skipFirst=false; }
4700 else if (c==' ') { indent++; }
4701 else if (searchIndent && !skipFirst)
4702 {
4703 searchIndent=false;
4704 if (indent<minIndent) minIndent=indent;
4705 }
4706 }
4707
4708 // no indent to remove -> we're done
4709 if (minIndent==0) return substitute(s,"@ilinebr","\\ilinebr");
4710
4711 // remove minimum indentation for each line
4712 TextStream result;
4713 p=s.data();
4714 indent=0;
4715 skipFirst=skipFirstLine;
4716 while ((c=*p++))
4717 {
4718 if (c=='\n') // start of new line
4719 {
4720 indent=0;
4721 result << c;
4722 skipFirst=false;
4723 }
4724 else if (indent<minIndent && !skipFirst) // skip until we reach minIndent
4725 {
4726 if (c=='\t')
4727 {
4728 int newIndent = indent+tabSize-(indent%tabSize);
4729 int i=newIndent;
4730 while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
4731 {
4732 result << ' ';
4733 i--;
4734 }
4735 indent=newIndent;
4736 }
4737 else // space
4738 {
4739 indent++;
4740 }
4741 }
4742 else if (c=='\\' && literal_at(p,"ilinebr "))
4743 // we also need to remove the indentation after a \ilinebr command at the end of a line
4744 {
4745 result << "\\ilinebr ";
4746 p+=8;
4747 int skipAmount=0;
4748 for (int j=0;j<minIndent;j++) if (*(p+j)==' ') skipAmount++; // test to see if we have the indent
4749 if (skipAmount==minIndent)
4750 {
4751 p+=skipAmount; // remove the indent
4752 }
4753 }
4754 else if (c=='@' && literal_at(p,"ilinebr"))
4755 {
4756 result << "\\ilinebr";
4757 p+=7;
4758 }
4759 else // copy anything until the end of the line
4760 {
4761 result << c;
4762 }
4763 }
4764
4765 //printf("stripIndentation: result=\n%s\n------\n",qPrint(result.str()));
4766
4767 return result.str();
4768}
4769
4770// strip up to \a indentationLevel spaces from each line in \a doc (excluding the first line
4771// when skipFirstLine is set to true)
4772void stripIndentationVerbatim(DString &doc,size_t indentationLevel, bool skipFirstLine)
4773{
4774 //printf("stripIndentationVerbatim(level=%d):\n%s\n------\n",indentationLevel,qPrint(doc));
4775 if (indentationLevel <= 0 || doc.empty()) return; // nothing to strip
4776
4777 // by stripping content the string will only become shorter so we write the results
4778 // back into the input string and then resize it at the end.
4779 char c = 0;
4780 const char *src = doc.data();
4781 char *dst = doc.rawData();
4782 bool insideIndent = !skipFirstLine; // skip the initial line from stripping
4783 size_t cnt = 0;
4784 if (!skipFirstLine) cnt = indentationLevel;
4785 while ((c=*src++))
4786 {
4787 // invariant: dst<=src
4788 switch(c)
4789 {
4790 case '\n':
4791 *dst++ = c;
4792 insideIndent = true;
4793 cnt = indentationLevel;
4794 break;
4795 case ' ':
4796 if (insideIndent)
4797 {
4798 if (cnt>0) // count down the spacing until the end of the indent
4799 {
4800 cnt--;
4801 }
4802 else // reached the end of the indent, start of the part of the line to keep
4803 {
4804 insideIndent = false;
4805 *dst++ = c;
4806 }
4807 }
4808 else // part after indent, copy to the output
4809 {
4810 *dst++ = c;
4811 }
4812 break;
4813 default:
4814 insideIndent = false;
4815 *dst++ = c;
4816 break;
4817 }
4818 }
4819 doc.resize(static_cast<uint32_t>(dst-doc.data()));
4820 //printf("stripIndentationVerbatim: result=\n%s\n------\n",qPrint(doc));
4821}
4822
4823//----------------------------------------------------------------------------
4824
4825/** Strip the direction part from docs and return it as a string in canonical form.
4826 * The input \a docs string can start with e.g. "[in]", "[in, out]", "[inout]", "[out,in]"...
4827 * @returns either "[in,out]", "[in]", or "[out]" or the empty string.
4828 */
4830{
4831 std::string s = docs.str();
4832 static const reg::Ex re(R"(\‍[([ inout,]+)\‍])");
4833 reg::Iterator it(s,re);
4835 if (it!=end)
4836 {
4837 const auto &match = *it;
4838 size_t p = match.position();
4839 size_t l = match.length();
4840 if (p==0 && l>2)
4841 {
4842 // make dir the part inside [...] without separators
4843 std::string dir = match[1].str();
4844 // strip , and ' ' from dir
4845 dir.erase(std::remove_if(dir.begin(),dir.end(),
4846 [](const char c) { return c==' ' || c==','; }
4847 ),dir.end());
4848 unsigned char ioMask=0;
4849 size_t inIndex = dir.find( "in");
4850 if ( inIndex!=std::string::npos) { dir.erase( inIndex,2); ioMask|=(1<<0); }
4851 size_t outIndex = dir.find("out");
4852 if (outIndex!=std::string::npos) { dir.erase(outIndex,3); ioMask|=(1<<1); }
4853 if (dir.empty() && ioMask!=0) // only in and/or out attributes found
4854 {
4855 docs = s.substr(l); // strip attributes
4856 if (ioMask==((1<<0)|(1<<1))) return "[in,out]";
4857 else if (ioMask==(1<<0)) return "[in]";
4858 else if (ioMask==(1<<1)) return "[out]";
4859 }
4860 }
4861 }
4862 return "";
4863}
4864
4866{
4867 DString paramDocs;
4868 if (al.hasDocumentation(true))
4869 {
4870 for (const Argument &a : al)
4871 {
4872 if (a.hasDocumentation(true))
4873 {
4874 DString docsWithoutDir = a.docs;
4875 DString direction = extractDirection(docsWithoutDir);
4876 DString name = a.name;
4877 if (name.empty())
4878 {
4879 name = "-";
4880 }
4881 paramDocs+=" \\ilinebr @param"+direction+" "+name+" "+docsWithoutDir;
4882 }
4883 }
4884 }
4885 return paramDocs;
4886}
4887
4888
4889//-----------------------------------------------------------
4890
4891/** Computes for a given list type \a inListType, which are the
4892 * the corresponding list type(s) in the base class that are to be
4893 * added to this list.
4894 *
4895 * So for public inheritance, the mapping is 1-1, so outListType1=inListType
4896 * Private members are to be hidden completely.
4897 *
4898 * For protected inheritance, both protected and public members of the
4899 * base class should be joined in the protected member section.
4900 *
4901 * For private inheritance, both protected and public members of the
4902 * base class should be joined in the private member section.
4903 */
4905 MemberListType inListType,
4906 Protection inProt,
4907 MemberListType *outListType1,
4908 MemberListType *outListType2
4909 )
4910{
4911 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
4912
4913 // default representing 1-1 mapping
4914 *outListType1=inListType;
4915 *outListType2=MemberListType::Invalid();
4916
4917 if (inProt==Protection::Public)
4918 {
4919 if (inListType.isPrivate())
4920 {
4921 *outListType1=MemberListType::Invalid();
4922 }
4923 }
4924 else if (inProt==Protection::Protected)
4925 {
4926 if (inListType.isPrivate() || inListType.isPublic())
4927 {
4928 *outListType1=MemberListType::Invalid();
4929 }
4930 else if (inListType.isProtected())
4931 {
4932 *outListType2=inListType.toPublic();
4933 }
4934 }
4935 else if (inProt==Protection::Private)
4936 {
4937 if (inListType.isPublic() || inListType.isProtected())
4938 {
4939 *outListType1=MemberListType::Invalid();
4940 }
4941 else if (inListType.isPrivate())
4942 {
4943 if (extractPrivate)
4944 {
4945 *outListType1=inListType.toPublic();
4946 *outListType2=inListType.toProtected();
4947 }
4948 else
4949 {
4950 *outListType1=MemberListType::Invalid();
4951 }
4952 }
4953 }
4954
4955 //printf("convertProtectionLevel(type=%s prot=%d): %s,%s\n",
4956 // qPrint(inListType.to_string()),inProt,qPrint(outListType1->to_string()),qPrint(outListType2->to_string()));
4957}
4958
4960{
4961 return Doxygen::mainPage!=nullptr && Doxygen::mainPage->hasTitle();
4962}
4963
4965{
4966 return getDotImageExtension(Config_getEnumAsString(DOT_IMAGE_FORMAT));
4967}
4969{
4970 size_t i= format.find(':'); // strip renderer part when using e.g. 'png:cairo:gd' as format
4971 return i==DString::npos ? format : format.left(i);
4972}
4973
4975{
4976 StringVector splitExt = split(format.str(),":");
4977 DString extGen;
4978 for (size_t i = splitExt.size(); i > 0; )
4979 {
4980 --i;
4981 extGen += splitExt[i] + (i!=0 ? "." : "");
4982 }
4983 return extGen;
4984}
4985
4986bool openOutputFile(const DString &outFile,std::ofstream &f)
4987{
4988 ASSERT(!f.is_open());
4989 bool fileOpened=false;
4990 bool writeToStdout=outFile=="-";
4991 if (writeToStdout) // write to stdout
4992 {
4993 f.basic_ios<char>::rdbuf(std::cout.rdbuf());
4994 fileOpened = true;
4995 }
4996 else // write to file
4997 {
4998 FileInfo fi(outFile.str());
4999 if (fi.exists()) // create a backup
5000 {
5001 Dir dir;
5002 FileInfo backup(fi.filePath()+".bak");
5003 if (backup.exists()) // remove existing backup
5004 dir.remove(backup.filePath());
5005 dir.rename(fi.filePath(),fi.filePath()+".bak");
5006 }
5007 f = Portable::openOutputStream(outFile);
5008 fileOpened = f.is_open();
5009 }
5010 return fileOpened;
5011}
5012
5013//------------------------------------------------------
5014
5015//! remove disabled blocks and all block markers from \a s and return the result as a string
5016DString selectBlocks(const DString &s,const SelectionBlockList &blockList,const SelectionMarkerInfo &markerInfo)
5017{
5018 if (s.empty()) return s;
5019
5020 // helper to find the end of a block
5021 auto skipBlock = [&markerInfo](const char *p,const SelectionBlock &blk)
5022 {
5023 char c = 0;
5024 while ((c=*p))
5025 {
5026 if (c==markerInfo.markerChar && dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // end marker
5027 {
5028 size_t len = markerInfo.endLen;
5029 bool negate = *(p+markerInfo.endLen)=='!';
5030 if (negate) len++;
5031 size_t blkNameLen = dstrlen(blk.name);
5032 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5033 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5034 {
5035 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5036 return p+len+blkNameLen+markerInfo.closeLen;
5037 }
5038 else // not the right marker id
5039 {
5040 p++;
5041 }
5042 }
5043 else // not and end marker
5044 {
5045 p++;
5046 }
5047 }
5048 return p;
5049 };
5050
5051 DString result;
5052 result.reserve(s.length());
5053 const char *p = s.data();
5054 char c = 0;
5055 while ((c=*p))
5056 {
5057 if (c==markerInfo.markerChar) // potential start of marker
5058 {
5059 if (dstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
5060 {
5061 bool found = false;
5062 size_t len = markerInfo.beginLen;
5063 bool negate = *(p+len)=='!';
5064 if (negate) len++;
5065 for (const auto &blk : blockList)
5066 {
5067 size_t blkNameLen = dstrlen(blk.name);
5068 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5069 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5070 {
5071 bool blockEnabled = blk.enabled!=negate;
5072 //printf("Found start marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5073 p+=len+blkNameLen+markerInfo.closeLen;
5074 if (!blockEnabled) // skip until the end of the block
5075 {
5076 //printf("skipping block\n");
5077 p=skipBlock(p,blk);
5078 }
5079 found=true;
5080 break;
5081 }
5082 }
5083 if (!found) // unknown marker id
5084 {
5085 result+=c;
5086 p++;
5087 }
5088 }
5089 else if (dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
5090 {
5091 bool found = false;
5092 size_t len = markerInfo.endLen;
5093 bool negate = *(p+len)=='!';
5094 if (negate) len++;
5095 for (const auto &blk : blockList)
5096 {
5097 size_t blkNameLen = dstrlen(blk.name);
5098 if (dstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
5099 dstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5100 {
5101 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
5102 p+=len+blkNameLen+markerInfo.closeLen;
5103 found=true;
5104 break;
5105 }
5106 }
5107 if (!found) // unknown marker id
5108 {
5109 result+=c;
5110 p++;
5111 }
5112 }
5113 else // not a start or end marker
5114 {
5115 result+=c;
5116 p++;
5117 }
5118 }
5119 else // not a marker character
5120 {
5121 result+=c;
5122 p++;
5123 }
5124 }
5125 //printf("====\n%s\n-----\n%s\n~~~~\n",qPrint(s),qPrint(result));
5126 return result;
5127}
5128
5129void checkBlocks(const DString &s, const DString fileName,const SelectionMarkerInfo &markerInfo)
5130{
5131 if (s.empty()) return;
5132
5133 const char *p = s.data();
5134 char c = 0;
5135 while ((c=*p))
5136 {
5137 if (c==markerInfo.markerChar) // potential start of marker
5138 {
5139 if (dstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
5140 {
5141 size_t len = markerInfo.beginLen;
5142 bool negate = *(p+len)=='!';
5143 if (negate) len++;
5144 p += len;
5145 DString marker;
5146 while (*p)
5147 {
5148 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
5149 {
5150 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
5151 break;
5152 }
5153 else if (markerInfo.closeLen!= 0 && dstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5154 {
5155 p += markerInfo.closeLen;
5156 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
5157 break;
5158 }
5159 marker += *p;
5160 p++;
5161 }
5162 }
5163 else if (dstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
5164 {
5165 size_t len = markerInfo.endLen;
5166 bool negate = *(p+len)=='!';
5167 if (negate) len++;
5168 p += len;
5169 DString marker;
5170 while (*p)
5171 {
5172 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
5173 {
5174 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
5175 break;
5176 }
5177 else if (markerInfo.closeLen!= 0 && dstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
5178 {
5179 p += markerInfo.closeLen;
5180 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
5181 break;
5182 }
5183 marker += *p;
5184 p++;
5185 }
5186 }
5187 }
5188 p++;
5189 }
5190}
5191
5192
5193
5194DString detab(const DString &s,size_t &refIndent)
5195{
5196 int tabSize = Config_getInt(TAB_SIZE);
5197 size_t size = s.length();
5198 DString result;
5199 result.reserve(size+256);
5200 const char *data = s.data();
5201 size_t i=0;
5202 int col=0;
5203 constexpr auto doxy_nbsp = "&_doxy_nbsp;"; // doxygen escape command for UTF-8 nbsp
5204 const int maxIndent=1000000; // value representing infinity
5205 int minIndent=maxIndent;
5206 bool skip = false;
5207 while (i<size)
5208 {
5209 char c = data[i++];
5210 switch(c)
5211 {
5212 case '\t': // expand tab
5213 {
5214 int stop = tabSize - (col%tabSize);
5215 //printf("expand at %d stop=%d\n",col,stop);
5216 col+=stop;
5217 while (stop--) result+=' ';
5218 }
5219 break;
5220 case '\\':
5221 if (data[i] == '\\') // escaped command -> ignore
5222 {
5223 result+=c;
5224 result+=data[i++];
5225 col+=2;
5226 }
5227 else if (i+5<size && literal_at(data+i,"iskip")) // command
5228 {
5229 i+=5;
5230 skip = true;
5231 }
5232 else if (i+8<size && literal_at(data+i,"endiskip")) // command
5233 {
5234 i+=8;
5235 skip = false;
5236 }
5237 else // some other command
5238 {
5239 result+=c;
5240 col++;
5241 }
5242 break;
5243 case '\n': // reset column counter
5244 result+=c;
5245 col=0;
5246 break;
5247 case ' ': // increment column counter
5248 result+=c;
5249 col++;
5250 break;
5251 default: // non-whitespace => update minIndent
5252 if (static_cast<signed char>(c)<0 && i<size) // multibyte sequence
5253 {
5254 // special handling of the UTF-8 nbsp character 0xC2 0xA0
5255 int nb = isUTF8NonBreakableSpace(data);
5256 if (nb>0)
5257 {
5258 result+=doxy_nbsp;
5259 i+=nb-1;
5260 }
5261 else
5262 {
5263 int bytes = getUTF8CharNumBytes(c);
5264 for (int j=0;j<bytes-1 && c;j++)
5265 {
5266 result+=c;
5267 c = data[i++];
5268 }
5269 result+=c;
5270 }
5271 }
5272 else
5273 {
5274 result+=c;
5275 }
5276 if (!skip && col<minIndent) minIndent=col;
5277 col++;
5278 }
5279 }
5280 if (minIndent!=maxIndent) refIndent=minIndent; else refIndent=0;
5281 //printf("detab(\n%s\n)=[\n%s\n]\n",qPrint(s),qPrint(out.get()));
5282 return result;
5283}
5284
5286{
5287 DString projectCookie = Config_getString(HTML_PROJECT_COOKIE);
5288 if (projectCookie.empty()) return DString();
5289 return md5str(projectCookie.view())+"_";
5290}
5291
5292//! Return the index of the last :: in the string \a name that is still before the first <
5294{
5295 int l = static_cast<int>(name.length());
5296 int lastSepPos = -1;
5297 const char *p = name.data();
5298 int i=l-2;
5299 int sharpCount=0;
5300 // --- begin optimized version of ts=name.findRev(">::");
5301 int ts = -1;
5302 while (i>=0)
5303 {
5304 if (p[i]=='>')
5305 {
5306 if (sharpCount==0 && p[i+1]==':' && p[i+2]==':')
5307 {
5308 ts=i;
5309 break;
5310 }
5311 sharpCount++;
5312 }
5313 else if (p[i]=='<')
5314 {
5315 sharpCount--;
5316 }
5317 i--;
5318 }
5319 // --- end optimized version
5320 if (ts==-1) ts=0; else p+=++ts;
5321 for (i=ts;i<l-1;i++)
5322 {
5323 char c=*p++;
5324 if (c==':' && *p==':') lastSepPos=i;
5325 if (c=='<') break;
5326 }
5327 return lastSepPos;
5328}
5329
5330// in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
5331// A -> A
5332// A<T> -> A-1-g
5333// A<T,S> -> A-2-g
5335{
5336 if (size_t idx = name.find('<'); idx!=DString::npos)
5337 {
5338 return name.left(idx)+"-"+DString().setNum(name.contains(",")+1)+"-g";
5339 }
5340 return name;
5341}
5342
5344{
5345 DString result=name;
5346 if (result.endsWith("-g"))
5347 {
5348 size_t idx = result.find('-');
5349 result = result.left(idx)+templArgs;
5350 }
5351 return result;
5352}
5353
5355{
5356 DString text=rawStart;
5357 size_t i = text.find('"');
5359 return text.mid(i+1,text.length()-i-2); // text=...R"xyz( -> delimiter=xyz
5360}
5361
5363{
5364 DString text=rawEnd;
5365 return text.mid(1,text.length()-2); // text=)xyz" -> delimiter=xyz
5366}
5367
5368//----------------------------------------------------------------------------------------------------------
5369
5370static std::mutex writeFileContents_lock;
5372
5373DString writeInlineGraph(const DString &baseName,const DString &extension,const DString &content,bool &exists)
5374{
5375 DString fileName = baseName + md5str(content.view()) + extension;
5376 { // ==== start atomic section
5377 std::lock_guard lock(writeFileContents_lock);
5378 auto it=writeFileContents_set.find(fileName.str());
5379 exists = it!=writeFileContents_set.end();
5380 if (!exists)
5381 {
5382 writeFileContents_set.insert(fileName.str());
5383 if (auto file = Portable::openOutputStream(fileName); file.is_open())
5384 {
5385 file.write( content.data(), content.length() );
5386 file.close();
5387 }
5388 else
5389 {
5390 err("Could not open file {} for writing\n",fileName);
5391 return DString();
5392 }
5393 }
5394 } // ==== end atomic section
5395 return fileName;
5396}
5397
5399{
5400 if (Config_getBool(DOT_CLEANUP))
5401 {
5402 for (const auto& fileName: writeFileContents_set)
5403 {
5404 Dir().remove(qPrint(fileName));
5405 }
5406 }
5407}
5408
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:2028
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:4035
const char * langName
Definition util.cpp:4033
const char * parserName
Definition util.cpp:4034
const char * defExt
Definition util.cpp:4036
size_t beginLen
Definition util.h:219
const char * closeStr
Definition util.h:222
const char * beginStr
Definition util.h:218
size_t closeLen
Definition util.h:223
const char * endStr
Definition util.h:220
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:3955
bool useCaseSenseNames()
Returns true if the names of the symbols can be case sensitive.
Definition util.cpp:2679
bool isURL(const DString &url)
Checks whether the given url starts with a supported protocol.
Definition util.cpp:4646
DString substituteTemplateArgumentsInString(const DString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:3524
static DString extractDirection(DString &docs)
Strip the direction part from docs and return it as a string in canonical form.
Definition util.cpp:4829
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:5370
DString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:4865
DString stripLeadingAndTrailingEmptyLines(const DString &s, int &docLine)
Special version of DString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:3987
bool mainPageHasTitle()
Definition util.cpp:4959
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:4568
bool protectionLevelVisible(Protection prot)
Definition util.cpp:4671
DString stripFromIncludePath(const DString &path)
Definition util.cpp:227
DString mergeScopes(const DString &leftScope, const DString &rightScope)
Definition util.cpp:3753
bool resolveLink(const DString &scName, const DString &lr, bool, const Definition **resContext, DString &resAnchor, SrcLangExt lang, const DString &prefix)
Definition util.cpp:2308
DString filterTitle(const DString &title)
Definition util.cpp:4454
DString detab(const DString &s, size_t &refIndent)
Definition util.cpp:5194
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:5016
bool matchTemplateArguments(const ArgumentList &srcAl, const ArgumentList &dstAl)
Definition util.cpp:1833
void stripIndentationVerbatim(DString &doc, size_t indentationLevel, bool skipFirstLine)
Definition util.cpp:4772
void addCodeOnlyMappings()
Definition util.cpp:4162
static int g_usedNamesCount
Definition util.cpp:2855
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:4283
DString extractBeginRawStringDelimiter(const char *rawStart)
Definition util.cpp:5354
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:5285
#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:5373
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:4271
static const char constScope[]
Definition util.cpp:379
static bool recursivelyAddGroupListToTitle(OutputList &ol, const Definition *d, bool root)
Definition util.cpp:3882
DString unescapeCharsInString(const DString &s)
Definition util.cpp:2775
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:4491
DString replaceAnonymousScopes(const DString &s, const DString &replacement)
Definition util.cpp:155
void cleanupInlineGraphs()
Definition util.cpp:5398
DString correctURL(const DString &url, const DString &relPath)
Corrects URL url according to the relative path relPath.
Definition util.cpp:4659
int computeQualifiedIndex(const DString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:5293
DString convertToJSString(const DString &s, bool keepEntities, bool singleQuotes)
Definition util.cpp:3351
DString convertToId(const DString &s)
Definition util.cpp:3202
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:3076
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:426
static std::unordered_map< std::string, SrcLangExt > g_extLookup
Definition util.cpp:4029
bool checkExtension(const DString &fName, const DString &ext)
Definition util.cpp:3926
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2863
DString showDate(const DString &fmt)
Definition util.cpp:2567
bool leftScopeMatch(const DString &scope, const DString &name)
Definition util.cpp:740
DString stripExtensionGeneral(const DString &fName, const DString &ext)
Definition util.cpp:3945
DString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:903
static ModuleDef * findModuleDef(const Definition *d)
Definition util.cpp:3853
void addGroupListToTitle(OutputList &ol, const Definition *d)
Definition util.cpp:3921
DString extractEndRawStringDelimiter(const char *rawEnd)
Definition util.cpp:5362
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:2996
DString stripScope(const DString &name)
Definition util.cpp:3109
DString getFileNameExtension(const DString &fn)
Definition util.cpp:4210
void createSubDirs(const Dir &d)
Definition util.cpp:2969
DString convertToHtml(const DString &s, bool keepEntities)
Definition util.cpp:3291
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:4374
DString normalizeNonTemplateArgumentsInString(const DString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:3465
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4168
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:4219
void initDefaultExtensionMapping()
Definition util.cpp:4095
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
void checkBlocks(const DString &s, const DString fileName, const SelectionMarkerInfo &markerInfo)
Definition util.cpp:5129
static void transcodeCharacterBuffer(const DString &fileName, std::string &contents, const DString &inputEncoding, const DString &outputEncoding)
Definition util.cpp:4338
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:2494
void extractNamespaceName(const DString &scopeName, DString &className, DString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3026
DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
Definition util.cpp:3232
DString stripTemplateSpecifiersFromScope(const DString &fullName, bool parentOnly, DString *pLastScopeStripped, DString scopeName, bool allowArtificial)
Definition util.cpp:3686
DString relativePathToRoot(const DString &name)
Definition util.cpp:2912
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:2580
static StringUnorderedSet writeFileContents_set
Definition util.cpp:5371
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:4904
static const char volatileScope[]
Definition util.cpp:380
DString getEncoding(const FileInfo &fi)
Definition util.cpp:4477
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:4619
int getPrefixIndex(const DString &name)
Definition util.cpp:2651
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4629
bool updateLanguageMapping(const DString &extension, const DString &language)
Definition util.cpp:4063
void stackTrace()
Definition util.cpp:4310
DString demangleCSharpGenericName(const DString &name, const DString &templArgs)
Definition util.cpp:5343
DString createHtmlUrl(const DString &relPath, const DString &ref, bool isLocalFile, const DString &targetFileName, const DString &anchor)
Definition util.cpp:4502
DString projectLogoSize()
Definition util.cpp:2599
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:5334
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:1866
DString getDotImageExtension()
Definition util.cpp:4964
#define NOMATCH
Definition util.cpp:1489
DString escapeCharsInString(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2688
static std::mutex g_usedNamesMutex
Definition util.cpp:2854
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:3798
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:4683
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:3380
DString determineAbsoluteIncludeName(const DString &curFile, const DString &incFileName)
Definition util.cpp:2929
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:3974
DString getDotImageExtensionGenerated(const DString &format)
Definition util.cpp:4974
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:4538
static bool isLowerCase(DString &s)
Definition util.cpp:1999
DString stripPath(const DString &s)
Definition util.cpp:3960
static std::unordered_map< std::string, int > g_usedNames
Definition util.cpp:2853
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:4986
static std::vector< Lang2ExtMap > g_lang2extMap
Definition util.cpp:4039
DString stripAnonymousNamespaceScope(const DString &s)
Definition util.cpp:167
DString findExampleFilePath(const DString &file, bool &ambig)
Definition util.cpp:2451
SrcLangExt getLanguageFromCodeLang(DString &fileName)
Routine to handle the language attribute of the \code command.
Definition util.cpp:4186
A bunch of utility functions.
std::vector< KeywordSubstitution > KeywordSubstitutionList
Definition util.h:265
std::vector< SelectionBlock > SelectionBlockList
Definition util.h:213