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