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