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