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