Orb/Doxygen/src/util.cpp
changeset 3 d8fccb2cd802
child 4 468f4c8d3d5b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orb/Doxygen/src/util.cpp	Fri Apr 23 20:47:58 2010 +0100
@@ -0,0 +1,7013 @@
+/*****************************************************************************
+ *
+ * 
+ *
+ * Copyright (C) 1997-2008 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby 
+ * granted. No representations are made about the suitability of this software 
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <md5.h>
+
+#include "qtbc.h"
+#include <qregexp.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qcache.h>
+
+#include "util.h"
+#include "message.h"
+#include "classdef.h"
+#include "filedef.h"
+#include "doxygen.h"
+#include "outputlist.h"
+#include "defargs.h"
+#include "language.h"
+#include "config.h"
+#include "htmlhelp.h"
+#include "example.h"
+#include "version.h"
+#include "groupdef.h"
+#include "reflist.h"
+#include "pagedef.h"
+#include "debug.h"
+#include "searchindex.h"
+#include "doxygen.h"
+#include "textdocvisitor.h"
+#include "portable.h"
+#include "parserintf.h"
+#include "bufstr.h"
+
+#define ENABLE_TRACINGSUPPORT 0
+
+#if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
+#define TRACINGSUPPORT
+#endif
+
+#ifdef TRACINGSUPPORT
+#include <execinfo.h>
+#include <unistd.h>
+#endif
+
+
+//------------------------------------------------------------------------
+
+// selects one of the name to sub-dir mapping algorithms that is used
+// to select a sub directory when CREATE_SUBDIRS is set to YES.
+
+#define ALGO_COUNT 1
+#define ALGO_CRC16 2
+#define ALGO_MD5   3
+    
+//#define MAP_ALGO ALGO_COUNT
+//#define MAP_ALGO ALGO_CRC16
+#define MAP_ALGO ALGO_MD5
+
+#define REL_PATH_TO_ROOT "../../"
+
+//------------------------------------------------------------------------
+// TextGeneratorOLImpl implementation
+//------------------------------------------------------------------------
+
+TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od) 
+{
+}
+
+void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
+{ 
+  if (keepSpaces)
+  {
+    const char *p=s;
+    if (p)
+    {
+      char cs[2];
+      char c;
+      cs[1]='\0';
+      while ((c=*p++))
+      {
+        if (c==' ') m_od.writeNonBreakableSpace(1); 
+        else cs[0]=c,m_od.docify(cs);
+      }
+    }
+  }
+  else
+  {
+    m_od.docify(s); 
+  }
+}
+
+void TextGeneratorOLImpl::writeBreak() const
+{ 
+  m_od.pushGeneratorState();
+  m_od.disableAllBut(OutputGenerator::Html);
+  m_od.lineBreak("typebreak");
+  m_od.popGeneratorState();
+}
+
+void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
+                                    const char *anchor,const char *text
+                                   ) const
+{
+  m_od.writeObjectLink(extRef,file,anchor,text);
+}
+
+//------------------------------------------------------------------------
+//------------------------------------------------------------------------
+
+// an inheritance tree of depth of 100000 should be enough for everyone :-)
+const int maxInheritanceDepth = 100000; 
+
+/*! 
+  Removes all anoymous scopes from string s
+  Possible examples:
+\verbatim
+   "bla::@10::blep"      => "bla::blep"
+   "bla::@10::@11::blep" => "bla::blep"
+   "@10::blep"           => "blep"
+   " @10::blep"          => "blep"
+   "@9::@10::blep"       => "blep"
+   "bla::@1"             => "bla"
+   "bla::@1::@2"         => "bla"
+   "bla @1"              => "bla"
+\endverbatim
+ */
+QCString removeAnonymousScopes(const QCString &s)
+{
+  QCString result;
+  if (s.isEmpty()) return result;
+  static QRegExp re("[ :]*@[0-9]+[: ]*");
+  int i,l,sl=s.length();
+  int p=0;
+  while ((i=re.match(s,p,&l))!=-1)
+  {
+    result+=s.mid(p,i-p);
+    int c=i;
+    bool b1=FALSE,b2=FALSE;
+    while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
+    c=i+l-1;
+    while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
+    if (b1 && b2) 
+    { 
+      result+="::"; 
+    }
+    p=i+l;
+  }
+  result+=s.right(sl-p);
+  //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
+  return result;
+}
+
+// replace anonymous scopes with __anonymous__ or replacement if provided
+QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
+{
+  QCString result;
+  if (s.isEmpty()) return result;
+  static QRegExp re("@[0-9]+");
+  int i,l,sl=s.length();
+  int p=0;
+  while ((i=re.match(s,p,&l))!=-1)
+  {
+    result+=s.mid(p,i-p);
+    if (replacement)
+    {
+      result+=replacement;
+    }
+    else
+    {
+      result+="__anonymous__";
+    }
+    p=i+l;
+  }
+  result+=s.right(sl-p);
+  //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
+  return result;
+}
+
+
+// strip annonymous left hand side part of the scope
+QCString stripAnonymousNamespaceScope(const QCString &s)
+{
+  int i,p=0,l;
+  QCString newScope;
+  while ((i=getScopeFragment(s,p,&l))!=-1)
+  {
+    //printf("Scope fragment %s\n",s.mid(i,l).data());
+    if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
+    {
+      if (s.at(i)!='@')
+      {
+        if (!newScope.isEmpty()) newScope+="::";
+        newScope+=s.mid(i,l);
+      }
+    }
+    else
+    {
+      if (!newScope.isEmpty()) newScope+="::";
+      newScope+=s.right(s.length()-i);
+      goto done;
+    }
+    p=i+l;
+  }
+done:
+  //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
+  return newScope;
+}
+
+void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
+{
+  od.pushGeneratorState();
+  
+  od.disable(OutputGenerator::Html);
+  od.disable(OutputGenerator::Man);
+  if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex);
+  if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF);
+  od.startPageRef();
+  od.docify(theTranslator->trPageAbbreviation());
+  od.endPageRef(cn,mn);
+
+  od.popGeneratorState();
+}
+
+/*! Generate a place holder for a position in a list. Used for
+ *  translators to be able to specify different elements orders
+ *  depending on whether text flows from left to right or visa versa.
+ */
+QCString generateMarker(int id)
+{
+  QCString result;
+  result.sprintf("@%d",id);
+  return result;
+}
+
+static QCString stripFromPath(const QCString &path,QStrList &l)
+{
+  // look at all the strings in the list and strip the longest match  
+  const char *s=l.first();
+  QCString potential;
+  unsigned int length = 0;
+  while (s)
+  {
+    QCString prefix = s;
+    if (prefix.length() > length &&
+        stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
+    {
+      length = prefix.length();
+      potential = path.right(path.length()-prefix.length());
+    }
+    s = l.next();
+  }
+  if (length) return potential;
+  return path;
+}
+
+/*! strip part of \a path if it matches
+ *  one of the paths in the Config_getList("STRIP_FROM_PATH") list
+ */
+QCString stripFromPath(const QCString &path)
+{
+  return stripFromPath(path,Config_getList("STRIP_FROM_PATH"));
+}
+
+/*! strip part of \a path if it matches
+ *  one of the paths in the Config_getList("INCLUDE_PATH") list
+ */
+QCString stripFromIncludePath(const QCString &path)
+{
+  return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH"));
+}
+
+/*! try to determine if \a name is a source or a header file name by looking
+ * at the extension. A number of variations is allowed in both upper and 
+ * lower case) If anyone knows or uses another extension please let me know :-)
+ */
+int guessSection(const char *name)
+{
+  QCString n=((QCString)name).lower();
+  if (n.right(2)==".c"    || // source
+      n.right(3)==".cc"   ||
+      n.right(4)==".cxx"  ||
+      n.right(4)==".cpp"  ||
+      n.right(4)==".c++"  ||
+      n.right(5)==".java" ||
+      n.right(3)==".ii"   || // inline
+      n.right(4)==".ixx"  ||
+      n.right(4)==".ipp"  ||
+      n.right(4)==".i++"  ||
+      n.right(4)==".inl"  ||
+      n.right(4)==".xml"
+     ) return Entry::SOURCE_SEC;
+  if (n.right(2)==".h"   || // header
+      n.right(3)==".hh"  ||
+      n.right(4)==".hxx" ||
+      n.right(4)==".hpp" ||
+      n.right(4)==".h++" ||
+      n.right(4)==".idl" ||
+      n.right(4)==".ddl" ||
+      n.right(5)==".pidl"
+     ) return Entry::HEADER_SEC;
+  return 0;
+}
+
+QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
+                        Definition **typedefContext)
+{
+  //printf("<<resolveTypeDef(%s,%s)\n",
+  //          context ? context->name().data() : "<none>",qualifiedName.data());
+  QCString result;
+  if (qualifiedName.isEmpty()) 
+  {
+    //printf("  qualified name empty!\n");
+    return result;
+  }
+
+  Definition *mContext=context;
+  if (typedefContext) *typedefContext=context;
+
+  // see if the qualified name has a scope part
+  int scopeIndex = qualifiedName.findRev("::");
+  QCString resName=qualifiedName;
+  if (scopeIndex!=-1) // strip scope part for the name
+  {
+    resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
+    if (resName.isEmpty())
+    {
+      // qualifiedName was of form A:: !
+      //printf("  qualified name of form A::!\n");
+      return result;
+    }
+  }
+  MemberDef *md=0;
+  while (mContext && md==0)
+  {
+    // step 1: get the right scope
+    Definition *resScope=mContext;
+    if (scopeIndex!=-1) 
+    {
+      // split-off scope part
+      QCString resScopeName = qualifiedName.left(scopeIndex);
+      //printf("resScopeName=`%s'\n",resScopeName.data());
+
+      // look-up scope in context
+      int is,ps=0;
+      int l;
+      while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
+      {
+        QCString qualScopePart = resScopeName.mid(is,l);
+        QCString tmp = resolveTypeDef(mContext,qualScopePart);
+        if (!tmp.isEmpty()) qualScopePart=tmp;
+        resScope = resScope->findInnerCompound(qualScopePart);
+        //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
+        if (resScope==0) break;
+        ps=is+l;
+      }
+    }
+    //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
+    
+    // step 2: get the member
+    if (resScope) // no scope or scope found in the current context 
+    {
+      //printf("scope found: %s, look for typedef %s\n",
+      //     resScope->qualifiedName().data(),resName.data());
+      MemberNameSDict *mnd=0;
+      if (resScope->definitionType()==Definition::TypeClass)
+      {
+        mnd=Doxygen::memberNameSDict;
+      }
+      else
+      {
+        mnd=Doxygen::functionNameSDict;
+      }
+      MemberName *mn=mnd->find(resName);
+      if (mn)
+      {
+        MemberNameIterator mni(*mn);
+        MemberDef *tmd=0;
+        int minDist=-1;
+        for (;(tmd=mni.current());++mni)
+        {
+          //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
+          //    tmd->name().data(), resScope->name().data(), 
+          //    tmd->getOuterScope()->name().data(), mContext);
+          if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
+          {
+            int dist=isAccessibleFrom(resScope,0,tmd);
+            if (dist!=-1 && (md==0 || dist<minDist))
+            {
+              md = tmd;
+              minDist = dist;
+            }
+          }
+        }
+      }
+    }
+    mContext=mContext->getOuterScope();
+  }
+
+  // step 3: get the member's type
+  if (md)
+  {
+    //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s'\n",
+    //    qualifiedName.data(),context->name().data(),md->typeString()
+    //    );
+    result=md->typeString();
+    if (result.find("*)")!=-1) // typedef of a function/member pointer
+    {
+      result+=md->argsString();
+    }
+    if (typedefContext) *typedefContext=md->getOuterScope();
+  }
+  else
+  {
+    //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
+    //    qualifiedName.data(),context ? context->name().data() : "<global>");
+  }
+  return result;
+  
+}
+
+
+/*! Get a class definition given its name. 
+ *  Returns 0 if the class is not found.
+ */
+ClassDef *getClass(const char *name)
+{
+  if (name==0 || name[0]=='\0') return 0;
+  return Doxygen::classSDict->find(name);
+}
+
+NamespaceDef *getResolvedNamespace(const char *name)
+{
+  if (name==0 || name[0]=='\0') return 0;
+  QCString *subst = Doxygen::namespaceAliasDict[name];
+  if (subst)
+  {
+    int count=0; // recursion detection guard
+    QCString *newSubst;
+    while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
+    {
+      subst=newSubst;
+      count++;
+    }
+    if (count==10)
+    {
+      warn_cont("Warning: possible recursive namespace alias detected for %s!\n",name);
+    }
+    return Doxygen::namespaceSDict->find(subst->data());
+  }
+  else
+  {
+    return Doxygen::namespaceSDict->find(name);
+  }
+}
+
+static QDict<MemberDef> g_resolvedTypedefs;
+static QDict<Definition> g_visitedNamespaces;
+
+// forward declaration
+ClassDef *getResolvedClassRec(Definition *scope,
+                              FileDef *fileScope,
+                              const char *n,
+                              MemberDef **pTypeDef,
+                              QCString *pTemplSpec,
+                              QCString *pResolvedType
+                             );
+int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
+                     const QCString &explicitScopePart);
+
+/*! Returns the class representing the value of the typedef represented by \a md
+ *  within file \a fileScope.
+ *
+ *  Example: typedef A T; will return the class representing A if it is a class.
+ * 
+ *  Example: typedef int T; will return 0, since "int" is not a class.
+ */
+ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md,
+                            MemberDef **pMemType,QCString *pTemplSpec,
+                            QCString *pResolvedType,
+                            ArgumentList *actTemplParams)
+{
+  //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
+  bool isCached = md->isTypedefValCached(); // value already cached
+  if (isCached)
+  {
+    //printf("Already cached %s->%s [%s]\n",
+    //    md->name().data(),
+    //    md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
+    //    md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
+
+    if (pTemplSpec)    *pTemplSpec    = md->getCachedTypedefTemplSpec();
+    if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
+    return md->getCachedTypedefVal();
+  }
+  //printf("new typedef\n");
+  QCString qname = md->qualifiedName();
+  if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
+
+  g_resolvedTypedefs.insert(qname,md); // put on the trace list
+  
+  ClassDef *typeClass = md->getClassDef();
+  QCString type = md->typeString(); // get the "value" of the typedef
+  if (typeClass && typeClass->isTemplate() && 
+      actTemplParams && actTemplParams->count()>0)
+  {
+    type = substituteTemplateArgumentsInString(type,
+            typeClass->templateArguments(),actTemplParams);
+  }
+  QCString typedefValue = type;
+  int tl=type.length();
+  int ip=tl-1; // remove * and & at the end
+  while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' ')) 
+  {
+    ip--;
+  }
+  type=type.left(ip+1);
+  type.stripPrefix("const ");  // strip leading "const"
+  type.stripPrefix("struct "); // strip leading "struct"
+  type.stripPrefix("union ");  // strip leading "union"
+  int sp=0;
+  tl=type.length(); // length may have been changed
+  while (sp<tl && type.at(sp)==' ') sp++;
+  MemberDef *memTypeDef = 0;
+  ClassDef  *result = getResolvedClassRec(md->getOuterScope(),
+                                  fileScope,type,&memTypeDef,0,pResolvedType);
+  // if type is a typedef then return what it resolves to.
+  if (memTypeDef && memTypeDef->isTypedef()) 
+  {
+    result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
+    goto done;
+  }
+  else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
+  {
+    *pMemType = memTypeDef;
+  }
+
+  //printf("type=%s result=%p\n",type.data(),result);
+  if (result==0)
+  {
+    // try unspecialized version if type is template
+    int si=type.findRev("::");
+    int i=type.find('<');
+    if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
+    {
+      if (pTemplSpec) *pTemplSpec = type.mid(i);
+      result = getResolvedClassRec(md->getOuterScope(),fileScope,
+                                   type.left(i),0,0,pResolvedType);
+      //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
+      //    result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
+    }
+    else if (si!=-1) // A::B
+    {
+      i=type.find('<',si);
+      if (i==-1) // Something like A<T>::B => lookup A::B
+      {
+        i=type.length();
+      }
+      else // Something like A<T>::B<S> => lookup A::B, spec=<S>
+      {
+        if (pTemplSpec) *pTemplSpec = type.mid(i);
+      }
+      result = getResolvedClassRec(md->getOuterScope(),fileScope,
+           stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
+           pResolvedType);
+    }
+
+    //if (result) ip=si+sp+1;
+  }
+
+done:
+  if (pResolvedType)
+  {
+    if (result)
+    {
+      *pResolvedType=result->qualifiedName();
+      //printf("*pResolvedType=%s\n",pResolvedType->data());
+      if (sp>0)    pResolvedType->prepend(typedefValue.left(sp));
+      if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
+    }
+    else
+    {
+      *pResolvedType=typedefValue;
+    }
+  }
+
+  // remember computed value for next time
+  if (result && result->getDefFileName()!="<code>") 
+    // this check is needed to prevent that temporary classes that are 
+    // introduced while parsing code fragments are being cached here.
+  {
+    //printf("setting cached typedef %p in result %p\n",md,result);
+    //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
+    //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
+    md->cacheTypedefVal(result,
+        pTemplSpec ? *pTemplSpec : QCString(),
+        pResolvedType ? *pResolvedType : QCString()
+       );
+  }
+  
+  g_resolvedTypedefs.remove(qname); // remove from the trace list
+  
+  return result;
+}
+
+/*! Substitutes a simple unqualified \a name within \a scope. Returns the
+ *  value of the typedef or \a name if no typedef was found.
+ */
+static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
+            MemberDef **pTypeDef=0)
+{
+  QCString result=name;
+  if (name.isEmpty()) return result;
+
+  // lookup scope fragment in the symbol map
+  DefinitionIntf *di = Doxygen::symbolMap->find(name);
+  if (di==0) return result; // no matches
+
+  MemberDef *bestMatch=0;
+  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
+  {
+    // search for the best match
+    DefinitionListIterator dli(*(DefinitionList*)di);
+    Definition *d;
+    int minDistance=10000; // init at "infinite"
+    for (dli.toFirst();(d=dli.current());++dli) // foreach definition
+    {
+      // only look at members
+      if (d->definitionType()==Definition::TypeMember)
+      {
+        // that are also typedefs
+        MemberDef *md = (MemberDef *)d;
+        if (md->isTypedef()) // d is a typedef
+        {
+          // test accessibility of typedef within scope.
+          int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
+          if (distance!=-1 && distance<minDistance) 
+            // definition is accessible and a better match
+          {
+            minDistance=distance;
+            bestMatch = md; 
+          }
+        }
+      }
+    }
+  }
+  else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
+  {
+    Definition *d = (Definition*)di;
+    // that are also typedefs
+    MemberDef *md = (MemberDef *)di;
+    if (md->isTypedef()) // d is a typedef
+    {
+      // test accessibility of typedef within scope.
+      int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
+      if (distance!=-1) // definition is accessible 
+      {
+        bestMatch = md; 
+      }
+    }
+  }
+  if (bestMatch) 
+  {
+    result = bestMatch->typeString();
+    if (pTypeDef) *pTypeDef=bestMatch;
+  }
+  
+  //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+  //                                  name.data(),result.data());
+  return result;
+}
+
+static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
+{
+  if (cl)
+  {
+    SDict<Definition>::Iterator cli(*cl);
+    Definition *cd;
+    for (cli.toFirst();(cd=cli.current());++cli)
+    {
+      if (cd->localName()==localName)
+      {
+        return cd;
+      }
+    }
+  }
+  return 0;
+}
+
+/*! Starting with scope \a start, the string \a path is interpreted as
+ *  a part of a qualified scope name (e.g. A::B::C), and the scope is 
+ *  searched. If found the scope definition is returned, otherwise 0 
+ *  is returned.
+ */
+static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
+{
+  int is,ps;
+  int l;
+  Definition *current=start;
+  ps=0;
+  //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
+  // for each part of the explicit scope
+  while ((is=getScopeFragment(path,ps,&l))!=-1)
+  {
+    // try to resolve the part if it is a typedef
+    MemberDef *typeDef=0;
+    QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
+    //printf("      qualScopePart=%s\n",qualScopePart.data());
+    if (typeDef)
+    {
+      ClassDef *type = newResolveTypedef(fileScope,typeDef);
+      if (type)
+      {
+        //printf("Found type %s\n",type->name().data());
+        return type;
+      }
+    }
+    Definition *next = current->findInnerCompound(qualScopePart);
+    //printf("++ Looking for %s inside %s result %s\n",
+    //     qualScopePart.data(),
+    //     current->name().data(),
+    //     next?next->name().data():"<null>");
+    if (next==0) // failed to follow the path 
+    {
+      //printf("==> next==0!\n");
+      if (current->definitionType()==Definition::TypeNamespace)
+      {
+        next = endOfPathIsUsedClass(
+            ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
+      }
+      else if (current->definitionType()==Definition::TypeFile)
+      {
+        next = endOfPathIsUsedClass(
+            ((FileDef *)current)->getUsedClasses(),qualScopePart);
+      }
+      current = next;
+      if (current==0) break;
+    }
+    else // continue to follow scope
+    {
+      current = next;
+      //printf("==> current = %p\n",current);
+    }
+    ps=is+l;
+  }
+  //printf("followPath(start=%s,path=%s) result=%s\n",
+  //    start->name().data(),path.data(),current?current->name().data():"<null>");
+  return current; // path could be followed
+}
+
+bool accessibleViaUsingClass(const SDict<Definition> *cl,
+                             FileDef *fileScope,
+                             Definition *item,
+                             const QCString &explicitScopePart=""
+                            )
+{
+  //printf("accessibleViaUsingClass(%p)\n",cl);
+  if (cl) // see if the class was imported via a using statement 
+  {
+    SDict<Definition>::Iterator cli(*cl);
+    Definition *ucd;
+    bool explicitScopePartEmpty = explicitScopePart.isEmpty();
+    for (cli.toFirst();(ucd=cli.current());++cli)
+    {
+      //printf("Trying via used class %s\n",ucd->name().data());
+      Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
+      if (sc && sc==item) return TRUE; 
+      //printf("Try via used class done\n");
+    }
+  }
+  return FALSE;
+}
+
+bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
+                                 FileDef *fileScope,
+                                 Definition *item,
+                                 const QCString &explicitScopePart="")
+{
+  static QDict<void> visitedDict;
+  if (nl) // check used namespaces for the class
+  {
+    NamespaceSDict::Iterator nli(*nl);
+    NamespaceDef *und;
+    int count=0;
+    for (nli.toFirst();(und=nli.current());++nli,count++)
+    {
+      //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
+      //    count,nl->count());
+      Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
+      if (sc && item->getOuterScope()==sc) 
+      {
+        //printf("] found it\n");
+        return TRUE; 
+      }
+      QCString key=und->name();
+      if (und->getUsedNamespaces() && visitedDict.find(key)==0)
+      {
+        visitedDict.insert(key,(void *)0x08);
+
+        if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
+        {
+          //printf("] found it via recursion\n");
+          return TRUE;
+        }
+
+        visitedDict.remove(key);
+      }
+      //printf("] Try via used namespace done\n");
+    }
+  }
+  return FALSE;
+}
+
+
+/* Returns the "distance" (=number of levels up) from item to scope, or -1
+ * if item in not inside scope. 
+ */
+int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
+{
+  //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
+  //    scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
+
+  QCString key(40);
+  key.sprintf("%p:%p:%p",scope,fileScope,item);
+  static QDict<void> visitedDict;
+  if (visitedDict.find(key)) 
+  {
+    //printf("> already found\n");
+    return -1; // already looked at this
+  }
+  visitedDict.insert(key,(void *)0x8);
+
+  int result=0; // assume we found it
+  int i;
+
+  Definition *itemScope=item->getOuterScope();
+
+  if ( 
+      itemScope==scope ||                                                  // same thing
+      (item->definitionType()==Definition::TypeMember &&                   // a member
+       itemScope && itemScope->definitionType()==Definition::TypeClass  && // of a class
+       scope->definitionType()==Definition::TypeClass &&                   // accessible
+       ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item)           // from scope
+      ) ||
+      (item->definitionType()==Definition::TypeClass &&                    // a nested class
+       itemScope && itemScope->definitionType()==Definition::TypeClass &&  // inside a base 
+       scope->definitionType()==Definition::TypeClass &&                   // class of scope
+       ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)          
+      )
+     ) 
+  {
+    //printf("> found it\n");
+  }
+  else if (scope==Doxygen::globalScope)
+  {
+    if (fileScope)
+    {
+      SDict<Definition> *cl = fileScope->getUsedClasses();
+      if (accessibleViaUsingClass(cl,fileScope,item)) 
+      {
+        //printf("> found via used class\n");
+        goto done;
+      }
+      NamespaceSDict *nl = fileScope->getUsedNamespaces();
+      if (accessibleViaUsingNamespace(nl,fileScope,item)) 
+      {
+        //printf("> found via used namespace\n");
+        goto done;
+      }
+    }
+    //printf("> reached global scope\n");
+    result=-1; // not found in path to globalScope
+  }
+  else // keep searching
+  {
+    // check if scope is a namespace, which is using other classes and namespaces
+    if (scope->definitionType()==Definition::TypeNamespace)
+    {
+      NamespaceDef *nscope = (NamespaceDef*)scope;
+      //printf("  %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
+      SDict<Definition> *cl = nscope->getUsedClasses();
+      if (accessibleViaUsingClass(cl,fileScope,item)) 
+      {
+        //printf("> found via used class\n");
+        goto done;
+      }
+      NamespaceSDict *nl = nscope->getUsedNamespaces();
+      if (accessibleViaUsingNamespace(nl,fileScope,item)) 
+      {
+        //printf("> found via used namespace\n");
+        goto done;
+      }
+    }
+    // repeat for the parent scope
+    i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
+    //printf("> result=%d\n",i);
+    result= (i==-1) ? -1 : i+2;
+  }
+done:
+  visitedDict.remove(key);
+  //Doxygen::lookupCache.insert(key,new int(result));
+  return result;
+}
+
+
+/* Returns the "distance" (=number of levels up) from item to scope, or -1
+ * if item in not in this scope. The explicitScopePart limits the search
+ * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
+ * Example:
+ *
+ * class A { public: class I {}; };
+ * class B { public: class J {}; };
+ *
+ * - Looking for item=='J' inside scope=='B' will return 0.
+ * - Looking for item=='I' inside scope=='B' will return -1 
+ *   (as it is not found in B nor in the global scope).
+ * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but 
+ *   not found and then A::I is searched in the global scope, which matches and 
+ *   thus the result is 1.
+ */
+int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,
+                     Definition *item,const QCString &explicitScopePart)
+{
+  if (explicitScopePart.isEmpty())
+  {
+    // handle degenerate case where there is no explicit scope.
+    return isAccessibleFrom(scope,fileScope,item);
+  }
+
+  QCString key(40+explicitScopePart.length());
+  key.sprintf("%p:%p:%p:%s",scope,fileScope,item,explicitScopePart.data());
+  static QDict<void> visitedDict;
+  if (visitedDict.find(key)) 
+  {
+    //printf("Already visited!\n");
+    return -1; // already looked at this
+  }
+  visitedDict.insert(key,(void *)0x8);
+
+  //printf("  <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
+  //                                      item?item->name().data():"<none>",
+  //                                      explicitScopePart.data());
+  int result=0; // assume we found it
+  Definition *newScope = followPath(scope,fileScope,explicitScopePart);
+  if (newScope)  // explicitScope is inside scope => newScope is the result
+  {
+    Definition *itemScope = item->getOuterScope();
+    //printf("    scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
+    //if (newScope && newScope->definitionType()==Definition::TypeClass)
+    //{
+    //  ClassDef *cd = (ClassDef *)newScope;
+    //  printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
+    //}
+    if (itemScope==newScope)  // exact match of scopes => distance==0
+    {
+      //printf("> found it\n");
+    }
+    else if (itemScope && newScope &&
+             itemScope->definitionType()==Definition::TypeClass &&
+             newScope->definitionType()==Definition::TypeClass &&
+             ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
+            )
+    {
+      // inheritance is also ok. Example: looking for B::I, where 
+      // class A { public: class I {} };
+      // class B : public A {}
+      // but looking for B::I, where
+      // class A { public: class I {} };
+      // class B { public: class I {} };
+      // will find A::I, so we still prefer a direct match and give this one a distance of 1
+      result=1;
+
+      //printf("scope(%s) is base class of newScope(%s)\n",
+      //    scope->name().data(),newScope->name().data());
+    }
+    else
+    {
+      int i=-1;
+      if (newScope->definitionType()==Definition::TypeNamespace)
+      {
+        g_visitedNamespaces.insert(newScope->name(),newScope);
+        // this part deals with the case where item is a class
+        // A::B::C but is explicit referenced as A::C, where B is imported
+        // in A via a using directive.
+        //printf("newScope is a namespace: %s!\n",newScope->name().data());
+        NamespaceDef *nscope = (NamespaceDef*)newScope;
+        SDict<Definition> *cl = nscope->getUsedClasses();
+        if (cl)
+        {
+          SDict<Definition>::Iterator cli(*cl);
+          Definition *cd;
+          for (cli.toFirst();(cd=cli.current());++cli)
+          {
+            //printf("Trying for class %s\n",cd->name().data());
+            if (cd==item)
+            {
+              //printf("> class is used in this scope\n");
+              goto done;
+            }
+          }
+        }
+        NamespaceSDict *nl = nscope->getUsedNamespaces();
+        if (nl)
+        {
+          NamespaceSDict::Iterator nli(*nl);
+          NamespaceDef *nd;
+          for (nli.toFirst();(nd=nli.current());++nli)
+          {
+            if (g_visitedNamespaces.find(nd->name())==0)
+            {
+              //printf("Trying for namespace %s\n",nd->name().data());
+              i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
+              if (i!=-1)
+              {
+                //printf("> found via explicit scope of used namespace\n");
+                goto done;
+              }
+            }
+          }
+        }
+      }
+      // repeat for the parent scope
+      if (scope!=Doxygen::globalScope)
+      {
+        i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
+            item,explicitScopePart);
+      }
+      //printf("  | result=%d\n",i);
+      result = (i==-1) ? -1 : i+2;
+    }
+  }
+  else // failed to resolve explicitScope
+  {
+    //printf("    failed to resolve: scope=%s\n",scope->name().data());
+    if (scope->definitionType()==Definition::TypeNamespace)
+    {
+      NamespaceDef *nscope = (NamespaceDef*)scope;
+      NamespaceSDict *nl = nscope->getUsedNamespaces();
+      if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 
+      {
+        //printf("> found in used namespace\n");
+        goto done;
+      }
+    }
+    if (scope==Doxygen::globalScope)
+    {
+      if (fileScope)
+      {
+        NamespaceSDict *nl = fileScope->getUsedNamespaces();
+        if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 
+        {
+          //printf("> found in used namespace\n");
+          goto done;
+        }
+      }
+      //printf("> not found\n");
+      result=-1;
+    }
+    else // continue by looking into the parent scope
+    {
+      int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
+          item,explicitScopePart);
+      //printf("> result=%d\n",i);
+      result= (i==-1) ? -1 : i+2;
+    }
+  }
+done:
+  //printf("  > result=%d\n",result);
+  visitedDict.remove(key);
+  //Doxygen::lookupCache.insert(key,new int(result));
+  return result;
+}
+
+int computeQualifiedIndex(const QCString &name)
+{
+  int i = name.find('<');
+  return name.findRev("::",i==-1 ? name.length() : i);
+}
+
+static void getResolvedSymbol(Definition *scope,
+                       FileDef *fileScope,
+                       Definition *d, 
+                       const QCString &explicitScopePart,
+                       ArgumentList *actTemplParams,
+                       int &minDistance,
+                       ClassDef *&bestMatch,
+                       MemberDef *&bestTypedef,
+                       QCString &bestTemplSpec,
+                       QCString &bestResolvedType
+                      )
+{
+  //printf("  => found type %x name=%s d=%p\n",
+  //       d->definitionType(),d->name().data(),d);
+
+  // only look at classes and members that are enums or typedefs
+  if (d->definitionType()==Definition::TypeClass ||
+      (d->definitionType()==Definition::TypeMember && 
+       (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate()) 
+      )
+     )
+  {
+    g_visitedNamespaces.clear();
+    // test accessibility of definition within scope.
+    int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+    //printf("  %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
+    if (distance!=-1) // definition is accessible
+    {
+      // see if we are dealing with a class or a typedef
+      if (d->definitionType()==Definition::TypeClass) // d is a class
+      {
+        ClassDef *cd = (ClassDef *)d;
+        //printf("cd=%s\n",cd->name().data());
+        if (!cd->isTemplateArgument()) // skip classes that
+          // are only there to 
+          // represent a template 
+          // argument
+        {
+          //printf("is not a templ arg\n");
+          if (distance<minDistance) // found a definition that is "closer"
+          {
+            minDistance=distance;
+            bestMatch = cd; 
+            bestTypedef = 0;
+            bestTemplSpec.resize(0);
+            bestResolvedType = cd->qualifiedName();
+          }
+          else if (distance==minDistance &&
+              fileScope && bestMatch &&
+              fileScope->getUsedNamespaces() && 
+              d->getOuterScope()->definitionType()==Definition::TypeNamespace && 
+              bestMatch->getOuterScope()==Doxygen::globalScope
+              )
+          {
+            // in case the distance is equal it could be that a class X
+            // is defined in a namespace and in the global scope. When searched
+            // in the global scope the distance is 0 in both cases. We have
+            // to choose one of the definitions: we choose the one in the
+            // namespace if the fileScope imports namespaces and the definition
+            // found was in a namespace while the best match so far isn't.
+            // Just a non-perfect heuristic but it could help in some situations
+            // (kdecore code is an example).
+            minDistance=distance;
+            bestMatch = cd; 
+            bestTypedef = 0;
+            bestTemplSpec.resize(0);
+            bestResolvedType = cd->qualifiedName();
+          }
+        }
+        else
+        {
+          //printf("  is a template argument!\n");
+        }
+      }
+      else if (d->definitionType()==Definition::TypeMember)
+      {
+        MemberDef *md = (MemberDef *)d;
+        //printf("  member isTypedef()=%d\n",md->isTypedef());
+        if (md->isTypedef()) // d is a typedef
+        {
+          QCString args=md->argsString();
+          if (args.isEmpty()) // do not expand "typedef t a[4];"
+          {
+            //printf("    found typedef!\n");
+
+            // we found a symbol at this distance, but if it didn't
+            // resolve to a class, we still have to make sure that
+            // something at a greater distance does not match, since
+            // that symbol is hidden by this one.
+            if (distance<minDistance)
+            {
+              QCString spec;
+              QCString type;
+              minDistance=distance;
+              MemberDef *enumType = 0;
+              ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
+              if (cd)  // type resolves to a class
+              {
+                //printf("      bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
+                bestMatch = cd;
+                bestTypedef = md;
+                bestTemplSpec = spec;
+                bestResolvedType = type;
+              }
+              else if (enumType) // type resolves to a enum
+              {
+                //printf("      is enum\n");
+                bestMatch = 0;
+                bestTypedef = enumType;
+                bestTemplSpec = "";
+                bestResolvedType = enumType->qualifiedName();
+              }
+              else if (md->isReference()) // external reference
+              {
+                bestMatch = 0;
+                bestTypedef = md;
+                bestTemplSpec = spec;
+                bestResolvedType = type;
+              }
+              else
+              {
+                //printf("      no match\n");
+              }
+            }
+            else
+            {
+              //printf("      not the best match %d min=%d\n",distance,minDistance);
+            }
+          }
+          else
+          {
+            //printf("     not a simple typedef\n")
+          }
+        }
+        else if (md->isEnumerate())
+        {
+          if (distance<minDistance)
+          {
+            minDistance=distance;
+            bestMatch = 0;
+            bestTypedef = md;
+            bestTemplSpec = "";
+            bestResolvedType = md->qualifiedName();
+          }
+        }
+      }
+    } // if definition accessible
+    else
+    {
+      //printf("  Not accessible!\n");
+    }
+  } // if definition is a class or member
+  //printf("  bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
+}
+
+/* Find the fully qualified class name refered to by the input class
+ * or typedef name against the input scope.
+ * Loops through scope and each of its parent scopes looking for a
+ * match against the input name. Can recursively call itself when 
+ * resolving typedefs.
+ */
+ClassDef *getResolvedClassRec(Definition *scope,
+    FileDef *fileScope,
+    const char *n,
+    MemberDef **pTypeDef,
+    QCString *pTemplSpec,
+    QCString *pResolvedType
+    )
+{
+  //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
+  QCString name;
+  QCString explicitScopePart;
+  QCString strippedTemplateParams;
+  name=stripTemplateSpecifiersFromScope
+                     (removeRedundantWhiteSpace(n),TRUE,
+                      &strippedTemplateParams);
+  ArgumentList actTemplParams;
+  if (!strippedTemplateParams.isEmpty()) // template part that was stripped
+  {
+    stringToArgumentList(strippedTemplateParams,&actTemplParams);
+  }
+
+  int qualifierIndex = computeQualifiedIndex(name);
+  //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
+  if (qualifierIndex!=-1) // qualified name
+  {
+    // split off the explicit scope part
+    explicitScopePart=name.left(qualifierIndex);
+    // todo: improve namespace alias substitution
+    replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
+    name=name.mid(qualifierIndex+2);
+  }
+
+  if (name.isEmpty()) 
+  {
+    //printf("] empty name\n");
+    return 0; // empty name
+  }
+
+  DefinitionIntf *di = Doxygen::symbolMap->find(name);
+  //printf("Looking for symbol %s result=%p\n",name.data(),di);
+  if (di==0) 
+  {
+    return 0;
+  }
+
+  bool hasUsingStatements = 
+    (fileScope && ((fileScope->getUsedNamespaces() && 
+                    fileScope->getUsedNamespaces()->count()>0) ||
+                   (fileScope->getUsedClasses() && 
+                    fileScope->getUsedClasses()->count()>0)) 
+    );
+  //printf("hasUsingStatements=%d\n",hasUsingStatements);
+  // Since it is often the case that the same name is searched in the same
+  // scope over an over again (especially for the linked source code generation)
+  // we use a cache to collect previous results. This is possible since the
+  // result of a lookup is deterministic. As the key we use the concatenated
+  // scope, the name to search for and the explicit scope prefix. The speedup
+  // achieved by this simple cache can be enormous.
+  int scopeNameLen = scope->name().length()+1;
+  int nameLen = name.length()+1;
+  int explicitPartLen = explicitScopePart.length();
+  int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
+
+  // below is a more efficient coding of
+  // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
+  QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
+  char *p=key.data();
+  qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
+  p+=scopeNameLen;
+  qstrcpy(p,name); *(p+nameLen-1)='+';
+  p+=nameLen;
+  qstrcpy(p,explicitScopePart);
+  p+=explicitPartLen;
+
+  // if a file scope is given and it contains using statements we should
+  // also use the file part in the key (as a class name can be in
+  // two different namespaces and a using statement in a file can select 
+  // one of them).
+  if (hasUsingStatements)
+  {
+    // below is a more efficient coding of
+    // key+="+"+fileScope->name();
+    *p++='+';
+    qstrcpy(p,fileScope->absFilePath());
+    p+=fileScopeLen-1;
+  }
+  *p='\0';
+
+  LookupInfo *pval=Doxygen::lookupCache.find(key);
+  //printf("Searching for %s result=%p\n",key.data(),pval);
+  if (pval)
+  {
+    //printf("LookupInfo %p %p '%s' %p\n", 
+    //    pval->classDef, pval->typeDef, pval->templSpec.data(), 
+    //    pval->resolvedType.data()); 
+    if (pTemplSpec)    *pTemplSpec=pval->templSpec;
+    if (pTypeDef)      *pTypeDef=pval->typeDef;
+    if (pResolvedType) *pResolvedType=pval->resolvedType;
+    //printf("] cachedMatch=%s\n",
+    //    pval->classDef?pval->classDef->name().data():"<none>");
+    //if (pTemplSpec) 
+    //  printf("templSpec=%s\n",pTemplSpec->data());
+    return pval->classDef; 
+  }
+  else // not found yet; we already add a 0 to avoid the possibility of 
+    // endless recursion.
+  {
+    Doxygen::lookupCache.insert(key,new LookupInfo);
+  }
+
+  ClassDef *bestMatch=0;
+  MemberDef *bestTypedef=0;
+  QCString bestTemplSpec;
+  QCString bestResolvedType;
+  int minDistance=10000; // init at "infinite"
+
+  if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
+  {
+    //printf("  name is not unique\n");
+    DefinitionListIterator dli(*(DefinitionList*)di);
+    Definition *d;
+    int count=0;
+    for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
+    {
+      getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
+                        minDistance,bestMatch,bestTypedef,bestTemplSpec,
+                        bestResolvedType);
+    }
+  }
+  else // unique name
+  {
+    //printf("  name is unique\n");
+    Definition *d = (Definition *)di;
+    getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
+                      minDistance,bestMatch,bestTypedef,bestTemplSpec,
+                      bestResolvedType);
+  }
+
+  if (pTypeDef) 
+  {
+    *pTypeDef = bestTypedef;
+  }
+  if (pTemplSpec)
+  {
+    *pTemplSpec = bestTemplSpec;
+  }
+  if (pResolvedType)
+  {
+    *pResolvedType = bestResolvedType;
+  }
+  //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
+  //    bestMatch,bestResolvedType.data());
+
+  pval=Doxygen::lookupCache.find(key);
+  if (pval)
+  {
+    pval->classDef     = bestMatch;
+    pval->typeDef      = bestTypedef;
+    pval->templSpec    = bestTemplSpec;
+    pval->resolvedType = bestResolvedType;
+  }
+  else
+  {
+    Doxygen::lookupCache.insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
+  }
+  //printf("] bestMatch=%s distance=%d\n",
+  //    bestMatch?bestMatch->name().data():"<none>",minDistance);
+  //if (pTemplSpec) 
+  //  printf("templSpec=%s\n",pTemplSpec->data());
+  return bestMatch;
+}
+
+/* Find the fully qualified class name refered to by the input class
+ * or typedef name against the input scope.
+ * Loops through scope and each of its parent scopes looking for a
+ * match against the input name. 
+ */
+ClassDef *getResolvedClass(Definition *scope,
+    FileDef *fileScope,
+    const char *n,
+    MemberDef **pTypeDef,
+    QCString *pTemplSpec,
+    bool mayBeUnlinkable,
+    bool mayBeHidden,
+    QCString *pResolvedType
+    )
+{
+  g_resolvedTypedefs.clear();
+  if (scope==0 ||
+      (scope->definitionType()!=Definition::TypeClass && 
+       scope->definitionType()!=Definition::TypeNamespace
+      ) ||
+      (fileScope && fileScope->isJava() && QCString(n).find("::")!=-1)
+     )
+  {
+    scope=Doxygen::globalScope;
+  }
+  //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
+  //    scope?scope->name().data():"<global>",
+  //    fileScope?fileScope->name().data():"<none>",
+  //    n,
+  //    mayBeUnlinkable
+  //   );
+  ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
+  if (!mayBeUnlinkable && result && !result->isLinkable()) 
+  {
+    if (!mayBeHidden || !result->isHidden())
+    {
+      result=0; // don't link to artifical/hidden classes unless explicitly allowed
+    }
+  }
+  //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+  //                                  n,result?result->name().data():"<none>");
+  return result;
+}
+
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+
+static bool findOperator(const QCString &s,int i)
+{
+  int b = s.findRev("operator",i);
+  if (b==-1) return FALSE; // not found
+  b+=8;
+  while (b<i) // check if there are only spaces inbetween 
+    // the operator and the >
+  {
+    if (!isspace((uchar)s.at(b))) return FALSE;
+    b++;
+  }
+  return TRUE;
+}
+
+static bool findOperator2(const QCString &s,int i)
+{
+  int b = s.findRev("operator",i);
+  if (b==-1) return FALSE; // not found
+  b+=8;
+  while (b<i) // check if there are only non-ascii
+              // characters in front of the operator
+  {
+    if (isId((uchar)s.at(b))) return FALSE;
+    b++;
+  }
+  return TRUE;
+}
+
+static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
+static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
+
+QCString removeRedundantWhiteSpace(const QCString &s)
+{
+  static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT");
+  if (s.isEmpty()) return s;
+  int resultLen = 1024;
+  int resultPos = 0;
+  QCString result(resultLen);
+  // we use ADD_CHAR(c) instead of result+=c to 
+  // improve the performance of this function
+#define ADD_CHAR(c) if (resultPos>=resultLen) { resultLen+=1024; result.resize(resultLen); } \
+                    result[resultPos++]=(c)
+  uint i;
+  uint l=s.length();
+  uint csp=0;
+  uint vsp=0;
+  for (i=0;i<l;i++)
+  {
+nextChar:
+    char c=s.at(i);
+
+    // search for "const"
+    if (csp<6 && c==constScope[csp] && // character matches substring "const"
+         (csp>0 ||                     // if it is the first character 
+          i==0  ||                     // the previous may not be a digit
+          !isId(s.at(i-1))
+         )
+       )
+      csp++; 
+    else // reset counter
+      csp=0;
+
+    // search for "virtual"
+    if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
+         (vsp>0 ||                       // if it is the first character
+          i==0  ||                       // the previous may not be a digit 
+          !isId(s.at(i-1))
+         )
+       )
+      vsp++;
+    else // reset counter
+      vsp=0;
+
+    if (c=='"') // quoted string
+    {
+      i++;
+      ADD_CHAR(c);
+      while (i<l)
+      {
+        char cc=s.at(i);
+        ADD_CHAR(cc);
+        if (cc=='\\') // escaped character
+        { 
+          ADD_CHAR(s.at(i+1));
+          i+=2; 
+        }
+        else if (cc=='"') // end of string
+        { i++; goto nextChar; }
+        else // any other character
+        { i++; }
+      }
+    }
+    else if (i<l-2 && c=='<' &&  // current char is a <
+        (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space
+        (i<8 || !findOperator(s,i)) // string in front is not "operator"
+        )
+    {
+      ADD_CHAR('<');
+      ADD_CHAR(' ');
+    }
+    else if (i>0 && c=='>' && // current char is a >
+        (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
+        (i<8 || !findOperator(s,i)) // string in front is not "operator"
+        )
+    {
+      ADD_CHAR(' ');
+      ADD_CHAR('>');
+    }
+    else if (i>0 && c==',' && !isspace((uchar)s.at(i-1))
+        && ((i<l-1 && isId(s.at(i+1)))
+          || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2)))  // for PHP
+          || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3)))))  // for PHP
+    {
+      ADD_CHAR(',');
+      ADD_CHAR(' ');
+    }
+    else if (i>0 && 
+        ((isId(s.at(i)) && s.at(i-1)==')') || 
+         (s.at(i)=='\''  && s.at(i-1)==' ')
+        )
+        )
+    {
+      ADD_CHAR(' ');
+      ADD_CHAR(s.at(i));
+    }
+    else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ &&
+             !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 
+               s.at(i+1)==')' || 
+               s.at(i+1)==',' || 
+               s.at(i+1)=='\0'
+              )
+            ) 
+      // prevent const ::A from being converted to const::A
+    {
+      ADD_CHAR('t');
+      ADD_CHAR(' ');
+      if (s.at(i+1)==' ') i++;
+      csp=0;
+    }
+    else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/) 
+      // replace const::A by const ::A
+    {
+      ADD_CHAR(' ');
+      ADD_CHAR(':');
+      csp=0;
+    }
+    else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ &&
+             !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 
+               s.at(i+1)==')' || 
+               s.at(i+1)==',' || 
+               s.at(i+1)=='\0'
+              )
+            ) 
+      // prevent virtual ::A from being converted to virtual::A
+    {
+      ADD_CHAR('l');
+      ADD_CHAR(' ');
+      if (s.at(i+1)==' ') i++;
+      vsp=0;
+    }
+    else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/) 
+      // replace virtual::A by virtual ::A
+    {
+      ADD_CHAR(' ');
+      ADD_CHAR(':');
+      vsp=0;
+    }
+    else if (!isspace((uchar)c) || // not a space
+        ( i>0 && i<l-1 &&          // internal character
+          (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']')
+          && (isId(s.at(i+1)) || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2)))
+            || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))
+        ) 
+        )
+    {
+      if (c=='*' || c=='&' || c=='@' || c=='$')
+      {  
+        //uint rl=result.length();
+        uint rl=resultPos;
+        if ((rl>0 && (isId(result.at(rl-1)) || result.at(rl-1)=='>')) &&
+            ((c!='*' && c!='&') || !findOperator2(s,i)) // avoid splitting operator* and operator->* and operator&
+           ) 
+        {
+          ADD_CHAR(' ');
+        }
+      }
+      ADD_CHAR(c);
+      if (cliSupport &&
+          (c=='^' || c=='%') && i>1 && isId(s.at(i-1)) &&
+          !findOperator(s,i)
+         ) 
+      {
+        ADD_CHAR(' '); // C++/CLI: Type^ name and Type% name
+      }
+    }
+  }
+  //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data());
+  ADD_CHAR(0);
+  result.resize(resultPos);
+  return result;
+}  
+
+bool rightScopeMatch(const QCString &scope, const QCString &name)
+{
+  return (name==scope || // equal 
+      (scope.right(name.length())==name && // substring 
+       scope.at(scope.length()-name.length()-1)==':' // scope
+      ) 
+      );
+}
+
+bool leftScopeMatch(const QCString &scope, const QCString &name)
+{
+  return (name==scope || // equal 
+      (scope.left(name.length())==name && // substring 
+       scope.at(name.length())==':' // scope
+      ) 
+      );
+}
+
+
+void linkifyText(const TextGeneratorIntf &out,Definition *scope,
+    FileDef *fileScope,const char *,
+    const char *text, bool autoBreak,bool external,
+    bool keepSpaces)
+{
+  //printf("`%s'\n",text);
+  static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9.:\\x80-\\xFF]*");
+  static QRegExp regExpSplit("(?!:),");
+  QCString txtStr=text;
+  int strLen = txtStr.length();
+  //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d\n",
+  //    scope?scope->name().data():"<none>",
+  //    fileScope?fileScope->name().data():"<none>",
+  //    txtStr.data(),strLen);
+  int matchLen;
+  int index=0;
+  int newIndex;
+  int skipIndex=0;
+  int floatingIndex=0;
+  if (strLen==0) return;
+  // read a word from the text string
+  while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 && 
+      (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
+      )
+  {
+    // add non-word part to the result
+    floatingIndex+=newIndex-skipIndex+matchLen;
+    bool insideString=FALSE; 
+    int i;
+    for (i=index;i<newIndex;i++) 
+    { 
+      if (txtStr.at(i)=='"') insideString=!insideString; 
+    }
+
+    //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
+    if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
+    {
+      QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
+      int splitLength = splitText.length();
+      int offset=1;
+      i=splitText.find(regExpSplit,0);
+      if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
+      if (i==-1) i=splitText.find('>');
+      if (i==-1) i=splitText.find(' ');
+      //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
+      if (i!=-1) // add a link-break at i in case of Html output
+      {
+        out.writeString(splitText.left(i+offset),keepSpaces);
+        out.writeBreak();
+        out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
+        floatingIndex=splitLength-i-offset+matchLen;
+      } 
+      else
+      {
+        out.writeString(splitText,keepSpaces); 
+      }
+    }
+    else
+    {
+      //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex)); 
+      out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces); 
+    }
+    // get word from string
+    QCString word=txtStr.mid(newIndex,matchLen);
+    QCString matchWord = substitute(word,".","::");
+    //printf("linkifyText word=%s matchWord=%s scope=%s\n",
+    //    word.data(),matchWord.data(),scope?scope->name().data():"<none>");
+    bool found=FALSE;
+    if (!insideString)
+    {
+      ClassDef     *cd=0;
+      FileDef      *fd=0;
+      MemberDef    *md=0;
+      NamespaceDef *nd=0;
+      GroupDef     *gd=0;
+      //printf("** Match word '%s'\n",matchWord.data());
+
+      MemberDef *typeDef=0;
+      if ((cd=getResolvedClass(scope,fileScope,matchWord,&typeDef))) 
+      {
+        //printf("Found class %s\n",cd->name().data());
+        // add link to the result
+        if (external ? cd->isLinkable() : cd->isLinkableInProject())
+        {
+          out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word);
+          found=TRUE;
+        }
+      }
+      else if (typeDef)
+      {
+        //printf("Found typedef %s\n",typeDef->name().data());
+        if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
+        {
+          out.writeLink(typeDef->getReference(),
+              typeDef->getOutputFileBase(),
+              typeDef->anchor(),
+              word);
+          found=TRUE;
+        }
+      }
+      else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
+      {
+        // add link to the result
+        if (external ? cd->isLinkable() : cd->isLinkableInProject())
+        {
+          out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word);
+          found=TRUE;
+        }
+      }
+      else
+      {
+        //printf("   -> nothing\n");
+      }
+
+      QCString scopeName;
+      if (scope && 
+          (scope->definitionType()==Definition::TypeClass || 
+           scope->definitionType()==Definition::TypeNamespace
+          ) 
+         )
+      {
+        scopeName=scope->name();
+      }
+      //printf("ScopeName=%s\n",scopeName.data());
+      //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data()); 
+      if (!found && 
+          getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) && 
+          (md->isTypedef() || md->isEnumerate() || 
+           md->isReference() || md->isVariable()
+          ) && 
+          (external ? md->isLinkable() : md->isLinkableInProject()) 
+         )
+      {
+        //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
+        //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
+        //                       md->anchor(),word);
+        out.writeLink(md->getReference(),md->getOutputFileBase(),
+            md->anchor(),word);
+        found=TRUE;
+      }
+    }
+
+    if (!found) // add word to the result
+    {
+      out.writeString(word,keepSpaces);
+    }
+    // set next start point in the string
+    //printf("index=%d/%d\n",index,txtStr.length());
+    skipIndex=index=newIndex+matchLen;
+  }
+  // add last part of the string to the result.
+  //ol.docify(txtStr.right(txtStr.length()-skipIndex));
+  out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
+}
+
+
+void writeExample(OutputList &ol,ExampleSDict *ed)
+{
+  QCString exampleLine=theTranslator->trWriteList(ed->count());
+
+  //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
+  //bool manEnabled   = ol.isEnabled(OutputGenerator::Man);
+  //bool htmlEnabled  = ol.isEnabled(OutputGenerator::Html);
+  QRegExp marker("@[0-9]+");
+  int index=0,newIndex,matchLen;
+  // now replace all markers in inheritLine with links to the classes
+  while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
+  {
+    bool ok;
+    ol.parseText(exampleLine.mid(index,newIndex-index));
+    uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
+    Example *e=ed->at(entryIndex);
+    if (ok && e) 
+    {
+      ol.pushGeneratorState();
+      //if (latexEnabled) ol.disable(OutputGenerator::Latex);
+      ol.disable(OutputGenerator::Latex);
+      ol.disable(OutputGenerator::RTF);
+      // link for Html / man
+      ol.writeObjectLink(0,e->file,e->anchor,e->name);
+      ol.popGeneratorState();
+
+      ol.pushGeneratorState();
+      //if (latexEnabled) ol.enable(OutputGenerator::Latex);
+      ol.disable(OutputGenerator::Man);
+      ol.disable(OutputGenerator::Html);
+      // link for Latex / pdf with anchor because the sources
+      // are not hyperlinked (not possible with a verbatim environment).
+      ol.writeObjectLink(0,e->file,0,e->name);
+      //if (manEnabled) ol.enable(OutputGenerator::Man);
+      //if (htmlEnabled) ol.enable(OutputGenerator::Html);
+      ol.popGeneratorState();
+    }
+    index=newIndex+matchLen;
+  } 
+  ol.parseText(exampleLine.right(exampleLine.length()-index));
+  ol.writeString(".");
+}
+
+
+QCString argListToString(ArgumentList *al,bool useCanonicalType)
+{
+  QCString result;
+  if (al==0) return result;
+  Argument *a=al->first();
+  result+="(";
+  while (a)
+  {
+    QCString type1 = useCanonicalType && !a->canType.isEmpty() ? 
+      a->canType : a->type;
+    QCString type2;
+    int i=type1.find(")("); // hack to deal with function pointers
+    if (i!=-1)
+    {
+      type2=type1.mid(i);
+      type1=type1.left(i);
+    }
+    if (!a->attrib.isEmpty())
+    {
+      result+=a->attrib+" ";
+    }
+    if (!a->name.isEmpty() || !a->array.isEmpty())
+    {
+      result+= type1+" "+a->name+type2+a->array;
+    }
+    else
+    {
+      result+= type1+type2;
+    }
+    if (!a->defval.isEmpty())
+    {
+      result+="="+a->defval;
+    }
+    a = al->next();
+    if (a) result+=", "; 
+  }
+  result+=")";
+  if (al->constSpecifier) result+=" const";
+  if (al->volatileSpecifier) result+=" volatile";
+  return removeRedundantWhiteSpace(result);
+}
+
+QCString tempArgListToString(ArgumentList *al)
+{
+  QCString result;
+  if (al==0) return result;
+  result="<";
+  Argument *a=al->first();
+  while (a)
+  {
+    if (!a->name.isEmpty()) // add template argument name
+    {
+      result+=a->name;
+    }
+    else // extract name from type
+    {
+      int i=a->type.length()-1;
+      while (i>=0 && isId(a->type.at(i))) i--;
+      if (i>0)
+      {
+        result+=a->type.right(a->type.length()-i-1);
+      }
+      else // nothing found -> take whole name
+      {
+        result+=a->type;
+      }
+    }
+    a=al->next();
+    if (a) result+=", ";
+  }
+  result+=">";
+  return removeRedundantWhiteSpace(result);
+}
+
+
+// compute the HTML anchors for a list of members
+void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId)
+{
+  int count=0;
+  if (ml==0) return;
+  MemberListIterator mli(*ml);
+  MemberDef *md;
+  for (;(md=mli.current());++mli)
+  {
+    if (!md->isReference())
+    {
+      QCString anchor;
+      if (groupId==-1)
+        anchor.sprintf("%c%d",id,count++);
+      else
+        anchor.sprintf("%c%d_%d",id,groupId,count++);
+      if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
+      md->setAnchor(anchor);
+      //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
+      //    md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
+    }
+  }
+}
+
+//----------------------------------------------------------------------------
+
+/*! takes the \a buf of the given lenght \a len and converts CR LF (DOS)
+ * or CR (MAC) line ending to LF (Unix).  Returns the length of the
+ * converted content (i.e. the same as \a len (Unix, MAC) or
+ * smaller (DOS).
+ */
+int filterCRLF(char *buf,int len)
+{
+  int src = 0;    // source index
+  int dest = 0;   // destination index
+  char c;         // current character
+
+  while (src<len)
+  {
+    c = buf[src++];            // Remember the processed character.
+    if (c == '\r')             // CR to be solved (MAC, DOS)
+    {
+      c = '\n';                // each CR to LF
+      if (src<len && buf[src] == '\n')
+        ++src;                 // skip LF just after CR (DOS) 
+    }
+    else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
+    {
+      c = ' ';                 // turn into a space
+    }
+    buf[dest++] = c;           // copy the (modified) character to dest
+  }
+  return dest;                 // length of the valid part of the buf
+}
+
+
+/*! looks for a filter for the file \a name.  Returns the name of the filter
+ *  if there is a match for the file name, otherwise an empty string.
+ */
+QCString getFileFilter(const char* name)
+{
+  // sanity check
+  if (name==0) return "";
+
+  // first look for filter pattern list
+  QStrList& filterList = Config_getList("FILTER_PATTERNS");
+
+  if (filterList.isEmpty()) 
+  {
+    // use INPUT_FILTER instead (For all files)
+    return Config_getString("INPUT_FILTER");
+  }
+
+  // compare the file name to the filter pattern list
+  QStrListIterator sli(filterList);
+  char* filterStr;
+  for (sli.toFirst(); (filterStr = sli.current()); ++sli)
+  {
+    QCString fs = filterStr;
+    int i_equals=fs.find('=');
+
+    if (i_equals!=-1)
+    {
+      QCString filterPattern = fs.left(i_equals);
+      QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE); 
+      if (fpat.match(name)!=-1) 
+      {
+        // found a match!
+        QCString filterName = fs.mid(i_equals+1);
+        if (filterName.find(' ')!=-1)
+        { // add quotes if the name has spaces
+          filterName="\""+filterName+"\"";
+        }
+        return filterName;
+      }
+    }
+  }
+
+  // no match
+  return "";
+}
+
+QCString recodeString(const QCString &str,const char *fromEncoding,const char *toEncoding)
+{
+  QCString inputEncoding  = fromEncoding;
+  QCString outputEncoding = toEncoding;
+  if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || 
+      inputEncoding==outputEncoding) return str;
+  int inputSize=str.length();
+  int outputSize=inputSize*4+1;
+  QCString output(outputSize);
+  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
+  if (cd==(void *)(-1))
+  {
+    err("Error: unsupported character conversion: '%s'->'%s'\n",
+        inputEncoding.data(),outputEncoding.data());
+    exit(1);
+  }
+  size_t iLeft=inputSize;
+  size_t oLeft=outputSize;
+  const char *inputPtr  = str.data();
+  char       *outputPtr = output.data();
+  if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
+  {
+    outputSize-=oLeft;
+    output.resize(outputSize+1);
+    output.at(outputSize)='\0';
+    //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
+  }
+  else
+  {
+    err("Error: failed to translate characters from %s to %s: %s\n",
+        inputEncoding.data(),outputEncoding.data(),strerror(errno));
+    exit(1);
+  }
+  portable_iconv_close(cd);
+  return output;
+}
+
+
+QCString transcodeCharacterStringToUTF8(const QCString &input)
+{
+  bool error=FALSE;
+  static QCString inputEncoding = Config_getString("INPUT_ENCODING");
+  const char *outputEncoding = "UTF-8";
+  if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
+  int inputSize=input.length();
+  int outputSize=inputSize*4+1;
+  QCString output(outputSize);
+  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
+  if (cd==(void *)(-1)) 
+  {
+    err("Error: unsupported character conversion: '%s'->'%s'\n",
+        inputEncoding.data(),outputEncoding);
+    error=TRUE;
+  }
+  if (!error)
+  {
+    size_t iLeft=inputSize;
+    size_t oLeft=outputSize;
+    const char *inputPtr = input.data();
+    char *outputPtr = output.data();
+    if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
+    {
+      outputSize-=oLeft;
+      output.resize(outputSize+1);
+      output.at(outputSize)='\0';
+      //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
+    }
+    else
+    {
+      err("Error: failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
+          inputEncoding.data(),outputEncoding,input.data());
+      error=TRUE;
+    }
+  }
+  portable_iconv_close(cd);
+  return error ? input : output;
+}
+
+/*! reads a file with name \a name and returns it as a string. If \a filter
+ *  is TRUE the file will be filtered by any user specified input filter.
+ *  If \a name is "-" the string will be read from standard input. 
+ */
+QCString fileToString(const char *name,bool filter)
+{
+  if (name==0 || name[0]==0) return 0;
+  QFile f;
+
+  bool fileOpened=FALSE;
+  if (name[0]=='-' && name[1]==0) // read from stdin
+  {
+    fileOpened=f.open(IO_ReadOnly,stdin);
+    if (fileOpened)
+    {
+      const int bSize=4096;
+      QCString contents(bSize);
+      int totalSize=0;
+      int size;
+      while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
+      {
+        totalSize+=bSize;
+        contents.resize(totalSize+bSize); 
+      }
+      totalSize = filterCRLF(contents.data(),totalSize+size)+2;
+      contents.resize(totalSize);
+      contents.at(totalSize-2)='\n'; // to help the scanner
+      contents.at(totalSize-1)='\0';
+      return contents;
+    }
+  }
+  else // read from file
+  {
+    QFileInfo fi(name);
+    if (!fi.exists() || !fi.isFile())
+    {
+      err("Error: file `%s' not found\n",name);
+      return "";
+    }
+    QCString filterName = getFileFilter(name);
+    if (filterName.isEmpty() || !filter)
+    {
+      f.setName(name);
+      fileOpened=f.open(IO_ReadOnly);
+      if (fileOpened)
+      {
+        int fsize=f.size();
+        QCString contents(fsize+2);
+        f.readBlock(contents.data(),fsize);
+        if (fsize==0 || contents[fsize-1]=='\n') 
+          contents[fsize]='\0';
+        else
+          contents[fsize]='\n'; // to help the scanner
+        contents[fsize+1]='\0';
+        f.close();
+        int newSize = filterCRLF(contents.data(),fsize+2);
+        if (newSize!=fsize+2) 
+        {
+          contents.resize(newSize);
+        }
+        return transcodeCharacterStringToUTF8(contents);
+      }
+    }
+    else // filter the input
+    {
+      QCString cmd=filterName+" \""+name+"\"";
+      Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
+      FILE *f=portable_popen(cmd,"r");
+      if (!f)
+      {
+        err("Error: could not execute filter %s\n",filterName.data());
+        return "";
+      }
+      const int bSize=4096;
+      QCString contents(bSize);
+      int totalSize=0;
+      int size;
+      while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize)
+      {
+        totalSize+=bSize;
+        contents.resize(totalSize+bSize); 
+      }
+      totalSize = filterCRLF(contents.data(),totalSize+size)+2;
+      contents.resize(totalSize);
+      contents.at(totalSize-2)='\n'; // to help the scanner
+      contents.at(totalSize-1)='\0';
+      portable_pclose(f);
+      return transcodeCharacterStringToUTF8(contents);
+    }
+  }
+  if (!fileOpened)  
+  {
+    err("Error: cannot open file `%s' for reading\n",name);
+  }
+  return "";
+}
+
+QCString dateToString(bool includeTime)
+{
+  if (includeTime)
+  {
+    return convertToQCString(QDateTime::currentDateTime().toString());
+  }
+  else
+  {
+    const QDate &d=QDate::currentDate();
+    QCString result;
+    result.sprintf("%d %s %d",
+        d.day(),
+        convertToQCString(d.monthName(d.month())).data(),
+        d.year());
+    return result;
+  }
+}
+
+QCString yearToString()
+{
+  const QDate &d=QDate::currentDate();
+  QCString result;
+  result.sprintf("%d", d.year());
+  return result;
+}
+
+//----------------------------------------------------------------------
+// recursive function that returns the number of branches in the 
+// inheritance tree that the base class `bcd' is below the class `cd'
+
+int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
+{
+  if (bcd->categoryOf()) // use class that is being extended in case of 
+    // an Objective-C category
+  {
+    bcd=bcd->categoryOf();
+  }
+  if (cd==bcd) return level; 
+  if (level==256)
+  {
+    err("Error: Internal inconsistency: found class %s seem to have a recursive "
+        "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
+    return -1;
+  }
+  int m=maxInheritanceDepth; 
+  if (cd->baseClasses())
+  {
+    BaseClassListIterator bcli(*cd->baseClasses());
+    for ( ; bcli.current() ; ++bcli)
+    {
+      //printf("class %s base class %s\n",cd->name().data(),bcli.current()->classDef->name().data());
+      int mc=minClassDistance(bcli.current()->classDef,bcd,level+1);
+      if (mc<m) m=mc;
+      if (m<0) break;
+    }
+  }
+  return m;
+}
+
+//static void printArgList(ArgumentList *al)
+//{
+//  if (al==0) return;
+//  ArgumentListIterator ali(*al);
+//  Argument *a;
+//  printf("(");
+//  for (;(a=ali.current());++ali)
+//  {
+//    printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():""); 
+//  }
+//  printf(")");
+//}
+
+#ifndef NEWMATCH
+// strip any template specifiers that follow className in string s
+static QCString trimTemplateSpecifiers(
+    const QCString &namespaceName,
+    const QCString &className,
+    const QCString &s
+    )
+{
+  //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
+  QCString scopeName=mergeScopes(namespaceName,className);
+  ClassDef *cd=getClass(scopeName);
+  if (cd==0) return s; // should not happen, but guard anyway.
+
+  QCString result=s;
+
+  int i=className.length()-1;
+  if (i>=0 && className.at(i)=='>') // template specialization
+  {
+    // replace unspecialized occurrences in s, with their specialized versions.
+    int count=1;
+    int cl=i+1;
+    while (i>=0)
+    {
+      char c=className.at(i);
+      if (c=='>') count++,i--;
+      else if (c=='<') { count--; if (count==0) break; }
+      else i--;
+    }
+    QCString unspecClassName=className.left(i);
+    int l=i;
+    int p=0;
+    while ((i=result.find(unspecClassName,p))!=-1)
+    {
+      if (result.at(i+l)!='<') // unspecialized version
+      {
+        result=result.left(i)+className+result.right(result.length()-i-l);
+        l=cl;
+      }
+      p=i+l;
+    }
+  }
+
+  //printf("result after specialization: %s\n",result.data());
+
+  QCString qualName=cd->qualifiedNameWithTemplateParameters();
+  //printf("QualifiedName = %s\n",qualName.data());
+  // We strip the template arguments following className (if any)
+  if (!qualName.isEmpty()) // there is a class name
+  {
+    int is,ps=0;
+    int p=0,l,i;
+
+    while ((is=getScopeFragment(qualName,ps,&l))!=-1)
+    {
+      QCString qualNamePart = qualName.right(qualName.length()-is);
+      //printf("qualNamePart=%s\n",qualNamePart.data());
+      while ((i=result.find(qualNamePart,p))!=-1)
+      {
+        int ql=qualNamePart.length();
+        result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
+        p=i+cd->name().length();
+      }
+      ps=is+l;
+    }
+  }
+  //printf("result=%s\n",result.data());
+
+  return result.stripWhiteSpace();
+}
+
+/*!
+ * @param pattern pattern to look for
+ * @param s string to search in
+ * @param p position to start
+ * @param len resulting pattern length
+ * @returns position on which string is found, or -1 if not found
+ */
+static int findScopePattern(const QCString &pattern,const QCString &s,
+    int p,int *len)
+{
+  int sl=s.length();
+  int pl=pattern.length();
+  int sp=0; 
+  *len=0;
+  while (p<sl)
+  {
+    sp=p; // start of match
+    int pp=0; // pattern position
+    while (p<sl && pp<pl)
+    {
+      if (s.at(p)=='<') // skip template arguments while matching
+      {
+        int bc=1;
+        //printf("skipping pos=%d c=%c\n",p,s.at(p));
+        p++;
+        while (p<sl)
+        {
+          if (s.at(p)=='<') bc++;
+          else if (s.at(p)=='>') 
+          {
+            bc--;
+            if (bc==0) 
+            {
+              p++;
+              break;
+            }
+          }
+          //printf("skipping pos=%d c=%c\n",p,s.at(p));
+          p++;
+        }
+      }
+      else if (s.at(p)==pattern.at(pp))
+      {
+        //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
+        p++;
+        pp++;
+      }
+      else // no match
+      {
+        //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
+        p=sp+1;
+        break;
+      }
+    }
+    if (pp==pl) // whole pattern matches
+    {
+      *len=p-sp;
+      return sp;
+    }
+  }
+  return -1;
+}
+
+static QCString trimScope(const QCString &name,const QCString &s)
+{
+  int scopeOffset=name.length();
+  QCString result=s;
+  do // for each scope
+  {
+    QCString tmp;
+    QCString scope=name.left(scopeOffset)+"::";
+    //printf("Trying with scope=`%s'\n",scope.data());
+
+    int i,p=0,l;
+    while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
+    {
+      tmp+=result.mid(p,i-p); // add part before pattern
+      p=i+l;
+    }
+    tmp+=result.right(result.length()-p); // add trailing part
+
+    scopeOffset=name.findRev("::",scopeOffset-1);
+    result = tmp;
+  } while (scopeOffset>0);   
+  //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
+  return result;
+}
+#endif
+
+void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
+{
+  //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
+  BaseClassListIterator bcli(*bcl);
+  BaseClassDef *bcd;
+  for (;(bcd=bcli.current());++bcli)
+  {
+    ClassDef *cd=bcd->classDef;
+    //printf("Trying class %s\n",cd->name().data());
+    int spos=s.find(cd->name()+"::");
+    if (spos!=-1)
+    {
+      s = s.left(spos)+s.right(
+          s.length()-spos-cd->name().length()-2
+          );
+    }
+    //printf("base class `%s'\n",cd->name().data());
+    if (cd->baseClasses())
+      trimBaseClassScope(cd->baseClasses(),s,level+1); 
+  }
+}
+
+#if 0
+/*! if either t1 or t2 contains a namespace scope, then remove that
+ *  scope. If neither or both have a namespace scope, t1 and t2 remain
+ *  unchanged.
+ */
+static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
+{
+  int p1=t1.length();
+  int p2=t2.length();
+  for (;;)
+  {
+    int i1=p1==0 ? -1 : t1.findRev("::",p1);
+    int i2=p2==0 ? -1 : t2.findRev("::",p2);
+    if (i1==-1 && i2==-1)
+    {
+      return;
+    }
+    if (i1!=-1 && i2==-1) // only t1 has a scope
+    {
+      QCString scope=t1.left(i1);
+      replaceNamespaceAliases(scope,i1);
+
+      int so=nsName.length();
+      do
+      {
+        QCString fullScope=nsName.left(so);
+        if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
+        fullScope+=scope;
+        if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
+        {
+          t1 = t1.right(t1.length()-i1-2);
+          return;
+        }
+        if (so==0)
+        {
+          so=-1;
+        }
+        else if ((so=nsName.findRev("::",so-1))==-1)
+        {
+          so=0;
+        }
+      }
+      while (so>=0);
+    }
+    else if (i1==-1 && i2!=-1) // only t2 has a scope
+    {
+      QCString scope=t2.left(i2);
+      replaceNamespaceAliases(scope,i2);
+
+      int so=nsName.length();
+      do
+      {
+        QCString fullScope=nsName.left(so);
+        if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
+        fullScope+=scope;
+        if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
+        {
+          t2 = t2.right(t2.length()-i2-2);
+          return;
+        }
+        if (so==0)
+        {
+          so=-1;
+        }
+        else if ((so=nsName.findRev("::",so-1))==-1)
+        {
+          so=0;
+        }
+      }
+      while (so>=0);
+    }
+    p1 = QMAX(i1-2,0);
+    p2 = QMAX(i2-2,0);
+  }
+}
+#endif
+
+static void stripIrrelevantString(QCString &target,const QCString &str)
+{
+  if (target==str) { target.resize(0); return; }
+  int i,p=0;
+  int l=str.length();
+  bool changed=FALSE;
+  while ((i=target.find(str,p))!=-1)
+  {
+    bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
+      (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
+    if (isMatch)
+    {
+      int i1=target.find('*',i+l);
+      int i2=target.find('&',i+l);
+      if (i1==-1 && i2==-1)
+      {
+        // strip str from target at index i
+        target=target.left(i)+target.right(target.length()-i-l); 
+        changed=TRUE;
+        i-=l;
+      }
+      else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
+      {
+        // move str to front
+        target=str+" "+target.left(i)+target.right(target.length()-i-l);
+        changed=TRUE;
+        i++;
+      }
+    }
+    p = i+l;
+  }
+  if (changed) target=target.stripWhiteSpace();
+}
+
+/*! According to the C++ spec and Ivan Vecerina:
+
+  Parameter declarations  that differ only in the presence or absence
+  of const and/or volatile are equivalent.
+
+  So the following example, show what is stripped by this routine
+  for const. The same is done for volatile.
+
+  \code
+  const T param     ->   T param          // not relevant
+  const T& param    ->   const T& param   // const needed               
+  T* const param    ->   T* param         // not relevant                   
+  const T* param    ->   const T* param   // const needed
+  \endcode
+ */
+void stripIrrelevantConstVolatile(QCString &s)
+{
+  //printf("stripIrrelevantConstVolatile(%s)=",s.data());
+  stripIrrelevantString(s,"const");
+  stripIrrelevantString(s,"volatile");
+  //printf("%s\n",s.data());
+}
+
+
+// a bit of debug support for matchArguments
+#define MATCH
+#define NOMATCH
+//#define MATCH printf("Match at line %d\n",__LINE__);
+//#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
+
+#ifndef NEWMATCH
+static bool matchArgument(const Argument *srcA,const Argument *dstA,
+    const QCString &className,
+    const QCString &namespaceName,
+    NamespaceSDict *usingNamespaces,
+    SDict<Definition> *usingClasses)
+{
+  //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
+  //    srcA->type.data(),srcA->name.data(),
+  //    dstA->type.data(),dstA->name.data(),
+  //    usingNamespaces,
+  //    usingClasses);
+
+  // TODO: resolve any typedefs names that are part of srcA->type
+  //       before matching. This should use className and namespaceName
+  //       and usingNamespaces and usingClass to determine which typedefs
+  //       are in-scope, so it will not be very efficient :-(
+
+  QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
+  QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
+  QCString srcAName=srcA->name.stripWhiteSpace();
+  QCString dstAName=dstA->name.stripWhiteSpace();
+  srcAType.stripPrefix("class ");
+  dstAType.stripPrefix("class ");
+
+  // allow distingishing "const A" from "const B" even though 
+  // from a syntactic point of view they would be two names of the same 
+  // type "const". This is not fool prove ofcourse, but should at least 
+  // catch the most common cases.
+  if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
+  {
+    srcAType+=" ";
+    srcAType+=srcAName;
+  } 
+  if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
+  {
+    dstAType+=" ";
+    dstAType+=dstAName;
+  }
+  if (srcAName=="const" || srcAName=="volatile")
+  {
+    srcAType+=srcAName;
+    srcAName.resize(0);
+  }
+  else if (dstA->name=="const" || dstA->name=="volatile")
+  {
+    dstAType+=dstA->name;
+    dstAName.resize(0);
+  }
+
+  stripIrrelevantConstVolatile(srcAType);
+  stripIrrelevantConstVolatile(dstAType);
+
+  // strip typename keyword
+  if (strncmp(srcAType,"typename ",9)==0)
+  {
+    srcAType = srcAType.right(srcAType.length()-9); 
+  }
+  if (strncmp(dstAType,"typename ",9)==0)
+  {
+    dstAType = dstAType.right(dstAType.length()-9); 
+  }
+
+  srcAType = removeRedundantWhiteSpace(srcAType);
+  dstAType = removeRedundantWhiteSpace(dstAType);
+
+  //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
+  //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
+
+  //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
+  //      dstAType.data(),dstAName.data());
+
+  if (srcA->array!=dstA->array) // nomatch for char[] against char
+  {
+    NOMATCH
+      return FALSE;
+  }
+  if (srcAType!=dstAType) // check if the argument only differs on name 
+  {
+
+    // remove a namespace scope that is only in one type 
+    // (assuming a using statement was used)
+    //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
+    //trimNamespaceScope(srcAType,dstAType,namespaceName);
+    //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
+
+    //QCString srcScope;
+    //QCString dstScope;
+
+    // strip redundant scope specifiers
+    if (!className.isEmpty())
+    {
+      srcAType=trimScope(className,srcAType);
+      dstAType=trimScope(className,dstAType);
+      //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
+      ClassDef *cd;
+      if (!namespaceName.isEmpty())
+        cd=getClass(namespaceName+"::"+className);
+      else
+        cd=getClass(className);
+      if (cd && cd->baseClasses())
+      {
+        trimBaseClassScope(cd->baseClasses(),srcAType); 
+        trimBaseClassScope(cd->baseClasses(),dstAType); 
+      }
+      //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
+    }
+    if (!namespaceName.isEmpty())
+    {
+      srcAType=trimScope(namespaceName,srcAType);
+      dstAType=trimScope(namespaceName,dstAType);
+    }
+    //printf("#usingNamespace=%d\n",usingNamespaces->count());
+    if (usingNamespaces && usingNamespaces->count()>0)
+    {
+      NamespaceSDict::Iterator nli(*usingNamespaces);
+      NamespaceDef *nd;
+      for (;(nd=nli.current());++nli)
+      {
+        srcAType=trimScope(nd->name(),srcAType);
+        dstAType=trimScope(nd->name(),dstAType);
+      }
+    }
+    //printf("#usingClasses=%d\n",usingClasses->count());
+    if (usingClasses && usingClasses->count()>0)
+    {
+      SDict<Definition>::Iterator cli(*usingClasses);
+      Definition *cd;
+      for (;(cd=cli.current());++cli)
+      {
+        srcAType=trimScope(cd->name(),srcAType);
+        dstAType=trimScope(cd->name(),dstAType);
+      }
+    }
+
+    //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
+    //    dstAType.data(),dstAName.data());
+
+    if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
+        (srcAType+" "+srcAName)==dstAType)
+    {
+      MATCH
+        return TRUE;
+    }
+    else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
+        (dstAType+" "+dstAName)==srcAType)
+    {
+      MATCH
+        return TRUE;
+    }
+
+
+    uint srcPos=0,dstPos=0; 
+    bool equal=TRUE;
+    while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
+    {
+      equal=srcAType.at(srcPos)==dstAType.at(dstPos);
+      if (equal) srcPos++,dstPos++; 
+    }
+    uint srcATypeLen=srcAType.length();
+    uint dstATypeLen=dstAType.length();
+    if (srcPos<srcATypeLen && dstPos<dstATypeLen)
+    {
+      // if nothing matches or the match ends in the middle or at the
+      // end of a string then there is no match
+      if (srcPos==0 || dstPos==0) 
+      {
+        NOMATCH
+          return FALSE;
+      }
+      if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
+      {
+        //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
+        // check if a name if already found -> if no then there is no match
+        if (!srcAName.isEmpty() || !dstAName.isEmpty()) 
+        {
+          NOMATCH
+            return FALSE;
+        }
+        // types only
+        while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
+        while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
+        if (srcPos<srcATypeLen || 
+            dstPos<dstATypeLen ||
+            (srcPos==srcATypeLen && dstPos==dstATypeLen)
+           ) 
+        {
+          NOMATCH
+            return FALSE;
+        }
+      }
+      else
+      {
+        // otherwise we assume that a name starts at the current position.
+        while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
+        while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
+
+        // if nothing more follows for both types then we assume we have
+        // found a match. Note that now `signed int' and `signed' match, but
+        // seeing that int is not a name can only be done by looking at the
+        // semantics.
+
+        if (srcPos!=srcATypeLen || dstPos!=dstATypeLen) 
+        { 
+          NOMATCH
+            return FALSE; 
+        }
+      }
+    }
+    else if (dstPos<dstAType.length())
+    {
+      if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
+      {
+        if (!dstAName.isEmpty()) // dst has its name separated from its type
+        {
+          NOMATCH
+            return FALSE;
+        }
+        while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
+        if (dstPos!=dstAType.length()) 
+        {
+          NOMATCH
+            return FALSE; // more than a difference in name -> no match
+        }
+      }
+      else  // maybe dst has a name while src has not
+      {
+        dstPos++;
+        while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
+        if (dstPos!=dstAType.length() || !srcAName.isEmpty()) 
+        {
+          NOMATCH
+            return FALSE; // nope not a name -> no match
+        }
+      }
+    }
+    else if (srcPos<srcAType.length())
+    {
+      if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
+      {
+        if (!srcAName.isEmpty()) // src has its name separated from its type
+        {
+          NOMATCH
+            return FALSE;
+        }
+        while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
+        if (srcPos!=srcAType.length()) 
+        {
+          NOMATCH
+            return FALSE; // more than a difference in name -> no match
+        }
+      }
+      else // maybe src has a name while dst has not
+      {
+        srcPos++;
+        while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
+        if (srcPos!=srcAType.length() || !dstAName.isEmpty()) 
+        {
+          NOMATCH
+            return FALSE; // nope not a name -> no match
+        }
+      }
+    }
+  }
+  MATCH
+    return TRUE;
+}
+
+
+/*!
+ * Matches the arguments list srcAl with the argument list dstAl
+ * Returns TRUE if the argument lists are equal. Two argument list are 
+ * considered equal if the number of arguments is equal and the types of all 
+ * arguments are equal. Furthermore the const and volatile specifiers 
+ * stored in the list should be equal.
+ */
+bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
+    const char *cl,const char *ns,bool checkCV,
+    NamespaceSDict *usingNamespaces,
+    SDict<Definition> *usingClasses)
+{
+  QCString className=cl;
+  QCString namespaceName=ns;
+
+  // strip template specialization from class name if present
+  //int til=className.find('<'),tir=className.find('>');
+  //if (til!=-1 && tir!=-1 && tir>til) 
+  //{
+  //  className=className.left(til)+className.right(className.length()-tir-1);
+  //}
+
+  //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
+  //    srcAl ? argListToString(srcAl).data() : "",
+  //    dstAl ? argListToString(dstAl).data() : "",
+  //    cl,ns,checkCV,
+  //    usingNamespaces?usingNamespaces->count():0,
+  //    usingClasses?usingClasses->count():0
+  //    );
+
+  if (srcAl==0 || dstAl==0)
+  {
+    bool match = srcAl==dstAl; // at least one of the members is not a function
+    if (match)
+    {
+      MATCH
+        return TRUE;
+    }
+    else
+    {
+      NOMATCH
+        return FALSE;
+    }
+  }
+
+  // handle special case with void argument
+  if ( srcAl->count()==0 && dstAl->count()==1 && 
+      dstAl->getFirst()->type=="void" )
+  { // special case for finding match between func() and func(void)
+    Argument *a=new Argument;
+    a->type = "void";
+    srcAl->append(a);
+    MATCH
+      return TRUE;
+  }
+  if ( dstAl->count()==0 && srcAl->count()==1 &&
+      srcAl->getFirst()->type=="void" )
+  { // special case for finding match between func(void) and func()
+    Argument *a=new Argument;
+    a->type = "void";
+    dstAl->append(a);
+    MATCH
+      return TRUE;
+  }
+
+  if (srcAl->count() != dstAl->count())
+  {
+    NOMATCH
+      return FALSE; // different number of arguments -> no match
+  }
+
+  if (checkCV)
+  {
+    if (srcAl->constSpecifier != dstAl->constSpecifier) 
+    {
+      NOMATCH
+        return FALSE; // one member is const, the other not -> no match
+    }
+    if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
+    {
+      NOMATCH
+        return FALSE; // one member is volatile, the other not -> no match
+    }
+  }
+
+  // so far the argument list could match, so we need to compare the types of
+  // all arguments.
+  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+  Argument *srcA,*dstA;
+  for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+  { 
+    if (!matchArgument(srcA,dstA,className,namespaceName,
+          usingNamespaces,usingClasses))
+    {
+      NOMATCH
+        return FALSE;
+    }
+  }
+  MATCH
+    return TRUE; // all arguments match 
+}
+
+#endif
+
+#if 0
+static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
+{
+  ASSERT(symbol!=0);
+  if (symbol->definitionType()==Definition::TypeMember && 
+      ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
+    // to resolve it
+  {
+    MemberDef *md = 0;
+    ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
+    if (cd)
+    {
+      return cd->qualifiedName()+templSpec;
+    }
+    else if (md)
+    {
+      return md->qualifiedName();
+    }
+  }
+  return symbol->qualifiedName();
+}
+#endif
+
+static QCString stripDeclKeywords(const QCString &s)
+{
+  int i=s.find(" class ");
+  if (i!=-1) return s.left(i)+s.mid(i+6);
+  i=s.find(" typename ");
+  if (i!=-1) return s.left(i)+s.mid(i+9);
+  i=s.find(" union ");
+  if (i!=-1) return s.left(i)+s.mid(i+6);
+  i=s.find(" struct ");
+  if (i!=-1) return s.left(i)+s.mid(i+7);
+  return s;
+}
+
+// forward decl for circular dependencies
+static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
+
+QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
+{
+  
+  QCString templSpec = spec.stripWhiteSpace();
+  // this part had been commented out before... but it is needed to match for instance
+  // std::list<std::string> against list<string> so it is now back again!
+  if (!templSpec.isEmpty() && templSpec.at(0) == '<') 
+  {
+    templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
+  }
+  QCString resolvedType = resolveTypeDef(d,templSpec);
+  if (!resolvedType.isEmpty()) // not known as a typedef either
+  {
+    templSpec = resolvedType;
+  }
+  //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
+  return templSpec;
+}
+
+
+static QCString getCanonicalTypeForIdentifier(
+    Definition *d,FileDef *fs,const QCString &word,
+    QCString *tSpec)
+{
+  QCString symName,scope,result,templSpec,tmpName;
+  //DefinitionList *defList=0;
+  if (tSpec && !tSpec->isEmpty()) templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
+
+  if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
+  {
+    symName=tmpName; // name without scope
+  }
+  else
+  {
+    symName=word;
+  }
+  //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
+  //    word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
+
+  ClassDef *cd = 0;
+  MemberDef *mType = 0;
+  QCString ts;
+  QCString resolvedType;
+
+  // lookup class / class template instance
+  cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
+  bool isTemplInst = cd && !templSpec.isEmpty();
+  if (!cd && !templSpec.isEmpty())
+  {
+    // class template specialization not known, look up class template
+    cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
+  }
+  if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
+
+  //printf("  getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
+  //    symName.data(),
+  //    word.data(),
+  //    cd?cd->name().data():"<none>",
+  //    d?d->name().data():"<none>",
+  //    fs?fs->name().data():"<none>",
+  //    cd?cd->isTemplate():-1
+  //   );
+
+  //printf("  >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
+  //    (word+templSpec).data(),
+  //    cd?cd->qualifiedName().data():"<none>",
+  //    templSpec.data(),ts.data(),
+  //    tSpec?tSpec->data():"<null>",
+  //    cd?cd->isTemplate():FALSE,
+  //    resolvedType.data());
+
+  //printf("  mtype=%s\n",mType?mType->name().data():"<none>");
+
+  if (cd) // resolves to a known class type
+  {
+    if (cd==d && tSpec) *tSpec="";
+
+    if (mType && mType->isTypedef()) // but via a typedef
+    {
+      result = resolvedType;
+    }
+    else
+    {
+      if (isTemplInst)
+      {
+        // spec is already part of class type
+        templSpec="";
+        if (tSpec) *tSpec="";
+      }
+      else if (!ts.isEmpty() && templSpec.isEmpty())
+      {
+        // use formal template args for spec
+        templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
+      }
+
+      result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
+
+      if (cd->isTemplate() && tSpec) //
+      {
+        if (!templSpec.isEmpty()) // specific instance
+        {
+          result=cd->name()+templSpec;
+        }
+        else // use template type
+        {
+          result=cd->qualifiedNameWithTemplateParameters();
+        }
+        // template class, so remove the template part (it is part of the class name)
+        *tSpec="";
+      }
+      else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
+      {
+        // obscure case, where a class is used as a template, but doxygen think it is
+        // not (could happen when loading the class from a tag file).
+        *tSpec="";
+      }
+    }
+  }
+  else if (mType && mType->isEnumerate()) // an enum
+  {
+    result = mType->qualifiedName();
+  }
+  else // fallback
+  {
+    resolvedType = resolveTypeDef(d,word);
+    //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
+    if (resolvedType.isEmpty()) // not known as a typedef either
+    {
+      result = word;
+    }
+    else
+    {
+      result = resolvedType;
+    }
+  }
+  //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
+  return result;
+}
+
+static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
+{
+  type = type.stripWhiteSpace();
+
+  // strip const and volatile keywords that are not relevant for the type
+  stripIrrelevantConstVolatile(type);
+
+  // strip leading keywords
+  type.stripPrefix("class ");
+  type.stripPrefix("struct ");
+  type.stripPrefix("union ");
+  type.stripPrefix("enum ");
+  type.stripPrefix("typename ");
+
+  type = removeRedundantWhiteSpace(type);
+  //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
+  //    d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
+
+  //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
+
+  QCString canType;
+  QCString templSpec,word;
+  int i,p=0,pp=0;
+  while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
+    // foreach identifier in the type
+  {
+    //printf("     i=%d p=%d\n",i,p);
+    if (i>pp) canType += type.mid(pp,i-pp);
+
+
+    QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
+
+    // in case the ct is empty it means that "word" represents scope "d"
+    // and this does not need to be added to the canonical 
+    // type (it is redundant), so/ we skip it. This solves problem 589616.
+    if (ct.isEmpty() && type.mid(p,2)=="::")
+    {
+      p+=2;
+    }
+    else
+    {
+      canType += ct;
+    }
+    //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
+    //    word.data(),templSpec.data(),canType.data(),ct.data());
+    if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
+                              // (i.e. type is not a template specialization)
+                              // then resolve any identifiers inside. 
+    {
+      static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+      int tp=0,tl,ti;
+      // for each identifier template specifier
+      //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
+      while ((ti=re.match(templSpec,tp,&tl))!=-1)
+      {
+        canType += templSpec.mid(tp,ti-tp);
+        canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
+        tp=ti+tl;
+      }
+      canType+=templSpec.right(templSpec.length()-tp);
+    }
+
+    pp=p;
+  }
+  canType += type.right(type.length()-pp);
+  //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
+
+  return removeRedundantWhiteSpace(canType);
+}
+
+static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
+{
+  QCString type = arg->type.stripWhiteSpace();
+  QCString name = arg->name;
+  //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
+  if ((type=="const" || type=="volatile") && !name.isEmpty()) 
+  { // name is part of type => correct
+    type+=" ";
+    type+=name;
+  } 
+  if (name=="const" || name=="volatile")
+  { // name is part of type => correct
+    if (!type.isEmpty()) type+=" ";
+    type+=name;
+  }
+
+  return extractCanonicalType(d,fs,type);
+}
+
+static bool matchArgument2(
+    Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
+    Definition *dstScope,FileDef *dstFileScope,Argument *dstA
+    )
+{
+  //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
+  //    srcScope ? srcScope->name().data() : "",
+  //    srcA->type.data(),srcA->name.data(),srcA->canType.data(),
+  //    dstScope ? dstScope->name().data() : "",
+  //    dstA->type.data(),dstA->name.data(),dstA->canType.data());
+
+  if (srcA->array!=dstA->array) // nomatch for char[] against char
+  {
+    NOMATCH
+    return FALSE;
+  }
+  QCString sSrcName = " "+srcA->name;
+  QCString sDstName = " "+dstA->name;
+  QCString srcType  = srcA->type;
+  QCString dstType  = dstA->type;
+  stripIrrelevantConstVolatile(srcType);
+  stripIrrelevantConstVolatile(dstType);
+  //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
+  //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
+  if (sSrcName==dstType.right(sSrcName.length()))
+  { // case "unsigned int" <-> "unsigned int i"
+    srcA->type+=sSrcName;
+    srcA->name="";
+    srcA->canType=""; // invalidate cached type value
+  }
+  else if (sDstName==srcType.right(sDstName.length()))
+  { // case "unsigned int i" <-> "unsigned int"
+    dstA->type+=sDstName;
+    dstA->name="";
+    dstA->canType=""; // invalidate cached type value
+  }
+
+  if (srcA->canType.isEmpty())
+  {
+    srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
+  }
+  if (dstA->canType.isEmpty())
+  {
+    dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
+  }
+
+  if (srcA->canType==dstA->canType)
+  {
+    MATCH
+    return TRUE;
+  }
+  else
+  {
+    //printf("   Canonical types do not match [%s]<->[%s]\n",
+    //    srcA->canType.data(),dstA->canType.data());
+    NOMATCH
+    return FALSE;
+  }
+}
+
+
+// new algorithm for argument matching
+bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
+    Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
+    bool checkCV
+    )
+{
+  //printf("*** matchArguments2\n");
+  ASSERT(srcScope!=0 && dstScope!=0);
+
+  if (srcAl==0 || dstAl==0)
+  {
+    bool match = srcAl==dstAl; // at least one of the members is not a function
+    if (match)
+    {
+      MATCH
+      return TRUE;
+    }
+    else
+    {
+      NOMATCH
+      return FALSE;
+    }
+  }
+
+  // handle special case with void argument
+  if ( srcAl->count()==0 && dstAl->count()==1 && 
+      dstAl->getFirst()->type=="void" )
+  { // special case for finding match between func() and func(void)
+    Argument *a=new Argument;
+    a->type = "void";
+    srcAl->append(a);
+    MATCH
+    return TRUE;
+  }
+  if ( dstAl->count()==0 && srcAl->count()==1 &&
+      srcAl->getFirst()->type=="void" )
+  { // special case for finding match between func(void) and func()
+    Argument *a=new Argument;
+    a->type = "void";
+    dstAl->append(a);
+    MATCH
+    return TRUE;
+  }
+
+  if (srcAl->count() != dstAl->count())
+  {
+    NOMATCH
+    return FALSE; // different number of arguments -> no match
+  }
+
+  if (checkCV)
+  {
+    if (srcAl->constSpecifier != dstAl->constSpecifier) 
+    {
+      NOMATCH
+      return FALSE; // one member is const, the other not -> no match
+    }
+    if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
+    {
+      NOMATCH
+      return FALSE; // one member is volatile, the other not -> no match
+    }
+  }
+
+  // so far the argument list could match, so we need to compare the types of
+  // all arguments.
+  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+  Argument *srcA,*dstA;
+  for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+  { 
+    if (!matchArgument2(srcScope,srcFileScope,srcA,
+          dstScope,dstFileScope,dstA)
+       )
+    {
+      NOMATCH
+      return FALSE;
+    }
+  }
+  MATCH
+  return TRUE; // all arguments match 
+}
+
+
+
+// merges the initializer of two argument lists
+// pre:  the types of the arguments in the list should match.
+void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
+{
+  //printf("mergeArguments `%s', `%s'\n",
+  //    argListToString(srcAl).data(),argListToString(dstAl).data());
+
+  if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
+  {
+    return; // invalid argument lists -> do not merge
+  }
+
+  ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+  Argument *srcA,*dstA;
+  for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+  {
+    if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
+    {
+      //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
+      srcA->defval=dstA->defval.copy();
+    }
+    else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
+    {
+      //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
+      dstA->defval=srcA->defval.copy();
+    }
+
+    // fix wrongly detected const or volatile specificiers before merging.
+    // example: "const A *const" is detected as type="const A *" name="const"
+    if (srcA->name=="const" || srcA->name=="volatile")
+    {
+      srcA->type+=" "+srcA->name;
+      srcA->name.resize(0);
+    }
+    if (dstA->name=="const" || dstA->name=="volatile")
+    {
+      dstA->type+=" "+dstA->name;
+      dstA->name.resize(0);
+    }
+
+    if (srcA->type==dstA->type)
+    {
+      //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
+      if (srcA->name.isEmpty() && !dstA->name.isEmpty())
+      {
+        //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
+        //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
+        srcA->type = dstA->type.copy();
+        srcA->name = dstA->name.copy();
+      }
+      else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
+      {
+        //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
+        //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+        dstA->type = srcA->type.copy();
+        dstA->name = dstA->name.copy();
+      }
+      else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
+      {
+        //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
+        if (forceNameOverwrite)
+        {
+          srcA->name = dstA->name;
+        }
+        else
+        {
+          if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
+          {
+            srcA->name = dstA->name;
+          }
+          else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
+          {
+            dstA->name = srcA->name;
+          }
+        }
+      }
+    }
+    else
+    {
+      //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
+      srcA->type=srcA->type.stripWhiteSpace();
+      dstA->type=dstA->type.stripWhiteSpace();
+      if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
+      {
+        srcA->type+=" "+srcA->name;
+        srcA->name=dstA->name;
+      }
+      else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
+      {
+        dstA->type+=" "+dstA->name;
+        dstA->name=srcA->name;
+      }
+      else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
+      {
+        srcA->name = dstA->name;
+      }
+      else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
+      {
+        dstA->name = srcA->name;
+      }
+    }
+    int i1=srcA->type.find("::"),
+        i2=dstA->type.find("::"),
+        j1=srcA->type.length()-i1-2,
+        j2=dstA->type.length()-i2-2;
+    if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
+    {
+      //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
+      //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+      dstA->type = srcA->type.left(i1+2)+dstA->type;
+      dstA->name = dstA->name.copy();
+    }
+    else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
+    {
+      //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
+      //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+      srcA->type = dstA->type.left(i2+2)+srcA->type;
+      srcA->name = dstA->name.copy();
+    }
+    if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
+    {
+      srcA->docs = dstA->docs.copy();
+    }
+    else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
+    {
+      dstA->docs = srcA->docs.copy();
+    }
+    //printf("Merge argument `%s|%s' `%s|%s'\n",
+    //  srcA->type.data(),srcA->name.data(),
+    //  dstA->type.data(),dstA->name.data());
+  }
+}
+
+static void findMembersWithSpecificName(MemberName *mn,
+                                        const char *args,
+                                        bool checkStatics,
+                                        FileDef *currentFile,
+                                        bool checkCV,
+                                        QList<MemberDef> &members)
+{
+  //printf("\n  findMembersWithSpecificName() - start\n");
+  //printf("  findMembersWithSpecificName() Function with global scope name `%s' args=`%s'\n",mn->memberName(),args);
+  MemberListIterator mli(*mn);
+  MemberDef *md;
+  for (mli.toFirst();(md=mli.current());++mli)
+  {
+    FileDef  *fd=md->getFileDef();
+    GroupDef *gd=md->getGroupDef();
+    //printf("  findMembersWithSpecificName() md->name()=`%s' md->args=`%s' fd=%p gd=%p file=%s\n",
+	//	md->name().data(),args,fd,gd,fd?fd->absFilePath().data():"");
+	//if (gd) printf("  findMembersWithSpecificName() group isLinkable()=%d\n", gd->isLinkable());
+	//if (fd) printf("  findMembersWithSpecificName() file  isLinkable()=%d\n", fd->isLinkable());
+	//if (md) printf("  findMembersWithSpecificName() memb  isLinkable()=%d\n", md->isLinkable());
+    if (
+        ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) && 
+        md->getNamespaceDef()==0 && md->isLinkable() &&
+        (!checkStatics || !md->isStatic() || currentFile==0 || fd==currentFile) // statics must appear in the same file
+       )
+    {
+      //printf("  findMembersWithSpecificName() fd=%p gd=%p args=`%s'\n",fd,gd,args);
+      bool match=TRUE;
+      ArgumentList *argList=0;
+      if (args && !md->isDefine() && strcmp(args,"()")!=0)
+      {
+        argList=new ArgumentList;
+        LockingPtr<ArgumentList> mdAl = md->argumentList();
+        stringToArgumentList(args,argList);
+        match=matchArguments2(
+            md->getOuterScope(),fd,mdAl.pointer(),
+            Doxygen::globalScope,fd,argList,
+            checkCV); 
+        delete argList; argList=0;
+      }
+      if (match) 
+      {
+        //printf("  findMembersWithSpecificName() Found match!\n");
+        members.append(md);
+      }
+    }
+  }
+  //printf("  findMembersWithSpecificName() - done\n");
+}
+
+/*!
+ * Searches for a member definition given its name `memberName' as a string.
+ * memberName may also include a (partial) scope to indicate the scope
+ * in which the member is located.
+ *
+ * The parameter `scName' is a string representing the name of the scope in 
+ * which the link was found.
+ *
+ * In case of a function args contains a string representation of the 
+ * argument list. Passing 0 means the member has no arguments. 
+ * Passing "()" means any argument list will do, but "()" is preferred.
+ *
+ * The function returns TRUE if the member is known and documented or
+ * FALSE if it is not.
+ * If TRUE is returned parameter `md' contains a pointer to the member 
+ * definition. Furthermore exactly one of the parameter `cd', `nd', or `fd' 
+ * will be non-zero:
+ *   - if `cd' is non zero, the member was found in a class pointed to by cd.
+ *   - if `nd' is non zero, the member was found in a namespace pointed to by nd.
+ *   - if `fd' is non zero, the member was found in the global namespace of
+ *     file fd.
+ */
+bool getDefs(const QCString &scName,const QCString &memberName, 
+    const char *args,
+    MemberDef *&md, 
+    ClassDef *&cd, FileDef *&fd, NamespaceDef *&nd, GroupDef *&gd,
+    bool forceEmptyScope,
+    FileDef *currentFile,
+    bool checkCV
+    )
+{
+  //printf("\ngetDefs(): - start\n");
+  fd=0, md=0, cd=0, nd=0, gd=0;
+  if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
+
+  QCString scopeName=scName;
+  //printf("getDefs(): Search for name=\"%s\" args=%s in scope=%s\n",
+  //          memberName.data(),args,scopeName.data());
+
+  int is,im=0,pm=0;
+  // strip common part of the scope from the scopeName
+  while ((is=scopeName.findRev("::"))!=-1 && 
+      (im=memberName.find("::",pm))!=-1 &&
+      (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
+      )
+  {
+    scopeName=scopeName.left(is); 
+    pm=im+2;
+  }
+  //printf("getDefs(): result after scope corrections scope=%s name=%s\n",
+  //          scopeName.data(),memberName.data());
+
+  QCString mName=memberName;
+  QCString mScope;
+  if (memberName.left(9)!="operator " && // treat operator conversion methods
+      // as a special case
+      (im=memberName.findRev("::"))!=-1 && 
+      im<(int)memberName.length()-2 // not A::
+     )
+  {
+    mScope=memberName.left(im); 
+    mName=memberName.right(memberName.length()-im-2);
+  }
+
+  // handle special the case where both scope name and member scope are equal
+  if (mScope==scopeName) scopeName.resize(0);
+
+  //printf("getDefs(): mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
+
+  MemberName *mn = Doxygen::memberNameSDict->find(mName);
+  //printf("getDefs(): mName=%s mn=%p\n",mName.data(),mn);
+  if (!forceEmptyScope && mn && !(scopeName.isEmpty() && mScope.isEmpty()))
+  {
+    //printf("getDefs(): >member name found\n");
+    int scopeOffset=scopeName.length();
+    do
+    {
+      QCString className = scopeName.left(scopeOffset);
+      if (!className.isEmpty() && !mScope.isEmpty())
+      {
+        className+="::"+mScope;
+      }
+      else if (!mScope.isEmpty())
+      {
+        className=mScope.copy();
+      }
+      //printf("getDefs(): Trying class scope %s\n",className.data());
+
+      ClassDef *fcd=0;
+      // todo: fill in correct fileScope!
+      if ((fcd=getResolvedClass(Doxygen::globalScope,0,className)) &&  // is it a documented class
+          fcd->isLinkable() 
+         )
+      {
+        //printf("getDefs(): getDefs(): Found fcd=%p\n",fcd);
+        MemberListIterator mmli(*mn);
+        MemberDef *mmd;
+        int mdist=maxInheritanceDepth; 
+        ArgumentList *argList=0;
+        if (args)
+        {
+          argList=new ArgumentList;
+          stringToArgumentList(args,argList);
+        }
+        for (mmli.toFirst();(mmd=mmli.current());++mmli)
+        {
+          //if (mmd->isLinkable())
+          //{
+          LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+          bool match=args==0 || 
+            matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+                fcd,fcd->getFileDef(),argList,
+                checkCV
+                );  
+          //printf("getDefs(): match=%d\n",match);
+          if (match)
+          {
+            ClassDef *mcd=mmd->getClassDef();
+            if (mcd)
+            {
+              int m=minClassDistance(fcd,mcd);
+              if (m<mdist && mcd->isLinkable())
+              {
+                mdist=m;
+                cd=mcd;
+                md=mmd;
+              }
+            }
+          }
+          //}
+        }
+        if (argList)
+        {
+          delete argList; argList=0;
+        }
+        if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0)
+          // no exact match found, but if args="()" an arbitrary member will do
+        {
+          //printf("getDefs(): >Searching for arbitrary member\n");
+          for (mmli.toFirst();(mmd=mmli.current());++mmli)
+          {
+            //if (mmd->isLinkable())
+            //{
+            ClassDef *mcd=mmd->getClassDef();
+            //printf("getDefs(): >Class %s found\n",mcd->name().data());
+            if (mcd)
+            {
+              int m=minClassDistance(fcd,mcd);
+              if (m<mdist /* && mcd->isLinkable()*/ )
+              {
+                //printf("getDefs(): Class distance %d\n",m);
+                mdist=m;
+                cd=mcd;
+                md=mmd;
+              }
+            }
+            //}
+          }
+        }
+        //printf("getDefs(): >Success=%d\n",mdist<maxInheritanceDepth);
+        if (mdist<maxInheritanceDepth) 
+        {
+          if (!md->isLinkable()) 
+          {
+			//printf("getDefs(): >Success but not isLinkable=%d\n", md->isLinkable());
+            md=0; // avoid returning things we cannot link to
+            cd=0;
+            return FALSE; // match found, but was not linkable
+          }
+          else
+          {
+            gd=md->getGroupDef();
+            if (gd) cd=0;
+			//printf("getDefs(): >Success, isLinkable=%d\n", md->isLinkable());
+            return TRUE; /* found match */
+          }
+        }
+      } 
+      /* go to the parent scope */
+
+      if (scopeOffset==0)
+      {
+        scopeOffset=-1;
+      }
+      else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
+      {
+        scopeOffset=0;
+      }
+    } while (scopeOffset>=0);
+
+    // unknown or undocumented scope 
+  }
+  if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
+  {
+    MemberListIterator mmli(*mn);
+    MemberDef *mmd, *fuzzy_mmd = 0;
+    ArgumentList *argList = 0;
+    bool hasEmptyArgs = args && strcmp(args, "()") == 0;
+
+    if (args)
+      stringToArgumentList(args, argList = new ArgumentList);
+
+    for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
+    {
+      if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
+           !mmd->getClassDef())
+        continue;
+
+      if (!args) break;
+
+      QCString className = mmd->getClassDef()->name();
+
+      LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+      if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+            Doxygen::globalScope,mmd->getFileDef(),argList,
+            checkCV
+            )
+         ) break;
+
+      if (!fuzzy_mmd && hasEmptyArgs)
+        fuzzy_mmd = mmd;
+    }
+
+    if (argList) delete argList, argList = 0;
+
+    mmd = mmd ? mmd : fuzzy_mmd;
+
+    if (mmd)
+    {
+      md = mmd;
+      cd = mmd->getClassDef();
+	  //printf("getDefs(): >Success, mmd=%s\n", mmd->name().data());
+      return TRUE;
+    }
+  }
+
+
+  // maybe an namespace, file or group member ?
+  //printf("getDefs(): Testing for global function scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
+  //              scopeName.data(),mScope.data(),mName.data());
+  if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
+  {
+    //printf("getDefs(): >function name found\n");
+    NamespaceDef *fnd=0;
+    int scopeOffset=scopeName.length();
+    do
+    {
+      QCString namespaceName = scopeName.left(scopeOffset);
+      if (!namespaceName.isEmpty() && !mScope.isEmpty())
+      {
+        namespaceName+="::"+mScope;
+      }
+      else if (!mScope.isEmpty())
+      {
+        namespaceName=mScope.copy();
+      }
+      //printf("getDefs(): Trying namespace %s\n",namespaceName.data());
+      if (!namespaceName.isEmpty() && 
+          (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
+          fnd->isLinkable()
+         )
+      {
+        //printf("getDefs(): Function inside existing namespace `%s'\n",namespaceName.data());
+        bool found=FALSE;
+        MemberListIterator mmli(*mn);
+        MemberDef *mmd;
+        for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
+        {
+          //printf("getDefs(): mmd->getNamespaceDef()=%p fnd=%p\n",
+          //    mmd->getNamespaceDef(),fnd);
+          if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
+          { // namespace is found
+            bool match=TRUE;
+            ArgumentList *argList=0;
+            if (args && strcmp(args,"()")!=0)
+            {
+              argList=new ArgumentList;
+              LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+              stringToArgumentList(args,argList);
+              match=matchArguments2(
+                  mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+                  fnd,mmd->getFileDef(),argList,
+                  checkCV); 
+            }
+            if (match)
+            {
+              nd=fnd;
+              md=mmd;
+              found=TRUE;
+            }
+            if (args)
+            {
+              delete argList; argList=0;
+            }
+          }
+        }
+        if (!found && args && !strcmp(args,"()")) 
+          // no exact match found, but if args="()" an arbitrary 
+          // member will do
+        {
+          for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
+          {
+            if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
+            {
+              nd=fnd;
+              md=mmd;
+              found=TRUE;
+            }
+          }
+        }
+        if (found) 
+        {
+          if (!md->isLinkable()) 
+          {
+			//printf("getDefs(): >Success, but isLinkable=%d\n", md->isLinkable());
+            md=0; // avoid returning things we cannot link to
+            nd=0;
+            return FALSE; // match found but not linkable
+          }
+          else
+          {
+            gd=md->getGroupDef();
+            if (gd && gd->isLinkable()) nd=0; else gd=0;
+			//printf("getDefs(): >Success, isLinkable=%d\n", md->isLinkable());
+            return TRUE;
+          }
+        }
+      }
+      if (scopeOffset==0)
+      {
+        scopeOffset=-1;
+      }
+      else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
+      {
+        scopeOffset=0;
+      }
+    } while (scopeOffset>=0);
+
+    //else // no scope => global function
+    {
+      QList<MemberDef> members;
+      // search for matches with strict static checking
+      findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,members);
+      if (members.count()==0) // nothing found
+      {
+        // search again without strict static checking
+        findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,members);
+      }
+
+#if 0
+      //printf("  Function with global scope name `%s' args=`%s'\n",memberName.data(),args);
+      MemberListIterator mli(*mn);
+      for (mli.toFirst();(md=mli.current());++mli)
+      {
+        fd=md->getFileDef();
+        gd=md->getGroupDef();
+        //printf("  md->name()=`%s' md->args=`%s' fd=%p gd=%p\n",
+        //    md->name().data(),args,fd,gd);
+        if (
+            ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) && 
+            md->getNamespaceDef()==0 && md->isLinkable() &&
+            (!md->isStatic() || fd==currentFile) // statics must appear in the same file
+           )
+        {
+          //printf("    fd=%p gd=%p args=`%s'\n",fd,gd,args);
+          bool match=TRUE;
+          ArgumentList *argList=0;
+          if (args && !md->isDefine() && strcmp(args,"()")!=0)
+          {
+            argList=new ArgumentList;
+            LockingPtr<ArgumentList> mdAl = md->argumentList();
+            stringToArgumentList(args,argList);
+            match=matchArguments2(
+                md->getOuterScope(),fd,mdAl.pointer(),
+                Doxygen::globalScope,fd,argList,
+                checkCV); 
+            delete argList; argList=0;
+          }
+          if (match) 
+          {
+            //printf("Found match!\n");
+            members.append(md);
+          }
+        }
+      }
+#endif
+      if (members.count()!=1 && args && !strcmp(args,"()"))
+      {
+        // no exact match found, but if args="()" an arbitrary 
+        // member will do
+        md=mn->last();
+        while (md /* && md->isLinkable()*/)
+        {
+          //printf("getDefs(): Found member `%s'\n",md->name().data());
+          //printf("getDefs(): member is linkable md->name()=`%s'\n",md->name().data());
+          fd=md->getFileDef();
+          gd=md->getGroupDef();
+          if (
+              (gd && gd->isLinkable()) || (fd && fd->isLinkable()) 
+             )
+          {
+            members.append(md);
+          }
+          md=mn->prev();
+        }
+      }
+      //printf("getDefs(): found %d candidate members\n",members.count());
+      if (members.count()>0) // at least one match
+      {
+        md=members.last();
+      }
+      if (md) // found a matching global member
+      {
+        fd=md->getFileDef();
+        gd=md->getGroupDef();
+		//printf("getDefs(): fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd?gd->isLinkable():-1);
+        if (gd && gd->isLinkable()) fd=0; else gd=0;
+        return TRUE;
+      }
+    }
+  }
+
+  // no nothing found
+  //printf("getDefs(): End of routine\n");
+  return FALSE;
+}
+
+/*!
+ * Searches for a scope definition given its name as a string via parameter
+ * `scope'. 
+ *
+ * The parameter `docScope' is a string representing the name of the scope in 
+ * which the `scope' string was found.
+ *
+ * The function returns TRUE if the scope is known and documented or
+ * FALSE if it is not.
+ * If TRUE is returned exactly one of the parameter `cd', `nd' 
+ * will be non-zero:
+ *   - if `cd' is non zero, the scope was a class pointed to by cd.
+ *   - if `nd' is non zero, the scope was a namespace pointed to by nd.
+ */
+static bool getScopeDefs(const char *docScope,const char *scope,
+    ClassDef *&cd, NamespaceDef *&nd)
+{
+  cd=0;nd=0;
+
+  QCString scopeName=scope;
+  //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
+  if (scopeName.isEmpty()) return FALSE;
+
+  bool explicitGlobalScope=FALSE;
+  if (scopeName.at(0)==':' && scopeName.at(1)==':')
+  {
+    scopeName=scopeName.right(scopeName.length()-2);  
+    explicitGlobalScope=TRUE;
+  }
+
+  QCString docScopeName=docScope;
+  int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
+
+  do // for each possible docScope (from largest to and including empty)
+  {
+    QCString fullName=scopeName.copy();
+    if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
+
+    if ((cd=getClass(fullName)) && cd->isLinkable())
+    {
+      return TRUE; // class link written => quit 
+    }
+    else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
+    {
+      return TRUE; // namespace link written => quit 
+    }
+    if (scopeOffset==0)
+    {
+      scopeOffset=-1;
+    }
+    else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
+    {
+      scopeOffset=0;
+    }
+  } while (scopeOffset>=0);
+
+  return FALSE;
+}
+
+static bool isLowerCase(QCString &s)
+{
+  char *p=s.data();
+  if (p==0) return TRUE;
+  int c;
+  while ((c=*p++)) if (!islower(c)) return FALSE;
+  return TRUE; 
+}
+
+/*! Returns an object to reference to given its name and context 
+ *  @post return value TRUE implies *resContext!=0 or *resMember!=0
+ */
+bool resolveRef(/* in */  const char *scName,
+    /* in */  const char *name,
+    /* in */  bool inSeeBlock,
+    /* out */ Definition **resContext,
+    /* out */ MemberDef  **resMember,
+    bool lookForSpecialization,
+    FileDef *currentFile
+    )
+{
+  QCString tsName = name;
+  bool memberScopeFirst = tsName.find('#')!=-1;
+  QCString fullName = substitute(tsName,"#","::");
+  fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
+
+  int bracePos=fullName.findRev('('); // reverse is needed for operator()(...)
+  int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
+  int scopePos=fullName.findRev("::",endNamePos);
+
+  // default result values
+  *resContext=0;
+  *resMember=0;
+
+  if (bracePos==-1) // simple name
+  {
+    ClassDef *cd=0;
+    NamespaceDef *nd=0;
+
+    // the following if() was commented out for releases in the range 
+    // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
+    if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
+    { // link to lower case only name => do not try to autolink 
+      return FALSE;
+    }
+
+    //printf("scName=%s fullName=%s\n",scName,fullName.data());
+
+    // check if this is a class or namespace reference
+    if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
+    {
+      if (cd) // scope matches that of a class
+      {
+        *resContext = cd;
+      }
+      else // scope matches that of a namespace
+      {
+        ASSERT(nd!=0);
+        *resContext = nd;
+      }
+      return TRUE;
+    }
+    else if (scName==fullName || (!inSeeBlock && scopePos==-1)) 
+      // nothing to link => output plain text
+    {
+      //printf("found scName=%s fullName=%s scName==fullName=%d "
+      //    "inSeeBlock=%d scopePos=%d!\n",
+      //    scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
+      return FALSE;
+    }
+    // continue search...
+  }
+
+  // extract userscope+name
+  QCString nameStr=fullName.left(endNamePos);
+
+  // extract arguments
+  QCString argsStr;
+  if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
+
+  // strip template specifier
+  // TODO: match against the correct partial template instantiation 
+  int templPos=nameStr.find('<');
+  bool tryUnspecializedVersion = FALSE;
+  if (templPos!=-1 && nameStr.find("operator")==-1)
+  {
+    int endTemplPos=nameStr.findRev('>');
+    if (endTemplPos!=-1)
+    {
+      if (!lookForSpecialization)
+      {
+        nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
+      }
+      else
+      {
+        tryUnspecializedVersion = TRUE;
+      }
+    }
+  }
+
+  QCString scopeStr=scName;
+
+  MemberDef    *md = 0;
+  ClassDef     *cd = 0;
+  FileDef      *fd = 0;
+  NamespaceDef *nd = 0;
+  GroupDef     *gd = 0;
+
+  // check if nameStr is a member or global.
+  //printf("getDefs(scope=%s,name=%s,args=%s)\n",scopeStr.data(),nameStr.data(),argsStr.data());
+  if (getDefs(scopeStr,nameStr,argsStr,
+        md,cd,fd,nd,gd,
+        scopePos==0 && !memberScopeFirst,
+        currentFile,
+        TRUE
+        )
+     )
+  {
+    //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
+    if      (md) { *resMember=md; *resContext=md; }
+    else if (cd) *resContext=cd;
+    else if (nd) *resContext=nd;
+    else if (fd) *resContext=fd;
+    else if (gd) *resContext=gd;
+    else         { *resContext=0; *resMember=0; return FALSE; }
+    //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
+    //    md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
+    return TRUE;
+  }
+  else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
+  { // group link
+    *resContext=gd;
+    return TRUE;
+  }
+  else if (tsName.find('.')!=-1) // maybe a link to a file
+  {
+    bool ambig;
+    fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
+    if (fd && !ambig)
+    {
+      *resContext=fd;
+      return TRUE;
+    }
+  }
+
+  if (tryUnspecializedVersion)
+  {
+    return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE);
+  }
+
+  return FALSE;
+}
+
+QCString linkToText(const char *link,bool isFileName)
+{
+  static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
+  QCString result=link;
+  if (!result.isEmpty())
+  {
+    // replace # by ::
+    result=substitute(result,"#","::");
+    // replace . by ::
+    if (!isFileName) result=substitute(result,".","::");
+    // strip leading :: prefix if present
+    if (result.at(0)==':' && result.at(1)==':')
+    {
+      result=result.right(result.length()-2);
+    }
+    if (optimizeOutputJava)
+    {
+      result=substitute(result,"::",".");
+    }
+  }
+  return result;
+}
+
+/*
+ * generate a reference to a class, namespace or member.
+ * `scName' is the name of the scope that contains the documentation 
+ * string that is returned.
+ * `name' is the name that we want to link to.
+ * `name' may have five formats:
+ *    1) "ScopeName"
+ *    2) "memberName()"    one of the (overloaded) function or define 
+ *                         with name memberName.
+ *    3) "memberName(...)" a specific (overloaded) function or define 
+ *                         with name memberName
+ *    4) "::name           a global variable or define
+ *    4) "\#memberName     member variable, global variable or define
+ *    5) ("ScopeName::")+"memberName()" 
+ *    6) ("ScopeName::")+"memberName(...)" 
+ *    7) ("ScopeName::")+"memberName" 
+ * instead of :: the \# symbol may also be used.
+ */
+
+bool generateRef(OutputDocInterface &od,const char *scName,
+    const char *name,bool inSeeBlock,const char *rt)
+{
+  //printf("generateRef(scName=%s,name=%s,rt=%s)\n",scName,name,rt);
+
+  Definition *compound;
+  MemberDef *md;
+
+  // create default link text
+  QCString linkText = linkToText(rt,FALSE);
+
+  if (resolveRef(scName,name,inSeeBlock,&compound,&md))
+  {
+    if (md && md->isLinkable()) // link to member
+    {
+      od.writeObjectLink(md->getReference(),
+          md->getOutputFileBase(),
+          md->anchor(),linkText);
+      // generate the page reference (for LaTeX)
+      if (!md->isReference())
+      {
+        writePageRef(od,md->getOutputFileBase(),md->anchor());
+      }
+      return TRUE;
+    }
+    else if (compound && compound->isLinkable()) // link to compound
+    {
+      if (rt==0 && compound->definitionType()==Definition::TypeGroup)
+      {
+        linkText=((GroupDef *)compound)->groupTitle();
+      }
+      if (compound && compound->definitionType()==Definition::TypeFile)
+      {
+        linkText=linkToText(rt,TRUE);
+      }
+      od.writeObjectLink(compound->getReference(),
+          compound->getOutputFileBase(),
+          0,linkText);
+      if (!compound->isReference())
+      {
+        writePageRef(od,compound->getOutputFileBase(),0);
+      }
+      return TRUE;
+    }
+  }
+  od.docify(linkText);
+  return FALSE;
+}
+
+bool resolveLink(/* in */ const char *scName,
+    /* in */ const char *lr,
+    /* in */ bool inSeeBlock,
+    /* out */ Definition **resContext,
+    /* out */ QCString &resAnchor
+    )
+{
+  *resContext=0;
+
+  QCString linkRef=lr;
+  //printf("ResolveLink linkRef=%s\n",lr);
+  FileDef  *fd;
+  GroupDef *gd;
+  PageDef  *pd;
+  ClassDef *cd;
+  DirDef   *dir;
+  NamespaceDef *nd;
+  bool ambig;
+  if (linkRef.isEmpty()) // no reference name!
+  {
+    return FALSE;
+  }
+  else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
+  {
+    GroupDef *gd = pd->getGroupDef();
+    if (gd)
+    {
+      SectionInfo *si=0;
+      if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()];
+      *resContext=gd;
+      if (si) resAnchor = si->label;
+    }
+    else
+    {
+      *resContext=pd;
+    }
+    return TRUE;
+  }
+  else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
+  {
+    *resContext=pd;
+    return TRUE;
+  }
+  else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
+  {
+    *resContext=gd;
+    return TRUE;
+  }
+  else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
+      && fd->isLinkable())
+  {
+    *resContext=fd;
+    return TRUE;
+  }
+  else if ((cd=getClass(linkRef))) // class link
+  {
+    *resContext=cd;
+    return TRUE;
+  }
+  else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
+  {
+    *resContext=cd;
+    return TRUE;
+  }
+  else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
+  {
+    *resContext=nd;
+    return TRUE;
+  }
+  else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath()+"/"))
+      && dir->isLinkable()) // TODO: make this location independent like filedefs
+  {
+    *resContext=dir;
+    return TRUE;
+  }
+  else // probably a member reference
+  {
+    MemberDef *md;
+    bool res = resolveRef(scName,lr,inSeeBlock,resContext,&md);
+    if (md) resAnchor=md->anchor();
+    return res;
+  }
+}
+
+
+//----------------------------------------------------------------------
+// General function that generates the HTML code for a reference to some
+// file, class or member from text `lr' within the context of class `clName'. 
+// This link has the text 'lt' (if not 0), otherwise `lr' is used as a
+// basis for the link's text.
+// returns TRUE if a link could be generated.
+
+bool generateLink(OutputDocInterface &od,const char *clName,
+    const char *lr,bool inSeeBlock,const char *lt)
+{
+  //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
+  Definition *compound;
+  //PageDef *pageDef=0;
+  QCString anchor,linkText=linkToText(lt,FALSE);
+  //printf("generateLink linkText=%s\n",linkText.data());
+  if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
+  {
+    if (compound) // link to compound
+    {
+      if (lt==0 && anchor.isEmpty() &&                      /* compound link */
+          compound->definitionType()==Definition::TypeGroup /* is group */ 
+         )
+      {
+        linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
+      }
+      else if (compound->definitionType()==Definition::TypeFile)
+      {
+        linkText=linkToText(lt,TRUE); 
+      }
+      od.writeObjectLink(compound->getReference(),
+          compound->getOutputFileBase(),anchor,linkText);
+      if (!compound->isReference())
+      {
+        writePageRef(od,compound->getOutputFileBase(),anchor);
+      }
+    }
+    else
+    {
+      err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
+    }
+    return TRUE;
+  }
+  else // link could not be found
+  {
+    od.docify(linkText);
+    return FALSE;
+  }
+}
+
+void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
+{
+  //printf("generateFileRef(%s,%s)\n",name,text);
+  QCString linkText = text ? text : name;
+  //FileInfo *fi;
+  FileDef *fd;
+  bool ambig;
+  if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) && 
+      fd->isLinkable()) 
+    // link to documented input file
+    od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
+  else
+    od.docify(linkText); 
+}
+
+//----------------------------------------------------------------------
+
+#if 0
+QCString substituteClassNames(const QCString &s)
+{
+  int i=0,l,p;
+  QCString result;
+  if (s.isEmpty()) return result;
+  QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
+  while ((p=r.match(s,i,&l))!=-1)
+  {
+    QCString *subst;
+    if (p>i) result+=s.mid(i,p-i);
+    if ((subst=substituteDict[s.mid(p,l)]))
+    {
+      result+=*subst;
+    }
+    else
+    {
+      result+=s.mid(p,l);
+    }
+    i=p+l;
+  }
+  result+=s.mid(i,s.length()-i);
+  return result;
+}
+#endif
+
+//----------------------------------------------------------------------
+// substitute all occurences of `src' in `s' by `dst'
+
+QCString substitute(const char *s,const char *src,const char *dst)
+{
+  if (s==0 || src==0) return s;
+  const char *p, *q;
+  int srcLen = strlen(src);
+  int dstLen = dst ? strlen(dst) : 0;
+  int resLen;
+  if (srcLen!=dstLen)
+  {
+    int count;
+    for (count=0, p=s; (q=strstr(p,src))!=0; p=q+srcLen) count++;
+    resLen = p-s+strlen(p)+count*(dstLen-srcLen);
+  }
+  else // result has same size as s
+  {
+    resLen = strlen(s);
+  }
+  QCString result(resLen+1);
+  char *r;
+  for (r=result.data(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
+  {
+    int l = (int)(q-p);
+    memcpy(r,p,l);
+    r+=l;
+    if (dst) memcpy(r,dst,dstLen);
+    r+=dstLen;
+  }
+  strcpy(r,p);
+  //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
+  return result;
+}
+
+//----------------------------------------------------------------------
+
+struct FindFileCacheElem
+{
+  FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
+  FileDef *fileDef;
+  bool isAmbig;
+};
+
+//static QCache<FindFileCacheElem> g_findFileDefCache(5000);
+
+FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
+{
+  //Debug::print(Debug::IncludeGraph, 0, "findFileDef() Dict=%p Looking for: %s\n", fnDict, n);
+  ambig=FALSE;
+  if (n==0) return 0;
+
+  /*
+  QCString key;
+  key.sprintf("%p:",fnDict);
+  key+=n;
+
+  g_findFileDefCache.setAutoDelete(TRUE);
+  FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
+  if (cachedResult)
+  {
+    ambig = cachedResult->isAmbig;
+	Debug::print(Debug::IncludeGraph, 0, "findFileDef() In cache: %p\n", cachedResult->fileDef);
+    return cachedResult->fileDef;
+  }
+  else
+  {
+    cachedResult = new FindFileCacheElem(0,FALSE);
+  }
+  */
+  QCString name=convertToQCString(QDir::cleanDirPath(n));
+  QCString path;
+  int slashPos;
+  FileName *fn;
+  if (name.isEmpty()) goto exit;
+  slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
+  if (slashPos!=-1)
+  {
+    path=name.left(slashPos+1);
+    name=name.right(name.length()-slashPos-1); 
+  }
+  //Debug::print(Debug::IncludeGraph, 0, "findFileDef() path=`%s' name=`%s'\n",path.data(),name.data());
+  //printf("findFileDef path=`%s' name=`%s'\n",path.data(),name.data());
+  if (name.isEmpty()) goto exit;
+  if ((fn=(*fnDict)[name]))
+  {
+    if (fn->count()==1)
+    {
+      FileDef *fd = fn->getFirst();
+      if (path.isEmpty() || fd->getPath().right(path.length())==path)
+      {
+        /*
+		cachedResult->fileDef = fd;
+        g_findFileDefCache.insert(key,cachedResult);
+		*/
+		//Debug::print(Debug::IncludeGraph, 0, "findFileDef() In dictionary: %p\n", fd);
+        return fd;
+      }
+    }
+    else // file name alone is ambigious
+    {
+      int count=0;
+      FileNameIterator fni(*fn);
+      FileDef *fd;
+      FileDef *lastMatch=0;
+      QCString pathStripped = stripFromIncludePath(path);
+      for (fni.toFirst();(fd=fni.current());++fni)
+      {
+        QCString fdStripPath = stripFromIncludePath(fd->getPath());
+        if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped) 
+        { 
+          count++; 
+          lastMatch=fd; 
+        }
+      }
+
+      ambig=(count>1);
+      /*
+	  cachedResult->isAmbig = ambig;
+      cachedResult->fileDef = lastMatch;
+      g_findFileDefCache.insert(key,cachedResult);
+	  */
+	  //Debug::print(Debug::IncludeGraph, 0, "findFileDef() ambiguos: %p\n", lastMatch);
+      return lastMatch;
+    }
+  }
+exit:
+  /*
+  g_findFileDefCache.insert(key,cachedResult);
+  */
+  //Debug::print(Debug::IncludeGraph, 0, "findFileDef() failed\n");
+  return 0;
+}
+
+//----------------------------------------------------------------------
+
+QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
+{
+  QCString result;
+  QCString name=n;
+  QCString path;
+  int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
+  if (slashPos!=-1)
+  {
+    path=name.left(slashPos+1);
+    name=name.right(name.length()-slashPos-1); 
+  }
+  FileName *fn;
+  if ((fn=(*fnDict)[name]))
+  {
+    FileNameIterator fni(*fn);
+    FileDef *fd;
+    for (fni.toFirst();(fd=fni.current());++fni)
+    {
+      if (path.isEmpty() || fd->getPath().right(path.length())==path)
+      {
+        result+="   "+fd->absFilePath()+"\n";
+      }
+    }
+  }
+  return result;
+}
+
+//----------------------------------------------------------------------
+
+QCString substituteKeywords(const QCString &s,const char *title,const QCString &relPath)
+{
+  QCString result = s.copy();
+  if (title) result = substitute(result,"$title",title);
+  result = substitute(result,"$datetime",dateToString(TRUE));
+  result = substitute(result,"$date",dateToString(FALSE));
+  result = substitute(result,"$year",yearToString());
+  result = substitute(result,"$doxygenversion",versionString);
+  result = substitute(result,"$projectname",Config_getString("PROJECT_NAME"));
+  result = substitute(result,"$projectnumber",Config_getString("PROJECT_NUMBER"));
+  result = substitute(result,"$relpath$",relPath);
+  return result;
+}
+
+//----------------------------------------------------------------------
+
+/*! Returns the character index within \a name of the first prefix
+ *  in Config_getList("IGNORE_PREFIX") that matches \a name at the left hand side,
+ *  or zero if no match was found
+ */ 
+int getPrefixIndex(const QCString &name)
+{
+  if (name.isEmpty()) return 0;
+  static QStrList &sl = Config_getList("IGNORE_PREFIX");
+  char *s = sl.first();
+  while (s)
+  {
+    const char *ps=s;
+    const char *pd=name.data();
+    int i=0;
+    while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
+    if (*ps==0 && *pd!=0)
+    {
+      return i;
+    }
+    s = sl.next();
+  }
+  return 0;
+}
+
+//----------------------------------------------------------------------------
+
+static void initBaseClassHierarchy(BaseClassList *bcl)
+{
+  if (bcl==0) return;
+  BaseClassListIterator bcli(*bcl);
+  for ( ; bcli.current(); ++bcli)
+  {
+    ClassDef *cd=bcli.current()->classDef;
+    if (cd->baseClasses()==0) // no base classes => new root
+    {
+      initBaseClassHierarchy(cd->baseClasses());
+    }
+    cd->visited=FALSE;
+  }
+}
+
+//----------------------------------------------------------------------------
+
+void initClassHierarchy(ClassSDict *cl)
+{
+  ClassSDict::Iterator cli(*cl);
+  ClassDef *cd;
+  for ( ; (cd=cli.current()); ++cli)
+  {
+    cd->visited=FALSE;
+    initBaseClassHierarchy(cd->baseClasses());
+  }
+}
+
+//----------------------------------------------------------------------------
+
+bool hasVisibleRoot(BaseClassList *bcl)
+{
+  if (bcl)
+  {
+    BaseClassListIterator bcli(*bcl);
+    for ( ; bcli.current(); ++bcli)
+    {
+      ClassDef *cd=bcli.current()->classDef;
+      if (cd->isVisibleInHierarchy()) return TRUE;
+      hasVisibleRoot(cd->baseClasses());
+    }
+  }
+  return FALSE;
+}
+
+//----------------------------------------------------------------------
+
+QCString escapeCharsInString(const char *name,bool allowDots)
+{
+  static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES");
+  QCString result;
+  char c;
+  const char *p=name;
+  while ((c=*p++)!=0)
+  {
+    switch(c)
+    {
+      case '_': result+="__"; break;
+      case '-': result+="-";  break;
+      case ':': result+="_1"; break;
+      case '/': result+="_2"; break;
+      case '<': result+="_3"; break;
+      case '>': result+="_4"; break;
+      case '*': result+="_5"; break;
+      case '&': result+="_6"; break;
+      case '|': result+="_7"; break;
+      case '.': if (allowDots) result+="."; else result+="_8"; break;
+      case '!': result+="_9"; break;
+      case ',': result+="_00"; break;
+      case ' ': result+="_01"; break;
+      case '{': result+="_02"; break;
+      case '}': result+="_03"; break;
+      case '?': result+="_04"; break;
+      case '^': result+="_05"; break;
+      case '%': result+="_06"; break;
+      case '(': result+="_07"; break;
+      case ')': result+="_08"; break;
+      case '+': result+="_09"; break;
+      case '=': result+="_0A"; break;
+      default: 
+                if (c<0)
+                {
+                  static char map[] = "0123456789ABCDEF";
+                  char ids[5];
+                  unsigned char id = (unsigned char)c;
+                  ids[0]='_';
+                  ids[1]='x';
+                  ids[2]=map[id>>4];
+                  ids[3]=map[id&0xF];
+                  ids[4]=0;
+                  result+=ids;
+                }
+                else if (caseSenseNames || !isupper(c))
+                {
+                  result+=c;
+                }
+                else
+                {
+                  result+="_";
+                  result+=tolower(c); 
+                }
+                break;
+    }
+  }
+  return result;
+}
+
+/*! This function determines the file name on disk of an item
+ *  given its name, which could be a class name with template 
+ *  arguments, so special characters need to be escaped.
+ */
+QCString convertNameToFile(const char *name,bool allowDots)
+{
+  static bool shortNames = Config_getBool("SHORT_NAMES");
+  static bool createSubdirs = Config_getBool("CREATE_SUBDIRS");
+  QCString result;
+  if (shortNames) // use short names only
+  {
+    static QDict<int> usedNames(10007);
+    usedNames.setAutoDelete(TRUE);
+    static int count=1;
+
+    int *value=usedNames.find(name);
+    int num;
+    if (value==0)
+    {
+      usedNames.insert(name,new int(count));
+      num = count++;
+    }
+    else
+    {
+      num = *value;
+    }
+    result.sprintf("a%05d",num); 
+  }
+  else // long names
+  {
+    result=escapeCharsInString(name,allowDots);
+    int resultLen = result.length();
+    if (resultLen>=128) // prevent names that cannot be created!
+    {
+      // third algorithm based on MD5 hash
+      uchar md5_sig[16];
+      QCString sigStr(33);
+      MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
+      MD5SigToString(md5_sig,sigStr.data(),33);
+      result=result.left(128-32)+sigStr; 
+    }
+  }
+  if (createSubdirs)
+  {
+    int l1Dir=0,l2Dir=0;
+
+#if MAP_ALGO==ALGO_COUNT 
+    // old algorithm, has the problem that after regeneration the
+    // output can be located in a different dir.
+    if (Doxygen::htmlDirMap==0) 
+    {
+      Doxygen::htmlDirMap=new QDict<int>(100003);
+      Doxygen::htmlDirMap->setAutoDelete(TRUE);
+    }
+    static int curDirNum=0;
+    int *dirNum = Doxygen::htmlDirMap->find(result);
+    if (dirNum==0) // new name
+    {
+      Doxygen::htmlDirMap->insert(result,new int(curDirNum)); 
+      l1Dir = (curDirNum)&0xf;    // bits 0-3
+      l2Dir = (curDirNum>>4)&0xff; // bits 4-11
+      curDirNum++;
+    }
+    else // existing name
+    {
+      l1Dir = (*dirNum)&0xf;       // bits 0-3
+      l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
+    }
+#elif MAP_ALGO==ALGO_CRC16
+    // second algorithm based on CRC-16 checksum
+    int dirNum = qChecksum(result,result.length());
+    l1Dir = dirNum&0xf;
+    l2Dir = (dirNum>>4)&0xff;
+#elif MAP_ALGO==ALGO_MD5
+    // third algorithm based on MD5 hash
+    uchar md5_sig[16];
+    MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
+    l1Dir = md5_sig[14]&0xf;
+    l2Dir = md5_sig[15];
+#endif
+    result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
+  }
+  //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
+  return result;
+}
+
+QCString relativePathToRoot(const char *name)
+{
+  QCString result;
+  if (Config_getBool("CREATE_SUBDIRS"))
+  {
+    if (name==0)
+    {
+      return REL_PATH_TO_ROOT;
+    }
+    else
+    {
+      QCString n = name;
+      int i = n.findRev('/');
+      if (i!=-1)
+      {
+        result=REL_PATH_TO_ROOT;
+      }
+    }
+  }
+  return result;
+}
+
+void createSubDirs(QDir &d)
+{
+  if (Config_getBool("CREATE_SUBDIRS"))
+  {
+    // create 4096 subdirectories
+    int l1,l2;
+    for (l1=0;l1<16;l1++)
+    {
+      d.mkdir(QString().sprintf("d%x",l1));
+      for (l2=0;l2<256;l2++)
+      {
+        d.mkdir(QString().sprintf("d%x/d%02x",l1,l2));
+      }
+    }
+  }
+}
+
+/*! Input is a scopeName, output is the scopename split into a
+ *  namespace part (as large as possible) and a classname part.
+ */
+void extractNamespaceName(const QCString &scopeName,
+    QCString &className,QCString &namespaceName,
+    bool allowEmptyClass)
+{
+  int i,p;
+  QCString clName=scopeName;
+  NamespaceDef *nd = 0;
+  if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
+  { // the whole name is a namespace (and not a class)
+    namespaceName=nd->name().copy();
+    className.resize(0);
+    goto done;
+  }
+  p=clName.length()-2;
+  while (p>=0 && (i=clName.findRev("::",p))!=-1) 
+    // see if the first part is a namespace (and not a class)
+  {
+    //printf("Trying %s\n",clName.left(i).data());
+    if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
+    {
+      //printf("found!\n");
+      namespaceName=nd->name().copy();
+      className=clName.right(clName.length()-i-2);
+      goto done;
+    } 
+    p=i-2; // try a smaller piece of the scope
+  }
+  //printf("not found!\n");
+
+  // not found, so we just have to guess.
+  className=scopeName.copy();
+  namespaceName.resize(0);
+
+done:
+  if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
+  {
+    // class and namespace with the same name, correct to return the class.
+    className=namespaceName.copy();
+    namespaceName.resize(0);
+  }
+  //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
+  //       className.data(),namespaceName.data());
+  return;
+}
+
+QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
+{
+  QCString result=scope.copy();
+  if (!templ.isEmpty() && scope.find('<')==-1)
+  {
+    int si,pi=0;
+    ClassDef *cd=0;
+    while (
+        (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) && 
+        ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0) 
+        ) 
+    { 
+      //printf("Tried `%s'\n",(scope.left(si)+templ).data()); 
+      pi=si+2; 
+    }
+    if (si==-1) // not nested => append template specifier
+    {
+      result+=templ; 
+    }
+    else // nested => insert template specifier before after first class name
+    {
+      result=scope.left(si) + templ + scope.right(scope.length()-si);
+    }
+  }
+  //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
+  //    scope.data(),templ.data(),result.data());
+  return result;
+}
+
+#if 0 // original version
+/*! Strips the scope from a name. Examples: A::B will return A
+ *  and A<T>::B<N::C<D> > will return A<T>.
+ */
+QCString stripScope(const char *name)
+{
+  QCString result = name;
+  int l=result.length();
+  int p=l-1;
+  bool done;
+  int count;
+
+  while (p>=0)
+  {
+    char c=result.at(p);
+    switch (c)
+    {
+      case ':': 
+        //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
+        return result.right(l-p-1);
+      case '>':
+        count=1;
+        done=FALSE;
+        //printf("pos < = %d\n",p);
+        p--;
+        while (p>=0 && !done)
+        {
+          c=result.at(p--);
+          switch (c)
+          {
+            case '>': count++; break;
+            case '<': count--; if (count<=0) done=TRUE; break;
+            default: 
+                      //printf("c=%c count=%d\n",c,count);
+                      break;
+          }
+        }
+        //printf("pos > = %d\n",p+1);
+        break;
+      default:
+        p--;
+    }
+  }
+  //printf("stripScope(%s)=%s\n",name,name);
+  return name;
+}
+#endif
+
+// new version by Davide Cesari which also works for Fortran
+QCString stripScope(const char *name)
+{
+  QCString result = name;
+  int l=result.length();
+  int p;
+  bool done = FALSE;
+  bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
+  int count=0;
+
+  do
+  {
+    p=l-1; // start at the end of the string
+    while (p>=0 && count>=0)
+    {
+      char c=result.at(p);
+      switch (c)
+      {
+        case ':': 
+          //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
+          return result.right(l-p-1);
+        case '>':
+          if (skipBracket) // we don't care about brackets
+          {
+            p--;
+          }
+          else // count open/close brackets
+          {
+            if (p>0 && result.at(p-1)=='>') // skip >> operator
+            {
+              p-=2;
+              break;
+            }
+            count=1;
+            //printf("pos < = %d\n",p);
+            p--;
+            bool foundMatch=false;
+            while (p>=0 && !foundMatch)
+            {
+              c=result.at(p--);
+              switch (c)
+              {
+                case '>': 
+                  count++; 
+                  break;
+                case '<': 
+                  if (p>0)
+                  {
+                    if (result.at(p-1) == '<') // skip << operator
+                    {
+                      p--;
+                      break;
+                    }
+                  }
+                  count--; 
+                  foundMatch = count==0;
+                  break;
+                default: 
+                  //printf("c=%c count=%d\n",c,count);
+                  break;
+              }
+            }
+          }
+          //printf("pos > = %d\n",p+1);
+          break;
+        default:
+          p--;
+      }
+    }
+    done = count==0 || skipBracket; // reparse if brackets do not match
+    skipBracket=TRUE;
+  }
+  while (!done); // if < > unbalanced repeat ignoring them
+  //printf("stripScope(%s)=%s\n",name,name);
+  return name;
+}
+
+
+/*! Converts a string to an XML-encoded string */
+QCString convertToXML(const char *s)
+{
+  QCString result;
+  if (s==0) return result;
+  const char *p=s;
+  char c;
+  while ((c=*p++))
+  {
+    switch (c)
+    {
+      case '<':  result+="&lt;";   break;
+      case '>':  result+="&gt;";   break;
+      case '&':  result+="&amp;";  break;
+      case '\'': result+="&apos;"; break; 
+      case '"':  result+="&quot;"; break;
+      default:   result+=c;        break;
+    }
+  }
+  return result;
+}
+
+/*! Converts a string to a HTML-encoded string */
+QCString convertToHtml(const char *s,bool keepEntities)
+{
+  QCString result;
+  if (s==0) return result;
+  const char *p=s;
+  char c;
+  while ((c=*p++))
+  {
+    switch (c)
+    {
+      case '<':  result+="&lt;";   break;
+      case '>':  result+="&gt;";   break;
+      case '&':  if (keepEntities)
+                 {
+                   const char *e=p;
+                   char ce;
+                   while ((ce=*e++))
+                   {
+                     if (ce==';' || (!(isId(ce) || ce=='#'))) break;
+                   }
+                   if (ce==';') // found end of an entity
+                   {
+                     // copy entry verbatim
+                     result+=c;
+                     while (p<e) result+=*p++;
+                   }
+                   else
+                   {
+                     result+="&amp;";
+                   }
+                 }
+                 else
+                 {
+                   result+="&amp;";  
+                 }
+                 break;
+      case '\'': result+="&#39;"; break; 
+      case '"':  result+="&quot;"; break;
+      default:   result+=c;        break;
+    }
+  }
+  return result;
+}
+
+QCString convertCharEntitiesToUTF8(const QCString &s)
+{
+  static QDict<char> entityMap(67);
+  static bool init=TRUE;
+  QCString result;
+  static QRegExp entityPat("&[a-zA-Z]+;");
+
+  if (init)
+  {
+    entityMap.insert("copy",  "\xC2\xA9");
+    entityMap.insert("tm",    "\xE2\x84\xA2");
+    entityMap.insert("trade", "\xE2\x84\xA2");
+    entityMap.insert("reg",   "\xC2\xAE");
+    entityMap.insert("lsquo", "\xE2\x80\x98");
+    entityMap.insert("rsquo", "\xE2\x80\x99");
+    entityMap.insert("ldquo", "\xE2\x80\x9C");
+    entityMap.insert("rdquo", "\xE2\x80\x9D");
+    entityMap.insert("ndash", "\xE2\x80\x93");
+    entityMap.insert("mdash", "\xE2\x80\x94");
+    entityMap.insert("Auml",  "\xC3\x84");
+    entityMap.insert("Euml",  "\xC3\x8B");
+    entityMap.insert("Iuml",  "\xC3\x8F");
+    entityMap.insert("Ouml",  "\xC3\x96");
+    entityMap.insert("Uuml",  "\xC3\x9C");
+    entityMap.insert("Yuml",  "\xC5\xB8");
+    entityMap.insert("auml",  "\xC3\xA4");
+    entityMap.insert("euml",  "\xC3\xAB");
+    entityMap.insert("iuml",  "\xC3\xAF");
+    entityMap.insert("ouml",  "\xC3\xB6");
+    entityMap.insert("uuml",  "\xC3\xBC");
+    entityMap.insert("yuml",  "\xC3\xBF");
+    entityMap.insert("Aacute","\xC3\x81");
+    entityMap.insert("Eacute","\xC3\x89");
+    entityMap.insert("Iacute","\xC3\x8D");
+    entityMap.insert("Oacute","\xC3\x93");
+    entityMap.insert("Uacute","\xC3\x9A");
+    entityMap.insert("aacute","\xC3\xA1");
+    entityMap.insert("eacute","\xC3\xA9");
+    entityMap.insert("iacute","\xC3\xAD");
+    entityMap.insert("oacute","\xC3\xB3");
+    entityMap.insert("uacute","\xC3\xBA");
+    entityMap.insert("Agrave","\xC3\x80");
+    entityMap.insert("Egrave","\xC3\x88");
+    entityMap.insert("Igrave","\xC3\x8C");
+    entityMap.insert("Ograve","\xC3\x92");
+    entityMap.insert("Ugrave","\xC3\x99");
+    entityMap.insert("agrave","\xC3\xA0");
+    entityMap.insert("egrave","\xC3\xA8");
+    entityMap.insert("igrave","\xC3\xAC");
+    entityMap.insert("ograve","\xC3\xB2");
+    entityMap.insert("ugrave","\xC3\xB9");
+    entityMap.insert("Acirc", "\xC3\x82");
+    entityMap.insert("Ecirc", "\xC3\x8A");
+    entityMap.insert("Icirc", "\xC3\x8E");
+    entityMap.insert("Ocirc", "\xC3\x94");
+    entityMap.insert("Ucirc", "\xC3\x9B");
+    entityMap.insert("acirc", "\xC3\xA2");
+    entityMap.insert("ecirc", "\xC3\xAA");
+    entityMap.insert("icirc", "\xC3\xAE");
+    entityMap.insert("ocirc", "\xC3\xB4");
+    entityMap.insert("ucirc", "\xC3\xBB");
+    entityMap.insert("Atilde","\xC3\x83");
+    entityMap.insert("Ntilde","\xC3\x91");
+    entityMap.insert("Otilde","\xC3\x95");
+    entityMap.insert("atilde","\xC3\xA3");
+    entityMap.insert("ntilde","\xC3\xB1");
+    entityMap.insert("otilde","\xC3\xB5");
+    entityMap.insert("szlig", "\xC3\x9F");
+    entityMap.insert("Ccedil","\xC3\x87");
+    entityMap.insert("ccedil","\xC3\xA7");
+    entityMap.insert("Aring", "\xC3\x85");
+    entityMap.insert("aring", "\xC3\xA5");
+    entityMap.insert("nbsp",  "\xC2\xA0");
+    init=FALSE;
+  }
+
+  if (s==0) return result;
+  int p,i=0,l;
+  while ((p=entityPat.match(s,i,&l))!=-1)
+  {
+    if (p>i) result+=s.mid(i,p-i);
+    QCString entity = s.mid(p+1,l-2);
+    char *code = entityMap.find(entity);
+    if (code)
+    {
+      result+=code;
+    }
+    else
+    {
+      result+=s.mid(p,l);
+    }
+    i=p+l;
+  }
+  result+=s.mid(i,s.length()-i);
+  return result;
+}
+
+/*! Returns the standard string that is generated when the \\overload
+ * command is used.
+ */
+QCString getOverloadDocs()
+{
+  return theTranslator->trOverloadText();
+  //"This is an overloaded member function, "
+  //       "provided for convenience. It differs from the above "
+  //       "function only in what argument(s) it accepts.";
+}
+
+void addMembersToMemberGroup(MemberList *ml,
+    MemberGroupSDict **ppMemberGroupSDict,
+    Definition *context)
+{
+  ASSERT(context!=0);
+  //printf("addMemberToMemberGroup()\n");
+  if (ml==0) return;
+  MemberListIterator mli(*ml);
+  MemberDef *md;
+  uint index;
+  for (index=0;(md=mli.current());)
+  {
+    if (md->isEnumerate()) // insert enum value of this enum into groups
+    {
+      LockingPtr<MemberList> fmdl=md->enumFieldList();
+      if (fmdl!=0)
+      {
+        MemberDef *fmd=fmdl->first();
+        while (fmd)
+        {
+          int groupId=fmd->getMemberGroupId();
+          if (groupId!=-1)
+          {
+            MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
+            //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
+            //QCString *pDocs      = Doxygen::memberDocDict[groupId];
+            if (info)
+            {
+              if (*ppMemberGroupSDict==0)
+              {
+                *ppMemberGroupSDict = new MemberGroupSDict;
+                (*ppMemberGroupSDict)->setAutoDelete(TRUE);
+              }
+              MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
+              if (mg==0)
+              {
+                mg = new MemberGroup(
+                    context,
+                    groupId,
+                    info->header,
+                    info->doc,
+                    info->docFile
+                    );
+                (*ppMemberGroupSDict)->append(groupId,mg);
+              }
+              mg->insertMember(fmd); // insert in member group
+              fmd->setMemberGroup(mg);
+            }
+          }
+          fmd=fmdl->next();
+        }
+      }
+    }
+    int groupId=md->getMemberGroupId();
+    if (groupId!=-1)
+    {
+      MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
+      //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
+      //QCString *pDocs      = Doxygen::memberDocDict[groupId];
+      if (info)
+      {
+        if (*ppMemberGroupSDict==0)
+        {
+          *ppMemberGroupSDict = new MemberGroupSDict;
+          (*ppMemberGroupSDict)->setAutoDelete(TRUE);
+        }
+        MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
+        if (mg==0)
+        {
+          mg = new MemberGroup(
+              context,
+              groupId,
+              info->header,
+              info->doc,
+              info->docFile
+              );
+          (*ppMemberGroupSDict)->append(groupId,mg);
+        }
+        md = ml->take(index); // remove from member list
+        mg->insertMember(md); // insert in member group
+        mg->setRefItems(info->m_sli);
+        md->setMemberGroup(mg);
+        continue;
+      }
+    }
+    ++mli;++index;
+  }
+}
+
+/*! Extracts a (sub-)string from \a type starting at \a pos that
+ *  could form a class. The index of the match is returned and the found
+ *  class \a name and a template argument list \a templSpec. If -1 is returned
+ *  there are no more matches.
+ */
+int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec)
+{
+  static const QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
+  name.resize(0);
+  templSpec.resize(0);
+  int i,l;
+  int typeLen=type.length();
+  if (typeLen>0)
+  {
+    if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
+    {
+      int ts=i+l;
+      int te=ts;
+      int tl=0;
+      while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
+      if (type.at(ts)=='<') // assume template instance
+      {
+        // locate end of template
+        te=ts+1;
+        int brCount=1;
+        while (te<typeLen && brCount!=0)
+        {
+          if (type.at(te)=='<') 
+          {
+            if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
+          }
+          if (type.at(te)=='>') 
+          {
+            if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
+          }
+          te++;
+        }
+      }
+      name = type.mid(i,l);
+      if (te>ts) 
+      {
+        templSpec = type.mid(ts,te-ts),tl+=te-ts;
+        pos=i+l+tl;
+      }
+      else // no template part
+      {
+        pos=i+l;
+      }
+      //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
+      //    type.data(),pos,name.data(),templSpec.data());
+      return i;
+    }
+  }
+  pos = typeLen;
+  //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
+  //       type.data(),pos,name.data(),templSpec.data());
+  return -1;
+}
+
+/*! Substitutes any occurrence of a formal argument from argument list
+ *  \a formalArgs in \a name by the corresponding actual argument in
+ *  argument list \a actualArgs. The result after substitution
+ *  is returned as a string. The argument \a name is used to
+ *  prevent recursive substitution.
+ */
+QCString substituteTemplateArgumentsInString(
+    const QCString &name,
+    ArgumentList *formalArgs,
+    ArgumentList *actualArgs)
+{
+  //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
+  //    name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
+  if (formalArgs==0) return name;
+  QCString result;
+  static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+  int p=0,l,i;
+  // for each identifier in the base class name (e.g. B<T> -> B and T)
+  while ((i=re.match(name,p,&l))!=-1)
+  {
+    result += name.mid(p,i-p);
+    QCString n = name.mid(i,l);
+    ArgumentListIterator formAli(*formalArgs);
+    Argument *formArg;
+    Argument *actArg=actualArgs->first();
+
+    // if n is a template argument, then we substitute it
+    // for its template instance argument.
+    bool found=FALSE;
+    for (formAli.toFirst();
+        (formArg=formAli.current()) && !found;
+        ++formAli,actArg=actualArgs->next()
+        )
+    {
+      if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
+      {
+        formArg->name = formArg->type.mid(6);
+        formArg->type = "class";
+      }
+      if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
+      {
+        formArg->name = formArg->type.mid(9);
+        formArg->type = "typename";
+      }
+      if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
+      {
+        //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
+        //  n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
+        //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
+        //    formArg->name.data(),actArg->type.data(),actArg->name.data()
+        //    );
+        if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
+        {
+          // replace formal argument with the actual argument of the instance
+          if (!leftScopeMatch(actArg->type,n)) 
+            // the scope guard is to prevent recursive lockup for 
+            // template<class A> class C : public<A::T>, 
+            // where A::T would become A::T::T here, 
+            // since n==A and actArg->type==A::T
+            // see bug595833 for an example
+          {
+            if (actArg->name.isEmpty())
+            {
+              result += actArg->type+" "; 
+              found=TRUE;
+            }
+            else 
+              // for case where the actual arg is something like "unsigned int"
+              // the "int" part is in actArg->name.
+            {
+              result += actArg->type+" "+actArg->name+" "; 
+              found=TRUE;
+            }
+          }
+        }
+        else if (formArg->name==n && 
+                 actArg==0 && 
+                 !formArg->defval.isEmpty() &&
+                 formArg->defval!=name /* to prevent recursion */
+            )
+        {
+          result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
+          found=TRUE;
+        }
+      }
+      else if (formArg->name==n && 
+               actArg==0 && 
+               !formArg->defval.isEmpty() &&
+               formArg->defval!=name /* to prevent recursion */
+              )
+      {
+        result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
+        found=TRUE;
+      }
+    }
+    if (!found) result += n;
+    p=i+l;
+  }
+  result+=name.right(name.length()-p);
+  //printf("      Inheritance relation %s -> %s\n",
+  //    name.data(),result.data());
+  return result.stripWhiteSpace();
+}
+
+
+/*! Makes a deep copy of argument list \a src. Will allocate memory, that
+ *  is owned by the caller. 
+ */
+ArgumentList *copyArgumentList(const ArgumentList *src)
+{
+  ASSERT(src!=0);
+  ArgumentList *dst = new ArgumentList;
+  dst->setAutoDelete(TRUE);
+  ArgumentListIterator tali(*src);
+  Argument *a;
+  for (;(a=tali.current());++tali)
+  {
+    dst->append(new Argument(*a));
+  }
+  dst->constSpecifier    = src->constSpecifier;
+  dst->volatileSpecifier = src->volatileSpecifier;
+  dst->pureSpecifier     = src->pureSpecifier;
+  return dst;
+}
+
+/*! Makes a deep copy of the list of argument lists \a srcLists. 
+ *  Will allocate memory, that is owned by the caller.
+ */
+QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
+{
+  ASSERT(srcLists!=0);
+  QList<ArgumentList> *dstLists = new QList<ArgumentList>;
+  dstLists->setAutoDelete(TRUE);
+  QListIterator<ArgumentList> sli(*srcLists);
+  ArgumentList *sl;
+  for (;(sl=sli.current());++sli)
+  {
+    dstLists->append(copyArgumentList(sl));
+  }
+  return dstLists;
+}
+
+/*! Strips template specifiers from scope \a fullName, except those 
+ *  that make up specialized classes. The switch \a parentOnly 
+ *  determines whether or not a template "at the end" of a scope 
+ *  should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will 
+ *  try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will 
+ *  strip both unless A<T> or B<S> are specialized template classes. 
+ */
+QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
+    bool parentOnly,
+    QCString *pLastScopeStripped)
+{
+  QCString result;
+  int p=0;
+  int l=fullName.length();
+  int i=fullName.find('<');
+  while (i!=-1)
+  {
+    //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
+    int e=i+1;
+    bool done=FALSE;
+    int count=1;
+    while (e<l && !done)
+    {
+      char c=fullName.at(e++);
+      if (c=='<') 
+      {
+        count++;
+      }
+      else if (c=='>') 
+      {
+        count--;
+        done = count==0;
+      }
+    }
+    int si= fullName.find("::",e);
+
+    if (parentOnly && si==-1) break; 
+    // we only do the parent scope, so we stop here if needed
+
+    result+=fullName.mid(p,i-p);
+    //printf("  trying %s\n",(result+fullName.mid(i,e-i)).data());
+    if (getClass(result+fullName.mid(i,e-i))!=0)
+    {
+      result+=fullName.mid(i,e-i);
+      //printf("  2:result+=%s cd=%s\n",fullName.mid(i,e-i-1).data(),cd->name().data());
+    }
+    else if (pLastScopeStripped)
+    {
+      //printf("  last stripped scope '%s'\n",fullName.mid(i,e-i).data());
+      *pLastScopeStripped=fullName.mid(i,e-i);
+    }
+    p=e;
+    i=fullName.find('<',p);
+  }
+  result+=fullName.right(l-p);
+  //printf("3:result+=%s\n",fullName.right(l-p).data());
+  return result;
+}
+
+/*! Merges two scope parts together. The parts may (partially) overlap.
+ *  Example1: \c A::B and \c B::C will result in \c A::B::C <br>
+ *  Example2: \c A and \c B will be \c A::B <br>
+ *  Example3: \c A::B and B will be \c A::B
+ *  
+ *  @param leftScope the left hand part of the scope.
+ *  @param rightScope the right hand part of the scope.
+ *  @returns the merged scope. 
+ */
+QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
+{
+  // case leftScope=="A" rightScope=="A::B" => result = "A::B"
+  if (leftScopeMatch(rightScope,leftScope)) return rightScope;
+  QCString result;
+  int i=0,p=leftScope.length();
+
+  // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
+  // case leftScope=="A::B" rightScope=="B" => result = "A::B"
+  bool found=FALSE;
+  while ((i=leftScope.findRev("::",p))!=-1)
+  {
+    if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
+    {
+      result = leftScope.left(i+2)+rightScope;
+      found=TRUE;
+    }
+    p=i-1;
+  }
+  if (found) return result;
+
+  // case leftScope=="A" rightScope=="B" => result = "A::B"
+  result=leftScope.copy();
+  if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
+  result+=rightScope;
+  return result;
+}
+
+/*! Returns a fragment from scope \a s, starting at position \a p.
+ *
+ *  @param s the scope name as a string.
+ *  @param p the start position (0 is the first).
+ *  @param l the resulting length of the fragment.
+ *  @returns the location of the fragment, or -1 if non is found.
+ */
+int getScopeFragment(const QCString &s,int p,int *l)
+{
+  int sl=s.length();
+  int sp=p;
+  int count=0;
+  bool done;
+  if (sp>=sl) return -1;
+  while (sp<sl)
+  {
+    char c=s.at(sp);
+    if (c==':') sp++,p++; else break;
+  }
+  while (sp<sl)
+  {
+    char c=s.at(sp);
+    switch (c)
+    {
+      case ':': // found next part
+        goto found;
+      case '<': // skip template specifier
+        count=1;sp++;
+        done=FALSE;
+        while (sp<sl && !done)
+        {
+          // TODO: deal with << and >> operators!
+          char c=s.at(sp++);
+          switch(c)
+          {
+            case '<': count++; break;
+            case '>': count--; if (count==0) done=TRUE; break;
+            default: break;
+          }
+        }
+        break;
+      default:
+        sp++;
+        break;
+    }
+  }
+found:
+  *l=sp-p;
+  //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
+  return p;
+}
+
+//----------------------------------------------------------------------------
+
+PageDef *addRelatedPage(const char *name,const QCString &ptitle,
+    const QCString &doc,
+    QList<SectionInfo> * /*anchors*/,
+    const char *fileName,int startLine,
+    const QList<ListItemInfo> *sli,
+    GroupDef *gd,
+    TagInfo *tagInfo
+    )
+{
+  PageDef *pd=0;
+  //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
+  if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
+  {
+    // append documentation block to the page.
+    pd->setDocumentation(doc,fileName,startLine);
+    //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name);
+  }
+  else // new page
+  {
+    QCString baseName=name;
+    if (baseName.right(4)==".tex") 
+      baseName=baseName.left(baseName.length()-4);
+    else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
+      baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
+
+    QCString title=ptitle.stripWhiteSpace();
+    pd=new PageDef(fileName,startLine,baseName,doc,title);
+
+    pd->setRefItems(sli);
+
+    if (tagInfo)
+    {
+      pd->setReference(tagInfo->tagName);
+    }
+
+    pd->setFileName(convertNameToFile(pd->name(),FALSE));
+
+    //printf("Appending page `%s'\n",baseName.data());
+    Doxygen::pageSDict->append(baseName,pd);
+
+    if (gd) gd->addPage(pd);
+
+    if (!pd->title().isEmpty())
+    {
+      //outputList->writeTitle(pi->name,pi->title);
+
+      // a page name is a label as well!
+      QCString file;
+      if (gd)
+      {
+        file=gd->getOutputFileBase();
+      }
+      else 
+      {
+        file=pd->getOutputFileBase();
+      }
+      SectionInfo *si=new SectionInfo(
+          file,pd->name(),pd->title(),SectionInfo::Page,pd->getReference());
+      //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
+      //      si->label.data(),si->definition?si->definition->name().data():"<none>",
+      //      si->fileName.data());
+      //printf("  SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
+      //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
+      Doxygen::sectionDict.insert(pd->name(),si);
+    }
+  }
+  return pd;
+}
+
+//----------------------------------------------------------------------------
+
+void addRefItem(const QList<ListItemInfo> *sli,
+    const char *key, 
+    const char *prefix, const char *name,const char *title,const char *args)
+{
+  //printf("addRefItem(sli=%p,prefix=%s,name=%s,title=%s,args=%s)\n",sli,prefix,name,title,args);
+  if (sli)
+  {
+    QListIterator<ListItemInfo> slii(*sli);
+    ListItemInfo *lii;
+    for (slii.toFirst();(lii=slii.current());++slii)
+    {
+      RefList *refList = Doxygen::xrefLists->find(lii->type);
+      if (refList
+          &&
+          (
+           // either not a built-in list or the list is enabled
+           (lii->type!="todo"       || Config_getBool("GENERATE_TODOLIST")) &&
+           (lii->type!="test"       || Config_getBool("GENERATE_TESTLIST")) &&
+           (lii->type!="bug"        || Config_getBool("GENERATE_BUGLIST"))  &&
+           (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
+          )
+         )
+      {
+        RefItem *item = refList->getRefItem(lii->itemId);
+        ASSERT(item!=0);
+
+        item->prefix = prefix;
+        item->name   = name;
+        item->title  = title;
+        item->args   = args;
+
+        refList->insertIntoList(key,item);
+
+#if 0
+
+        //printf("anchor=%s written=%d\n",item->listAnchor.data(),item->written);
+        //if (item->written) return;
+
+        QCString doc;
+        doc =  "\\anchor ";
+        doc += item->listAnchor;
+        doc += " <dl><dt>";
+        doc += prefix;
+        doc += " \\_internalref ";
+        doc += name;
+        doc += " \"";
+        doc += title;
+        doc += "\"";
+        if (args) doc += args;
+        doc += "</dt>\n<dd>";
+        doc += item->text;
+        doc += "</dd></dl>\n";
+        addRelatedPage(refList->listName(),refList->pageTitle(),doc,0,refList->listName(),1,0,0,0);
+        //item->written=TRUE;
+#endif
+      }
+    }
+  }
+}
+
+void addGroupListToTitle(OutputList &ol,Definition *d)
+{
+  LockingPtr<GroupList> groups = d->partOfGroups();
+  if (groups!=0) // write list of group to which this definition belongs
+  {
+    ol.pushGeneratorState();
+    ol.disableAllBut(OutputGenerator::Html);
+    ol.lineBreak();
+    ol.startSmall();
+    ol.docify("[");
+    GroupListIterator gli(*groups);
+    GroupDef *gd;
+    bool first=TRUE;
+    for (gli.toFirst();(gd=gli.current());++gli)
+    {
+      if (!first) { ol.docify(","); ol.writeNonBreakableSpace(1); } else first=FALSE; 
+      ol.writeObjectLink(gd->getReference(),
+          gd->getOutputFileBase(),0,gd->groupTitle());
+    }
+    ol.docify("]");
+    ol.endSmall();
+    ol.popGeneratorState();
+  }
+}
+
+#if 0
+/*!
+ * Function converts Latin1 character to latex string representin the same
+ * character.
+ */
+static void latin1ToLatex(QTextStream &t,unsigned char c)
+{
+  switch (c)
+  {
+    // the Latin-1 characters
+    case 161: t << "!`";            break;
+    case 181: t << "$\\mu$";        break;
+    case 191: t << "?`";            break;
+    case 192: t << "\\`{A}";        break;
+    case 193: t << "\\'{A}";        break;
+    case 194: t << "\\^{A}";        break;
+    case 195: t << "\\~{A}";        break;
+    case 196: t << "\\\"{A}";       break;
+    case 197: t << "\\AA{}";        break;
+    case 198: t << "\\AE{}";        break;
+    case 199: t << "\\c{C}";        break;
+    case 200: t << "\\`{E}";        break;
+    case 201: t << "\\'{E}";        break;
+    case 202: t << "\\^{E}";        break;
+    case 203: t << "\\\"{E}";       break;
+    case 204: t << "\\`{I}";        break;
+    case 205: t << "\\'{I}";        break;
+    case 206: t << "\\^{I}";        break;
+    case 207: t << "\\\"{I}";       break;
+    case 208: t << "D ";            break; // anyone know the real code?
+    case 209: t << "\\~{N}";        break;
+    case 210: t << "\\`{O}";        break;
+    case 211: t << "\\'{O}";        break;
+    case 212: t << "\\^{O}";        break;
+    case 213: t << "\\~{O}";        break;
+    case 214: t << "\\\"{O}";       break;
+    case 215: t << "$\\times$";     break;
+    case 216: t << "\\O";           break;
+    case 217: t << "\\`{U}";        break;
+    case 218: t << "\\'{U}";        break;
+    case 219: t << "\\^{U}";        break;
+    case 220: t << "\\\"{U}";       break;
+    case 221: t << "\\'{Y}";        break;
+    case 223: t << "\\ss{}";        break; 
+    case 224: t << "\\`{a}";        break;
+    case 225: t << "\\'{a}";        break;
+    case 226: t << "\\^{a}";        break;
+    case 227: t << "\\~{a}";        break;
+    case 228: t << "\\\"{a}";       break;
+    case 229: t << "\\aa{}";        break;
+    case 230: t << "\\ae{}";        break;
+    case 231: t << "\\c{c}";        break;
+    case 232: t << "\\`{e}";        break;
+    case 233: t << "\\'{e}";        break;
+    case 234: t << "\\^{e}";        break;
+    case 235: t << "\\\"{e}";       break;
+    case 236: t << "\\`{\\i}";      break;
+    case 237: t << "\\'{\\i}";      break;
+    case 238: t << "\\^{\\i}";      break;
+    case 239: t << "\\\"{\\i}";     break;
+    case 241: t << "\\~{n}";        break;
+    case 242: t << "\\`{o}";        break;
+    case 243: t << "\\'{o}";        break;
+    case 244: t << "\\^{o}";        break;
+    case 245: t << "\\~{o}";        break;
+    case 246: t << "\\\"{o}";       break;
+    case 248: t << "\\o{}";         break;
+    case 249: t << "\\`{u}";        break;
+    case 250: t << "\\'{u}";        break;
+    case 251: t << "\\^{u}";        break;
+    case 252: t << "\\\"{u}";       break;
+    case 253: t << "\\'{y}";        break;
+    case 255: t << "\\\"{y}";       break;           
+    default: t << (char)c;
+  }
+}
+
+/*!
+ * Function converts Latin2 character to latex string representin the same
+ * character.
+ */
+static void latin2ToLatex(QTextStream &t,unsigned char c)
+{
+  switch (c)
+  {
+    case 0xA1: t << "\\k{A}";   break;
+    case 0xA2: t << (char)c;    break;
+    case 0xA3: t << "\\L{}";    break;
+    case 0xA4: t << (char)c;    break;
+    case 0xA5: t << (char)c;    break;
+    case 0xA6: t << "\\'{S}";   break;
+    case 0xA7: t << (char)c;    break;
+    case 0xA8: t << (char)c;    break;
+    case 0xA9: t << "\\v{S}";   break;
+    case 0xAA: t << "\\c{S}";   break;
+    case 0xAB: t << "\\v{T}";   break;
+    case 0xAC: t << "\\'{Z}";   break;
+    case 0xAD: t << (char)c;    break;
+    case 0xAE: t << "\\v{Z}";   break;
+    case 0xAF: t << "\\.{Z}";   break;
+
+    case 0xB0: t << (char)c;    break;
+    case 0xB1: t << "\\k{a}";   break;
+    case 0xB2: t << (char)c;    break;
+    case 0xB3: t << "\\l{}";    break;
+    case 0xB4: t << (char)c;    break;
+    case 0xB5: t << (char)c;    break;
+    case 0xB6: t << "\\'{s}";   break;
+    case 0xB7: t << (char)c;    break;
+    case 0xB8: t << (char)c;    break;
+    case 0xB9: t << "\\v{s}";   break;
+    case 0xBA: t << "\\c{s}";   break;
+    case 0xBB: t << "\\v{t}";   break;
+    case 0xBC: t << "\\'{z}";   break;
+    case 0xBD: t << (char)c;    break;
+    case 0xBE: t << "\\v{z}";   break;
+    case 0xBF: t << "\\.{z}";   break;
+
+    case 0xC0: t << "\\'{R}";   break;
+    case 0xC1: t << "\\'{A}";   break;
+    case 0xC2: t << "\\^{A}";   break;
+    case 0xC3: t << "\\u{A}";   break;
+    case 0xC4: t << "\\\"{A}";  break;
+    case 0xC5: t << "\\'{L}";   break;
+    case 0xC6: t << "\\'{C}";   break;
+    case 0xC7: t << "\\c{C}";   break;
+    case 0xC8: t << "\\v{C}";   break;
+    case 0xC9: t << "\\'{E}";   break;
+    case 0xCA: t << "\\k{E}";   break;
+    case 0xCB: t << "\\\"{E}";  break;
+    case 0xCC: t << "\\v{E}";   break;
+    case 0xCD: t << "\\'{I}";   break;
+    case 0xCE: t << "\\^{I}";   break;
+    case 0xCF: t << "\\v{D}";   break;
+
+    case 0xD0: t << "\\DJ "; break;
+    case 0xD1: t << "\\'{N}";   break;
+    case 0xD2: t << "\\v{N}";   break;
+    case 0xD3: t << "\\'{O}";   break;
+    case 0xD4: t << "\\^{O}";   break;
+    case 0xD5: t << "\\H{O}";   break;
+    case 0xD6: t << "\\\"{O}";  break;
+    case 0xD7: t << (char)c;    break;
+    case 0xD8: t << "\\v{R}";   break;
+    case 0xD9: t << (char)c;    break;
+    case 0xDA: t << "\\'{U}";   break;
+    case 0xDB: t << "\\H{U}";   break;
+    case 0xDC: t << "\\\"{U}";  break;
+    case 0xDD: t << "\\'{Y}";   break;
+    case 0xDE: t << "\\c{T}";   break;
+    case 0xDF: t << "\\ss";     break;
+
+    case 0xE0: t << "\\'{r}";   break;
+    case 0xE1: t << "\\'{a}";   break;
+    case 0xE2: t << "\\^{a}";   break;
+    case 0xE3: t << (char)c;    break;
+    case 0xE4: t << "\\\"{a}";  break;
+    case 0xE5: t << "\\'{l}";   break;
+    case 0xE6: t << "\\'{c}";   break;
+    case 0xE7: t << "\\c{c}";   break;
+    case 0xE8: t << "\\v{c}";   break;
+    case 0xE9: t << "\\'{e}";   break;
+    case 0xEA: t << "\\k{e}";   break;
+    case 0xEB: t << "\\\"{e}";  break;
+    case 0xEC: t << "\\v{e}";   break;
+    case 0xED: t << "\\'{\\i}"; break;
+    case 0xEE: t << "\\^{\\i}"; break;
+    case 0xEF: t << "\\v{d}";   break;
+
+    case 0xF0: t << "\\dj "; break;
+    case 0xF1: t << "\\'{n}";   break;
+    case 0xF2: t << "\\v{n}";   break;
+    case 0xF3: t << "\\'{o}";   break;
+    case 0xF4: t << "\\^{o}";   break;
+    case 0xF5: t << "\\H{o}";   break;
+    case 0xF6: t << "\\\"{o}";  break;
+    case 0xF7: t << (char)c;    break;
+    case 0xF8: t << "\\v{r}";   break;
+    case 0xF9: t << (char)c;    break;
+    case 0xFA: t << "\\'{u}";   break;
+    case 0xFB: t << "\\H{u}";   break;
+    case 0xFC: t << "\\\"{u}";  break;
+    case 0xFD: t << "\\'{y}";   break;
+    case 0xFE: t << (char)c;    break;
+    case 0xFF: t << (char)c;    break;
+
+    default: t << (char)c;
+  }
+}
+#endif
+
+void filterLatexString(QTextStream &t,const char *str,
+    bool insideTabbing,bool insidePre,bool insideItem)
+{
+  if (str)
+  {
+    const unsigned char *p=(const unsigned char *)str;
+    unsigned char c;
+    unsigned char pc='\0';
+    while (*p)
+    {
+      c=*p++;
+
+      if (insidePre)
+      {
+        switch(c)
+        {
+          case '\\': t << "\\(\\backslash\\)"; break;
+          case '{':  t << "\\{"; break;
+          case '}':  t << "\\}"; break;
+          case '_':  t << "\\_"; break;
+          default: 
+                     t << (char)c;
+        }
+      }
+      else
+      {
+        switch(c)
+        {
+          case '#':  t << "\\#";           break;
+          case '$':  t << "\\$";           break;
+          case '%':  t << "\\%";           break;
+          case '^':  t << "$^\\wedge$";    break;
+          case '&':  t << "\\&";           break;
+          case '*':  t << "$\\ast$";       break;
+          case '_':  t << "\\_"; 
+                     if (!insideTabbing) t << "\\-";  
+                     break;
+          case '{':  t << "\\{";           break;
+          case '}':  t << "\\}";           break;
+          case '<':  t << "$<$";           break;
+          case '>':  t << "$>$";           break;
+          case '|':  t << "$|$";           break;
+          case '~':  t << "$\\sim$";       break;
+          case '[':  if (Config_getBool("PDF_HYPERLINKS") || insideItem) 
+                       t << "\\mbox{[}"; 
+                     else
+                       t << "[";
+                     break;
+          case ']':  if (pc=='[') t << "$\\,$";
+                       if (Config_getBool("PDF_HYPERLINKS") || insideItem)
+                         t << "\\mbox{]}";
+                       else
+                         t << "]";             
+                     break;
+          case '-':  t << "-\\/";
+                     break;
+          case '\\': if (*p=='<') 
+                     { t << "$<$"; p++; }
+                     else if (*p=='>')
+                     { t << "$>$"; p++; } 
+                     else  
+                     { t << "$\\backslash$"; }
+                     break;           
+          case '"':  { t << "\\char`\\\"{}"; }
+                     break;
+
+          default:   
+                     t << (char)c;
+#if 0
+                     {
+                       // Some languages use wide characters
+                       if (isJapanese || isKorean || isChinese || isSerbian)
+                       { 
+                         if (c>=128) 
+                         {
+                           t << (char)c;
+                           if (*p)  
+                           {
+                             c = *p++;
+                             t << (char)c;
+                           }
+                         }
+                         else // ascii char => see if we can insert a hypenation hint
+                         {
+                           if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
+                           t << (char)c;    
+                         } 
+                       }
+                       else if (isCzech || isRussian || isUkrainian || isSlovene)
+                       {
+                         if (c>=128)
+                         {
+                           t << (char)c;
+                         }
+                         else // ascii char => see if we can insert a hypenation hint
+                         {
+                           if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
+                           t << (char)c;
+                         }
+                       }
+                       else if (isGreek)
+                       {
+                         if (c<128)
+                         {
+                           t << "\\textlatin{" << (char)c << "}";
+                         }
+                         else
+                         {
+                           t << (char)c;
+                         }
+                       }
+                       else if (isLatin2)
+                       {
+                         if (c>=128)
+                         {
+                           latin2ToLatex(t,c);
+                         }
+                         else
+                         { 
+                           // see if we can insert an hyphenation hint
+                           if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
+                           t << (char)c;
+                         }
+                       }
+                       else // another language => assume latin1 charset
+                       {
+                         if (c>=128)
+                         {
+                           latin1ToLatex(t,c);
+                         }
+                         else
+                         { 
+                           // see if we can insert an hyphenation hint
+                           if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
+                           t << (char)c;
+                         }
+                       }
+                     }
+#endif
+        }
+      }
+      pc = c;
+    }
+  }
+}
+
+
+QCString rtfFormatBmkStr(const char *name)
+{
+  static QCString g_nextTag( "AAAAAAAAAA" );
+  static QDict<QCString> g_tagDict( 5003 );
+
+  g_tagDict.setAutoDelete(TRUE);
+
+  // To overcome the 40-character tag limitation, we
+  // substitute a short arbitrary string for the name
+  // supplied, and keep track of the correspondence
+  // between names and strings.
+  QCString key( name );
+  QCString* tag = g_tagDict.find( key );
+  if ( !tag )
+  {
+    // This particular name has not yet been added
+    // to the list. Add it, associating it with the
+    // next tag value, and increment the next tag.
+    tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
+    g_tagDict.insert( key, tag );
+
+    // This is the increment part
+    char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1;
+    for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
+    {
+      if ( ( ++(*nxtTag) ) > 'Z' )
+      {
+        *nxtTag = 'A';
+      }
+      else
+      {
+        // Since there was no carry, we can stop now
+        break;
+      }
+    }
+  }
+
+  return *tag;
+}
+
+QCString stripExtension(const char *fName)
+{
+  QCString result=fName;
+  if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
+  {
+    result=result.left(result.length()-Doxygen::htmlFileExtension.length());
+  }
+  return result;
+}
+
+
+void replaceNamespaceAliases(QCString &scope,int i)
+{
+  //printf("replaceNamespaceAliases(%s,%d)\n",scope.data(),i);
+  while (i>0)
+  {
+    QCString *s = Doxygen::namespaceAliasDict[scope.left(i)];
+    if (s)
+    {
+      scope=*s+scope.right(scope.length()-i);
+      i=s->length();
+    }
+    i=scope.findRev("::",i-1);
+  }
+  //printf("replaceNamespaceAliases() result=%s\n",scope.data());
+}
+
+QCString stripPath(const char *s)
+{
+  QCString result=s;
+  int i=result.findRev('/');
+  if (i!=-1)
+  {
+    result=result.mid(i+1);
+  }
+  return result;
+}
+
+/** returns \c TRUE iff string \a s contains word \a w */
+bool containsWord(const QCString &s,const QCString &word)
+{
+  static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
+  int p=0,i,l;
+  while ((i=wordExp.match(s,p,&l))!=-1)
+  {
+    if (s.mid(i,l)==word) return TRUE;
+    p=i+l;
+  }
+  return FALSE;
+}
+
+bool findAndRemoveWord(QCString &s,const QCString &word)
+{
+  static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
+  int p=0,i,l;
+  while ((i=wordExp.match(s,p,&l))!=-1)
+  {
+    if (s.mid(i,l)==word) 
+    {
+      if (i>0 && isspace(s.at(i-1))) 
+        i--,l++;
+      else if (i+l<(int)s.length() && isspace(s.at(i+l))) 
+        l++;
+      s = s.left(i)+s.mid(i+l); // remove word + spacing
+      return TRUE;
+    }
+    p=i+l;
+  }
+  return FALSE;
+}
+
+/** Special version of QCString::stripWhiteSpace() that only strips
+ *  empty lines.
+ */
+QCString stripLeadingAndTrailingEmptyLines(const QCString &s)
+{
+  const char *p = s.data();
+  if (p==0) return 0;
+
+  // search for leading empty lines
+  int i=0,li=-1,l=s.length();
+  char c;
+  while ((c=*p++))
+  {
+    if (c==' ' || c=='\t' || c=='\r') i++;
+    else if (c=='\n') i++,li=i;
+    else break;
+  }
+
+  // search for trailing empty lines
+  int b=l-1,bi=-1;
+  p=s.data()+b;
+  while (b>=0)
+  {
+    c=*p; p--;
+    if (c==' ' || c=='\t' || c=='\r') b--;
+    else if (c=='\n') bi=b,b--;
+    else break;
+  }
+
+  // return whole string if no leading or trailing lines where found
+  if (li==-1 && bi==-1) return s;
+
+  // return substring
+  if (bi==-1) bi=l;
+  if (li==-1) li=0;
+  if (bi<=li) return 0; // only empty lines
+  return s.mid(li,bi-li);
+}
+
+#if 0
+void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
+    const QCString &str,bool priority,const QCString &anchor)
+{
+  static bool searchEngine = Config_getBool("SEARCHENGINE");
+  if (searchEngine)
+  {
+    Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
+    static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+    int i,p=0,l;
+    while ((i=wordPattern.match(str,p,&l))!=-1)
+    {
+      Doxygen::searchIndex->addWord(str.mid(i,l),priority);
+      p=i+l;
+    }
+  }
+}
+#endif
+
+//--------------------------------------------------------------------------
+
+static QDict<int> g_extLookup;
+
+static struct Lang2ExtMap
+{
+  const char *langName;
+  const char *parserName;
+  SrcLangExt parserId;
+} 
+g_lang2extMap[] =
+{
+//  language       parser     parser option
+  { "idl",         "c",       SrcLangExt_IDL    },
+  { "java",        "c",       SrcLangExt_Java   },
+  { "javascript",  "c",       SrcLangExt_JS     },
+  { "c#",          "c",       SrcLangExt_CSharp },
+  { "d",           "c",       SrcLangExt_D      },
+  { "php",         "c",       SrcLangExt_PHP    },
+  { "objective-c", "c",       SrcLangExt_ObjC   },
+  { "c",           "c",       SrcLangExt_Cpp    },
+  { "c++",         "c",       SrcLangExt_Cpp    },
+  { "python",      "python",  SrcLangExt_Python },
+  { "fortran",     "fortran", SrcLangExt_F90    },
+  { "vhdl",        "vhdl",    SrcLangExt_VHDL   },
+  { "dbusxml",     "dbusxml", SrcLangExt_XML    },
+  { 0,             0,        (SrcLangExt)0      }
+};
+
+bool updateLanguageMapping(const QCString &extension,const QCString &language)
+{
+  //getLanguageFromFileName("dummy"); // force initializion of the g_extLookup map
+  const Lang2ExtMap *p = g_lang2extMap;
+  QCString langName = language.lower();
+  while (p->langName)
+  {
+    if (langName==p->langName) break;
+    p++;
+  }
+  if (!p->langName) return FALSE;
+
+  // found the language
+  SrcLangExt parserId = p->parserId;
+  QCString extName = extension;
+  if (extName.isEmpty()) return FALSE;
+  if (extName.at(0)!='.') extName.prepend(".");
+  if (g_extLookup.find(extension)!=0) // language was already register for this ext
+  {
+    g_extLookup.remove(extension);
+  }
+  g_extLookup.insert(extension,new int(parserId));
+  if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
+  {
+    err("Failed to assign extension %s to parser %s for language %s\n",
+        extName.data(),p->parserName,language.data());
+  }
+  else
+  {
+    //msg("Registered extension %s to language parser %s...\n",
+    //    extName.data(),language.data());
+  }
+  return TRUE;
+}
+
+void initDefaultExtensionMapping()
+{
+  g_extLookup.setAutoDelete(TRUE);
+  updateLanguageMapping(".idl",   "idl"); 
+  updateLanguageMapping(".ddl",   "idl"); 
+  updateLanguageMapping(".odl",   "idl"); 
+  updateLanguageMapping(".java",  "java");
+  updateLanguageMapping(".as",    "javascript"); 
+  updateLanguageMapping(".js",    "javascript");
+  updateLanguageMapping(".cs",    "c#");
+  updateLanguageMapping(".d",     "d");
+  updateLanguageMapping(".php",   "php"); 
+  updateLanguageMapping(".php4",  "php");
+  updateLanguageMapping(".php5",  "php");
+  updateLanguageMapping(".inc",   "php");
+  updateLanguageMapping(".phtml", "php");
+  updateLanguageMapping(".m",     "objective-c");
+  updateLanguageMapping(".M",     "objective-c");
+  updateLanguageMapping(".mm",    "objective-c");
+  updateLanguageMapping(".py",    "python");
+  updateLanguageMapping(".f",     "fortran");
+  updateLanguageMapping(".f90",   "fortran");
+  updateLanguageMapping(".vhd",   "vhdl");
+  updateLanguageMapping(".vhdl",  "vhdl");
+  //updateLanguageMapping(".xml",   "dbusxml");
+}
+
+SrcLangExt getLanguageFromFileName(const QCString fileName)
+{
+  int i = fileName.findRev('.');
+  if (i!=-1) // name has an extension
+  {
+    QCString extStr=fileName.right(fileName.length()-i);
+    if (!extStr.isEmpty()) // non-empty extension
+    {
+      int *pVal=g_extLookup.find(extStr);
+      if (pVal) // listed extension
+      {
+        return (SrcLangExt)*pVal; 
+      }
+    }
+  }
+  return SrcLangExt_Cpp; // not listed => assume C-ish language.
+}
+
+//--------------------------------------------------------------------------
+
+/*! Returns true iff the given name string appears to be a typedef in scope. */
+bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
+{
+  if (scope==0 ||
+      (scope->definitionType()!=Definition::TypeClass &&
+       scope->definitionType()!=Definition::TypeNamespace
+      )
+     )
+  {
+    scope=Doxygen::globalScope;
+  }
+
+  QCString name = n;
+  if (name.isEmpty())
+    return FALSE; // no name was given
+
+  DefinitionIntf *di = Doxygen::symbolMap->find(name);
+  if (di==0)
+    return FALSE; // could not find any matching symbols
+
+  // mostly copied from getResolvedClassRec()
+  QCString explicitScopePart;
+  int qualifierIndex = computeQualifiedIndex(name);
+  if (qualifierIndex!=-1)
+  {
+    explicitScopePart = name.left(qualifierIndex);
+    replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
+    name = name.mid(qualifierIndex+2);
+  }
+
+  int minDistance = 10000;
+  MemberDef *bestMatch = 0;
+
+  if (di->definitionType()==DefinitionIntf::TypeSymbolList)
+  {
+    // find the closest closest matching definition
+    DefinitionListIterator dli(*(DefinitionList*)di);
+    Definition *d;
+    for (dli.toFirst();(d=dli.current());++dli)
+    {
+      if (d->definitionType()==Definition::TypeMember)
+      {
+        g_visitedNamespaces.clear();
+        int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+        if (distance!=-1 && distance<minDistance)
+        {
+          minDistance = distance;
+          bestMatch = (MemberDef *)d;
+        }
+      }
+    }
+  }
+  else if (di->definitionType()==Definition::TypeMember)
+  {
+    Definition *d = (Definition *)di;
+    g_visitedNamespaces.clear();
+    int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+    if (distance!=-1 && distance<minDistance)
+    {
+      minDistance = distance;
+      bestMatch = (MemberDef *)d;
+    }
+  }
+
+  if (bestMatch && bestMatch->isTypedef())
+    return TRUE; // closest matching symbol is a typedef
+  else
+    return FALSE;
+}
+
+QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
+    const QString &doc,const QCString &fileName,int lineNr)
+{
+  QString result;
+  if (doc.isEmpty()) return result.data();
+  QTextStream t(&result,IO_WriteOnly);
+  DocNode *root = validatingParseDoc(fileName,lineNr,
+      (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
+  TextDocVisitor *visitor = new TextDocVisitor(t);
+  root->accept(visitor);
+  delete visitor;
+  delete root;
+  int i=0;
+  if (result.length()>80)
+  {
+    for (i=80;i<100;i++) // search for nice truncation point
+    {
+      if (result.at(i).isSpace() || 
+          result.at(i)==',' || 
+          result.at(i)=='.' || 
+          result.at(i)=='?')
+      {
+        break;
+      }
+    }
+  }
+  if (i>0) result=result.left(i)+"...";
+  return result.data();
+}
+
+//--------------------------------------------------------------------------------------
+
+static QDict<void> aliasesProcessed;
+
+QCString expandAliasRec(const QCString s)
+{
+  QCString result;
+  static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
+  QCString value=s;
+  int i,p=0,l;
+  while ((i=cmdPat.match(value,p,&l))!=-1)
+  {
+    result+=value.mid(p,i-p);
+    QCString args = extractAliasArgs(value,i+l);
+    bool hasArgs = !args.isEmpty();            // found directly after command
+    QCString cmd;
+    if (hasArgs)
+    {
+      int numArgs = countAliasArguments(args);
+      cmd  = value.mid(i+1,l-1)+QCString().sprintf("{%d}",numArgs);  // alias name + {n}
+    }
+    else
+    {
+      cmd = value.mid(i+1,l-1);
+    }
+    //printf("Found command '%s' args='%s'\n",cmd.data(),args.data());
+    QCString *aliasText=Doxygen::aliasDict.find(cmd);
+    if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias
+    {
+      //printf("is an alias!\n");
+      aliasesProcessed.insert(cmd,(void *)0x8);
+      QCString val = *aliasText;
+      if (hasArgs)
+      {
+        val = replaceAliasArguments(val,args);
+        //printf("replace '%s'->'%s' args='%s'\n",
+        //       aliasText->data(),val.data(),args.data());
+      }
+      result+=expandAliasRec(val);
+      aliasesProcessed.remove(cmd);
+      p=i+l;
+      if (hasArgs) p+=args.length()+2;
+    }
+    else // command is not an alias
+    {
+      //printf("not an alias!\n");
+      result+=value.mid(i,l);
+      p=i+l;
+    }
+  }
+  result+=value.right(value.length()-p);
+
+  //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
+  return result;
+}
+
+static QCString replaceAliasArgument(const QCString &aliasValue,int paramNum,
+                                     const QCString &paramValue)
+{
+  QCString result;
+  QCString paramMarker;
+  paramMarker.sprintf("\\%d",paramNum);
+  int markerLen = paramMarker.length();
+  int p=0,i;
+  while ((i=aliasValue.find(paramMarker,p))!=-1) // search for marker
+  {
+    result+=aliasValue.mid(p,i-p);
+    //printf("Found marker '%s' at %d len=%d for param '%s' in '%s'\n",
+    //                 paramMarker.data(),i,markerLen,paramValue.data(),aliasValue.data());
+    if (i==0 || aliasValue.at(i-1)!='\\') // found unescaped marker
+    {
+      result += paramValue;
+      p=i+markerLen;
+    }
+    else // ignore escaped markers
+    {
+      result += aliasValue.mid(i,markerLen);
+      p=i+1;
+    }
+  }
+  result+=aliasValue.right(aliasValue.length()-p);
+  result = expandAliasRec(substitute(result,"\\,",","));
+  //printf("replaceAliasArgument('%s',%d,'%s')->%s\n",
+  //    aliasValue.data(),paramNum,paramValue.data(),result.data());
+  return result;
+}
+
+QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
+{
+  QCString result = aliasValue;
+  QList<QCString> args;
+  int p=0,i,c=1;
+  for (i=0;i<(int)argList.length();i++)
+  {
+    if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\'))
+    {
+      result = replaceAliasArgument(result,c,argList.mid(p,i-p));
+      p=i+1;
+      c++;
+    }
+  }
+  if (p<(int)argList.length())
+  {
+    result = replaceAliasArgument(result,c,argList.right(argList.length()-p));
+  }
+  return result;
+}
+
+int countAliasArguments(const QCString argList)
+{
+  int count=1;
+  int l = argList.length();
+  int i;
+  for (i=0;i<l;i++) 
+  {
+    if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++;
+  }
+  return count;
+}
+
+QCString extractAliasArgs(const QCString &args,int pos)
+{
+  int i;
+  int bc=0;
+  if (args.at(pos)=='{') // alias has argument
+  {
+    for (i=pos;i<(int)args.length();i++)
+    {
+      if (args.at(i)=='{') bc++;
+      if (args.at(i)=='}') bc--;
+      if (bc==0) 
+      {
+        //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
+        return args.mid(pos+1,i-pos-1);
+      }
+    }
+  }
+  return "";
+}
+
+QCString resolveAliasCmd(const QCString aliasCmd)
+{
+  QCString result;
+  aliasesProcessed.clear();
+  //printf("Expanding: '%s'\n",aliasCmd.data());
+  result = expandAliasRec(aliasCmd);
+  //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
+  return result;
+}
+
+QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
+{
+  QCString result;
+  aliasesProcessed.clear();
+  // avoid expanding this command recursively
+  aliasesProcessed.insert(aliasName,(void *)0x8);
+  // expand embedded commands
+  //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
+  result = expandAliasRec(aliasValue);
+  //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
+  return result;
+}
+
+void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
+{
+  if (al==0) return;
+  ol.startConstraintList(theTranslator->trTypeConstraints()); 
+  ArgumentListIterator ali(*al);
+  Argument *a;
+  for (;(a=ali.current());++ali)
+  {
+    ol.startConstraintParam();
+    ol.parseText(a->name);
+    ol.endConstraintParam();
+    ol.startConstraintType();
+    linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
+    ol.endConstraintType();
+    ol.startConstraintDocs();
+    ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
+    ol.endConstraintDocs();
+  }
+  ol.endConstraintList();
+}
+
+bool usingTreeIndex()
+{
+  static bool treeView = Config_getBool("USE_INLINE_TREES");
+  return treeView;
+}
+
+void stackTrace()
+{
+#ifdef TRACINGSUPPORT
+  void *backtraceFrames[128];
+  int frameCount = backtrace(backtraceFrames, 128);
+  static char cmd[40960];
+  char *p = cmd;
+  p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
+  for (int x = 0; x < frameCount; x++) 
+  {
+    p += sprintf(p,"%p ", backtraceFrames[x]);
+  }
+  fprintf(stderr,"========== STACKTRACE START ==============\n");
+  if (FILE *fp = popen(cmd, "r"))
+  {
+    char resBuf[512];
+    while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
+    {
+      fwrite(resBuf, 1, len, stderr);
+    }
+    pclose(fp);
+  }
+  fprintf(stderr,"============ STACKTRACE END ==============\n");
+  //fprintf(stderr,"%s\n", frameStrings[x]);
+#endif
+}
+
+static int transcodeCharacterBuffer(BufStr &srcBuf,int size,
+           const char *inputEncoding,const char *outputEncoding)
+{
+  if (inputEncoding==0 || outputEncoding==0) return size;
+  if (qstricmp(inputEncoding,outputEncoding)==0) return size;
+  void *cd = portable_iconv_open(outputEncoding,inputEncoding);
+  if (cd==(void *)(-1)) 
+  {
+    err("Error: unsupported character conversion: '%s'->'%s': %s\n"
+        "Check the INPUT_ENCODING setting in the config file!\n",
+        inputEncoding,outputEncoding,strerror(errno));
+    exit(1);
+  }
+  int tmpBufSize=size*4+1;
+  BufStr tmpBuf(tmpBufSize);
+  size_t iLeft=size;
+  size_t oLeft=tmpBufSize;
+  const char *srcPtr = srcBuf.data();
+  char *dstPtr = tmpBuf.data();
+  uint newSize=0;
+  if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
+  {
+    newSize = tmpBufSize-oLeft;
+    srcBuf.shrink(newSize);
+    strncpy(srcBuf.data(),tmpBuf.data(),newSize);
+    //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
+  }
+  else
+  {
+    err("Error: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
+        inputEncoding,outputEncoding);
+    exit(1);
+  }
+  portable_iconv_close(cd);
+  return newSize;
+}
+
+//! read a file name \a fileName and optionally filter and transcode it
+bool readInputFile(const char *fileName,BufStr &inBuf)
+{
+  // try to open file
+  int size=0;
+  //uint oldPos = dest.curPos();
+  //printf(".......oldPos=%d\n",oldPos);
+
+  QFileInfo fi(fileName);
+  if (!fi.exists()) return FALSE;
+  QCString filterName = getFileFilter(fileName);
+  if (filterName.isEmpty())
+  {
+    QFile f(fileName);
+    if (!f.open(IO_ReadOnly))
+    {
+      err("Error: could not open file %s\n",fileName);
+      return FALSE;
+    }
+    size=fi.size();
+    // read the file
+    inBuf.skip(size);
+    if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
+    {
+      err("Error while reading file %s\n",fileName);
+      return FALSE;
+    }
+  }
+  else
+  {
+    QCString cmd=filterName+" \""+fileName+"\"";
+    Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
+    FILE *f=portable_popen(cmd,"r");
+    if (!f)
+    {
+      err("Error: could not execute filter %s\n",filterName.data());
+      return FALSE;
+    }
+    const int bufSize=1024;
+    char buf[bufSize];
+    int numRead;
+    while ((numRead=fread(buf,1,bufSize,f))>0) 
+    {
+      //printf(">>>>>>>>Reading %d bytes\n",numRead);
+      inBuf.addArray(buf,numRead),size+=numRead;
+    }
+    portable_pclose(f);
+  }
+
+  int start=0;
+  if (inBuf.size()>=2 &&
+      ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM
+       (inBuf.at(0)==-2 && inBuf.at(1)==-1)    // big endian BOM
+      )
+     ) // UCS-2 encoded file
+  {
+    transcodeCharacterBuffer(inBuf,inBuf.curPos(),
+        "UCS-2","UTF-8");
+  }
+  else if (inBuf.size()>=3 &&
+           (uchar)inBuf.at(0)==0xEF &&
+           (uchar)inBuf.at(1)==0xBB &&
+           (uchar)inBuf.at(2)==0xBF
+     )
+  {
+    // UTF-8 encoded file
+    inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
+  }
+  else // transcode according to the INPUT_ENCODING setting
+  {
+    // do character transcoding if needed.
+    transcodeCharacterBuffer(inBuf,inBuf.curPos(),
+        Config_getString("INPUT_ENCODING"),"UTF-8");
+  }
+
+  inBuf.addChar('\n'); /* to prevent problems under Windows ? */
+
+  // and translate CR's
+  size=inBuf.curPos()-start;
+  int newSize=filterCRLF(inBuf.data()+start,size);
+  //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
+  if (newSize!=size) // we removed chars
+  {
+    inBuf.shrink(newSize); // resize the array
+    //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
+  }
+  inBuf.at(inBuf.curPos())='\0';
+  return TRUE;
+}
+
+// Replace %word by word in title
+QCString filterTitle(const QCString &title)
+{
+  QCString tf;
+  static QRegExp re("%[A-Z_a-z]");
+  int p=0,i,l;
+  while ((i=re.match(title,p,&l))!=-1)
+  {
+    tf+=title.mid(p,i-p);
+    tf+=title.mid(i+1,l-1); // skip %
+    p=i+l;
+  }
+  tf+=title.right(title.length()-p);
+  return tf;
+}
+