My Project
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
util.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  *
4  * Copyright (C) 1997-2015 by Dimitri van Heesch.
5  *
6  * Permission to use, copy, modify, and distribute this software and its
7  * documentation under the terms of the GNU General Public License is hereby
8  * granted. No representations are made about the suitability of this software
9  * for any purpose. It is provided "as is" without express or implied warranty.
10  * See the GNU General Public License for more details.
11  *
12  * Documents produced by Doxygen are derivative works derived from the
13  * input used in their production; they are not affected by this license.
14  *
15  */
16 
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <math.h>
21 #include <limits.h>
22 
23 #include "md5.h"
24 
25 #include <qregexp.h>
26 #include <qfileinfo.h>
27 #include <qdir.h>
28 #include <qdatetime.h>
29 #include <qcache.h>
30 
31 #include "util.h"
32 #include "message.h"
33 #include "classdef.h"
34 #include "filedef.h"
35 #include "doxygen.h"
36 #include "outputlist.h"
37 #include "defargs.h"
38 #include "language.h"
39 #include "config.h"
40 #include "htmlhelp.h"
41 #include "example.h"
42 #include "version.h"
43 #include "groupdef.h"
44 #include "reflist.h"
45 #include "pagedef.h"
46 #include "debug.h"
47 #include "searchindex.h"
48 #include "doxygen.h"
49 #include "textdocvisitor.h"
50 #include "portable.h"
51 #include "parserintf.h"
52 #include "bufstr.h"
53 #include "image.h"
54 #include "growbuf.h"
55 #include "entry.h"
56 #include "arguments.h"
57 #include "memberlist.h"
58 #include "classlist.h"
59 #include "namespacedef.h"
60 #include "membername.h"
61 #include "filename.h"
62 #include "membergroup.h"
63 #include "dirdef.h"
64 #include "htmlentity.h"
65 
66 #define ENABLE_TRACINGSUPPORT 0
67 
68 #if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
69 #define TRACINGSUPPORT
70 #endif
71 
72 #ifdef TRACINGSUPPORT
73 #include <execinfo.h>
74 #include <unistd.h>
75 #endif
76 
77 
78 //------------------------------------------------------------------------
79 
80 // selects one of the name to sub-dir mapping algorithms that is used
81 // to select a sub directory when CREATE_SUBDIRS is set to YES.
82 
83 #define ALGO_COUNT 1
84 #define ALGO_CRC16 2
85 #define ALGO_MD5 3
86 
87 //#define MAP_ALGO ALGO_COUNT
88 //#define MAP_ALGO ALGO_CRC16
89 #define MAP_ALGO ALGO_MD5
90 
91 #define REL_PATH_TO_ROOT "../../"
92 
93 //------------------------------------------------------------------------
94 // TextGeneratorOLImpl implementation
95 //------------------------------------------------------------------------
96 
98 {
99 }
100 
101 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
102 {
103  if (s==0) return;
104  //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces);
105  if (keepSpaces)
106  {
107  const char *p=s;
108  if (p)
109  {
110  char cs[2];
111  char c;
112  cs[1]='\0';
113  while ((c=*p++))
114  {
115  if (c==' ') m_od.writeNonBreakableSpace(1);
116  else cs[0]=c,m_od.docify(cs);
117  }
118  }
119  }
120  else
121  {
122  m_od.docify(s);
123  }
124 }
125 
126 void TextGeneratorOLImpl::writeBreak(int indent) const
127 {
128  m_od.lineBreak("typebreak");
129  int i;
130  for (i=0;i<indent;i++)
131  {
133  }
134 }
135 
136 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
137  const char *anchor,const char *text
138  ) const
139 {
140  //printf("TextGeneratorOlImpl::writeLink('%s')\n",text);
141  m_od.writeObjectLink(extRef,file,anchor,text);
142 }
143 
144 //------------------------------------------------------------------------
145 //------------------------------------------------------------------------
146 
147 // an inheritance tree of depth of 100000 should be enough for everyone :-)
148 const int maxInheritanceDepth = 100000;
149 
164 QCString removeAnonymousScopes(const QCString &s)
165 {
166  QCString result;
167  if (s.isEmpty()) return result;
168  static QRegExp re("[ :]*@[0-9]+[: ]*");
169  int i,l,sl=s.length();
170  int p=0;
171  while ((i=re.match(s,p,&l))!=-1)
172  {
173  result+=s.mid(p,i-p);
174  int c=i;
175  bool b1=FALSE,b2=FALSE;
176  while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
177  c=i+l-1;
178  while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
179  if (b1 && b2)
180  {
181  result+="::";
182  }
183  p=i+l;
184  }
185  result+=s.right(sl-p);
186  //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
187  return result;
188 }
189 
190 // replace anonymous scopes with __anonymous__ or replacement if provided
191 QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
192 {
193  QCString result;
194  if (s.isEmpty()) return result;
195  static QRegExp re("@[0-9]+");
196  int i,l,sl=s.length();
197  int p=0;
198  while ((i=re.match(s,p,&l))!=-1)
199  {
200  result+=s.mid(p,i-p);
201  if (replacement)
202  {
203  result+=replacement;
204  }
205  else
206  {
207  result+="__anonymous__";
208  }
209  p=i+l;
210  }
211  result+=s.right(sl-p);
212  //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
213  return result;
214 }
215 
216 
217 // strip anonymous left hand side part of the scope
218 QCString stripAnonymousNamespaceScope(const QCString &s)
219 {
220  int i,p=0,l;
221  QCString newScope;
222  int sl = s.length();
223  while ((i=getScopeFragment(s,p,&l))!=-1)
224  {
225  //printf("Scope fragment %s\n",s.mid(i,l).data());
226  if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
227  {
228  if (s.at(i)!='@')
229  {
230  if (!newScope.isEmpty()) newScope+="::";
231  newScope+=s.mid(i,l);
232  }
233  }
234  else if (i<sl)
235  {
236  if (!newScope.isEmpty()) newScope+="::";
237  newScope+=s.right(sl-i);
238  goto done;
239  }
240  p=i+l;
241  }
242 done:
243  //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
244  return newScope;
245 }
246 
247 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
248 {
249  od.pushGeneratorState();
250 
253  if (Config_getBool(PDF_HYPERLINKS)) od.disable(OutputGenerator::Latex);
254  if (Config_getBool(RTF_HYPERLINKS)) od.disable(OutputGenerator::RTF);
255  od.startPageRef();
257  od.endPageRef(cn,mn);
258 
259  od.popGeneratorState();
260 }
261 
266 QCString generateMarker(int id)
267 {
268  const int maxMarkerStrLen = 20;
269  char result[maxMarkerStrLen];
270  qsnprintf(result,maxMarkerStrLen,"@%d",id);
271  return result;
272 }
273 
274 static QCString stripFromPath(const QCString &path,QStrList &l)
275 {
276  // look at all the strings in the list and strip the longest match
277  const char *s=l.first();
278  QCString potential;
279  unsigned int length = 0;
280  while (s)
281  {
282  QCString prefix = s;
283  if (prefix.length() > length &&
284  qstricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
285  {
286  length = prefix.length();
287  potential = path.right(path.length()-prefix.length());
288  }
289  s = l.next();
290  }
291  if (length) return potential;
292  return path;
293 }
294 
298 QCString stripFromPath(const QCString &path)
299 {
300  return stripFromPath(path,Config_getList(STRIP_FROM_PATH));
301 }
302 
306 QCString stripFromIncludePath(const QCString &path)
307 {
308  return stripFromPath(path,Config_getList(STRIP_FROM_INC_PATH));
309 }
310 
315 int guessSection(const char *name)
316 {
317  QCString n=((QCString)name).lower();
318  if (n.right(2)==".c" || // source
319  n.right(3)==".cc" ||
320  n.right(4)==".cxx" ||
321  n.right(4)==".cpp" ||
322  n.right(4)==".c++" ||
323  n.right(5)==".java" ||
324  n.right(2)==".m" ||
325  n.right(2)==".M" ||
326  n.right(3)==".mm" ||
327  n.right(3)==".ii" || // inline
328  n.right(4)==".ixx" ||
329  n.right(4)==".ipp" ||
330  n.right(4)==".i++" ||
331  n.right(4)==".inl" ||
332  n.right(4)==".xml"
333  ) return Entry::SOURCE_SEC;
334  if (n.right(2)==".h" || // header
335  n.right(3)==".hh" ||
336  n.right(4)==".hxx" ||
337  n.right(4)==".hpp" ||
338  n.right(4)==".h++" ||
339  n.right(4)==".idl" ||
340  n.right(4)==".ddl" ||
341  n.right(5)==".pidl"
342  ) return Entry::HEADER_SEC;
343  return 0;
344 }
345 
346 QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
347  Definition **typedefContext)
348 {
349  //printf("<<resolveTypeDef(%s,%s)\n",
350  // context ? context->name().data() : "<none>",qualifiedName.data());
351  QCString result;
352  if (qualifiedName.isEmpty())
353  {
354  //printf(" qualified name empty!\n");
355  return result;
356  }
357 
358  Definition *mContext=context;
359  if (typedefContext) *typedefContext=context;
360 
361  // see if the qualified name has a scope part
362  int scopeIndex = qualifiedName.findRev("::");
363  QCString resName=qualifiedName;
364  if (scopeIndex!=-1) // strip scope part for the name
365  {
366  resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
367  if (resName.isEmpty())
368  {
369  // qualifiedName was of form A:: !
370  //printf(" qualified name of form A::!\n");
371  return result;
372  }
373  }
374  MemberDef *md=0;
375  while (mContext && md==0)
376  {
377  // step 1: get the right scope
378  Definition *resScope=mContext;
379  if (scopeIndex!=-1)
380  {
381  // split-off scope part
382  QCString resScopeName = qualifiedName.left(scopeIndex);
383  //printf("resScopeName=`%s'\n",resScopeName.data());
384 
385  // look-up scope in context
386  int is,ps=0;
387  int l;
388  while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
389  {
390  QCString qualScopePart = resScopeName.mid(is,l);
391  QCString tmp = resolveTypeDef(mContext,qualScopePart);
392  if (!tmp.isEmpty()) qualScopePart=tmp;
393  resScope = resScope->findInnerCompound(qualScopePart);
394  //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
395  if (resScope==0) break;
396  ps=is+l;
397  }
398  }
399  //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
400 
401  // step 2: get the member
402  if (resScope) // no scope or scope found in the current context
403  {
404  //printf("scope found: %s, look for typedef %s\n",
405  // resScope->qualifiedName().data(),resName.data());
406  MemberNameSDict *mnd=0;
407  if (resScope->definitionType()==Definition::TypeClass)
408  {
410  }
411  else
412  {
414  }
415  MemberName *mn=mnd->find(resName);
416  if (mn)
417  {
418  MemberNameIterator mni(*mn);
419  MemberDef *tmd=0;
420  int minDist=-1;
421  for (;(tmd=mni.current());++mni)
422  {
423  //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
424  // tmd->name().data(), resScope->name().data(),
425  // tmd->getOuterScope()->name().data(), mContext);
426  if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
427  {
428  int dist=isAccessibleFrom(resScope,0,tmd);
429  if (dist!=-1 && (md==0 || dist<minDist))
430  {
431  md = tmd;
432  minDist = dist;
433  }
434  }
435  }
436  }
437  }
438  mContext=mContext->getOuterScope();
439  }
440 
441  // step 3: get the member's type
442  if (md)
443  {
444  //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s' args='%s'\n",
445  // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
446  // );
447  result=md->typeString();
448  QCString args = md->argsString();
449  if (args.find(")(")!=-1) // typedef of a function/member pointer
450  {
451  result+=args;
452  }
453  else if (args.find('[')!=-1) // typedef of an array
454  {
455  result+=args;
456  }
457  if (typedefContext) *typedefContext=md->getOuterScope();
458  }
459  else
460  {
461  //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
462  // qualifiedName.data(),context ? context->name().data() : "<global>");
463  }
464  return result;
465 
466 }
467 
468 
472 ClassDef *getClass(const char *n)
473 {
474  if (n==0 || n[0]=='\0') return 0;
475  QCString name=n;
476  ClassDef *result = Doxygen::classSDict->find(name);
477  //if (result==0 && !exact) // also try generic and protocol versions
478  //{
479  // result = Doxygen::classSDict->find(name+"-g");
480  // if (result==0)
481  // {
482  // result = Doxygen::classSDict->find(name+"-p");
483  // }
484  //}
485  //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>");
486  return result;
487 }
488 
490 {
491  if (name==0 || name[0]=='\0') return 0;
492  QCString *subst = Doxygen::namespaceAliasDict[name];
493  if (subst)
494  {
495  int count=0; // recursion detection guard
496  QCString *newSubst;
497  while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
498  {
499  subst=newSubst;
500  count++;
501  }
502  if (count==10)
503  {
504  warn_uncond("possible recursive namespace alias detected for %s!\n",name);
505  }
506  return Doxygen::namespaceSDict->find(subst->data());
507  }
508  else
509  {
510  return Doxygen::namespaceSDict->find(name);
511  }
512 }
513 
514 static QDict<MemberDef> g_resolvedTypedefs;
515 static QDict<Definition> g_visitedNamespaces;
516 
517 // forward declaration
519  FileDef *fileScope,
520  const char *n,
521  MemberDef **pTypeDef,
522  QCString *pTemplSpec,
523  QCString *pResolvedType
524  );
525 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
526  const QCString &explicitScopePart);
527 
536  MemberDef **pMemType,QCString *pTemplSpec,
537  QCString *pResolvedType,
538  ArgumentList *actTemplParams)
539 {
540  //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
541  bool isCached = md->isTypedefValCached(); // value already cached
542  if (isCached)
543  {
544  //printf("Already cached %s->%s [%s]\n",
545  // md->name().data(),
546  // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
547  // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
548 
549  if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
550  if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
551  return md->getCachedTypedefVal();
552  }
553  //printf("new typedef\n");
554  QCString qname = md->qualifiedName();
555  if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
556 
557  g_resolvedTypedefs.insert(qname,md); // put on the trace list
558 
559  ClassDef *typeClass = md->getClassDef();
560  QCString type = md->typeString(); // get the "value" of the typedef
561  if (typeClass && typeClass->isTemplate() &&
562  actTemplParams && actTemplParams->count()>0)
563  {
565  typeClass->templateArguments(),actTemplParams);
566  }
567  QCString typedefValue = type;
568  int tl=type.length();
569  int ip=tl-1; // remove * and & at the end
570  while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
571  {
572  ip--;
573  }
574  type=type.left(ip+1);
575  type.stripPrefix("const "); // strip leading "const"
576  type.stripPrefix("struct "); // strip leading "struct"
577  type.stripPrefix("union "); // strip leading "union"
578  int sp=0;
579  tl=type.length(); // length may have been changed
580  while (sp<tl && type.at(sp)==' ') sp++;
581  MemberDef *memTypeDef = 0;
583  fileScope,type,&memTypeDef,0,pResolvedType);
584  // if type is a typedef then return what it resolves to.
585  if (memTypeDef && memTypeDef->isTypedef())
586  {
587  result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
588  goto done;
589  }
590  else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
591  {
592  *pMemType = memTypeDef;
593  }
594 
595  //printf("type=%s result=%p\n",type.data(),result);
596  if (result==0)
597  {
598  // try unspecialized version if type is template
599  int si=type.findRev("::");
600  int i=type.find('<');
601  if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
602  {
603  if (pTemplSpec) *pTemplSpec = type.mid(i);
604  result = getResolvedClassRec(md->getOuterScope(),fileScope,
605  type.left(i),0,0,pResolvedType);
606  //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
607  // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
608  }
609  else if (si!=-1) // A::B
610  {
611  i=type.find('<',si);
612  if (i==-1) // Something like A<T>::B => lookup A::B
613  {
614  i=type.length();
615  }
616  else // Something like A<T>::B<S> => lookup A::B, spec=<S>
617  {
618  if (pTemplSpec) *pTemplSpec = type.mid(i);
619  }
620  result = getResolvedClassRec(md->getOuterScope(),fileScope,
621  stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
622  pResolvedType);
623  }
624 
625  //if (result) ip=si+sp+1;
626  }
627 
628 done:
629  if (pResolvedType)
630  {
631  if (result)
632  {
633  *pResolvedType=result->qualifiedName();
634  //printf("*pResolvedType=%s\n",pResolvedType->data());
635  if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
636  if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
637  }
638  else
639  {
640  *pResolvedType=typedefValue;
641  }
642  }
643 
644  // remember computed value for next time
645  if (result && result->getDefFileName()!="<code>")
646  // this check is needed to prevent that temporary classes that are
647  // introduced while parsing code fragments are being cached here.
648  {
649  //printf("setting cached typedef %p in result %p\n",md,result);
650  //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
651  //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
652  md->cacheTypedefVal(result,
653  pTemplSpec ? *pTemplSpec : QCString(),
654  pResolvedType ? *pResolvedType : QCString()
655  );
656  }
657 
658  g_resolvedTypedefs.remove(qname); // remove from the trace list
659 
660  return result;
661 }
662 
666 static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
667  MemberDef **pTypeDef=0)
668 {
669  QCString result=name;
670  if (name.isEmpty()) return result;
671 
672  // lookup scope fragment in the symbol map
673  DefinitionIntf *di = Doxygen::symbolMap->find(name);
674  if (di==0) return result; // no matches
675 
676  MemberDef *bestMatch=0;
677  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
678  {
679  // search for the best match
681  Definition *d;
682  int minDistance=10000; // init at "infinite"
683  for (dli.toFirst();(d=dli.current());++dli) // foreach definition
684  {
685  // only look at members
687  {
688  // that are also typedefs
689  MemberDef *md = (MemberDef *)d;
690  if (md->isTypedef()) // d is a typedef
691  {
692  // test accessibility of typedef within scope.
693  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
694  if (distance!=-1 && distance<minDistance)
695  // definition is accessible and a better match
696  {
697  minDistance=distance;
698  bestMatch = md;
699  }
700  }
701  }
702  }
703  }
704  else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
705  {
706  Definition *d = (Definition*)di;
707  // that are also typedefs
708  MemberDef *md = (MemberDef *)di;
709  if (md->isTypedef()) // d is a typedef
710  {
711  // test accessibility of typedef within scope.
712  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
713  if (distance!=-1) // definition is accessible
714  {
715  bestMatch = md;
716  }
717  }
718  }
719  if (bestMatch)
720  {
721  result = bestMatch->typeString();
722  if (pTypeDef) *pTypeDef=bestMatch;
723  }
724 
725  //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
726  // name.data(),result.data());
727  return result;
728 }
729 
730 static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
731 {
732  if (cl)
733  {
735  Definition *cd;
736  for (cli.toFirst();(cd=cli.current());++cli)
737  {
738  if (cd->localName()==localName)
739  {
740  return cd;
741  }
742  }
743  }
744  return 0;
745 }
746 
752 static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
753 {
754  int is,ps;
755  int l;
756  Definition *current=start;
757  ps=0;
758  //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
759  // for each part of the explicit scope
760  while ((is=getScopeFragment(path,ps,&l))!=-1)
761  {
762  // try to resolve the part if it is a typedef
763  MemberDef *typeDef=0;
764  QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
765  //printf(" qualScopePart=%s\n",qualScopePart.data());
766  if (typeDef)
767  {
768  ClassDef *type = newResolveTypedef(fileScope,typeDef);
769  if (type)
770  {
771  //printf("Found type %s\n",type->name().data());
772  return type;
773  }
774  }
775  Definition *next = current->findInnerCompound(qualScopePart);
776  //printf("++ Looking for %s inside %s result %s\n",
777  // qualScopePart.data(),
778  // current->name().data(),
779  // next?next->name().data():"<null>");
780  if (next==0) // failed to follow the path
781  {
782  //printf("==> next==0!\n");
784  {
785  next = endOfPathIsUsedClass(
786  ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
787  }
788  else if (current->definitionType()==Definition::TypeFile)
789  {
790  next = endOfPathIsUsedClass(
791  ((FileDef *)current)->getUsedClasses(),qualScopePart);
792  }
793  current = next;
794  if (current==0) break;
795  }
796  else // continue to follow scope
797  {
798  current = next;
799  //printf("==> current = %p\n",current);
800  }
801  ps=is+l;
802  }
803  //printf("followPath(start=%s,path=%s) result=%s\n",
804  // start->name().data(),path.data(),current?current->name().data():"<null>");
805  return current; // path could be followed
806 }
807 
809  FileDef *fileScope,
810  Definition *item,
811  const QCString &explicitScopePart=""
812  )
813 {
814  //printf("accessibleViaUsingClass(%p)\n",cl);
815  if (cl) // see if the class was imported via a using statement
816  {
818  Definition *ucd;
819  bool explicitScopePartEmpty = explicitScopePart.isEmpty();
820  for (cli.toFirst();(ucd=cli.current());++cli)
821  {
822  //printf("Trying via used class %s\n",ucd->name().data());
823  Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
824  if (sc && sc==item) return TRUE;
825  //printf("Try via used class done\n");
826  }
827  }
828  return FALSE;
829 }
830 
832  FileDef *fileScope,
833  Definition *item,
834  const QCString &explicitScopePart="")
835 {
836  static QDict<void> visitedDict;
837  if (nl) // check used namespaces for the class
838  {
839  NamespaceSDict::Iterator nli(*nl);
840  NamespaceDef *und;
841  int count=0;
842  for (nli.toFirst();(und=nli.current());++nli,count++)
843  {
844  //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
845  // count,nl->count());
846  Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
847  if (sc && item->getOuterScope()==sc)
848  {
849  //printf("] found it\n");
850  return TRUE;
851  }
852  QCString key=und->name();
853  if (und->getUsedNamespaces() && visitedDict.find(key)==0)
854  {
855  visitedDict.insert(key,(void *)0x08);
856 
857  if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
858  {
859  //printf("] found it via recursion\n");
860  return TRUE;
861  }
862 
863  visitedDict.remove(key);
864  }
865  //printf("] Try via used namespace done\n");
866  }
867  }
868  return FALSE;
869 }
870 
871 const int MAX_STACK_SIZE = 1000;
872 
877 {
878  public:
880  void push(Definition *scope,FileDef *fileScope,Definition *item)
881  {
883  {
884  m_elements[m_index].scope = scope;
885  m_elements[m_index].fileScope = fileScope;
886  m_elements[m_index].item = item;
887  m_index++;
888  }
889  }
890  void push(Definition *scope,FileDef *fileScope,Definition *item,const QCString &expScope)
891  {
893  {
894  m_elements[m_index].scope = scope;
895  m_elements[m_index].fileScope = fileScope;
896  m_elements[m_index].item = item;
897  m_elements[m_index].expScope = expScope;
898  m_index++;
899  }
900  }
901  void pop()
902  {
903  if (m_index>0) m_index--;
904  }
905  bool find(Definition *scope,FileDef *fileScope, Definition *item)
906  {
907  int i=0;
908  for (i=0;i<m_index;i++)
909  {
910  AccessElem *e = &m_elements[i];
911  if (e->scope==scope && e->fileScope==fileScope && e->item==item)
912  {
913  return TRUE;
914  }
915  }
916  return FALSE;
917  }
918  bool find(Definition *scope,FileDef *fileScope, Definition *item,const QCString &expScope)
919  {
920  int i=0;
921  for (i=0;i<m_index;i++)
922  {
923  AccessElem *e = &m_elements[i];
924  if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope)
925  {
926  return TRUE;
927  }
928  }
929  return FALSE;
930  }
931 
932  private:
934  struct AccessElem
935  {
939  QCString expScope;
940  };
941  int m_index;
943 };
944 
945 /* Returns the "distance" (=number of levels up) from item to scope, or -1
946  * if item in not inside scope.
947  */
948 int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
949 {
950  //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
951  // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
952 
953  static AccessStack accessStack;
954  if (accessStack.find(scope,fileScope,item))
955  {
956  return -1;
957  }
958  accessStack.push(scope,fileScope,item);
959 
960  int result=0; // assume we found it
961  int i;
962 
963  Definition *itemScope=item->getOuterScope();
964  bool memberAccessibleFromScope =
965  (item->definitionType()==Definition::TypeMember && // a member
966  itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
967  scope->definitionType()==Definition::TypeClass && // accessible
968  ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope
969  );
970  bool nestedClassInsideBaseClass =
971  (item->definitionType()==Definition::TypeClass && // a nested class
972  itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
973  scope->definitionType()==Definition::TypeClass && // class of scope
974  ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)
975  );
976 
977  if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass)
978  {
979  //printf("> found it\n");
980  if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent
981  // this is preferred over nested class in this class
982  // see bug 686956
983  }
984  else if (scope==Doxygen::globalScope)
985  {
986  if (fileScope)
987  {
988  SDict<Definition> *cl = fileScope->getUsedClasses();
989  if (accessibleViaUsingClass(cl,fileScope,item))
990  {
991  //printf("> found via used class\n");
992  goto done;
993  }
994  NamespaceSDict *nl = fileScope->getUsedNamespaces();
995  if (accessibleViaUsingNamespace(nl,fileScope,item))
996  {
997  //printf("> found via used namespace\n");
998  goto done;
999  }
1000  }
1001  //printf("> reached global scope\n");
1002  result=-1; // not found in path to globalScope
1003  }
1004  else // keep searching
1005  {
1006  // check if scope is a namespace, which is using other classes and namespaces
1007  if (scope->definitionType()==Definition::TypeNamespace)
1008  {
1009  NamespaceDef *nscope = (NamespaceDef*)scope;
1010  //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
1011  SDict<Definition> *cl = nscope->getUsedClasses();
1012  if (accessibleViaUsingClass(cl,fileScope,item))
1013  {
1014  //printf("> found via used class\n");
1015  goto done;
1016  }
1017  NamespaceSDict *nl = nscope->getUsedNamespaces();
1018  if (accessibleViaUsingNamespace(nl,fileScope,item))
1019  {
1020  //printf("> found via used namespace\n");
1021  goto done;
1022  }
1023  }
1024  // repeat for the parent scope
1025  i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
1026  //printf("> result=%d\n",i);
1027  result= (i==-1) ? -1 : i+2;
1028  }
1029 done:
1030  accessStack.pop();
1031  //Doxygen::lookupCache.insert(key,new int(result));
1032  return result;
1033 }
1034 
1035 
1036 /* Returns the "distance" (=number of levels up) from item to scope, or -1
1037  * if item in not in this scope. The explicitScopePart limits the search
1038  * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
1039  * Example:
1040  *
1041  * class A { public: class I {}; };
1042  * class B { public: class J {}; };
1043  *
1044  * - Looking for item=='J' inside scope=='B' will return 0.
1045  * - Looking for item=='I' inside scope=='B' will return -1
1046  * (as it is not found in B nor in the global scope).
1047  * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
1048  * not found and then A::I is searched in the global scope, which matches and
1049  * thus the result is 1.
1050  */
1052  Definition *item,const QCString &explicitScopePart)
1053 {
1054  if (explicitScopePart.isEmpty())
1055  {
1056  // handle degenerate case where there is no explicit scope.
1057  return isAccessibleFrom(scope,fileScope,item);
1058  }
1059 
1060  static AccessStack accessStack;
1061  if (accessStack.find(scope,fileScope,item,explicitScopePart))
1062  {
1063  return -1;
1064  }
1065  accessStack.push(scope,fileScope,item,explicitScopePart);
1066 
1067 
1068  //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
1069  // item?item->name().data():"<none>",
1070  // explicitScopePart.data());
1071  int result=0; // assume we found it
1072  Definition *newScope = followPath(scope,fileScope,explicitScopePart);
1073  if (newScope) // explicitScope is inside scope => newScope is the result
1074  {
1075  Definition *itemScope = item->getOuterScope();
1076  //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
1077  //if (newScope && newScope->definitionType()==Definition::TypeClass)
1078  //{
1079  // ClassDef *cd = (ClassDef *)newScope;
1080  // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
1081  //}
1082  if (itemScope==newScope) // exact match of scopes => distance==0
1083  {
1084  //printf("> found it\n");
1085  }
1086  else if (itemScope && newScope &&
1087  itemScope->definitionType()==Definition::TypeClass &&
1088  newScope->definitionType()==Definition::TypeClass &&
1089  ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
1090  )
1091  {
1092  // inheritance is also ok. Example: looking for B::I, where
1093  // class A { public: class I {} };
1094  // class B : public A {}
1095  // but looking for B::I, where
1096  // class A { public: class I {} };
1097  // class B { public: class I {} };
1098  // will find A::I, so we still prefer a direct match and give this one a distance of 1
1099  result=1;
1100 
1101  //printf("scope(%s) is base class of newScope(%s)\n",
1102  // scope->name().data(),newScope->name().data());
1103  }
1104  else
1105  {
1106  int i=-1;
1107  if (newScope->definitionType()==Definition::TypeNamespace)
1108  {
1109  g_visitedNamespaces.insert(newScope->name(),newScope);
1110  // this part deals with the case where item is a class
1111  // A::B::C but is explicit referenced as A::C, where B is imported
1112  // in A via a using directive.
1113  //printf("newScope is a namespace: %s!\n",newScope->name().data());
1114  NamespaceDef *nscope = (NamespaceDef*)newScope;
1115  SDict<Definition> *cl = nscope->getUsedClasses();
1116  if (cl)
1117  {
1118  SDict<Definition>::Iterator cli(*cl);
1119  Definition *cd;
1120  for (cli.toFirst();(cd=cli.current());++cli)
1121  {
1122  //printf("Trying for class %s\n",cd->name().data());
1123  if (cd==item)
1124  {
1125  //printf("> class is used in this scope\n");
1126  goto done;
1127  }
1128  }
1129  }
1130  NamespaceSDict *nl = nscope->getUsedNamespaces();
1131  if (nl)
1132  {
1133  NamespaceSDict::Iterator nli(*nl);
1134  NamespaceDef *nd;
1135  for (nli.toFirst();(nd=nli.current());++nli)
1136  {
1137  if (g_visitedNamespaces.find(nd->name())==0)
1138  {
1139  //printf("Trying for namespace %s\n",nd->name().data());
1140  i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
1141  if (i!=-1)
1142  {
1143  //printf("> found via explicit scope of used namespace\n");
1144  goto done;
1145  }
1146  }
1147  }
1148  }
1149  }
1150  // repeat for the parent scope
1151  if (scope!=Doxygen::globalScope)
1152  {
1153  i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1154  item,explicitScopePart);
1155  }
1156  //printf(" | result=%d\n",i);
1157  result = (i==-1) ? -1 : i+2;
1158  }
1159  }
1160  else // failed to resolve explicitScope
1161  {
1162  //printf(" failed to resolve: scope=%s\n",scope->name().data());
1164  {
1165  NamespaceDef *nscope = (NamespaceDef*)scope;
1166  NamespaceSDict *nl = nscope->getUsedNamespaces();
1167  if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1168  {
1169  //printf("> found in used namespace\n");
1170  goto done;
1171  }
1172  }
1173  if (scope==Doxygen::globalScope)
1174  {
1175  if (fileScope)
1176  {
1177  NamespaceSDict *nl = fileScope->getUsedNamespaces();
1178  if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1179  {
1180  //printf("> found in used namespace\n");
1181  goto done;
1182  }
1183  }
1184  //printf("> not found\n");
1185  result=-1;
1186  }
1187  else // continue by looking into the parent scope
1188  {
1189  int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1190  item,explicitScopePart);
1191  //printf("> result=%d\n",i);
1192  result= (i==-1) ? -1 : i+2;
1193  }
1194  }
1195 
1196 done:
1197  //printf(" > result=%d\n",result);
1198  accessStack.pop();
1199  //Doxygen::lookupCache.insert(key,new int(result));
1200  return result;
1201 }
1202 
1203 int computeQualifiedIndex(const QCString &name)
1204 {
1205  int i = name.find('<');
1206  return name.findRev("::",i==-1 ? name.length() : i);
1207 }
1208 
1209 static void getResolvedSymbol(Definition *scope,
1210  FileDef *fileScope,
1211  Definition *d,
1212  const QCString &explicitScopePart,
1213  ArgumentList *actTemplParams,
1214  int &minDistance,
1215  ClassDef *&bestMatch,
1216  MemberDef *&bestTypedef,
1217  QCString &bestTemplSpec,
1218  QCString &bestResolvedType
1219  )
1220 {
1221  //printf(" => found type %x name=%s d=%p\n",
1222  // d->definitionType(),d->name().data(),d);
1223 
1224  // only look at classes and members that are enums or typedefs
1227  (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate())
1228  )
1229  )
1230  {
1231  g_visitedNamespaces.clear();
1232  // test accessibility of definition within scope.
1233  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
1234  //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
1235  if (distance!=-1) // definition is accessible
1236  {
1237  // see if we are dealing with a class or a typedef
1238  if (d->definitionType()==Definition::TypeClass) // d is a class
1239  {
1240  ClassDef *cd = (ClassDef *)d;
1241  //printf("cd=%s\n",cd->name().data());
1242  if (!cd->isTemplateArgument()) // skip classes that
1243  // are only there to
1244  // represent a template
1245  // argument
1246  {
1247  //printf("is not a templ arg\n");
1248  if (distance<minDistance) // found a definition that is "closer"
1249  {
1250  minDistance=distance;
1251  bestMatch = cd;
1252  bestTypedef = 0;
1253  bestTemplSpec.resize(0);
1254  bestResolvedType = cd->qualifiedName();
1255  }
1256  else if (distance==minDistance &&
1257  fileScope && bestMatch &&
1258  fileScope->getUsedNamespaces() &&
1259  d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
1260  bestMatch->getOuterScope()==Doxygen::globalScope
1261  )
1262  {
1263  // in case the distance is equal it could be that a class X
1264  // is defined in a namespace and in the global scope. When searched
1265  // in the global scope the distance is 0 in both cases. We have
1266  // to choose one of the definitions: we choose the one in the
1267  // namespace if the fileScope imports namespaces and the definition
1268  // found was in a namespace while the best match so far isn't.
1269  // Just a non-perfect heuristic but it could help in some situations
1270  // (kdecore code is an example).
1271  minDistance=distance;
1272  bestMatch = cd;
1273  bestTypedef = 0;
1274  bestTemplSpec.resize(0);
1275  bestResolvedType = cd->qualifiedName();
1276  }
1277  }
1278  else
1279  {
1280  //printf(" is a template argument!\n");
1281  }
1282  }
1283  else if (d->definitionType()==Definition::TypeMember)
1284  {
1285  MemberDef *md = (MemberDef *)d;
1286  //printf(" member isTypedef()=%d\n",md->isTypedef());
1287  if (md->isTypedef()) // d is a typedef
1288  {
1289  QCString args=md->argsString();
1290  if (args.isEmpty()) // do not expand "typedef t a[4];"
1291  {
1292  //printf(" found typedef!\n");
1293 
1294  // we found a symbol at this distance, but if it didn't
1295  // resolve to a class, we still have to make sure that
1296  // something at a greater distance does not match, since
1297  // that symbol is hidden by this one.
1298  if (distance<minDistance)
1299  {
1300  QCString spec;
1301  QCString type;
1302  minDistance=distance;
1303  MemberDef *enumType = 0;
1304  ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
1305  if (cd) // type resolves to a class
1306  {
1307  //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
1308  bestMatch = cd;
1309  bestTypedef = md;
1310  bestTemplSpec = spec;
1311  bestResolvedType = type;
1312  }
1313  else if (enumType) // type resolves to a enum
1314  {
1315  //printf(" is enum\n");
1316  bestMatch = 0;
1317  bestTypedef = enumType;
1318  bestTemplSpec = "";
1319  bestResolvedType = enumType->qualifiedName();
1320  }
1321  else if (md->isReference()) // external reference
1322  {
1323  bestMatch = 0;
1324  bestTypedef = md;
1325  bestTemplSpec = spec;
1326  bestResolvedType = type;
1327  }
1328  else
1329  {
1330  bestMatch = 0;
1331  bestTypedef = md;
1332  bestTemplSpec.resize(0);
1333  bestResolvedType.resize(0);
1334  //printf(" no match\n");
1335  }
1336  }
1337  else
1338  {
1339  //printf(" not the best match %d min=%d\n",distance,minDistance);
1340  }
1341  }
1342  else
1343  {
1344  //printf(" not a simple typedef\n")
1345  }
1346  }
1347  else if (md->isEnumerate())
1348  {
1349  if (distance<minDistance)
1350  {
1351  minDistance=distance;
1352  bestMatch = 0;
1353  bestTypedef = md;
1354  bestTemplSpec = "";
1355  bestResolvedType = md->qualifiedName();
1356  }
1357  }
1358  }
1359  } // if definition accessible
1360  else
1361  {
1362  //printf(" Not accessible!\n");
1363  }
1364  } // if definition is a class or member
1365  //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
1366 }
1367 
1368 /* Find the fully qualified class name referred to by the input class
1369  * or typedef name against the input scope.
1370  * Loops through scope and each of its parent scopes looking for a
1371  * match against the input name. Can recursively call itself when
1372  * resolving typedefs.
1373  */
1375  FileDef *fileScope,
1376  const char *n,
1377  MemberDef **pTypeDef,
1378  QCString *pTemplSpec,
1379  QCString *pResolvedType
1380  )
1381 {
1382  //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
1383  QCString name;
1384  QCString explicitScopePart;
1385  QCString strippedTemplateParams;
1387  (removeRedundantWhiteSpace(n),TRUE,
1388  &strippedTemplateParams);
1389  ArgumentList actTemplParams;
1390  if (!strippedTemplateParams.isEmpty()) // template part that was stripped
1391  {
1392  stringToArgumentList(strippedTemplateParams,&actTemplParams);
1393  }
1394 
1395  int qualifierIndex = computeQualifiedIndex(name);
1396  //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
1397  if (qualifierIndex!=-1) // qualified name
1398  {
1399  // split off the explicit scope part
1400  explicitScopePart=name.left(qualifierIndex);
1401  // todo: improve namespace alias substitution
1402  replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
1403  name=name.mid(qualifierIndex+2);
1404  }
1405 
1406  if (name.isEmpty())
1407  {
1408  //printf("] empty name\n");
1409  return 0; // empty name
1410  }
1411 
1412  //printf("Looking for symbol %s\n",name.data());
1413  DefinitionIntf *di = Doxygen::symbolMap->find(name);
1414  // the -g (for C# generics) and -p (for ObjC protocols) are now already
1415  // stripped from the key used in the symbolMap, so that is not needed here.
1416  if (di==0)
1417  {
1418  //di = Doxygen::symbolMap->find(name+"-g");
1419  //if (di==0)
1420  //{
1421  di = Doxygen::symbolMap->find(name+"-p");
1422  if (di==0)
1423  {
1424  //printf("no such symbol!\n");
1425  return 0;
1426  }
1427  //}
1428  }
1429  //printf("found symbol!\n");
1430 
1431  bool hasUsingStatements =
1432  (fileScope && ((fileScope->getUsedNamespaces() &&
1433  fileScope->getUsedNamespaces()->count()>0) ||
1434  (fileScope->getUsedClasses() &&
1435  fileScope->getUsedClasses()->count()>0))
1436  );
1437  //printf("hasUsingStatements=%d\n",hasUsingStatements);
1438  // Since it is often the case that the same name is searched in the same
1439  // scope over an over again (especially for the linked source code generation)
1440  // we use a cache to collect previous results. This is possible since the
1441  // result of a lookup is deterministic. As the key we use the concatenated
1442  // scope, the name to search for and the explicit scope prefix. The speedup
1443  // achieved by this simple cache can be enormous.
1444  int scopeNameLen = scope->name().length()+1;
1445  int nameLen = name.length()+1;
1446  int explicitPartLen = explicitScopePart.length();
1447  int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
1448 
1449  // below is a more efficient coding of
1450  // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
1451  QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
1452  char *p=key.rawData();
1453  qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
1454  p+=scopeNameLen;
1455  qstrcpy(p,name); *(p+nameLen-1)='+';
1456  p+=nameLen;
1457  qstrcpy(p,explicitScopePart);
1458  p+=explicitPartLen;
1459 
1460  // if a file scope is given and it contains using statements we should
1461  // also use the file part in the key (as a class name can be in
1462  // two different namespaces and a using statement in a file can select
1463  // one of them).
1464  if (hasUsingStatements)
1465  {
1466  // below is a more efficient coding of
1467  // key+="+"+fileScope->name();
1468  *p++='+';
1469  qstrcpy(p,fileScope->absFilePath());
1470  p+=fileScopeLen-1;
1471  }
1472  *p='\0';
1473 
1474  LookupInfo *pval=Doxygen::lookupCache->find(key);
1475  //printf("Searching for %s result=%p\n",key.data(),pval);
1476  if (pval)
1477  {
1478  //printf("LookupInfo %p %p '%s' %p\n",
1479  // pval->classDef, pval->typeDef, pval->templSpec.data(),
1480  // pval->resolvedType.data());
1481  if (pTemplSpec) *pTemplSpec=pval->templSpec;
1482  if (pTypeDef) *pTypeDef=pval->typeDef;
1483  if (pResolvedType) *pResolvedType=pval->resolvedType;
1484  //printf("] cachedMatch=%s\n",
1485  // pval->classDef?pval->classDef->name().data():"<none>");
1486  //if (pTemplSpec)
1487  // printf("templSpec=%s\n",pTemplSpec->data());
1488  return pval->classDef;
1489  }
1490  else // not found yet; we already add a 0 to avoid the possibility of
1491  // endless recursion.
1492  {
1493  Doxygen::lookupCache->insert(key,new LookupInfo);
1494  }
1495 
1496  ClassDef *bestMatch=0;
1497  MemberDef *bestTypedef=0;
1498  QCString bestTemplSpec;
1499  QCString bestResolvedType;
1500  int minDistance=10000; // init at "infinite"
1501 
1502  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
1503  {
1504  //printf(" name is not unique\n");
1506  Definition *d;
1507  int count=0;
1508  for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
1509  {
1510  getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1511  minDistance,bestMatch,bestTypedef,bestTemplSpec,
1512  bestResolvedType);
1513  }
1514  }
1515  else // unique name
1516  {
1517  //printf(" name is unique\n");
1518  Definition *d = (Definition *)di;
1519  getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1520  minDistance,bestMatch,bestTypedef,bestTemplSpec,
1521  bestResolvedType);
1522  }
1523 
1524  if (pTypeDef)
1525  {
1526  *pTypeDef = bestTypedef;
1527  }
1528  if (pTemplSpec)
1529  {
1530  *pTemplSpec = bestTemplSpec;
1531  }
1532  if (pResolvedType)
1533  {
1534  *pResolvedType = bestResolvedType;
1535  }
1536  //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
1537  // bestMatch,bestResolvedType.data());
1538 
1539  pval=Doxygen::lookupCache->find(key);
1540  if (pval)
1541  {
1542  pval->classDef = bestMatch;
1543  pval->typeDef = bestTypedef;
1544  pval->templSpec = bestTemplSpec;
1545  pval->resolvedType = bestResolvedType;
1546  }
1547  else
1548  {
1549  Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
1550  }
1551  //printf("] bestMatch=%s distance=%d\n",
1552  // bestMatch?bestMatch->name().data():"<none>",minDistance);
1553  //if (pTemplSpec)
1554  // printf("templSpec=%s\n",pTemplSpec->data());
1555  return bestMatch;
1556 }
1557 
1558 /* Find the fully qualified class name referred to by the input class
1559  * or typedef name against the input scope.
1560  * Loops through scope and each of its parent scopes looking for a
1561  * match against the input name.
1562  */
1564  FileDef *fileScope,
1565  const char *n,
1566  MemberDef **pTypeDef,
1567  QCString *pTemplSpec,
1568  bool mayBeUnlinkable,
1569  bool mayBeHidden,
1570  QCString *pResolvedType
1571  )
1572 {
1573  static bool optimizeOutputVhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1574  g_resolvedTypedefs.clear();
1575  if (scope==0 ||
1576  (scope->definitionType()!=Definition::TypeClass &&
1578  ) ||
1579  (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
1580  )
1581  {
1582  scope=Doxygen::globalScope;
1583  }
1584  //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
1585  // scope?scope->name().data():"<global>",
1586  // fileScope?fileScope->name().data():"<none>",
1587  // n,
1588  // mayBeUnlinkable
1589  // );
1590  ClassDef *result;
1591  if (optimizeOutputVhdl)
1592  {
1593  result = getClass(n);
1594  }
1595  else
1596  {
1597  result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
1598  }
1599  if (result==0) // for nested classes imported via tag files, the scope may not
1600  // present, so we check the class name directly as well.
1601  // See also bug701314
1602  {
1603  result = getClass(n);
1604  }
1605  if (!mayBeUnlinkable && result && !result->isLinkable())
1606  {
1607  if (!mayBeHidden || !result->isHidden())
1608  {
1609  //printf("result was %s\n",result?result->name().data():"<none>");
1610  result=0; // don't link to artificial/hidden classes unless explicitly allowed
1611  }
1612  }
1613  //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
1614  // n,result?result->name().data():"<none>");
1615  return result;
1616 }
1617 
1618 //-------------------------------------------------------------------------
1619 //-------------------------------------------------------------------------
1620 //-------------------------------------------------------------------------
1621 //-------------------------------------------------------------------------
1622 
1623 static bool findOperator(const QCString &s,int i)
1624 {
1625  int b = s.findRev("operator",i);
1626  if (b==-1) return FALSE; // not found
1627  b+=8;
1628  while (b<i) // check if there are only spaces in between
1629  // the operator and the >
1630  {
1631  if (!isspace((uchar)s.at(b))) return FALSE;
1632  b++;
1633  }
1634  return TRUE;
1635 }
1636 
1637 static bool findOperator2(const QCString &s,int i)
1638 {
1639  int b = s.findRev("operator",i);
1640  if (b==-1) return FALSE; // not found
1641  b+=8;
1642  while (b<i) // check if there are only non-ascii
1643  // characters in front of the operator
1644  {
1645  if (isId((uchar)s.at(b))) return FALSE;
1646  b++;
1647  }
1648  return TRUE;
1649 }
1650 
1651 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
1652 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
1653 static const char operatorScope[] = { 'o', 'p', 'e', 'r', 'a', 't', 'o', 'r', '?', '?', '?' };
1654 
1656 {
1658  {
1659  charMap['('].before=FALSE;
1660  charMap['='].before=FALSE;
1661  charMap['&'].before=FALSE;
1662  charMap['*'].before=FALSE;
1663  charMap['['].before=FALSE;
1664  charMap['|'].before=FALSE;
1665  charMap['+'].before=FALSE;
1666  charMap[';'].before=FALSE;
1667  charMap[':'].before=FALSE;
1668  charMap['/'].before=FALSE;
1669 
1670  charMap['='].after=FALSE;
1671  charMap[' '].after=FALSE;
1672  charMap[']'].after=FALSE;
1673  charMap['\t'].after=FALSE;
1674  charMap['\n'].after=FALSE;
1675  charMap[')'].after=FALSE;
1676  charMap[','].after=FALSE;
1677  charMap['<'].after=FALSE;
1678  charMap['|'].after=FALSE;
1679  charMap['+'].after=FALSE;
1680  charMap['('].after=FALSE;
1681  charMap['/'].after=FALSE;
1682  }
1683  struct CharElem
1684  {
1685  CharElem() : before(TRUE), after(TRUE) {}
1686  bool before;
1687  bool after;
1688  };
1689 
1691 };
1692 
1694 
1695 // Note: this function is not reentrant due to the use of static buffer!
1696 QCString removeRedundantWhiteSpace(const QCString &s)
1697 {
1698  static bool cliSupport = Config_getBool(CPP_CLI_SUPPORT);
1699  static bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1700 
1701  if (s.isEmpty() || vhdl) return s;
1702 
1703  // We use a static character array to
1704  // improve the performance of this function
1705  static char *growBuf = 0;
1706  static int growBufLen = 0;
1707  if (s.length()*3>growBufLen) // For input character we produce at most 3 output characters,
1708  {
1709  growBufLen = s.length()*3;
1710  growBuf = (char *)realloc(growBuf,growBufLen+1); // add 1 for 0-terminator
1711  }
1712  if (growBuf==0) return s; // should not happen, only we run out of memory
1713 
1714  char *src=s.rawData();
1715  char *dst=growBuf;
1716 
1717  uint i=0;
1718  uint l=s.length();
1719  uint csp=0;
1720  uint vsp=0;
1721  uint osp=0;
1722  char c;
1723  char pc=0;
1724  // skip leading whitespace
1725  while (i<l && isspace((uchar)src[i]))
1726  {
1727  i++;
1728  }
1729  for (;i<l;i++)
1730  {
1731  c=src[i];
1732  char nc=i<l-1 ? src[i+1] : ' ';
1733 
1734  // search for "const"
1735  if (csp<6 && c==constScope[csp] && // character matches substring "const"
1736  (csp>0 || // inside search string
1737  i==0 || // if it is the first character
1738  !isId(pc) // the previous may not be a digit
1739  )
1740  )
1741  csp++;
1742  else // reset counter
1743  csp=0;
1744 
1745  // search for "virtual"
1746  if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
1747  (vsp>0 || // inside search string
1748  i==0 || // if it is the first character
1749  !isId(pc) // the previous may not be a digit
1750  )
1751  )
1752  vsp++;
1753  else // reset counter
1754  vsp=0;
1755 
1756  // search for "operator"
1757  if (osp<11 && (osp>=8 || c==operatorScope[osp]) && // character matches substring "operator" followed by 3 arbitrary characters
1758  (osp>0 || // inside search string
1759  i==0 || // if it is the first character
1760  !isId(pc) // the previous may not be a digit
1761  )
1762  )
1763  osp++;
1764  else // reset counter
1765  osp=0;
1766 
1767  switch(c)
1768  {
1769  case '"': // quoted string
1770  {
1771  *dst++=c;
1772  pc = c;
1773  i++;
1774  for (;i<l;i++) // find end of string
1775  {
1776  c = src[i];
1777  *dst++=c;
1778  if (c=='\\' && i+1<l)
1779  {
1780  pc = c;
1781  i++;
1782  c = src[i];
1783  *dst+=c;
1784  }
1785  else if (c=='"')
1786  {
1787  break;
1788  }
1789  pc = c;
1790  }
1791  }
1792  break;
1793  case '<': // current char is a <
1794  *dst++=c;
1795  if (i<l-1 &&
1796  (isId(nc)) && // next char is an id char
1797  (osp<8) // string in front is not "operator"
1798  )
1799  {
1800  *dst++=' '; // add extra space
1801  }
1802  break;
1803  case '>': // current char is a >
1804  if (i>0 && !isspace((uchar)pc) &&
1805  (isId(pc) || pc=='*' || pc=='&' || pc=='.') && // prev char is an id char or space or *&.
1806  (osp<8 || (osp==8 && pc!='-')) // string in front is not "operator>" or "operator->"
1807  )
1808  {
1809  *dst++=' '; // add extra space in front
1810  }
1811  *dst++=c;
1812  if (i<l-1 && (nc=='-' || nc=='&')) // '>-' -> '> -'
1813  {
1814  *dst++=' '; // add extra space after
1815  }
1816  break;
1817  case ',': // current char is a ,
1818  *dst++=c;
1819  if (i>0 && !isspace((uchar)pc) &&
1820  ((i<l-1 && (isId(nc) || nc=='[')) || // the [ is for attributes (see bug702170)
1821  (i<l-2 && nc=='$' && isId(src[i+2])) || // for PHP: ',$name' -> ', $name'
1822  (i<l-3 && nc=='&' && src[i+2]=='$' && isId(src[i+3])) // for PHP: ',&$name' -> ', &$name'
1823  )
1824  )
1825  {
1826  *dst++=' '; // add extra space after
1827  }
1828  break;
1829  case '^': // CLI 'Type^name' -> 'Type^ name'
1830  case '%': // CLI 'Type%name' -> 'Type% name'
1831  *dst++=c;
1832  if (cliSupport && i<l-1 && (isId(nc) || nc=='-'))
1833  {
1834  *dst++=' '; // add extra space after
1835  }
1836  break;
1837  case ')': // current char is a ) -> ')name' -> ') name'
1838  *dst++=c;
1839  if (i<l-1 && (isId(nc) || nc=='-'))
1840  {
1841  *dst++=' '; // add extra space after
1842  }
1843  break;
1844  case '*':
1845  if (i>0 && pc!=' ' && pc!='\t' && pc!=':' &&
1846  pc!='*' && pc!='&' && pc!='(' && pc!='/' &&
1847  pc!='.' && (osp<9 || (pc=='>' && osp==11)))
1848  // avoid splitting &&, **, .*, operator*, operator->*
1849  {
1850  *dst++=' ';
1851  }
1852  *dst++=c;
1853  break;
1854  case '&':
1855  if (i>0 && isId(pc))
1856  {
1857  *dst++=' ';
1858  }
1859  *dst++=c;
1860  break;
1861  case '@': // '@name' -> ' @name'
1862  case '$': // '$name' -> ' $name'
1863  case '\'': // ''name' -> '' name'
1864  if (i>0 && i<l-1 && pc!='=' && pc!=':' && !isspace(pc) &&
1865  isId(nc) && osp<8) // ")id" -> ") id"
1866  {
1867  *dst++=' ';
1868  }
1869  *dst++=c;
1870  break;
1871  case ':': // current char is a :
1872  if (csp==6) // replace const::A by const ::A
1873  {
1874  *dst++=' ';
1875  csp=0;
1876  }
1877  else if (vsp==8) // replace virtual::A by virtual ::A
1878  {
1879  *dst++=' ';
1880  vsp=0;
1881  }
1882  *dst++=c;
1883  break;
1884  case ' ': // fallthrough
1885  case '\n': // fallthrough
1886  case '\t':
1887  {
1888  if (g_charAroundSpace.charMap[(uchar)pc].before &&
1889  g_charAroundSpace.charMap[(uchar)nc].after &&
1890  !(pc==',' && nc=='.'))
1891  // remove spaces/tabs
1892  {
1893  *dst++=' ';
1894  }
1895  }
1896  break;
1897  default:
1898  *dst++=c;
1899  if (c=='t' && csp==5 && i<l-1 && // found 't' in 'const'
1900  !(isId(nc) || nc==')' || nc==',' || isspace(nc))
1901  ) // prevent const ::A from being converted to const::A
1902  {
1903  *dst++=' ';
1904  csp=0;
1905  }
1906  else if (c=='l' && vsp==7 && i<l-1 && // found 'l' in 'virtual'
1907  !(isId(nc) || nc==')' || nc==',' || isspace(nc))
1908  ) // prevent virtual ::A from being converted to virtual::A
1909  {
1910  *dst++=' ';
1911  vsp=0;
1912  }
1913  break;
1914  }
1915  pc=c;
1916  }
1917  *dst++='\0';
1918  return growBuf;
1919 }
1920 
1925 int findParameterList(const QCString &name)
1926 {
1927  int pos=-1;
1928  int templateDepth=0;
1929  do
1930  {
1931  if (templateDepth > 0)
1932  {
1933  int nextOpenPos=name.findRev('>', pos);
1934  int nextClosePos=name.findRev('<', pos);
1935  if (nextOpenPos!=-1 && nextOpenPos>nextClosePos)
1936  {
1937  ++templateDepth;
1938  pos=nextOpenPos-1;
1939  }
1940  else if (nextClosePos!=-1)
1941  {
1942  --templateDepth;
1943  pos=nextClosePos-1;
1944  }
1945  else // more >'s than <'s, see bug701295
1946  {
1947  return -1;
1948  }
1949  }
1950  else
1951  {
1952  int lastAnglePos=name.findRev('>', pos);
1953  int bracePos=name.findRev('(', pos);
1954  if (lastAnglePos!=-1 && lastAnglePos>bracePos)
1955  {
1956  ++templateDepth;
1957  pos=lastAnglePos-1;
1958  }
1959  else
1960  {
1961  int bp = bracePos>0 ? name.findRev('(',bracePos-1) : -1;
1962  // bp test is to allow foo(int(&)[10]), but we need to make an exception for operator()
1963  return bp==-1 || (bp>=8 && name.mid(bp-8,10)=="operator()") ? bracePos : bp;
1964  }
1965  }
1966  } while (pos!=-1);
1967  return -1;
1968 }
1969 
1970 bool rightScopeMatch(const QCString &scope, const QCString &name)
1971 {
1972  int sl=scope.length();
1973  int nl=name.length();
1974  return (name==scope || // equal
1975  (scope.right(nl)==name && // substring
1976  sl-nl>1 && scope.at(sl-nl-1)==':' && scope.at(sl-nl-2)==':' // scope
1977  )
1978  );
1979 }
1980 
1981 bool leftScopeMatch(const QCString &scope, const QCString &name)
1982 {
1983  int sl=scope.length();
1984  int nl=name.length();
1985  return (name==scope || // equal
1986  (scope.left(nl)==name && // substring
1987  sl>nl+1 && scope.at(nl)==':' && scope.at(nl+1)==':' // scope
1988  )
1989  );
1990 }
1991 
1992 
1994  FileDef *fileScope,Definition *self,
1995  const char *text, bool autoBreak,bool external,
1996  bool keepSpaces,int indentLevel)
1997 {
1998  //printf("linkify=`%s'\n",text);
1999  static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*");
2000  static QRegExp regExpSplit("(?!:),");
2001  QCString txtStr=text;
2002  int strLen = txtStr.length();
2003  //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n",
2004  // scope?scope->name().data():"<none>",
2005  // fileScope?fileScope->name().data():"<none>",
2006  // txtStr.data(),strLen,external);
2007  int matchLen;
2008  int index=0;
2009  int newIndex;
2010  int skipIndex=0;
2011  int floatingIndex=0;
2012  if (strLen==0) return;
2013  // read a word from the text string
2014  while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 &&
2015  (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
2016  )
2017  {
2018  // add non-word part to the result
2019  floatingIndex+=newIndex-skipIndex+matchLen;
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  ClassDef *cd=0;
2064  FileDef *fd=0;
2065  MemberDef *md=0;
2066  NamespaceDef *nd=0;
2067  GroupDef *gd=0;
2068  //printf("** Match word '%s'\n",matchWord.data());
2069 
2070  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 &&
2132  (scope->definitionType()==Definition::TypeClass ||
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  out.writeLink(md->getReference(),md->getOutputFileBase(),
2162  md->anchor(),word);
2163  //printf("found symbol %s\n",matchWord.data());
2164  found=TRUE;
2165  }
2166  }
2167  }
2168 
2169  if (!found) // add word to the result
2170  {
2171  out.writeString(word,keepSpaces);
2172  }
2173  // set next start point in the string
2174  //printf("index=%d/%d\n",index,txtStr.length());
2175  skipIndex=index=newIndex+matchLen;
2176  }
2177  // add last part of the string to the result.
2178  //ol.docify(txtStr.right(txtStr.length()-skipIndex));
2179  out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
2180 }
2181 
2182 
2184 {
2185  QCString exampleLine=theTranslator->trWriteList(ed->count());
2186 
2187  //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
2188  //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
2189  //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
2190  QRegExp marker("@[0-9]+");
2191  int index=0,newIndex,matchLen;
2192  // now replace all markers in inheritLine with links to the classes
2193  while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
2194  {
2195  bool ok;
2196  ol.parseText(exampleLine.mid(index,newIndex-index));
2197  uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
2198  Example *e=ed->at(entryIndex);
2199  if (ok && e)
2200  {
2201  ol.pushGeneratorState();
2202  //if (latexEnabled) ol.disable(OutputGenerator::Latex);
2205  // link for Html / man
2206  //printf("writeObjectLink(file=%s)\n",e->file.data());
2207  ol.writeObjectLink(0,e->file,e->anchor,e->name);
2208  ol.popGeneratorState();
2209 
2210  ol.pushGeneratorState();
2211  //if (latexEnabled) ol.enable(OutputGenerator::Latex);
2214  // link for Latex / pdf with anchor because the sources
2215  // are not hyperlinked (not possible with a verbatim environment).
2216  ol.writeObjectLink(0,e->file,0,e->name);
2217  //if (manEnabled) ol.enable(OutputGenerator::Man);
2218  //if (htmlEnabled) ol.enable(OutputGenerator::Html);
2219  ol.popGeneratorState();
2220  }
2221  index=newIndex+matchLen;
2222  }
2223  ol.parseText(exampleLine.right(exampleLine.length()-index));
2224  ol.writeString(".");
2225 }
2226 
2227 
2228 QCString argListToString(ArgumentList *al,bool useCanonicalType,bool showDefVals)
2229 {
2230  QCString result;
2231  if (al==0) return result;
2232  ArgumentListIterator ali(*al);
2233  Argument *a=ali.current();
2234  result+="(";
2235  while (a)
2236  {
2237  QCString type1 = useCanonicalType && !a->canType.isEmpty() ?
2238  a->canType : a->type;
2239  QCString type2;
2240  int i=type1.find(")("); // hack to deal with function pointers
2241  if (i!=-1)
2242  {
2243  type2=type1.mid(i);
2244  type1=type1.left(i);
2245  }
2246  if (!a->attrib.isEmpty())
2247  {
2248  result+=a->attrib+" ";
2249  }
2250  if (!a->name.isEmpty() || !a->array.isEmpty())
2251  {
2252  result+= type1+" "+a->name+type2+a->array;
2253  }
2254  else
2255  {
2256  result+= type1+type2;
2257  }
2258  if (!a->defval.isEmpty() && showDefVals)
2259  {
2260  result+="="+a->defval;
2261  }
2262  ++ali;
2263  a = ali.current();
2264  if (a) result+=", ";
2265  }
2266  result+=")";
2267  if (al->constSpecifier) result+=" const";
2268  if (al->volatileSpecifier) result+=" volatile";
2269  if (!al->trailingReturnType.isEmpty()) result+=" -> "+al->trailingReturnType;
2270  if (al->pureSpecifier) result+=" =0";
2271  return removeRedundantWhiteSpace(result);
2272 }
2273 
2275 {
2276  QCString result;
2277  if (al==0) return result;
2278  result="<";
2279  ArgumentListIterator ali(*al);
2280  Argument *a=ali.current();
2281  while (a)
2282  {
2283  if (!a->name.isEmpty()) // add template argument name
2284  {
2285  if (a->type.left(4)=="out") // C# covariance
2286  {
2287  result+="out ";
2288  }
2289  else if (a->type.left(3)=="in") // C# contravariance
2290  {
2291  result+="in ";
2292  }
2293  if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2294  {
2295  result+=a->type+" ";
2296  }
2297  result+=a->name;
2298  }
2299  else // extract name from type
2300  {
2301  int i=a->type.length()-1;
2302  while (i>=0 && isId(a->type.at(i))) i--;
2303  if (i>0)
2304  {
2305  result+=a->type.right(a->type.length()-i-1);
2306  if (a->type.find("...")!=-1)
2307  {
2308  result+="...";
2309  }
2310  }
2311  else // nothing found -> take whole name
2312  {
2313  result+=a->type;
2314  }
2315  }
2316  if (!a->typeConstraint.isEmpty() && lang==SrcLangExt_Java)
2317  {
2318  result+=" extends "; // TODO: now Java specific, C# has where...
2319  result+=a->typeConstraint;
2320  }
2321  ++ali;
2322  a=ali.current();
2323  if (a) result+=", ";
2324  }
2325  result+=">";
2326  return removeRedundantWhiteSpace(result);
2327 }
2328 
2329 
2330 // compute the HTML anchors for a list of members
2332 {
2333  //int count=0;
2334  if (ml==0) return;
2335  MemberListIterator mli(*ml);
2336  MemberDef *md;
2337  for (;(md=mli.current());++mli)
2338  {
2339  if (!md->isReference())
2340  {
2341  //QCString anchor;
2342  //if (groupId==-1)
2343  // anchor.sprintf("%c%d",id,count++);
2344  //else
2345  // anchor.sprintf("%c%d_%d",id,groupId,count++);
2346  //if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
2347  md->setAnchor();
2348  //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
2349  // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
2350  }
2351  }
2352 }
2353 
2354 //----------------------------------------------------------------------------
2355 
2361 int filterCRLF(char *buf,int len)
2362 {
2363  int src = 0; // source index
2364  int dest = 0; // destination index
2365  char c; // current character
2366 
2367  while (src<len)
2368  {
2369  c = buf[src++]; // Remember the processed character.
2370  if (c == '\r') // CR to be solved (MAC, DOS)
2371  {
2372  c = '\n'; // each CR to LF
2373  if (src<len && buf[src] == '\n')
2374  ++src; // skip LF just after CR (DOS)
2375  }
2376  else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
2377  {
2378  c = ' '; // turn into a space
2379  }
2380  buf[dest++] = c; // copy the (modified) character to dest
2381  }
2382  return dest; // length of the valid part of the buf
2383 }
2384 
2385 static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
2386 {
2387  found=FALSE;
2388  // compare the file name to the filter pattern list
2389  QStrListIterator sli(filterList);
2390  char* filterStr;
2391  for (sli.toFirst(); (filterStr = sli.current()); ++sli)
2392  {
2393  QCString fs = filterStr;
2394  int i_equals=fs.find('=');
2395  if (i_equals!=-1)
2396  {
2397  QCString filterPattern = fs.left(i_equals);
2398  QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE);
2399  if (fpat.match(name)!=-1)
2400  {
2401  // found a match!
2402  QCString filterName = fs.mid(i_equals+1);
2403  if (filterName.find(' ')!=-1)
2404  { // add quotes if the name has spaces
2405  filterName="\""+filterName+"\"";
2406  }
2407  found=TRUE;
2408  return filterName;
2409  }
2410  }
2411  }
2412 
2413  // no match
2414  return "";
2415 }
2416 
2422 QCString getFileFilter(const char* name,bool isSourceCode)
2423 {
2424  // sanity check
2425  if (name==0) return "";
2426 
2427  QStrList& filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS);
2428  QStrList& filterList = Config_getList(FILTER_PATTERNS);
2429 
2430  QCString filterName;
2431  bool found=FALSE;
2432  if (isSourceCode && !filterSrcList.isEmpty())
2433  { // first look for source filter pattern list
2434  filterName = getFilterFromList(name,filterSrcList,found);
2435  }
2436  if (!found && filterName.isEmpty())
2437  { // then look for filter pattern list
2438  filterName = getFilterFromList(name,filterList,found);
2439  }
2440  if (!found)
2441  { // then use the generic input filter
2442  return Config_getString(INPUT_FILTER);
2443  }
2444  else
2445  {
2446  /* remove surrounding double quotes */
2447  if ((filterName.right(1) == "\"") && (filterName.left(1) == "\""))
2448  {
2449  filterName.remove(filterName.length() - 1, 1);
2450  filterName.remove(0, 1);
2451  }
2452  return filterName;
2453  }
2454 }
2455 
2456 
2457 QCString transcodeCharacterStringToUTF8(const QCString &input)
2458 {
2459  bool error=FALSE;
2460  static QCString inputEncoding = Config_getString(INPUT_ENCODING);
2461  const char *outputEncoding = "UTF-8";
2462  if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
2463  int inputSize=input.length();
2464  int outputSize=inputSize*4+1;
2465  QCString output(outputSize);
2466  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
2467  if (cd==(void *)(-1))
2468  {
2469  err("unsupported character conversion: '%s'->'%s'\n",
2470  inputEncoding.data(),outputEncoding);
2471  error=TRUE;
2472  }
2473  if (!error)
2474  {
2475  size_t iLeft=inputSize;
2476  size_t oLeft=outputSize;
2477  char *inputPtr = input.rawData();
2478  char *outputPtr = output.rawData();
2479  if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
2480  {
2481  outputSize-=(int)oLeft;
2482  output.resize(outputSize+1);
2483  output.at(outputSize)='\0';
2484  //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
2485  }
2486  else
2487  {
2488  err("failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
2489  inputEncoding.data(),outputEncoding,input.data());
2490  error=TRUE;
2491  }
2492  }
2494  return error ? input : output;
2495 }
2496 
2501 QCString fileToString(const char *name,bool filter,bool isSourceCode)
2502 {
2503  if (name==0 || name[0]==0) return 0;
2504  QFile f;
2505 
2506  bool fileOpened=FALSE;
2507  if (name[0]=='-' && name[1]==0) // read from stdin
2508  {
2509  fileOpened=f.open(IO_ReadOnly,stdin);
2510  if (fileOpened)
2511  {
2512  const int bSize=4096;
2513  QCString contents(bSize);
2514  int totalSize=0;
2515  int size;
2516  while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize)
2517  {
2518  totalSize+=bSize;
2519  contents.resize(totalSize+bSize);
2520  }
2521  totalSize = filterCRLF(contents.rawData(),totalSize+size)+2;
2522  contents.resize(totalSize);
2523  contents.at(totalSize-2)='\n'; // to help the scanner
2524  contents.at(totalSize-1)='\0';
2525  return contents;
2526  }
2527  }
2528  else // read from file
2529  {
2530  QFileInfo fi(name);
2531  if (!fi.exists() || !fi.isFile())
2532  {
2533  err("file `%s' not found\n",name);
2534  return "";
2535  }
2536  BufStr buf(fi.size());
2537  fileOpened=readInputFile(name,buf,filter,isSourceCode);
2538  if (fileOpened)
2539  {
2540  int s = buf.size();
2541  if (s>1 && buf.at(s-2)!='\n')
2542  {
2543  buf.at(s-1)='\n';
2544  buf.addChar(0);
2545  }
2546  return buf.data();
2547  }
2548  }
2549  if (!fileOpened)
2550  {
2551  err("cannot open file `%s' for reading\n",name);
2552  }
2553  return "";
2554 }
2555 
2556 QCString dateToString(bool includeTime)
2557 {
2558  QDateTime current = QDateTime::currentDateTime();
2559  QCString sourceDateEpoch = portable_getenv("SOURCE_DATE_EPOCH");
2560  if (!sourceDateEpoch.isEmpty())
2561  {
2562  bool ok;
2563  uint64 epoch = sourceDateEpoch.toUInt64(&ok);
2564  if (!ok)
2565  {
2566  static bool warnedOnce=FALSE;
2567  if (!warnedOnce)
2568  {
2569  warn_uncond("Environment variable SOURCE_DATE_EPOCH does not contain a valid number; value is '%s'\n",
2570  sourceDateEpoch.data());
2571  warnedOnce=TRUE;
2572  }
2573  }
2574  else if (epoch>UINT_MAX)
2575  {
2576  static bool warnedOnce=FALSE;
2577  if (!warnedOnce)
2578  {
2579  warn_uncond("Environment variable SOURCE_DATA_EPOCH must have a value smaller than or equal to %llu; actual value %llu\n",UINT_MAX,epoch);
2580  warnedOnce=TRUE;
2581  }
2582  }
2583  else // all ok, replace current time with epoch value
2584  {
2585  current.setTimeUtc_t((ulong)epoch); // TODO: add support for 64bit epoch value
2586  }
2587  }
2588  return theTranslator->trDateTime(current.date().year(),
2589  current.date().month(),
2590  current.date().day(),
2591  current.date().dayOfWeek(),
2592  current.time().hour(),
2593  current.time().minute(),
2594  current.time().second(),
2595  includeTime);
2596 }
2597 
2598 QCString yearToString()
2599 {
2600  const QDate &d=QDate::currentDate();
2601  QCString result;
2602  result.sprintf("%d", d.year());
2603  return result;
2604 }
2605 
2606 //----------------------------------------------------------------------
2607 // recursive function that returns the number of branches in the
2608 // inheritance tree that the base class `bcd' is below the class `cd'
2609 
2610 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
2611 {
2612  if (bcd->categoryOf()) // use class that is being extended in case of
2613  // an Objective-C category
2614  {
2615  bcd=bcd->categoryOf();
2616  }
2617  if (cd==bcd) return level;
2618  if (level==256)
2619  {
2620  warn_uncond("class %s seem to have a recursive "
2621  "inheritance relation!\n",cd->name().data());
2622  return -1;
2623  }
2624  int m=maxInheritanceDepth;
2625  if (cd->baseClasses())
2626  {
2627  BaseClassListIterator bcli(*cd->baseClasses());
2628  BaseClassDef *bcdi;
2629  for (;(bcdi=bcli.current());++bcli)
2630  {
2631  int mc=minClassDistance(bcdi->classDef,bcd,level+1);
2632  if (mc<m) m=mc;
2633  if (m<0) break;
2634  }
2635  }
2636  return m;
2637 }
2638 
2640 {
2641  if (bcd->categoryOf()) // use class that is being extended in case of
2642  // an Objective-C category
2643  {
2644  bcd=bcd->categoryOf();
2645  }
2646  if (cd==bcd)
2647  {
2648  goto exit;
2649  }
2650  if (level==256)
2651  {
2652  err("Internal inconsistency: found class %s seem to have a recursive "
2653  "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
2654  }
2655  else if (cd->baseClasses())
2656  {
2657  BaseClassListIterator bcli(*cd->baseClasses());
2658  BaseClassDef *bcdi;
2659  for (;(bcdi=bcli.current()) && prot!=Private;++bcli)
2660  {
2661  Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1);
2662  if (baseProt==Private) prot=Private;
2663  else if (baseProt==Protected) prot=Protected;
2664  }
2665  }
2666 exit:
2667  //printf("classInheritedProtectionLevel(%s,%s)=%d\n",cd->name().data(),bcd->name().data(),prot);
2668  return prot;
2669 }
2670 
2671 //static void printArgList(ArgumentList *al)
2672 //{
2673 // if (al==0) return;
2674 // ArgumentListIterator ali(*al);
2675 // Argument *a;
2676 // printf("(");
2677 // for (;(a=ali.current());++ali)
2678 // {
2679 // printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():"");
2680 // }
2681 // printf(")");
2682 //}
2683 
2684 #ifndef NEWMATCH
2685 // strip any template specifiers that follow className in string s
2686 static QCString trimTemplateSpecifiers(
2687  const QCString &namespaceName,
2688  const QCString &className,
2689  const QCString &s
2690  )
2691 {
2692  //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
2693  QCString scopeName=mergeScopes(namespaceName,className);
2694  ClassDef *cd=getClass(scopeName);
2695  if (cd==0) return s; // should not happen, but guard anyway.
2696 
2697  QCString result=s;
2698 
2699  int i=className.length()-1;
2700  if (i>=0 && className.at(i)=='>') // template specialization
2701  {
2702  // replace unspecialized occurrences in s, with their specialized versions.
2703  int count=1;
2704  int cl=i+1;
2705  while (i>=0)
2706  {
2707  char c=className.at(i);
2708  if (c=='>') count++,i--;
2709  else if (c=='<') { count--; if (count==0) break; }
2710  else i--;
2711  }
2712  QCString unspecClassName=className.left(i);
2713  int l=i;
2714  int p=0;
2715  while ((i=result.find(unspecClassName,p))!=-1)
2716  {
2717  if (result.at(i+l)!='<') // unspecialized version
2718  {
2719  result=result.left(i)+className+result.right(result.length()-i-l);
2720  l=cl;
2721  }
2722  p=i+l;
2723  }
2724  }
2725 
2726  //printf("result after specialization: %s\n",result.data());
2727 
2728  QCString qualName=cd->qualifiedNameWithTemplateParameters();
2729  //printf("QualifiedName = %s\n",qualName.data());
2730  // We strip the template arguments following className (if any)
2731  if (!qualName.isEmpty()) // there is a class name
2732  {
2733  int is,ps=0;
2734  int p=0,l,i;
2735 
2736  while ((is=getScopeFragment(qualName,ps,&l))!=-1)
2737  {
2738  QCString qualNamePart = qualName.right(qualName.length()-is);
2739  //printf("qualNamePart=%s\n",qualNamePart.data());
2740  while ((i=result.find(qualNamePart,p))!=-1)
2741  {
2742  int ql=qualNamePart.length();
2743  result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
2744  p=i+cd->name().length();
2745  }
2746  ps=is+l;
2747  }
2748  }
2749  //printf("result=%s\n",result.data());
2750 
2751  return result.stripWhiteSpace();
2752 }
2753 
2761 static int findScopePattern(const QCString &pattern,const QCString &s,
2762  int p,int *len)
2763 {
2764  int sl=s.length();
2765  int pl=pattern.length();
2766  int sp=0;
2767  *len=0;
2768  while (p<sl)
2769  {
2770  sp=p; // start of match
2771  int pp=0; // pattern position
2772  while (p<sl && pp<pl)
2773  {
2774  if (s.at(p)=='<') // skip template arguments while matching
2775  {
2776  int bc=1;
2777  //printf("skipping pos=%d c=%c\n",p,s.at(p));
2778  p++;
2779  while (p<sl)
2780  {
2781  if (s.at(p)=='<') bc++;
2782  else if (s.at(p)=='>')
2783  {
2784  bc--;
2785  if (bc==0)
2786  {
2787  p++;
2788  break;
2789  }
2790  }
2791  //printf("skipping pos=%d c=%c\n",p,s.at(p));
2792  p++;
2793  }
2794  }
2795  else if (s.at(p)==pattern.at(pp))
2796  {
2797  //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
2798  p++;
2799  pp++;
2800  }
2801  else // no match
2802  {
2803  //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
2804  p=sp+1;
2805  break;
2806  }
2807  }
2808  if (pp==pl) // whole pattern matches
2809  {
2810  *len=p-sp;
2811  return sp;
2812  }
2813  }
2814  return -1;
2815 }
2816 
2817 static QCString trimScope(const QCString &name,const QCString &s)
2818 {
2819  int scopeOffset=name.length();
2820  QCString result=s;
2821  do // for each scope
2822  {
2823  QCString tmp;
2824  QCString scope=name.left(scopeOffset)+"::";
2825  //printf("Trying with scope=`%s'\n",scope.data());
2826 
2827  int i,p=0,l;
2828  while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
2829  {
2830  tmp+=result.mid(p,i-p); // add part before pattern
2831  p=i+l;
2832  }
2833  tmp+=result.right(result.length()-p); // add trailing part
2834 
2835  scopeOffset=name.findRev("::",scopeOffset-1);
2836  result = tmp;
2837  } while (scopeOffset>0);
2838  //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
2839  return result;
2840 }
2841 #endif
2842 
2843 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
2844 {
2845  //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
2846  BaseClassListIterator bcli(*bcl);
2847  BaseClassDef *bcd;
2848  for (;(bcd=bcli.current());++bcli)
2849  {
2850  ClassDef *cd=bcd->classDef;
2851  //printf("Trying class %s\n",cd->name().data());
2852  int spos=s.find(cd->name()+"::");
2853  if (spos!=-1)
2854  {
2855  s = s.left(spos)+s.right(
2856  s.length()-spos-cd->name().length()-2
2857  );
2858  }
2859  //printf("base class `%s'\n",cd->name().data());
2860  if (cd->baseClasses())
2861  trimBaseClassScope(cd->baseClasses(),s,level+1);
2862  }
2863 }
2864 
2865 #if 0
2866 
2870 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
2871 {
2872  int p1=t1.length();
2873  int p2=t2.length();
2874  for (;;)
2875  {
2876  int i1=p1==0 ? -1 : t1.findRev("::",p1);
2877  int i2=p2==0 ? -1 : t2.findRev("::",p2);
2878  if (i1==-1 && i2==-1)
2879  {
2880  return;
2881  }
2882  if (i1!=-1 && i2==-1) // only t1 has a scope
2883  {
2884  QCString scope=t1.left(i1);
2885  replaceNamespaceAliases(scope,i1);
2886 
2887  int so=nsName.length();
2888  do
2889  {
2890  QCString fullScope=nsName.left(so);
2891  if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2892  fullScope+=scope;
2893  if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2894  {
2895  t1 = t1.right(t1.length()-i1-2);
2896  return;
2897  }
2898  if (so==0)
2899  {
2900  so=-1;
2901  }
2902  else if ((so=nsName.findRev("::",so-1))==-1)
2903  {
2904  so=0;
2905  }
2906  }
2907  while (so>=0);
2908  }
2909  else if (i1==-1 && i2!=-1) // only t2 has a scope
2910  {
2911  QCString scope=t2.left(i2);
2912  replaceNamespaceAliases(scope,i2);
2913 
2914  int so=nsName.length();
2915  do
2916  {
2917  QCString fullScope=nsName.left(so);
2918  if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2919  fullScope+=scope;
2920  if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2921  {
2922  t2 = t2.right(t2.length()-i2-2);
2923  return;
2924  }
2925  if (so==0)
2926  {
2927  so=-1;
2928  }
2929  else if ((so=nsName.findRev("::",so-1))==-1)
2930  {
2931  so=0;
2932  }
2933  }
2934  while (so>=0);
2935  }
2936  p1 = QMAX(i1-2,0);
2937  p2 = QMAX(i2-2,0);
2938  }
2939 }
2940 #endif
2941 
2942 static void stripIrrelevantString(QCString &target,const QCString &str)
2943 {
2944  if (target==str) { target.resize(0); return; }
2945  int i,p=0;
2946  int l=str.length();
2947  bool changed=FALSE;
2948  while ((i=target.find(str,p))!=-1)
2949  {
2950  bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
2951  (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
2952  if (isMatch)
2953  {
2954  int i1=target.find('*',i+l);
2955  int i2=target.find('&',i+l);
2956  if (i1==-1 && i2==-1)
2957  {
2958  // strip str from target at index i
2959  target=target.left(i)+target.right(target.length()-i-l);
2960  changed=TRUE;
2961  i-=l;
2962  }
2963  else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
2964  {
2965  // move str to front
2966  target=str+" "+target.left(i)+target.right(target.length()-i-l);
2967  changed=TRUE;
2968  i++;
2969  }
2970  }
2971  p = i+l;
2972  }
2973  if (changed) target=target.stripWhiteSpace();
2974 }
2975 
2994 {
2995  //printf("stripIrrelevantConstVolatile(%s)=",s.data());
2996  stripIrrelevantString(s,"const");
2997  stripIrrelevantString(s,"volatile");
2998  stripIrrelevantString(s,"final");
2999  //printf("%s\n",s.data());
3000 }
3001 
3002 
3003 // a bit of debug support for matchArguments
3004 #define MATCH
3005 #define NOMATCH
3006 //#define MATCH printf("Match at line %d\n",__LINE__);
3007 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
3008 
3009 #ifndef NEWMATCH
3010 static bool matchArgument(const Argument *srcA,const Argument *dstA,
3011  const QCString &className,
3012  const QCString &namespaceName,
3013  NamespaceSDict *usingNamespaces,
3014  SDict<Definition> *usingClasses)
3015 {
3016  //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
3017  // srcA->type.data(),srcA->name.data(),
3018  // dstA->type.data(),dstA->name.data(),
3019  // usingNamespaces,
3020  // usingClasses);
3021 
3022  // TODO: resolve any typedefs names that are part of srcA->type
3023  // before matching. This should use className and namespaceName
3024  // and usingNamespaces and usingClass to determine which typedefs
3025  // are in-scope, so it will not be very efficient :-(
3026 
3027  QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
3028  QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
3029  QCString srcAName=srcA->name.stripWhiteSpace();
3030  QCString dstAName=dstA->name.stripWhiteSpace();
3031  srcAType.stripPrefix("class ");
3032  dstAType.stripPrefix("class ");
3033 
3034  // allow distinguishing "const A" from "const B" even though
3035  // from a syntactic point of view they would be two names of the same
3036  // type "const". This is not fool prove of course, but should at least
3037  // catch the most common cases.
3038  if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
3039  {
3040  srcAType+=" ";
3041  srcAType+=srcAName;
3042  }
3043  if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
3044  {
3045  dstAType+=" ";
3046  dstAType+=dstAName;
3047  }
3048  if (srcAName=="const" || srcAName=="volatile")
3049  {
3050  srcAType+=srcAName;
3051  srcAName.resize(0);
3052  }
3053  else if (dstA->name=="const" || dstA->name=="volatile")
3054  {
3055  dstAType+=dstA->name;
3056  dstAName.resize(0);
3057  }
3058 
3059  stripIrrelevantConstVolatile(srcAType);
3060  stripIrrelevantConstVolatile(dstAType);
3061 
3062  // strip typename keyword
3063  if (qstrncmp(srcAType,"typename ",9)==0)
3064  {
3065  srcAType = srcAType.right(srcAType.length()-9);
3066  }
3067  if (qstrncmp(dstAType,"typename ",9)==0)
3068  {
3069  dstAType = dstAType.right(dstAType.length()-9);
3070  }
3071 
3072  srcAType = removeRedundantWhiteSpace(srcAType);
3073  dstAType = removeRedundantWhiteSpace(dstAType);
3074 
3075  //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
3076  //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
3077 
3078  //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
3079  // dstAType.data(),dstAName.data());
3080 
3081  if (srcA->array!=dstA->array) // nomatch for char[] against char
3082  {
3083  NOMATCH
3084  return FALSE;
3085  }
3086  if (srcAType!=dstAType) // check if the argument only differs on name
3087  {
3088 
3089  // remove a namespace scope that is only in one type
3090  // (assuming a using statement was used)
3091  //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
3092  //trimNamespaceScope(srcAType,dstAType,namespaceName);
3093  //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
3094 
3095  //QCString srcScope;
3096  //QCString dstScope;
3097 
3098  // strip redundant scope specifiers
3099  if (!className.isEmpty())
3100  {
3101  srcAType=trimScope(className,srcAType);
3102  dstAType=trimScope(className,dstAType);
3103  //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
3104  ClassDef *cd;
3105  if (!namespaceName.isEmpty())
3106  cd=getClass(namespaceName+"::"+className);
3107  else
3108  cd=getClass(className);
3109  if (cd && cd->baseClasses())
3110  {
3111  trimBaseClassScope(cd->baseClasses(),srcAType);
3112  trimBaseClassScope(cd->baseClasses(),dstAType);
3113  }
3114  //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
3115  }
3116  if (!namespaceName.isEmpty())
3117  {
3118  srcAType=trimScope(namespaceName,srcAType);
3119  dstAType=trimScope(namespaceName,dstAType);
3120  }
3121  //printf("#usingNamespace=%d\n",usingNamespaces->count());
3122  if (usingNamespaces && usingNamespaces->count()>0)
3123  {
3124  NamespaceSDict::Iterator nli(*usingNamespaces);
3125  NamespaceDef *nd;
3126  for (;(nd=nli.current());++nli)
3127  {
3128  srcAType=trimScope(nd->name(),srcAType);
3129  dstAType=trimScope(nd->name(),dstAType);
3130  }
3131  }
3132  //printf("#usingClasses=%d\n",usingClasses->count());
3133  if (usingClasses && usingClasses->count()>0)
3134  {
3135  SDict<Definition>::Iterator cli(*usingClasses);
3136  Definition *cd;
3137  for (;(cd=cli.current());++cli)
3138  {
3139  srcAType=trimScope(cd->name(),srcAType);
3140  dstAType=trimScope(cd->name(),dstAType);
3141  }
3142  }
3143 
3144  //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
3145  // dstAType.data(),dstAName.data());
3146 
3147  if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
3148  (srcAType+" "+srcAName)==dstAType)
3149  {
3150  MATCH
3151  return TRUE;
3152  }
3153  else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
3154  (dstAType+" "+dstAName)==srcAType)
3155  {
3156  MATCH
3157  return TRUE;
3158  }
3159 
3160 
3161  uint srcPos=0,dstPos=0;
3162  bool equal=TRUE;
3163  while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
3164  {
3165  equal=srcAType.at(srcPos)==dstAType.at(dstPos);
3166  if (equal) srcPos++,dstPos++;
3167  }
3168  uint srcATypeLen=srcAType.length();
3169  uint dstATypeLen=dstAType.length();
3170  if (srcPos<srcATypeLen && dstPos<dstATypeLen)
3171  {
3172  // if nothing matches or the match ends in the middle or at the
3173  // end of a string then there is no match
3174  if (srcPos==0 || dstPos==0)
3175  {
3176  NOMATCH
3177  return FALSE;
3178  }
3179  if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
3180  {
3181  //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
3182  // check if a name if already found -> if no then there is no match
3183  if (!srcAName.isEmpty() || !dstAName.isEmpty())
3184  {
3185  NOMATCH
3186  return FALSE;
3187  }
3188  // types only
3189  while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3190  while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3191  if (srcPos<srcATypeLen ||
3192  dstPos<dstATypeLen ||
3193  (srcPos==srcATypeLen && dstPos==dstATypeLen)
3194  )
3195  {
3196  NOMATCH
3197  return FALSE;
3198  }
3199  }
3200  else
3201  {
3202  // otherwise we assume that a name starts at the current position.
3203  while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3204  while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3205 
3206  // if nothing more follows for both types then we assume we have
3207  // found a match. Note that now `signed int' and `signed' match, but
3208  // seeing that int is not a name can only be done by looking at the
3209  // semantics.
3210 
3211  if (srcPos!=srcATypeLen || dstPos!=dstATypeLen)
3212  {
3213  NOMATCH
3214  return FALSE;
3215  }
3216  }
3217  }
3218  else if (dstPos<dstAType.length())
3219  {
3220  if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
3221  {
3222  if (!dstAName.isEmpty()) // dst has its name separated from its type
3223  {
3224  NOMATCH
3225  return FALSE;
3226  }
3227  while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3228  if (dstPos!=dstAType.length())
3229  {
3230  NOMATCH
3231  return FALSE; // more than a difference in name -> no match
3232  }
3233  }
3234  else // maybe dst has a name while src has not
3235  {
3236  dstPos++;
3237  while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3238  if (dstPos!=dstAType.length() || !srcAName.isEmpty())
3239  {
3240  NOMATCH
3241  return FALSE; // nope not a name -> no match
3242  }
3243  }
3244  }
3245  else if (srcPos<srcAType.length())
3246  {
3247  if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
3248  {
3249  if (!srcAName.isEmpty()) // src has its name separated from its type
3250  {
3251  NOMATCH
3252  return FALSE;
3253  }
3254  while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3255  if (srcPos!=srcAType.length())
3256  {
3257  NOMATCH
3258  return FALSE; // more than a difference in name -> no match
3259  }
3260  }
3261  else // maybe src has a name while dst has not
3262  {
3263  srcPos++;
3264  while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3265  if (srcPos!=srcAType.length() || !dstAName.isEmpty())
3266  {
3267  NOMATCH
3268  return FALSE; // nope not a name -> no match
3269  }
3270  }
3271  }
3272  }
3273  MATCH
3274  return TRUE;
3275 }
3276 
3277 
3286  const char *cl,const char *ns,bool checkCV,
3287  NamespaceSDict *usingNamespaces,
3288  SDict<Definition> *usingClasses)
3289 {
3290  QCString className=cl;
3291  QCString namespaceName=ns;
3292 
3293  // strip template specialization from class name if present
3294  //int til=className.find('<'),tir=className.find('>');
3295  //if (til!=-1 && tir!=-1 && tir>til)
3296  //{
3297  // className=className.left(til)+className.right(className.length()-tir-1);
3298  //}
3299 
3300  //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
3301  // srcAl ? argListToString(srcAl).data() : "",
3302  // dstAl ? argListToString(dstAl).data() : "",
3303  // cl,ns,checkCV,
3304  // usingNamespaces?usingNamespaces->count():0,
3305  // usingClasses?usingClasses->count():0
3306  // );
3307 
3308  if (srcAl==0 || dstAl==0)
3309  {
3310  bool match = srcAl==dstAl; // at least one of the members is not a function
3311  if (match)
3312  {
3313  MATCH
3314  return TRUE;
3315  }
3316  else
3317  {
3318  NOMATCH
3319  return FALSE;
3320  }
3321  }
3322 
3323  // handle special case with void argument
3324  if ( srcAl->count()==0 && dstAl->count()==1 &&
3325  dstAl->getFirst()->type=="void" )
3326  { // special case for finding match between func() and func(void)
3327  Argument *a=new Argument;
3328  a->type = "void";
3329  srcAl->append(a);
3330  MATCH
3331  return TRUE;
3332  }
3333  if ( dstAl->count()==0 && srcAl->count()==1 &&
3334  srcAl->getFirst()->type=="void" )
3335  { // special case for finding match between func(void) and func()
3336  Argument *a=new Argument;
3337  a->type = "void";
3338  dstAl->append(a);
3339  MATCH
3340  return TRUE;
3341  }
3342 
3343  if (srcAl->count() != dstAl->count())
3344  {
3345  NOMATCH
3346  return FALSE; // different number of arguments -> no match
3347  }
3348 
3349  if (checkCV)
3350  {
3351  if (srcAl->constSpecifier != dstAl->constSpecifier)
3352  {
3353  NOMATCH
3354  return FALSE; // one member is const, the other not -> no match
3355  }
3356  if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3357  {
3358  NOMATCH
3359  return FALSE; // one member is volatile, the other not -> no match
3360  }
3361  }
3362 
3363  // so far the argument list could match, so we need to compare the types of
3364  // all arguments.
3365  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3366  Argument *srcA,*dstA;
3367  for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3368  {
3369  if (!matchArgument(srcA,dstA,className,namespaceName,
3370  usingNamespaces,usingClasses))
3371  {
3372  NOMATCH
3373  return FALSE;
3374  }
3375  }
3376  MATCH
3377  return TRUE; // all arguments match
3378 }
3379 
3380 #endif
3381 
3382 #if 0
3383 static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
3384 {
3385  ASSERT(symbol!=0);
3386  if (symbol->definitionType()==Definition::TypeMember &&
3387  ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
3388  // to resolve it
3389  {
3390  MemberDef *md = 0;
3391  ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
3392  if (cd)
3393  {
3394  return cd->qualifiedName()+templSpec;
3395  }
3396  else if (md)
3397  {
3398  return md->qualifiedName();
3399  }
3400  }
3401  return symbol->qualifiedName();
3402 }
3403 #endif
3404 
3405 static QCString stripDeclKeywords(const QCString &s)
3406 {
3407  int i=s.find(" class ");
3408  if (i!=-1) return s.left(i)+s.mid(i+6);
3409  i=s.find(" typename ");
3410  if (i!=-1) return s.left(i)+s.mid(i+9);
3411  i=s.find(" union ");
3412  if (i!=-1) return s.left(i)+s.mid(i+6);
3413  i=s.find(" struct ");
3414  if (i!=-1) return s.left(i)+s.mid(i+7);
3415  return s;
3416 }
3417 
3418 // forward decl for circular dependencies
3419 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
3420 
3421 QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
3422 {
3423 
3424  QCString templSpec = spec.stripWhiteSpace();
3425  // this part had been commented out before... but it is needed to match for instance
3426  // std::list<std::string> against list<string> so it is now back again!
3427  if (!templSpec.isEmpty() && templSpec.at(0) == '<')
3428  {
3429  templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
3430  }
3431  QCString resolvedType = resolveTypeDef(d,templSpec);
3432  if (!resolvedType.isEmpty()) // not known as a typedef either
3433  {
3434  templSpec = resolvedType;
3435  }
3436  //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
3437  return templSpec;
3438 }
3439 
3440 
3442  Definition *d,FileDef *fs,const QCString &word,
3443  QCString *tSpec,int count=0)
3444 {
3445  if (count>10) return word; // oops recursion
3446 
3447  QCString symName,result,templSpec,tmpName;
3448  //DefinitionList *defList=0;
3449  if (tSpec && !tSpec->isEmpty())
3450  templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
3451 
3452  if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
3453  {
3454  symName=tmpName; // name without scope
3455  }
3456  else
3457  {
3458  symName=word;
3459  }
3460  //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
3461  // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
3462 
3463  ClassDef *cd = 0;
3464  MemberDef *mType = 0;
3465  QCString ts;
3466  QCString resolvedType;
3467 
3468  // lookup class / class template instance
3469  cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
3470  bool isTemplInst = cd && !templSpec.isEmpty();
3471  if (!cd && !templSpec.isEmpty())
3472  {
3473  // class template specialization not known, look up class template
3474  cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
3475  }
3476  if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
3477 
3478  //printf("cd=%p mtype=%p\n",cd,mType);
3479  //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
3480  // symName.data(),
3481  // word.data(),
3482  // cd?cd->name().data():"<none>",
3483  // d?d->name().data():"<none>",
3484  // fs?fs->name().data():"<none>",
3485  // cd?cd->isTemplate():-1
3486  // );
3487 
3488  //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
3489  // (word+templSpec).data(),
3490  // cd?cd->qualifiedName().data():"<none>",
3491  // templSpec.data(),ts.data(),
3492  // tSpec?tSpec->data():"<null>",
3493  // cd?cd->isTemplate():FALSE,
3494  // resolvedType.data());
3495 
3496  //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
3497 
3498  if (cd) // resolves to a known class type
3499  {
3500  if (cd==d && tSpec) *tSpec="";
3501 
3502  if (mType && mType->isTypedef()) // but via a typedef
3503  {
3504  result = resolvedType+ts; // the +ts was added for bug 685125
3505  }
3506  else
3507  {
3508  if (isTemplInst)
3509  {
3510  // spec is already part of class type
3511  templSpec="";
3512  if (tSpec) *tSpec="";
3513  }
3514  else if (!ts.isEmpty() && templSpec.isEmpty())
3515  {
3516  // use formal template args for spec
3517  templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
3518  }
3519 
3520  result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
3521 
3522  if (cd->isTemplate() && tSpec) //
3523  {
3524  if (!templSpec.isEmpty()) // specific instance
3525  {
3526  result=cd->name()+templSpec;
3527  }
3528  else // use template type
3529  {
3531  }
3532  // template class, so remove the template part (it is part of the class name)
3533  *tSpec="";
3534  }
3535  else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
3536  {
3537  // obscure case, where a class is used as a template, but doxygen think it is
3538  // not (could happen when loading the class from a tag file).
3539  *tSpec="";
3540  }
3541  }
3542  }
3543  else if (mType && mType->isEnumerate()) // an enum
3544  {
3545  result = mType->qualifiedName();
3546  }
3547  else if (mType && mType->isTypedef()) // a typedef
3548  {
3549  //result = mType->qualifiedName(); // changed after 1.7.2
3550  //result = mType->typeString();
3551  //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
3552  if (word!=mType->typeString())
3553  {
3554  result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
3555  }
3556  else
3557  {
3558  result = mType->typeString();
3559  }
3560  }
3561  else // fallback
3562  {
3563  resolvedType = resolveTypeDef(d,word);
3564  //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
3565  if (resolvedType.isEmpty()) // not known as a typedef either
3566  {
3567  result = word;
3568  }
3569  else
3570  {
3571  result = resolvedType;
3572  }
3573  }
3574  //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
3575  return result;
3576 }
3577 
3578 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
3579 {
3580  type = type.stripWhiteSpace();
3581 
3582  // strip const and volatile keywords that are not relevant for the type
3584 
3585  // strip leading keywords
3586  type.stripPrefix("class ");
3587  type.stripPrefix("struct ");
3588  type.stripPrefix("union ");
3589  type.stripPrefix("enum ");
3590  type.stripPrefix("typename ");
3591 
3592  type = removeRedundantWhiteSpace(type);
3593  //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
3594  // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
3595 
3596  //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
3597 
3598  QCString canType;
3599  QCString templSpec,word;
3600  int i,p=0,pp=0;
3601  while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
3602  // foreach identifier in the type
3603  {
3604  //printf(" i=%d p=%d\n",i,p);
3605  if (i>pp) canType += type.mid(pp,i-pp);
3606 
3607 
3608  QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
3609 
3610  // in case the ct is empty it means that "word" represents scope "d"
3611  // and this does not need to be added to the canonical
3612  // type (it is redundant), so/ we skip it. This solves problem 589616.
3613  if (ct.isEmpty() && type.mid(p,2)=="::")
3614  {
3615  p+=2;
3616  }
3617  else
3618  {
3619  canType += ct;
3620  }
3621  //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
3622  // word.data(),templSpec.data(),canType.data(),ct.data());
3623  if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
3624  // (i.e. type is not a template specialization)
3625  // then resolve any identifiers inside.
3626  {
3627  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
3628  int tp=0,tl,ti;
3629  // for each identifier template specifier
3630  //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
3631  while ((ti=re.match(templSpec,tp,&tl))!=-1)
3632  {
3633  canType += templSpec.mid(tp,ti-tp);
3634  canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
3635  tp=ti+tl;
3636  }
3637  canType+=templSpec.right(templSpec.length()-tp);
3638  }
3639 
3640  pp=p;
3641  }
3642  canType += type.right(type.length()-pp);
3643  //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
3644 
3645  return removeRedundantWhiteSpace(canType);
3646 }
3647 
3648 static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
3649 {
3650  QCString type = arg->type.stripWhiteSpace();
3651  QCString name = arg->name;
3652  //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
3653  if ((type=="const" || type=="volatile") && !name.isEmpty())
3654  { // name is part of type => correct
3655  type+=" ";
3656  type+=name;
3657  }
3658  if (name=="const" || name=="volatile")
3659  { // name is part of type => correct
3660  if (!type.isEmpty()) type+=" ";
3661  type+=name;
3662  }
3663  if (!arg->array.isEmpty())
3664  {
3665  type+=arg->array;
3666  }
3667 
3668  return extractCanonicalType(d,fs,type);
3669 }
3670 
3671 static bool matchArgument2(
3672  Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
3673  Definition *dstScope,FileDef *dstFileScope,Argument *dstA
3674  )
3675 {
3676  //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
3677  // srcScope ? srcScope->name().data() : "",
3678  // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
3679  // dstScope ? dstScope->name().data() : "",
3680  // dstA->type.data(),dstA->name.data(),dstA->canType.data());
3681 
3682  //if (srcA->array!=dstA->array) // nomatch for char[] against char
3683  //{
3684  // NOMATCH
3685  // return FALSE;
3686  //}
3687  QCString sSrcName = " "+srcA->name;
3688  QCString sDstName = " "+dstA->name;
3689  QCString srcType = srcA->type;
3690  QCString dstType = dstA->type;
3693  //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
3694  //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
3695  if (sSrcName==dstType.right(sSrcName.length()))
3696  { // case "unsigned int" <-> "unsigned int i"
3697  srcA->type+=sSrcName;
3698  srcA->name="";
3699  srcA->canType=""; // invalidate cached type value
3700  }
3701  else if (sDstName==srcType.right(sDstName.length()))
3702  { // case "unsigned int i" <-> "unsigned int"
3703  dstA->type+=sDstName;
3704  dstA->name="";
3705  dstA->canType=""; // invalidate cached type value
3706  }
3707 
3708  if (srcA->canType.isEmpty())
3709  {
3710  srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
3711  }
3712  if (dstA->canType.isEmpty())
3713  {
3714  dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
3715  }
3716 
3717  if (srcA->canType==dstA->canType)
3718  {
3719  MATCH
3720  return TRUE;
3721  }
3722  else
3723  {
3724  //printf(" Canonical types do not match [%s]<->[%s]\n",
3725  // srcA->canType.data(),dstA->canType.data());
3726  NOMATCH
3727  return FALSE;
3728  }
3729 }
3730 
3731 
3732 // new algorithm for argument matching
3733 bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
3734  Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
3735  bool checkCV
3736  )
3737 {
3738  //printf("*** matchArguments2\n");
3739  ASSERT(srcScope!=0 && dstScope!=0);
3740 
3741  if (srcAl==0 || dstAl==0)
3742  {
3743  bool match = srcAl==dstAl; // at least one of the members is not a function
3744  if (match)
3745  {
3746  MATCH
3747  return TRUE;
3748  }
3749  else
3750  {
3751  NOMATCH
3752  return FALSE;
3753  }
3754  }
3755 
3756  // handle special case with void argument
3757  if ( srcAl->count()==0 && dstAl->count()==1 &&
3758  dstAl->getFirst()->type=="void" )
3759  { // special case for finding match between func() and func(void)
3760  Argument *a=new Argument;
3761  a->type = "void";
3762  srcAl->append(a);
3763  MATCH
3764  return TRUE;
3765  }
3766  if ( dstAl->count()==0 && srcAl->count()==1 &&
3767  srcAl->getFirst()->type=="void" )
3768  { // special case for finding match between func(void) and func()
3769  Argument *a=new Argument;
3770  a->type = "void";
3771  dstAl->append(a);
3772  MATCH
3773  return TRUE;
3774  }
3775 
3776  if (srcAl->count() != dstAl->count())
3777  {
3778  NOMATCH
3779  return FALSE; // different number of arguments -> no match
3780  }
3781 
3782  if (checkCV)
3783  {
3784  if (srcAl->constSpecifier != dstAl->constSpecifier)
3785  {
3786  NOMATCH
3787  return FALSE; // one member is const, the other not -> no match
3788  }
3789  if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3790  {
3791  NOMATCH
3792  return FALSE; // one member is volatile, the other not -> no match
3793  }
3794  }
3795 
3796  // so far the argument list could match, so we need to compare the types of
3797  // all arguments.
3798  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3799  Argument *srcA,*dstA;
3800  for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3801  {
3802  if (!matchArgument2(srcScope,srcFileScope,srcA,
3803  dstScope,dstFileScope,dstA)
3804  )
3805  {
3806  NOMATCH
3807  return FALSE;
3808  }
3809  }
3810  MATCH
3811  return TRUE; // all arguments match
3812 }
3813 
3814 
3815 
3816 // merges the initializer of two argument lists
3817 // pre: the types of the arguments in the list should match.
3818 void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
3819 {
3820  //printf("mergeArguments `%s', `%s'\n",
3821  // argListToString(srcAl).data(),argListToString(dstAl).data());
3822 
3823  if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
3824  {
3825  return; // invalid argument lists -> do not merge
3826  }
3827 
3828  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3829  Argument *srcA,*dstA;
3830  for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3831  {
3832  if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
3833  {
3834  //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
3835  srcA->defval=dstA->defval.copy();
3836  }
3837  else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
3838  {
3839  //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
3840  dstA->defval=srcA->defval.copy();
3841  }
3842 
3843  // fix wrongly detected const or volatile specifiers before merging.
3844  // example: "const A *const" is detected as type="const A *" name="const"
3845  if (srcA->name=="const" || srcA->name=="volatile")
3846  {
3847  srcA->type+=" "+srcA->name;
3848  srcA->name.resize(0);
3849  }
3850  if (dstA->name=="const" || dstA->name=="volatile")
3851  {
3852  dstA->type+=" "+dstA->name;
3853  dstA->name.resize(0);
3854  }
3855 
3856  if (srcA->type==dstA->type)
3857  {
3858  //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3859  if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3860  {
3861  //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3862  //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
3863  srcA->type = dstA->type.copy();
3864  srcA->name = dstA->name.copy();
3865  }
3866  else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
3867  {
3868  //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3869  //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3870  dstA->type = srcA->type.copy();
3871  dstA->name = dstA->name.copy();
3872  }
3873  else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
3874  {
3875  //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
3876  if (forceNameOverwrite)
3877  {
3878  srcA->name = dstA->name;
3879  }
3880  else
3881  {
3882  if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3883  {
3884  srcA->name = dstA->name;
3885  }
3886  else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
3887  {
3888  dstA->name = srcA->name;
3889  }
3890  }
3891  }
3892  }
3893  else
3894  {
3895  //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3896  srcA->type=srcA->type.stripWhiteSpace();
3897  dstA->type=dstA->type.stripWhiteSpace();
3898  if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
3899  {
3900  srcA->type+=" "+srcA->name;
3901  srcA->name=dstA->name;
3902  }
3903  else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
3904  {
3905  dstA->type+=" "+dstA->name;
3906  dstA->name=srcA->name;
3907  }
3908  else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3909  {
3910  srcA->name = dstA->name;
3911  }
3912  else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
3913  {
3914  dstA->name = srcA->name;
3915  }
3916  }
3917  int i1=srcA->type.find("::"),
3918  i2=dstA->type.find("::"),
3919  j1=srcA->type.length()-i1-2,
3920  j2=dstA->type.length()-i2-2;
3921  if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
3922  {
3923  //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3924  //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3925  dstA->type = srcA->type.left(i1+2)+dstA->type;
3926  dstA->name = dstA->name.copy();
3927  }
3928  else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
3929  {
3930  //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3931  //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3932  srcA->type = dstA->type.left(i2+2)+srcA->type;
3933  srcA->name = dstA->name.copy();
3934  }
3935  if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3936  {
3937  srcA->docs = dstA->docs.copy();
3938  }
3939  else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
3940  {
3941  dstA->docs = srcA->docs.copy();
3942  }
3943  //printf("Merge argument `%s|%s' `%s|%s'\n",
3944  // srcA->type.data(),srcA->name.data(),
3945  // dstA->type.data(),dstA->name.data());
3946  }
3947 }
3948 
3950  const char *args,
3951  bool checkStatics,
3952  FileDef *currentFile,
3953  bool checkCV,
3954  const char *forceTagFile,
3955  QList<MemberDef> &members)
3956 {
3957  //printf(" Function with global scope name `%s' args=`%s'\n",
3958  // mn->memberName(),args);
3959  MemberNameIterator mli(*mn);
3960  MemberDef *md;
3961  for (mli.toFirst();(md=mli.current());++mli)
3962  {
3963  FileDef *fd=md->getFileDef();
3964  GroupDef *gd=md->getGroupDef();
3965  //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p current=%p ref=%s\n",
3966  // md->name().data(),args,fd,gd,currentFile,md->getReference().data());
3967  if (
3968  ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
3969  md->getNamespaceDef()==0 && md->isLinkable() &&
3970  (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
3971  currentFile==0 || fd==currentFile) // statics must appear in the same file
3972  )
3973  {
3974  bool match=TRUE;
3975  ArgumentList *argList=0;
3976  if (args && !md->isDefine() && qstrcmp(args,"()")!=0)
3977  {
3978  argList=new ArgumentList;
3979  ArgumentList *mdAl = md->argumentList();
3980  stringToArgumentList(args,argList);
3981  match=matchArguments2(
3982  md->getOuterScope(),fd,mdAl,
3983  Doxygen::globalScope,fd,argList,
3984  checkCV);
3985  delete argList; argList=0;
3986  }
3987  if (match && (forceTagFile==0 || md->getReference()==forceTagFile))
3988  {
3989  //printf("Found match!\n");
3990  members.append(md);
3991  }
3992  }
3993  }
3994 }
3995 
4018 bool getDefs(const QCString &scName,
4019  const QCString &mbName,
4020  const char *args,
4021  MemberDef *&md,
4022  ClassDef *&cd,
4023  FileDef *&fd,
4024  NamespaceDef *&nd,
4025  GroupDef *&gd,
4026  bool forceEmptyScope,
4027  FileDef *currentFile,
4028  bool checkCV,
4029  const char *forceTagFile
4030  )
4031 {
4032  fd=0, md=0, cd=0, nd=0, gd=0;
4033  if (mbName.isEmpty()) return FALSE; /* empty name => nothing to link */
4034 
4035  QCString scopeName=scName;
4036  QCString memberName=mbName;
4037  scopeName = substitute(scopeName,"\\","::"); // for PHP
4038  memberName = substitute(memberName,"\\","::"); // for PHP
4039  //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
4040  // memberName.data(),args,scopeName.data(),forceEmptyScope);
4041 
4042  int is,im=0,pm=0;
4043  // strip common part of the scope from the scopeName
4044  while ((is=scopeName.findRev("::"))!=-1 &&
4045  (im=memberName.find("::",pm))!=-1 &&
4046  (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
4047  )
4048  {
4049  scopeName=scopeName.left(is);
4050  pm=im+2;
4051  }
4052  //printf("result after scope corrections scope=%s name=%s\n",
4053  // scopeName.data(),memberName.data());
4054 
4055  QCString mName=memberName;
4056  QCString mScope;
4057  if (memberName.left(9)!="operator " && // treat operator conversion methods
4058  // as a special case
4059  (im=memberName.findRev("::"))!=-1 &&
4060  im<(int)memberName.length()-2 // not A::
4061  )
4062  {
4063  mScope=memberName.left(im);
4064  mName=memberName.right(memberName.length()-im-2);
4065  }
4066 
4067  // handle special the case where both scope name and member scope are equal
4068  if (mScope==scopeName) scopeName.resize(0);
4069 
4070  //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
4071 
4073  //printf("mName=%s mn=%p\n",mName.data(),mn);
4074 
4075  if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
4076  mn && !(scopeName.isEmpty() && mScope.isEmpty()))
4077  {
4078  //printf(" >member name '%s' found\n",mName.data());
4079  int scopeOffset=scopeName.length();
4080  do
4081  {
4082  QCString className = scopeName.left(scopeOffset);
4083  if (!className.isEmpty() && !mScope.isEmpty())
4084  {
4085  className+="::"+mScope;
4086  }
4087  else if (!mScope.isEmpty())
4088  {
4089  className=mScope;
4090  }
4091 
4092  MemberDef *tmd=0;
4093  ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className,&tmd);
4094  if (fcd==0 && className.find('<')!=-1) // try without template specifiers as well
4095  {
4096  QCString nameWithoutTemplates = stripTemplateSpecifiersFromScope(className,FALSE);
4097  fcd=getResolvedClass(Doxygen::globalScope,0,nameWithoutTemplates,&tmd);
4098  }
4099  //printf("Trying class scope %s: fcd=%p tmd=%p\n",className.data(),fcd,tmd);
4100  // todo: fill in correct fileScope!
4101  if (fcd && // is it a documented class
4102  fcd->isLinkable()
4103  )
4104  {
4105  //printf(" Found fcd=%p\n",fcd);
4106  MemberNameIterator mmli(*mn);
4107  MemberDef *mmd;
4108  int mdist=maxInheritanceDepth;
4109  ArgumentList *argList=0;
4110  if (args)
4111  {
4112  argList=new ArgumentList;
4113  stringToArgumentList(args,argList);
4114  }
4115  for (mmli.toFirst();(mmd=mmli.current());++mmli)
4116  {
4117  if (!mmd->isStrongEnumValue())
4118  {
4119  ArgumentList *mmdAl = mmd->argumentList();
4120  bool match=args==0 ||
4121  matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4122  fcd,fcd->getFileDef(),argList,
4123  checkCV
4124  );
4125  //printf("match=%d\n",match);
4126  if (match)
4127  {
4128  ClassDef *mcd=mmd->getClassDef();
4129  if (mcd)
4130  {
4131  int m=minClassDistance(fcd,mcd);
4132  if (m<mdist && mcd->isLinkable())
4133  {
4134  mdist=m;
4135  cd=mcd;
4136  md=mmd;
4137  }
4138  }
4139  }
4140  }
4141  }
4142  if (argList)
4143  {
4144  delete argList; argList=0;
4145  }
4146  if (mdist==maxInheritanceDepth && args && qstrcmp(args,"()")==0)
4147  // no exact match found, but if args="()" an arbitrary member will do
4148  {
4149  //printf(" >Searching for arbitrary member\n");
4150  for (mmli.toFirst();(mmd=mmli.current());++mmli)
4151  {
4152  //if (mmd->isLinkable())
4153  //{
4154  ClassDef *mcd=mmd->getClassDef();
4155  //printf(" >Class %s found\n",mcd->name().data());
4156  if (mcd)
4157  {
4158  int m=minClassDistance(fcd,mcd);
4159  if (m<mdist /* && mcd->isLinkable()*/ )
4160  {
4161  //printf("Class distance %d\n",m);
4162  mdist=m;
4163  cd=mcd;
4164  md=mmd;
4165  }
4166  }
4167  //}
4168  }
4169  }
4170  //printf(" >Succes=%d\n",mdist<maxInheritanceDepth);
4171  if (mdist<maxInheritanceDepth)
4172  {
4173  if (!md->isLinkable() || md->isStrongEnumValue())
4174  {
4175  md=0; // avoid returning things we cannot link to
4176  cd=0;
4177  return FALSE; // match found, but was not linkable
4178  }
4179  else
4180  {
4181  gd=md->getGroupDef();
4182  if (gd) cd=0;
4183  return TRUE; /* found match */
4184  }
4185  }
4186  }
4187  if (tmd && tmd->isEnumerate() && tmd->isStrong()) // scoped enum
4188  {
4189  //printf("Found scoped enum!\n");
4190  MemberList *tml = tmd->enumFieldList();
4191  if (tml)
4192  {
4193  MemberListIterator tmi(*tml);
4194  MemberDef *emd;
4195  for (;(emd=tmi.current());++tmi)
4196  {
4197  if (emd->localName()==mName)
4198  {
4199  if (emd->isLinkable())
4200  {
4201  cd=tmd->getClassDef();
4202  md=emd;
4203  return TRUE;
4204  }
4205  else
4206  {
4207  cd=0;
4208  md=0;
4209  return FALSE;
4210  }
4211  }
4212  }
4213  }
4214  }
4215  /* go to the parent scope */
4216  if (scopeOffset==0)
4217  {
4218  scopeOffset=-1;
4219  }
4220  else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4221  {
4222  scopeOffset=0;
4223  }
4224  } while (scopeOffset>=0);
4225 
4226  }
4227  if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
4228  {
4229  //printf("Global symbol\n");
4230  MemberNameIterator mmli(*mn);
4231  MemberDef *mmd, *fuzzy_mmd = 0;
4232  ArgumentList *argList = 0;
4233  bool hasEmptyArgs = args && qstrcmp(args, "()") == 0;
4234 
4235  if (args)
4236  stringToArgumentList(args, argList = new ArgumentList);
4237 
4238  for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
4239  {
4240  if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
4241  !mmd->getClassDef())
4242  continue;
4243 
4244  if (!args) break;
4245 
4246  ArgumentList *mmdAl = mmd->argumentList();
4247  if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4248  Doxygen::globalScope,mmd->getFileDef(),argList,
4249  checkCV
4250  )
4251  ) break;
4252 
4253  if (!fuzzy_mmd && hasEmptyArgs)
4254  fuzzy_mmd = mmd;
4255  }
4256 
4257  if (argList) delete argList, argList = 0;
4258 
4259  mmd = mmd ? mmd : fuzzy_mmd;
4260 
4261  if (mmd && !mmd->isStrongEnumValue())
4262  {
4263  md = mmd;
4264  cd = mmd->getClassDef();
4265  return TRUE;
4266  }
4267  }
4268 
4269 
4270  // maybe an namespace, file or group member ?
4271  //printf("Testing for global symbol scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
4272  // scopeName.data(),mScope.data(),mName.data());
4273  if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
4274  {
4275  //printf(" >symbol name found\n");
4276  NamespaceDef *fnd=0;
4277  int scopeOffset=scopeName.length();
4278  do
4279  {
4280  QCString namespaceName = scopeName.left(scopeOffset);
4281  if (!namespaceName.isEmpty() && !mScope.isEmpty())
4282  {
4283  namespaceName+="::"+mScope;
4284  }
4285  else if (!mScope.isEmpty())
4286  {
4287  namespaceName=mScope.copy();
4288  }
4289  //printf("Trying namespace %s\n",namespaceName.data());
4290  if (!namespaceName.isEmpty() &&
4291  (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
4292  fnd->isLinkable()
4293  )
4294  {
4295  //printf("Symbol inside existing namespace `%s' count=%d\n",
4296  // namespaceName.data(),mn->count());
4297  bool found=FALSE;
4298  MemberNameIterator mmli(*mn);
4299  MemberDef *mmd;
4300  for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4301  {
4302  //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
4303  // mmd->getNamespaceDef(),fnd);
4304  MemberDef *emd = mmd->getEnumScope();
4305  if (emd && emd->isStrong())
4306  {
4307  //printf("yes match %s<->%s!\n",mScope.data(),emd->localName().data());
4308  if (emd->getNamespaceDef()==fnd &&
4309  rightScopeMatch(mScope,emd->localName()))
4310  {
4311  //printf("found it!\n");
4312  nd=fnd;
4313  md=mmd;
4314  found=TRUE;
4315  }
4316  else
4317  {
4318  md=0;
4319  cd=0;
4320  return FALSE;
4321  }
4322  }
4323  else if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
4324  { // namespace is found
4325  bool match=TRUE;
4326  ArgumentList *argList=0;
4327  if (args && qstrcmp(args,"()")!=0)
4328  {
4329  argList=new ArgumentList;
4330  ArgumentList *mmdAl = mmd->argumentList();
4331  stringToArgumentList(args,argList);
4332  match=matchArguments2(
4333  mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4334  fnd,mmd->getFileDef(),argList,
4335  checkCV);
4336  }
4337  if (match)
4338  {
4339  nd=fnd;
4340  md=mmd;
4341  found=TRUE;
4342  }
4343  if (args)
4344  {
4345  delete argList; argList=0;
4346  }
4347  }
4348  }
4349  if (!found && args && !qstrcmp(args,"()"))
4350  // no exact match found, but if args="()" an arbitrary
4351  // member will do
4352  {
4353  for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4354  {
4355  if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
4356  {
4357  nd=fnd;
4358  md=mmd;
4359  found=TRUE;
4360  }
4361  }
4362  }
4363  if (found)
4364  {
4365  if (!md->isLinkable())
4366  {
4367  md=0; // avoid returning things we cannot link to
4368  nd=0;
4369  return FALSE; // match found but not linkable
4370  }
4371  else
4372  {
4373  gd=md->getGroupDef();
4374  if (gd && gd->isLinkable()) nd=0; else gd=0;
4375  return TRUE;
4376  }
4377  }
4378  }
4379  else
4380  {
4381  //printf("not a namespace\n");
4382  MemberNameIterator mmli(*mn);
4383  MemberDef *mmd;
4384  for (mmli.toFirst();(mmd=mmli.current());++mmli)
4385  {
4386  MemberDef *tmd = mmd->getEnumScope();
4387  //printf("try member %s tmd=%s\n",mmd->name().data(),tmd?tmd->name().data():"<none>");
4388  int ni=namespaceName.findRev("::");
4389  //printf("namespaceName=%s ni=%d\n",namespaceName.data(),ni);
4390  bool notInNS = tmd && ni==-1 && tmd->getNamespaceDef()==0 && (mScope.isEmpty() || mScope==tmd->name());
4391  bool sameNS = tmd && tmd->getNamespaceDef() && namespaceName.left(ni)==tmd->getNamespaceDef()->name();
4392  //printf("notInNS=%d sameNS=%d\n",notInNS,sameNS);
4393  if (tmd && tmd->isStrong() && // C++11 enum class
4394  (notInNS || sameNS) &&
4395  namespaceName.length()>0 // enum is part of namespace so this should not be empty
4396  )
4397  {
4398  md=mmd;
4399  fd=mmd->getFileDef();
4400  gd=mmd->getGroupDef();
4401  if (gd && gd->isLinkable()) fd=0; else gd=0;
4402  //printf("Found scoped enum %s fd=%p gd=%p\n",
4403  // mmd->name().data(),fd,gd);
4404  return TRUE;
4405  }
4406  }
4407  }
4408  if (scopeOffset==0)
4409  {
4410  scopeOffset=-1;
4411  }
4412  else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4413  {
4414  scopeOffset=0;
4415  }
4416  } while (scopeOffset>=0);
4417 
4418  //else // no scope => global function
4419  {
4420  QList<MemberDef> members;
4421  // search for matches with strict static checking
4422  findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members);
4423  if (members.count()==0) // nothing found
4424  {
4425  // search again without strict static checking
4426  findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members);
4427  }
4428  //printf("found %d members\n",members.count());
4429  if (members.count()!=1 && args && !qstrcmp(args,"()"))
4430  {
4431  // no exact match found, but if args="()" an arbitrary
4432  // member will do
4433  MemberNameIterator mni(*mn);
4434  for (mni.toLast();(md=mni.current());--mni)
4435  {
4436  //printf("Found member `%s'\n",md->name().data());
4437  //printf("member is linkable md->name()=`%s'\n",md->name().data());
4438  fd=md->getFileDef();
4439  gd=md->getGroupDef();
4440  MemberDef *tmd = md->getEnumScope();
4441  if (
4442  (gd && gd->isLinkable()) || (fd && fd->isLinkable()) ||
4443  (tmd && tmd->isStrong())
4444  )
4445  {
4446  members.append(md);
4447  }
4448  }
4449  }
4450  //printf("found %d candidate members\n",members.count());
4451  if (members.count()>0) // at least one match
4452  {
4453  if (currentFile)
4454  {
4455  //printf("multiple results; pick one from file:%s\n", currentFile->name().data());
4456  QListIterator<MemberDef> mit(members);
4457  for (mit.toFirst();(md=mit.current());++mit)
4458  {
4459  if (md->getFileDef() && md->getFileDef()->name() == currentFile->name())
4460  {
4461  break; // found match in the current file
4462  }
4463  }
4464  if (!md) // member not in the current file
4465  {
4466  md=members.getLast();
4467  }
4468  }
4469  else
4470  {
4471  md=members.getLast();
4472  }
4473  }
4474  if (md && (md->getEnumScope()==0 || !md->getEnumScope()->isStrong()))
4475  // found a matching global member, that is not a scoped enum value (or uniquely matches)
4476  {
4477  fd=md->getFileDef();
4478  gd=md->getGroupDef();
4479  //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
4480  if (gd && gd->isLinkable()) fd=0; else gd=0;
4481  return TRUE;
4482  }
4483  }
4484  }
4485 
4486  // no nothing found
4487  return FALSE;
4488 }
4489 
4504 static bool getScopeDefs(const char *docScope,const char *scope,
4505  ClassDef *&cd, NamespaceDef *&nd)
4506 {
4507  cd=0;nd=0;
4508 
4509  QCString scopeName=scope;
4510  //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
4511  if (scopeName.isEmpty()) return FALSE;
4512 
4513  bool explicitGlobalScope=FALSE;
4514  if (scopeName.at(0)==':' && scopeName.at(1)==':')
4515  {
4516  scopeName=scopeName.right(scopeName.length()-2);
4517  explicitGlobalScope=TRUE;
4518  }
4519 
4520  QCString docScopeName=docScope;
4521  int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
4522 
4523  do // for each possible docScope (from largest to and including empty)
4524  {
4525  QCString fullName=scopeName.copy();
4526  if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
4527 
4528  if (((cd=getClass(fullName)) || // normal class
4529  (cd=getClass(fullName+"-p")) //|| // ObjC protocol
4530  //(cd=getClass(fullName+"-g")) // C# generic
4531  ) && cd->isLinkable())
4532  {
4533  return TRUE; // class link written => quit
4534  }
4535  else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
4536  {
4537  return TRUE; // namespace link written => quit
4538  }
4539  if (scopeOffset==0)
4540  {
4541  scopeOffset=-1;
4542  }
4543  else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
4544  {
4545  scopeOffset=0;
4546  }
4547  } while (scopeOffset>=0);
4548 
4549  return FALSE;
4550 }
4551 
4552 static bool isLowerCase(QCString &s)
4553 {
4554  uchar *p=(uchar*)s.data();
4555  if (p==0) return TRUE;
4556  int c;
4557  while ((c=*p++)) if (!islower(c)) return FALSE;
4558  return TRUE;
4559 }
4560 
4564 bool resolveRef(/* in */ const char *scName,
4565  /* in */ const char *name,
4566  /* in */ bool inSeeBlock,
4567  /* out */ Definition **resContext,
4568  /* out */ MemberDef **resMember,
4569  bool lookForSpecialization,
4570  FileDef *currentFile,
4571  bool checkScope
4572  )
4573 {
4574  //printf("resolveRef(scope=%s,name=%s,inSeeBlock=%d)\n",scName,name,inSeeBlock);
4575  QCString tsName = name;
4576  //bool memberScopeFirst = tsName.find('#')!=-1;
4577  QCString fullName = substitute(tsName,"#","::");
4578  if (fullName.find("anonymous_namespace{")==-1)
4579  {
4580  fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
4581  }
4582  else
4583  {
4584  fullName = removeRedundantWhiteSpace(fullName);
4585  }
4586 
4587  int bracePos=findParameterList(fullName);
4588  int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
4589  int scopePos=fullName.findRev("::",endNamePos);
4590  bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
4591  (scopePos>2 || // ::N::A
4592  tsName.left(2)=="::" || // ::foo in local scope
4593  scName==0 // #foo in global scope
4594  );
4595 
4596  // default result values
4597  *resContext=0;
4598  *resMember=0;
4599 
4600  if (bracePos==-1) // simple name
4601  {
4602  ClassDef *cd=0;
4603  NamespaceDef *nd=0;
4604 
4605  // the following if() was commented out for releases in the range
4606  // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
4607  if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
4608  { // link to lower case only name => do not try to autolink
4609  return FALSE;
4610  }
4611 
4612  //printf("scName=%s fullName=%s\n",scName,fullName.data());
4613 
4614  // check if this is a class or namespace reference
4615  if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
4616  {
4617  if (cd) // scope matches that of a class
4618  {
4619  *resContext = cd;
4620  }
4621  else // scope matches that of a namespace
4622  {
4623  ASSERT(nd!=0);
4624  *resContext = nd;
4625  }
4626  return TRUE;
4627  }
4628  else if (scName==fullName || (!inSeeBlock && scopePos==-1))
4629  // nothing to link => output plain text
4630  {
4631  //printf("found scName=%s fullName=%s scName==fullName=%d "
4632  // "inSeeBlock=%d scopePos=%d!\n",
4633  // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
4634  return FALSE;
4635  }
4636  // continue search...
4637  }
4638 
4639  // extract userscope+name
4640  QCString nameStr=fullName.left(endNamePos);
4641  if (explicitScope) nameStr=nameStr.mid(2);
4642 
4643  // extract arguments
4644  QCString argsStr;
4645  if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
4646 
4647  // strip template specifier
4648  // TODO: match against the correct partial template instantiation
4649  int templPos=nameStr.find('<');
4650  bool tryUnspecializedVersion = FALSE;
4651  if (templPos!=-1 && nameStr.find("operator")==-1)
4652  {
4653  int endTemplPos=nameStr.findRev('>');
4654  if (endTemplPos!=-1)
4655  {
4656  if (!lookForSpecialization)
4657  {
4658  nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
4659  }
4660  else
4661  {
4662  tryUnspecializedVersion = TRUE;
4663  }
4664  }
4665  }
4666 
4667  QCString scopeStr=scName;
4668 
4669  MemberDef *md = 0;
4670  ClassDef *cd = 0;
4671  FileDef *fd = 0;
4672  NamespaceDef *nd = 0;
4673  GroupDef *gd = 0;
4674 
4675  // check if nameStr is a member or global.
4676  //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
4677  // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
4678  if (getDefs(scopeStr,nameStr,argsStr,
4679  md,cd,fd,nd,gd,
4680  //scopePos==0 && !memberScopeFirst, // forceEmptyScope
4681  explicitScope, // replaces prev line due to bug 600829
4682  currentFile,
4683  TRUE // checkCV
4684  )
4685  )
4686  {
4687  //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
4688  if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
4689  !md->isStrongEnumValue() &&
4690  (!scopeStr.isEmpty() || nameStr.find("::")>0))
4691  {
4692  // we did find a member, but it is a global one while we were explicitly
4693  // looking for a scoped variable. See bug 616387 for an example why this check is needed.
4694  // note we do need to support autolinking to "::symbol" hence the >0
4695  //printf("not global member!\n");
4696  *resContext=0;
4697  *resMember=0;
4698  return FALSE;
4699  }
4700  //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
4701  if (md) { *resMember=md; *resContext=md; }
4702  else if (cd) *resContext=cd;
4703  else if (nd) *resContext=nd;
4704  else if (fd) *resContext=fd;
4705  else if (gd) *resContext=gd;
4706  else { *resContext=0; *resMember=0; return FALSE; }
4707  //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
4708  // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
4709  return TRUE;
4710  }
4711  else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
4712  { // group link
4713  *resContext=gd;
4714  return TRUE;
4715  }
4716  else if (tsName.find('.')!=-1) // maybe a link to a file
4717  {
4718  bool ambig;
4719  fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
4720  if (fd && !ambig)
4721  {
4722  *resContext=fd;
4723  return TRUE;
4724  }
4725  }
4726 
4727  if (tryUnspecializedVersion)
4728  {
4729  return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
4730  }
4731  if (bracePos!=-1) // Try without parameters as well, could be a contructor invocation
4732  {
4733  *resContext=getClass(fullName.left(bracePos));
4734  if (*resContext)
4735  {
4736  return TRUE;
4737  }
4738  }
4739  //printf("resolveRef: %s not found!\n",name);
4740 
4741  return FALSE;
4742 }
4743 
4744 QCString linkToText(SrcLangExt lang,const char *link,bool isFileName)
4745 {
4746  //static bool optimizeOutputJava = Config_getBool(OPTIMIZE_OUTPUT_JAVA);
4747  QCString result=link;
4748  if (!result.isEmpty())
4749  {
4750  // replace # by ::
4751  result=substitute(result,"#","::");
4752  // replace . by ::
4753  if (!isFileName && result.find('<')==-1) result=substitute(result,".","::");
4754  // strip leading :: prefix if present
4755  if (result.at(0)==':' && result.at(1)==':')
4756  {
4757  result=result.right(result.length()-2);
4758  }
4759  QCString sep = getLanguageSpecificSeparator(lang);
4760  if (sep!="::")
4761  {
4762  result=substitute(result,"::",sep);
4763  }
4764  }
4765  return result;
4766 }
4767 
4768 #if 0
4769 /*
4770  * generate a reference to a class, namespace or member.
4771  * `scName' is the name of the scope that contains the documentation
4772  * string that is returned.
4773  * `name' is the name that we want to link to.
4774  * `name' may have five formats:
4775  * 1) "ScopeName"
4776  * 2) "memberName()" one of the (overloaded) function or define
4777  * with name memberName.
4778  * 3) "memberName(...)" a specific (overloaded) function or define
4779  * with name memberName
4780  * 4) "::name a global variable or define
4781  * 4) "\#memberName member variable, global variable or define
4782  * 5) ("ScopeName::")+"memberName()"
4783  * 6) ("ScopeName::")+"memberName(...)"
4784  * 7) ("ScopeName::")+"memberName"
4785  * instead of :: the \# symbol may also be used.
4786  */
4787 
4788 bool generateRef(OutputDocInterface &od,const char *scName,
4789  const char *name,bool inSeeBlock,const char *rt)
4790 {
4791  //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
4792 
4793  Definition *compound;
4794  MemberDef *md;
4795 
4796  // create default link text
4797  QCString linkText = linkToText(rt,FALSE);
4798 
4799  if (resolveRef(scName,name,inSeeBlock,&compound,&md))
4800  {
4801  if (md && md->isLinkable()) // link to member
4802  {
4803  od.writeObjectLink(md->getReference(),
4804  md->getOutputFileBase(),
4805  md->anchor(),linkText);
4806  // generate the page reference (for LaTeX)
4807  if (!md->isReference())
4808  {
4809  writePageRef(od,md->getOutputFileBase(),md->anchor());
4810  }
4811  return TRUE;
4812  }
4813  else if (compound && compound->isLinkable()) // link to compound
4814  {
4815  if (rt==0 && compound->definitionType()==Definition::TypeGroup)
4816  {
4817  linkText=((GroupDef *)compound)->groupTitle();
4818  }
4819  if (compound && compound->definitionType()==Definition::TypeFile)
4820  {
4821  linkText=linkToText(rt,TRUE);
4822  }
4823  od.writeObjectLink(compound->getReference(),
4824  compound->getOutputFileBase(),
4825  0,linkText);
4826  if (!compound->isReference())
4827  {
4828  writePageRef(od,compound->getOutputFileBase(),0);
4829  }
4830  return TRUE;
4831  }
4832  }
4833  od.docify(linkText);
4834  return FALSE;
4835 }
4836 #endif
4837 
4838 bool resolveLink(/* in */ const char *scName,
4839  /* in */ const char *lr,
4840  /* in */ bool /*inSeeBlock*/,
4841  /* out */ Definition **resContext,
4842  /* out */ QCString &resAnchor
4843  )
4844 {
4845  *resContext=0;
4846 
4847  QCString linkRef=lr;
4848  QCString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,FALSE);
4849  //printf("ResolveLink linkRef=%s\n",lr);
4850  FileDef *fd;
4851  GroupDef *gd;
4852  PageDef *pd;
4853  ClassDef *cd;
4854  DirDef *dir;
4855  NamespaceDef *nd;
4856  SectionInfo *si=0;
4857  bool ambig;
4858  if (linkRef.isEmpty()) // no reference name!
4859  {
4860  return FALSE;
4861  }
4862  else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
4863  {
4864  GroupDef *gd = pd->getGroupDef();
4865  if (gd)
4866  {
4867  if (!pd->name().isEmpty()) si=Doxygen::sectionDict->find(pd->name());
4868  *resContext=gd;
4869  if (si) resAnchor = si->label;
4870  }
4871  else
4872  {
4873  *resContext=pd;
4874  }
4875  return TRUE;
4876  }
4877  else if ((si=Doxygen::sectionDict->find(linkRef)))
4878  {
4879  *resContext=si->definition;
4880  resAnchor = si->label;
4881  return TRUE;
4882  }
4883  else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
4884  {
4885  *resContext=pd;
4886  return TRUE;
4887  }
4888  else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
4889  {
4890  *resContext=gd;
4891  return TRUE;
4892  }
4893  else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
4894  && fd->isLinkable())
4895  {
4896  *resContext=fd;
4897  return TRUE;
4898  }
4899  else if ((cd=getClass(linkRef))) // class link
4900  {
4901  *resContext=cd;
4902  resAnchor=cd->anchor();
4903  return TRUE;
4904  }
4905  else if ((cd=getClass(linkRefWithoutTemplates))) // C#/Java generic class link
4906  {
4907  *resContext=cd;
4908  resAnchor=cd->anchor();
4909  return TRUE;
4910  }
4911  else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
4912  {
4913  *resContext=cd;
4914  resAnchor=cd->anchor();
4915  return TRUE;
4916  }
4917 // else if ((cd=getClass(linkRef+"-g"))) // C# generic link
4918 // {
4919 // *resContext=cd;
4920 // resAnchor=cd->anchor();
4921 // return TRUE;
4922 // }
4923  else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
4924  {
4925  *resContext=nd;
4926  return TRUE;
4927  }
4928  else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/"))
4929  && dir->isLinkable()) // TODO: make this location independent like filedefs
4930  {
4931  *resContext=dir;
4932  return TRUE;
4933  }
4934  else // probably a member reference
4935  {
4936  MemberDef *md;
4937  bool res = resolveRef(scName,lr,TRUE,resContext,&md);
4938  if (md) resAnchor=md->anchor();
4939  return res;
4940  }
4941 }
4942 
4943 
4944 //----------------------------------------------------------------------
4945 // General function that generates the HTML code for a reference to some
4946 // file, class or member from text `lr' within the context of class `clName'.
4947 // This link has the text 'lt' (if not 0), otherwise `lr' is used as a
4948 // basis for the link's text.
4949 // returns TRUE if a link could be generated.
4950 
4951 bool generateLink(OutputDocInterface &od,const char *clName,
4952  const char *lr,bool inSeeBlock,const char *lt)
4953 {
4954  //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
4955  Definition *compound;
4956  //PageDef *pageDef=0;
4957  QCString anchor,linkText=linkToText(SrcLangExt_Unknown,lt,FALSE);
4958  //printf("generateLink linkText=%s\n",linkText.data());
4959  if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
4960  {
4961  if (compound) // link to compound
4962  {
4963  if (lt==0 && anchor.isEmpty() && /* compound link */
4964  compound->definitionType()==Definition::TypeGroup /* is group */
4965  )
4966  {
4967  linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
4968  }
4969  else if (compound->definitionType()==Definition::TypeFile)
4970  {
4971  linkText=linkToText(compound->getLanguage(),lt,TRUE);
4972  }
4973  od.writeObjectLink(compound->getReference(),
4974  compound->getOutputFileBase(),anchor,linkText);
4975  if (!compound->isReference())
4976  {
4977  writePageRef(od,compound->getOutputFileBase(),anchor);
4978  }
4979  }
4980  else
4981  {
4982  err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
4983  }
4984  return TRUE;
4985  }
4986  else // link could not be found
4987  {
4988  od.docify(linkText);
4989  return FALSE;
4990  }
4991 }
4992 
4993 void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
4994 {
4995  //printf("generateFileRef(%s,%s)\n",name,text);
4996  QCString linkText = text ? text : name;
4997  //FileInfo *fi;
4998  FileDef *fd;
4999  bool ambig;
5000  if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) &&
5001  fd->isLinkable())
5002  // link to documented input file
5003  od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
5004  else
5005  od.docify(linkText);
5006 }
5007 
5008 //----------------------------------------------------------------------
5009 
5010 #if 0
5011 QCString substituteClassNames(const QCString &s)
5012 {
5013  int i=0,l,p;
5014  QCString result;
5015  if (s.isEmpty()) return result;
5016  QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
5017  while ((p=r.match(s,i,&l))!=-1)
5018  {
5019  QCString *subst;
5020  if (p>i) result+=s.mid(i,p-i);
5021  if ((subst=substituteDict[s.mid(p,l)]))
5022  {
5023  result+=*subst;
5024  }
5025  else
5026  {
5027  result+=s.mid(p,l);
5028  }
5029  i=p+l;
5030  }
5031  result+=s.mid(i,s.length()-i);
5032  return result;
5033 }
5034 #endif
5035 
5036 //----------------------------------------------------------------------
5037 
5040 {
5041  FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
5043  bool isAmbig;
5044 };
5045 
5046 static QCache<FindFileCacheElem> g_findFileDefCache(5000);
5047 
5048 FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
5049 {
5050  ambig=FALSE;
5051  if (n==0) return 0;
5052 
5053  const int maxAddrSize = 20;
5054  char addr[maxAddrSize];
5055  qsnprintf(addr,maxAddrSize,"%p:",fnDict);
5056  QCString key = addr;
5057  key+=n;
5058 
5059  g_findFileDefCache.setAutoDelete(TRUE);
5060  FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
5061  //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
5062  if (cachedResult)
5063  {
5064  ambig = cachedResult->isAmbig;
5065  //printf("cached: fileDef=%p\n",cachedResult->fileDef);
5066  return cachedResult->fileDef;
5067  }
5068  else
5069  {
5070  cachedResult = new FindFileCacheElem(0,FALSE);
5071  }
5072 
5073  QCString name=QDir::cleanDirPath(n).utf8();
5074  QCString path;
5075  int slashPos;
5076  FileName *fn;
5077  if (name.isEmpty()) goto exit;
5078  slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
5079  if (slashPos!=-1)
5080  {
5081  path=name.left(slashPos+1);
5082  name=name.right(name.length()-slashPos-1);
5083  //printf("path=%s name=%s\n",path.data(),name.data());
5084  }
5085  if (name.isEmpty()) goto exit;
5086  if ((fn=(*fnDict)[name]))
5087  {
5088  //printf("fn->count()=%d\n",fn->count());
5089  if (fn->count()==1)
5090  {
5091  FileDef *fd = fn->getFirst();
5092 #if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
5093  bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
5094 #else // Unix
5095  bool isSamePath = fd->getPath().right(path.length())==path;
5096 #endif
5097  if (path.isEmpty() || isSamePath)
5098  {
5099  cachedResult->fileDef = fd;
5100  g_findFileDefCache.insert(key,cachedResult);
5101  //printf("=1 ===> add to cache %p\n",fd);
5102  return fd;
5103  }
5104  }
5105  else // file name alone is ambiguous
5106  {
5107  int count=0;
5108  FileNameIterator fni(*fn);
5109  FileDef *fd;
5110  FileDef *lastMatch=0;
5111  QCString pathStripped = stripFromIncludePath(path);
5112  for (fni.toFirst();(fd=fni.current());++fni)
5113  {
5114  QCString fdStripPath = stripFromIncludePath(fd->getPath());
5115  if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
5116  {
5117  count++;
5118  lastMatch=fd;
5119  }
5120  }
5121  //printf(">1 ===> add to cache %p\n",fd);
5122 
5123  ambig=(count>1);
5124  cachedResult->isAmbig = ambig;
5125  cachedResult->fileDef = lastMatch;
5126  g_findFileDefCache.insert(key,cachedResult);
5127  return lastMatch;
5128  }
5129  }
5130  else
5131  {
5132  //printf("not found!\n");
5133  }
5134 exit:
5135  //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
5136  g_findFileDefCache.insert(key,cachedResult);
5137  //delete cachedResult;
5138  return 0;
5139 }
5140 
5141 //----------------------------------------------------------------------
5142 
5143 QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
5144 {
5145  QCString result;
5146  QCString name=n;
5147  QCString path;
5148  int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
5149  if (slashPos!=-1)
5150  {
5151  path=name.left(slashPos+1);
5152  name=name.right(name.length()-slashPos-1);
5153  }
5154  FileName *fn;
5155  if ((fn=(*fnDict)[name]))
5156  {
5157  FileNameIterator fni(*fn);
5158  FileDef *fd;
5159  for (fni.toFirst();(fd=fni.current());++fni)
5160  {
5161  if (path.isEmpty() || fd->getPath().right(path.length())==path)
5162  {
5163  result+=" "+fd->absFilePath()+"\n";
5164  }
5165  }
5166  }
5167  return result;
5168 }
5169 
5170 //----------------------------------------------------------------------
5171 
5173 QCString substitute(const QCString &s,const QCString &src,const QCString &dst)
5174 {
5175  if (s.isEmpty() || src.isEmpty()) return s;
5176  const char *p, *q;
5177  int srcLen = src.length();
5178  int dstLen = dst.length();
5179  int resLen;
5180  if (srcLen!=dstLen)
5181  {
5182  int count;
5183  for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++;
5184  resLen = s.length()+count*(dstLen-srcLen);
5185  }
5186  else // result has same size as s
5187  {
5188  resLen = s.length();
5189  }
5190  QCString result(resLen+1);
5191  char *r;
5192  for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
5193  {
5194  int l = (int)(q-p);
5195  memcpy(r,p,l);
5196  r+=l;
5197  if (dst) memcpy(r,dst,dstLen);
5198  r+=dstLen;
5199  }
5200  qstrcpy(r,p);
5201  //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
5202  return result;
5203 }
5204 
5205 //----------------------------------------------------------------------
5206 
5207 QCString substituteKeywords(const QCString &s,const char *title,
5208  const char *projName,const char *projNum,const char *projBrief)
5209 {
5210  QCString result = s;
5211  if (title) result = substitute(result,"$title",title);
5212  result = substitute(result,"$datetime",dateToString(TRUE));
5213  result = substitute(result,"$date",dateToString(FALSE));
5214  result = substitute(result,"$year",yearToString());
5215  result = substitute(result,"$doxygenversion",versionString);
5216  result = substitute(result,"$projectname",projName);
5217  result = substitute(result,"$projectnumber",projNum);
5218  result = substitute(result,"$projectbrief",projBrief);
5219  result = substitute(result,"$projectlogo",stripPath(Config_getString(PROJECT_LOGO)));
5220  return result;
5221 }
5222 
5223 //----------------------------------------------------------------------
5224 
5229 int getPrefixIndex(const QCString &name)
5230 {
5231  if (name.isEmpty()) return 0;
5232  static QStrList &sl = Config_getList(IGNORE_PREFIX);
5233  char *s = sl.first();
5234  while (s)
5235  {
5236  const char *ps=s;
5237  const char *pd=name.data();
5238  int i=0;
5239  while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
5240  if (*ps==0 && *pd!=0)
5241  {
5242  return i;
5243  }
5244  s = sl.next();
5245  }
5246  return 0;
5247 }
5248 
5249 //----------------------------------------------------------------------------
5250 
5252 {
5253  if (bcl==0) return;
5254  BaseClassListIterator bcli(*bcl);
5255  for ( ; bcli.current(); ++bcli)
5256  {
5257  ClassDef *cd=bcli.current()->classDef;
5258  if (cd->baseClasses()==0) // no base classes => new root
5259  {
5261  }
5262  cd->visited=FALSE;
5263  }
5264 }
5265 //----------------------------------------------------------------------------
5266 
5268 {
5269  BaseClassList *bcl;
5270 
5271  if (cd->getLanguage()==SrcLangExt_VHDL) // reverse baseClass/subClass relation
5272  {
5273  if (cd->baseClasses()==0) return FALSE;
5274  bcl=cd->baseClasses();
5275  }
5276  else
5277  {
5278  if (cd->subClasses()==0) return FALSE;
5279  bcl=cd->subClasses();
5280  }
5281 
5282  BaseClassListIterator bcli(*bcl);
5283  for ( ; bcli.current() ; ++bcli)
5284  {
5285  if (bcli.current()->classDef->isVisibleInHierarchy())
5286  {
5287  return TRUE;
5288  }
5289  }
5290  return FALSE;
5291 }
5292 
5293 
5294 //----------------------------------------------------------------------------
5295 
5297 {
5298  ClassSDict::Iterator cli(*cl);
5299  ClassDef *cd;
5300  for ( ; (cd=cli.current()); ++cli)
5301  {
5302  cd->visited=FALSE;
5304  }
5305 }
5306 
5307 //----------------------------------------------------------------------------
5308 
5310 {
5311  if (bcl)
5312  {
5313  BaseClassListIterator bcli(*bcl);
5314  for ( ; bcli.current(); ++bcli)
5315  {
5316  ClassDef *cd=bcli.current()->classDef;
5317  if (cd->isVisibleInHierarchy()) return TRUE;
5318  hasVisibleRoot(cd->baseClasses());
5319  }
5320  }
5321  return FALSE;
5322 }
5323 
5324 //----------------------------------------------------------------------
5325 
5326 // note that this function is not reentrant due to the use of static growBuf!
5327 QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
5328 {
5329  static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES);
5330  static bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
5331  static GrowBuf growBuf;
5332  growBuf.clear();
5333  char c;
5334  const char *p=name;
5335  while ((c=*p++)!=0)
5336  {
5337  switch(c)
5338  {
5339  case '_': if (allowUnderscore) growBuf.addChar('_'); else growBuf.addStr("__"); break;
5340  case '-': growBuf.addChar('-'); break;
5341  case ':': growBuf.addStr("_1"); break;
5342  case '/': growBuf.addStr("_2"); break;
5343  case '<': growBuf.addStr("_3"); break;
5344  case '>': growBuf.addStr("_4"); break;
5345  case '*': growBuf.addStr("_5"); break;
5346  case '&': growBuf.addStr("_6"); break;
5347  case '|': growBuf.addStr("_7"); break;
5348  case '.': if (allowDots) growBuf.addChar('.'); else growBuf.addStr("_8"); break;
5349  case '!': growBuf.addStr("_9"); break;
5350  case ',': growBuf.addStr("_00"); break;
5351  case ' ': growBuf.addStr("_01"); break;
5352  case '{': growBuf.addStr("_02"); break;
5353  case '}': growBuf.addStr("_03"); break;
5354  case '?': growBuf.addStr("_04"); break;
5355  case '^': growBuf.addStr("_05"); break;
5356  case '%': growBuf.addStr("_06"); break;
5357  case '(': growBuf.addStr("_07"); break;
5358  case ')': growBuf.addStr("_08"); break;
5359  case '+': growBuf.addStr("_09"); break;
5360  case '=': growBuf.addStr("_0A"); break;
5361  case '$': growBuf.addStr("_0B"); break;
5362  case '\\': growBuf.addStr("_0C"); break;
5363  case '@': growBuf.addStr("_0D"); break;
5364  default:
5365  if (c<0)
5366  {
5367  char ids[5];
5368  const unsigned char uc = (unsigned char)c;
5369  bool doEscape = TRUE;
5370  if (allowUnicodeNames && uc <= 0xf7)
5371  {
5372  const char* pt = p;
5373  ids[ 0 ] = c;
5374  int l = 0;
5375  if ((uc&0xE0)==0xC0)
5376  {
5377  l=2; // 11xx.xxxx: >=2 byte character
5378  }
5379  if ((uc&0xF0)==0xE0)
5380  {
5381  l=3; // 111x.xxxx: >=3 byte character
5382  }
5383  if ((uc&0xF8)==0xF0)
5384  {
5385  l=4; // 1111.xxxx: >=4 byte character
5386  }
5387  doEscape = l==0;
5388  for (int m=1; m<l && !doEscape; ++m)
5389  {
5390  unsigned char ct = (unsigned char)*pt;
5391  if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character
5392  {
5393  doEscape=TRUE;
5394  }
5395  else
5396  {
5397  ids[ m ] = *pt++;
5398  }
5399  }
5400  if ( !doEscape ) // got a valid unicode character
5401  {
5402  ids[ l ] = 0;
5403  growBuf.addStr( ids );
5404  p += l - 1;
5405  }
5406  }
5407  if (doEscape) // not a valid unicode char or escaping needed
5408  {
5409  static char map[] = "0123456789ABCDEF";
5410  unsigned char id = (unsigned char)c;
5411  ids[0]='_';
5412  ids[1]='x';
5413  ids[2]=map[id>>4];
5414  ids[3]=map[id&0xF];
5415  ids[4]=0;
5416  growBuf.addStr(ids);
5417  }
5418  }
5419  else if (caseSenseNames || !isupper(c))
5420  {
5421  growBuf.addChar(c);
5422  }
5423  else
5424  {
5425  growBuf.addChar('_');
5426  growBuf.addChar(tolower(c));
5427  }
5428  break;
5429  }
5430  }
5431  growBuf.addChar(0);
5432  return growBuf.get();
5433 }
5434 
5439 QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
5440 {
5441  if (name==0 || name[0]=='\0') return "";
5442  static bool shortNames = Config_getBool(SHORT_NAMES);
5443  static bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
5444  QCString result;
5445  if (shortNames) // use short names only
5446  {
5447  static QDict<int> usedNames(10007);
5448  usedNames.setAutoDelete(TRUE);
5449  static int count=1;
5450 
5451  int *value=usedNames.find(name);
5452  int num;
5453  if (value==0)
5454  {
5455  usedNames.insert(name,new int(count));
5456  num = count++;
5457  }
5458  else
5459  {
5460  num = *value;
5461  }
5462  result.sprintf("a%05d",num);
5463  }
5464  else // long names
5465  {
5466  result=escapeCharsInString(name,allowDots,allowUnderscore);
5467  int resultLen = result.length();
5468  if (resultLen>=128) // prevent names that cannot be created!
5469  {
5470  // third algorithm based on MD5 hash
5471  uchar md5_sig[16];
5472  QCString sigStr(33);
5473  MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
5474  MD5SigToString(md5_sig,sigStr.rawData(),33);
5475  result=result.left(128-32)+sigStr;
5476  }
5477  }
5478  if (createSubdirs)
5479  {
5480  int l1Dir=0,l2Dir=0;
5481 
5482 #if MAP_ALGO==ALGO_COUNT
5483  // old algorithm, has the problem that after regeneration the
5484  // output can be located in a different dir.
5485  if (Doxygen::htmlDirMap==0)
5486  {
5487  Doxygen::htmlDirMap=new QDict<int>(100003);
5488  Doxygen::htmlDirMap->setAutoDelete(TRUE);
5489  }
5490  static int curDirNum=0;
5491  int *dirNum = Doxygen::htmlDirMap->find(result);
5492  if (dirNum==0) // new name
5493  {
5494  Doxygen::htmlDirMap->insert(result,new int(curDirNum));
5495  l1Dir = (curDirNum)&0xf; // bits 0-3
5496  l2Dir = (curDirNum>>4)&0xff; // bits 4-11
5497  curDirNum++;
5498  }
5499  else // existing name
5500  {
5501  l1Dir = (*dirNum)&0xf; // bits 0-3
5502  l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
5503  }
5504 #elif MAP_ALGO==ALGO_CRC16
5505  // second algorithm based on CRC-16 checksum
5506  int dirNum = qChecksum(result,result.length());
5507  l1Dir = dirNum&0xf;
5508  l2Dir = (dirNum>>4)&0xff;
5509 #elif MAP_ALGO==ALGO_MD5
5510  // third algorithm based on MD5 hash
5511  uchar md5_sig[16];
5512  MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
5513  l1Dir = md5_sig[14]&0xf;
5514  l2Dir = md5_sig[15];
5515 #endif
5516  result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
5517  }
5518  //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
5519  return result;
5520 }
5521 
5522 QCString relativePathToRoot(const char *name)
5523 {
5524  QCString result;
5525  if (Config_getBool(CREATE_SUBDIRS))
5526  {
5527  if (name==0)
5528  {
5529  return REL_PATH_TO_ROOT;
5530  }
5531  else
5532  {
5533  QCString n = name;
5534  int i = n.findRev('/');
5535  if (i!=-1)
5536  {
5537  result=REL_PATH_TO_ROOT;
5538  }
5539  }
5540  }
5541  return result;
5542 }
5543 
5544 void createSubDirs(QDir &d)
5545 {
5546  if (Config_getBool(CREATE_SUBDIRS))
5547  {
5548  // create 4096 subdirectories
5549  int l1,l2;
5550  for (l1=0;l1<16;l1++)
5551  {
5552  d.mkdir(QCString().sprintf("d%x",l1));
5553  for (l2=0;l2<256;l2++)
5554  {
5555  d.mkdir(QCString().sprintf("d%x/d%02x",l1,l2));
5556  }
5557  }
5558  }
5559 }
5560 
5564 void extractNamespaceName(const QCString &scopeName,
5565  QCString &className,QCString &namespaceName,
5566  bool allowEmptyClass)
5567 {
5568  int i,p;
5569  QCString clName=scopeName;
5570  NamespaceDef *nd = 0;
5571  if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
5572  { // the whole name is a namespace (and not a class)
5573  namespaceName=nd->name().copy();
5574  className.resize(0);
5575  goto done;
5576  }
5577  p=clName.length()-2;
5578  while (p>=0 && (i=clName.findRev("::",p))!=-1)
5579  // see if the first part is a namespace (and not a class)
5580  {
5581  //printf("Trying %s\n",clName.left(i).data());
5582  if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
5583  {
5584  //printf("found!\n");
5585  namespaceName=nd->name().copy();
5586  className=clName.right(clName.length()-i-2);
5587  goto done;
5588  }
5589  p=i-2; // try a smaller piece of the scope
5590  }
5591  //printf("not found!\n");
5592 
5593  // not found, so we just have to guess.
5594  className=scopeName.copy();
5595  namespaceName.resize(0);
5596 
5597 done:
5598  if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
5599  {
5600  // class and namespace with the same name, correct to return the class.
5601  className=namespaceName.copy();
5602  namespaceName.resize(0);
5603  }
5604  //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
5605  // className.data(),namespaceName.data());
5606  if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p")
5607  {
5608  className = className.left(className.length()-2);
5609  }
5610  return;
5611 }
5612 
5613 QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
5614 {
5615  QCString result=scope.copy();
5616  if (!templ.isEmpty() && scope.find('<')==-1)
5617  {
5618  int si,pi=0;
5619  ClassDef *cd=0;
5620  while (
5621  (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
5622  ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0)
5623  )
5624  {
5625  //printf("Tried `%s'\n",(scope.left(si)+templ).data());
5626  pi=si+2;
5627  }
5628  if (si==-1) // not nested => append template specifier
5629  {
5630  result+=templ;
5631  }
5632  else // nested => insert template specifier before after first class name
5633  {
5634  result=scope.left(si) + templ + scope.right(scope.length()-si);
5635  }
5636  }
5637  //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
5638  // scope.data(),templ.data(),result.data());
5639  return result;
5640 }
5641 
5642 #if 0 // original version
5643 
5646 QCString stripScope(const char *name)
5647 {
5648  QCString result = name;
5649  int l=result.length();
5650  int p=l-1;
5651  bool done;
5652  int count;
5653 
5654  while (p>=0)
5655  {
5656  char c=result.at(p);
5657  switch (c)
5658  {
5659  case ':':
5660  //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5661  return result.right(l-p-1);
5662  case '>':
5663  count=1;
5664  done=FALSE;
5665  //printf("pos < = %d\n",p);
5666  p--;
5667  while (p>=0 && !done)
5668  {
5669  c=result.at(p--);
5670  switch (c)
5671  {
5672  case '>': count++; break;
5673  case '<': count--; if (count<=0) done=TRUE; break;
5674  default:
5675  //printf("c=%c count=%d\n",c,count);
5676  break;
5677  }
5678  }
5679  //printf("pos > = %d\n",p+1);
5680  break;
5681  default:
5682  p--;
5683  }
5684  }
5685  //printf("stripScope(%s)=%s\n",name,name);
5686  return name;
5687 }
5688 #endif
5689 
5690 // new version by Davide Cesari which also works for Fortran
5691 QCString stripScope(const char *name)
5692 {
5693  QCString result = name;
5694  int l=result.length();
5695  int p;
5696  bool done = FALSE;
5697  bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
5698  int count=0;
5699 
5700  do
5701  {
5702  p=l-1; // start at the end of the string
5703  while (p>=0 && count>=0)
5704  {
5705  char c=result.at(p);
5706  switch (c)
5707  {
5708  case ':':
5709  // only exit in the case of ::
5710  //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5711  if (p>0 && result.at(p-1)==':') return result.right(l-p-1);
5712  p--;
5713  break;
5714  case '>':
5715  if (skipBracket) // we don't care about brackets
5716  {
5717  p--;
5718  }
5719  else // count open/close brackets
5720  {
5721  if (p>0 && result.at(p-1)=='>') // skip >> operator
5722  {
5723  p-=2;
5724  break;
5725  }
5726  count=1;
5727  //printf("pos < = %d\n",p);
5728  p--;
5729  bool foundMatch=false;
5730  while (p>=0 && !foundMatch)
5731  {
5732  c=result.at(p--);
5733  switch (c)
5734  {
5735  case '>':
5736  count++;
5737  break;
5738  case '<':
5739  if (p>0)
5740  {
5741  if (result.at(p-1) == '<') // skip << operator
5742  {
5743  p--;
5744  break;
5745  }
5746  }
5747  count--;
5748  foundMatch = count==0;
5749  break;
5750  default:
5751  //printf("c=%c count=%d\n",c,count);
5752  break;
5753  }
5754  }
5755  }
5756  //printf("pos > = %d\n",p+1);
5757  break;
5758  default:
5759  p--;
5760  }
5761  }
5762  done = count==0 || skipBracket; // reparse if brackets do not match
5763  skipBracket=TRUE;
5764  }
5765  while (!done); // if < > unbalanced repeat ignoring them
5766  //printf("stripScope(%s)=%s\n",name,name);
5767  return name;
5768 }
5769 
5771 QCString convertToId(const char *s)
5772 {
5773  static const char hex[] = "0123456789ABCDEF";
5774  static GrowBuf growBuf;
5775  growBuf.clear();
5776  if (s==0) return "";
5777  const char *p=s;
5778  char c;
5779  bool first=TRUE;
5780  while ((c=*p++))
5781  {
5782  char encChar[4];
5783  if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-' || c==':' || c=='.')
5784  { // any permissive character except _
5785  if (first && c>='0' && c<='9') growBuf.addChar('a'); // don't start with a digit
5786  growBuf.addChar(c);
5787  }
5788  else
5789  {
5790  encChar[0]='_';
5791  encChar[1]=hex[((unsigned char)c)>>4];
5792  encChar[2]=hex[((unsigned char)c)&0xF];
5793  encChar[3]=0;
5794  growBuf.addStr(encChar);
5795  }
5796  first=FALSE;
5797  }
5798  growBuf.addChar(0);
5799  return growBuf.get();
5800 }
5801 
5803 QCString convertToXML(const char *s)
5804 {
5805  static GrowBuf growBuf;
5806  growBuf.clear();
5807  if (s==0) return "";
5808  const char *p=s;
5809  char c;
5810  while ((c=*p++))
5811  {
5812  switch (c)
5813  {
5814  case '<': growBuf.addStr("&lt;"); break;
5815  case '>': growBuf.addStr("&gt;"); break;
5816  case '&': growBuf.addStr("&amp;"); break;
5817  case '\'': growBuf.addStr("&apos;"); break;
5818  case '"': growBuf.addStr("&quot;"); break;
5819  case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
5820  case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
5821  case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
5822  case 27: case 28: case 29: case 30: case 31:
5823  break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
5824  default: growBuf.addChar(c); break;
5825  }
5826  }
5827  growBuf.addChar(0);
5828  return growBuf.get();
5829 }
5830 
5832 QCString convertToHtml(const char *s,bool keepEntities)
5833 {
5834  static GrowBuf growBuf;
5835  growBuf.clear();
5836  if (s==0) return "";
5837  const char *p=s;
5838  char c;
5839  while ((c=*p++))
5840  {
5841  switch (c)
5842  {
5843  case '<': growBuf.addStr("&lt;"); break;
5844  case '>': growBuf.addStr("&gt;"); break;
5845  case '&': if (keepEntities)
5846  {
5847  const char *e=p;
5848  char ce;
5849  while ((ce=*e++))
5850  {
5851  if (ce==';' || (!(isId(ce) || ce=='#'))) break;
5852  }
5853  if (ce==';') // found end of an entity
5854  {
5855  // copy entry verbatim
5856  growBuf.addChar(c);
5857  while (p<e) growBuf.addChar(*p++);
5858  }
5859  else
5860  {
5861  growBuf.addStr("&amp;");
5862  }
5863  }
5864  else
5865  {
5866  growBuf.addStr("&amp;");
5867  }
5868  break;
5869  case '\'': growBuf.addStr("&#39;"); break;
5870  case '"': growBuf.addStr("&quot;"); break;
5871  default: growBuf.addChar(c); break;
5872  }
5873  }
5874  growBuf.addChar(0);
5875  return growBuf.get();
5876 }
5877 
5878 QCString convertToJSString(const char *s)
5879 {
5880  static GrowBuf growBuf;
5881  growBuf.clear();
5882  if (s==0) return "";
5883  const char *p=s;
5884  char c;
5885  while ((c=*p++))
5886  {
5887  switch (c)
5888  {
5889  case '"': growBuf.addStr("\\\""); break;
5890  case '\\': growBuf.addStr("\\\\"); break;
5891  default: growBuf.addChar(c); break;
5892  }
5893  }
5894  growBuf.addChar(0);
5895  return convertCharEntitiesToUTF8(growBuf.get());
5896 }
5897 
5898 QCString convertToLaTeX(const QCString &s,bool insideTabbing,bool keepSpaces)
5899 {
5900  QGString result;
5901  FTextStream t(&result);
5902  filterLatexString(t,s,insideTabbing,FALSE,FALSE,keepSpaces);
5903  return result.data();
5904 }
5905 
5906 
5907 
5908 QCString convertCharEntitiesToUTF8(const QCString &s)
5909 {
5910  QCString result;
5911  static QRegExp entityPat("&[a-zA-Z]+[0-9]*;");
5912 
5913  if (s.length()==0) return result;
5914  static GrowBuf growBuf;
5915  growBuf.clear();
5916  int p,i=0,l;
5917  while ((p=entityPat.match(s,i,&l))!=-1)
5918  {
5919  if (p>i)
5920  {
5921  growBuf.addStr(s.mid(i,p-i));
5922  }
5923  QCString entity = s.mid(p,l);
5925  const char *code=0;
5926  if (symType!=DocSymbol::Sym_Unknown && (code=HtmlEntityMapper::instance()->utf8(symType)))
5927  {
5928  growBuf.addStr(code);
5929  }
5930  else
5931  {
5932  growBuf.addStr(s.mid(p,l));
5933  }
5934  i=p+l;
5935  }
5936  growBuf.addStr(s.mid(i,s.length()-i));
5937  growBuf.addChar(0);
5938  //printf("convertCharEntitiesToUTF8(%s)->%s\n",s.data(),growBuf.get());
5939  return growBuf.get();
5940 }
5941 
5946 {
5947  return theTranslator->trOverloadText();
5948  //"This is an overloaded member function, "
5949  // "provided for convenience. It differs from the above "
5950  // "function only in what argument(s) it accepts.";
5951 }
5952 
5954  MemberGroupSDict **ppMemberGroupSDict,
5955  Definition *context)
5956 {
5957  ASSERT(context!=0);
5958  //printf("addMemberToMemberGroup()\n");
5959  if (ml==0) return;
5960  MemberListIterator mli(*ml);
5961  MemberDef *md;
5962  uint index;
5963  for (index=0;(md=mli.current());)
5964  {
5965  if (md->isEnumerate()) // insert enum value of this enum into groups
5966  {
5967  MemberList *fmdl=md->enumFieldList();
5968  if (fmdl!=0)
5969  {
5970  MemberListIterator fmli(*fmdl);
5971  MemberDef *fmd;
5972  for (fmli.toFirst();(fmd=fmli.current());++fmli)
5973  {
5974  int groupId=fmd->getMemberGroupId();
5975  if (groupId!=-1)
5976  {
5977  MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5978  //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5979  //QCString *pDocs = Doxygen::memberDocDict[groupId];
5980  if (info)
5981  {
5982  if (*ppMemberGroupSDict==0)
5983  {
5984  *ppMemberGroupSDict = new MemberGroupSDict;
5985  (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5986  }
5987  MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5988  if (mg==0)
5989  {
5990  mg = new MemberGroup(
5991  context,
5992  groupId,
5993  info->header,
5994  info->doc,
5995  info->docFile,
5996  info->docLine
5997  );
5998  (*ppMemberGroupSDict)->append(groupId,mg);
5999  }
6000  mg->insertMember(fmd); // insert in member group
6001  fmd->setMemberGroup(mg);
6002  }
6003  }
6004  }
6005  }
6006  }
6007  int groupId=md->getMemberGroupId();
6008  if (groupId!=-1)
6009  {
6010  MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
6011  //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
6012  //QCString *pDocs = Doxygen::memberDocDict[groupId];
6013  if (info)
6014  {
6015  if (*ppMemberGroupSDict==0)
6016  {
6017  *ppMemberGroupSDict = new MemberGroupSDict;
6018  (*ppMemberGroupSDict)->setAutoDelete(TRUE);
6019  }
6020  MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
6021  if (mg==0)
6022  {
6023  mg = new MemberGroup(
6024  context,
6025  groupId,
6026  info->header,
6027  info->doc,
6028  info->docFile,
6029  info->docLine
6030  );
6031  (*ppMemberGroupSDict)->append(groupId,mg);
6032  }
6033  md = ml->take(index); // remove from member list
6034  mg->insertMember(md); // insert in member group
6035  mg->setRefItems(info->m_sli);
6036  md->setMemberGroup(mg);
6037  continue;
6038  }
6039  }
6040  ++mli;++index;
6041  }
6042 }
6043 
6049 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
6050 {
6051  static const QRegExp re_norm("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
6052  static const QRegExp re_ftn("[a-z_A-Z\\x80-\\xFF][()=_a-z_A-Z0-9:\\x80-\\xFF]*");
6053  QRegExp re;
6054 
6055  name.resize(0);
6056  templSpec.resize(0);
6057  int i,l;
6058  int typeLen=type.length();
6059  if (typeLen>0)
6060  {
6061  if (lang == SrcLangExt_Fortran)
6062  {
6063  if (type.at(pos)==',') return -1;
6064  if (type.left(4).lower()=="type")
6065  {
6066  re = re_norm;
6067  }
6068  else
6069  {
6070  re = re_ftn;
6071  }
6072  }
6073  else
6074  {
6075  re = re_norm;
6076  }
6077 
6078  if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
6079  {
6080  int ts=i+l;
6081  int te=ts;
6082  int tl=0;
6083  while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
6084  if (type.at(ts)=='<') // assume template instance
6085  {
6086  // locate end of template
6087  te=ts+1;
6088  int brCount=1;
6089  while (te<typeLen && brCount!=0)
6090  {
6091  if (type.at(te)=='<')
6092  {
6093  if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
6094  }
6095  if (type.at(te)=='>')
6096  {
6097  if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
6098  }
6099  te++;
6100  }
6101  }
6102  name = type.mid(i,l);
6103  if (te>ts)
6104  {
6105  templSpec = type.mid(ts,te-ts),tl+=te-ts;
6106  pos=i+l+tl;
6107  }
6108  else // no template part
6109  {
6110  pos=i+l;
6111  }
6112  //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
6113  // type.data(),pos,name.data(),templSpec.data());
6114  return i;
6115  }
6116  }
6117  pos = typeLen;
6118  //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
6119  // type.data(),pos,name.data(),templSpec.data());
6120  return -1;
6121 }
6122 
6124  const QCString &name,
6125  Definition *context,
6126  const ArgumentList * formalArgs)
6127 {
6128  // skip until <
6129  int p=name.find('<');
6130  if (p==-1) return name;
6131  p++;
6132  QCString result = name.left(p);
6133 
6134  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
6135  int l,i;
6136  // for each identifier in the template part (e.g. B<T> -> T)
6137  while ((i=re.match(name,p,&l))!=-1)
6138  {
6139  result += name.mid(p,i-p);
6140  QCString n = name.mid(i,l);
6141  bool found=FALSE;
6142  if (formalArgs) // check that n is not a formal template argument
6143  {
6144  ArgumentListIterator formAli(*formalArgs);
6145  Argument *formArg;
6146  for (formAli.toFirst();
6147  (formArg=formAli.current()) && !found;
6148  ++formAli
6149  )
6150  {
6151  found = formArg->name==n;
6152  }
6153  }
6154  if (!found)
6155  {
6156  // try to resolve the type
6157  ClassDef *cd = getResolvedClass(context,0,n);
6158  if (cd)
6159  {
6160  result+=cd->name();
6161  }
6162  else
6163  {
6164  result+=n;
6165  }
6166  }
6167  else
6168  {
6169  result+=n;
6170  }
6171  p=i+l;
6172  }
6173  result+=name.right(name.length()-p);
6174  //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",name.data(),result.data());
6175  return removeRedundantWhiteSpace(result);
6176 }
6177 
6178 
6186  const QCString &name,
6187  ArgumentList *formalArgs,
6188  ArgumentList *actualArgs)
6189 {
6190  //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
6191  // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
6192  if (formalArgs==0) return name;
6193  QCString result;
6194  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
6195  int p=0,l,i;
6196  // for each identifier in the base class name (e.g. B<T> -> B and T)
6197  while ((i=re.match(name,p,&l))!=-1)
6198  {
6199  result += name.mid(p,i-p);
6200  QCString n = name.mid(i,l);
6201  ArgumentListIterator formAli(*formalArgs);
6202  ArgumentListIterator actAli(*actualArgs);
6203  Argument *formArg;
6204  Argument *actArg;
6205 
6206  // if n is a template argument, then we substitute it
6207  // for its template instance argument.
6208  bool found=FALSE;
6209  for (formAli.toFirst();
6210  (formArg=formAli.current()) && !found;
6211  ++formAli,++actAli
6212  )
6213  {
6214  actArg = actAli.current();
6215  if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
6216  {
6217  formArg->name = formArg->type.mid(6);
6218  formArg->type = "class";
6219  }
6220  if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
6221  {
6222  formArg->name = formArg->type.mid(9);
6223  formArg->type = "typename";
6224  }
6225  if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
6226  {
6227  //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
6228  // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
6229  //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
6230  // formArg->name.data(),actArg ? actArg->type.data() : "",actArg ? actArg->name.data() : ""
6231  // );
6232  if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
6233  {
6234  // replace formal argument with the actual argument of the instance
6235  if (!leftScopeMatch(actArg->type,n))
6236  // the scope guard is to prevent recursive lockup for
6237  // template<class A> class C : public<A::T>,
6238  // where A::T would become A::T::T here,
6239  // since n==A and actArg->type==A::T
6240  // see bug595833 for an example
6241  {
6242  if (actArg->name.isEmpty())
6243  {
6244  result += actArg->type+" ";
6245  found=TRUE;
6246  }
6247  else
6248  // for case where the actual arg is something like "unsigned int"
6249  // the "int" part is in actArg->name.
6250  {
6251  result += actArg->type+" "+actArg->name+" ";
6252  found=TRUE;
6253  }
6254  }
6255  }
6256  else if (formArg->name==n &&
6257  actArg==0 &&
6258  !formArg->defval.isEmpty() &&
6259  formArg->defval!=name /* to prevent recursion */
6260  )
6261  {
6262  result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
6263  found=TRUE;
6264  }
6265  }
6266  else if (formArg->name==n &&
6267  actArg==0 &&
6268  !formArg->defval.isEmpty() &&
6269  formArg->defval!=name /* to prevent recursion */
6270  )
6271  {
6272  result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
6273  found=TRUE;
6274  }
6275  }
6276  if (!found)
6277  {
6278  result += n;
6279  }
6280  p=i+l;
6281  }
6282  result+=name.right(name.length()-p);
6283  //printf(" Inheritance relation %s -> %s\n",
6284  // name.data(),result.data());
6285  return result.stripWhiteSpace();
6286 }
6287 
6291 QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
6292 {
6293  ASSERT(srcLists!=0);
6294  QList<ArgumentList> *dstLists = new QList<ArgumentList>;
6295  dstLists->setAutoDelete(TRUE);
6296  QListIterator<ArgumentList> sli(*srcLists);
6297  ArgumentList *sl;
6298  for (;(sl=sli.current());++sli)
6299  {
6300  dstLists->append(sl->deepCopy());
6301  }
6302  return dstLists;
6303 }
6304 
6312 QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
6313  bool parentOnly,
6314  QCString *pLastScopeStripped)
6315 {
6316  QCString result;
6317  int p=0;
6318  int l=fullName.length();
6319  int i=fullName.find('<');
6320  while (i!=-1)
6321  {
6322  //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
6323  int e=i+1;
6324  bool done=FALSE;
6325  int count=1;
6326  while (e<l && !done)
6327  {
6328  char c=fullName.at(e++);
6329  if (c=='<')
6330  {
6331  count++;
6332  }
6333  else if (c=='>')
6334  {
6335  count--;
6336  done = count==0;
6337  }
6338  }
6339  int si= fullName.find("::",e);
6340 
6341  if (parentOnly && si==-1) break;
6342  // we only do the parent scope, so we stop here if needed
6343 
6344  result+=fullName.mid(p,i-p);
6345  //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data());
6346  if (getClass(result+fullName.mid(i,e-i))!=0)
6347  {
6348  result+=fullName.mid(i,e-i);
6349  //printf(" 2:result+=%s\n",fullName.mid(i,e-i-1).data());
6350  }
6351  else if (pLastScopeStripped)
6352  {
6353  //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data());
6354  *pLastScopeStripped=fullName.mid(i,e-i);
6355  }
6356  p=e;
6357  i=fullName.find('<',p);
6358  }
6359  result+=fullName.right(l-p);
6360  //printf("3:result+=%s\n",fullName.right(l-p).data());
6361  return result;
6362 }
6363 
6373 QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
6374 {
6375  // case leftScope=="A" rightScope=="A::B" => result = "A::B"
6376  if (leftScopeMatch(rightScope,leftScope)) return rightScope;
6377  QCString result;
6378  int i=0,p=leftScope.length();
6379 
6380  // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
6381  // case leftScope=="A::B" rightScope=="B" => result = "A::B"
6382  bool found=FALSE;
6383  while ((i=leftScope.findRev("::",p))!=-1)
6384  {
6385  if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
6386  {
6387  result = leftScope.left(i+2)+rightScope;
6388  found=TRUE;
6389  }
6390  p=i-1;
6391  }
6392  if (found) return result;
6393 
6394  // case leftScope=="A" rightScope=="B" => result = "A::B"
6395  result=leftScope.copy();
6396  if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
6397  result+=rightScope;
6398  return result;
6399 }
6400 
6408 int getScopeFragment(const QCString &s,int p,int *l)
6409 {
6410  int sl=s.length();
6411  int sp=p;
6412  int count=0;
6413  bool done;
6414  if (sp>=sl) return -1;
6415  while (sp<sl)
6416  {
6417  char c=s.at(sp);
6418  if (c==':') sp++,p++; else break;
6419  }
6420  while (sp<sl)
6421  {
6422  char c=s.at(sp);
6423  switch (c)
6424  {
6425  case ':': // found next part
6426  goto found;
6427  case '<': // skip template specifier
6428  count=1;sp++;
6429  done=FALSE;
6430  while (sp<sl && !done)
6431  {
6432  // TODO: deal with << and >> operators!
6433  char c=s.at(sp++);
6434  switch(c)
6435  {
6436  case '<': count++; break;
6437  case '>': count--; if (count==0) done=TRUE; break;
6438  default: break;
6439  }
6440  }
6441  break;
6442  default:
6443  sp++;
6444  break;
6445  }
6446  }
6447 found:
6448  *l=sp-p;
6449  //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
6450  return p;
6451 }
6452 
6453 //----------------------------------------------------------------------------
6454 
6455 PageDef *addRelatedPage(const char *name,const QCString &ptitle,
6456  const QCString &doc,
6457  QList<SectionInfo> * /*anchors*/,
6458  const char *fileName,int startLine,
6459  const QList<ListItemInfo> *sli,
6460  GroupDef *gd,
6461  TagInfo *tagInfo,
6462  SrcLangExt lang
6463  )
6464 {
6465  PageDef *pd=0;
6466  //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
6467  if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
6468  {
6469  // append documentation block to the page.
6470  pd->setDocumentation(doc,fileName,startLine);
6471  //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pd,name);
6472  }
6473  else // new page
6474  {
6475  QCString baseName=name;
6476  if (baseName.right(4)==".tex")
6477  baseName=baseName.left(baseName.length()-4);
6478  else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
6479  baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
6480 
6481  QCString title=ptitle.stripWhiteSpace();
6482  pd=new PageDef(fileName,startLine,baseName,doc,title);
6483 
6484  pd->setRefItems(sli);
6485  pd->setLanguage(lang);
6486 
6487  if (tagInfo)
6488  {
6489  pd->setReference(tagInfo->tagName);
6490  pd->setFileName(tagInfo->fileName);
6491  }
6492 
6493  //printf("Appending page `%s'\n",baseName.data());
6494  Doxygen::pageSDict->append(baseName,pd);
6495 
6496  if (gd) gd->addPage(pd);
6497 
6498  if (!pd->title().isEmpty())
6499  {
6500  //outputList->writeTitle(pi->name,pi->title);
6501 
6502  // a page name is a label as well!
6503  QCString file;
6504  if (gd)
6505  {
6506  file=gd->getOutputFileBase();
6507  }
6508  else
6509  {
6510  file=pd->getOutputFileBase();
6511  }
6512  SectionInfo *si = Doxygen::sectionDict->find(pd->name());
6513  if (si)
6514  {
6515  if (si->lineNr != -1)
6516  {
6517  warn(file,-1,"multiple use of section label '%s', (first occurrence: %s, line %d)",pd->name().data(),si->fileName.data(),si->lineNr);
6518  }
6519  else
6520  {
6521  warn(file,-1,"multiple use of section label '%s', (first occurrence: %s)",pd->name().data(),si->fileName.data());
6522  }
6523  }
6524  else
6525  {
6526  si=new SectionInfo(
6527  file,-1,pd->name(),pd->title(),SectionInfo::Page,0,pd->getReference());
6528  //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
6529  // si->label.data(),si->definition?si->definition->name().data():"<none>",
6530  // si->fileName.data());
6531  //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
6532  //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
6533  Doxygen::sectionDict->append(pd->name(),si);
6534  }
6535  }
6536  }
6537  return pd;
6538 }
6539 
6540 //----------------------------------------------------------------------------
6541 
6542 void addRefItem(const QList<ListItemInfo> *sli,
6543  const char *key,
6544  const char *prefix, const char *name,const char *title,const char *args,Definition *scope)
6545 {
6546  //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args);
6547  if (sli && key && key[0]!='@') // check for @ to skip anonymous stuff (see bug427012)
6548  {
6549  QListIterator<ListItemInfo> slii(*sli);
6550  ListItemInfo *lii;
6551  for (slii.toFirst();(lii=slii.current());++slii)
6552  {
6553  RefList *refList = Doxygen::xrefLists->find(lii->type);
6554  if (refList
6555  &&
6556  (
6557  // either not a built-in list or the list is enabled
6558  (lii->type!="todo" || Config_getBool(GENERATE_TODOLIST)) &&
6559  (lii->type!="test" || Config_getBool(GENERATE_TESTLIST)) &&
6560  (lii->type!="bug" || Config_getBool(GENERATE_BUGLIST)) &&
6561  (lii->type!="deprecated" || Config_getBool(GENERATE_DEPRECATEDLIST))
6562  )
6563  )
6564  {
6565  RefItem *item = refList->getRefItem(lii->itemId);
6566  ASSERT(item!=0);
6567 
6568  item->prefix = prefix;
6569  item->scope = scope;
6570  item->name = name;
6571  item->title = title;
6572  item->args = args;
6573 
6574  refList->insertIntoList(key,item);
6575 
6576  }
6577  }
6578  }
6579 }
6580 
6582 {
6583  GroupList *groups = d->partOfGroups();
6584  if (groups) // write list of group to which this definition belongs
6585  {
6586  if (root)
6587  {
6588  ol.pushGeneratorState();
6590  ol.writeString("<div class=\"ingroups\">");
6591  }
6592  GroupListIterator gli(*groups);
6593  GroupDef *gd;
6594  bool first=true;
6595  for (gli.toFirst();(gd=gli.current());++gli)
6596  {
6597  if (recursivelyAddGroupListToTitle(ol, gd, FALSE))
6598  {
6599  ol.writeString(" &raquo; ");
6600  }
6601  if (!first) { ol.writeString(" &#124; "); } else first=FALSE;
6602  ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),0,gd->groupTitle());
6603  }
6604  if (root)
6605  {
6606  ol.writeString("</div>");
6607  ol.popGeneratorState();
6608  }
6609  return true;
6610  }
6611  return false;
6612 }
6613 
6615 {
6616  recursivelyAddGroupListToTitle(ol,d,TRUE);
6617 }
6618 
6619 void filterLatexString(FTextStream &t,const char *str,
6620  bool insideTabbing,bool insidePre,bool insideItem,bool keepSpaces)
6621 {
6622  if (str==0) return;
6623  //if (strlen(str)<2) stackTrace();
6624  const unsigned char *p=(const unsigned char *)str;
6625  const unsigned char *q;
6626  int cnt;
6627  unsigned char c;
6628  unsigned char pc='\0';
6629  while (*p)
6630  {
6631  c=*p++;
6632 
6633  if (insidePre)
6634  {
6635  switch(c)
6636  {
6637  case '\\': t << "\\(\\backslash\\)"; break;
6638  case '{': t << "\\{"; break;
6639  case '}': t << "\\}"; break;
6640  case '_': t << "\\_"; break;
6641  case ' ': if (keepSpaces) t << "~"; else t << ' ';
6642  break;
6643  default:
6644  t << (char)c;
6645  break;
6646  }
6647  }
6648  else
6649  {
6650  switch(c)
6651  {
6652  case '#': t << "\\#"; break;
6653  case '$': t << "\\$"; break;
6654  case '%': t << "\\%"; break;
6655  case '^': t << "$^\\wedge$"; break;
6656  case '&': // possibility to have a special symbol
6657  q = p;
6658  cnt = 2; // we have to count & and ; as well
6659  while ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') || (*q >= '0' && *q <= '9'))
6660  {
6661  cnt++;
6662  q++;
6663  }
6664  if (*q == ';')
6665  {
6666  --p; // we need & as well
6667  DocSymbol::SymType res = HtmlEntityMapper::instance()->name2sym(QCString((char *)p).left(cnt));
6668  if (res == DocSymbol::Sym_Unknown)
6669  {
6670  p++;
6671  t << "\\&";
6672  }
6673  else
6674  {
6675  t << HtmlEntityMapper::instance()->latex(res);
6676  q++;
6677  p = q;
6678  }
6679  }
6680  else
6681  {
6682  t << "\\&";
6683  }
6684  break;
6685  case '*': t << "$\\ast$"; break;
6686  case '_': if (!insideTabbing) t << "\\+";
6687  t << "\\_";
6688  if (!insideTabbing) t << "\\+";
6689  break;
6690  case '{': t << "\\{"; break;
6691  case '}': t << "\\}"; break;
6692  case '<': t << "$<$"; break;
6693  case '>': t << "$>$"; break;
6694  case '|': t << "$\\vert$"; break;
6695  case '~': t << "$\\sim$"; break;
6696  case '[': if (Config_getBool(PDF_HYPERLINKS) || insideItem)
6697  t << "\\mbox{[}";
6698  else
6699  t << "[";
6700  break;
6701  case ']': if (pc=='[') t << "$\\,$";
6702  if (Config_getBool(PDF_HYPERLINKS) || insideItem)
6703  t << "\\mbox{]}";
6704  else
6705  t << "]";
6706  break;
6707  case '-': t << "-\\/";
6708  break;
6709  case '\\': t << "\\textbackslash{}";
6710  break;
6711  case '"': t << "\\char`\\\"{}";
6712  break;
6713  case '\'': t << "\\textquotesingle{}";
6714  break;
6715  case ' ': if (keepSpaces) { if (insideTabbing) t << "\\>"; else t << '~'; } else t << ' ';
6716  break;
6717 
6718  default:
6719  //if (!insideTabbing && forceBreaks && c!=' ' && *p!=' ')
6720  if (!insideTabbing &&
6721  ((c>='A' && c<='Z' && pc!=' ' && pc!='\0' && *p) || (c==':' && pc!=':') || (pc=='.' && isId(c)))
6722  )
6723  {
6724  t << "\\+";
6725  }
6726  t << (char)c;
6727  }
6728  }
6729  pc = c;
6730  }
6731 }
6732 
6733 QCString latexEscapeLabelName(const char *s,bool insideTabbing)
6734 {
6735  QGString result;
6736  QCString tmp(qstrlen(s)+1);
6737  FTextStream t(&result);
6738  const char *p=s;
6739  char c;
6740  int i;
6741  while ((c=*p++))
6742  {
6743  switch (c)
6744  {
6745  case '|': t << "\\texttt{\"|}"; break;
6746  case '!': t << "\"!"; break;
6747  case '%': t << "\\%"; break;
6748  case '{': t << "\\lcurly{}"; break;
6749  case '}': t << "\\rcurly{}"; break;
6750  case '~': t << "````~"; break; // to get it a bit better in index together with other special characters
6751  // NOTE: adding a case here, means adding it to while below as well!
6752  default:
6753  i=0;
6754  // collect as long string as possible, before handing it to docify
6755  tmp[i++]=c;
6756  while ((c=*p) && c!='|' && c!='!' && c!='%' && c!='{' && c!='}' && c!='~')
6757  {
6758  tmp[i++]=c;
6759  p++;
6760  }
6761  tmp[i]=0;
6762  filterLatexString(t,tmp.data(),insideTabbing);
6763  break;
6764  }
6765  }
6766  return result.data();
6767 }
6768 
6769 QCString latexEscapeIndexChars(const char *s,bool insideTabbing)
6770 {
6771  QGString result;
6772  QCString tmp(qstrlen(s)+1);
6773  FTextStream t(&result);
6774  const char *p=s;
6775  char c;
6776  int i;
6777  while ((c=*p++))
6778  {
6779  switch (c)
6780  {
6781  case '!': t << "\"!"; break;
6782  case '"': t << "\"\""; break;
6783  case '@': t << "\"@"; break;
6784  case '|': t << "\\texttt{\"|}"; break;
6785  case '[': t << "["; break;
6786  case ']': t << "]"; break;
6787  case '{': t << "\\lcurly{}"; break;
6788  case '}': t << "\\rcurly{}"; break;
6789  // NOTE: adding a case here, means adding it to while below as well!
6790  default:
6791  i=0;
6792  // collect as long string as possible, before handing it to docify
6793  tmp[i++]=c;
6794  while ((c=*p) && c!='"' && c!='@' && c!='[' && c!=']' && c!='!' && c!='{' && c!='}' && c!='|')
6795  {
6796  tmp[i++]=c;
6797  p++;
6798  }
6799  tmp[i]=0;
6800  filterLatexString(t,tmp.data(),insideTabbing);
6801  break;
6802  }
6803  }
6804  return result.data();
6805 }
6806 
6807 QCString latexEscapePDFString(const char *s)
6808 {
6809  QGString result;
6810  FTextStream t(&result);
6811  const char *p=s;
6812  char c;
6813  while ((c=*p++))
6814  {
6815  switch (c)
6816  {
6817  case '\\': t << "\\textbackslash{}"; break;
6818  case '{': t << "\\{"; break;
6819  case '}': t << "\\}"; break;
6820  case '_': t << "\\_"; break;
6821  case '%': t << "\\%"; break;
6822  case '&': t << "\\&"; break;
6823  default:
6824  t << c;
6825  break;
6826  }
6827  }
6828  return result.data();
6829 }
6830 
6831 
6832 QCString rtfFormatBmkStr(const char *name)
6833 {
6834  static QCString g_nextTag( "AAAAAAAAAA" );
6835  static QDict<QCString> g_tagDict( 5003 );
6836 
6837  g_tagDict.setAutoDelete(TRUE);
6838 
6839  // To overcome the 40-character tag limitation, we
6840  // substitute a short arbitrary string for the name
6841  // supplied, and keep track of the correspondence
6842  // between names and strings.
6843  QCString key( name );
6844  QCString* tag = g_tagDict.find( key );
6845  if ( !tag )
6846  {
6847  // This particular name has not yet been added
6848  // to the list. Add it, associating it with the
6849  // next tag value, and increment the next tag.
6850  tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
6851  g_tagDict.insert( key, tag );
6852 
6853  // This is the increment part
6854  char* nxtTag = g_nextTag.rawData() + g_nextTag.length() - 1;
6855  for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
6856  {
6857  if ( ( ++(*nxtTag) ) > 'Z' )
6858  {
6859  *nxtTag = 'A';
6860  }
6861  else
6862  {
6863  // Since there was no carry, we can stop now
6864  break;
6865  }
6866  }
6867  }
6868 
6869  return *tag;
6870 }
6871 
6872 bool checkExtension(const char *fName, const char *ext)
6873 {
6874  return (QCString(fName).right(QCString(ext).length())==ext);
6875 }
6876 
6877 QCString stripExtensionGeneral(const char *fName, const char *ext)
6878 {
6879  QCString result=fName;
6880  if (result.right(QCString(ext).length())==QCString(ext))
6881  {
6882  result=result.left(result.length()-QCString(ext).length());
6883  }
6884  return result;
6885 }
6886 
6887 QCString stripExtension(const char *fName)
6888 {
6890 }
6891 
6892 void replaceNamespaceAliases(QCString &scope,int i)
6893 {
6894  while (i>0)
6895  {
6896  QCString ns = scope.left(i);
6897  QCString *s = Doxygen::namespaceAliasDict[ns];
6898  if (s)
6899  {
6900  scope=*s+scope.right(scope.length()-i);
6901  i=s->length();
6902  }
6903  if (i>0 && ns==scope.left(i)) break;
6904  }
6905 }
6906 
6907 QCString stripPath(const char *s)
6908 {
6909  QCString result=s;
6910  int i=result.findRev('/');
6911  if (i!=-1)
6912  {
6913  result=result.mid(i+1);
6914  }
6915  i=result.findRev('\\');
6916  if (i!=-1)
6917  {
6918  result=result.mid(i+1);
6919  }
6920  return result;
6921 }
6922 
6924 bool containsWord(const QCString &s,const QCString &word)
6925 {
6926  static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6927  int p=0,i,l;
6928  while ((i=wordExp.match(s,p,&l))!=-1)
6929  {
6930  if (s.mid(i,l)==word) return TRUE;
6931  p=i+l;
6932  }
6933  return FALSE;
6934 }
6935 
6936 bool findAndRemoveWord(QCString &s,const QCString &word)
6937 {
6938  static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6939  int p=0,i,l;
6940  while ((i=wordExp.match(s,p,&l))!=-1)
6941  {
6942  if (s.mid(i,l)==word)
6943  {
6944  if (i>0 && isspace((uchar)s.at(i-1)))
6945  i--,l++;
6946  else if (i+l<(int)s.length() && isspace(s.at(i+l)))
6947  l++;
6948  s = s.left(i)+s.mid(i+l); // remove word + spacing
6949  return TRUE;
6950  }
6951  p=i+l;
6952  }
6953  return FALSE;
6954 }
6955 
6964 QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine)
6965 {
6966  const char *p = s.data();
6967  if (p==0) return 0;
6968 
6969  // search for leading empty lines
6970  int i=0,li=-1,l=s.length();
6971  char c;
6972  while ((c=*p++))
6973  {
6974  if (c==' ' || c=='\t' || c=='\r') i++;
6975  else if (c=='\n') i++,li=i,docLine++;
6976  else break;
6977  }
6978 
6979  // search for trailing empty lines
6980  int b=l-1,bi=-1;
6981  p=s.data()+b;
6982  while (b>=0)
6983  {
6984  c=*p; p--;
6985  if (c==' ' || c=='\t' || c=='\r') b--;
6986  else if (c=='\n') bi=b,b--;
6987  else break;
6988  }
6989 
6990  // return whole string if no leading or trailing lines where found
6991  if (li==-1 && bi==-1) return s;
6992 
6993  // return substring
6994  if (bi==-1) bi=l;
6995  if (li==-1) li=0;
6996  if (bi<=li) return 0; // only empty lines
6997  return s.mid(li,bi-li);
6998 }
6999 
7000 #if 0
7001 void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
7002  const QCString &str,bool priority,const QCString &anchor)
7003 {
7004  static bool searchEngine = Config_getBool(SEARCHENGINE);
7005  if (searchEngine)
7006  {
7007  Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
7008  static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
7009  int i,p=0,l;
7010  while ((i=wordPattern.match(str,p,&l))!=-1)
7011  {
7012  Doxygen::searchIndex->addWord(str.mid(i,l),priority);
7013  p=i+l;
7014  }
7015  }
7016 }
7017 #endif
7018 
7019 //--------------------------------------------------------------------------
7020 
7021 static QDict<int> g_extLookup;
7022 
7023 static struct Lang2ExtMap
7024 {
7025  const char *langName;
7026  const char *parserName;
7028 }
7029 g_lang2extMap[] =
7030 {
7031 // language parser parser option
7032  { "idl", "c", SrcLangExt_IDL },
7033  { "java", "c", SrcLangExt_Java },
7034  { "javascript", "c", SrcLangExt_JS },
7035  { "csharp", "c", SrcLangExt_CSharp },
7036  { "d", "c", SrcLangExt_D },
7037  { "php", "c", SrcLangExt_PHP },
7038  { "objective-c", "c", SrcLangExt_ObjC },
7039  { "c", "c", SrcLangExt_Cpp },
7040  { "c++", "c", SrcLangExt_Cpp },
7041  { "python", "python", SrcLangExt_Python },
7042  { "fortran", "fortran", SrcLangExt_Fortran },
7043  { "fortranfree", "fortranfree", SrcLangExt_Fortran },
7044  { "fortranfixed", "fortranfixed", SrcLangExt_Fortran },
7045  { "vhdl", "vhdl", SrcLangExt_VHDL },
7046  { "xml", "xml", SrcLangExt_XML },
7047  { "tcl", "tcl", SrcLangExt_Tcl },
7048  { "md", "md", SrcLangExt_Markdown },
7049  { 0, 0, (SrcLangExt)0 }
7050 };
7051 
7052 bool updateLanguageMapping(const QCString &extension,const QCString &language)
7053 {
7054  const Lang2ExtMap *p = g_lang2extMap;
7055  QCString langName = language.lower();
7056  while (p->langName)
7057  {
7058  if (langName==p->langName) break;
7059  p++;
7060  }
7061  if (!p->langName) return FALSE;
7062 
7063  // found the language
7064  SrcLangExt parserId = p->parserId;
7065  QCString extName = extension.lower();
7066  if (extName.isEmpty()) return FALSE;
7067  if (extName.at(0)!='.') extName.prepend(".");
7068  if (g_extLookup.find(extension)!=0) // language was already register for this ext
7069  {
7070  g_extLookup.remove(extension);
7071  }
7072  //printf("registering extension %s\n",extName.data());
7073  g_extLookup.insert(extName,new int(parserId));
7074  if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
7075  {
7076  err("Failed to assign extension %s to parser %s for language %s\n",
7077  extName.data(),p->parserName,language.data());
7078  }
7079  else
7080  {
7081  //msg("Registered extension %s to language parser %s...\n",
7082  // extName.data(),language.data());
7083  }
7084  return TRUE;
7085 }
7086 
7088 {
7089  // NOTE: when adding an extension, also add the extension in config.xml
7090  g_extLookup.setAutoDelete(TRUE);
7091  // extension parser id
7092  updateLanguageMapping(".dox", "c");
7093  updateLanguageMapping(".txt", "c"); // see bug 760836
7094  updateLanguageMapping(".doc", "c");
7095  updateLanguageMapping(".c", "c");
7096  updateLanguageMapping(".C", "c");
7097  updateLanguageMapping(".cc", "c");
7098  updateLanguageMapping(".CC", "c");
7099  updateLanguageMapping(".cxx", "c");
7100  updateLanguageMapping(".cpp", "c");
7101  updateLanguageMapping(".c++", "c");
7102  updateLanguageMapping(".ii", "c");
7103  updateLanguageMapping(".ixx", "c");
7104  updateLanguageMapping(".ipp", "c");
7105  updateLanguageMapping(".i++", "c");
7106  updateLanguageMapping(".inl", "c");
7107  updateLanguageMapping(".h", "c");
7108  updateLanguageMapping(".H", "c");
7109  updateLanguageMapping(".hh", "c");
7110  updateLanguageMapping(".HH", "c");
7111  updateLanguageMapping(".hxx", "c");
7112  updateLanguageMapping(".hpp", "c");
7113  updateLanguageMapping(".h++", "c");
7114  updateLanguageMapping(".idl", "idl");
7115  updateLanguageMapping(".ddl", "idl");
7116  updateLanguageMapping(".odl", "idl");
7117  updateLanguageMapping(".java", "java");
7118  //updateLanguageMapping(".as", "javascript"); // not officially supported
7119  //updateLanguageMapping(".js", "javascript"); // not officially supported
7120  updateLanguageMapping(".cs", "csharp");
7121  updateLanguageMapping(".d", "d");
7122  updateLanguageMapping(".php", "php");
7123  updateLanguageMapping(".php4", "php");
7124  updateLanguageMapping(".php5", "php");
7125  updateLanguageMapping(".inc", "php");
7126  updateLanguageMapping(".phtml", "php");
7127  updateLanguageMapping(".m", "objective-c");
7128  updateLanguageMapping(".M", "objective-c");
7129  updateLanguageMapping(".mm", "c"); // see bug746361
7130  updateLanguageMapping(".py", "python");
7131  updateLanguageMapping(".pyw", "python");
7132  updateLanguageMapping(".f", "fortran");
7133  updateLanguageMapping(".for", "fortran");
7134  updateLanguageMapping(".f90", "fortran");
7135  updateLanguageMapping(".f95", "fortran");
7136  updateLanguageMapping(".f03", "fortran");
7137  updateLanguageMapping(".f08", "fortran");
7138  updateLanguageMapping(".vhd", "vhdl");
7139  updateLanguageMapping(".vhdl", "vhdl");
7140  updateLanguageMapping(".tcl", "tcl");
7141  updateLanguageMapping(".ucf", "vhdl");
7142  updateLanguageMapping(".qsf", "vhdl");
7143  updateLanguageMapping(".md", "md");
7144  updateLanguageMapping(".markdown", "md");
7145 }
7146 
7148 {
7149  updateLanguageMapping(".xml", "xml");
7150 }
7151 
7152 SrcLangExt getLanguageFromFileName(const QCString fileName)
7153 {
7154  int i = fileName.findRev('.');
7155  if (i!=-1) // name has an extension
7156  {
7157  QCString extStr=fileName.right(fileName.length()-i).lower();
7158  if (!extStr.isEmpty()) // non-empty extension
7159  {
7160  int *pVal=g_extLookup.find(extStr);
7161  if (pVal) // listed extension
7162  {
7163  //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
7164  return (SrcLangExt)*pVal;
7165  }
7166  }
7167  }
7168  //printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
7169  return SrcLangExt_Cpp; // not listed => assume C-ish language.
7170 }
7171 
7172 //--------------------------------------------------------------------------
7173 
7175  const char *n)
7176 {
7177  if (scope==0 ||
7180  )
7181  )
7182  {
7183  scope=Doxygen::globalScope;
7184  }
7185 
7186  QCString name = n;
7187  if (name.isEmpty())
7188  return 0; // no name was given
7189 
7190  DefinitionIntf *di = Doxygen::symbolMap->find(name);
7191  if (di==0)
7192  return 0; // could not find any matching symbols
7193 
7194  // mostly copied from getResolvedClassRec()
7195  QCString explicitScopePart;
7196  int qualifierIndex = computeQualifiedIndex(name);
7197  if (qualifierIndex!=-1)
7198  {
7199  explicitScopePart = name.left(qualifierIndex);
7200  replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
7201  name = name.mid(qualifierIndex+2);
7202  }
7203  //printf("explicitScopePart=%s\n",explicitScopePart.data());
7204 
7205  int minDistance = 10000;
7206  MemberDef *bestMatch = 0;
7207 
7209  {
7210  //printf("multiple matches!\n");
7211  // find the closest closest matching definition
7213  Definition *d;
7214  for (dli.toFirst();(d=dli.current());++dli)
7215  {
7217  {
7218  g_visitedNamespaces.clear();
7219  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
7220  if (distance!=-1 && distance<minDistance)
7221  {
7222  minDistance = distance;
7223  bestMatch = (MemberDef *)d;
7224  //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
7225  }
7226  }
7227  }
7228  }
7229  else if (di->definitionType()==Definition::TypeMember)
7230  {
7231  //printf("unique match!\n");
7232  Definition *d = (Definition *)di;
7233  g_visitedNamespaces.clear();
7234  int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
7235  if (distance!=-1 && distance<minDistance)
7236  {
7237  minDistance = distance;
7238  bestMatch = (MemberDef *)d;
7239  //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
7240  }
7241  }
7242  return bestMatch;
7243 }
7244 
7246 bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
7247 {
7248  MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
7249 
7250  if (bestMatch && bestMatch->isTypedef())
7251  return TRUE; // closest matching symbol is a typedef
7252  else
7253  return FALSE;
7254 }
7255 
7256 const char *writeUtf8Char(FTextStream &t,const char *s)
7257 {
7258  char c=*s++;
7259  t << c;
7260  if (c<0) // multibyte character
7261  {
7262  if (((uchar)c&0xE0)==0xC0)
7263  {
7264  t << *s++; // 11xx.xxxx: >=2 byte character
7265  }
7266  if (((uchar)c&0xF0)==0xE0)
7267  {
7268  t << *s++; // 111x.xxxx: >=3 byte character
7269  }
7270  if (((uchar)c&0xF8)==0xF0)
7271  {
7272  t << *s++; // 1111.xxxx: >=4 byte character
7273  }
7274  if (((uchar)c&0xFC)==0xF8)
7275  {
7276  t << *s++; // 1111.1xxx: >=5 byte character
7277  }
7278  if (((uchar)c&0xFE)==0xFC)
7279  {
7280  t << *s++; // 1111.1xxx: 6 byte character
7281  }
7282  }
7283  return s;
7284 }
7285 
7286 int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos)
7287 {
7288  int bytes=1;
7289  if (startPos>=len) return len;
7290  char c = utf8Str[startPos];
7291  if (c<0) // multibyte utf-8 character
7292  {
7293  if (((uchar)c&0xE0)==0xC0)
7294  {
7295  bytes++; // 11xx.xxxx: >=2 byte character
7296  }
7297  if (((uchar)c&0xF0)==0xE0)
7298  {
7299  bytes++; // 111x.xxxx: >=3 byte character
7300  }
7301  if (((uchar)c&0xF8)==0xF0)
7302  {
7303  bytes++; // 1111.xxxx: >=4 byte character
7304  }
7305  if (((uchar)c&0xFC)==0xF8)
7306  {
7307  bytes++; // 1111.1xxx: >=5 byte character
7308  }
7309  if (((uchar)c&0xFE)==0xFC)
7310  {
7311  bytes++; // 1111.1xxx: 6 byte character
7312  }
7313  }
7314  else if (c=='&') // skip over character entities
7315  {
7316  static QRegExp re1("&#[0-9]+;"); // numerical entity
7317  static QRegExp re2("&[A-Z_a-z]+;"); // named entity
7318  int l1,l2;
7319  int i1 = re1.match(utf8Str,startPos,&l1);
7320  int i2 = re2.match(utf8Str,startPos,&l2);
7321  if (i1!=-1)
7322  {
7323  bytes=l1;
7324  }
7325  else if (i2!=-1)
7326  {
7327  bytes=l2;
7328  }
7329  }
7330  return startPos+bytes;
7331 }
7332 
7333 QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
7334  const QCString &doc,const QCString &fileName,int lineNr)
7335 {
7336  QGString s;
7337  if (doc.isEmpty()) return s.data();
7338  FTextStream t(&s);
7339  DocNode *root = validatingParseDoc(fileName,lineNr,
7340  (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
7341  TextDocVisitor *visitor = new TextDocVisitor(t);
7342  root->accept(visitor);
7343  delete visitor;
7344  delete root;
7345  QCString result = convertCharEntitiesToUTF8(s.data());
7346  int i=0;
7347  int charCnt=0;
7348  int l=result.length();
7349  bool addEllipsis=FALSE;
7350  while ((i=nextUtf8CharPosition(result,l,i))<l)
7351  {
7352  charCnt++;
7353  if (charCnt>=80) break;
7354  }
7355  if (charCnt>=80) // try to truncate the string
7356  {
7357  while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100)
7358  {
7359  charCnt++;
7360  if (result.at(i)>=0 && isspace(result.at(i)))
7361  {
7362  addEllipsis=TRUE;
7363  }
7364  else if (result.at(i)==',' ||
7365  result.at(i)=='.' ||
7366  result.at(i)=='?')
7367  {
7368  break;
7369  }
7370  }
7371  }
7372  if (addEllipsis || charCnt==100) result=result.left(i)+"...";
7373  return result.data();
7374 }
7375 
7376 //--------------------------------------------------------------------------------------
7377 
7378 static QDict<void> aliasesProcessed;
7379 
7380 static QCString expandAliasRec(const QCString s,bool allowRecursion=FALSE);
7381 
7382 struct Marker
7383 {
7384  Marker(int p, int n,int s) : pos(p),number(n),size(s) {}
7385  int pos; // position in the string
7386  int number; // argument number
7387  int size; // size of the marker
7388 };
7389 
7400 static int findEndOfCommand(const char *s)
7401 {
7402  const char *p = s;
7403  char c;
7404  int i=0;
7405  if (p)
7406  {
7407  while ((c=*p) && isId(c)) p++;
7408  if (c=='{')
7409  {
7410  QCString args = extractAliasArgs(p,0);
7411  i+=args.length();
7412  }
7413  i+=p-s;
7414  }
7415  return i;
7416 }
7417 
7422 static QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
7423 {
7424  //printf("----- replaceAliasArguments(val=[%s],args=[%s])\n",aliasValue.data(),argList.data());
7425 
7426  // first make a list of arguments from the comma separated argument list
7427  QList<QCString> args;
7428  args.setAutoDelete(TRUE);
7429  int i,l=(int)argList.length();
7430  int s=0;
7431  for (i=0;i<l;i++)
7432  {
7433  char c = argList.at(i);
7434  if (c==',' && (i==0 || argList.at(i-1)!='\\'))
7435  {
7436  args.append(new QCString(argList.mid(s,i-s)));
7437  s=i+1; // start of next argument
7438  }
7439  else if (c=='@' || c=='\\')
7440  {
7441  // check if this is the start of another aliased command (see bug704172)
7442  i+=findEndOfCommand(argList.data()+i+1);
7443  }
7444  }
7445  if (l>s) args.append(new QCString(argList.right(l-s)));
7446  //printf("found %d arguments\n",args.count());
7447 
7448  // next we look for the positions of the markers and add them to a list
7449  QList<Marker> markerList;
7450  markerList.setAutoDelete(TRUE);
7451  l = aliasValue.length();
7452  int markerStart=0;
7453  int markerEnd=0;
7454  for (i=0;i<l;i++)
7455  {
7456  if (markerStart==0 && aliasValue.at(i)=='\\') // start of a \xx marker
7457  {
7458  markerStart=i+1;
7459  }
7460  else if (markerStart>0 && aliasValue.at(i)>='0' && aliasValue.at(i)<='9')
7461  {
7462  // read digit that make up the marker number
7463  markerEnd=i+1;
7464  }
7465  else
7466  {
7467  if (markerStart>0 && markerEnd>markerStart) // end of marker
7468  {
7469  int markerLen = markerEnd-markerStart;
7470  markerList.append(new Marker(markerStart-1, // include backslash
7471  atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
7472  //printf("found marker at %d with len %d and number %d\n",
7473  // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
7474  }
7475  markerStart=0; // outside marker
7476  markerEnd=0;
7477  }
7478  }
7479  if (markerStart>0)
7480  {
7481  markerEnd=l;
7482  }
7483  if (markerStart>0 && markerEnd>markerStart)
7484  {
7485  int markerLen = markerEnd-markerStart;
7486  markerList.append(new Marker(markerStart-1, // include backslash
7487  atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
7488  //printf("found marker at %d with len %d and number %d\n",
7489  // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
7490  }
7491 
7492  // then we replace the markers with the corresponding arguments in one pass
7493  QCString result;
7494  int p=0;
7495  for (i=0;i<(int)markerList.count();i++)
7496  {
7497  Marker *m = markerList.at(i);
7498  result+=aliasValue.mid(p,m->pos-p);
7499  //printf("part before marker %d: '%s'\n",i,aliasValue.mid(p,m->pos-p).data());
7500  if (m->number>0 && m->number<=(int)args.count()) // valid number
7501  {
7502  result+=expandAliasRec(*args.at(m->number-1),TRUE);
7503  //printf("marker index=%d pos=%d number=%d size=%d replacement %s\n",i,m->pos,m->number,m->size,
7504  // args.at(m->number-1)->data());
7505  }
7506  p=m->pos+m->size; // continue after the marker
7507  }
7508  result+=aliasValue.right(l-p); // append remainder
7509  //printf("string after replacement of markers: '%s'\n",result.data());
7510 
7511  // expand the result again
7512  result = substitute(result,"\\{","{");
7513  result = substitute(result,"\\}","}");
7514  result = expandAliasRec(substitute(result,"\\,",","));
7515 
7516  return result;
7517 }
7518 
7519 static QCString escapeCommas(const QCString &s)
7520 {
7521  QGString result;
7522  const char *p = s.data();
7523  char c,pc=0;
7524  while ((c=*p++))
7525  {
7526  if (c==',' && pc!='\\')
7527  {
7528  result+="\\,";
7529  }
7530  else
7531  {
7532  result+=c;
7533  }
7534  pc=c;
7535  }
7536  result+='\0';
7537  //printf("escapeCommas: '%s'->'%s'\n",s.data(),result.data());
7538  return result.data();
7539 }
7540 
7541 static QCString expandAliasRec(const QCString s,bool allowRecursion)
7542 {
7543  QCString result;
7544  static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
7545  QCString value=s;
7546  int i,p=0,l;
7547  while ((i=cmdPat.match(value,p,&l))!=-1)
7548  {
7549  result+=value.mid(p,i-p);
7550  QCString args = extractAliasArgs(value,i+l);
7551  bool hasArgs = !args.isEmpty(); // found directly after command
7552  int argsLen = args.length();
7553  QCString cmd = value.mid(i+1,l-1);
7554  QCString cmdNoArgs = cmd;
7555  int numArgs=0;
7556  if (hasArgs)
7557  {
7558  numArgs = countAliasArguments(args);
7559  cmd += QCString().sprintf("{%d}",numArgs); // alias name + {n}
7560  }
7561  QCString *aliasText=Doxygen::aliasDict.find(cmd);
7562  if (numArgs>1 && aliasText==0)
7563  { // in case there is no command with numArgs parameters, but there is a command with 1 parameter,
7564  // we also accept all text as the argument of that command (so you don't have to escape commas)
7565  aliasText=Doxygen::aliasDict.find(cmdNoArgs+"{1}");
7566  if (aliasText)
7567  {
7568  cmd = cmdNoArgs+"{1}";
7569  args = escapeCommas(args); // escape , so that everything is seen as one argument
7570  }
7571  }
7572  //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s' aliasText=%s\n",
7573  // s.data(),cmd.data(),numArgs,args.data(),aliasText?aliasText->data():"<none>");
7574  if ((allowRecursion || aliasesProcessed.find(cmd)==0) && aliasText) // expand the alias
7575  {
7576  //printf("is an alias!\n");
7577  if (!allowRecursion) aliasesProcessed.insert(cmd,(void *)0x8);
7578  QCString val = *aliasText;
7579  if (hasArgs)
7580  {
7581  val = replaceAliasArguments(val,args);
7582  //printf("replace '%s'->'%s' args='%s'\n",
7583  // aliasText->data(),val.data(),args.data());
7584  }
7585  result+=expandAliasRec(val);
7586  if (!allowRecursion) aliasesProcessed.remove(cmd);
7587  p=i+l;
7588  if (hasArgs) p+=argsLen+2;
7589  }
7590  else // command is not an alias
7591  {
7592  //printf("not an alias!\n");
7593  result+=value.mid(i,l);
7594  p=i+l;
7595  }
7596  }
7597  result+=value.right(value.length()-p);
7598 
7599  //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
7600  return result;
7601 }
7602 
7603 
7604 int countAliasArguments(const QCString argList)
7605 {
7606  int count=1;
7607  int l = argList.length();
7608  int i;
7609  for (i=0;i<l;i++)
7610  {
7611  char c = argList.at(i);
7612  if (c==',' && (i==0 || argList.at(i-1)!='\\')) count++;
7613  else if (c=='@' || c=='\\')
7614  {
7615  // check if this is the start of another aliased command (see bug704172)
7616  i+=findEndOfCommand(argList.data()+i+1);
7617  }
7618  }
7619  //printf("countAliasArguments=%d\n",count);
7620  return count;
7621 }
7622 
7623 QCString extractAliasArgs(const QCString &args,int pos)
7624 {
7625  int i;
7626  int bc=0;
7627  char prevChar=0;
7628  if (args.at(pos)=='{') // alias has argument
7629  {
7630  for (i=pos;i<(int)args.length();i++)
7631  {
7632  if (prevChar!='\\')
7633  {
7634  if (args.at(i)=='{') bc++;
7635  if (args.at(i)=='}') bc--;
7636  prevChar=args.at(i);
7637  }
7638  else
7639  {
7640  prevChar=0;
7641  }
7642 
7643  if (bc==0)
7644  {
7645  //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
7646  return args.mid(pos+1,i-pos-1);
7647  }
7648  }
7649  }
7650  return "";
7651 }
7652 
7653 QCString resolveAliasCmd(const QCString aliasCmd)
7654 {
7655  QCString result;
7656  aliasesProcessed.clear();
7657  //printf("Expanding: '%s'\n",aliasCmd.data());
7658  result = expandAliasRec(aliasCmd);
7659  //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
7660  return result;
7661 }
7662 
7663 QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
7664 {
7665  QCString result;
7666  aliasesProcessed.clear();
7667  // avoid expanding this command recursively
7668  aliasesProcessed.insert(aliasName,(void *)0x8);
7669  // expand embedded commands
7670  //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
7671  result = expandAliasRec(aliasValue);
7672  //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
7673  return result;
7674 }
7675 
7677 {
7678  if (al==0) return;
7680  ArgumentListIterator ali(*al);
7681  Argument *a;
7682  for (;(a=ali.current());++ali)
7683  {
7684  ol.startConstraintParam();
7685  ol.parseText(a->name);
7686  ol.endConstraintParam();
7687  ol.startConstraintType();
7688  linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
7689  ol.endConstraintType();
7690  ol.startConstraintDocs();
7691  ol.generateDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
7692  ol.endConstraintDocs();
7693  }
7694  ol.endConstraintList();
7695 }
7696 
7697 //----------------------------------------------------------------------------
7698 
7700 {
7701 #ifdef TRACINGSUPPORT
7702  void *backtraceFrames[128];
7703  int frameCount = backtrace(backtraceFrames, 128);
7704  static char cmd[40960];
7705  char *p = cmd;
7706  p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
7707  for (int x = 0; x < frameCount; x++)
7708  {
7709  p += sprintf(p,"%p ", backtraceFrames[x]);
7710  }
7711  fprintf(stderr,"========== STACKTRACE START ==============\n");
7712  if (FILE *fp = popen(cmd, "r"))
7713  {
7714  char resBuf[512];
7715  while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
7716  {
7717  fwrite(resBuf, 1, len, stderr);
7718  }
7719  pclose(fp);
7720  }
7721  fprintf(stderr,"============ STACKTRACE END ==============\n");
7722  //fprintf(stderr,"%s\n", frameStrings[x]);
7723 #endif
7724 }
7725 
7726 static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size,
7727  const char *inputEncoding,const char *outputEncoding)
7728 {
7729  if (inputEncoding==0 || outputEncoding==0) return size;
7730  if (qstricmp(inputEncoding,outputEncoding)==0) return size;
7731  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
7732  if (cd==(void *)(-1))
7733  {
7734  err("unsupported character conversion: '%s'->'%s': %s\n"
7735  "Check the INPUT_ENCODING setting in the config file!\n",
7736  inputEncoding,outputEncoding,strerror(errno));
7737  exit(1);
7738  }
7739  int tmpBufSize=size*4+1;
7740  BufStr tmpBuf(tmpBufSize);
7741  size_t iLeft=size;
7742  size_t oLeft=tmpBufSize;
7743  char *srcPtr = srcBuf.data();
7744  char *dstPtr = tmpBuf.data();
7745  uint newSize=0;
7746  if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
7747  {
7748  newSize = tmpBufSize-(int)oLeft;
7749  srcBuf.shrink(newSize);
7750  strncpy(srcBuf.data(),tmpBuf.data(),newSize);
7751  //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
7752  }
7753  else
7754  {
7755  err("%s: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
7756  fileName,inputEncoding,outputEncoding);
7757  exit(1);
7758  }
7760  return newSize;
7761 }
7762 
7764 bool readInputFile(const char *fileName,BufStr &inBuf,bool filter,bool isSourceCode)
7765 {
7766  // try to open file
7767  int size=0;
7768  //uint oldPos = dest.curPos();
7769  //printf(".......oldPos=%d\n",oldPos);
7770 
7771  QFileInfo fi(fileName);
7772  if (!fi.exists()) return FALSE;
7773  QCString filterName = getFileFilter(fileName,isSourceCode);
7774  if (filterName.isEmpty() || !filter)
7775  {
7776  QFile f(fileName);
7777  if (!f.open(IO_ReadOnly))
7778  {
7779  err("could not open file %s\n",fileName);
7780  return FALSE;
7781  }
7782  size=fi.size();
7783  // read the file
7784  inBuf.skip(size);
7785  if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
7786  {
7787  err("problems while reading file %s\n",fileName);
7788  return FALSE;
7789  }
7790  }
7791  else
7792  {
7793  QCString cmd=filterName+" \""+fileName+"\"";
7794  Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
7795  FILE *f=portable_popen(cmd,"r");
7796  if (!f)
7797  {
7798  err("could not execute filter %s\n",filterName.data());
7799  return FALSE;
7800  }
7801  const int bufSize=1024;
7802  char buf[bufSize];
7803  int numRead;
7804  while ((numRead=(int)fread(buf,1,bufSize,f))>0)
7805  {
7806  //printf(">>>>>>>>Reading %d bytes\n",numRead);
7807  inBuf.addArray(buf,numRead),size+=numRead;
7808  }
7809  portable_pclose(f);
7810  inBuf.at(inBuf.curPos()) ='\0';
7811  Debug::print(Debug::FilterOutput, 0, "Filter output\n");
7812  Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",qPrint(inBuf));
7813  }
7814 
7815  int start=0;
7816  if (size>=2 &&
7817  ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM
7818  (inBuf.at(0)==-2 && inBuf.at(1)==-1) // big endian BOM
7819  )
7820  ) // UCS-2 encoded file
7821  {
7822  transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
7823  "UCS-2","UTF-8");
7824  }
7825  else if (size>=3 &&
7826  (uchar)inBuf.at(0)==0xEF &&
7827  (uchar)inBuf.at(1)==0xBB &&
7828  (uchar)inBuf.at(2)==0xBF
7829  ) // UTF-8 encoded file
7830  {
7831  inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
7832  }
7833  else // transcode according to the INPUT_ENCODING setting
7834  {
7835  // do character transcoding if needed.
7836  transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
7837  Config_getString(INPUT_ENCODING),"UTF-8");
7838  }
7839 
7840  //inBuf.addChar('\n'); /* to prevent problems under Windows ? */
7841 
7842  // and translate CR's
7843  size=inBuf.curPos()-start;
7844  int newSize=filterCRLF(inBuf.data()+start,size);
7845  //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
7846  if (newSize!=size) // we removed chars
7847  {
7848  inBuf.shrink(newSize); // resize the array
7849  //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
7850  }
7851  inBuf.addChar(0);
7852  return TRUE;
7853 }
7854 
7855 // Replace %word by word in title
7856 QCString filterTitle(const QCString &title)
7857 {
7858  QCString tf;
7859  static QRegExp re("%[A-Z_a-z]");
7860  int p=0,i,l;
7861  while ((i=re.match(title,p,&l))!=-1)
7862  {
7863  tf+=title.mid(p,i-p);
7864  tf+=title.mid(i+1,l-1); // skip %
7865  p=i+l;
7866  }
7867  tf+=title.right(title.length()-p);
7868  return tf;
7869 }
7870 
7871 //----------------------------------------------------------------------------
7872 // returns TRUE if the name of the file represented by `fi' matches
7873 // one of the file patterns in the `patList' list.
7874 
7875 bool patternMatch(const QFileInfo &fi,const QStrList *patList)
7876 {
7877  bool found=FALSE;
7878  if (patList)
7879  {
7880  QStrListIterator it(*patList);
7881  QCString pattern;
7882 
7883  QCString fn = fi.fileName().data();
7884  QCString fp = fi.filePath().data();
7885  QCString afp= fi.absFilePath().data();
7886 
7887  for (it.toFirst();(pattern=it.current());++it)
7888  {
7889  if (!pattern.isEmpty())
7890  {
7891  int i=pattern.find('=');
7892  if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
7893 
7894 #if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
7895  QRegExp re(pattern,FALSE,TRUE); // case insensitive match
7896 #else // unix
7897  QRegExp re(pattern,TRUE,TRUE); // case sensitive match
7898 #endif
7899  found = re.match(fn)!=-1 ||
7900  re.match(fp)!=-1 ||
7901  re.match(afp)!=-1;
7902  if (found) break;
7903  //printf("Matching `%s' against pattern `%s' found=%d\n",
7904  // fi->fileName().data(),pattern.data(),found);
7905  }
7906  }
7907  }
7908  return found;
7909 }
7910 
7911 #if 0 // move to HtmlGenerator::writeSummaryLink
7912 void writeSummaryLink(OutputList &ol,const char *label,const char *title,
7913  bool &first,const char *file)
7914 {
7915  if (first)
7916  {
7917  ol.writeString(" <div class=\"summary\">\n");
7918  first=FALSE;
7919  }
7920  else
7921  {
7922  ol.writeString(" &#124;\n");
7923  }
7924  if (file)
7925  {
7926  ol.writeString("<a href=\"");
7927  ol.writeString(file);
7929  }
7930  else
7931  {
7932  ol.writeString("<a href=\"#");
7933  ol.writeString(label);
7934  }
7935  ol.writeString("\">");
7936  ol.writeString(title);
7937  ol.writeString("</a>");
7938 }
7939 #endif
7940 
7942 {
7943  static bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW);
7944  if (extLinksInWindow) return "target=\"_blank\" "; else return "";
7945 }
7946 
7947 QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
7948 {
7949  QCString result;
7950  if (!ref.isEmpty())
7951  {
7952  QCString *dest = Doxygen::tagDestinationDict[ref];
7953  if (dest)
7954  {
7955  result = *dest;
7956  int l = result.length();
7957  if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
7958  { // relative path -> prepend relPath.
7959  result.prepend(relPath);
7960  l+=relPath.length();
7961  }
7962  if (!href){
7963  result.prepend("doxygen=\""+ref+":");
7964  l+=10+ref.length();
7965  }
7966  if (l>0 && result.at(l-1)!='/') result+='/';
7967  if (!href) result.append("\" ");
7968  }
7969  }
7970  else
7971  {
7972  result = relPath;
7973  }
7974  return result;
7975 }
7976 
7980 void writeColoredImgData(const char *dir,ColoredImgDataItem data[])
7981 {
7982  static int hue = Config_getInt(HTML_COLORSTYLE_HUE);
7983  static int sat = Config_getInt(HTML_COLORSTYLE_SAT);
7984  static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
7985  while (data->name)
7986  {
7987  QCString fileName;
7988  fileName=(QCString)dir+"/"+data->name;
7989  QFile f(fileName);
7990  if (f.open(IO_WriteOnly))
7991  {
7992  ColoredImage img(data->width,data->height,data->content,data->alpha,
7993  sat,hue,gamma);
7994  img.save(fileName);
7995  }
7996  else
7997  {
7998  fprintf(stderr,"Warning: Cannot open file %s for writing\n",data->name);
7999  }
8001  data++;
8002  }
8003 }
8004 
8010 QCString replaceColorMarkers(const char *str)
8011 {
8012  QCString result;
8013  QCString s=str;
8014  if (s.isEmpty()) return result;
8015  static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]");
8016  static const char hex[] = "0123456789ABCDEF";
8017  static int hue = Config_getInt(HTML_COLORSTYLE_HUE);
8018  static int sat = Config_getInt(HTML_COLORSTYLE_SAT);
8019  static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
8020  int i,l,sl=s.length(),p=0;
8021  while ((i=re.match(s,p,&l))!=-1)
8022  {
8023  result+=s.mid(p,i-p);
8024  QCString lumStr = s.mid(i+2,l-2);
8025 #define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
8026  ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
8027  ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
8028 
8029  double r,g,b;
8030  int red,green,blue;
8031  int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
8032  ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
8033  pow(level/255.0,gamma/100.0),&r,&g,&b);
8034  red = (int)(r*255.0);
8035  green = (int)(g*255.0);
8036  blue = (int)(b*255.0);
8037  char colStr[8];
8038  colStr[0]='#';
8039  colStr[1]=hex[red>>4];
8040  colStr[2]=hex[red&0xf];
8041  colStr[3]=hex[green>>4];
8042  colStr[4]=hex[green&0xf];
8043  colStr[5]=hex[blue>>4];
8044  colStr[6]=hex[blue&0xf];
8045  colStr[7]=0;
8046  //printf("replacing %s->%s (level=%d)\n",lumStr.data(),colStr,level);
8047  result+=colStr;
8048  p=i+l;
8049  }
8050  result+=s.right(sl-p);
8051  return result;
8052 }
8053 
8057 bool copyFile(const QCString &src,const QCString &dest)
8058 {
8059  QFile sf(src);
8060  if (sf.open(IO_ReadOnly))
8061  {
8062  QFileInfo fi(src);
8063  QFile df(dest);
8064  if (df.open(IO_WriteOnly))
8065  {
8066  char *buffer = new char[fi.size()];
8067  sf.readBlock(buffer,fi.size());
8068  df.writeBlock(buffer,fi.size());
8069  df.flush();
8070  delete[] buffer;
8071  }
8072  else
8073  {
8074  err("could not write to file %s\n",dest.data());
8075  return FALSE;
8076  }
8077  }
8078  else
8079  {
8080  err("could not open user specified file %s\n",src.data());
8081  return FALSE;
8082  }
8083  return TRUE;
8084 }
8085 
8090 QCString extractBlock(const QCString text,const QCString marker)
8091 {
8092  QCString result;
8093  int p=0,i;
8094  bool found=FALSE;
8095 
8096  // find the character positions of the markers
8097  int m1 = text.find(marker);
8098  if (m1==-1) return result;
8099  int m2 = text.find(marker,m1+marker.length());
8100  if (m2==-1) return result;
8101 
8102  // find start and end line positions for the markers
8103  int l1=-1,l2=-1;
8104  while (!found && (i=text.find('\n',p))!=-1)
8105  {
8106  found = (p<=m1 && m1<i); // found the line with the start marker
8107  p=i+1;
8108  }
8109  l1=p;
8110  int lp=i;
8111  if (found)
8112  {
8113  while ((i=text.find('\n',p))!=-1)
8114  {
8115  if (p<=m2 && m2<i) // found the line with the end marker
8116  {
8117  l2=p;
8118  break;
8119  }
8120  p=i+1;
8121  lp=i;
8122  }
8123  }
8124  if (l2==-1) // marker at last line without newline (see bug706874)
8125  {
8126  l2=lp;
8127  }
8128  //printf("text=[%s]\n",text.mid(l1,l2-l1).data());
8129  return l2>l1 ? text.mid(l1,l2-l1) : QCString();
8130 }
8131 
8135 int lineBlock(const QCString text,const QCString marker)
8136 {
8137  int result = 1;
8138  int p=0,i;
8139  bool found=FALSE;
8140 
8141  // find the character positions of the first marker
8142  int m1 = text.find(marker);
8143  if (m1==-1) return result;
8144 
8145  // find start line positions for the markers
8146  while (!found && (i=text.find('\n',p))!=-1)
8147  {
8148  found = (p<=m1 && m1<i); // found the line with the start marker
8149  p=i+1;
8150  result++;
8151  }
8152  return result;
8153 }
8154 
8156 QCString langToString(SrcLangExt lang)
8157 {
8158  switch(lang)
8159  {
8160  case SrcLangExt_Unknown: return "Unknown";
8161  case SrcLangExt_IDL: return "IDL";
8162  case SrcLangExt_Java: return "Java";
8163  case SrcLangExt_CSharp: return "C#";
8164  case SrcLangExt_D: return "D";
8165  case SrcLangExt_PHP: return "PHP";
8166  case SrcLangExt_ObjC: return "Objective-C";
8167  case SrcLangExt_Cpp: return "C++";
8168  case SrcLangExt_JS: return "Javascript";
8169  case SrcLangExt_Python: return "Python";
8170  case SrcLangExt_Fortran: return "Fortran";
8171  case SrcLangExt_VHDL: return "VHDL";
8172  case SrcLangExt_XML: return "XML";
8173  case SrcLangExt_Tcl: return "Tcl";
8174  case SrcLangExt_Markdown: return "Markdown";
8175  }
8176  return "Unknown";
8177 }
8178 
8180 QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope)
8181 {
8182  if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp || lang==SrcLangExt_VHDL || lang==SrcLangExt_Python)
8183  {
8184  return ".";
8185  }
8186  else if (lang==SrcLangExt_PHP && !classScope)
8187  {
8188  return "\\";
8189  }
8190  else
8191  {
8192  return "::";
8193  }
8194 }
8195 
8199 QCString correctURL(const QCString &url,const QCString &relPath)
8200 {
8201  QCString result = url;
8202  if (!relPath.isEmpty() &&
8203  url.left(5)!="http:" && url.left(6)!="https:" &&
8204  url.left(4)!="ftp:" && url.left(5)!="file:")
8205  {
8206  result.prepend(relPath);
8207  }
8208  return result;
8209 }
8210 
8211 //---------------------------------------------------------------------------
8212 
8214 {
8215  static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
8216  static bool extractPackage = Config_getBool(EXTRACT_PACKAGE);
8217 
8218  return (prot!=Private && prot!=Package) ||
8219  (prot==Private && extractPrivate) ||
8220  (prot==Package && extractPackage);
8221 }
8222 
8223 //---------------------------------------------------------------------------
8224 
8225 QCString stripIndentation(const QCString &s)
8226 {
8227  if (s.isEmpty()) return s; // empty string -> we're done
8228 
8229  //printf("stripIndentation:\n%s\n------\n",s.data());
8230  // compute minimum indentation over all lines
8231  const char *p=s.data();
8232  char c;
8233  int indent=0;
8234  int minIndent=1000000; // "infinite"
8235  bool searchIndent=TRUE;
8236  static int tabSize=Config_getInt(TAB_SIZE);
8237  while ((c=*p++))
8238  {
8239  if (c=='\t') indent+=tabSize - (indent%tabSize);
8240  else if (c=='\n') indent=0,searchIndent=TRUE;
8241  else if (c==' ') indent++;
8242  else if (searchIndent)
8243  {
8244  searchIndent=FALSE;
8245  if (indent<minIndent) minIndent=indent;
8246  }
8247  }
8248 
8249  // no indent to remove -> we're done
8250  if (minIndent==0) return s;
8251 
8252  // remove minimum indentation for each line
8253  QGString result;
8254  p=s.data();
8255  indent=0;
8256  while ((c=*p++))
8257  {
8258  if (c=='\n') // start of new line
8259  {
8260  indent=0;
8261  result+=c;
8262  }
8263  else if (indent<minIndent) // skip until we reach minIndent
8264  {
8265  if (c=='\t')
8266  {
8267  int newIndent = indent+tabSize-(indent%tabSize);
8268  int i=newIndent;
8269  while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
8270  {
8271  result+=' ';
8272  i--;
8273  }
8274  indent=newIndent;
8275  }
8276  else // space
8277  {
8278  indent++;
8279  }
8280  }
8281  else // copy anything until the end of the line
8282  {
8283  result+=c;
8284  }
8285  }
8286 
8287  result+='\0';
8288  return result.data();
8289 }
8290 
8291 
8292 bool fileVisibleInIndex(FileDef *fd,bool &genSourceFile)
8293 {
8294  static bool allExternals = Config_getBool(ALLEXTERNALS);
8295  bool isDocFile = fd->isDocumentationFile();
8296  genSourceFile = !isDocFile && fd->generateSourceFile();
8297  return ( ((allExternals && fd->isLinkable()) ||
8298  fd->isLinkableInProject()
8299  ) &&
8300  !isDocFile
8301  );
8302 }
8303 
8305 {
8306  static bool referencedByRelation = Config_getBool(REFERENCED_BY_RELATION);
8307  static bool referencesRelation = Config_getBool(REFERENCES_RELATION);
8308 
8309  //printf("--> addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data());
8310  if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types
8311  if ((referencedByRelation || dst->hasCallerGraph()) &&
8312  src->showInCallGraph()
8313  )
8314  {
8315  dst->addSourceReferencedBy(src);
8316  MemberDef *mdDef = dst->memberDefinition();
8317  if (mdDef)
8318  {
8319  mdDef->addSourceReferencedBy(src);
8320  }
8321  MemberDef *mdDecl = dst->memberDeclaration();
8322  if (mdDecl)
8323  {
8324  mdDecl->addSourceReferencedBy(src);
8325  }
8326  }
8327  if ((referencesRelation || src->hasCallGraph()) &&
8328  src->showInCallGraph()
8329  )
8330  {
8331  src->addSourceReferences(dst);
8332  MemberDef *mdDef = src->memberDefinition();
8333  if (mdDef)
8334  {
8335  mdDef->addSourceReferences(dst);
8336  }
8337  MemberDef *mdDecl = src->memberDeclaration();
8338  if (mdDecl)
8339  {
8340  mdDecl->addSourceReferences(dst);
8341  }
8342  }
8343 }
8344 
8345 //--------------------------------------------------------------------------------------
8346 
8355 uint getUtf8Code( const QCString& s, int idx )
8356 {
8357  const int length = s.length();
8358  if (idx >= length) { return 0; }
8359  const uint c0 = (uchar)s.at(idx);
8360  if ( c0 < 0xC2 || c0 >= 0xF8 ) // 1 byte character
8361  {
8362  return c0;
8363  }
8364  if (idx+1 >= length) { return 0; }
8365  const uint c1 = ((uchar)s.at(idx+1)) & 0x3f;
8366  if ( c0 < 0xE0 ) // 2 byte character
8367  {
8368  return ((c0 & 0x1f) << 6) | c1;
8369  }
8370  if (idx+2 >= length) { return 0; }
8371  const uint c2 = ((uchar)s.at(idx+2)) & 0x3f;
8372  if ( c0 < 0xF0 ) // 3 byte character
8373  {
8374  return ((c0 & 0x0f) << 12) | (c1 << 6) | c2;
8375  }
8376  if (idx+3 >= length) { return 0; }
8377  // 4 byte character
8378  const uint c3 = ((uchar)s.at(idx+3)) & 0x3f;
8379  return ((c0 & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
8380 }
8381 
8382 
8391 uint getUtf8CodeToLower( const QCString& s, int idx )
8392 {
8393  const uint v = getUtf8Code( s, idx );
8394  return v < 0x7f ? tolower( v ) : v;
8395 }
8396 
8397 
8406 uint getUtf8CodeToUpper( const QCString& s, int idx )
8407 {
8408  const uint v = getUtf8Code( s, idx );
8409  return v < 0x7f ? toupper( v ) : v;
8410 }
8411 
8412 //--------------------------------------------------------------------------------------
8413 
8414 bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses)
8415 {
8416  if (nd->getNamespaceSDict())
8417  {
8419  NamespaceDef *cnd;
8420  for (cnli.toFirst();(cnd=cnli.current());++cnli)
8421  {
8422  if (cnd->isLinkableInProject() && cnd->localName().find('@')==-1)
8423  {
8424  return TRUE;
8425  }
8426  else if (namespaceHasVisibleChild(cnd,includeClasses))
8427  {
8428  return TRUE;
8429  }
8430  }
8431  }
8432  if (includeClasses && nd->getClassSDict())
8433  {
8434  ClassSDict::Iterator cli(*nd->getClassSDict());
8435  ClassDef *cd;
8436  for (;(cd=cli.current());++cli)
8437  {
8438  if (cd->isLinkableInProject() && cd->templateMaster()==0)
8439  {
8440  return TRUE;
8441  }
8442  }
8443  }
8444  return FALSE;
8445 }
8446 
8447 //----------------------------------------------------------------------------
8448 
8450 {
8451  static bool allExternals = Config_getBool(ALLEXTERNALS);
8452  return (allExternals && cd->isLinkable()) || cd->isLinkableInProject();
8453 }
8454 
8455 //----------------------------------------------------------------------------
8456 
8457 QCString extractDirection(QCString &docs)
8458 {
8459  QRegExp re("\\[[^\\]]+\\]"); // [...]
8460  int l=0;
8461  if (re.match(docs,0,&l)==0)
8462  {
8463  int inPos = docs.find("in", 1,FALSE);
8464  int outPos = docs.find("out",1,FALSE);
8465  bool input = inPos!=-1 && inPos<l;
8466  bool output = outPos!=-1 && outPos<l;
8467  if (input || output) // in,out attributes
8468  {
8469  docs = docs.mid(l); // strip attributes
8470  if (input && output) return "[in,out]";
8471  else if (input) return "[in]";
8472  else if (output) return "[out]";
8473  }
8474  }
8475  return QCString();
8476 }
8477 
8478 //-----------------------------------------------------------
8479 
8494  MemberListType inListType,
8495  Protection inProt,
8496  int *outListType1,
8497  int *outListType2
8498  )
8499 {
8500  static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
8501  // default representing 1-1 mapping
8502  *outListType1=inListType;
8503  *outListType2=-1;
8504  if (inProt==Public)
8505  {
8506  switch (inListType) // in the private section of the derived class,
8507  // the private section of the base class should not
8508  // be visible
8509  {
8516  *outListType1=-1;
8517  *outListType2=-1;
8518  break;
8519  default:
8520  break;
8521  }
8522  }
8523  else if (inProt==Protected) // Protected inheritance
8524  {
8525  switch (inListType) // in the protected section of the derived class,
8526  // both the public and protected members are shown
8527  // as protected
8528  {
8541  *outListType1=-1;
8542  *outListType2=-1;
8543  break;
8544 
8546  *outListType2=MemberListType_pubMethods;
8547  break;
8549  *outListType2=MemberListType_pubStaticMethods;
8550  break;
8552  *outListType2=MemberListType_pubSlots;
8553  break;
8555  *outListType2=MemberListType_pubAttribs;
8556  break;
8558  *outListType2=MemberListType_pubStaticAttribs;
8559  break;
8561  *outListType2=MemberListType_pubTypes;
8562  break;
8563  default:
8564  break;
8565  }
8566  }
8567  else if (inProt==Private)
8568  {
8569  switch (inListType) // in the private section of the derived class,
8570  // both the public and protected members are shown
8571  // as private
8572  {
8585  *outListType1=-1;
8586  *outListType2=-1;
8587  break;
8588 
8590  if (extractPrivate)
8591  {
8592  *outListType1=MemberListType_pubMethods;
8593  *outListType2=MemberListType_proMethods;
8594  }
8595  else
8596  {
8597  *outListType1=-1;
8598  *outListType2=-1;
8599  }
8600  break;
8602  if (extractPrivate)
8603  {
8604  *outListType1=MemberListType_pubStaticMethods;
8605  *outListType2=MemberListType_proStaticMethods;
8606  }
8607  else
8608  {
8609  *outListType1=-1;
8610  *outListType2=-1;
8611  }
8612  break;
8614  if (extractPrivate)
8615  {
8616  *outListType1=MemberListType_pubSlots;
8617  *outListType2=MemberListType_proSlots;
8618  }
8619  else
8620  {
8621  *outListType1=-1;
8622  *outListType2=-1;
8623  }
8624  break;
8626  if (extractPrivate)
8627  {
8628  *outListType1=MemberListType_pubAttribs;
8629  *outListType2=MemberListType_proAttribs;
8630  }
8631  else
8632  {
8633  *outListType1=-1;
8634  *outListType2=-1;
8635  }
8636  break;
8638  if (extractPrivate)
8639  {
8640  *outListType1=MemberListType_pubStaticAttribs;
8641  *outListType2=MemberListType_proStaticAttribs;
8642  }
8643  else
8644  {
8645  *outListType1=-1;
8646  *outListType2=-1;
8647  }
8648  break;
8650  if (extractPrivate)
8651  {
8652  *outListType1=MemberListType_pubTypes;
8653  *outListType2=MemberListType_proTypes;
8654  }
8655  else
8656  {
8657  *outListType1=-1;
8658  *outListType2=-1;
8659  }
8660  break;
8661  default:
8662  break;
8663  }
8664  }
8665  //printf("convertProtectionLevel(type=%d prot=%d): %d,%d\n",
8666  // inListType,inProt,*outListType1,*outListType2);
8667 }
8668 
8670 {
8671  if (Doxygen::mainPage==0) return FALSE;
8672  if (Doxygen::mainPage->title().isEmpty()) return FALSE;
8673  if (Doxygen::mainPage->title().lower()=="notitle") return FALSE;
8674  return TRUE;
8675 }
8676 
8677 QCString getDotImageExtension(void)
8678 {
8679  QCString imgExt = Config_getEnum(DOT_IMAGE_FORMAT);
8680  imgExt = imgExt.replace( QRegExp(":.*"), "" );
8681  return imgExt;
8682 }
8683 
8684 bool openOutputFile(const char *outFile,QFile &f)
8685 {
8686  bool fileOpened=FALSE;
8687  bool writeToStdout=(outFile[0]=='-' && outFile[1]=='\0');
8688  if (writeToStdout) // write to stdout
8689  {
8690  fileOpened = f.open(IO_WriteOnly,stdout);
8691  }
8692  else // write to file
8693  {
8694  QFileInfo fi(outFile);
8695  if (fi.exists()) // create a backup
8696  {
8697  QDir dir=fi.dir();
8698  QFileInfo backup(fi.fileName()+".bak");
8699  if (backup.exists()) // remove existing backup
8700  dir.remove(backup.fileName());
8701  dir.rename(fi.fileName(),fi.fileName()+".bak");
8702  }
8703  f.setName(outFile);
8704  fileOpened = f.open(IO_WriteOnly|IO_Translate);
8705  }
8706  return fileOpened;
8707 }
8708 
8710 {
8711  // User-specified packages
8712  QStrList &extraPackages = Config_getList(EXTRA_PACKAGES);
8713  if (!extraPackages.isEmpty())
8714  {
8715  t << "% Packages requested by user\n";
8716  const char *pkgName=extraPackages.first();
8717  while (pkgName)
8718  {
8719  if ((pkgName[0] == '[') || (pkgName[0] == '{'))
8720  t << "\\usepackage" << pkgName << "\n";
8721  else
8722  t << "\\usepackage{" << pkgName << "}\n";
8723  pkgName=extraPackages.next();
8724  }
8725  t << "\n";
8726  }
8727 }
8728