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