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