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