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