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 {
116 m_ol.writeNonBreakableSpace(1);
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();
269 ol.docify(theTranslator->trPageAbbreviation());
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 {
450 mn=Doxygen::memberNameLinkedMap->find(resName);
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 isFileName)
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 (!isFileName && 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
2733
2734bool resolveLink(/* in */ const QCString &scName,
2735 /* in */ const QCString &lr,
2736 /* in */ bool /*inSeeBlock*/,
2737 /* out */ const Definition **resContext,
2738 /* out */ QCString &resAnchor,
2739 /* in */ SrcLangExt lang,
2740 /* in */ const QCString &prefix
2741 )
2742{
2743 *resContext=nullptr;
2744
2745 QCString linkRef=lr;
2746 if (lang==SrcLangExt::CSharp)
2747 {
2748 linkRef = mangleCSharpGenericName(linkRef);
2749 }
2750 QCString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,FALSE);
2751 AUTO_TRACE("scName='{}',ref='{}'",scName,lr);
2752 const FileDef *fd = nullptr;
2753 const GroupDef *gd = nullptr;
2754 const PageDef *pd = nullptr;
2755 const ClassDef *cd = nullptr;
2756 const DirDef *dir = nullptr;
2757 const ConceptDef *cnd = nullptr;
2758 const ModuleDef *modd = nullptr;
2759 const NamespaceDef *nd = nullptr;
2760 const SectionInfo *si = nullptr;
2761 bool ambig = false;
2762 if (linkRef.isEmpty()) // no reference name!
2763 {
2764 AUTO_TRACE_EXIT("no_ref");
2765 return FALSE;
2766 }
2767 else if ((pd=Doxygen::pageLinkedMap->find(linkRef))) // link to a page
2768 {
2769 gd = pd->getGroupDef();
2770 if (gd)
2771 {
2772 if (!pd->name().isEmpty()) si=SectionManager::instance().find(pd->name());
2773 *resContext=gd;
2774 if (si) resAnchor = si->label();
2775 }
2776 else
2777 {
2778 *resContext=pd;
2779 }
2780 AUTO_TRACE_EXIT("page");
2781 return TRUE;
2782 }
2783 else if ((si=SectionManager::instance().find(prefix+linkRef)))
2784 {
2785 *resContext=si->definition();
2786 resAnchor = si->label();
2787 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2788 return TRUE;
2789 }
2790 else if (!prefix.isEmpty() && (si=SectionManager::instance().find(linkRef)))
2791 {
2792 *resContext=si->definition();
2793 resAnchor = si->label();
2794 AUTO_TRACE_EXIT("section anchor={} def={}",resAnchor,si->definition()?si->definition()->name():"<none>");
2795 return TRUE;
2796 }
2797 else if ((pd=Doxygen::exampleLinkedMap->find(linkRef))) // link to an example
2798 {
2799 *resContext=pd;
2800 AUTO_TRACE_EXIT("example");
2801 return TRUE;
2802 }
2803 else if ((gd=Doxygen::groupLinkedMap->find(linkRef))) // link to a group
2804 {
2805 *resContext=gd;
2806 AUTO_TRACE_EXIT("group");
2807 return TRUE;
2808 }
2809 else if ((fd=findFileDef(Doxygen::inputNameLinkedMap,linkRef,ambig)) // file link
2810 && fd->isLinkable())
2811 {
2812 *resContext=fd;
2813 AUTO_TRACE_EXIT("file");
2814 return TRUE;
2815 }
2816 else if ((cd=getClass(linkRef))) // class link
2817 {
2818 *resContext=cd;
2819 resAnchor=cd->anchor();
2820 AUTO_TRACE_EXIT("class");
2821 return TRUE;
2822 }
2823 else if (lang==SrcLangExt::Java &&
2824 (cd=getClass(linkRefWithoutTemplates))) // Java generic class link
2825 {
2826 *resContext=cd;
2827 resAnchor=cd->anchor();
2828 AUTO_TRACE_EXIT("generic");
2829 return TRUE;
2830 }
2831 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
2832 {
2833 *resContext=cd;
2834 resAnchor=cd->anchor();
2835 AUTO_TRACE_EXIT("protocol");
2836 return TRUE;
2837 }
2838 else if ((cnd=getConcept(linkRef))) // C++20 concept definition
2839 {
2840 *resContext=cnd;
2841 resAnchor=cnd->anchor();
2842 AUTO_TRACE_EXIT("concept");
2843 return TRUE;
2844 }
2845 else if ((modd=ModuleManager::instance().modules().find(linkRef)))
2846 {
2847 *resContext=modd;
2848 resAnchor=modd->anchor();
2849 AUTO_TRACE_EXIT("module");
2850 return TRUE;
2851 }
2852 else if ((nd=Doxygen::namespaceLinkedMap->find(linkRef)))
2853 {
2854 *resContext=nd;
2855 AUTO_TRACE_EXIT("namespace");
2856 return TRUE;
2857 }
2858 else if ((dir=Doxygen::dirLinkedMap->find(FileInfo(linkRef.str()).absFilePath()+"/"))
2859 && dir->isLinkable()) // TODO: make this location independent like filedefs
2860 {
2861 *resContext=dir;
2862 AUTO_TRACE_EXIT("directory");
2863 return TRUE;
2864 }
2865 else // probably a member reference
2866 {
2867 const MemberDef *md = nullptr;
2868 bool res = resolveRef(scName,lr,TRUE,resContext,&md,lang);
2869 if (md) resAnchor=md->anchor();
2870 AUTO_TRACE_EXIT("member? res={}",res);
2871 return res;
2872 }
2873}
2874
2875
2876void generateFileRef(OutputList &ol,const QCString &name,const QCString &text)
2877{
2878 //printf("generateFileRef(%s,%s)\n",name,text);
2879 QCString linkText = text.isEmpty() ? text : name;
2880 //FileInfo *fi;
2881 bool ambig = false;
2883 if (fd && fd->isLinkable())
2884 // link to documented input file
2885 ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),QCString(),linkText);
2886 else
2887 ol.docify(linkText);
2888}
2889
2890//----------------------------------------------------------------------
2891
2892/** Cache element for the file name to FileDef mapping cache. */
2894{
2895 FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
2898};
2899
2901
2902static std::mutex g_findFileDefMutex;
2903
2904FileDef *findFileDef(const FileNameLinkedMap *fnMap,const QCString &n,bool &ambig)
2905{
2906 ambig=FALSE;
2907 if (n.isEmpty()) return nullptr;
2908
2909
2910 const int maxAddrSize = 20;
2911 char addr[maxAddrSize];
2912 qsnprintf(addr,maxAddrSize,"%p:",reinterpret_cast<const void*>(fnMap));
2913 QCString key = addr;
2914 key+=n;
2915
2916 std::lock_guard<std::mutex> lock(g_findFileDefMutex);
2917 FindFileCacheElem *cachedResult = g_findFileDefCache.find(key.str());
2918 //printf("key=%s cachedResult=%p\n",qPrint(key),cachedResult);
2919 if (cachedResult)
2920 {
2921 ambig = cachedResult->isAmbig;
2922 //printf("cached: fileDef=%p\n",cachedResult->fileDef);
2923 return cachedResult->fileDef;
2924 }
2925 else
2926 {
2927 cachedResult = g_findFileDefCache.insert(key.str(),FindFileCacheElem(nullptr,FALSE));
2928 }
2929
2930 QCString name=Dir::cleanDirPath(n.str());
2931 QCString path;
2932 if (name.isEmpty()) return nullptr;
2933 int slashPos=std::max(name.findRev('/'),name.findRev('\\'));
2934 if (slashPos!=-1)
2935 {
2936 path=removeLongPathMarker(name.left(slashPos+1));
2937 name=name.right(name.length()-slashPos-1);
2938 }
2939 if (name.isEmpty()) return nullptr;
2940 const FileName *fn = fnMap->find(name);
2941 if (fn)
2942 {
2943 //printf("fn->size()=%zu\n",fn->size());
2944 if (fn->size()==1)
2945 {
2946 const std::unique_ptr<FileDef> &fd = fn->front();
2947 bool isSamePath = Portable::fileSystemIsCaseSensitive() ?
2948 fd->getPath().right(path.length())==path :
2949 fd->getPath().right(path.length()).lower()==path.lower();
2950 if (path.isEmpty() || isSamePath)
2951 {
2952 cachedResult->fileDef = fd.get();
2953 return fd.get();
2954 }
2955 }
2956 else // file name alone is ambiguous
2957 {
2958 int count=0;
2959 FileDef *lastMatch=nullptr;
2960 QCString pathStripped = stripFromIncludePath(path);
2961 for (const auto &fd_p : *fn)
2962 {
2963 FileDef *fd = fd_p.get();
2964 QCString fdStripPath = stripFromIncludePath(fd->getPath());
2965 if (fdStripPath == pathStripped)
2966 {
2967 // if the stripped paths are equal, we have a perfect match
2968 count = 1;
2969 lastMatch=fd;
2970 break;
2971 }
2972 if (path.isEmpty() ||
2973 (!pathStripped.isEmpty() && fdStripPath.endsWith(pathStripped)) ||
2974 (pathStripped.isEmpty() && fdStripPath.isEmpty()))
2975 {
2976 count++;
2977 lastMatch=fd;
2978 }
2979 }
2980
2981 ambig=(count>1);
2982 cachedResult->isAmbig = ambig;
2983 cachedResult->fileDef = lastMatch;
2984 return lastMatch;
2985 }
2986 }
2987 else
2988 {
2989 //printf("not found!\n");
2990 }
2991 return nullptr;
2992}
2993
2994//----------------------------------------------------------------------
2995
2996QCString findFilePath(const QCString &file,bool &ambig)
2997{
2998 ambig=false;
2999 QCString result;
3000 bool found=false;
3001 if (!found)
3002 {
3003 FileInfo fi(file.str());
3004 if (fi.exists())
3005 {
3006 result=fi.absFilePath();
3007 found=true;
3008 }
3009 }
3010 if (!found)
3011 {
3012 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
3013 for (const auto &s : examplePathList)
3014 {
3015 std::string absFileName = s+(Portable::pathSeparator()+file).str();
3016 FileInfo fi(absFileName);
3017 if (fi.exists())
3018 {
3019 result=fi.absFilePath();
3020 found=true;
3021 }
3022 }
3023 }
3024
3025 if (!found)
3026 {
3027 // as a fallback we also look in the exampleNameDict
3029 if (fd && !ambig)
3030 {
3031 result=fd->absFilePath();
3032 }
3033 }
3034 return result;
3035}
3036
3037//----------------------------------------------------------------------
3038
3040{
3041 QCString result;
3042 QCString name=Dir::cleanDirPath(n.str());
3043 QCString path;
3044 int slashPos=std::max(name.findRev('/'),name.findRev('\\'));
3045 if (slashPos!=-1)
3046 {
3047 path=removeLongPathMarker(name.left(slashPos+1));
3048 name=name.right(name.length()-slashPos-1);
3049 }
3050 const FileName *fn=fnMap->find(name);
3051 if (fn)
3052 {
3053 bool first = true;
3054 QCString pathStripped = stripFromIncludePath(path);
3055 for (const auto &fd_p : *fn)
3056 {
3057 FileDef *fd = fd_p.get();
3058 QCString fdStripPath = stripFromIncludePath(fd->getPath());
3059 if (path.isEmpty() ||
3060 (!pathStripped.isEmpty() && fdStripPath.endsWith(pathStripped)) ||
3061 (pathStripped.isEmpty() && fdStripPath.isEmpty()))
3062 {
3063 if (!first) result += "\n";
3064 else first = false;
3065 result+=" "+fd->absFilePath();
3066 }
3067 }
3068
3069 }
3070 return result;
3071}
3072
3073//----------------------------------------------------------------------
3074
3076{
3077 std::string substRes;
3078 int line = 1;
3079 const char *p = s.data();
3080 if (p)
3081 {
3082 // reserve some room for expansion
3083 substRes.reserve(s.length()+1024);
3084 char c = 0;
3085 while ((c=*p))
3086 {
3087 bool found = false;
3088 if (c=='$')
3089 {
3090 for (const auto &kw : keywords)
3091 {
3092 size_t keyLen = qstrlen(kw.keyword);
3093 if (qstrncmp(p,kw.keyword,keyLen)==0)
3094 {
3095 const char *startArg = p+keyLen;
3096 bool expectParam = std::holds_alternative<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
3097 //printf("%s: expectParam=%d *startArg=%c\n",kw.keyword,expectParam,*startArg);
3098 if (expectParam && *startArg=='(') // $key(value)
3099 {
3100 size_t j=1;
3101 const char *endArg = nullptr;
3102 while ((c=*(startArg+j)) && c!=')' && c!='\n' && c!=0) j++;
3103 if (c==')') endArg=startArg+j;
3104 if (endArg)
3105 {
3106 QCString value = QCString(startArg+1).left(endArg-startArg-1);
3107 auto &&getValue = std::get<KeywordSubstitution::GetValueWithParam>(kw.getValueVariant);
3108 substRes+=getValue(value).str();
3109 p=endArg+1;
3110 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue(value)));
3111 }
3112 else
3113 {
3114 //printf("missing argument\n");
3115 warn(file,line,"Missing argument for '{}'",kw.keyword);
3116 p+=keyLen;
3117 }
3118 }
3119 else if (!expectParam) // $key
3120 {
3121 auto &&getValue = std::get<KeywordSubstitution::GetValue>(kw.getValueVariant);
3122 substRes+=getValue().str();
3123 //printf("found '%s'->'%s'\n",kw.keyword,qPrint(getValue()));
3124 p+=keyLen;
3125 }
3126 else
3127 {
3128 //printf("%s %d Expected arguments, none specified '%s'\n",qPrint(file), line, qPrint(kw.keyword));
3129 warn(file,line,"Expected arguments for '{}' but none were specified",kw.keyword);
3130 p+=keyLen;
3131 }
3132 found = true;
3133 break;
3134 }
3135 }
3136 }
3137 if (!found) // copy
3138 {
3139 if (c=='\n') line++;
3140 substRes+=c;
3141 p++;
3142 }
3143 }
3144 }
3145 return substRes;
3146}
3147
3149{
3150 // get the current date and time
3151 std::tm dat{};
3152 int specFormat=0;
3153 QCString specDate = "";
3154 QCString err = dateTimeFromString(specDate,dat,specFormat);
3155
3156 // do the conversion
3157 int usedFormat=0;
3158 return formatDateTime(fmt,dat,usedFormat);
3159}
3160
3162{
3163 QCString projectLogo = Config_getString(PROJECT_LOGO);
3164 if (!projectLogo.isEmpty())
3165 {
3166 // check for optional width= and height= specifier
3167 int wi = projectLogo.find(" width=");
3168 if (wi!=-1) // and strip them
3169 {
3170 projectLogo = projectLogo.left(wi);
3171 }
3172 int hi = projectLogo.find(" height=");
3173 if (hi!=-1)
3174 {
3175 projectLogo = projectLogo.left(hi);
3176 }
3177 }
3178 //printf("projectlogo='%s'\n",qPrint(projectLogo));
3179 return projectLogo;
3180}
3181
3183{
3184 QCString sizeVal;
3185 QCString projectLogo = Config_getString(PROJECT_LOGO);
3186 if (!projectLogo.isEmpty())
3187 {
3188 auto extractDimension = [&projectLogo](const char *startMarker,size_t startPos,size_t endPos) -> QCString
3189 {
3190 QCString result = projectLogo.mid(startPos,endPos-startPos).stripWhiteSpace().quoted();
3191 if (result.length()>=2 && result.at(0)!='"' && result.at(result.length()-1)!='"')
3192 {
3193 result="\""+result+"\"";
3194 }
3195 result.prepend(startMarker);
3196 return result;
3197 };
3198 // check for optional width= and height= specifier
3199 int wi = projectLogo.find(" width=");
3200 int hi = projectLogo.find(" height=");
3201 if (wi!=-1 && hi!=-1)
3202 {
3203 if (wi<hi) // "... width=x height=y..."
3204 {
3205 sizeVal = extractDimension(" width=", wi+7, hi) + " "
3206 + extractDimension(" height=", hi+8, projectLogo.length());
3207 }
3208 else // "... height=y width=x..."
3209 {
3210 sizeVal = extractDimension(" height=", hi+8, wi) + " "
3211 + extractDimension(" width=", wi+7, projectLogo.length());
3212 }
3213 }
3214 else if (wi!=-1) // ... width=x..."
3215 {
3216 sizeVal = extractDimension(" width=", wi+7, projectLogo.length());
3217 }
3218 else if (hi!=-1) // ... height=x..."
3219 {
3220 sizeVal = extractDimension(" height=", hi+8, projectLogo.length());
3221 }
3222 }
3223 //printf("projectsize='%s'\n",qPrint(sizeVal));
3224 return sizeVal;
3225}
3226
3227QCString substituteKeywords(const QCString &file,const QCString &s,const QCString &title,
3228 const QCString &projName,const QCString &projNum,const QCString &projBrief)
3229{
3230 return substituteKeywords(file,s,
3231 {
3232 // keyword value getter
3233 { "$title", [&]() { return !title.isEmpty() ? title : projName; } },
3234 { "$doxygenversion", [&]() { return getDoxygenVersion(); } },
3235 { "$projectname", [&]() { return projName; } },
3236 { "$projectnumber", [&]() { return projNum; } },
3237 { "$projectbrief", [&]() { return projBrief; } },
3238 { "$projectlogo", [&]() { return stripPath(projectLogoFile()); } },
3239 { "$logosize", [&]() { return projectLogoSize(); } },
3240 { "$projecticon", [&]() { return stripPath(Config_getString(PROJECT_ICON)); } },
3241 { "$langISO", [&]() { return theTranslator->trISOLang(); } },
3242 { "$showdate", [&](const QCString &fmt) { return showDate(fmt); } }
3243 });
3244}
3245
3246//----------------------------------------------------------------------
3247
3248/*! Returns the character index within \a name of the first prefix
3249 * in Config_getList(IGNORE_PREFIX) that matches \a name at the left hand side,
3250 * or zero if no match was found
3251 */
3252int getPrefixIndex(const QCString &name)
3253{
3254 if (name.isEmpty()) return 0;
3255 const StringVector &sl = Config_getList(IGNORE_PREFIX);
3256 for (const auto &s : sl)
3257 {
3258 const char *ps=s.c_str();
3259 const char *pd=name.data();
3260 int i=0;
3261 while (*ps!=0 && *pd!=0 && *ps==*pd)
3262 {
3263 ps++;
3264 pd++;
3265 i++;
3266 }
3267 if (*ps==0 && *pd!=0)
3268 {
3269 return i;
3270 }
3271 }
3272 return 0;
3273}
3274
3275//----------------------------------------------------------------------------
3276
3277//----------------------------------------------------------------------
3278
3279#if 0
3280// copies the next UTF8 character from input stream into buffer ids
3281// returns the size of the character in bytes (or 0 if it is invalid)
3282// the character itself will be copied as a UTF-8 encoded string to ids.
3283int getUtf8Char(const char *input,char ids[MAX_UTF8_CHAR_SIZE],CaseModifier modifier)
3284{
3285 int inputLen=1;
3286 const unsigned char uc = (unsigned char)*input;
3287 bool validUTF8Char = false;
3288 if (uc <= 0xf7)
3289 {
3290 const char* pt = input+1;
3291 int l = 0;
3292 if ((uc&0x80)==0x00)
3293 {
3294 switch (modifier)
3295 {
3296 case CaseModifier::None: ids[0]=*input; break;
3297 case CaseModifier::ToUpper: ids[0]=(char)toupper(*input); break;
3298 case CaseModifier::ToLower: ids[0]=(char)tolower(*input); break;
3299 }
3300 l=1; // 0xxx.xxxx => normal single byte ascii character
3301 }
3302 else
3303 {
3304 ids[ 0 ] = *input;
3305 if ((uc&0xE0)==0xC0)
3306 {
3307 l=2; // 110x.xxxx: >=2 byte character
3308 }
3309 if ((uc&0xF0)==0xE0)
3310 {
3311 l=3; // 1110.xxxx: >=3 byte character
3312 }
3313 if ((uc&0xF8)==0xF0)
3314 {
3315 l=4; // 1111.0xxx: >=4 byte character
3316 }
3317 }
3318 validUTF8Char = l>0;
3319 for (int m=1; m<l && validUTF8Char; ++m)
3320 {
3321 unsigned char ct = (unsigned char)*pt;
3322 if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character
3323 {
3324 validUTF8Char=false;
3325 }
3326 else
3327 {
3328 ids[ m ] = *pt++;
3329 }
3330 }
3331 if (validUTF8Char) // got a valid unicode character
3332 {
3333 ids[ l ] = 0;
3334 inputLen=l;
3335 }
3336 }
3337 return inputLen;
3338}
3339#endif
3340
3342{
3343 auto caseSenseNames = Config_getEnum(CASE_SENSE_NAMES);
3344
3345 if (caseSenseNames == CASE_SENSE_NAMES_t::YES) return true;
3346 else if (caseSenseNames == CASE_SENSE_NAMES_t::NO) return false;
3348}
3349
3350QCString escapeCharsInString(const QCString &name,bool allowDots,bool allowUnderscore)
3351{
3352 if (name.isEmpty()) return name;
3353 bool caseSenseNames = getCaseSenseNames();
3354 bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
3355 QCString result;
3356 result.reserve(name.length()+8);
3357 signed char c = 0;
3358 const char *p=name.data();
3359 while ((c=*p++)!=0)
3360 {
3361 switch(c)
3362 {
3363 case '_': if (allowUnderscore) result+='_'; else result+="__"; break;
3364 case '-': result+='-'; break;
3365 case ':': result+="_1"; break;
3366 case '/': result+="_2"; break;
3367 case '<': result+="_3"; break;
3368 case '>': result+="_4"; break;
3369 case '*': result+="_5"; break;
3370 case '&': result+="_6"; break;
3371 case '|': result+="_7"; break;
3372 case '.': if (allowDots) result+='.'; else result+="_8"; break;
3373 case '!': result+="_9"; break;
3374 case ',': result+="_00"; break;
3375 case ' ': result+="_01"; break;
3376 case '{': result+="_02"; break;
3377 case '}': result+="_03"; break;
3378 case '?': result+="_04"; break;
3379 case '^': result+="_05"; break;
3380 case '%': result+="_06"; break;
3381 case '(': result+="_07"; break;
3382 case ')': result+="_08"; break;
3383 case '+': result+="_09"; break;
3384 case '=': result+="_0a"; break;
3385 case '$': result+="_0b"; break;
3386 case '\\': result+="_0c"; break;
3387 case '@': result+="_0d"; break;
3388 case ']': result+="_0e"; break;
3389 case '[': result+="_0f"; break;
3390 case '#': result+="_0g"; break;
3391 case '"': result+="_0h"; break;
3392 case '~': result+="_0i"; break;
3393 case '\'': result+="_0j"; break;
3394 case ';': result+="_0k"; break;
3395 case '`': result+="_0l"; break;
3396 default:
3397 if (c<0)
3398 {
3399 bool doEscape = true;
3400 if (allowUnicodeNames)
3401 {
3402 int charLen = getUTF8CharNumBytes(c);
3403 if (charLen>0)
3404 {
3405 result+=QCString(p-1,charLen);
3406 p+=charLen;
3407 doEscape = false;
3408 }
3409 }
3410 if (doEscape) // not a valid unicode char or escaping needed
3411 {
3412 char ids[5];
3413 unsigned char id = static_cast<unsigned char>(c);
3414 ids[0]='_';
3415 ids[1]='x';
3416 ids[2]=hex[id>>4];
3417 ids[3]=hex[id&0xF];
3418 ids[4]=0;
3419 result+=ids;
3420 }
3421 }
3422 else if (caseSenseNames || !isupper(c))
3423 {
3424 result+=c;
3425 }
3426 else
3427 {
3428 result+='_';
3429 result+=static_cast<char>(tolower(c));
3430 }
3431 break;
3432 }
3433 }
3434 return result;
3435}
3436
3438{
3439 if (s.isEmpty()) return s;
3440 bool caseSenseNames = getCaseSenseNames();
3441 QCString result;
3442 result.reserve(s.length());
3443 const char *p = s.data();
3444 if (p)
3445 {
3446 char c = 0;
3447 while ((c=*p++))
3448 {
3449 if (c=='_') // 2 or 3 character escape
3450 {
3451 switch (*p)
3452 {
3453 case '_': result+=c; p++; break; // __ -> '_'
3454 case '1': result+=':'; p++; break; // _1 -> ':'
3455 case '2': result+='/'; p++; break; // _2 -> '/'
3456 case '3': result+='<'; p++; break; // _3 -> '<'
3457 case '4': result+='>'; p++; break; // _4 -> '>'
3458 case '5': result+='*'; p++; break; // _5 -> '*'
3459 case '6': result+='&'; p++; break; // _6 -> '&'
3460 case '7': result+='|'; p++; break; // _7 -> '|'
3461 case '8': result+='.'; p++; break; // _8 -> '.'
3462 case '9': result+='!'; p++; break; // _9 -> '!'
3463 case '0': // 3 character escape
3464 switch (*(p+1))
3465 {
3466 case '0': result+=','; p+=2; break; // _00 -> ','
3467 case '1': result+=' '; p+=2; break; // _01 -> ' '
3468 case '2': result+='{'; p+=2; break; // _02 -> '{'
3469 case '3': result+='}'; p+=2; break; // _03 -> '}'
3470 case '4': result+='?'; p+=2; break; // _04 -> '?'
3471 case '5': result+='^'; p+=2; break; // _05 -> '^'
3472 case '6': result+='%'; p+=2; break; // _06 -> '%'
3473 case '7': result+='('; p+=2; break; // _07 -> '('
3474 case '8': result+=')'; p+=2; break; // _08 -> ')'
3475 case '9': result+='+'; p+=2; break; // _09 -> '+'
3476 case 'a': result+='='; p+=2; break; // _0a -> '='
3477 case 'b': result+='$'; p+=2; break; // _0b -> '$'
3478 case 'c': result+='\\'; p+=2; break;// _0c -> '\'
3479 case 'd': result+='@'; p+=2; break; // _0d -> '@'
3480 case 'e': result+=']'; p+=2; break; // _0e -> ']'
3481 case 'f': result+='['; p+=2; break; // _0f -> '['
3482 case 'g': result+='#'; p+=2; break; // _0g -> '#'
3483 case 'h': result+='"'; p+=2; break; // _0h -> '"'
3484 case 'i': result+='~'; p+=2; break; // _0i -> '~'
3485 case 'j': result+='\''; p+=2; break;// _0j -> '\'
3486 case 'k': result+=';'; p+=2; break; // _0k -> ';'
3487 case 'l': result+='`'; p+=2; break; // _0l -> '`'
3488 default: // unknown escape, just pass underscore character as-is
3489 result+=c;
3490 break;
3491 }
3492 break;
3493 default:
3494 if (!caseSenseNames && c>='a' && c<='z') // lower to upper case escape, _a -> 'A'
3495 {
3496 result+=static_cast<char>(toupper(*p));
3497 p++;
3498 }
3499 else // unknown escape, pass underscore character as-is
3500 {
3501 result+=c;
3502 }
3503 break;
3504 }
3505 }
3506 else // normal character; pass as is
3507 {
3508 result+=c;
3509 }
3510 }
3511 }
3512 return result;
3513}
3514
3515static std::unordered_map<std::string,int> g_usedNames;
3516static std::mutex g_usedNamesMutex;
3517static int g_usedNamesCount=1;
3518
3519
3520
3521/*! This function determines the file name on disk of an item
3522 * given its name, which could be a class name with template
3523 * arguments, so special characters need to be escaped.
3524 */
3525QCString convertNameToFile(const QCString &name,bool allowDots,bool allowUnderscore)
3526{
3527 if (name.isEmpty()) return name;
3528 bool shortNames = Config_getBool(SHORT_NAMES);
3529 bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
3530 QCString result;
3531 if (shortNames) // use short names only
3532 {
3533 std::lock_guard<std::mutex> lock(g_usedNamesMutex);
3534 auto kv = g_usedNames.find(name.str());
3535 uint32_t num=0;
3536 if (kv!=g_usedNames.end())
3537 {
3538 num = kv->second;
3539 }
3540 else
3541 {
3542 num = g_usedNamesCount;
3543 g_usedNames.emplace(name.str(),g_usedNamesCount++);
3544 }
3545 result.sprintf("a%05d",num);
3546 }
3547 else // long names
3548 {
3549 result=escapeCharsInString(name,allowDots,allowUnderscore);
3550 size_t resultLen = result.length();
3551 if (resultLen>=128) // prevent names that cannot be created!
3552 {
3553 // third algorithm based on MD5 hash
3554 uint8_t md5_sig[16];
3555 char sigStr[33];
3556 MD5Buffer(result.data(),static_cast<unsigned int>(resultLen),md5_sig);
3557 MD5SigToString(md5_sig,sigStr);
3558 result=result.left(128-32)+sigStr;
3559 }
3560 }
3561 if (createSubdirs)
3562 {
3563 int l1Dir=0,l2Dir=0;
3564 int createSubdirsLevel = Config_getInt(CREATE_SUBDIRS_LEVEL);
3565 int createSubdirsBitmaskL2 = (1<<createSubdirsLevel)-1;
3566
3567 // compute md5 hash to determine sub directory to use
3568 uint8_t md5_sig[16];
3569 MD5Buffer(result.data(),static_cast<unsigned int>(result.length()),md5_sig);
3570 l1Dir = md5_sig[14] & 0xf;
3571 l2Dir = md5_sig[15] & createSubdirsBitmaskL2;
3572
3573 result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
3574 }
3575 //printf("*** convertNameToFile(%s)->%s\n",qPrint(name),qPrint(result));
3576 return result;
3577}
3578
3580{
3581 QCString fn = stripFromPath(fileName)+":"+QCString().setNum(count);
3582 const int sig_size=16;
3583 uint8_t md5_sig[sig_size];
3584 MD5Buffer(fn.data(),static_cast<unsigned int>(fn.length()),md5_sig);
3585 char result[sig_size*3+2];
3586 char *p = result;
3587 *p++='@';
3588 for (int i=0;i<sig_size;i++)
3589 {
3590 static const char oct[]="01234567";
3591 uint8_t byte = md5_sig[i];
3592 *p++=oct[(byte>>6)&7];
3593 *p++=oct[(byte>>3)&7];
3594 *p++=oct[(byte>>0)&7];
3595 }
3596 *p='\0';
3597 return result;
3598}
3599
3601{
3602 QCString result;
3603 if (Config_getBool(CREATE_SUBDIRS))
3604 {
3605 if (name.isEmpty())
3606 {
3607 return REL_PATH_TO_ROOT;
3608 }
3609 else
3610 {
3611 int i = name.findRev('/');
3612 if (i!=-1)
3613 {
3614 result=REL_PATH_TO_ROOT;
3615 }
3616 }
3617 }
3618 return result;
3619}
3620
3621QCString determineAbsoluteIncludeName(const QCString &curFile,const QCString &incFileName)
3622{
3623 bool searchIncludes = Config_getBool(SEARCH_INCLUDES);
3624 QCString absIncFileName = incFileName;
3625 FileInfo fi(curFile.str());
3626 if (fi.exists())
3627 {
3628 QCString absName = fi.dirPath(TRUE)+"/"+incFileName;
3629 FileInfo fi2(absName.str());
3630 if (fi2.exists())
3631 {
3632 absIncFileName=fi2.absFilePath();
3633 }
3634 else if (searchIncludes) // search in INCLUDE_PATH as well
3635 {
3636 const StringVector &includePath = Config_getList(INCLUDE_PATH);
3637 for (const auto &incPath : includePath)
3638 {
3639 FileInfo fi3(incPath);
3640 if (fi3.exists() && fi3.isDir())
3641 {
3642 absName = fi3.absFilePath()+"/"+incFileName;
3643 //printf("trying absName=%s\n",qPrint(absName));
3644 FileInfo fi4(absName.str());
3645 if (fi4.exists())
3646 {
3647 absIncFileName=fi4.absFilePath();
3648 break;
3649 }
3650 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
3651 }
3652 }
3653 }
3654 //printf( "absIncFileName = %s\n", qPrint(absIncFileName) );
3655 }
3656 return absIncFileName;
3657}
3658
3659
3660
3661void createSubDirs(const Dir &d)
3662{
3663 if (Config_getBool(CREATE_SUBDIRS))
3664 {
3665 // create up to 4096 subdirectories
3666 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
3667 for (int l1=0; l1<16; l1++)
3668 {
3669 QCString subdir;
3670 subdir.sprintf("d%x",l1);
3671 if (!d.exists(subdir.str()) && !d.mkdir(subdir.str()))
3672 {
3673 term("Failed to create output directory '{}'\n",subdir);
3674 }
3675 for (int l2=0; l2<createSubdirsLevelPow2; l2++)
3676 {
3677 QCString subsubdir;
3678 subsubdir.sprintf("d%x/d%02x",l1,l2);
3679 if (!d.exists(subsubdir.str()) && !d.mkdir(subsubdir.str()))
3680 {
3681 term("Failed to create output directory '{}'\n",subsubdir);
3682 }
3683 }
3684 }
3685 }
3686}
3687
3688void clearSubDirs(const Dir &d)
3689{
3690 if (Config_getBool(CREATE_SUBDIRS))
3691 {
3692 // remove empty subdirectories
3693 int createSubdirsLevelPow2 = 1 << Config_getInt(CREATE_SUBDIRS_LEVEL);
3694 for (int l1=0;l1<16;l1++)
3695 {
3696 QCString subdir;
3697 subdir.sprintf("d%x",l1);
3698 for (int l2=0; l2 < createSubdirsLevelPow2; l2++)
3699 {
3700 QCString subsubdir;
3701 subsubdir.sprintf("d%x/d%02x",l1,l2);
3702 if (d.exists(subsubdir.str()) && d.isEmpty(subsubdir.str()))
3703 {
3704 d.rmdir(subsubdir.str());
3705 }
3706 }
3707 if (d.exists(subdir.str()) && d.isEmpty(subdir.str()))
3708 {
3709 d.rmdir(subdir.str());
3710 }
3711 }
3712 }
3713}
3714
3715/*! Input is a scopeName, output is the scopename split into a
3716 * namespace part (as large as possible) and a classname part.
3717 */
3718void extractNamespaceName(const QCString &scopeName,
3719 QCString &className,QCString &namespaceName,
3720 bool allowEmptyClass)
3721{
3722 int i=0, p=0;
3723 QCString clName=scopeName;
3724 NamespaceDef *nd = nullptr;
3725 if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==nullptr)
3726 { // the whole name is a namespace (and not a class)
3727 namespaceName=nd->name();
3728 className.clear();
3729 goto done;
3730 }
3731 p=static_cast<int>(clName.length())-2;
3732 while (p>=0 && (i=clName.findRev("::",p))!=-1)
3733 // see if the first part is a namespace (and not a class)
3734 {
3735 //printf("Trying %s\n",qPrint(clName.left(i)));
3736 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==nullptr)
3737 {
3738 //printf("found!\n");
3739 namespaceName=nd->name();
3740 className=clName.right(clName.length()-i-2);
3741 goto done;
3742 }
3743 p=i-2; // try a smaller piece of the scope
3744 }
3745 //printf("not found!\n");
3746
3747 // not found, so we just have to guess.
3748 className=scopeName;
3749 namespaceName.clear();
3750
3751done:
3752 if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
3753 {
3754 // class and namespace with the same name, correct to return the class.
3755 className=namespaceName;
3756 namespaceName.clear();
3757 }
3758 //printf("extractNamespace '%s' => '%s|%s'\n",qPrint(scopeName),
3759 // qPrint(className),qPrint(namespaceName));
3760 if (className.endsWith("-p"))
3761 {
3762 className = className.left(className.length()-2);
3763 }
3764 return;
3765}
3766
3768{
3769 QCString result=scope;
3770 if (!templ.isEmpty() && scope.find('<')==-1)
3771 {
3772 int si=0, pi=0;
3773 ClassDef *cd=nullptr;
3774 while (
3775 (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
3776 ((cd=getClass(scope.left(si)))==nullptr || cd->templateArguments().empty())
3777 )
3778 {
3779 //printf("Tried '%s'\n",qPrint((scope.left(si)+templ)));
3780 pi=si+2;
3781 }
3782 if (si==-1) // not nested => append template specifier
3783 {
3784 result+=templ;
3785 }
3786 else // nested => insert template specifier before after first class name
3787 {
3788 result=scope.left(si) + templ + scope.right(scope.length()-si);
3789 }
3790 }
3791 //printf("insertTemplateSpecifierInScope('%s','%s')=%s\n",
3792 // qPrint(scope),qPrint(templ),qPrint(result));
3793 return result;
3794}
3795
3796
3797/*! Strips the scope from a name. Examples: A::B will return A
3798 * and A<T>::B<N::C<D> > will return A<T>.
3799 */
3801{
3802 QCString result = name;
3803 int l = static_cast<int>(result.length());
3804 int p = 0;
3805 bool done = FALSE;
3806 bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
3807 int count=0;
3808 int round=0;
3809
3810 do
3811 {
3812 p=l-1; // start at the end of the string
3813 while (p>=0 && count>=0)
3814 {
3815 char c=result.at(p);
3816 switch (c)
3817 {
3818 case ':':
3819 // only exit in the case of ::
3820 //printf("stripScope(%s)=%s\n",name,qPrint(result.right(l-p-1)));
3821 if (p>0 && result.at(p-1)==':' && (count==0 || skipBracket))
3822 {
3823 return result.right(l-p-1);
3824 }
3825 p--;
3826 break;
3827 case '>':
3828 if (skipBracket) // we don't care about brackets
3829 {
3830 p--;
3831 }
3832 else // count open/close brackets
3833 {
3834 if (p>0 && result.at(p-1)=='>') // skip >> operator
3835 {
3836 p-=2;
3837 break;
3838 }
3839 count=1;
3840 //printf("pos < = %d\n",p);
3841 p--;
3842 bool foundMatch=false;
3843 while (p>=0 && !foundMatch)
3844 {
3845 c=result.at(p--);
3846 switch (c)
3847 {
3848 case ')':
3849 round++;
3850 break;
3851 case '(':
3852 round--;
3853 break;
3854 case '>': // ignore > inside (...) to support e.g. (sizeof(T)>0) inside template parameters
3855 if (round==0) count++;
3856 break;
3857 case '<':
3858 if (round==0)
3859 {
3860 if (p>0)
3861 {
3862 if (result.at(p-1) == '<') // skip << operator
3863 {
3864 p--;
3865 break;
3866 }
3867 }
3868 count--;
3869 foundMatch = count==0;
3870 }
3871 break;
3872 default:
3873 //printf("c=%c count=%d\n",c,count);
3874 break;
3875 }
3876 }
3877 }
3878 //printf("pos > = %d\n",p+1);
3879 break;
3880 default:
3881 p--;
3882 }
3883 }
3884 done = count==0 || skipBracket; // reparse if brackets do not match
3885 skipBracket=TRUE;
3886 }
3887 while (!done); // if < > unbalanced repeat ignoring them
3888 //printf("stripScope(%s)=%s\n",name,name);
3889 return name;
3890}
3891
3892/*! Converts a string to a HTML id string */
3894{
3895 if (s.isEmpty()) return s;
3896 QCString result;
3897 result.reserve(s.length()+8);
3898 const char *p = s.data();
3899 char c = 0;
3900 bool first = true;
3901 while ((c=*p++))
3902 {
3903 char encChar[4];
3904 if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-')
3905 { // any permissive character except _
3906 if (first && c>='0' && c<='9') result+='a'; // don't start with a digit
3907 result+=c;
3908 }
3909 else
3910 {
3911 encChar[0]='_';
3912 encChar[1]=hex[static_cast<unsigned char>(c)>>4];
3913 encChar[2]=hex[static_cast<unsigned char>(c)&0xF];
3914 encChar[3]=0;
3915 result+=encChar;
3916 }
3917 first=false;
3918 }
3919 return result;
3920}
3921
3922/*! Some strings have been corrected but the requirement regarding the fact
3923 * that an id cannot have a digit at the first position. To overcome problems
3924 * with double labels we always place an "a" in front
3925 */
3927{
3928 if (s.isEmpty()) return s;
3929 return "a" + s;
3930}
3931
3932/*! Converts a string to an XML-encoded string */
3933QCString convertToXML(const QCString &s, bool keepEntities)
3934{
3935 if (s.isEmpty()) return s;
3936 QCString result;
3937 result.reserve(s.length()+32);
3938 const char *p = s.data();
3939 char c = 0;
3940 while ((c=*p++))
3941 {
3942 switch (c)
3943 {
3944 case '<': result+="&lt;"; break;
3945 case '>': result+="&gt;"; break;
3946 case '&': if (keepEntities)
3947 {
3948 const char *e=p;
3949 char ce = 0;
3950 while ((ce=*e++))
3951 {
3952 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
3953 }
3954 if (ce==';') // found end of an entity
3955 {
3956 // copy entry verbatim
3957 result+=c;
3958 while (p<e) result+=*p++;
3959 }
3960 else
3961 {
3962 result+="&amp;";
3963 }
3964 }
3965 else
3966 {
3967 result+="&amp;";
3968 }
3969 break;
3970 case '\'': result+="&apos;"; break;
3971 case '"': result+="&quot;"; break;
3972 case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
3973 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
3974 case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
3975 case 27: case 28: case 29: case 30: case 31:
3976 break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
3977 default: result+=c; break;
3978 }
3979 }
3980 return result;
3981}
3982
3983/*! Converts a string to a HTML-encoded string */
3984QCString convertToHtml(const QCString &s,bool keepEntities)
3985{
3986 if (s.isEmpty()) return s;
3987 QCString result;
3988 result.reserve(s.length()+32);
3989 const char *p=s.data();
3990 char c = 0;
3991 while ((c=*p++))
3992 {
3993 switch (c)
3994 {
3995 case '<': result+="&lt;"; break;
3996 case '>': result+="&gt;"; break;
3997 case '&': if (keepEntities)
3998 {
3999 const char *e=p;
4000 char ce = 0;
4001 while ((ce=*e++))
4002 {
4003 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
4004 }
4005 if (ce==';') // found end of an entity
4006 {
4007 // copy entry verbatim
4008 result+=c;
4009 while (p<e) result+=*p++;
4010 }
4011 else
4012 {
4013 result+="&amp;";
4014 }
4015 }
4016 else
4017 {
4018 result+="&amp;";
4019 }
4020 break;
4021 case '\'': result+="&#39;"; break;
4022 case '"': result+="&quot;"; break;
4023 default:
4024 {
4025 uint8_t uc = static_cast<uint8_t>(c);
4026 if (uc<32 && !isspace(c))
4027 {
4028 result+="&#x24";
4029 result+=hex[uc>>4];
4030 result+=hex[uc&0xF];
4031 result+=';';
4032 }
4033 else
4034 {
4035 result+=c;
4036 }
4037 }
4038 break;
4039 }
4040 }
4041 return result;
4042}
4043
4044QCString convertToJSString(const QCString &s,bool keepEntities,bool singleQuotes)
4045{
4046 if (s.isEmpty()) return s;
4047 QCString result;
4048 result.reserve(s.length()+32);
4049 const char *p=s.data();
4050 char c = 0;
4051 while ((c=*p++))
4052 {
4053 switch (c)
4054 {
4055 case '"': if (!singleQuotes) result+="\\\""; else result+=c;
4056 break;
4057 case '\'': if (singleQuotes) result+="\\\'"; else result+=c;
4058 break;
4059 case '\\': if (*p=='u' && *(p+1)=='{') result+="\\"; // keep \u{..} unicode escapes
4060 else result+="\\\\";
4061 break;
4062 default: result+=c; break;
4063 }
4064 }
4065 return keepEntities ? result : convertCharEntitiesToUTF8(result);
4066}
4067
4069{
4070 if (str.isEmpty()) return QCString();
4071
4072 std::string s = str.data();
4073 static const reg::Ex re(R"(&\a\w*;)");
4074 reg::Iterator it(s,re);
4076
4077 QCString result;
4078 result.reserve(str.length()+32);
4079 size_t p=0, i=0, l=0;
4080 for (; it!=end ; ++it)
4081 {
4082 const auto &match = *it;
4083 p = match.position();
4084 l = match.length();
4085 if (p>i)
4086 {
4087 result+=s.substr(i,p-i);
4088 }
4089 QCString entity(match.str());
4091 const char *code=nullptr;
4092 if (symType!=HtmlEntityMapper::Sym_Unknown && (code=HtmlEntityMapper::instance().utf8(symType)))
4093 {
4094 result+=code;
4095 }
4096 else
4097 {
4098 result+=entity;
4099 }
4100 i=p+l;
4101 }
4102 result+=s.substr(i);
4103 //printf("convertCharEntitiesToUTF8(%s)->%s\n",qPrint(s),qPrint(result));
4104 return result;
4105}
4106
4107/*! Returns the standard string that is generated when the \\overload
4108 * command is used.
4109 */
4111{
4112 return theTranslator->trOverloadText();
4113 //"This is an overloaded member function, "
4114 // "provided for convenience. It differs from the above "
4115 // "function only in what argument(s) it accepts.";
4116}
4117
4119 MemberGroupList *pMemberGroups,
4120 const Definition *context)
4121{
4122 ASSERT(context!=nullptr);
4123 //printf("addMemberToMemberGroup() context=%s\n",qPrint(context->name()));
4124 if (ml==nullptr) return;
4125
4126 struct MoveMemberInfo
4127 {
4128 MoveMemberInfo(MemberDef *md,MemberGroup *mg,const RefItemVector &rv)
4129 : memberDef(md), memberGroup(mg), sli(rv) {}
4130 MemberDef *memberDef;
4131 MemberGroup *memberGroup;
4132 RefItemVector sli;
4133 };
4134 std::vector<MoveMemberInfo> movedMembers;
4135
4136 for (const auto &md : *ml)
4137 {
4138 if (md->isEnumerate()) // insert enum value of this enum into groups
4139 {
4140 for (const auto &fmd : md->enumFieldList())
4141 {
4142 int groupId=fmd->getMemberGroupId();
4143 if (groupId!=-1)
4144 {
4145 auto it = Doxygen::memberGroupInfoMap.find(groupId);
4147 {
4148 const auto &info = it->second;
4149 auto mg_it = std::find_if(pMemberGroups->begin(),
4150 pMemberGroups->end(),
4151 [&groupId](const auto &g)
4152 { return g->groupId()==groupId; }
4153 );
4154 MemberGroup *mg_ptr = nullptr;
4155 if (mg_it==pMemberGroups->end())
4156 {
4157 auto mg = std::make_unique<MemberGroup>(
4158 context,
4159 groupId,
4160 info->header,
4161 info->doc,
4162 info->docFile,
4163 info->docLine,
4164 ml->container());
4165 mg_ptr = mg.get();
4166 pMemberGroups->push_back(std::move(mg));
4167 }
4168 else
4169 {
4170 mg_ptr = (*mg_it).get();
4171 }
4172 mg_ptr->insertMember(fmd); // insert in member group
4174 if (fmdm)
4175 {
4176 fmdm->setMemberGroup(mg_ptr);
4177 }
4178 }
4179 }
4180 }
4181 }
4182 int groupId=md->getMemberGroupId();
4183 if (groupId!=-1)
4184 {
4185 auto it = Doxygen::memberGroupInfoMap.find(groupId);
4187 {
4188 const auto &info = it->second;
4189 auto mg_it = std::find_if(pMemberGroups->begin(),
4190 pMemberGroups->end(),
4191 [&groupId](const auto &g)
4192 { return g->groupId()==groupId; }
4193 );
4194 MemberGroup *mg_ptr = nullptr;
4195 if (mg_it==pMemberGroups->end())
4196 {
4197 auto mg = std::make_unique<MemberGroup>(
4198 context,
4199 groupId,
4200 info->header,
4201 info->doc,
4202 info->docFile,
4203 info->docLine,
4204 ml->container());
4205 mg_ptr = mg.get();
4206 pMemberGroups->push_back(std::move(mg));
4207 }
4208 else
4209 {
4210 mg_ptr = (*mg_it).get();
4211 }
4212 movedMembers.emplace_back(md,mg_ptr,info->m_sli);
4213 }
4214 }
4215 }
4216
4217 // move the members to their group
4218 for (const auto &mmi : movedMembers)
4219 {
4220 ml->remove(mmi.memberDef); // remove from member list
4221 mmi.memberGroup->insertMember(mmi.memberDef->resolveAlias()); // insert in member group
4222 mmi.memberGroup->setRefItems(mmi.sli);
4223 MemberDefMutable *rmdm = toMemberDefMutable(mmi.memberDef);
4224 if (rmdm)
4225 {
4226 rmdm->setMemberGroup(mmi.memberGroup);
4227 }
4228 }
4229}
4230
4231/*! Extracts a (sub-)string from \a type starting at \a pos that
4232 * could form a class. The index of the match is returned and the found
4233 * class \a name and a template argument list \a templSpec. If -1 is returned
4234 * there are no more matches.
4235 */
4236int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
4237{
4238 AUTO_TRACE("type='{}' pos={} name='{}' lang={}",type,pos,name,lang);
4239 static const reg::Ex re_norm(R"(\a[\w:]*)");
4240 static const reg::Ex re_fortran(R"(\a[\w:()=]*)");
4241 const reg::Ex *re = &re_norm;
4242
4243 name.clear();
4244 templSpec.clear();
4245 if (type.isEmpty())
4246 {
4247 AUTO_TRACE_EXIT("empty type");
4248 return -1;
4249 }
4250 size_t typeLen=type.length();
4251 if (typeLen>0)
4252 {
4253 if (lang == SrcLangExt::Fortran)
4254 {
4255 if (type[pos]==',')
4256 {
4257 AUTO_TRACE_EXIT("comma");
4258 return -1;
4259 }
4260 if (!type.lower().startsWith("type"))
4261 {
4262 re = &re_fortran;
4263 }
4264 }
4265 std::string s = type.str();
4266 reg::Iterator it(s,*re,static_cast<int>(pos));
4268
4269 if (it!=end)
4270 {
4271 const auto &match = *it;
4272 size_t i = match.position();
4273 size_t l = match.length();
4274 size_t ts = i+l;
4275 size_t te = ts;
4276 size_t tl = 0;
4277
4278 while (ts<typeLen && type[static_cast<uint32_t>(ts)]==' ') { ts++; tl++; } // skip any whitespace
4279 if (ts<typeLen && type[static_cast<uint32_t>(ts)]=='<') // assume template instance
4280 {
4281 // locate end of template
4282 te=ts+1;
4283 int brCount=1;
4284 while (te<typeLen && brCount!=0)
4285 {
4286 if (type[static_cast<uint32_t>(te)]=='<')
4287 {
4288 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='<') te++; else brCount++;
4289 }
4290 if (type[static_cast<uint32_t>(te)]=='>')
4291 {
4292 if (te<typeLen-1 && type[static_cast<uint32_t>(te)+1]=='>') te++; else brCount--;
4293 }
4294 te++;
4295 }
4296 }
4297 name = match.str();
4298 if (te>ts)
4299 {
4300 templSpec = QCString(type).mid(ts,te-ts);
4301 tl+=te-ts;
4302 pos=static_cast<int>(i+l+tl);
4303 }
4304 else // no template part
4305 {
4306 pos=static_cast<int>(i+l);
4307 }
4308 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE i=%d\n",
4309 // qPrint(type),pos,qPrint(name),qPrint(templSpec),i);
4310 AUTO_TRACE_EXIT("pos={} templSpec='{}' return={}",pos,templSpec,i);
4311 return static_cast<int>(i);
4312 }
4313 }
4314 pos = static_cast<int>(typeLen);
4315 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
4316 // qPrint(type),pos,qPrint(name),qPrint(templSpec));
4317 AUTO_TRACE_EXIT("not found");
4318 return -1;
4319}
4320
4322 const QCString &name,
4323 const Definition *context,
4324 const ArgumentList &formalArgs)
4325{
4326 // skip until <
4327 int p=name.find('<');
4328 if (p==-1) return name;
4329 p++;
4330 QCString result = name.left(p);
4331
4332 std::string s = name.mid(p).str();
4333 static const reg::Ex re(R"([\a:][\w:]*)");
4334 reg::Iterator it(s,re);
4336 size_t pi=0;
4337 // for each identifier in the template part (e.g. B<T> -> T)
4338 for (; it!=end ; ++it)
4339 {
4340 const auto &match = *it;
4341 size_t i = match.position();
4342 size_t l = match.length();
4343 result += s.substr(pi,i-pi);
4344 QCString n(match.str());
4345 bool found=FALSE;
4346 for (const Argument &formArg : formalArgs)
4347 {
4348 if (formArg.name == n)
4349 {
4350 found=TRUE;
4351 break;
4352 }
4353 }
4354 if (!found)
4355 {
4356 // try to resolve the type
4357 SymbolResolver resolver;
4358 const ClassDef *cd = resolver.resolveClass(context,n);
4359 if (cd)
4360 {
4361 result+=cd->name();
4362 }
4363 else
4364 {
4365 result+=n;
4366 }
4367 }
4368 else
4369 {
4370 result+=n;
4371 }
4372 pi=i+l;
4373 }
4374 result+=s.substr(pi);
4375 //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",qPrint(name),qPrint(result));
4376 return removeRedundantWhiteSpace(result);
4377}
4378
4379
4380/*! Substitutes any occurrence of a formal argument from argument list
4381 * \a formalArgs in \a name by the corresponding actual argument in
4382 * argument list \a actualArgs. The result after substitution
4383 * is returned as a string. The argument \a name is used to
4384 * prevent recursive substitution.
4385 */
4387 const QCString &nm,
4388 const ArgumentList &formalArgs,
4389 const ArgumentList *actualArgs)
4390{
4391 AUTO_TRACE("name={} formalArgs={} actualArgs={}",nm,argListToString(formalArgs),actualArgs ? argListToString(*actualArgs) : QCString());
4392 if (formalArgs.empty()) return nm;
4393 QCString result;
4394
4395 static const reg::Ex re(R"(\a\w*)");
4396 std::string name = nm.str();
4397 reg::Iterator it(name,re);
4399 size_t p=0;
4400
4401 for (; it!=end ; ++it)
4402 {
4403 const auto &match = *it;
4404 size_t i = match.position();
4405 size_t l = match.length();
4406 if (i>p) result += name.substr(p,i-p);
4407 QCString n(match.str());
4409 if (actualArgs)
4410 {
4411 actIt = actualArgs->begin();
4412 }
4413 //printf(": name=%s\n",qPrint(name));
4414
4415 // if n is a template argument, then we substitute it
4416 // for its template instance argument.
4417 bool found=FALSE;
4418 for (auto formIt = formalArgs.begin();
4419 formIt!=formalArgs.end() && !found;
4420 ++formIt
4421 )
4422 {
4423 Argument formArg = *formIt;
4424 Argument actArg;
4425 if (actualArgs && actIt!=actualArgs->end())
4426 {
4427 actArg = *actIt;
4428 }
4429 if (formArg.type.startsWith("class ") && formArg.name.isEmpty())
4430 {
4431 formArg.name = formArg.type.mid(6);
4432 formArg.type = "class";
4433 }
4434 else if (formArg.type.startsWith("typename ") && formArg.name.isEmpty())
4435 {
4436 formArg.name = formArg.type.mid(9);
4437 formArg.type = "typename";
4438 }
4439 else if (formArg.type.startsWith("class...")) // match 'class... name' to 'name...'
4440 {
4441 formArg.name += "...";
4442 formArg.type = formArg.type.left(5)+formArg.type.mid(8);
4443 }
4444 else if (formArg.type.startsWith("typename...")) // match 'typename... name' to 'name...'
4445 {
4446 formArg.name += "...";
4447 formArg.type = formArg.type.left(8)+formArg.type.mid(11);
4448 }
4449 //printf(": n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s' actArg->type='%s' actArg->name='%s' \n",
4450 // qPrint(n),qPrint(formArg.type),qPrint(formArg.name),qPrint(formArg.defval),qPrint(actArg.type),qPrint(actArg.name));
4451 if (formArg.type=="class" || formArg.type=="typename" || formArg.type.startsWith("template"))
4452 {
4453 if (formArg.name==n && actualArgs && actIt!=actualArgs->end() && !actArg.type.isEmpty()) // base class is a template argument
4454 {
4455 static constexpr auto hasRecursion = [](const QCString &prefix,const QCString &nameArg,const QCString &subst) -> bool
4456 {
4457 int ii=0;
4458 int pp=0;
4459
4460 ii = subst.find('<');
4461 //printf("prefix='%s' subst='%s'\n",qPrint(prefix.mid(prefix.length()-ii-2,ii+1)),qPrint(subst.left(ii+1)));
4462 if (ii!=-1 && static_cast<int>(prefix.length())>=ii+2 && prefix.mid(prefix.length()-ii-2,ii+1)==subst.left(ii+1))
4463 {
4464 return true; // don't replace 'A< ' with 'A< A<...', see issue #10951
4465 }
4466
4467 while ((ii=subst.find(nameArg,pp))!=-1)
4468 {
4469 bool beforeNonWord = ii==0 || !isId(subst.at(ii-1));
4470 bool afterNonWord = subst.length()==ii+nameArg.length() || !isId(subst.at(ii+nameArg.length()));
4471 if (beforeNonWord && afterNonWord)
4472 {
4473 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
4474 }
4475 pp=ii+static_cast<int>(nameArg.length());
4476 }
4477 return false;
4478 };
4479 // replace formal argument with the actual argument of the instance
4480 AUTO_TRACE_ADD("result={} n={} type={} hasRecursion={}",result,n,actArg.type,hasRecursion(result,n,actArg.type));
4481 if (!hasRecursion(result,n,actArg.type))
4482 // the scope guard is to prevent recursive lockup for
4483 // template<class A> class C : public<A::T>,
4484 // where A::T would become A::T::T here,
4485 // since n==A and actArg->type==A::T
4486 // see bug595833 for an example
4487 //
4488 // Also prevent recursive substitution if n is part of actArg.type, i.e.
4489 // n='A' in argType='S< A >' would produce 'S< S< A > >'
4490 {
4491 if (actArg.name.isEmpty())
4492 {
4493 result += actArg.type;
4494 }
4495 else
4496 // for case where the actual arg is something like "unsigned int"
4497 // the "int" part is in actArg->name.
4498 {
4499 result += actArg.type+" "+actArg.name;
4500 }
4501 found=TRUE;
4502 }
4503 }
4504 else if (formArg.name==n &&
4505 (actualArgs==nullptr || actIt==actualArgs->end()) &&
4506 !formArg.defval.isEmpty() &&
4507 formArg.defval!=nm /* to prevent recursion */
4508 )
4509 {
4510 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
4511 found=TRUE;
4512 }
4513 }
4514 else if (formArg.name==n &&
4515 (actualArgs==nullptr || actIt==actualArgs->end()) &&
4516 !formArg.defval.isEmpty() &&
4517 formArg.defval!=nm /* to prevent recursion */
4518 )
4519 {
4520 result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs);
4521 found=TRUE;
4522 }
4523 if (actualArgs && actIt!=actualArgs->end())
4524 {
4525 actIt++;
4526 }
4527 }
4528 if (!found)
4529 {
4530 result += n;
4531 }
4532 p=i+l;
4533 }
4534 result+=name.substr(p);
4535 result=result.simplifyWhiteSpace();
4536 AUTO_TRACE_EXIT("result={}",result);
4537 return result.stripWhiteSpace();
4538}
4539
4540
4541/*! Strips template specifiers from scope \a fullName, except those
4542 * that make up specialized classes. The switch \a parentOnly
4543 * determines whether or not a template "at the end" of a scope
4544 * should be considered, e.g. with \a parentOnly is \c TRUE, `A<T>::B<S>` will
4545 * try to strip `<T>` and not `<S>`, while \a parentOnly is \c FALSE will
4546 * strip both unless `A<T>` or `B<S>` are specialized template classes.
4547 */
4549 bool parentOnly,
4550 QCString *pLastScopeStripped,
4551 QCString scopeName,
4552 bool allowArtificial)
4553{
4554 //printf("stripTemplateSpecifiersFromScope(name=%s,scopeName=%s)\n",qPrint(fullName),qPrint(scopeName));
4555 int i=fullName.find('<');
4556 if (i==-1) return fullName;
4557 QCString result;
4558 int p=0;
4559 int l=static_cast<int>(fullName.length());
4560 while (i!=-1)
4561 {
4562 //printf("1:result+=%s\n",qPrint(fullName.mid(p,i-p)));
4563 int e=i+1;
4564 int count=1;
4565 int round=0;
4566 while (e<l && count>0)
4567 {
4568 char c=fullName.at(e++);
4569 switch (c)
4570 {
4571 case '(': round++; break;
4572 case ')': if (round>0) round--; break;
4573 case '<': if (round==0) count++; break;
4574 case '>': if (round==0) count--; break;
4575 default:
4576 break;
4577 }
4578 }
4579 int si= fullName.find("::",e);
4580
4581 if (parentOnly && si==-1) break;
4582 // we only do the parent scope, so we stop here if needed
4583
4584 result+=fullName.mid(p,i-p);
4585 //printf(" trying %s\n",qPrint(mergeScopes(scopeName,result+fullName.mid(i,e-i))));
4586 ClassDef *cd = getClass(mergeScopes(scopeName,result+fullName.mid(i,e-i)));
4587 if (cd!=nullptr && (allowArtificial || !cd->isArtificial()))
4588 {
4589 result+=fullName.mid(i,e-i);
4590 //printf(" 2:result+=%s\n",qPrint(fullName.mid(i,e-i-1)));
4591 }
4592 else if (pLastScopeStripped)
4593 {
4594 //printf(" last stripped scope '%s'\n",qPrint(fullName.mid(i,e-i)));
4595 *pLastScopeStripped=fullName.mid(i,e-i);
4596 }
4597 p=e;
4598 i=fullName.find('<',p);
4599 }
4600 result+=fullName.right(l-p);
4601 //printf("3:result+=%s\n",qPrint(fullName.right(l-p)));
4602 //printf("end result=%s\n",qPrint(result));
4603 return result;
4604}
4605
4606/*! Merges two scope parts together. The parts may (partially) overlap.
4607 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
4608 * Example2: \c A and \c B will be \c A::B <br>
4609 * Example3: \c A::B and B will be \c A::B
4610 *
4611 * @param leftScope the left hand part of the scope.
4612 * @param rightScope the right hand part of the scope.
4613 * @returns the merged scope.
4614 */
4615QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
4616{
4617 AUTO_TRACE("leftScope='{}' rightScope='{}'",leftScope,rightScope);
4618 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
4619 if (leftScopeMatch(leftScope,rightScope))
4620 {
4621 AUTO_TRACE_EXIT("case1={}",rightScope);
4622 return rightScope;
4623 }
4624 QCString result;
4625 int i=0,p=static_cast<int>(leftScope.length());
4626
4627 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
4628 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
4629 bool found=FALSE;
4630 while ((i=leftScope.findRev("::",p))>0)
4631 {
4632 if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
4633 {
4634 result = leftScope.left(i+2)+rightScope;
4635 found=TRUE;
4636 }
4637 p=i-1;
4638 }
4639 if (found)
4640 {
4641 AUTO_TRACE_EXIT("case2={}",result);
4642 return result;
4643 }
4644
4645 // case leftScope=="A" rightScope=="B" => result = "A::B"
4646 result=leftScope;
4647 if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
4648 result+=rightScope;
4649 AUTO_TRACE_EXIT("case3={}",result);
4650 return result;
4651}
4652
4653/*! Returns a fragment from scope \a s, starting at position \a p.
4654 *
4655 * @param s the scope name as a string.
4656 * @param p the start position (0 is the first).
4657 * @param l the resulting length of the fragment.
4658 * @returns the location of the fragment, or -1 if non is found.
4659 */
4660int getScopeFragment(const QCString &s,int p,int *l)
4661{
4662 int sl=static_cast<int>(s.length());
4663 int sp=p;
4664 int count=0;
4665 bool done=false;
4666 if (sp>=sl) return -1;
4667 while (sp<sl)
4668 {
4669 char c=s.at(sp);
4670 if (c==':')
4671 {
4672 sp++;
4673 p++;
4674 }
4675 else
4676 {
4677 break;
4678 }
4679 }
4680 while (sp<sl)
4681 {
4682 char c=s.at(sp);
4683 switch (c)
4684 {
4685 case ':': // found next part
4686 goto found;
4687 case '<': // skip template specifier
4688 count=1;sp++;
4689 done=false;
4690 while (sp<sl && !done)
4691 {
4692 // TODO: deal with << and >> operators!
4693 c=s.at(sp++);
4694 switch(c)
4695 {
4696 case '<': count++; break;
4697 case '>': count--; if (count==0) done=true; break;
4698 default: break;
4699 }
4700 }
4701 break;
4702 default:
4703 sp++;
4704 break;
4705 }
4706 }
4707found:
4708 *l=sp-p;
4709 //printf("getScopeFragment(%s,%d)=%s\n",qPrint(s),p,qPrint(s.mid(p,*l)));
4710 return p;
4711}
4712
4713//----------------------------------------------------------------------------
4714
4715PageDef *addRelatedPage(const QCString &name,const QCString &ptitle,
4716 const QCString &doc,
4717 const QCString &fileName,
4718 int docLine,
4719 int startLine,
4720 const RefItemVector &sli,
4721 GroupDef *gd,
4722 const TagInfo *tagInfo,
4723 bool xref,
4724 SrcLangExt lang
4725 )
4726{
4727 PageDef *pd=nullptr;
4728 //printf("addRelatedPage(name=%s gd=%p)\n",qPrint(name),gd);
4729 QCString title=ptitle.stripWhiteSpace();
4730 bool newPage = true;
4731 if ((pd=Doxygen::pageLinkedMap->find(name)) && !pd->isReference())
4732 {
4733 if (!xref && !title.isEmpty() && pd->title()!=pd->name() && pd->title()!=title)
4734 {
4735 warn(fileName,startLine,"multiple use of page label '{}' with different titles, (other occurrence: {}, line: {})",
4736 name,pd->docFile(),pd->getStartBodyLine());
4737 }
4738 if (!title.isEmpty() && pd->title()==pd->name()) // pd has no real title yet
4739 {
4740 pd->setTitle(title);
4742 if (si)
4743 {
4744 si->setTitle(title);
4745 }
4746 }
4747 // append documentation block to the page.
4748 pd->setDocumentation(doc,fileName,docLine);
4749 //printf("Adding page docs '%s' pi=%p name=%s\n",qPrint(doc),pd,name);
4750 // append (x)refitems to the page.
4751 pd->setRefItems(sli);
4752 newPage = false;
4753 }
4754
4755 if (newPage) // new page
4756 {
4757 QCString baseName=name;
4758 if (baseName.endsWith(".tex"))
4759 baseName=baseName.left(baseName.length()-4);
4760 else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
4761 baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
4762
4763 //printf("Appending page '%s'\n",qPrint(baseName));
4764 if (pd) // replace existing page
4765 {
4766 pd->setDocumentation(doc,fileName,docLine);
4768 pd->setShowLineNo(FALSE);
4769 pd->setNestingLevel(0);
4770 pd->setPageScope(nullptr);
4771 pd->setTitle(title);
4772 pd->setReference(QCString());
4773 }
4774 else // newPage
4775 {
4776 pd = Doxygen::pageLinkedMap->add(baseName,
4777 createPageDef(fileName,docLine,baseName,doc,title));
4778 }
4779 pd->setBodySegment(startLine,startLine,-1);
4780
4781 pd->setRefItems(sli);
4782 pd->setLanguage(lang);
4783
4784 if (tagInfo)
4785 {
4786 pd->setReference(tagInfo->tagName);
4787 pd->setFileName(tagInfo->fileName);
4788 }
4789
4790 if (gd) gd->addPage(pd);
4791
4792 if (pd->hasTitle())
4793 {
4794 //outputList->writeTitle(pi->name,pi->title);
4795
4796 // a page name is a label as well!
4797 QCString file;
4798 QCString orgFile;
4799 int line = -1;
4800 if (gd)
4801 {
4802 file=gd->getOutputFileBase();
4803 orgFile=gd->getOutputFileBase();
4804 }
4805 else
4806 {
4807 file=pd->getOutputFileBase();
4808 orgFile=pd->docFile();
4809 line = pd->getStartBodyLine();
4810 }
4811 const SectionInfo *si = SectionManager::instance().find(pd->name());
4812 if (si)
4813 {
4814 if (!si->ref().isEmpty()) // we are from a tag file
4815 {
4817 file,-1,pd->title(),SectionType::Page,0,pd->getReference());
4818 }
4819 else if (si->lineNr() != -1)
4820 {
4821 warn(orgFile,line,"multiple use of section label '{}', (first occurrence: {}, line {})",pd->name(),si->fileName(),si->lineNr());
4822 }
4823 else
4824 {
4825 warn(orgFile,line,"multiple use of section label '{}', (first occurrence: {})",pd->name(),si->fileName());
4826 }
4827 }
4828 else
4829 {
4831 file,-1,pd->title(),SectionType::Page,0,pd->getReference());
4832 //printf("si->label='%s' si->definition=%s si->fileName='%s'\n",
4833 // qPrint(si->label),si->definition?si->definition->name().data():"<none>",
4834 // qPrint(si->fileName));
4835 //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,qPrint(si->fileName));
4836 //printf("Adding section key=%s si->fileName=%s\n",qPrint(pageName),qPrint(si->fileName));
4837 }
4838 }
4839 }
4840 return pd;
4841}
4842
4843//----------------------------------------------------------------------------
4844
4846 const QCString &key, const QCString &prefix, const QCString &name,
4847 const QCString &title, const QCString &args, const Definition *scope)
4848{
4849 //printf("addRefItem(sli=%d,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",(int)sli.size(),key,prefix,name,title,args);
4850 if (!key.isEmpty() && key[0]!='@') // check for @ to skip anonymous stuff (see bug427012)
4851 {
4852 for (RefItem *item : sli)
4853 {
4854 item->setPrefix(prefix);
4855 item->setScope(scope);
4856 item->setName(name);
4857 item->setTitle(title);
4858 item->setArgs(args);
4859 item->setGroup(key);
4860 }
4861 }
4862}
4863
4865{
4866 ModuleDef *mod = nullptr;
4868 {
4869 const FileDef *fd = toFileDef(d);
4870 if (fd) mod = fd->getModuleDef();
4871 }
4873 {
4874 const ClassDef *cd = toClassDef(d);
4875 if (cd)
4876 {
4877 const FileDef *fd = cd->getFileDef();
4878 if (fd) mod = fd->getModuleDef();
4879 }
4880 }
4882 {
4883 const ConceptDef *cd = toConceptDef(d);
4884 if (cd)
4885 {
4886 const FileDef *fd = cd->getFileDef();
4887 if (fd) mod = fd->getModuleDef();
4888 }
4889 }
4890 return mod;
4891}
4892
4893static bool recursivelyAddGroupListToTitle(OutputList &ol,const Definition *d,bool root)
4894{
4895 ModuleDef *mod = root ? findModuleDef(d) : nullptr;
4896 if (!d->partOfGroups().empty() || mod!=nullptr) // write list of group to which this definition belongs
4897 {
4898 if (root)
4899 {
4900 ol.pushGeneratorState();
4902 ol.writeString("<div class=\"ingroups\">");
4903 }
4904 bool first=true;
4905 for (const auto &gd : d->partOfGroups())
4906 {
4907 if (!first) { ol.writeString(" &#124; "); } else first=false;
4909 {
4910 ol.writeString(" &raquo; ");
4911 }
4912 ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),QCString(),gd->groupTitle());
4913 }
4914 if (root)
4915 {
4916 // add module as a group to the file as well
4917 if (mod)
4918 {
4919 if (!first) { ol.writeString(" &#124; "); } else first=false;
4920 ol.writeString(theTranslator->trModule(false,true)+" ");
4922 mod->displayName());
4923 }
4924 ol.writeString("</div>");
4925 ol.popGeneratorState();
4926 }
4927 return true;
4928 }
4929 return false;
4930}
4931
4936
4937bool checkExtension(const QCString &fName, const QCString &ext)
4938{
4939 return fName.right(ext.length())==ext;
4940}
4941
4943{
4944 if (fName.isEmpty()) return;
4945 int i_fs = fName.findRev('/');
4946 int i_bs = fName.findRev('\\');
4947 int i = fName.find('.',std::max({ i_fs, i_bs ,0})); // search for . after path part
4948 if (i==-1)
4949 {
4951 }
4952}
4953
4955{
4956 QCString result=fName;
4957 if (result.right(ext.length())==ext)
4958 {
4959 result=result.left(result.length()-ext.length());
4960 }
4961 return result;
4962}
4963
4968
4970{
4971 QCString result=s;
4972 int i=result.findRev('/');
4973 if (i!=-1)
4974 {
4975 result=result.mid(i+1);
4976 }
4977 i=result.findRev('\\');
4978 if (i!=-1)
4979 {
4980 result=result.mid(i+1);
4981 }
4982 return result;
4983}
4984
4985QCString makeBaseName(const QCString &name, const QCString &ext)
4986{
4987 return stripExtensionGeneral(stripPath(name), ext);
4988}
4989
4990/** returns \c TRUE iff string \a s contains word \a w */
4991bool containsWord(const QCString &str,const char *word)
4992{
4993 if (str.isEmpty() || word==nullptr) return false;
4994 static const reg::Ex re(R"(\a+)");
4995 std::string s = str.str();
4996 for (reg::Iterator it(s,re) ; it!=reg::Iterator() ; ++it)
4997 {
4998 if (it->str()==word) return true;
4999 }
5000 return false;
5001}
5002
5003/** removes occurrences of whole \a word from \a sentence,
5004 * while keeps internal spaces and reducing multiple sequences of spaces.
5005 * Example: sentence=` cat+ catfish cat cat concat cat`, word=`cat` returns: `+ catfish concat`
5006 */
5007bool findAndRemoveWord(QCString &sentence,const char *word)
5008{
5009 static reg::Ex re(R"(\s*(<\a+>)\s*)");
5010 std::string s = sentence.str();
5011 reg::Iterator it(s,re);
5013 std::string result;
5014 bool found=false;
5015 size_t p=0;
5016 for ( ; it!=end ; ++it)
5017 {
5018 const auto match = *it;
5019 std::string part = match[1].str();
5020 if (part!=word)
5021 {
5022 size_t i = match.position();
5023 size_t l = match.length();
5024 result+=s.substr(p,i-p);
5025 result+=match.str();
5026 p=i+l;
5027 }
5028 else
5029 {
5030 found=true;
5031 size_t i = match[1].position();
5032 size_t l = match[1].length();
5033 result+=s.substr(p,i-p);
5034 p=i+l;
5035 }
5036 }
5037 result+=s.substr(p);
5038 sentence = QCString(result).simplifyWhiteSpace();
5039 return found;
5040}
5041
5042/** Special version of QCString::stripWhiteSpace() that only strips
5043 * completely blank lines.
5044 * @param s the string to be stripped
5045 * @param docLine the line number corresponding to the start of the
5046 * string. This will be adjusted based on the number of lines stripped
5047 * from the start.
5048 * @returns The stripped string.
5049 */
5051{
5052 if (s.isEmpty()) return QCString();
5053 const char *p = s.data();
5054
5055 // search for leading empty lines
5056 int i=0,li=-1,l=static_cast<int>(s.length());
5057 char c = 0;
5058 while ((c=*p))
5059 {
5060 if (c==' ' || c=='\t' || c=='\r') { i++; p++; }
5061 else if (c=='\\' && literal_at(p,"\\ilinebr")) { i+=8; li=i; p+=8; }
5062 else if (c=='\n') { i++; li=i; docLine++; p++; }
5063 else break;
5064 }
5065
5066 // search for trailing empty lines
5067 int b=l-1,bi=-1;
5068 p=s.data()+b;
5069 while (b>=0)
5070 {
5071 c=*p;
5072 if (c==' ' || c=='\t' || c=='\r') { b--; p--; }
5073 else if (c=='r' && b>=7 && literal_at(p-7,"\\ilinebr")) { bi=b-7; b-=8; p-=8; }
5074 else if (c=='>' && b>=11 && literal_at(p-11,"\\ilinebr<br>")) { bi=b-11; b-=12; p-=12; }
5075 else if (c=='\n') { bi=b; b--; p--; }
5076 else break;
5077 }
5078
5079 // return whole string if no leading or trailing lines where found
5080 if (li==-1 && bi==-1) return s;
5081
5082 // return substring
5083 if (bi==-1) bi=l;
5084 if (li==-1) li=0;
5085 if (bi<=li) return QCString(); // only empty lines
5086 //printf("docLine='%s' len=%d li=%d bi=%d\n",qPrint(s),s.length(),li,bi);
5087 return s.mid(li,bi-li);
5088}
5089
5090//--------------------------------------------------------------------------
5091
5092static std::unordered_map<std::string,SrcLangExt> g_extLookup;
5093
5095{
5096 const char *langName;
5097 const char *parserName;
5099 const char *defExt;
5100};
5101
5102static std::vector<Lang2ExtMap> g_lang2extMap =
5103{
5104// language parser parser option
5105 { "idl", "c", SrcLangExt::IDL, ".idl" },
5106 { "java", "c", SrcLangExt::Java, ".java"},
5107 { "javascript", "c", SrcLangExt::JS, ".js" },
5108 { "csharp", "c", SrcLangExt::CSharp, ".cs" },
5109 { "d", "c", SrcLangExt::D, ".d" },
5110 { "php", "c", SrcLangExt::PHP, ".php" },
5111 { "objective-c", "c", SrcLangExt::ObjC, ".m" },
5112 { "c", "c", SrcLangExt::Cpp, ".c" },
5113 { "c++", "c", SrcLangExt::Cpp, ".cpp" },
5114 { "slice", "c", SrcLangExt::Slice, ".ice" },
5115 { "python", "python", SrcLangExt::Python, ".py" },
5116 { "fortran", "fortran", SrcLangExt::Fortran, ".f" },
5117 { "fortranfree", "fortranfree", SrcLangExt::Fortran, ".f90" },
5118 { "fortranfixed", "fortranfixed", SrcLangExt::Fortran, ".f" },
5119 { "vhdl", "vhdl", SrcLangExt::VHDL, ".vhdl"},
5120 { "xml", "xml", SrcLangExt::XML, ".xml" },
5121 { "sql", "sql", SrcLangExt::SQL, ".sql" },
5122 { "md", "md", SrcLangExt::Markdown, ".md" },
5123 { "lex", "lex", SrcLangExt::Lex, ".l" },
5124};
5125
5126bool updateLanguageMapping(const QCString &extension,const QCString &language)
5127{
5128 QCString langName = language.lower();
5129 auto it1 = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
5130 [&langName](const auto &info) { return info.langName==langName; });
5131 if (it1 == g_lang2extMap.end()) return false;
5132
5133 // found the language
5134 SrcLangExt parserId = it1->parserId;
5135 QCString extName = extension.lower();
5136 if (extName.isEmpty()) return FALSE;
5137 if (extName.at(0)!='.') extName.prepend(".");
5138 auto it2 = g_extLookup.find(extName.str());
5139 if (it2!=g_extLookup.end())
5140 {
5141 g_extLookup.erase(it2); // language was already register for this ext
5142 }
5143 //printf("registering extension %s\n",qPrint(extName));
5144 g_extLookup.emplace(extName.str(),parserId);
5145 if (!Doxygen::parserManager->registerExtension(extName,it1->parserName))
5146 {
5147 err("Failed to assign extension {} to parser {} for language {}\n",
5148 extName.data(),it1->parserName,language);
5149 }
5150 else
5151 {
5152 //msg("Registered extension {} to language parser {}...\n",
5153 // extName,language);
5154 }
5155 return TRUE;
5156}
5157
5159{
5160 // NOTE: when adding an extension, also add the extension in config.xml
5161 // extension parser id
5162 updateLanguageMapping(".dox", "c");
5163 updateLanguageMapping(".txt", "c"); // see bug 760836
5164 updateLanguageMapping(".doc", "c");
5165 updateLanguageMapping(".c", "c");
5166 updateLanguageMapping(".C", "c");
5167 updateLanguageMapping(".cc", "c");
5168 updateLanguageMapping(".CC", "c");
5169 updateLanguageMapping(".cxx", "c");
5170 updateLanguageMapping(".cpp", "c");
5171 updateLanguageMapping(".c++", "c");
5172 updateLanguageMapping(".cxxm", "c"); // C++20 modules
5173 updateLanguageMapping(".cppm", "c"); // C++20 modules
5174 updateLanguageMapping(".ccm", "c"); // C++20 modules
5175 updateLanguageMapping(".c++m", "c"); // C++20 modules
5176 updateLanguageMapping(".ii", "c");
5177 updateLanguageMapping(".ixx", "c");
5178 updateLanguageMapping(".ipp", "c");
5179 updateLanguageMapping(".i++", "c");
5180 updateLanguageMapping(".inl", "c");
5181 updateLanguageMapping(".h", "c");
5182 updateLanguageMapping(".H", "c");
5183 updateLanguageMapping(".hh", "c");
5184 updateLanguageMapping(".HH", "c");
5185 updateLanguageMapping(".hxx", "c");
5186 updateLanguageMapping(".hpp", "c");
5187 updateLanguageMapping(".h++", "c");
5188 updateLanguageMapping(".idl", "idl");
5189 updateLanguageMapping(".ddl", "idl");
5190 updateLanguageMapping(".odl", "idl");
5191 updateLanguageMapping(".java", "java");
5192 //updateLanguageMapping(".as", "javascript"); // not officially supported
5193 //updateLanguageMapping(".js", "javascript"); // not officially supported
5194 updateLanguageMapping(".cs", "csharp");
5195 updateLanguageMapping(".d", "d");
5196 updateLanguageMapping(".php", "php");
5197 updateLanguageMapping(".php4", "php");
5198 updateLanguageMapping(".php5", "php");
5199 updateLanguageMapping(".inc", "php");
5200 updateLanguageMapping(".phtml", "php");
5201 updateLanguageMapping(".m", "objective-c");
5202 updateLanguageMapping(".M", "objective-c");
5203 updateLanguageMapping(".mm", "c"); // see bug746361
5204 updateLanguageMapping(".py", "python");
5205 updateLanguageMapping(".pyw", "python");
5206 updateLanguageMapping(".f", "fortran");
5207 updateLanguageMapping(".for", "fortran");
5208 updateLanguageMapping(".f90", "fortran");
5209 updateLanguageMapping(".f95", "fortran");
5210 updateLanguageMapping(".f03", "fortran");
5211 updateLanguageMapping(".f08", "fortran");
5212 updateLanguageMapping(".f18", "fortran");
5213 updateLanguageMapping(".vhd", "vhdl");
5214 updateLanguageMapping(".vhdl", "vhdl");
5215 updateLanguageMapping(".ucf", "vhdl");
5216 updateLanguageMapping(".qsf", "vhdl");
5217 updateLanguageMapping(".md", "md");
5218 updateLanguageMapping(".markdown", "md");
5219 updateLanguageMapping(".ice", "slice");
5220 updateLanguageMapping(".l", "lex");
5221 updateLanguageMapping(".doxygen_lex_c", "c"); // this is a placeholder so we can map initializations
5222 // in the lex scanning to cpp
5223}
5224
5226{
5227 updateLanguageMapping(".xml", "xml");
5228 updateLanguageMapping(".sql", "sql");
5229}
5230
5232{
5233 FileInfo fi(fileName.str());
5234 // we need only the part after the last ".", newer implementations of FileInfo have 'suffix()' for this.
5235 QCString extName = QCString(fi.extension(FALSE)).lower();
5236 if (extName.isEmpty()) extName=".no_extension";
5237 if (extName.at(0)!='.') extName.prepend(".");
5238 auto it = g_extLookup.find(extName.str());
5239 if (it!=g_extLookup.end()) // listed extension
5240 {
5241 //printf("getLanguageFromFileName(%s)=%x\n",qPrint(fi.extension()),*pVal);
5242 return it->second;
5243 }
5244 //printf("getLanguageFromFileName(%s) not found!\n",qPrint(fileName));
5245 return defLang; // not listed => assume C-ish language.
5246}
5247
5248/// Routine to handle the language attribute of the `\code` command
5250{
5251 // try the extension
5252 auto lang = getLanguageFromFileName(fileName, SrcLangExt::Unknown);
5253 if (lang == SrcLangExt::Unknown)
5254 {
5255 // try the language names
5256 QCString langName = fileName.lower();
5257 if (langName.at(0)=='.') langName = langName.mid(1);
5258 auto it = std::find_if(g_lang2extMap.begin(),g_lang2extMap.end(),
5259 [&langName](const auto &info) { return info.langName==langName; });
5260 if (it != g_lang2extMap.end())
5261 {
5262 lang = it->parserId;
5263 fileName = it->defExt;
5264 }
5265 else // default to C++
5266 {
5267 return SrcLangExt::Cpp;
5268 }
5269 }
5270 return lang;
5271}
5272
5274{
5275 if (fn.isEmpty()) return "";
5276 int lastDot = fn.findRev('.');
5277 if (lastDot!=-1) return fn.mid(lastDot);
5278 return "";
5279}
5280
5281//--------------------------------------------------------------------------
5282
5283static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
5284 const QCString &n)
5285{
5286 if (scope==nullptr ||
5289 )
5290 )
5291 {
5293 }
5294
5295 QCString name = n;
5296 if (name.isEmpty())
5297 return nullptr; // no name was given
5298
5299 auto &range = Doxygen::symbolMap->find(name);
5300 if (range.empty())
5301 return nullptr; // could not find any matching symbols
5302
5303 // mostly copied from getResolvedClassRec()
5304 QCString explicitScopePart;
5305 int qualifierIndex = computeQualifiedIndex(name);
5306 if (qualifierIndex!=-1)
5307 {
5308 explicitScopePart = name.left(qualifierIndex);
5309 replaceNamespaceAliases(explicitScopePart);
5310 name = name.mid(qualifierIndex+2);
5311 }
5312 //printf("explicitScopePart=%s\n",qPrint(explicitScopePart));
5313
5314 int minDistance = 10000;
5315 MemberDef *bestMatch = nullptr;
5316
5317 for (Definition *d : range)
5318 {
5319 if (d->definitionType()==Definition::TypeMember)
5320 {
5321 SymbolResolver resolver(fileScope);
5322 int distance = resolver.isAccessibleFromWithExpScope(scope,d,explicitScopePart);
5323 if (distance!=-1 && distance<minDistance)
5324 {
5325 minDistance = distance;
5326 bestMatch = toMemberDef(d);
5327 //printf("new best match %s distance=%d\n",qPrint(bestMatch->qualifiedName()),distance);
5328 }
5329 }
5330 }
5331 return bestMatch;
5332}
5333
5334/*! Returns true iff the given name string appears to be a typedef in scope. */
5335bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const QCString &n)
5336{
5337 MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
5338
5339 if (bestMatch && bestMatch->isTypedef())
5340 return TRUE; // closest matching symbol is a typedef
5341 else
5342 return FALSE;
5343}
5344
5345static int nextUTF8CharPosition(const QCString &utf8Str,uint32_t len,uint32_t startPos)
5346{
5347 if (startPos>=len) return len;
5348 uint8_t c = static_cast<uint8_t>(utf8Str[startPos]);
5349 int bytes=getUTF8CharNumBytes(c);
5350 if (c=='&') // skip over character entities
5351 {
5352 bytes=1;
5353 int (*matcher)(int) = nullptr;
5354 c = static_cast<uint8_t>(utf8Str[startPos+bytes]);
5355 if (c=='#') // numerical entity?
5356 {
5357 bytes++;
5358 c = static_cast<uint8_t>(utf8Str[startPos+bytes]);
5359 if (c=='x') // hexadecimal entity?
5360 {
5361 bytes++;
5362 matcher = std::isxdigit;
5363 }
5364 else // decimal entity
5365 {
5366 matcher = std::isdigit;
5367 }
5368 }
5369 else if (std::isalnum(c)) // named entity?
5370 {
5371 bytes++;
5372 matcher = std::isalnum;
5373 }
5374 if (matcher)
5375 {
5376 while ((c = static_cast<uint8_t>(utf8Str[startPos+bytes]))!=0 && matcher(c))
5377 {
5378 bytes++;
5379 }
5380 }
5381 if (c!=';')
5382 {
5383 bytes=1; // not a valid entity, reset bytes counter
5384 }
5385 }
5386 return startPos+bytes;
5387}
5388
5390 const QCString &doc,const QCString &fileName,int lineNr)
5391{
5392 if (doc.isEmpty()) return "";
5393 //printf("parseCommentAsText(%s)\n",qPrint(doc));
5394 TextStream t;
5395 auto parser { createDocParser() };
5396 auto ast { validatingParseDoc(*parser.get(),
5397 fileName,
5398 lineNr,
5399 scope,
5400 md,
5401 doc,
5402 DocOptions()
5403 .setAutolinkSupport(false))
5404 };
5405 auto astImpl = dynamic_cast<const DocNodeAST*>(ast.get());
5406 if (astImpl)
5407 {
5408 TextDocVisitor visitor(t);
5409 std::visit(visitor,astImpl->root);
5410 }
5412 int i=0;
5413 int charCnt=0;
5414 int l=static_cast<int>(result.length());
5415 while ((i=nextUTF8CharPosition(result,l,i))<l)
5416 {
5417 charCnt++;
5418 if (charCnt>=80) break;
5419 }
5420 if (charCnt>=80) // try to truncate the string
5421 {
5422 while ((i=nextUTF8CharPosition(result,l,i))<l && charCnt<100)
5423 {
5424 charCnt++;
5425 if (result.at(i)==',' ||
5426 result.at(i)=='.' ||
5427 result.at(i)=='!' ||
5428 result.at(i)=='?' ||
5429 result.at(i)=='}') // good for UTF-16 characters and } otherwise also a good point to stop the string
5430 {
5431 i++; // we want to be "behind" last inspected character
5432 break;
5433 }
5434 }
5435 }
5436 if ( i < l) result=result.left(i)+"...";
5437 return result.data();
5438}
5439
5440//--------------------------------------------------------------------------------------
5441
5442static std::mutex g_docCacheMutex;
5443static std::unordered_map<std::string,QCString> g_docCache;
5444
5445QCString parseCommentAsHtml(const Definition *scope,const MemberDef *member,const QCString &doc,const QCString &fileName,int lineNr)
5446{
5447 std::lock_guard lock(g_docCacheMutex);
5448 auto it = g_docCache.find(doc.str());
5449 if (it != g_docCache.end())
5450 {
5451 //printf("Cache: [%s]->[%s]\n",qPrint(doc),qPrint(it->second));
5452 return it->second;
5453 }
5454 auto parser { createDocParser() };
5455 auto ast { validatingParseTitle(*parser.get(),fileName,lineNr,doc) };
5456 auto astImpl = dynamic_cast<const DocNodeAST*>(ast.get());
5457 QCString result;
5458 if (astImpl)
5459 {
5460 TextStream t;
5461 OutputCodeList codeList;
5462 codeList.add<HtmlCodeGenerator>(&t);
5463 HtmlDocVisitor visitor(t,codeList,scope,fileName);
5464 std::visit(visitor,astImpl->root);
5465 result = t.str();
5466 }
5467 else // fallback, should not happen
5468 {
5469 result = filterTitle(doc);
5470 }
5471 //printf("Conversion: [%s]->[%s]\n",qPrint(doc),qPrint(result));
5472 g_docCache.insert(std::make_pair(doc.str(),result));
5473 return result;
5474}
5475
5476
5477//--------------------------------------------------------------------------------------
5478
5480{
5481 if (al.empty()) return;
5482 ol.startConstraintList(theTranslator->trTypeConstraints());
5483 for (const Argument &a : al)
5484 {
5486 ol.parseText(a.name);
5487 ol.endConstraintParam();
5489 linkifyText(TextGeneratorOLImpl(ol),a.type,LinkifyTextOptions().setScope(d));
5490 ol.endConstraintType();
5492 ol.generateDoc(d->docFile(),
5493 d->docLine(),
5494 d,
5495 nullptr,
5496 a.docs,
5497 DocOptions()
5498 .setIndexWords(true));
5499 ol.endConstraintDocs();
5500 }
5501 ol.endConstraintList();
5502}
5503
5504//----------------------------------------------------------------------------
5505
5507{
5508#ifdef TRACINGSUPPORT
5509 void *backtraceFrames[128];
5510 int frameCount = backtrace(backtraceFrames, 128);
5511 const size_t cmdLen = 40960;
5512 static char cmd[cmdLen];
5513 char *p = cmd;
5514 p += qsnprintf(p,cmdLen,"/usr/bin/atos -p %d ", (int)getpid());
5515 for (int x = 0; x < frameCount; x++)
5516 {
5517 p += qsnprintf(p,cmdLen,"%p ", backtraceFrames[x]);
5518 }
5519 fprintf(stderr,"========== STACKTRACE START ==============\n");
5520 if (FILE *fp = Portable::popen(cmd, "r"))
5521 {
5522 char resBuf[512];
5523 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
5524 {
5525 fwrite(resBuf, 1, len, stderr);
5526 }
5527 Portable::pclose(fp);
5528 }
5529 fprintf(stderr,"============ STACKTRACE END ==============\n");
5530 //fprintf(stderr,"%s\n", frameStrings[x]);
5531#endif
5532}
5533
5534static void transcodeCharacterBuffer(const QCString &fileName,std::string &contents,
5535 const QCString &inputEncoding,const QCString &outputEncoding)
5536{
5537 if (inputEncoding.isEmpty() || outputEncoding.isEmpty()) return; // no encoding specified
5538 if (qstricmp(inputEncoding,outputEncoding)==0) return; // input encoding same as output encoding
5539 void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data());
5540 if (cd==reinterpret_cast<void *>(-1))
5541 {
5542 term("unsupported character conversion: '{}'->'{}': {}\n"
5543 "Check the INPUT_ENCODING setting in the config file!\n",
5544 inputEncoding,outputEncoding,strerror(errno));
5545 }
5546 size_t iLeft = contents.size();
5547 const char *srcPtr = contents.data();
5548 size_t tmpBufSize = contents.size()*4+1;
5549 size_t oLeft = tmpBufSize;
5550 std::string tmpBuf;
5551 tmpBuf.resize(tmpBufSize);
5552 char *dstPtr = tmpBuf.data();
5553 size_t newSize=0;
5554 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
5555 {
5556 newSize = tmpBufSize-oLeft;
5557 tmpBuf.resize(newSize);
5558 std::swap(contents,tmpBuf);
5559 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,qPrint(srcBuf));
5560 }
5561 else
5562 {
5563 term("{}: failed to translate characters from {} to {}: check INPUT_ENCODING\n",
5564 fileName,inputEncoding,outputEncoding);
5565 }
5567}
5568
5569//! read a file name \a fileName and optionally filter and transcode it
5570bool readInputFile(const QCString &fileName,std::string &contents,bool filter,bool isSourceCode)
5571{
5572 // try to open file
5573 FileInfo fi(fileName.str());
5574 if (!fi.exists()) return FALSE;
5575 QCString filterName = getFileFilter(fileName,isSourceCode);
5576 if (filterName.isEmpty() || !filter)
5577 {
5578 std::ifstream f = Portable::openInputStream(fileName,true);
5579 if (!f.is_open())
5580 {
5581 err("could not open file {}\n",fileName);
5582 return FALSE;
5583 }
5584 // read the file
5585 auto fileSize = fi.size();
5586 contents.resize(fileSize);
5587 f.read(contents.data(),fileSize);
5588 if (f.fail())
5589 {
5590 err("problems while reading file {}\n",fileName);
5591 return FALSE;
5592 }
5593 }
5594 else
5595 {
5596 QCString cmd=filterName+" \""+fileName+"\"";
5597 Debug::print(Debug::ExtCmd,0,"Executing popen(`{}`)\n",cmd);
5598 FILE *f=Portable::popen(cmd,"r");
5599 if (!f)
5600 {
5601 err("could not execute filter {}\n",filterName);
5602 return FALSE;
5603 }
5604 const int bufSize=4096;
5605 char buf[bufSize];
5606 int numRead = 0;
5607 while ((numRead=static_cast<int>(fread(buf,1,bufSize,f)))>0)
5608 {
5609 //printf(">>>>>>>>Reading %d bytes\n",numRead);
5610 contents.append(buf,numRead);
5611 }
5613 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
5614 Debug::print(Debug::FilterOutput,0,"-------------\n{}\n-------------\n",contents);
5615 }
5616
5617 if (contents.size()>=2 &&
5618 static_cast<uint8_t>(contents[0])==0xFF &&
5619 static_cast<uint8_t>(contents[1])==0xFE // Little endian BOM
5620 ) // UCS-2LE encoded file
5621 {
5622 transcodeCharacterBuffer(fileName,contents,"UCS-2LE","UTF-8");
5623 }
5624 else if (contents.size()>=2 &&
5625 static_cast<uint8_t>(contents[0])==0xFE &&
5626 static_cast<uint8_t>(contents[1])==0xFF // big endian BOM
5627 ) // UCS-2BE encoded file
5628 {
5629 transcodeCharacterBuffer(fileName,contents,"UCS-2BE","UTF-8");
5630 }
5631 else if (contents.size()>=3 &&
5632 static_cast<uint8_t>(contents[0])==0xEF &&
5633 static_cast<uint8_t>(contents[1])==0xBB &&
5634 static_cast<uint8_t>(contents[2])==0xBF
5635 ) // UTF-8 encoded file
5636 {
5637 contents.erase(0,3); // remove UTF-8 BOM: no translation needed
5638 }
5639 else // transcode according to the INPUT_ENCODING setting
5640 {
5641 // do character transcoding if needed.
5642 transcodeCharacterBuffer(fileName,contents,getEncoding(fi),"UTF-8");
5643 }
5644
5645 filterCRLF(contents);
5646 return true;
5647}
5648
5649// Replace %word by word in title
5651{
5652 std::string tf;
5653 std::string t = title.str();
5654 static const reg::Ex re(R"(%[a-z_A-Z]+)");
5655 reg::Iterator it(t,re);
5657 size_t p = 0;
5658 for (; it!=end ; ++it)
5659 {
5660 const auto &match = *it;
5661 size_t i = match.position();
5662 size_t l = match.length();
5663 if (i>p) tf+=t.substr(p,i-p);
5664 tf+=match.str().substr(1); // skip %
5665 p=i+l;
5666 }
5667 tf+=t.substr(p);
5668 return tf;
5669}
5670
5671//---------------------------------------------------------------------------------------------------
5672
5673template<class PatternList, class PatternElem, typename PatternGet = QCString(*)(const PatternElem &)>
5675 const PatternList &patList,
5676 PatternElem &elem,
5677 PatternGet getter)
5678{
5679 bool caseSenseNames = getCaseSenseNames();
5680 bool found = FALSE;
5681
5682 if (!patList.empty())
5683 {
5684 std::string fn = fi.fileName();
5685 std::string fp = fi.filePath();
5686 std::string afp= fi.absFilePath();
5687
5688 for (const auto &li : patList)
5689 {
5690 std::string pattern = getter(li).str();
5691 if (!pattern.empty())
5692 {
5693 size_t i=pattern.find('=');
5694 if (i!=std::string::npos) pattern=pattern.substr(0,i); // strip of the extension specific filter name
5695
5696 if (!caseSenseNames)
5697 {
5698 pattern = QCString(pattern).lower().str();
5699 fn = QCString(fn).lower().str();
5700 fp = QCString(fp).lower().str();
5701 afp = QCString(afp).lower().str();
5702 }
5703 reg::Ex re(pattern,reg::Ex::Mode::Wildcard);
5704 found = re.isValid() && (reg::match(fn,re) ||
5705 (fn!=fp && reg::match(fp,re)) ||
5706 (fn!=afp && fp!=afp && reg::match(afp,re)));
5707 if (found)
5708 {
5709 elem = li;
5710 break;
5711 }
5712 //printf("Matching '%s' against pattern '%s' found=%d\n",
5713 // qPrint(fi->fileName()),qPrint(pattern),found);
5714 }
5715 }
5716 }
5717 return found;
5718}
5719
5720//----------------------------------------------------------------------------
5721// returns TRUE if the name of the file represented by 'fi' matches
5722// one of the file patterns in the 'patList' list.
5723
5724bool patternMatch(const FileInfo &fi,const StringVector &patList)
5725{
5726 std::string elem;
5727 auto getter = [](std::string s) -> QCString { return s; };
5728 return genericPatternMatch(fi,patList,elem,getter);
5729}
5730
5732{
5733 InputFileEncoding elem;
5734 auto getter = [](const InputFileEncoding &e) -> QCString { return e.pattern; };
5735 if (genericPatternMatch(fi,Doxygen::inputFileEncodingList,elem,getter)) // check for file specific encoding
5736 {
5737 return elem.encoding;
5738 }
5739 else // fall back to default encoding
5740 {
5741 return Config_getString(INPUT_ENCODING);
5742 }
5743}
5744
5746{
5747 bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW);
5748 if (extLinksInWindow)
5749 return "target=\"_blank\" ";
5750 else if (parent)
5751 return "target=\"_parent\" ";
5752 else
5753 return "";
5754}
5755
5757 const QCString &ref,
5758 bool href,
5759 bool isLocalFile,
5760 const QCString &targetFileName,
5761 const QCString &anchor)
5762{
5763 QCString url;
5764 if (!ref.isEmpty())
5765 {
5766 url = externalRef(relPath,ref,href);
5767 }
5768 if (!targetFileName.isEmpty())
5769 {
5770 QCString fn = targetFileName;
5771 if (ref.isEmpty())
5772 {
5773 if (!anchor.isEmpty() && isLocalFile)
5774 {
5775 fn=""; // omit file name for local links
5776 }
5777 else
5778 {
5779 url = relPath;
5780 }
5781 }
5782 url+=fn;
5783 }
5784 if (!anchor.isEmpty())
5785 {
5786 if (!url.endsWith("=")) url+="#";
5787 url+=anchor;
5788 }
5789 //printf("createHtmlUrl(relPath=%s,local=%d,target=%s,anchor=%s)=%s\n",qPrint(relPath),isLocalFile,qPrint(targetFileName),qPrint(anchor),qPrint(url));
5790 return url;
5791}
5792
5793QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
5794{
5795 QCString result;
5796 if (!ref.isEmpty())
5797 {
5798 auto it = Doxygen::tagDestinationMap.find(ref.str());
5800 {
5801 result = it->second;
5802 size_t l = result.length();
5803 if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
5804 { // relative path -> prepend relPath.
5805 result.prepend(relPath);
5806 l+=relPath.length();
5807 }
5808 if (l>0 && result.at(l-1)!='/') result+='/';
5809 if (!href) result.append("\" ");
5810 }
5811 }
5812 else
5813 {
5814 result = relPath;
5815 }
5816 return result;
5817}
5818
5819/** Replaces any markers of the form \#\#AA in input string \a str
5820 * by new markers of the form \#AABBCC, where \#AABBCC represents a
5821 * valid color, based on the intensity represented by hex number AA
5822 * and the current HTML_COLORSTYLE_* settings.
5823 */
5825{
5826 if (str.isEmpty()) return QCString();
5827 std::string result;
5828 std::string s=str.str();
5829 static const reg::Ex re(R"(##[0-9A-Fa-f][0-9A-Fa-f])");
5830 reg::Iterator it(s,re);
5832 int hue = Config_getInt(HTML_COLORSTYLE_HUE);
5833 int sat = Config_getInt(HTML_COLORSTYLE_SAT);
5834 int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
5835 size_t sl=s.length();
5836 size_t p=0;
5837 for (; it!=end ; ++it)
5838 {
5839 const auto &match = *it;
5840 size_t i = match.position();
5841 size_t l = match.length();
5842 if (i>p) result+=s.substr(p,i-p);
5843 std::string lumStr = match.str().substr(2);
5844#define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
5845 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
5846 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
5847
5848 double r = 0,g = 0,b = 0;
5849 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
5850 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
5851 pow(level/255.0,gamma/100.0),&r,&g,&b);
5852 int red = static_cast<int>(r*255.0);
5853 int green = static_cast<int>(g*255.0);
5854 int blue = static_cast<int>(b*255.0);
5855 char colStr[8];
5856 colStr[0]='#';
5857 colStr[1]=hex[red>>4];
5858 colStr[2]=hex[red&0xf];
5859 colStr[3]=hex[green>>4];
5860 colStr[4]=hex[green&0xf];
5861 colStr[5]=hex[blue>>4];
5862 colStr[6]=hex[blue&0xf];
5863 colStr[7]=0;
5864 //printf("replacing %s->%s (level=%d)\n",qPrint(lumStr),colStr,level);
5865 result+=colStr;
5866 p=i+l;
5867 }
5868 if (p<sl) result+=s.substr(p);
5869 return result;
5870}
5871
5872/** Copies the contents of file with name \a src to the newly created
5873 * file with name \a dest. Returns TRUE if successful.
5874 */
5875bool copyFile(const QCString &src,const QCString &dest)
5876{
5877 if (!Dir().copy(src.str(),dest.str()))
5878 {
5879 err("could not copy file {} to {}\n",src,dest);
5880 return false;
5881 }
5882 return true;
5883}
5884
5885/** Returns the line number of the line following the line with the marker.
5886 * \sa routine extractBlock
5887 */
5888int lineBlock(const QCString &text,const QCString &marker)
5889{
5890 int result = 1;
5891
5892 // find the character positions of the first marker
5893 int m1 = text.find(marker);
5894 if (m1==-1) return result;
5895
5896 // find start line positions for the markers
5897 bool found=false;
5898 int p=0, i=0;
5899 while (!found && (i=text.find('\n',p))!=-1)
5900 {
5901 found = (p<=m1 && m1<i); // found the line with the start marker
5902 p=i+1;
5903 result++;
5904 }
5905 return result;
5906}
5907
5908/** Returns a string representation of \a lang. */
5910{
5911 return to_string(lang);
5912}
5913
5914/** Returns the scope separator to use given the programming language \a lang */
5916{
5917 if (lang==SrcLangExt::Java || lang==SrcLangExt::CSharp || lang==SrcLangExt::VHDL || lang==SrcLangExt::Python)
5918 {
5919 return ".";
5920 }
5921 else if (lang==SrcLangExt::PHP && !classScope)
5922 {
5923 return "\\";
5924 }
5925 else
5926 {
5927 return "::";
5928 }
5929}
5930/** Checks whether the given url starts with a supported protocol */
5931bool isURL(const QCString &url)
5932{
5933 static const std::unordered_set<std::string> schemes = {
5934 "http", "https", "ftp", "ftps", "sftp", "file", "news", "irc", "ircs"
5935 };
5936 QCString loc_url = url.stripWhiteSpace();
5937 int colonPos = loc_url.find(':');
5938 return colonPos!=-1 && schemes.find(loc_url.left(colonPos).str())!=schemes.end();
5939}
5940/** Corrects URL \a url according to the relative path \a relPath.
5941 * Returns the corrected URL. For absolute URLs no correction will be done.
5942 */
5943QCString correctURL(const QCString &url,const QCString &relPath)
5944{
5945 QCString result = url;
5946 if (!relPath.isEmpty() && !isURL(url))
5947 {
5948 result.prepend(relPath);
5949 }
5950 return result;
5951}
5952
5953//---------------------------------------------------------------------------
5954
5956{
5957 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
5958 bool extractPackage = Config_getBool(EXTRACT_PACKAGE);
5959
5960 return (prot!=Protection::Private && prot!=Protection::Package) ||
5961 (prot==Protection::Private && extractPrivate) ||
5962 (prot==Protection::Package && extractPackage);
5963}
5964
5965//---------------------------------------------------------------------------
5966
5967QCString stripIndentation(const QCString &s,bool skipFirstLine)
5968{
5969 if (s.isEmpty()) return s; // empty string -> we're done
5970
5971 //printf("stripIndentation:\n%s\n------\n",qPrint(s));
5972 // compute minimum indentation over all lines
5973 const char *p=s.data();
5974 char c=0;
5975 int indent=0;
5976 int minIndent=1000000; // "infinite"
5977 bool searchIndent=true;
5978 int tabSize=Config_getInt(TAB_SIZE);
5979 bool skipFirst = skipFirstLine;
5980 while ((c=*p++))
5981 {
5982 if (c=='\t') { indent+=tabSize - (indent%tabSize); }
5983 else if (c=='\n') { indent=0; searchIndent=true; skipFirst=false; }
5984 else if (c==' ') { indent++; }
5985 else if (searchIndent && !skipFirst)
5986 {
5987 searchIndent=false;
5988 if (indent<minIndent) minIndent=indent;
5989 }
5990 }
5991
5992 // no indent to remove -> we're done
5993 if (minIndent==0) return substitute(s,"@ilinebr","\\ilinebr");
5994
5995 // remove minimum indentation for each line
5996 TextStream result;
5997 p=s.data();
5998 indent=0;
5999 skipFirst=skipFirstLine;
6000 while ((c=*p++))
6001 {
6002 if (c=='\n') // start of new line
6003 {
6004 indent=0;
6005 result << c;
6006 skipFirst=false;
6007 }
6008 else if (indent<minIndent && !skipFirst) // skip until we reach minIndent
6009 {
6010 if (c=='\t')
6011 {
6012 int newIndent = indent+tabSize-(indent%tabSize);
6013 int i=newIndent;
6014 while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
6015 {
6016 result << ' ';
6017 i--;
6018 }
6019 indent=newIndent;
6020 }
6021 else // space
6022 {
6023 indent++;
6024 }
6025 }
6026 else if (c=='\\' && literal_at(p,"ilinebr "))
6027 // we also need to remove the indentation after a \ilinebr command at the end of a line
6028 {
6029 result << "\\ilinebr ";
6030 p+=8;
6031 int skipAmount=0;
6032 for (int j=0;j<minIndent;j++) if (*(p+j)==' ') skipAmount++; // test to see if we have the indent
6033 if (skipAmount==minIndent)
6034 {
6035 p+=skipAmount; // remove the indent
6036 }
6037 }
6038 else if (c=='@' && literal_at(p,"ilinebr"))
6039 {
6040 result << "\\ilinebr";
6041 p+=7;
6042 }
6043 else // copy anything until the end of the line
6044 {
6045 result << c;
6046 }
6047 }
6048
6049 //printf("stripIndentation: result=\n%s\n------\n",qPrint(result.str()));
6050
6051 return result.str();
6052}
6053
6054// strip up to \a indentationLevel spaces from each line in \a doc (excluding the first line
6055// when skipFirstLine is set to true)
6056void stripIndentationVerbatim(QCString &doc,const int indentationLevel, bool skipFirstLine)
6057{
6058 //printf("stripIndentationVerbatim(level=%d):\n%s\n------\n",indentationLevel,qPrint(doc));
6059 if (indentationLevel <= 0 || doc.isEmpty()) return; // nothing to strip
6060
6061 // by stripping content the string will only become shorter so we write the results
6062 // back into the input string and then resize it at the end.
6063 char c = 0;
6064 const char *src = doc.data();
6065 char *dst = doc.rawData();
6066 bool insideIndent = !skipFirstLine; // skip the initial line from stripping
6067 int cnt = 0;
6068 if (!skipFirstLine) cnt = indentationLevel;
6069 while ((c=*src++))
6070 {
6071 // invariant: dst<=src
6072 switch(c)
6073 {
6074 case '\n':
6075 *dst++ = c;
6076 insideIndent = true;
6077 cnt = indentationLevel;
6078 break;
6079 case ' ':
6080 if (insideIndent)
6081 {
6082 if (cnt>0) // count down the spacing until the end of the indent
6083 {
6084 cnt--;
6085 }
6086 else // reached the end of the indent, start of the part of the line to keep
6087 {
6088 insideIndent = false;
6089 *dst++ = c;
6090 }
6091 }
6092 else // part after indent, copy to the output
6093 {
6094 *dst++ = c;
6095 }
6096 break;
6097 default:
6098 insideIndent = false;
6099 *dst++ = c;
6100 break;
6101 }
6102 }
6103 doc.resize(static_cast<uint32_t>(dst-doc.data()));
6104 //printf("stripIndentationVerbatim: result=\n%s\n------\n",qPrint(doc));
6105}
6106
6107bool fileVisibleInIndex(const FileDef *fd,bool &genSourceFile)
6108{
6109 bool allExternals = Config_getBool(ALLEXTERNALS);
6110 bool isDocFile = fd->isDocumentationFile();
6111 genSourceFile = !isDocFile && fd->generateSourceFile();
6112 return ( ((allExternals && fd->isLinkable()) ||
6114 ) &&
6115 !isDocFile
6116 );
6117}
6118
6119//--------------------------------------------------------------------------------------
6120
6121#if 0
6122/*! @brief Get one unicode character as an unsigned integer from utf-8 string
6123 *
6124 * @param s utf-8 encoded string
6125 * @param idx byte position of given string \a s.
6126 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT
6127 * @see getNextUtf8OrToLower()
6128 * @see getNextUtf8OrToUpper()
6129 */
6130uint32_t getUtf8Code( const QCString& s, int idx )
6131{
6132 const int length = s.length();
6133 if (idx >= length) { return 0; }
6134 const uint32_t c0 = (uint8_t)s.at(idx);
6135 if ( c0 < 0xC2 || c0 >= 0xF8 ) // 1 byte character
6136 {
6137 return c0;
6138 }
6139 if (idx+1 >= length) { return 0; }
6140 const uint32_t c1 = ((uint8_t)s.at(idx+1)) & 0x3f;
6141 if ( c0 < 0xE0 ) // 2 byte character
6142 {
6143 return ((c0 & 0x1f) << 6) | c1;
6144 }
6145 if (idx+2 >= length) { return 0; }
6146 const uint32_t c2 = ((uint8_t)s.at(idx+2)) & 0x3f;
6147 if ( c0 < 0xF0 ) // 3 byte character
6148 {
6149 return ((c0 & 0x0f) << 12) | (c1 << 6) | c2;
6150 }
6151 if (idx+3 >= length) { return 0; }
6152 // 4 byte character
6153 const uint32_t c3 = ((uint8_t)s.at(idx+3)) & 0x3f;
6154 return ((c0 & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
6155}
6156
6157
6158/*! @brief Returns one unicode character as an unsigned integer
6159 * from utf-8 string, making the character lower case if it was upper case.
6160 *
6161 * @param s utf-8 encoded string
6162 * @param idx byte position of given string \a s.
6163 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'A'-'Z'
6164 * @see getNextUtf8Code()
6165*/
6166uint32_t getUtf8CodeToLower( const QCString& s, int idx )
6167{
6168 const uint32_t v = getUtf8Code( s, idx );
6169 return v < 0x7f ? tolower( v ) : v;
6170}
6171
6172
6173/*! @brief Returns one unicode character as an unsigned integer
6174 * from utf-8 string, making the character upper case if it was lower case.
6175 *
6176 * @param s utf-8 encoded string
6177 * @param idx byte position of given string \a s.
6178 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'A'-'Z'
6179 * @see getNextUtf8Code()
6180 */
6181uint32_t getUtf8CodeToUpper( const QCString& s, int idx )
6182{
6183 const uint32_t v = getUtf8Code( s, idx );
6184 return v < 0x7f ? toupper( v ) : v;
6185}
6186#endif
6187
6188
6189
6190//----------------------------------------------------------------------------
6191
6192/** Strip the direction part from docs and return it as a string in canonical form
6193 * The input \a docs string can start with e.g. "[in]", "[in, out]", "[inout]", "[out,in]"...
6194 * @returns either "[in,out]", "[in]", or "[out]" or the empty string.
6195 */
6197{
6198 std::string s = docs.str();
6199 static const reg::Ex re(R"(\‍[([ inout,]+)\‍])");
6200 reg::Iterator it(s,re);
6202 if (it!=end)
6203 {
6204 const auto &match = *it;
6205 size_t p = match.position();
6206 size_t l = match.length();
6207 if (p==0 && l>2)
6208 {
6209 // make dir the part inside [...] without separators
6210 std::string dir = match[1].str();
6211 // strip , and ' ' from dir
6212 dir.erase(std::remove_if(dir.begin(),dir.end(),
6213 [](const char c) { return c==' ' || c==','; }
6214 ),dir.end());
6215 unsigned char ioMask=0;
6216 size_t inIndex = dir.find( "in");
6217 if ( inIndex!=std::string::npos) { dir.erase( inIndex,2); ioMask|=(1<<0); }
6218 size_t outIndex = dir.find("out");
6219 if (outIndex!=std::string::npos) { dir.erase(outIndex,3); ioMask|=(1<<1); }
6220 if (dir.empty() && ioMask!=0) // only in and/or out attributes found
6221 {
6222 docs = s.substr(l); // strip attributes
6223 if (ioMask==((1<<0)|(1<<1))) return "[in,out]";
6224 else if (ioMask==(1<<0)) return "[in]";
6225 else if (ioMask==(1<<1)) return "[out]";
6226 }
6227 }
6228 }
6229 return "";
6230}
6231
6232//-----------------------------------------------------------
6233
6234/** Computes for a given list type \a inListType, which are the
6235 * the corresponding list type(s) in the base class that are to be
6236 * added to this list.
6237 *
6238 * So for public inheritance, the mapping is 1-1, so outListType1=inListType
6239 * Private members are to be hidden completely.
6240 *
6241 * For protected inheritance, both protected and public members of the
6242 * base class should be joined in the protected member section.
6243 *
6244 * For private inheritance, both protected and public members of the
6245 * base class should be joined in the private member section.
6246 */
6248 MemberListType inListType,
6249 Protection inProt,
6250 MemberListType *outListType1,
6251 MemberListType *outListType2
6252 )
6253{
6254 bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
6255
6256 // default representing 1-1 mapping
6257 *outListType1=inListType;
6258 *outListType2=MemberListType::Invalid();
6259
6260 if (inProt==Protection::Public)
6261 {
6262 if (inListType.isPrivate())
6263 {
6264 *outListType1=MemberListType::Invalid();
6265 }
6266 }
6267 else if (inProt==Protection::Protected)
6268 {
6269 if (inListType.isPrivate() || inListType.isPublic())
6270 {
6271 *outListType1=MemberListType::Invalid();
6272 }
6273 else if (inListType.isProtected())
6274 {
6275 *outListType2=inListType.toPublic();
6276 }
6277 }
6278 else if (inProt==Protection::Private)
6279 {
6280 if (inListType.isPublic() || inListType.isProtected())
6281 {
6282 *outListType1=MemberListType::Invalid();
6283 }
6284 else if (inListType.isPrivate())
6285 {
6286 if (extractPrivate)
6287 {
6288 *outListType1=inListType.toPublic();
6289 *outListType2=inListType.toProtected();
6290 }
6291 else
6292 {
6293 *outListType1=MemberListType::Invalid();
6294 }
6295 }
6296 }
6297
6298 //printf("convertProtectionLevel(type=%s prot=%d): %s,%s\n",
6299 // qPrint(inListType.to_string()),inProt,qPrint(outListType1->to_string()),qPrint(outListType2->to_string()));
6300}
6301
6303{
6304 return Doxygen::mainPage!=nullptr && Doxygen::mainPage->hasTitle();
6305}
6306
6308{
6309 QCString imgExt = Config_getEnumAsString(DOT_IMAGE_FORMAT);
6310 int i= imgExt.find(':'); // strip renderer part when using e.g. 'png:cairo:gd' as format
6311 return i==-1 ? imgExt : imgExt.left(i);
6312}
6313
6314bool openOutputFile(const QCString &outFile,std::ofstream &f)
6315{
6316 assert(!f.is_open());
6317 bool fileOpened=FALSE;
6318 bool writeToStdout=outFile=="-";
6319 if (writeToStdout) // write to stdout
6320 {
6321 f.basic_ios<char>::rdbuf(std::cout.rdbuf());
6322 fileOpened = true;
6323 }
6324 else // write to file
6325 {
6326 FileInfo fi(outFile.str());
6327 if (fi.exists()) // create a backup
6328 {
6329 Dir dir;
6330 FileInfo backup(fi.filePath()+".bak");
6331 if (backup.exists()) // remove existing backup
6332 dir.remove(backup.filePath());
6333 dir.rename(fi.filePath(),fi.filePath()+".bak");
6334 }
6335 f = Portable::openOutputStream(outFile);
6336 fileOpened = f.is_open();
6337 }
6338 return fileOpened;
6339}
6340
6341static bool keyWordsFortranC(const char *contents)
6342{
6343 static const std::unordered_set<std::string> fortran_C_keywords = {
6344 "character", "call", "close", "common", "continue",
6345 "case", "contains", "cycle", "class", "codimension",
6346 "concurrent", "contiguous", "critical"
6347 };
6348
6349 if (*contents != 'c' && *contents != 'C') return false;
6350
6351 const char *c = contents;
6352 QCString keyword;
6353 while (*c && *c != ' ') {keyword += *c; c++;}
6354 keyword = keyword.lower();
6355
6356 return (fortran_C_keywords.find(keyword.str()) != fortran_C_keywords.end());
6357}
6358
6359//------------------------------------------------------
6360// simplified way to know if this is fixed form
6361bool recognizeFixedForm(const QCString &contents, FortranFormat format)
6362{
6363 int column=0;
6364 bool skipLine=FALSE;
6365
6366 if (format == FortranFormat::Fixed) return TRUE;
6367 if (format == FortranFormat::Free) return FALSE;
6368
6369 int tabSize=Config_getInt(TAB_SIZE);
6370 size_t sizCont = contents.length();
6371 for (size_t i=0;i<sizCont;i++)
6372 {
6373 column++;
6374
6375 switch(contents.at(i))
6376 {
6377 case '\n':
6378 column=0;
6379 skipLine=FALSE;
6380 break;
6381 case '\t':
6382 column += tabSize-1;
6383 break;
6384 case ' ':
6385 break;
6386 case '\000':
6387 return FALSE;
6388 case '#':
6389 skipLine=TRUE;
6390 break;
6391 case 'C':
6392 case 'c':
6393 if (column==1)
6394 {
6395 return !keyWordsFortranC(contents.data()+i);
6396 }
6397 // fallthrough
6398 case '*':
6399 if (column==1) return TRUE;
6400 if (skipLine) break;
6401 return FALSE;
6402 case '!':
6403 if (column!=6) skipLine=TRUE;
6404 break;
6405 default:
6406 if (skipLine) break;
6407 if (column>=7) return TRUE;
6408 return FALSE;
6409 }
6410 }
6411 return FALSE;
6412}
6413
6415{
6417 QCString parserName = Doxygen::parserManager->getParserName(ext);
6418
6419 if (parserName == "fortranfixed") return FortranFormat::Fixed;
6420 else if (parserName == "fortranfree") return FortranFormat::Free;
6421
6423}
6424//------------------------------------------------------------------------
6425
6426//! remove disabled blocks and all block markers from \a s and return the result as a string
6427QCString selectBlocks(const QCString &s,const SelectionBlockList &blockList,const SelectionMarkerInfo &markerInfo)
6428{
6429 if (s.isEmpty()) return s;
6430
6431 // helper to find the end of a block
6432 auto skipBlock = [&markerInfo](const char *p,const SelectionBlock &blk)
6433 {
6434 char c = 0;
6435 while ((c=*p))
6436 {
6437 if (c==markerInfo.markerChar && qstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // end marker
6438 {
6439 size_t len = markerInfo.endLen;
6440 bool negate = *(p+markerInfo.endLen)=='!';
6441 if (negate) len++;
6442 size_t blkNameLen = qstrlen(blk.name);
6443 if (qstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
6444 qstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
6445 {
6446 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
6447 return p+len+blkNameLen+markerInfo.closeLen;
6448 }
6449 else // not the right marker id
6450 {
6451 p++;
6452 }
6453 }
6454 else // not and end marker
6455 {
6456 p++;
6457 }
6458 }
6459 return p;
6460 };
6461
6462 QCString result;
6463 result.reserve(s.length());
6464 const char *p = s.data();
6465 char c = 0;
6466 while ((c=*p))
6467 {
6468 if (c==markerInfo.markerChar) // potential start of marker
6469 {
6470 if (qstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
6471 {
6472 bool found = false;
6473 size_t len = markerInfo.beginLen;
6474 bool negate = *(p+len)=='!';
6475 if (negate) len++;
6476 for (const auto &blk : blockList)
6477 {
6478 size_t blkNameLen = qstrlen(blk.name);
6479 if (qstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
6480 qstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
6481 {
6482 bool blockEnabled = blk.enabled!=negate;
6483 //printf("Found start marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
6484 p+=len+blkNameLen+markerInfo.closeLen;
6485 if (!blockEnabled) // skip until the end of the block
6486 {
6487 //printf("skipping block\n");
6488 p=skipBlock(p,blk);
6489 }
6490 found=true;
6491 break;
6492 }
6493 }
6494 if (!found) // unknown marker id
6495 {
6496 result+=c;
6497 p++;
6498 }
6499 }
6500 else if (qstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
6501 {
6502 bool found = false;
6503 size_t len = markerInfo.endLen;
6504 bool negate = *(p+len)=='!';
6505 if (negate) len++;
6506 for (const auto &blk : blockList)
6507 {
6508 size_t blkNameLen = qstrlen(blk.name);
6509 if (qstrncmp(p+len,blk.name,blkNameLen)==0 && // matching marker name
6510 qstrncmp(p+len+blkNameLen,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
6511 {
6512 //printf("Found end marker %s enabled=%d negate=%d\n",blk.name,blk.enabled,negate);
6513 p+=len+blkNameLen+markerInfo.closeLen;
6514 found=true;
6515 break;
6516 }
6517 }
6518 if (!found) // unknown marker id
6519 {
6520 result+=c;
6521 p++;
6522 }
6523 }
6524 else // not a start or end marker
6525 {
6526 result+=c;
6527 p++;
6528 }
6529 }
6530 else // not a marker character
6531 {
6532 result+=c;
6533 p++;
6534 }
6535 }
6536 //printf("====\n%s\n-----\n%s\n~~~~\n",qPrint(s),qPrint(result));
6537 return result;
6538}
6539
6540void checkBlocks(const QCString &s, const QCString fileName,const SelectionMarkerInfo &markerInfo)
6541{
6542 if (s.isEmpty()) return;
6543
6544 const char *p = s.data();
6545 char c = 0;
6546 while ((c=*p))
6547 {
6548 if (c==markerInfo.markerChar) // potential start of marker
6549 {
6550 if (qstrncmp(p,markerInfo.beginStr,markerInfo.beginLen)==0) // start of begin marker
6551 {
6552 size_t len = markerInfo.beginLen;
6553 bool negate = *(p+len)=='!';
6554 if (negate) len++;
6555 p += len;
6556 QCString marker;
6557 while (*p)
6558 {
6559 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
6560 {
6561 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
6562 break;
6563 }
6564 else if (markerInfo.closeLen!= 0 && qstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
6565 {
6566 p += markerInfo.closeLen;
6567 warn(fileName,-1,"Remaining begin replacement with marker '{}'",marker);
6568 break;
6569 }
6570 marker += *p;
6571 p++;
6572 }
6573 }
6574 else if (qstrncmp(p,markerInfo.endStr,markerInfo.endLen)==0) // start of end marker
6575 {
6576 size_t len = markerInfo.endLen;
6577 bool negate = *(p+len)=='!';
6578 if (negate) len++;
6579 p += len;
6580 QCString marker;
6581 while (*p)
6582 {
6583 if (markerInfo.closeLen==0 && *p=='\n') // matching end of line
6584 {
6585 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
6586 break;
6587 }
6588 else if (markerInfo.closeLen!= 0 && qstrncmp(p,markerInfo.closeStr,markerInfo.closeLen)==0) // matching marker closing
6589 {
6590 p += markerInfo.closeLen;
6591 warn(fileName,-1,"Remaining end replacement with marker '{}'",marker);
6592 break;
6593 }
6594 marker += *p;
6595 p++;
6596 }
6597 }
6598 }
6599 p++;
6600 }
6601}
6602
6603
6605{
6606 std::string out;
6607 out.reserve(s.length());
6608 const char *p=s.data();
6609 if (p)
6610 {
6611 char c = 0;
6612 while ((c=*p++))
6613 {
6614 if (c=='\n')
6615 {
6616 const char *e = p;
6617 while (*e==' ' || *e=='\t') e++;
6618 if (*e=='\n')
6619 {
6620 p=e;
6621 }
6622 else out+=c;
6623 }
6624 else
6625 {
6626 out+=c;
6627 }
6628 }
6629 }
6630 //printf("removeEmptyLines(%s)=%s\n",qPrint(s),qPrint(out));
6631 return out;
6632}
6633
6634/// split input string \a s by string delimiter \a delimiter.
6635/// returns a vector of non-empty strings that are between the delimiters
6636StringVector split(const std::string &s,const std::string &delimiter)
6637{
6638 StringVector result;
6639 size_t prev = 0, pos = 0, len = s.length();
6640 do
6641 {
6642 pos = s.find(delimiter, prev);
6643 if (pos == std::string::npos) pos = len;
6644 if (pos>prev) result.push_back(s.substr(prev,pos-prev));
6645 prev = pos + delimiter.length();
6646 }
6647 while (pos<len && prev<len);
6648 return result;
6649}
6650
6651/// split input string \a s by regular expression delimiter \a delimiter.
6652/// returns a vector of non-empty strings that are between the delimiters
6653StringVector split(const std::string &s,const reg::Ex &delimiter)
6654{
6655 StringVector result;
6656 reg::Iterator iter(s, delimiter);
6658 size_t p=0;
6659 for ( ; iter != end; ++iter)
6660 {
6661 const auto &match = *iter;
6662 size_t i=match.position();
6663 size_t l=match.length();
6664 if (i>p) result.push_back(s.substr(p,i-p));
6665 p=i+l;
6666 }
6667 if (p<s.length()) result.push_back(s.substr(p));
6668 return result;
6669}
6670
6671/// find the index of a string in a vector of strings, returns -1 if the string could not be found
6672int findIndex(const StringVector &sv,const std::string &s)
6673{
6674 auto it = std::find(sv.begin(),sv.end(),s);
6675 return it!=sv.end() ? static_cast<int>(it-sv.begin()) : -1;
6676}
6677
6678/// find the index of the first occurrence of pattern \a re in a string \a s
6679/// returns -1 if the pattern could not be found
6680int findIndex(const std::string &s,const reg::Ex &re)
6681{
6682 reg::Match match;
6683 return reg::search(s,match,re) ? static_cast<int>(match.position()) : -1;
6684}
6685
6686/// create a string where the string in the vector are joined by the given delimiter
6687std::string join(const StringVector &sv,const std::string &delimiter)
6688{
6689 std::string result;
6690 bool first=true;
6691 for (const auto &s : sv)
6692 {
6693 if (!first) result+=delimiter;
6694 first=false;
6695 result+=s;
6696 }
6697 return result;
6698}
6699
6700QCString integerToAlpha(int n, bool upper)
6701{
6702 QCString result;
6703 int residual = n;
6704
6705 char modVal[2];
6706 modVal[1] = 0;
6707 while (residual > 0)
6708 {
6709 modVal[0] = (upper ? 'A': 'a') + (residual-1)%26;
6710 result = modVal + result;
6711 residual = (residual-1) / 26;
6712 }
6713 return result;
6714}
6715
6716QCString integerToRoman(int n, bool upper)
6717{
6718 static const char *str_romans_upper[] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
6719 static const char *str_romans_lower[] = { "m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i" };
6720 static const int values[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
6721 static const char **str_romans = upper ? str_romans_upper : str_romans_lower;
6722
6723 QCString result;
6724 int residual = n;
6725
6726 for (int i = 0; i < 13; ++i)
6727 {
6728 while (residual - values[i] >= 0)
6729 {
6730 result += str_romans[i];
6731 residual -= values[i];
6732 }
6733 }
6734
6735 return result;
6736}
6737
6738QCString detab(const QCString &s,size_t &refIndent)
6739{
6740 int tabSize = Config_getInt(TAB_SIZE);
6741 size_t size = s.length();
6742 QCString result;
6743 result.reserve(size+256);
6744 const char *data = s.data();
6745 size_t i=0;
6746 int col=0;
6747 constexpr auto doxy_nbsp = "&_doxy_nbsp;"; // doxygen escape command for UTF-8 nbsp
6748 const int maxIndent=1000000; // value representing infinity
6749 int minIndent=maxIndent;
6750 bool skip = false;
6751 while (i<size)
6752 {
6753 char c = data[i++];
6754 switch(c)
6755 {
6756 case '\t': // expand tab
6757 {
6758 int stop = tabSize - (col%tabSize);
6759 //printf("expand at %d stop=%d\n",col,stop);
6760 col+=stop;
6761 while (stop--) result+=' ';
6762 }
6763 break;
6764 case '\\':
6765 if (data[i] == '\\') // escaped command -> ignore
6766 {
6767 result+=c;
6768 result+=data[i++];
6769 col+=2;
6770 }
6771 else if (i+5<size && literal_at(data+i,"iskip")) // command
6772 {
6773 i+=5;
6774 skip = true;
6775 }
6776 else if (i+8<size && literal_at(data+i,"endiskip")) // command
6777 {
6778 i+=8;
6779 skip = false;
6780 }
6781 else // some other command
6782 {
6783 result+=c;
6784 col++;
6785 }
6786 break;
6787 case '\n': // reset column counter
6788 result+=c;
6789 col=0;
6790 break;
6791 case ' ': // increment column counter
6792 result+=c;
6793 col++;
6794 break;
6795 default: // non-whitespace => update minIndent
6796 if (c<0 && i<size) // multibyte sequence
6797 {
6798 // special handling of the UTF-8 nbsp character 0xC2 0xA0
6799 int nb = isUTF8NonBreakableSpace(data);
6800 if (nb>0)
6801 {
6802 result+=doxy_nbsp;
6803 i+=nb-1;
6804 }
6805 else
6806 {
6807 int bytes = getUTF8CharNumBytes(c);
6808 for (int j=0;j<bytes-1 && c;j++)
6809 {
6810 result+=c;
6811 c = data[i++];
6812 }
6813 result+=c;
6814 }
6815 }
6816 else
6817 {
6818 result+=c;
6819 }
6820 if (!skip && col<minIndent) minIndent=col;
6821 col++;
6822 }
6823 }
6824 if (minIndent!=maxIndent) refIndent=minIndent; else refIndent=0;
6825 //printf("detab(\n%s\n)=[\n%s\n]\n",qPrint(s),qPrint(out.get()));
6826 return result;
6827}
6828
6830{
6831 QCString projectCookie = Config_getString(HTML_PROJECT_COOKIE);
6832 if (projectCookie.isEmpty()) return QCString();
6833 uint8_t md5_sig[16];
6834 char sigStr[34];
6835 MD5Buffer(projectCookie.data(),static_cast<unsigned int>(projectCookie.length()),md5_sig);
6836 MD5SigToString(md5_sig,sigStr);
6837 sigStr[32]='_'; sigStr[33]=0;
6838 return sigStr;
6839}
6840
6841//! Return the index of the last :: in the string \a name that is still before the first <
6843{
6844 int l = static_cast<int>(name.length());
6845 int lastSepPos = -1;
6846 const char *p = name.data();
6847 int i=l-2;
6848 int sharpCount=0;
6849 // --- begin optimized version of ts=name.findRev(">::");
6850 int ts = -1;
6851 while (i>=0)
6852 {
6853 if (p[i]=='>')
6854 {
6855 if (sharpCount==0 && p[i+1]==':' && p[i+2]==':')
6856 {
6857 ts=i;
6858 break;
6859 }
6860 sharpCount++;
6861 }
6862 else if (p[i]=='<')
6863 {
6864 sharpCount--;
6865 }
6866 i--;
6867 }
6868 // --- end optimized version
6869 if (ts==-1) ts=0; else p+=++ts;
6870 for (i=ts;i<l-1;i++)
6871 {
6872 char c=*p++;
6873 if (c==':' && *p==':') lastSepPos=i;
6874 if (c=='<') break;
6875 }
6876 return lastSepPos;
6877}
6878
6880{
6881 if (Config_getBool(CALL_GRAPH) !=md1->hasCallGraph()) md2->overrideCallGraph(md1->hasCallGraph());
6882 if (Config_getBool(CALLER_GRAPH)!=md1->hasCallerGraph()) md2->overrideCallerGraph(md1->hasCallerGraph());
6883 if (Config_getBool(CALL_GRAPH) !=md2->hasCallGraph()) md1->overrideCallGraph( md2->hasCallGraph());
6884 if (Config_getBool(CALLER_GRAPH)!=md2->hasCallerGraph()) md1->overrideCallerGraph(md2->hasCallerGraph());
6885
6886 if (Config_getBool(SHOW_ENUM_VALUES) !=md1->hasEnumValues()) md2->overrideEnumValues(md1->hasEnumValues());
6887 if (Config_getBool(SHOW_ENUM_VALUES) !=md2->hasEnumValues()) md1->overrideEnumValues( md2->hasEnumValues());
6888
6889 if (Config_getBool(REFERENCED_BY_RELATION)!=md1->hasReferencedByRelation()) md2->overrideReferencedByRelation(md1->hasReferencedByRelation());
6890 if (Config_getBool(REFERENCES_RELATION) !=md1->hasReferencesRelation()) md2->overrideReferencesRelation(md1->hasReferencesRelation());
6891 if (Config_getBool(REFERENCED_BY_RELATION)!=md2->hasReferencedByRelation()) md1->overrideReferencedByRelation(md2->hasReferencedByRelation());
6892 if (Config_getBool(REFERENCES_RELATION) !=md2->hasReferencesRelation()) md1->overrideReferencesRelation(md2->hasReferencesRelation());
6893
6894 if (Config_getBool(INLINE_SOURCES)!=md1->hasInlineSource()) md2->overrideInlineSource(md1->hasInlineSource());
6895 if (Config_getBool(INLINE_SOURCES)!=md2->hasInlineSource()) md1->overrideInlineSource(md2->hasInlineSource());
6896}
6897
6898size_t updateColumnCount(const char *s,size_t col)
6899{
6900 if (s)
6901 {
6902 const int tabSize = Config_getInt(TAB_SIZE);
6903 char c;
6904 while ((c=*s++))
6905 {
6906 switch(c)
6907 {
6908 case '\t': col+=tabSize - (col%tabSize);
6909 break;
6910 case '\n': col=0;
6911 break;
6912 default:
6913 col++;
6914 if (c<0) // multi-byte character
6915 {
6916 int numBytes = getUTF8CharNumBytes(c);
6917 for (int i=0;i<numBytes-1 && (c=*s++);i++) {} // skip over extra chars
6918 if (c==0) return col; // end of string half way a multibyte char
6919 }
6920 break;
6921 }
6922 }
6923 }
6924 return col;
6925}
6926
6927// in C# A, A<T>, and A<T,S> are different classes, so we need some way to disguish them using this name mangling
6928// A -> A
6929// A<T> -> A-1-g
6930// A<T,S> -> A-2-g
6932{
6933 int idx = name.find('<');
6934 if (idx!=-1)
6935 {
6936 return name.left(idx)+"-"+QCString().setNum(name.contains(",")+1)+"-g";
6937 }
6938 return name;
6939}
6940
6942{
6943 QCString result=name;
6944 if (result.endsWith("-g"))
6945 {
6946 int idx = result.find('-');
6947 result = result.left(idx)+templArgs;
6948 }
6949 return result;
6950}
6951
6953{
6954 QCString text=rawStart;
6955 int i = text.find('"');
6956 assert(i!=-1);
6957 return text.mid(i+1,text.length()-i-2); // text=...R"xyz( -> delimiter=xyz
6958}
6959
6961{
6962 QCString text=rawEnd;
6963 return text.mid(1,text.length()-2); // text=)xyz" -> delimiter=xyz
6964}
6965
6966static std::mutex writeFileContents_lock;
6968
6969/** Thread-safe function to write a string to a file.
6970 * The contents will be used to create a hash that will be used to make the name unique.
6971 * @param[in] baseName the base name of the file to write including path.
6972 * @param[in] extension the file extension to use.
6973 * @param[in] content the data to write to the file
6974 * @param[out] exists is set to true if the file was already written before.
6975 * @returns the name of the file written or an empty string in case of an error.
6976 */
6977QCString writeFileContents(const QCString &baseName,const QCString &extension,const QCString &content,bool &exists)
6978{
6979 uint8_t md5_sig[16];
6980 char sigStr[33];
6981 MD5Buffer(content.data(),static_cast<unsigned int>(content.length()),md5_sig);
6982 MD5SigToString(md5_sig,sigStr);
6983
6984 QCString fileName = baseName + sigStr + extension;
6985 { // ==== start atomic section
6986 std::lock_guard lock(writeFileContents_lock);
6987 auto it=writeFileContents_set.find(fileName.str());
6988 exists = it!=writeFileContents_set.end();
6989 if (!exists)
6990 {
6991 writeFileContents_set.insert(fileName.str());
6992 if (auto file = Portable::openOutputStream(fileName); file.is_open())
6993 {
6994 file.write( content.data(), content.length() );
6995 file.close();
6996 }
6997 else
6998 {
6999 err("Could not open file {} for writing\n",fileName);
7000 return QCString();
7001 }
7002 }
7003 } // ==== end atomic section
7004 return fileName;
7005}
7006
7007
7009{
7010 if (Config_getBool(DOT_CLEANUP))
7011 {
7012 for (const auto& fileName: writeFileContents_set)
7013 {
7014 Dir().remove(qPrint(fileName));
7015 }
7016 }
7017}
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:1472
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:816
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:73
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.
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.
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 parseText(const QCString &textStr)
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
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
char * rawData()
Returns a writable pointer to the data.
Definition qcstring.h:178
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
void resize(size_t newlen)
Definition qcstring.h:180
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
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:396
QCString right(size_t len) const
Definition qcstring.h:234
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:185
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
std::string_view view() const
Definition qcstring.h:174
QCString left(size_t len) const
Definition qcstring.h:229
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:148
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:213
QCString quoted() const
Definition qcstring.h:275
void clear()
Definition qcstring.h:182
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
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.
Concrete visitor implementation for TEXT output.
Abstract interface for a hyperlinked text fragment.
Definition util.h:63
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:76
TextGeneratorOLImpl(OutputList &ol)
Definition util.cpp:102
void writeBreak(int indent) const override
Definition util.cpp:130
OutputList & m_ol
Definition util.h:85
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
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:837
Class to iterate through matches.
Definition regex.h:230
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:822
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:1336
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:9327
#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:844
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:866
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:855
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:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
uint32_t qstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition qcstring.h:58
#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:2894
FileDef * fileDef
Definition util.cpp:2896
FindFileCacheElem(FileDef *fd, bool ambig)
Definition util.cpp:2895
bool forceEmptyScope
Definition util.h:164
const FileDef * currentFile
Definition util.h:165
QCString scopeName
Definition util.h:161
bool insideCode
Definition util.h:167
bool checkCV
Definition util.h:166
QCString args
Definition util.h:163
QCString memberName
Definition util.h:162
const MemberDef * md
Definition util.h:173
const ConceptDef * cnd
Definition util.h:178
const FileDef * fd
Definition util.h:175
const ModuleDef * modd
Definition util.h:179
const GroupDef * gd
Definition util.h:177
bool found
Definition util.h:172
const ClassDef * cd
Definition util.h:174
const NamespaceDef * nd
Definition util.h:176
QCString encoding
Definition doxygen.h:70
SrcLangExt parserId
Definition util.cpp:5098
const char * langName
Definition util.cpp:5096
const char * parserName
Definition util.cpp:5097
const char * defExt
Definition util.cpp:5099
int indentLevel() const
Definition util.h:106
bool external() const
Definition util.h:104
const ArgumentList * argumentList() const
Definition util.h:102
const Definition * scope() const
Definition util.h:99
const Definition * self() const
Definition util.h:101
size_t breakThreshold() const
Definition util.h:107
bool keepSpaces() const
Definition util.h:105
const FileDef * fileScope() const
Definition util.h:100
bool autoBreak() const
Definition util.h:103
size_t beginLen
Definition util.h:236
const char * closeStr
Definition util.h:239
const char * beginStr
Definition util.h:235
size_t closeLen
Definition util.h:240
const char * endStr
Definition util.h:237
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:572
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:6977
QCString externalRef(const QCString &relPath, const QCString &ref, bool href)
Definition util.cpp:5793
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 The input docs string ...
Definition util.cpp:6196
QCString mergeScopes(const QCString &leftScope, const QCString &rightScope)
Definition util.cpp:4615
QCString findFilePath(const QCString &file, bool &ambig)
Definition util.cpp:2996
QCString normalizeNonTemplateArgumentsInString(const QCString &name, const Definition *context, const ArgumentList &formalArgs)
Definition util.cpp:4321
QCString linkToText(SrcLangExt lang, const QCString &link, bool isFileName)
Definition util.cpp:2708
QCString convertToJSString(const QCString &s, bool keepEntities, bool singleQuotes)
Definition util.cpp:4044
size_t updateColumnCount(const char *s, size_t col)
Definition util.cpp:6898
static std::mutex writeFileContents_lock
Definition util.cpp:6966
void trimBaseClassScope(const BaseClassList &bcl, QCString &s, int level=0)
Definition util.cpp:1531
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5231
bool mainPageHasTitle()
Definition util.cpp:6302
QCString insertTemplateSpecifierInScope(const QCString &scope, const QCString &templ)
Definition util.cpp:3767
bool protectionLevelVisible(Protection prot)
Definition util.cpp:5955
QCString generateAnonymousAnchor(const QCString &fileName, int count)
Definition util.cpp:3579
static std::unordered_map< std::string, QCString > g_docCache
Definition util.cpp:5443
QCString parseCommentAsHtml(const Definition *scope, const MemberDef *member, const QCString &doc, const QCString &fileName, int lineNr)
Definition util.cpp:5445
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:6687
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:5888
void addCodeOnlyMappings()
Definition util.cpp:5225
QCString convertToHtml(const QCString &s, bool keepEntities)
Definition util.cpp:3984
QCString substituteTemplateArgumentsInString(const QCString &nm, const ArgumentList &formalArgs, const ArgumentList *actualArgs)
Definition util.cpp:4386
static int g_usedNamesCount
Definition util.cpp:3517
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:5967
QCString parseCommentAsText(const Definition *scope, const MemberDef *md, const QCString &doc, const QCString &fileName, int lineNr)
Definition util.cpp:5389
int extractClassNameFromType(const QCString &type, int &pos, QCString &name, QCString &templSpec, SrcLangExt lang)
Definition util.cpp:4236
QCString integerToRoman(int n, bool upper)
Definition util.cpp:6716
void checkBlocks(const QCString &s, const QCString fileName, const SelectionMarkerInfo &markerInfo)
Definition util.cpp:6540
void stripIndentationVerbatim(QCString &doc, const int indentationLevel, bool skipFirstLine)
Definition util.cpp:6056
void writeTypeConstraints(OutputList &ol, const Definition *d, const ArgumentList &al)
Definition util.cpp:5479
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:5943
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:4969
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:6604
QCString extractEndRawStringDelimiter(const char *rawEnd)
Definition util.cpp:6960
static const char constScope[]
Definition util.cpp:520
static bool recursivelyAddGroupListToTitle(OutputList &ol, const Definition *d, bool root)
Definition util.cpp:4893
bool containsWord(const QCString &str, const char *word)
returns TRUE iff string s contains word w
Definition util.cpp:4991
bool checkIfTypedef(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5335
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:5570
bool transcodeCharacterStringToUTF8(std::string &input, const char *inputEncoding)
Definition util.cpp:1456
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:5724
void generateFileRef(OutputList &ol, const QCString &name, const QCString &text)
Definition util.cpp:2876
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:6427
static std::mutex g_findFileDefMutex
Definition util.cpp:2902
QCString escapeCharsInString(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3350
static std::unordered_map< std::string, SrcLangExt > g_extLookup
Definition util.cpp:5092
static QCString stripDeclKeywords(const QCString &s)
Definition util.cpp:1623
bool recognizeFixedForm(const QCString &contents, FortranFormat format)
Definition util.cpp:6361
bool openOutputFile(const QCString &outFile, std::ofstream &f)
Definition util.cpp:6314
QCString substituteKeywords(const QCString &file, const QCString &s, const KeywordSubstitutionList &keywords)
Definition util.cpp:3075
static MemberDef * getMemberFromSymbol(const Definition *scope, const FileDef *fileScope, const QCString &n)
Definition util.cpp:5283
QCString tempArgListToString(const ArgumentList &al, SrcLangExt lang, bool includeDefault)
Definition util.cpp:1299
static ModuleDef * findModuleDef(const Definition *d)
Definition util.cpp:4864
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:4845
void addGroupListToTitle(OutputList &ol, const Definition *d)
Definition util.cpp:4932
QCString relativePathToRoot(const QCString &name)
Definition util.cpp:3600
SrcLangExt getLanguageFromCodeLang(QCString &fileName)
Routine to handle the language attribute of the \code command.
Definition util.cpp:5249
QCString integerToAlpha(int n, bool upper)
Definition util.cpp:6700
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:3688
static void transcodeCharacterBuffer(const QCString &fileName, std::string &contents, const QCString &inputEncoding, const QCString &outputEncoding)
Definition util.cpp:5534
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3039
QCString demangleCSharpGenericName(const QCString &name, const QCString &templArgs)
Definition util.cpp:6941
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
QCString stripExtensionGeneral(const QCString &fName, const QCString &ext)
Definition util.cpp:4954
QCString filterTitle(const QCString &title)
Definition util.cpp:5650
QCString unescapeCharsInString(const QCString &s)
Definition util.cpp:3437
QCString removeAnonymousScopes(const QCString &str)
Definition util.cpp:161
void createSubDirs(const Dir &d)
Definition util.cpp:3661
bool fileVisibleInIndex(const FileDef *fd, bool &genSourceFile)
Definition util.cpp:6107
QCString stripScope(const QCString &name)
Definition util.cpp:3800
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:4937
bool isURL(const QCString &url)
Checks whether the given url starts with a supported protocol.
Definition util.cpp:5931
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:2734
int computeQualifiedIndex(const QCString &name)
Return the index of the last :: in the string name that is still before the first <.
Definition util.cpp:6842
QCString stripExtension(const QCString &fName)
Definition util.cpp:4964
void initDefaultExtensionMapping()
Definition util.cpp:5158
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:5007
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:3525
static bool isLowerCase(QCString &s)
Definition util.cpp:2440
static const char virtualScope[]
Definition util.cpp:522
QCString convertToXML(const QCString &s, bool keepEntities)
Definition util.cpp:3933
QCString langToString(SrcLangExt lang)
Returns a string representation of lang.
Definition util.cpp:5909
QCString determineAbsoluteIncludeName(const QCString &curFile, const QCString &incFileName)
Definition util.cpp:3621
QCString detab(const QCString &s, size_t &refIndent)
Definition util.cpp:6738
EntryType guessSection(const QCString &name)
Definition util.cpp:338
void extractNamespaceName(const QCString &scopeName, QCString &className, QCString &namespaceName, bool allowEmptyClass)
Definition util.cpp:3718
static StringUnorderedSet writeFileContents_set
Definition util.cpp:6967
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:6247
static const char volatileScope[]
Definition util.cpp:521
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:6672
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:3926
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:5915
void stackTrace()
Definition util.cpp:5506
void mergeMemberOverrideOptions(MemberDefMutable *md1, MemberDefMutable *md2)
Definition util.cpp:6879
bool getCaseSenseNames()
Definition util.cpp:3341
static int nextUTF8CharPosition(const QCString &utf8Str, uint32_t len, uint32_t startPos)
Definition util.cpp:5345
QCString getDotImageExtension()
Definition util.cpp:6307
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:6931
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:2308
QCString makeBaseName(const QCString &name, const QCString &ext)
Definition util.cpp:4985
QCString getProjectId()
Definition util.cpp:6829
#define NOMATCH
Definition util.cpp:1931
QCString projectLogoFile()
Definition util.cpp:3161
static std::mutex g_usedNamesMutex
Definition util.cpp:3516
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:4715
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:6636
QCString getEncoding(const FileInfo &fi)
Definition util.cpp:5731
static std::mutex g_docCacheMutex
Definition util.cpp:5442
void linkifyText(const TextGeneratorIntf &out, const QCString &text, const LinkifyTextOptions &options)
Definition util.cpp:893
static bool keyWordsFortranC(const char *contents)
Definition util.cpp:6341
FortranFormat convertFileNameFortranParserCode(QCString fn)
Definition util.cpp:6414
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:5674
QCString stripLeadingAndTrailingEmptyLines(const QCString &s, int &docLine)
Special version of QCString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:5050
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:5875
QCString externalLinkTarget(const bool parent)
Definition util.cpp:5745
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:6952
QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped, QCString scopeName, bool allowArtificial)
Definition util.cpp:4548
QCString getOverloadDocs()
Definition util.cpp:4110
static QCString getFilterFromList(const QCString &name, const StringVector &filterList, bool &found)
Definition util.cpp:1381
static QCString projectLogoSize()
Definition util.cpp:3182
void cleanupInlineGraph()
Definition util.cpp:7008
static QCString showDate(const QCString &fmt)
Definition util.cpp:3148
int getPrefixIndex(const QCString &name)
Definition util.cpp:3252
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:5126
QCString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:1204
static std::unordered_map< std::string, int > g_usedNames
Definition util.cpp:3515
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:5824
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5273
#define HEXTONUM(x)
QCString convertToId(const QCString &s)
Definition util.cpp:3893
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:2904
QCString convertCharEntitiesToUTF8(const QCString &str)
Definition util.cpp:4068
static std::vector< Lang2ExtMap > g_lang2extMap
Definition util.cpp:5102
int getScopeFragment(const QCString &s, int p, int *l)
Definition util.cpp:4660
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:4942
QCString createHtmlUrl(const QCString &relPath, const QCString &ref, bool href, bool isLocalFile, const QCString &targetFileName, const QCString &anchor)
Definition util.cpp:5756
A bunch of utility functions.
std::vector< KeywordSubstitution > KeywordSubstitutionList
Definition util.h:296
std::vector< SelectionBlock > SelectionBlockList
Definition util.h:230
bool isId(int c)
Definition util.h:256