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