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