util.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  *
00003  * $Id: util.cpp,v 1.74 2001/03/19 19:27:42 root Exp $
00004  *
00005  * Copyright (C) 1997-2008 by Dimitri van Heesch.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and its
00008  * documentation under the terms of the GNU General Public License is hereby 
00009  * granted. No representations are made about the suitability of this software 
00010  * for any purpose. It is provided "as is" without express or implied warranty.
00011  * See the GNU General Public License for more details.
00012  *
00013  * Documents produced by Doxygen are derivative works derived from the
00014  * input used in their production; they are not affected by this license.
00015  *
00016  */
00017 
00018 #include <stdlib.h>
00019 #include <ctype.h>
00020 #include <errno.h>
00021 
00022 #include <md5.h>
00023 
00024 #include "qtbc.h"
00025 #include <qregexp.h>
00026 #include <qfileinfo.h>
00027 #include <qdir.h>
00028 #include <qdatetime.h>
00029 #include <qcache.h>
00030 
00031 #include "util.h"
00032 #include "message.h"
00033 #include "classdef.h"
00034 #include "filedef.h"
00035 #include "doxygen.h"
00036 #include "outputlist.h"
00037 #include "defargs.h"
00038 #include "language.h"
00039 #include "config.h"
00040 #include "htmlhelp.h"
00041 #include "example.h"
00042 #include "version.h"
00043 #include "groupdef.h"
00044 #include "reflist.h"
00045 #include "pagedef.h"
00046 #include "debug.h"
00047 #include "searchindex.h"
00048 #include "doxygen.h"
00049 #include "textdocvisitor.h"
00050 #include "portable.h"
00051 
00052 //------------------------------------------------------------------------
00053 
00054 // selects one of the name to sub-dir mapping algorithms that is used
00055 // to select a sub directory when CREATE_SUBDIRS is set to YES.
00056 
00057 #define ALGO_COUNT 1
00058 #define ALGO_CRC16 2
00059 #define ALGO_MD5   3
00060     
00061 //#define MAP_ALGO ALGO_COUNT
00062 //#define MAP_ALGO ALGO_CRC16
00063 #define MAP_ALGO ALGO_MD5
00064 
00065 #define REL_PATH_TO_ROOT "../../"
00066 
00067 //------------------------------------------------------------------------
00068 // TextGeneratorOLImpl implementation
00069 //------------------------------------------------------------------------
00070 
00071 TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od) 
00072 {
00073 }
00074 
00075 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
00076 { 
00077   if (keepSpaces)
00078   {
00079     const char *p=s;
00080     if (p)
00081     {
00082       char cs[2];
00083       char c;
00084       cs[1]='\0';
00085       while ((c=*p++))
00086       {
00087         if (c==' ') m_od.writeNonBreakableSpace(1); 
00088         else cs[0]=c,m_od.docify(cs);
00089       }
00090     }
00091   }
00092   else
00093   {
00094     m_od.docify(s); 
00095   }
00096 }
00097 
00098 void TextGeneratorOLImpl::writeBreak() const
00099 { 
00100   m_od.pushGeneratorState();
00101   m_od.disableAllBut(OutputGenerator::Html);
00102   m_od.lineBreak();
00103   m_od.popGeneratorState();
00104 }
00105 
00106 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
00107                                     const char *anchor,const char *text
00108                                    ) const
00109 {
00110   m_od.writeObjectLink(extRef,file,anchor,text);
00111 }
00112 
00113 //------------------------------------------------------------------------
00114 //------------------------------------------------------------------------
00115 
00116 // an inheritance tree of depth of 100000 should be enough for everyone :-)
00117 const int maxInheritanceDepth = 100000; 
00118 
00119 bool isId(int c)
00120 {
00121   if (c<0 || c>255) return FALSE;
00122   return c=='_' || isalnum(c);
00123 }
00124 
00125 
00140 QCString removeAnonymousScopes(const QCString &s)
00141 {
00142   QCString result;
00143   if (s.isEmpty()) return result;
00144   static QRegExp re("[ :]*@[0-9]+[: ]*");
00145   int i,l,sl=s.length();
00146   int p=0;
00147   while ((i=re.match(s,p,&l))!=-1)
00148   {
00149     result+=s.mid(p,i-p);
00150     int c=i;
00151     bool b1=FALSE,b2=FALSE;
00152     while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
00153     c=i+l-1;
00154     while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
00155     if (b1 && b2) 
00156     { 
00157       result+="::"; 
00158     }
00159     p=i+l;
00160   }
00161   result+=s.right(sl-p);
00162   //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
00163   return result;
00164 }
00165 
00166 // replace anonymous scopes with __anonymous__ or replacement if provided
00167 QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
00168 {
00169   QCString result;
00170   if (s.isEmpty()) return result;
00171   static QRegExp re("@[0-9]+");
00172   int i,l,sl=s.length();
00173   int p=0;
00174   while ((i=re.match(s,p,&l))!=-1)
00175   {
00176     result+=s.mid(p,i-p);
00177     if (replacement)
00178     {
00179       result+=replacement;
00180     }
00181     else
00182     {
00183       result+="__anonymous__";
00184     }
00185     p=i+l;
00186   }
00187   result+=s.right(sl-p);
00188   //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
00189   return result;
00190 }
00191 
00192 
00193 // strip annonymous left hand side part of the scope
00194 QCString stripAnonymousNamespaceScope(const QCString &s)
00195 {
00196   int i,p=0,l;
00197   QCString newScope;
00198   while ((i=getScopeFragment(s,p,&l))!=-1)
00199   {
00200     //printf("Scope fragment %s\n",s.mid(i,l).data());
00201     if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
00202     {
00203       if (s.at(i)!='@')
00204       {
00205         if (!newScope.isEmpty()) newScope+="::";
00206         newScope+=s.mid(i,l);
00207       }
00208     }
00209     else
00210     {
00211       if (!newScope.isEmpty()) newScope+="::";
00212       newScope+=s.right(s.length()-i);
00213       goto done;
00214     }
00215     p=i+l;
00216   }
00217 done:
00218   //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
00219   return newScope;
00220 }
00221 
00222 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
00223 {
00224   od.pushGeneratorState();
00225   
00226   od.disable(OutputGenerator::Html);
00227   od.disable(OutputGenerator::Man);
00228   if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex);
00229   if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF);
00230   od.startPageRef();
00231   od.docify(theTranslator->trPageAbbreviation());
00232   od.endPageRef(cn,mn);
00233 
00234   od.popGeneratorState();
00235 }
00236 
00241 QCString generateMarker(int id)
00242 {
00243   QCString result;
00244   result.sprintf("@%d",id);
00245   return result;
00246 }
00247 
00248 static QCString stripFromPath(const QCString &path,QStrList &l)
00249 {
00250   // look at all the strings in the list and strip the longest match  
00251   const char *s=l.first();
00252   QCString potential;
00253   unsigned int length = 0;
00254   while (s)
00255   {
00256     QCString prefix = s;
00257     if (prefix.length() > length &&
00258         stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
00259     {
00260       length = prefix.length();
00261       potential = path.right(path.length()-prefix.length());
00262     }
00263     s = l.next();
00264   }
00265   if (length) return potential;
00266   return path;
00267 }
00268 
00272 QCString stripFromPath(const QCString &path)
00273 {
00274   return stripFromPath(path,Config_getList("STRIP_FROM_PATH"));
00275 }
00276 
00280 QCString stripFromIncludePath(const QCString &path)
00281 {
00282   return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH"));
00283 }
00284 
00289 int guessSection(const char *name)
00290 {
00291   QCString n=((QCString)name).lower();
00292   if (n.right(2)==".c"    || // source
00293       n.right(3)==".cc"   ||
00294       n.right(4)==".cxx"  ||
00295       n.right(4)==".cpp"  ||
00296       n.right(4)==".c++"  ||
00297       n.right(5)==".java" ||
00298       n.right(3)==".ii"   || // inline
00299       n.right(4)==".ixx"  ||
00300       n.right(4)==".ipp"  ||
00301       n.right(4)==".i++"  ||
00302       n.right(4)==".inl"
00303      ) return Entry::SOURCE_SEC;
00304   if (n.right(2)==".h"   || // header
00305       n.right(3)==".hh"  ||
00306       n.right(4)==".hxx" ||
00307       n.right(4)==".hpp" ||
00308       n.right(4)==".h++" ||
00309       n.right(4)==".idl" ||
00310       n.right(4)==".ddl" ||
00311       n.right(5)==".pidl"
00312      ) return Entry::HEADER_SEC;
00313   return 0;
00314 }
00315 
00316 QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
00317                         Definition **typedefContext)
00318 {
00319   //printf("<<resolveTypeDef(%s,%s)\n",
00320   //          context ? context->name().data() : "<none>",qualifiedName.data());
00321   QCString result;
00322   if (qualifiedName.isEmpty()) 
00323   {
00324     //printf("  qualified name empty!\n");
00325     return result;
00326   }
00327 
00328   Definition *mContext=context;
00329   if (typedefContext) *typedefContext=context;
00330 
00331   // see if the qualified name has a scope part
00332   int scopeIndex = qualifiedName.findRev("::");
00333   QCString resName=qualifiedName;
00334   if (scopeIndex!=-1) // strip scope part for the name
00335   {
00336     resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
00337     if (resName.isEmpty())
00338     {
00339       // qualifiedName was of form A:: !
00340       //printf("  qualified name of form A::!\n");
00341       return result;
00342     }
00343   }
00344   MemberDef *md=0;
00345   while (mContext && md==0)
00346   {
00347     // step 1: get the right scope
00348     Definition *resScope=mContext;
00349     if (scopeIndex!=-1) 
00350     {
00351       // split-off scope part
00352       QCString resScopeName = qualifiedName.left(scopeIndex);
00353       //printf("resScopeName=`%s'\n",resScopeName.data());
00354 
00355       // look-up scope in context
00356       int is,ps=0;
00357       int l;
00358       while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
00359       {
00360         QCString qualScopePart = resScopeName.mid(is,l);
00361         QCString tmp = resolveTypeDef(mContext,qualScopePart);
00362         if (!tmp.isEmpty()) qualScopePart=tmp;
00363         resScope = resScope->findInnerCompound(qualScopePart);
00364         //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
00365         if (resScope==0) break;
00366         ps=is+l;
00367       }
00368     }
00369     //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
00370     
00371     // step 2: get the member
00372     if (resScope) // no scope or scope found in the current context 
00373     {
00374       //printf("scope found: %s, look for typedef %s\n",
00375       //     resScope->qualifiedName().data(),resName.data());
00376       MemberNameSDict *mnd=0;
00377       if (resScope->definitionType()==Definition::TypeClass)
00378       {
00379         mnd=Doxygen::memberNameSDict;
00380       }
00381       else
00382       {
00383         mnd=Doxygen::functionNameSDict;
00384       }
00385       MemberName *mn=mnd->find(resName);
00386       if (mn)
00387       {
00388         MemberNameIterator mni(*mn);
00389         MemberDef *tmd=0;
00390         for (;(tmd=mni.current());++mni)
00391         {
00392           //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
00393           //    tmd->name().data(), resScope->name().data(), 
00394           //    tmd->getOuterScope()->name().data(), mContext);
00395           if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
00396           {
00397             // look if resScope is visible within tmd->getOuterScope()
00398             Definition *d = tmd->getOuterScope();
00399             while (d && d!=resScope) d=d->getOuterScope();
00400             if (d)
00401             {
00402               md=tmd;
00403             }
00404           }
00405         }
00406       }
00407     }
00408     mContext=mContext->getOuterScope();
00409   }
00410 
00411   // step 3: get the member's type
00412   if (md)
00413   {
00414     //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s'\n",
00415     //    qualifiedName.data(),context->name().data(),md->typeString()
00416     //    );
00417     result=md->typeString();
00418     if (result.find("*)")!=-1) // typedef of a function/member pointer
00419     {
00420       result+=md->argsString();
00421     }
00422     if (typedefContext) *typedefContext=md->getOuterScope();
00423   }
00424   else
00425   {
00426     //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
00427     //    qualifiedName.data(),context ? context->name().data() : "<global>");
00428   }
00429   return result;
00430   
00431 }
00432 
00433 
00437 ClassDef *getClass(const char *name)
00438 {
00439   if (name==0 || name[0]=='\0') return 0;
00440   return Doxygen::classSDict->find(name);
00441 }
00442 
00443 NamespaceDef *getResolvedNamespace(const char *name)
00444 {
00445   if (name==0 || name[0]=='\0') return 0;
00446   QCString *subst = Doxygen::namespaceAliasDict[name];
00447   if (subst)
00448   {
00449     int count=0; // recursion detection guard
00450     QCString *newSubst;
00451     while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
00452     {
00453       subst=newSubst;
00454       count++;
00455     }
00456     if (count==10)
00457     {
00458       warn_cont("Warning: possible recursive namespace alias detected for %s!\n",name);
00459     }
00460     return Doxygen::namespaceSDict->find(subst->data());
00461   }
00462   else
00463   {
00464     return Doxygen::namespaceSDict->find(name);
00465   }
00466 }
00467 
00468 static QDict<MemberDef> g_resolvedTypedefs;
00469 static QDict<Definition> g_visitedNamespaces;
00470 
00471 // forward declaration
00472 ClassDef *getResolvedClassRec(Definition *scope,
00473                               FileDef *fileScope,
00474                               const char *n,
00475                               MemberDef **pTypeDef,
00476                               QCString *pTemplSpec,
00477                               QCString *pResolvedType
00478                              );
00479 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
00480                      const QCString &explicitScopePart);
00481 
00489 ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md,
00490                             MemberDef **pMemType,QCString *pTemplSpec,
00491                             QCString *pResolvedType)
00492 {
00493   //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
00494   bool isCached = md->isTypedefValCached(); // value already cached
00495   if (isCached)
00496   {
00497     //printf("Already cached %s->%s [%s]\n",
00498     //    md->name().data(),
00499     //    md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
00500     //    md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
00501 
00502     if (pTemplSpec)    *pTemplSpec    = md->getCachedTypedefTemplSpec();
00503     if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
00504     return md->getCachedTypedefVal();
00505   }
00506   //printf("new typedef\n");
00507   QCString qname = md->qualifiedName();
00508   if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
00509 
00510   g_resolvedTypedefs.insert(qname,md); // put on the trace list
00511   
00512   QCString type = md->typeString(); // get the "value" of the typedef
00513   QCString typedefValue = type;
00514   int tl=type.length();
00515   int ip=tl-1; // remove * and & at the end
00516   while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' ')) 
00517   {
00518     ip--;
00519   }
00520   type=type.left(ip+1);
00521   type.stripPrefix("const ");  // strip leading "const"
00522   type.stripPrefix("struct "); // strip leading "struct"
00523   type.stripPrefix("union ");  // strip leading "union"
00524   int sp=0;
00525   tl=type.length(); // length may have been changed
00526   while (sp<tl && type.at(sp)==' ') sp++;
00527   MemberDef *memTypeDef = 0;
00528   ClassDef  *result = getResolvedClassRec(md->getOuterScope(),
00529                                   fileScope,type,&memTypeDef,0,pResolvedType);
00530   // if type is a typedef then return what it resolves to.
00531   if (memTypeDef && memTypeDef->isTypedef()) 
00532   {
00533     result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
00534     goto done;
00535   }
00536   else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
00537   {
00538     *pMemType = memTypeDef;
00539   }
00540 
00541   //printf("type=%s result=%p\n",type.data(),result);
00542   if (result==0)
00543   {
00544     // try unspecialized version if type is template
00545     int si=type.findRev("::");
00546     int i=type.find('<');
00547     if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
00548     {
00549       if (pTemplSpec) *pTemplSpec = type.mid(i);
00550       result = getResolvedClassRec(md->getOuterScope(),fileScope,
00551                                    type.left(i),0,0,pResolvedType);
00552       //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
00553       //    result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
00554     }
00555     else if (si!=-1) // A::B
00556     {
00557       i=type.find('<',si);
00558       if (i==-1) // Something like A<T>::B => lookup A::B
00559       {
00560         i=type.length();
00561       }
00562       else // Something like A<T>::B<S> => lookup A::B, spec=<S>
00563       {
00564         if (pTemplSpec) *pTemplSpec = type.mid(i);
00565       }
00566       result = getResolvedClassRec(md->getOuterScope(),fileScope,
00567            stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
00568            pResolvedType);
00569     }
00570 
00571     //if (result) ip=si+sp+1;
00572   }
00573 
00574 done:
00575   if (pResolvedType)
00576   {
00577     if (result)
00578     {
00579       *pResolvedType=result->qualifiedName();
00580       //printf("*pResolvedType=%s\n",pResolvedType->data());
00581       if (sp>0)    pResolvedType->prepend(typedefValue.left(sp));
00582       if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
00583     }
00584     else
00585     {
00586       *pResolvedType=typedefValue;
00587     }
00588   }
00589 
00590   // remember computed value for next time
00591   if (result && result->getDefFileName()!="<code>") 
00592     // this check is needed to prevent that temporary classes that are 
00593     // introduced while parsing code fragments are being cached here.
00594   {
00595     //printf("setting cached typedef %p in result %p\n",md,result);
00596     //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
00597     //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
00598     md->cacheTypedefVal(result,
00599         pTemplSpec ? *pTemplSpec : QCString(),
00600         pResolvedType ? *pResolvedType : QCString()
00601        );
00602   }
00603   
00604   g_resolvedTypedefs.remove(qname); // remove from the trace list
00605   
00606   return result;
00607 }
00608 
00612 static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
00613             MemberDef **pTypeDef=0)
00614 {
00615   QCString result=name;
00616   if (name.isEmpty()) return result;
00617 
00618   // lookup scope fragment in the symbol map
00619   DefinitionIntf *di = Doxygen::symbolMap->find(name);
00620   if (di==0) return result; // no matches
00621 
00622   MemberDef *bestMatch=0;
00623   if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
00624   {
00625     // search for the best match
00626     DefinitionListIterator dli(*(DefinitionList*)di);
00627     Definition *d;
00628     int minDistance=10000; // init at "infinite"
00629     for (dli.toFirst();(d=dli.current());++dli) // foreach definition
00630     {
00631       // only look at members
00632       if (d->definitionType()==Definition::TypeMember)
00633       {
00634         // that are also typedefs
00635         MemberDef *md = (MemberDef *)d;
00636         if (md->isTypedef()) // d is a typedef
00637         {
00638           // test accessibility of typedef within scope.
00639           int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
00640           if (distance!=-1 && distance<minDistance) 
00641             // definition is accessible and a better match
00642           {
00643             minDistance=distance;
00644             bestMatch = md; 
00645           }
00646         }
00647       }
00648     }
00649   }
00650   else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
00651   {
00652     Definition *d = (Definition*)di;
00653     // that are also typedefs
00654     MemberDef *md = (MemberDef *)di;
00655     if (md->isTypedef()) // d is a typedef
00656     {
00657       // test accessibility of typedef within scope.
00658       int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
00659       if (distance!=-1) // definition is accessible 
00660       {
00661         bestMatch = md; 
00662       }
00663     }
00664   }
00665   if (bestMatch) 
00666   {
00667     result = bestMatch->typeString();
00668     if (pTypeDef) *pTypeDef=bestMatch;
00669   }
00670   
00671   //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
00672   //                                  name.data(),result.data());
00673   return result;
00674 }
00675 
00676 static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
00677 {
00678   if (cl)
00679   {
00680     SDict<Definition>::Iterator cli(*cl);
00681     Definition *cd;
00682     for (cli.toFirst();(cd=cli.current());++cli)
00683     {
00684       if (cd->localName()==localName)
00685       {
00686         return cd;
00687       }
00688     }
00689   }
00690   return 0;
00691 }
00692 
00698 static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
00699 {
00700   int is,ps;
00701   int l;
00702   Definition *current=start;
00703   ps=0;
00704   //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
00705   // for each part of the explicit scope
00706   while ((is=getScopeFragment(path,ps,&l))!=-1)
00707   {
00708     // try to resolve the part if it is a typedef
00709     MemberDef *typeDef=0;
00710     QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
00711     //printf("      qualScopePart=%s\n",qualScopePart.data());
00712     if (typeDef)
00713     {
00714       ClassDef *type = newResolveTypedef(fileScope,typeDef);
00715       if (type)
00716       {
00717         //printf("Found type %s\n",type->name().data());
00718         return type;
00719       }
00720     }
00721     Definition *next = current->findInnerCompound(qualScopePart);
00722     //printf("++ Looking for %s inside %s result %s\n",
00723     //     qualScopePart.data(),
00724     //     current->name().data(),
00725     //     next?next->name().data():"<null>");
00726     if (next==0) // failed to follow the path 
00727     {
00728       //printf("==> next==0!\n");
00729       if (current->definitionType()==Definition::TypeNamespace)
00730       {
00731         next = endOfPathIsUsedClass(
00732             ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
00733       }
00734       else if (current->definitionType()==Definition::TypeFile)
00735       {
00736         next = endOfPathIsUsedClass(
00737             ((FileDef *)current)->getUsedClasses(),qualScopePart);
00738       }
00739       current = next;
00740       if (current==0) break;
00741     }
00742     else // continue to follow scope
00743     {
00744       current = next;
00745       //printf("==> current = %p\n",current);
00746     }
00747     ps=is+l;
00748   }
00749   //printf("followPath(start=%s,path=%s) result=%s\n",
00750   //    start->name().data(),path.data(),current?current->name().data():"<null>");
00751   return current; // path could be followed
00752 }
00753 
00754 bool accessibleViaUsingClass(const SDict<Definition> *cl,
00755                              FileDef *fileScope,
00756                              Definition *item,
00757                              const QCString &explicitScopePart=""
00758                             )
00759 {
00760   if (cl) // see if the class was imported via a using statement 
00761   {
00762     SDict<Definition>::Iterator cli(*cl);
00763     Definition *ucd;
00764     bool explicitScopePartEmpty = explicitScopePart.isEmpty();
00765     for (cli.toFirst();(ucd=cli.current());++cli)
00766     {
00767       //printf("Trying via used class %s\n",ucd->name().data());
00768       Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
00769       if (sc && sc==item) return TRUE; 
00770       //printf("Try via used class done\n");
00771     }
00772   }
00773   return FALSE;
00774 }
00775 
00776 bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
00777                                  FileDef *fileScope,
00778                                  Definition *item,
00779                                  const QCString &explicitScopePart="")
00780 {
00781   static QDict<void> visitedDict;
00782   if (nl) // check used namespaces for the class
00783   {
00784     NamespaceSDict::Iterator nli(*nl);
00785     NamespaceDef *und;
00786     int count=0;
00787     for (nli.toFirst();(und=nli.current());++nli,count++)
00788     {
00789       //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
00790       //    count,nl->count());
00791       Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
00792       if (sc && item->getOuterScope()==sc) 
00793       {
00794         //printf("] found it\n");
00795         return TRUE; 
00796       }
00797       QCString key=und->name();
00798       if (und->getUsedNamespaces() && visitedDict.find(key)==0)
00799       {
00800         visitedDict.insert(key,(void *)0x08);
00801 
00802         if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
00803         {
00804           //printf("] found it via recursion\n");
00805           return TRUE;
00806         }
00807 
00808         visitedDict.remove(key);
00809       }
00810       //printf("] Try via used namespace done\n");
00811     }
00812   }
00813   return FALSE;
00814 }
00815 
00816 
00817 /* Returns the "distance" (=number of levels up) from item to scope, or -1
00818  * if item in not inside scope. 
00819  */
00820 int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
00821 {
00822   //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
00823   //    scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
00824 
00825   QCString key(40);
00826   key.sprintf("%p:%p:%p",scope,fileScope,item);
00827   static QDict<void> visitedDict;
00828   if (visitedDict.find(key)) 
00829   {
00830     //printf("> already found\n");
00831     return -1; // already looked at this
00832   }
00833   visitedDict.insert(key,(void *)0x8);
00834 
00835   int result=0; // assume we found it
00836   int i;
00837 
00838   Definition *itemScope=item->getOuterScope();
00839 
00840   if ( 
00841       itemScope==scope ||                                                  // same thing
00842       (item->definitionType()==Definition::TypeMember &&                   // a member
00843        itemScope && itemScope->definitionType()==Definition::TypeClass  && // of a class
00844        scope->definitionType()==Definition::TypeClass &&                   // accessible
00845        ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item)           // from scope
00846       ) ||
00847       (item->definitionType()==Definition::TypeClass &&                    // a nested class
00848        itemScope && itemScope->definitionType()==Definition::TypeClass &&  // inside a base 
00849        scope->definitionType()==Definition::TypeClass &&                   // class of scope
00850        ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)          
00851       )
00852      ) 
00853   {
00854     //printf("> found it\n");
00855   }
00856   else if (scope==Doxygen::globalScope)
00857   {
00858     if (fileScope)
00859     {
00860       SDict<Definition> *cl = fileScope->getUsedClasses();
00861       if (accessibleViaUsingClass(cl,fileScope,item)) 
00862       {
00863         //printf("> found via used class\n");
00864         goto done;
00865       }
00866       NamespaceSDict *nl = fileScope->getUsedNamespaces();
00867       if (accessibleViaUsingNamespace(nl,fileScope,item)) 
00868       {
00869         //printf("> found via used namespace\n");
00870         goto done;
00871       }
00872     }
00873     //printf("> reached global scope\n");
00874     result=-1; // not found in path to globalScope
00875   }
00876   else // keep searching
00877   {
00878     // check if scope is a namespace, which is using other classes and namespaces
00879     if (scope->definitionType()==Definition::TypeNamespace)
00880     {
00881       NamespaceDef *nscope = (NamespaceDef*)scope;
00882       //printf("  %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
00883       SDict<Definition> *cl = nscope->getUsedClasses();
00884       if (accessibleViaUsingClass(cl,fileScope,item)) 
00885       {
00886         //printf("> found via used class\n");
00887         goto done;
00888       }
00889       NamespaceSDict *nl = nscope->getUsedNamespaces();
00890       if (accessibleViaUsingNamespace(nl,fileScope,item)) 
00891       {
00892         //printf("> found via used namespace\n");
00893         goto done;
00894       }
00895     }
00896     // repeat for the parent scope
00897     i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
00898     //printf("> result=%d\n",i);
00899     result= (i==-1) ? -1 : i+2;
00900   }
00901 done:
00902   visitedDict.remove(key);
00903   //Doxygen::lookupCache.insert(key,new int(result));
00904   return result;
00905 }
00906 
00907 
00908 /* Returns the "distance" (=number of levels up) from item to scope, or -1
00909  * if item in not in this scope. The explicitScopePart limits the search
00910  * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
00911  * Example:
00912  *
00913  * class A { public: class I {}; };
00914  * class B { public: class J {}; };
00915  *
00916  * - Looking for item=='J' inside scope=='B' will return 0.
00917  * - Looking for item=='I' inside scope=='B' will return -1 
00918  *   (as it is not found in B nor in the global scope).
00919  * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but 
00920  *   not found and then A::I is searched in the global scope, which matches and 
00921  *   thus the result is 1.
00922  */
00923 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,
00924                      Definition *item,const QCString &explicitScopePart)
00925 {
00926   if (explicitScopePart.isEmpty())
00927   {
00928     // handle degenerate case where there is no explicit scope.
00929     return isAccessibleFrom(scope,fileScope,item);
00930   }
00931 
00932   QCString key(40+explicitScopePart.length());
00933   key.sprintf("%p:%p:%p:%s",scope,fileScope,item,explicitScopePart.data());
00934   static QDict<void> visitedDict;
00935   if (visitedDict.find(key)) 
00936   {
00937     //printf("Already visited!\n");
00938     return -1; // already looked at this
00939   }
00940   visitedDict.insert(key,(void *)0x8);
00941 
00942   //printf("  <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
00943   //                                      item?item->name().data():"<none>",
00944   //                                      explicitScopePart.data());
00945   int result=0; // assume we found it
00946   Definition *newScope = followPath(scope,fileScope,explicitScopePart);
00947   if (newScope)  // explicitScope is inside scope => newScope is the result
00948   {
00949     Definition *itemScope = item->getOuterScope();
00950     //printf("    scope traversal successful %s<->%s!\n",item->getOuterScope()->name().data(),newScope->name().data());
00951     //if (newScope && newScope->definitionType()==Definition::TypeClass)
00952     //{
00953     //  ClassDef *cd = (ClassDef *)newScope;
00954     //  printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
00955     //}
00956     if (itemScope==newScope)  // exact match of scopes => distance==0
00957     {
00958       //printf("> found it\n");
00959     }
00960     else if (itemScope && newScope &&
00961              itemScope->definitionType()==Definition::TypeClass &&
00962              newScope->definitionType()==Definition::TypeClass &&
00963              ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
00964             )
00965     {
00966       // inheritance is also ok. Example: looking for B::I, where 
00967       // class A { public: class I {} };
00968       // class B : public A {}
00969       // but looking for B::I, where
00970       // class A { public: class I {} };
00971       // class B { public: class I {} };
00972       // will find A::I, so we still prefer a direct match and give this one a distance of 1
00973       result=1;
00974 
00975       //printf("scope(%s) is base class of newScope(%s)\n",
00976       //    scope->name().data(),newScope->name().data());
00977     }
00978     else 
00979     {
00980       int i=-1;
00981       if (newScope->definitionType()==Definition::TypeNamespace)
00982       {
00983         g_visitedNamespaces.insert(newScope->name(),newScope);
00984         // this part deals with the case where item is a class
00985         // A::B::C but is explicit referenced as A::C, where B is imported
00986         // in A via a using directive.
00987         //printf("newScope is a namespace: %s!\n",newScope->name().data());
00988         NamespaceDef *nscope = (NamespaceDef*)newScope;
00989         SDict<Definition> *cl = nscope->getUsedClasses();
00990         if (cl)
00991         {
00992           SDict<Definition>::Iterator cli(*cl);
00993           Definition *cd;
00994           for (cli.toFirst();(cd=cli.current());++cli)
00995           {
00996             //printf("Trying for class %s\n",cd->name().data());
00997             if (cd==item)
00998             {
00999               //printf("> class is used in this scope\n");
01000               goto done;
01001             }
01002           }
01003         }
01004         NamespaceSDict *nl = nscope->getUsedNamespaces();
01005         if (nl)
01006         {
01007           NamespaceSDict::Iterator nli(*nl);
01008           NamespaceDef *nd;
01009           for (nli.toFirst();(nd=nli.current());++nli)
01010           {
01011             if (g_visitedNamespaces.find(nd->name())==0)
01012             {
01013               //printf("Trying for namespace %s\n",nd->name().data());
01014               i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
01015               if (i!=-1)
01016               {
01017                 //printf("> found via explicit scope of used namespace\n");
01018                 goto done;
01019               }
01020             }
01021           }
01022         }
01023       }
01024       // repeat for the parent scope
01025       if (scope!=Doxygen::globalScope)
01026       {
01027         i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
01028             item,explicitScopePart);
01029       }
01030       //printf("> result=%d\n",i);
01031       result = (i==-1) ? -1 : i+2;
01032     }
01033   }
01034   else // failed to resolve explicitScope
01035   {
01036     //printf("failed to resolve: scope=%s\n",scope->name().data());
01037     if (scope->definitionType()==Definition::TypeNamespace)
01038     {
01039       NamespaceDef *nscope = (NamespaceDef*)scope;
01040       NamespaceSDict *nl = nscope->getUsedNamespaces();
01041       if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 
01042       {
01043         //printf("> found in used namespace\n");
01044         goto done;
01045       }
01046     }
01047     if (scope==Doxygen::globalScope)
01048     {
01049       if (fileScope)
01050       {
01051         NamespaceSDict *nl = fileScope->getUsedNamespaces();
01052         if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 
01053         {
01054           //printf("> found in used namespace\n");
01055           goto done;
01056         }
01057       }
01058       //printf("> not found\n");
01059       result=-1;
01060     }
01061     else // continue by looking into the parent scope
01062     {
01063       int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
01064           item,explicitScopePart);
01065       //printf("> result=%d\n",i);
01066       result= (i==-1) ? -1 : i+2;
01067     }
01068   }
01069 done:
01070   //printf("  > result=%d\n",result);
01071   visitedDict.remove(key);
01072   //Doxygen::lookupCache.insert(key,new int(result));
01073   return result;
01074 }
01075 
01076 int computeQualifiedIndex(const QCString &name)
01077 {
01078   int i = name.find('<');
01079   return name.findRev("::",i==-1 ? name.length() : i);
01080 }
01081 
01082 static void getResolvedSymbol(Definition *scope,
01083                        FileDef *fileScope,
01084                        Definition *d, 
01085                        const QCString &explicitScopePart,
01086                        int &minDistance,
01087                        ClassDef *&bestMatch,
01088                        MemberDef *&bestTypedef,
01089                        QCString &bestTemplSpec,
01090                        QCString &bestResolvedType
01091                       )
01092 {
01093   //printf("  => found type %x name=%s d=%p\n",
01094   //       d->definitionType(),d->name().data(),d);
01095 
01096   // only look at classes and members that are enums or typedefs
01097   if (d->definitionType()==Definition::TypeClass ||
01098       (d->definitionType()==Definition::TypeMember && 
01099        (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate()) 
01100       )
01101      )
01102   {
01103     g_visitedNamespaces.clear();
01104     // test accessibility of definition within scope.
01105     int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
01106     //printf("  distance %s (%p) is %d\n",d->name().data(),d,distance);
01107     if (distance!=-1) // definition is accessible
01108     {
01109       // see if we are dealing with a class or a typedef
01110       if (d->definitionType()==Definition::TypeClass) // d is a class
01111       {
01112         ClassDef *cd = (ClassDef *)d;
01113         //printf("cd=%s\n",cd->name().data());
01114         if (!cd->isTemplateArgument()) // skip classes that
01115           // are only there to 
01116           // represent a template 
01117           // argument
01118         {
01119           //printf("is not a templ arg\n");
01120           if (distance<minDistance) // found a definition that is "closer"
01121           {
01122             minDistance=distance;
01123             bestMatch = cd; 
01124             bestTypedef = 0;
01125             bestTemplSpec.resize(0);
01126             bestResolvedType = cd->qualifiedName();
01127           }
01128           else if (distance==minDistance &&
01129               fileScope && bestMatch &&
01130               fileScope->getUsedNamespaces() && 
01131               d->getOuterScope()->definitionType()==Definition::TypeNamespace && 
01132               bestMatch->getOuterScope()==Doxygen::globalScope
01133               )
01134           {
01135             // in case the distance is equal it could be that a class X
01136             // is defined in a namespace and in the global scope. When searched
01137             // in the global scope the distance is 0 in both cases. We have
01138             // to choose one of the definitions: we choose the one in the
01139             // namespace if the fileScope imports namespaces and the definition
01140             // found was in a namespace while the best match so far isn't.
01141             // Just a non-perfect heuristic but it could help in some situations
01142             // (kdecore code is an example).
01143             minDistance=distance;
01144             bestMatch = cd; 
01145             bestTypedef = 0;
01146             bestTemplSpec.resize(0);
01147             bestResolvedType = cd->qualifiedName();
01148           }
01149         }
01150         else
01151         {
01152           //printf("  is a template argument!\n");
01153         }
01154       }
01155       else if (d->definitionType()==Definition::TypeMember)
01156       {
01157         MemberDef *md = (MemberDef *)d;
01158         //printf("  member isTypedef()=%d\n",md->isTypedef());
01159         if (md->isTypedef()) // d is a typedef
01160         {
01161           QCString args=md->argsString();
01162           if (args.isEmpty()) // do not expand "typedef t a[4];"
01163           {
01164             //printf("    found typedef!\n");
01165 
01166             // we found a symbol at this distance, but if it didn't
01167             // resolve to a class, we still have to make sure that
01168             // something at a greater distance does not match, since
01169             // that symbol is hidden by this one.
01170             if (distance<minDistance)
01171             {
01172               QCString spec;
01173               QCString type;
01174               minDistance=distance;
01175               MemberDef *enumType = 0;
01176               ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type);
01177               if (cd)  // type resolves to a class
01178               {
01179                 //printf("      bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
01180                 bestMatch = cd;
01181                 bestTypedef = md;
01182                 bestTemplSpec = spec;
01183                 bestResolvedType = type;
01184               }
01185               else if (enumType) // type resolves to a enum
01186               {
01187                 //printf("      is enum\n");
01188                 bestMatch = 0;
01189                 bestTypedef = enumType;
01190                 bestTemplSpec = "";
01191                 bestResolvedType = enumType->qualifiedName();
01192               }
01193               else if (md->isReference()) // external reference
01194               {
01195                 bestMatch = 0;
01196                 bestTypedef = md;
01197                 bestTemplSpec = spec;
01198                 bestResolvedType = type;
01199               }
01200               else
01201               {
01202                 //printf("      no match\n");
01203               }
01204             }
01205             else
01206             {
01207               //printf("      not the best match %d min=%d\n",distance,minDistance);
01208             }
01209           }
01210           else
01211           {
01212             //printf("     not a simple typedef\n")
01213           }
01214         }
01215         else if (md->isEnumerate())
01216         {
01217           if (distance<minDistance)
01218           {
01219             minDistance=distance;
01220             bestMatch = 0;
01221             bestTypedef = md;
01222             bestTemplSpec = "";
01223             bestResolvedType = md->qualifiedName();
01224           }
01225         }
01226       }
01227     } // if definition accessible
01228     else
01229     {
01230       //printf("  Not accessible!\n");
01231     }
01232   } // if definition is a class or member
01233   //printf("  bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
01234 }
01235 
01236 /* Find the fully qualified class name refered to by the input class
01237  * or typedef name against the input scope.
01238  * Loops through scope and each of its parent scopes looking for a
01239  * match against the input name. Can recursively call itself when 
01240  * resolving typedefs.
01241  */
01242 ClassDef *getResolvedClassRec(Definition *scope,
01243     FileDef *fileScope,
01244     const char *n,
01245     MemberDef **pTypeDef,
01246     QCString *pTemplSpec,
01247     QCString *pResolvedType
01248     )
01249 {
01250   //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
01251   QCString name=n;
01252   QCString explicitScopePart;
01253 
01254   int qualifierIndex = computeQualifiedIndex(name);
01255   //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
01256   if (qualifierIndex!=-1) // qualified name
01257   {
01258     // split off the explicit scope part
01259     explicitScopePart=name.left(qualifierIndex);
01260     // todo: improve namespace alias substitution
01261     replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
01262     name=name.mid(qualifierIndex+2);
01263   }
01264 
01265   if (name.isEmpty()) 
01266   {
01267     //printf("] empty name\n");
01268     return 0; // empty name
01269   }
01270 
01271   DefinitionIntf *di = Doxygen::symbolMap->find(name);
01272   //printf("Looking for symbol %s result=%p\n",name.data(),di);
01273   if (di==0) 
01274   {
01275     return 0;
01276   }
01277 
01278   bool hasUsingStatements = 
01279     (fileScope && ((fileScope->getUsedNamespaces() && 
01280                     fileScope->getUsedNamespaces()->count()>0) ||
01281                    (fileScope->getUsedClasses() && 
01282                     fileScope->getUsedClasses()->count()>0)) 
01283     );
01284   //printf("hasUsingStatements=%d\n",hasUsingStatements);
01285   // Since it is often the case that the same name is searched in the same
01286   // scope over an over again (especially for the linked source code generation)
01287   // we use a cache to collect previous results. This is possible since the
01288   // result of a lookup is deterministic. As the key we use the concatenated
01289   // scope, the name to search for and the explicit scope prefix. The speedup
01290   // achieved by this simple cache can be enormous.
01291   int scopeNameLen = scope->name().length()+1;
01292   int nameLen = name.length()+1;
01293   int explicitPartLen = explicitScopePart.length();
01294   int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
01295 
01296   // below is a more efficient coding of
01297   // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
01298   QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
01299   char *p=key.data();
01300   qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
01301   p+=scopeNameLen;
01302   qstrcpy(p,name); *(p+nameLen-1)='+';
01303   p+=nameLen;
01304   qstrcpy(p,explicitScopePart);
01305   p+=explicitPartLen;
01306 
01307   // if a file scope is given and it contains using statements we should
01308   // also use the file part in the key (as a class name can be in
01309   // two different namespaces and a using statement in a file can select 
01310   // one of them).
01311   if (hasUsingStatements)
01312   {
01313     // below is a more efficient coding of
01314     // key+="+"+fileScope->name();
01315     *p++='+';
01316     qstrcpy(p,fileScope->absFilePath());
01317     p+=fileScopeLen-1;
01318   }
01319   *p='\0';
01320 
01321   LookupInfo *pval=Doxygen::lookupCache.find(key);
01322   //printf("Searching for %s result=%p\n",key.data(),pval);
01323   if (pval)
01324   {
01325     //printf("LookupInfo %p %p '%s' %p\n", 
01326     //    pval->classDef, pval->typeDef, pval->templSpec.data(), 
01327     //    pval->resolvedType.data()); 
01328     if (pTemplSpec)    *pTemplSpec=pval->templSpec;
01329     if (pTypeDef)      *pTypeDef=pval->typeDef;
01330     if (pResolvedType) *pResolvedType=pval->resolvedType;
01331     //printf("] cachedMatch=%s\n",
01332     //    pval->classDef?pval->classDef->name().data():"<none>");
01333     //if (pTemplSpec) 
01334     //  printf("templSpec=%s\n",pTemplSpec->data());
01335     return pval->classDef; 
01336   }
01337   else // not found yet; we already add a 0 to avoid the possibility of 
01338     // endless recursion.
01339   {
01340     Doxygen::lookupCache.insert(key,new LookupInfo);
01341   }
01342 
01343   ClassDef *bestMatch=0;
01344   MemberDef *bestTypedef=0;
01345   QCString bestTemplSpec;
01346   QCString bestResolvedType;
01347   int minDistance=10000; // init at "infinite"
01348 
01349   if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
01350   {
01351     //printf("  name is not unique\n");
01352     DefinitionListIterator dli(*(DefinitionList*)di);
01353     Definition *d;
01354     int count=0;
01355     for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
01356     {
01357       getResolvedSymbol(scope,fileScope,d,explicitScopePart,
01358                         minDistance,bestMatch,bestTypedef,bestTemplSpec,
01359                         bestResolvedType);
01360     }
01361   }
01362   else // unique name
01363   {
01364     //printf("  name is unique\n");
01365     Definition *d = (Definition *)di;
01366     getResolvedSymbol(scope,fileScope,d,explicitScopePart,
01367                       minDistance,bestMatch,bestTypedef,bestTemplSpec,
01368                       bestResolvedType);
01369   }
01370 
01371   if (pTypeDef) 
01372   {
01373     *pTypeDef = bestTypedef;
01374   }
01375   if (pTemplSpec)
01376   {
01377     *pTemplSpec = bestTemplSpec;
01378   }
01379   if (pResolvedType)
01380   {
01381     *pResolvedType = bestResolvedType;
01382   }
01383   //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
01384   //    bestMatch,bestResolvedType.data());
01385 
01386   pval=Doxygen::lookupCache.find(key);
01387   if (pval)
01388   {
01389     pval->classDef     = bestMatch;
01390     pval->typeDef      = bestTypedef;
01391     pval->templSpec    = bestTemplSpec;
01392     pval->resolvedType = bestResolvedType;
01393   }
01394   else
01395   {
01396     Doxygen::lookupCache.insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
01397   }
01398   //printf("] bestMatch=%s distance=%d\n",
01399   //    bestMatch?bestMatch->name().data():"<none>",minDistance);
01400   //if (pTemplSpec) 
01401   //  printf("templSpec=%s\n",pTemplSpec->data());
01402   return bestMatch;
01403 }
01404 
01405 /* Find the fully qualified class name refered to by the input class
01406  * or typedef name against the input scope.
01407  * Loops through scope and each of its parent scopes looking for a
01408  * match against the input name. 
01409  */
01410 ClassDef *getResolvedClass(Definition *scope,
01411     FileDef *fileScope,
01412     const char *n,
01413     MemberDef **pTypeDef,
01414     QCString *pTemplSpec,
01415     bool mayBeUnlinkable,
01416     bool mayBeHidden,
01417     QCString *pResolvedType
01418     )
01419 {
01420   g_resolvedTypedefs.clear();
01421   if (scope==0 ||
01422       (scope->definitionType()!=Definition::TypeClass && 
01423        scope->definitionType()!=Definition::TypeNamespace
01424       ) ||
01425       (fileScope && fileScope->isJava() && QCString(n).find("::")!=-1)
01426      )
01427   {
01428     scope=Doxygen::globalScope;
01429   }
01430   //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
01431   //    scope?scope->name().data():"<global>",
01432   //    fileScope?fileScope->name().data():"<none>",
01433   //    n,
01434   //    mayBeUnlinkable
01435   //   );
01436   ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
01437   if (!mayBeUnlinkable && result && !result->isLinkable()) 
01438   {
01439     if (!mayBeHidden || !result->isHidden())
01440     {
01441       result=0; // don't link to artifical/hidden classes unless explicitly allowed
01442     }
01443   }
01444   //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
01445   //                                  n,result?result->name().data():"<none>");
01446   return result;
01447 }
01448 
01449 //-------------------------------------------------------------------------
01450 //-------------------------------------------------------------------------
01451 //-------------------------------------------------------------------------
01452 //-------------------------------------------------------------------------
01453 
01454 static bool findOperator(const QCString &s,int i)
01455 {
01456   int b = s.findRev("operator",i);
01457   if (b==-1) return FALSE; // not found
01458   b+=8;
01459   while (b<i) // check if there are only spaces inbetween 
01460     // the operator and the >
01461   {
01462     if (!isspace((uchar)s.at(b))) return FALSE;
01463     b++;
01464   }
01465   return TRUE;
01466 }
01467 
01468 static bool findOperator2(const QCString &s,int i)
01469 {
01470   int b = s.findRev("operator",i);
01471   if (b==-1) return FALSE; // not found
01472   b+=8;
01473   while (b<i) // check if there are only non-ascii
01474               // characters in front of the operator
01475   {
01476     if (isId((uchar)s.at(b))) return FALSE;
01477     b++;
01478   }
01479   return TRUE;
01480 }
01481 
01482 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
01483 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
01484 
01485 QCString removeRedundantWhiteSpace(const QCString &s)
01486 {
01487   static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT");
01488   if (s.isEmpty()) return s;
01489   QCString result;
01490   uint i;
01491   uint l=s.length();
01492   uint csp=0;
01493   uint vsp=0;
01494   for (i=0;i<l;i++)
01495   {
01496 nextChar:
01497     char c=s.at(i);
01498 
01499     // search for "const"
01500     if (csp<6 && c==constScope[csp] && // character matches substring "const"
01501          (csp>0 ||                     // if it is the first character 
01502           i==0  ||                     // the previous may not be a digit
01503           !isId(s.at(i-1))
01504          )
01505        )
01506       csp++; 
01507     else // reset counter
01508       csp=0;
01509 
01510     // search for "virtual"
01511     if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
01512          (vsp>0 ||                       // if it is the first character
01513           i==0  ||                       // the previous may not be a digit 
01514           !isId(s.at(i-1))
01515          )
01516        )
01517       vsp++;
01518     else // reset counter
01519       vsp=0;
01520 
01521     if (c=='"') // quoted string
01522     {
01523       i++;
01524       result+=c;
01525       while (i<l)
01526       {
01527         char cc=s.at(i);
01528         result+=cc;
01529         if (cc=='\\') // escaped character
01530         { result+=s.at(i+1); i+=2; }
01531         else if (cc=='"') // end of string
01532         { i++; goto nextChar; }
01533         else // any other character
01534         { i++; }
01535       }
01536     }
01537     else if (i<l-2 && c=='<' &&  // current char is a <
01538         (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space
01539         (i<8 || !findOperator(s,i)) // string in front is not "operator"
01540         )
01541     {
01542       result+="< "; // insert extra space for layouting (nested) templates
01543     }
01544     else if (i>0 && c=='>' && // current char is a >
01545         (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
01546         (i<8 || !findOperator(s,i)) // string in front is not "operator"
01547         )
01548     {
01549       result+=" >"; // insert extra space for layouting (nested) templates
01550     }
01551     else if (i>0 && c==',' && !isspace((uchar)s.at(i-1))
01552         && ((i<l-1 && isId(s.at(i+1)))
01553           || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2)))  // for PHP
01554           || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3)))))  // for PHP
01555     {
01556       result+=", ";
01557     }
01558     else if (i>0 && 
01559         ((isId(s.at(i)) && s.at(i-1)==')') || 
01560          (s.at(i)=='\''  && s.at(i-1)==' ')
01561         )
01562         )
01563     {
01564       result+=' ';
01565       result+=s.at(i);
01566     }
01567     else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ &&
01568              !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 
01569                s.at(i+1)==')' || 
01570                s.at(i+1)==',' || 
01571                s.at(i+1)=='\0'
01572               )
01573             ) 
01574       // prevent const ::A from being converted to const::A
01575     {
01576       result+="t ";
01577       if (s.at(i+1)==' ') i++;
01578       csp=0;
01579     }
01580     else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/) 
01581       // replace const::A by const ::A
01582     {
01583       result+=" :";
01584       csp=0;
01585     }
01586     else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ &&
01587              !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 
01588                s.at(i+1)==')' || 
01589                s.at(i+1)==',' || 
01590                s.at(i+1)=='\0'
01591               )
01592             ) 
01593       // prevent virtual ::A from being converted to virtual::A
01594     {
01595       result+="l ";
01596       if (s.at(i+1)==' ') i++;
01597       vsp=0;
01598     }
01599     else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/) 
01600       // replace virtual::A by virtual ::A
01601     {
01602       result+=" :";
01603       vsp=0;
01604     }
01605     else if (!isspace((uchar)c) || // not a space
01606         ( i>0 && i<l-1 &&          // internal character
01607           (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']')
01608           && (isId(s.at(i+1)) || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2)))
01609             || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))
01610         ) 
01611         )
01612     {
01613       if (c=='*' || c=='&' || c=='@' || c=='$')
01614       {  
01615         uint rl=result.length();
01616         if ((rl>0 && (isId(result.at(rl-1)) || result.at(rl-1)=='>')) &&
01617             (c!='*' || !findOperator2(s,i)) // avoid splitting operator* and operator->*
01618            ) 
01619         {
01620           result+=' ';
01621         }
01622       }
01623       result+=c;
01624       if (cliSupport && (c=='^' || c=='%') && i>1 && isId(s.at(i-1))) result+=' '; // C++/CLI: Type^ name and Type% name
01625     }
01626   }
01627   //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data());
01628   return result;
01629 }  
01630 
01631 bool rightScopeMatch(const QCString &scope, const QCString &name)
01632 {
01633   return (name==scope || // equal 
01634       (scope.right(name.length())==name && // substring 
01635        scope.at(scope.length()-name.length()-1)==':' // scope
01636       ) 
01637       );
01638 }
01639 
01640 bool leftScopeMatch(const QCString &scope, const QCString &name)
01641 {
01642   return (name==scope || // equal 
01643       (scope.left(name.length())==name && // substring 
01644        scope.at(name.length())==':' // scope
01645       ) 
01646       );
01647 }
01648 
01649 
01650 void linkifyText(const TextGeneratorIntf &out,Definition *scope,
01651     FileDef *fileScope,const char *,
01652     const char *text, bool autoBreak,bool external,
01653     bool keepSpaces)
01654 {
01655   //printf("`%s'\n",text);
01656   static QRegExp regExp("[a-z_A-Z][~!a-z_A-Z0-9.:]*");
01657   static QRegExp regExpSplit("(?!:),");
01658   QCString txtStr=text;
01659   int strLen = txtStr.length();
01660   //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d\n",
01661   //    scope?scope->name().data():"<none>",
01662   //    fileScope?fileScope->name().data():"<none>",
01663   //    txtStr.data(),strLen);
01664   int matchLen;
01665   int index=0;
01666   int newIndex;
01667   int skipIndex=0;
01668   int floatingIndex=0;
01669   if (strLen==0) return;
01670   // read a word from the text string
01671   while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 && 
01672       (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
01673       )
01674   {
01675     // add non-word part to the result
01676     floatingIndex+=newIndex-skipIndex+matchLen;
01677     bool insideString=FALSE; 
01678     int i;
01679     for (i=index;i<newIndex;i++) 
01680     { 
01681       if (txtStr.at(i)=='"') insideString=!insideString; 
01682     }
01683 
01684     //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
01685     if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
01686     {
01687       QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
01688       int splitLength = splitText.length();
01689       int offset=1;
01690       i=splitText.find(regExpSplit,0);
01691       if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
01692       if (i==-1) i=splitText.find('>');
01693       if (i==-1) i=splitText.find(' ');
01694       //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
01695       if (i!=-1) // add a link-break at i in case of Html output
01696       {
01697         out.writeString(splitText.left(i+offset),keepSpaces);
01698         out.writeBreak();
01699         out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
01700         floatingIndex=splitLength-i-offset+matchLen;
01701       } 
01702       else
01703       {
01704         out.writeString(splitText,keepSpaces); 
01705       }
01706     }
01707     else
01708     {
01709       //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex)); 
01710       out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces); 
01711     }
01712     // get word from string
01713     QCString word=txtStr.mid(newIndex,matchLen);
01714     QCString matchWord = substitute(word,".","::");
01715     //printf("linkifyText word=%s matchWord=%s scope=%s\n",
01716     //    word.data(),matchWord.data(),scope?scope->name().data():"<none>");
01717     bool found=FALSE;
01718     if (!insideString)
01719     {
01720       ClassDef     *cd=0;
01721       FileDef      *fd=0;
01722       MemberDef    *md=0;
01723       NamespaceDef *nd=0;
01724       GroupDef     *gd=0;
01725       //printf("** Match word '%s'\n",matchWord.data());
01726 
01727       MemberDef *typeDef=0;
01728       if ((cd=getResolvedClass(scope,fileScope,matchWord,&typeDef))) 
01729       {
01730         //printf("Found class %s\n",cd->name().data());
01731         // add link to the result
01732         if (external ? cd->isLinkable() : cd->isLinkableInProject())
01733         {
01734           out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word);
01735           found=TRUE;
01736         }
01737       }
01738       else if (typeDef)
01739       {
01740         //printf("Found typedef %s\n",typeDef->name().data());
01741         if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
01742         {
01743           out.writeLink(typeDef->getReference(),
01744               typeDef->getOutputFileBase(),
01745               typeDef->anchor(),
01746               word);
01747           found=TRUE;
01748         }
01749       }
01750       else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
01751       {
01752         // add link to the result
01753         if (external ? cd->isLinkable() : cd->isLinkableInProject())
01754         {
01755           out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word);
01756           found=TRUE;
01757         }
01758       }
01759       else
01760       {
01761         //printf("   -> nothing\n");
01762       }
01763 
01764       QCString scopeName;
01765       if (scope && 
01766           (scope->definitionType()==Definition::TypeClass || 
01767            scope->definitionType()==Definition::TypeNamespace
01768           ) 
01769          )
01770       {
01771         scopeName=scope->name();
01772       }
01773       //printf("ScopeName=%s\n",scopeName.data());
01774       //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data()); 
01775       if (!found && 
01776           getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) && 
01777           (md->isTypedef() || md->isEnumerate() || 
01778            md->isReference() || md->isVariable()
01779           ) && 
01780           (external ? md->isLinkable() : md->isLinkableInProject()) 
01781          )
01782       {
01783         //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
01784         //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
01785         //                       md->anchor(),word);
01786         out.writeLink(md->getReference(),md->getOutputFileBase(),
01787             md->anchor(),word);
01788         found=TRUE;
01789       }
01790     }
01791 
01792     if (!found) // add word to the result
01793     {
01794       out.writeString(word,keepSpaces);
01795     }
01796     // set next start point in the string
01797     //printf("index=%d/%d\n",index,txtStr.length());
01798     skipIndex=index=newIndex+matchLen;
01799   }
01800   // add last part of the string to the result.
01801   //ol.docify(txtStr.right(txtStr.length()-skipIndex));
01802   out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
01803 }
01804 
01805 
01806 void writeExample(OutputList &ol,ExampleSDict *ed)
01807 {
01808   QCString exampleLine=theTranslator->trWriteList(ed->count());
01809 
01810   //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
01811   //bool manEnabled   = ol.isEnabled(OutputGenerator::Man);
01812   //bool htmlEnabled  = ol.isEnabled(OutputGenerator::Html);
01813   QRegExp marker("@[0-9]+");
01814   int index=0,newIndex,matchLen;
01815   // now replace all markers in inheritLine with links to the classes
01816   while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
01817   {
01818     bool ok;
01819     ol.parseText(exampleLine.mid(index,newIndex-index));
01820     uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
01821     Example *e=ed->at(entryIndex);
01822     if (ok && e) 
01823     {
01824       ol.pushGeneratorState();
01825       //if (latexEnabled) ol.disable(OutputGenerator::Latex);
01826       ol.disable(OutputGenerator::Latex);
01827       ol.disable(OutputGenerator::RTF);
01828       // link for Html / man
01829       ol.writeObjectLink(0,e->file,e->anchor,e->name);
01830       ol.popGeneratorState();
01831 
01832       ol.pushGeneratorState();
01833       //if (latexEnabled) ol.enable(OutputGenerator::Latex);
01834       ol.disable(OutputGenerator::Man);
01835       ol.disable(OutputGenerator::Html);
01836       // link for Latex / pdf with anchor because the sources
01837       // are not hyperlinked (not possible with a verbatim environment).
01838       ol.writeObjectLink(0,e->file,0,e->name);
01839       //if (manEnabled) ol.enable(OutputGenerator::Man);
01840       //if (htmlEnabled) ol.enable(OutputGenerator::Html);
01841       ol.popGeneratorState();
01842     }
01843     index=newIndex+matchLen;
01844   } 
01845   ol.parseText(exampleLine.right(exampleLine.length()-index));
01846   ol.writeString(".");
01847 }
01848 
01849 
01850 QCString argListToString(ArgumentList *al,bool useCanonicalType)
01851 {
01852   QCString result;
01853   if (al==0) return result;
01854   Argument *a=al->first();
01855   result+="(";
01856   while (a)
01857   {
01858     QCString type1 = useCanonicalType && !a->canType.isEmpty() ? 
01859       a->canType : a->type;
01860     QCString type2;
01861     int i=type1.find(")("); // hack to deal with function pointers
01862     if (i!=-1)
01863     {
01864       type2=type1.mid(i);
01865       type1=type1.left(i);
01866     }
01867     if (!a->attrib.isEmpty())
01868     {
01869       result+=a->attrib+" ";
01870     }
01871     if (!a->name.isEmpty() || !a->array.isEmpty())
01872     {
01873       result+= type1+" "+a->name+type2+a->array;
01874     }
01875     else
01876     {
01877       result+= type1+type2;
01878     }
01879     if (!a->defval.isEmpty())
01880     {
01881       result+="="+a->defval;
01882     }
01883     a = al->next();
01884     if (a) result+=", "; 
01885   }
01886   result+=")";
01887   if (al->constSpecifier) result+=" const";
01888   if (al->volatileSpecifier) result+=" volatile";
01889   return removeRedundantWhiteSpace(result);
01890 }
01891 
01892 QCString tempArgListToString(ArgumentList *al)
01893 {
01894   QCString result;
01895   if (al==0) return result;
01896   result="<";
01897   Argument *a=al->first();
01898   while (a)
01899   {
01900     if (!a->name.isEmpty()) // add template argument name
01901     {
01902       result+=a->name;
01903     }
01904     else // extract name from type
01905     {
01906       int i=a->type.length()-1;
01907       while (i>=0 && isId(a->type.at(i))) i--;
01908       if (i>0)
01909       {
01910         result+=a->type.right(a->type.length()-i-1);
01911       }
01912     }
01913     a=al->next();
01914     if (a) result+=", ";
01915   }
01916   result+=">";
01917   return removeRedundantWhiteSpace(result);
01918 }
01919 
01920 
01921 // compute the HTML anchors for a list of members
01922 void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId)
01923 {
01924   int count=0;
01925   if (ml==0) return;
01926   MemberListIterator mli(*ml);
01927   MemberDef *md;
01928   for (;(md=mli.current());++mli)
01929   {
01930     if (!md->isReference())
01931     {
01932       QCString anchor;
01933       if (groupId==-1)
01934         anchor.sprintf("%c%d",id,count++);
01935       else
01936         anchor.sprintf("%c%d_%d",id,groupId,count++);
01937       if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
01938       md->setAnchor(anchor);
01939       //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
01940       //    md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
01941     }
01942   }
01943 }
01944 
01945 //----------------------------------------------------------------------------
01946 
01952 int filterCRLF(char *buf,int len)
01953 {
01954   int src = 0;    // source index
01955   int dest = 0;   // destination index
01956   char c;         // current character
01957 
01958   while (src<len)
01959   {
01960     c = buf[src++];            // Remember the processed character.
01961     if (c == '\r')             // CR to be solved (MAC, DOS)
01962     {
01963       c = '\n';                // each CR to LF
01964       if (src<len && buf[src] == '\n')
01965         ++src;                 // skip LF just after CR (DOS) 
01966     }
01967     else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
01968     {
01969       c = ' ';                 // turn into a space
01970     }
01971     buf[dest++] = c;           // copy the (modified) character to dest
01972   }
01973   return dest;                 // length of the valid part of the buf
01974 }
01975 
01976 
01980 QCString getFileFilter(const char* name)
01981 {
01982   // sanity check
01983   if (name==0) return "";
01984 
01985   // first look for filter pattern list
01986   QStrList& filterList = Config_getList("FILTER_PATTERNS");
01987 
01988   if (filterList.isEmpty()) 
01989   {
01990     // use INPUT_FILTER instead (For all files)
01991     return Config_getString("INPUT_FILTER");
01992   }
01993 
01994   // compare the file name to the filter pattern list
01995   QStrListIterator sli(filterList);
01996   char* filterStr;
01997   for (sli.toFirst(); (filterStr = sli.current()); ++sli)
01998   {
01999     QCString fs = filterStr;
02000     int i_equals=fs.find('=');
02001 
02002     if (i_equals!=-1)
02003     {
02004       QCString filterPattern = fs.left(i_equals);
02005       QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE); 
02006       if (fpat.match(name)!=-1) 
02007       {
02008         // found a match!
02009         QCString filterName = fs.mid(i_equals+1);
02010         if (filterName.find(' ')!=-1)
02011         { // add quotes if the name has spaces
02012           filterName="\""+filterName+"\"";
02013         }
02014         return filterName;
02015       }
02016     }
02017   }
02018 
02019   // no match
02020   return "";
02021 }
02022 
02023 QCString recodeString(const QCString &str,const char *fromEncoding,const char *toEncoding)
02024 {
02025   QCString inputEncoding  = fromEncoding;
02026   QCString outputEncoding = toEncoding;
02027   if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || 
02028       inputEncoding==outputEncoding) return str;
02029   int inputSize=str.length();
02030   int outputSize=inputSize*4+1;
02031   QCString output(outputSize);
02032   void *cd = portable_iconv_open(outputEncoding,inputEncoding);
02033   if (cd==(void *)(-1))
02034   {
02035     err("Error: unsupported character conversion: '%s'->'%s'\n",
02036         inputEncoding.data(),outputEncoding.data());
02037     exit(1);
02038   }
02039   size_t iLeft=inputSize;
02040   size_t oLeft=outputSize;
02041   const char *inputPtr  = str.data();
02042   char       *outputPtr = output.data();
02043   if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
02044   {
02045     outputSize-=oLeft;
02046     output.resize(outputSize+1);
02047     output.at(outputSize)='\0';
02048     //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
02049   }
02050   else
02051   {
02052     err("Error: failed to translate characters from %s to %s: %s\n",
02053         inputEncoding.data(),outputEncoding.data(),strerror(errno));
02054     exit(1);
02055   }
02056   portable_iconv_close(cd);
02057   return output;
02058 }
02059 
02060 
02061 QCString transcodeCharacterStringToUTF8(const QCString &input)
02062 {
02063   static QCString inputEncoding = Config_getString("INPUT_ENCODING");
02064   const char *outputEncoding = "UTF-8";
02065   if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
02066   int inputSize=input.length();
02067   int outputSize=inputSize*4+1;
02068   QCString output(outputSize);
02069   void *cd = portable_iconv_open(outputEncoding,inputEncoding);
02070   if (cd==(void *)(-1)) 
02071   {
02072     err("Error: unsupported character conversion: '%s'->'%s'\n",
02073         inputEncoding.data(),outputEncoding);
02074     exit(1);
02075   }
02076   size_t iLeft=inputSize;
02077   size_t oLeft=outputSize;
02078   const char *inputPtr = input.data();
02079   char *outputPtr = output.data();
02080   if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
02081   {
02082     outputSize-=oLeft;
02083     output.resize(outputSize+1);
02084     output.at(outputSize)='\0';
02085     //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
02086   }
02087   else
02088   {
02089     err("Error: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
02090         inputEncoding.data(),outputEncoding);
02091     exit(1);
02092   }
02093   portable_iconv_close(cd);
02094   return output;
02095 }
02096 
02101 QCString fileToString(const char *name,bool filter)
02102 {
02103   if (name==0 || name[0]==0) return 0;
02104   QFile f;
02105 
02106   bool fileOpened=FALSE;
02107   if (name[0]=='-' && name[1]==0) // read from stdin
02108   {
02109     fileOpened=f.open(IO_ReadOnly,stdin);
02110     if (fileOpened)
02111     {
02112       const int bSize=4096;
02113       QCString contents(bSize);
02114       int totalSize=0;
02115       int size;
02116       while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
02117       {
02118         totalSize+=bSize;
02119         contents.resize(totalSize+bSize); 
02120       }
02121       totalSize = filterCRLF(contents.data(),totalSize+size)+2;
02122       contents.resize(totalSize);
02123       contents.at(totalSize-2)='\n'; // to help the scanner
02124       contents.at(totalSize-1)='\0';
02125       return contents;
02126     }
02127   }
02128   else // read from file
02129   {
02130     QFileInfo fi(name);
02131     if (!fi.exists() || !fi.isFile())
02132     {
02133       err("Error: file `%s' not found\n",name);
02134       return "";
02135     }
02136     QCString filterName = getFileFilter(name);
02137     if (filterName.isEmpty() || !filter)
02138     {
02139       f.setName(name);
02140       fileOpened=f.open(IO_ReadOnly);
02141       if (fileOpened)
02142       {
02143         int fsize=f.size();
02144         QCString contents(fsize+2);
02145         f.readBlock(contents.data(),fsize);
02146         if (fsize==0 || contents[fsize-1]=='\n') 
02147           contents[fsize]='\0';
02148         else
02149           contents[fsize]='\n'; // to help the scanner
02150         contents[fsize+1]='\0';
02151         f.close();
02152         int newSize = filterCRLF(contents.data(),fsize+2);
02153         if (newSize!=fsize+2) 
02154         {
02155           contents.resize(newSize);
02156         }
02157         return transcodeCharacterStringToUTF8(contents);
02158       }
02159     }
02160     else // filter the input
02161     {
02162       QCString cmd=filterName+" \""+name+"\"";
02163       Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
02164       FILE *f=portable_popen(cmd,"r");
02165       if (!f)
02166       {
02167         err("Error: could not execute filter %s\n",filterName.data());
02168         return "";
02169       }
02170       const int bSize=4096;
02171       QCString contents(bSize);
02172       int totalSize=0;
02173       int size;
02174       while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize)
02175       {
02176         totalSize+=bSize;
02177         contents.resize(totalSize+bSize); 
02178       }
02179       totalSize = filterCRLF(contents.data(),totalSize+size)+2;
02180       contents.resize(totalSize);
02181       contents.at(totalSize-2)='\n'; // to help the scanner
02182       contents.at(totalSize-1)='\0';
02183       portable_pclose(f);
02184       return transcodeCharacterStringToUTF8(contents);
02185     }
02186   }
02187   if (!fileOpened)  
02188   {
02189     err("Error: cannot open file `%s' for reading\n",name);
02190   }
02191   return "";
02192 }
02193 
02194 QCString dateToString(bool includeTime)
02195 {
02196   if (includeTime)
02197   {
02198     return convertToQCString(QDateTime::currentDateTime().toString());
02199   }
02200   else
02201   {
02202     const QDate &d=QDate::currentDate();
02203     QCString result;
02204     result.sprintf("%d %s %d",
02205         d.day(),
02206         convertToQCString(d.monthName(d.month())).data(),
02207         d.year());
02208     return result;
02209   }
02210   //QDate date=dt.date();
02211   //QTime time=dt.time();
02212   //QCString dtString;
02213   //dtString.sprintf("%02d:%02d, %04d/%02d/%02d",
02214   //    time.hour(),time.minute(),date.year(),date.month(),date.day());
02215   //return dtString;
02216 }
02217 
02218 QCString yearToString()
02219 {
02220   const QDate &d=QDate::currentDate();
02221   QCString result;
02222   result.sprintf("%d", d.year());
02223   return result;
02224 }
02225 
02226 //----------------------------------------------------------------------
02227 // recursive function that returns the number of branches in the 
02228 // inheritance tree that the base class `bcd' is below the class `cd'
02229 
02230 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
02231 {
02232   if (bcd->categoryOf()) // use class that is being extended in case of 
02233     // an Objective-C category
02234   {
02235     bcd=bcd->categoryOf();
02236   }
02237   if (cd==bcd) return level; 
02238   if (level==256)
02239   {
02240     err("Error: Internal inconsistency: found class %s seem to have a recursive "
02241         "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
02242     return -1;
02243   }
02244   int m=maxInheritanceDepth; 
02245   if (cd->baseClasses())
02246   {
02247     BaseClassListIterator bcli(*cd->baseClasses());
02248     for ( ; bcli.current() ; ++bcli)
02249     {
02250       //printf("class %s base class %s\n",cd->name().data(),bcli.current()->classDef->name().data());
02251       int mc=minClassDistance(bcli.current()->classDef,bcd,level+1);
02252       if (mc<m) m=mc;
02253       if (m<0) break;
02254     }
02255   }
02256   return m;
02257 }
02258 
02259 //static void printArgList(ArgumentList *al)
02260 //{
02261 //  if (al==0) return;
02262 //  ArgumentListIterator ali(*al);
02263 //  Argument *a;
02264 //  printf("(");
02265 //  for (;(a=ali.current());++ali)
02266 //  {
02267 //    printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():""); 
02268 //  }
02269 //  printf(")");
02270 //}
02271 
02272 #ifndef NEWMATCH
02273 // strip any template specifiers that follow className in string s
02274 static QCString trimTemplateSpecifiers(
02275     const QCString &namespaceName,
02276     const QCString &className,
02277     const QCString &s
02278     )
02279 {
02280   //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
02281   QCString scopeName=mergeScopes(namespaceName,className);
02282   ClassDef *cd=getClass(scopeName);
02283   if (cd==0) return s; // should not happen, but guard anyway.
02284 
02285   QCString result=s;
02286 
02287   int i=className.length()-1;
02288   if (i>=0 && className.at(i)=='>') // template specialization
02289   {
02290     // replace unspecialized occurrences in s, with their specialized versions.
02291     int count=1;
02292     int cl=i+1;
02293     while (i>=0)
02294     {
02295       char c=className.at(i);
02296       if (c=='>') count++,i--;
02297       else if (c=='<') { count--; if (count==0) break; }
02298       else i--;
02299     }
02300     QCString unspecClassName=className.left(i);
02301     int l=i;
02302     int p=0;
02303     while ((i=result.find(unspecClassName,p))!=-1)
02304     {
02305       if (result.at(i+l)!='<') // unspecialized version
02306       {
02307         result=result.left(i)+className+result.right(result.length()-i-l);
02308         l=cl;
02309       }
02310       p=i+l;
02311     }
02312   }
02313 
02314   //printf("result after specialization: %s\n",result.data());
02315 
02316   QCString qualName=cd->qualifiedNameWithTemplateParameters();
02317   //printf("QualifiedName = %s\n",qualName.data());
02318   // We strip the template arguments following className (if any)
02319   if (!qualName.isEmpty()) // there is a class name
02320   {
02321     int is,ps=0;
02322     int p=0,l,i;
02323 
02324     while ((is=getScopeFragment(qualName,ps,&l))!=-1)
02325     {
02326       QCString qualNamePart = qualName.right(qualName.length()-is);
02327       //printf("qualNamePart=%s\n",qualNamePart.data());
02328       while ((i=result.find(qualNamePart,p))!=-1)
02329       {
02330         int ql=qualNamePart.length();
02331         result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
02332         p=i+cd->name().length();
02333       }
02334       ps=is+l;
02335     }
02336   }
02337   //printf("result=%s\n",result.data());
02338 
02339   return result.stripWhiteSpace();
02340 }
02341 
02349 static int findScopePattern(const QCString &pattern,const QCString &s,
02350     int p,int *len)
02351 {
02352   int sl=s.length();
02353   int pl=pattern.length();
02354   int sp=0; 
02355   *len=0;
02356   while (p<sl)
02357   {
02358     sp=p; // start of match
02359     int pp=0; // pattern position
02360     while (p<sl && pp<pl)
02361     {
02362       if (s.at(p)=='<') // skip template arguments while matching
02363       {
02364         int bc=1;
02365         //printf("skipping pos=%d c=%c\n",p,s.at(p));
02366         p++;
02367         while (p<sl)
02368         {
02369           if (s.at(p)=='<') bc++;
02370           else if (s.at(p)=='>') 
02371           {
02372             bc--;
02373             if (bc==0) 
02374             {
02375               p++;
02376               break;
02377             }
02378           }
02379           //printf("skipping pos=%d c=%c\n",p,s.at(p));
02380           p++;
02381         }
02382       }
02383       else if (s.at(p)==pattern.at(pp))
02384       {
02385         //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
02386         p++;
02387         pp++;
02388       }
02389       else // no match
02390       {
02391         //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
02392         p=sp+1;
02393         break;
02394       }
02395     }
02396     if (pp==pl) // whole pattern matches
02397     {
02398       *len=p-sp;
02399       return sp;
02400     }
02401   }
02402   return -1;
02403 }
02404 
02405 static QCString trimScope(const QCString &name,const QCString &s)
02406 {
02407   int scopeOffset=name.length();
02408   QCString result=s;
02409   do // for each scope
02410   {
02411     QCString tmp;
02412     QCString scope=name.left(scopeOffset)+"::";
02413     //printf("Trying with scope=`%s'\n",scope.data());
02414 
02415     int i,p=0,l;
02416     while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
02417     {
02418       tmp+=result.mid(p,i-p); // add part before pattern
02419       p=i+l;
02420     }
02421     tmp+=result.right(result.length()-p); // add trailing part
02422 
02423     scopeOffset=name.findRev("::",scopeOffset-1);
02424     result = tmp;
02425   } while (scopeOffset>0);   
02426   //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
02427   return result;
02428 }
02429 #endif
02430 
02431 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
02432 {
02433   //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
02434   BaseClassListIterator bcli(*bcl);
02435   BaseClassDef *bcd;
02436   for (;(bcd=bcli.current());++bcli)
02437   {
02438     ClassDef *cd=bcd->classDef;
02439     //printf("Trying class %s\n",cd->name().data());
02440     int spos=s.find(cd->name()+"::");
02441     if (spos!=-1)
02442     {
02443       s = s.left(spos)+s.right(
02444           s.length()-spos-cd->name().length()-2
02445           );
02446     }
02447     //printf("base class `%s'\n",cd->name().data());
02448     if (cd->baseClasses())
02449       trimBaseClassScope(cd->baseClasses(),s,level+1); 
02450   }
02451 }
02452 
02453 #if 0
02454 
02458 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
02459 {
02460   int p1=t1.length();
02461   int p2=t2.length();
02462   for (;;)
02463   {
02464     int i1=p1==0 ? -1 : t1.findRev("::",p1);
02465     int i2=p2==0 ? -1 : t2.findRev("::",p2);
02466     if (i1==-1 && i2==-1)
02467     {
02468       return;
02469     }
02470     if (i1!=-1 && i2==-1) // only t1 has a scope
02471     {
02472       QCString scope=t1.left(i1);
02473       replaceNamespaceAliases(scope,i1);
02474 
02475       int so=nsName.length();
02476       do
02477       {
02478         QCString fullScope=nsName.left(so);
02479         if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
02480         fullScope+=scope;
02481         if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
02482         {
02483           t1 = t1.right(t1.length()-i1-2);
02484           return;
02485         }
02486         if (so==0)
02487         {
02488           so=-1;
02489         }
02490         else if ((so=nsName.findRev("::",so-1))==-1)
02491         {
02492           so=0;
02493         }
02494       }
02495       while (so>=0);
02496     }
02497     else if (i1==-1 && i2!=-1) // only t2 has a scope
02498     {
02499       QCString scope=t2.left(i2);
02500       replaceNamespaceAliases(scope,i2);
02501 
02502       int so=nsName.length();
02503       do
02504       {
02505         QCString fullScope=nsName.left(so);
02506         if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
02507         fullScope+=scope;
02508         if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
02509         {
02510           t2 = t2.right(t2.length()-i2-2);
02511           return;
02512         }
02513         if (so==0)
02514         {
02515           so=-1;
02516         }
02517         else if ((so=nsName.findRev("::",so-1))==-1)
02518         {
02519           so=0;
02520         }
02521       }
02522       while (so>=0);
02523     }
02524     p1 = QMAX(i1-2,0);
02525     p2 = QMAX(i2-2,0);
02526   }
02527 }
02528 #endif
02529 
02530 static void stripIrrelevantString(QCString &target,const QCString &str)
02531 {
02532   if (target==str) { target.resize(0); return; }
02533   int i,p=0;
02534   int l=str.length();
02535   bool changed=FALSE;
02536   while ((i=target.find(str,p))!=-1)
02537   {
02538     bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
02539       (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
02540     if (isMatch)
02541     {
02542       int i1=target.find('*',i+l);
02543       int i2=target.find('&',i+l);
02544       if (i1==-1 && i2==-1)
02545       {
02546         // strip str from target at index i
02547         target=target.left(i)+target.right(target.length()-i-l); 
02548         changed=TRUE;
02549         i-=l;
02550       }
02551       else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
02552       {
02553         // move str to front
02554         target=str+" "+target.left(i)+target.right(target.length()-i-l);
02555         changed=TRUE;
02556         i++;
02557       }
02558     }
02559     p = i+l;
02560   }
02561   if (changed) target=target.stripWhiteSpace();
02562 }
02563 
02579 void stripIrrelevantConstVolatile(QCString &s)
02580 {
02581   //printf("stripIrrelevantConstVolatile(%s)=",s.data());
02582   stripIrrelevantString(s,"const");
02583   stripIrrelevantString(s,"volatile");
02584   //printf("%s\n",s.data());
02585 }
02586 
02587 
02588 // a bit of debug support for matchArguments
02589 #define MATCH
02590 #define NOMATCH
02591 //#define MATCH printf("Match at line %d\n",__LINE__);
02592 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
02593 
02594 #ifndef NEWMATCH
02595 static bool matchArgument(const Argument *srcA,const Argument *dstA,
02596     const QCString &className,
02597     const QCString &namespaceName,
02598     NamespaceSDict *usingNamespaces,
02599     SDict<Definition> *usingClasses)
02600 {
02601   //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
02602   //    srcA->type.data(),srcA->name.data(),
02603   //    dstA->type.data(),dstA->name.data(),
02604   //    usingNamespaces,
02605   //    usingClasses);
02606 
02607   // TODO: resolve any typedefs names that are part of srcA->type
02608   //       before matching. This should use className and namespaceName
02609   //       and usingNamespaces and usingClass to determine which typedefs
02610   //       are in-scope, so it will not be very efficient :-(
02611 
02612   QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
02613   QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
02614   QCString srcAName=srcA->name.stripWhiteSpace();
02615   QCString dstAName=dstA->name.stripWhiteSpace();
02616   srcAType.stripPrefix("class ");
02617   dstAType.stripPrefix("class ");
02618 
02619   // allow distingishing "const A" from "const B" even though 
02620   // from a syntactic point of view they would be two names of the same 
02621   // type "const". This is not fool prove ofcourse, but should at least 
02622   // catch the most common cases.
02623   if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
02624   {
02625     srcAType+=" ";
02626     srcAType+=srcAName;
02627   } 
02628   if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
02629   {
02630     dstAType+=" ";
02631     dstAType+=dstAName;
02632   }
02633   if (srcAName=="const" || srcAName=="volatile")
02634   {
02635     srcAType+=srcAName;
02636     srcAName.resize(0);
02637   }
02638   else if (dstA->name=="const" || dstA->name=="volatile")
02639   {
02640     dstAType+=dstA->name;
02641     dstAName.resize(0);
02642   }
02643 
02644   stripIrrelevantConstVolatile(srcAType);
02645   stripIrrelevantConstVolatile(dstAType);
02646 
02647   // strip typename keyword
02648   if (strncmp(srcAType,"typename ",9)==0)
02649   {
02650     srcAType = srcAType.right(srcAType.length()-9); 
02651   }
02652   if (strncmp(dstAType,"typename ",9)==0)
02653   {
02654     dstAType = dstAType.right(dstAType.length()-9); 
02655   }
02656 
02657   srcAType = removeRedundantWhiteSpace(srcAType);
02658   dstAType = removeRedundantWhiteSpace(dstAType);
02659 
02660   //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
02661   //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
02662 
02663   //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
02664   //      dstAType.data(),dstAName.data());
02665 
02666   if (srcA->array!=dstA->array) // nomatch for char[] against char
02667   {
02668     NOMATCH
02669       return FALSE;
02670   }
02671   if (srcAType!=dstAType) // check if the argument only differs on name 
02672   {
02673 
02674     // remove a namespace scope that is only in one type 
02675     // (assuming a using statement was used)
02676     //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
02677     //trimNamespaceScope(srcAType,dstAType,namespaceName);
02678     //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
02679 
02680     //QCString srcScope;
02681     //QCString dstScope;
02682 
02683     // strip redundant scope specifiers
02684     if (!className.isEmpty())
02685     {
02686       srcAType=trimScope(className,srcAType);
02687       dstAType=trimScope(className,dstAType);
02688       //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
02689       ClassDef *cd;
02690       if (!namespaceName.isEmpty())
02691         cd=getClass(namespaceName+"::"+className);
02692       else
02693         cd=getClass(className);
02694       if (cd && cd->baseClasses())
02695       {
02696         trimBaseClassScope(cd->baseClasses(),srcAType); 
02697         trimBaseClassScope(cd->baseClasses(),dstAType); 
02698       }
02699       //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
02700     }
02701     if (!namespaceName.isEmpty())
02702     {
02703       srcAType=trimScope(namespaceName,srcAType);
02704       dstAType=trimScope(namespaceName,dstAType);
02705     }
02706     //printf("#usingNamespace=%d\n",usingNamespaces->count());
02707     if (usingNamespaces && usingNamespaces->count()>0)
02708     {
02709       NamespaceSDict::Iterator nli(*usingNamespaces);
02710       NamespaceDef *nd;
02711       for (;(nd=nli.current());++nli)
02712       {
02713         srcAType=trimScope(nd->name(),srcAType);
02714         dstAType=trimScope(nd->name(),dstAType);
02715       }
02716     }
02717     //printf("#usingClasses=%d\n",usingClasses->count());
02718     if (usingClasses && usingClasses->count()>0)
02719     {
02720       SDict<Definition>::Iterator cli(*usingClasses);
02721       Definition *cd;
02722       for (;(cd=cli.current());++cli)
02723       {
02724         srcAType=trimScope(cd->name(),srcAType);
02725         dstAType=trimScope(cd->name(),dstAType);
02726       }
02727     }
02728 
02729     //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
02730     //    dstAType.data(),dstAName.data());
02731 
02732     if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
02733         (srcAType+" "+srcAName)==dstAType)
02734     {
02735       MATCH
02736         return TRUE;
02737     }
02738     else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
02739         (dstAType+" "+dstAName)==srcAType)
02740     {
02741       MATCH
02742         return TRUE;
02743     }
02744 
02745 
02746     uint srcPos=0,dstPos=0; 
02747     bool equal=TRUE;
02748     while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
02749     {
02750       equal=srcAType.at(srcPos)==dstAType.at(dstPos);
02751       if (equal) srcPos++,dstPos++; 
02752     }
02753     uint srcATypeLen=srcAType.length();
02754     uint dstATypeLen=dstAType.length();
02755     if (srcPos<srcATypeLen && dstPos<dstATypeLen)
02756     {
02757       // if nothing matches or the match ends in the middle or at the
02758       // end of a string then there is no match
02759       if (srcPos==0 || dstPos==0) 
02760       {
02761         NOMATCH
02762           return FALSE;
02763       }
02764       if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
02765       {
02766         //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
02767         // check if a name if already found -> if no then there is no match
02768         if (!srcAName.isEmpty() || !dstAName.isEmpty()) 
02769         {
02770           NOMATCH
02771             return FALSE;
02772         }
02773         // types only
02774         while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
02775         while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
02776         if (srcPos<srcATypeLen || 
02777             dstPos<dstATypeLen ||
02778             (srcPos==srcATypeLen && dstPos==dstATypeLen)
02779            ) 
02780         {
02781           NOMATCH
02782             return FALSE;
02783         }
02784       }
02785       else
02786       {
02787         // otherwise we assume that a name starts at the current position.
02788         while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
02789         while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
02790 
02791         // if nothing more follows for both types then we assume we have
02792         // found a match. Note that now `signed int' and `signed' match, but
02793         // seeing that int is not a name can only be done by looking at the
02794         // semantics.
02795 
02796         if (srcPos!=srcATypeLen || dstPos!=dstATypeLen) 
02797         { 
02798           NOMATCH
02799             return FALSE; 
02800         }
02801       }
02802     }
02803     else if (dstPos<dstAType.length())
02804     {
02805       if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
02806       {
02807         if (!dstAName.isEmpty()) // dst has its name separated from its type
02808         {
02809           NOMATCH
02810             return FALSE;
02811         }
02812         while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
02813         if (dstPos!=dstAType.length()) 
02814         {
02815           NOMATCH
02816             return FALSE; // more than a difference in name -> no match
02817         }
02818       }
02819       else  // maybe dst has a name while src has not
02820       {
02821         dstPos++;
02822         while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
02823         if (dstPos!=dstAType.length() || !srcAName.isEmpty()) 
02824         {
02825           NOMATCH
02826             return FALSE; // nope not a name -> no match
02827         }
02828       }
02829     }
02830     else if (srcPos<srcAType.length())
02831     {
02832       if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
02833       {
02834         if (!srcAName.isEmpty()) // src has its name separated from its type
02835         {
02836           NOMATCH
02837             return FALSE;
02838         }
02839         while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
02840         if (srcPos!=srcAType.length()) 
02841         {
02842           NOMATCH
02843             return FALSE; // more than a difference in name -> no match
02844         }
02845       }
02846       else // maybe src has a name while dst has not
02847       {
02848         srcPos++;
02849         while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
02850         if (srcPos!=srcAType.length() || !dstAName.isEmpty()) 
02851         {
02852           NOMATCH
02853             return FALSE; // nope not a name -> no match
02854         }
02855       }
02856     }
02857   }
02858   MATCH
02859     return TRUE;
02860 }
02861 
02862 
02870 bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
02871     const char *cl,const char *ns,bool checkCV,
02872     NamespaceSDict *usingNamespaces,
02873     SDict<Definition> *usingClasses)
02874 {
02875   QCString className=cl;
02876   QCString namespaceName=ns;
02877 
02878   // strip template specialization from class name if present
02879   //int til=className.find('<'),tir=className.find('>');
02880   //if (til!=-1 && tir!=-1 && tir>til) 
02881   //{
02882   //  className=className.left(til)+className.right(className.length()-tir-1);
02883   //}
02884 
02885   //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
02886   //    srcAl ? argListToString(srcAl).data() : "",
02887   //    dstAl ? argListToString(dstAl).data() : "",
02888   //    cl,ns,checkCV,
02889   //    usingNamespaces?usingNamespaces->count():0,
02890   //    usingClasses?usingClasses->count():0
02891   //    );
02892 
02893   if (srcAl==0 || dstAl==0)
02894   {
02895     bool match = srcAl==dstAl; // at least one of the members is not a function
02896     if (match)
02897     {
02898       MATCH
02899         return TRUE;
02900     }
02901     else
02902     {
02903       NOMATCH
02904         return FALSE;
02905     }
02906   }
02907 
02908   // handle special case with void argument
02909   if ( srcAl->count()==0 && dstAl->count()==1 && 
02910       dstAl->getFirst()->type=="void" )
02911   { // special case for finding match between func() and func(void)
02912     Argument *a=new Argument;
02913     a->type = "void";
02914     srcAl->append(a);
02915     MATCH
02916       return TRUE;
02917   }
02918   if ( dstAl->count()==0 && srcAl->count()==1 &&
02919       srcAl->getFirst()->type=="void" )
02920   { // special case for finding match between func(void) and func()
02921     Argument *a=new Argument;
02922     a->type = "void";
02923     dstAl->append(a);
02924     MATCH
02925       return TRUE;
02926   }
02927 
02928   if (srcAl->count() != dstAl->count())
02929   {
02930     NOMATCH
02931       return FALSE; // different number of arguments -> no match
02932   }
02933 
02934   if (checkCV)
02935   {
02936     if (srcAl->constSpecifier != dstAl->constSpecifier) 
02937     {
02938       NOMATCH
02939         return FALSE; // one member is const, the other not -> no match
02940     }
02941     if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
02942     {
02943       NOMATCH
02944         return FALSE; // one member is volatile, the other not -> no match
02945     }
02946   }
02947 
02948   // so far the argument list could match, so we need to compare the types of
02949   // all arguments.
02950   ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
02951   Argument *srcA,*dstA;
02952   for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
02953   { 
02954     if (!matchArgument(srcA,dstA,className,namespaceName,
02955           usingNamespaces,usingClasses))
02956     {
02957       NOMATCH
02958         return FALSE;
02959     }
02960   }
02961   MATCH
02962     return TRUE; // all arguments match 
02963 }
02964 
02965 #endif
02966 
02967 #if 0
02968 static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
02969 {
02970   ASSERT(symbol!=0);
02971   if (symbol->definitionType()==Definition::TypeMember && 
02972       ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
02973     // to resolve it
02974   {
02975     MemberDef *md = 0;
02976     ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
02977     if (cd)
02978     {
02979       return cd->qualifiedName()+templSpec;
02980     }
02981     else if (md)
02982     {
02983       return md->qualifiedName();
02984     }
02985   }
02986   return symbol->qualifiedName();
02987 }
02988 #endif
02989 
02990 static QCString stripDeclKeywords(const QCString &s)
02991 {
02992   int i=s.find(" class ");
02993   if (i!=-1) return s.left(i)+s.mid(i+6);
02994   i=s.find(" typename ");
02995   if (i!=-1) return s.left(i)+s.mid(i+9);
02996   i=s.find(" union ");
02997   if (i!=-1) return s.left(i)+s.mid(i+6);
02998   i=s.find(" struct ");
02999   if (i!=-1) return s.left(i)+s.mid(i+7);
03000   return s;
03001 }
03002 
03003 // forward decl for circular dependencies
03004 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
03005 
03006 QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
03007 {
03008   
03009   QCString templSpec = spec.stripWhiteSpace();
03010   // this part had been commented out before... but it is needed to match for instance
03011   // std::list<std::string> against list<string> so it is now back again!
03012   if (!templSpec.isEmpty() && templSpec.at(0) == '<') 
03013   {
03014     templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
03015   }
03016   QCString resolvedType = resolveTypeDef(d,templSpec);
03017   if (!resolvedType.isEmpty()) // not known as a typedef either
03018   {
03019     templSpec = resolvedType;
03020   }
03021   //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
03022   return templSpec;
03023 }
03024 
03025 
03026 static QCString getCanonicalTypeForIdentifier(
03027     Definition *d,FileDef *fs,const QCString &word,
03028     QCString *tSpec)
03029 {
03030   QCString symName,scope,result,templSpec,tmpName;
03031   //DefinitionList *defList=0;
03032   if (tSpec && !tSpec->isEmpty()) templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
03033 
03034   if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
03035   {
03036     symName=tmpName; // name without scope
03037   }
03038   else
03039   {
03040     symName=word;
03041   }
03042   //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
03043   //    word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
03044 
03045   ClassDef *cd = 0;
03046   MemberDef *mType = 0;
03047   QCString ts;
03048   QCString resolvedType;
03049 
03050   // lookup class / class template instance
03051   cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
03052   bool isTemplInst = cd && !templSpec.isEmpty();
03053   if (!cd && !templSpec.isEmpty())
03054   {
03055     // class template specialization not known, look up class template
03056     cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
03057   }
03058   if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
03059 
03060   //printf("  getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
03061   //    symName.data(),
03062   //    word.data(),
03063   //    cd?cd->name().data():"<none>",
03064   //    d?d->name().data():"<none>",
03065   //    fs?fs->name().data():"<none>",
03066   //    cd?cd->isTemplate():-1
03067   //   );
03068 
03069   //printf("  >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
03070   //    (word+templSpec).data(),
03071   //    cd?cd->qualifiedName().data():"<none>",
03072   //   templSpec.data(),ts.data(),
03073   //   tSpec?tSpec->data():"<null>",
03074   //    cd?cd->isTemplate():FALSE,
03075   //    resolvedType.data());
03076 
03077   //printf("  mtype=%s\n",mType?mType->name().data():"<none>");
03078 
03079   if (cd) // resolves to a known class type
03080   {
03081     if (mType && mType->isTypedef()) // but via a typedef
03082     {
03083       result = resolvedType;
03084     }
03085     else
03086     {
03087       if (isTemplInst)
03088       {
03089         // spec is already part of class type
03090         templSpec="";
03091         if (tSpec) *tSpec="";
03092       }
03093       else if (!ts.isEmpty() && templSpec.isEmpty())
03094       {
03095         // use formal template args for spec
03096         templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
03097       }
03098 
03099       result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
03100 
03101       if (cd->isTemplate() && tSpec) //
03102       {
03103         if (!templSpec.isEmpty()) // specific instance
03104         {
03105           result=cd->name()+templSpec;
03106         }
03107         else // use template type
03108         {
03109           result=cd->qualifiedNameWithTemplateParameters();
03110         }
03111         // template class, so remove the template part (it is part of the class name)
03112         *tSpec="";
03113       }
03114       else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
03115       {
03116         // obscure case, where a class is used as a template, but doxygen think it is
03117         // not (could happen when loading the class from a tag file).
03118         *tSpec="";
03119       }
03120     }
03121   }
03122   else if (mType && mType->isEnumerate()) // an enum
03123   {
03124     result = mType->qualifiedName();
03125   }
03126   else
03127   {
03128     resolvedType = resolveTypeDef(d,word);
03129     if (resolvedType.isEmpty()) // not known as a typedef either
03130     {
03131       result = word;
03132     }
03133     else
03134     {
03135       result = resolvedType;
03136     }
03137   }
03138   //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
03139   return result;
03140 }
03141 
03142 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
03143 {
03144   type = type.stripWhiteSpace();
03145 
03146   // strip const and volatile keywords that are not relevant for the type
03147   stripIrrelevantConstVolatile(type);
03148 
03149   // strip leading keywords
03150   type.stripPrefix("class ");
03151   type.stripPrefix("struct ");
03152   type.stripPrefix("union ");
03153   type.stripPrefix("enum ");
03154   type.stripPrefix("typename ");
03155 
03156   type = removeRedundantWhiteSpace(type);
03157   //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
03158   //    d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
03159 
03160   static QRegExp id("[a-z_A-Z][:a-z_A-Z0-9]*");
03161 
03162   QCString canType,templSpec,word;
03163   int i,p=0,pp=0;
03164   while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
03165     // foreach identifier in the type
03166   {
03167     //printf("     i=%d p=%d\n",i,p);
03168     if (i>pp) canType += type.mid(pp,i-pp);
03169 
03170 
03171     canType += getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
03172     //printf(" word=%s templSpec=%s canType=%s\n",word.data(),templSpec.data(),canType.data());
03173     if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
03174                               // (i.e. type is not a template specialization)
03175                               // then resolve any identifiers inside. 
03176     {
03177       static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*");
03178       int tp=0,tl,ti;
03179       // for each identifier template specifier
03180       //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
03181       while ((ti=re.match(templSpec,tp,&tl))!=-1)
03182       {
03183         canType += templSpec.mid(tp,ti-tp);
03184         canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
03185         tp=ti+tl;
03186       }
03187       canType+=templSpec.right(templSpec.length()-tp);
03188     }
03189 
03190     pp=p;
03191   }
03192   canType += type.right(type.length()-pp);
03193   //printf("extractCanonicalType = %s->%s\n",type.data(),canType.data());
03194 
03195   return removeRedundantWhiteSpace(canType);
03196 }
03197 
03198 static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
03199 {
03200   QCString type = arg->type.stripWhiteSpace();
03201   QCString name = arg->name;
03202   //printf("extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
03203   if ((type=="const" || type=="volatile") && !name.isEmpty()) 
03204   { // name is part of type => correct
03205     type+=" ";
03206     type+=name;
03207   } 
03208   if (name=="const" || name=="volatile")
03209   { // name is part of type => correct
03210     if (!type.isEmpty()) type+=" ";
03211     type+=name;
03212   }
03213 
03214   return extractCanonicalType(d,fs,type);
03215 }
03216 
03217 static bool matchArgument2(
03218     Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
03219     Definition *dstScope,FileDef *dstFileScope,Argument *dstA
03220     )
03221 {
03222   //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
03223   //    srcScope ? srcScope->name().data() : "",
03224   //    srcA->type.data(),srcA->name.data(),srcA->canType.data(),
03225   //    dstScope ? dstScope->name().data() : "",
03226   //    dstA->type.data(),dstA->name.data(),dstA->canType.data());
03227 
03228   if (srcA->array!=dstA->array) // nomatch for char[] against char
03229   {
03230     NOMATCH
03231     return FALSE;
03232   }
03233   QCString sSrcName = " "+srcA->name;
03234   QCString sDstName = " "+dstA->name;
03235   QCString srcType  = srcA->type;
03236   QCString dstType  = dstA->type;
03237   stripIrrelevantConstVolatile(srcType);
03238   stripIrrelevantConstVolatile(dstType);
03239   //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
03240   //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
03241   if (sSrcName==dstType.right(sSrcName.length()))
03242   { // case "unsigned int" <-> "unsigned int i"
03243     srcA->type+=sSrcName;
03244     srcA->name="";
03245     srcA->canType=""; // invalidate cached type value
03246   }
03247   else if (sDstName==srcType.right(sDstName.length()))
03248   { // case "unsigned int i" <-> "unsigned int"
03249     dstA->type+=sDstName;
03250     dstA->name="";
03251     dstA->canType=""; // invalidate cached type value
03252   }
03253 
03254   if (srcA->canType.isEmpty())
03255   {
03256     srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
03257   }
03258   if (dstA->canType.isEmpty())
03259   {
03260     dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
03261   }
03262 
03263   if (srcA->canType==dstA->canType)
03264   {
03265     MATCH
03266     return TRUE;
03267   }
03268   else
03269   {
03270     //printf("   Canonical types do not match [%s]<->[%s]\n",
03271     //    srcA->canType.data(),dstA->canType.data());
03272     NOMATCH
03273     return FALSE;
03274   }
03275 }
03276 
03277 
03278 // new algorithm for argument matching
03279 bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
03280     Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
03281     bool checkCV
03282     )
03283 {
03284   ASSERT(srcScope!=0 && dstScope!=0);
03285 
03286   if (srcAl==0 || dstAl==0)
03287   {
03288     bool match = srcAl==dstAl; // at least one of the members is not a function
03289     if (match)
03290     {
03291       MATCH
03292       return TRUE;
03293     }
03294     else
03295     {
03296       NOMATCH
03297       return FALSE;
03298     }
03299   }
03300 
03301   // handle special case with void argument
03302   if ( srcAl->count()==0 && dstAl->count()==1 && 
03303       dstAl->getFirst()->type=="void" )
03304   { // special case for finding match between func() and func(void)
03305     Argument *a=new Argument;
03306     a->type = "void";
03307     srcAl->append(a);
03308     MATCH
03309     return TRUE;
03310   }
03311   if ( dstAl->count()==0 && srcAl->count()==1 &&
03312       srcAl->getFirst()->type=="void" )
03313   { // special case for finding match between func(void) and func()
03314     Argument *a=new Argument;
03315     a->type = "void";
03316     dstAl->append(a);
03317     MATCH
03318     return TRUE;
03319   }
03320 
03321   if (srcAl->count() != dstAl->count())
03322   {
03323     NOMATCH
03324     return FALSE; // different number of arguments -> no match
03325   }
03326 
03327   if (checkCV)
03328   {
03329     if (srcAl->constSpecifier != dstAl->constSpecifier) 
03330     {
03331       NOMATCH
03332       return FALSE; // one member is const, the other not -> no match
03333     }
03334     if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
03335     {
03336       NOMATCH
03337       return FALSE; // one member is volatile, the other not -> no match
03338     }
03339   }
03340 
03341   // so far the argument list could match, so we need to compare the types of
03342   // all arguments.
03343   ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
03344   Argument *srcA,*dstA;
03345   for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
03346   { 
03347     if (!matchArgument2(srcScope,srcFileScope,srcA,
03348           dstScope,dstFileScope,dstA)
03349        )
03350     {
03351       NOMATCH
03352       return FALSE;
03353     }
03354   }
03355   MATCH
03356   return TRUE; // all arguments match 
03357 }
03358 
03359 
03360 
03361 // merges the initializer of two argument lists
03362 // pre:  the types of the arguments in the list should match.
03363 void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
03364 {
03365   //printf("mergeArguments `%s', `%s'\n",
03366   //    argListToString(srcAl).data(),argListToString(dstAl).data());
03367 
03368   if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
03369   {
03370     return; // invalid argument lists -> do not merge
03371   }
03372 
03373   ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
03374   Argument *srcA,*dstA;
03375   for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
03376   {
03377     if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
03378     {
03379       //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
03380       srcA->defval=dstA->defval.copy();
03381     }
03382     else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
03383     {
03384       //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
03385       dstA->defval=srcA->defval.copy();
03386     }
03387 
03388     // fix wrongly detected const or volatile specificiers before merging.
03389     // example: "const A *const" is detected as type="const A *" name="const"
03390     if (srcA->name=="const" || srcA->name=="volatile")
03391     {
03392       srcA->type+=" "+srcA->name;
03393       srcA->name.resize(0);
03394     }
03395     if (dstA->name=="const" || dstA->name=="volatile")
03396     {
03397       dstA->type+=" "+dstA->name;
03398       dstA->name.resize(0);
03399     }
03400 
03401     if (srcA->type==dstA->type)
03402     {
03403       if (srcA->name.isEmpty() && !dstA->name.isEmpty())
03404       {
03405         //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
03406         //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
03407         srcA->type = dstA->type.copy();
03408         srcA->name = dstA->name.copy();
03409       }
03410       else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
03411       {
03412         //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
03413         //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
03414         dstA->type = srcA->type.copy();
03415         dstA->name = dstA->name.copy();
03416       }
03417       else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
03418       {
03419         //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
03420         if (forceNameOverwrite)
03421         {
03422           srcA->name = dstA->name;
03423         }
03424         else
03425         {
03426           if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
03427           {
03428             srcA->name = dstA->name;
03429           }
03430           else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
03431           {
03432             dstA->name = srcA->name;
03433           }
03434         }
03435       }
03436     }
03437     else
03438     {
03439       //printf("merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
03440       if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
03441       {
03442         srcA->type+=" "+srcA->name;
03443         srcA->name=dstA->name;
03444       }
03445       else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
03446       {
03447         dstA->type+=" "+dstA->name;
03448         dstA->name=srcA->name;
03449       }
03450       else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
03451       {
03452         srcA->name = dstA->name;
03453       }
03454       else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
03455       {
03456         dstA->name = srcA->name;
03457       }
03458     }
03459     int i1=srcA->type.find("::"),
03460         i2=dstA->type.find("::"),
03461         j1=srcA->type.length()-i1-2,
03462         j2=dstA->type.length()-i2-2;
03463     if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
03464     {
03465       //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
03466       //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
03467       dstA->type = srcA->type.left(i1+2)+dstA->type;
03468       dstA->name = dstA->name.copy();
03469     }
03470     else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
03471     {
03472       //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
03473       //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
03474       srcA->type = dstA->type.left(i2+2)+srcA->type;
03475       srcA->name = dstA->name.copy();
03476     }
03477     if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
03478     {
03479       srcA->docs = dstA->docs.copy();
03480     }
03481     else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
03482     {
03483       dstA->docs = srcA->docs.copy();
03484     }
03485     //printf("Merge argument `%s|%s' `%s|%s'\n",
03486     //  srcA->type.data(),srcA->name.data(),
03487     //  dstA->type.data(),dstA->name.data());
03488   }
03489 }
03490 
03513 bool getDefs(const QCString &scName,const QCString &memberName, 
03514     const char *args,
03515     MemberDef *&md, 
03516     ClassDef *&cd, FileDef *&fd, NamespaceDef *&nd, GroupDef *&gd,
03517     bool forceEmptyScope,
03518     FileDef *currentFile,
03519     bool checkCV
03520     )
03521 {
03522   fd=0, md=0, cd=0, nd=0, gd=0;
03523   if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
03524 
03525   QCString scopeName=scName;
03526   //printf("Search for name=%s args=%s in scope=%s\n",
03527   //          memberName.data(),args,scopeName.data());
03528 
03529   int is,im=0,pm=0;
03530   // strip common part of the scope from the scopeName
03531   while ((is=scopeName.findRev("::"))!=-1 && 
03532       (im=memberName.find("::",pm))!=-1 &&
03533       (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
03534       )
03535   {
03536     scopeName=scopeName.left(is); 
03537     pm=im+2;
03538   }
03539   //printf("result after scope corrections scope=%s name=%s\n",
03540   //          scopeName.data(),memberName.data());
03541 
03542   QCString mName=memberName;
03543   QCString mScope;
03544   if (memberName.left(9)!="operator " && // treat operator conversion methods
03545       // as a special case
03546       (im=memberName.findRev("::"))!=-1 && 
03547       im<(int)memberName.length()-2 // not A::
03548      )
03549   {
03550     mScope=memberName.left(im); 
03551     mName=memberName.right(memberName.length()-im-2);
03552   }
03553 
03554   // handle special the case where both scope name and member scope are equal
03555   if (mScope==scopeName) scopeName.resize(0);
03556 
03557   //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
03558 
03559   MemberName *mn = Doxygen::memberNameSDict->find(mName);
03560   //printf("mName=%s mn=%p\n",mName.data(),mn);
03561   if (!forceEmptyScope && mn && !(scopeName.isEmpty() && mScope.isEmpty()))
03562   {
03563     //printf("  >member name found\n");
03564     int scopeOffset=scopeName.length();
03565     do
03566     {
03567       QCString className = scopeName.left(scopeOffset);
03568       if (!className.isEmpty() && !mScope.isEmpty())
03569       {
03570         className+="::"+mScope;
03571       }
03572       else if (!mScope.isEmpty())
03573       {
03574         className=mScope.copy();
03575       }
03576       //printf("Trying class scope %s\n",className.data());
03577 
03578       ClassDef *fcd=0;
03579       // todo: fill in correct fileScope!
03580       if ((fcd=getResolvedClass(Doxygen::globalScope,0,className)) &&  // is it a documented class
03581           fcd->isLinkable() 
03582          )
03583       {
03584         //printf("  Found fcd=%p\n",fcd);
03585         MemberListIterator mmli(*mn);
03586         MemberDef *mmd;
03587         int mdist=maxInheritanceDepth; 
03588         ArgumentList *argList=0;
03589         if (args)
03590         {
03591           argList=new ArgumentList;
03592           stringToArgumentList(args,argList);
03593         }
03594         for (mmli.toFirst();(mmd=mmli.current());++mmli)
03595         {
03596           //if (mmd->isLinkable())
03597           //{
03598           LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
03599           bool match=args==0 || 
03600             matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
03601                 fcd,fcd->getFileDef(),argList,
03602                 checkCV
03603                 );  
03604           //printf("match=%d\n",match);
03605           if (match)
03606           {
03607             ClassDef *mcd=mmd->getClassDef();
03608             if (mcd)
03609             {
03610               int m=minClassDistance(fcd,mcd);
03611               if (m<mdist && mcd->isLinkable())
03612               {
03613                 mdist=m;
03614                 cd=mcd;
03615                 md=mmd;
03616               }
03617             }
03618           }
03619           //}
03620         }
03621         if (argList)
03622         {
03623           delete argList; argList=0;
03624         }
03625         if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0)
03626           // no exact match found, but if args="()" an arbitrary member will do
03627         {
03628           //printf("  >Searching for arbitrary member\n");
03629           for (mmli.toFirst();(mmd=mmli.current());++mmli)
03630           {
03631             //if (mmd->isLinkable())
03632             //{
03633             ClassDef *mcd=mmd->getClassDef();
03634             //printf("  >Class %s found\n",mcd->name().data());
03635             if (mcd)
03636             {
03637               int m=minClassDistance(fcd,mcd);
03638               if (m<mdist /* && mcd->isLinkable()*/ )
03639               {
03640                 //printf("Class distance %d\n",m);
03641                 mdist=m;
03642                 cd=mcd;
03643                 md=mmd;
03644               }
03645             }
03646             //}
03647           }
03648         }
03649         //printf("  >Succes=%d\n",mdist<maxInheritanceDepth);
03650         if (mdist<maxInheritanceDepth) 
03651         {
03652           if (!md->isLinkable()) 
03653           {
03654             md=0; // avoid returning things we cannot link to
03655             cd=0;
03656             return FALSE; // match found, but was not linkable
03657           }
03658           else
03659           {
03660             gd=md->getGroupDef();
03661             if (gd) cd=0;
03662             return TRUE; /* found match */
03663           }
03664         }
03665       } 
03666       /* go to the parent scope */
03667 
03668       if (scopeOffset==0)
03669       {
03670         scopeOffset=-1;
03671       }
03672       else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
03673       {
03674         scopeOffset=0;
03675       }
03676     } while (scopeOffset>=0);
03677 
03678     // unknown or undocumented scope 
03679   }
03680   if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
03681   {
03682     MemberListIterator mmli(*mn);
03683     MemberDef *mmd, *fuzzy_mmd = 0;
03684     ArgumentList *argList = 0;
03685     bool hasEmptyArgs = args && strcmp(args, "()") == 0;
03686 
03687     if (args)
03688       stringToArgumentList(args, argList = new ArgumentList);
03689 
03690     for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
03691     {
03692       if (!mmd->isLinkable() || !mmd->isRelated() || !mmd->getClassDef())
03693         continue;
03694 
03695       if (!args) break;
03696 
03697       QCString className = mmd->getClassDef()->name();
03698 
03699       LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
03700       if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
03701             Doxygen::globalScope,mmd->getFileDef(),argList,
03702             checkCV
03703             )
03704          ) break;
03705 
03706       if (!fuzzy_mmd && hasEmptyArgs)
03707         fuzzy_mmd = mmd;
03708     }
03709 
03710     if (argList) delete argList, argList = 0;
03711 
03712     mmd = mmd ? mmd : fuzzy_mmd;
03713 
03714     if (mmd)
03715     {
03716       md = mmd;
03717       cd = mmd->getClassDef();
03718       return TRUE;
03719     }
03720   }
03721 
03722 
03723   // maybe an namespace, file or group member ?
03724   //printf("Testing for global function scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
03725   //              scopeName.data(),mScope.data(),mName.data());
03726   if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
03727   {
03728     //printf("  >function name found\n");
03729     NamespaceDef *fnd=0;
03730     int scopeOffset=scopeName.length();
03731     do
03732     {
03733       QCString namespaceName = scopeName.left(scopeOffset);
03734       if (!namespaceName.isEmpty() && !mScope.isEmpty())
03735       {
03736         namespaceName+="::"+mScope;
03737       }
03738       else if (!mScope.isEmpty())
03739       {
03740         namespaceName=mScope.copy();
03741       }
03742       //printf("Trying namespace %s\n",namespaceName.data());
03743       if (!namespaceName.isEmpty() && 
03744           (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
03745           fnd->isLinkable()
03746          )
03747       {
03748         //printf("Function inside existing namespace `%s'\n",namespaceName.data());
03749         bool found=FALSE;
03750         MemberListIterator mmli(*mn);
03751         MemberDef *mmd;
03752         for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
03753         {
03754           //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
03755           //    mmd->getNamespaceDef(),fnd);
03756           if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
03757           { // namespace is found
03758             bool match=TRUE;
03759             ArgumentList *argList=0;
03760             if (args && strcmp(args,"()")!=0)
03761             {
03762               argList=new ArgumentList;
03763               LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
03764               stringToArgumentList(args,argList);
03765               match=matchArguments2(
03766                   mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
03767                   fnd,mmd->getFileDef(),argList,
03768                   checkCV); 
03769             }
03770             if (match)
03771             {
03772               nd=fnd;
03773               md=mmd;
03774               found=TRUE;
03775             }
03776             if (args)
03777             {
03778               delete argList; argList=0;
03779             }
03780           }
03781         }
03782         if (!found && args && !strcmp(args,"()")) 
03783           // no exact match found, but if args="()" an arbitrary 
03784           // member will do
03785         {
03786           for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
03787           {
03788             if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
03789             {
03790               nd=fnd;
03791               md=mmd;
03792               found=TRUE;
03793             }
03794           }
03795         }
03796         if (found) 
03797         {
03798           if (!md->isLinkable()) 
03799           {
03800             md=0; // avoid returning things we cannot link to
03801             nd=0;
03802             return FALSE; // match found but not linkable
03803           }
03804           else
03805           {
03806             gd=md->getGroupDef();
03807             if (gd && gd->isLinkable()) nd=0; else gd=0;
03808             return TRUE;
03809           }
03810         }
03811       }
03812       if (scopeOffset==0)
03813       {
03814         scopeOffset=-1;
03815       }
03816       else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
03817       {
03818         scopeOffset=0;
03819       }
03820     } while (scopeOffset>=0);
03821 
03822     //else // no scope => global function
03823     {
03824       QList<MemberDef> members;
03825 
03826       //printf("  Function with global scope name `%s' args=`%s'\n",memberName.data(),args);
03827       MemberListIterator mli(*mn);
03828       for (mli.toFirst();(md=mli.current());++mli)
03829       {
03830         fd=md->getFileDef();
03831         gd=md->getGroupDef();
03832         //printf("  md->name()=`%s' md->args=`%s' fd=%p gd=%p\n",
03833         //    md->name().data(),args,fd,gd);
03834         if (
03835             ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) && 
03836             md->getNamespaceDef()==0
03837            )
03838         {
03839           //printf("    fd=%p gd=%p args=`%s'\n",fd,gd,args);
03840           bool match=TRUE;
03841           ArgumentList *argList=0;
03842           if (args && !md->isDefine() && strcmp(args,"()")!=0)
03843           {
03844             argList=new ArgumentList;
03845             LockingPtr<ArgumentList> mdAl = md->argumentList();
03846             stringToArgumentList(args,argList);
03847             match=matchArguments2(
03848                 md->getOuterScope(),fd,mdAl.pointer(),
03849                 Doxygen::globalScope,fd,argList,
03850                 checkCV); 
03851             delete argList; argList=0;
03852           }
03853           if (match) 
03854           {
03855             //printf("Found match!\n");
03856             members.append(md);
03857           }
03858         }
03859       }
03860       if (members.count()!=1 && args && !strcmp(args,"()"))
03861       {
03862         // no exact match found, but if args="()" an arbitrary 
03863         // member will do
03864         md=mn->last();
03865         while (md /* && md->isLinkable()*/)
03866         {
03867           //printf("Found member `%s'\n",md->name().data());
03868           //printf("member is linkable md->name()=`%s'\n",md->name().data());
03869           fd=md->getFileDef();
03870           gd=md->getGroupDef();
03871           if (
03872               (gd && gd->isLinkable()) || (fd && fd->isLinkable()) 
03873              )
03874           {
03875             members.append(md);
03876           }
03877           md=mn->prev();
03878         }
03879       }
03880       //printf("found %d candidate members\n",members.count());
03881       if (members.count()==1 || currentFile!=0)
03882       {
03883         md=members.first();
03884       }
03885       else if (members.count()>1)
03886       {
03887         //printf("Found more than one matching member!\n");
03888         // use some C scoping rules to determine the correct link
03889         // 1. member in current file
03890         // 2. non-static member in different file
03891         if (currentFile==0)
03892         {
03893           bool ambig;
03894           currentFile = findFileDef(Doxygen::inputNameDict,0/*namespaceName*/,ambig);
03895         }
03896         MemberDef *bmd = 0;
03897         for (md=members.first(); md; md=members.next())
03898         {
03899           if (currentFile && md->getBodyDef()==currentFile)
03900           {
03901             bmd = 0;
03902             break;
03903           }
03904           if (!md->isStatic() || Config_getBool("EXTRACT_STATIC")) bmd = md;     
03905         }
03906         if (bmd) md=bmd;
03907       }
03908       if (md && !md->isLinkable()) md=0; // ignore things we cannot link to
03909       if (md) // found a matching global member
03910       {
03911         fd=md->getFileDef();
03912         gd=md->getGroupDef();
03913         //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
03914         if (gd && gd->isLinkable()) fd=0; else gd=0;
03915         return TRUE;
03916       }
03917     }
03918   }
03919 
03920   // no nothing found
03921   return FALSE;
03922 }
03923 
03938 static bool getScopeDefs(const char *docScope,const char *scope,
03939     ClassDef *&cd, NamespaceDef *&nd)
03940 {
03941   cd=0;nd=0;
03942 
03943   QCString scopeName=scope;
03944   //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
03945   if (scopeName.isEmpty()) return FALSE;
03946 
03947   bool explicitGlobalScope=FALSE;
03948   if (scopeName.at(0)==':' && scopeName.at(1)==':')
03949   {
03950     scopeName=scopeName.right(scopeName.length()-2);  
03951     explicitGlobalScope=TRUE;
03952   }
03953 
03954   QCString docScopeName=docScope;
03955   int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
03956 
03957   do // for each possible docScope (from largest to and including empty)
03958   {
03959     QCString fullName=scopeName.copy();
03960     if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
03961 
03962     if ((cd=getClass(fullName)) && cd->isLinkable())
03963     {
03964       return TRUE; // class link written => quit 
03965     }
03966     else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
03967     {
03968       return TRUE; // namespace link written => quit 
03969     }
03970     if (scopeOffset==0)
03971     {
03972       scopeOffset=-1;
03973     }
03974     else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
03975     {
03976       scopeOffset=0;
03977     }
03978   } while (scopeOffset>=0);
03979 
03980   return FALSE;
03981 }
03982 
03983 //static bool isLowerCase(QCString &s)
03984 //{
03985 //  char *p=s.data();
03986 //  if (p==0) return TRUE;
03987 //  int c;
03988 //  while ((c=*p++)) if (!islower(c)) return FALSE;
03989 //  return TRUE; 
03990 //}
03991 
03992 
03993 
03997 bool resolveRef(/* in */  const char *scName,
03998     /* in */  const char *name,
03999     /* in */  bool inSeeBlock,
04000     /* out */ Definition **resContext,
04001     /* out */ MemberDef  **resMember
04002     )
04003 {
04004   QCString tsName = name;
04005   bool memberScopeFirst = tsName.find('#')!=-1;
04006   QCString fullName = substitute(tsName,"#","::");
04007   fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
04008 
04009   int bracePos=fullName.findRev('('); // reverse is needed for operator()(...)
04010   int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
04011   int scopePos=fullName.findRev("::",endNamePos);
04012 
04013   // default result values
04014   *resContext=0;
04015   *resMember=0;
04016 
04017   if (bracePos==-1) // simple name
04018   {
04019     ClassDef *cd=0;
04020     NamespaceDef *nd=0;
04021 
04022     //if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
04023     //{ // link to lower case only name => do not try to autolink 
04024     //  return FALSE;
04025     //}
04026 
04027     //printf("scName=%s fullName=%s\n",scName,fullName.data());
04028 
04029     // check if this is a class or namespace reference
04030     if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
04031     {
04032       if (cd) // scope matches that of a class
04033       {
04034         *resContext = cd;
04035       }
04036       else // scope matches that of a namespace
04037       {
04038         ASSERT(nd!=0);
04039         *resContext = nd;
04040       }
04041       return TRUE;
04042     }
04043     else if (scName==fullName || (!inSeeBlock && scopePos==-1)) 
04044       // nothing to link => output plain text
04045     {
04046       //printf("found scName=%s fullName=%s scName==fullName=%d "
04047       //    "inSeeBlock=%d scopePos=%d!\n",
04048       //    scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
04049       return FALSE;
04050     }
04051     // continue search...
04052   }
04053 
04054   // extract userscope+name
04055   QCString nameStr=fullName.left(endNamePos);
04056 
04057   // extract arguments
04058   QCString argsStr;
04059   if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
04060 
04061   // strip template specifier
04062   // TODO: match against the correct partial template instantiation 
04063   int templPos=nameStr.find('<');
04064   if (templPos!=-1 && nameStr.find("operator")==-1)
04065   {
04066     int endTemplPos=nameStr.findRev('>');
04067     nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
04068   }
04069 
04070   QCString scopeStr=scName;
04071 
04072   MemberDef    *md = 0;
04073   ClassDef     *cd = 0;
04074   FileDef      *fd = 0;
04075   NamespaceDef *nd = 0;
04076   GroupDef     *gd = 0;
04077 
04078   // check if nameStr is a member or global.
04079   //printf("getDefs(scope=%s,name=%s,args=%s)\n",scopeStr.data(),nameStr.data(),argsStr.data());
04080   if (getDefs(scopeStr,nameStr,argsStr,
04081         md,cd,fd,nd,gd,
04082         scopePos==0 && !memberScopeFirst,
04083         0,
04084         TRUE
04085         )
04086      )
04087   {
04088     //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
04089     if      (md) { *resMember=md; *resContext=md; }
04090     else if (cd) *resContext=cd;
04091     else if (nd) *resContext=nd;
04092     else if (fd) *resContext=fd;
04093     else if (gd) *resContext=gd;
04094     else         { *resContext=0; *resMember=0; return FALSE; }
04095     //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
04096     //    md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
04097     return TRUE;
04098   }
04099   else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
04100   { // group link
04101     *resContext=gd;
04102     return TRUE;
04103   }
04104   else if (tsName.find('.')!=-1) // maybe a link to a file
04105   {
04106     bool ambig;
04107     fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
04108     if (fd && !ambig)
04109     {
04110       *resContext=fd;
04111       return TRUE;
04112     }
04113   }
04114 
04115   return FALSE;
04116 }
04117 
04118 QCString linkToText(const char *link,bool isFileName)
04119 {
04120   static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
04121   QCString result=link;
04122   if (!result.isEmpty())
04123   {
04124     // replace # by ::
04125     result=substitute(result,"#","::");
04126     // replace . by ::
04127     if (!isFileName) result=substitute(result,".","::");
04128     // strip leading :: prefix if present
04129     if (result.at(0)==':' && result.at(1)==':')
04130     {
04131       result=result.right(result.length()-2);
04132     }
04133     if (optimizeOutputJava)
04134     {
04135       result=substitute(result,"::",".");
04136     }
04137   }
04138   return result;
04139 }
04140 
04160 bool generateRef(OutputDocInterface &od,const char *scName,
04161     const char *name,bool inSeeBlock,const char *rt)
04162 {
04163   //printf("generateRef(scName=%s,name=%s,rt=%s)\n",scName,name,rt);
04164 
04165   Definition *compound;
04166   MemberDef *md;
04167 
04168   // create default link text
04169   QCString linkText = linkToText(rt,FALSE);
04170 
04171   if (resolveRef(scName,name,inSeeBlock,&compound,&md))
04172   {
04173     if (md && md->isLinkable()) // link to member
04174     {
04175       od.writeObjectLink(md->getReference(),
04176           md->getOutputFileBase(),
04177           md->anchor(),linkText);
04178       // generate the page reference (for LaTeX)
04179       if (!md->isReference())
04180       {
04181         writePageRef(od,md->getOutputFileBase(),md->anchor());
04182       }
04183       return TRUE;
04184     }
04185     else if (compound && compound->isLinkable()) // link to compound
04186     {
04187       if (rt==0 && compound->definitionType()==Definition::TypeGroup)
04188       {
04189         linkText=((GroupDef *)compound)->groupTitle();
04190       }
04191       if (compound && compound->definitionType()==Definition::TypeFile)
04192       {
04193         linkText=linkToText(rt,TRUE);
04194       }
04195       od.writeObjectLink(compound->getReference(),
04196           compound->getOutputFileBase(),
04197           0,linkText);
04198       if (!compound->isReference())
04199       {
04200         writePageRef(od,compound->getOutputFileBase(),0);
04201       }
04202       return TRUE;
04203     }
04204   }
04205   od.docify(linkText);
04206   return FALSE;
04207 }
04208 
04209 bool resolveLink(/* in */ const char *scName,
04210     /* in */ const char *lr,
04211     /* in */ bool inSeeBlock,
04212     /* out */ Definition **resContext,
04213     /* out */ QCString &resAnchor
04214     )
04215 {
04216   *resContext=0;
04217 
04218   QCString linkRef=lr;
04219   //printf("ResolveLink linkRef=%s\n",lr);
04220   FileDef  *fd;
04221   GroupDef *gd;
04222   PageDef  *pd;
04223   ClassDef *cd;
04224   DirDef   *dir;
04225   NamespaceDef *nd;
04226   bool ambig;
04227   if (linkRef.isEmpty()) // no reference name!
04228   {
04229     return FALSE;
04230   }
04231   else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
04232   {
04233     GroupDef *gd = pd->getGroupDef();
04234     if (gd)
04235     {
04236       SectionInfo *si=0;
04237       if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()];
04238       *resContext=gd;
04239       if (si) resAnchor = si->label;
04240     }
04241     else
04242     {
04243       *resContext=pd;
04244     }
04245     return TRUE;
04246   }
04247   else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
04248   {
04249     *resContext=pd;
04250     return TRUE;
04251   }
04252   else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
04253   {
04254     *resContext=gd;
04255     return TRUE;
04256   }
04257   else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
04258       && fd->isLinkable())
04259   {
04260     *resContext=fd;
04261     return TRUE;
04262   }
04263   else if ((cd=getClass(linkRef))) // class link
04264   {
04265     *resContext=cd;
04266     return TRUE;
04267   }
04268   else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
04269   {
04270     *resContext=cd;
04271     return TRUE;
04272   }
04273   else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
04274   {
04275     *resContext=nd;
04276     return TRUE;
04277   }
04278   else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath()+"/"))
04279       && dir->isLinkable()) // TODO: make this location independent like filedefs
04280   {
04281     *resContext=dir;
04282     return TRUE;
04283   }
04284   else // probably a member reference
04285   {
04286     MemberDef *md;
04287     bool res = resolveRef(scName,lr,inSeeBlock,resContext,&md);
04288     if (md) resAnchor=md->anchor();
04289     return res;
04290   }
04291 }
04292 
04293 
04294 //----------------------------------------------------------------------
04295 // General function that generates the HTML code for a reference to some
04296 // file, class or member from text `lr' within the context of class `clName'. 
04297 // This link has the text 'lt' (if not 0), otherwise `lr' is used as a
04298 // basis for the link's text.
04299 // returns TRUE if a link could be generated.
04300 
04301 bool generateLink(OutputDocInterface &od,const char *clName,
04302     const char *lr,bool inSeeBlock,const char *lt)
04303 {
04304   //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
04305   Definition *compound;
04306   //PageDef *pageDef=0;
04307   QCString anchor,linkText=linkToText(lt,FALSE);
04308   //printf("generateLink linkText=%s\n",linkText.data());
04309   if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
04310   {
04311     if (compound) // link to compound
04312     {
04313       if (lt==0 && anchor.isEmpty() &&                      /* compound link */
04314           compound->definitionType()==Definition::TypeGroup /* is group */ 
04315          )
04316       {
04317         linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
04318       }
04319       else if (compound->definitionType()==Definition::TypeFile)
04320       {
04321         linkText=linkToText(lt,TRUE); 
04322       }
04323       od.writeObjectLink(compound->getReference(),
04324           compound->getOutputFileBase(),anchor,linkText);
04325       if (!compound->isReference())
04326       {
04327         writePageRef(od,compound->getOutputFileBase(),anchor);
04328       }
04329     }
04330     else
04331     {
04332       err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
04333     }
04334     return TRUE;
04335   }
04336   else // link could not be found
04337   {
04338     od.docify(linkText);
04339     return FALSE;
04340   }
04341 }
04342 
04343 void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
04344 {
04345   //printf("generateFileRef(%s,%s)\n",name,text);
04346   QCString linkText = text ? text : name;
04347   //FileInfo *fi;
04348   FileDef *fd;
04349   bool ambig;
04350   if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) && 
04351       fd->isLinkable()) 
04352     // link to documented input file
04353     od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
04354   else
04355     od.docify(linkText); 
04356 }
04357 
04358 //----------------------------------------------------------------------
04359 
04360 #if 0
04361 QCString substituteClassNames(const QCString &s)
04362 {
04363   int i=0,l,p;
04364   QCString result;
04365   if (s.isEmpty()) return result;
04366   QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
04367   while ((p=r.match(s,i,&l))!=-1)
04368   {
04369     QCString *subst;
04370     if (p>i) result+=s.mid(i,p-i);
04371     if ((subst=substituteDict[s.mid(p,l)]))
04372     {
04373       result+=*subst;
04374     }
04375     else
04376     {
04377       result+=s.mid(p,l);
04378     }
04379     i=p+l;
04380   }
04381   result+=s.mid(i,s.length()-i);
04382   return result;
04383 }
04384 #endif
04385 
04386 //----------------------------------------------------------------------
04387 // substitute all occurences of `src' in `s' by `dst'
04388 
04389 QCString substitute(const char *s,const char *src,const char *dst)
04390 {
04391   // TODO: optimize by using strstr() instead of find
04392   QCString input=s;
04393   QCString output;
04394   int i=0,p;
04395   while ((p=input.find(src,i))!=-1)
04396   {
04397     output+=input.mid(i,p-i);
04398     output+=dst;
04399     i=p+strlen(src);
04400   }
04401   output+=input.mid(i,input.length()-i);
04402   return output;
04403 }
04404 
04405 //----------------------------------------------------------------------
04406 
04407 struct FindFileCacheElem
04408 {
04409   FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
04410   FileDef *fileDef;
04411   bool isAmbig;
04412 };
04413 
04414 static QCache<FindFileCacheElem> g_findFileDefCache(5000);
04415 
04416 FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
04417 {
04418   ambig=FALSE;
04419   if (n==0) return 0;
04420 
04421   QCString key;
04422   key.sprintf("%p:",fnDict);
04423   key+=n;
04424 
04425   g_findFileDefCache.setAutoDelete(TRUE);
04426   FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
04427   if (cachedResult)
04428   {
04429     ambig = cachedResult->isAmbig;
04430     return cachedResult->fileDef;
04431   }
04432   else
04433   {
04434     cachedResult = new FindFileCacheElem(0,FALSE);
04435   }
04436 
04437   QCString name=convertToQCString(QDir::cleanDirPath(n));
04438   QCString path;
04439   int slashPos;
04440   FileName *fn;
04441   if (name.isEmpty()) goto exit;
04442   slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
04443   if (slashPos!=-1)
04444   {
04445     path=name.left(slashPos+1);
04446     name=name.right(name.length()-slashPos-1); 
04447   }
04448   //printf("findFileDef path=`%s' name=`%s'\n",path.data(),name.data());
04449   if (name.isEmpty()) goto exit;
04450   if ((fn=(*fnDict)[name]))
04451   {
04452     if (fn->count()==1)
04453     {
04454       FileDef *fd = fn->getFirst();
04455       if (path.isEmpty() || fd->getPath().right(path.length())==path)
04456       {
04457         cachedResult->fileDef = fd;
04458         g_findFileDefCache.insert(key,cachedResult);
04459         return fd;
04460       }
04461     }
04462     else // file name alone is ambigious
04463     {
04464       int count=0;
04465       FileNameIterator fni(*fn);
04466       FileDef *fd;
04467       FileDef *lastMatch=0;
04468       QCString pathStripped = stripFromIncludePath(path);
04469       for (fni.toFirst();(fd=fni.current());++fni)
04470       {
04471         QCString fdStripPath = stripFromIncludePath(fd->getPath());
04472         if (path.isEmpty() || fdStripPath==pathStripped) 
04473         { 
04474           count++; 
04475           lastMatch=fd; 
04476         }
04477       }
04478       ambig=(count>1);
04479       cachedResult->isAmbig = ambig;
04480       cachedResult->fileDef = lastMatch;
04481       g_findFileDefCache.insert(key,cachedResult);
04482       return lastMatch;
04483     }
04484   }
04485 exit:
04486   g_findFileDefCache.insert(key,cachedResult);
04487   return 0;
04488 }
04489 
04490 //----------------------------------------------------------------------
04491 
04492 QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
04493 {
04494   QCString result;
04495   QCString name=n;
04496   QCString path;
04497   int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
04498   if (slashPos!=-1)
04499   {
04500     path=name.left(slashPos+1);
04501     name=name.right(name.length()-slashPos-1); 
04502   }
04503   FileName *fn;
04504   if ((fn=(*fnDict)[name]))
04505   {
04506     FileNameIterator fni(*fn);
04507     FileDef *fd;
04508     for (fni.toFirst();(fd=fni.current());++fni)
04509     {
04510       if (path.isEmpty() || fd->getPath().right(path.length())==path)
04511       {
04512         result+="   "+fd->absFilePath()+"\n";
04513       }
04514     }
04515   }
04516   return result;
04517 }
04518 
04519 //----------------------------------------------------------------------
04520 
04521 QCString substituteKeywords(const QCString &s,const char *title,const QCString &relPath)
04522 {
04523   QCString result = s.copy();
04524   if (title) result = substitute(result,"$title",title);
04525   result = substitute(result,"$datetime",dateToString(TRUE));
04526   result = substitute(result,"$date",dateToString(FALSE));
04527   result = substitute(result,"$year",yearToString());
04528   result = substitute(result,"$doxygenversion",versionString);
04529   result = substitute(result,"$projectname",Config_getString("PROJECT_NAME"));
04530   result = substitute(result,"$projectnumber",Config_getString("PROJECT_NUMBER"));
04531   result = substitute(result,"$relpath$",relPath);
04532   return result;
04533 }
04534 
04535 //----------------------------------------------------------------------
04536 
04541 int getPrefixIndex(const QCString &name)
04542 {
04543   if (name.isEmpty()) return 0;
04544   QStrList &sl = Config_getList("IGNORE_PREFIX");
04545   char *s = sl.first();
04546   while (s)
04547   {
04548     const char *ps=s;
04549     const char *pd=name.data();
04550     int i=0;
04551     while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
04552     if (*ps==0 && *pd!=0)
04553     {
04554       return i;
04555     }
04556     s = sl.next();
04557   }
04558   return 0;
04559 }
04560 
04561 //----------------------------------------------------------------------------
04562 
04563 static void initBaseClassHierarchy(BaseClassList *bcl)
04564 {
04565   if (bcl==0) return;
04566   BaseClassListIterator bcli(*bcl);
04567   for ( ; bcli.current(); ++bcli)
04568   {
04569     ClassDef *cd=bcli.current()->classDef;
04570     if (cd->baseClasses()==0) // no base classes => new root
04571     {
04572       initBaseClassHierarchy(cd->baseClasses());
04573     }
04574     cd->visited=FALSE;
04575   }
04576 }
04577 
04578 //----------------------------------------------------------------------------
04579 
04580 void initClassHierarchy(ClassSDict *cl)
04581 {
04582   ClassSDict::Iterator cli(*cl);
04583   ClassDef *cd;
04584   for ( ; (cd=cli.current()); ++cli)
04585   {
04586     cd->visited=FALSE;
04587     initBaseClassHierarchy(cd->baseClasses());
04588   }
04589 }
04590 
04591 //----------------------------------------------------------------------------
04592 
04593 bool hasVisibleRoot(BaseClassList *bcl)
04594 {
04595   if (bcl)
04596   {
04597     BaseClassListIterator bcli(*bcl);
04598     for ( ; bcli.current(); ++bcli)
04599     {
04600       ClassDef *cd=bcli.current()->classDef;
04601       if (cd->isVisibleInHierarchy()) return TRUE;
04602       hasVisibleRoot(cd->baseClasses());
04603     }
04604   }
04605   return FALSE;
04606 }
04607 
04608 //----------------------------------------------------------------------
04609 
04610 QCString escapeCharsInString(const char *name,bool allowDots)
04611 {
04612   static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES");
04613   QCString result;
04614   char c;
04615   const char *p=name;
04616   while ((c=*p++)!=0)
04617   {
04618     switch(c)
04619     {
04620       case '_': result+="__"; break;
04621       case '-': result+="-";  break;
04622       case ':': result+="_1"; break;
04623       case '/': result+="_2"; break;
04624       case '<': result+="_3"; break;
04625       case '>': result+="_4"; break;
04626       case '*': result+="_5"; break;
04627       case '&': result+="_6"; break;
04628       case '|': result+="_7"; break;
04629       case '.': if (allowDots) result+="."; else result+="_8"; break;
04630       case '!': result+="_9"; break;
04631       case ',': result+="_00"; break;
04632       case ' ': result+="_01"; break;
04633       case '{': result+="_02"; break;
04634       case '}': result+="_03"; break;
04635       case '?': result+="_04"; break;
04636       case '^': result+="_05"; break;
04637       case '%': result+="_06"; break;
04638       case '(': result+="_07"; break;
04639       case ')': result+="_08"; break;
04640       case '+': result+="_09"; break;
04641       case '=': result+="_0A"; break;
04642       default: 
04643                 if (caseSenseNames || !isupper(c))
04644                 {
04645                   result+=c;
04646                 }
04647                 else
04648                 {
04649                   result+="_";
04650                   result+=tolower(c); 
04651                 }
04652                 break;
04653     }
04654   }
04655   return result;
04656 }
04657 
04662 QCString convertNameToFile(const char *name,bool allowDots)
04663 {
04664   static bool shortNames = Config_getBool("SHORT_NAMES");
04665   static bool createSubdirs = Config_getBool("CREATE_SUBDIRS");
04666   QCString result;
04667   if (shortNames) // use short names only
04668   {
04669     static QDict<int> usedNames(10007);
04670     usedNames.setAutoDelete(TRUE);
04671     static int count=1;
04672 
04673     int *value=usedNames.find(name);
04674     int num;
04675     if (value==0)
04676     {
04677       usedNames.insert(name,new int(count));
04678       num = count++;
04679     }
04680     else
04681     {
04682       num = *value;
04683     }
04684     result.sprintf("a%05d",num); 
04685   }
04686   else // long names
04687   {
04688     result=escapeCharsInString(name,allowDots);
04689     int resultLen = result.length();
04690     if (resultLen>=128) // prevent names that cannot be created!
04691     {
04692       // third algorithm based on MD5 hash
04693       uchar md5_sig[16];
04694       QCString sigStr(33);
04695       MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
04696       MD5SigToString(md5_sig,sigStr.data(),33);
04697       result=result.left(128-32)+sigStr; 
04698     }
04699   }
04700   if (createSubdirs)
04701   {
04702     int l1Dir=0,l2Dir=0;
04703 
04704 #if MAP_ALGO==ALGO_COUNT 
04705     // old algorithm, has the problem that after regeneration the
04706     // output can be located in a different dir.
04707     if (Doxygen::htmlDirMap==0) 
04708     {
04709       Doxygen::htmlDirMap=new QDict<int>(100003);
04710       Doxygen::htmlDirMap->setAutoDelete(TRUE);
04711     }
04712     static int curDirNum=0;
04713     int *dirNum = Doxygen::htmlDirMap->find(result);
04714     if (dirNum==0) // new name
04715     {
04716       Doxygen::htmlDirMap->insert(result,new int(curDirNum)); 
04717       l1Dir = (curDirNum)&0xf;    // bits 0-3
04718       l2Dir = (curDirNum>>4)&0xff; // bits 4-11
04719       curDirNum++;
04720     }
04721     else // existing name
04722     {
04723       l1Dir = (*dirNum)&0xf;       // bits 0-3
04724       l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
04725     }
04726 #elif MAP_ALGO==ALGO_CRC16
04727     // second algorithm based on CRC-16 checksum
04728     int dirNum = qChecksum(result,result.length());
04729     l1Dir = dirNum&0xf;
04730     l2Dir = (dirNum>>4)&0xff;
04731 #elif MAP_ALGO==ALGO_MD5
04732     // third algorithm based on MD5 hash
04733     uchar md5_sig[16];
04734     MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
04735     l1Dir = md5_sig[14]&0xf;
04736     l2Dir = md5_sig[15];
04737 #endif
04738     result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
04739   }
04740   //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
04741   return result;
04742 }
04743 
04744 QCString relativePathToRoot(const char *name)
04745 {
04746   QCString result;
04747   if (Config_getBool("CREATE_SUBDIRS"))
04748   {
04749     if (name==0)
04750     {
04751       return REL_PATH_TO_ROOT;
04752     }
04753     else
04754     {
04755       QCString n = name;
04756       int i = n.findRev('/');
04757       if (i!=-1)
04758       {
04759         result=REL_PATH_TO_ROOT;
04760       }
04761     }
04762   }
04763   return result;
04764 }
04765 
04766 void createSubDirs(QDir &d)
04767 {
04768   if (Config_getBool("CREATE_SUBDIRS"))
04769   {
04770     // create 4096 subdirectories
04771     int l1,l2;
04772     for (l1=0;l1<16;l1++)
04773     {
04774       d.mkdir(QString().sprintf("d%x",l1));
04775       for (l2=0;l2<256;l2++)
04776       {
04777         d.mkdir(QString().sprintf("d%x/d%02x",l1,l2));
04778       }
04779     }
04780   }
04781 }
04782 
04786 void extractNamespaceName(const QCString &scopeName,
04787     QCString &className,QCString &namespaceName,
04788     bool allowEmptyClass)
04789 {
04790   int i,p;
04791   QCString clName=scopeName;
04792   NamespaceDef *nd = 0;
04793   if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
04794   { // the whole name is a namespace (and not a class)
04795     namespaceName=nd->name().copy();
04796     className.resize(0);
04797     goto done;
04798   }
04799   p=clName.length()-2;
04800   while (p>=0 && (i=clName.findRev("::",p))!=-1) 
04801     // see if the first part is a namespace (and not a class)
04802   {
04803     //printf("Trying %s\n",clName.left(i).data());
04804     if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
04805     {
04806       //printf("found!\n");
04807       namespaceName=nd->name().copy();
04808       className=clName.right(clName.length()-i-2);
04809       goto done;
04810     } 
04811     p=i-2; // try a smaller piece of the scope
04812   }
04813   //printf("not found!\n");
04814 
04815   // not found, so we just have to guess.
04816   className=scopeName.copy();
04817   namespaceName.resize(0);
04818 
04819 done:
04820   if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
04821   {
04822     // class and namespace with the same name, correct to return the class.
04823     className=namespaceName.copy();
04824     namespaceName.resize(0);
04825   }
04826   //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
04827   //       className.data(),namespaceName.data());
04828   return;
04829 }
04830 
04831 QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
04832 {
04833   QCString result=scope.copy();
04834   if (!templ.isEmpty() && scope.find('<')==-1)
04835   {
04836     int si,pi=0;
04837     ClassDef *cd=0;
04838     while (
04839         (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) && 
04840         ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0) 
04841         ) 
04842     { 
04843       //printf("Tried `%s'\n",(scope.left(si)+templ).data()); 
04844       pi=si+2; 
04845     }
04846     if (si==-1) // not nested => append template specifier
04847     {
04848       result+=templ; 
04849     }
04850     else // nested => insert template specifier before after first class name
04851     {
04852       result=scope.left(si) + templ + scope.right(scope.length()-si);
04853     }
04854   }
04855   //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
04856   //    scope.data(),templ.data(),result.data());
04857   return result;
04858 }
04859 
04860 #if 0 // original version
04861 
04864 QCString stripScope(const char *name)
04865 {
04866   QCString result = name;
04867   int l=result.length();
04868   int p=l-1;
04869   bool done;
04870   int count;
04871 
04872   while (p>=0)
04873   {
04874     char c=result.at(p);
04875     switch (c)
04876     {
04877       case ':': 
04878         //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
04879         return result.right(l-p-1);
04880       case '>':
04881         count=1;
04882         done=FALSE;
04883         //printf("pos < = %d\n",p);
04884         p--;
04885         while (p>=0 && !done)
04886         {
04887           c=result.at(p--);
04888           switch (c)
04889           {
04890             case '>': count++; break;
04891             case '<': count--; if (count<=0) done=TRUE; break;
04892             default: 
04893                       //printf("c=%c count=%d\n",c,count);
04894                       break;
04895           }
04896         }
04897         //printf("pos > = %d\n",p+1);
04898         break;
04899       default:
04900         p--;
04901     }
04902   }
04903   //printf("stripScope(%s)=%s\n",name,name);
04904   return name;
04905 }
04906 #endif
04907 
04908 // new version by Davide Cesari which also works for Fortran
04909 QCString stripScope(const char *name)
04910 {
04911   QCString result = name;
04912   int l=result.length();
04913   int p;
04914   bool done = FALSE;
04915   bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
04916   int count=0;
04917 
04918   do
04919   {
04920     p=l-1; // start at the end of the string
04921     while (p>=0 && count>=0)
04922     {
04923       char c=result.at(p);
04924       switch (c)
04925       {
04926         case ':': 
04927           //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
04928           return result.right(l-p-1);
04929         case '>':
04930           if (skipBracket) // we don't care about brackets
04931           {
04932             p--;
04933           }
04934           else // count open/close brackets
04935           {
04936             if (p>0 && result.at(p-1)=='>') // skip >> operator
04937             {
04938               p-=2;
04939               break;
04940             }
04941             count=1;
04942             //printf("pos < = %d\n",p);
04943             p--;
04944             while (p>=0)
04945             {
04946               c=result.at(p--);
04947               switch (c)
04948               {
04949                 case '>': 
04950                   count++; 
04951                   break;
04952                 case '<': 
04953                   if (p>0)
04954                   {
04955                     if (result.at(p-1) == '<') // skip << operator
04956                     {
04957                       p--;
04958                       break;
04959                     }
04960                   }
04961                   count--; 
04962                   break;
04963                 default: 
04964                   //printf("c=%c count=%d\n",c,count);
04965                   break;
04966               }
04967             }
04968           }
04969           //printf("pos > = %d\n",p+1);
04970           break;
04971         default:
04972           p--;
04973       }
04974     }
04975     done = count==0 || skipBracket; // reparse if brackets do not match
04976     skipBracket=TRUE;
04977   }
04978   while (!done); // if < > unbalanced repeat ignoring them
04979   //printf("stripScope(%s)=%s\n",name,name);
04980   return name;
04981 }
04982 
04983 
04985 QCString convertToXML(const char *s)
04986 {
04987   QCString result;
04988   if (s==0) return result;
04989   const char *p=s;
04990   char c;
04991   while ((c=*p++))
04992   {
04993     switch (c)
04994     {
04995       case '<':  result+="&lt;";   break;
04996       case '>':  result+="&gt;";   break;
04997       case '&':  result+="&amp;";  break;
04998       case '\'': result+="&apos;"; break; 
04999       case '"':  result+="&quot;"; break;
05000       default:   result+=c;        break;
05001     }
05002   }
05003   return result;
05004 }
05005 
05007 QCString convertToHtml(const char *s,bool keepEntities)
05008 {
05009   QCString result;
05010   if (s==0) return result;
05011   const char *p=s;
05012   char c;
05013   while ((c=*p++))
05014   {
05015     switch (c)
05016     {
05017       case '<':  result+="&lt;";   break;
05018       case '>':  result+="&gt;";   break;
05019       case '&':  if (keepEntities)
05020                  {
05021                    const char *e=p;
05022                    char ce;
05023                    while ((ce=*e++))
05024                    {
05025                      if (ce==';' || (!(isId(ce) || ce=='#'))) break;
05026                    }
05027                    if (ce==';') // found end of an entity
05028                    {
05029                      // copy entry verbatim
05030                      result+=c;
05031                      while (p<e) result+=*p++;
05032                    }
05033                    else
05034                    {
05035                      result+="&amp;";
05036                    }
05037                  }
05038                  else
05039                  {
05040                    result+="&amp;";  
05041                  }
05042                  break;
05043       case '\'': result+="&#39;"; break; 
05044       case '"':  result+="&quot;"; break;
05045       default:   result+=c;        break;
05046     }
05047   }
05048   return result;
05049 }
05050 
05051 QCString convertCharEntitiesToUTF8(const QCString &s)
05052 {
05053   static QDict<char> entityMap(67);
05054   static bool init=TRUE;
05055   QCString result;
05056   static QRegExp entityPat("&[a-zA-Z]+;");
05057 
05058   if (init)
05059   {
05060     entityMap.insert("copy",  "\xC2\xA9");
05061     entityMap.insert("tm",    "\xE2\x84\xA2");
05062     entityMap.insert("trade", "\xE2\x84\xA2");
05063     entityMap.insert("reg",   "\xC2\xAE");
05064     entityMap.insert("lsquo", "\xE2\x80\x98");
05065     entityMap.insert("rsquo", "\xE2\x80\x99");
05066     entityMap.insert("ldquo", "\xE2\x80\x9C");
05067     entityMap.insert("rdquo", "\xE2\x80\x9D");
05068     entityMap.insert("ndash", "\xE2\x80\x93");
05069     entityMap.insert("mdash", "\xE2\x80\x94");
05070     entityMap.insert("Auml",  "\xC3\x84");
05071     entityMap.insert("Euml",  "\xC3\x8B");
05072     entityMap.insert("Iuml",  "\xC3\x8F");
05073     entityMap.insert("Ouml",  "\xC3\x96");
05074     entityMap.insert("Uuml",  "\xC3\x9C");
05075     entityMap.insert("Yuml",  "\xC5\xB8");
05076     entityMap.insert("auml",  "\xC3\xA4");
05077     entityMap.insert("euml",  "\xC3\xAB");
05078     entityMap.insert("iuml",  "\xC3\xAF");
05079     entityMap.insert("ouml",  "\xC3\xB6");
05080     entityMap.insert("uuml",  "\xC3\xBC");
05081     entityMap.insert("yuml",  "\xC3\xBF");
05082     entityMap.insert("Aacute","\xC3\x81");
05083     entityMap.insert("Eacute","\xC3\x89");
05084     entityMap.insert("Iacute","\xC3\x8D");
05085     entityMap.insert("Oacute","\xC3\x93");
05086     entityMap.insert("Uacute","\xC3\x9A");
05087     entityMap.insert("aacute","\xC3\xA1");
05088     entityMap.insert("eacute","\xC3\xA9");
05089     entityMap.insert("iacute","\xC3\xAD");
05090     entityMap.insert("oacute","\xC3\xB3");
05091     entityMap.insert("uacute","\xC3\xBA");
05092     entityMap.insert("Agrave","\xC3\x80");
05093     entityMap.insert("Egrave","\xC3\x88");
05094     entityMap.insert("Igrave","\xC3\x8C");
05095     entityMap.insert("Ograve","\xC3\x92");
05096     entityMap.insert("Ugrave","\xC3\x99");
05097     entityMap.insert("agrave","\xC3\xA0");
05098     entityMap.insert("egrave","\xC3\xA8");
05099     entityMap.insert("igrave","\xC3\xAC");
05100     entityMap.insert("ograve","\xC3\xB2");
05101     entityMap.insert("ugrave","\xC3\xB9");
05102     entityMap.insert("Acirc", "\xC3\x82");
05103     entityMap.insert("Ecirc", "\xC3\x8A");
05104     entityMap.insert("Icirc", "\xC3\x8E");
05105     entityMap.insert("Ocirc", "\xC3\x94");
05106     entityMap.insert("Ucirc", "\xC3\x9B");
05107     entityMap.insert("acirc", "\xC3\xA2");
05108     entityMap.insert("ecirc", "\xC3\xAA");
05109     entityMap.insert("icirc", "\xC3\xAE");
05110     entityMap.insert("ocirc", "\xC3\xB4");
05111     entityMap.insert("ucirc", "\xC3\xBB");
05112     entityMap.insert("Atilde","\xC3\x83");
05113     entityMap.insert("Ntilde","\xC3\x91");
05114     entityMap.insert("Otilde","\xC3\x95");
05115     entityMap.insert("atilde","\xC3\xA3");
05116     entityMap.insert("ntilde","\xC3\xB1");
05117     entityMap.insert("otilde","\xC3\xB5");
05118     entityMap.insert("szlig", "\xC3\x9F");
05119     entityMap.insert("Ccedil","\xC3\x87");
05120     entityMap.insert("ccedil","\xC3\xA7");
05121     entityMap.insert("Aring", "\xC3\x85");
05122     entityMap.insert("aring", "\xC3\xA5");
05123     entityMap.insert("nbsp",  "\xC2\xA0");
05124     init=FALSE;
05125   }
05126 
05127   if (s==0) return result;
05128   int p,i=0,l;
05129   while ((p=entityPat.match(s,i,&l))!=-1)
05130   {
05131     if (p>i) result+=s.mid(i,p-i);
05132     QCString entity = s.mid(p+1,l-2);
05133     char *code = entityMap.find(entity);
05134     if (code)
05135     {
05136       result+=code;
05137     }
05138     else
05139     {
05140       result+=s.mid(p,l);
05141     }
05142     i=p+l;
05143   }
05144   result+=s.mid(i,s.length()-i);
05145   return result;
05146 }
05147 
05151 QCString getOverloadDocs()
05152 {
05153   return theTranslator->trOverloadText();
05154   //"This is an overloaded member function, "
05155   //       "provided for convenience. It differs from the above "
05156   //       "function only in what argument(s) it accepts.";
05157 }
05158 
05159 void addMembersToMemberGroup(MemberList *ml,
05160     MemberGroupSDict **ppMemberGroupSDict,
05161     Definition *context)
05162 {
05163   ASSERT(context!=0);
05164   //printf("addMemberToMemberGroup()\n");
05165   if (ml==0) return;
05166   MemberListIterator mli(*ml);
05167   MemberDef *md;
05168   uint index;
05169   for (index=0;(md=mli.current());)
05170   {
05171     if (md->isEnumerate()) // insert enum value of this enum into groups
05172     {
05173       LockingPtr<MemberList> fmdl=md->enumFieldList();
05174       if (fmdl!=0)
05175       {
05176         MemberDef *fmd=fmdl->first();
05177         while (fmd)
05178         {
05179           int groupId=fmd->getMemberGroupId();
05180           if (groupId!=-1)
05181           {
05182             MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
05183             //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
05184             //QCString *pDocs      = Doxygen::memberDocDict[groupId];
05185             if (info)
05186             {
05187               if (*ppMemberGroupSDict==0)
05188               {
05189                 *ppMemberGroupSDict = new MemberGroupSDict;
05190                 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
05191               }
05192               MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
05193               if (mg==0)
05194               {
05195                 mg = new MemberGroup(
05196                     context,
05197                     groupId,
05198                     info->header,
05199                     info->doc,
05200                     info->docFile
05201                     );
05202                 (*ppMemberGroupSDict)->append(groupId,mg);
05203               }
05204               mg->insertMember(fmd); // insert in member group
05205               fmd->setMemberGroup(mg);
05206             }
05207           }
05208           fmd=fmdl->next();
05209         }
05210       }
05211     }
05212     int groupId=md->getMemberGroupId();
05213     if (groupId!=-1)
05214     {
05215       MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
05216       //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
05217       //QCString *pDocs      = Doxygen::memberDocDict[groupId];
05218       if (info)
05219       {
05220         if (*ppMemberGroupSDict==0)
05221         {
05222           *ppMemberGroupSDict = new MemberGroupSDict;
05223           (*ppMemberGroupSDict)->setAutoDelete(TRUE);
05224         }
05225         MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
05226         if (mg==0)
05227         {
05228           mg = new MemberGroup(
05229               context,
05230               groupId,
05231               info->header,
05232               info->doc,
05233               info->docFile
05234               );
05235           (*ppMemberGroupSDict)->append(groupId,mg);
05236         }
05237         md = ml->take(index); // remove from member list
05238         mg->insertMember(md); // insert in member group
05239         md->setMemberGroup(mg);
05240         continue;
05241       }
05242     }
05243     ++mli;++index;
05244   }
05245 }
05246 
05252 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec)
05253 {
05254   static const QRegExp re("[a-z_A-Z][a-z_A-Z0-9:]*");
05255   name.resize(0);
05256   templSpec.resize(0);
05257   int i,l;
05258   int typeLen=type.length();
05259   if (typeLen>0)
05260   {
05261     if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
05262     {
05263       int ts=i+l;
05264       int te=ts;
05265       int tl=0;
05266       while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
05267       if (type.at(ts)=='<') // assume template instance
05268       {
05269         // locate end of template
05270         te=ts+1;
05271         int brCount=1;
05272         while (te<typeLen && brCount!=0)
05273         {
05274           if (type.at(te)=='<') 
05275           {
05276             if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
05277           }
05278           if (type.at(te)=='>') 
05279           {
05280             if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
05281           }
05282           te++;
05283         }
05284       }
05285       name = type.mid(i,l);
05286       if (te>ts) 
05287       {
05288         templSpec = type.mid(ts,te-ts),tl+=te-ts;
05289         pos=i+l+tl;
05290       }
05291       else // no template part
05292       {
05293         pos=i+l;
05294       }
05295       //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
05296       //    type.data(),pos,name.data(),templSpec.data());
05297       return i;
05298     }
05299   }
05300   pos = typeLen;
05301   //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
05302   //       type.data(),pos,name.data(),templSpec.data());
05303   return -1;
05304 }
05305 
05312 QCString substituteTemplateArgumentsInString(
05313     const QCString &name,
05314     ArgumentList *formalArgs,
05315     ArgumentList *actualArgs)
05316 {
05317   //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
05318   //    name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
05319   if (formalArgs==0) return name;
05320   QCString result;
05321   static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*");
05322   int p=0,l,i;
05323   // for each identifier in the base class name (e.g. B<T> -> B and T)
05324   while ((i=re.match(name,p,&l))!=-1)
05325   {
05326     result += name.mid(p,i-p);
05327     QCString n = name.mid(i,l);
05328     ArgumentListIterator formAli(*formalArgs);
05329     Argument *formArg;
05330     Argument *actArg=actualArgs->first();
05331 
05332     // if n is a template argument, then we substitute it
05333     // for its template instance argument.
05334     bool found=FALSE;
05335     for (formAli.toFirst();
05336         (formArg=formAli.current()) && !found;
05337         ++formAli,actArg=actualArgs->next()
05338         )
05339     {
05340       //printf("formArg->type=%s\n",formArg->type.data());
05341       if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
05342       {
05343         //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
05344         //  n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
05345         if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
05346         {
05347           // replace formal argument with the actual argument of the instance
05348           if (actArg->name.isEmpty())
05349           {
05350             result += actArg->type+" "; 
05351           }
05352           else // for case where the actual arg is something like "unsigned int"
05353             // the "int" part is in actArg->name.
05354           {
05355             result += actArg->type+" "+actArg->name+" "; 
05356           }
05357           found=TRUE;
05358         }
05359         else if (formArg->name==n && actArg==0 && !formArg->defval.isEmpty() &&
05360             formArg->defval!=name /* to prevent recursion */
05361             )
05362         {
05363           result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
05364           found=TRUE;
05365         }
05366       }
05367       else if (formArg->name==n && actArg==0 && !formArg->defval.isEmpty() &&
05368           formArg->defval!=name /* to prevent recursion */
05369           )
05370       {
05371         result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
05372         found=TRUE;
05373       }
05374     }
05375     if (!found) result += n;
05376     p=i+l;
05377   }
05378   result+=name.right(name.length()-p);
05379   //printf("      Inheritance relation %s -> %s\n",
05380   //    name.data(),result.data());
05381   return result;
05382 }
05383 
05384 
05388 ArgumentList *copyArgumentList(const ArgumentList *src)
05389 {
05390   ASSERT(src!=0);
05391   ArgumentList *dst = new ArgumentList;
05392   dst->setAutoDelete(TRUE);
05393   ArgumentListIterator tali(*src);
05394   Argument *a;
05395   for (;(a=tali.current());++tali)
05396   {
05397     dst->append(new Argument(*a));
05398   }
05399   dst->constSpecifier    = src->constSpecifier;
05400   dst->volatileSpecifier = src->volatileSpecifier;
05401   dst->pureSpecifier     = src->pureSpecifier;
05402   return dst;
05403 }
05404 
05408 QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
05409 {
05410   ASSERT(srcLists!=0);
05411   QList<ArgumentList> *dstLists = new QList<ArgumentList>;
05412   dstLists->setAutoDelete(TRUE);
05413   QListIterator<ArgumentList> sli(*srcLists);
05414   ArgumentList *sl;
05415   for (;(sl=sli.current());++sli)
05416   {
05417     dstLists->append(copyArgumentList(sl));
05418   }
05419   return dstLists;
05420 }
05421 
05429 QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
05430     bool parentOnly,
05431     QCString *pLastScopeStripped)
05432 {
05433   QCString result;
05434   int p=0;
05435   int l=fullName.length();
05436   int i=fullName.find('<');
05437   while (i!=-1)
05438   {
05439     //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
05440     int e=i+1;
05441     bool done=FALSE;
05442     int count=1;
05443     while (e<l && !done)
05444     {
05445       char c=fullName.at(e++);
05446       if (c=='<') 
05447       {
05448         count++;
05449       }
05450       else if (c=='>') 
05451       {
05452         count--;
05453         done = count==0;
05454       }
05455     }
05456     int si= fullName.find("::",e);
05457 
05458     if (parentOnly && si==-1) break; 
05459     // we only do the parent scope, so we stop here if needed
05460 
05461     result+=fullName.mid(p,i-p);
05462     //printf("  trying %s\n",(result+fullName.mid(i,e-i)).data());
05463     if (getClass(result+fullName.mid(i,e-i))!=0) 
05464     {
05465       result+=fullName.mid(i,e-i);
05466       //printf("2:result+=%s\n",fullName.mid(i,e-i-1).data());
05467     }
05468     else if (pLastScopeStripped)
05469     {
05470       *pLastScopeStripped=fullName.mid(i,e-i);
05471     }
05472     p=e;
05473     i=fullName.find('<',p);
05474   }
05475   result+=fullName.right(l-p);
05476   //printf("3:result+=%s\n",fullName.right(l-p).data());
05477   return result;
05478 }
05479 
05489 QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
05490 {
05491   // case leftScope=="A" rightScope=="A::B" => result = "A::B"
05492   if (leftScopeMatch(rightScope,leftScope)) return rightScope;
05493   QCString result;
05494   int i=0,p=leftScope.length();
05495 
05496   // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
05497   // case leftScope=="A::B" rightScope=="B" => result = "A::B"
05498   bool found=FALSE;
05499   while ((i=leftScope.findRev("::",p))!=-1)
05500   {
05501     if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
05502     {
05503       result = leftScope.left(i+2)+rightScope;
05504       found=TRUE;
05505     }
05506     p=i-1;
05507   }
05508   if (found) return result;
05509 
05510   // case leftScope=="A" rightScope=="B" => result = "A::B"
05511   result=leftScope.copy();
05512   if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
05513   result+=rightScope;
05514   return result;
05515 }
05516 
05524 int getScopeFragment(const QCString &s,int p,int *l)
05525 {
05526   int sl=s.length();
05527   int sp=p;
05528   int count=0;
05529   bool done;
05530   if (sp>=sl) return -1;
05531   while (sp<sl)
05532   {
05533     char c=s.at(sp);
05534     if (c==':') sp++,p++; else break;
05535   }
05536   while (sp<sl)
05537   {
05538     char c=s.at(sp);
05539     switch (c)
05540     {
05541       case ':': // found next part
05542         goto found;
05543       case '<': // skip template specifier
05544         count=1;sp++;
05545         done=FALSE;
05546         while (sp<sl && !done)
05547         {
05548           // TODO: deal with << and >> operators!
05549           char c=s.at(sp++);
05550           switch(c)
05551           {
05552             case '<': count++; break;
05553             case '>': count--; if (count==0) done=TRUE; break;
05554             default: break;
05555           }
05556         }
05557         break;
05558       default:
05559         sp++;
05560         break;
05561     }
05562   }
05563 found:
05564   *l=sp-p;
05565   //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
05566   return p;
05567 }
05568 
05569 //----------------------------------------------------------------------------
05570 
05571 PageDef *addRelatedPage(const char *name,const QCString &ptitle,
05572     const QCString &doc,
05573     QList<SectionInfo> * /*anchors*/,
05574     const char *fileName,int startLine,
05575     const QList<ListItemInfo> *sli,
05576     GroupDef *gd,
05577     TagInfo *tagInfo
05578     )
05579 {
05580   PageDef *pd=0;
05581   //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
05582   if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
05583   {
05584     // append documentation block to the page.
05585     pd->setDocumentation(doc,fileName,startLine);
05586     //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name);
05587   }
05588   else // new page
05589   {
05590     QCString baseName=name;
05591     if (baseName.right(4)==".tex") 
05592       baseName=baseName.left(baseName.length()-4);
05593     else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
05594       baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
05595 
05596     QCString title=ptitle.stripWhiteSpace();
05597     pd=new PageDef(fileName,startLine,baseName,doc,title);
05598 
05599     pd->setRefItems(sli);
05600 
05601     if (tagInfo)
05602     {
05603       pd->setReference(tagInfo->tagName);
05604     }
05605 
05606     QCString pageName;
05607     if (Config_getBool("CASE_SENSE_NAMES"))
05608       pageName=pd->name();
05609     else
05610       pageName=pd->name().lower();
05611     pd->setFileName(pageName);
05612 
05613     //printf("Appending page `%s'\n",baseName.data());
05614     Doxygen::pageSDict->append(baseName,pd);
05615 
05616     if (gd) gd->addPage(pd);
05617 
05618     if (!pd->title().isEmpty())
05619     {
05620       //outputList->writeTitle(pi->name,pi->title);
05621 
05622       // a page name is a label as well!
05623       QCString file;
05624       if (gd)
05625       {
05626         file=gd->getOutputFileBase();
05627       }
05628       else if (pd->getGroupDef())
05629       {
05630         file=pd->getGroupDef()->getOutputFileBase().copy();
05631       }
05632       else
05633       {
05634         file=pageName;
05635       }
05636       SectionInfo *si=new SectionInfo(
05637           file,pd->name(),pd->title(),SectionInfo::Page,pd->getReference());
05638       //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
05639       //      si->label.data(),si->definition?si->definition->name().data():"<none>",
05640       //      si->fileName.data());
05641       //printf("  SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
05642       //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
05643       Doxygen::sectionDict.insert(pageName,si);
05644     }
05645   }
05646   return pd;
05647 }
05648 
05649 //----------------------------------------------------------------------------
05650 
05651 void addRefItem(const QList<ListItemInfo> *sli,
05652     const char *prefix,
05653     const char *name,const char *title,const char *args)
05654 {
05655   //printf("addRefItem(sli=%p,prefix=%s,name=%s,title=%s,args=%s)\n",sli,prefix,name,title,args);
05656   if (sli)
05657   {
05658     QListIterator<ListItemInfo> slii(*sli);
05659     ListItemInfo *lii;
05660     for (slii.toFirst();(lii=slii.current());++slii)
05661     {
05662       RefList *refList = Doxygen::xrefLists->find(lii->type);
05663       if (refList && 
05664           (
05665            // either not a built-in list or the list is enabled
05666            (lii->type!="todo"       || Config_getBool("GENERATE_TODOLIST")) && 
05667            (lii->type!="test"       || Config_getBool("GENERATE_TESTLIST")) && 
05668            (lii->type!="bug"        || Config_getBool("GENERATE_BUGLIST"))  && 
05669            (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
05670           ) 
05671          )
05672       {
05673         RefItem *item = refList->getRefItem(lii->itemId);
05674         ASSERT(item!=0);
05675         //printf("anchor=%s written=%d\n",item->listAnchor.data(),item->written);
05676         if (item->written) return;
05677 
05678         QCString doc(1000);
05679         doc =  "\\anchor ";
05680         doc += item->listAnchor;
05681         doc += " <dl><dt>";
05682         doc += prefix;
05683         doc += " \\_internalref ";
05684         doc += name;
05685         doc += " \"";
05686         doc += title;
05687         doc += "\"";
05688         if (args) doc += args;
05689         doc += "</dt>\n<dd>";
05690         doc += item->text;
05691         doc += "</dd></dl>\n";
05692         addRelatedPage(refList->listName(),refList->pageTitle(),doc,0,refList->listName(),1,0,0,0);
05693         item->written=TRUE;
05694       }
05695     }
05696   }
05697 }
05698 
05699 void addGroupListToTitle(OutputList &ol,Definition *d)
05700 {
05701   LockingPtr<GroupList> groups = d->partOfGroups();
05702   if (groups!=0) // write list of group to which this definition belongs
05703   {
05704     ol.pushGeneratorState();
05705     ol.disableAllBut(OutputGenerator::Html);
05706     ol.lineBreak();
05707     ol.startSmall();
05708     ol.docify("[");
05709     GroupListIterator gli(*groups);
05710     GroupDef *gd;
05711     bool first=TRUE;
05712     for (gli.toFirst();(gd=gli.current());++gli)
05713     {
05714       if (!first) { ol.docify(","); ol.writeNonBreakableSpace(1); } else first=FALSE; 
05715       ol.writeObjectLink(gd->getReference(),
05716           gd->getOutputFileBase(),0,gd->groupTitle());
05717     }
05718     ol.docify("]");
05719     ol.endSmall();
05720     ol.popGeneratorState();
05721   }
05722 }
05723 
05724 #if 0
05725 
05729 static void latin1ToLatex(QTextStream &t,unsigned char c)
05730 {
05731   switch (c)
05732   {
05733     // the Latin-1 characters
05734     case 161: t << "!`";            break;
05735     case 181: t << "$\\mu$";        break;
05736     case 191: t << "?`";            break;
05737     case 192: t << "\\`{A}";        break;
05738     case 193: t << "\\'{A}";        break;
05739     case 194: t << "\\^{A}";        break;
05740     case 195: t << "\\~{A}";        break;
05741     case 196: t << "\\\"{A}";       break;
05742     case 197: t << "\\AA{}";        break;
05743     case 198: t << "\\AE{}";        break;
05744     case 199: t << "\\c{C}";        break;
05745     case 200: t << "\\`{E}";        break;
05746     case 201: t << "\\'{E}";        break;
05747     case 202: t << "\\^{E}";        break;
05748     case 203: t << "\\\"{E}";       break;
05749     case 204: t << "\\`{I}";        break;
05750     case 205: t << "\\'{I}";        break;
05751     case 206: t << "\\^{I}";        break;
05752     case 207: t << "\\\"{I}";       break;
05753     case 208: t << "D ";            break; // anyone know the real code?
05754     case 209: t << "\\~{N}";        break;
05755     case 210: t << "\\`{O}";        break;
05756     case 211: t << "\\'{O}";        break;
05757     case 212: t << "\\^{O}";        break;
05758     case 213: t << "\\~{O}";        break;
05759     case 214: t << "\\\"{O}";       break;
05760     case 215: t << "$\\times$";     break;
05761     case 216: t << "\\O";           break;
05762     case 217: t << "\\`{U}";        break;
05763     case 218: t << "\\'{U}";        break;
05764     case 219: t << "\\^{U}";        break;
05765     case 220: t << "\\\"{U}";       break;
05766     case 221: t << "\\'{Y}";        break;
05767     case 223: t << "\\ss{}";        break; 
05768     case 224: t << "\\`{a}";        break;
05769     case 225: t << "\\'{a}";        break;
05770     case 226: t << "\\^{a}";        break;
05771     case 227: t << "\\~{a}";        break;
05772     case 228: t << "\\\"{a}";       break;
05773     case 229: t << "\\aa{}";        break;
05774     case 230: t << "\\ae{}";        break;
05775     case 231: t << "\\c{c}";        break;
05776     case 232: t << "\\`{e}";        break;
05777     case 233: t << "\\'{e}";        break;
05778     case 234: t << "\\^{e}";        break;
05779     case 235: t << "\\\"{e}";       break;
05780     case 236: t << "\\`{\\i}";      break;
05781     case 237: t << "\\'{\\i}";      break;
05782     case 238: t << "\\^{\\i}";      break;
05783     case 239: t << "\\\"{\\i}";     break;
05784     case 241: t << "\\~{n}";        break;
05785     case 242: t << "\\`{o}";        break;
05786     case 243: t << "\\'{o}";        break;
05787     case 244: t << "\\^{o}";        break;
05788     case 245: t << "\\~{o}";        break;
05789     case 246: t << "\\\"{o}";       break;
05790     case 248: t << "\\o{}";         break;
05791     case 249: t << "\\`{u}";        break;
05792     case 250: t << "\\'{u}";        break;
05793     case 251: t << "\\^{u}";        break;
05794     case 252: t << "\\\"{u}";       break;
05795     case 253: t << "\\'{y}";        break;
05796     case 255: t << "\\\"{y}";       break;           
05797     default: t << (char)c;
05798   }
05799 }
05800 
05805 static void latin2ToLatex(QTextStream &t,unsigned char c)
05806 {
05807   switch (c)
05808   {
05809     case 0xA1: t << "\\k{A}";   break;
05810     case 0xA2: t << (char)c;    break;
05811     case 0xA3: t << "\\L{}";    break;
05812     case 0xA4: t << (char)c;    break;
05813     case 0xA5: t << (char)c;    break;
05814     case 0xA6: t << "\\'{S}";   break;
05815     case 0xA7: t << (char)c;    break;
05816     case 0xA8: t << (char)c;    break;
05817     case 0xA9: t << "\\v{S}";   break;
05818     case 0xAA: t << "\\c{S}";   break;
05819     case 0xAB: t << "\\v{T}";   break;
05820     case 0xAC: t << "\\'{Z}";   break;
05821     case 0xAD: t << (char)c;    break;
05822     case 0xAE: t << "\\v{Z}";   break;
05823     case 0xAF: t << "\\.{Z}";   break;
05824 
05825     case 0xB0: t << (char)c;    break;
05826     case 0xB1: t << "\\k{a}";   break;
05827     case 0xB2: t << (char)c;    break;
05828     case 0xB3: t << "\\l{}";    break;
05829     case 0xB4: t << (char)c;    break;
05830     case 0xB5: t << (char)c;    break;
05831     case 0xB6: t << "\\'{s}";   break;
05832     case 0xB7: t << (char)c;    break;
05833     case 0xB8: t << (char)c;    break;
05834     case 0xB9: t << "\\v{s}";   break;
05835     case 0xBA: t << "\\c{s}";   break;
05836     case 0xBB: t << "\\v{t}";   break;
05837     case 0xBC: t << "\\'{z}";   break;
05838     case 0xBD: t << (char)c;    break;
05839     case 0xBE: t << "\\v{z}";   break;
05840     case 0xBF: t << "\\.{z}";   break;
05841 
05842     case 0xC0: t << "\\'{R}";   break;
05843     case 0xC1: t << "\\'{A}";   break;
05844     case 0xC2: t << "\\^{A}";   break;
05845     case 0xC3: t << "\\u{A}";   break;
05846     case 0xC4: t << "\\\"{A}";  break;
05847     case 0xC5: t << "\\'{L}";   break;
05848     case 0xC6: t << "\\'{C}";   break;
05849     case 0xC7: t << "\\c{C}";   break;
05850     case 0xC8: t << "\\v{C}";   break;
05851     case 0xC9: t << "\\'{E}";   break;
05852     case 0xCA: t << "\\k{E}";   break;
05853     case 0xCB: t << "\\\"{E}";  break;
05854     case 0xCC: t << "\\v{E}";   break;
05855     case 0xCD: t << "\\'{I}";   break;
05856     case 0xCE: t << "\\^{I}";   break;
05857     case 0xCF: t << "\\v{D}";   break;
05858 
05859     case 0xD0: t << "\\DJ "; break;
05860     case 0xD1: t << "\\'{N}";   break;
05861     case 0xD2: t << "\\v{N}";   break;
05862     case 0xD3: t << "\\'{O}";   break;
05863     case 0xD4: t << "\\^{O}";   break;
05864     case 0xD5: t << "\\H{O}";   break;
05865     case 0xD6: t << "\\\"{O}";  break;
05866     case 0xD7: t << (char)c;    break;
05867     case 0xD8: t << "\\v{R}";   break;
05868     case 0xD9: t << (char)c;    break;
05869     case 0xDA: t << "\\'{U}";   break;
05870     case 0xDB: t << "\\H{U}";   break;
05871     case 0xDC: t << "\\\"{U}";  break;
05872     case 0xDD: t << "\\'{Y}";   break;
05873     case 0xDE: t << "\\c{T}";   break;
05874     case 0xDF: t << "\\ss";     break;
05875 
05876     case 0xE0: t << "\\'{r}";   break;
05877     case 0xE1: t << "\\'{a}";   break;
05878     case 0xE2: t << "\\^{a}";   break;
05879     case 0xE3: t << (char)c;    break;
05880     case 0xE4: t << "\\\"{a}";  break;
05881     case 0xE5: t << "\\'{l}";   break;
05882     case 0xE6: t << "\\'{c}";   break;
05883     case 0xE7: t << "\\c{c}";   break;
05884     case 0xE8: t << "\\v{c}";   break;
05885     case 0xE9: t << "\\'{e}";   break;
05886     case 0xEA: t << "\\k{e}";   break;
05887     case 0xEB: t << "\\\"{e}";  break;
05888     case 0xEC: t << "\\v{e}";   break;
05889     case 0xED: t << "\\'{\\i}"; break;
05890     case 0xEE: t << "\\^{\\i}"; break;
05891     case 0xEF: t << "\\v{d}";   break;
05892 
05893     case 0xF0: t << "\\dj "; break;
05894     case 0xF1: t << "\\'{n}";   break;
05895     case 0xF2: t << "\\v{n}";   break;
05896     case 0xF3: t << "\\'{o}";   break;
05897     case 0xF4: t << "\\^{o}";   break;
05898     case 0xF5: t << "\\H{o}";   break;
05899     case 0xF6: t << "\\\"{o}";  break;
05900     case 0xF7: t << (char)c;    break;
05901     case 0xF8: t << "\\v{r}";   break;
05902     case 0xF9: t << (char)c;    break;
05903     case 0xFA: t << "\\'{u}";   break;
05904     case 0xFB: t << "\\H{u}";   break;
05905     case 0xFC: t << "\\\"{u}";  break;
05906     case 0xFD: t << "\\'{y}";   break;
05907     case 0xFE: t << (char)c;    break;
05908     case 0xFF: t << (char)c;    break;
05909 
05910     default: t << (char)c;
05911   }
05912 }
05913 #endif
05914 
05915 void filterLatexString(QTextStream &t,const char *str,
05916     bool insideTabbing,bool insidePre,bool insideItem)
05917 {
05918 #if 0
05919   static bool isCzech         = theTranslator->idLanguage()=="czech";
05920   static bool isSerbian       = theTranslator->idLanguage()=="serbian";
05921   static bool isJapanese      = theTranslator->idLanguage()=="japanese" ||
05922     theTranslator->idLanguage()=="japanese-en";
05923   static bool isKorean        = theTranslator->idLanguage()=="korean" ||
05924     theTranslator->idLanguage()=="korean-en";
05925   static bool isRussian       = theTranslator->idLanguage()=="russian";
05926   static bool isUkrainian     = theTranslator->idLanguage()=="ukrainian";
05927   static bool isSlovene       = theTranslator->idLanguage()=="solvene";
05928   static bool isChinese       = theTranslator->idLanguage()=="chinese" || 
05929     theTranslator->idLanguage()=="chinese-traditional";
05930   static bool isLatin2        = theTranslator->idLanguageCharset()=="iso-8859-2";
05931   static bool isGreek         = theTranslator->idLanguage()=="greek";
05932   //printf("filterLatexString(%s)\n",str);
05933 #endif
05934   if (str)
05935   {
05936     const unsigned char *p=(const unsigned char *)str;
05937     unsigned char c;
05938     unsigned char pc='\0';
05939     while (*p)
05940     {
05941       c=*p++;
05942 
05943       if (insidePre)
05944       {
05945         switch(c)
05946         {
05947           case '\\': t << "\\(\\backslash\\)"; break;
05948           case '{':  t << "\\{"; break;
05949           case '}':  t << "\\}"; break;
05950           case '_':  t << "\\_"; break;
05951           default: 
05952                      t << (char)c;
05953 #if 0
05954                      {
05955                        // Some languages use wide characters
05956                        if (c>=128 && (isJapanese || isKorean || isChinese || isSerbian))
05957                        { 
05958                          t << (char)c;
05959                          if (*p)  
05960                          {
05961                            c = *p++;
05962                            t << (char)c;
05963                          }
05964                        }
05965                        else
05966                        {
05967                          t << (char)c; 
05968                        }
05969                        break;
05970                      }
05971 #endif
05972         }
05973       }
05974       else
05975       {
05976         switch(c)
05977         {
05978           case '#':  t << "\\#";           break;
05979           case '$':  t << "\\$";           break;
05980           case '%':  t << "\\%";           break;
05981           case '^':  t << "$^\\wedge$";    break;
05982           case '&':  t << "\\&";           break;
05983           case '*':  t << "$\\ast$";       break;
05984           case '_':  t << "\\_"; 
05985                      if (!insideTabbing) t << "\\-";  
05986                      break;
05987           case '{':  t << "\\{";           break;
05988           case '}':  t << "\\}";           break;
05989           case '<':  t << "$<$";           break;
05990           case '>':  t << "$>$";           break;
05991           case '|':  t << "$|$";           break;
05992           case '~':  t << "$\\sim$";       break;
05993           case '[':  if (Config_getBool("PDF_HYPERLINKS") || insideItem) 
05994                        t << "\\mbox{[}"; 
05995                      else
05996                        t << "[";
05997                      break;
05998           case ']':  if (pc=='[') t << "$\\,$";
05999                        if (Config_getBool("PDF_HYPERLINKS") || insideItem)
06000                          t << "\\mbox{]}";
06001                        else
06002                          t << "]";             
06003                      break;
06004           case '-':  if (*p=='>') 
06005                      { t << " $\\rightarrow$ "; p++; }
06006                      else
06007                      { t << (char)c; }
06008                      break;
06009           case '\\': if (*p=='<') 
06010                      { t << "$<$"; p++; }
06011                      else if (*p=='>')
06012                      { t << "$>$"; p++; } 
06013                      else  
06014                      { t << "$\\backslash$"; }
06015                      break;           
06016           case '"':  { t << "\\char`\\\"{}"; }
06017                      break;
06018 
06019           default:   
06020                      t << (char)c;
06021 #if 0
06022                      {
06023                        // Some languages use wide characters
06024                        if (isJapanese || isKorean || isChinese || isSerbian)
06025                        { 
06026                          if (c>=128) 
06027                          {
06028                            t << (char)c;
06029                            if (*p)  
06030                            {
06031                              c = *p++;
06032                              t << (char)c;
06033                            }
06034                          }
06035                          else // ascii char => see if we can insert a hypenation hint
06036                          {
06037                            if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
06038                            t << (char)c;    
06039                          } 
06040                        }
06041                        else if (isCzech || isRussian || isUkrainian || isSlovene)
06042                        {
06043                          if (c>=128)
06044                          {
06045                            t << (char)c;
06046                          }
06047                          else // ascii char => see if we can insert a hypenation hint
06048                          {
06049                            if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
06050                            t << (char)c;
06051                          }
06052                        }
06053                        else if (isGreek)
06054                        {
06055                          if (c<128)
06056                          {
06057                            t << "\\textlatin{" << (char)c << "}";
06058                          }
06059                          else
06060                          {
06061                            t << (char)c;
06062                          }
06063                        }
06064                        else if (isLatin2)
06065                        {
06066                          if (c>=128)
06067                          {
06068                            latin2ToLatex(t,c);
06069                          }
06070                          else
06071                          { 
06072                            // see if we can insert an hyphenation hint
06073                            if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
06074                            t << (char)c;
06075                          }
06076                        }
06077                        else // another language => assume latin1 charset
06078                        {
06079                          if (c>=128)
06080                          {
06081                            latin1ToLatex(t,c);
06082                          }
06083                          else
06084                          { 
06085                            // see if we can insert an hyphenation hint
06086                            if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-";
06087                            t << (char)c;
06088                          }
06089                        }
06090                      }
06091 #endif
06092         }
06093       }
06094       pc = c;
06095     }
06096   }
06097 }
06098 
06099 
06100 QCString rtfFormatBmkStr(const char *name)
06101 {
06102   static QCString g_nextTag( "AAAAAAAAAA" );
06103   static QDict<QCString> g_tagDict( 5003 );
06104 
06105   g_tagDict.setAutoDelete(TRUE);
06106 
06107   // To overcome the 40-character tag limitation, we
06108   // substitute a short arbitrary string for the name
06109   // supplied, and keep track of the correspondence
06110   // between names and strings.
06111   QCString key( name );
06112   QCString* tag = g_tagDict.find( key );
06113   if ( !tag )
06114   {
06115     // This particular name has not yet been added
06116     // to the list. Add it, associating it with the
06117     // next tag value, and increment the next tag.
06118     tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
06119     g_tagDict.insert( key, tag );
06120 
06121     // This is the increment part
06122     char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1;
06123     for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
06124     {
06125       if ( ( ++(*nxtTag) ) > 'Z' )
06126       {
06127         *nxtTag = 'A';
06128       }
06129       else
06130       {
06131         // Since there was no carry, we can stop now
06132         break;
06133       }
06134     }
06135   }
06136 
06137   return *tag;
06138 }
06139 
06140 QCString stripExtension(const char *fName)
06141 {
06142   QCString result=fName;
06143   if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
06144   {
06145     result=result.left(result.length()-Doxygen::htmlFileExtension.length());
06146   }
06147   return result;
06148 }
06149 
06150 
06151 void replaceNamespaceAliases(QCString &scope,int i)
06152 {
06153   //printf("replaceNamespaceAliases(%s,%d)\n",scope.data(),i);
06154   while (i>0)
06155   {
06156     QCString *s = Doxygen::namespaceAliasDict[scope.left(i)];
06157     if (s)
06158     {
06159       scope=*s+scope.right(scope.length()-i);
06160       i=s->length();
06161     }
06162     i=scope.findRev("::",i-1);
06163   }
06164   //printf("replaceNamespaceAliases() result=%s\n",scope.data());
06165 }
06166 
06167 QCString stripPath(const char *s)
06168 {
06169   QCString result=s;
06170   int i=result.findRev('/');
06171   if (i!=-1)
06172   {
06173     result=result.mid(i+1);
06174   }
06175   return result;
06176 }
06177 
06179 bool containsWord(const QCString &s,const QCString &word)
06180 {
06181   static QRegExp wordExp("[a-z_A-Z]+");
06182   int p=0,i,l;
06183   while ((i=wordExp.match(s,p,&l))!=-1)
06184   {
06185     if (s.mid(i,l)==word) return TRUE;
06186     p=i+l;
06187   }
06188   return FALSE;
06189 }
06190 
06191 bool findAndRemoveWord(QCString &s,const QCString &word)
06192 {
06193   static QRegExp wordExp("[a-z_A-Z]+");
06194   int p=0,i,l;
06195   while ((i=wordExp.match(s,p,&l))!=-1)
06196   {
06197     if (s.mid(i,l)==word) 
06198     {
06199       if (i>0 && isspace(s.at(i-1))) 
06200         i--,l++;
06201       else if (i+l<(int)s.length() && isspace(s.at(i+l))) 
06202         l++;
06203       s = s.left(i)+s.mid(i+l); // remove word + spacing
06204       return TRUE;
06205     }
06206     p=i+l;
06207   }
06208   return FALSE;
06209 }
06210 
06214 QCString stripLeadingAndTrailingEmptyLines(const QCString &s)
06215 {
06216   const char *p = s.data();
06217   if (p==0) return 0;
06218 
06219   // search for leading empty lines
06220   int i=0,li=-1,l=s.length();
06221   char c;
06222   while ((c=*p++))
06223   {
06224     if (c==' ' || c=='\t' || c=='\r') i++;
06225     else if (c=='\n') i++,li=i;
06226     else break;
06227   }
06228 
06229   // search for trailing empty lines
06230   int b=l-1,bi=-1;
06231   p=s.data()+b;
06232   while (b>=0)
06233   {
06234     c=*p; p--;
06235     if (c==' ' || c=='\t' || c=='\r') b--;
06236     else if (c=='\n') bi=b,b--;
06237     else break;
06238   }
06239 
06240   // return whole string if no leading or trailing lines where found
06241   if (li==-1 && bi==-1) return s;
06242 
06243   // return substring
06244   if (bi==-1) bi=l;
06245   if (li==-1) li=0;
06246   if (bi<=li) return 0; // only empty lines
06247   return s.mid(li,bi-li);
06248 }
06249 
06250 void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
06251     const QCString &str,bool priority,const QCString &anchor)
06252 {
06253   static bool searchEngine = Config_getBool("SEARCHENGINE");
06254   if (searchEngine)
06255   {
06256     Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
06257     static QRegExp wordPattern("[a-z_A-Z][a-z_A-Z0-9]*");
06258     int i,p=0,l;
06259     while ((i=wordPattern.match(str,p,&l))!=-1)
06260     {
06261       Doxygen::searchIndex->addWord(str.mid(i,l),priority);
06262       p=i+l;
06263     }
06264   }
06265 }
06266 
06267 SrcLangExt getLanguageFromFileName(const QCString fileName)
06268 {
06269   int i = fileName.findRev('.');
06270   static bool init=FALSE;
06271   static QDict<int> extLookup;
06272   extLookup.setAutoDelete(TRUE);
06273   if (!init) // one time initialization
06274   {
06275     extLookup.insert(".idl",   new int(SrcLangExt_IDL));
06276     extLookup.insert(".ddl",   new int(SrcLangExt_IDL));
06277     extLookup.insert(".odl",   new int(SrcLangExt_IDL));
06278     extLookup.insert(".ddl",   new int(SrcLangExt_IDL));
06279     extLookup.insert(".java",  new int(SrcLangExt_Java));
06280     extLookup.insert(".as",    new int(SrcLangExt_JS));
06281     extLookup.insert(".js",    new int(SrcLangExt_JS));
06282     extLookup.insert(".cs",    new int(SrcLangExt_CSharp));
06283     extLookup.insert(".d",     new int(SrcLangExt_D));
06284     extLookup.insert(".php",   new int(SrcLangExt_PHP));
06285     extLookup.insert(".php4",  new int(SrcLangExt_PHP));
06286     extLookup.insert(".php5",  new int(SrcLangExt_PHP));
06287     extLookup.insert(".inc",   new int(SrcLangExt_PHP));
06288     extLookup.insert(".phtml", new int(SrcLangExt_PHP));
06289     extLookup.insert(".m",     new int(SrcLangExt_ObjC));
06290     extLookup.insert(".M",     new int(SrcLangExt_ObjC));
06291     extLookup.insert(".mm",    new int(SrcLangExt_ObjC));
06292     extLookup.insert(".py",    new int(SrcLangExt_Python));
06293     extLookup.insert(".f",     new int(SrcLangExt_F90));
06294     extLookup.insert(".f90",   new int(SrcLangExt_F90));
06295     extLookup.insert(".vhd",   new int(SrcLangExt_VHDL));
06296     extLookup.insert(".vhdl",  new int(SrcLangExt_VHDL));
06297     init=TRUE;
06298   }
06299   if (i!=-1) // name has an extension
06300   {
06301     QCString extStr=fileName.right(fileName.length()-i);
06302     if (!extStr.isEmpty()) // non-empty extension
06303     {
06304       int *pVal=extLookup.find(extStr);
06305       if (pVal) // listed extension
06306       {
06307         return (SrcLangExt)*pVal; // cast void* address to enum value
06308       }
06309     }
06310   }
06311   return SrcLangExt_Cpp; // not listed => assume C-ish language.
06312 }
06313 
06315 bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
06316 {
06317   if (scope==0 ||
06318       (scope->definitionType()!=Definition::TypeClass &&
06319        scope->definitionType()!=Definition::TypeNamespace
06320       )
06321      )
06322   {
06323     scope=Doxygen::globalScope;
06324   }
06325 
06326   QCString name = n;
06327   if (name.isEmpty())
06328     return FALSE; // no name was given
06329 
06330   DefinitionIntf *di = Doxygen::symbolMap->find(name);
06331   if (di==0)
06332     return FALSE; // could not find any matching symbols
06333 
06334   // mostly copied from getResolvedClassRec()
06335   QCString explicitScopePart;
06336   int qualifierIndex = computeQualifiedIndex(name);
06337   if (qualifierIndex!=-1)
06338   {
06339     explicitScopePart = name.left(qualifierIndex);
06340     replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
06341     name = name.mid(qualifierIndex+2);
06342   }
06343 
06344   int minDistance = 10000;
06345   MemberDef *bestMatch = 0;
06346 
06347   if (di->definitionType()==DefinitionIntf::TypeSymbolList)
06348   {
06349     // find the closest closest matching definition
06350     DefinitionListIterator dli(*(DefinitionList*)di);
06351     Definition *d;
06352     for (dli.toFirst();(d=dli.current());++dli)
06353     {
06354       if (d->definitionType()==Definition::TypeMember)
06355       {
06356         g_visitedNamespaces.clear();
06357         int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
06358         if (distance!=-1 && distance<minDistance)
06359         {
06360           minDistance = distance;
06361           bestMatch = (MemberDef *)d;
06362         }
06363       }
06364     }
06365   }
06366   else if (di->definitionType()==Definition::TypeMember)
06367   {
06368     Definition *d = (Definition *)di;
06369     g_visitedNamespaces.clear();
06370     int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
06371     if (distance!=-1 && distance<minDistance)
06372     {
06373       minDistance = distance;
06374       bestMatch = (MemberDef *)d;
06375     }
06376   }
06377 
06378   if (bestMatch && bestMatch->isTypedef())
06379     return TRUE; // closest matching symbol is a typedef
06380   else
06381     return FALSE;
06382 }
06383 
06384 QCString parseCommentAsText(const QString &doc,const QCString &fileName,int lineNr)
06385 {
06386   QString result;
06387   if (doc.isEmpty()) return result.data();
06388   QTextStream t(&result,IO_WriteOnly);
06389   DocNode *root = validatingParseDoc(fileName,lineNr,Doxygen::globalScope,0,doc,FALSE,FALSE);
06390   TextDocVisitor *visitor = new TextDocVisitor(t);
06391   root->accept(visitor);
06392   delete visitor;
06393   delete root;
06394   int i=0;
06395   if (result.length()>80)
06396   {
06397     for (i=80;i<100;i++) // search for nice truncation point
06398     {
06399       if (result.at(i).isSpace() || 
06400           result.at(i)==',' || 
06401           result.at(i)=='.' || 
06402           result.at(i)=='?')
06403       {
06404         break;
06405       }
06406     }
06407   }
06408   if (i>0) result=result.left(i)+"...";
06409   return result.data();
06410 }
06411 
06412 //--------------------------------------------------------------------------------------
06413 
06414 static QDict<void> aliasesProcessed;
06415 
06416 QCString expandAliasRec(const QCString s)
06417 {
06418   QCString result;
06419   static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
06420   QCString value=s;
06421   int i,p=0,l;
06422   while ((i=cmdPat.match(value,p,&l))!=-1)
06423   {
06424     result+=value.mid(p,i-p);
06425     QCString args = extractAliasArgs(value,i+l);
06426     bool hasArgs = !args.isEmpty();            // found directly after command
06427     QCString cmd;
06428     if (hasArgs)
06429     {
06430       int numArgs = countAliasArguments(args);
06431       cmd  = value.mid(i+1,l-1)+QCString().sprintf("{%d}",numArgs);  // alias name + {n}
06432     }
06433     else
06434     {
06435       cmd = value.mid(i+1,l-1);
06436     }
06437     //printf("Found command '%s' args='%s'\n",cmd.data(),args.data());
06438     QCString *aliasText=Doxygen::aliasDict.find(cmd);
06439     if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias
06440     {
06441       //printf("is an alias!\n");
06442       aliasesProcessed.insert(cmd,(void *)0x8);
06443       QCString val = *aliasText;
06444       if (hasArgs)
06445       {
06446         val = replaceAliasArguments(val,args);
06447         //printf("replace '%s'->'%s' args='%s'\n",
06448         //       aliasText->data(),val.data(),args.data());
06449       }
06450       result+=expandAliasRec(val);
06451       aliasesProcessed.remove(cmd);
06452       p=i+l;
06453       if (hasArgs) p+=args.length()+2;
06454     }
06455     else // command is not an alias
06456     {
06457       //printf("not an alias!\n");
06458       result+=value.mid(i,l);
06459       p=i+l;
06460     }
06461   }
06462   result+=value.right(value.length()-p);
06463 
06464   //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
06465   return result;
06466 }
06467 
06468 static QCString replaceAliasArgument(const QCString &aliasValue,int paramNum,
06469                                      const QCString &paramValue)
06470 {
06471   QCString result;
06472   QCString paramMarker;
06473   paramMarker.sprintf("\\%d",paramNum);
06474   int markerLen = paramMarker.length();
06475   int p=0,i;
06476   while ((i=aliasValue.find(paramMarker,p))!=-1) // search for marker
06477   {
06478     result+=aliasValue.mid(p,i-p);
06479     //printf("Found marker '%s' at %d len=%d for param '%s' in '%s'\n",
06480     //                 paramMarker.data(),i,markerLen,paramValue.data(),aliasValue.data());
06481     if (i==0 || aliasValue.at(i-1)!='\\') // found unescaped marker
06482     {
06483       result += paramValue;
06484       p=i+markerLen;
06485     }
06486     else // ignore escaped markers
06487     {
06488       result += aliasValue.mid(i,markerLen);
06489       p=i+1;
06490     }
06491   }
06492   result+=aliasValue.right(aliasValue.length()-p);
06493   result = expandAliasRec(substitute(result,"\\,",","));
06494   //printf("replaceAliasArgument('%s',%d,'%s')->%s\n",
06495   //    aliasValue.data(),paramNum,paramValue.data(),result.data());
06496   return result;
06497 }
06498 
06499 QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
06500 {
06501   QCString result = aliasValue;
06502   QList<QCString> args;
06503   int p=0,i,c=1;
06504   for (i=0;i<(int)argList.length();i++)
06505   {
06506     if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\'))
06507     {
06508       result = replaceAliasArgument(result,c,argList.mid(p,i-p));
06509       p=i+1;
06510       c++;
06511     }
06512   }
06513   if (p<(int)argList.length())
06514   {
06515     result = replaceAliasArgument(result,c,argList.right(argList.length()-p));
06516   }
06517   return result;
06518 }
06519 
06520 int countAliasArguments(const QCString argList)
06521 {
06522   int count=1;
06523   int l = argList.length();
06524   int i;
06525   for (i=0;i<l;i++) 
06526   {
06527     if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++;
06528   }
06529   return count;
06530 }
06531 
06532 QCString extractAliasArgs(const QCString &args,int pos)
06533 {
06534   int i;
06535   int bc=0;
06536   if (args.at(pos)=='{') // alias has argument
06537   {
06538     for (i=pos;i<(int)args.length();i++)
06539     {
06540       if (args.at(i)=='{') bc++;
06541       if (args.at(i)=='}') bc--;
06542       if (bc==0) 
06543       {
06544         //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
06545         return args.mid(pos+1,i-pos-1);
06546       }
06547     }
06548   }
06549   return "";
06550 }
06551 
06552 QCString resolveAliasCmd(const QCString aliasCmd)
06553 {
06554   QCString result;
06555   aliasesProcessed.clear();
06556   //printf("Expanding: '%s'\n",aliasCmd.data());
06557   result = expandAliasRec(aliasCmd);
06558   //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
06559   return result;
06560 }
06561 
06562 QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
06563 {
06564   QCString result;
06565   aliasesProcessed.clear();
06566   // avoid expanding this command recursively
06567   aliasesProcessed.insert(aliasName,(void *)0x8);
06568   // expand embedded commands
06569   //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
06570   result = expandAliasRec(aliasValue);
06571   //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
06572   return result;
06573 }
06574 
06575 void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
06576 {
06577   if (al==0) return;
06578   ol.startConstraintList(theTranslator->trTypeConstraints()); 
06579   ArgumentListIterator ali(*al);
06580   Argument *a;
06581   for (;(a=ali.current());++ali)
06582   {
06583     ol.startConstraintParam();
06584     ol.parseText(a->name);
06585     ol.endConstraintParam();
06586     ol.startConstraintType();
06587     linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
06588     ol.endConstraintType();
06589     ol.startConstraintDocs();
06590     ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
06591     ol.endConstraintDocs();
06592   }
06593   ol.endConstraintList();
06594 }
06595 



Generated on Mon Mar 31 10:58:49 2008 by  doxygen 1.5.1