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