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