doxygen  1.8.18
About: Doxygen is a source code documentation generator tool for C++, C, Objective-C, C#, PHP, Java, Python, IDL (diverse flavors), Fortran, VHDL, Tcl, and to some extent D. Different output formats are supported.
  Fossies Dox: doxygen-1.8.18.src.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

util.cpp
Go to the documentation of this file.
1 
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <math.h>
21 #include <limits.h>
22 #include <cinttypes>
23 
24 
25 #include "md5.h"
26 
27 #include <qregexp.h>
28 #include <qfileinfo.h>
29 #include <qdir.h>
30 #include <qdatetime.h>
31 #include <qcache.h>
32 
33 #include "util.h"
34 #include "message.h"
35 #include "classdef.h"
36 #include "filedef.h"
37 #include "doxygen.h"
38 #include "outputlist.h"
39 #include "defargs.h"
40 #include "language.h"
41 #include "config.h"
42 #include "htmlhelp.h"
43 #include "example.h"
44 #include "version.h"
45 #include "groupdef.h"
46 #include "reflist.h"
47 #include "pagedef.h"
48 #include "debug.h"
49 #include "searchindex.h"
50 #include "doxygen.h"
51 #include "textdocvisitor.h"
52 #include "latexdocvisitor.h"
53 #include "portable.h"
54 #include "parserintf.h"
55 #include "bufstr.h"
56 #include "image.h"
57 #include "growbuf.h"
58 #include "entry.h"
59 #include "arguments.h"
60 #include "memberlist.h"
61 #include "classlist.h"
62 #include "namespacedef.h"
63 #include "membername.h"
64 #include "filename.h"
65 #include "membergroup.h"
66 #include "dirdef.h"
67 #include "htmlentity.h"
68 
69 #define ENABLE_TRACINGSUPPORT 0
70 
71 #if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
72 #define TRACINGSUPPORT
73 #endif
74 
75 #ifdef TRACINGSUPPORT
76 #include <execinfo.h>
77 #include <unistd.h>
78 #endif
79 
80 
81 //------------------------------------------------------------------------
82 
83 // selects one of the name to sub-dir mapping algorithms that is used
84 // to select a sub directory when CREATE_SUBDIRS is set to YES.
85 
86 #define ALGO_COUNT 1
87 #define ALGO_CRC16 2
88 #define ALGO_MD5 3
89 
90 //#define MAP_ALGO ALGO_COUNT
91 //#define MAP_ALGO ALGO_CRC16
92 #define MAP_ALGO ALGO_MD5
93 
94 #define REL_PATH_TO_ROOT "../../"
95 
96 //------------------------------------------------------------------------
97 // TextGeneratorOLImpl implementation
98 //------------------------------------------------------------------------
99 
101 {
102 }
103 
104 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
105 {
106  if (s==0) return;
107  //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces);
108  if (keepSpaces)
109  {
110  const char *p=s;
111  if (p)
112  {
113  char cs[2];
114  char c;
115  cs[1]='\0';
116  while ((c=*p++))
117  {
118  if (c==' ') m_od.writeNonBreakableSpace(1);
119  else cs[0]=c,m_od.docify(cs);
120  }
121  }
122  }
123  else
124  {
125  m_od.docify(s);
126  }
127 }
128 
129 void TextGeneratorOLImpl::writeBreak(int indent) const
130 {
131  m_od.lineBreak("typebreak");
132  int i;
133  for (i=0;i<indent;i++)
134  {
136  }
137 }
138 
139 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
140  const char *anchor,const char *text
141  ) const
142 {
143  //printf("TextGeneratorOlImpl::writeLink('%s')\n",text);
144  m_od.writeObjectLink(extRef,file,anchor,text);
145 }
146 
147 //------------------------------------------------------------------------
148 //------------------------------------------------------------------------
149 
150 // an inheritance tree of depth of 100000 should be enough for everyone :-)
151 const int maxInheritanceDepth = 100000;
152 
168 {
169  QCString result;
170  if (s.isEmpty()) return result;
171  static QRegExp re("[ :]*@[0-9]+[: ]*");
172  int i,l,sl=s.length();
173  int p=0;
174  while ((i=re.match(s,p,&l))!=-1)
175  {
176  result+=s.mid(p,i-p);
177  int c=i;
178  bool b1=FALSE,b2=FALSE;
179  while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
180  c=i+l-1;
181  while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
182  if (b1 && b2)
183  {
184  result+="::";
185  }
186  p=i+l;
187  }
188  result+=s.right(sl-p);
189  //printf("removeAnonymousScopes('%s')='%s'\n",s.data(),result.data());
190  return result;
191 }
192 
193 // replace anonymous scopes with __anonymous__ or replacement if provided
194 QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
195 {
196  QCString result;
197  if (s.isEmpty()) return result;
198  static QRegExp re("@[0-9]+");
199  int i,l,sl=s.length();
200  int p=0;
201  while ((i=re.match(s,p,&l))!=-1)
202  {
203  result+=s.mid(p,i-p);
204  if (replacement)
205  {
206  result+=replacement;
207  }
208  else
209  {
210  result+="__anonymous__";
211  }
212  p=i+l;
213  }
214  result+=s.right(sl-p);
215  //printf("replaceAnonymousScopes('%s')='%s'\n",s.data(),result.data());
216  return result;
217 }
218 
219 
220 // strip anonymous left hand side part of the scope
222 {
223  int i,p=0,l;
224  QCString newScope;
225  int sl = s.length();
226  while ((i=getScopeFragment(s,p,&l))!=-1)
227  {
228  //printf("Scope fragment %s\n",s.mid(i,l).data());
229  if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
230  {
231  if (s.at(i)!='@')
232  {
233  if (!newScope.isEmpty()) newScope+="::";
234  newScope+=s.mid(i,l);
235  }
236  }
237  else if (i<sl)
238  {
239  if (!newScope.isEmpty()) newScope+="::";
240  newScope+=s.right(sl-i);
241  goto done;
242  }
243  p=i+l;
244  }
245 done:
246  //printf("stripAnonymousNamespaceScope('%s')='%s'\n",s.data(),newScope.data());
247  return newScope;
248 }
249 
250 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
251 {
252  od.pushGeneratorState();
253 
257  if (Config_getBool(PDF_HYPERLINKS)) od.disable(OutputGenerator::Latex);
258  if (Config_getBool(RTF_HYPERLINKS)) od.disable(OutputGenerator::RTF);
259  od.startPageRef();
261  od.endPageRef(cn,mn);
262 
263  od.popGeneratorState();
264 }
265 
271 {
272  const int maxMarkerStrLen = 20;
273  char result[maxMarkerStrLen];
274  qsnprintf(result,maxMarkerStrLen,"@%d",id);
275  return result;
276 }
277 
279 {
280  // look at all the strings in the list and strip the longest match
281  const char *s=l.first();
282  QCString potential;
283  unsigned int length = 0;
284  while (s)
285  {
286  QCString prefix = s;
287  if (prefix.length() > length &&
288  qstricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
289  {
290  length = prefix.length();
291  potential = path.right(path.length()-prefix.length());
292  }
293  s = l.next();
294  }
295  if (length) return potential;
296  return path;
297 }
298 
303 {
304  return stripFromPath(path,Config_getList(STRIP_FROM_PATH));
305 }
306 
311 {
312  return stripFromPath(path,Config_getList(STRIP_FROM_INC_PATH));
313 }
314 
319 int guessSection(const char *name)
320 {
321  QCString n=((QCString)name).lower();
322  if (n.right(2)==".c" || // source
323  n.right(3)==".cc" ||
324  n.right(4)==".cxx" ||
325  n.right(4)==".cpp" ||
326  n.right(4)==".c++" ||
327  n.right(5)==".java" ||
328  n.right(2)==".m" ||
329  n.right(3)==".mm" ||
330  n.right(3)==".ii" || // inline
331  n.right(4)==".ixx" ||
332  n.right(4)==".ipp" ||
333  n.right(4)==".i++" ||
334  n.right(4)==".inl" ||
335  n.right(4)==".xml" ||
336  n.right(4)==".sql"
337  ) return Entry::SOURCE_SEC;
338  if (n.right(2)==".h" || // header
339  n.right(3)==".hh" ||
340  n.right(4)==".hxx" ||
341  n.right(4)==".hpp" ||
342  n.right(4)==".h++" ||
343  n.right(4)==".idl" ||
344  n.right(4)==".ddl" ||
345  n.right(5)==".pidl" ||
346  n.right(4)==".ice"
347  ) return Entry::HEADER_SEC;
348  return 0;
349 }
350 
351 QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName,
352  const Definition **typedefContext)
353 {
354  //printf("<<resolveTypeDef(%s,%s)\n",
355  // context ? context->name().data() : "<none>",qualifiedName.data());
356  QCString result;
357  if (qualifiedName.isEmpty())
358  {
359  //printf(" qualified name empty!\n");
360  return result;
361  }
362 
363  const Definition *mContext=context;
364  if (typedefContext) *typedefContext=context;
365 
366  // see if the qualified name has a scope part
367  int scopeIndex = qualifiedName.findRev("::");
368  QCString resName=qualifiedName;
369  if (scopeIndex!=-1) // strip scope part for the name
370  {
371  resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
372  if (resName.isEmpty())
373  {
374  // qualifiedName was of form A:: !
375  //printf(" qualified name of form A::!\n");
376  return result;
377  }
378  }
379  MemberDef *md=0;
380  while (mContext && md==0)
381  {
382  // step 1: get the right scope
383  const Definition *resScope=mContext;
384  if (scopeIndex!=-1)
385  {
386  // split-off scope part
387  QCString resScopeName = qualifiedName.left(scopeIndex);
388  //printf("resScopeName='%s'\n",resScopeName.data());
389 
390  // look-up scope in context
391  int is,ps=0;
392  int l;
393  while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
394  {
395  QCString qualScopePart = resScopeName.mid(is,l);
396  QCString tmp = resolveTypeDef(mContext,qualScopePart);
397  if (!tmp.isEmpty()) qualScopePart=tmp;
398  resScope = resScope->findInnerCompound(qualScopePart);
399  //printf("qualScopePart='%s' resScope=%p\n",qualScopePart.data(),resScope);
400  if (resScope==0) break;
401  ps=is+l;
402  }
403  }
404  //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
405 
406  // step 2: get the member
407  if (resScope) // no scope or scope found in the current context
408  {
409  //printf("scope found: %s, look for typedef %s\n",
410  // resScope->qualifiedName().data(),resName.data());
411  MemberNameLinkedMap *mnd=0;
412  if (resScope->definitionType()==Definition::TypeClass)
413  {
415  }
416  else
417  {
419  }
420  MemberName *mn=mnd->find(resName);
421  if (mn)
422  {
423  int minDist=-1;
424  for (const auto &tmd : *mn)
425  {
426  //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
427  // tmd->name().data(), resScope->name().data(),
428  // tmd->getOuterScope()->name().data(), mContext);
429  if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
430  {
431  int dist=isAccessibleFrom(resScope,0,tmd.get());
432  if (dist!=-1 && (md==0 || dist<minDist))
433  {
434  md = tmd.get();
435  minDist = dist;
436  }
437  }
438  }
439  }
440  }
441  mContext=mContext->getOuterScope();
442  }
443 
444  // step 3: get the member's type
445  if (md)
446  {
447  //printf(">>resolveTypeDef: Found typedef name '%s' in scope '%s' value='%s' args='%s'\n",
448  // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
449  // );
450  result=md->typeString();
451  QCString args = md->argsString();
452  if (args.find(")(")!=-1) // typedef of a function/member pointer
453  {
454  result+=args;
455  }
456  else if (args.find('[')!=-1) // typedef of an array
457  {
458  result+=args;
459  }
460  if (typedefContext) *typedefContext=md->getOuterScope();
461  }
462  else
463  {
464  //printf(">>resolveTypeDef: Typedef '%s' not found in scope '%s'!\n",
465  // qualifiedName.data(),context ? context->name().data() : "<global>");
466  }
467  return result;
468 
469 }
470 
471 
475 ClassDef *getClass(const char *n)
476 {
477  if (n==0 || n[0]=='\0') return 0;
478  QCString name=n;
479  ClassDef *result = Doxygen::classSDict->find(name);
480  //if (result==0 && !exact) // also try generic and protocol versions
481  //{
482  // result = Doxygen::classSDict->find(name+"-g");
483  // if (result==0)
484  // {
485  // result = Doxygen::classSDict->find(name+"-p");
486  // }
487  //}
488  //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>");
489  return result;
490 }
491 
493 {
494  if (name==0 || name[0]=='\0') return 0;
495  QCString *subst = Doxygen::namespaceAliasDict[name];
496  if (subst)
497  {
498  int count=0; // recursion detection guard
499  QCString *newSubst;
500  while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
501  {
502  subst=newSubst;
503  count++;
504  }
505  if (count==10)
506  {
507  warn_uncond("possible recursive namespace alias detected for %s!\n",name);
508  }
509  return Doxygen::namespaceSDict->find(subst->data());
510  }
511  else
512  {
513  return Doxygen::namespaceSDict->find(name);
514  }
515 }
516 
519 
520 // forward declaration
521 static const ClassDef *getResolvedClassRec(const Definition *scope,
522  const FileDef *fileScope,
523  const char *n,
524  const MemberDef **pTypeDef,
525  QCString *pTemplSpec,
526  QCString *pResolvedType
527  );
528 int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope,const Definition *item,
529  const QCString &explicitScopePart);
530 
538 const ClassDef *newResolveTypedef(const FileDef *fileScope,
539  const MemberDef *md,
540  const MemberDef **pMemType,
541  QCString *pTemplSpec,
542  QCString *pResolvedType,
543  const ArgumentList *actTemplParams)
544 {
545  //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
546  bool isCached = md->isTypedefValCached(); // value already cached
547  if (isCached)
548  {
549  //printf("Already cached %s->%s [%s]\n",
550  // md->name().data(),
551  // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
552  // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
553 
554  if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
555  if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
556  return md->getCachedTypedefVal();
557  }
558  //printf("new typedef\n");
559  QCString qname = md->qualifiedName();
560  if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
561 
562  g_resolvedTypedefs.insert(qname,md); // put on the trace list
563 
564  const ClassDef *typeClass = md->getClassDef();
565  QCString type = md->typeString(); // get the "value" of the typedef
566  if (typeClass && typeClass->isTemplate() &&
567  actTemplParams && !actTemplParams->empty())
568  {
570  typeClass->templateArguments(),*actTemplParams);
571  }
572  QCString typedefValue = type;
573  int tl=type.length();
574  int ip=tl-1; // remove * and & at the end
575  while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
576  {
577  ip--;
578  }
579  type=type.left(ip+1);
580  type.stripPrefix("const "); // strip leading "const"
581  type.stripPrefix("struct "); // strip leading "struct"
582  type.stripPrefix("union "); // strip leading "union"
583  int sp=0;
584  tl=type.length(); // length may have been changed
585  while (sp<tl && type.at(sp)==' ') sp++;
586  const MemberDef *memTypeDef = 0;
587  const ClassDef *result = getResolvedClassRec(md->getOuterScope(),
588  fileScope,type,&memTypeDef,0,pResolvedType);
589  // if type is a typedef then return what it resolves to.
590  if (memTypeDef && memTypeDef->isTypedef())
591  {
592  result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
593  goto done;
594  }
595  else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
596  {
597  *pMemType = memTypeDef;
598  }
599 
600  //printf("type=%s result=%p\n",type.data(),result);
601  if (result==0)
602  {
603  // try unspecialized version if type is template
604  int si=type.findRev("::");
605  int i=type.find('<');
606  if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
607  {
608  if (pTemplSpec) *pTemplSpec = type.mid(i);
609  result = getResolvedClassRec(md->getOuterScope(),fileScope,
610  type.left(i),0,0,pResolvedType);
611  //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
612  // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
613  }
614  else if (si!=-1) // A::B
615  {
616  i=type.find('<',si);
617  if (i==-1) // Something like A<T>::B => lookup A::B
618  {
619  i=type.length();
620  }
621  else // Something like A<T>::B<S> => lookup A::B, spec=<S>
622  {
623  if (pTemplSpec) *pTemplSpec = type.mid(i);
624  }
625  result = getResolvedClassRec(md->getOuterScope(),fileScope,
627  pResolvedType);
628  }
629 
630  //if (result) ip=si+sp+1;
631  }
632 
633 done:
634  if (pResolvedType)
635  {
636  if (result)
637  {
638  *pResolvedType=result->qualifiedName();
639  //printf("*pResolvedType=%s\n",pResolvedType->data());
640  if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
641  if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
642  }
643  else
644  {
645  *pResolvedType=typedefValue;
646  }
647  }
648 
649  // remember computed value for next time
650  if (result && result->getDefFileName()!="<code>")
651  // this check is needed to prevent that temporary classes that are
652  // introduced while parsing code fragments are being cached here.
653  {
654  //printf("setting cached typedef %p in result %p\n",md,result);
655  //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
656  //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
657  const_cast<MemberDef*>(md)->cacheTypedefVal(result,
658  pTemplSpec ? *pTemplSpec : QCString(),
659  pResolvedType ? *pResolvedType : QCString()
660  );
661  }
662 
663  g_resolvedTypedefs.remove(qname); // remove from the trace list
664 
665  return result;
666 }
667 
671 static QCString substTypedef(const Definition *scope,const FileDef *fileScope,const QCString &name,
672  const MemberDef **pTypeDef=0)
673 {
674  QCString result=name;
675  if (name.isEmpty()) return result;
676 
677  // lookup scope fragment in the symbol map
678  DefinitionIntf *di = Doxygen::symbolMap->find(name);
679  if (di==0) return result; // no matches
680 
681  MemberDef *bestMatch=0;
682  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
683  {
684  // search for the best match
686  Definition *d;
687  int minDistance=10000; // init at "infinite"
688  for (dli.toFirst();(d=dli.current());++dli) // foreach definition
689  {
690  // only look at members
692  {
693  // that are also typedefs
694  MemberDef *md = dynamic_cast<MemberDef *>(d);
695  if (md->isTypedef()) // d is a typedef
696  {
697  // test accessibility of typedef within scope.
698  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
699  if (distance!=-1 && distance<minDistance)
700  // definition is accessible and a better match
701  {
702  minDistance=distance;
703  bestMatch = md;
704  }
705  }
706  }
707  }
708  }
709  else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
710  {
711  Definition *d = (Definition*)di;
712  // that are also typedefs
713  MemberDef *md = dynamic_cast<MemberDef *>(di);
714  if (md->isTypedef()) // d is a typedef
715  {
716  // test accessibility of typedef within scope.
717  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
718  if (distance!=-1) // definition is accessible
719  {
720  bestMatch = md;
721  }
722  }
723  }
724  if (bestMatch)
725  {
726  result = bestMatch->typeString();
727  if (pTypeDef) *pTypeDef=bestMatch;
728  }
729 
730  //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
731  // name.data(),result.data());
732  return result;
733 }
734 
735 static const Definition *endOfPathIsUsedClass(const SDict<Definition> *cl,const QCString &localName)
736 {
737  if (cl)
738  {
740  Definition *cd;
741  for (cli.toFirst();(cd=cli.current());++cli)
742  {
743  if (cd->localName()==localName)
744  {
745  return cd;
746  }
747  }
748  }
749  return 0;
750 }
751 
757 static const Definition *followPath(const Definition *start,const FileDef *fileScope,const QCString &path)
758 {
759  int is,ps;
760  int l;
761  const Definition *current=start;
762  ps=0;
763  //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
764  // for each part of the explicit scope
765  while ((is=getScopeFragment(path,ps,&l))!=-1)
766  {
767  // try to resolve the part if it is a typedef
768  const MemberDef *typeDef=0;
769  QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
770  //printf(" qualScopePart=%s\n",qualScopePart.data());
771  if (typeDef)
772  {
773  const ClassDef *type = newResolveTypedef(fileScope,typeDef);
774  if (type)
775  {
776  //printf("Found type %s\n",type->name().data());
777  return type;
778  }
779  }
780  const Definition *next = current->findInnerCompound(qualScopePart);
781  //printf("++ Looking for %s inside %s result %s\n",
782  // qualScopePart.data(),
783  // current->name().data(),
784  // next?next->name().data():"<null>");
785  if (next==0) // failed to follow the path
786  {
787  //printf("==> next==0!\n");
789  {
790  next = endOfPathIsUsedClass(
791  (dynamic_cast<const NamespaceDef *>(current))->getUsedClasses(),qualScopePart);
792  }
793  else if (current->definitionType()==Definition::TypeFile)
794  {
795  next = endOfPathIsUsedClass(
796  (dynamic_cast<const FileDef *>(current))->getUsedClasses(),qualScopePart);
797  }
798  current = next;
799  if (current==0) break;
800  }
801  else // continue to follow scope
802  {
803  current = next;
804  //printf("==> current = %p\n",current);
805  }
806  ps=is+l;
807  }
808  //printf("followPath(start=%s,path=%s) result=%s\n",
809  // start->name().data(),path.data(),current?current->name().data():"<null>");
810  return current; // path could be followed
811 }
812 
814  const FileDef *fileScope,
815  const Definition *item,
816  const QCString &explicitScopePart=""
817  )
818 {
819  //printf("accessibleViaUsingClass(%p)\n",cl);
820  if (cl) // see if the class was imported via a using statement
821  {
823  Definition *ucd;
824  bool explicitScopePartEmpty = explicitScopePart.isEmpty();
825  for (cli.toFirst();(ucd=cli.current());++cli)
826  {
827  //printf("Trying via used class %s\n",ucd->name().data());
828  const Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
829  if (sc && sc==item) return TRUE;
830  //printf("Try via used class done\n");
831  }
832  }
833  return FALSE;
834 }
835 
837  const FileDef *fileScope,
838  const Definition *item,
839  const QCString &explicitScopePart="")
840 {
841  static QDict<void> visitedDict;
842  if (nl) // check used namespaces for the class
843  {
844  NamespaceSDict::Iterator nli(*nl);
845  NamespaceDef *und;
846  int count=0;
847  for (nli.toFirst();(und=nli.current());++nli,count++)
848  {
849  //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
850  // count,nl->count());
851  const Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
852  if (sc && item->getOuterScope()==sc)
853  {
854  //printf("] found it\n");
855  return TRUE;
856  }
857  if (item->getLanguage()==SrcLangExt_Cpp)
858  {
859  QCString key=und->name();
860  if (und->getUsedNamespaces() && visitedDict.find(key)==0)
861  {
862  visitedDict.insert(key,(void *)0x08);
863 
864  if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
865  {
866  //printf("] found it via recursion\n");
867  return TRUE;
868  }
869 
870  visitedDict.remove(key);
871  }
872  }
873  //printf("] Try via used namespace done\n");
874  }
875  }
876  return FALSE;
877 }
878 
879 const int MAX_STACK_SIZE = 1000;
880 
885 {
886  public:
888  void push(const Definition *scope,const FileDef *fileScope,const Definition *item)
889  {
891  {
892  m_elements[m_index].scope = scope;
893  m_elements[m_index].fileScope = fileScope;
894  m_elements[m_index].item = item;
895  m_index++;
896  }
897  }
898  void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const QCString &expScope)
899  {
901  {
902  m_elements[m_index].scope = scope;
903  m_elements[m_index].fileScope = fileScope;
904  m_elements[m_index].item = item;
905  m_elements[m_index].expScope = expScope;
906  m_index++;
907  }
908  }
909  void pop()
910  {
911  if (m_index>0) m_index--;
912  }
913  bool find(const Definition *scope,const FileDef *fileScope, const Definition *item)
914  {
915  int i=0;
916  for (i=0;i<m_index;i++)
917  {
918  AccessElem *e = &m_elements[i];
919  if (e->scope==scope && e->fileScope==fileScope && e->item==item)
920  {
921  return TRUE;
922  }
923  }
924  return FALSE;
925  }
926  bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const QCString &expScope)
927  {
928  int i=0;
929  for (i=0;i<m_index;i++)
930  {
931  AccessElem *e = &m_elements[i];
932  if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope)
933  {
934  return TRUE;
935  }
936  }
937  return FALSE;
938  }
939 
940  private:
942  struct AccessElem
943  {
946  const Definition *item;
948  };
949  int m_index;
951 };
952 
953 /* Returns the "distance" (=number of levels up) from item to scope, or -1
954  * if item in not inside scope.
955  */
956 int isAccessibleFrom(const Definition *scope,const FileDef *fileScope,const Definition *item)
957 {
958  //printf("<isAccessibleFrom(scope=%s,item=%s itemScope=%s)\n",
959  // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
960 
961  static AccessStack accessStack;
962  if (accessStack.find(scope,fileScope,item))
963  {
964  return -1;
965  }
966  accessStack.push(scope,fileScope,item);
967 
968  int result=0; // assume we found it
969  int i;
970 
971  Definition *itemScope=item->getOuterScope();
972  bool memberAccessibleFromScope =
973  (item->definitionType()==Definition::TypeMember && // a member
974  itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
975  scope->definitionType()==Definition::TypeClass && // accessible
976  (dynamic_cast<const ClassDef*>(scope))->isAccessibleMember(dynamic_cast<const MemberDef *>(item)) // from scope
977  );
978  bool nestedClassInsideBaseClass =
979  (item->definitionType()==Definition::TypeClass && // a nested class
980  itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
981  scope->definitionType()==Definition::TypeClass && // class of scope
982  (dynamic_cast<const ClassDef*>(scope))->isBaseClass(dynamic_cast<ClassDef*>(itemScope),TRUE)
983  );
984 
985  if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass)
986  {
987  //printf("> found it\n");
988  if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent
989  // this is preferred over nested class in this class
990  // see bug 686956
991  }
992  else if (scope==Doxygen::globalScope)
993  {
994  if (fileScope)
995  {
996  SDict<Definition> *cl = fileScope->getUsedClasses();
997  if (accessibleViaUsingClass(cl,fileScope,item))
998  {
999  //printf("> found via used class\n");
1000  goto done;
1001  }
1002  NamespaceSDict *nl = fileScope->getUsedNamespaces();
1003  if (accessibleViaUsingNamespace(nl,fileScope,item))
1004  {
1005  //printf("> found via used namespace\n");
1006  goto done;
1007  }
1008  }
1009  //printf("> reached global scope\n");
1010  result=-1; // not found in path to globalScope
1011  }
1012  else // keep searching
1013  {
1014  // check if scope is a namespace, which is using other classes and namespaces
1016  {
1017  const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
1018  //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
1019  const SDict<Definition> *cl = nscope->getUsedClasses();
1020  if (accessibleViaUsingClass(cl,fileScope,item))
1021  {
1022  //printf("> found via used class\n");
1023  goto done;
1024  }
1025  const NamespaceSDict *nl = nscope->getUsedNamespaces();
1026  if (accessibleViaUsingNamespace(nl,fileScope,item))
1027  {
1028  //printf("> found via used namespace\n");
1029  goto done;
1030  }
1031  }
1032  // repeat for the parent scope
1033  i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
1034  //printf("> result=%d\n",i);
1035  result= (i==-1) ? -1 : i+2;
1036  }
1037 done:
1038  accessStack.pop();
1039  //Doxygen::lookupCache.insert(key,new int(result));
1040  return result;
1041 }
1042 
1043 
1044 /* Returns the "distance" (=number of levels up) from item to scope, or -1
1045  * if item in not in this scope. The explicitScopePart limits the search
1046  * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
1047  * Example:
1048  *
1049  * class A { public: class I {}; };
1050  * class B { public: class J {}; };
1051  *
1052  * - Looking for item=='J' inside scope=='B' will return 0.
1053  * - Looking for item=='I' inside scope=='B' will return -1
1054  * (as it is not found in B nor in the global scope).
1055  * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
1056  * not found and then A::I is searched in the global scope, which matches and
1057  * thus the result is 1.
1058  */
1059 int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope,
1060  const Definition *item,const QCString &explicitScopePart)
1061 {
1062  if (explicitScopePart.isEmpty())
1063  {
1064  // handle degenerate case where there is no explicit scope.
1065  return isAccessibleFrom(scope,fileScope,item);
1066  }
1067 
1068  static AccessStack accessStack;
1069  if (accessStack.find(scope,fileScope,item,explicitScopePart))
1070  {
1071  return -1;
1072  }
1073  accessStack.push(scope,fileScope,item,explicitScopePart);
1074 
1075 
1076  //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
1077  // item?item->name().data():"<none>",
1078  // explicitScopePart.data());
1079  int result=0; // assume we found it
1080  const Definition *newScope = followPath(scope,fileScope,explicitScopePart);
1081  if (newScope) // explicitScope is inside scope => newScope is the result
1082  {
1083  Definition *itemScope = item->getOuterScope();
1084  //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
1085  //if (newScope && newScope->definitionType()==Definition::TypeClass)
1086  //{
1087  // ClassDef *cd = (ClassDef *)newScope;
1088  // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
1089  //}
1090  if (itemScope==newScope) // exact match of scopes => distance==0
1091  {
1092  //printf("> found it\n");
1093  }
1094  else if (itemScope && newScope &&
1095  itemScope->definitionType()==Definition::TypeClass &&
1096  newScope->definitionType()==Definition::TypeClass &&
1097  (dynamic_cast<const ClassDef*>(newScope))->isBaseClass(dynamic_cast<const ClassDef*>(itemScope),TRUE,0)
1098  )
1099  {
1100  // inheritance is also ok. Example: looking for B::I, where
1101  // class A { public: class I {} };
1102  // class B : public A {}
1103  // but looking for B::I, where
1104  // class A { public: class I {} };
1105  // class B { public: class I {} };
1106  // will find A::I, so we still prefer a direct match and give this one a distance of 1
1107  result=1;
1108 
1109  //printf("scope(%s) is base class of newScope(%s)\n",
1110  // scope->name().data(),newScope->name().data());
1111  }
1112  else
1113  {
1114  int i=-1;
1115  if (newScope->definitionType()==Definition::TypeNamespace)
1116  {
1117  g_visitedNamespaces.insert(newScope->name(),newScope);
1118  // this part deals with the case where item is a class
1119  // A::B::C but is explicit referenced as A::C, where B is imported
1120  // in A via a using directive.
1121  //printf("newScope is a namespace: %s!\n",newScope->name().data());
1122  const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(newScope);
1123  const SDict<Definition> *cl = nscope->getUsedClasses();
1124  if (cl)
1125  {
1126  SDict<Definition>::Iterator cli(*cl);
1127  const Definition *cd;
1128  for (cli.toFirst();(cd=cli.current());++cli)
1129  {
1130  //printf("Trying for class %s\n",cd->name().data());
1131  if (cd==item)
1132  {
1133  //printf("> class is used in this scope\n");
1134  goto done;
1135  }
1136  }
1137  }
1138  const NamespaceSDict *nl = nscope->getUsedNamespaces();
1139  if (nl)
1140  {
1141  NamespaceSDict::Iterator nli(*nl);
1142  const NamespaceDef *nd;
1143  for (nli.toFirst();(nd=nli.current());++nli)
1144  {
1145  if (g_visitedNamespaces.find(nd->name())==0)
1146  {
1147  //printf("Trying for namespace %s\n",nd->name().data());
1148  i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
1149  if (i!=-1)
1150  {
1151  //printf("> found via explicit scope of used namespace\n");
1152  goto done;
1153  }
1154  }
1155  }
1156  }
1157  }
1158  // repeat for the parent scope
1159  if (scope!=Doxygen::globalScope)
1160  {
1161  i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1162  item,explicitScopePart);
1163  }
1164  //printf(" | result=%d\n",i);
1165  result = (i==-1) ? -1 : i+2;
1166  }
1167  }
1168  else // failed to resolve explicitScope
1169  {
1170  //printf(" failed to resolve: scope=%s\n",scope->name().data());
1172  {
1173  const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope);
1174  const NamespaceSDict *nl = nscope->getUsedNamespaces();
1175  if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1176  {
1177  //printf("> found in used namespace\n");
1178  goto done;
1179  }
1180  }
1181  if (scope==Doxygen::globalScope)
1182  {
1183  if (fileScope)
1184  {
1185  const NamespaceSDict *nl = fileScope->getUsedNamespaces();
1186  if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1187  {
1188  //printf("> found in used namespace\n");
1189  goto done;
1190  }
1191  }
1192  //printf("> not found\n");
1193  result=-1;
1194  }
1195  else // continue by looking into the parent scope
1196  {
1197  int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1198  item,explicitScopePart);
1199  //printf("> result=%d\n",i);
1200  result= (i==-1) ? -1 : i+2;
1201  }
1202  }
1203 
1204 done:
1205  //printf(" > result=%d\n",result);
1206  accessStack.pop();
1207  //Doxygen::lookupCache.insert(key,new int(result));
1208  return result;
1209 }
1210 
1212 {
1213  int i = name.find('<');
1214  return name.findRev("::",i==-1 ? name.length() : i);
1215 }
1216 
1217 static void getResolvedSymbol(const Definition *scope,
1218  const FileDef *fileScope,
1219  Definition *d,
1220  const QCString &explicitScopePart,
1221  ArgumentList *actTemplParams,
1222  int &minDistance,
1223  const ClassDef *&bestMatch,
1224  const MemberDef *&bestTypedef,
1225  QCString &bestTemplSpec,
1226  QCString &bestResolvedType
1227  )
1228 {
1229  //printf(" => found type %x name=%s d=%p\n",
1230  // d->definitionType(),d->name().data(),d);
1231 
1232  // only look at classes and members that are enums or typedefs
1235  ((dynamic_cast<MemberDef*>(d))->isTypedef() || (dynamic_cast<MemberDef*>(d))->isEnumerate())
1236  )
1237  )
1238  {
1239  g_visitedNamespaces.clear();
1240  // test accessibility of definition within scope.
1241  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
1242  //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
1243  if (distance!=-1) // definition is accessible
1244  {
1245  // see if we are dealing with a class or a typedef
1246  if (d->definitionType()==Definition::TypeClass) // d is a class
1247  {
1248  ClassDef *cd = dynamic_cast<ClassDef *>(d);
1249  //printf("cd=%s\n",cd->name().data());
1250  if (!cd->isTemplateArgument()) // skip classes that
1251  // are only there to
1252  // represent a template
1253  // argument
1254  {
1255  //printf("is not a templ arg\n");
1256  if (distance<minDistance) // found a definition that is "closer"
1257  {
1258  minDistance=distance;
1259  bestMatch = cd;
1260  bestTypedef = 0;
1261  bestTemplSpec.resize(0);
1262  bestResolvedType = cd->qualifiedName();
1263  }
1264  else if (distance==minDistance &&
1265  fileScope && bestMatch &&
1266  fileScope->getUsedNamespaces() &&
1268  bestMatch->getOuterScope()==Doxygen::globalScope
1269  )
1270  {
1271  // in case the distance is equal it could be that a class X
1272  // is defined in a namespace and in the global scope. When searched
1273  // in the global scope the distance is 0 in both cases. We have
1274  // to choose one of the definitions: we choose the one in the
1275  // namespace if the fileScope imports namespaces and the definition
1276  // found was in a namespace while the best match so far isn't.
1277  // Just a non-perfect heuristic but it could help in some situations
1278  // (kdecore code is an example).
1279  minDistance=distance;
1280  bestMatch = cd;
1281  bestTypedef = 0;
1282  bestTemplSpec.resize(0);
1283  bestResolvedType = cd->qualifiedName();
1284  }
1285  }
1286  else
1287  {
1288  //printf(" is a template argument!\n");
1289  }
1290  }
1291  else if (d->definitionType()==Definition::TypeMember)
1292  {
1293  MemberDef *md = dynamic_cast<MemberDef *>(d);
1294  //printf(" member isTypedef()=%d\n",md->isTypedef());
1295  if (md->isTypedef()) // d is a typedef
1296  {
1297  QCString args=md->argsString();
1298  if (args.isEmpty()) // do not expand "typedef t a[4];"
1299  {
1300  //printf(" found typedef!\n");
1301 
1302  // we found a symbol at this distance, but if it didn't
1303  // resolve to a class, we still have to make sure that
1304  // something at a greater distance does not match, since
1305  // that symbol is hidden by this one.
1306  if (distance<minDistance)
1307  {
1308  QCString spec;
1309  QCString type;
1310  minDistance=distance;
1311  const MemberDef *enumType = 0;
1312  const ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
1313  if (cd) // type resolves to a class
1314  {
1315  //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
1316  bestMatch = cd;
1317  bestTypedef = md;
1318  bestTemplSpec = spec;
1319  bestResolvedType = type;
1320  }
1321  else if (enumType) // type resolves to a enum
1322  {
1323  //printf(" is enum\n");
1324  bestMatch = 0;
1325  bestTypedef = enumType;
1326  bestTemplSpec = "";
1327  bestResolvedType = enumType->qualifiedName();
1328  }
1329  else if (md->isReference()) // external reference
1330  {
1331  bestMatch = 0;
1332  bestTypedef = md;
1333  bestTemplSpec = spec;
1334  bestResolvedType = type;
1335  }
1336  else
1337  {
1338  bestMatch = 0;
1339  bestTypedef = md;
1340  bestTemplSpec.resize(0);
1341  bestResolvedType.resize(0);
1342  //printf(" no match\n");
1343  }
1344  }
1345  else
1346  {
1347  //printf(" not the best match %d min=%d\n",distance,minDistance);
1348  }
1349  }
1350  else
1351  {
1352  //printf(" not a simple typedef\n")
1353  }
1354  }
1355  else if (md->isEnumerate())
1356  {
1357  if (distance<minDistance)
1358  {
1359  minDistance=distance;
1360  bestMatch = 0;
1361  bestTypedef = md;
1362  bestTemplSpec = "";
1363  bestResolvedType = md->qualifiedName();
1364  }
1365  }
1366  }
1367  } // if definition accessible
1368  else
1369  {
1370  //printf(" Not accessible!\n");
1371  }
1372  } // if definition is a class or member
1373  //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
1374 }
1375 
1376 /* Find the fully qualified class name referred to by the input class
1377  * or typedef name against the input scope.
1378  * Loops through scope and each of its parent scopes looking for a
1379  * match against the input name. Can recursively call itself when
1380  * resolving typedefs.
1381  */
1382 static const ClassDef *getResolvedClassRec(const Definition *scope,
1383  const FileDef *fileScope,
1384  const char *n,
1385  const MemberDef **pTypeDef,
1386  QCString *pTemplSpec,
1387  QCString *pResolvedType
1388  )
1389 {
1390  //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
1391  QCString name;
1392  QCString explicitScopePart;
1393  QCString strippedTemplateParams;
1396  &strippedTemplateParams);
1397  ArgumentList actTemplParams;
1398  if (!strippedTemplateParams.isEmpty()) // template part that was stripped
1399  {
1400  stringToArgumentList(scope->getLanguage(),strippedTemplateParams,actTemplParams);
1401  }
1402 
1403  int qualifierIndex = computeQualifiedIndex(name);
1404  //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
1405  if (qualifierIndex!=-1) // qualified name
1406  {
1407  // split off the explicit scope part
1408  explicitScopePart=name.left(qualifierIndex);
1409  // todo: improve namespace alias substitution
1410  replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
1411  name=name.mid(qualifierIndex+2);
1412  }
1413 
1414  if (name.isEmpty())
1415  {
1416  //printf("] empty name\n");
1417  return 0; // empty name
1418  }
1419 
1420  //printf("Looking for symbol %s\n",name.data());
1421  DefinitionIntf *di = Doxygen::symbolMap->find(name);
1422  // the -g (for C# generics) and -p (for ObjC protocols) are now already
1423  // stripped from the key used in the symbolMap, so that is not needed here.
1424  if (di==0)
1425  {
1426  //di = Doxygen::symbolMap->find(name+"-g");
1427  //if (di==0)
1428  //{
1429  di = Doxygen::symbolMap->find(name+"-p");
1430  if (di==0)
1431  {
1432  //printf("no such symbol!\n");
1433  return 0;
1434  }
1435  //}
1436  }
1437  //printf("found symbol!\n");
1438 
1439  bool hasUsingStatements =
1440  (fileScope && ((fileScope->getUsedNamespaces() &&
1441  fileScope->getUsedNamespaces()->count()>0) ||
1442  (fileScope->getUsedClasses() &&
1443  fileScope->getUsedClasses()->count()>0))
1444  );
1445  //printf("hasUsingStatements=%d\n",hasUsingStatements);
1446  // Since it is often the case that the same name is searched in the same
1447  // scope over an over again (especially for the linked source code generation)
1448  // we use a cache to collect previous results. This is possible since the
1449  // result of a lookup is deterministic. As the key we use the concatenated
1450  // scope, the name to search for and the explicit scope prefix. The speedup
1451  // achieved by this simple cache can be enormous.
1452  int scopeNameLen = scope->name().length()+1;
1453  int nameLen = name.length()+1;
1454  int explicitPartLen = explicitScopePart.length();
1455  int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
1456 
1457  // below is a more efficient coding of
1458  // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
1459  QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
1460  char *p=key.rawData();
1461  qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
1462  p+=scopeNameLen;
1463  qstrcpy(p,name); *(p+nameLen-1)='+';
1464  p+=nameLen;
1465  qstrcpy(p,explicitScopePart);
1466  p+=explicitPartLen;
1467 
1468  // if a file scope is given and it contains using statements we should
1469  // also use the file part in the key (as a class name can be in
1470  // two different namespaces and a using statement in a file can select
1471  // one of them).
1472  if (hasUsingStatements)
1473  {
1474  // below is a more efficient coding of
1475  // key+="+"+fileScope->name();
1476  *p++='+';
1477  qstrcpy(p,fileScope->absFilePath());
1478  p+=fileScopeLen-1;
1479  }
1480  *p='\0';
1481 
1482  LookupInfo *pval=Doxygen::lookupCache->find(key);
1483  //printf("Searching for %s result=%p\n",key.data(),pval);
1484  if (pval)
1485  {
1486  //printf("LookupInfo %p %p '%s' %p\n",
1487  // pval->classDef, pval->typeDef, pval->templSpec.data(),
1488  // pval->resolvedType.data());
1489  if (pTemplSpec) *pTemplSpec=pval->templSpec;
1490  if (pTypeDef) *pTypeDef=pval->typeDef;
1491  if (pResolvedType) *pResolvedType=pval->resolvedType;
1492  //printf("] cachedMatch=%s\n",
1493  // pval->classDef?pval->classDef->name().data():"<none>");
1494  //if (pTemplSpec)
1495  // printf("templSpec=%s\n",pTemplSpec->data());
1496  return pval->classDef;
1497  }
1498  else // not found yet; we already add a 0 to avoid the possibility of
1499  // endless recursion.
1500  {
1502  }
1503 
1504  const ClassDef *bestMatch=0;
1505  const MemberDef *bestTypedef=0;
1506  QCString bestTemplSpec;
1507  QCString bestResolvedType;
1508  int minDistance=10000; // init at "infinite"
1509 
1510  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
1511  {
1512  //printf(" name is not unique\n");
1514  Definition *d;
1515  int count=0;
1516  for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
1517  {
1518  getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1519  minDistance,bestMatch,bestTypedef,bestTemplSpec,
1520  bestResolvedType);
1521  }
1522  }
1523  else // unique name
1524  {
1525  //printf(" name is unique\n");
1526  Definition *d = (Definition *)di;
1527  getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1528  minDistance,bestMatch,bestTypedef,bestTemplSpec,
1529  bestResolvedType);
1530  }
1531 
1532  if (pTypeDef)
1533  {
1534  *pTypeDef = bestTypedef;
1535  }
1536  if (pTemplSpec)
1537  {
1538  *pTemplSpec = bestTemplSpec;
1539  }
1540  if (pResolvedType)
1541  {
1542  *pResolvedType = bestResolvedType;
1543  }
1544  //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
1545  // bestMatch,bestResolvedType.data());
1546 
1547  pval=Doxygen::lookupCache->find(key);
1548  if (pval)
1549  {
1550  pval->classDef = bestMatch;
1551  pval->typeDef = bestTypedef;
1552  pval->templSpec = bestTemplSpec;
1553  pval->resolvedType = bestResolvedType;
1554  }
1555  else
1556  {
1557  Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
1558  }
1559  //printf("] bestMatch=%s distance=%d\n",
1560  // bestMatch?bestMatch->name().data():"<none>",minDistance);
1561  //if (pTemplSpec)
1562  // printf("templSpec=%s\n",pTemplSpec->data());
1563  return bestMatch;
1564 }
1565 
1566 /* Find the fully qualified class name referred to by the input class
1567  * or typedef name against the input scope.
1568  * Loops through scope and each of its parent scopes looking for a
1569  * match against the input name.
1570  */
1572  const FileDef *fileScope,
1573  const char *n,
1574  const MemberDef **pTypeDef,
1575  QCString *pTemplSpec,
1576  bool mayBeUnlinkable,
1577  bool mayBeHidden,
1578  QCString *pResolvedType
1579  )
1580 {
1581  static bool optimizeOutputVhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1582  g_resolvedTypedefs.clear();
1583  if (scope==0 ||
1586  ) ||
1587  (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
1588  )
1589  {
1590  scope=Doxygen::globalScope;
1591  }
1592  //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
1593  // scope?scope->name().data():"<global>",
1594  // fileScope?fileScope->name().data():"<none>",
1595  // n,
1596  // mayBeUnlinkable
1597  // );
1598  const ClassDef *result;
1599  if (optimizeOutputVhdl)
1600  {
1601  result = getClass(n);
1602  }
1603  else
1604  {
1605  result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
1606  }
1607  if (result==0) // for nested classes imported via tag files, the scope may not
1608  // present, so we check the class name directly as well.
1609  // See also bug701314
1610  {
1611  result = getClass(n);
1612  }
1613  if (!mayBeUnlinkable && result && !result->isLinkable())
1614  {
1615  if (!mayBeHidden || !result->isHidden())
1616  {
1617  //printf("result was %s\n",result?result->name().data():"<none>");
1618  result=0; // don't link to artificial/hidden classes unless explicitly allowed
1619  }
1620  }
1621  //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
1622  // n,result?result->name().data():"<none>");
1623  return result;
1624 }
1625 
1626 //-------------------------------------------------------------------------
1627 //-------------------------------------------------------------------------
1628 //-------------------------------------------------------------------------
1629 //-------------------------------------------------------------------------
1630 
1631 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
1632 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
1633 static const char operatorScope[] = { 'o', 'p', 'e', 'r', 'a', 't', 'o', 'r', '?', '?', '?' };
1634 
1636 {
1638  {
1639  charMap[static_cast<int>('(')].before=FALSE;
1640  charMap[static_cast<int>('=')].before=FALSE;
1641  charMap[static_cast<int>('&')].before=FALSE;
1642  charMap[static_cast<int>('*')].before=FALSE;
1643  charMap[static_cast<int>('[')].before=FALSE;
1644  charMap[static_cast<int>('|')].before=FALSE;
1645  charMap[static_cast<int>('+')].before=FALSE;
1646  charMap[static_cast<int>(';')].before=FALSE;
1647  charMap[static_cast<int>(':')].before=FALSE;
1648  charMap[static_cast<int>('/')].before=FALSE;
1649 
1650  charMap[static_cast<int>('=')].after=FALSE;
1651  charMap[static_cast<int>(' ')].after=FALSE;
1652  charMap[static_cast<int>('[')].after=FALSE;
1653  charMap[static_cast<int>(']')].after=FALSE;
1654  charMap[static_cast<int>('\t')].after=FALSE;
1655  charMap[static_cast<int>('\n')].after=FALSE;
1656  charMap[static_cast<int>(')')].after=FALSE;
1657  charMap[static_cast<int>(',')].after=FALSE;
1658  charMap[static_cast<int>('<')].after=FALSE;
1659  charMap[static_cast<int>('|')].after=FALSE;
1660  charMap[static_cast<int>('+')].after=FALSE;
1661  charMap[static_cast<int>('(')].after=FALSE;
1662  charMap[static_cast<int>('/')].after=FALSE;
1663  }
1664  struct CharElem
1665  {
1667  bool before;
1668  bool after;
1669  };
1670 
1672 };
1673 
1675 
1676 // Note: this function is not reentrant due to the use of static buffer!
1678 {
1679  static bool cliSupport = Config_getBool(CPP_CLI_SUPPORT);
1680  static bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1681 
1682  if (s.isEmpty() || vhdl) return s;
1683 
1684  // We use a static character array to
1685  // improve the performance of this function
1686  static char *growBuf = 0;
1687  static int growBufLen = 0;
1688  if ((int)s.length()*3>growBufLen) // For input character we produce at most 3 output characters,
1689  {
1690  growBufLen = s.length()*3;
1691  growBuf = (char *)realloc(growBuf,growBufLen+1); // add 1 for 0-terminator
1692  }
1693  if (growBuf==0) return s; // should not happen, only we run out of memory
1694 
1695  char *src=s.rawData();
1696  char *dst=growBuf;
1697 
1698  uint i=0;
1699  uint l=s.length();
1700  uint csp=0;
1701  uint vsp=0;
1702  uint osp=0;
1703  char c;
1704  char pc=0;
1705  // skip leading whitespace
1706  while (i<l && isspace((uchar)src[i]))
1707  {
1708  i++;
1709  }
1710  for (;i<l;i++)
1711  {
1712  c=src[i];
1713  char nc=i<l-1 ? src[i+1] : ' ';
1714 
1715  // search for "const"
1716  if (csp<6 && c==constScope[csp] && // character matches substring "const"
1717  (csp>0 || // inside search string
1718  i==0 || // if it is the first character
1719  !isId(pc) // the previous may not be a digit
1720  )
1721  )
1722  csp++;
1723  else // reset counter
1724  csp=0;
1725 
1726  // search for "virtual"
1727  if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
1728  (vsp>0 || // inside search string
1729  i==0 || // if it is the first character
1730  !isId(pc) // the previous may not be a digit
1731  )
1732  )
1733  vsp++;
1734  else // reset counter
1735  vsp=0;
1736 
1737  // search for "operator"
1738  if (osp<11 && (osp>=8 || c==operatorScope[osp]) && // character matches substring "operator" followed by 3 arbitrary characters
1739  (osp>0 || // inside search string
1740  i==0 || // if it is the first character
1741  !isId(pc) // the previous may not be a digit
1742  )
1743  )
1744  osp++;
1745  else // reset counter
1746  osp=0;
1747 
1748  switch(c)
1749  {
1750  case '"': // quoted string
1751  {
1752  *dst++=c;
1753  pc = c;
1754  i++;
1755  for (;i<l;i++) // find end of string
1756  {
1757  c = src[i];
1758  *dst++=c;
1759  if (c=='\\' && i+1<l)
1760  {
1761  pc = c;
1762  i++;
1763  c = src[i];
1764  *dst++=c;
1765  }
1766  else if (c=='"')
1767  {
1768  break;
1769  }
1770  pc = c;
1771  }
1772  }
1773  break;
1774  case '<': // current char is a <
1775  *dst++=c;
1776  if (i<l-1 &&
1777  (isId(nc)) && // next char is an id char
1778  (osp<8) // string in front is not "operator"
1779  )
1780  {
1781  *dst++=' '; // add extra space
1782  }
1783  break;
1784  case '>': // current char is a >
1785  if (i>0 && !isspace((uchar)pc) &&
1786  (isId(pc) || pc=='*' || pc=='&' || pc=='.') && // prev char is an id char or space or *&.
1787  (osp<8 || (osp==8 && pc!='-')) // string in front is not "operator>" or "operator->"
1788  )
1789  {
1790  *dst++=' '; // add extra space in front
1791  }
1792  *dst++=c;
1793  if (i<l-1 && (nc=='-' || nc=='&')) // '>-' -> '> -'
1794  {
1795  *dst++=' '; // add extra space after
1796  }
1797  break;
1798  case ',': // current char is a ,
1799  *dst++=c;
1800  if (i>0 && !isspace((uchar)pc) &&
1801  ((i<l-1 && (isId(nc) || nc=='[')) || // the [ is for attributes (see bug702170)
1802  (i<l-2 && nc=='$' && isId(src[i+2])) || // for PHP: ',$name' -> ', $name'
1803  (i<l-3 && nc=='&' && src[i+2]=='$' && isId(src[i+3])) // for PHP: ',&$name' -> ', &$name'
1804  )
1805  )
1806  {
1807  *dst++=' '; // add extra space after
1808  }
1809  break;
1810  case '^': // CLI 'Type^name' -> 'Type^ name'
1811  case '%': // CLI 'Type%name' -> 'Type% name'
1812  *dst++=c;
1813  if (cliSupport && i<l-1 && (isId(nc) || nc=='-'))
1814  {
1815  *dst++=' '; // add extra space after
1816  }
1817  break;
1818  case ')': // current char is a ) -> ')name' -> ') name'
1819  *dst++=c;
1820  if (i<l-1 && (isId(nc) || nc=='-'))
1821  {
1822  *dst++=' '; // add extra space after
1823  }
1824  break;
1825  case '*':
1826  if (i>0 && pc!=' ' && pc!='\t' && pc!=':' &&
1827  pc!='*' && pc!='&' && pc!='(' && pc!='/' &&
1828  pc!='.' && osp<9
1829  )
1830  // avoid splitting &&, **, .*, operator*, operator->*
1831  {
1832  *dst++=' ';
1833  }
1834  *dst++=c;
1835  break;
1836  case '&':
1837  if (i>0 && isId(pc) && osp<9)
1838  {
1839  if (nc != '=')
1840  // avoid splitting operator&=
1841  {
1842  *dst++=' ';
1843  }
1844  }
1845  *dst++=c;
1846  break;
1847  case '@': // '@name' -> ' @name'
1848  case '$': // '$name' -> ' $name'
1849  case '\'': // ''name' -> '' name'
1850  if (i>0 && i<l-1 && pc!='=' && pc!=':' && !isspace((uchar)pc) &&
1851  isId(nc) && osp<8) // ")id" -> ") id"
1852  {
1853  *dst++=' ';
1854  }
1855  *dst++=c;
1856  break;
1857  case ':': // current char is a :
1858  if (csp==6) // replace const::A by const ::A
1859  {
1860  *dst++=' ';
1861  csp=0;
1862  }
1863  else if (vsp==8) // replace virtual::A by virtual ::A
1864  {
1865  *dst++=' ';
1866  vsp=0;
1867  }
1868  *dst++=c;
1869  break;
1870  case ' ': // fallthrough
1871  case '\n': // fallthrough
1872  case '\t':
1873  {
1875  g_charAroundSpace.charMap[(uchar)nc].after &&
1876  !(pc==',' && nc=='.') &&
1877  (osp<8 || (osp>=8 && isId(pc) && isId(nc)))
1878  // e.g. 'operator >>' -> 'operator>>',
1879  // 'operator "" _x' -> 'operator""_x',
1880  // but not 'operator int' -> 'operatorint'
1881  )
1882  { // keep space
1883  *dst++=' ';
1884  }
1885  else if ((pc=='*' || pc=='&' || pc=='.') && nc=='>')
1886  {
1887  *dst++=' ';
1888  }
1889  }
1890  break;
1891  default:
1892  *dst++=c;
1893  if (c=='t' && csp==5 && i<l-1 && // found 't' in 'const'
1894  !(isId(nc) || nc==')' || nc==',' || isspace((uchar)nc))
1895  ) // prevent const ::A from being converted to const::A
1896  {
1897  *dst++=' ';
1898  csp=0;
1899  }
1900  else if (c=='l' && vsp==7 && i<l-1 && // found 'l' in 'virtual'
1901  !(isId(nc) || nc==')' || nc==',' || isspace((uchar)nc))
1902  ) // prevent virtual ::A from being converted to virtual::A
1903  {
1904  *dst++=' ';
1905  vsp=0;
1906  }
1907  break;
1908  }
1909  pc=c;
1910  }
1911  *dst++='\0';
1912  //printf("removeRedundantWhitespace(%s)->%s\n",s.data(),growBuf);
1913  return growBuf;
1914 }
1915 
1920 int findParameterList(const QCString &name)
1921 {
1922  int pos=-1;
1923  int templateDepth=0;
1924  do
1925  {
1926  if (templateDepth > 0)
1927  {
1928  int nextOpenPos=name.findRev('>', pos);
1929  int nextClosePos=name.findRev('<', pos);
1930  if (nextOpenPos!=-1 && nextOpenPos>nextClosePos)
1931  {
1932  ++templateDepth;
1933  pos=nextOpenPos-1;
1934  }
1935  else if (nextClosePos!=-1)
1936  {
1937  --templateDepth;
1938  pos=nextClosePos-1;
1939  }
1940  else // more >'s than <'s, see bug701295
1941  {
1942  return -1;
1943  }
1944  }
1945  else
1946  {
1947  int lastAnglePos=name.findRev('>', pos);
1948  int bracePos=name.findRev('(', pos);
1949  if (lastAnglePos!=-1 && lastAnglePos>bracePos)
1950  {
1951  ++templateDepth;
1952  pos=lastAnglePos-1;
1953  }
1954  else
1955  {
1956  int bp = bracePos>0 ? name.findRev('(',bracePos-1) : -1;
1957  // bp test is to allow foo(int(&)[10]), but we need to make an exception for operator()
1958  return bp==-1 || (bp>=8 && name.mid(bp-8,10)=="operator()") ? bracePos : bp;
1959  }
1960  }
1961  } while (pos!=-1);
1962  return -1;
1963 }
1964 
1965 bool rightScopeMatch(const QCString &scope, const QCString &name)
1966 {
1967  int sl=scope.length();
1968  int nl=name.length();
1969  return (name==scope || // equal
1970  (scope.right(nl)==name && // substring
1971  sl-nl>1 && scope.at(sl-nl-1)==':' && scope.at(sl-nl-2)==':' // scope
1972  )
1973  );
1974 }
1975 
1976 bool leftScopeMatch(const QCString &scope, const QCString &name)
1977 {
1978  int sl=scope.length();
1979  int nl=name.length();
1980  return (name==scope || // equal
1981  (scope.left(nl)==name && // substring
1982  sl>nl+1 && scope.at(nl)==':' && scope.at(nl+1)==':' // scope
1983  )
1984  );
1985 }
1986 
1987 
1988 void linkifyText(const TextGeneratorIntf &out, const Definition *scope,
1989  const FileDef *fileScope,const Definition *self,
1990  const char *text, bool autoBreak,bool external,
1991  bool keepSpaces,int indentLevel)
1992 {
1993  //printf("linkify='%s'\n",text);
1994  static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*");
1995  static QRegExp regExpSplit("(?!:),");
1996  QCString txtStr=text;
1997  int strLen = txtStr.length();
1998  //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n",
1999  // scope?scope->name().data():"<none>",
2000  // fileScope?fileScope->name().data():"<none>",
2001  // txtStr.data(),strLen,external);
2002  int matchLen;
2003  int index=0;
2004  int newIndex;
2005  int skipIndex=0;
2006  int floatingIndex=0;
2007  if (strLen==0) return;
2008  // read a word from the text string
2009  while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1)
2010  {
2011  floatingIndex+=newIndex-skipIndex+matchLen;
2012  if (newIndex>0 && txtStr.at(newIndex-1)=='0') // ignore hex numbers (match x00 in 0x00)
2013  {
2014  out.writeString(txtStr.mid(skipIndex,newIndex+matchLen-skipIndex),keepSpaces);
2015  skipIndex=index=newIndex+matchLen;
2016  continue;
2017  }
2018 
2019  // add non-word part to the result
2020  bool insideString=FALSE;
2021  int i;
2022  for (i=index;i<newIndex;i++)
2023  {
2024  if (txtStr.at(i)=='"') insideString=!insideString;
2025  }
2026 
2027  //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
2028  if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
2029  {
2030  QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
2031  int splitLength = splitText.length();
2032  int offset=1;
2033  i=splitText.find(regExpSplit,0);
2034  if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
2035  if (i==-1) i=splitText.find('>');
2036  if (i==-1) i=splitText.find(' ');
2037  //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
2038  if (i!=-1) // add a link-break at i in case of Html output
2039  {
2040  out.writeString(splitText.left(i+offset),keepSpaces);
2041  out.writeBreak(indentLevel==0 ? 0 : indentLevel+1);
2042  out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
2043  floatingIndex=splitLength-i-offset+matchLen;
2044  }
2045  else
2046  {
2047  out.writeString(splitText,keepSpaces);
2048  }
2049  }
2050  else
2051  {
2052  //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
2053  out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces);
2054  }
2055  // get word from string
2056  QCString word=txtStr.mid(newIndex,matchLen);
2057  QCString matchWord = substitute(substitute(word,"\\","::"),".","::");
2058  //printf("linkifyText word=%s matchWord=%s scope=%s\n",
2059  // word.data(),matchWord.data(),scope?scope->name().data():"<none>");
2060  bool found=FALSE;
2061  if (!insideString)
2062  {
2063  const MemberDef *md=0;
2064  const ClassDef *cd=0;
2065  const FileDef *fd=0;
2066  const NamespaceDef *nd=0;
2067  const GroupDef *gd=0;
2068  //printf("** Match word '%s'\n",matchWord.data());
2069 
2070  const MemberDef *typeDef=0;
2071  cd=getResolvedClass(scope,fileScope,matchWord,&typeDef);
2072  if (typeDef) // First look at typedef then class, see bug 584184.
2073  {
2074  //printf("Found typedef %s\n",typeDef->name().data());
2075  if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
2076  {
2077  if (typeDef->getOuterScope()!=self)
2078  {
2079  out.writeLink(typeDef->getReference(),
2080  typeDef->getOutputFileBase(),
2081  typeDef->anchor(),
2082  word);
2083  found=TRUE;
2084  }
2085  }
2086  }
2087  if (!found && (cd || (cd=getClass(matchWord))))
2088  {
2089  //printf("Found class %s\n",cd->name().data());
2090  // add link to the result
2091  if (external ? cd->isLinkable() : cd->isLinkableInProject())
2092  {
2093  if (cd!=self)
2094  {
2095  out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2096  found=TRUE;
2097  }
2098  }
2099  }
2100  else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
2101  {
2102  // add link to the result
2103  if (external ? cd->isLinkable() : cd->isLinkableInProject())
2104  {
2105  if (cd!=self)
2106  {
2107  out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2108  found=TRUE;
2109  }
2110  }
2111  }
2112 // else if ((cd=getClass(matchWord+"-g"))) // C# generic as well
2113 // {
2114 // // add link to the result
2115 // if (external ? cd->isLinkable() : cd->isLinkableInProject())
2116 // {
2117 // if (cd!=self)
2118 // {
2119 // out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2120 // found=TRUE;
2121 // }
2122 // }
2123 // }
2124  else
2125  {
2126  //printf(" -> nothing\n");
2127  }
2128 
2129  int m = matchWord.findRev("::");
2130  QCString scopeName;
2131  if (scope &&
2134  )
2135  )
2136  {
2137  scopeName=scope->name();
2138  }
2139  else if (m!=-1)
2140  {
2141  scopeName = matchWord.left(m);
2142  matchWord = matchWord.mid(m+2);
2143  }
2144 
2145  //printf("ScopeName=%s\n",scopeName.data());
2146  //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data());
2147  if (!found &&
2148  getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) &&
2149  //(md->isTypedef() || md->isEnumerate() ||
2150  // md->isReference() || md->isVariable()
2151  //) &&
2152  (external ? md->isLinkable() : md->isLinkableInProject())
2153  )
2154  {
2155  //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
2156  //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
2157  // md->anchor(),word);
2158  if (md!=self && (self==0 || md->name()!=self->name()))
2159  // name check is needed for overloaded members, where getDefs just returns one
2160  {
2161  /* in case of Fortran scop and the variable is a non Fortran variable: don't link,
2162  * see also getLink in fortrancode.l
2163  */
2164  if (!(scope && (scope->getLanguage() == SrcLangExt_Fortran) && md->isVariable() && (md->getLanguage() != SrcLangExt_Fortran)))
2165  {
2166  out.writeLink(md->getReference(),md->getOutputFileBase(),
2167  md->anchor(),word);
2168  //printf("found symbol %s\n",matchWord.data());
2169  found=TRUE;
2170  }
2171  }
2172  }
2173  }
2174 
2175  if (!found) // add word to the result
2176  {
2177  out.writeString(word,keepSpaces);
2178  }
2179  // set next start point in the string
2180  //printf("index=%d/%d\n",index,txtStr.length());
2181  skipIndex=index=newIndex+matchLen;
2182  }
2183  // add last part of the string to the result.
2184  //ol.docify(txtStr.right(txtStr.length()-skipIndex));
2185  out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
2186 }
2187 
2188 
2190 {
2191  QCString exampleLine=theTranslator->trWriteList(ed->count());
2192 
2193  //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
2194  //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
2195  //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
2196  QRegExp marker("@[0-9]+");
2197  int index=0,newIndex,matchLen;
2198  // now replace all markers in inheritLine with links to the classes
2199  while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
2200  {
2201  bool ok;
2202  ol.parseText(exampleLine.mid(index,newIndex-index));
2203  uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
2204  Example *e=ed->at(entryIndex);
2205  if (ok && e)
2206  {
2207  ol.pushGeneratorState();
2208  //if (latexEnabled) ol.disable(OutputGenerator::Latex);
2212  // link for Html / man
2213  //printf("writeObjectLink(file=%s)\n",e->file.data());
2214  ol.writeObjectLink(0,e->file,e->anchor,e->name);
2215  ol.popGeneratorState();
2216 
2217  ol.pushGeneratorState();
2218  //if (latexEnabled) ol.enable(OutputGenerator::Latex);
2221  // link for Latex / pdf with anchor because the sources
2222  // are not hyperlinked (not possible with a verbatim environment).
2223  ol.writeObjectLink(0,e->file,0,e->name);
2224  //if (manEnabled) ol.enable(OutputGenerator::Man);
2225  //if (htmlEnabled) ol.enable(OutputGenerator::Html);
2226  ol.popGeneratorState();
2227  }
2228  index=newIndex+matchLen;
2229  }
2230  ol.parseText(exampleLine.right(exampleLine.length()-index));
2231  ol.writeString(".");
2232 }
2233 
2234 
2235 QCString argListToString(const ArgumentList &al,bool useCanonicalType,bool showDefVals)
2236 {
2237  QCString result;
2238  if (!al.hasParameters()) return result;
2239  result+="(";
2240  for (auto it = al.begin() ; it!=al.end() ;)
2241  {
2242  Argument a = *it;
2243  QCString type1 = useCanonicalType && !a.canType.isEmpty() ? a.canType : a.type;
2244  QCString type2;
2245  int i=type1.find(")("); // hack to deal with function pointers
2246  if (i!=-1)
2247  {
2248  type2=type1.mid(i);
2249  type1=type1.left(i);
2250  }
2251  if (!a.attrib.isEmpty())
2252  {
2253  result+=a.attrib+" ";
2254  }
2255  if (!a.name.isEmpty() || !a.array.isEmpty())
2256  {
2257  result+= type1+" "+a.name+type2+a.array;
2258  }
2259  else
2260  {
2261  result+= type1+type2;
2262  }
2263  if (!a.defval.isEmpty() && showDefVals)
2264  {
2265  result+="="+a.defval;
2266  }
2267  ++it;
2268  if (it!=al.end()) result+=", ";
2269  }
2270  result+=")";
2271  if (al.constSpecifier) result+=" const";
2272  if (al.volatileSpecifier) result+=" volatile";
2273  if (al.refQualifier==RefQualifierLValue) result+=" &";
2274  else if (al.refQualifier==RefQualifierRValue) result+=" &&";
2275  if (!al.trailingReturnType.isEmpty()) result+=" -> "+al.trailingReturnType;
2276  if (al.pureSpecifier) result+=" =0";
2277  return removeRedundantWhiteSpace(result);
2278 }
2279 
2281 {
2282  QCString result;
2283  if (al.empty()) return result;
2284  result="<";
2285  auto it = al.begin();
2286  while (it!=al.end())
2287  {
2288  Argument a = *it;
2289  if (!a.name.isEmpty()) // add template argument name
2290  {
2291  if (a.type.left(4)=="out") // C# covariance
2292  {
2293  result+="out ";
2294  }
2295  else if (a.type.left(3)=="in") // C# contravariance
2296  {
2297  result+="in ";
2298  }
2299  if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2300  {
2301  result+=a.type+" ";
2302  }
2303  result+=a.name;
2304  }
2305  else // extract name from type
2306  {
2307  int i=a.type.length()-1;
2308  while (i>=0 && isId(a.type.at(i))) i--;
2309  if (i>0)
2310  {
2311  result+=a.type.right(a.type.length()-i-1);
2312  if (a.type.find("...")!=-1)
2313  {
2314  result+="...";
2315  }
2316  }
2317  else // nothing found -> take whole name
2318  {
2319  result+=a.type;
2320  }
2321  }
2322  if (!a.typeConstraint.isEmpty() && lang==SrcLangExt_Java)
2323  {
2324  result+=" extends "; // TODO: now Java specific, C# has where...
2325  result+=a.typeConstraint;
2326  }
2327  ++it;
2328  if (it!=al.end()) result+=", ";
2329  }
2330  result+=">";
2331  return removeRedundantWhiteSpace(result);
2332 }
2333 
2334 
2335 // compute the HTML anchors for a list of members
2337 {
2338  //int count=0;
2339  if (ml==0) return;
2340  MemberListIterator mli(*ml);
2341  MemberDef *md;
2342  for (;(md=mli.current());++mli)
2343  {
2344  if (!md->isReference())
2345  {
2346  //QCString anchor;
2347  //if (groupId==-1)
2348  // anchor.sprintf("%c%d",id,count++);
2349  //else
2350  // anchor.sprintf("%c%d_%d",id,groupId,count++);
2351  //if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
2352  md->setAnchor();
2353  //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
2354  // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
2355  }
2356  }
2357 }
2358 
2359 //----------------------------------------------------------------------------
2360 
2366 int filterCRLF(char *buf,int len)
2367 {
2368  int src = 0; // source index
2369  int dest = 0; // destination index
2370  char c; // current character
2371 
2372  while (src<len)
2373  {
2374  c = buf[src++]; // Remember the processed character.
2375  if (c == '\r') // CR to be solved (MAC, DOS)
2376  {
2377  c = '\n'; // each CR to LF
2378  if (src<len && buf[src] == '\n')
2379  ++src; // skip LF just after CR (DOS)
2380  }
2381  else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
2382  {
2383  c = ' '; // turn into a space
2384  }
2385  buf[dest++] = c; // copy the (modified) character to dest
2386  }
2387  return dest; // length of the valid part of the buf
2388 }
2389 
2390 static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
2391 {
2392  found=FALSE;
2393  // compare the file name to the filter pattern list
2394  QStrListIterator sli(filterList);
2395  char* filterStr;
2396  for (sli.toFirst(); (filterStr = sli.current()); ++sli)
2397  {
2398  QCString fs = filterStr;
2399  int i_equals=fs.find('=');
2400  if (i_equals!=-1)
2401  {
2402  QCString filterPattern = fs.left(i_equals);
2403  QRegExp fpat(filterPattern,Portable::fileSystemIsCaseSensitive(),TRUE);
2404  if (fpat.match(name)!=-1)
2405  {
2406  // found a match!
2407  QCString filterName = fs.mid(i_equals+1);
2408  if (filterName.find(' ')!=-1)
2409  { // add quotes if the name has spaces
2410  filterName="\""+filterName+"\"";
2411  }
2412  found=TRUE;
2413  return filterName;
2414  }
2415  }
2416  }
2417 
2418  // no match
2419  return "";
2420 }
2421 
2427 QCString getFileFilter(const char* name,bool isSourceCode)
2428 {
2429  // sanity check
2430  if (name==0) return "";
2431 
2432  QStrList& filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS);
2433  QStrList& filterList = Config_getList(FILTER_PATTERNS);
2434 
2435  QCString filterName;
2436  bool found=FALSE;
2437  if (isSourceCode && !filterSrcList.isEmpty())
2438  { // first look for source filter pattern list
2439  filterName = getFilterFromList(name,filterSrcList,found);
2440  }
2441  if (!found && filterName.isEmpty())
2442  { // then look for filter pattern list
2443  filterName = getFilterFromList(name,filterList,found);
2444  }
2445  if (!found)
2446  { // then use the generic input filter
2447  return Config_getString(INPUT_FILTER);
2448  }
2449  else
2450  {
2451  /* remove surrounding double quotes */
2452  if ((filterName.right(1) == "\"") && (filterName.left(1) == "\""))
2453  {
2454  filterName.remove(filterName.length() - 1, 1);
2455  filterName.remove(0, 1);
2456  }
2457  return filterName;
2458  }
2459 }
2460 
2461 
2463 {
2464  bool error=FALSE;
2465  static QCString inputEncoding = Config_getString(INPUT_ENCODING);
2466  const char *outputEncoding = "UTF-8";
2467  if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
2468  int inputSize=input.length();
2469  int outputSize=inputSize*4+1;
2470  QCString output(outputSize);
2471  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
2472  if (cd==(void *)(-1))
2473  {
2474  err("unsupported character conversion: '%s'->'%s'\n",
2475  inputEncoding.data(),outputEncoding);
2476  error=TRUE;
2477  }
2478  if (!error)
2479  {
2480  size_t iLeft=inputSize;
2481  size_t oLeft=outputSize;
2482  char *inputPtr = input.rawData();
2483  char *outputPtr = output.rawData();
2484  if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
2485  {
2486  outputSize-=(int)oLeft;
2487  output.resize(outputSize+1);
2488  output.at(outputSize)='\0';
2489  //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
2490  }
2491  else
2492  {
2493  err("failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
2494  inputEncoding.data(),outputEncoding,input.data());
2495  error=TRUE;
2496  }
2497  }
2499  return error ? input : output;
2500 }
2501 
2506 QCString fileToString(const char *name,bool filter,bool isSourceCode)
2507 {
2508  if (name==0 || name[0]==0) return 0;
2509  QFile f;
2510 
2511  bool fileOpened=FALSE;
2512  if (name[0]=='-' && name[1]==0) // read from stdin
2513  {
2514  fileOpened=f.open(IO_ReadOnly,stdin);
2515  if (fileOpened)
2516  {
2517  const int bSize=4096;
2518  QCString contents(bSize);
2519  int totalSize=0;
2520  int size;
2521  while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize)
2522  {
2523  totalSize+=bSize;
2524  contents.resize(totalSize+bSize);
2525  }
2526  totalSize = filterCRLF(contents.rawData(),totalSize+size)+2;
2527  contents.resize(totalSize);
2528  contents.at(totalSize-2)='\n'; // to help the scanner
2529  contents.at(totalSize-1)='\0';
2530  return contents;
2531  }
2532  }
2533  else // read from file
2534  {
2535  QFileInfo fi(name);
2536  if (!fi.exists() || !fi.isFile())
2537  {
2538  err("file '%s' not found\n",name);
2539  return "";
2540  }
2541  BufStr buf(fi.size());
2542  fileOpened=readInputFile(name,buf,filter,isSourceCode);
2543  if (fileOpened)
2544  {
2545  int s = buf.size();
2546  if (s>1 && buf.at(s-2)!='\n')
2547  {
2548  buf.at(s-1)='\n';
2549  buf.addChar(0);
2550  }
2551  return buf.data();
2552  }
2553  }
2554  if (!fileOpened)
2555  {
2556  err("cannot open file '%s' for reading\n",name);
2557  }
2558  return "";
2559 }
2560 
2562 {
2564  QCString sourceDateEpoch = Portable::getenv("SOURCE_DATE_EPOCH");
2565  if (!sourceDateEpoch.isEmpty())
2566  {
2567  bool ok;
2568  uint64 epoch = sourceDateEpoch.toUInt64(&ok);
2569  if (!ok)
2570  {
2571  static bool warnedOnce=FALSE;
2572  if (!warnedOnce)
2573  {
2574  warn_uncond("Environment variable SOURCE_DATE_EPOCH does not contain a valid number; value is '%s'\n",
2575  sourceDateEpoch.data());
2576  warnedOnce=TRUE;
2577  }
2578  }
2579  else if (epoch>UINT_MAX)
2580  {
2581  static bool warnedOnce=FALSE;
2582  if (!warnedOnce)
2583  {
2584  warn_uncond("Environment variable SOURCE_DATE_EPOCH must have a value smaller than or equal to %d; actual value %" PRIu64 "\n",UINT_MAX, (uint64_t)epoch);
2585  warnedOnce=TRUE;
2586  }
2587  }
2588  else // all ok, replace current time with epoch value
2589  {
2590  current.setTimeUtc_t((ulong)epoch); // TODO: add support for 64bit epoch value
2591  }
2592  }
2593  return current;
2594 }
2595 
2596 QCString dateToString(bool includeTime)
2597 {
2598  const QDateTime current = getCurrentDateTime();
2599  return theTranslator->trDateTime(current.date().year(),
2600  current.date().month(),
2601  current.date().day(),
2602  current.date().dayOfWeek(),
2603  current.time().hour(),
2604  current.time().minute(),
2605  current.time().second(),
2606  includeTime);
2607 }
2608 
2610 {
2611  const QDateTime current = getCurrentDateTime();
2612  QCString result;
2613  result.sprintf("%d", current.date().year());
2614  return result;
2615 }
2616 
2617 //----------------------------------------------------------------------
2618 // recursive function that returns the number of branches in the
2619 // inheritance tree that the base class 'bcd' is below the class 'cd'
2620 
2621 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
2622 {
2623  if (bcd->categoryOf()) // use class that is being extended in case of
2624  // an Objective-C category
2625  {
2626  bcd=bcd->categoryOf();
2627  }
2628  if (cd==bcd) return level;
2629  if (level==256)
2630  {
2631  warn_uncond("class %s seem to have a recursive "
2632  "inheritance relation!\n",cd->name().data());
2633  return -1;
2634  }
2635  int m=maxInheritanceDepth;
2636  if (cd->baseClasses())
2637  {
2638  BaseClassListIterator bcli(*cd->baseClasses());
2639  BaseClassDef *bcdi;
2640  for (;(bcdi=bcli.current());++bcli)
2641  {
2642  int mc=minClassDistance(bcdi->classDef,bcd,level+1);
2643  if (mc<m) m=mc;
2644  if (m<0) break;
2645  }
2646  }
2647  return m;
2648 }
2649 
2651 {
2652  if (bcd->categoryOf()) // use class that is being extended in case of
2653  // an Objective-C category
2654  {
2655  bcd=bcd->categoryOf();
2656  }
2657  if (cd==bcd)
2658  {
2659  goto exit;
2660  }
2661  if (level==256)
2662  {
2663  err("Internal inconsistency: found class %s seem to have a recursive "
2664  "inheritance relation! Please send a bug report to doxygen@gmail.com\n",cd->name().data());
2665  }
2666  else if (cd->baseClasses())
2667  {
2668  BaseClassListIterator bcli(*cd->baseClasses());
2669  const BaseClassDef *bcdi;
2670  for (;(bcdi=bcli.current()) && prot!=Private;++bcli)
2671  {
2672  Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1);
2673  if (baseProt==Private) prot=Private;
2674  else if (baseProt==Protected) prot=Protected;
2675  }
2676  }
2677 exit:
2678  //printf("classInheritedProtectionLevel(%s,%s)=%d\n",cd->name().data(),bcd->name().data(),prot);
2679  return prot;
2680 }
2681 
2682 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
2683 {
2684  //printf("trimBaseClassScope level=%d '%s'\n",level,s.data());
2685  BaseClassListIterator bcli(*bcl);
2686  BaseClassDef *bcd;
2687  for (;(bcd=bcli.current());++bcli)
2688  {
2689  ClassDef *cd=bcd->classDef;
2690  //printf("Trying class %s\n",cd->name().data());
2691  int spos=s.find(cd->name()+"::");
2692  if (spos!=-1)
2693  {
2694  s = s.left(spos)+s.right(
2695  s.length()-spos-cd->name().length()-2
2696  );
2697  }
2698  //printf("base class '%s'\n",cd->name().data());
2699  if (cd->baseClasses())
2700  trimBaseClassScope(cd->baseClasses(),s,level+1);
2701  }
2702 }
2703 
2704 #if 0
2705 
2709 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
2710 {
2711  int p1=t1.length();
2712  int p2=t2.length();
2713  for (;;)
2714  {
2715  int i1=p1==0 ? -1 : t1.findRev("::",p1);
2716  int i2=p2==0 ? -1 : t2.findRev("::",p2);
2717  if (i1==-1 && i2==-1)
2718  {
2719  return;
2720  }
2721  if (i1!=-1 && i2==-1) // only t1 has a scope
2722  {
2723  QCString scope=t1.left(i1);
2724  replaceNamespaceAliases(scope,i1);
2725 
2726  int so=nsName.length();
2727  do
2728  {
2729  QCString fullScope=nsName.left(so);
2730  if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2731  fullScope+=scope;
2732  if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2733  {
2734  t1 = t1.right(t1.length()-i1-2);
2735  return;
2736  }
2737  if (so==0)
2738  {
2739  so=-1;
2740  }
2741  else if ((so=nsName.findRev("::",so-1))==-1)
2742  {
2743  so=0;
2744  }
2745  }
2746  while (so>=0);
2747  }
2748  else if (i1==-1 && i2!=-1) // only t2 has a scope
2749  {
2750  QCString scope=t2.left(i2);
2751  replaceNamespaceAliases(scope,i2);
2752 
2753  int so=nsName.length();
2754  do
2755  {
2756  QCString fullScope=nsName.left(so);
2757  if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2758  fullScope+=scope;
2759  if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2760  {
2761  t2 = t2.right(t2.length()-i2-2);
2762  return;
2763  }
2764  if (so==0)
2765  {
2766  so=-1;
2767  }
2768  else if ((so=nsName.findRev("::",so-1))==-1)
2769  {
2770  so=0;
2771  }
2772  }
2773  while (so>=0);
2774  }
2775  p1 = QMAX(i1-2,0);
2776  p2 = QMAX(i2-2,0);
2777  }
2778 }
2779 #endif
2780 
2781 static void stripIrrelevantString(QCString &target,const QCString &str)
2782 {
2783  if (target==str) { target.resize(0); return; }
2784  int i,p=0;
2785  int l=str.length();
2786  bool changed=FALSE;
2787  while ((i=target.find(str,p))!=-1)
2788  {
2789  bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
2790  (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
2791  if (isMatch)
2792  {
2793  int i1=target.find('*',i+l);
2794  int i2=target.find('&',i+l);
2795  if (i1==-1 && i2==-1)
2796  {
2797  // strip str from target at index i
2798  target=target.left(i)+target.right(target.length()-i-l);
2799  changed=TRUE;
2800  i-=l;
2801  }
2802  else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
2803  {
2804  // move str to front
2805  target=str+" "+target.left(i)+target.right(target.length()-i-l);
2806  changed=TRUE;
2807  i++;
2808  }
2809  }
2810  p = i+l;
2811  }
2812  if (changed) target=target.stripWhiteSpace();
2813 }
2814 
2833 {
2834  //printf("stripIrrelevantConstVolatile(%s)=",s.data());
2835  stripIrrelevantString(s,"const");
2836  stripIrrelevantString(s,"volatile");
2837  stripIrrelevantString(s,"final");
2838  //printf("%s\n",s.data());
2839 }
2840 
2841 
2842 // a bit of debug support for matchArguments
2843 #define MATCH
2844 #define NOMATCH
2845 //#define MATCH printf("Match at line %d\n",__LINE__);
2846 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
2847 
2849 {
2850  int i=s.find(" class ");
2851  if (i!=-1) return s.left(i)+s.mid(i+6);
2852  i=s.find(" typename ");
2853  if (i!=-1) return s.left(i)+s.mid(i+9);
2854  i=s.find(" union ");
2855  if (i!=-1) return s.left(i)+s.mid(i+6);
2856  i=s.find(" struct ");
2857  if (i!=-1) return s.left(i)+s.mid(i+7);
2858  return s;
2859 }
2860 
2861 // forward decl for circular dependencies
2862 static QCString extractCanonicalType(const Definition *d,const FileDef *fs,QCString type);
2863 
2865 {
2866 
2867  QCString templSpec = spec.stripWhiteSpace();
2868  // this part had been commented out before... but it is needed to match for instance
2869  // std::list<std::string> against list<string> so it is now back again!
2870  if (!templSpec.isEmpty() && templSpec.at(0) == '<')
2871  {
2872  templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
2873  }
2874  QCString resolvedType = resolveTypeDef(d,templSpec);
2875  if (!resolvedType.isEmpty()) // not known as a typedef either
2876  {
2877  templSpec = resolvedType;
2878  }
2879  //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
2880  return templSpec;
2881 }
2882 
2883 
2885  const Definition *d,const FileDef *fs,const QCString &word,
2886  QCString *tSpec,int count=0)
2887 {
2888  if (count>10) return word; // oops recursion
2889 
2890  QCString symName,result,templSpec,tmpName;
2891  //DefinitionList *defList=0;
2892  if (tSpec && !tSpec->isEmpty())
2893  templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
2894 
2895  if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
2896  {
2897  symName=tmpName; // name without scope
2898  }
2899  else
2900  {
2901  symName=word;
2902  }
2903  //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
2904  // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
2905 
2906  const ClassDef *cd = 0;
2907  const MemberDef *mType = 0;
2908  QCString ts;
2909  QCString resolvedType;
2910 
2911  // lookup class / class template instance
2912  cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
2913  bool isTemplInst = cd && !templSpec.isEmpty();
2914  if (!cd && !templSpec.isEmpty())
2915  {
2916  // class template specialization not known, look up class template
2917  cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
2918  }
2919  if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
2920 
2921  //printf("cd=%p mtype=%p\n",cd,mType);
2922  //printf(" getCanonicalTypeForIdentifier: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
2923  // symName.data(),
2924  // word.data(),
2925  // cd?cd->name().data():"<none>",
2926  // d?d->name().data():"<none>",
2927  // fs?fs->name().data():"<none>",
2928  // cd?cd->isTemplate():-1
2929  // );
2930 
2931  //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
2932  // (word+templSpec).data(),
2933  // cd?cd->qualifiedName().data():"<none>",
2934  // templSpec.data(),ts.data(),
2935  // tSpec?tSpec->data():"<null>",
2936  // cd?cd->isTemplate():FALSE,
2937  // resolvedType.data());
2938 
2939  //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
2940 
2941  if (cd) // resolves to a known class type
2942  {
2943  if (cd==d && tSpec) *tSpec="";
2944 
2945  if (mType && mType->isTypedef()) // but via a typedef
2946  {
2947  result = resolvedType+ts; // the +ts was added for bug 685125
2948  }
2949  else
2950  {
2951  if (isTemplInst)
2952  {
2953  // spec is already part of class type
2954  templSpec="";
2955  if (tSpec) *tSpec="";
2956  }
2957  else if (!ts.isEmpty() && templSpec.isEmpty())
2958  {
2959  // use formal template args for spec
2960  templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
2961  }
2962 
2963  result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
2964 
2965  if (cd->isTemplate() && tSpec) //
2966  {
2967  if (!templSpec.isEmpty()) // specific instance
2968  {
2969  result=cd->name()+templSpec;
2970  }
2971  else // use template type
2972  {
2974  }
2975  // template class, so remove the template part (it is part of the class name)
2976  *tSpec="";
2977  }
2978  else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
2979  {
2980  // obscure case, where a class is used as a template, but doxygen think it is
2981  // not (could happen when loading the class from a tag file).
2982  *tSpec="";
2983  }
2984  }
2985  }
2986  else if (mType && mType->isEnumerate()) // an enum
2987  {
2988  result = mType->qualifiedName();
2989  }
2990  else if (mType && mType->isTypedef()) // a typedef
2991  {
2992  //result = mType->qualifiedName(); // changed after 1.7.2
2993  //result = mType->typeString();
2994  //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
2995  if (word!=mType->typeString())
2996  {
2997  result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
2998  }
2999  else
3000  {
3001  result = mType->typeString();
3002  }
3003  }
3004  else // fallback
3005  {
3006  resolvedType = resolveTypeDef(d,word);
3007  //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
3008  if (resolvedType.isEmpty()) // not known as a typedef either
3009  {
3010  result = word;
3011  }
3012  else
3013  {
3014  result = resolvedType;
3015  }
3016  }
3017  //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
3018  return result;
3019 }
3020 
3022 {
3023  type = type.stripWhiteSpace();
3024 
3025  // strip const and volatile keywords that are not relevant for the type
3027 
3028  // strip leading keywords
3029  type.stripPrefix("class ");
3030  type.stripPrefix("struct ");
3031  type.stripPrefix("union ");
3032  type.stripPrefix("enum ");
3033  type.stripPrefix("typename ");
3034 
3035  type = removeRedundantWhiteSpace(type);
3036  //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
3037  // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
3038 
3039  //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
3040 
3041  QCString canType;
3042  QCString templSpec,word;
3043  int i,p=0,pp=0;
3044  while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
3045  // foreach identifier in the type
3046  {
3047  //printf(" i=%d p=%d\n",i,p);
3048  if (i>pp) canType += type.mid(pp,i-pp);
3049 
3050 
3051  QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
3052 
3053  // in case the ct is empty it means that "word" represents scope "d"
3054  // and this does not need to be added to the canonical
3055  // type (it is redundant), so/ we skip it. This solves problem 589616.
3056  if (ct.isEmpty() && type.mid(p,2)=="::")
3057  {
3058  p+=2;
3059  }
3060  else
3061  {
3062  canType += ct;
3063  }
3064  //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
3065  // word.data(),templSpec.data(),canType.data(),ct.data());
3066  if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
3067  // (i.e. type is not a template specialization)
3068  // then resolve any identifiers inside.
3069  {
3070  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
3071  int tp=0,tl,ti;
3072  // for each identifier template specifier
3073  //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
3074  while ((ti=re.match(templSpec,tp,&tl))!=-1)
3075  {
3076  canType += templSpec.mid(tp,ti-tp);
3077  canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
3078  tp=ti+tl;
3079  }
3080  canType+=templSpec.right(templSpec.length()-tp);
3081  }
3082 
3083  pp=p;
3084  }
3085  canType += type.right(type.length()-pp);
3086  //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
3087 
3088  return removeRedundantWhiteSpace(canType);
3089 }
3090 
3091 static QCString extractCanonicalArgType(const Definition *d,const FileDef *fs,const Argument &arg)
3092 {
3093  QCString type = arg.type.stripWhiteSpace();
3094  QCString name = arg.name;
3095  //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
3096  if ((type=="const" || type=="volatile") && !name.isEmpty())
3097  { // name is part of type => correct
3098  type+=" ";
3099  type+=name;
3100  }
3101  if (name=="const" || name=="volatile")
3102  { // name is part of type => correct
3103  if (!type.isEmpty()) type+=" ";
3104  type+=name;
3105  }
3106  if (!arg.array.isEmpty())
3107  {
3108  type+=arg.array;
3109  }
3110 
3111  return extractCanonicalType(d,fs,type);
3112 }
3113 
3114 static bool matchArgument2(
3115  const Definition *srcScope,const FileDef *srcFileScope,Argument &srcA,
3116  const Definition *dstScope,const FileDef *dstFileScope,Argument &dstA
3117  )
3118 {
3119  //printf(">> match argument: %s::'%s|%s' (%s) <-> %s::'%s|%s' (%s)\n",
3120  // srcScope ? srcScope->name().data() : "",
3121  // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
3122  // dstScope ? dstScope->name().data() : "",
3123  // dstA->type.data(),dstA->name.data(),dstA->canType.data());
3124 
3125  //if (srcA->array!=dstA->array) // nomatch for char[] against char
3126  //{
3127  // NOMATCH
3128  // return FALSE;
3129  //}
3130  QCString sSrcName = " "+srcA.name;
3131  QCString sDstName = " "+dstA.name;
3132  QCString srcType = srcA.type;
3133  QCString dstType = dstA.type;
3136  //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
3137  //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
3138  if (sSrcName==dstType.right(sSrcName.length()))
3139  { // case "unsigned int" <-> "unsigned int i"
3140  srcA.type+=sSrcName;
3141  srcA.name="";
3142  srcA.canType=""; // invalidate cached type value
3143  }
3144  else if (sDstName==srcType.right(sDstName.length()))
3145  { // case "unsigned int i" <-> "unsigned int"
3146  dstA.type+=sDstName;
3147  dstA.name="";
3148  dstA.canType=""; // invalidate cached type value
3149  }
3150 
3151  if (srcA.canType.isEmpty())
3152  {
3153  srcA.canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
3154  }
3155  if (dstA.canType.isEmpty())
3156  {
3157  dstA.canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
3158  }
3159 
3160  if (srcA.canType==dstA.canType)
3161  {
3162  MATCH
3163  return TRUE;
3164  }
3165  else
3166  {
3167  //printf(" Canonical types do not match [%s]<->[%s]\n",
3168  // srcA->canType.data(),dstA->canType.data());
3169  NOMATCH
3170  return FALSE;
3171  }
3172 }
3173 
3174 
3175 // new algorithm for argument matching
3176 bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const ArgumentList &inSrcAl,
3177  const Definition *dstScope,const FileDef *dstFileScope,const ArgumentList &inDstAl,
3178  bool checkCV)
3179 {
3180  ASSERT(srcScope!=0 && dstScope!=0);
3181 
3182  ArgumentList srcAl = inSrcAl;
3183  ArgumentList dstAl = inDstAl;
3184 
3185  // handle special case with void argument
3186  if ( srcAl.empty() && dstAl.size()==1 && dstAl.front().type=="void" )
3187  { // special case for finding match between func() and func(void)
3188  Argument a;
3189  a.type = "void";
3190  srcAl.push_back(a);
3191  MATCH
3192  return TRUE;
3193  }
3194  if ( dstAl.empty() && srcAl.size()==1 && srcAl.front().type=="void" )
3195  { // special case for finding match between func(void) and func()
3196  Argument a;
3197  a.type = "void";
3198  dstAl.push_back(a);
3199  MATCH
3200  return TRUE;
3201  }
3202 
3203  if (srcAl.size() != dstAl.size())
3204  {
3205  NOMATCH
3206  return FALSE; // different number of arguments -> no match
3207  }
3208 
3209  if (checkCV)
3210  {
3211  if (srcAl.constSpecifier != dstAl.constSpecifier)
3212  {
3213  NOMATCH
3214  return FALSE; // one member is const, the other not -> no match
3215  }
3216  if (srcAl.volatileSpecifier != dstAl.volatileSpecifier)
3217  {
3218  NOMATCH
3219  return FALSE; // one member is volatile, the other not -> no match
3220  }
3221  }
3222 
3223  if (srcAl.refQualifier != dstAl.refQualifier)
3224  {
3225  NOMATCH
3226  return FALSE; // one member is has a different ref-qualifier than the other
3227  }
3228 
3229  // so far the argument list could match, so we need to compare the types of
3230  // all arguments.
3231  auto srcIt = srcAl.begin();
3232  auto dstIt = dstAl.begin();
3233  for (;srcIt!=srcAl.end() && dstIt!=dstAl.end();++srcIt,++dstIt)
3234  {
3235  Argument &srcA = *srcIt;
3236  Argument &dstA = *dstIt;
3237  if (!matchArgument2(srcScope,srcFileScope,srcA,
3238  dstScope,dstFileScope,dstA)
3239  )
3240  {
3241  NOMATCH
3242  return FALSE;
3243  }
3244  }
3245  MATCH
3246  return TRUE; // all arguments match
3247 }
3248 
3249 
3250 
3251 // merges the initializer of two argument lists
3252 // pre: the types of the arguments in the list should match.
3253 void mergeArguments(ArgumentList &srcAl,ArgumentList &dstAl,bool forceNameOverwrite)
3254 {
3255  //printf("mergeArguments '%s', '%s'\n",
3256  // argListToString(srcAl).data(),argListToString(dstAl).data());
3257 
3258  if (srcAl.size()!=dstAl.size())
3259  {
3260  return; // invalid argument lists -> do not merge
3261  }
3262 
3263  auto srcIt=srcAl.begin();
3264  auto dstIt=dstAl.begin();
3265  while (srcIt!=srcAl.end() && dstIt!=dstAl.end())
3266  {
3267  Argument &srcA = *srcIt;
3268  Argument &dstA = *dstIt;
3269 
3270  if (srcA.defval.isEmpty() && !dstA.defval.isEmpty())
3271  {
3272  //printf("Defval changing '%s'->'%s'\n",srcA.defval.data(),dstA.defval.data());
3273  srcA.defval=dstA.defval;
3274  }
3275  else if (!srcA.defval.isEmpty() && dstA.defval.isEmpty())
3276  {
3277  //printf("Defval changing '%s'->'%s'\n",dstA.defval.data(),srcA.defval.data());
3278  dstA.defval=srcA.defval;
3279  }
3280 
3281  // fix wrongly detected const or volatile specifiers before merging.
3282  // example: "const A *const" is detected as type="const A *" name="const"
3283  if (srcA.name=="const" || srcA.name=="volatile")
3284  {
3285  srcA.type+=" "+srcA.name;
3286  srcA.name.resize(0);
3287  }
3288  if (dstA.name=="const" || dstA.name=="volatile")
3289  {
3290  dstA.type+=" "+dstA.name;
3291  dstA.name.resize(0);
3292  }
3293 
3294  if (srcA.type==dstA.type)
3295  {
3296  //printf("1. merging %s:%s <-> %s:%s\n",srcA.type.data(),srcA.name.data(),dstA.type.data(),dstA.name.data());
3297  if (srcA.name.isEmpty() && !dstA.name.isEmpty())
3298  {
3299  //printf("type: '%s':='%s'\n",srcA.type.data(),dstA.type.data());
3300  //printf("name: '%s':='%s'\n",srcA.name.data(),dstA.name.data());
3301  srcA.type = dstA.type;
3302  srcA.name = dstA.name;
3303  }
3304  else if (!srcA.name.isEmpty() && dstA.name.isEmpty())
3305  {
3306  //printf("type: '%s':='%s'\n",dstA.type.data(),srcA.type.data());
3307  //printf("name: '%s':='%s'\n",dstA.name.data(),srcA.name.data());
3308  dstA.type = srcA.type;
3309  dstA.name = dstA.name;
3310  }
3311  else if (!srcA.name.isEmpty() && !dstA.name.isEmpty())
3312  {
3313  //printf("srcA.name=%s dstA.name=%s\n",srcA.name.data(),dstA.name.data());
3314  if (forceNameOverwrite)
3315  {
3316  srcA.name = dstA.name;
3317  }
3318  else
3319  {
3320  if (srcA.docs.isEmpty() && !dstA.docs.isEmpty())
3321  {
3322  srcA.name = dstA.name;
3323  }
3324  else if (!srcA.docs.isEmpty() && dstA.docs.isEmpty())
3325  {
3326  dstA.name = srcA.name;
3327  }
3328  }
3329  }
3330  }
3331  else
3332  {
3333  //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA.type.data(),srcA.name.data(),dstA.type.data(),dstA.name.data());
3334  srcA.type=srcA.type.stripWhiteSpace();
3335  dstA.type=dstA.type.stripWhiteSpace();
3336  if (srcA.type+" "+srcA.name==dstA.type) // "unsigned long:int" <-> "unsigned long int:bla"
3337  {
3338  srcA.type+=" "+srcA.name;
3339  srcA.name=dstA.name;
3340  }
3341  else if (dstA.type+" "+dstA.name==srcA.type) // "unsigned long int bla" <-> "unsigned long int"
3342  {
3343  dstA.type+=" "+dstA.name;
3344  dstA.name=srcA.name;
3345  }
3346  else if (srcA.name.isEmpty() && !dstA.name.isEmpty())
3347  {
3348  srcA.name = dstA.name;
3349  }
3350  else if (dstA.name.isEmpty() && !srcA.name.isEmpty())
3351  {
3352  dstA.name = srcA.name;
3353  }
3354  }
3355  int i1=srcA.type.find("::"),
3356  i2=dstA.type.find("::"),
3357  j1=srcA.type.length()-i1-2,
3358  j2=dstA.type.length()-i2-2;
3359  if (i1!=-1 && i2==-1 && srcA.type.right(j1)==dstA.type)
3360  {
3361  //printf("type: '%s':='%s'\n",dstA.type.data(),srcA.type.data());
3362  //printf("name: '%s':='%s'\n",dstA.name.data(),srcA.name.data());
3363  dstA.type = srcA.type.left(i1+2)+dstA.type;
3364  dstA.name = dstA.name;
3365  }
3366  else if (i1==-1 && i2!=-1 && dstA.type.right(j2)==srcA.type)
3367  {
3368  //printf("type: '%s':='%s'\n",srcA.type.data(),dstA.type.data());
3369  //printf("name: '%s':='%s'\n",dstA.name.data(),srcA.name.data());
3370  srcA.type = dstA.type.left(i2+2)+srcA.type;
3371  srcA.name = dstA.name;
3372  }
3373  if (srcA.docs.isEmpty() && !dstA.docs.isEmpty())
3374  {
3375  srcA.docs = dstA.docs;
3376  }
3377  else if (dstA.docs.isEmpty() && !srcA.docs.isEmpty())
3378  {
3379  dstA.docs = srcA.docs;
3380  }
3381  //printf("Merge argument '%s|%s' '%s|%s'\n",
3382  // srcA.type.data(),srcA.name.data(),
3383  // dstA.type.data(),dstA.name.data());
3384  ++srcIt;
3385  ++dstIt;
3386  }
3387 }
3388 
3390  const char *args,
3391  bool checkStatics,
3392  const FileDef *currentFile,
3393  bool checkCV,
3394  const char *forceTagFile,
3395  QList<MemberDef> &members)
3396 {
3397  //printf(" Function with global scope name '%s' args='%s'\n",
3398  // mn->memberName(),args);
3399  for (const auto &md : *mn)
3400  {
3401  const FileDef *fd=md->getFileDef();
3402  const GroupDef *gd=md->getGroupDef();
3403  //printf(" md->name()='%s' md->args='%s' fd=%p gd=%p current=%p ref=%s\n",
3404  // md->name().data(),args,fd,gd,currentFile,md->getReference().data());
3405  if (
3406  ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
3407  md->getNamespaceDef()==0 && md->isLinkable() &&
3408  (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
3409  currentFile==0 || fd==currentFile) // statics must appear in the same file
3410  )
3411  {
3412  bool match=TRUE;
3413  if (args && !md->isDefine() && qstrcmp(args,"()")!=0)
3414  {
3415  const ArgumentList &mdAl = md->argumentList();
3416  ArgumentList argList;
3417  stringToArgumentList(md->getLanguage(),args,argList);
3418  match=matchArguments2(
3419  md->getOuterScope(),fd,mdAl,
3420  Doxygen::globalScope,fd,argList,
3421  checkCV);
3422  }
3423  if (match && (forceTagFile==0 || md->getReference()==forceTagFile))
3424  {
3425  //printf("Found match!\n");
3426  members.append(md.get());
3427  }
3428  }
3429  }
3430 }
3431 
3454 bool getDefs(const QCString &scName,
3455  const QCString &mbName,
3456  const char *args,
3457  const MemberDef *&md,
3458  const ClassDef *&cd,
3459  const FileDef *&fd,
3460  const NamespaceDef *&nd,
3461  const GroupDef *&gd,
3462  bool forceEmptyScope,
3463  const FileDef *currentFile,
3464  bool checkCV,
3465  const char *forceTagFile
3466  )
3467 {
3468  fd=0, md=0, cd=0, nd=0, gd=0;
3469  if (mbName.isEmpty()) return FALSE; /* empty name => nothing to link */
3470 
3471  QCString scopeName=scName;
3472  QCString memberName=mbName;
3473  scopeName = substitute(scopeName,"\\","::"); // for PHP
3474  memberName = substitute(memberName,"\\","::"); // for PHP
3475  //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
3476  // memberName.data(),args,scopeName.data(),forceEmptyScope);
3477 
3478  int is,im=0,pm=0;
3479  // strip common part of the scope from the scopeName
3480  while ((is=scopeName.findRev("::"))!=-1 &&
3481  (im=memberName.find("::",pm))!=-1 &&
3482  (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
3483  )
3484  {
3485  scopeName=scopeName.left(is);
3486  pm=im+2;
3487  }
3488  //printf("result after scope corrections scope=%s name=%s\n",
3489  // scopeName.data(),memberName.data());
3490 
3491  QCString mName=memberName;
3492  QCString mScope;
3493  if (memberName.left(9)!="operator " && // treat operator conversion methods
3494  // as a special case
3495  (im=memberName.findRev("::"))!=-1 &&
3496  im<(int)memberName.length()-2 // not A::
3497  )
3498  {
3499  mScope=memberName.left(im);
3500  mName=memberName.right(memberName.length()-im-2);
3501  }
3502 
3503  // handle special the case where both scope name and member scope are equal
3504  if (mScope==scopeName) scopeName.resize(0);
3505 
3506  //printf("mScope='%s' mName='%s'\n",mScope.data(),mName.data());
3507 
3509  //printf("mName=%s mn=%p\n",mName.data(),mn);
3510 
3511  if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
3512  mn && !(scopeName.isEmpty() && mScope.isEmpty()))
3513  {
3514  //printf(" >member name '%s' found\n",mName.data());
3515  int scopeOffset=scopeName.length();
3516  do
3517  {
3518  QCString className = scopeName.left(scopeOffset);
3519  if (!className.isEmpty() && !mScope.isEmpty())
3520  {
3521  className+="::"+mScope;
3522  }
3523  else if (!mScope.isEmpty())
3524  {
3525  className=mScope;
3526  }
3527 
3528  const MemberDef *tmd=0;
3529  const ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className,&tmd);
3530  if (fcd==0 && className.find('<')!=-1) // try without template specifiers as well
3531  {
3532  QCString nameWithoutTemplates = stripTemplateSpecifiersFromScope(className,FALSE);
3533  fcd=getResolvedClass(Doxygen::globalScope,0,nameWithoutTemplates,&tmd);
3534  }
3535  //printf("Trying class scope %s: fcd=%p tmd=%p\n",className.data(),fcd,tmd);
3536  // todo: fill in correct fileScope!
3537  if (fcd && // is it a documented class
3538  fcd->isLinkable()
3539  )
3540  {
3541  //printf(" Found fcd=%p\n",fcd);
3542  int mdist=maxInheritanceDepth;
3543  ArgumentList argList;
3544  if (args)
3545  {
3546  stringToArgumentList(fcd->getLanguage(),args,argList);
3547  }
3548  for (const auto &mmd : *mn)
3549  {
3550  if (!mmd->isStrongEnumValue())
3551  {
3552  const ArgumentList &mmdAl = mmd->argumentList();
3553  bool match=args==0 ||
3554  matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
3555  fcd, fcd->getFileDef(),argList,
3556  checkCV);
3557  //printf("match=%d\n",match);
3558  if (match)
3559  {
3560  ClassDef *mcd=mmd->getClassDef();
3561  if (mcd)
3562  {
3563  int m=minClassDistance(fcd,mcd);
3564  if (m<mdist && mcd->isLinkable())
3565  {
3566  mdist=m;
3567  cd=mcd;
3568  md=mmd.get();
3569  }
3570  }
3571  }
3572  }
3573  }
3574  if (mdist==maxInheritanceDepth && args && qstrcmp(args,"()")==0)
3575  // no exact match found, but if args="()" an arbitrary member will do
3576  {
3577  //printf(" >Searching for arbitrary member\n");
3578  for (const auto &mmd : *mn)
3579  {
3580  //if (mmd->isLinkable())
3581  //{
3582  ClassDef *mcd=mmd->getClassDef();
3583  //printf(" >Class %s found\n",mcd->name().data());
3584  if (mcd)
3585  {
3586  int m=minClassDistance(fcd,mcd);
3587  if (m<mdist /* && mcd->isLinkable()*/ )
3588  {
3589  //printf("Class distance %d\n",m);
3590  mdist=m;
3591  cd=mcd;
3592  md=mmd.get();
3593  }
3594  }
3595  //}
3596  }
3597  }
3598  //printf(" >Success=%d\n",mdist<maxInheritanceDepth);
3599  if (mdist<maxInheritanceDepth)
3600  {
3601  if (!md->isLinkable() || md->isStrongEnumValue())
3602  {
3603  md=0; // avoid returning things we cannot link to
3604  cd=0;
3605  return FALSE; // match found, but was not linkable
3606  }
3607  else
3608  {
3609  gd=md->getGroupDef();
3610  if (gd) cd=0;
3611  return TRUE; /* found match */
3612  }
3613  }
3614  }
3615  if (tmd && tmd->isEnumerate() && tmd->isStrong()) // scoped enum
3616  {
3617  //printf("Found scoped enum!\n");
3618  const MemberList *tml = tmd->enumFieldList();
3619  if (tml)
3620  {
3621  MemberListIterator tmi(*tml);
3622  MemberDef *emd;
3623  for (;(emd=tmi.current());++tmi)
3624  {
3625  if (emd->localName()==mName)
3626  {
3627  if (emd->isLinkable())
3628  {
3629  cd=tmd->getClassDef();
3630  md=emd;
3631  return TRUE;
3632  }
3633  else
3634  {
3635  cd=0;
3636  md=0;
3637  return FALSE;
3638  }
3639  }
3640  }
3641  }
3642  }
3643  /* go to the parent scope */
3644  if (scopeOffset==0)
3645  {
3646  scopeOffset=-1;
3647  }
3648  else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
3649  {
3650  scopeOffset=0;
3651  }
3652  } while (scopeOffset>=0);
3653 
3654  }
3655  if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
3656  {
3657  //printf("Global symbol\n");
3658  MemberDef *fuzzy_mmd = 0;
3659  ArgumentList argList;
3660  bool hasEmptyArgs = args && qstrcmp(args, "()") == 0;
3661 
3662  if (args)
3663  {
3664  stringToArgumentList(SrcLangExt_Cpp, args, argList);
3665  }
3666 
3667  for (const auto &mmd : *mn)
3668  {
3669  if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
3670  !mmd->getClassDef())
3671  {
3672  continue;
3673  }
3674 
3675  if (!args)
3676  {
3677  fuzzy_mmd = mmd.get();
3678  break;
3679  }
3680 
3681  ArgumentList &mmdAl = mmd->argumentList();
3682  if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
3683  Doxygen::globalScope,mmd->getFileDef(),argList,
3684  checkCV
3685  )
3686  )
3687  {
3688  fuzzy_mmd = mmd.get();
3689  break;
3690  }
3691 
3692  if (!fuzzy_mmd && hasEmptyArgs)
3693  {
3694  fuzzy_mmd = mmd.get();
3695  }
3696  }
3697 
3698  if (fuzzy_mmd && !fuzzy_mmd->isStrongEnumValue())
3699  {
3700  md = fuzzy_mmd;
3701  cd = fuzzy_mmd->getClassDef();
3702  return TRUE;
3703  }
3704  }
3705 
3706 
3707  // maybe an namespace, file or group member ?
3708  //printf("Testing for global symbol scopeName='%s' mScope='%s' :: mName='%s'\n",
3709  // scopeName.data(),mScope.data(),mName.data());
3710  if ((mn=Doxygen::functionNameLinkedMap->find(mName))) // name is known
3711  {
3712  //printf(" >symbol name found\n");
3713  NamespaceDef *fnd=0;
3714  int scopeOffset=scopeName.length();
3715  do
3716  {
3717  QCString namespaceName = scopeName.left(scopeOffset);
3718  if (!namespaceName.isEmpty() && !mScope.isEmpty())
3719  {
3720  namespaceName+="::"+mScope;
3721  }
3722  else if (!mScope.isEmpty())
3723  {
3724  namespaceName=mScope.copy();
3725  }
3726  //printf("Trying namespace %s\n",namespaceName.data());
3727  if (!namespaceName.isEmpty() &&
3728  (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
3729  fnd->isLinkable()
3730  )
3731  {
3732  //printf("Symbol inside existing namespace '%s' count=%d\n",
3733  // namespaceName.data(),mn->count());
3734  bool found=FALSE;
3735  for (const auto &mmd : *mn)
3736  {
3737  //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
3738  // mmd->getNamespaceDef(),fnd);
3739  const MemberDef *emd = mmd->getEnumScope();
3740  if (emd && emd->isStrong())
3741  {
3742  //printf("yes match %s<->%s!\n",mScope.data(),emd->localName().data());
3743  if (emd->getNamespaceDef()==fnd &&
3744  rightScopeMatch(mScope,emd->localName()))
3745  {
3746  //printf("found it!\n");
3747  nd=fnd;
3748  md=mmd.get();
3749  found=TRUE;
3750  break;
3751  }
3752  else
3753  {
3754  md=0;
3755  cd=0;
3756  return FALSE;
3757  }
3758  }
3759  else if (mmd->getOuterScope()==fnd /* && mmd->isLinkable() */ )
3760  { // namespace is found
3761  bool match=TRUE;
3762  ArgumentList argList;
3763  if (args && qstrcmp(args,"()")!=0)
3764  {
3765  const ArgumentList &mmdAl = mmd->argumentList();
3766  stringToArgumentList(mmd->getLanguage(),args,argList);
3767  match=matchArguments2(
3768  mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
3769  fnd,mmd->getFileDef(),argList,
3770  checkCV);
3771  }
3772  if (match)
3773  {
3774  nd=fnd;
3775  md=mmd.get();
3776  found=TRUE;
3777  break;
3778  }
3779  }
3780  }
3781  if (!found && args && !qstrcmp(args,"()"))
3782  // no exact match found, but if args="()" an arbitrary
3783  // member will do
3784  {
3785  for (const auto &mmd : *mn)
3786  {
3787  if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
3788  {
3789  nd=fnd;
3790  md=mmd.get();
3791  found=TRUE;
3792  break;
3793  }
3794  }
3795  }
3796  if (found)
3797  {
3798  if (!md->isLinkable())
3799  {
3800  md=0; // avoid returning things we cannot link to
3801  nd=0;
3802  return FALSE; // match found but not linkable
3803  }
3804  else
3805  {
3806  gd=md->resolveAlias()->getGroupDef();
3807  if (gd && gd->isLinkable()) nd=0; else gd=0;
3808  return TRUE;
3809  }
3810  }
3811  }
3812  else
3813  {
3814  //printf("not a namespace\n");
3815  for (const auto &mmd : *mn)
3816  {
3817  const MemberDef *tmd = mmd->getEnumScope();
3818  //printf("try member %s tmd=%s\n",mmd->name().data(),tmd?tmd->name().data():"<none>");
3819  int ni=namespaceName.findRev("::");
3820  //printf("namespaceName=%s ni=%d\n",namespaceName.data(),ni);
3821  bool notInNS = tmd && ni==-1 && tmd->getNamespaceDef()==0 && (mScope.isEmpty() || mScope==tmd->name());
3822  bool sameNS = tmd && tmd->getNamespaceDef() && namespaceName.left(ni)==tmd->getNamespaceDef()->name() && namespaceName.mid(ni+2)==tmd->name();
3823  //printf("notInNS=%d sameNS=%d\n",notInNS,sameNS);
3824  if (tmd && tmd->isStrong() && // C++11 enum class
3825  (notInNS || sameNS) &&
3826  namespaceName.length()>0 // enum is part of namespace so this should not be empty
3827  )
3828  {
3829  md=mmd.get();
3830  fd=mmd->getFileDef();
3831  gd=mmd->getGroupDef();
3832  if (gd && gd->isLinkable()) fd=0; else gd=0;
3833  //printf("Found scoped enum %s fd=%p gd=%p\n",
3834  // mmd->name().data(),fd,gd);
3835  return TRUE;
3836  }
3837  }
3838  }
3839  if (scopeOffset==0)
3840  {
3841  scopeOffset=-1;
3842  }
3843  else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
3844  {
3845  scopeOffset=0;
3846  }
3847  } while (scopeOffset>=0);
3848 
3849  //else // no scope => global function
3850  {
3851  QList<MemberDef> members;
3852  // search for matches with strict static checking
3853  findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members);
3854  if (members.count()==0) // nothing found
3855  {
3856  // search again without strict static checking
3857  findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members);
3858  }
3859  //printf("found %d members\n",members.count());
3860  if (members.count()!=1 && args && !qstrcmp(args,"()"))
3861  {
3862  // no exact match found, but if args="()" an arbitrary
3863  // member will do
3864  //MemberNameIterator mni(*mn);
3865  //for (mni.toLast();(md=mni.current());--mni)
3866  for (auto it = mn->rbegin(); it!=mn->rend(); ++it)
3867  {
3868  const auto &mmd = *it;
3869  //printf("Found member '%s'\n",mmd->name().data());
3870  //printf("member is linkable mmd->name()='%s'\n",mmd->name().data());
3871  fd=mmd->getFileDef();
3872  gd=mmd->getGroupDef();
3873  const MemberDef *tmd = mmd->getEnumScope();
3874  if (
3875  (gd && gd->isLinkable()) || (fd && fd->isLinkable()) ||
3876  (tmd && tmd->isStrong())
3877  )
3878  {
3879  members.append(mmd.get());
3880  }
3881  }
3882  }
3883  //printf("found %d candidate members\n",members.count());
3884  if (members.count()>0) // at least one match
3885  {
3886  if (currentFile)
3887  {
3888  //printf("multiple results; pick one from file:%s\n", currentFile->name().data());
3889  QListIterator<MemberDef> mit(members);
3890  for (mit.toFirst();(md=mit.current());++mit)
3891  {
3892  if (md->getFileDef() && md->getFileDef()->name() == currentFile->name())
3893  {
3894  break; // found match in the current file
3895  }
3896  }
3897  if (!md) // member not in the current file
3898  {
3899  md=members.getLast();
3900  }
3901  }
3902  else
3903  {
3904  md=members.getLast();
3905  }
3906  }
3907  if (md && (md->getEnumScope()==0 || !md->getEnumScope()->isStrong()))
3908  // found a matching global member, that is not a scoped enum value (or uniquely matches)
3909  {
3910  fd=md->getFileDef();
3911  gd=md->getGroupDef();
3912  //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
3913  if (gd && gd->isLinkable()) fd=0; else gd=0;
3914  return TRUE;
3915  }
3916  }
3917  }
3918 
3919  // no nothing found
3920  return FALSE;
3921 }
3922 
3937 static bool getScopeDefs(const char *docScope,const char *scope,
3938  ClassDef *&cd, NamespaceDef *&nd)
3939 {
3940  cd=0;nd=0;
3941 
3942  QCString scopeName=scope;
3943  //printf("getScopeDefs: docScope='%s' scope='%s'\n",docScope,scope);
3944  if (scopeName.isEmpty()) return FALSE;
3945 
3946  bool explicitGlobalScope=FALSE;
3947  if (scopeName.at(0)==':' && scopeName.at(1)==':')
3948  {
3949  scopeName=scopeName.right(scopeName.length()-2);
3950  explicitGlobalScope=TRUE;
3951  }
3952  if (scopeName.isEmpty())
3953  {
3954  return FALSE;
3955  }
3956 
3957  QCString docScopeName=docScope;
3958  int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
3959 
3960  do // for each possible docScope (from largest to and including empty)
3961  {
3962  QCString fullName=scopeName.copy();
3963  if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
3964 
3965  if (((cd=getClass(fullName)) || // normal class
3966  (cd=getClass(fullName+"-p")) //|| // ObjC protocol
3967  //(cd=getClass(fullName+"-g")) // C# generic
3968  ) && cd->isLinkable())
3969  {
3970  return TRUE; // class link written => quit
3971  }
3972  else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
3973  {
3974  return TRUE; // namespace link written => quit
3975  }
3976  if (scopeOffset==0)
3977  {
3978  scopeOffset=-1;
3979  }
3980  else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
3981  {
3982  scopeOffset=0;
3983  }
3984  } while (scopeOffset>=0);
3985 
3986  return FALSE;
3987 }
3988 
3989 static bool isLowerCase(QCString &s)
3990 {
3991  uchar *p=(uchar*)s.data();
3992  if (p==0) return TRUE;
3993  int c;
3994  while ((c=*p++)) if (!islower(c)) return FALSE;
3995  return TRUE;
3996 }
3997 
4001 bool resolveRef(/* in */ const char *scName,
4002  /* in */ const char *name,
4003  /* in */ bool inSeeBlock,
4004  /* out */ const Definition **resContext,
4005  /* out */ const MemberDef **resMember,
4006  bool lookForSpecialization,
4007  const FileDef *currentFile,
4008  bool checkScope
4009  )
4010 {
4011  //printf("resolveRef(scope=%s,name=%s,inSeeBlock=%d)\n",scName,name,inSeeBlock);
4012  QCString tsName = name;
4013  //bool memberScopeFirst = tsName.find('#')!=-1;
4014  QCString fullName = substitute(tsName,"#","::");
4015  if (fullName.find("anonymous_namespace{")==-1)
4016  {
4017  fullName = removeRedundantWhiteSpace(substitute(fullName,".","::",3));
4018  }
4019  else
4020  {
4021  fullName = removeRedundantWhiteSpace(fullName);
4022  }
4023 
4024  int bracePos=findParameterList(fullName);
4025  int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
4026  int scopePos=fullName.findRev("::",endNamePos);
4027  bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
4028  (scopePos>2 || // ::N::A
4029  tsName.left(2)=="::" || // ::foo in local scope
4030  scName==0 // #foo in global scope
4031  );
4032 
4033  // default result values
4034  *resContext=0;
4035  *resMember=0;
4036 
4037  if (bracePos==-1) // simple name
4038  {
4039  ClassDef *cd=0;
4040  NamespaceDef *nd=0;
4041 
4042  // the following if() was commented out for releases in the range
4043  // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
4044  if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
4045  { // link to lower case only name => do not try to autolink
4046  return FALSE;
4047  }
4048 
4049  //printf("scName=%s fullName=%s\n",scName,fullName.data());
4050 
4051  // check if this is a class or namespace reference
4052  if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
4053  {
4054  if (cd) // scope matches that of a class
4055  {
4056  *resContext = cd;
4057  }
4058  else // scope matches that of a namespace
4059  {
4060  ASSERT(nd!=0);
4061  *resContext = nd;
4062  }
4063  return TRUE;
4064  }
4065  else if (scName==fullName || (!inSeeBlock && scopePos==-1))
4066  // nothing to link => output plain text
4067  {
4068  //printf("found scName=%s fullName=%s scName==fullName=%d "
4069  // "inSeeBlock=%d scopePos=%d!\n",
4070  // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
4071  return FALSE;
4072  }
4073  // continue search...
4074  }
4075 
4076  // extract userscope+name
4077  QCString nameStr=fullName.left(endNamePos);
4078  if (explicitScope) nameStr=nameStr.mid(2);
4079 
4080  // extract arguments
4081  QCString argsStr;
4082  if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
4083 
4084  // strip template specifier
4085  // TODO: match against the correct partial template instantiation
4086  int templPos=nameStr.find('<');
4087  bool tryUnspecializedVersion = FALSE;
4088  if (templPos!=-1 && nameStr.find("operator")==-1)
4089  {
4090  int endTemplPos=nameStr.findRev('>');
4091  if (endTemplPos!=-1)
4092  {
4093  if (!lookForSpecialization)
4094  {
4095  nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
4096  }
4097  else
4098  {
4099  tryUnspecializedVersion = TRUE;
4100  }
4101  }
4102  }
4103 
4104  QCString scopeStr=scName;
4105 
4106  const MemberDef *md = 0;
4107  const ClassDef *cd = 0;
4108  const FileDef *fd = 0;
4109  const NamespaceDef *nd = 0;
4110  const GroupDef *gd = 0;
4111 
4112  // check if nameStr is a member or global.
4113  //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
4114  // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
4115  if (getDefs(scopeStr,nameStr,argsStr,
4116  md,cd,fd,nd,gd,
4117  //scopePos==0 && !memberScopeFirst, // forceEmptyScope
4118  explicitScope, // replaces prev line due to bug 600829
4119  currentFile,
4120  TRUE // checkCV
4121  )
4122  )
4123  {
4124  //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
4125  if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
4126  !md->isStrongEnumValue() &&
4127  (!scopeStr.isEmpty() || nameStr.find("::")>0))
4128  {
4129  // we did find a member, but it is a global one while we were explicitly
4130  // looking for a scoped variable. See bug 616387 for an example why this check is needed.
4131  // note we do need to support autolinking to "::symbol" hence the >0
4132  //printf("not global member!\n");
4133  *resContext=0;
4134  *resMember=0;
4135  return FALSE;
4136  }
4137  //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
4138  if (md) { *resMember=md; *resContext=md; }
4139  else if (cd) *resContext=cd;
4140  else if (nd) *resContext=nd;
4141  else if (fd) *resContext=fd;
4142  else if (gd) *resContext=gd;
4143  else { *resContext=0; *resMember=0; return FALSE; }
4144  //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
4145  // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
4146  return TRUE;
4147  }
4148  else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
4149  { // group link
4150  *resContext=gd;
4151  return TRUE;
4152  }
4153  else if (tsName.find('.')!=-1) // maybe a link to a file
4154  {
4155  bool ambig;
4156  fd=findFileDef(Doxygen::inputNameLinkedMap,tsName,ambig);
4157  if (fd && !ambig)
4158  {
4159  *resContext=fd;
4160  return TRUE;
4161  }
4162  }
4163 
4164  if (tryUnspecializedVersion)
4165  {
4166  return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
4167  }
4168  if (bracePos!=-1) // Try without parameters as well, could be a constructor invocation
4169  {
4170  *resContext=getClass(fullName.left(bracePos));
4171  if (*resContext)
4172  {
4173  return TRUE;
4174  }
4175  }
4176  //printf("resolveRef: %s not found!\n",name);
4177 
4178  return FALSE;
4179 }
4180 
4181 QCString linkToText(SrcLangExt lang,const char *link,bool isFileName)
4182 {
4183  //static bool optimizeOutputJava = Config_getBool(OPTIMIZE_OUTPUT_JAVA);
4184  QCString result=link;
4185  if (!result.isEmpty())
4186  {
4187  // replace # by ::
4188  result=substitute(result,"#","::");
4189  // replace . by ::
4190  if (!isFileName && result.find('<')==-1) result=substitute(result,".","::",3);
4191  // strip leading :: prefix if present
4192  if (result.at(0)==':' && result.at(1)==':')
4193  {
4194  result=result.right(result.length()-2);
4195  }
4197  if (sep!="::")
4198  {
4199  result=substitute(result,"::",sep);
4200  }
4201  }
4202  return result;
4203 }
4204 
4205 #if 0
4206 /*
4207  * generate a reference to a class, namespace or member.
4208  * 'scName' is the name of the scope that contains the documentation
4209  * string that is returned.
4210  * 'name' is the name that we want to link to.
4211  * 'name' may have the following formats:
4212  * 1) "ScopeName"
4213  * 2) "memberName()" one of the (overloaded) function or define
4214  * with name memberName.
4215  * 3) "memberName(...)" a specific (overloaded) function or define
4216  * with name memberName
4217  * 4) "::name a global variable or define
4218  * 4) "\#memberName member variable, global variable or define
4219  * 5) ("ScopeName::")+"memberName()"
4220  * 6) ("ScopeName::")+"memberName(...)"
4221  * 7) ("ScopeName::")+"memberName"
4222  * instead of :: the \# symbol may also be used.
4223  */
4224 
4225 bool generateRef(OutputDocInterface &od,const char *scName,
4226  const char *name,bool inSeeBlock,const char *rt)
4227 {
4228  //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
4229 
4230  Definition *compound;
4231  MemberDef *md;
4232 
4233  // create default link text
4234  QCString linkText = linkToText(rt,FALSE);
4235 
4236  if (resolveRef(scName,name,inSeeBlock,&compound,&md))
4237  {
4238  if (md && md->isLinkable()) // link to member
4239  {
4240  od.writeObjectLink(md->getReference(),
4241  md->getOutputFileBase(),
4242  md->anchor(),linkText);
4243  // generate the page reference (for LaTeX)
4244  if (!md->isReference())
4245  {
4246  writePageRef(od,md->getOutputFileBase(),md->anchor());
4247  }
4248  return TRUE;
4249  }
4250  else if (compound && compound->isLinkable()) // link to compound
4251  {
4252  if (rt==0 && compound->definitionType()==Definition::TypeGroup)
4253  {
4254  linkText=((GroupDef *)compound)->groupTitle();
4255  }
4256  if (compound && compound->definitionType()==Definition::TypeFile)
4257  {
4258  linkText=linkToText(rt,TRUE);
4259  }
4260  od.writeObjectLink(compound->getReference(),
4261  compound->getOutputFileBase(),
4262  0,linkText);
4263  if (!compound->isReference())
4264  {
4265  writePageRef(od,compound->getOutputFileBase(),0);
4266  }
4267  return TRUE;
4268  }
4269  }
4270  od.docify(linkText);
4271  return FALSE;
4272 }
4273 #endif
4274 
4275 bool resolveLink(/* in */ const char *scName,
4276  /* in */ const char *lr,
4277  /* in */ bool /*inSeeBlock*/,
4278  /* out */ const Definition **resContext,
4279  /* out */ QCString &resAnchor
4280  )
4281 {
4282  *resContext=0;
4283 
4284  QCString linkRef=lr;
4285  QCString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,FALSE);
4286  //printf("ResolveLink linkRef=%s\n",lr);
4287  const FileDef *fd;
4288  const GroupDef *gd;
4289  const PageDef *pd;
4290  const ClassDef *cd;
4291  const DirDef *dir;
4292  const NamespaceDef *nd;
4293  const SectionInfo *si=0;
4294  bool ambig;
4295  if (linkRef.isEmpty()) // no reference name!
4296  {
4297  return FALSE;
4298  }
4299  else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
4300  {
4301  gd = pd->getGroupDef();
4302  if (gd)
4303  {
4304  if (!pd->name().isEmpty()) si=SectionManager::instance().find(pd->name());
4305  *resContext=gd;
4306  if (si) resAnchor = si->label();
4307  }
4308  else
4309  {
4310  *resContext=pd;
4311  }
4312  return TRUE;
4313  }
4314  else if ((si=SectionManager::instance().find(linkRef)))
4315  {
4316  *resContext=si->definition();
4317  resAnchor = si->label();
4318  return TRUE;
4319  }
4320  else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
4321  {
4322  *resContext=pd;
4323  return TRUE;
4324  }
4325  else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
4326  {
4327  *resContext=gd;
4328  return TRUE;
4329  }
4330  else if ((fd=findFileDef(Doxygen::inputNameLinkedMap,linkRef,ambig)) // file link
4331  && fd->isLinkable())
4332  {
4333  *resContext=fd;
4334  return TRUE;
4335  }
4336  else if ((cd=getClass(linkRef))) // class link
4337  {
4338  *resContext=cd;
4339  resAnchor=cd->anchor();
4340  return TRUE;
4341  }
4342  else if ((cd=getClass(linkRefWithoutTemplates))) // C#/Java generic class link
4343  {
4344  *resContext=cd;
4345  resAnchor=cd->anchor();
4346  return TRUE;
4347  }
4348  else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
4349  {
4350  *resContext=cd;
4351  resAnchor=cd->anchor();
4352  return TRUE;
4353  }
4354 // else if ((cd=getClass(linkRef+"-g"))) // C# generic link
4355 // {
4356 // *resContext=cd;
4357 // resAnchor=cd->anchor();
4358 // return TRUE;
4359 // }
4360  else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
4361  {
4362  *resContext=nd;
4363  return TRUE;
4364  }
4365  else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/"))
4366  && dir->isLinkable()) // TODO: make this location independent like filedefs
4367  {
4368  *resContext=dir;
4369  return TRUE;
4370  }
4371  else // probably a member reference
4372  {
4373  const MemberDef *md = 0;
4374  bool res = resolveRef(scName,lr,TRUE,resContext,&md);
4375  if (md) resAnchor=md->anchor();
4376  return res;
4377  }
4378 }
4379 
4380 
4381 //----------------------------------------------------------------------
4382 // General function that generates the HTML code for a reference to some
4383 // file, class or member from text 'lr' within the context of class 'clName'.
4384 // This link has the text 'lt' (if not 0), otherwise 'lr' is used as a
4385 // basis for the link's text.
4386 // returns TRUE if a link could be generated.
4387 
4388 bool generateLink(OutputDocInterface &od,const char *clName,
4389  const char *lr,bool inSeeBlock,const char *lt)
4390 {
4391  //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
4392  const Definition *compound = 0;
4393  //PageDef *pageDef=0;
4394  QCString anchor,linkText=linkToText(SrcLangExt_Unknown,lt,FALSE);
4395  //printf("generateLink linkText=%s\n",linkText.data());
4396  if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
4397  {
4398  if (compound) // link to compound
4399  {
4400  if (lt==0 && anchor.isEmpty() && /* compound link */
4401  compound->definitionType()==Definition::TypeGroup /* is group */
4402  )
4403  {
4404  linkText=(dynamic_cast<const GroupDef *>(compound))->groupTitle(); // use group's title as link
4405  }
4406  else if (compound->definitionType()==Definition::TypeFile)
4407  {
4408  linkText=linkToText(compound->getLanguage(),lt,TRUE);
4409  }
4410  od.writeObjectLink(compound->getReference(),
4411  compound->getOutputFileBase(),anchor,linkText);
4412  if (!compound->isReference())
4413  {
4414  writePageRef(od,compound->getOutputFileBase(),anchor);
4415  }
4416  }
4417  else
4418  {
4419  err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
4420  }
4421  return TRUE;
4422  }
4423  else // link could not be found
4424  {
4425  od.docify(linkText);
4426  return FALSE;
4427  }
4428 }
4429 
4430 void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
4431 {
4432  //printf("generateFileRef(%s,%s)\n",name,text);
4433  QCString linkText = text ? text : name;
4434  //FileInfo *fi;
4435  FileDef *fd;
4436  bool ambig;
4437  if ((fd=findFileDef(Doxygen::inputNameLinkedMap,name,ambig)) &&
4438  fd->isLinkable())
4439  // link to documented input file
4440  od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
4441  else
4442  od.docify(linkText);
4443 }
4444 
4445 //----------------------------------------------------------------------
4446 
4447 #if 0
4449 {
4450  int i=0,l,p;
4451  QCString result;
4452  if (s.isEmpty()) return result;
4453  QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
4454  while ((p=r.match(s,i,&l))!=-1)
4455  {
4456  QCString *subst;
4457  if (p>i) result+=s.mid(i,p-i);
4458  if ((subst=substituteDict[s.mid(p,l)]))
4459  {
4460  result+=*subst;
4461  }
4462  else
4463  {
4464  result+=s.mid(p,l);
4465  }
4466  i=p+l;
4467  }
4468  result+=s.mid(i,s.length()-i);
4469  return result;
4470 }
4471 #endif
4472 
4473 //----------------------------------------------------------------------
4474 
4477 {
4478  FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
4480  bool isAmbig;
4481 };
4482 
4484 
4485 FileDef *findFileDef(const FileNameLinkedMap *fnMap,const char *n,bool &ambig)
4486 {
4487  ambig=FALSE;
4488  if (n==0) return 0;
4489 
4490  const int maxAddrSize = 20;
4491  char addr[maxAddrSize];
4492  qsnprintf(addr,maxAddrSize,"%p:",(void*)fnMap);
4493  QCString key = addr;
4494  key+=n;
4495 
4496  g_findFileDefCache.setAutoDelete(TRUE);
4497  FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
4498  //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
4499  if (cachedResult)
4500  {
4501  ambig = cachedResult->isAmbig;
4502  //printf("cached: fileDef=%p\n",cachedResult->fileDef);
4503  return cachedResult->fileDef;
4504  }
4505  else
4506  {
4507  cachedResult = new FindFileCacheElem(0,FALSE);
4508  }
4509 
4510  QCString name=QDir::cleanDirPath(n).utf8();
4511  QCString path;
4512  int slashPos;
4513  const FileName *fn;
4514  if (name.isEmpty()) goto exit;
4515  slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4516  if (slashPos!=-1)
4517  {
4518  path=name.left(slashPos+1);
4519  name=name.right(name.length()-slashPos-1);
4520  //printf("path=%s name=%s\n",path.data(),name.data());
4521  }
4522  if (name.isEmpty()) goto exit;
4523  if ((fn=fnMap->find(name)))
4524  {
4525  //printf("fn->count()=%d\n",fn->count());
4526  if (fn->size()==1)
4527  {
4528  const std::unique_ptr<FileDef> &fd = fn->front();
4529 #if defined(_WIN32) || defined(__MACOSX__) || defined(__CYGWIN__) // Windows or MacOSX
4530  bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
4531 #else // Unix
4532  bool isSamePath = fd->getPath().right(path.length())==path;
4533 #endif
4534  if (path.isEmpty() || isSamePath)
4535  {
4536  cachedResult->fileDef = fd.get();
4537  g_findFileDefCache.insert(key,cachedResult);
4538  //printf("=1 ===> add to cache %p\n",fd);
4539  return fd.get();
4540  }
4541  }
4542  else // file name alone is ambiguous
4543  {
4544  int count=0;
4545  FileDef *lastMatch=0;
4546  QCString pathStripped = stripFromIncludePath(path);
4547  for (const auto &fd : *fn)
4548  {
4549  QCString fdStripPath = stripFromIncludePath(fd->getPath());
4550  if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
4551  {
4552  count++;
4553  lastMatch=fd.get();
4554  }
4555  }
4556  //printf(">1 ===> add to cache %p\n",fd);
4557 
4558  ambig=(count>1);
4559  cachedResult->isAmbig = ambig;
4560  cachedResult->fileDef = lastMatch;
4561  g_findFileDefCache.insert(key,cachedResult);
4562  return lastMatch;
4563  }
4564  }
4565  else
4566  {
4567  //printf("not found!\n");
4568  }
4569 exit:
4570  //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
4571  g_findFileDefCache.insert(key,cachedResult);
4572  //delete cachedResult;
4573  return 0;
4574 }
4575 
4576 //----------------------------------------------------------------------
4577 
4578 QCString showFileDefMatches(const FileNameLinkedMap *fnMap,const char *n)
4579 {
4580  QCString result;
4581  QCString name=n;
4582  QCString path;
4583  int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4584  if (slashPos!=-1)
4585  {
4586  path=name.left(slashPos+1);
4587  name=name.right(name.length()-slashPos-1);
4588  }
4589  const FileName *fn;
4590  if ((fn=fnMap->find(name)))
4591  {
4592  for (const auto &fd : *fn)
4593  {
4594  if (path.isEmpty() || fd->getPath().right(path.length())==path)
4595  {
4596  result+=" "+fd->absFilePath()+"\n";
4597  }
4598  }
4599  }
4600  return result;
4601 }
4602 
4603 //----------------------------------------------------------------------
4604 
4606 QCString substitute(const QCString &s,const QCString &src,const QCString &dst)
4607 {
4608  if (s.isEmpty() || src.isEmpty()) return s;
4609  const char *p, *q;
4610  int srcLen = src.length();
4611  int dstLen = dst.length();
4612  int resLen;
4613  if (srcLen!=dstLen)
4614  {
4615  int count;
4616  for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++;
4617  resLen = s.length()+count*(dstLen-srcLen);
4618  }
4619  else // result has same size as s
4620  {
4621  resLen = s.length();
4622  }
4623  QCString result(resLen+1);
4624  char *r;
4625  for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
4626  {
4627  int l = (int)(q-p);
4628  memcpy(r,p,l);
4629  r+=l;
4630 
4631  if (dst) memcpy(r,dst,dstLen);
4632  r+=dstLen;
4633  }
4634  qstrcpy(r,p);
4635  //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
4636  return result;
4637 }
4638 
4639 
4644 QCString substitute(const QCString &s,const QCString &src,const QCString &dst,int skip_seq)
4645 {
4646  if (s.isEmpty() || src.isEmpty()) return s;
4647  const char *p, *q;
4648  int srcLen = src.length();
4649  int dstLen = dst.length();
4650  int resLen;
4651  if (srcLen!=dstLen)
4652  {
4653  int count;
4654  for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++;
4655  resLen = s.length()+count*(dstLen-srcLen);
4656  }
4657  else // result has same size as s
4658  {
4659  resLen = s.length();
4660  }
4661  QCString result(resLen+1);
4662  char *r;
4663  for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
4664  {
4665  // search a consecutive sequence of src
4666  int seq = 0, skip = 0;
4667  if (skip_seq)
4668  {
4669  for (const char *n=q+srcLen; qstrncmp(n,src,srcLen)==0; seq=1+skip, n+=srcLen)
4670  ++skip; // number of consecutive src after the current one
4671 
4672  // verify the allowed number of consecutive src to skip
4673  if (skip_seq > 0 && skip_seq != seq)
4674  seq = skip = 0;
4675  }
4676 
4677  // skip a consecutive sequence of src when necessary
4678  int l = (int)((q + seq * srcLen)-p);
4679  memcpy(r,p,l);
4680  r+=l;
4681 
4682  if (skip)
4683  {
4684  // skip only the consecutive src found after the current one
4685  q += skip * srcLen;
4686  // the next loop will skip the current src, aka (p=q+srcLen)
4687  continue;
4688  }
4689 
4690  if (dst) memcpy(r,dst,dstLen);
4691  r+=dstLen;
4692  }
4693  qstrcpy(r,p);
4694  result.resize((int)strlen(result.data())+1);
4695  //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
4696  return result;
4697 }
4698 
4700 QCString substitute(const QCString &s,char srcChar,char dstChar)
4701 {
4702  int l=s.length();
4703  QCString result(l+1);
4704  char *q=result.rawData();
4705  if (l>0)
4706  {
4707  const char *p=s.data();
4708  char c;
4709  while ((c=*p++)) *q++ = (c==srcChar) ? dstChar : c;
4710  }
4711  *q='\0';
4712  return result;
4713 }
4714 
4715 //----------------------------------------------------------------------
4716 
4717 QCString substituteKeywords(const QCString &s,const char *title,
4718  const char *projName,const char *projNum,const char *projBrief)
4719 {
4720  QCString result = s;
4721  if (title) result = substitute(result,"$title",title);
4722  result = substitute(result,"$datetime",dateToString(TRUE));
4723  result = substitute(result,"$date",dateToString(FALSE));
4724  result = substitute(result,"$year",yearToString());
4725  result = substitute(result,"$doxygenversion",getDoxygenVersion());
4726  result = substitute(result,"$projectname",projName);
4727  result = substitute(result,"$projectnumber",projNum);
4728  result = substitute(result,"$projectbrief",projBrief);
4729  result = substitute(result,"$projectlogo",stripPath(Config_getString(PROJECT_LOGO)));
4730  return result;
4731 }
4732 
4733 //----------------------------------------------------------------------
4734 
4739 int getPrefixIndex(const QCString &name)
4740 {
4741  if (name.isEmpty()) return 0;
4742  static QStrList &sl = Config_getList(IGNORE_PREFIX);
4743  char *s = sl.first();
4744  while (s)
4745  {
4746  const char *ps=s;
4747  const char *pd=name.data();
4748  int i=0;
4749  while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
4750  if (*ps==0 && *pd!=0)
4751  {
4752  return i;
4753  }
4754  s = sl.next();
4755  }
4756  return 0;
4757 }
4758 
4759 //----------------------------------------------------------------------------
4760 
4762 {
4763  if (bcl==0) return;
4764  BaseClassListIterator bcli(*bcl);
4765  for ( ; bcli.current(); ++bcli)
4766  {
4767  ClassDef *cd=bcli.current()->classDef;
4768  if (cd->baseClasses()==0) // no base classes => new root
4769  {
4771  }
4772  cd->setVisited(FALSE);
4773  }
4774 }
4775 //----------------------------------------------------------------------------
4776 
4778 {
4779  BaseClassList *bcl;
4780 
4781  if (cd->getLanguage()==SrcLangExt_VHDL) // reverse baseClass/subClass relation
4782  {
4783  if (cd->baseClasses()==0) return FALSE;
4784  bcl=cd->baseClasses();
4785  }
4786  else
4787  {
4788  if (cd->subClasses()==0) return FALSE;
4789  bcl=cd->subClasses();
4790  }
4791 
4792  BaseClassListIterator bcli(*bcl);
4793  for ( ; bcli.current() ; ++bcli)
4794  {
4795  if (bcli.current()->classDef->isVisibleInHierarchy())
4796  {
4797  return TRUE;
4798  }
4799  }
4800  return FALSE;
4801 }
4802 
4803 
4804 //----------------------------------------------------------------------------
4805 
4807 {
4808  ClassSDict::Iterator cli(*cl);
4809  ClassDef *cd;
4810  for ( ; (cd=cli.current()); ++cli)
4811  {
4812  cd->setVisited(FALSE);
4814  }
4815 }
4816 
4817 //----------------------------------------------------------------------------
4818 
4820 {
4821  if (bcl)
4822  {
4823  BaseClassListIterator bcli(*bcl);
4824  for ( ; bcli.current(); ++bcli)
4825  {
4826  const ClassDef *cd=bcli.current()->classDef;
4827  if (cd->isVisibleInHierarchy()) return TRUE;
4828  hasVisibleRoot(cd->baseClasses());
4829  }
4830  }
4831  return FALSE;
4832 }
4833 
4834 //----------------------------------------------------------------------
4835 
4836 // note that this function is not reentrant due to the use of static growBuf!
4837 QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
4838 {
4839  static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES);
4840  static bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
4841  static GrowBuf growBuf;
4842  growBuf.clear();
4843  if (name==0) return "";
4844  signed char c;
4845  const signed char *p=(const signed char*)name;
4846  while ((c=*p++)!=0)
4847  {
4848  switch(c)
4849  {
4850  case '_': if (allowUnderscore) growBuf.addChar('_'); else growBuf.addStr("__"); break;
4851  case '-': growBuf.addChar('-'); break;
4852  case ':': growBuf.addStr("_1"); break;
4853  case '/': growBuf.addStr("_2"); break;
4854  case '<': growBuf.addStr("_3"); break;
4855  case '>': growBuf.addStr("_4"); break;
4856  case '*': growBuf.addStr("_5"); break;
4857  case '&': growBuf.addStr("_6"); break;
4858  case '|': growBuf.addStr("_7"); break;
4859  case '.': if (allowDots) growBuf.addChar('.'); else growBuf.addStr("_8"); break;
4860  case '!': growBuf.addStr("_9"); break;
4861  case ',': growBuf.addStr("_00"); break;
4862  case ' ': growBuf.addStr("_01"); break;
4863  case '{': growBuf.addStr("_02"); break;
4864  case '}': growBuf.addStr("_03"); break;
4865  case '?': growBuf.addStr("_04"); break;
4866  case '^': growBuf.addStr("_05"); break;
4867  case '%': growBuf.addStr("_06"); break;
4868  case '(': growBuf.addStr("_07"); break;
4869  case ')': growBuf.addStr("_08"); break;
4870  case '+': growBuf.addStr("_09"); break;
4871  case '=': growBuf.addStr("_0a"); break;
4872  case '$': growBuf.addStr("_0b"); break;
4873  case '\\': growBuf.addStr("_0c"); break;
4874  case '@': growBuf.addStr("_0d"); break;
4875  case ']': growBuf.addStr("_0e"); break;
4876  case '[': growBuf.addStr("_0f"); break;
4877  default:
4878  if (c<0)
4879  {
4880  char ids[5];
4881  const unsigned char uc = (unsigned char)c;
4882  bool doEscape = TRUE;
4883  if (allowUnicodeNames && uc <= 0xf7)
4884  {
4885  const signed char* pt = p;
4886  ids[ 0 ] = c;
4887  int l = 0;
4888  if ((uc&0xE0)==0xC0)
4889  {
4890  l=2; // 11xx.xxxx: >=2 byte character
4891  }
4892  if ((uc&0xF0)==0xE0)
4893  {
4894  l=3; // 111x.xxxx: >=3 byte character
4895  }
4896  if ((uc&0xF8)==0xF0)
4897  {
4898  l=4; // 1111.xxxx: >=4 byte character
4899  }
4900  doEscape = l==0;
4901  for (int m=1; m<l && !doEscape; ++m)
4902  {
4903  unsigned char ct = (unsigned char)*pt;
4904  if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character
4905  {
4906  doEscape=TRUE;
4907  }
4908  else
4909  {
4910  ids[ m ] = *pt++;
4911  }
4912  }
4913  if ( !doEscape ) // got a valid unicode character
4914  {
4915  ids[ l ] = 0;
4916  growBuf.addStr( ids );
4917  p += l - 1;
4918  }
4919  }
4920  if (doEscape) // not a valid unicode char or escaping needed
4921  {
4922  static char map[] = "0123456789ABCDEF";
4923  unsigned char id = (unsigned char)c;
4924  ids[0]='_';
4925  ids[1]='x';
4926  ids[2]=map[id>>4];
4927  ids[3]=map[id&0xF];
4928  ids[4]=0;
4929  growBuf.addStr(ids);
4930  }
4931  }
4932  else if (caseSenseNames || !isupper(c))
4933  {
4934  growBuf.addChar(c);
4935  }
4936  else
4937  {
4938  growBuf.addChar('_');
4939  growBuf.addChar((char)tolower(c));
4940  }
4941  break;
4942  }
4943  }
4944  growBuf.addChar(0);
4945  return growBuf.get();
4946 }
4947 
4949 {
4950  static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES);
4951  QCString result;
4952  const char *p = s;
4953  if (p)
4954  {
4955  char c;
4956  while ((c=*p++))
4957  {
4958  if (c=='_') // 2 or 3 character escape
4959  {
4960  switch (*p)
4961  {
4962  case '_': result+=c; p++; break; // __ -> '_'
4963  case '1': result+=':'; p++; break; // _1 -> ':'
4964  case '2': result+='/'; p++; break; // _2 -> '/'
4965  case '3': result+='<'; p++; break; // _3 -> '<'
4966  case '4': result+='>'; p++; break; // _4 -> '>'
4967  case '5': result+='*'; p++; break; // _5 -> '*'
4968  case '6': result+='&'; p++; break; // _6 -> '&'
4969  case '7': result+='|'; p++; break; // _7 -> '|'
4970  case '8': result+='.'; p++; break; // _8 -> '.'
4971  case '9': result+='!'; p++; break; // _9 -> '!'
4972  case '0': // 3 character escape
4973  switch (*(p+1))
4974  {
4975  case '0': result+=','; p+=2; break; // _00 -> ','
4976  case '1': result+=' '; p+=2; break; // _01 -> ' '
4977  case '2': result+='{'; p+=2; break; // _02 -> '{'
4978  case '3': result+='}'; p+=2; break; // _03 -> '}'
4979  case '4': result+='?'; p+=2; break; // _04 -> '?'
4980  case '5': result+='^'; p+=2; break; // _05 -> '^'
4981  case '6': result+='%'; p+=2; break; // _06 -> '%'
4982  case '7': result+='('; p+=2; break; // _07 -> '('
4983  case '8': result+=')'; p+=2; break; // _08 -> ')'
4984  case '9': result+='+'; p+=2; break; // _09 -> '+'
4985  case 'a': result+='='; p+=2; break; // _0a -> '='
4986  case 'b': result+='$'; p+=2; break; // _0b -> '$'
4987  case 'c': result+='\\'; p+=2; break;// _0c -> '\'
4988  case 'd': result+='@'; p+=2; break; // _0d -> '@'
4989  case 'e': result+=']'; p+=2; break; // _0e -> ']'
4990  case 'f': result+='['; p+=2; break; // _0f -> '['
4991  default: // unknown escape, just pass underscore character as-is
4992  result+=c;
4993  break;
4994  }
4995  break;
4996  default:
4997  if (!caseSenseNames && c>='a' && c<='z') // lower to upper case escape, _a -> 'A'
4998  {
4999  result+=(char)toupper(*p);
5000  p++;
5001  }
5002  else // unknown escape, pass underscore character as-is
5003  {
5004  result+=c;
5005  }
5006  break;
5007  }
5008  }
5009  else // normal character; pass as is
5010  {
5011  result+=c;
5012  }
5013  }
5014  }
5015  return result;
5016 }
5017 
5022 QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
5023 {
5024  if (name==0 || name[0]=='\0') return "";
5025  static bool shortNames = Config_getBool(SHORT_NAMES);
5026  static bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
5027  QCString result;
5028  if (shortNames) // use short names only
5029  {
5030  static QDict<int> usedNames(10007);
5031  usedNames.setAutoDelete(TRUE);
5032  static int count=1;
5033 
5034  int *value=usedNames.find(name);
5035  int num;
5036  if (value==0)
5037  {
5038  usedNames.insert(name,new int(count));
5039  num = count++;
5040  }
5041  else
5042  {
5043  num = *value;
5044  }
5045  result.sprintf("a%05d",num);
5046  }
5047  else // long names
5048  {
5049  result=escapeCharsInString(name,allowDots,allowUnderscore);
5050  int resultLen = result.length();
5051  if (resultLen>=128) // prevent names that cannot be created!
5052  {
5053  // third algorithm based on MD5 hash
5054  uchar md5_sig[16];
5055  QCString sigStr(33);
5056  MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
5057  MD5SigToString(md5_sig,sigStr.rawData(),33);
5058  result=result.left(128-32)+sigStr;
5059  }
5060  }
5061  if (createSubdirs)
5062  {
5063  int l1Dir=0,l2Dir=0;
5064 
5065 #if MAP_ALGO==ALGO_COUNT
5066  // old algorithm, has the problem that after regeneration the
5067  // output can be located in a different dir.
5068  if (Doxygen::htmlDirMap==0)
5069  {
5070  Doxygen::htmlDirMap=new QDict<int>(100003);
5071  Doxygen::htmlDirMap->setAutoDelete(TRUE);
5072  }
5073  static int curDirNum=0;
5074  int *dirNum = Doxygen::htmlDirMap->find(result);
5075  if (dirNum==0) // new name
5076  {
5077  Doxygen::htmlDirMap->insert(result,new int(curDirNum));
5078  l1Dir = (curDirNum)&0xf; // bits 0-3
5079  l2Dir = (curDirNum>>4)&0xff; // bits 4-11
5080  curDirNum++;
5081  }
5082  else // existing name
5083  {
5084  l1Dir = (*dirNum)&0xf; // bits 0-3
5085  l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
5086  }
5087 #elif MAP_ALGO==ALGO_CRC16
5088  // second algorithm based on CRC-16 checksum
5089  int dirNum = qChecksum(result,result.length());
5090  l1Dir = dirNum&0xf;
5091  l2Dir = (dirNum>>4)&0xff;
5092 #elif MAP_ALGO==ALGO_MD5
5093  // third algorithm based on MD5 hash
5094  uchar md5_sig[16];
5095  MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
5096  l1Dir = md5_sig[14]&0xf;
5097  l2Dir = md5_sig[15];
5098 #endif
5099  result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
5100  }
5101  //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
5102  return result;
5103 }
5104 
5105 QCString relativePathToRoot(const char *name)
5106 {
5107  QCString result;
5108  if (Config_getBool(CREATE_SUBDIRS))
5109  {
5110  if (name==0)
5111  {
5112  return REL_PATH_TO_ROOT;
5113  }
5114  else
5115  {
5116  QCString n = name;
5117  int i = n.findRev('/');
5118  if (i!=-1)
5119  {
5120  result=REL_PATH_TO_ROOT;
5121  }
5122  }
5123  }
5124  return result;
5125 }
5126 
5128 {
5129  if (Config_getBool(CREATE_SUBDIRS))
5130  {
5131  // create 4096 subdirectories
5132  int l1,l2;
5133  for (l1=0;l1<16;l1++)
5134  {
5135  d.mkdir(QCString().sprintf("d%x",l1));
5136  for (l2=0;l2<256;l2++)
5137  {
5138  d.mkdir(QCString().sprintf("d%x/d%02x",l1,l2));
5139  }
5140  }
5141  }
5142 }
5143 
5147 void extractNamespaceName(const QCString &scopeName,
5148  QCString &className,QCString &namespaceName,
5149  bool allowEmptyClass)
5150 {
5151  int i,p;
5152  QCString clName=scopeName;
5153  NamespaceDef *nd = 0;
5154  if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
5155  { // the whole name is a namespace (and not a class)
5156  namespaceName=nd->name().copy();
5157  className.resize(0);
5158  goto done;
5159  }
5160  p=clName.length()-2;
5161  while (p>=0 && (i=clName.findRev("::",p))!=-1)
5162  // see if the first part is a namespace (and not a class)
5163  {
5164  //printf("Trying %s\n",clName.left(i).data());
5165  if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
5166  {
5167  //printf("found!\n");
5168  namespaceName=nd->name().copy();
5169  className=clName.right(clName.length()-i-2);
5170  goto done;
5171  }
5172  p=i-2; // try a smaller piece of the scope
5173  }
5174  //printf("not found!\n");
5175 
5176  // not found, so we just have to guess.
5177  className=scopeName.copy();
5178  namespaceName.resize(0);
5179 
5180 done:
5181  if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
5182  {
5183  // class and namespace with the same name, correct to return the class.
5184  className=namespaceName.copy();
5185  namespaceName.resize(0);
5186  }
5187  //printf("extractNamespace '%s' => '%s|%s'\n",scopeName.data(),
5188  // className.data(),namespaceName.data());
5189  if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p")
5190  {
5191  className = className.left(className.length()-2);
5192  }
5193  return;
5194 }
5195 
5197 {
5198  QCString result=scope.copy();
5199  if (!templ.isEmpty() && scope.find('<')==-1)
5200  {
5201  int si,pi=0;
5202  ClassDef *cd=0;
5203  while (
5204  (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
5205  ((cd=getClass(scope.left(si)))==0 || cd->templateArguments().empty())
5206  )
5207  {
5208  //printf("Tried '%s'\n",(scope.left(si)+templ).data());
5209  pi=si+2;
5210  }
5211  if (si==-1) // not nested => append template specifier
5212  {
5213  result+=templ;
5214  }
5215  else // nested => insert template specifier before after first class name
5216  {
5217  result=scope.left(si) + templ + scope.right(scope.length()-si);
5218  }
5219  }
5220  //printf("insertTemplateSpecifierInScope('%s','%s')=%s\n",
5221  // scope.data(),templ.data(),result.data());
5222  return result;
5223 }
5224 
5225 #if 0 // original version
5226 
5229 QCString stripScope(const char *name)
5230 {
5231  QCString result = name;
5232  int l=result.length();
5233  int p=l-1;
5234  bool done;
5235  int count;
5236 
5237  while (p>=0)
5238  {
5239  char c=result.at(p);
5240  switch (c)
5241  {
5242  case ':':
5243  //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5244  return result.right(l-p-1);
5245  case '>':
5246  count=1;
5247  done=FALSE;
5248  //printf("pos < = %d\n",p);
5249  p--;
5250  while (p>=0 && !done)
5251  {
5252  c=result.at(p--);
5253  switch (c)
5254  {
5255  case '>': count++; break;
5256  case '<': count--; if (count<=0) done=TRUE; break;
5257  default:
5258  //printf("c=%c count=%d\n",c,count);
5259  break;
5260  }
5261  }
5262  //printf("pos > = %d\n",p+1);
5263  break;
5264  default:
5265  p--;
5266  }
5267  }
5268  //printf("stripScope(%s)=%s\n",name,name);
5269  return name;
5270 }
5271 #endif
5272 
5273 // new version by Davide Cesari which also works for Fortran
5274 QCString stripScope(const char *name)
5275 {
5276  QCString result = name;
5277  int l=result.length();
5278  int p;
5279  bool done = FALSE;
5280  bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
5281  int count=0;
5282 
5283  do
5284  {
5285  p=l-1; // start at the end of the string
5286  while (p>=0 && count>=0)
5287  {
5288  char c=result.at(p);
5289  switch (c)
5290  {
5291  case ':':
5292  // only exit in the case of ::
5293  //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5294  if (p>0 && result.at(p-1)==':') return result.right(l-p-1);
5295  p--;
5296  break;
5297  case '>':
5298  if (skipBracket) // we don't care about brackets
5299  {
5300  p--;
5301  }
5302  else // count open/close brackets
5303  {
5304  if (p>0 && result.at(p-1)=='>') // skip >> operator
5305  {
5306  p-=2;
5307  break;
5308  }
5309  count=1;
5310  //printf("pos < = %d\n",p);
5311  p--;
5312  bool foundMatch=false;
5313  while (p>=0 && !foundMatch)
5314  {
5315  c=result.at(p--);
5316  switch (c)
5317  {
5318  case '>':
5319  count++;
5320  break;
5321  case '<':
5322  if (p>0)
5323  {
5324  if (result.at(p-1) == '<') // skip << operator
5325  {
5326  p--;
5327  break;
5328  }
5329  }
5330  count--;
5331  foundMatch = count==0;
5332  break;
5333  default:
5334  //printf("c=%c count=%d\n",c,count);
5335  break;
5336  }
5337  }
5338  }
5339  //printf("pos > = %d\n",p+1);
5340  break;
5341  default:
5342  p--;
5343  }
5344  }
5345  done = count==0 || skipBracket; // reparse if brackets do not match
5346  skipBracket=TRUE;
5347  }
5348  while (!done); // if < > unbalanced repeat ignoring them
5349  //printf("stripScope(%s)=%s\n",name,name);
5350  return name;
5351 }
5352 
5354 QCString convertToId(const char *s)
5355 {
5356  static const char hex[] = "0123456789ABCDEF";
5357  static GrowBuf growBuf;
5358  growBuf.clear();
5359  if (s==0) return "";
5360  const char *p=s;
5361  char c;
5362  bool first=TRUE;
5363  while ((c=*p++))
5364  {
5365  char encChar[4];
5366  if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-' || c==':' || c=='.')
5367  { // any permissive character except _
5368  if (first && c>='0' && c<='9') growBuf.addChar('a'); // don't start with a digit
5369  growBuf.addChar(c);
5370  }
5371  else
5372  {
5373  encChar[0]='_';
5374  encChar[1]=hex[((unsigned char)c)>>4];
5375  encChar[2]=hex[((unsigned char)c)&0xF];
5376  encChar[3]=0;
5377  growBuf.addStr(encChar);
5378  }
5379  first=FALSE;
5380  }
5381  growBuf.addChar(0);
5382  return growBuf.get();
5383 }
5384 
5386 QCString convertToXML(const char *s, bool keepEntities)
5387 {
5388  static GrowBuf growBuf;
5389  growBuf.clear();
5390  if (s==0) return "";
5391  const char *p=s;
5392  char c;
5393  while ((c=*p++))
5394  {
5395  switch (c)
5396  {
5397  case '<': growBuf.addStr("&lt;"); break;
5398  case '>': growBuf.addStr("&gt;"); break;
5399  case '&': if (keepEntities)
5400  {
5401  const char *e=p;
5402  char ce;
5403  while ((ce=*e++))
5404  {
5405  if (ce==';' || (!(isId(ce) || ce=='#'))) break;
5406  }
5407  if (ce==';') // found end of an entity
5408  {
5409  // copy entry verbatim
5410  growBuf.addChar(c);
5411  while (p<e) growBuf.addChar(*p++);
5412  }
5413  else
5414  {
5415  growBuf.addStr("&amp;");
5416  }
5417  }
5418  else
5419  {
5420  growBuf.addStr("&amp;");
5421  }
5422  break;
5423  case '\'': growBuf.addStr("&apos;"); break;
5424  case '"': growBuf.addStr("&quot;"); break;
5425  case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
5426  case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
5427  case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
5428  case 27: case 28: case 29: case 30: case 31:
5429  break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
5430  default: growBuf.addChar(c); break;
5431  }
5432  }
5433  growBuf.addChar(0);
5434  return growBuf.get();
5435 }
5436 
5439 {
5440  static GrowBuf growBuf;
5441  growBuf.clear();
5442  if (s==0) return "";
5443  const unsigned char *q;
5444  int cnt;
5445  const unsigned char *p=(const unsigned char *)s;
5446  char c;
5447  while ((c=*p++))
5448  {
5449  switch (c)
5450  {
5451  case '<': growBuf.addStr("&lt;"); break;
5452  case '>': growBuf.addStr("&gt;"); break;
5453  case '&': // possibility to have a special symbol
5454  q = p;
5455  cnt = 2; // we have to count & and ; as well
5456  while ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') || (*q >= '0' && *q <= '9'))
5457  {
5458  cnt++;
5459  q++;
5460  }
5461  if (*q == ';')
5462  {
5463  --p; // we need & as well
5464  DocSymbol::SymType res = HtmlEntityMapper::instance()->name2sym(QCString((char *)p).left(cnt));
5465  if (res == DocSymbol::Sym_Unknown)
5466  {
5467  p++;
5468  growBuf.addStr("&amp;");
5469  }
5470  else
5471  {
5472  growBuf.addStr(HtmlEntityMapper::instance()->docbook(res));
5473  q++;
5474  p = q;
5475  }
5476  }
5477  else
5478  {
5479  growBuf.addStr("&amp;");
5480  }
5481  break;
5482  case '\'': growBuf.addStr("&apos;"); break;
5483  case '"': growBuf.addStr("&quot;"); break;
5484  case '\007': growBuf.addStr("&#x2407;"); break;
5485  case 1: case 2: case 3: case 4: case 5: case 6: case 8:
5486  case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
5487  case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
5488  case 27: case 28: case 29: case 30: case 31:
5489  break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
5490  default: growBuf.addChar(c); break;
5491  }
5492  }
5493  growBuf.addChar(0);
5494  return growBuf.get();
5495 }
5496 
5498 QCString convertToHtml(const char *s,bool keepEntities)
5499 {
5500  static GrowBuf growBuf;
5501  growBuf.clear();
5502  if (s==0) return "";
5504  const char *p=s;
5505  char c;
5506  while ((c=*p++))
5507  {
5508  switch (c)
5509  {
5510  case '<': growBuf.addStr("&lt;"); break;
5511  case '>': growBuf.addStr("&gt;"); break;
5512  case '&': if (keepEntities)
5513  {
5514  const char *e=p;
5515  char ce;
5516  while ((ce=*e++))
5517  {
5518  if (ce==';' || (!(isId(ce) || ce=='#'))) break;
5519  }
5520  if (ce==';') // found end of an entity
5521  {
5522  // copy entry verbatim
5523  growBuf.addChar(c);
5524  while (p<e) growBuf.addChar(*p++);
5525  }
5526  else
5527  {
5528  growBuf.addStr("&amp;");
5529  }
5530  }
5531  else
5532  {
5533  growBuf.addStr("&amp;");
5534  }
5535  break;
5536  case '\'': growBuf.addStr("&#39;"); break;
5537  case '"': growBuf.addStr("&quot;"); break;
5538  default: growBuf.addChar(c); break;
5539  }
5540  }
5541  growBuf.addChar(0);
5542  return growBuf.get();
5543 }
5544 
5545 QCString convertToJSString(const char *s, bool applyTextDir)
5546 {
5547  static GrowBuf growBuf;
5548  growBuf.clear();
5549  if (s==0) return "";
5550  if (applyTextDir)
5552  const char *p=s;
5553  char c;
5554  while ((c=*p++))
5555  {
5556  switch (c)
5557  {
5558  case '"': growBuf.addStr("\\\""); break;
5559  case '\\': growBuf.addStr("\\\\"); break;
5560  default: growBuf.addChar(c); break;
5561  }
5562  }
5563  growBuf.addChar(0);
5564  return convertCharEntitiesToUTF8(growBuf.get());
5565 }
5566 
5568 {
5569  static GrowBuf growBuf;
5570  growBuf.clear();
5571  if (s==0) return "";
5572  const char *p=s;
5573  char c;
5574  while ((c=*p++))
5575  {
5576  switch (c)
5577  {
5578  case '(': growBuf.addStr("\\("); break;
5579  case ')': growBuf.addStr("\\)"); break;
5580  default: growBuf.addChar(c); break;
5581  }
5582  }
5583  growBuf.addChar(0);
5584  return growBuf.get();
5585 }
5586 
5587 QCString convertToLaTeX(const QCString &s,bool insideTabbing,bool keepSpaces)
5588 {
5589  QGString result;
5590  FTextStream t(&result);
5591  filterLatexString(t,s,insideTabbing,FALSE,FALSE,keepSpaces);
5592  return result.data();
5593 }
5594 
5595 
5596 
5598 {
5599  QCString result;
5600  static QRegExp entityPat("&[a-zA-Z]+[0-9]*;");
5601 
5602  if (s.length()==0) return result;
5603  static GrowBuf growBuf;
5604  growBuf.clear();
5605  int p,i=0,l;
5606  while ((p=entityPat.match(s,i,&l))!=-1)
5607  {
5608  if (p>i)
5609  {
5610  growBuf.addStr(s.mid(i,p-i));
5611  }
5612  QCString entity = s.mid(p,l);
5614  const char *code=0;
5615  if (symType!=DocSymbol::Sym_Unknown && (code=HtmlEntityMapper::instance()->utf8(symType)))
5616  {
5617  growBuf.addStr(code);
5618  }
5619  else
5620  {
5621  growBuf.addStr(s.mid(p,l));
5622  }
5623  i=p+l;
5624  }
5625  growBuf.addStr(s.mid(i,s.length()-i));
5626  growBuf.addChar(0);
5627  //printf("convertCharEntitiesToUTF8(%s)->%s\n",s.data(),growBuf.get());
5628  return growBuf.get();
5629 }
5630 
5635 {
5636  return theTranslator->trOverloadText();
5637  //"This is an overloaded member function, "
5638  // "provided for convenience. It differs from the above "
5639  // "function only in what argument(s) it accepts.";
5640 }
5641 
5643  MemberGroupSDict **ppMemberGroupSDict,
5644  const Definition *context)
5645 {
5646  ASSERT(context!=0);
5647  //printf("addMemberToMemberGroup()\n");
5648  if (ml==0) return;
5649  MemberListIterator mli(*ml);
5650  MemberDef *md;
5651  uint index;
5652  for (index=0;(md=mli.current());)
5653  {
5654  if (md->isEnumerate()) // insert enum value of this enum into groups
5655  {
5656  const MemberList *fmdl=md->enumFieldList();
5657  if (fmdl!=0)
5658  {
5659  MemberListIterator fmli(*fmdl);
5660  MemberDef *fmd;
5661  for (fmli.toFirst();(fmd=fmli.current());++fmli)
5662  {
5663  int groupId=fmd->getMemberGroupId();
5664  if (groupId!=-1)
5665  {
5666  MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5667  //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5668  //QCString *pDocs = Doxygen::memberDocDict[groupId];
5669  if (info)
5670  {
5671  if (*ppMemberGroupSDict==0)
5672  {
5673  *ppMemberGroupSDict = new MemberGroupSDict;
5674  (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5675  }
5676  MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5677  if (mg==0)
5678  {
5679  mg = new MemberGroup(
5680  groupId,
5681  info->header,
5682  info->doc,
5683  info->docFile,
5684  info->docLine
5685  );
5686  (*ppMemberGroupSDict)->append(groupId,mg);
5687  }
5688  mg->insertMember(fmd); // insert in member group
5689  fmd->setMemberGroup(mg);
5690  }
5691  }
5692  }
5693  }
5694  }
5695  int groupId=md->getMemberGroupId();
5696  if (groupId!=-1)
5697  {
5698  MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5699  //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5700  //QCString *pDocs = Doxygen::memberDocDict[groupId];
5701  if (info)
5702  {
5703  if (*ppMemberGroupSDict==0)
5704  {
5705  *ppMemberGroupSDict = new MemberGroupSDict;
5706  (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5707  }
5708  MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5709  if (mg==0)
5710  {
5711  mg = new MemberGroup(
5712  groupId,
5713  info->header,
5714  info->doc,
5715  info->docFile,
5716  info->docLine
5717  );
5718  (*ppMemberGroupSDict)->append(groupId,mg);
5719  }
5720  md = ml->take(index); // remove from member list
5721  mg->insertMember(md); // insert in member group
5722  mg->setRefItems(info->m_sli);
5723  md->setMemberGroup(mg);
5724  continue;
5725  }
5726  }
5727  ++mli;++index;
5728  }
5729 }
5730 
5736 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
5737 {
5738  static const QRegExp re_norm("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
5739  static const QRegExp re_ftn("[a-z_A-Z\\x80-\\xFF][()=_a-z_A-Z0-9:\\x80-\\xFF]*");
5740  QRegExp re;
5741 
5742  name.resize(0);
5743  templSpec.resize(0);
5744  int i,l;
5745  int typeLen=type.length();
5746  if (typeLen>0)
5747  {
5748  if (lang == SrcLangExt_Fortran)
5749  {
5750  if (type.at(pos)==',') return -1;
5751  if (type.left(4).lower()=="type")
5752  {
5753  re = re_norm;
5754  }
5755  else
5756  {
5757  re = re_ftn;
5758  }
5759  }
5760  else
5761  {
5762  re = re_norm;
5763  }
5764 
5765  if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
5766  {
5767  int ts=i+l;
5768  int te=ts;
5769  int tl=0;
5770  while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
5771  if (type.at(ts)=='<') // assume template instance
5772  {
5773  // locate end of template
5774  te=ts+1;
5775  int brCount=1;
5776  while (te<typeLen && brCount!=0)
5777  {
5778  if (type.at(te)=='<')
5779  {
5780  if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
5781  }
5782  if (type.at(te)=='>')
5783  {
5784  if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
5785  }
5786  te++;
5787  }
5788  }
5789  name = type.mid(i,l);
5790  if (te>ts)
5791  {
5792  templSpec = type.mid(ts,te-ts),tl+=te-ts;
5793  pos=i+l+tl;
5794  }
5795  else // no template part
5796  {
5797  pos=i+l;
5798  }
5799  //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
5800  // type.data(),pos,name.data(),templSpec.data());
5801  return i;
5802  }
5803  }
5804  pos = typeLen;
5805  //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
5806  // type.data(),pos,name.data(),templSpec.data());
5807  return -1;
5808 }
5809 
5811  const QCString &name,
5812  const Definition *context,
5813  const ArgumentList &formalArgs)
5814 {
5815  // skip until <
5816  int p=name.find('<');
5817  if (p==-1) return name;
5818  p++;
5819  QCString result = name.left(p);
5820 
5821  static QRegExp re("[a-z:_A-Z\\x80-\\xFF][a-z:_A-Z0-9\\x80-\\xFF]*");
5822  int l,i;
5823  // for each identifier in the template part (e.g. B<T> -> T)
5824  while ((i=re.match(name,p,&l))!=-1)
5825  {
5826  result += name.mid(p,i-p);
5827  QCString n = name.mid(i,l);
5828  bool found=FALSE;
5829  for (const Argument formArg : formalArgs)
5830  {
5831  if (formArg.name == n)
5832  {
5833  found=TRUE;
5834  break;
5835  }
5836  }
5837  if (!found)
5838  {
5839  // try to resolve the type
5840  const ClassDef *cd = getResolvedClass(context,0,n);
5841  if (cd)
5842  {
5843  result+=cd->name();
5844  }
5845  else
5846  {
5847  result+=n;
5848  }
5849  }
5850  else
5851  {
5852  result+=n;
5853  }
5854  p=i+l;
5855  }
5856  result+=name.right(name.length()-p);
5857  //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",name.data(),result.data());
5858  return removeRedundantWhiteSpace(result);
5859 }
5860 
5861 
5869  const QCString &name,
5870  const ArgumentList &formalArgs,
5871  const ArgumentList &actualArgs)
5872 {
5873  //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
5874  // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
5875  if (formalArgs.empty()) return name;
5876  QCString result;
5877  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
5878  int p=0,l,i;
5879  // for each identifier in the base class name (e.g. B<T> -> B and T)
5880  while ((i=re.match(name,p,&l))!=-1)
5881  {
5882  result += name.mid(p,i-p);
5883  QCString n = name.mid(i,l);
5884  auto actIt = actualArgs.begin();
5885 
5886  // if n is a template argument, then we substitute it
5887  // for its template instance argument.
5888  bool found=FALSE;
5889  for (auto formIt = formalArgs.begin();
5890  formIt!=formalArgs.end() && !found;
5891  ++formIt
5892  )
5893  {
5894  Argument formArg = *formIt;
5895  Argument actArg;
5896  if (actIt!=actualArgs.end())
5897  {
5898  actArg = *actIt;
5899  }
5900  if (formArg.type.left(6)=="class " && formArg.name.isEmpty())
5901  {
5902  formArg.name = formArg.type.mid(6);
5903  formArg.type = "class";
5904  }
5905  if (formArg.type.left(9)=="typename " && formArg.name.isEmpty())
5906  {
5907  formArg.name = formArg.type.mid(9);
5908  formArg.type = "typename";
5909  }
5910  if (formArg.type=="class" || formArg.type=="typename" || formArg.type.left(8)=="template")
5911  {
5912  //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
5913  // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
5914  //printf(">> n='%s' formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
5915  // n.data(),formArg.name.data(),actIt!=actualArgs.end() ? actIt->type.data() : "",actIt!=actualArgs.end() ? actIt->name.data() : ""
5916  // );
5917  if (formArg.name==n && actIt!=actualArgs.end() && !actArg.type.isEmpty()) // base class is a template argument
5918  {
5919  // replace formal argument with the actual argument of the instance
5920  if (!leftScopeMatch(actArg.type,n))
5921  // the scope guard is to prevent recursive lockup for
5922  // template<class A> class C : public<A::T>,
5923  // where A::T would become A::T::T here,
5924  // since n==A and actArg->type==A::T
5925  // see bug595833 for an example
5926  {
5927  if (actArg.name.isEmpty())
5928  {
5929  result += actArg.type+" ";
5930  found=TRUE;
5931  }
5932  else
5933  // for case where the actual arg is something like "unsigned int"
5934  // the "int" part is in actArg->name.
5935  {
5936  result += actArg.type+" "+actArg.name+" ";
5937  found=TRUE;
5938  }
5939  }
5940  }
5941  else if (formArg.name==n &&
5942  actIt==actualArgs.end() &&
5943  !formArg.defval.isEmpty() &&
5944  formArg.defval!=name /* to prevent recursion */
5945  )
5946  {
5947  result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs)+