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