diff options
author | dimitri <dimitri@afe2bf4a-e733-0410-8a33-86f594647bc7> | 2012-03-22 19:19:06 (GMT) |
---|---|---|
committer | dimitri <dimitri@afe2bf4a-e733-0410-8a33-86f594647bc7> | 2012-03-22 19:19:06 (GMT) |
commit | e620712c9dd41c56bbd56d16a5a3469b96fafbf0 (patch) | |
tree | 2a7451abfd771d43ea2b3ac2443d94c3eb5cfee7 /trunk/src/util.cpp | |
parent | 8f455b66da9db238655242d1213c05affac412d9 (diff) | |
download | Doxygen-Release_1_8_0.zip Doxygen-Release_1_8_0.tar.gz Doxygen-Release_1_8_0.tar.bz2 |
Created accidentallyRelease_1_8_0
Diffstat (limited to 'trunk/src/util.cpp')
-rw-r--r-- | trunk/src/util.cpp | 7361 |
1 files changed, 0 insertions, 7361 deletions
diff --git a/trunk/src/util.cpp b/trunk/src/util.cpp deleted file mode 100644 index 41b2991..0000000 --- a/trunk/src/util.cpp +++ /dev/null @@ -1,7361 +0,0 @@ -/***************************************************************************** - * - * - * - * 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(); - QString 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 *name) -{ - if (name==0 || name[0]=='\0') return 0; - return Doxygen::classSDict->find(name); -} - -NamespaceDef *getResolvedNamespace(const char *name) -{ - if (name==0 || name[0]=='\0') return 0; - QCString *subst = Doxygen::namespaceAliasDict[name]; - if (subst) - { - int count=0; // recursion detection guard - QCString *newSubst; - while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10) - { - subst=newSubst; - count++; - } - if (count==10) - { - 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; - -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: - 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); - 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(' '); - } - } - 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,const char *, - 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\n", - // scope?scope->name().data():"<none>", - // fileScope?fileScope->name().data():"<none>", - // txtStr.data(),strLen); - int matchLen; - int index=0; - int newIndex; - int skipIndex=0; - int floatingIndex=0; - if (strLen==0) return; - // read a word from the text string - while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 && - (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers - ) - { - // add non-word part to the result - floatingIndex+=newIndex-skipIndex+matchLen; - bool insideString=FALSE; - int i; - for (i=index;i<newIndex;i++) - { - if (txtStr.at(i)=='"') insideString=!insideString; - } - - //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak); - if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point - { - QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex); - int splitLength = splitText.length(); - int offset=1; - i=splitText.find(regExpSplit,0); - if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; } - if (i==-1) i=splitText.find('>'); - if (i==-1) i=splitText.find(' '); - //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset); - if (i!=-1) // add a link-break at i in case of Html output - { - out.writeString(splitText.left(i+offset),keepSpaces); - out.writeBreak(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()) - { - out.writeLink(typeDef->getReference(), - typeDef->getOutputFileBase(), - typeDef->anchor(), - word); - found=TRUE; - } - } - if (!found && cd) - { - //printf("Found class %s\n",cd->name().data()); - // add link to the result - if (external ? cd->isLinkable() : cd->isLinkableInProject()) - { - out.writeLink(cd->getReference(),cd->getOutputFileBase(),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()) - { - 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()) - { - 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); - 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"; - 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; - const char *inputPtr = input.data(); - char *outputPtr = output.data(); - if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft)) - { - outputSize-=oLeft; - output.resize(outputSize+1); - output.at(outputSize)='\0'; - //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data()); - } - else - { - err("error: failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n", - inputEncoding.data(),outputEncoding,input.data()); - error=TRUE; - } - } - portable_iconv_close(cd); - return error ? input : output; -} - -/*! reads a file with name \a name and returns it as a string. If \a filter - * is TRUE the file will be filtered by any user specified input filter. - * If \a name is "-" the string will be read from standard input. - */ -QCString fileToString(const char *name,bool filter,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()) - { -#if 0 - BaseClassListIterator bcli(*cd->baseClasses()); - for ( ; bcli.current() ; ++bcli) - { - //printf("class %s base class %s\n",cd->name().data(),bcli.current()->classDef->name().data()); - int mc=minClassDistance(bcli.current()->classDef,bcd,level+1); - if (mc<m) m=mc; - if (m<0) break; - } -#endif - 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; -} - -//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()+"/")) - && 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 - -//---------------------------------------------------------------------- - -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; - 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()); - 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("<"); break; - case '>': growBuf.addStr(">"); break; - case '&': growBuf.addStr("&"); break; - case '\'': growBuf.addStr("'"); break; - case '"': growBuf.addStr("""); 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("<"); break; - case '>': growBuf.addStr(">"); 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("&"); - } - } - else - { - growBuf.addStr("&"); - } - break; - case '\'': growBuf.addStr("'"); break; - case '"': growBuf.addStr("""); 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(67); - static bool init=TRUE; - QCString result; - static QRegExp entityPat("&[a-zA-Z]+;"); - - if (init) - { - entityMap.insert("copy", "\xC2\xA9"); - entityMap.insert("tm", "\xE2\x84\xA2"); - entityMap.insert("trade", "\xE2\x84\xA2"); - entityMap.insert("reg", "\xC2\xAE"); - entityMap.insert("lsquo", "\xE2\x80\x98"); - entityMap.insert("rsquo", "\xE2\x80\x99"); - entityMap.insert("ldquo", "\xE2\x80\x9C"); - entityMap.insert("rdquo", "\xE2\x80\x9D"); - entityMap.insert("ndash", "\xE2\x80\x93"); - entityMap.insert("mdash", "\xE2\x80\x94"); - entityMap.insert("Auml", "\xC3\x84"); - entityMap.insert("Euml", "\xC3\x8B"); - entityMap.insert("Iuml", "\xC3\x8F"); - entityMap.insert("Ouml", "\xC3\x96"); - entityMap.insert("Uuml", "\xC3\x9C"); - entityMap.insert("Yuml", "\xC5\xB8"); - entityMap.insert("auml", "\xC3\xA4"); - entityMap.insert("euml", "\xC3\xAB"); - entityMap.insert("iuml", "\xC3\xAF"); - entityMap.insert("ouml", "\xC3\xB6"); - entityMap.insert("uuml", "\xC3\xBC"); - entityMap.insert("yuml", "\xC3\xBF"); - entityMap.insert("Aacute","\xC3\x81"); - entityMap.insert("Eacute","\xC3\x89"); - entityMap.insert("Iacute","\xC3\x8D"); - entityMap.insert("Oacute","\xC3\x93"); - entityMap.insert("Uacute","\xC3\x9A"); - entityMap.insert("aacute","\xC3\xA1"); - entityMap.insert("eacute","\xC3\xA9"); - entityMap.insert("iacute","\xC3\xAD"); - entityMap.insert("oacute","\xC3\xB3"); - entityMap.insert("uacute","\xC3\xBA"); - entityMap.insert("Agrave","\xC3\x80"); - entityMap.insert("Egrave","\xC3\x88"); - entityMap.insert("Igrave","\xC3\x8C"); - entityMap.insert("Ograve","\xC3\x92"); - entityMap.insert("Ugrave","\xC3\x99"); - entityMap.insert("agrave","\xC3\xA0"); - entityMap.insert("egrave","\xC3\xA8"); - entityMap.insert("igrave","\xC3\xAC"); - entityMap.insert("ograve","\xC3\xB2"); - entityMap.insert("ugrave","\xC3\xB9"); - entityMap.insert("Acirc", "\xC3\x82"); - entityMap.insert("Ecirc", "\xC3\x8A"); - entityMap.insert("Icirc", "\xC3\x8E"); - entityMap.insert("Ocirc", "\xC3\x94"); - entityMap.insert("Ucirc", "\xC3\x9B"); - entityMap.insert("acirc", "\xC3\xA2"); - entityMap.insert("ecirc", "\xC3\xAA"); - entityMap.insert("icirc", "\xC3\xAE"); - entityMap.insert("ocirc", "\xC3\xB4"); - entityMap.insert("ucirc", "\xC3\xBB"); - entityMap.insert("Atilde","\xC3\x83"); - entityMap.insert("Ntilde","\xC3\x91"); - entityMap.insert("Otilde","\xC3\x95"); - entityMap.insert("atilde","\xC3\xA3"); - entityMap.insert("ntilde","\xC3\xB1"); - entityMap.insert("otilde","\xC3\xB5"); - entityMap.insert("szlig", "\xC3\x9F"); - entityMap.insert("Ccedil","\xC3\x87"); - entityMap.insert("ccedil","\xC3\xA7"); - entityMap.insert("Aring", "\xC3\x85"); - entityMap.insert("aring", "\xC3\xA5"); - entityMap.insert("nbsp", "\xC2\xA0"); - init=FALSE; - } - - if (s==0) return result; - int p,i=0,l; - while ((p=entityPat.match(s,i,&l))!=-1) - { - if (p>i) result+=s.mid(i,p-i); - QCString entity = s.mid(p+1,l-2); - char *code = entityMap.find(entity); - if (code) - { - result+=code; - } - else - { - result+=s.mid(p,l); - } - i=p+l; - } - result+=s.mid(i,s.length()-i); - return result; -} - -/*! Returns the standard string that is generated when the \\overload - * command is used. - */ -QCString getOverloadDocs() -{ - return theTranslator->trOverloadText(); - //"This is an overloaded member function, " - // "provided for convenience. It differs from the above " - // "function only in what argument(s) it accepts."; -} - -void addMembersToMemberGroup(MemberList *ml, - MemberGroupSDict **ppMemberGroupSDict, - Definition *context) -{ - ASSERT(context!=0); - //printf("addMemberToMemberGroup()\n"); - if (ml==0) return; - MemberListIterator mli(*ml); - MemberDef *md; - uint index; - for (index=0;(md=mli.current());) - { - if (md->isEnumerate()) // insert enum value of this enum into groups - { - LockingPtr<MemberList> fmdl=md->enumFieldList(); - if (fmdl!=0) - { - MemberDef *fmd=fmdl->first(); - while (fmd) - { - int groupId=fmd->getMemberGroupId(); - if (groupId!=-1) - { - MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId]; - //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId]; - //QCString *pDocs = Doxygen::memberDocDict[groupId]; - if (info) - { - if (*ppMemberGroupSDict==0) - { - *ppMemberGroupSDict = new MemberGroupSDict; - (*ppMemberGroupSDict)->setAutoDelete(TRUE); - } - MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); - if (mg==0) - { - mg = new MemberGroup( - context, - groupId, - info->header, - info->doc, - info->docFile - ); - (*ppMemberGroupSDict)->append(groupId,mg); - } - mg->insertMember(fmd); // insert in member group - fmd->setMemberGroup(mg); - } - } - fmd=fmdl->next(); - } - } - } - int groupId=md->getMemberGroupId(); - if (groupId!=-1) - { - MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId]; - //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId]; - //QCString *pDocs = Doxygen::memberDocDict[groupId]; - if (info) - { - if (*ppMemberGroupSDict==0) - { - *ppMemberGroupSDict = new MemberGroupSDict; - (*ppMemberGroupSDict)->setAutoDelete(TRUE); - } - MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); - if (mg==0) - { - mg = new MemberGroup( - context, - groupId, - info->header, - info->doc, - info->docFile - ); - (*ppMemberGroupSDict)->append(groupId,mg); - } - md = ml->take(index); // remove from member list - mg->insertMember(md); // insert in member group - mg->setRefItems(info->m_sli); - md->setMemberGroup(mg); - continue; - } - } - ++mli;++index; - } -} - -/*! Extracts a (sub-)string from \a type starting at \a pos that - * could form a class. The index of the match is returned and the found - * class \a name and a template argument list \a templSpec. If -1 is returned - * there are no more matches. - */ -int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,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 argument list \a src. Will allocate memory, that - * is owned by the caller. - */ -ArgumentList *copyArgumentList(const ArgumentList *src) -{ - ASSERT(src!=0); - ArgumentList *dst = new ArgumentList; - dst->setAutoDelete(TRUE); - ArgumentListIterator tali(*src); - Argument *a; - for (;(a=tali.current());++tali) - { - dst->append(new Argument(*a)); - } - dst->constSpecifier = src->constSpecifier; - dst->volatileSpecifier = src->volatileSpecifier; - dst->pureSpecifier = src->pureSpecifier; - return dst; -} - -/*! Makes a deep copy of the list of argument lists \a srcLists. - * Will allocate memory, that is owned by the caller. - */ -QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists) -{ - ASSERT(srcLists!=0); - QList<ArgumentList> *dstLists = new QList<ArgumentList>; - dstLists->setAutoDelete(TRUE); - QListIterator<ArgumentList> sli(*srcLists); - ArgumentList *sl; - for (;(sl=sli.current());++sli) - { - dstLists->append(copyArgumentList(sl)); - } - return dstLists; -} - -/*! Strips template specifiers from scope \a fullName, except those - * that make up specialized classes. The switch \a parentOnly - * determines whether or not a template "at the end" of a scope - * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will - * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will - * strip both unless A<T> or B<S> are specialized template classes. - */ -QCString stripTemplateSpecifiersFromScope(const QCString &fullName, - bool parentOnly, - QCString *pLastScopeStripped) -{ - QCString result; - int p=0; - int l=fullName.length(); - int i=fullName.find('<'); - while (i!=-1) - { - //printf("1:result+=%s\n",fullName.mid(p,i-p).data()); - int e=i+1; - bool done=FALSE; - int count=1; - while (e<l && !done) - { - char c=fullName.at(e++); - if (c=='<') - { - count++; - } - else if (c=='>') - { - count--; - done = count==0; - } - } - int si= fullName.find("::",e); - - if (parentOnly && si==-1) break; - // we only do the parent scope, so we stop here if needed - - result+=fullName.mid(p,i-p); - //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data()); - if (getClass(result+fullName.mid(i,e-i))!=0) - { - result+=fullName.mid(i,e-i); - //printf(" 2:result+=%s\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(" | "); } 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 << "$\\backslash$"; } - 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 = 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(); -} - -bool usingTreeIndex() -{ - static bool treeView = Config_getBool("USE_INLINE_TREES"); - return treeView; -} - -void stackTrace() -{ -#ifdef TRACINGSUPPORT - void *backtraceFrames[128]; - int frameCount = backtrace(backtraceFrames, 128); - static char cmd[40960]; - char *p = cmd; - p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid()); - for (int x = 0; x < frameCount; x++) - { - p += sprintf(p,"%p ", backtraceFrames[x]); - } - fprintf(stderr,"========== STACKTRACE START ==============\n"); - if (FILE *fp = popen(cmd, "r")) - { - char resBuf[512]; - while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp)) - { - fwrite(resBuf, 1, len, stderr); - } - pclose(fp); - } - fprintf(stderr,"============ STACKTRACE END ==============\n"); - //fprintf(stderr,"%s\n", frameStrings[x]); -#endif -} - -static int transcodeCharacterBuffer(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; - const char *srcPtr = srcBuf.data(); - char *dstPtr = tmpBuf.data(); - uint newSize=0; - if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft)) - { - newSize = tmpBufSize-oLeft; - srcBuf.shrink(newSize); - strncpy(srcBuf.data(),tmpBuf.data(),newSize); - //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data()); - } - else - { - err("%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; -} - -void writeSummaryLink(OutputList &ol,const char *label,const char *title, - bool &first) -{ - if (first) - { - ol.writeString(" <div class=\"summary\">\n"); - first=FALSE; - } - else - { - ol.writeString(" |\n"); - } - ol.writeString("<a href=\"#"); - ol.writeString(label); - ol.writeString("\">"); - ol.writeString(title); - ol.writeString("</a>"); -} - -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); -} - |