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