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