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