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