/***************************************************************************** * * * * Copyright (C) 1997-2008 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 #include #include #include #include "qtbc.h" #include #include #include #include #include #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" //------------------------------------------------------------------------ // 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(); 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 && 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" ) 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("<name().data() : "",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():""); // 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; 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*/) { // look if resScope is visible within tmd->getOuterScope() Definition *d = tmd->getOuterScope(); while (d && d!=resScope) d=d->getOuterScope(); if (d) { md=tmd; } } } } } 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() : ""); } 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 g_resolvedTypedefs; static QDict 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():"", // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():""); 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 (spgetOuterScope(), 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():"",sp,ip,tl); } else if (si!=-1) // A::B { i=type.find('<',si); if (i==-1) // Something like A::B => lookup A::B { i=type.length(); } else // Something like A::B => lookup A::B, spec= { 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 (ipappend(typedefValue.right(tl-ip-1)); } else { *pResolvedType=typedefValue; } } // remember computed value for next time if (result && result->getDefFileName()!="") // 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():""); 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 && distancedefinitionType()==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():"", // name.data(),result.data()); return result; } static Definition *endOfPathIsUsedClass(SDict *cl,const QCString &localName) { if (cl) { SDict::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():"",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():""); 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():""); return current; // path could be followed } bool accessibleViaUsingClass(const SDict *cl, FileDef *fileScope, Definition *item, const QCString &explicitScopePart="" ) { if (cl) // see if the class was imported via a using statement { SDict::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 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("name().data(),item->name().data(),item->getOuterScope()->name().data()); QCString key(40); key.sprintf("%p:%p:%p",scope,fileScope,item); static QDict 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 *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 *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 visitedDict; if (visitedDict.find(key)) { //printf("Already visited!\n"); return -1; // already looked at this } visitedDict.insert(key,(void *)0x8); //printf(" name().data():"", // item?item->name().data():"", // 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 *cl = nscope->getUsedClasses(); if (cl) { SDict::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 (distancequalifiedName(); } 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 (distancequalifiedName(); } 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 (distancequalifiedName(); } } } } // 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():"",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():""); //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():"",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():"", // fileScope?fileScope->name().data():"", // 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():"", // n,result?result->name().data():""); 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 { 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=resultLen) { resultLen+=1024; result.resize(resultLen); } \ result[resultPos++]=(c) uint i; uint l=s.length(); uint csp=0; uint vsp=0; for (i=0;i0 || // 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 (i0 && 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)) && ((i0 && ((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' || s.at(i-1)==']') && (isId(s.at(i+1)) || (i0 && (isId(result.at(rl-1)) || result.at(rl-1)=='>')) && (c!='*' || !findOperator2(s,i)) // avoid splitting operator* and operator->* ) { ADD_CHAR(' '); } } ADD_CHAR(c); if (cliSupport && (c=='^' || c=='%') && i>1 && isId(s.at(i-1))) { 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():"", // fileScope?fileScope->name().data():"", // 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;i35 && 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():""); 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():""); //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'%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; } QCString transcodeCharacterStringToUTF8(const QCString &input) { 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); exit(1); } 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\n", inputEncoding.data(),outputEncoding); exit(1); } portable_iconv_close(cd); return 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) { 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; } //QDate date=dt.date(); //QTime time=dt.time(); //QCString dtString; //dtString.sprintf("%02d:%02d, %04d/%02d/%02d", // time.hour(),time.minute(),date.year(),date.month(),date.day()); //return dtString; } 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 (mctype.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') { 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 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 *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::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 if no then there is no match if (!srcAName.isEmpty() || !dstAName.isEmpty()) { NOMATCH return FALSE; } // types only while (srcPos no match } } else // maybe dst has a name while src has not { dstPos++; while (dstPos no match } } } else if (srcPos no match } } else // maybe src has a name while dst has not { srcPos++; while (srcPos 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 *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 against list 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():"",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():"", // d?d->name().data():"", // fs?fs->name().data():"", // 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():"", // templSpec.data(),ts.data(), // tSpec?tSpec->data():"", // cd?cd->isTemplate():FALSE, // resolvedType.data()); //printf(" mtype=%s\n",mType?mType->name().data():""); if (cd) // resolves to a known class type { 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() : "",fs ? fs->name().data() : ""); static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*"); QCString canType,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); canType += getCanonicalTypeForIdentifier(d,fs,word,&templSpec); //printf(" word=%s templSpec=%s canType=%s\n",word.data(),templSpec.data(),canType.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()); } } /*! * 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 ) { fd=0, md=0, cd=0, nd=0, gd=0; if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ QCString scopeName=scName; //printf("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("result after scope corrections scope=%s name=%s\n", // scopeName.data(),memberName.data()); QCString mName=memberName; QCString mScope; if (memberName.left(9)!="operator " && // treat operator conversion methods // as a special case (im=memberName.findRev("::"))!=-1 && im<(int)memberName.length()-2 // not A:: ) { mScope=memberName.left(im); mName=memberName.right(memberName.length()-im-2); } // handle special the case where both scope name and member scope are equal if (mScope==scopeName) scopeName.resize(0); //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data()); MemberName *mn = Doxygen::memberNameSDict->find(mName); //printf("mName=%s mn=%p\n",mName.data(),mn); if (!forceEmptyScope && mn && !(scopeName.isEmpty() && mScope.isEmpty())) { //printf(" >member name found\n"); int scopeOffset=scopeName.length(); do { QCString className = scopeName.left(scopeOffset); if (!className.isEmpty() && !mScope.isEmpty()) { className+="::"+mScope; } else if (!mScope.isEmpty()) { className=mScope.copy(); } //printf("Trying class scope %s\n",className.data()); ClassDef *fcd=0; // todo: fill in correct fileScope! if ((fcd=getResolvedClass(Doxygen::globalScope,0,className)) && // is it a documented class fcd->isLinkable() ) { //printf(" Found fcd=%p\n",fcd); MemberListIterator mmli(*mn); MemberDef *mmd; int mdist=maxInheritanceDepth; ArgumentList *argList=0; if (args) { argList=new ArgumentList; stringToArgumentList(args,argList); } for (mmli.toFirst();(mmd=mmli.current());++mmli) { //if (mmd->isLinkable()) //{ LockingPtr mmdAl = mmd->argumentList(); bool match=args==0 || matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), fcd,fcd->getFileDef(),argList, checkCV ); //printf("match=%d\n",match); if (match) { ClassDef *mcd=mmd->getClassDef(); if (mcd) { int m=minClassDistance(fcd,mcd); if (misLinkable()) { mdist=m; cd=mcd; md=mmd; } } } //} } if (argList) { delete argList; argList=0; } if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0) // no exact match found, but if args="()" an arbitrary member will do { //printf(" >Searching for arbitrary member\n"); for (mmli.toFirst();(mmd=mmli.current());++mmli) { //if (mmd->isLinkable()) //{ ClassDef *mcd=mmd->getClassDef(); //printf(" >Class %s found\n",mcd->name().data()); if (mcd) { int m=minClassDistance(fcd,mcd); if (misLinkable()*/ ) { //printf("Class distance %d\n",m); mdist=m; cd=mcd; md=mmd; } } //} } } //printf(" >Succes=%d\n",mdistisLinkable()) { md=0; // avoid returning things we cannot link to cd=0; return FALSE; // match found, but was not linkable } else { gd=md->getGroupDef(); if (gd) cd=0; return TRUE; /* found match */ } } } /* go to the parent scope */ if (scopeOffset==0) { scopeOffset=-1; } else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1) { scopeOffset=0; } } while (scopeOffset>=0); // 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 mmdAl = mmd->argumentList(); if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), Doxygen::globalScope,mmd->getFileDef(),argList, checkCV ) ) break; if (!fuzzy_mmd && hasEmptyArgs) fuzzy_mmd = mmd; } if (argList) delete argList, argList = 0; mmd = mmd ? mmd : fuzzy_mmd; if (mmd) { md = mmd; cd = mmd->getClassDef(); return TRUE; } } // maybe an namespace, file or group member ? //printf("Testing for global function scopeName=`%s' mScope=`%s' :: mName=`%s'\n", // scopeName.data(),mScope.data(),mName.data()); if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known { //printf(" >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("Trying namespace %s\n",namespaceName.data()); if (!namespaceName.isEmpty() && (fnd=Doxygen::namespaceSDict->find(namespaceName)) && fnd->isLinkable() ) { //printf("Function inside existing namespace `%s'\n",namespaceName.data()); bool found=FALSE; MemberListIterator mmli(*mn); MemberDef *mmd; for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) { //printf("mmd->getNamespaceDef()=%p fnd=%p\n", // mmd->getNamespaceDef(),fnd); if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ ) { // namespace is found bool match=TRUE; ArgumentList *argList=0; if (args && strcmp(args,"()")!=0) { argList=new ArgumentList; LockingPtr mmdAl = mmd->argumentList(); stringToArgumentList(args,argList); match=matchArguments2( mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), fnd,mmd->getFileDef(),argList, checkCV); } if (match) { nd=fnd; md=mmd; found=TRUE; } if (args) { delete argList; argList=0; } } } if (!found && args && !strcmp(args,"()")) // no exact match found, but if args="()" an arbitrary // member will do { for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) { if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ ) { nd=fnd; md=mmd; found=TRUE; } } } if (found) { if (!md->isLinkable()) { md=0; // avoid returning things we cannot link to nd=0; return FALSE; // match found but not linkable } else { gd=md->getGroupDef(); if (gd && gd->isLinkable()) nd=0; else gd=0; return TRUE; } } } if (scopeOffset==0) { scopeOffset=-1; } else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1) { scopeOffset=0; } } while (scopeOffset>=0); //else // no scope => global function { QList members; //printf(" Function with global scope name `%s' args=`%s'\n",memberName.data(),args); MemberListIterator mli(*mn); for (mli.toFirst();(md=mli.current());++mli) { fd=md->getFileDef(); gd=md->getGroupDef(); //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p\n", // md->name().data(),args,fd,gd); if ( ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) && md->getNamespaceDef()==0 && md->isLinkable() ) { //printf(" 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 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("Found match!\n"); members.append(md); } } } if (members.count()!=1 && args && !strcmp(args,"()")) { // no exact match found, but if args="()" an arbitrary // member will do md=mn->last(); while (md /* && md->isLinkable()*/) { //printf("Found member `%s'\n",md->name().data()); //printf("member is linkable md->name()=`%s'\n",md->name().data()); fd=md->getFileDef(); gd=md->getGroupDef(); if ( (gd && gd->isLinkable()) || (fd && fd->isLinkable()) ) { members.append(md); } md=mn->prev(); } } //printf("found %d candidate members\n",members.count()); if (members.count()==1 || currentFile!=0) { md=members.first(); } else if (members.count()>1) { //printf("Found more than one matching member!\n"); // use some C scoping rules to determine the correct link // 1. member in current file // 2. non-static member in different file if (currentFile==0) { bool ambig; currentFile = findFileDef(Doxygen::inputNameDict,0/*namespaceName*/,ambig); } MemberDef *bmd = 0; for (md=members.first(); md; md=members.next()) { if (currentFile && md->getBodyDef()==currentFile) { bmd = 0; break; } if (!md->isStatic() || Config_getBool("EXTRACT_STATIC")) bmd = md; } if (bmd) md=bmd; } if (md) // found a matching global member { fd=md->getFileDef(); gd=md->getGroupDef(); //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable()); if (gd && gd->isLinkable()) fd=0; else gd=0; return TRUE; } } } // no nothing found return FALSE; } /*! * Searches for a scope definition given its name as a string via parameter * `scope'. * * The parameter `docScope' is a string representing the name of the scope in * which the `scope' string was found. * * The function returns TRUE if the scope is known and documented or * FALSE if it is not. * If TRUE is returned exactly one of the parameter `cd', `nd' * will be non-zero: * - if `cd' is non zero, the scope was a class pointed to by cd. * - if `nd' is non zero, the scope was a namespace pointed to by nd. */ static bool getScopeDefs(const char *docScope,const char *scope, ClassDef *&cd, NamespaceDef *&nd) { cd=0;nd=0; QCString scopeName=scope; //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope); if (scopeName.isEmpty()) return FALSE; bool explicitGlobalScope=FALSE; if (scopeName.at(0)==':' && scopeName.at(1)==':') { scopeName=scopeName.right(scopeName.length()-2); explicitGlobalScope=TRUE; } QCString docScopeName=docScope; int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length(); do // for each possible docScope (from largest to and including empty) { QCString fullName=scopeName.copy(); if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::"); if ((cd=getClass(fullName)) && 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 ) { 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; //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, 0, 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 || dst==0) return s; const char *p, *q; int srcLen = strlen(src); int dstLen = strlen(dst); 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; 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 g_findFileDefCache(5000); FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig) { ambig=FALSE; if (n==0) return 0; QCString key; key.sprintf("%p:",fnDict); key+=n; g_findFileDefCache.setAutoDelete(TRUE); FindFileCacheElem *cachedResult = g_findFileDefCache.find(key); if (cachedResult) { ambig = cachedResult->isAmbig; return cachedResult->fileDef; } else { cachedResult = new FindFileCacheElem(0,FALSE); } QCString name=convertToQCString(QDir::cleanDirPath(n)); QCString path; int slashPos; FileName *fn; if (name.isEmpty()) goto exit; slashPos=QMAX(name.findRev('/'),name.findRev('\\')); if (slashPos!=-1) { path=name.left(slashPos+1); name=name.right(name.length()-slashPos-1); } //printf("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); 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); return lastMatch; } } exit: g_findFileDefCache.insert(key,cachedResult); return 0; } //---------------------------------------------------------------------- QCString showFileDefMatches(const FileNameDict *fnDict,const char *n) { QCString result; QCString name=n; QCString path; int slashPos=QMAX(name.findRev('/'),name.findRev('\\')); if (slashPos!=-1) { path=name.left(slashPos+1); name=name.right(name.length()-slashPos-1); } FileName *fn; if ((fn=(*fnDict)[name])) { FileNameIterator fni(*fn); FileDef *fd; for (fni.toFirst();(fd=fni.current());++fni) { if (path.isEmpty() || fd->getPath().right(path.length())==path) { result+=" "+fd->absFilePath()+"\n"; } } } return result; } //---------------------------------------------------------------------- QCString substituteKeywords(const QCString &s,const char *title,const 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; 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) { static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES"); QCString result; char c; const char *p=name; while ((c=*p++)!=0) { switch(c) { case '_': 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) { static bool shortNames = Config_getBool("SHORT_NAMES"); static bool createSubdirs = Config_getBool("CREATE_SUBDIRS"); QCString result; if (shortNames) // use short names only { static QDict 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); 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(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::B > will return A. */ 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 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 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 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') { if (te') 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 -> 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() ) { //printf("formArg->type=%s\n",formArg->type.data()); 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()); 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 (actArg->name.isEmpty()) { result += actArg->type+" "; } 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; } /*! 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 *copyArgumentLists(const QList *srcLists) { ASSERT(srcLists!=0); QList *dstLists = new QList; dstLists->setAutoDelete(TRUE); QListIterator 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::B will * try to strip \ and not \, while \a parentOnly is \c FALSE will * strip both unless A or B 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') { count--; done = count==0; } } int si= fullName.find("::",e); if (parentOnly && si==-1) break; // we only do the parent scope, so we stop here if needed result+=fullName.mid(p,i-p); //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data()); if (getClass(result+fullName.mid(i,e-i))!=0) { result+=fullName.mid(i,e-i); //printf("2:result+=%s\n",fullName.mid(i,e-i-1).data()); } else if (pLastScopeStripped) { //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data()); *pLastScopeStripped=fullName.mid(i,e-i); } p=e; i=fullName.find('<',p); } result+=fullName.right(l-p); //printf("3:result+=%s\n",fullName.right(l-p).data()); return result; } /*! Merges two scope parts together. The parts may (partially) overlap. * Example1: \c A::B and \c B::C will result in \c A::B::C
* Example2: \c A and \c B will be \c A::B
* 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> 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 * /*anchors*/, const char *fileName,int startLine, const QList *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); } QCString pageName; if (Config_getBool("CASE_SENSE_NAMES")) pageName=pd->name(); else pageName=pd->name().lower(); pd->setFileName(pageName); //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 if (pd->getGroupDef()) { file=pd->getGroupDef()->getOutputFileBase().copy(); } else { file=pageName; } 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():"", // 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(pageName,si); } } return pd; } //---------------------------------------------------------------------------- void addRefItem(const QList *sli, const char *prefix, const char *name,const char *title,const char *args) { //printf("addRefItem(sli=%p,prefix=%s,name=%s,title=%s,args=%s)\n",sli,prefix,name,title,args); if (sli) { QListIterator 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); //printf("anchor=%s written=%d\n",item->listAnchor.data(),item->written); if (item->written) return; QCString doc(1000); doc = "\\anchor "; doc += item->listAnchor; doc += "
"; doc += prefix; doc += " \\_internalref "; doc += name; doc += " \""; doc += title; doc += "\""; if (args) doc += args; doc += "
\n
"; doc += item->text; doc += "
\n"; addRelatedPage(refList->listName(),refList->pageTitle(),doc,0,refList->listName(),1,0,0,0); item->written=TRUE; } } } } void addGroupListToTitle(OutputList &ol,Definition *d) { LockingPtr 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 0 static bool isCzech = theTranslator->idLanguage()=="czech"; static bool isSerbian = theTranslator->idLanguage()=="serbian"; static bool isJapanese = theTranslator->idLanguage()=="japanese" || theTranslator->idLanguage()=="japanese-en"; static bool isKorean = theTranslator->idLanguage()=="korean" || theTranslator->idLanguage()=="korean-en"; static bool isRussian = theTranslator->idLanguage()=="russian"; static bool isUkrainian = theTranslator->idLanguage()=="ukrainian"; static bool isSlovene = theTranslator->idLanguage()=="solvene"; static bool isChinese = theTranslator->idLanguage()=="chinese" || theTranslator->idLanguage()=="chinese-traditional"; static bool isLatin2 = theTranslator->idLanguageCharset()=="iso-8859-2"; static bool isGreek = theTranslator->idLanguage()=="greek"; //printf("filterLatexString(%s)\n",str); #endif 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; #if 0 { // Some languages use wide characters if (c>=128 && (isJapanese || isKorean || isChinese || isSerbian)) { t << (char)c; if (*p) { c = *p++; t << (char)c; } } else { t << (char)c; } break; } #endif } } 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 '-': if (*p=='>') { t << " $\\rightarrow$ "; p++; } else { t << (char)c; } 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 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(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); } 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; } } } SrcLangExt getLanguageFromFileName(const QCString fileName) { int i = fileName.findRev('.'); static bool init=FALSE; static QDict extLookup; extLookup.setAutoDelete(TRUE); if (!init) // one time initialization { extLookup.insert(".idl", new int(SrcLangExt_IDL)); extLookup.insert(".ddl", new int(SrcLangExt_IDL)); extLookup.insert(".odl", new int(SrcLangExt_IDL)); extLookup.insert(".ddl", new int(SrcLangExt_IDL)); extLookup.insert(".java", new int(SrcLangExt_Java)); extLookup.insert(".as", new int(SrcLangExt_JS)); extLookup.insert(".js", new int(SrcLangExt_JS)); extLookup.insert(".cs", new int(SrcLangExt_CSharp)); extLookup.insert(".d", new int(SrcLangExt_D)); extLookup.insert(".php", new int(SrcLangExt_PHP)); extLookup.insert(".php4", new int(SrcLangExt_PHP)); extLookup.insert(".php5", new int(SrcLangExt_PHP)); extLookup.insert(".inc", new int(SrcLangExt_PHP)); extLookup.insert(".phtml", new int(SrcLangExt_PHP)); extLookup.insert(".m", new int(SrcLangExt_ObjC)); extLookup.insert(".M", new int(SrcLangExt_ObjC)); extLookup.insert(".mm", new int(SrcLangExt_ObjC)); extLookup.insert(".py", new int(SrcLangExt_Python)); extLookup.insert(".f", new int(SrcLangExt_F90)); extLookup.insert(".f90", new int(SrcLangExt_F90)); extLookup.insert(".vhd", new int(SrcLangExt_VHDL)); extLookup.insert(".vhdl", new int(SrcLangExt_VHDL)); init=TRUE; } if (i!=-1) // name has an extension { QCString extStr=fileName.right(fileName.length()-i); if (!extStr.isEmpty()) // non-empty extension { int *pVal=extLookup.find(extStr); if (pVal) // listed extension { return (SrcLangExt)*pVal; // cast void* address to enum value } } } 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 && distancedefinitionType()==Definition::TypeMember) { Definition *d = (Definition *)di; g_visitedNamespaces.clear(); int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); if (distance!=-1 && distanceisTypedef()) return TRUE; // closest matching symbol is a typedef else return FALSE; } QCString parseCommentAsText(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,Doxygen::globalScope,0,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 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 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'%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() { QCString& TreeView=Config_getEnum("GENERATE_TREEVIEW"); return TreeView=="FRAME" || TreeView=="ALL" || TreeView=="YES"; }