summaryrefslogtreecommitdiff
path: root/src/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.cpp')
-rw-r--r--src/util.cpp7573
1 files changed, 7573 insertions, 0 deletions
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..2679b65
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,7573 @@
+/*****************************************************************************
+ *
+ *
+ *
+ * Copyright (C) 1997-2012 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+
+#include "md5.h"
+
+#include "qtbc.h"
+#include <qregexp.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qcache.h>
+
+#include "util.h"
+#include "message.h"
+#include "classdef.h"
+#include "filedef.h"
+#include "doxygen.h"
+#include "outputlist.h"
+#include "defargs.h"
+#include "language.h"
+#include "config.h"
+#include "htmlhelp.h"
+#include "example.h"
+#include "version.h"
+#include "groupdef.h"
+#include "reflist.h"
+#include "pagedef.h"
+#include "debug.h"
+#include "searchindex.h"
+#include "doxygen.h"
+#include "textdocvisitor.h"
+#include "portable.h"
+#include "parserintf.h"
+#include "bufstr.h"
+#include "image.h"
+#include "growbuf.h"
+#include "entry.h"
+#include "arguments.h"
+
+#define ENABLE_TRACINGSUPPORT 0
+
+#if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
+#define TRACINGSUPPORT
+#endif
+
+#ifdef TRACINGSUPPORT
+#include <execinfo.h>
+#include <unistd.h>
+#endif
+
+
+//------------------------------------------------------------------------
+
+// selects one of the name to sub-dir mapping algorithms that is used
+// to select a sub directory when CREATE_SUBDIRS is set to YES.
+
+#define ALGO_COUNT 1
+#define ALGO_CRC16 2
+#define ALGO_MD5 3
+
+//#define MAP_ALGO ALGO_COUNT
+//#define MAP_ALGO ALGO_CRC16
+#define MAP_ALGO ALGO_MD5
+
+#define REL_PATH_TO_ROOT "../../"
+
+//------------------------------------------------------------------------
+// TextGeneratorOLImpl implementation
+//------------------------------------------------------------------------
+
+TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od)
+{
+}
+
+void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
+{
+ if (s==0) return;
+ //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces);
+ if (keepSpaces)
+ {
+ const char *p=s;
+ if (p)
+ {
+ char cs[2];
+ char c;
+ cs[1]='\0';
+ while ((c=*p++))
+ {
+ if (c==' ') m_od.writeNonBreakableSpace(1);
+ else cs[0]=c,m_od.docify(cs);
+ }
+ }
+ }
+ else
+ {
+ m_od.docify(s);
+ }
+}
+
+void TextGeneratorOLImpl::writeBreak(int indent) const
+{
+ m_od.lineBreak("typebreak");
+ int i;
+ for (i=0;i<indent;i++)
+ {
+ m_od.writeNonBreakableSpace(3);
+ }
+}
+
+void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
+ const char *anchor,const char *text
+ ) const
+{
+ //printf("TextGeneratorOlImpl::writeLink('%s')\n",text);
+ m_od.writeObjectLink(extRef,file,anchor,text);
+}
+
+//------------------------------------------------------------------------
+//------------------------------------------------------------------------
+
+// an inheritance tree of depth of 100000 should be enough for everyone :-)
+const int maxInheritanceDepth = 100000;
+
+/*!
+ Removes all anonymous scopes from string s
+ Possible examples:
+\verbatim
+ "bla::@10::blep" => "bla::blep"
+ "bla::@10::@11::blep" => "bla::blep"
+ "@10::blep" => "blep"
+ " @10::blep" => "blep"
+ "@9::@10::blep" => "blep"
+ "bla::@1" => "bla"
+ "bla::@1::@2" => "bla"
+ "bla @1" => "bla"
+\endverbatim
+ */
+QCString removeAnonymousScopes(const QCString &s)
+{
+ QCString result;
+ if (s.isEmpty()) return result;
+ static QRegExp re("[ :]*@[0-9]+[: ]*");
+ int i,l,sl=s.length();
+ int p=0;
+ while ((i=re.match(s,p,&l))!=-1)
+ {
+ result+=s.mid(p,i-p);
+ int c=i;
+ bool b1=FALSE,b2=FALSE;
+ while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
+ c=i+l-1;
+ while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
+ if (b1 && b2)
+ {
+ result+="::";
+ }
+ p=i+l;
+ }
+ result+=s.right(sl-p);
+ //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
+ return result;
+}
+
+// replace anonymous scopes with __anonymous__ or replacement if provided
+QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
+{
+ QCString result;
+ if (s.isEmpty()) return result;
+ static QRegExp re("@[0-9]+");
+ int i,l,sl=s.length();
+ int p=0;
+ while ((i=re.match(s,p,&l))!=-1)
+ {
+ result+=s.mid(p,i-p);
+ if (replacement)
+ {
+ result+=replacement;
+ }
+ else
+ {
+ result+="__anonymous__";
+ }
+ p=i+l;
+ }
+ result+=s.right(sl-p);
+ //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
+ return result;
+}
+
+
+// strip anonymous left hand side part of the scope
+QCString stripAnonymousNamespaceScope(const QCString &s)
+{
+ int i,p=0,l;
+ QCString newScope;
+ while ((i=getScopeFragment(s,p,&l))!=-1)
+ {
+ //printf("Scope fragment %s\n",s.mid(i,l).data());
+ if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
+ {
+ if (s.at(i)!='@')
+ {
+ if (!newScope.isEmpty()) newScope+="::";
+ newScope+=s.mid(i,l);
+ }
+ }
+ else
+ {
+ if (!newScope.isEmpty()) newScope+="::";
+ newScope+=s.right(s.length()-i);
+ goto done;
+ }
+ p=i+l;
+ }
+done:
+ //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
+ return newScope;
+}
+
+void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
+{
+ od.pushGeneratorState();
+
+ od.disable(OutputGenerator::Html);
+ od.disable(OutputGenerator::Man);
+ if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex);
+ if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF);
+ od.startPageRef();
+ od.docify(theTranslator->trPageAbbreviation());
+ od.endPageRef(cn,mn);
+
+ od.popGeneratorState();
+}
+
+/*! Generate a place holder for a position in a list. Used for
+ * translators to be able to specify different elements orders
+ * depending on whether text flows from left to right or visa versa.
+ */
+QCString generateMarker(int id)
+{
+ QCString result;
+ result.sprintf("@%d",id);
+ return result;
+}
+
+static QCString stripFromPath(const QCString &path,QStrList &l)
+{
+ // look at all the strings in the list and strip the longest match
+ const char *s=l.first();
+ QCString potential;
+ unsigned int length = 0;
+ while (s)
+ {
+ QCString prefix = s;
+ if (prefix.length() > length &&
+ stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
+ {
+ length = prefix.length();
+ potential = path.right(path.length()-prefix.length());
+ }
+ s = l.next();
+ }
+ if (length) return potential;
+ return path;
+}
+
+/*! strip part of \a path if it matches
+ * one of the paths in the Config_getList("STRIP_FROM_PATH") list
+ */
+QCString stripFromPath(const QCString &path)
+{
+ return stripFromPath(path,Config_getList("STRIP_FROM_PATH"));
+}
+
+/*! strip part of \a path if it matches
+ * one of the paths in the Config_getList("INCLUDE_PATH") list
+ */
+QCString stripFromIncludePath(const QCString &path)
+{
+ return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH"));
+}
+
+/*! try to determine if \a name is a source or a header file name by looking
+ * at the extension. A number of variations is allowed in both upper and
+ * lower case) If anyone knows or uses another extension please let me know :-)
+ */
+int guessSection(const char *name)
+{
+ QCString n=((QCString)name).lower();
+ if (n.right(2)==".c" || // source
+ n.right(3)==".cc" ||
+ n.right(4)==".cxx" ||
+ n.right(4)==".cpp" ||
+ n.right(4)==".c++" ||
+ n.right(5)==".java" ||
+ n.right(2)==".m" ||
+ n.right(2)==".M" ||
+ n.right(3)==".mm" ||
+ n.right(3)==".ii" || // inline
+ n.right(4)==".ixx" ||
+ n.right(4)==".ipp" ||
+ n.right(4)==".i++" ||
+ n.right(4)==".inl" ||
+ n.right(4)==".xml"
+ ) return Entry::SOURCE_SEC;
+ if (n.right(2)==".h" || // header
+ n.right(3)==".hh" ||
+ n.right(4)==".hxx" ||
+ n.right(4)==".hpp" ||
+ n.right(4)==".h++" ||
+ n.right(4)==".idl" ||
+ n.right(4)==".ddl" ||
+ n.right(5)==".pidl"
+ ) return Entry::HEADER_SEC;
+ return 0;
+}
+
+QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
+ Definition **typedefContext)
+{
+ //printf("<<resolveTypeDef(%s,%s)\n",
+ // context ? context->name().data() : "<none>",qualifiedName.data());
+ QCString result;
+ if (qualifiedName.isEmpty())
+ {
+ //printf(" qualified name empty!\n");
+ return result;
+ }
+
+ Definition *mContext=context;
+ if (typedefContext) *typedefContext=context;
+
+ // see if the qualified name has a scope part
+ int scopeIndex = qualifiedName.findRev("::");
+ QCString resName=qualifiedName;
+ if (scopeIndex!=-1) // strip scope part for the name
+ {
+ resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
+ if (resName.isEmpty())
+ {
+ // qualifiedName was of form A:: !
+ //printf(" qualified name of form A::!\n");
+ return result;
+ }
+ }
+ MemberDef *md=0;
+ while (mContext && md==0)
+ {
+ // step 1: get the right scope
+ Definition *resScope=mContext;
+ if (scopeIndex!=-1)
+ {
+ // split-off scope part
+ QCString resScopeName = qualifiedName.left(scopeIndex);
+ //printf("resScopeName=`%s'\n",resScopeName.data());
+
+ // look-up scope in context
+ int is,ps=0;
+ int l;
+ while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
+ {
+ QCString qualScopePart = resScopeName.mid(is,l);
+ QCString tmp = resolveTypeDef(mContext,qualScopePart);
+ if (!tmp.isEmpty()) qualScopePart=tmp;
+ resScope = resScope->findInnerCompound(qualScopePart);
+ //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
+ if (resScope==0) break;
+ ps=is+l;
+ }
+ }
+ //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
+
+ // step 2: get the member
+ if (resScope) // no scope or scope found in the current context
+ {
+ //printf("scope found: %s, look for typedef %s\n",
+ // resScope->qualifiedName().data(),resName.data());
+ MemberNameSDict *mnd=0;
+ if (resScope->definitionType()==Definition::TypeClass)
+ {
+ mnd=Doxygen::memberNameSDict;
+ }
+ else
+ {
+ mnd=Doxygen::functionNameSDict;
+ }
+ MemberName *mn=mnd->find(resName);
+ if (mn)
+ {
+ MemberNameIterator mni(*mn);
+ MemberDef *tmd=0;
+ int minDist=-1;
+ for (;(tmd=mni.current());++mni)
+ {
+ //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
+ // tmd->name().data(), resScope->name().data(),
+ // tmd->getOuterScope()->name().data(), mContext);
+ if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
+ {
+ int dist=isAccessibleFrom(resScope,0,tmd);
+ if (dist!=-1 && (md==0 || dist<minDist))
+ {
+ md = tmd;
+ minDist = dist;
+ }
+ }
+ }
+ }
+ }
+ mContext=mContext->getOuterScope();
+ }
+
+ // step 3: get the member's type
+ if (md)
+ {
+ //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s' args='%s'\n",
+ // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
+ // );
+ result=md->typeString();
+ QCString args = md->argsString();
+ if (args.find(")(")!=-1) // typedef of a function/member pointer
+ {
+ result+=args;
+ }
+ else if (args.find('[')!=-1) // typedef of an array
+ {
+ result+=args;
+ }
+ if (typedefContext) *typedefContext=md->getOuterScope();
+ }
+ else
+ {
+ //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
+ // qualifiedName.data(),context ? context->name().data() : "<global>");
+ }
+ return result;
+
+}
+
+
+/*! Get a class definition given its name.
+ * Returns 0 if the class is not found.
+ */
+ClassDef *getClass(const char *n)
+{
+ if (n==0 || n[0]=='\0') return 0;
+ QCString name=n;
+ ClassDef *result = Doxygen::classSDict->find(name);
+ //if (result==0 && !exact) // also try generic and protocol versions
+ //{
+ // result = Doxygen::classSDict->find(name+"-g");
+ // if (result==0)
+ // {
+ // result = Doxygen::classSDict->find(name+"-p");
+ // }
+ //}
+ //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>");
+ return result;
+}
+
+NamespaceDef *getResolvedNamespace(const char *name)
+{
+ if (name==0 || name[0]=='\0') return 0;
+ QCString *subst = Doxygen::namespaceAliasDict[name];
+ if (subst)
+ {
+ int count=0; // recursion detection guard
+ QCString *newSubst;
+ while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
+ {
+ subst=newSubst;
+ count++;
+ }
+ if (count==10)
+ {
+ err("warning: possible recursive namespace alias detected for %s!\n",name);
+ }
+ return Doxygen::namespaceSDict->find(subst->data());
+ }
+ else
+ {
+ return Doxygen::namespaceSDict->find(name);
+ }
+}
+
+static QDict<MemberDef> g_resolvedTypedefs;
+static QDict<Definition> g_visitedNamespaces;
+
+// forward declaration
+static ClassDef *getResolvedClassRec(Definition *scope,
+ FileDef *fileScope,
+ const char *n,
+ MemberDef **pTypeDef,
+ QCString *pTemplSpec,
+ QCString *pResolvedType
+ );
+int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
+ const QCString &explicitScopePart);
+
+/*! Returns the class representing the value of the typedef represented by \a md
+ * within file \a fileScope.
+ *
+ * Example: typedef A T; will return the class representing A if it is a class.
+ *
+ * Example: typedef int T; will return 0, since "int" is not a class.
+ */
+ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md,
+ MemberDef **pMemType,QCString *pTemplSpec,
+ QCString *pResolvedType,
+ ArgumentList *actTemplParams)
+{
+ //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
+ bool isCached = md->isTypedefValCached(); // value already cached
+ if (isCached)
+ {
+ //printf("Already cached %s->%s [%s]\n",
+ // md->name().data(),
+ // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
+ // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
+
+ if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
+ if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
+ return md->getCachedTypedefVal();
+ }
+ //printf("new typedef\n");
+ QCString qname = md->qualifiedName();
+ if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
+
+ g_resolvedTypedefs.insert(qname,md); // put on the trace list
+
+ ClassDef *typeClass = md->getClassDef();
+ QCString type = md->typeString(); // get the "value" of the typedef
+ if (typeClass && typeClass->isTemplate() &&
+ actTemplParams && actTemplParams->count()>0)
+ {
+ type = substituteTemplateArgumentsInString(type,
+ typeClass->templateArguments(),actTemplParams);
+ }
+ QCString typedefValue = type;
+ int tl=type.length();
+ int ip=tl-1; // remove * and & at the end
+ while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
+ {
+ ip--;
+ }
+ type=type.left(ip+1);
+ type.stripPrefix("const "); // strip leading "const"
+ type.stripPrefix("struct "); // strip leading "struct"
+ type.stripPrefix("union "); // strip leading "union"
+ int sp=0;
+ tl=type.length(); // length may have been changed
+ while (sp<tl && type.at(sp)==' ') sp++;
+ MemberDef *memTypeDef = 0;
+ ClassDef *result = getResolvedClassRec(md->getOuterScope(),
+ fileScope,type,&memTypeDef,0,pResolvedType);
+ // if type is a typedef then return what it resolves to.
+ if (memTypeDef && memTypeDef->isTypedef())
+ {
+ result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
+ goto done;
+ }
+ else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
+ {
+ *pMemType = memTypeDef;
+ }
+
+ //printf("type=%s result=%p\n",type.data(),result);
+ if (result==0)
+ {
+ // try unspecialized version if type is template
+ int si=type.findRev("::");
+ int i=type.find('<');
+ if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
+ {
+ if (pTemplSpec) *pTemplSpec = type.mid(i);
+ result = getResolvedClassRec(md->getOuterScope(),fileScope,
+ type.left(i),0,0,pResolvedType);
+ //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
+ // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
+ }
+ else if (si!=-1) // A::B
+ {
+ i=type.find('<',si);
+ if (i==-1) // Something like A<T>::B => lookup A::B
+ {
+ i=type.length();
+ }
+ else // Something like A<T>::B<S> => lookup A::B, spec=<S>
+ {
+ if (pTemplSpec) *pTemplSpec = type.mid(i);
+ }
+ result = getResolvedClassRec(md->getOuterScope(),fileScope,
+ stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
+ pResolvedType);
+ }
+
+ //if (result) ip=si+sp+1;
+ }
+
+done:
+ if (pResolvedType)
+ {
+ if (result)
+ {
+ *pResolvedType=result->qualifiedName();
+ //printf("*pResolvedType=%s\n",pResolvedType->data());
+ if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
+ if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
+ }
+ else
+ {
+ *pResolvedType=typedefValue;
+ }
+ }
+
+ // remember computed value for next time
+ if (result && result->getDefFileName()!="<code>")
+ // this check is needed to prevent that temporary classes that are
+ // introduced while parsing code fragments are being cached here.
+ {
+ //printf("setting cached typedef %p in result %p\n",md,result);
+ //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
+ //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
+ md->cacheTypedefVal(result,
+ pTemplSpec ? *pTemplSpec : QCString(),
+ pResolvedType ? *pResolvedType : QCString()
+ );
+ }
+
+ g_resolvedTypedefs.remove(qname); // remove from the trace list
+
+ return result;
+}
+
+/*! Substitutes a simple unqualified \a name within \a scope. Returns the
+ * value of the typedef or \a name if no typedef was found.
+ */
+static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
+ MemberDef **pTypeDef=0)
+{
+ QCString result=name;
+ if (name.isEmpty()) return result;
+
+ // lookup scope fragment in the symbol map
+ DefinitionIntf *di = Doxygen::symbolMap->find(name);
+ if (di==0) return result; // no matches
+
+ MemberDef *bestMatch=0;
+ if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
+ {
+ // search for the best match
+ DefinitionListIterator dli(*(DefinitionList*)di);
+ Definition *d;
+ int minDistance=10000; // init at "infinite"
+ for (dli.toFirst();(d=dli.current());++dli) // foreach definition
+ {
+ // only look at members
+ if (d->definitionType()==Definition::TypeMember)
+ {
+ // that are also typedefs
+ MemberDef *md = (MemberDef *)d;
+ if (md->isTypedef()) // d is a typedef
+ {
+ // test accessibility of typedef within scope.
+ int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
+ if (distance!=-1 && distance<minDistance)
+ // definition is accessible and a better match
+ {
+ minDistance=distance;
+ bestMatch = md;
+ }
+ }
+ }
+ }
+ }
+ else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
+ {
+ Definition *d = (Definition*)di;
+ // that are also typedefs
+ MemberDef *md = (MemberDef *)di;
+ if (md->isTypedef()) // d is a typedef
+ {
+ // test accessibility of typedef within scope.
+ int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
+ if (distance!=-1) // definition is accessible
+ {
+ bestMatch = md;
+ }
+ }
+ }
+ if (bestMatch)
+ {
+ result = bestMatch->typeString();
+ if (pTypeDef) *pTypeDef=bestMatch;
+ }
+
+ //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+ // name.data(),result.data());
+ return result;
+}
+
+static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
+{
+ if (cl)
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ Definition *cd;
+ for (cli.toFirst();(cd=cli.current());++cli)
+ {
+ if (cd->localName()==localName)
+ {
+ return cd;
+ }
+ }
+ }
+ return 0;
+}
+
+/*! Starting with scope \a start, the string \a path is interpreted as
+ * a part of a qualified scope name (e.g. A::B::C), and the scope is
+ * searched. If found the scope definition is returned, otherwise 0
+ * is returned.
+ */
+static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
+{
+ int is,ps;
+ int l;
+ Definition *current=start;
+ ps=0;
+ //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
+ // for each part of the explicit scope
+ while ((is=getScopeFragment(path,ps,&l))!=-1)
+ {
+ // try to resolve the part if it is a typedef
+ MemberDef *typeDef=0;
+ QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
+ //printf(" qualScopePart=%s\n",qualScopePart.data());
+ if (typeDef)
+ {
+ ClassDef *type = newResolveTypedef(fileScope,typeDef);
+ if (type)
+ {
+ //printf("Found type %s\n",type->name().data());
+ return type;
+ }
+ }
+ Definition *next = current->findInnerCompound(qualScopePart);
+ //printf("++ Looking for %s inside %s result %s\n",
+ // qualScopePart.data(),
+ // current->name().data(),
+ // next?next->name().data():"<null>");
+ if (next==0) // failed to follow the path
+ {
+ //printf("==> next==0!\n");
+ if (current->definitionType()==Definition::TypeNamespace)
+ {
+ next = endOfPathIsUsedClass(
+ ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
+ }
+ else if (current->definitionType()==Definition::TypeFile)
+ {
+ next = endOfPathIsUsedClass(
+ ((FileDef *)current)->getUsedClasses(),qualScopePart);
+ }
+ current = next;
+ if (current==0) break;
+ }
+ else // continue to follow scope
+ {
+ current = next;
+ //printf("==> current = %p\n",current);
+ }
+ ps=is+l;
+ }
+ //printf("followPath(start=%s,path=%s) result=%s\n",
+ // start->name().data(),path.data(),current?current->name().data():"<null>");
+ return current; // path could be followed
+}
+
+bool accessibleViaUsingClass(const SDict<Definition> *cl,
+ FileDef *fileScope,
+ Definition *item,
+ const QCString &explicitScopePart=""
+ )
+{
+ //printf("accessibleViaUsingClass(%p)\n",cl);
+ if (cl) // see if the class was imported via a using statement
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ Definition *ucd;
+ bool explicitScopePartEmpty = explicitScopePart.isEmpty();
+ for (cli.toFirst();(ucd=cli.current());++cli)
+ {
+ //printf("Trying via used class %s\n",ucd->name().data());
+ Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
+ if (sc && sc==item) return TRUE;
+ //printf("Try via used class done\n");
+ }
+ }
+ return FALSE;
+}
+
+bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
+ FileDef *fileScope,
+ Definition *item,
+ const QCString &explicitScopePart="")
+{
+ static QDict<void> visitedDict;
+ if (nl) // check used namespaces for the class
+ {
+ NamespaceSDict::Iterator nli(*nl);
+ NamespaceDef *und;
+ int count=0;
+ for (nli.toFirst();(und=nli.current());++nli,count++)
+ {
+ //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
+ // count,nl->count());
+ Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
+ if (sc && item->getOuterScope()==sc)
+ {
+ //printf("] found it\n");
+ return TRUE;
+ }
+ QCString key=und->name();
+ if (und->getUsedNamespaces() && visitedDict.find(key)==0)
+ {
+ visitedDict.insert(key,(void *)0x08);
+
+ if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
+ {
+ //printf("] found it via recursion\n");
+ return TRUE;
+ }
+
+ visitedDict.remove(key);
+ }
+ //printf("] Try via used namespace done\n");
+ }
+ }
+ return FALSE;
+}
+
+const int MAX_STACK_SIZE = 1000;
+
+/** Helper class representing the stack of items considered while resolving
+ * the scope.
+ */
+class AccessStack
+{
+ public:
+ AccessStack() : m_index(0) {}
+ void push(Definition *scope,FileDef *fileScope,Definition *item)
+ {
+ if (m_index<MAX_STACK_SIZE)
+ {
+ m_elements[m_index].scope = scope;
+ m_elements[m_index].fileScope = fileScope;
+ m_elements[m_index].item = item;
+ m_index++;
+ }
+ }
+ void push(Definition *scope,FileDef *fileScope,Definition *item,const QCString &expScope)
+ {
+ if (m_index<MAX_STACK_SIZE)
+ {
+ m_elements[m_index].scope = scope;
+ m_elements[m_index].fileScope = fileScope;
+ m_elements[m_index].item = item;
+ m_elements[m_index].expScope = expScope;
+ m_index++;
+ }
+ }
+ void pop()
+ {
+ if (m_index>0) m_index--;
+ }
+ bool find(Definition *scope,FileDef *fileScope, Definition *item)
+ {
+ int i=0;
+ for (i=0;i<m_index;i++)
+ {
+ AccessElem *e = &m_elements[i];
+ if (e->scope==scope && e->fileScope==fileScope && e->item==item)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ bool find(Definition *scope,FileDef *fileScope, Definition *item,const QCString &expScope)
+ {
+ int i=0;
+ for (i=0;i<m_index;i++)
+ {
+ AccessElem *e = &m_elements[i];
+ if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ private:
+ /** Element in the stack. */
+ struct AccessElem
+ {
+ Definition *scope;
+ FileDef *fileScope;
+ Definition *item;
+ QCString expScope;
+ };
+ int m_index;
+ AccessElem m_elements[MAX_STACK_SIZE];
+};
+
+/* Returns the "distance" (=number of levels up) from item to scope, or -1
+ * if item in not inside scope.
+ */
+int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
+{
+ //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
+ // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
+
+ static AccessStack accessStack;
+ if (accessStack.find(scope,fileScope,item))
+ {
+ return -1;
+ }
+ accessStack.push(scope,fileScope,item);
+
+ int result=0; // assume we found it
+ int i;
+
+ Definition *itemScope=item->getOuterScope();
+
+ if (
+ itemScope==scope || // same thing
+ (item->definitionType()==Definition::TypeMember && // a member
+ itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
+ scope->definitionType()==Definition::TypeClass && // accessible
+ ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope
+ ) ||
+ (item->definitionType()==Definition::TypeClass && // a nested class
+ itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
+ scope->definitionType()==Definition::TypeClass && // class of scope
+ ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)
+ )
+ )
+ {
+ //printf("> found it\n");
+ }
+ else if (scope==Doxygen::globalScope)
+ {
+ if (fileScope)
+ {
+ SDict<Definition> *cl = fileScope->getUsedClasses();
+ if (accessibleViaUsingClass(cl,fileScope,item))
+ {
+ //printf("> found via used class\n");
+ goto done;
+ }
+ NamespaceSDict *nl = fileScope->getUsedNamespaces();
+ if (accessibleViaUsingNamespace(nl,fileScope,item))
+ {
+ //printf("> found via used namespace\n");
+ goto done;
+ }
+ }
+ //printf("> reached global scope\n");
+ result=-1; // not found in path to globalScope
+ }
+ else // keep searching
+ {
+ // check if scope is a namespace, which is using other classes and namespaces
+ if (scope->definitionType()==Definition::TypeNamespace)
+ {
+ NamespaceDef *nscope = (NamespaceDef*)scope;
+ //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
+ SDict<Definition> *cl = nscope->getUsedClasses();
+ if (accessibleViaUsingClass(cl,fileScope,item))
+ {
+ //printf("> found via used class\n");
+ goto done;
+ }
+ NamespaceSDict *nl = nscope->getUsedNamespaces();
+ if (accessibleViaUsingNamespace(nl,fileScope,item))
+ {
+ //printf("> found via used namespace\n");
+ goto done;
+ }
+ }
+ // repeat for the parent scope
+ i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
+ //printf("> result=%d\n",i);
+ result= (i==-1) ? -1 : i+2;
+ }
+done:
+ accessStack.pop();
+ //Doxygen::lookupCache.insert(key,new int(result));
+ return result;
+}
+
+
+/* Returns the "distance" (=number of levels up) from item to scope, or -1
+ * if item in not in this scope. The explicitScopePart limits the search
+ * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
+ * Example:
+ *
+ * class A { public: class I {}; };
+ * class B { public: class J {}; };
+ *
+ * - Looking for item=='J' inside scope=='B' will return 0.
+ * - Looking for item=='I' inside scope=='B' will return -1
+ * (as it is not found in B nor in the global scope).
+ * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
+ * not found and then A::I is searched in the global scope, which matches and
+ * thus the result is 1.
+ */
+int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,
+ Definition *item,const QCString &explicitScopePart)
+{
+ if (explicitScopePart.isEmpty())
+ {
+ // handle degenerate case where there is no explicit scope.
+ return isAccessibleFrom(scope,fileScope,item);
+ }
+
+ static AccessStack accessStack;
+ if (accessStack.find(scope,fileScope,item,explicitScopePart))
+ {
+ return -1;
+ }
+ accessStack.push(scope,fileScope,item,explicitScopePart);
+
+
+ //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
+ // item?item->name().data():"<none>",
+ // explicitScopePart.data());
+ int result=0; // assume we found it
+ Definition *newScope = followPath(scope,fileScope,explicitScopePart);
+ if (newScope) // explicitScope is inside scope => newScope is the result
+ {
+ Definition *itemScope = item->getOuterScope();
+ //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
+ //if (newScope && newScope->definitionType()==Definition::TypeClass)
+ //{
+ // ClassDef *cd = (ClassDef *)newScope;
+ // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
+ //}
+ if (itemScope==newScope) // exact match of scopes => distance==0
+ {
+ //printf("> found it\n");
+ }
+ else if (itemScope && newScope &&
+ itemScope->definitionType()==Definition::TypeClass &&
+ newScope->definitionType()==Definition::TypeClass &&
+ ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
+ )
+ {
+ // inheritance is also ok. Example: looking for B::I, where
+ // class A { public: class I {} };
+ // class B : public A {}
+ // but looking for B::I, where
+ // class A { public: class I {} };
+ // class B { public: class I {} };
+ // will find A::I, so we still prefer a direct match and give this one a distance of 1
+ result=1;
+
+ //printf("scope(%s) is base class of newScope(%s)\n",
+ // scope->name().data(),newScope->name().data());
+ }
+ else
+ {
+ int i=-1;
+ if (newScope->definitionType()==Definition::TypeNamespace)
+ {
+ g_visitedNamespaces.insert(newScope->name(),newScope);
+ // this part deals with the case where item is a class
+ // A::B::C but is explicit referenced as A::C, where B is imported
+ // in A via a using directive.
+ //printf("newScope is a namespace: %s!\n",newScope->name().data());
+ NamespaceDef *nscope = (NamespaceDef*)newScope;
+ SDict<Definition> *cl = nscope->getUsedClasses();
+ if (cl)
+ {
+ SDict<Definition>::Iterator cli(*cl);
+ Definition *cd;
+ for (cli.toFirst();(cd=cli.current());++cli)
+ {
+ //printf("Trying for class %s\n",cd->name().data());
+ if (cd==item)
+ {
+ //printf("> class is used in this scope\n");
+ goto done;
+ }
+ }
+ }
+ NamespaceSDict *nl = nscope->getUsedNamespaces();
+ if (nl)
+ {
+ NamespaceSDict::Iterator nli(*nl);
+ NamespaceDef *nd;
+ for (nli.toFirst();(nd=nli.current());++nli)
+ {
+ if (g_visitedNamespaces.find(nd->name())==0)
+ {
+ //printf("Trying for namespace %s\n",nd->name().data());
+ i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
+ if (i!=-1)
+ {
+ //printf("> found via explicit scope of used namespace\n");
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ // repeat for the parent scope
+ if (scope!=Doxygen::globalScope)
+ {
+ i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
+ item,explicitScopePart);
+ }
+ //printf(" | result=%d\n",i);
+ result = (i==-1) ? -1 : i+2;
+ }
+ }
+ else // failed to resolve explicitScope
+ {
+ //printf(" failed to resolve: scope=%s\n",scope->name().data());
+ if (scope->definitionType()==Definition::TypeNamespace)
+ {
+ NamespaceDef *nscope = (NamespaceDef*)scope;
+ NamespaceSDict *nl = nscope->getUsedNamespaces();
+ if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
+ {
+ //printf("> found in used namespace\n");
+ goto done;
+ }
+ }
+ if (scope==Doxygen::globalScope)
+ {
+ if (fileScope)
+ {
+ NamespaceSDict *nl = fileScope->getUsedNamespaces();
+ if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
+ {
+ //printf("> found in used namespace\n");
+ goto done;
+ }
+ }
+ //printf("> not found\n");
+ result=-1;
+ }
+ else // continue by looking into the parent scope
+ {
+ int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
+ item,explicitScopePart);
+ //printf("> result=%d\n",i);
+ result= (i==-1) ? -1 : i+2;
+ }
+ }
+
+done:
+ //printf(" > result=%d\n",result);
+ accessStack.pop();
+ //Doxygen::lookupCache.insert(key,new int(result));
+ return result;
+}
+
+int computeQualifiedIndex(const QCString &name)
+{
+ int i = name.find('<');
+ return name.findRev("::",i==-1 ? name.length() : i);
+}
+
+static void getResolvedSymbol(Definition *scope,
+ FileDef *fileScope,
+ Definition *d,
+ const QCString &explicitScopePart,
+ ArgumentList *actTemplParams,
+ int &minDistance,
+ ClassDef *&bestMatch,
+ MemberDef *&bestTypedef,
+ QCString &bestTemplSpec,
+ QCString &bestResolvedType
+ )
+{
+ //printf(" => found type %x name=%s d=%p\n",
+ // d->definitionType(),d->name().data(),d);
+
+ // only look at classes and members that are enums or typedefs
+ if (d->definitionType()==Definition::TypeClass ||
+ (d->definitionType()==Definition::TypeMember &&
+ (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate())
+ )
+ )
+ {
+ g_visitedNamespaces.clear();
+ // test accessibility of definition within scope.
+ int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+ //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
+ if (distance!=-1) // definition is accessible
+ {
+ // see if we are dealing with a class or a typedef
+ if (d->definitionType()==Definition::TypeClass) // d is a class
+ {
+ ClassDef *cd = (ClassDef *)d;
+ //printf("cd=%s\n",cd->name().data());
+ if (!cd->isTemplateArgument()) // skip classes that
+ // are only there to
+ // represent a template
+ // argument
+ {
+ //printf("is not a templ arg\n");
+ if (distance<minDistance) // found a definition that is "closer"
+ {
+ minDistance=distance;
+ bestMatch = cd;
+ bestTypedef = 0;
+ bestTemplSpec.resize(0);
+ bestResolvedType = cd->qualifiedName();
+ }
+ else if (distance==minDistance &&
+ fileScope && bestMatch &&
+ fileScope->getUsedNamespaces() &&
+ d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
+ bestMatch->getOuterScope()==Doxygen::globalScope
+ )
+ {
+ // in case the distance is equal it could be that a class X
+ // is defined in a namespace and in the global scope. When searched
+ // in the global scope the distance is 0 in both cases. We have
+ // to choose one of the definitions: we choose the one in the
+ // namespace if the fileScope imports namespaces and the definition
+ // found was in a namespace while the best match so far isn't.
+ // Just a non-perfect heuristic but it could help in some situations
+ // (kdecore code is an example).
+ minDistance=distance;
+ bestMatch = cd;
+ bestTypedef = 0;
+ bestTemplSpec.resize(0);
+ bestResolvedType = cd->qualifiedName();
+ }
+ }
+ else
+ {
+ //printf(" is a template argument!\n");
+ }
+ }
+ else if (d->definitionType()==Definition::TypeMember)
+ {
+ MemberDef *md = (MemberDef *)d;
+ //printf(" member isTypedef()=%d\n",md->isTypedef());
+ if (md->isTypedef()) // d is a typedef
+ {
+ QCString args=md->argsString();
+ if (args.isEmpty()) // do not expand "typedef t a[4];"
+ {
+ //printf(" found typedef!\n");
+
+ // we found a symbol at this distance, but if it didn't
+ // resolve to a class, we still have to make sure that
+ // something at a greater distance does not match, since
+ // that symbol is hidden by this one.
+ if (distance<minDistance)
+ {
+ QCString spec;
+ QCString type;
+ minDistance=distance;
+ MemberDef *enumType = 0;
+ ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
+ if (cd) // type resolves to a class
+ {
+ //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
+ bestMatch = cd;
+ bestTypedef = md;
+ bestTemplSpec = spec;
+ bestResolvedType = type;
+ }
+ else if (enumType) // type resolves to a enum
+ {
+ //printf(" is enum\n");
+ bestMatch = 0;
+ bestTypedef = enumType;
+ bestTemplSpec = "";
+ bestResolvedType = enumType->qualifiedName();
+ }
+ else if (md->isReference()) // external reference
+ {
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec = spec;
+ bestResolvedType = type;
+ }
+ else
+ {
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec.resize(0);
+ bestResolvedType.resize(0);
+ //printf(" no match\n");
+ }
+ }
+ else
+ {
+ //printf(" not the best match %d min=%d\n",distance,minDistance);
+ }
+ }
+ else
+ {
+ //printf(" not a simple typedef\n")
+ }
+ }
+ else if (md->isEnumerate())
+ {
+ if (distance<minDistance)
+ {
+ minDistance=distance;
+ bestMatch = 0;
+ bestTypedef = md;
+ bestTemplSpec = "";
+ bestResolvedType = md->qualifiedName();
+ }
+ }
+ }
+ } // if definition accessible
+ else
+ {
+ //printf(" Not accessible!\n");
+ }
+ } // if definition is a class or member
+ //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
+}
+
+/* Find the fully qualified class name referred to by the input class
+ * or typedef name against the input scope.
+ * Loops through scope and each of its parent scopes looking for a
+ * match against the input name. Can recursively call itself when
+ * resolving typedefs.
+ */
+static ClassDef *getResolvedClassRec(Definition *scope,
+ FileDef *fileScope,
+ const char *n,
+ MemberDef **pTypeDef,
+ QCString *pTemplSpec,
+ QCString *pResolvedType
+ )
+{
+ //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
+ QCString name;
+ QCString explicitScopePart;
+ QCString strippedTemplateParams;
+ name=stripTemplateSpecifiersFromScope
+ (removeRedundantWhiteSpace(n),TRUE,
+ &strippedTemplateParams);
+ ArgumentList actTemplParams;
+ if (!strippedTemplateParams.isEmpty()) // template part that was stripped
+ {
+ stringToArgumentList(strippedTemplateParams,&actTemplParams);
+ }
+
+ int qualifierIndex = computeQualifiedIndex(name);
+ //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
+ if (qualifierIndex!=-1) // qualified name
+ {
+ // split off the explicit scope part
+ explicitScopePart=name.left(qualifierIndex);
+ // todo: improve namespace alias substitution
+ replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
+ name=name.mid(qualifierIndex+2);
+ }
+
+ if (name.isEmpty())
+ {
+ //printf("] empty name\n");
+ return 0; // empty name
+ }
+
+ //printf("Looking for symbol %s\n",name.data());
+ DefinitionIntf *di = Doxygen::symbolMap->find(name);
+ // the -g (for C# generics) and -p (for ObjC protocols) are now already
+ // stripped from the key used in the symbolMap, so that is not needed here.
+ if (di==0)
+ {
+ //di = Doxygen::symbolMap->find(name+"-g");
+ //if (di==0)
+ //{
+ di = Doxygen::symbolMap->find(name+"-p");
+ if (di==0)
+ {
+ //printf("no such symbol!\n");
+ return 0;
+ }
+ //}
+ }
+ //printf("found symbol!\n");
+
+ bool hasUsingStatements =
+ (fileScope && ((fileScope->getUsedNamespaces() &&
+ fileScope->getUsedNamespaces()->count()>0) ||
+ (fileScope->getUsedClasses() &&
+ fileScope->getUsedClasses()->count()>0))
+ );
+ //printf("hasUsingStatements=%d\n",hasUsingStatements);
+ // Since it is often the case that the same name is searched in the same
+ // scope over an over again (especially for the linked source code generation)
+ // we use a cache to collect previous results. This is possible since the
+ // result of a lookup is deterministic. As the key we use the concatenated
+ // scope, the name to search for and the explicit scope prefix. The speedup
+ // achieved by this simple cache can be enormous.
+ int scopeNameLen = scope->name().length()+1;
+ int nameLen = name.length()+1;
+ int explicitPartLen = explicitScopePart.length();
+ int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
+
+ // below is a more efficient coding of
+ // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
+ QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
+ char *p=key.data();
+ qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
+ p+=scopeNameLen;
+ qstrcpy(p,name); *(p+nameLen-1)='+';
+ p+=nameLen;
+ qstrcpy(p,explicitScopePart);
+ p+=explicitPartLen;
+
+ // if a file scope is given and it contains using statements we should
+ // also use the file part in the key (as a class name can be in
+ // two different namespaces and a using statement in a file can select
+ // one of them).
+ if (hasUsingStatements)
+ {
+ // below is a more efficient coding of
+ // key+="+"+fileScope->name();
+ *p++='+';
+ qstrcpy(p,fileScope->absFilePath());
+ p+=fileScopeLen-1;
+ }
+ *p='\0';
+
+ LookupInfo *pval=Doxygen::lookupCache->find(key);
+ //printf("Searching for %s result=%p\n",key.data(),pval);
+ if (pval)
+ {
+ //printf("LookupInfo %p %p '%s' %p\n",
+ // pval->classDef, pval->typeDef, pval->templSpec.data(),
+ // pval->resolvedType.data());
+ if (pTemplSpec) *pTemplSpec=pval->templSpec;
+ if (pTypeDef) *pTypeDef=pval->typeDef;
+ if (pResolvedType) *pResolvedType=pval->resolvedType;
+ //printf("] cachedMatch=%s\n",
+ // pval->classDef?pval->classDef->name().data():"<none>");
+ //if (pTemplSpec)
+ // printf("templSpec=%s\n",pTemplSpec->data());
+ return pval->classDef;
+ }
+ else // not found yet; we already add a 0 to avoid the possibility of
+ // endless recursion.
+ {
+ Doxygen::lookupCache->insert(key,new LookupInfo);
+ }
+
+ ClassDef *bestMatch=0;
+ MemberDef *bestTypedef=0;
+ QCString bestTemplSpec;
+ QCString bestResolvedType;
+ int minDistance=10000; // init at "infinite"
+
+ if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
+ {
+ //printf(" name is not unique\n");
+ DefinitionListIterator dli(*(DefinitionList*)di);
+ Definition *d;
+ int count=0;
+ for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
+ {
+ getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
+ minDistance,bestMatch,bestTypedef,bestTemplSpec,
+ bestResolvedType);
+ }
+ }
+ else // unique name
+ {
+ //printf(" name is unique\n");
+ Definition *d = (Definition *)di;
+ getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
+ minDistance,bestMatch,bestTypedef,bestTemplSpec,
+ bestResolvedType);
+ }
+
+ if (pTypeDef)
+ {
+ *pTypeDef = bestTypedef;
+ }
+ if (pTemplSpec)
+ {
+ *pTemplSpec = bestTemplSpec;
+ }
+ if (pResolvedType)
+ {
+ *pResolvedType = bestResolvedType;
+ }
+ //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
+ // bestMatch,bestResolvedType.data());
+
+ pval=Doxygen::lookupCache->find(key);
+ if (pval)
+ {
+ pval->classDef = bestMatch;
+ pval->typeDef = bestTypedef;
+ pval->templSpec = bestTemplSpec;
+ pval->resolvedType = bestResolvedType;
+ }
+ else
+ {
+ Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
+ }
+ //printf("] bestMatch=%s distance=%d\n",
+ // bestMatch?bestMatch->name().data():"<none>",minDistance);
+ //if (pTemplSpec)
+ // printf("templSpec=%s\n",pTemplSpec->data());
+ return bestMatch;
+}
+
+/* Find the fully qualified class name referred to by the input class
+ * or typedef name against the input scope.
+ * Loops through scope and each of its parent scopes looking for a
+ * match against the input name.
+ */
+ClassDef *getResolvedClass(Definition *scope,
+ FileDef *fileScope,
+ const char *n,
+ MemberDef **pTypeDef,
+ QCString *pTemplSpec,
+ bool mayBeUnlinkable,
+ bool mayBeHidden,
+ QCString *pResolvedType
+ )
+{
+ g_resolvedTypedefs.clear();
+ if (scope==0 ||
+ (scope->definitionType()!=Definition::TypeClass &&
+ scope->definitionType()!=Definition::TypeNamespace
+ ) ||
+ (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
+ )
+ {
+ scope=Doxygen::globalScope;
+ }
+ //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
+ // scope?scope->name().data():"<global>",
+ // fileScope?fileScope->name().data():"<none>",
+ // n,
+ // mayBeUnlinkable
+ // );
+ ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
+ if (!mayBeUnlinkable && result && !result->isLinkable())
+ {
+ if (!mayBeHidden || !result->isHidden())
+ {
+ //printf("result was %s\n",result?result->name().data():"<none>");
+ result=0; // don't link to artificial/hidden classes unless explicitly allowed
+ }
+ }
+ //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
+ // n,result?result->name().data():"<none>");
+ return result;
+}
+
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+
+static bool findOperator(const QCString &s,int i)
+{
+ int b = s.findRev("operator",i);
+ if (b==-1) return FALSE; // not found
+ b+=8;
+ while (b<i) // check if there are only spaces in between
+ // the operator and the >
+ {
+ if (!isspace((uchar)s.at(b))) return FALSE;
+ b++;
+ }
+ return TRUE;
+}
+
+static bool findOperator2(const QCString &s,int i)
+{
+ int b = s.findRev("operator",i);
+ if (b==-1) return FALSE; // not found
+ b+=8;
+ while (b<i) // check if there are only non-ascii
+ // characters in front of the operator
+ {
+ if (isId((uchar)s.at(b))) return FALSE;
+ b++;
+ }
+ return TRUE;
+}
+
+static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
+static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
+
+// Note: this function is not reentrant due to the use of static buffer!
+QCString removeRedundantWhiteSpace(const QCString &s)
+{
+ static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT");
+ if (s.isEmpty()) return s;
+ static GrowBuf growBuf;
+ //int resultLen = 1024;
+ //int resultPos = 0;
+ //QCString result(resultLen);
+ // we use growBuf.addChar(c) instead of result+=c to
+ // improve the performance of this function
+ growBuf.clear();
+ uint i;
+ uint l=s.length();
+ uint csp=0;
+ uint vsp=0;
+ for (i=0;i<l;i++)
+ {
+nextChar:
+ char c=s.at(i);
+
+ // search for "const"
+ if (csp<6 && c==constScope[csp] && // character matches substring "const"
+ (csp>0 || // if it is the first character
+ i==0 || // the previous may not be a digit
+ !isId(s.at(i-1))
+ )
+ )
+ csp++;
+ else // reset counter
+ csp=0;
+
+ // search for "virtual"
+ if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
+ (vsp>0 || // if it is the first character
+ i==0 || // the previous may not be a digit
+ !isId(s.at(i-1))
+ )
+ )
+ vsp++;
+ else // reset counter
+ vsp=0;
+
+ if (c=='"') // quoted string
+ {
+ i++;
+ growBuf.addChar(c);
+ while (i<l)
+ {
+ char cc=s.at(i);
+ growBuf.addChar(cc);
+ if (cc=='\\') // escaped character
+ {
+ growBuf.addChar(s.at(i+1));
+ i+=2;
+ }
+ else if (cc=='"') // end of string
+ { i++; goto nextChar; }
+ else // any other character
+ { i++; }
+ }
+ }
+ else if (i<l-2 && c=='<' && // current char is a <
+ (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space
+ (i<8 || !findOperator(s,i)) // string in front is not "operator"
+ )
+ {
+ growBuf.addChar('<');
+ growBuf.addChar(' ');
+ }
+ else if (i>0 && c=='>' && // current char is a >
+ (isId(s.at(i-1)) || isspace((uchar)s.at(i-1)) || s.at(i-1)=='*' || s.at(i-1)=='&') && // prev char is an id char or space
+ (i<8 || !findOperator(s,i)) // string in front is not "operator"
+ )
+ {
+ growBuf.addChar(' ');
+ growBuf.addChar('>');
+ }
+ else if (i>0 && c==',' && !isspace((uchar)s.at(i-1))
+ && ((i<l-1 && isId(s.at(i+1)))
+ || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) // for PHP
+ || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))) // for PHP
+ {
+ growBuf.addChar(',');
+ growBuf.addChar(' ');
+ }
+ else if (i>0 &&
+ ((isId(s.at(i)) && s.at(i-1)==')') ||
+ (s.at(i)=='\'' && s.at(i-1)==' ')
+ )
+ )
+ {
+ growBuf.addChar(' ');
+ growBuf.addChar(s.at(i));
+ }
+ else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ &&
+ !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
+ s.at(i+1)==')' ||
+ s.at(i+1)==',' ||
+ s.at(i+1)=='\0'
+ )
+ )
+ // prevent const ::A from being converted to const::A
+ {
+ growBuf.addChar('t');
+ growBuf.addChar(' ');
+ if (s.at(i+1)==' ') i++;
+ csp=0;
+ }
+ else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/)
+ // replace const::A by const ::A
+ {
+ growBuf.addChar(' ');
+ growBuf.addChar(':');
+ csp=0;
+ }
+ else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ &&
+ !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
+ s.at(i+1)==')' ||
+ s.at(i+1)==',' ||
+ s.at(i+1)=='\0'
+ )
+ )
+ // prevent virtual ::A from being converted to virtual::A
+ {
+ growBuf.addChar('l');
+ growBuf.addChar(' ');
+ if (s.at(i+1)==' ') i++;
+ vsp=0;
+ }
+ else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/)
+ // replace virtual::A by virtual ::A
+ {
+ growBuf.addChar(' ');
+ growBuf.addChar(':');
+ vsp=0;
+ }
+ else if (!isspace((uchar)c) || // not a space
+ ( i>0 && i<l-1 && // internal character
+ (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']') &&
+ (isId(s.at(i+1)) ||
+ (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) ||
+ (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3)))
+ )
+ )
+ )
+ {
+ if (c=='*' || c=='&' || c=='@' || c=='$')
+ {
+ //uint rl=result.length();
+ uint rl=growBuf.getPos();
+ if ((rl>0 && (isId(growBuf.at(rl-1)) || growBuf.at(rl-1)=='>')) &&
+ ((c!='*' && c!='&') || !findOperator2(s,i)) // avoid splitting operator* and operator->* and operator&
+ )
+ {
+ growBuf.addChar(' ');
+ }
+ }
+ else if (c=='-')
+ {
+ uint rl=growBuf.getPos();
+ if (rl>0 && growBuf.at(rl-1)==')' && i<l-1 && s.at(i+1)=='>') // trailing return type ')->' => ') ->'
+ {
+ growBuf.addChar(' ');
+ }
+ }
+ growBuf.addChar(c);
+ if (cliSupport &&
+ (c=='^' || c=='%') && i>1 && isId(s.at(i-1)) &&
+ !findOperator(s,i)
+ )
+ {
+ growBuf.addChar(' '); // C++/CLI: Type^ name and Type% name
+ }
+ }
+ }
+ //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data());
+ growBuf.addChar(0);
+ //result.resize(resultPos);
+ return growBuf.get();
+}
+
+bool rightScopeMatch(const QCString &scope, const QCString &name)
+{
+ return (name==scope || // equal
+ (scope.right(name.length())==name && // substring
+ scope.at(scope.length()-name.length()-1)==':' // scope
+ )
+ );
+}
+
+bool leftScopeMatch(const QCString &scope, const QCString &name)
+{
+ return (name==scope || // equal
+ (scope.left(name.length())==name && // substring
+ scope.at(name.length())==':' // scope
+ )
+ );
+}
+
+
+void linkifyText(const TextGeneratorIntf &out,Definition *scope,
+ FileDef *fileScope,Definition *self,
+ const char *text, bool autoBreak,bool external,
+ bool keepSpaces,int indentLevel)
+{
+ //printf("linkify=`%s'\n",text);
+ static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*");
+ static QRegExp regExpSplit("(?!:),");
+ QCString txtStr=text;
+ int strLen = txtStr.length();
+ //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n",
+ // scope?scope->name().data():"<none>",
+ // fileScope?fileScope->name().data():"<none>",
+ // txtStr.data(),strLen,external);
+ int matchLen;
+ int index=0;
+ int newIndex;
+ int skipIndex=0;
+ int floatingIndex=0;
+ if (strLen==0) return;
+ // read a word from the text string
+ while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 &&
+ (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
+ )
+ {
+ // add non-word part to the result
+ floatingIndex+=newIndex-skipIndex+matchLen;
+ bool insideString=FALSE;
+ int i;
+ for (i=index;i<newIndex;i++)
+ {
+ if (txtStr.at(i)=='"') insideString=!insideString;
+ }
+
+ //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
+ if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
+ {
+ QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
+ int splitLength = splitText.length();
+ int offset=1;
+ i=splitText.find(regExpSplit,0);
+ if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
+ if (i==-1) i=splitText.find('>');
+ if (i==-1) i=splitText.find(' ');
+ //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
+ if (i!=-1) // add a link-break at i in case of Html output
+ {
+ out.writeString(splitText.left(i+offset),keepSpaces);
+ out.writeBreak(indentLevel==0 ? 0 : indentLevel+1);
+ out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
+ floatingIndex=splitLength-i-offset+matchLen;
+ }
+ else
+ {
+ out.writeString(splitText,keepSpaces);
+ }
+ }
+ else
+ {
+ //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
+ out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces);
+ }
+ // get word from string
+ QCString word=txtStr.mid(newIndex,matchLen);
+ QCString matchWord = substitute(substitute(word,"\\","::"),".","::");
+ //printf("linkifyText word=%s matchWord=%s scope=%s\n",
+ // word.data(),matchWord.data(),scope?scope->name().data():"<none>");
+ bool found=FALSE;
+ if (!insideString)
+ {
+ ClassDef *cd=0;
+ FileDef *fd=0;
+ MemberDef *md=0;
+ NamespaceDef *nd=0;
+ GroupDef *gd=0;
+ //printf("** Match word '%s'\n",matchWord.data());
+
+ MemberDef *typeDef=0;
+ cd=getResolvedClass(scope,fileScope,matchWord,&typeDef);
+ if (typeDef) // First look at typedef then class, see bug 584184.
+ {
+ //printf("Found typedef %s\n",typeDef->name().data());
+ if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
+ {
+ if (typeDef->getOuterScope()!=self)
+ {
+ out.writeLink(typeDef->getReference(),
+ typeDef->getOutputFileBase(),
+ typeDef->anchor(),
+ word);
+ found=TRUE;
+ }
+ }
+ }
+ if (!found && (cd || (cd=getClass(matchWord))))
+ {
+ //printf("Found class %s\n",cd->name().data());
+ // add link to the result
+ if (external ? cd->isLinkable() : cd->isLinkableInProject())
+ {
+ if (cd!=self)
+ {
+ out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
+ found=TRUE;
+ }
+ }
+ }
+ else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
+ {
+ // add link to the result
+ if (external ? cd->isLinkable() : cd->isLinkableInProject())
+ {
+ if (cd!=self)
+ {
+ out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
+ found=TRUE;
+ }
+ }
+ }
+// else if ((cd=getClass(matchWord+"-g"))) // C# generic as well
+// {
+// // add link to the result
+// if (external ? cd->isLinkable() : cd->isLinkableInProject())
+// {
+// if (cd!=self)
+// {
+// out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
+// found=TRUE;
+// }
+// }
+// }
+ else
+ {
+ //printf(" -> nothing\n");
+ }
+
+ int m = matchWord.findRev("::");
+ QCString scopeName;
+ if (scope &&
+ (scope->definitionType()==Definition::TypeClass ||
+ scope->definitionType()==Definition::TypeNamespace
+ )
+ )
+ {
+ scopeName=scope->name();
+ }
+ else if (m!=-1)
+ {
+ scopeName = matchWord.left(m);
+ matchWord = matchWord.mid(m+2);
+ }
+
+ //printf("ScopeName=%s\n",scopeName.data());
+ //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data());
+ if (!found &&
+ getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) &&
+ //(md->isTypedef() || md->isEnumerate() ||
+ // md->isReference() || md->isVariable()
+ //) &&
+ (external ? md->isLinkable() : md->isLinkableInProject())
+ )
+ {
+ //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
+ //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
+ // md->anchor(),word);
+ if (md!=self && (self==0 || md->name()!=self->name()))
+ // name check is needed for overloaded members, where getDefs just returns one
+ {
+ out.writeLink(md->getReference(),md->getOutputFileBase(),
+ md->anchor(),word);
+ //printf("found symbol %s\n",matchWord.data());
+ found=TRUE;
+ }
+ }
+ }
+
+ if (!found) // add word to the result
+ {
+ out.writeString(word,keepSpaces);
+ }
+ // set next start point in the string
+ //printf("index=%d/%d\n",index,txtStr.length());
+ skipIndex=index=newIndex+matchLen;
+ }
+ // add last part of the string to the result.
+ //ol.docify(txtStr.right(txtStr.length()-skipIndex));
+ out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
+}
+
+
+void writeExample(OutputList &ol,ExampleSDict *ed)
+{
+ QCString exampleLine=theTranslator->trWriteList(ed->count());
+
+ //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
+ //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
+ //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
+ QRegExp marker("@[0-9]+");
+ int index=0,newIndex,matchLen;
+ // now replace all markers in inheritLine with links to the classes
+ while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
+ {
+ bool ok;
+ ol.parseText(exampleLine.mid(index,newIndex-index));
+ uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
+ Example *e=ed->at(entryIndex);
+ if (ok && e)
+ {
+ ol.pushGeneratorState();
+ //if (latexEnabled) ol.disable(OutputGenerator::Latex);
+ ol.disable(OutputGenerator::Latex);
+ ol.disable(OutputGenerator::RTF);
+ // link for Html / man
+ //printf("writeObjectLink(file=%s)\n",e->file.data());
+ ol.writeObjectLink(0,e->file,e->anchor,e->name);
+ ol.popGeneratorState();
+
+ ol.pushGeneratorState();
+ //if (latexEnabled) ol.enable(OutputGenerator::Latex);
+ ol.disable(OutputGenerator::Man);
+ ol.disable(OutputGenerator::Html);
+ // link for Latex / pdf with anchor because the sources
+ // are not hyperlinked (not possible with a verbatim environment).
+ ol.writeObjectLink(0,e->file,0,e->name);
+ //if (manEnabled) ol.enable(OutputGenerator::Man);
+ //if (htmlEnabled) ol.enable(OutputGenerator::Html);
+ ol.popGeneratorState();
+ }
+ index=newIndex+matchLen;
+ }
+ ol.parseText(exampleLine.right(exampleLine.length()-index));
+ ol.writeString(".");
+}
+
+
+QCString argListToString(ArgumentList *al,bool useCanonicalType,bool showDefVals)
+{
+ QCString result;
+ if (al==0) return result;
+ Argument *a=al->first();
+ result+="(";
+ while (a)
+ {
+ QCString type1 = useCanonicalType && !a->canType.isEmpty() ?
+ a->canType : a->type;
+ QCString type2;
+ int i=type1.find(")("); // hack to deal with function pointers
+ if (i!=-1)
+ {
+ type2=type1.mid(i);
+ type1=type1.left(i);
+ }
+ if (!a->attrib.isEmpty())
+ {
+ result+=a->attrib+" ";
+ }
+ if (!a->name.isEmpty() || !a->array.isEmpty())
+ {
+ result+= type1+" "+a->name+type2+a->array;
+ }
+ else
+ {
+ result+= type1+type2;
+ }
+ if (!a->defval.isEmpty() && showDefVals)
+ {
+ result+="="+a->defval;
+ }
+ a = al->next();
+ if (a) result+=", ";
+ }
+ result+=")";
+ if (al->constSpecifier) result+=" const";
+ if (al->volatileSpecifier) result+=" volatile";
+ if (!al->trailingReturnType.isEmpty()) result+=" -> "+al->trailingReturnType;
+ if (al->pureSpecifier) result+=" =0";
+ return removeRedundantWhiteSpace(result);
+}
+
+QCString tempArgListToString(ArgumentList *al)
+{
+ QCString result;
+ if (al==0) return result;
+ result="<";
+ Argument *a=al->first();
+ while (a)
+ {
+ if (!a->name.isEmpty()) // add template argument name
+ {
+ if (a->type.left(4)=="out") // C# covariance
+ {
+ result+="out ";
+ }
+ else if (a->type.left(3)=="in") // C# contravariance
+ {
+ result+="in ";
+ }
+ result+=a->name;
+ }
+ else // extract name from type
+ {
+ int i=a->type.length()-1;
+ while (i>=0 && isId(a->type.at(i))) i--;
+ if (i>0)
+ {
+ result+=a->type.right(a->type.length()-i-1);
+ }
+ else // nothing found -> take whole name
+ {
+ result+=a->type;
+ }
+ }
+ a=al->next();
+ if (a) result+=", ";
+ }
+ result+=">";
+ return removeRedundantWhiteSpace(result);
+}
+
+
+// compute the HTML anchors for a list of members
+void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId)
+{
+ int count=0;
+ if (ml==0) return;
+ MemberListIterator mli(*ml);
+ MemberDef *md;
+ for (;(md=mli.current());++mli)
+ {
+ if (!md->isReference())
+ {
+ QCString anchor;
+ if (groupId==-1)
+ anchor.sprintf("%c%d",id,count++);
+ else
+ anchor.sprintf("%c%d_%d",id,groupId,count++);
+ if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
+ md->setAnchor(anchor);
+ //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
+ // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+/*! takes the \a buf of the given length \a len and converts CR LF (DOS)
+ * or CR (MAC) line ending to LF (Unix). Returns the length of the
+ * converted content (i.e. the same as \a len (Unix, MAC) or
+ * smaller (DOS).
+ */
+int filterCRLF(char *buf,int len)
+{
+ int src = 0; // source index
+ int dest = 0; // destination index
+ char c; // current character
+
+ while (src<len)
+ {
+ c = buf[src++]; // Remember the processed character.
+ if (c == '\r') // CR to be solved (MAC, DOS)
+ {
+ c = '\n'; // each CR to LF
+ if (src<len && buf[src] == '\n')
+ ++src; // skip LF just after CR (DOS)
+ }
+ else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
+ {
+ c = ' '; // turn into a space
+ }
+ buf[dest++] = c; // copy the (modified) character to dest
+ }
+ return dest; // length of the valid part of the buf
+}
+
+static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
+{
+ found=FALSE;
+ // compare the file name to the filter pattern list
+ QStrListIterator sli(filterList);
+ char* filterStr;
+ for (sli.toFirst(); (filterStr = sli.current()); ++sli)
+ {
+ QCString fs = filterStr;
+ int i_equals=fs.find('=');
+ if (i_equals!=-1)
+ {
+ QCString filterPattern = fs.left(i_equals);
+ QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE);
+ if (fpat.match(name)!=-1)
+ {
+ // found a match!
+ QCString filterName = fs.mid(i_equals+1);
+ if (filterName.find(' ')!=-1)
+ { // add quotes if the name has spaces
+ filterName="\""+filterName+"\"";
+ }
+ found=TRUE;
+ return filterName;
+ }
+ }
+ }
+
+ // no match
+ return "";
+}
+
+/*! looks for a filter for the file \a name. Returns the name of the filter
+ * if there is a match for the file name, otherwise an empty string.
+ * In case \a inSourceCode is TRUE then first the source filter list is
+ * considered.
+ */
+QCString getFileFilter(const char* name,bool isSourceCode)
+{
+ // sanity check
+ if (name==0) return "";
+
+ QStrList& filterSrcList = Config_getList("FILTER_SOURCE_PATTERNS");
+ QStrList& filterList = Config_getList("FILTER_PATTERNS");
+
+ QCString filterName;
+ bool found=FALSE;
+ if (isSourceCode && !filterSrcList.isEmpty())
+ { // first look for source filter pattern list
+ filterName = getFilterFromList(name,filterSrcList,found);
+ }
+ if (!found && filterName.isEmpty())
+ { // then look for filter pattern list
+ filterName = getFilterFromList(name,filterList,found);
+ }
+ if (!found)
+ { // then use the generic input filter
+ return Config_getString("INPUT_FILTER");
+ }
+ else
+ {
+ return filterName;
+ }
+}
+
+
+QCString transcodeCharacterStringToUTF8(const QCString &input)
+{
+ bool error=FALSE;
+ static QCString inputEncoding = Config_getString("INPUT_ENCODING");
+ const char *outputEncoding = "UTF-8";
+ if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
+ int inputSize=input.length();
+ int outputSize=inputSize*4+1;
+ QCString output(outputSize);
+ void *cd = portable_iconv_open(outputEncoding,inputEncoding);
+ if (cd==(void *)(-1))
+ {
+ err("error: unsupported character conversion: '%s'->'%s'\n",
+ inputEncoding.data(),outputEncoding);
+ error=TRUE;
+ }
+ if (!error)
+ {
+ size_t iLeft=inputSize;
+ size_t oLeft=outputSize;
+ char *inputPtr = input.data();
+ char *outputPtr = output.data();
+ if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
+ {
+ outputSize-=oLeft;
+ output.resize(outputSize+1);
+ output.at(outputSize)='\0';
+ //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
+ }
+ else
+ {
+ err("error: failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
+ inputEncoding.data(),outputEncoding,input.data());
+ error=TRUE;
+ }
+ }
+ portable_iconv_close(cd);
+ return error ? input : output;
+}
+
+/*! reads a file with name \a name and returns it as a string. If \a filter
+ * is TRUE the file will be filtered by any user specified input filter.
+ * If \a name is "-" the string will be read from standard input.
+ */
+QCString fileToString(const char *name,bool filter,bool isSourceCode)
+{
+ if (name==0 || name[0]==0) return 0;
+ QFile f;
+
+ bool fileOpened=FALSE;
+ if (name[0]=='-' && name[1]==0) // read from stdin
+ {
+ fileOpened=f.open(IO_ReadOnly,stdin);
+ if (fileOpened)
+ {
+ const int bSize=4096;
+ QCString contents(bSize);
+ int totalSize=0;
+ int size;
+ while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
+ {
+ totalSize+=bSize;
+ contents.resize(totalSize+bSize);
+ }
+ totalSize = filterCRLF(contents.data(),totalSize+size)+2;
+ contents.resize(totalSize);
+ contents.at(totalSize-2)='\n'; // to help the scanner
+ contents.at(totalSize-1)='\0';
+ return contents;
+ }
+ }
+ else // read from file
+ {
+ QFileInfo fi(name);
+ if (!fi.exists() || !fi.isFile())
+ {
+ err("error: file `%s' not found\n",name);
+ return "";
+ }
+ QCString filterName = getFileFilter(name,isSourceCode);
+ if (filterName.isEmpty() || !filter)
+ {
+ f.setName(name);
+ fileOpened=f.open(IO_ReadOnly);
+ if (fileOpened)
+ {
+ int fsize=f.size();
+ QCString contents(fsize+2);
+ f.readBlock(contents.data(),fsize);
+ if (fsize==0 || contents[fsize-1]=='\n')
+ contents[fsize]='\0';
+ else
+ contents[fsize]='\n'; // to help the scanner
+ contents[fsize+1]='\0';
+ f.close();
+ int newSize = filterCRLF(contents.data(),fsize+2);
+ if (newSize!=fsize+2)
+ {
+ contents.resize(newSize);
+ }
+ return transcodeCharacterStringToUTF8(contents);
+ }
+ }
+ else // filter the input
+ {
+ QCString cmd=filterName+" \""+name+"\"";
+ Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
+ FILE *f=portable_popen(cmd,"r");
+ if (!f)
+ {
+ err("error: could not execute filter %s\n",filterName.data());
+ return "";
+ }
+ const int bSize=4096;
+ QCString contents(bSize);
+ int totalSize=0;
+ int size;
+ while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize)
+ {
+ totalSize+=bSize;
+ contents.resize(totalSize+bSize);
+ }
+ totalSize = filterCRLF(contents.data(),totalSize+size)+2;
+ contents.resize(totalSize);
+ contents.at(totalSize-2)='\n'; // to help the scanner
+ contents.at(totalSize-1)='\0';
+ portable_pclose(f);
+ Debug::print(Debug::FilterOutput, 0, "Filter output\n");
+ Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",contents.data());
+ return transcodeCharacterStringToUTF8(contents);
+ }
+ }
+ if (!fileOpened)
+ {
+ err("error: cannot open file `%s' for reading\n",name);
+ }
+ return "";
+}
+
+QCString dateToString(bool includeTime)
+{
+ QDateTime current = QDateTime::currentDateTime();
+ return theTranslator->trDateTime(current.date().year(),
+ current.date().month(),
+ current.date().day(),
+ current.date().dayOfWeek(),
+ current.time().hour(),
+ current.time().minute(),
+ current.time().second(),
+ includeTime);
+}
+
+QCString yearToString()
+{
+ const QDate &d=QDate::currentDate();
+ QCString result;
+ result.sprintf("%d", d.year());
+ return result;
+}
+
+//----------------------------------------------------------------------
+// recursive function that returns the number of branches in the
+// inheritance tree that the base class `bcd' is below the class `cd'
+
+int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
+{
+ if (bcd->categoryOf()) // use class that is being extended in case of
+ // an Objective-C category
+ {
+ bcd=bcd->categoryOf();
+ }
+ if (cd==bcd) return level;
+ if (level==256)
+ {
+ err("error: Internal inconsistency: found class %s seem to have a recursive "
+ "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
+ return -1;
+ }
+ int m=maxInheritanceDepth;
+ if (cd->baseClasses())
+ {
+ BaseClassDef *bcdi = cd->baseClasses()->first();
+ while (bcdi)
+ {
+ int mc=minClassDistance(bcdi->classDef,bcd,level+1);
+ if (mc<m) m=mc;
+ if (m<0) break;
+ bcdi = cd->baseClasses()->next();
+ }
+ }
+ return m;
+}
+
+Protection classInheritedProtectionLevel(ClassDef *cd,ClassDef *bcd,Protection prot,int level)
+{
+ if (bcd->categoryOf()) // use class that is being extended in case of
+ // an Objective-C category
+ {
+ bcd=bcd->categoryOf();
+ }
+ if (cd==bcd)
+ {
+ goto exit;
+ }
+ if (level==256)
+ {
+ err("error: Internal inconsistency: found class %s seem to have a recursive "
+ "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
+ }
+ else if (cd->baseClasses())
+ {
+ BaseClassDef *bcdi = cd->baseClasses()->first();
+ while (bcdi && prot!=Private)
+ {
+ Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1);
+ if (baseProt==Private) prot=Private;
+ else if (baseProt==Protected) prot=Protected;
+ bcdi = cd->baseClasses()->next();
+ }
+ }
+exit:
+ //printf("classInheritedProtectionLevel(%s,%s)=%d\n",cd->name().data(),bcd->name().data(),prot);
+ return prot;
+}
+
+//static void printArgList(ArgumentList *al)
+//{
+// if (al==0) return;
+// ArgumentListIterator ali(*al);
+// Argument *a;
+// printf("(");
+// for (;(a=ali.current());++ali)
+// {
+// printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():"");
+// }
+// printf(")");
+//}
+
+#ifndef NEWMATCH
+// strip any template specifiers that follow className in string s
+static QCString trimTemplateSpecifiers(
+ const QCString &namespaceName,
+ const QCString &className,
+ const QCString &s
+ )
+{
+ //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
+ QCString scopeName=mergeScopes(namespaceName,className);
+ ClassDef *cd=getClass(scopeName);
+ if (cd==0) return s; // should not happen, but guard anyway.
+
+ QCString result=s;
+
+ int i=className.length()-1;
+ if (i>=0 && className.at(i)=='>') // template specialization
+ {
+ // replace unspecialized occurrences in s, with their specialized versions.
+ int count=1;
+ int cl=i+1;
+ while (i>=0)
+ {
+ char c=className.at(i);
+ if (c=='>') count++,i--;
+ else if (c=='<') { count--; if (count==0) break; }
+ else i--;
+ }
+ QCString unspecClassName=className.left(i);
+ int l=i;
+ int p=0;
+ while ((i=result.find(unspecClassName,p))!=-1)
+ {
+ if (result.at(i+l)!='<') // unspecialized version
+ {
+ result=result.left(i)+className+result.right(result.length()-i-l);
+ l=cl;
+ }
+ p=i+l;
+ }
+ }
+
+ //printf("result after specialization: %s\n",result.data());
+
+ QCString qualName=cd->qualifiedNameWithTemplateParameters();
+ //printf("QualifiedName = %s\n",qualName.data());
+ // We strip the template arguments following className (if any)
+ if (!qualName.isEmpty()) // there is a class name
+ {
+ int is,ps=0;
+ int p=0,l,i;
+
+ while ((is=getScopeFragment(qualName,ps,&l))!=-1)
+ {
+ QCString qualNamePart = qualName.right(qualName.length()-is);
+ //printf("qualNamePart=%s\n",qualNamePart.data());
+ while ((i=result.find(qualNamePart,p))!=-1)
+ {
+ int ql=qualNamePart.length();
+ result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
+ p=i+cd->name().length();
+ }
+ ps=is+l;
+ }
+ }
+ //printf("result=%s\n",result.data());
+
+ return result.stripWhiteSpace();
+}
+
+/*!
+ * @param pattern pattern to look for
+ * @param s string to search in
+ * @param p position to start
+ * @param len resulting pattern length
+ * @returns position on which string is found, or -1 if not found
+ */
+static int findScopePattern(const QCString &pattern,const QCString &s,
+ int p,int *len)
+{
+ int sl=s.length();
+ int pl=pattern.length();
+ int sp=0;
+ *len=0;
+ while (p<sl)
+ {
+ sp=p; // start of match
+ int pp=0; // pattern position
+ while (p<sl && pp<pl)
+ {
+ if (s.at(p)=='<') // skip template arguments while matching
+ {
+ int bc=1;
+ //printf("skipping pos=%d c=%c\n",p,s.at(p));
+ p++;
+ while (p<sl)
+ {
+ if (s.at(p)=='<') bc++;
+ else if (s.at(p)=='>')
+ {
+ bc--;
+ if (bc==0)
+ {
+ p++;
+ break;
+ }
+ }
+ //printf("skipping pos=%d c=%c\n",p,s.at(p));
+ p++;
+ }
+ }
+ else if (s.at(p)==pattern.at(pp))
+ {
+ //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
+ p++;
+ pp++;
+ }
+ else // no match
+ {
+ //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
+ p=sp+1;
+ break;
+ }
+ }
+ if (pp==pl) // whole pattern matches
+ {
+ *len=p-sp;
+ return sp;
+ }
+ }
+ return -1;
+}
+
+static QCString trimScope(const QCString &name,const QCString &s)
+{
+ int scopeOffset=name.length();
+ QCString result=s;
+ do // for each scope
+ {
+ QCString tmp;
+ QCString scope=name.left(scopeOffset)+"::";
+ //printf("Trying with scope=`%s'\n",scope.data());
+
+ int i,p=0,l;
+ while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
+ {
+ tmp+=result.mid(p,i-p); // add part before pattern
+ p=i+l;
+ }
+ tmp+=result.right(result.length()-p); // add trailing part
+
+ scopeOffset=name.findRev("::",scopeOffset-1);
+ result = tmp;
+ } while (scopeOffset>0);
+ //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
+ return result;
+}
+#endif
+
+void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
+{
+ //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
+ BaseClassListIterator bcli(*bcl);
+ BaseClassDef *bcd;
+ for (;(bcd=bcli.current());++bcli)
+ {
+ ClassDef *cd=bcd->classDef;
+ //printf("Trying class %s\n",cd->name().data());
+ int spos=s.find(cd->name()+"::");
+ if (spos!=-1)
+ {
+ s = s.left(spos)+s.right(
+ s.length()-spos-cd->name().length()-2
+ );
+ }
+ //printf("base class `%s'\n",cd->name().data());
+ if (cd->baseClasses())
+ trimBaseClassScope(cd->baseClasses(),s,level+1);
+ }
+}
+
+#if 0
+/*! if either t1 or t2 contains a namespace scope, then remove that
+ * scope. If neither or both have a namespace scope, t1 and t2 remain
+ * unchanged.
+ */
+static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
+{
+ int p1=t1.length();
+ int p2=t2.length();
+ for (;;)
+ {
+ int i1=p1==0 ? -1 : t1.findRev("::",p1);
+ int i2=p2==0 ? -1 : t2.findRev("::",p2);
+ if (i1==-1 && i2==-1)
+ {
+ return;
+ }
+ if (i1!=-1 && i2==-1) // only t1 has a scope
+ {
+ QCString scope=t1.left(i1);
+ replaceNamespaceAliases(scope,i1);
+
+ int so=nsName.length();
+ do
+ {
+ QCString fullScope=nsName.left(so);
+ if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
+ fullScope+=scope;
+ if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
+ {
+ t1 = t1.right(t1.length()-i1-2);
+ return;
+ }
+ if (so==0)
+ {
+ so=-1;
+ }
+ else if ((so=nsName.findRev("::",so-1))==-1)
+ {
+ so=0;
+ }
+ }
+ while (so>=0);
+ }
+ else if (i1==-1 && i2!=-1) // only t2 has a scope
+ {
+ QCString scope=t2.left(i2);
+ replaceNamespaceAliases(scope,i2);
+
+ int so=nsName.length();
+ do
+ {
+ QCString fullScope=nsName.left(so);
+ if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
+ fullScope+=scope;
+ if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
+ {
+ t2 = t2.right(t2.length()-i2-2);
+ return;
+ }
+ if (so==0)
+ {
+ so=-1;
+ }
+ else if ((so=nsName.findRev("::",so-1))==-1)
+ {
+ so=0;
+ }
+ }
+ while (so>=0);
+ }
+ p1 = QMAX(i1-2,0);
+ p2 = QMAX(i2-2,0);
+ }
+}
+#endif
+
+static void stripIrrelevantString(QCString &target,const QCString &str)
+{
+ if (target==str) { target.resize(0); return; }
+ int i,p=0;
+ int l=str.length();
+ bool changed=FALSE;
+ while ((i=target.find(str,p))!=-1)
+ {
+ bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
+ (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
+ if (isMatch)
+ {
+ int i1=target.find('*',i+l);
+ int i2=target.find('&',i+l);
+ if (i1==-1 && i2==-1)
+ {
+ // strip str from target at index i
+ target=target.left(i)+target.right(target.length()-i-l);
+ changed=TRUE;
+ i-=l;
+ }
+ else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
+ {
+ // move str to front
+ target=str+" "+target.left(i)+target.right(target.length()-i-l);
+ changed=TRUE;
+ i++;
+ }
+ }
+ p = i+l;
+ }
+ if (changed) target=target.stripWhiteSpace();
+}
+
+/*! According to the C++ spec and Ivan Vecerina:
+
+ Parameter declarations that differ only in the presence or absence
+ of const and/or volatile are equivalent.
+
+ So the following example, show what is stripped by this routine
+ for const. The same is done for volatile.
+
+ \code
+ const T param -> T param // not relevant
+ const T& param -> const T& param // const needed
+ T* const param -> T* param // not relevant
+ const T* param -> const T* param // const needed
+ \endcode
+ */
+void stripIrrelevantConstVolatile(QCString &s)
+{
+ //printf("stripIrrelevantConstVolatile(%s)=",s.data());
+ stripIrrelevantString(s,"const");
+ stripIrrelevantString(s,"volatile");
+ //printf("%s\n",s.data());
+}
+
+
+// a bit of debug support for matchArguments
+#define MATCH
+#define NOMATCH
+//#define MATCH printf("Match at line %d\n",__LINE__);
+//#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
+
+#ifndef NEWMATCH
+static bool matchArgument(const Argument *srcA,const Argument *dstA,
+ const QCString &className,
+ const QCString &namespaceName,
+ NamespaceSDict *usingNamespaces,
+ SDict<Definition> *usingClasses)
+{
+ //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
+ // srcA->type.data(),srcA->name.data(),
+ // dstA->type.data(),dstA->name.data(),
+ // usingNamespaces,
+ // usingClasses);
+
+ // TODO: resolve any typedefs names that are part of srcA->type
+ // before matching. This should use className and namespaceName
+ // and usingNamespaces and usingClass to determine which typedefs
+ // are in-scope, so it will not be very efficient :-(
+
+ QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
+ QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
+ QCString srcAName=srcA->name.stripWhiteSpace();
+ QCString dstAName=dstA->name.stripWhiteSpace();
+ srcAType.stripPrefix("class ");
+ dstAType.stripPrefix("class ");
+
+ // allow distinguishing "const A" from "const B" even though
+ // from a syntactic point of view they would be two names of the same
+ // type "const". This is not fool prove of course, but should at least
+ // catch the most common cases.
+ if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
+ {
+ srcAType+=" ";
+ srcAType+=srcAName;
+ }
+ if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
+ {
+ dstAType+=" ";
+ dstAType+=dstAName;
+ }
+ if (srcAName=="const" || srcAName=="volatile")
+ {
+ srcAType+=srcAName;
+ srcAName.resize(0);
+ }
+ else if (dstA->name=="const" || dstA->name=="volatile")
+ {
+ dstAType+=dstA->name;
+ dstAName.resize(0);
+ }
+
+ stripIrrelevantConstVolatile(srcAType);
+ stripIrrelevantConstVolatile(dstAType);
+
+ // strip typename keyword
+ if (strncmp(srcAType,"typename ",9)==0)
+ {
+ srcAType = srcAType.right(srcAType.length()-9);
+ }
+ if (strncmp(dstAType,"typename ",9)==0)
+ {
+ dstAType = dstAType.right(dstAType.length()-9);
+ }
+
+ srcAType = removeRedundantWhiteSpace(srcAType);
+ dstAType = removeRedundantWhiteSpace(dstAType);
+
+ //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
+ //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
+
+ //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
+ // dstAType.data(),dstAName.data());
+
+ if (srcA->array!=dstA->array) // nomatch for char[] against char
+ {
+ NOMATCH
+ return FALSE;
+ }
+ if (srcAType!=dstAType) // check if the argument only differs on name
+ {
+
+ // remove a namespace scope that is only in one type
+ // (assuming a using statement was used)
+ //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
+ //trimNamespaceScope(srcAType,dstAType,namespaceName);
+ //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
+
+ //QCString srcScope;
+ //QCString dstScope;
+
+ // strip redundant scope specifiers
+ if (!className.isEmpty())
+ {
+ srcAType=trimScope(className,srcAType);
+ dstAType=trimScope(className,dstAType);
+ //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
+ ClassDef *cd;
+ if (!namespaceName.isEmpty())
+ cd=getClass(namespaceName+"::"+className);
+ else
+ cd=getClass(className);
+ if (cd && cd->baseClasses())
+ {
+ trimBaseClassScope(cd->baseClasses(),srcAType);
+ trimBaseClassScope(cd->baseClasses(),dstAType);
+ }
+ //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
+ }
+ if (!namespaceName.isEmpty())
+ {
+ srcAType=trimScope(namespaceName,srcAType);
+ dstAType=trimScope(namespaceName,dstAType);
+ }
+ //printf("#usingNamespace=%d\n",usingNamespaces->count());
+ if (usingNamespaces && usingNamespaces->count()>0)
+ {
+ NamespaceSDict::Iterator nli(*usingNamespaces);
+ NamespaceDef *nd;
+ for (;(nd=nli.current());++nli)
+ {
+ srcAType=trimScope(nd->name(),srcAType);
+ dstAType=trimScope(nd->name(),dstAType);
+ }
+ }
+ //printf("#usingClasses=%d\n",usingClasses->count());
+ if (usingClasses && usingClasses->count()>0)
+ {
+ SDict<Definition>::Iterator cli(*usingClasses);
+ Definition *cd;
+ for (;(cd=cli.current());++cli)
+ {
+ srcAType=trimScope(cd->name(),srcAType);
+ dstAType=trimScope(cd->name(),dstAType);
+ }
+ }
+
+ //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
+ // dstAType.data(),dstAName.data());
+
+ if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
+ (srcAType+" "+srcAName)==dstAType)
+ {
+ MATCH
+ return TRUE;
+ }
+ else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
+ (dstAType+" "+dstAName)==srcAType)
+ {
+ MATCH
+ return TRUE;
+ }
+
+
+ uint srcPos=0,dstPos=0;
+ bool equal=TRUE;
+ while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
+ {
+ equal=srcAType.at(srcPos)==dstAType.at(dstPos);
+ if (equal) srcPos++,dstPos++;
+ }
+ uint srcATypeLen=srcAType.length();
+ uint dstATypeLen=dstAType.length();
+ if (srcPos<srcATypeLen && dstPos<dstATypeLen)
+ {
+ // if nothing matches or the match ends in the middle or at the
+ // end of a string then there is no match
+ if (srcPos==0 || dstPos==0)
+ {
+ NOMATCH
+ return FALSE;
+ }
+ if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
+ {
+ //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
+ // check if a name if already found -> if no then there is no match
+ if (!srcAName.isEmpty() || !dstAName.isEmpty())
+ {
+ NOMATCH
+ return FALSE;
+ }
+ // types only
+ while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
+ while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
+ if (srcPos<srcATypeLen ||
+ dstPos<dstATypeLen ||
+ (srcPos==srcATypeLen && dstPos==dstATypeLen)
+ )
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+ else
+ {
+ // otherwise we assume that a name starts at the current position.
+ while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
+ while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
+
+ // if nothing more follows for both types then we assume we have
+ // found a match. Note that now `signed int' and `signed' match, but
+ // seeing that int is not a name can only be done by looking at the
+ // semantics.
+
+ if (srcPos!=srcATypeLen || dstPos!=dstATypeLen)
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+ }
+ else if (dstPos<dstAType.length())
+ {
+ if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
+ {
+ if (!dstAName.isEmpty()) // dst has its name separated from its type
+ {
+ NOMATCH
+ return FALSE;
+ }
+ while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
+ if (dstPos!=dstAType.length())
+ {
+ NOMATCH
+ return FALSE; // more than a difference in name -> no match
+ }
+ }
+ else // maybe dst has a name while src has not
+ {
+ dstPos++;
+ while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
+ if (dstPos!=dstAType.length() || !srcAName.isEmpty())
+ {
+ NOMATCH
+ return FALSE; // nope not a name -> no match
+ }
+ }
+ }
+ else if (srcPos<srcAType.length())
+ {
+ if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
+ {
+ if (!srcAName.isEmpty()) // src has its name separated from its type
+ {
+ NOMATCH
+ return FALSE;
+ }
+ while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
+ if (srcPos!=srcAType.length())
+ {
+ NOMATCH
+ return FALSE; // more than a difference in name -> no match
+ }
+ }
+ else // maybe src has a name while dst has not
+ {
+ srcPos++;
+ while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
+ if (srcPos!=srcAType.length() || !dstAName.isEmpty())
+ {
+ NOMATCH
+ return FALSE; // nope not a name -> no match
+ }
+ }
+ }
+ }
+ MATCH
+ return TRUE;
+}
+
+
+/*!
+ * Matches the arguments list srcAl with the argument list dstAl
+ * Returns TRUE if the argument lists are equal. Two argument list are
+ * considered equal if the number of arguments is equal and the types of all
+ * arguments are equal. Furthermore the const and volatile specifiers
+ * stored in the list should be equal.
+ */
+bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
+ const char *cl,const char *ns,bool checkCV,
+ NamespaceSDict *usingNamespaces,
+ SDict<Definition> *usingClasses)
+{
+ QCString className=cl;
+ QCString namespaceName=ns;
+
+ // strip template specialization from class name if present
+ //int til=className.find('<'),tir=className.find('>');
+ //if (til!=-1 && tir!=-1 && tir>til)
+ //{
+ // className=className.left(til)+className.right(className.length()-tir-1);
+ //}
+
+ //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
+ // srcAl ? argListToString(srcAl).data() : "",
+ // dstAl ? argListToString(dstAl).data() : "",
+ // cl,ns,checkCV,
+ // usingNamespaces?usingNamespaces->count():0,
+ // usingClasses?usingClasses->count():0
+ // );
+
+ if (srcAl==0 || dstAl==0)
+ {
+ bool match = srcAl==dstAl; // at least one of the members is not a function
+ if (match)
+ {
+ MATCH
+ return TRUE;
+ }
+ else
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+
+ // handle special case with void argument
+ if ( srcAl->count()==0 && dstAl->count()==1 &&
+ dstAl->getFirst()->type=="void" )
+ { // special case for finding match between func() and func(void)
+ Argument *a=new Argument;
+ a->type = "void";
+ srcAl->append(a);
+ MATCH
+ return TRUE;
+ }
+ if ( dstAl->count()==0 && srcAl->count()==1 &&
+ srcAl->getFirst()->type=="void" )
+ { // special case for finding match between func(void) and func()
+ Argument *a=new Argument;
+ a->type = "void";
+ dstAl->append(a);
+ MATCH
+ return TRUE;
+ }
+
+ if (srcAl->count() != dstAl->count())
+ {
+ NOMATCH
+ return FALSE; // different number of arguments -> no match
+ }
+
+ if (checkCV)
+ {
+ if (srcAl->constSpecifier != dstAl->constSpecifier)
+ {
+ NOMATCH
+ return FALSE; // one member is const, the other not -> no match
+ }
+ if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
+ {
+ NOMATCH
+ return FALSE; // one member is volatile, the other not -> no match
+ }
+ }
+
+ // so far the argument list could match, so we need to compare the types of
+ // all arguments.
+ ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+ Argument *srcA,*dstA;
+ for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+ {
+ if (!matchArgument(srcA,dstA,className,namespaceName,
+ usingNamespaces,usingClasses))
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+ MATCH
+ return TRUE; // all arguments match
+}
+
+#endif
+
+#if 0
+static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
+{
+ ASSERT(symbol!=0);
+ if (symbol->definitionType()==Definition::TypeMember &&
+ ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
+ // to resolve it
+ {
+ MemberDef *md = 0;
+ ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
+ if (cd)
+ {
+ return cd->qualifiedName()+templSpec;
+ }
+ else if (md)
+ {
+ return md->qualifiedName();
+ }
+ }
+ return symbol->qualifiedName();
+}
+#endif
+
+static QCString stripDeclKeywords(const QCString &s)
+{
+ int i=s.find(" class ");
+ if (i!=-1) return s.left(i)+s.mid(i+6);
+ i=s.find(" typename ");
+ if (i!=-1) return s.left(i)+s.mid(i+9);
+ i=s.find(" union ");
+ if (i!=-1) return s.left(i)+s.mid(i+6);
+ i=s.find(" struct ");
+ if (i!=-1) return s.left(i)+s.mid(i+7);
+ return s;
+}
+
+// forward decl for circular dependencies
+static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
+
+QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
+{
+
+ QCString templSpec = spec.stripWhiteSpace();
+ // this part had been commented out before... but it is needed to match for instance
+ // std::list<std::string> against list<string> so it is now back again!
+ if (!templSpec.isEmpty() && templSpec.at(0) == '<')
+ {
+ templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
+ }
+ QCString resolvedType = resolveTypeDef(d,templSpec);
+ if (!resolvedType.isEmpty()) // not known as a typedef either
+ {
+ templSpec = resolvedType;
+ }
+ //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
+ return templSpec;
+}
+
+
+static QCString getCanonicalTypeForIdentifier(
+ Definition *d,FileDef *fs,const QCString &word,
+ QCString *tSpec,int count=0)
+{
+ if (count>10) return word; // oops recursion
+
+ QCString symName,scope,result,templSpec,tmpName;
+ //DefinitionList *defList=0;
+ if (tSpec && !tSpec->isEmpty())
+ templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
+
+ if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
+ {
+ symName=tmpName; // name without scope
+ }
+ else
+ {
+ symName=word;
+ }
+ //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
+ // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
+
+ ClassDef *cd = 0;
+ MemberDef *mType = 0;
+ QCString ts;
+ QCString resolvedType;
+
+ // lookup class / class template instance
+ cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
+ bool isTemplInst = cd && !templSpec.isEmpty();
+ if (!cd && !templSpec.isEmpty())
+ {
+ // class template specialization not known, look up class template
+ cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
+ }
+ if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
+
+ //printf("cd=%p mtype=%p\n",cd,mType);
+ //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
+ // symName.data(),
+ // word.data(),
+ // cd?cd->name().data():"<none>",
+ // d?d->name().data():"<none>",
+ // fs?fs->name().data():"<none>",
+ // cd?cd->isTemplate():-1
+ // );
+
+ //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
+ // (word+templSpec).data(),
+ // cd?cd->qualifiedName().data():"<none>",
+ // templSpec.data(),ts.data(),
+ // tSpec?tSpec->data():"<null>",
+ // cd?cd->isTemplate():FALSE,
+ // resolvedType.data());
+
+ //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
+
+ if (cd) // resolves to a known class type
+ {
+ if (cd==d && tSpec) *tSpec="";
+
+ if (mType && mType->isTypedef()) // but via a typedef
+ {
+ result = resolvedType;
+ }
+ else
+ {
+ if (isTemplInst)
+ {
+ // spec is already part of class type
+ templSpec="";
+ if (tSpec) *tSpec="";
+ }
+ else if (!ts.isEmpty() && templSpec.isEmpty())
+ {
+ // use formal template args for spec
+ templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
+ }
+
+ result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
+
+ if (cd->isTemplate() && tSpec) //
+ {
+ if (!templSpec.isEmpty()) // specific instance
+ {
+ result=cd->name()+templSpec;
+ }
+ else // use template type
+ {
+ result=cd->qualifiedNameWithTemplateParameters();
+ }
+ // template class, so remove the template part (it is part of the class name)
+ *tSpec="";
+ }
+ else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
+ {
+ // obscure case, where a class is used as a template, but doxygen think it is
+ // not (could happen when loading the class from a tag file).
+ *tSpec="";
+ }
+ }
+ }
+ else if (mType && mType->isEnumerate()) // an enum
+ {
+ result = mType->qualifiedName();
+ }
+ else if (mType && mType->isTypedef()) // a typedef
+ {
+ //result = mType->qualifiedName(); // changed after 1.7.2
+ //result = mType->typeString();
+ //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
+ if (word!=mType->typeString())
+ {
+ result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
+ }
+ else
+ {
+ result = mType->typeString();
+ }
+ }
+ else // fallback
+ {
+ resolvedType = resolveTypeDef(d,word);
+ //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
+ if (resolvedType.isEmpty()) // not known as a typedef either
+ {
+ result = word;
+ }
+ else
+ {
+ result = resolvedType;
+ }
+ }
+ //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
+ return result;
+}
+
+static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
+{
+ type = type.stripWhiteSpace();
+
+ // strip const and volatile keywords that are not relevant for the type
+ stripIrrelevantConstVolatile(type);
+
+ // strip leading keywords
+ type.stripPrefix("class ");
+ type.stripPrefix("struct ");
+ type.stripPrefix("union ");
+ type.stripPrefix("enum ");
+ type.stripPrefix("typename ");
+
+ type = removeRedundantWhiteSpace(type);
+ //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
+ // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
+
+ //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
+
+ QCString canType;
+ QCString templSpec,word;
+ int i,p=0,pp=0;
+ while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
+ // foreach identifier in the type
+ {
+ //printf(" i=%d p=%d\n",i,p);
+ if (i>pp) canType += type.mid(pp,i-pp);
+
+
+ QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
+
+ // in case the ct is empty it means that "word" represents scope "d"
+ // and this does not need to be added to the canonical
+ // type (it is redundant), so/ we skip it. This solves problem 589616.
+ if (ct.isEmpty() && type.mid(p,2)=="::")
+ {
+ p+=2;
+ }
+ else
+ {
+ canType += ct;
+ }
+ //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
+ // word.data(),templSpec.data(),canType.data(),ct.data());
+ if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
+ // (i.e. type is not a template specialization)
+ // then resolve any identifiers inside.
+ {
+ static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+ int tp=0,tl,ti;
+ // for each identifier template specifier
+ //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
+ while ((ti=re.match(templSpec,tp,&tl))!=-1)
+ {
+ canType += templSpec.mid(tp,ti-tp);
+ canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
+ tp=ti+tl;
+ }
+ canType+=templSpec.right(templSpec.length()-tp);
+ }
+
+ pp=p;
+ }
+ canType += type.right(type.length()-pp);
+ //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
+
+ return removeRedundantWhiteSpace(canType);
+}
+
+static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
+{
+ QCString type = arg->type.stripWhiteSpace();
+ QCString name = arg->name;
+ //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
+ if ((type=="const" || type=="volatile") && !name.isEmpty())
+ { // name is part of type => correct
+ type+=" ";
+ type+=name;
+ }
+ if (name=="const" || name=="volatile")
+ { // name is part of type => correct
+ if (!type.isEmpty()) type+=" ";
+ type+=name;
+ }
+ if (!arg->array.isEmpty())
+ {
+ type+=arg->array;
+ }
+
+ return extractCanonicalType(d,fs,type);
+}
+
+static bool matchArgument2(
+ Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
+ Definition *dstScope,FileDef *dstFileScope,Argument *dstA
+ )
+{
+ //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
+ // srcScope ? srcScope->name().data() : "",
+ // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
+ // dstScope ? dstScope->name().data() : "",
+ // dstA->type.data(),dstA->name.data(),dstA->canType.data());
+
+ //if (srcA->array!=dstA->array) // nomatch for char[] against char
+ //{
+ // NOMATCH
+ // return FALSE;
+ //}
+ QCString sSrcName = " "+srcA->name;
+ QCString sDstName = " "+dstA->name;
+ QCString srcType = srcA->type;
+ QCString dstType = dstA->type;
+ stripIrrelevantConstVolatile(srcType);
+ stripIrrelevantConstVolatile(dstType);
+ //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
+ //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
+ if (sSrcName==dstType.right(sSrcName.length()))
+ { // case "unsigned int" <-> "unsigned int i"
+ srcA->type+=sSrcName;
+ srcA->name="";
+ srcA->canType=""; // invalidate cached type value
+ }
+ else if (sDstName==srcType.right(sDstName.length()))
+ { // case "unsigned int i" <-> "unsigned int"
+ dstA->type+=sDstName;
+ dstA->name="";
+ dstA->canType=""; // invalidate cached type value
+ }
+
+ if (srcA->canType.isEmpty())
+ {
+ srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
+ }
+ if (dstA->canType.isEmpty())
+ {
+ dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
+ }
+
+ if (srcA->canType==dstA->canType)
+ {
+ MATCH
+ return TRUE;
+ }
+ else
+ {
+ //printf(" Canonical types do not match [%s]<->[%s]\n",
+ // srcA->canType.data(),dstA->canType.data());
+ NOMATCH
+ return FALSE;
+ }
+}
+
+
+// new algorithm for argument matching
+bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
+ Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
+ bool checkCV
+ )
+{
+ //printf("*** matchArguments2\n");
+ ASSERT(srcScope!=0 && dstScope!=0);
+
+ if (srcAl==0 || dstAl==0)
+ {
+ bool match = srcAl==dstAl; // at least one of the members is not a function
+ if (match)
+ {
+ MATCH
+ return TRUE;
+ }
+ else
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+
+ // handle special case with void argument
+ if ( srcAl->count()==0 && dstAl->count()==1 &&
+ dstAl->getFirst()->type=="void" )
+ { // special case for finding match between func() and func(void)
+ Argument *a=new Argument;
+ a->type = "void";
+ srcAl->append(a);
+ MATCH
+ return TRUE;
+ }
+ if ( dstAl->count()==0 && srcAl->count()==1 &&
+ srcAl->getFirst()->type=="void" )
+ { // special case for finding match between func(void) and func()
+ Argument *a=new Argument;
+ a->type = "void";
+ dstAl->append(a);
+ MATCH
+ return TRUE;
+ }
+
+ if (srcAl->count() != dstAl->count())
+ {
+ NOMATCH
+ return FALSE; // different number of arguments -> no match
+ }
+
+ if (checkCV)
+ {
+ if (srcAl->constSpecifier != dstAl->constSpecifier)
+ {
+ NOMATCH
+ return FALSE; // one member is const, the other not -> no match
+ }
+ if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
+ {
+ NOMATCH
+ return FALSE; // one member is volatile, the other not -> no match
+ }
+ }
+
+ // so far the argument list could match, so we need to compare the types of
+ // all arguments.
+ ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+ Argument *srcA,*dstA;
+ for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+ {
+ if (!matchArgument2(srcScope,srcFileScope,srcA,
+ dstScope,dstFileScope,dstA)
+ )
+ {
+ NOMATCH
+ return FALSE;
+ }
+ }
+ MATCH
+ return TRUE; // all arguments match
+}
+
+
+
+// merges the initializer of two argument lists
+// pre: the types of the arguments in the list should match.
+void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
+{
+ //printf("mergeArguments `%s', `%s'\n",
+ // argListToString(srcAl).data(),argListToString(dstAl).data());
+
+ if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
+ {
+ return; // invalid argument lists -> do not merge
+ }
+
+ ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
+ Argument *srcA,*dstA;
+ for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
+ {
+ if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
+ {
+ //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
+ srcA->defval=dstA->defval.copy();
+ }
+ else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
+ {
+ //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
+ dstA->defval=srcA->defval.copy();
+ }
+
+ // fix wrongly detected const or volatile specifiers before merging.
+ // example: "const A *const" is detected as type="const A *" name="const"
+ if (srcA->name=="const" || srcA->name=="volatile")
+ {
+ srcA->type+=" "+srcA->name;
+ srcA->name.resize(0);
+ }
+ if (dstA->name=="const" || dstA->name=="volatile")
+ {
+ dstA->type+=" "+dstA->name;
+ dstA->name.resize(0);
+ }
+
+ if (srcA->type==dstA->type)
+ {
+ //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
+ if (srcA->name.isEmpty() && !dstA->name.isEmpty())
+ {
+ //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
+ //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
+ srcA->type = dstA->type.copy();
+ srcA->name = dstA->name.copy();
+ }
+ else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
+ {
+ //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
+ //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+ dstA->type = srcA->type.copy();
+ dstA->name = dstA->name.copy();
+ }
+ else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
+ {
+ //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
+ if (forceNameOverwrite)
+ {
+ srcA->name = dstA->name;
+ }
+ else
+ {
+ if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
+ {
+ srcA->name = dstA->name;
+ }
+ else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
+ {
+ dstA->name = srcA->name;
+ }
+ }
+ }
+ }
+ else
+ {
+ //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
+ srcA->type=srcA->type.stripWhiteSpace();
+ dstA->type=dstA->type.stripWhiteSpace();
+ if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
+ {
+ srcA->type+=" "+srcA->name;
+ srcA->name=dstA->name;
+ }
+ else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
+ {
+ dstA->type+=" "+dstA->name;
+ dstA->name=srcA->name;
+ }
+ else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
+ {
+ srcA->name = dstA->name;
+ }
+ else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
+ {
+ dstA->name = srcA->name;
+ }
+ }
+ int i1=srcA->type.find("::"),
+ i2=dstA->type.find("::"),
+ j1=srcA->type.length()-i1-2,
+ j2=dstA->type.length()-i2-2;
+ if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
+ {
+ //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
+ //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+ dstA->type = srcA->type.left(i1+2)+dstA->type;
+ dstA->name = dstA->name.copy();
+ }
+ else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
+ {
+ //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
+ //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
+ srcA->type = dstA->type.left(i2+2)+srcA->type;
+ srcA->name = dstA->name.copy();
+ }
+ if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
+ {
+ srcA->docs = dstA->docs.copy();
+ }
+ else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
+ {
+ dstA->docs = srcA->docs.copy();
+ }
+ //printf("Merge argument `%s|%s' `%s|%s'\n",
+ // srcA->type.data(),srcA->name.data(),
+ // dstA->type.data(),dstA->name.data());
+ }
+}
+
+static void findMembersWithSpecificName(MemberName *mn,
+ const char *args,
+ bool checkStatics,
+ FileDef *currentFile,
+ bool checkCV,
+ const char *forceTagFile,
+ QList<MemberDef> &members)
+{
+ //printf(" Function with global scope name `%s' args=`%s'\n",
+ // mn->memberName(),args);
+ MemberListIterator mli(*mn);
+ MemberDef *md;
+ for (mli.toFirst();(md=mli.current());++mli)
+ {
+ FileDef *fd=md->getFileDef();
+ GroupDef *gd=md->getGroupDef();
+ //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p current=%p ref=%s\n",
+ // md->name().data(),args,fd,gd,currentFile,md->getReference().data());
+ if (
+ ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
+ md->getNamespaceDef()==0 && md->isLinkable() &&
+ (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
+ currentFile==0 || fd==currentFile) // statics must appear in the same file
+ )
+ {
+ bool match=TRUE;
+ ArgumentList *argList=0;
+ if (args && !md->isDefine() && strcmp(args,"()")!=0)
+ {
+ argList=new ArgumentList;
+ LockingPtr<ArgumentList> mdAl = md->argumentList();
+ stringToArgumentList(args,argList);
+ match=matchArguments2(
+ md->getOuterScope(),fd,mdAl.pointer(),
+ Doxygen::globalScope,fd,argList,
+ checkCV);
+ delete argList; argList=0;
+ }
+ if (match && (forceTagFile==0 || md->getReference()==forceTagFile))
+ {
+ //printf("Found match!\n");
+ members.append(md);
+ }
+ }
+ }
+}
+
+/*!
+ * Searches for a member definition given its name `memberName' as a string.
+ * memberName may also include a (partial) scope to indicate the scope
+ * in which the member is located.
+ *
+ * The parameter `scName' is a string representing the name of the scope in
+ * which the link was found.
+ *
+ * In case of a function args contains a string representation of the
+ * argument list. Passing 0 means the member has no arguments.
+ * Passing "()" means any argument list will do, but "()" is preferred.
+ *
+ * The function returns TRUE if the member is known and documented or
+ * FALSE if it is not.
+ * If TRUE is returned parameter `md' contains a pointer to the member
+ * definition. Furthermore exactly one of the parameter `cd', `nd', or `fd'
+ * will be non-zero:
+ * - if `cd' is non zero, the member was found in a class pointed to by cd.
+ * - if `nd' is non zero, the member was found in a namespace pointed to by nd.
+ * - if `fd' is non zero, the member was found in the global namespace of
+ * file fd.
+ */
+bool getDefs(const QCString &scName,
+ const QCString &memberName,
+ const char *args,
+ MemberDef *&md,
+ ClassDef *&cd,
+ FileDef *&fd,
+ NamespaceDef *&nd,
+ GroupDef *&gd,
+ bool forceEmptyScope,
+ FileDef *currentFile,
+ bool checkCV,
+ const char *forceTagFile
+ )
+{
+ fd=0, md=0, cd=0, nd=0, gd=0;
+ if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
+
+ QCString scopeName=scName;
+ scopeName = substitute(scopeName,"\\","::"); // for PHP
+ //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
+ // memberName.data(),args,scopeName.data(),forceEmptyScope);
+
+ int is,im=0,pm=0;
+ // strip common part of the scope from the scopeName
+ while ((is=scopeName.findRev("::"))!=-1 &&
+ (im=memberName.find("::",pm))!=-1 &&
+ (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
+ )
+ {
+ scopeName=scopeName.left(is);
+ pm=im+2;
+ }
+ //printf("result after scope corrections scope=%s name=%s\n",
+ // scopeName.data(),memberName.data());
+
+ QCString mName=memberName;
+ QCString mScope;
+ if (memberName.left(9)!="operator " && // treat operator conversion methods
+ // as a special case
+ (im=memberName.findRev("::"))!=-1 &&
+ im<(int)memberName.length()-2 // not A::
+ )
+ {
+ mScope=memberName.left(im);
+ mName=memberName.right(memberName.length()-im-2);
+ }
+
+ // handle special the case where both scope name and member scope are equal
+ if (mScope==scopeName) scopeName.resize(0);
+
+ //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
+
+ MemberName *mn = Doxygen::memberNameSDict->find(mName);
+ //printf("mName=%s mn=%p\n",mName.data(),mn);
+
+ if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
+ mn && !(scopeName.isEmpty() && mScope.isEmpty()))
+ {
+ //printf(" >member name '%s' found\n",mName.data());
+ int scopeOffset=scopeName.length();
+ do
+ {
+ QCString className = scopeName.left(scopeOffset);
+ if (!className.isEmpty() && !mScope.isEmpty())
+ {
+ className+="::"+mScope;
+ }
+ else if (!mScope.isEmpty())
+ {
+ className=mScope;
+ }
+
+ ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className);
+ //printf("Trying class scope %s: %p\n",className.data(),fcd);
+ // todo: fill in correct fileScope!
+ if (fcd && // is it a documented class
+ fcd->isLinkable()
+ )
+ {
+ //printf(" Found fcd=%p\n",fcd);
+ MemberListIterator mmli(*mn);
+ MemberDef *mmd;
+ int mdist=maxInheritanceDepth;
+ ArgumentList *argList=0;
+ if (args)
+ {
+ argList=new ArgumentList;
+ stringToArgumentList(args,argList);
+ }
+ for (mmli.toFirst();(mmd=mmli.current());++mmli)
+ {
+ //if (mmd->isLinkable())
+ //{
+ LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+ bool match=args==0 ||
+ matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+ fcd,fcd->getFileDef(),argList,
+ checkCV
+ );
+ //printf("match=%d\n",match);
+ if (match)
+ {
+ ClassDef *mcd=mmd->getClassDef();
+ if (mcd)
+ {
+ int m=minClassDistance(fcd,mcd);
+ if (m<mdist && mcd->isLinkable())
+ {
+ mdist=m;
+ cd=mcd;
+ md=mmd;
+ }
+ }
+ }
+ //}
+ }
+ if (argList)
+ {
+ delete argList; argList=0;
+ }
+ if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0)
+ // no exact match found, but if args="()" an arbitrary member will do
+ {
+ //printf(" >Searching for arbitrary member\n");
+ for (mmli.toFirst();(mmd=mmli.current());++mmli)
+ {
+ //if (mmd->isLinkable())
+ //{
+ ClassDef *mcd=mmd->getClassDef();
+ //printf(" >Class %s found\n",mcd->name().data());
+ if (mcd)
+ {
+ int m=minClassDistance(fcd,mcd);
+ if (m<mdist /* && mcd->isLinkable()*/ )
+ {
+ //printf("Class distance %d\n",m);
+ mdist=m;
+ cd=mcd;
+ md=mmd;
+ }
+ }
+ //}
+ }
+ }
+ //printf(" >Succes=%d\n",mdist<maxInheritanceDepth);
+ if (mdist<maxInheritanceDepth)
+ {
+ if (!md->isLinkable())
+ {
+ md=0; // avoid returning things we cannot link to
+ cd=0;
+ return FALSE; // match found, but was not linkable
+ }
+ else
+ {
+ gd=md->getGroupDef();
+ if (gd) cd=0;
+ return TRUE; /* found match */
+ }
+ }
+ }
+ /* go to the parent scope */
+ if (scopeOffset==0)
+ {
+ scopeOffset=-1;
+ }
+ else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
+ {
+ scopeOffset=0;
+ }
+ } while (scopeOffset>=0);
+
+ }
+ if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
+ {
+ MemberListIterator mmli(*mn);
+ MemberDef *mmd, *fuzzy_mmd = 0;
+ ArgumentList *argList = 0;
+ bool hasEmptyArgs = args && strcmp(args, "()") == 0;
+
+ if (args)
+ stringToArgumentList(args, argList = new ArgumentList);
+
+ for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
+ {
+ if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
+ !mmd->getClassDef())
+ continue;
+
+ if (!args) break;
+
+ QCString className = mmd->getClassDef()->name();
+
+ LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+ if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+ Doxygen::globalScope,mmd->getFileDef(),argList,
+ checkCV
+ )
+ ) break;
+
+ if (!fuzzy_mmd && hasEmptyArgs)
+ fuzzy_mmd = mmd;
+ }
+
+ if (argList) delete argList, argList = 0;
+
+ mmd = mmd ? mmd : fuzzy_mmd;
+
+ if (mmd)
+ {
+ md = mmd;
+ cd = mmd->getClassDef();
+ return TRUE;
+ }
+ }
+
+
+ // maybe an namespace, file or group member ?
+ //printf("Testing for global symbol scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
+ // scopeName.data(),mScope.data(),mName.data());
+ if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
+ {
+ //printf(" >symbol name found\n");
+ NamespaceDef *fnd=0;
+ int scopeOffset=scopeName.length();
+ do
+ {
+ QCString namespaceName = scopeName.left(scopeOffset);
+ if (!namespaceName.isEmpty() && !mScope.isEmpty())
+ {
+ namespaceName+="::"+mScope;
+ }
+ else if (!mScope.isEmpty())
+ {
+ namespaceName=mScope.copy();
+ }
+ //printf("Trying namespace %s\n",namespaceName.data());
+ if (!namespaceName.isEmpty() &&
+ (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
+ fnd->isLinkable()
+ )
+ {
+ //printf("Function inside existing namespace `%s'\n",namespaceName.data());
+ bool found=FALSE;
+ MemberListIterator mmli(*mn);
+ MemberDef *mmd;
+ for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
+ {
+ //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
+ // mmd->getNamespaceDef(),fnd);
+ if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
+ { // namespace is found
+ bool match=TRUE;
+ ArgumentList *argList=0;
+ if (args && strcmp(args,"()")!=0)
+ {
+ argList=new ArgumentList;
+ LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
+ stringToArgumentList(args,argList);
+ match=matchArguments2(
+ mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
+ fnd,mmd->getFileDef(),argList,
+ checkCV);
+ }
+ if (match)
+ {
+ nd=fnd;
+ md=mmd;
+ found=TRUE;
+ }
+ if (args)
+ {
+ delete argList; argList=0;
+ }
+ }
+ }
+ if (!found && args && !strcmp(args,"()"))
+ // no exact match found, but if args="()" an arbitrary
+ // member will do
+ {
+ for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
+ {
+ if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
+ {
+ nd=fnd;
+ md=mmd;
+ found=TRUE;
+ }
+ }
+ }
+ if (found)
+ {
+ if (!md->isLinkable())
+ {
+ md=0; // avoid returning things we cannot link to
+ nd=0;
+ return FALSE; // match found but not linkable
+ }
+ else
+ {
+ gd=md->getGroupDef();
+ if (gd && gd->isLinkable()) nd=0; else gd=0;
+ return TRUE;
+ }
+ }
+ }
+ if (scopeOffset==0)
+ {
+ scopeOffset=-1;
+ }
+ else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
+ {
+ scopeOffset=0;
+ }
+ } while (scopeOffset>=0);
+
+ //else // no scope => global function
+ {
+ QList<MemberDef> members;
+ // search for matches with strict static checking
+ findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members);
+ if (members.count()==0) // nothing found
+ {
+ // search again without strict static checking
+ findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members);
+ }
+ //printf("found %d members\n",members.count());
+ if (members.count()!=1 && args && !strcmp(args,"()"))
+ {
+ // no exact match found, but if args="()" an arbitrary
+ // member will do
+ md=mn->last();
+ while (md /* && md->isLinkable()*/)
+ {
+ //printf("Found member `%s'\n",md->name().data());
+ //printf("member is linkable md->name()=`%s'\n",md->name().data());
+ fd=md->getFileDef();
+ gd=md->getGroupDef();
+ if (
+ (gd && gd->isLinkable()) || (fd && fd->isLinkable())
+ )
+ {
+ members.append(md);
+ }
+ md=mn->prev();
+ }
+ }
+ //printf("found %d candidate members\n",members.count());
+ if (members.count()>0) // at least one match
+ {
+ md=members.last();
+ }
+ if (md) // found a matching global member
+ {
+ fd=md->getFileDef();
+ gd=md->getGroupDef();
+ //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
+ if (gd && gd->isLinkable()) fd=0; else gd=0;
+ return TRUE;
+ }
+ }
+ }
+
+ // no nothing found
+ return FALSE;
+}
+
+/*!
+ * Searches for a scope definition given its name as a string via parameter
+ * `scope`.
+ *
+ * The parameter `docScope` is a string representing the name of the scope in
+ * which the `scope` string was found.
+ *
+ * The function returns TRUE if the scope is known and documented or
+ * FALSE if it is not.
+ * If TRUE is returned exactly one of the parameter `cd`, `nd`
+ * will be non-zero:
+ * - if `cd` is non zero, the scope was a class pointed to by cd.
+ * - if `nd` is non zero, the scope was a namespace pointed to by nd.
+ */
+static bool getScopeDefs(const char *docScope,const char *scope,
+ ClassDef *&cd, NamespaceDef *&nd)
+{
+ cd=0;nd=0;
+
+ QCString scopeName=scope;
+ //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
+ if (scopeName.isEmpty()) return FALSE;
+
+ bool explicitGlobalScope=FALSE;
+ if (scopeName.at(0)==':' && scopeName.at(1)==':')
+ {
+ scopeName=scopeName.right(scopeName.length()-2);
+ explicitGlobalScope=TRUE;
+ }
+
+ QCString docScopeName=docScope;
+ int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
+
+ do // for each possible docScope (from largest to and including empty)
+ {
+ QCString fullName=scopeName.copy();
+ if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
+
+ if (((cd=getClass(fullName)) || // normal class
+ (cd=getClass(fullName+"-p")) //|| // ObjC protocol
+ //(cd=getClass(fullName+"-g")) // C# generic
+ ) && cd->isLinkable())
+ {
+ return TRUE; // class link written => quit
+ }
+ else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
+ {
+ return TRUE; // namespace link written => quit
+ }
+ if (scopeOffset==0)
+ {
+ scopeOffset=-1;
+ }
+ else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
+ {
+ scopeOffset=0;
+ }
+ } while (scopeOffset>=0);
+
+ return FALSE;
+}
+
+static bool isLowerCase(QCString &s)
+{
+ uchar *p=(uchar*)s.data();
+ if (p==0) return TRUE;
+ int c;
+ while ((c=*p++)) if (!islower(c)) return FALSE;
+ return TRUE;
+}
+
+/*! Returns an object to reference to given its name and context
+ * @post return value TRUE implies *resContext!=0 or *resMember!=0
+ */
+bool resolveRef(/* in */ const char *scName,
+ /* in */ const char *name,
+ /* in */ bool inSeeBlock,
+ /* out */ Definition **resContext,
+ /* out */ MemberDef **resMember,
+ bool lookForSpecialization,
+ FileDef *currentFile,
+ bool checkScope
+ )
+{
+ QCString tsName = name;
+ //bool memberScopeFirst = tsName.find('#')!=-1;
+ QCString fullName = substitute(tsName,"#","::");
+ fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
+
+ int bracePos=fullName.findRev('('); // reverse is needed for operator()(...)
+ int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
+ int scopePos=fullName.findRev("::",endNamePos);
+ bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
+ (scopePos>2 || // ::N::A
+ tsName.left(2)=="::" || // ::foo in local scope
+ scName==0 // #foo in global scope
+ );
+
+ // default result values
+ *resContext=0;
+ *resMember=0;
+
+ if (bracePos==-1) // simple name
+ {
+ ClassDef *cd=0;
+ NamespaceDef *nd=0;
+
+ // the following if() was commented out for releases in the range
+ // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
+ if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
+ { // link to lower case only name => do not try to autolink
+ return FALSE;
+ }
+
+ //printf("scName=%s fullName=%s\n",scName,fullName.data());
+
+ // check if this is a class or namespace reference
+ if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
+ {
+ if (cd) // scope matches that of a class
+ {
+ *resContext = cd;
+ }
+ else // scope matches that of a namespace
+ {
+ ASSERT(nd!=0);
+ *resContext = nd;
+ }
+ return TRUE;
+ }
+ else if (scName==fullName || (!inSeeBlock && scopePos==-1))
+ // nothing to link => output plain text
+ {
+ //printf("found scName=%s fullName=%s scName==fullName=%d "
+ // "inSeeBlock=%d scopePos=%d!\n",
+ // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
+ return FALSE;
+ }
+ // continue search...
+ }
+
+ // extract userscope+name
+ QCString nameStr=fullName.left(endNamePos);
+ if (explicitScope) nameStr=nameStr.mid(2);
+
+ // extract arguments
+ QCString argsStr;
+ if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
+
+ // strip template specifier
+ // TODO: match against the correct partial template instantiation
+ int templPos=nameStr.find('<');
+ bool tryUnspecializedVersion = FALSE;
+ if (templPos!=-1 && nameStr.find("operator")==-1)
+ {
+ int endTemplPos=nameStr.findRev('>');
+ if (endTemplPos!=-1)
+ {
+ if (!lookForSpecialization)
+ {
+ nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
+ }
+ else
+ {
+ tryUnspecializedVersion = TRUE;
+ }
+ }
+ }
+
+ QCString scopeStr=scName;
+
+ MemberDef *md = 0;
+ ClassDef *cd = 0;
+ FileDef *fd = 0;
+ NamespaceDef *nd = 0;
+ GroupDef *gd = 0;
+
+ // check if nameStr is a member or global.
+ //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
+ // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
+ if (getDefs(scopeStr,nameStr,argsStr,
+ md,cd,fd,nd,gd,
+ //scopePos==0 && !memberScopeFirst, // forceEmptyScope
+ explicitScope, // replaces prev line due to bug 600829
+ currentFile,
+ TRUE // checkCV
+ )
+ )
+ {
+ //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
+ if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
+ (!scopeStr.isEmpty() || nameStr.find("::")>0))
+ {
+ // we did find a member, but it is a global one while we were explicitly
+ // looking for a scoped variable. See bug 616387 for an example why this check is needed.
+ // note we do need to support autolinking to "::symbol" hence the >0
+ //printf("not global member!\n");
+ *resContext=0;
+ *resMember=0;
+ return FALSE;
+ }
+ //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
+ if (md) { *resMember=md; *resContext=md; }
+ else if (cd) *resContext=cd;
+ else if (nd) *resContext=nd;
+ else if (fd) *resContext=fd;
+ else if (gd) *resContext=gd;
+ else { *resContext=0; *resMember=0; return FALSE; }
+ //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
+ // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
+ return TRUE;
+ }
+ else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
+ { // group link
+ *resContext=gd;
+ return TRUE;
+ }
+ else if (tsName.find('.')!=-1) // maybe a link to a file
+ {
+ bool ambig;
+ fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
+ if (fd && !ambig)
+ {
+ *resContext=fd;
+ return TRUE;
+ }
+ }
+
+ if (tryUnspecializedVersion)
+ {
+ return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
+ }
+ //printf("resolveRef: %s not found!\n",name);
+
+ return FALSE;
+}
+
+QCString linkToText(SrcLangExt lang,const char *link,bool isFileName)
+{
+ //static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
+ QCString result=link;
+ if (!result.isEmpty())
+ {
+ // replace # by ::
+ result=substitute(result,"#","::");
+ // replace . by ::
+ if (!isFileName) result=substitute(result,".","::");
+ // strip leading :: prefix if present
+ if (result.at(0)==':' && result.at(1)==':')
+ {
+ result=result.right(result.length()-2);
+ }
+ QCString sep = getLanguageSpecificSeparator(lang);
+ if (sep!="::")
+ {
+ result=substitute(result,"::",sep);
+ }
+ }
+ return result;
+}
+
+#if 0
+/*
+ * generate a reference to a class, namespace or member.
+ * `scName' is the name of the scope that contains the documentation
+ * string that is returned.
+ * `name' is the name that we want to link to.
+ * `name' may have five formats:
+ * 1) "ScopeName"
+ * 2) "memberName()" one of the (overloaded) function or define
+ * with name memberName.
+ * 3) "memberName(...)" a specific (overloaded) function or define
+ * with name memberName
+ * 4) "::name a global variable or define
+ * 4) "\#memberName member variable, global variable or define
+ * 5) ("ScopeName::")+"memberName()"
+ * 6) ("ScopeName::")+"memberName(...)"
+ * 7) ("ScopeName::")+"memberName"
+ * instead of :: the \# symbol may also be used.
+ */
+
+bool generateRef(OutputDocInterface &od,const char *scName,
+ const char *name,bool inSeeBlock,const char *rt)
+{
+ //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
+
+ Definition *compound;
+ MemberDef *md;
+
+ // create default link text
+ QCString linkText = linkToText(rt,FALSE);
+
+ if (resolveRef(scName,name,inSeeBlock,&compound,&md))
+ {
+ if (md && md->isLinkable()) // link to member
+ {
+ od.writeObjectLink(md->getReference(),
+ md->getOutputFileBase(),
+ md->anchor(),linkText);
+ // generate the page reference (for LaTeX)
+ if (!md->isReference())
+ {
+ writePageRef(od,md->getOutputFileBase(),md->anchor());
+ }
+ return TRUE;
+ }
+ else if (compound && compound->isLinkable()) // link to compound
+ {
+ if (rt==0 && compound->definitionType()==Definition::TypeGroup)
+ {
+ linkText=((GroupDef *)compound)->groupTitle();
+ }
+ if (compound && compound->definitionType()==Definition::TypeFile)
+ {
+ linkText=linkToText(rt,TRUE);
+ }
+ od.writeObjectLink(compound->getReference(),
+ compound->getOutputFileBase(),
+ 0,linkText);
+ if (!compound->isReference())
+ {
+ writePageRef(od,compound->getOutputFileBase(),0);
+ }
+ return TRUE;
+ }
+ }
+ od.docify(linkText);
+ return FALSE;
+}
+#endif
+
+bool resolveLink(/* in */ const char *scName,
+ /* in */ const char *lr,
+ /* in */ bool /*inSeeBlock*/,
+ /* out */ Definition **resContext,
+ /* out */ QCString &resAnchor
+ )
+{
+ *resContext=0;
+
+ QCString linkRef=lr;
+ //printf("ResolveLink linkRef=%s inSee=%d\n",lr,inSeeBlock);
+ FileDef *fd;
+ GroupDef *gd;
+ PageDef *pd;
+ ClassDef *cd;
+ DirDef *dir;
+ NamespaceDef *nd;
+ bool ambig;
+ if (linkRef.isEmpty()) // no reference name!
+ {
+ return FALSE;
+ }
+ else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
+ {
+ GroupDef *gd = pd->getGroupDef();
+ if (gd)
+ {
+ SectionInfo *si=0;
+ if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()];
+ *resContext=gd;
+ if (si) resAnchor = si->label;
+ }
+ else
+ {
+ *resContext=pd;
+ }
+ return TRUE;
+ }
+ else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
+ {
+ *resContext=pd;
+ return TRUE;
+ }
+ else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
+ {
+ *resContext=gd;
+ return TRUE;
+ }
+ else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
+ && fd->isLinkable())
+ {
+ *resContext=fd;
+ return TRUE;
+ }
+ else if ((cd=getClass(linkRef))) // class link
+ {
+ *resContext=cd;
+ resAnchor=cd->anchor();
+ return TRUE;
+ }
+ else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
+ {
+ *resContext=cd;
+ resAnchor=cd->anchor();
+ return TRUE;
+ }
+// else if ((cd=getClass(linkRef+"-g"))) // C# generic link
+// {
+// *resContext=cd;
+// resAnchor=cd->anchor();
+// return TRUE;
+// }
+ else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
+ {
+ *resContext=nd;
+ return TRUE;
+ }
+ else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/"))
+ && dir->isLinkable()) // TODO: make this location independent like filedefs
+ {
+ *resContext=dir;
+ return TRUE;
+ }
+ else // probably a member reference
+ {
+ MemberDef *md;
+ bool res = resolveRef(scName,lr,TRUE,resContext,&md);
+ if (md) resAnchor=md->anchor();
+ return res;
+ }
+}
+
+
+//----------------------------------------------------------------------
+// General function that generates the HTML code for a reference to some
+// file, class or member from text `lr' within the context of class `clName'.
+// This link has the text 'lt' (if not 0), otherwise `lr' is used as a
+// basis for the link's text.
+// returns TRUE if a link could be generated.
+
+bool generateLink(OutputDocInterface &od,const char *clName,
+ const char *lr,bool inSeeBlock,const char *lt)
+{
+ //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
+ Definition *compound;
+ //PageDef *pageDef=0;
+ QCString anchor,linkText=linkToText(SrcLangExt_Unknown,lt,FALSE);
+ //printf("generateLink linkText=%s\n",linkText.data());
+ if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
+ {
+ if (compound) // link to compound
+ {
+ if (lt==0 && anchor.isEmpty() && /* compound link */
+ compound->definitionType()==Definition::TypeGroup /* is group */
+ )
+ {
+ linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
+ }
+ else if (compound->definitionType()==Definition::TypeFile)
+ {
+ linkText=linkToText(compound->getLanguage(),lt,TRUE);
+ }
+ od.writeObjectLink(compound->getReference(),
+ compound->getOutputFileBase(),anchor,linkText);
+ if (!compound->isReference())
+ {
+ writePageRef(od,compound->getOutputFileBase(),anchor);
+ }
+ }
+ else
+ {
+ err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
+ }
+ return TRUE;
+ }
+ else // link could not be found
+ {
+ od.docify(linkText);
+ return FALSE;
+ }
+}
+
+void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
+{
+ //printf("generateFileRef(%s,%s)\n",name,text);
+ QCString linkText = text ? text : name;
+ //FileInfo *fi;
+ FileDef *fd;
+ bool ambig;
+ if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) &&
+ fd->isLinkable())
+ // link to documented input file
+ od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
+ else
+ od.docify(linkText);
+}
+
+//----------------------------------------------------------------------
+
+#if 0
+QCString substituteClassNames(const QCString &s)
+{
+ int i=0,l,p;
+ QCString result;
+ if (s.isEmpty()) return result;
+ QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
+ while ((p=r.match(s,i,&l))!=-1)
+ {
+ QCString *subst;
+ if (p>i) result+=s.mid(i,p-i);
+ if ((subst=substituteDict[s.mid(p,l)]))
+ {
+ result+=*subst;
+ }
+ else
+ {
+ result+=s.mid(p,l);
+ }
+ i=p+l;
+ }
+ result+=s.mid(i,s.length()-i);
+ return result;
+}
+#endif
+
+//----------------------------------------------------------------------
+
+/** Cache element for the file name to FileDef mapping cache. */
+struct FindFileCacheElem
+{
+ FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
+ FileDef *fileDef;
+ bool isAmbig;
+};
+
+static QCache<FindFileCacheElem> g_findFileDefCache(5000);
+
+FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
+{
+ ambig=FALSE;
+ if (n==0) return 0;
+
+ QCString key;
+ key.sprintf("%p:",fnDict);
+ key+=n;
+
+ g_findFileDefCache.setAutoDelete(TRUE);
+ FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
+ //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
+ if (cachedResult)
+ {
+ ambig = cachedResult->isAmbig;
+ //printf("cached: fileDef=%p\n",cachedResult->fileDef);
+ return cachedResult->fileDef;
+ }
+ else
+ {
+ cachedResult = new FindFileCacheElem(0,FALSE);
+ }
+
+ QCString name=convertToQCString(QDir::cleanDirPath(n));
+ QCString path;
+ int slashPos;
+ FileName *fn;
+ if (name.isEmpty()) goto exit;
+ slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
+ if (slashPos!=-1)
+ {
+ path=name.left(slashPos+1);
+ name=name.right(name.length()-slashPos-1);
+ //printf("path=%s name=%s\n",path.data(),name.data());
+ }
+ if (name.isEmpty()) goto exit;
+ if ((fn=(*fnDict)[name]))
+ {
+ //printf("fn->count()=%d\n",fn->count());
+ if (fn->count()==1)
+ {
+ FileDef *fd = fn->getFirst();
+#if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
+ bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
+#else // Unix
+ bool isSamePath = fd->getPath().right(path.length())==path;
+#endif
+ if (path.isEmpty() || isSamePath)
+ {
+ cachedResult->fileDef = fd;
+ g_findFileDefCache.insert(key,cachedResult);
+ //printf("=1 ===> add to cache %p\n",fd);
+ return fd;
+ }
+ }
+ else // file name alone is ambiguous
+ {
+ int count=0;
+ FileNameIterator fni(*fn);
+ FileDef *fd;
+ FileDef *lastMatch=0;
+ QCString pathStripped = stripFromIncludePath(path);
+ for (fni.toFirst();(fd=fni.current());++fni)
+ {
+ QCString fdStripPath = stripFromIncludePath(fd->getPath());
+ if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
+ {
+ count++;
+ lastMatch=fd;
+ }
+ }
+ //printf(">1 ===> add to cache %p\n",fd);
+
+ ambig=(count>1);
+ cachedResult->isAmbig = ambig;
+ cachedResult->fileDef = lastMatch;
+ g_findFileDefCache.insert(key,cachedResult);
+ return lastMatch;
+ }
+ }
+ else
+ {
+ //printf("not found!\n");
+ }
+exit:
+ //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
+ g_findFileDefCache.insert(key,cachedResult);
+ //delete cachedResult;
+ return 0;
+}
+
+//----------------------------------------------------------------------
+
+QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
+{
+ QCString result;
+ QCString name=n;
+ QCString path;
+ int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
+ if (slashPos!=-1)
+ {
+ path=name.left(slashPos+1);
+ name=name.right(name.length()-slashPos-1);
+ }
+ FileName *fn;
+ if ((fn=(*fnDict)[name]))
+ {
+ FileNameIterator fni(*fn);
+ FileDef *fd;
+ for (fni.toFirst();(fd=fni.current());++fni)
+ {
+ if (path.isEmpty() || fd->getPath().right(path.length())==path)
+ {
+ result+=" "+fd->absFilePath()+"\n";
+ }
+ }
+ }
+ return result;
+}
+
+//----------------------------------------------------------------------
+
+QCString substituteKeywords(const QCString &s,const char *title,
+ const char *projName,const char *projNum,const char *projBrief)
+{
+ QCString result = s;
+ if (title) result = substitute(result,"$title",title);
+ result = substitute(result,"$datetime",dateToString(TRUE));
+ result = substitute(result,"$date",dateToString(FALSE));
+ result = substitute(result,"$year",yearToString());
+ result = substitute(result,"$doxygenversion",versionString);
+ result = substitute(result,"$projectname",projName);
+ result = substitute(result,"$projectnumber",projNum);
+ result = substitute(result,"$projectbrief",projBrief);
+ result = substitute(result,"$projectlogo",stripPath(Config_getString("PROJECT_LOGO")));
+ return result;
+}
+
+//----------------------------------------------------------------------
+
+/*! Returns the character index within \a name of the first prefix
+ * in Config_getList("IGNORE_PREFIX") that matches \a name at the left hand side,
+ * or zero if no match was found
+ */
+int getPrefixIndex(const QCString &name)
+{
+ if (name.isEmpty()) return 0;
+ static QStrList &sl = Config_getList("IGNORE_PREFIX");
+ char *s = sl.first();
+ while (s)
+ {
+ const char *ps=s;
+ const char *pd=name.data();
+ int i=0;
+ while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
+ if (*ps==0 && *pd!=0)
+ {
+ return i;
+ }
+ s = sl.next();
+ }
+ return 0;
+}
+
+//----------------------------------------------------------------------------
+
+static void initBaseClassHierarchy(BaseClassList *bcl)
+{
+ if (bcl==0) return;
+ BaseClassListIterator bcli(*bcl);
+ for ( ; bcli.current(); ++bcli)
+ {
+ ClassDef *cd=bcli.current()->classDef;
+ if (cd->baseClasses()==0) // no base classes => new root
+ {
+ initBaseClassHierarchy(cd->baseClasses());
+ }
+ cd->visited=FALSE;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+void initClassHierarchy(ClassSDict *cl)
+{
+ ClassSDict::Iterator cli(*cl);
+ ClassDef *cd;
+ for ( ; (cd=cli.current()); ++cli)
+ {
+ cd->visited=FALSE;
+ initBaseClassHierarchy(cd->baseClasses());
+ }
+}
+
+//----------------------------------------------------------------------------
+
+bool hasVisibleRoot(BaseClassList *bcl)
+{
+ if (bcl)
+ {
+ BaseClassListIterator bcli(*bcl);
+ for ( ; bcli.current(); ++bcli)
+ {
+ ClassDef *cd=bcli.current()->classDef;
+ if (cd->isVisibleInHierarchy()) return TRUE;
+ hasVisibleRoot(cd->baseClasses());
+ }
+ }
+ return FALSE;
+}
+
+//----------------------------------------------------------------------
+
+// note that this function is not reentrant due to the use of static growBuf!
+QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
+{
+ static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES");
+ static GrowBuf growBuf;
+ growBuf.clear();
+ char c;
+ const char *p=name;
+ while ((c=*p++)!=0)
+ {
+ switch(c)
+ {
+ case '_': if (allowUnderscore) growBuf.addChar('_'); else growBuf.addStr("__"); break;
+ case '-': growBuf.addChar('-'); break;
+ case ':': growBuf.addStr("_1"); break;
+ case '/': growBuf.addStr("_2"); break;
+ case '<': growBuf.addStr("_3"); break;
+ case '>': growBuf.addStr("_4"); break;
+ case '*': growBuf.addStr("_5"); break;
+ case '&': growBuf.addStr("_6"); break;
+ case '|': growBuf.addStr("_7"); break;
+ case '.': if (allowDots) growBuf.addChar('.'); else growBuf.addStr("_8"); break;
+ case '!': growBuf.addStr("_9"); break;
+ case ',': growBuf.addStr("_00"); break;
+ case ' ': growBuf.addStr("_01"); break;
+ case '{': growBuf.addStr("_02"); break;
+ case '}': growBuf.addStr("_03"); break;
+ case '?': growBuf.addStr("_04"); break;
+ case '^': growBuf.addStr("_05"); break;
+ case '%': growBuf.addStr("_06"); break;
+ case '(': growBuf.addStr("_07"); break;
+ case ')': growBuf.addStr("_08"); break;
+ case '+': growBuf.addStr("_09"); break;
+ case '=': growBuf.addStr("_0A"); break;
+ case '$': growBuf.addStr("_0B"); break;
+ case '\\': growBuf.addStr("_0C"); break;
+ default:
+ if (c<0)
+ {
+ static char map[] = "0123456789ABCDEF";
+ char ids[5];
+ unsigned char id = (unsigned char)c;
+ ids[0]='_';
+ ids[1]='x';
+ ids[2]=map[id>>4];
+ ids[3]=map[id&0xF];
+ ids[4]=0;
+ growBuf.addStr(ids);
+ }
+ else if (caseSenseNames || !isupper(c))
+ {
+ growBuf.addChar(c);
+ }
+ else
+ {
+ growBuf.addChar('_');
+ growBuf.addChar(tolower(c));
+ }
+ break;
+ }
+ }
+ growBuf.addChar(0);
+ return growBuf.get();
+}
+
+/*! This function determines the file name on disk of an item
+ * given its name, which could be a class name with template
+ * arguments, so special characters need to be escaped.
+ */
+QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
+{
+ static bool shortNames = Config_getBool("SHORT_NAMES");
+ static bool createSubdirs = Config_getBool("CREATE_SUBDIRS");
+ QCString result;
+ if (shortNames) // use short names only
+ {
+ static QDict<int> usedNames(10007);
+ usedNames.setAutoDelete(TRUE);
+ static int count=1;
+
+ int *value=usedNames.find(name);
+ int num;
+ if (value==0)
+ {
+ usedNames.insert(name,new int(count));
+ num = count++;
+ }
+ else
+ {
+ num = *value;
+ }
+ result.sprintf("a%05d",num);
+ }
+ else // long names
+ {
+ result=escapeCharsInString(name,allowDots,allowUnderscore);
+ int resultLen = result.length();
+ if (resultLen>=128) // prevent names that cannot be created!
+ {
+ // third algorithm based on MD5 hash
+ uchar md5_sig[16];
+ QCString sigStr(33);
+ MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
+ MD5SigToString(md5_sig,sigStr.data(),33);
+ result=result.left(128-32)+sigStr;
+ }
+ }
+ if (createSubdirs)
+ {
+ int l1Dir=0,l2Dir=0;
+
+#if MAP_ALGO==ALGO_COUNT
+ // old algorithm, has the problem that after regeneration the
+ // output can be located in a different dir.
+ if (Doxygen::htmlDirMap==0)
+ {
+ Doxygen::htmlDirMap=new QDict<int>(100003);
+ Doxygen::htmlDirMap->setAutoDelete(TRUE);
+ }
+ static int curDirNum=0;
+ int *dirNum = Doxygen::htmlDirMap->find(result);
+ if (dirNum==0) // new name
+ {
+ Doxygen::htmlDirMap->insert(result,new int(curDirNum));
+ l1Dir = (curDirNum)&0xf; // bits 0-3
+ l2Dir = (curDirNum>>4)&0xff; // bits 4-11
+ curDirNum++;
+ }
+ else // existing name
+ {
+ l1Dir = (*dirNum)&0xf; // bits 0-3
+ l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
+ }
+#elif MAP_ALGO==ALGO_CRC16
+ // second algorithm based on CRC-16 checksum
+ int dirNum = qChecksum(result,result.length());
+ l1Dir = dirNum&0xf;
+ l2Dir = (dirNum>>4)&0xff;
+#elif MAP_ALGO==ALGO_MD5
+ // third algorithm based on MD5 hash
+ uchar md5_sig[16];
+ MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
+ l1Dir = md5_sig[14]&0xf;
+ l2Dir = md5_sig[15];
+#endif
+ result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
+ }
+ //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
+ return result;
+}
+
+QCString relativePathToRoot(const char *name)
+{
+ QCString result;
+ if (Config_getBool("CREATE_SUBDIRS"))
+ {
+ if (name==0)
+ {
+ return REL_PATH_TO_ROOT;
+ }
+ else
+ {
+ QCString n = name;
+ int i = n.findRev('/');
+ if (i!=-1)
+ {
+ result=REL_PATH_TO_ROOT;
+ }
+ }
+ }
+ return result;
+}
+
+void createSubDirs(QDir &d)
+{
+ if (Config_getBool("CREATE_SUBDIRS"))
+ {
+ // create 4096 subdirectories
+ int l1,l2;
+ for (l1=0;l1<16;l1++)
+ {
+ d.mkdir(QString().sprintf("d%x",l1));
+ for (l2=0;l2<256;l2++)
+ {
+ d.mkdir(QString().sprintf("d%x/d%02x",l1,l2));
+ }
+ }
+ }
+}
+
+/*! Input is a scopeName, output is the scopename split into a
+ * namespace part (as large as possible) and a classname part.
+ */
+void extractNamespaceName(const QCString &scopeName,
+ QCString &className,QCString &namespaceName,
+ bool allowEmptyClass)
+{
+ int i,p;
+ QCString clName=scopeName;
+ NamespaceDef *nd = 0;
+ if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
+ { // the whole name is a namespace (and not a class)
+ namespaceName=nd->name().copy();
+ className.resize(0);
+ goto done;
+ }
+ p=clName.length()-2;
+ while (p>=0 && (i=clName.findRev("::",p))!=-1)
+ // see if the first part is a namespace (and not a class)
+ {
+ //printf("Trying %s\n",clName.left(i).data());
+ if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
+ {
+ //printf("found!\n");
+ namespaceName=nd->name().copy();
+ className=clName.right(clName.length()-i-2);
+ goto done;
+ }
+ p=i-2; // try a smaller piece of the scope
+ }
+ //printf("not found!\n");
+
+ // not found, so we just have to guess.
+ className=scopeName.copy();
+ namespaceName.resize(0);
+
+done:
+ if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
+ {
+ // class and namespace with the same name, correct to return the class.
+ className=namespaceName.copy();
+ namespaceName.resize(0);
+ }
+ //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
+ // className.data(),namespaceName.data());
+ if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p")
+ {
+ className = className.left(className.length()-2);
+ }
+ return;
+}
+
+QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
+{
+ QCString result=scope.copy();
+ if (!templ.isEmpty() && scope.find('<')==-1)
+ {
+ int si,pi=0;
+ ClassDef *cd=0;
+ while (
+ (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
+ ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0)
+ )
+ {
+ //printf("Tried `%s'\n",(scope.left(si)+templ).data());
+ pi=si+2;
+ }
+ if (si==-1) // not nested => append template specifier
+ {
+ result+=templ;
+ }
+ else // nested => insert template specifier before after first class name
+ {
+ result=scope.left(si) + templ + scope.right(scope.length()-si);
+ }
+ }
+ //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
+ // scope.data(),templ.data(),result.data());
+ return result;
+}
+
+#if 0 // original version
+/*! Strips the scope from a name. Examples: A::B will return A
+ * and A<T>::B<N::C<D> > will return A<T>.
+ */
+QCString stripScope(const char *name)
+{
+ QCString result = name;
+ int l=result.length();
+ int p=l-1;
+ bool done;
+ int count;
+
+ while (p>=0)
+ {
+ char c=result.at(p);
+ switch (c)
+ {
+ case ':':
+ //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
+ return result.right(l-p-1);
+ case '>':
+ count=1;
+ done=FALSE;
+ //printf("pos < = %d\n",p);
+ p--;
+ while (p>=0 && !done)
+ {
+ c=result.at(p--);
+ switch (c)
+ {
+ case '>': count++; break;
+ case '<': count--; if (count<=0) done=TRUE; break;
+ default:
+ //printf("c=%c count=%d\n",c,count);
+ break;
+ }
+ }
+ //printf("pos > = %d\n",p+1);
+ break;
+ default:
+ p--;
+ }
+ }
+ //printf("stripScope(%s)=%s\n",name,name);
+ return name;
+}
+#endif
+
+// new version by Davide Cesari which also works for Fortran
+QCString stripScope(const char *name)
+{
+ QCString result = name;
+ int l=result.length();
+ int p;
+ bool done = FALSE;
+ bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
+ int count=0;
+
+ do
+ {
+ p=l-1; // start at the end of the string
+ while (p>=0 && count>=0)
+ {
+ char c=result.at(p);
+ switch (c)
+ {
+ case ':':
+ // only exit in the case of ::
+ //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
+ if (p>0 && result.at(p-1)==':') return result.right(l-p-1);
+ p--;
+ break;
+ case '>':
+ if (skipBracket) // we don't care about brackets
+ {
+ p--;
+ }
+ else // count open/close brackets
+ {
+ if (p>0 && result.at(p-1)=='>') // skip >> operator
+ {
+ p-=2;
+ break;
+ }
+ count=1;
+ //printf("pos < = %d\n",p);
+ p--;
+ bool foundMatch=false;
+ while (p>=0 && !foundMatch)
+ {
+ c=result.at(p--);
+ switch (c)
+ {
+ case '>':
+ count++;
+ break;
+ case '<':
+ if (p>0)
+ {
+ if (result.at(p-1) == '<') // skip << operator
+ {
+ p--;
+ break;
+ }
+ }
+ count--;
+ foundMatch = count==0;
+ break;
+ default:
+ //printf("c=%c count=%d\n",c,count);
+ break;
+ }
+ }
+ }
+ //printf("pos > = %d\n",p+1);
+ break;
+ default:
+ p--;
+ }
+ }
+ done = count==0 || skipBracket; // reparse if brackets do not match
+ skipBracket=TRUE;
+ }
+ while (!done); // if < > unbalanced repeat ignoring them
+ //printf("stripScope(%s)=%s\n",name,name);
+ return name;
+}
+
+
+/*! Converts a string to an XML-encoded string */
+QCString convertToXML(const char *s)
+{
+ static GrowBuf growBuf;
+ growBuf.clear();
+ if (s==0) return "";
+ const char *p=s;
+ char c;
+ while ((c=*p++))
+ {
+ switch (c)
+ {
+ case '<': growBuf.addStr("&lt;"); break;
+ case '>': growBuf.addStr("&gt;"); break;
+ case '&': growBuf.addStr("&amp;"); break;
+ case '\'': growBuf.addStr("&apos;"); break;
+ case '"': growBuf.addStr("&quot;"); break;
+ default: growBuf.addChar(c); break;
+ }
+ }
+ growBuf.addChar(0);
+ return growBuf.get();
+}
+
+/*! Converts a string to a HTML-encoded string */
+QCString convertToHtml(const char *s,bool keepEntities)
+{
+ static GrowBuf growBuf;
+ growBuf.clear();
+ if (s==0) return "";
+ const char *p=s;
+ char c;
+ while ((c=*p++))
+ {
+ switch (c)
+ {
+ case '<': growBuf.addStr("&lt;"); break;
+ case '>': growBuf.addStr("&gt;"); break;
+ case '&': if (keepEntities)
+ {
+ const char *e=p;
+ char ce;
+ while ((ce=*e++))
+ {
+ if (ce==';' || (!(isId(ce) || ce=='#'))) break;
+ }
+ if (ce==';') // found end of an entity
+ {
+ // copy entry verbatim
+ growBuf.addChar(c);
+ while (p<e) growBuf.addChar(*p++);
+ }
+ else
+ {
+ growBuf.addStr("&amp;");
+ }
+ }
+ else
+ {
+ growBuf.addStr("&amp;");
+ }
+ break;
+ case '\'': growBuf.addStr("&#39;"); break;
+ case '"': growBuf.addStr("&quot;"); break;
+ default: growBuf.addChar(c); break;
+ }
+ }
+ growBuf.addChar(0);
+ return growBuf.get();
+}
+
+QCString convertToJSString(const char *s)
+{
+ static GrowBuf growBuf;
+ growBuf.clear();
+ if (s==0) return "";
+ const char *p=s;
+ char c;
+ while ((c=*p++))
+ {
+ switch (c)
+ {
+ case '"': growBuf.addStr("\\\""); break;
+ case '\\': growBuf.addStr("\\\\"); break;
+ default: growBuf.addChar(c); break;
+ }
+ }
+ growBuf.addChar(0);
+ return growBuf.get();
+}
+
+
+QCString convertCharEntitiesToUTF8(const QCString &s)
+{
+ static QDict<char> entityMap(127);
+ static bool init=TRUE;
+ QCString result;
+ static QRegExp entityPat("&[a-zA-Z]+;");
+
+ if (init)
+ {
+ entityMap.insert("copy", "\xC2\xA9");
+ entityMap.insert("tm", "\xE2\x84\xA2");
+ entityMap.insert("trade", "\xE2\x84\xA2");
+ entityMap.insert("reg", "\xC2\xAE");
+ entityMap.insert("lsquo", "\xE2\x80\x98");
+ entityMap.insert("rsquo", "\xE2\x80\x99");
+ entityMap.insert("ldquo", "\xE2\x80\x9C");
+ entityMap.insert("rdquo", "\xE2\x80\x9D");
+ entityMap.insert("ndash", "\xE2\x80\x93");
+ entityMap.insert("mdash", "\xE2\x80\x94");
+ entityMap.insert("Auml", "\xC3\x84");
+ entityMap.insert("Euml", "\xC3\x8B");
+ entityMap.insert("Iuml", "\xC3\x8F");
+ entityMap.insert("Ouml", "\xC3\x96");
+ entityMap.insert("Uuml", "\xC3\x9C");
+ entityMap.insert("Yuml", "\xC5\xB8");
+ entityMap.insert("auml", "\xC3\xA4");
+ entityMap.insert("euml", "\xC3\xAB");
+ entityMap.insert("iuml", "\xC3\xAF");
+ entityMap.insert("ouml", "\xC3\xB6");
+ entityMap.insert("uuml", "\xC3\xBC");
+ entityMap.insert("yuml", "\xC3\xBF");
+ entityMap.insert("Aacute", "\xC3\x81");
+ entityMap.insert("Eacute", "\xC3\x89");
+ entityMap.insert("Iacute", "\xC3\x8D");
+ entityMap.insert("Oacute", "\xC3\x93");
+ entityMap.insert("Uacute", "\xC3\x9A");
+ entityMap.insert("aacute", "\xC3\xA1");
+ entityMap.insert("eacute", "\xC3\xA9");
+ entityMap.insert("iacute", "\xC3\xAD");
+ entityMap.insert("oacute", "\xC3\xB3");
+ entityMap.insert("uacute", "\xC3\xBA");
+ entityMap.insert("Agrave", "\xC3\x80");
+ entityMap.insert("Egrave", "\xC3\x88");
+ entityMap.insert("Igrave", "\xC3\x8C");
+ entityMap.insert("Ograve", "\xC3\x92");
+ entityMap.insert("Ugrave", "\xC3\x99");
+ entityMap.insert("agrave", "\xC3\xA0");
+ entityMap.insert("egrave", "\xC3\xA8");
+ entityMap.insert("igrave", "\xC3\xAC");
+ entityMap.insert("ograve", "\xC3\xB2");
+ entityMap.insert("ugrave", "\xC3\xB9");
+ entityMap.insert("Acirc", "\xC3\x82");
+ entityMap.insert("Ecirc", "\xC3\x8A");
+ entityMap.insert("Icirc", "\xC3\x8E");
+ entityMap.insert("Ocirc", "\xC3\x94");
+ entityMap.insert("Ucirc", "\xC3\x9B");
+ entityMap.insert("acirc", "\xC3\xA2");
+ entityMap.insert("ecirc", "\xC3\xAA");
+ entityMap.insert("icirc", "\xC3\xAE");
+ entityMap.insert("ocirc", "\xC3\xB4");
+ entityMap.insert("ucirc", "\xC3\xBB");
+ entityMap.insert("Atilde", "\xC3\x83");
+ entityMap.insert("Ntilde", "\xC3\x91");
+ entityMap.insert("Otilde", "\xC3\x95");
+ entityMap.insert("atilde", "\xC3\xA3");
+ entityMap.insert("ntilde", "\xC3\xB1");
+ entityMap.insert("otilde", "\xC3\xB5");
+ entityMap.insert("szlig", "\xC3\x9F");
+ entityMap.insert("Ccedil", "\xC3\x87");
+ entityMap.insert("ccedil", "\xC3\xA7");
+ entityMap.insert("Aring", "\xC3\x85");
+ entityMap.insert("aring", "\xC3\xA5");
+ entityMap.insert("nbsp", "\xC2\xA0");
+ entityMap.insert("Gamma", "\xCE\x93");
+ entityMap.insert("Delta", "\xCE\x94");
+ entityMap.insert("Theta", "\xCE\x98");
+ entityMap.insert("Lambda", "\xCE\x9B");
+ entityMap.insert("Xi", "\xCE\x9E");
+ entityMap.insert("Pi", "\xCE\xA0");
+ entityMap.insert("Sigma", "\xCE\xA3");
+ entityMap.insert("Upsilon", "\xCE\xA5");
+ entityMap.insert("Phi", "\xCE\xA6");
+ entityMap.insert("Psi", "\xCE\xA8");
+ entityMap.insert("Omega", "\xCE\xA9");
+ entityMap.insert("alpha", "\xCE\xB1");
+ entityMap.insert("beta", "\xCE\xB2");
+ entityMap.insert("gamma", "\xCE\xB3");
+ entityMap.insert("delta", "\xCE\xB4");
+ entityMap.insert("epsilon", "\xCE\xB5");
+ entityMap.insert("zeta", "\xCE\xB6");
+ entityMap.insert("eta", "\xCE\xB8");
+ entityMap.insert("theta", "\xCE\xB8");
+ entityMap.insert("iota", "\xCE\xB9");
+ entityMap.insert("kappa", "\xCE\xBA");
+ entityMap.insert("lambda", "\xCE\xBB");
+ entityMap.insert("mu", "\xCE\xBC");
+ entityMap.insert("nu", "\xCE\xBD");
+ entityMap.insert("xi", "\xCE\xBE");
+ entityMap.insert("pi", "\xCF\x80");
+ entityMap.insert("rho", "\xCF\x81");
+ entityMap.insert("sigma", "\xCF\x83");
+ entityMap.insert("tau", "\xCF\x84");
+ entityMap.insert("upsilon", "\xCF\x85");
+ entityMap.insert("phi", "\xCF\x86");
+ entityMap.insert("chi", "\xCF\x87");
+ entityMap.insert("psi", "\xCF\x88");
+ entityMap.insert("omega", "\xCF\x89");
+ entityMap.insert("sigmaf", "\xCF\x82");
+ entityMap.insert("sect", "\xC2\xA7");
+ entityMap.insert("deg", "\xC2\xB0");
+ entityMap.insert("prime", "\xE2\x80\xB2");
+ entityMap.insert("Prime", "\xE2\x80\xB2");
+ entityMap.insert("infin", "\xE2\x88\x9E");
+ entityMap.insert("empty", "\xE2\x88\x85");
+ entityMap.insert("plusmn", "\xC2\xB1");
+ entityMap.insert("times", "\xC3\x97");
+ entityMap.insert("minus", "\xE2\x88\x92");
+ entityMap.insert("sdot", "\xE2\x8B\x85");
+ entityMap.insert("part", "\xE2\x88\x82");
+ entityMap.insert("nabla", "\xE2\x88\x87");
+ entityMap.insert("radic", "\xE2\x88\x9A");
+ entityMap.insert("perp", "\xE2\x8A\xA5");
+ entityMap.insert("sum", "\xE2\x88\x91");
+ entityMap.insert("int", "\xE2\x88\xAB");
+ entityMap.insert("prod", "\xE2\x88\x8F");
+ entityMap.insert("sim", "\xE2\x88\xBC");
+ entityMap.insert("asymp", "\xE2\x89\x88");
+ entityMap.insert("ne", "\xE2\x89\xA0");
+ entityMap.insert("equiv", "\xE2\x89\xA1");
+ entityMap.insert("prop", "\xE2\x88\x9D");
+ entityMap.insert("le", "\xE2\x89\xA4");
+ entityMap.insert("ge", "\xE2\x89\xA5");
+ entityMap.insert("larr", "\xE2\x86\x90");
+ entityMap.insert("rarr", "\xE2\x86\x92");
+ entityMap.insert("isin", "\xE2\x88\x88");
+ entityMap.insert("notin", "\xE2\x88\x89");
+ entityMap.insert("lceil", "\xE2\x8C\x88");
+ entityMap.insert("rceil", "\xE2\x8C\x89");
+ entityMap.insert("lfloor", "\xE2\x8C\x8A");
+ entityMap.insert("rfloor", "\xE2\x8C\x8B");
+ init=FALSE;
+ }
+
+ if (s==0) return result;
+ int p,i=0,l;
+ while ((p=entityPat.match(s,i,&l))!=-1)
+ {
+ if (p>i) result+=s.mid(i,p-i);
+ QCString entity = s.mid(p+1,l-2);
+ char *code = entityMap.find(entity);
+ if (code)
+ {
+ result+=code;
+ }
+ else
+ {
+ result+=s.mid(p,l);
+ }
+ i=p+l;
+ }
+ result+=s.mid(i,s.length()-i);
+ return result;
+}
+
+/*! Returns the standard string that is generated when the \\overload
+ * command is used.
+ */
+QCString getOverloadDocs()
+{
+ return theTranslator->trOverloadText();
+ //"This is an overloaded member function, "
+ // "provided for convenience. It differs from the above "
+ // "function only in what argument(s) it accepts.";
+}
+
+void addMembersToMemberGroup(MemberList *ml,
+ MemberGroupSDict **ppMemberGroupSDict,
+ Definition *context)
+{
+ ASSERT(context!=0);
+ //printf("addMemberToMemberGroup()\n");
+ if (ml==0) return;
+ MemberListIterator mli(*ml);
+ MemberDef *md;
+ uint index;
+ for (index=0;(md=mli.current());)
+ {
+ if (md->isEnumerate()) // insert enum value of this enum into groups
+ {
+ LockingPtr<MemberList> fmdl=md->enumFieldList();
+ if (fmdl!=0)
+ {
+ MemberDef *fmd=fmdl->first();
+ while (fmd)
+ {
+ int groupId=fmd->getMemberGroupId();
+ if (groupId!=-1)
+ {
+ MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
+ //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
+ //QCString *pDocs = Doxygen::memberDocDict[groupId];
+ if (info)
+ {
+ if (*ppMemberGroupSDict==0)
+ {
+ *ppMemberGroupSDict = new MemberGroupSDict;
+ (*ppMemberGroupSDict)->setAutoDelete(TRUE);
+ }
+ MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
+ if (mg==0)
+ {
+ mg = new MemberGroup(
+ context,
+ groupId,
+ info->header,
+ info->doc,
+ info->docFile
+ );
+ (*ppMemberGroupSDict)->append(groupId,mg);
+ }
+ mg->insertMember(fmd); // insert in member group
+ fmd->setMemberGroup(mg);
+ }
+ }
+ fmd=fmdl->next();
+ }
+ }
+ }
+ int groupId=md->getMemberGroupId();
+ if (groupId!=-1)
+ {
+ MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
+ //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
+ //QCString *pDocs = Doxygen::memberDocDict[groupId];
+ if (info)
+ {
+ if (*ppMemberGroupSDict==0)
+ {
+ *ppMemberGroupSDict = new MemberGroupSDict;
+ (*ppMemberGroupSDict)->setAutoDelete(TRUE);
+ }
+ MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
+ if (mg==0)
+ {
+ mg = new MemberGroup(
+ context,
+ groupId,
+ info->header,
+ info->doc,
+ info->docFile
+ );
+ (*ppMemberGroupSDict)->append(groupId,mg);
+ }
+ md = ml->take(index); // remove from member list
+ mg->insertMember(md); // insert in member group
+ mg->setRefItems(info->m_sli);
+ md->setMemberGroup(mg);
+ continue;
+ }
+ }
+ ++mli;++index;
+ }
+}
+
+/*! Extracts a (sub-)string from \a type starting at \a pos that
+ * could form a class. The index of the match is returned and the found
+ * class \a name and a template argument list \a templSpec. If -1 is returned
+ * there are no more matches.
+ */
+int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
+{
+ static const QRegExp re_norm("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
+ static const QRegExp re_ftn("[a-z_A-Z\\x80-\\xFF][()=_a-z_A-Z0-9:\\x80-\\xFF]*");
+ QRegExp re;
+
+ if (lang == SrcLangExt_Fortran)
+ {
+ if (type.at(pos)==',') return -1;
+ if (type.left(4).lower()=="type")
+ {
+ re = re_norm;
+ }
+ else
+ {
+ re = re_ftn;
+ }
+ }
+ else
+ {
+ re = re_norm;
+ }
+
+ name.resize(0);
+ templSpec.resize(0);
+ int i,l;
+ int typeLen=type.length();
+ if (typeLen>0)
+ {
+ if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
+ {
+ int ts=i+l;
+ int te=ts;
+ int tl=0;
+ while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
+ if (type.at(ts)=='<') // assume template instance
+ {
+ // locate end of template
+ te=ts+1;
+ int brCount=1;
+ while (te<typeLen && brCount!=0)
+ {
+ if (type.at(te)=='<')
+ {
+ if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
+ }
+ if (type.at(te)=='>')
+ {
+ if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
+ }
+ te++;
+ }
+ }
+ name = type.mid(i,l);
+ if (te>ts)
+ {
+ templSpec = type.mid(ts,te-ts),tl+=te-ts;
+ pos=i+l+tl;
+ }
+ else // no template part
+ {
+ pos=i+l;
+ }
+ //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
+ // type.data(),pos,name.data(),templSpec.data());
+ return i;
+ }
+ }
+ pos = typeLen;
+ //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
+ // type.data(),pos,name.data(),templSpec.data());
+ return -1;
+}
+
+/*! Substitutes any occurrence of a formal argument from argument list
+ * \a formalArgs in \a name by the corresponding actual argument in
+ * argument list \a actualArgs. The result after substitution
+ * is returned as a string. The argument \a name is used to
+ * prevent recursive substitution.
+ */
+QCString substituteTemplateArgumentsInString(
+ const QCString &name,
+ ArgumentList *formalArgs,
+ ArgumentList *actualArgs)
+{
+ //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
+ // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
+ if (formalArgs==0) return name;
+ QCString result;
+ static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+ int p=0,l,i;
+ // for each identifier in the base class name (e.g. B<T> -> B and T)
+ while ((i=re.match(name,p,&l))!=-1)
+ {
+ result += name.mid(p,i-p);
+ QCString n = name.mid(i,l);
+ ArgumentListIterator formAli(*formalArgs);
+ Argument *formArg;
+ Argument *actArg=actualArgs->first();
+
+ // if n is a template argument, then we substitute it
+ // for its template instance argument.
+ bool found=FALSE;
+ for (formAli.toFirst();
+ (formArg=formAli.current()) && !found;
+ ++formAli,actArg=actualArgs->next()
+ )
+ {
+ if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
+ {
+ formArg->name = formArg->type.mid(6);
+ formArg->type = "class";
+ }
+ if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
+ {
+ formArg->name = formArg->type.mid(9);
+ formArg->type = "typename";
+ }
+ if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
+ {
+ //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
+ // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
+ //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
+ // formArg->name.data(),actArg->type.data(),actArg->name.data()
+ // );
+ if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
+ {
+ // replace formal argument with the actual argument of the instance
+ if (!leftScopeMatch(actArg->type,n))
+ // the scope guard is to prevent recursive lockup for
+ // template<class A> class C : public<A::T>,
+ // where A::T would become A::T::T here,
+ // since n==A and actArg->type==A::T
+ // see bug595833 for an example
+ {
+ if (actArg->name.isEmpty())
+ {
+ result += actArg->type+" ";
+ found=TRUE;
+ }
+ else
+ // for case where the actual arg is something like "unsigned int"
+ // the "int" part is in actArg->name.
+ {
+ result += actArg->type+" "+actArg->name+" ";
+ found=TRUE;
+ }
+ }
+ }
+ else if (formArg->name==n &&
+ actArg==0 &&
+ !formArg->defval.isEmpty() &&
+ formArg->defval!=name /* to prevent recursion */
+ )
+ {
+ result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
+ found=TRUE;
+ }
+ }
+ else if (formArg->name==n &&
+ actArg==0 &&
+ !formArg->defval.isEmpty() &&
+ formArg->defval!=name /* to prevent recursion */
+ )
+ {
+ result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
+ found=TRUE;
+ }
+ }
+ if (!found) result += n;
+ p=i+l;
+ }
+ result+=name.right(name.length()-p);
+ //printf(" Inheritance relation %s -> %s\n",
+ // name.data(),result.data());
+ return result.stripWhiteSpace();
+}
+
+/*! Makes a deep copy of the list of argument lists \a srcLists.
+ * Will allocate memory, that is owned by the caller.
+ */
+QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
+{
+ ASSERT(srcLists!=0);
+ QList<ArgumentList> *dstLists = new QList<ArgumentList>;
+ dstLists->setAutoDelete(TRUE);
+ QListIterator<ArgumentList> sli(*srcLists);
+ ArgumentList *sl;
+ for (;(sl=sli.current());++sli)
+ {
+ dstLists->append(sl->deepCopy());
+ }
+ return dstLists;
+}
+
+/*! Strips template specifiers from scope \a fullName, except those
+ * that make up specialized classes. The switch \a parentOnly
+ * determines whether or not a template "at the end" of a scope
+ * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will
+ * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will
+ * strip both unless A<T> or B<S> are specialized template classes.
+ */
+QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
+ bool parentOnly,
+ QCString *pLastScopeStripped)
+{
+ QCString result;
+ int p=0;
+ int l=fullName.length();
+ int i=fullName.find('<');
+ while (i!=-1)
+ {
+ //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
+ int e=i+1;
+ bool done=FALSE;
+ int count=1;
+ while (e<l && !done)
+ {
+ char c=fullName.at(e++);
+ if (c=='<')
+ {
+ count++;
+ }
+ else if (c=='>')
+ {
+ count--;
+ done = count==0;
+ }
+ }
+ int si= fullName.find("::",e);
+
+ if (parentOnly && si==-1) break;
+ // we only do the parent scope, so we stop here if needed
+
+ result+=fullName.mid(p,i-p);
+ //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data());
+ if (getClass(result+fullName.mid(i,e-i))!=0)
+ {
+ result+=fullName.mid(i,e-i);
+ //printf(" 2:result+=%s\n",fullName.mid(i,e-i-1).data());
+ }
+ else if (pLastScopeStripped)
+ {
+ //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data());
+ *pLastScopeStripped=fullName.mid(i,e-i);
+ }
+ p=e;
+ i=fullName.find('<',p);
+ }
+ result+=fullName.right(l-p);
+ //printf("3:result+=%s\n",fullName.right(l-p).data());
+ return result;
+}
+
+/*! Merges two scope parts together. The parts may (partially) overlap.
+ * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
+ * Example2: \c A and \c B will be \c A::B <br>
+ * Example3: \c A::B and B will be \c A::B
+ *
+ * @param leftScope the left hand part of the scope.
+ * @param rightScope the right hand part of the scope.
+ * @returns the merged scope.
+ */
+QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
+{
+ // case leftScope=="A" rightScope=="A::B" => result = "A::B"
+ if (leftScopeMatch(rightScope,leftScope)) return rightScope;
+ QCString result;
+ int i=0,p=leftScope.length();
+
+ // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
+ // case leftScope=="A::B" rightScope=="B" => result = "A::B"
+ bool found=FALSE;
+ while ((i=leftScope.findRev("::",p))!=-1)
+ {
+ if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
+ {
+ result = leftScope.left(i+2)+rightScope;
+ found=TRUE;
+ }
+ p=i-1;
+ }
+ if (found) return result;
+
+ // case leftScope=="A" rightScope=="B" => result = "A::B"
+ result=leftScope.copy();
+ if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
+ result+=rightScope;
+ return result;
+}
+
+/*! Returns a fragment from scope \a s, starting at position \a p.
+ *
+ * @param s the scope name as a string.
+ * @param p the start position (0 is the first).
+ * @param l the resulting length of the fragment.
+ * @returns the location of the fragment, or -1 if non is found.
+ */
+int getScopeFragment(const QCString &s,int p,int *l)
+{
+ int sl=s.length();
+ int sp=p;
+ int count=0;
+ bool done;
+ if (sp>=sl) return -1;
+ while (sp<sl)
+ {
+ char c=s.at(sp);
+ if (c==':') sp++,p++; else break;
+ }
+ while (sp<sl)
+ {
+ char c=s.at(sp);
+ switch (c)
+ {
+ case ':': // found next part
+ goto found;
+ case '<': // skip template specifier
+ count=1;sp++;
+ done=FALSE;
+ while (sp<sl && !done)
+ {
+ // TODO: deal with << and >> operators!
+ char c=s.at(sp++);
+ switch(c)
+ {
+ case '<': count++; break;
+ case '>': count--; if (count==0) done=TRUE; break;
+ default: break;
+ }
+ }
+ break;
+ default:
+ sp++;
+ break;
+ }
+ }
+found:
+ *l=sp-p;
+ //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
+ return p;
+}
+
+//----------------------------------------------------------------------------
+
+PageDef *addRelatedPage(const char *name,const QCString &ptitle,
+ const QCString &doc,
+ QList<SectionInfo> * /*anchors*/,
+ const char *fileName,int startLine,
+ const QList<ListItemInfo> *sli,
+ GroupDef *gd,
+ TagInfo *tagInfo,
+ SrcLangExt lang
+ )
+{
+ PageDef *pd=0;
+ //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
+ if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
+ {
+ // append documentation block to the page.
+ pd->setDocumentation(doc,fileName,startLine);
+ //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name);
+ }
+ else // new page
+ {
+ QCString baseName=name;
+ if (baseName.right(4)==".tex")
+ baseName=baseName.left(baseName.length()-4);
+ else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
+ baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
+
+ QCString title=ptitle.stripWhiteSpace();
+ pd=new PageDef(fileName,startLine,baseName,doc,title);
+
+ pd->setRefItems(sli);
+ pd->setLanguage(lang);
+
+ if (tagInfo)
+ {
+ pd->setReference(tagInfo->tagName);
+ }
+
+ pd->setFileName(convertNameToFile(pd->name(),FALSE,TRUE));
+
+ //printf("Appending page `%s'\n",baseName.data());
+ Doxygen::pageSDict->append(baseName,pd);
+
+ if (gd) gd->addPage(pd);
+
+ if (!pd->title().isEmpty())
+ {
+ //outputList->writeTitle(pi->name,pi->title);
+
+ // a page name is a label as well!
+ QCString file;
+ if (gd)
+ {
+ file=gd->getOutputFileBase();
+ }
+ else
+ {
+ file=pd->getOutputFileBase();
+ }
+ SectionInfo *si=new SectionInfo(
+ file,pd->name(),pd->title(),SectionInfo::Page,0,pd->getReference());
+ //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
+ // si->label.data(),si->definition?si->definition->name().data():"<none>",
+ // si->fileName.data());
+ //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
+ //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
+ Doxygen::sectionDict.append(pd->name(),si);
+ }
+ }
+ return pd;
+}
+
+//----------------------------------------------------------------------------
+
+void addRefItem(const QList<ListItemInfo> *sli,
+ const char *key,
+ const char *prefix, const char *name,const char *title,const char *args)
+{
+ //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args);
+ if (sli)
+ {
+ QListIterator<ListItemInfo> slii(*sli);
+ ListItemInfo *lii;
+ for (slii.toFirst();(lii=slii.current());++slii)
+ {
+ RefList *refList = Doxygen::xrefLists->find(lii->type);
+ if (refList
+ &&
+ (
+ // either not a built-in list or the list is enabled
+ (lii->type!="todo" || Config_getBool("GENERATE_TODOLIST")) &&
+ (lii->type!="test" || Config_getBool("GENERATE_TESTLIST")) &&
+ (lii->type!="bug" || Config_getBool("GENERATE_BUGLIST")) &&
+ (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
+ )
+ )
+ {
+ RefItem *item = refList->getRefItem(lii->itemId);
+ ASSERT(item!=0);
+
+ item->prefix = prefix;
+ item->name = name;
+ item->title = title;
+ item->args = args;
+
+ refList->insertIntoList(key,item);
+
+ }
+ }
+ }
+}
+
+void addGroupListToTitle(OutputList &ol,Definition *d)
+{
+ LockingPtr<GroupList> groups = d->partOfGroups();
+ if (groups!=0) // write list of group to which this definition belongs
+ {
+ ol.pushGeneratorState();
+ ol.disableAllBut(OutputGenerator::Html);
+ ol.writeString("<div class=\"ingroups\">");
+ GroupListIterator gli(*groups);
+ GroupDef *gd;
+ bool first=TRUE;
+ for (gli.toFirst();(gd=gli.current());++gli)
+ {
+ if (!first) { ol.writeString(" &#124; "); } else first=FALSE;
+ ol.writeObjectLink(gd->getReference(),
+ gd->getOutputFileBase(),0,gd->groupTitle());
+ }
+ ol.writeString("</div>");
+ ol.popGeneratorState();
+ }
+}
+
+void filterLatexString(FTextStream &t,const char *str,
+ bool insideTabbing,bool insidePre,bool insideItem)
+{
+ if (str==0) return;
+ //printf("filterLatexString(%s)\n",str);
+ //if (strlen(str)<2) stackTrace();
+ const unsigned char *p=(const unsigned char *)str;
+ unsigned char c;
+ unsigned char pc='\0';
+ while (*p)
+ {
+ c=*p++;
+
+ if (insidePre)
+ {
+ switch(c)
+ {
+ case '\\': t << "\\(\\backslash\\)"; break;
+ case '{': t << "\\{"; break;
+ case '}': t << "\\}"; break;
+ case '_': t << "\\_"; break;
+ default:
+ t << (char)c;
+ }
+ }
+ else
+ {
+ switch(c)
+ {
+ case '#': t << "\\#"; break;
+ case '$': t << "\\$"; break;
+ case '%': t << "\\%"; break;
+ case '^': t << "$^\\wedge$"; break;
+ case '&': t << "\\&"; break;
+ case '*': t << "$\\ast$"; break;
+ case '_': if (!insideTabbing) t << "\\-";
+ t << "\\_";
+ if (!insideTabbing) t << "\\-";
+ break;
+ case '{': t << "\\{"; break;
+ case '}': t << "\\}"; break;
+ case '<': t << "$<$"; break;
+ case '>': t << "$>$"; break;
+ case '|': t << "$|$"; break;
+ case '~': t << "$\\sim$"; break;
+ case '[': if (Config_getBool("PDF_HYPERLINKS") || insideItem)
+ t << "\\mbox{[}";
+ else
+ t << "[";
+ break;
+ case ']': if (pc=='[') t << "$\\,$";
+ if (Config_getBool("PDF_HYPERLINKS") || insideItem)
+ t << "\\mbox{]}";
+ else
+ t << "]";
+ break;
+ case '-': t << "-\\/";
+ break;
+ case '\\': if (*p=='<')
+ { t << "$<$"; p++; }
+ else if (*p=='>')
+ { t << "$>$"; p++; }
+ else
+ { t << "\\textbackslash{}"; }
+ break;
+ case '"': { t << "\\char`\\\"{}"; }
+ break;
+
+ default:
+ //if (!insideTabbing && forceBreaks && c!=' ' && *p!=' ')
+ if (!insideTabbing &&
+ ((c>='A' && c<='Z' && pc!=' ' && pc!='\0') || (c==':' && pc!=':') || (pc=='.' && isId(c)))
+ )
+ {
+ t << "\\-";
+ }
+ t << (char)c;
+ }
+ }
+ pc = c;
+ }
+}
+
+
+QCString rtfFormatBmkStr(const char *name)
+{
+ static QCString g_nextTag( "AAAAAAAAAA" );
+ static QDict<QCString> g_tagDict( 5003 );
+
+ g_tagDict.setAutoDelete(TRUE);
+
+ // To overcome the 40-character tag limitation, we
+ // substitute a short arbitrary string for the name
+ // supplied, and keep track of the correspondence
+ // between names and strings.
+ QCString key( name );
+ QCString* tag = g_tagDict.find( key );
+ if ( !tag )
+ {
+ // This particular name has not yet been added
+ // to the list. Add it, associating it with the
+ // next tag value, and increment the next tag.
+ tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
+ g_tagDict.insert( key, tag );
+
+ // This is the increment part
+ char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1;
+ for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
+ {
+ if ( ( ++(*nxtTag) ) > 'Z' )
+ {
+ *nxtTag = 'A';
+ }
+ else
+ {
+ // Since there was no carry, we can stop now
+ break;
+ }
+ }
+ }
+
+ return *tag;
+}
+
+QCString stripExtension(const char *fName)
+{
+ QCString result=fName;
+ if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
+ {
+ result=result.left(result.length()-Doxygen::htmlFileExtension.length());
+ }
+ return result;
+}
+
+
+void replaceNamespaceAliases(QCString &scope,int i)
+{
+ while (i>0)
+ {
+ QCString ns = scope.left(i);
+ QCString *s = Doxygen::namespaceAliasDict[ns];
+ if (s)
+ {
+ scope=*s+scope.right(scope.length()-i);
+ i=s->length();
+ }
+ if (i>0 && ns==scope.left(i)) break;
+ }
+}
+
+QCString stripPath(const char *s)
+{
+ QCString result=s;
+ int i=result.findRev('/');
+ if (i!=-1)
+ {
+ result=result.mid(i+1);
+ }
+ return result;
+}
+
+/** returns \c TRUE iff string \a s contains word \a w */
+bool containsWord(const QCString &s,const QCString &word)
+{
+ static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
+ int p=0,i,l;
+ while ((i=wordExp.match(s,p,&l))!=-1)
+ {
+ if (s.mid(i,l)==word) return TRUE;
+ p=i+l;
+ }
+ return FALSE;
+}
+
+bool findAndRemoveWord(QCString &s,const QCString &word)
+{
+ static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
+ int p=0,i,l;
+ while ((i=wordExp.match(s,p,&l))!=-1)
+ {
+ if (s.mid(i,l)==word)
+ {
+ if (i>0 && isspace((uchar)s.at(i-1)))
+ i--,l++;
+ else if (i+l<(int)s.length() && isspace(s.at(i+l)))
+ l++;
+ s = s.left(i)+s.mid(i+l); // remove word + spacing
+ return TRUE;
+ }
+ p=i+l;
+ }
+ return FALSE;
+}
+
+/** Special version of QCString::stripWhiteSpace() that only strips
+ * completely blank lines.
+ * @param s the string to be stripped
+ * @param docLine the line number corresponding to the start of the
+ * string. This will be adjusted based on the number of lines stripped
+ * from the start.
+ * @returns The stripped string.
+ */
+QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine)
+{
+ const char *p = s.data();
+ if (p==0) return 0;
+
+ // search for leading empty lines
+ int i=0,li=-1,l=s.length();
+ char c;
+ while ((c=*p++))
+ {
+ if (c==' ' || c=='\t' || c=='\r') i++;
+ else if (c=='\n') i++,li=i,docLine++;
+ else break;
+ }
+
+ // search for trailing empty lines
+ int b=l-1,bi=-1;
+ p=s.data()+b;
+ while (b>=0)
+ {
+ c=*p; p--;
+ if (c==' ' || c=='\t' || c=='\r') b--;
+ else if (c=='\n') bi=b,b--;
+ else break;
+ }
+
+ // return whole string if no leading or trailing lines where found
+ if (li==-1 && bi==-1) return s;
+
+ // return substring
+ if (bi==-1) bi=l;
+ if (li==-1) li=0;
+ if (bi<=li) return 0; // only empty lines
+ return s.mid(li,bi-li);
+}
+
+#if 0
+void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
+ const QCString &str,bool priority,const QCString &anchor)
+{
+ static bool searchEngine = Config_getBool("SEARCHENGINE");
+ if (searchEngine)
+ {
+ Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
+ static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
+ int i,p=0,l;
+ while ((i=wordPattern.match(str,p,&l))!=-1)
+ {
+ Doxygen::searchIndex->addWord(str.mid(i,l),priority);
+ p=i+l;
+ }
+ }
+}
+#endif
+
+//--------------------------------------------------------------------------
+
+static QDict<int> g_extLookup;
+
+static struct Lang2ExtMap
+{
+ const char *langName;
+ const char *parserName;
+ SrcLangExt parserId;
+}
+g_lang2extMap[] =
+{
+// language parser parser option
+ { "idl", "c", SrcLangExt_IDL },
+ { "java", "c", SrcLangExt_Java },
+ { "javascript", "c", SrcLangExt_JS },
+ { "csharp", "c", SrcLangExt_CSharp },
+ { "d", "c", SrcLangExt_D },
+ { "php", "c", SrcLangExt_PHP },
+ { "objective-c", "c", SrcLangExt_ObjC },
+ { "c", "c", SrcLangExt_Cpp },
+ { "c++", "c", SrcLangExt_Cpp },
+ { "python", "python", SrcLangExt_Python },
+ { "fortran", "fortran", SrcLangExt_Fortran },
+ { "vhdl", "vhdl", SrcLangExt_VHDL },
+ { "dbusxml", "dbusxml", SrcLangExt_XML },
+ { "tcl", "tcl", SrcLangExt_Tcl },
+ { "md", "md", SrcLangExt_Markdown },
+ { 0, 0, (SrcLangExt)0 }
+};
+
+bool updateLanguageMapping(const QCString &extension,const QCString &language)
+{
+ const Lang2ExtMap *p = g_lang2extMap;
+ QCString langName = language.lower();
+ while (p->langName)
+ {
+ if (langName==p->langName) break;
+ p++;
+ }
+ if (!p->langName) return FALSE;
+
+ // found the language
+ SrcLangExt parserId = p->parserId;
+ QCString extName = extension.lower();
+ if (extName.isEmpty()) return FALSE;
+ if (extName.at(0)!='.') extName.prepend(".");
+ if (g_extLookup.find(extension)!=0) // language was already register for this ext
+ {
+ g_extLookup.remove(extension);
+ }
+ //printf("registering extension %s\n",extName.data());
+ g_extLookup.insert(extName,new int(parserId));
+ if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
+ {
+ err("Failed to assign extension %s to parser %s for language %s\n",
+ extName.data(),p->parserName,language.data());
+ }
+ else
+ {
+ //msg("Registered extension %s to language parser %s...\n",
+ // extName.data(),language.data());
+ }
+ return TRUE;
+}
+
+void initDefaultExtensionMapping()
+{
+ g_extLookup.setAutoDelete(TRUE);
+ // extension parser id
+ updateLanguageMapping(".idl", "idl");
+ updateLanguageMapping(".ddl", "idl");
+ updateLanguageMapping(".odl", "idl");
+ updateLanguageMapping(".java", "java");
+ updateLanguageMapping(".as", "javascript");
+ updateLanguageMapping(".js", "javascript");
+ updateLanguageMapping(".cs", "csharp");
+ updateLanguageMapping(".d", "d");
+ updateLanguageMapping(".php", "php");
+ updateLanguageMapping(".php4", "php");
+ updateLanguageMapping(".php5", "php");
+ updateLanguageMapping(".inc", "php");
+ updateLanguageMapping(".phtml", "php");
+ updateLanguageMapping(".m", "objective-c");
+ updateLanguageMapping(".M", "objective-c");
+ updateLanguageMapping(".mm", "objective-c");
+ updateLanguageMapping(".py", "python");
+ updateLanguageMapping(".f", "fortran");
+ updateLanguageMapping(".for", "fortran");
+ updateLanguageMapping(".f90", "fortran");
+ updateLanguageMapping(".vhd", "vhdl");
+ updateLanguageMapping(".vhdl", "vhdl");
+ updateLanguageMapping(".tcl", "tcl");
+ updateLanguageMapping(".ucf", "vhdl");
+ updateLanguageMapping(".qsf", "vhdl");
+ updateLanguageMapping(".md", "md");
+ updateLanguageMapping(".markdown", "md");
+
+ //updateLanguageMapping(".xml", "dbusxml");
+}
+
+SrcLangExt getLanguageFromFileName(const QCString fileName)
+{
+ int i = fileName.findRev('.');
+ if (i!=-1) // name has an extension
+ {
+ QCString extStr=fileName.right(fileName.length()-i).lower();
+ if (!extStr.isEmpty()) // non-empty extension
+ {
+ int *pVal=g_extLookup.find(extStr);
+ if (pVal) // listed extension
+ {
+ //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
+ return (SrcLangExt)*pVal;
+ }
+ }
+ }
+ //printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
+ return SrcLangExt_Cpp; // not listed => assume C-ish language.
+}
+
+//--------------------------------------------------------------------------
+
+MemberDef *getMemberFromSymbol(Definition *scope,FileDef *fileScope,
+ const char *n)
+{
+ if (scope==0 ||
+ (scope->definitionType()!=Definition::TypeClass &&
+ scope->definitionType()!=Definition::TypeNamespace
+ )
+ )
+ {
+ scope=Doxygen::globalScope;
+ }
+
+ QCString name = n;
+ if (name.isEmpty())
+ return 0; // no name was given
+
+ DefinitionIntf *di = Doxygen::symbolMap->find(name);
+ if (di==0)
+ return 0; // could not find any matching symbols
+
+ // mostly copied from getResolvedClassRec()
+ QCString explicitScopePart;
+ int qualifierIndex = computeQualifiedIndex(name);
+ if (qualifierIndex!=-1)
+ {
+ explicitScopePart = name.left(qualifierIndex);
+ replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
+ name = name.mid(qualifierIndex+2);
+ }
+ //printf("explicitScopePart=%s\n",explicitScopePart.data());
+
+ int minDistance = 10000;
+ MemberDef *bestMatch = 0;
+
+ if (di->definitionType()==DefinitionIntf::TypeSymbolList)
+ {
+ //printf("multiple matches!\n");
+ // find the closest closest matching definition
+ DefinitionListIterator dli(*(DefinitionList*)di);
+ Definition *d;
+ for (dli.toFirst();(d=dli.current());++dli)
+ {
+ if (d->definitionType()==Definition::TypeMember)
+ {
+ g_visitedNamespaces.clear();
+ int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+ if (distance!=-1 && distance<minDistance)
+ {
+ minDistance = distance;
+ bestMatch = (MemberDef *)d;
+ //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
+ }
+ }
+ }
+ }
+ else if (di->definitionType()==Definition::TypeMember)
+ {
+ //printf("unique match!\n");
+ Definition *d = (Definition *)di;
+ g_visitedNamespaces.clear();
+ int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
+ if (distance!=-1 && distance<minDistance)
+ {
+ minDistance = distance;
+ bestMatch = (MemberDef *)d;
+ //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
+ }
+ }
+ return bestMatch;
+}
+
+/*! Returns true iff the given name string appears to be a typedef in scope. */
+bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
+{
+ MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
+
+ if (bestMatch && bestMatch->isTypedef())
+ return TRUE; // closest matching symbol is a typedef
+ else
+ return FALSE;
+}
+
+
+int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos)
+{
+ int bytes=1;
+ if (startPos>=len) return len;
+ char c = utf8Str[startPos];
+ if (c<0) // multibyte utf-8 character
+ {
+ bytes++; // 1xxx.xxxx: >=2 byte character
+ if (((uchar)c&0xE0)==0xE0)
+ {
+ bytes++; // 111x.xxxx: >=3 byte character
+ }
+ if (((uchar)c&0xF0)==0xF0)
+ {
+ bytes++; // 1111.xxxx: 4 byte character
+ }
+ }
+ else if (c=='&') // skip over character entities
+ {
+ static QRegExp re1("&#[0-9]+;"); // numerical entity
+ static QRegExp re2("&[A-Z_a-z]+;"); // named entity
+ int l1,l2;
+ int i1 = re1.match(utf8Str,startPos,&l1);
+ int i2 = re2.match(utf8Str,startPos,&l2);
+ if (i1!=-1)
+ {
+ bytes=l1;
+ }
+ else if (i2!=-1)
+ {
+ bytes=l2;
+ }
+ }
+ return startPos+bytes;
+}
+
+QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
+ const QCString &doc,const QCString &fileName,int lineNr)
+{
+ QGString s;
+ if (doc.isEmpty()) return s.data();
+ FTextStream t(&s);
+ DocNode *root = validatingParseDoc(fileName,lineNr,
+ (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
+ TextDocVisitor *visitor = new TextDocVisitor(t);
+ root->accept(visitor);
+ delete visitor;
+ delete root;
+ QCString result = convertCharEntitiesToUTF8(s.data());
+ int i=0;
+ int charCnt=0;
+ int l=result.length();
+ bool addEllipsis=FALSE;
+ while ((i=nextUtf8CharPosition(result,l,i))<l)
+ {
+ charCnt++;
+ if (charCnt>=80) break;
+ }
+ if (charCnt>=80) // try to truncate the string
+ {
+ while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100)
+ {
+ charCnt++;
+ if (result.at(i)>=0 && isspace(result.at(i)))
+ {
+ addEllipsis=TRUE;
+ }
+ else if (result.at(i)==',' ||
+ result.at(i)=='.' ||
+ result.at(i)=='?')
+ {
+ break;
+ }
+ }
+ }
+ if (addEllipsis || charCnt==100) result=result.left(i)+"...";
+ return result.data();
+}
+
+//--------------------------------------------------------------------------------------
+
+static QDict<void> aliasesProcessed;
+
+static QCString expandAliasRec(const QCString s);
+
+struct Marker
+{
+ Marker(int p, int n,int s) : pos(p),number(n),size(s) {}
+ int pos; // position in the string
+ int number; // argument number
+ int size; // size of the marker
+};
+
+/** Replaces the markers in an alias definition \a aliasValue
+ * with the corresponding values found in the comma separated argument
+ * list \a argList and the returns the result after recursive alias expansion.
+ */
+static QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
+{
+ //printf("----- replaceAliasArguments(val=[%s],args=[%s])\n",aliasValue.data(),argList.data());
+
+ // first make a list of arguments from the comma separated argument list
+ QList<QCString> args;
+ args.setAutoDelete(TRUE);
+ int i,l=(int)argList.length();
+ int s=0;
+ for (i=0;i<l;i++)
+ {
+ if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\'))
+ {
+ args.append(new QCString(argList.mid(s,i-s)));
+ s=i+1; // start of next argument
+ }
+ }
+ if (l>s) args.append(new QCString(argList.right(l-s)));
+ //printf("found %d arguments\n",args.count());
+
+ // next we look for the positions of the markers and add them to a list
+ QList<Marker> markerList;
+ markerList.setAutoDelete(TRUE);
+ l = aliasValue.length();
+ int markerStart=0;
+ int markerEnd=0;
+ for (i=0;i<l;i++)
+ {
+ if (markerStart==0 && aliasValue.at(i)=='\\') // start of a \xx marker
+ {
+ markerStart=i+1;
+ }
+ else if (markerStart>0 && aliasValue.at(i)>='0' && aliasValue.at(i)<='9')
+ {
+ // read digit that make up the marker number
+ markerEnd=i+1;
+ }
+ else
+ {
+ if (markerStart>0 && markerEnd>markerStart) // end of marker
+ {
+ int markerLen = markerEnd-markerStart;
+ markerList.append(new Marker(markerStart-1, // include backslash
+ atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
+ //printf("found marker at %d with len %d and number %d\n",
+ // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
+ }
+ markerStart=0; // outside marker
+ markerEnd=0;
+ }
+ }
+ if (markerStart>0)
+ {
+ markerEnd=l;
+ }
+ if (markerStart>0 && markerEnd>markerStart)
+ {
+ int markerLen = markerEnd-markerStart;
+ markerList.append(new Marker(markerStart-1, // include backslash
+ atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
+ //printf("found marker at %d with len %d and number %d\n",
+ // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
+ }
+
+ // then we replace the markers with the corresponding arguments in one pass
+ QCString result;
+ int p=0;
+ for (i=0;i<(int)markerList.count();i++)
+ {
+ Marker *m = markerList.at(i);
+ result+=aliasValue.mid(p,m->pos-p);
+ //printf("part before marker %d: '%s'\n",i,aliasValue.mid(p,m->pos-p).data());
+ if (m->number>0 && m->number<=(int)args.count()) // valid number
+ {
+ result+=*args.at(m->number-1);
+ //printf("marker index=%d pos=%d number=%d size=%d replacement %s\n",i,m->pos,m->number,m->size,
+ // args.at(m->number-1)->data());
+ }
+ p=m->pos+m->size; // continue after the marker
+ }
+ result+=aliasValue.right(l-p); // append remainder
+ //printf("string after replacement of markers: '%s'\n",result.data());
+
+ // expand the result again
+ result = substitute(result,"\\{","{");
+ result = substitute(result,"\\}","}");
+ result = expandAliasRec(substitute(result,"\\,",","));
+
+ return result;
+}
+
+static QCString escapeCommas(const QCString &s)
+{
+ QGString result;
+ const char *p = s.data();
+ char c,pc=0;
+ while ((c=*p++))
+ {
+ if (c==',' && pc!='\\')
+ {
+ result+="\\,";
+ }
+ else
+ {
+ result+=c;
+ }
+ pc=c;
+ }
+ result+='\0';
+ //printf("escapeCommas: '%s'->'%s'\n",s.data(),result.data());
+ return result.data();
+}
+
+static QCString expandAliasRec(const QCString s)
+{
+ QCString result;
+ static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
+ QCString value=s;
+ int i,p=0,l;
+ while ((i=cmdPat.match(value,p,&l))!=-1)
+ {
+ result+=value.mid(p,i-p);
+ QCString args = extractAliasArgs(value,i+l);
+ bool hasArgs = !args.isEmpty(); // found directly after command
+ int argsLen = args.length();
+ QCString cmd = value.mid(i+1,l-1);
+ QCString cmdNoArgs = cmd;
+ int numArgs=0;
+ if (hasArgs)
+ {
+ numArgs = countAliasArguments(args);
+ cmd += QCString().sprintf("{%d}",numArgs); // alias name + {n}
+ }
+ QCString *aliasText=Doxygen::aliasDict.find(cmd);
+ if (numArgs>1 && aliasText==0)
+ { // in case there is no command with numArgs parameters, but there is a command with 1 parameter,
+ // we also accept all text as the argument of that command (so you don't have to escape commas)
+ aliasText=Doxygen::aliasDict.find(cmdNoArgs+"{1}");
+ if (aliasText)
+ {
+ cmd = cmdNoArgs+"{1}";
+ args = escapeCommas(args); // escape , so that everything is seen as one argument
+ }
+ }
+ //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s' aliasText=%s\n",
+ // s.data(),cmd.data(),numArgs,args.data(),aliasText?aliasText->data():"<none>");
+ if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias
+ {
+ //printf("is an alias!\n");
+ aliasesProcessed.insert(cmd,(void *)0x8);
+ QCString val = *aliasText;
+ if (hasArgs)
+ {
+ val = replaceAliasArguments(val,args);
+ //printf("replace '%s'->'%s' args='%s'\n",
+ // aliasText->data(),val.data(),args.data());
+ }
+ result+=expandAliasRec(val);
+ aliasesProcessed.remove(cmd);
+ p=i+l;
+ if (hasArgs) p+=argsLen+2;
+ }
+ else // command is not an alias
+ {
+ //printf("not an alias!\n");
+ result+=value.mid(i,l);
+ p=i+l;
+ }
+ }
+ result+=value.right(value.length()-p);
+
+ //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
+ return result;
+}
+
+int countAliasArguments(const QCString argList)
+{
+ int count=1;
+ int l = argList.length();
+ int i;
+ for (i=0;i<l;i++)
+ {
+ if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++;
+ }
+ return count;
+}
+
+QCString extractAliasArgs(const QCString &args,int pos)
+{
+ int i;
+ int bc=0;
+ char prevChar=0;
+ if (args.at(pos)=='{') // alias has argument
+ {
+ for (i=pos;i<(int)args.length();i++)
+ {
+ if (prevChar!='\\')
+ {
+ if (args.at(i)=='{') bc++;
+ if (args.at(i)=='}') bc--;
+ prevChar=args.at(i);
+ }
+ else
+ {
+ prevChar=0;
+ }
+
+ if (bc==0)
+ {
+ //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
+ return args.mid(pos+1,i-pos-1);
+ }
+ }
+ }
+ return "";
+}
+
+QCString resolveAliasCmd(const QCString aliasCmd)
+{
+ QCString result;
+ aliasesProcessed.clear();
+ //printf("Expanding: '%s'\n",aliasCmd.data());
+ result = expandAliasRec(aliasCmd);
+ //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
+ return result;
+}
+
+QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
+{
+ QCString result;
+ aliasesProcessed.clear();
+ // avoid expanding this command recursively
+ aliasesProcessed.insert(aliasName,(void *)0x8);
+ // expand embedded commands
+ //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
+ result = expandAliasRec(aliasValue);
+ //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
+ return result;
+}
+
+void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
+{
+ if (al==0) return;
+ ol.startConstraintList(theTranslator->trTypeConstraints());
+ ArgumentListIterator ali(*al);
+ Argument *a;
+ for (;(a=ali.current());++ali)
+ {
+ ol.startConstraintParam();
+ ol.parseText(a->name);
+ ol.endConstraintParam();
+ ol.startConstraintType();
+ linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
+ ol.endConstraintType();
+ ol.startConstraintDocs();
+ ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
+ ol.endConstraintDocs();
+ }
+ ol.endConstraintList();
+}
+
+//----------------------------------------------------------------------------
+
+void stackTrace()
+{
+#ifdef TRACINGSUPPORT
+ void *backtraceFrames[128];
+ int frameCount = backtrace(backtraceFrames, 128);
+ static char cmd[40960];
+ char *p = cmd;
+ p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
+ for (int x = 0; x < frameCount; x++)
+ {
+ p += sprintf(p,"%p ", backtraceFrames[x]);
+ }
+ fprintf(stderr,"========== STACKTRACE START ==============\n");
+ if (FILE *fp = popen(cmd, "r"))
+ {
+ char resBuf[512];
+ while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
+ {
+ fwrite(resBuf, 1, len, stderr);
+ }
+ pclose(fp);
+ }
+ fprintf(stderr,"============ STACKTRACE END ==============\n");
+ //fprintf(stderr,"%s\n", frameStrings[x]);
+#endif
+}
+
+static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size,
+ const char *inputEncoding,const char *outputEncoding)
+{
+ if (inputEncoding==0 || outputEncoding==0) return size;
+ if (qstricmp(inputEncoding,outputEncoding)==0) return size;
+ void *cd = portable_iconv_open(outputEncoding,inputEncoding);
+ if (cd==(void *)(-1))
+ {
+ err("error: unsupported character conversion: '%s'->'%s': %s\n"
+ "Check the INPUT_ENCODING setting in the config file!\n",
+ inputEncoding,outputEncoding,strerror(errno));
+ exit(1);
+ }
+ int tmpBufSize=size*4+1;
+ BufStr tmpBuf(tmpBufSize);
+ size_t iLeft=size;
+ size_t oLeft=tmpBufSize;
+ char *srcPtr = srcBuf.data();
+ char *dstPtr = tmpBuf.data();
+ uint newSize=0;
+ if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
+ {
+ newSize = tmpBufSize-oLeft;
+ srcBuf.shrink(newSize);
+ strncpy(srcBuf.data(),tmpBuf.data(),newSize);
+ //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
+ }
+ else
+ {
+ err("%s: error: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
+ fileName,inputEncoding,outputEncoding);
+ exit(1);
+ }
+ portable_iconv_close(cd);
+ return newSize;
+}
+
+//! read a file name \a fileName and optionally filter and transcode it
+bool readInputFile(const char *fileName,BufStr &inBuf)
+{
+ // try to open file
+ int size=0;
+ //uint oldPos = dest.curPos();
+ //printf(".......oldPos=%d\n",oldPos);
+
+ QFileInfo fi(fileName);
+ if (!fi.exists()) return FALSE;
+ QCString filterName = getFileFilter(fileName,FALSE);
+ if (filterName.isEmpty())
+ {
+ QFile f(fileName);
+ if (!f.open(IO_ReadOnly))
+ {
+ err("error: could not open file %s\n",fileName);
+ return FALSE;
+ }
+ size=fi.size();
+ // read the file
+ inBuf.skip(size);
+ if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
+ {
+ err("error: problems while reading file %s\n",fileName);
+ return FALSE;
+ }
+ }
+ else
+ {
+ QCString cmd=filterName+" \""+fileName+"\"";
+ Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
+ FILE *f=portable_popen(cmd,"r");
+ if (!f)
+ {
+ err("error: could not execute filter %s\n",filterName.data());
+ return FALSE;
+ }
+ const int bufSize=1024;
+ char buf[bufSize];
+ int numRead;
+ while ((numRead=fread(buf,1,bufSize,f))>0)
+ {
+ //printf(">>>>>>>>Reading %d bytes\n",numRead);
+ inBuf.addArray(buf,numRead),size+=numRead;
+ }
+ portable_pclose(f);
+ inBuf.at(inBuf.curPos()) ='\0';
+ Debug::print(Debug::FilterOutput, 0, "Filter output\n");
+ Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",inBuf.data());
+ }
+
+ int start=0;
+ if (size>=2 &&
+ ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM
+ (inBuf.at(0)==-2 && inBuf.at(1)==-1) // big endian BOM
+ )
+ ) // UCS-2 encoded file
+ {
+ transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
+ "UCS-2","UTF-8");
+ }
+ else if (size>=3 &&
+ (uchar)inBuf.at(0)==0xEF &&
+ (uchar)inBuf.at(1)==0xBB &&
+ (uchar)inBuf.at(2)==0xBF
+ ) // UTF-8 encoded file
+ {
+ inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
+ }
+ else // transcode according to the INPUT_ENCODING setting
+ {
+ // do character transcoding if needed.
+ transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
+ Config_getString("INPUT_ENCODING"),"UTF-8");
+ }
+
+ inBuf.addChar('\n'); /* to prevent problems under Windows ? */
+
+ // and translate CR's
+ size=inBuf.curPos()-start;
+ int newSize=filterCRLF(inBuf.data()+start,size);
+ //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
+ if (newSize!=size) // we removed chars
+ {
+ inBuf.shrink(newSize); // resize the array
+ //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
+ }
+ inBuf.at(inBuf.curPos())='\0';
+ return TRUE;
+}
+
+// Replace %word by word in title
+QCString filterTitle(const QCString &title)
+{
+ QCString tf;
+ static QRegExp re("%[A-Z_a-z]");
+ int p=0,i,l;
+ while ((i=re.match(title,p,&l))!=-1)
+ {
+ tf+=title.mid(p,i-p);
+ tf+=title.mid(i+1,l-1); // skip %
+ p=i+l;
+ }
+ tf+=title.right(title.length()-p);
+ return tf;
+}
+
+//----------------------------------------------------------------------------
+// returns TRUE if the name of the file represented by `fi' matches
+// one of the file patterns in the `patList' list.
+
+bool patternMatch(const QFileInfo &fi,const QStrList *patList)
+{
+ bool found=FALSE;
+ if (patList)
+ {
+ QStrListIterator it(*patList);
+ QCString pattern;
+ for (it.toFirst();(pattern=it.current());++it)
+ {
+ if (!pattern.isEmpty() && !found)
+ {
+ int i=pattern.find('=');
+ if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
+
+#if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
+ QRegExp re(pattern,FALSE,TRUE); // case insensitive match
+#else // unix
+ QRegExp re(pattern,TRUE,TRUE); // case sensitive match
+#endif
+ found = found || re.match(fi.fileName().data())!=-1 ||
+ re.match(fi.filePath().data())!=-1 ||
+ re.match(fi.absFilePath().data())!=-1;
+ //printf("Matching `%s' against pattern `%s' found=%d\n",
+ // fi->fileName().data(),pattern.data(),found);
+ }
+ }
+ }
+ return found;
+}
+
+#if 0 // move to HtmlGenerator::writeSummaryLink
+void writeSummaryLink(OutputList &ol,const char *label,const char *title,
+ bool &first,const char *file)
+{
+ if (first)
+ {
+ ol.writeString(" <div class=\"summary\">\n");
+ first=FALSE;
+ }
+ else
+ {
+ ol.writeString(" &#124;\n");
+ }
+ if (file)
+ {
+ ol.writeString("<a href=\"");
+ ol.writeString(file);
+ ol.writeString(Doxygen::htmlFileExtension);
+ }
+ else
+ {
+ ol.writeString("<a href=\"#");
+ ol.writeString(label);
+ }
+ ol.writeString("\">");
+ ol.writeString(title);
+ ol.writeString("</a>");
+}
+#endif
+
+QCString externalLinkTarget()
+{
+ static bool extLinksInWindow = Config_getBool("EXT_LINKS_IN_WINDOW");
+ if (extLinksInWindow) return "target=\"_blank\" "; else return "";
+}
+
+QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
+{
+ QCString result;
+ if (!ref.isEmpty())
+ {
+ QCString *dest = Doxygen::tagDestinationDict[ref];
+ if (dest)
+ {
+ result = *dest;
+ int l = result.length();
+ if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
+ { // relative path -> prepend relPath.
+ result.prepend(relPath);
+ }
+ if (!href) result.prepend("doxygen=\""+ref+":");
+ if (l>0 && result.at(l-1)!='/') result+='/';
+ if (!href) result.append("\" ");
+ }
+ }
+ else
+ {
+ result = relPath;
+ }
+ return result;
+}
+
+/** Writes the intensity only bitmap representated by \a data as an image to
+ * directory \a dir using the colors defined by HTML_COLORSTYLE_*.
+ */
+void writeColoredImgData(const char *dir,ColoredImgDataItem data[])
+{
+ static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
+ static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
+ static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
+ while (data->name)
+ {
+ QCString fileName;
+ fileName=(QCString)dir+"/"+data->name;
+ QFile f(fileName);
+ if (f.open(IO_WriteOnly))
+ {
+ ColoredImage img(data->width,data->height,data->content,data->alpha,
+ sat,hue,gamma);
+ img.save(fileName);
+ }
+ else
+ {
+ fprintf(stderr,"Warning: Cannot open file %s for writing\n",data->name);
+ }
+ Doxygen::indexList.addImageFile(data->name);
+ data++;
+ }
+}
+
+/** Replaces any markers of the form \#\#AA in input string \a str
+ * by new markers of the form \#AABBCC, where \#AABBCC represents a
+ * valid color, based on the intensity represented by hex number AA
+ * and the current HTML_COLORSTYLE_* settings.
+ */
+QCString replaceColorMarkers(const char *str)
+{
+ QCString result;
+ QCString s=str;
+ if (s.isEmpty()) return result;
+ static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]");
+ static const char hex[] = "0123456789ABCDEF";
+ static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
+ static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
+ static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
+ int i,l,sl=s.length(),p=0;
+ while ((i=re.match(s,p,&l))!=-1)
+ {
+ result+=s.mid(p,i-p);
+ QCString lumStr = s.mid(i+2,l-2);
+#define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
+ ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
+ ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
+
+ double r,g,b;
+ int red,green,blue;
+ int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
+ ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
+ pow(level/255.0,gamma/100.0),&r,&g,&b);
+ red = (int)(r*255.0);
+ green = (int)(g*255.0);
+ blue = (int)(b*255.0);
+ char colStr[8];
+ colStr[0]='#';
+ colStr[1]=hex[red>>4];
+ colStr[2]=hex[red&0xf];
+ colStr[3]=hex[green>>4];
+ colStr[4]=hex[green&0xf];
+ colStr[5]=hex[blue>>4];
+ colStr[6]=hex[blue&0xf];
+ colStr[7]=0;
+ //printf("replacing %s->%s (level=%d)\n",lumStr.data(),colStr,level);
+ result+=colStr;
+ p=i+l;
+ }
+ result+=s.right(sl-p);
+ return result;
+}
+
+/** Copies the contents of file with name \a src to the newly created
+ * file with name \a dest. Returns TRUE if successful.
+ */
+bool copyFile(const QCString &src,const QCString &dest)
+{
+ QFile sf(src);
+ if (sf.open(IO_ReadOnly))
+ {
+ QFileInfo fi(src);
+ QFile df(dest);
+ if (df.open(IO_WriteOnly))
+ {
+ char *buffer = new char[fi.size()];
+ sf.readBlock(buffer,fi.size());
+ df.writeBlock(buffer,fi.size());
+ df.flush();
+ delete[] buffer;
+ }
+ else
+ {
+ err("error: could not write to file %s\n",dest.data());
+ return FALSE;
+ }
+ }
+ else
+ {
+ err("error: could not open user specified file %s\n",src.data());
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/** Returns the section of text, in between a pair of markers.
+ * Full lines are returned, excluding the lines on which the markers appear.
+ */
+QCString extractBlock(const QCString text,const QCString marker)
+{
+ QCString result;
+ int p=0,i;
+ bool found=FALSE;
+
+ // find the character positions of the markers
+ int m1 = text.find(marker);
+ if (m1==-1) return result;
+ int m2 = text.find(marker,m1+marker.length());
+ if (m2==-1) return result;
+
+ // find start and end line positions for the markers
+ int l1=-1,l2=-1;
+ while (!found && (i=text.find('\n',p))!=-1)
+ {
+ found = (p<=m1 && m1<i); // found the line with the start marker
+ p=i+1;
+ }
+ l1=p;
+ if (found)
+ {
+ while ((i=text.find('\n',p))!=-1)
+ {
+ if (p<=m2 && m2<i) // found the line with the end marker
+ {
+ l2=p;
+ break;
+ }
+ p=i+1;
+ }
+ }
+ //printf("text=[%s]\n",text.mid(l1,l2-l1).data());
+ return text.mid(l1,l2-l1);
+}
+
+/** Returns a string representation of \a lang. */
+QCString langToString(SrcLangExt lang)
+{
+ switch(lang)
+ {
+ case SrcLangExt_Unknown: return "Unknown";
+ case SrcLangExt_IDL: return "IDL";
+ case SrcLangExt_Java: return "Java";
+ case SrcLangExt_CSharp: return "C#";
+ case SrcLangExt_D: return "D";
+ case SrcLangExt_PHP: return "PHP";
+ case SrcLangExt_ObjC: return "Objective-C";
+ case SrcLangExt_Cpp: return "C++";
+ case SrcLangExt_JS: return "Javascript";
+ case SrcLangExt_Python: return "Python";
+ case SrcLangExt_Fortran: return "Fortran";
+ case SrcLangExt_VHDL: return "VHDL";
+ case SrcLangExt_XML: return "XML";
+ case SrcLangExt_Tcl: return "Tcl";
+ case SrcLangExt_Markdown: return "Markdown";
+ }
+ return "Unknown";
+}
+
+/** Returns the scope separator to use given the programming language \a lang */
+QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope)
+{
+ if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp || lang==SrcLangExt_VHDL || lang==SrcLangExt_Python)
+ {
+ return ".";
+ }
+ else if (lang==SrcLangExt_PHP && !classScope)
+ {
+ return "\\";
+ }
+ else
+ {
+ return "::";
+ }
+}
+
+/** Corrects URL \a url according to the relative path \a relPath.
+ * Returns the corrected URL. For absolute URLs no correction will be done.
+ */
+QCString correctURL(const QCString &url,const QCString &relPath)
+{
+ QCString result = url;
+ if (!relPath.isEmpty() &&
+ url.left(5)!="http:" && url.left(6)!="https:" &&
+ url.left(4)!="ftp:" && url.left(5)!="file:")
+ {
+ result.prepend(relPath);
+ }
+ return result;
+}
+
+//---------------------------------------------------------------------------
+
+bool protectionLevelVisible(Protection prot)
+{
+ static bool extractPrivate = Config_getBool("EXTRACT_PRIVATE");
+ static bool extractPackage = Config_getBool("EXTRACT_PACKAGE");
+
+ return (prot!=Private && prot!=Package) ||
+ (prot==Private && extractPrivate) ||
+ (prot==Package && extractPackage);
+}
+
+//---------------------------------------------------------------------------
+
+QCString stripIndentation(const QCString &s)
+{
+ if (s.isEmpty()) return s; // empty string -> we're done
+
+ //printf("stripIndentation:\n%s\n------\n",s.data());
+ // compute minimum indentation over all lines
+ const char *p=s.data();
+ char c;
+ int indent=0;
+ int minIndent=1000000; // "infinite"
+ bool searchIndent=TRUE;
+ static int tabSize=Config_getInt("TAB_SIZE");
+ while ((c=*p++))
+ {
+ if (c=='\t') indent+=tabSize - (indent%tabSize);
+ else if (c=='\n') indent=0,searchIndent=TRUE;
+ else if (c==' ') indent++;
+ else if (searchIndent)
+ {
+ searchIndent=FALSE;
+ if (indent<minIndent) minIndent=indent;
+ }
+ }
+
+ // no indent to remove -> we're done
+ if (minIndent==0) return s;
+
+ // remove minimum indentation for each line
+ QGString result;
+ p=s.data();
+ indent=0;
+ while ((c=*p++))
+ {
+ if (c=='\n') // start of new line
+ {
+ indent=0;
+ result+=c;
+ }
+ else if (indent<minIndent) // skip until we reach minIndent
+ {
+ if (c=='\t')
+ {
+ int newIndent = indent+tabSize-(indent%tabSize);
+ int i=newIndent;
+ while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
+ {
+ result+=' ';
+ i--;
+ }
+ indent=newIndent;
+ }
+ else // space
+ {
+ indent++;
+ }
+ }
+ else // copy anything until the end of the line
+ {
+ result+=c;
+ }
+ }
+
+ result+='\0';
+ return result.data();
+}
+
+
+bool fileVisibleInIndex(FileDef *fd,bool &genSourceFile)
+{
+ static bool allExternals = Config_getBool("ALLEXTERNALS");
+ bool isDocFile = fd->isDocumentationFile();
+ genSourceFile = !isDocFile && fd->generateSourceFile();
+ return ( ((allExternals && fd->isLinkable()) ||
+ fd->isLinkableInProject()
+ ) &&
+ !isDocFile
+ );
+}
+
+