diff options
Diffstat (limited to 'src/util.cpp')
-rw-r--r-- | src/util.cpp | 3896 |
1 files changed, 1097 insertions, 2799 deletions
diff --git a/src/util.cpp b/src/util.cpp index abcd910..7fcd5ed 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,11 +1,11 @@ /***************************************************************************** - * + * * * Copyright (C) 1997-2015 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 + * 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. * @@ -19,6 +19,11 @@ #include <errno.h> #include <math.h> #include <limits.h> +#include <cinttypes> +#include <string.h> + +#include <mutex> +#include <unordered_set> #include "md5.h" @@ -26,7 +31,6 @@ #include <qfileinfo.h> #include <qdir.h> #include <qdatetime.h> -#include <qcache.h> #include "util.h" #include "message.h" @@ -63,6 +67,7 @@ #include "membergroup.h" #include "dirdef.h" #include "htmlentity.h" +#include "symbolresolver.h" #define ENABLE_TRACINGSUPPORT 0 @@ -78,29 +83,20 @@ //------------------------------------------------------------------------ -// 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 "../../" +static const char *hex = "0123456789ABCDEF"; + //------------------------------------------------------------------------ // TextGeneratorOLImpl implementation //------------------------------------------------------------------------ -TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od) +TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od) { } void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const -{ +{ if (s==0) return; //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces); if (keepSpaces) @@ -113,19 +109,19 @@ void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const cs[1]='\0'; while ((c=*p++)) { - if (c==' ') m_od.writeNonBreakableSpace(1); + if (c==' ') m_od.writeNonBreakableSpace(1); else cs[0]=c,m_od.docify(cs); } } } else { - m_od.docify(s); + m_od.docify(s); } } void TextGeneratorOLImpl::writeBreak(int indent) const -{ +{ m_od.lineBreak("typebreak"); int i; for (i=0;i<indent;i++) @@ -146,9 +142,9 @@ void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file, //------------------------------------------------------------------------ // an inheritance tree of depth of 100000 should be enough for everyone :-) -const int maxInheritanceDepth = 100000; +const int maxInheritanceDepth = 100000; -/*! +/*! Removes all anonymous scopes from string s Possible examples: \verbatim @@ -177,9 +173,9 @@ QCString removeAnonymousScopes(const QCString &s) while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE; c=i+l-1; while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE; - if (b1 && b2) - { - result+="::"; + if (b1 && b2) + { + result+="::"; } p=i+l; } @@ -224,7 +220,7 @@ QCString stripAnonymousNamespaceScope(const QCString &s) 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 (Doxygen::namespaceLinkedMap->find(s.left(i+l))!=0) { if (s.at(i)!='@') { @@ -248,7 +244,7 @@ done: void writePageRef(OutputDocInterface &od,const char *cn,const char *mn) { od.pushGeneratorState(); - + od.disable(OutputGenerator::Html); od.disable(OutputGenerator::Man); od.disable(OutputGenerator::Docbook); @@ -273,22 +269,20 @@ QCString generateMarker(int id) return result; } -static QCString stripFromPath(const QCString &path,QStrList &l) +static QCString stripFromPath(const QCString &path,const StringVector &l) { - // look at all the strings in the list and strip the longest match - const char *s=l.first(); + // look at all the strings in the list and strip the longest match QCString potential; unsigned int length = 0; - while (s) + for (const auto &s : l) { - QCString prefix = s; + QCString prefix = s.c_str(); if (prefix.length() > length && qstricmp(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; @@ -311,7 +305,7 @@ QCString stripFromIncludePath(const QCString &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 + * 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) @@ -331,7 +325,7 @@ int guessSection(const char *name) n.right(4)==".i++" || n.right(4)==".inl" || n.right(4)==".xml" || - n.right(4)==".sql" + n.right(4)==".sql" ) return Entry::SOURCE_SEC; if (n.right(2)==".h" || // header n.right(3)==".hh" || @@ -352,7 +346,7 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName, //printf("<<resolveTypeDef(%s,%s)\n", // context ? context->name().data() : "<none>",qualifiedName.data()); QCString result; - if (qualifiedName.isEmpty()) + if (qualifiedName.isEmpty()) { //printf(" qualified name empty!\n"); return result; @@ -374,12 +368,12 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName, return result; } } - MemberDef *md=0; + const MemberDef *md=0; while (mContext && md==0) { // step 1: get the right scope const Definition *resScope=mContext; - if (scopeIndex!=-1) + if (scopeIndex!=-1) { // split-off scope part QCString resScopeName = qualifiedName.left(scopeIndex); @@ -400,35 +394,35 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName, } } //printf("resScope=%s\n",resScope?resScope->name().data():"<none>"); - + // step 2: get the member - if (resScope) // no scope or scope found in the current context + 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; + MemberNameLinkedMap *mnd=0; if (resScope->definitionType()==Definition::TypeClass) { - mnd=Doxygen::memberNameSDict; + mnd=Doxygen::memberNameLinkedMap; } else { - mnd=Doxygen::functionNameSDict; + mnd=Doxygen::functionNameLinkedMap; } MemberName *mn=mnd->find(resName); if (mn) { - MemberNameIterator mni(*mn); - MemberDef *tmd=0; int minDist=-1; - for (;(tmd=mni.current());++mni) + for (const auto &tmd_p : *mn) { + const MemberDef *tmd = tmd_p.get(); //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n", - // tmd->name().data(), resScope->name().data(), + // tmd->name().data(), resScope->name().data(), // tmd->getOuterScope()->name().data(), mContext); if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/) { - int dist=isAccessibleFrom(resScope,0,tmd); + SymbolResolver resolver; + int dist=resolver.isAccessibleFrom(resScope,tmd); if (dist!=-1 && (md==0 || dist<minDist)) { md = tmd; @@ -465,747 +459,43 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName, // qualifiedName.data(),context ? context->name().data() : "<global>"); } return result; - + } -/*! Get a class definition given its name. +/*! Get a class definition given its name. * Returns 0 if the class is not found. */ ClassDef *getClass(const char *n) { if (n==0 || n[0]=='\0') return 0; - QCString name=n; - ClassDef *result = Doxygen::classSDict->find(name); - //if (result==0 && !exact) // also try generic and protocol versions - //{ - // result = Doxygen::classSDict->find(name+"-g"); - // if (result==0) - // { - // result = Doxygen::classSDict->find(name+"-p"); - // } - //} - //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>"); - return result; + return Doxygen::classLinkedMap->find(n); } NamespaceDef *getResolvedNamespace(const char *name) { if (name==0 || name[0]=='\0') return 0; - QCString *subst = Doxygen::namespaceAliasDict[name]; - if (subst) + auto it = Doxygen::namespaceAliasMap.find(name); + if (it!=Doxygen::namespaceAliasMap.end()) { int count=0; // recursion detection guard - QCString *newSubst; - while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10) + StringUnorderedMap::iterator it2; + while ((it2=Doxygen::namespaceAliasMap.find(it->second))!=Doxygen::namespaceAliasMap.end() && + count<10) { - subst=newSubst; + it=it2; count++; } if (count==10) { warn_uncond("possible recursive namespace alias detected for %s!\n",name); } - return Doxygen::namespaceSDict->find(subst->data()); + return Doxygen::namespaceLinkedMap->find(it->second.data()); } else { - return Doxygen::namespaceSDict->find(name); - } -} - -static QDict<MemberDef> g_resolvedTypedefs; -static QDict<Definition> g_visitedNamespaces; - -// forward declaration -static const ClassDef *getResolvedClassRec(const Definition *scope, - const FileDef *fileScope, - const char *n, - const MemberDef **pTypeDef, - QCString *pTemplSpec, - QCString *pResolvedType - ); -int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope,const 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. - */ -const ClassDef *newResolveTypedef(const FileDef *fileScope, - const MemberDef *md, - const MemberDef **pMemType, - QCString *pTemplSpec, - QCString *pResolvedType, - const ArgumentList *actTemplParams) -{ - //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal()); - bool isCached = md->isTypedefValCached(); // value already cached - if (isCached) - { - //printf("Already cached %s->%s [%s]\n", - // md->name().data(), - // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>", - // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>"); - - if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec(); - if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef(); - return md->getCachedTypedefVal(); - } - //printf("new typedef\n"); - QCString qname = md->qualifiedName(); - if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done - - g_resolvedTypedefs.insert(qname,md); // put on the trace list - - const ClassDef *typeClass = md->getClassDef(); - QCString type = md->typeString(); // get the "value" of the typedef - if (typeClass && typeClass->isTemplate() && - actTemplParams && !actTemplParams->empty()) - { - type = substituteTemplateArgumentsInString(type, - typeClass->templateArguments(),*actTemplParams); - } - QCString typedefValue = type; - int tl=type.length(); - int ip=tl-1; // remove * and & at the end - while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' ')) - { - ip--; - } - type=type.left(ip+1); - type.stripPrefix("const "); // strip leading "const" - type.stripPrefix("struct "); // strip leading "struct" - type.stripPrefix("union "); // strip leading "union" - int sp=0; - tl=type.length(); // length may have been changed - while (sp<tl && type.at(sp)==' ') sp++; - const MemberDef *memTypeDef = 0; - const ClassDef *result = getResolvedClassRec(md->getOuterScope(), - fileScope,type,&memTypeDef,0,pResolvedType); - // if type is a typedef then return what it resolves to. - if (memTypeDef && memTypeDef->isTypedef()) - { - result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec); - goto done; - } - else if (memTypeDef && memTypeDef->isEnumerate() && pMemType) - { - *pMemType = memTypeDef; - } - - //printf("type=%s result=%p\n",type.data(),result); - if (result==0) - { - // try unspecialized version if type is template - int si=type.findRev("::"); - int i=type.find('<'); - if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version - { - if (pTemplSpec) *pTemplSpec = type.mid(i); - result = getResolvedClassRec(md->getOuterScope(),fileScope, - type.left(i),0,0,pResolvedType); - //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n", - // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl); - } - else if (si!=-1) // A::B - { - i=type.find('<',si); - if (i==-1) // Something like A<T>::B => lookup A::B - { - i=type.length(); - } - else // Something like A<T>::B<S> => lookup A::B, spec=<S> - { - if (pTemplSpec) *pTemplSpec = type.mid(i); - } - result = getResolvedClassRec(md->getOuterScope(),fileScope, - stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0, - pResolvedType); - } - - //if (result) ip=si+sp+1; - } - -done: - if (pResolvedType) - { - if (result) - { - *pResolvedType=result->qualifiedName(); - //printf("*pResolvedType=%s\n",pResolvedType->data()); - if (sp>0) pResolvedType->prepend(typedefValue.left(sp)); - if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1)); - } - else - { - *pResolvedType=typedefValue; - } - } - - // remember computed value for next time - if (result && result->getDefFileName()!="<code>") - // this check is needed to prevent that temporary classes that are - // introduced while parsing code fragments are being cached here. - { - //printf("setting cached typedef %p in result %p\n",md,result); - //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine()); - //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>"); - const_cast<MemberDef*>(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(const Definition *scope,const FileDef *fileScope,const QCString &name, - const 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 = dynamic_cast<MemberDef *>(d); - if (md->isTypedef()) // d is a typedef - { - // test accessibility of typedef within scope. - int distance = isAccessibleFromWithExpScope(scope,fileScope,d,""); - if (distance!=-1 && distance<minDistance) - // definition is accessible and a better match - { - minDistance=distance; - bestMatch = md; - } - } - } - } - } - else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol - { - Definition *d = (Definition*)di; - // that are also typedefs - MemberDef *md = dynamic_cast<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; - } - } + return Doxygen::namespaceLinkedMap->find(name); } - if (bestMatch) - { - result = bestMatch->typeString(); - if (pTypeDef) *pTypeDef=bestMatch; - } - - //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>", - // name.data(),result.data()); - return result; -} - -static const Definition *endOfPathIsUsedClass(const SDict<Definition> *cl,const QCString &localName) -{ - if (cl) - { - SDict<Definition>::Iterator cli(*cl); - Definition *cd; - for (cli.toFirst();(cd=cli.current());++cli) - { - if (cd->localName()==localName) - { - return cd; - } - } - } - return 0; -} - -/*! Starting with scope \a start, the string \a path is interpreted as - * a part of a qualified scope name (e.g. A::B::C), and the scope is - * searched. If found the scope definition is returned, otherwise 0 - * is returned. - */ -static const Definition *followPath(const Definition *start,const FileDef *fileScope,const QCString &path) -{ - int is,ps; - int l; - const Definition *current=start; - ps=0; - //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data()); - // for each part of the explicit scope - while ((is=getScopeFragment(path,ps,&l))!=-1) - { - // try to resolve the part if it is a typedef - const MemberDef *typeDef=0; - QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef); - //printf(" qualScopePart=%s\n",qualScopePart.data()); - if (typeDef) - { - const ClassDef *type = newResolveTypedef(fileScope,typeDef); - if (type) - { - //printf("Found type %s\n",type->name().data()); - return type; - } - } - const Definition *next = current->findInnerCompound(qualScopePart); - //printf("++ Looking for %s inside %s result %s\n", - // qualScopePart.data(), - // current->name().data(), - // next?next->name().data():"<null>"); - if (next==0) // failed to follow the path - { - //printf("==> next==0!\n"); - if (current->definitionType()==Definition::TypeNamespace) - { - next = endOfPathIsUsedClass( - (dynamic_cast<const NamespaceDef *>(current))->getUsedClasses(),qualScopePart); - } - else if (current->definitionType()==Definition::TypeFile) - { - next = endOfPathIsUsedClass( - (dynamic_cast<const FileDef *>(current))->getUsedClasses(),qualScopePart); - } - current = next; - if (current==0) break; - } - else // continue to follow scope - { - current = next; - //printf("==> current = %p\n",current); - } - ps=is+l; - } - //printf("followPath(start=%s,path=%s) result=%s\n", - // start->name().data(),path.data(),current?current->name().data():"<null>"); - return current; // path could be followed -} - -bool accessibleViaUsingClass(const SDict<Definition> *cl, - const FileDef *fileScope, - const Definition *item, - const QCString &explicitScopePart="" - ) -{ - //printf("accessibleViaUsingClass(%p)\n",cl); - if (cl) // see if the class was imported via a using statement - { - SDict<Definition>::Iterator cli(*cl); - Definition *ucd; - bool explicitScopePartEmpty = explicitScopePart.isEmpty(); - for (cli.toFirst();(ucd=cli.current());++cli) - { - //printf("Trying via used class %s\n",ucd->name().data()); - const 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, - const FileDef *fileScope, - const Definition *item, - const QCString &explicitScopePart="") -{ - static QDict<void> visitedDict; - if (nl) // check used namespaces for the class - { - NamespaceSDict::Iterator nli(*nl); - NamespaceDef *und; - int count=0; - for (nli.toFirst();(und=nli.current());++nli,count++) - { - //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(), - // count,nl->count()); - const Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart); - if (sc && item->getOuterScope()==sc) - { - //printf("] found it\n"); - return TRUE; - } - if (item->getLanguage()==SrcLangExt_Cpp) - { - QCString key=und->name(); - if (und->getUsedNamespaces() && visitedDict.find(key)==0) - { - visitedDict.insert(key,(void *)0x08); - - if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart)) - { - //printf("] found it via recursion\n"); - return TRUE; - } - - visitedDict.remove(key); - } - } - //printf("] Try via used namespace done\n"); - } - } - return FALSE; -} - -const int MAX_STACK_SIZE = 1000; - -/** Helper class representing the stack of items considered while resolving - * the scope. - */ -class AccessStack -{ - public: - AccessStack() : m_index(0) {} - void push(const Definition *scope,const FileDef *fileScope,const Definition *item) - { - if (m_index<MAX_STACK_SIZE) - { - m_elements[m_index].scope = scope; - m_elements[m_index].fileScope = fileScope; - m_elements[m_index].item = item; - m_index++; - } - } - void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const QCString &expScope) - { - if (m_index<MAX_STACK_SIZE) - { - m_elements[m_index].scope = scope; - m_elements[m_index].fileScope = fileScope; - m_elements[m_index].item = item; - m_elements[m_index].expScope = expScope; - m_index++; - } - } - void pop() - { - if (m_index>0) m_index--; - } - bool find(const Definition *scope,const FileDef *fileScope, const Definition *item) - { - int i=0; - for (i=0;i<m_index;i++) - { - AccessElem *e = &m_elements[i]; - if (e->scope==scope && e->fileScope==fileScope && e->item==item) - { - return TRUE; - } - } - return FALSE; - } - bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const QCString &expScope) - { - int i=0; - for (i=0;i<m_index;i++) - { - AccessElem *e = &m_elements[i]; - if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope) - { - return TRUE; - } - } - return FALSE; - } - - private: - /** Element in the stack. */ - struct AccessElem - { - const Definition *scope; - const FileDef *fileScope; - const Definition *item; - QCString expScope; - }; - int m_index; - AccessElem m_elements[MAX_STACK_SIZE]; -}; - -/* Returns the "distance" (=number of levels up) from item to scope, or -1 - * if item in not inside scope. - */ -int isAccessibleFrom(const Definition *scope,const FileDef *fileScope,const Definition *item) -{ - //printf("<isAccessibleFrom(scope=%s,item=%s itemScope=%s)\n", - // scope->name().data(),item->name().data(),item->getOuterScope()->name().data()); - - static AccessStack accessStack; - if (accessStack.find(scope,fileScope,item)) - { - return -1; - } - accessStack.push(scope,fileScope,item); - - int result=0; // assume we found it - int i; - - Definition *itemScope=item->getOuterScope(); - bool memberAccessibleFromScope = - (item->definitionType()==Definition::TypeMember && // a member - itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class - scope->definitionType()==Definition::TypeClass && // accessible - (dynamic_cast<const ClassDef*>(scope))->isAccessibleMember(dynamic_cast<const MemberDef *>(item)) // from scope - ); - bool nestedClassInsideBaseClass = - (item->definitionType()==Definition::TypeClass && // a nested class - itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base - scope->definitionType()==Definition::TypeClass && // class of scope - (dynamic_cast<const ClassDef*>(scope))->isBaseClass(dynamic_cast<ClassDef*>(itemScope),TRUE) - ); - - if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass) - { - //printf("> found it\n"); - if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent - // this is preferred over nested class in this class - // see bug 686956 - } - else if (scope==Doxygen::globalScope) - { - if (fileScope) - { - SDict<Definition> *cl = fileScope->getUsedClasses(); - if (accessibleViaUsingClass(cl,fileScope,item)) - { - //printf("> found via used class\n"); - goto done; - } - NamespaceSDict *nl = fileScope->getUsedNamespaces(); - if (accessibleViaUsingNamespace(nl,fileScope,item)) - { - //printf("> found via used namespace\n"); - goto done; - } - } - //printf("> reached global scope\n"); - result=-1; // not found in path to globalScope - } - else // keep searching - { - // check if scope is a namespace, which is using other classes and namespaces - if (scope->definitionType()==Definition::TypeNamespace) - { - const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope); - //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses()); - const SDict<Definition> *cl = nscope->getUsedClasses(); - if (accessibleViaUsingClass(cl,fileScope,item)) - { - //printf("> found via used class\n"); - goto done; - } - const NamespaceSDict *nl = nscope->getUsedNamespaces(); - if (accessibleViaUsingNamespace(nl,fileScope,item)) - { - //printf("> found via used namespace\n"); - goto done; - } - } - // repeat for the parent scope - i=isAccessibleFrom(scope->getOuterScope(),fileScope,item); - //printf("> result=%d\n",i); - result= (i==-1) ? -1 : i+2; - } -done: - accessStack.pop(); - //Doxygen::lookupCache.insert(key,new int(result)); - return result; -} - - -/* Returns the "distance" (=number of levels up) from item to scope, or -1 - * if item in not in this scope. The explicitScopePart limits the search - * to scopes that match \a scope (or its parent scope(s)) plus the explicit part. - * Example: - * - * class A { public: class I {}; }; - * class B { public: class J {}; }; - * - * - Looking for item=='J' inside scope=='B' will return 0. - * - Looking for item=='I' inside scope=='B' will return -1 - * (as it is not found in B nor in the global scope). - * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but - * not found and then A::I is searched in the global scope, which matches and - * thus the result is 1. - */ -int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope, - const Definition *item,const QCString &explicitScopePart) -{ - if (explicitScopePart.isEmpty()) - { - // handle degenerate case where there is no explicit scope. - return isAccessibleFrom(scope,fileScope,item); - } - - static AccessStack accessStack; - if (accessStack.find(scope,fileScope,item,explicitScopePart)) - { - return -1; - } - accessStack.push(scope,fileScope,item,explicitScopePart); - - - //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>", - // item?item->name().data():"<none>", - // explicitScopePart.data()); - int result=0; // assume we found it - const 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 && - (dynamic_cast<const ClassDef*>(newScope))->isBaseClass(dynamic_cast<const 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()); - const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(newScope); - const SDict<Definition> *cl = nscope->getUsedClasses(); - if (cl) - { - SDict<Definition>::Iterator cli(*cl); - const 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; - } - } - } - const NamespaceSDict *nl = nscope->getUsedNamespaces(); - if (nl) - { - NamespaceSDict::Iterator nli(*nl); - const 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) - { - const NamespaceDef *nscope = dynamic_cast<const NamespaceDef*>(scope); - const NamespaceSDict *nl = nscope->getUsedNamespaces(); - if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) - { - //printf("> found in used namespace\n"); - goto done; - } - } - if (scope==Doxygen::globalScope) - { - if (fileScope) - { - const NamespaceSDict *nl = fileScope->getUsedNamespaces(); - if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) - { - //printf("> found in used namespace\n"); - goto done; - } - } - //printf("> not found\n"); - result=-1; - } - else // continue by looking into the parent scope - { - int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope, - item,explicitScopePart); - //printf("> result=%d\n",i); - result= (i==-1) ? -1 : i+2; - } - } - -done: - //printf(" > result=%d\n",result); - accessStack.pop(); - //Doxygen::lookupCache.insert(key,new int(result)); - return result; } int computeQualifiedIndex(const QCString &name) @@ -1214,448 +504,11 @@ int computeQualifiedIndex(const QCString &name) return name.findRev("::",i==-1 ? name.length() : i); } -static void getResolvedSymbol(const Definition *scope, - const FileDef *fileScope, - Definition *d, - const QCString &explicitScopePart, - ArgumentList *actTemplParams, - int &minDistance, - const ClassDef *&bestMatch, - const 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 && - ((dynamic_cast<MemberDef*>(d))->isTypedef() || (dynamic_cast<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 = dynamic_cast<ClassDef *>(d); - //printf("cd=%s\n",cd->name().data()); - if (!cd->isTemplateArgument()) // skip classes that - // are only there to - // represent a template - // argument - { - //printf("is not a templ arg\n"); - if (distance<minDistance) // found a definition that is "closer" - { - minDistance=distance; - bestMatch = cd; - bestTypedef = 0; - bestTemplSpec.resize(0); - bestResolvedType = cd->qualifiedName(); - } - else if (distance==minDistance && - fileScope && bestMatch && - fileScope->getUsedNamespaces() && - d->getOuterScope()->definitionType()==Definition::TypeNamespace && - bestMatch->getOuterScope()==Doxygen::globalScope - ) - { - // in case the distance is equal it could be that a class X - // is defined in a namespace and in the global scope. When searched - // in the global scope the distance is 0 in both cases. We have - // to choose one of the definitions: we choose the one in the - // namespace if the fileScope imports namespaces and the definition - // found was in a namespace while the best match so far isn't. - // Just a non-perfect heuristic but it could help in some situations - // (kdecore code is an example). - minDistance=distance; - bestMatch = cd; - bestTypedef = 0; - bestTemplSpec.resize(0); - bestResolvedType = cd->qualifiedName(); - } - } - else - { - //printf(" is a template argument!\n"); - } - } - else if (d->definitionType()==Definition::TypeMember) - { - MemberDef *md = dynamic_cast<MemberDef *>(d); - //printf(" member isTypedef()=%d\n",md->isTypedef()); - if (md->isTypedef()) // d is a typedef - { - QCString args=md->argsString(); - if (args.isEmpty()) // do not expand "typedef t a[4];" - { - //printf(" found typedef!\n"); - - // we found a symbol at this distance, but if it didn't - // resolve to a class, we still have to make sure that - // something at a greater distance does not match, since - // that symbol is hidden by this one. - if (distance<minDistance) - { - QCString spec; - QCString type; - minDistance=distance; - const MemberDef *enumType = 0; - const ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams); - if (cd) // type resolves to a class - { - //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data()); - bestMatch = cd; - bestTypedef = md; - bestTemplSpec = spec; - bestResolvedType = type; - } - else if (enumType) // type resolves to a enum - { - //printf(" is enum\n"); - bestMatch = 0; - bestTypedef = enumType; - bestTemplSpec = ""; - bestResolvedType = enumType->qualifiedName(); - } - else if (md->isReference()) // external reference - { - bestMatch = 0; - bestTypedef = md; - bestTemplSpec = spec; - bestResolvedType = type; - } - else - { - bestMatch = 0; - bestTypedef = md; - bestTemplSpec.resize(0); - bestResolvedType.resize(0); - //printf(" no match\n"); - } - } - else - { - //printf(" not the best match %d min=%d\n",distance,minDistance); - } - } - else - { - //printf(" not a simple typedef\n") - } - } - else if (md->isEnumerate()) - { - if (distance<minDistance) - { - minDistance=distance; - bestMatch = 0; - bestTypedef = md; - bestTemplSpec = ""; - bestResolvedType = md->qualifiedName(); - } - } - } - } // if definition accessible - else - { - //printf(" Not accessible!\n"); - } - } // if definition is a class or member - //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data()); -} - -/* Find the fully qualified class name referred to by the input class - * or typedef name against the input scope. - * Loops through scope and each of its parent scopes looking for a - * match against the input name. Can recursively call itself when - * resolving typedefs. - */ -static const ClassDef *getResolvedClassRec(const Definition *scope, - const FileDef *fileScope, - const char *n, - const MemberDef **pTypeDef, - QCString *pTemplSpec, - QCString *pResolvedType - ) -{ - //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n); - QCString name; - QCString explicitScopePart; - QCString strippedTemplateParams; - name=stripTemplateSpecifiersFromScope - (removeRedundantWhiteSpace(n),TRUE, - &strippedTemplateParams); - ArgumentList actTemplParams; - if (!strippedTemplateParams.isEmpty()) // template part that was stripped - { - stringToArgumentList(scope->getLanguage(),strippedTemplateParams,actTemplParams); - } - - int qualifierIndex = computeQualifiedIndex(name); - //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex); - if (qualifierIndex!=-1) // qualified name - { - // split off the explicit scope part - explicitScopePart=name.left(qualifierIndex); - // todo: improve namespace alias substitution - replaceNamespaceAliases(explicitScopePart,explicitScopePart.length()); - name=name.mid(qualifierIndex+2); - } - - if (name.isEmpty()) - { - //printf("] empty name\n"); - return 0; // empty name - } - - //printf("Looking for symbol %s\n",name.data()); - DefinitionIntf *di = Doxygen::symbolMap->find(name); - // the -g (for C# generics) and -p (for ObjC protocols) are now already - // stripped from the key used in the symbolMap, so that is not needed here. - if (di==0) - { - //di = Doxygen::symbolMap->find(name+"-g"); - //if (di==0) - //{ - di = Doxygen::symbolMap->find(name+"-p"); - if (di==0) - { - //printf("no such symbol!\n"); - return 0; - } - //} - } - //printf("found symbol!\n"); - - bool hasUsingStatements = - (fileScope && ((fileScope->getUsedNamespaces() && - fileScope->getUsedNamespaces()->count()>0) || - (fileScope->getUsedClasses() && - fileScope->getUsedClasses()->count()>0)) - ); - //printf("hasUsingStatements=%d\n",hasUsingStatements); - // Since it is often the case that the same name is searched in the same - // scope over an over again (especially for the linked source code generation) - // we use a cache to collect previous results. This is possible since the - // result of a lookup is deterministic. As the key we use the concatenated - // scope, the name to search for and the explicit scope prefix. The speedup - // achieved by this simple cache can be enormous. - int scopeNameLen = scope->name().length()+1; - int nameLen = name.length()+1; - int explicitPartLen = explicitScopePart.length(); - int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0; - - // below is a more efficient coding of - // QCString key=scope->name()+"+"+name+"+"+explicitScopePart; - QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1); - char *p=key.rawData(); - qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+'; - p+=scopeNameLen; - qstrcpy(p,name); *(p+nameLen-1)='+'; - p+=nameLen; - qstrcpy(p,explicitScopePart); - p+=explicitPartLen; - - // if a file scope is given and it contains using statements we should - // also use the file part in the key (as a class name can be in - // two different namespaces and a using statement in a file can select - // one of them). - if (hasUsingStatements) - { - // below is a more efficient coding of - // key+="+"+fileScope->name(); - *p++='+'; - qstrcpy(p,fileScope->absFilePath()); - p+=fileScopeLen-1; - } - *p='\0'; - - LookupInfo *pval=Doxygen::lookupCache->find(key); - //printf("Searching for %s result=%p\n",key.data(),pval); - if (pval) - { - //printf("LookupInfo %p %p '%s' %p\n", - // pval->classDef, pval->typeDef, pval->templSpec.data(), - // pval->resolvedType.data()); - if (pTemplSpec) *pTemplSpec=pval->templSpec; - if (pTypeDef) *pTypeDef=pval->typeDef; - if (pResolvedType) *pResolvedType=pval->resolvedType; - //printf("] cachedMatch=%s\n", - // pval->classDef?pval->classDef->name().data():"<none>"); - //if (pTemplSpec) - // printf("templSpec=%s\n",pTemplSpec->data()); - return pval->classDef; - } - else // not found yet; we already add a 0 to avoid the possibility of - // endless recursion. - { - Doxygen::lookupCache->insert(key,new LookupInfo); - } - - const ClassDef *bestMatch=0; - const MemberDef *bestTypedef=0; - QCString bestTemplSpec; - QCString bestResolvedType; - int minDistance=10000; // init at "infinite" - - if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name - { - //printf(" name is not unique\n"); - DefinitionListIterator dli(*(DefinitionList*)di); - Definition *d; - int count=0; - for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition - { - getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams, - minDistance,bestMatch,bestTypedef,bestTemplSpec, - bestResolvedType); - } - } - else // unique name - { - //printf(" name is unique\n"); - Definition *d = (Definition *)di; - getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams, - minDistance,bestMatch,bestTypedef,bestTemplSpec, - bestResolvedType); - } - - if (pTypeDef) - { - *pTypeDef = bestTypedef; - } - if (pTemplSpec) - { - *pTemplSpec = bestTemplSpec; - } - if (pResolvedType) - { - *pResolvedType = bestResolvedType; - } - //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n", - // bestMatch,bestResolvedType.data()); - - pval=Doxygen::lookupCache->find(key); - if (pval) - { - pval->classDef = bestMatch; - pval->typeDef = bestTypedef; - pval->templSpec = bestTemplSpec; - pval->resolvedType = bestResolvedType; - } - else - { - Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType)); - } - //printf("] bestMatch=%s distance=%d\n", - // bestMatch?bestMatch->name().data():"<none>",minDistance); - //if (pTemplSpec) - // printf("templSpec=%s\n",pTemplSpec->data()); - return bestMatch; -} - -/* Find the fully qualified class name referred to by the input class - * or typedef name against the input scope. - * Loops through scope and each of its parent scopes looking for a - * match against the input name. - */ -const ClassDef *getResolvedClass(const Definition *scope, - const FileDef *fileScope, - const char *n, - const MemberDef **pTypeDef, - QCString *pTemplSpec, - bool mayBeUnlinkable, - bool mayBeHidden, - QCString *pResolvedType - ) -{ - static bool optimizeOutputVhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL); - g_resolvedTypedefs.clear(); - if (scope==0 || - (scope->definitionType()!=Definition::TypeClass && - scope->definitionType()!=Definition::TypeNamespace - ) || - (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1) - ) - { - scope=Doxygen::globalScope; - } - //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n", - // scope?scope->name().data():"<global>", - // fileScope?fileScope->name().data():"<none>", - // n, - // mayBeUnlinkable - // ); - const ClassDef *result; - if (optimizeOutputVhdl) - { - result = getClass(n); - } - else - { - result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType); - } - if (result==0) // for nested classes imported via tag files, the scope may not - // present, so we check the class name directly as well. - // See also bug701314 - { - result = getClass(n); - } - if (!mayBeUnlinkable && result && !result->isLinkable()) - { - if (!mayBeHidden || !result->isHidden()) - { - //printf("result was %s\n",result?result->name().data():"<none>"); - result=0; // don't link to artificial/hidden classes unless explicitly allowed - } - } - //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>", - // n,result?result->name().data():"<none>"); - return result; -} - //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- -static bool findOperator(const QCString &s,int i) -{ - int b = s.findRev("operator",i); - if (b==-1) return FALSE; // not found - b+=8; - while (b<i) // check if there are only spaces in between - // the operator and the > - { - if (!isspace((uchar)s.at(b))) return FALSE; - b++; - } - return TRUE; -} - -static bool findOperator2(const QCString &s,int i) -{ - int b = s.findRev("operator",i); - if (b==-1) return FALSE; // not found - b+=8; - while (b<i) // check if there are only non-ascii - // characters in front of the operator - { - if (isId((uchar)s.at(b))) return FALSE; - b++; - } - return TRUE; -} - static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' }; static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' }; static const char operatorScope[] = { 'o', 'p', 'e', 'r', 'a', 't', 'o', 'r', '?', '?', '?' }; @@ -1664,30 +517,30 @@ struct CharAroundSpace { CharAroundSpace() { - charMap['('].before=FALSE; - charMap['='].before=FALSE; - charMap['&'].before=FALSE; - charMap['*'].before=FALSE; - charMap['['].before=FALSE; - charMap['|'].before=FALSE; - charMap['+'].before=FALSE; - charMap[';'].before=FALSE; - charMap[':'].before=FALSE; - charMap['/'].before=FALSE; - - charMap['='].after=FALSE; - charMap[' '].after=FALSE; - charMap['['].after=FALSE; - charMap[']'].after=FALSE; - charMap['\t'].after=FALSE; - charMap['\n'].after=FALSE; - charMap[')'].after=FALSE; - charMap[','].after=FALSE; - charMap['<'].after=FALSE; - charMap['|'].after=FALSE; - charMap['+'].after=FALSE; - charMap['('].after=FALSE; - charMap['/'].after=FALSE; + charMap[static_cast<int>('(')].before=FALSE; + charMap[static_cast<int>('=')].before=FALSE; + charMap[static_cast<int>('&')].before=FALSE; + charMap[static_cast<int>('*')].before=FALSE; + charMap[static_cast<int>('[')].before=FALSE; + charMap[static_cast<int>('|')].before=FALSE; + charMap[static_cast<int>('+')].before=FALSE; + charMap[static_cast<int>(';')].before=FALSE; + charMap[static_cast<int>(':')].before=FALSE; + charMap[static_cast<int>('/')].before=FALSE; + + charMap[static_cast<int>('=')].after=FALSE; + charMap[static_cast<int>(' ')].after=FALSE; + charMap[static_cast<int>('[')].after=FALSE; + charMap[static_cast<int>(']')].after=FALSE; + charMap[static_cast<int>('\t')].after=FALSE; + charMap[static_cast<int>('\n')].after=FALSE; + charMap[static_cast<int>(')')].after=FALSE; + charMap[static_cast<int>(',')].after=FALSE; + charMap[static_cast<int>('<')].after=FALSE; + charMap[static_cast<int>('|')].after=FALSE; + charMap[static_cast<int>('+')].after=FALSE; + charMap[static_cast<int>('(')].after=FALSE; + charMap[static_cast<int>('/')].after=FALSE; } struct CharElem { @@ -1704,15 +557,16 @@ static CharAroundSpace g_charAroundSpace; // Note: this function is not reentrant due to the use of static buffer! QCString removeRedundantWhiteSpace(const QCString &s) { - static bool cliSupport = Config_getBool(CPP_CLI_SUPPORT); - static bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL); + bool cliSupport = Config_getBool(CPP_CLI_SUPPORT); + bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL); if (s.isEmpty() || vhdl) return s; // We use a static character array to // improve the performance of this function - static char *growBuf = 0; - static int growBufLen = 0; + // and thread_local is needed to make it multi-thread safe + static THREAD_LOCAL char *growBuf = 0; + static THREAD_LOCAL int growBufLen = 0; if ((int)s.length()*3>growBufLen) // For input character we produce at most 3 output characters, { growBufLen = s.length()*3; @@ -1866,7 +720,7 @@ QCString removeRedundantWhiteSpace(const QCString &s) { if (nc != '=') // avoid splitting operator&= - { + { *dst++=' '; } } @@ -1994,10 +848,10 @@ bool rightScopeMatch(const QCString &scope, const QCString &name) { int sl=scope.length(); int nl=name.length(); - return (name==scope || // equal - (scope.right(nl)==name && // substring + return (name==scope || // equal + (scope.right(nl)==name && // substring sl-nl>1 && scope.at(sl-nl-1)==':' && scope.at(sl-nl-2)==':' // scope - ) + ) ); } @@ -2005,10 +859,10 @@ bool leftScopeMatch(const QCString &scope, const QCString &name) { int sl=scope.length(); int nl=name.length(); - return (name==scope || // equal - (scope.left(nl)==name && // substring + return (name==scope || // equal + (scope.left(nl)==name && // substring sl>nl+1 && scope.at(nl)==':' && scope.at(nl+1)==':' // scope - ) + ) ); } @@ -2021,8 +875,10 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, //printf("linkify='%s'\n",text); static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*"); static QRegExp regExpSplit("(?!:),"); + if (text==0) return; QCString txtStr=text; int strLen = txtStr.length(); + if (strLen==0) return; //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n", // scope?scope->name().data():"<none>", // fileScope?fileScope->name().data():"<none>", @@ -2032,7 +888,6 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, 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) { @@ -2045,11 +900,11 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, } // add non-word part to the result - bool insideString=FALSE; + bool insideString=FALSE; int i; - for (i=index;i<newIndex;i++) - { - if (txtStr.at(i)=='"') insideString=!insideString; + for (i=index;i<newIndex;i++) + { + if (txtStr.at(i)=='"') insideString=!insideString; } //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak); @@ -2069,16 +924,16 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, out.writeBreak(indentLevel==0 ? 0 : indentLevel+1); out.writeString(splitText.right(splitLength-i-offset),keepSpaces); floatingIndex=splitLength-i-offset+matchLen; - } + } else { - out.writeString(splitText,keepSpaces); + out.writeString(splitText,keepSpaces); } } else { - //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex)); - out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces); + //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); @@ -2095,8 +950,9 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, const GroupDef *gd=0; //printf("** Match word '%s'\n",matchWord.data()); - const MemberDef *typeDef=0; - cd=getResolvedClass(scope,fileScope,matchWord,&typeDef); + SymbolResolver resolver(fileScope); + cd=resolver.resolveClass(scope,matchWord); + const MemberDef *typeDef = resolver.getTypedef(); if (typeDef) // First look at typedef then class, see bug 584184. { //printf("Found typedef %s\n",typeDef->name().data()); @@ -2112,7 +968,7 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, } } } - if (!found && (cd || (cd=getClass(matchWord)))) + if (!found && (cd || (cd=getClass(matchWord)))) { //printf("Found class %s\n",cd->name().data()); // add link to the result @@ -2156,10 +1012,10 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, int m = matchWord.findRev("::"); QCString scopeName; - if (scope && - (scope->definitionType()==Definition::TypeClass || + if (scope && + (scope->definitionType()==Definition::TypeClass || scope->definitionType()==Definition::TypeNamespace - ) + ) ) { scopeName=scope->name(); @@ -2171,19 +1027,19 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, } //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() || + //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()) + //) && + (external ? md->isLinkable() : md->isLinkableInProject()) ) { //printf("Found ref scope=%s\n",d?d->name().data():"<global>"); //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(), // md->anchor(),word); - if (md!=self && (self==0 || md->name()!=self->name())) + if (md!=self && (self==0 || md->name()!=self->name())) // name check is needed for overloaded members, where getDefs just returns one { /* in case of Fortran scop and the variable is a non Fortran variable: don't link, @@ -2214,9 +1070,9 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, } -void writeExample(OutputList &ol,ExampleSDict *ed) +void writeExamples(OutputList &ol,const ExampleList &list) { - QCString exampleLine=theTranslator->trWriteList(ed->count()); + QCString exampleLine=theTranslator->trWriteList((int)list.size()); //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex); //bool manEnabled = ol.isEnabled(OutputGenerator::Man); @@ -2229,9 +1085,9 @@ void writeExample(OutputList &ol,ExampleSDict *ed) 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) + if (ok && entryIndex<list.size()) { + const auto &e = list[entryIndex]; ol.pushGeneratorState(); //if (latexEnabled) ol.disable(OutputGenerator::Latex); ol.disable(OutputGenerator::Latex); @@ -2239,7 +1095,7 @@ void writeExample(OutputList &ol,ExampleSDict *ed) ol.disable(OutputGenerator::Docbook); // link for Html / man //printf("writeObjectLink(file=%s)\n",e->file.data()); - ol.writeObjectLink(0,e->file,e->anchor,e->name); + ol.writeObjectLink(0,e.file,e.anchor,e.name); ol.popGeneratorState(); ol.pushGeneratorState(); @@ -2248,13 +1104,13 @@ void writeExample(OutputList &ol,ExampleSDict *ed) 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); + 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("."); } @@ -2296,94 +1152,72 @@ QCString argListToString(const ArgumentList &al,bool useCanonicalType,bool showD if (it!=al.end()) result+=", "; } result+=")"; - if (al.constSpecifier) result+=" const"; - if (al.volatileSpecifier) result+=" volatile"; - if (al.refQualifier==RefQualifierLValue) result+=" &"; - else if (al.refQualifier==RefQualifierRValue) result+=" &&"; - if (!al.trailingReturnType.isEmpty()) result+=" -> "+al.trailingReturnType; - if (al.pureSpecifier) result+=" =0"; + if (al.constSpecifier()) result+=" const"; + if (al.volatileSpecifier()) result+=" volatile"; + if (al.refQualifier()==RefQualifierLValue) result+=" &"; + else if (al.refQualifier()==RefQualifierRValue) result+=" &&"; + if (!al.trailingReturnType().isEmpty()) result+=al.trailingReturnType(); + if (al.pureSpecifier()) result+=" =0"; return removeRedundantWhiteSpace(result); } -QCString tempArgListToString(const ArgumentList &al,SrcLangExt lang) +QCString tempArgListToString(const ArgumentList &al,SrcLangExt lang,bool includeDefault) { QCString result; if (al.empty()) return result; result="<"; - auto it = al.begin(); - while (it!=al.end()) + bool first=true; + for (const auto &a : al) { - Argument a = *it; - if (!a.name.isEmpty()) // add template argument name + if (a.defval.isEmpty() || includeDefault) { - if (a.type.left(4)=="out") // C# covariance + if (!first) result+=", "; + if (!a.name.isEmpty()) // add template argument name { - result+="out "; - } - else if (a.type.left(3)=="in") // C# contravariance - { - result+="in "; - } - if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp) - { - result+=a.type+" "; + if (a.type.left(4)=="out") // C# covariance + { + result+="out "; + } + else if (a.type.left(3)=="in") // C# contravariance + { + result+="in "; + } + if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp) + { + result+=a.type+" "; + } + result+=a.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) + else // extract name from type { - result+=a.type.right(a.type.length()-i-1); - if (a.type.find("...")!=-1) + 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); + if (a.type.find("...")!=-1) + { + result+="..."; + } + } + else // nothing found -> take whole name { - result+="..."; + result+=a.type; } } - else // nothing found -> take whole name + if (!a.typeConstraint.isEmpty() && lang==SrcLangExt_Java) { - result+=a.type; + result+=" extends "; // TODO: now Java specific, C# has where... + result+=a.typeConstraint; } + first=false; } - if (!a.typeConstraint.isEmpty() && lang==SrcLangExt_Java) - { - result+=" extends "; // TODO: now Java specific, C# has where... - result+=a.typeConstraint; - } - ++it; - if (it!=al.end()) result+=", "; } result+=">"; return removeRedundantWhiteSpace(result); } -// compute the HTML anchors for a list of members -void setAnchors(MemberList *ml) -{ - //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(); - //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n", - // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data()); - } - } -} - //---------------------------------------------------------------------------- /*! takes the \a buf of the given length \a len and converts CR LF (DOS) @@ -2404,7 +1238,7 @@ int filterCRLF(char *buf,int len) { c = '\n'; // each CR to LF if (src<len && buf[src] == '\n') - ++src; // skip LF just after CR (DOS) + ++src; // skip LF just after CR (DOS) } else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser { @@ -2415,21 +1249,19 @@ int filterCRLF(char *buf,int len) return dest; // length of the valid part of the buf } -static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found) +static QCString getFilterFromList(const char *name,const StringVector &filterList,bool &found) { found=FALSE; // compare the file name to the filter pattern list - QStrListIterator sli(filterList); - char* filterStr; - for (sli.toFirst(); (filterStr = sli.current()); ++sli) + for (const auto &filterStr : filterList) { - QCString fs = filterStr; + QCString fs = filterStr.c_str(); int i_equals=fs.find('='); if (i_equals!=-1) { QCString filterPattern = fs.left(i_equals); - QRegExp fpat(filterPattern,Portable::fileSystemIsCaseSensitive(),TRUE); - if (fpat.match(name)!=-1) + QRegExp fpat(filterPattern,Portable::fileSystemIsCaseSensitive(),TRUE); + if (fpat.match(name)!=-1) { // found a match! QCString filterName = fs.mid(i_equals+1); @@ -2457,12 +1289,12 @@ QCString getFileFilter(const char* name,bool isSourceCode) // sanity check if (name==0) return ""; - QStrList& filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS); - QStrList& filterList = Config_getList(FILTER_PATTERNS); + const StringVector& filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS); + const StringVector& filterList = Config_getList(FILTER_PATTERNS); QCString filterName; bool found=FALSE; - if (isSourceCode && !filterSrcList.isEmpty()) + if (isSourceCode && !filterSrcList.empty()) { // first look for source filter pattern list filterName = getFilterFromList(name,filterSrcList,found); } @@ -2497,7 +1329,7 @@ QCString transcodeCharacterStringToUTF8(const QCString &input) int outputSize=inputSize*4+1; QCString output(outputSize); void *cd = portable_iconv_open(outputEncoding,inputEncoding); - if (cd==(void *)(-1)) + if (cd==(void *)(-1)) { err("unsupported character conversion: '%s'->'%s'\n", inputEncoding.data(),outputEncoding); @@ -2529,7 +1361,7 @@ QCString transcodeCharacterStringToUTF8(const QCString &input) /*! 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. + * If \a name is "-" the string will be read from standard input. */ QCString fileToString(const char *name,bool filter,bool isSourceCode) { @@ -2543,13 +1375,13 @@ QCString fileToString(const char *name,bool filter,bool isSourceCode) if (fileOpened) { const int bSize=4096; - QCString contents(bSize); + QCString contents(bSize+1); int totalSize=0; int size; while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize) { totalSize+=bSize; - contents.resize(totalSize+bSize); + contents.resize(totalSize+bSize+1); } totalSize = filterCRLF(contents.rawData(),totalSize+size)+2; contents.resize(totalSize); @@ -2579,7 +1411,7 @@ QCString fileToString(const char *name,bool filter,bool isSourceCode) return buf.data(); } } - if (!fileOpened) + if (!fileOpened) { err("cannot open file '%s' for reading\n",name); } @@ -2609,7 +1441,7 @@ static QDateTime getCurrentDateTime() static bool warnedOnce=FALSE; if (!warnedOnce) { - warn_uncond("Environment variable SOURCE_DATE_EPOCH must have a value smaller than or equal to %llu; actual value %llu\n",UINT_MAX,epoch); + warn_uncond("Environment variable SOURCE_DATE_EPOCH must have a value smaller than or equal to %d; actual value %" PRIu64 "\n",UINT_MAX, (uint64_t)epoch); warnedOnce=TRUE; } } @@ -2643,46 +1475,41 @@ QCString yearToString() } //---------------------------------------------------------------------- -// recursive function that returns the number of branches in the +// 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 + 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 (cd==bcd) return level; if (level==256) { warn_uncond("class %s seem to have a recursive " "inheritance relation!\n",cd->name().data()); return -1; } - int m=maxInheritanceDepth; - if (cd->baseClasses()) + int m=maxInheritanceDepth; + for (const auto &bcdi : cd->baseClasses()) { - BaseClassListIterator bcli(*cd->baseClasses()); - BaseClassDef *bcdi; - for (;(bcdi=bcli.current());++bcli) - { - int mc=minClassDistance(bcdi->classDef,bcd,level+1); - if (mc<m) m=mc; - if (m<0) break; - } + int mc=minClassDistance(bcdi.classDef,bcd,level+1); + if (mc<m) m=mc; + if (m<0) break; } return m; } Protection classInheritedProtectionLevel(const ClassDef *cd,const ClassDef *bcd,Protection prot,int level) { - if (bcd->categoryOf()) // use class that is being extended in case of + if (bcd->categoryOf()) // use class that is being extended in case of // an Objective-C category { bcd=bcd->categoryOf(); } - if (cd==bcd) + if (cd==bcd) { goto exit; } @@ -2691,14 +1518,12 @@ Protection classInheritedProtectionLevel(const ClassDef *cd,const ClassDef *bcd, err("Internal inconsistency: found class %s seem to have a recursive " "inheritance relation! Please send a bug report to doxygen@gmail.com\n",cd->name().data()); } - else if (cd->baseClasses()) + else if (prot!=Private) { - BaseClassListIterator bcli(*cd->baseClasses()); - const BaseClassDef *bcdi; - for (;(bcdi=bcli.current()) && prot!=Private;++bcli) + for (const auto &bcdi : cd->baseClasses()) { - Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1); - if (baseProt==Private) prot=Private; + Protection baseProt = classInheritedProtectionLevel(bcdi.classDef,bcd,bcdi.prot,level+1); + if (baseProt==Private) prot=Private; else if (baseProt==Protected) prot=Protected; } } @@ -2707,173 +1532,12 @@ exit: return prot; } -#ifndef NEWMATCH -// strip any template specifiers that follow className in string s -static QCString trimTemplateSpecifiers( - const QCString &namespaceName, - const QCString &className, - const QCString &s - ) -{ - //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data()); - QCString scopeName=mergeScopes(namespaceName,className); - ClassDef *cd=getClass(scopeName); - if (cd==0) return s; // should not happen, but guard anyway. - - QCString result=s; - - int i=className.length()-1; - if (i>=0 && className.at(i)=='>') // template specialization - { - // replace unspecialized occurrences in s, with their specialized versions. - int count=1; - int cl=i+1; - while (i>=0) - { - char c=className.at(i); - if (c=='>') count++,i--; - else if (c=='<') { count--; if (count==0) break; } - else i--; - } - QCString unspecClassName=className.left(i); - int l=i; - int p=0; - while ((i=result.find(unspecClassName,p))!=-1) - { - if (result.at(i+l)!='<') // unspecialized version - { - result=result.left(i)+className+result.right(result.length()-i-l); - l=cl; - } - p=i+l; - } - } - - //printf("result after specialization: %s\n",result.data()); - - QCString qualName=cd->qualifiedNameWithTemplateParameters(); - //printf("QualifiedName = %s\n",qualName.data()); - // We strip the template arguments following className (if any) - if (!qualName.isEmpty()) // there is a class name - { - int is,ps=0; - int p=0,l,i; - - while ((is=getScopeFragment(qualName,ps,&l))!=-1) - { - QCString qualNamePart = qualName.right(qualName.length()-is); - //printf("qualNamePart=%s\n",qualNamePart.data()); - while ((i=result.find(qualNamePart,p))!=-1) - { - int ql=qualNamePart.length(); - result=result.left(i)+cd->name()+result.right(result.length()-i-ql); - p=i+cd->name().length(); - } - ps=is+l; - } - } - //printf("result=%s\n",result.data()); - - return result.stripWhiteSpace(); -} - -/*! - * @param pattern pattern to look for - * @param s string to search in - * @param p position to start - * @param len resulting pattern length - * @returns position on which string is found, or -1 if not found - */ -static int findScopePattern(const QCString &pattern,const QCString &s, - int p,int *len) -{ - int sl=s.length(); - int pl=pattern.length(); - int sp=0; - *len=0; - while (p<sl) - { - sp=p; // start of match - int pp=0; // pattern position - while (p<sl && pp<pl) - { - if (s.at(p)=='<') // skip template arguments while matching - { - int bc=1; - //printf("skipping pos=%d c=%c\n",p,s.at(p)); - p++; - while (p<sl) - { - if (s.at(p)=='<') bc++; - else if (s.at(p)=='>') - { - bc--; - if (bc==0) - { - p++; - break; - } - } - //printf("skipping pos=%d c=%c\n",p,s.at(p)); - p++; - } - } - else if (s.at(p)==pattern.at(pp)) - { - //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p)); - p++; - pp++; - } - else // no match - { - //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data()); - p=sp+1; - break; - } - } - if (pp==pl) // whole pattern matches - { - *len=p-sp; - return sp; - } - } - return -1; -} - -static QCString trimScope(const QCString &name,const QCString &s) -{ - int scopeOffset=name.length(); - QCString result=s; - do // for each scope - { - QCString tmp; - QCString scope=name.left(scopeOffset)+"::"; - //printf("Trying with scope='%s'\n",scope.data()); - - int i,p=0,l; - while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence - { - tmp+=result.mid(p,i-p); // add part before pattern - p=i+l; - } - tmp+=result.right(result.length()-p); // add trailing part - - scopeOffset=name.findRev("::",scopeOffset-1); - result = tmp; - } while (scopeOffset>0); - //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data()); - return result; -} -#endif - -void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0) +void trimBaseClassScope(const 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) + for (const auto &bcd : bcl) { - ClassDef *cd=bcd->classDef; + ClassDef *cd=bcd.classDef; //printf("Trying class %s\n",cd->name().data()); int spos=s.find(cd->name()+"::"); if (spos!=-1) @@ -2883,8 +1547,10 @@ void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0) ); } //printf("base class '%s'\n",cd->name().data()); - if (cd->baseClasses()) - trimBaseClassScope(cd->baseClasses(),s,level+1); + if (!cd->baseClasses().empty()) + { + trimBaseClassScope(cd->baseClasses(),s,level+1); + } } } @@ -2982,7 +1648,7 @@ static void stripIrrelevantString(QCString &target,const QCString &str) if (i1==-1 && i2==-1) { // strip str from target at index i - target=target.left(i)+target.right(target.length()-i-l); + target=target.left(i)+target.right(target.length()-i-l); changed=TRUE; i-=l; } @@ -3011,8 +1677,8 @@ static void stripIrrelevantString(QCString &target,const QCString &str) \code const T param -> T param // not relevant - const T& param -> const T& param // const needed - T* const param -> T* param // not relevant + const T& param -> const T& param // const needed + T* const param -> T* param // not relevant const T* param -> const T* param // const needed \endcode */ @@ -3032,276 +1698,6 @@ void stripIrrelevantConstVolatile(QCString &s) //#define MATCH printf("Match at line %d\n",__LINE__); //#define NOMATCH printf("Nomatch at line %d\n",__LINE__); -#ifndef NEWMATCH -static bool matchArgument(const Argument *srcA,const Argument *dstA, - const QCString &className, - const QCString &namespaceName, - NamespaceSDict *usingNamespaces, - SDict<Definition> *usingClasses) -{ - //printf("match argument start '%s|%s' <-> '%s|%s' using nsp=%p class=%p\n", - // srcA->type.data(),srcA->name.data(), - // dstA->type.data(),dstA->name.data(), - // usingNamespaces, - // usingClasses); - - // TODO: resolve any typedefs names that are part of srcA->type - // before matching. This should use className and namespaceName - // and usingNamespaces and usingClass to determine which typedefs - // are in-scope, so it will not be very efficient :-( - - QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type); - QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type); - QCString srcAName=srcA->name.stripWhiteSpace(); - QCString dstAName=dstA->name.stripWhiteSpace(); - srcAType.stripPrefix("class "); - dstAType.stripPrefix("class "); - - // allow distinguishing "const A" from "const B" even though - // from a syntactic point of view they would be two names of the same - // type "const". This is not fool prove of course, but should at least - // catch the most common cases. - if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty()) - { - srcAType+=" "; - srcAType+=srcAName; - } - if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty()) - { - dstAType+=" "; - dstAType+=dstAName; - } - if (srcAName=="const" || srcAName=="volatile") - { - srcAType+=srcAName; - srcAName.resize(0); - } - else if (dstA->name=="const" || dstA->name=="volatile") - { - dstAType+=dstA->name; - dstAName.resize(0); - } - - stripIrrelevantConstVolatile(srcAType); - stripIrrelevantConstVolatile(dstAType); - - // strip typename keyword - if (qstrncmp(srcAType,"typename ",9)==0) - { - srcAType = srcAType.right(srcAType.length()-9); - } - if (qstrncmp(dstAType,"typename ",9)==0) - { - dstAType = dstAType.right(dstAType.length()-9); - } - - srcAType = removeRedundantWhiteSpace(srcAType); - dstAType = removeRedundantWhiteSpace(dstAType); - - //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE); - //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE); - - //printf("srcA='%s|%s' dstA='%s|%s'\n",srcAType.data(),srcAName.data(), - // dstAType.data(),dstAName.data()); - - if (srcA->array!=dstA->array) // nomatch for char[] against char - { - NOMATCH - return FALSE; - } - if (srcAType!=dstAType) // check if the argument only differs on name - { - - // remove a namespace scope that is only in one type - // (assuming a using statement was used) - //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data()); - //trimNamespaceScope(srcAType,dstAType,namespaceName); - //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data()); - - //QCString srcScope; - //QCString dstScope; - - // strip redundant scope specifiers - if (!className.isEmpty()) - { - srcAType=trimScope(className,srcAType); - dstAType=trimScope(className,dstAType); - //printf("trimScope: '%s' <=> '%s'\n",srcAType.data(),dstAType.data()); - ClassDef *cd; - if (!namespaceName.isEmpty()) - cd=getClass(namespaceName+"::"+className); - else - cd=getClass(className); - if (cd && cd->baseClasses()) - { - trimBaseClassScope(cd->baseClasses(),srcAType); - trimBaseClassScope(cd->baseClasses(),dstAType); - } - //printf("trimBaseClassScope: '%s' <=> '%s'\n",srcAType.data(),dstAType.data()); - } - if (!namespaceName.isEmpty()) - { - srcAType=trimScope(namespaceName,srcAType); - dstAType=trimScope(namespaceName,dstAType); - } - //printf("#usingNamespace=%d\n",usingNamespaces->count()); - if (usingNamespaces && usingNamespaces->count()>0) - { - NamespaceSDict::Iterator nli(*usingNamespaces); - NamespaceDef *nd; - for (;(nd=nli.current());++nli) - { - srcAType=trimScope(nd->name(),srcAType); - dstAType=trimScope(nd->name(),dstAType); - } - } - //printf("#usingClasses=%d\n",usingClasses->count()); - if (usingClasses && usingClasses->count()>0) - { - SDict<Definition>::Iterator cli(*usingClasses); - Definition *cd; - for (;(cd=cli.current());++cli) - { - srcAType=trimScope(cd->name(),srcAType); - dstAType=trimScope(cd->name(),dstAType); - } - } - - //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(), - // dstAType.data(),dstAName.data()); - - if (!srcAName.isEmpty() && !dstA->type.isEmpty() && - (srcAType+" "+srcAName)==dstAType) - { - MATCH - return TRUE; - } - else if (!dstAName.isEmpty() && !srcA->type.isEmpty() && - (dstAType+" "+dstAName)==srcAType) - { - MATCH - return TRUE; - } - - - uint srcPos=0,dstPos=0; - bool equal=TRUE; - while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal) - { - equal=srcAType.at(srcPos)==dstAType.at(dstPos); - if (equal) srcPos++,dstPos++; - } - uint srcATypeLen=srcAType.length(); - uint dstATypeLen=dstAType.length(); - if (srcPos<srcATypeLen && dstPos<dstATypeLen) - { - // if nothing matches or the match ends in the middle or at the - // end of a string then there is no match - if (srcPos==0 || dstPos==0) - { - NOMATCH - return FALSE; - } - if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos))) - { - //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos); - // check if a name if already found -> if no then there is no match - if (!srcAName.isEmpty() || !dstAName.isEmpty()) - { - NOMATCH - return FALSE; - } - // types only - while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++; - while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++; - if (srcPos<srcATypeLen || - dstPos<dstATypeLen || - (srcPos==srcATypeLen && dstPos==dstATypeLen) - ) - { - NOMATCH - return FALSE; - } - } - else - { - // otherwise we assume that a name starts at the current position. - while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++; - while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++; - - // if nothing more follows for both types then we assume we have - // found a match. Note that now 'signed int' and 'signed' match, but - // seeing that int is not a name can only be done by looking at the - // semantics. - - if (srcPos!=srcATypeLen || dstPos!=dstATypeLen) - { - NOMATCH - return FALSE; - } - } - } - else if (dstPos<dstAType.length()) - { - if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ - { - if (!dstAName.isEmpty()) // dst has its name separated from its type - { - NOMATCH - return FALSE; - } - while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++; - if (dstPos!=dstAType.length()) - { - NOMATCH - return FALSE; // more than a difference in name -> no match - } - } - else // maybe dst has a name while src has not - { - dstPos++; - while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++; - if (dstPos!=dstAType.length() || !srcAName.isEmpty()) - { - NOMATCH - return FALSE; // nope not a name -> no match - } - } - } - else if (srcPos<srcAType.length()) - { - if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ - { - if (!srcAName.isEmpty()) // src has its name separated from its type - { - NOMATCH - return FALSE; - } - while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++; - if (srcPos!=srcAType.length()) - { - NOMATCH - return FALSE; // more than a difference in name -> no match - } - } - else // maybe src has a name while dst has not - { - srcPos++; - while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++; - if (srcPos!=srcAType.length() || !dstAName.isEmpty()) - { - NOMATCH - return FALSE; // nope not a name -> no match - } - } - } - } - MATCH - return TRUE; -} - -#endif - static QCString stripDeclKeywords(const QCString &s) { int i=s.find(" class "); @@ -3320,11 +1716,11 @@ static QCString extractCanonicalType(const Definition *d,const FileDef *fs,QCStr QCString getCanonicalTemplateSpec(const Definition *d,const FileDef *fs,const QCString& spec) { - + QCString templSpec = spec.stripWhiteSpace(); // this part had been commented out before... but it is needed to match for instance // std::list<std::string> against list<string> so it is now back again! - if (!templSpec.isEmpty() && templSpec.at(0) == '<') + if (!templSpec.isEmpty() && templSpec.at(0) == '<') { templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace()); } @@ -3345,8 +1741,7 @@ static QCString getCanonicalTypeForIdentifier( if (count>10) return word; // oops recursion QCString symName,result,templSpec,tmpName; - //DefinitionList *defList=0; - if (tSpec && !tSpec->isEmpty()) + if (tSpec && !tSpec->isEmpty()) templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec)); if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty()) @@ -3357,21 +1752,26 @@ static QCString getCanonicalTypeForIdentifier( { symName=word; } - //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n", - // word.data(),tSpec?tSpec->data():"<none>",templSpec.data()); - - const ClassDef *cd = 0; - const MemberDef *mType = 0; - QCString ts; - QCString resolvedType; + //printf("getCanonicalTypeForIdentifier(%s d=%s fs=%s ,[%s->%s]) start\n", + // word.data(), + // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>", + // tSpec?tSpec->data():"<none>",templSpec.data()); // lookup class / class template instance - cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType); + SymbolResolver resolver(fs); + const ClassDef *cd = resolver.resolveClass(d,word+templSpec,true,true); + const MemberDef *mType = resolver.getTypedef(); + QCString ts = resolver.getTemplateSpec(); + QCString resolvedType = resolver.getResolvedType(); + 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); + cd = resolver.resolveClass(d,word,true,true); + mType = resolver.getTypedef(); + ts = resolver.getTemplateSpec(); + resolvedType = resolver.getResolvedType(); } if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations @@ -3451,7 +1851,13 @@ static QCString getCanonicalTypeForIdentifier( //printf("word=%s typeString=%s\n",word.data(),mType->typeString()); if (word!=mType->typeString()) { - result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1); + QCString type = mType->typeString(); + if (type.startsWith("typename ")) + { + type.stripPrefix("typename "); + type = stripTemplateSpecifiersFromScope(type,FALSE); + } + result = getCanonicalTypeForIdentifier(d,fs,type,tSpec,count+1); } else { @@ -3504,11 +1910,10 @@ static QCString extractCanonicalType(const Definition *d,const FileDef *fs,QCStr //printf(" i=%d p=%d\n",i,p); if (i>pp) canType += type.mid(pp,i-pp); - QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec); // in case the ct is empty it means that "word" represents scope "d" - // and this does not need to be added to the canonical + // and this does not need to be added to the canonical // type (it is redundant), so/ we skip it. This solves problem 589616. if (ct.isEmpty() && type.mid(p,2)=="::") { @@ -3522,7 +1927,7 @@ static QCString extractCanonicalType(const Definition *d,const FileDef *fs,QCStr // word.data(),templSpec.data(),canType.data(),ct.data()); if (!templSpec.isEmpty()) // if we didn't use up the templSpec already // (i.e. type is not a template specialization) - // then resolve any identifiers inside. + // 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; @@ -3550,11 +1955,11 @@ static QCString extractCanonicalArgType(const Definition *d,const FileDef *fs,co 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()) + 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+=" "; @@ -3575,9 +1980,9 @@ static bool matchArgument2( { //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(), + // srcA.type.data(),srcA.name.data(),srcA.canType.data(), // dstScope ? dstScope->name().data() : "", - // dstA->type.data(),dstA->name.data(),dstA->canType.data()); + // dstA.type.data(),dstA.name.data(),dstA.canType.data()); //if (srcA->array!=dstA->array) // nomatch for char[] against char //{ @@ -3630,34 +2035,46 @@ static bool matchArgument2( // new algorithm for argument matching -bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const ArgumentList &inSrcAl, - const Definition *dstScope,const FileDef *dstFileScope,const ArgumentList &inDstAl, +bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const ArgumentList *srcAl, + const Definition *dstScope,const FileDef *dstFileScope,const ArgumentList *dstAl, bool checkCV) { ASSERT(srcScope!=0 && dstScope!=0); - ArgumentList srcAl = inSrcAl; - ArgumentList dstAl = inDstAl; + if (srcAl==0 || dstAl==0) + { + bool match = srcAl==dstAl; + if (match) + { + MATCH + return TRUE; + } + else + { + NOMATCH + return FALSE; + } + } // handle special case with void argument - if ( srcAl.empty() && dstAl.size()==1 && dstAl.front().type=="void" ) + if ( srcAl->empty() && dstAl->size()==1 && dstAl->front().type=="void" ) { // special case for finding match between func() and func(void) Argument a; a.type = "void"; - srcAl.push_back(a); + const_cast<ArgumentList*>(srcAl)->push_back(a); MATCH return TRUE; } - if ( dstAl.empty() && srcAl.size()==1 && srcAl.front().type=="void" ) + if ( dstAl->empty() && srcAl->size()==1 && srcAl->front().type=="void" ) { // special case for finding match between func(void) and func() Argument a; a.type = "void"; - dstAl.push_back(a); + const_cast<ArgumentList*>(dstAl)->push_back(a); MATCH return TRUE; } - if (srcAl.size() != dstAl.size()) + if (srcAl->size() != dstAl->size()) { NOMATCH return FALSE; // different number of arguments -> no match @@ -3665,19 +2082,19 @@ bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,cons if (checkCV) { - if (srcAl.constSpecifier != dstAl.constSpecifier) + if (srcAl->constSpecifier() != dstAl->constSpecifier()) { NOMATCH return FALSE; // one member is const, the other not -> no match } - if (srcAl.volatileSpecifier != dstAl.volatileSpecifier) + if (srcAl->volatileSpecifier() != dstAl->volatileSpecifier()) { NOMATCH return FALSE; // one member is volatile, the other not -> no match } } - if (srcAl.refQualifier != dstAl.refQualifier) + if (srcAl->refQualifier() != dstAl->refQualifier()) { NOMATCH return FALSE; // one member is has a different ref-qualifier than the other @@ -3685,12 +2102,12 @@ bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,cons // so far the argument list could match, so we need to compare the types of // all arguments. - auto srcIt = srcAl.begin(); - auto dstIt = dstAl.begin(); - for (;srcIt!=srcAl.end() && dstIt!=dstAl.end();++srcIt,++dstIt) + auto srcIt = srcAl->begin(); + auto dstIt = dstAl->begin(); + for (;srcIt!=srcAl->end() && dstIt!=dstAl->end();++srcIt,++dstIt) { - Argument &srcA = *srcIt; - Argument &dstA = *dstIt; + Argument &srcA = const_cast<Argument&>(*srcIt); + Argument &dstA = const_cast<Argument&>(*dstIt); if (!matchArgument2(srcScope,srcFileScope,srcA, dstScope,dstFileScope,dstA) ) @@ -3700,7 +2117,7 @@ bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,cons } } MATCH - return TRUE; // all arguments match + return TRUE; // all arguments match } @@ -3843,44 +2260,40 @@ void mergeArguments(ArgumentList &srcAl,ArgumentList &dstAl,bool forceNameOverwr } } -static void findMembersWithSpecificName(MemberName *mn, +static void findMembersWithSpecificName(const MemberName *mn, const char *args, bool checkStatics, const FileDef *currentFile, bool checkCV, - const char *forceTagFile, QList<MemberDef> &members) { //printf(" Function with global scope name '%s' args='%s'\n", // mn->memberName(),args); - MemberNameIterator mli(*mn); - const MemberDef *md = 0; - for (mli.toFirst();(md=mli.current());++mli) + for (const auto &md_p : *mn) { + const MemberDef *md = md_p.get(); const FileDef *fd=md->getFileDef(); const GroupDef *gd=md->getGroupDef(); //printf(" md->name()='%s' md->args='%s' fd=%p gd=%p current=%p ref=%s\n", // md->name().data(),args,fd,gd,currentFile,md->getReference().data()); if ( - ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) && + ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) && md->getNamespaceDef()==0 && md->isLinkable() && - (!checkStatics || (!md->isStatic() && !md->isDefine()) || + (!checkStatics || (!md->isStatic() && !md->isDefine()) || currentFile==0 || fd==currentFile) // statics must appear in the same file - ) + ) { bool match=TRUE; - ArgumentList *argList=0; if (args && !md->isDefine() && qstrcmp(args,"()")!=0) { const ArgumentList &mdAl = md->argumentList(); - ArgumentList argList; - stringToArgumentList(md->getLanguage(),args,argList); + auto argList_p = stringToArgumentList(md->getLanguage(),args); match=matchArguments2( - md->getOuterScope(),fd,mdAl, - Doxygen::globalScope,fd,argList, + md->getOuterScope(),fd,&mdAl, + Doxygen::globalScope,fd,argList_p.get(), checkCV); } - if (match && (forceTagFile==0 || md->getReference()==forceTagFile)) + if (match) { //printf("Found match!\n"); members.append(md); @@ -3894,17 +2307,17 @@ static void findMembersWithSpecificName(MemberName *mn, * 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 + * 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. + * 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' + * 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. @@ -3912,17 +2325,16 @@ static void findMembersWithSpecificName(MemberName *mn, * file fd. */ bool getDefs(const QCString &scName, - const QCString &mbName, + const QCString &mbName, const char *args, - const MemberDef *&md, - const ClassDef *&cd, - const FileDef *&fd, - const NamespaceDef *&nd, + const MemberDef *&md, + const ClassDef *&cd, + const FileDef *&fd, + const NamespaceDef *&nd, const GroupDef *&gd, bool forceEmptyScope, const FileDef *currentFile, - bool checkCV, - const char *forceTagFile + bool checkCV ) { fd=0, md=0, cd=0, nd=0, gd=0; @@ -3937,12 +2349,12 @@ bool getDefs(const QCString &scName, int is,im=0,pm=0; // strip common part of the scope from the scopeName - while ((is=scopeName.findRev("::"))!=-1 && + 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); + scopeName=scopeName.left(is); pm=im+2; } //printf("result after scope corrections scope=%s name=%s\n", @@ -3952,11 +2364,11 @@ bool getDefs(const QCString &scName, QCString mScope; if (memberName.left(9)!="operator " && // treat operator conversion methods // as a special case - (im=memberName.findRev("::"))!=-1 && + (im=memberName.findRev("::"))!=-1 && im<(int)memberName.length()-2 // not A:: ) { - mScope=memberName.left(im); + mScope=memberName.left(im); mName=memberName.right(memberName.length()-im-2); } @@ -3965,7 +2377,7 @@ bool getDefs(const QCString &scName, //printf("mScope='%s' mName='%s'\n",mScope.data(),mName.data()); - MemberName *mn = Doxygen::memberNameSDict->find(mName); + MemberName *mn = Doxygen::memberNameLinkedMap->find(mName); //printf("mName=%s mn=%p\n",mName.data(),mn); if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName @@ -3985,41 +2397,43 @@ bool getDefs(const QCString &scName, className=mScope; } - const MemberDef *tmd=0; - const ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className,&tmd); + SymbolResolver resolver; + const ClassDef *fcd=resolver.resolveClass(Doxygen::globalScope,className); + const MemberDef *tmd=resolver.getTypedef(); + if (fcd==0 && className.find('<')!=-1) // try without template specifiers as well { QCString nameWithoutTemplates = stripTemplateSpecifiersFromScope(className,FALSE); - fcd=getResolvedClass(Doxygen::globalScope,0,nameWithoutTemplates,&tmd); + fcd=resolver.resolveClass(Doxygen::globalScope,nameWithoutTemplates); + tmd=resolver.getTypedef(); } //printf("Trying class scope %s: fcd=%p tmd=%p\n",className.data(),fcd,tmd); // todo: fill in correct fileScope! if (fcd && // is it a documented class - fcd->isLinkable() + fcd->isLinkable() ) { //printf(" Found fcd=%p\n",fcd); - MemberNameIterator mmli(*mn); - MemberDef *mmd; - int mdist=maxInheritanceDepth; - ArgumentList argList; + int mdist=maxInheritanceDepth; + std::unique_ptr<ArgumentList> argList; if (args) { - stringToArgumentList(fcd->getLanguage(),args,argList); + argList = stringToArgumentList(fcd->getLanguage(),args); } - for (mmli.toFirst();(mmd=mmli.current());++mmli) + for (const auto &mmd_p : *mn) { + MemberDef *mmd = mmd_p.get(); if (!mmd->isStrongEnumValue()) { const ArgumentList &mmdAl = mmd->argumentList(); bool match=args==0 || - matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl, - fcd, fcd->getFileDef(),argList, + matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),&mmdAl, + fcd, fcd->getFileDef(),argList.get(), checkCV); //printf("match=%d\n",match); if (match) { - ClassDef *mcd=mmd->getClassDef(); + const ClassDef *mcd=mmd->getClassDef(); if (mcd) { int m=minClassDistance(fcd,mcd); @@ -4037,11 +2451,12 @@ bool getDefs(const QCString &scName, // 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) + for (const auto &mmd_p : *mn) { + MemberDef *mmd = mmd_p.get(); //if (mmd->isLinkable()) //{ - ClassDef *mcd=mmd->getClassDef(); + const ClassDef *mcd=mmd->getClassDef(); //printf(" >Class %s found\n",mcd->name().data()); if (mcd) { @@ -4058,9 +2473,9 @@ bool getDefs(const QCString &scName, } } //printf(" >Success=%d\n",mdist<maxInheritanceDepth); - if (mdist<maxInheritanceDepth) + if (mdist<maxInheritanceDepth) { - if (!md->isLinkable() || md->isStrongEnumValue()) + if (!md->isLinkable() || md->isStrongEnumValue()) { md=0; // avoid returning things we cannot link to cd=0; @@ -4073,7 +2488,7 @@ bool getDefs(const QCString &scName, return TRUE; /* found match */ } } - } + } if (tmd && tmd->isEnumerate() && tmd->isStrong()) // scoped enum { //printf("Found scoped enum!\n"); @@ -4117,18 +2532,18 @@ bool getDefs(const QCString &scName, if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function? { //printf("Global symbol\n"); - MemberNameIterator mmli(*mn); - MemberDef *mmd, *fuzzy_mmd = 0; - ArgumentList argList; + const MemberDef *fuzzy_mmd = 0; + std::unique_ptr<ArgumentList> argList; bool hasEmptyArgs = args && qstrcmp(args, "()") == 0; if (args) { - stringToArgumentList(SrcLangExt_Cpp, args, argList); + argList = stringToArgumentList(SrcLangExt_Cpp, args); } - for (mmli.toFirst(); (mmd = mmli.current()); ++mmli) + for (const auto &mmd_p : *mn) { + const MemberDef *mmd = mmd_p.get(); if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) || !mmd->getClassDef()) { @@ -4137,16 +2552,18 @@ bool getDefs(const QCString &scName, if (!args) { + fuzzy_mmd = mmd; break; } - ArgumentList &mmdAl = mmd->argumentList(); - if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl, - Doxygen::globalScope,mmd->getFileDef(),argList, + const ArgumentList &mmdAl = mmd->argumentList(); + if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),&mmdAl, + Doxygen::globalScope,mmd->getFileDef(),argList.get(), checkCV ) ) { + fuzzy_mmd = mmd; break; } @@ -4156,12 +2573,10 @@ bool getDefs(const QCString &scName, } } - mmd = mmd ? mmd : fuzzy_mmd; - - if (mmd && !mmd->isStrongEnumValue()) + if (fuzzy_mmd && !fuzzy_mmd->isStrongEnumValue()) { - md = mmd; - cd = mmd->getClassDef(); + md = fuzzy_mmd; + cd = fuzzy_mmd->getClassDef(); return TRUE; } } @@ -4170,7 +2585,7 @@ bool getDefs(const QCString &scName, // maybe an namespace, file or group member ? //printf("Testing for global symbol scopeName='%s' mScope='%s' :: mName='%s'\n", // scopeName.data(),mScope.data(),mName.data()); - if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known + if ((mn=Doxygen::functionNameLinkedMap->find(mName))) // name is known { //printf(" >symbol name found\n"); NamespaceDef *fnd=0; @@ -4187,31 +2602,31 @@ bool getDefs(const QCString &scName, namespaceName=mScope.copy(); } //printf("Trying namespace %s\n",namespaceName.data()); - if (!namespaceName.isEmpty() && - (fnd=Doxygen::namespaceSDict->find(namespaceName)) && + if (!namespaceName.isEmpty() && + (fnd=Doxygen::namespaceLinkedMap->find(namespaceName)) && fnd->isLinkable() ) { //printf("Symbol inside existing namespace '%s' count=%d\n", // namespaceName.data(),mn->count()); bool found=FALSE; - MemberNameIterator mmli(*mn); - const MemberDef *mmd; - for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) + for (const auto &mmd_p : *mn) { + const MemberDef *mmd = mmd_p.get(); //printf("mmd->getNamespaceDef()=%p fnd=%p\n", // mmd->getNamespaceDef(),fnd); const MemberDef *emd = mmd->getEnumScope(); if (emd && emd->isStrong()) { //printf("yes match %s<->%s!\n",mScope.data(),emd->localName().data()); - if (emd->getNamespaceDef()==fnd && + if (emd->getNamespaceDef()==fnd && rightScopeMatch(mScope,emd->localName())) { //printf("found it!\n"); nd=fnd; md=mmd; found=TRUE; + break; } else { @@ -4223,14 +2638,13 @@ bool getDefs(const QCString &scName, else if (mmd->getOuterScope()==fnd /* && mmd->isLinkable() */ ) { // namespace is found bool match=TRUE; - ArgumentList argList; if (args && qstrcmp(args,"()")!=0) { const ArgumentList &mmdAl = mmd->argumentList(); - stringToArgumentList(mmd->getLanguage(),args,argList); + auto argList_p = stringToArgumentList(mmd->getLanguage(),args); match=matchArguments2( - mmd->getOuterScope(),mmd->getFileDef(),mmdAl, - fnd,mmd->getFileDef(),argList, + mmd->getOuterScope(),mmd->getFileDef(),&mmdAl, + fnd,mmd->getFileDef(),argList_p.get(), checkCV); } if (match) @@ -4238,26 +2652,29 @@ bool getDefs(const QCString &scName, nd=fnd; md=mmd; found=TRUE; + break; } } } - if (!found && args && !qstrcmp(args,"()")) - // no exact match found, but if args="()" an arbitrary + if (!found && args && !qstrcmp(args,"()")) + // no exact match found, but if args="()" an arbitrary // member will do { - for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) + for (const auto &mmd_p : *mn) { + const MemberDef *mmd = mmd_p.get(); if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ ) { nd=fnd; md=mmd; found=TRUE; + break; } } } if (found) { - if (!md->isLinkable()) + if (!md->isLinkable()) { md=0; // avoid returning things we cannot link to nd=0; @@ -4274,10 +2691,9 @@ bool getDefs(const QCString &scName, else { //printf("not a namespace\n"); - MemberNameIterator mmli(*mn); - MemberDef *mmd; - for (mmli.toFirst();(mmd=mmli.current());++mmli) + for (const auto &mmd_p : *mn) { + const MemberDef *mmd = mmd_p.get(); const MemberDef *tmd = mmd->getEnumScope(); //printf("try member %s tmd=%s\n",mmd->name().data(),tmd?tmd->name().data():"<none>"); int ni=namespaceName.findRev("::"); @@ -4314,31 +2730,34 @@ bool getDefs(const QCString &scName, { QList<MemberDef> members; // search for matches with strict static checking - findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members); + findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,members); if (members.count()==0) // nothing found { // search again without strict static checking - findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members); + findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,members); } //printf("found %d members\n",members.count()); if (members.count()!=1 && args && !qstrcmp(args,"()")) { // no exact match found, but if args="()" an arbitrary // member will do - MemberNameIterator mni(*mn); - for (mni.toLast();(md=mni.current());--mni) + //MemberNameIterator mni(*mn); + //for (mni.toLast();(md=mni.current());--mni) + for (auto it = mn->rbegin(); it!=mn->rend(); ++it) { - //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(); - const MemberDef *tmd = md->getEnumScope(); + const auto &mmd_p = *it; + const MemberDef *mmd = mmd_p.get(); + //printf("Found member '%s'\n",mmd->name().data()); + //printf("member is linkable mmd->name()='%s'\n",mmd->name().data()); + fd=mmd->getFileDef(); + gd=mmd->getGroupDef(); + const MemberDef *tmd = mmd->getEnumScope(); if ( (gd && gd->isLinkable()) || (fd && fd->isLinkable()) || (tmd && tmd->isStrong()) ) { - members.append(md); + members.append(mmd); } } } @@ -4351,7 +2770,7 @@ bool getDefs(const QCString &scName, QListIterator<MemberDef> mit(members); for (mit.toFirst();(md=mit.current());++mit) { - if (md->getFileDef() && md->getFileDef()->name() == currentFile->name()) + if (md->getFileDef() && md->getFileDef()->name() == currentFile->name()) { break; // found match in the current file } @@ -4366,7 +2785,7 @@ bool getDefs(const QCString &scName, md=members.getLast(); } } - if (md && (md->getEnumScope()==0 || !md->getEnumScope()->isStrong())) + if (md && (md->getEnumScope()==0 || !md->getEnumScope()->isStrong())) // found a matching global member, that is not a scoped enum value (or uniquely matches) { fd=md->getFileDef(); @@ -4384,14 +2803,14 @@ bool getDefs(const QCString &scName, /*! * Searches for a scope definition given its name as a string via parameter - * `scope`. + * `scope`. * - * The parameter `docScope` is a string representing the name of the scope in + * 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` + * 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. @@ -4408,7 +2827,7 @@ static bool getScopeDefs(const char *docScope,const char *scope, bool explicitGlobalScope=FALSE; if (scopeName.at(0)==':' && scopeName.at(1)==':') { - scopeName=scopeName.right(scopeName.length()-2); + scopeName=scopeName.right(scopeName.length()-2); explicitGlobalScope=TRUE; } if (scopeName.isEmpty()) @@ -4429,11 +2848,11 @@ static bool getScopeDefs(const char *docScope,const char *scope, //(cd=getClass(fullName+"-g")) // C# generic ) && cd->isLinkable()) { - return TRUE; // class link written => quit + return TRUE; // class link written => quit } - else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable()) + else if ((nd=Doxygen::namespaceLinkedMap->find(fullName)) && nd->isLinkable()) { - return TRUE; // namespace link written => quit + return TRUE; // namespace link written => quit } if (scopeOffset==0) { @@ -4454,10 +2873,10 @@ static bool isLowerCase(QCString &s) if (p==0) return TRUE; int c; while ((c=*p++)) if (!islower(c)) return FALSE; - return TRUE; + return TRUE; } -/*! Returns an object to reference to given its name and context +/*! 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, @@ -4501,10 +2920,10 @@ bool resolveRef(/* in */ const char *scName, ClassDef *cd=0; NamespaceDef *nd=0; - // the following if() was commented out for releases in the range + // the following if() was commented out for releases in the range // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787. if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName)) - { // link to lower case only name => do not try to autolink + { // link to lower case only name => do not try to autolink return FALSE; } @@ -4524,7 +2943,7 @@ bool resolveRef(/* in */ const char *scName, } return TRUE; } - else if (scName==fullName || (!inSeeBlock && scopePos==-1)) + else if (scName==fullName || (!inSeeBlock && scopePos==-1)) // nothing to link => output plain text { //printf("found scName=%s fullName=%s scName==fullName=%d " @@ -4544,7 +2963,7 @@ bool resolveRef(/* in */ const char *scName, if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos); // strip template specifier - // TODO: match against the correct partial template instantiation + // TODO: match against the correct partial template instantiation int templPos=nameStr.find('<'); bool tryUnspecializedVersion = FALSE; if (templPos!=-1 && nameStr.find("operator")==-1) @@ -4584,11 +3003,11 @@ bool resolveRef(/* in */ const char *scName, ) { //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd); - if (checkScope && md && md->getOuterScope()==Doxygen::globalScope && + if (checkScope && md && md->getOuterScope()==Doxygen::globalScope && !md->isStrongEnumValue() && (!scopeStr.isEmpty() || nameStr.find("::")>0)) { - // we did find a member, but it is a global one while we were explicitly + // we did find a member, but it is a global one while we were explicitly // looking for a scoped variable. See bug 616387 for an example why this check is needed. // note we do need to support autolinking to "::symbol" hence the >0 //printf("not global member!\n"); @@ -4607,7 +3026,7 @@ bool resolveRef(/* in */ const char *scName, // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data()); return TRUE; } - else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr))) + else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupLinkedMap->find(nameStr))) { // group link *resContext=gd; return TRUE; @@ -4615,7 +3034,7 @@ bool resolveRef(/* in */ const char *scName, else if (tsName.find('.')!=-1) // maybe a link to a file { bool ambig; - fd=findFileDef(Doxygen::inputNameDict,tsName,ambig); + fd=findFileDef(Doxygen::inputNameLinkedMap,tsName,ambig); if (fd && !ambig) { *resContext=fd; @@ -4667,20 +3086,20 @@ QCString linkToText(SrcLangExt lang,const char *link,bool isFileName) #if 0 /* * generate a reference to a class, namespace or member. - * 'scName' is the name of the scope that contains the documentation + * '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 the following formats: * 1) "ScopeName" - * 2) "memberName()" one of the (overloaded) function or define + * 2) "memberName()" one of the (overloaded) function or define * with name memberName. - * 3) "memberName(...)" a specific (overloaded) function or define + * 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" + * 5) ("ScopeName::")+"memberName()" + * 6) ("ScopeName::")+"memberName(...)" + * 7) ("ScopeName::")+"memberName" * instead of :: the \# symbol may also be used. */ @@ -4752,20 +3171,20 @@ bool resolveLink(/* in */ const char *scName, const ClassDef *cd; const DirDef *dir; const NamespaceDef *nd; - SectionInfo *si=0; + const SectionInfo *si=0; bool ambig; if (linkRef.isEmpty()) // no reference name! { return FALSE; } - else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page + else if ((pd=Doxygen::pageLinkedMap->find(linkRef))) // link to a page { - const GroupDef *gd = pd->getGroupDef(); + gd = pd->getGroupDef(); if (gd) { - if (!pd->name().isEmpty()) si=Doxygen::sectionDict->find(pd->name()); + if (!pd->name().isEmpty()) si=SectionManager::instance().find(pd->name()); *resContext=gd; - if (si) resAnchor = si->label; + if (si) resAnchor = si->label(); } else { @@ -4773,23 +3192,23 @@ bool resolveLink(/* in */ const char *scName, } return TRUE; } - else if ((si=Doxygen::sectionDict->find(linkRef))) + else if ((si=SectionManager::instance().find(linkRef))) { - *resContext=si->definition; - resAnchor = si->label; + *resContext=si->definition(); + resAnchor = si->label(); return TRUE; } - else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example + else if ((pd=Doxygen::exampleLinkedMap->find(linkRef))) // link to an example { *resContext=pd; return TRUE; } - else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group + else if ((gd=Doxygen::groupLinkedMap->find(linkRef))) // link to a group { *resContext=gd; return TRUE; } - else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link + else if ((fd=findFileDef(Doxygen::inputNameLinkedMap,linkRef,ambig)) // file link && fd->isLinkable()) { *resContext=fd; @@ -4819,12 +3238,12 @@ bool resolveLink(/* in */ const char *scName, // resAnchor=cd->anchor(); // return TRUE; // } - else if ((nd=Doxygen::namespaceSDict->find(linkRef))) + else if ((nd=Doxygen::namespaceLinkedMap->find(linkRef))) { *resContext=nd; return TRUE; } - else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/")) + else if ((dir=Doxygen::dirLinkedMap->find(QFileInfo(linkRef).absFilePath().utf8()+"/")) && dir->isLinkable()) // TODO: make this location independent like filedefs { *resContext=dir; @@ -4842,7 +3261,7 @@ bool resolveLink(/* in */ const char *scName, //---------------------------------------------------------------------- // 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'. +// 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. @@ -4860,14 +3279,14 @@ bool generateLink(OutputDocInterface &od,const char *clName, if (compound) // link to compound { if (lt==0 && anchor.isEmpty() && /* compound link */ - compound->definitionType()==Definition::TypeGroup /* is group */ + compound->definitionType()==Definition::TypeGroup /* is group */ ) { - linkText=(dynamic_cast<const GroupDef *>(compound))->groupTitle(); // use group's title as link + linkText=(toGroupDef(compound))->groupTitle(); // use group's title as link } else if (compound->definitionType()==Definition::TypeFile) { - linkText=linkToText(compound->getLanguage(),lt,TRUE); + linkText=linkToText(compound->getLanguage(),lt,TRUE); } od.writeObjectLink(compound->getReference(), compound->getOutputFileBase(),anchor,linkText); @@ -4896,12 +3315,12 @@ void generateFileRef(OutputDocInterface &od,const char *name,const char *text) //FileInfo *fi; FileDef *fd; bool ambig; - if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) && - fd->isLinkable()) + if ((fd=findFileDef(Doxygen::inputNameLinkedMap,name,ambig)) && + fd->isLinkable()) // link to documented input file od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText); else - od.docify(linkText); + od.docify(linkText); } //---------------------------------------------------------------------- @@ -4942,21 +3361,24 @@ struct FindFileCacheElem bool isAmbig; }; -static QCache<FindFileCacheElem> g_findFileDefCache(5000); +static Cache<std::string,FindFileCacheElem> g_findFileDefCache(5000); -FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig) +static std::mutex g_findFileDefMutex; + +FileDef *findFileDef(const FileNameLinkedMap *fnMap,const char *n,bool &ambig) { ambig=FALSE; if (n==0) return 0; + std::unique_lock<std::mutex> lock(g_findFileDefMutex); + const int maxAddrSize = 20; char addr[maxAddrSize]; - qsnprintf(addr,maxAddrSize,"%p:",fnDict); + qsnprintf(addr,maxAddrSize,"%p:",(void*)fnMap); QCString key = addr; key+=n; - g_findFileDefCache.setAutoDelete(TRUE); - FindFileCacheElem *cachedResult = g_findFileDefCache.find(key); + FindFileCacheElem *cachedResult = g_findFileDefCache.find(key.str()); //printf("key=%s cachedResult=%p\n",key.data(),cachedResult); if (cachedResult) { @@ -4966,63 +3388,56 @@ FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig) } else { - cachedResult = new FindFileCacheElem(0,FALSE); + cachedResult = g_findFileDefCache.insert(key.str(),FindFileCacheElem(0,FALSE)); } QCString name=QDir::cleanDirPath(n).utf8(); QCString path; int slashPos; - FileName *fn; + const 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); + name=name.right(name.length()-slashPos-1); //printf("path=%s name=%s\n",path.data(),name.data()); } if (name.isEmpty()) goto exit; - if ((fn=(*fnDict)[name])) + if ((fn=fnMap->find(name))) { //printf("fn->count()=%d\n",fn->count()); - if (fn->count()==1) + if (fn->size()==1) { - FileDef *fd = fn->getFirst(); -#if defined(_WIN32) || defined(__MACOSX__) || defined(__CYGWIN__) // Windows or MacOSX - bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower(); -#else // Unix - bool isSamePath = fd->getPath().right(path.length())==path; -#endif + const std::unique_ptr<FileDef> &fd = fn->front(); + bool isSamePath = Portable::fileSystemIsCaseSensitive() ? + fd->getPath().right(path.length())==path : + fd->getPath().right(path.length()).lower()==path.lower(); if (path.isEmpty() || isSamePath) { - cachedResult->fileDef = fd; - g_findFileDefCache.insert(key,cachedResult); - //printf("=1 ===> add to cache %p\n",fd); - return fd; + cachedResult->fileDef = fd.get(); + return fd.get(); } } else // file name alone is ambiguous { int count=0; - FileNameIterator fni(*fn); - FileDef *fd; FileDef *lastMatch=0; QCString pathStripped = stripFromIncludePath(path); - for (fni.toFirst();(fd=fni.current());++fni) + for (const auto &fd_p : *fn) { + FileDef *fd = fd_p.get(); QCString fdStripPath = stripFromIncludePath(fd->getPath()); - if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped) - { - count++; - lastMatch=fd; + if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped) + { + count++; + lastMatch=fd; } } - //printf(">1 ===> add to cache %p\n",fd); ambig=(count>1); cachedResult->isAmbig = ambig; cachedResult->fileDef = lastMatch; - g_findFileDefCache.insert(key,cachedResult); return lastMatch; } } @@ -5031,15 +3446,13 @@ FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig) //printf("not found!\n"); } exit: - //printf("0 ===> add to cache %p: %s\n",cachedResult,n); - g_findFileDefCache.insert(key,cachedResult); //delete cachedResult; return 0; } //---------------------------------------------------------------------- -QCString showFileDefMatches(const FileNameDict *fnDict,const char *n) +QCString showFileDefMatches(const FileNameLinkedMap *fnMap,const char *n) { QCString result; QCString name=n; @@ -5048,14 +3461,12 @@ QCString showFileDefMatches(const FileNameDict *fnDict,const char *n) if (slashPos!=-1) { path=name.left(slashPos+1); - name=name.right(name.length()-slashPos-1); + name=name.right(name.length()-slashPos-1); } - FileName *fn; - if ((fn=(*fnDict)[name])) + const FileName *fn; + if ((fn=fnMap->find(name))) { - FileNameIterator fni(*fn); - FileDef *fd; - for (fni.toFirst();(fd=fni.current());++fni) + for (const auto &fd : *fn) { if (path.isEmpty() || fd->getPath().right(path.length())==path) { @@ -5068,118 +3479,6 @@ QCString showFileDefMatches(const FileNameDict *fnDict,const char *n) //---------------------------------------------------------------------- -/// substitute all occurrences of \a src in \a s by \a dst -QCString substitute(const QCString &s,const QCString &src,const QCString &dst) -{ - if (s.isEmpty() || src.isEmpty()) return s; - const char *p, *q; - int srcLen = src.length(); - int dstLen = dst.length(); - int resLen; - if (srcLen!=dstLen) - { - int count; - for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++; - resLen = s.length()+count*(dstLen-srcLen); - } - else // result has same size as s - { - resLen = s.length(); - } - QCString result(resLen+1); - char *r; - for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen) - { - int l = (int)(q-p); - memcpy(r,p,l); - r+=l; - - if (dst) memcpy(r,dst,dstLen); - r+=dstLen; - } - qstrcpy(r,p); - //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data()); - return result; -} - - -/// substitute all occurrences of \a src in \a s by \a dst, but skip -/// each consecutive sequence of \a src where the number consecutive -/// \a src matches \a skip_seq; if \a skip_seq is negative, skip any -/// number of consecutive \a src -QCString substitute(const QCString &s,const QCString &src,const QCString &dst,int skip_seq) -{ - if (s.isEmpty() || src.isEmpty()) return s; - const char *p, *q; - int srcLen = src.length(); - int dstLen = dst.length(); - int resLen; - if (srcLen!=dstLen) - { - int count; - for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++; - resLen = s.length()+count*(dstLen-srcLen); - } - else // result has same size as s - { - resLen = s.length(); - } - QCString result(resLen+1); - char *r; - for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen) - { - // search a consecutive sequence of src - int seq = 0, skip = 0; - if (skip_seq) - { - for (const char *n=q+srcLen; qstrncmp(n,src,srcLen)==0; seq=1+skip, n+=srcLen) - ++skip; // number of consecutive src after the current one - - // verify the allowed number of consecutive src to skip - if (skip_seq > 0 && skip_seq != seq) - seq = skip = 0; - } - - // skip a consecutive sequence of src when necessary - int l = (int)((q + seq * srcLen)-p); - memcpy(r,p,l); - r+=l; - - if (skip) - { - // skip only the consecutive src found after the current one - q += skip * srcLen; - // the next loop will skip the current src, aka (p=q+srcLen) - continue; - } - - if (dst) memcpy(r,dst,dstLen); - r+=dstLen; - } - qstrcpy(r,p); - result.resize(strlen(result.data())+1); - //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data()); - return result; -} - -/// substitute all occurrences of \a srcChar in \a s by \a dstChar -QCString substitute(const QCString &s,char srcChar,char dstChar) -{ - int l=s.length(); - QCString result(l+1); - char *q=result.rawData(); - if (l>0) - { - const char *p=s.data(); - char c; - while ((c=*p++)) *q++ = (c==srcChar) ? dstChar : c; - } - *q='\0'; - return result; -} - -//---------------------------------------------------------------------- - QCString substituteKeywords(const QCString &s,const char *title, const char *projName,const char *projNum,const char *projBrief) { @@ -5188,7 +3487,7 @@ QCString substituteKeywords(const QCString &s,const char *title, result = substitute(result,"$datetime",dateToString(TRUE)); result = substitute(result,"$date",dateToString(FALSE)); result = substitute(result,"$year",yearToString()); - result = substitute(result,"$doxygenversion",getVersion()); + result = substitute(result,"$doxygenversion",getDoxygenVersion()); result = substitute(result,"$projectname",projName); result = substitute(result,"$projectnumber",projNum); result = substitute(result,"$projectbrief",projBrief); @@ -5201,15 +3500,14 @@ QCString substituteKeywords(const QCString &s,const char *title, /*! Returns the character index within \a name of the first prefix * in Config_getList(IGNORE_PREFIX) that matches \a name at the left hand side, * or zero if no match was found - */ + */ int getPrefixIndex(const QCString &name) { if (name.isEmpty()) return 0; - static QStrList &sl = Config_getList(IGNORE_PREFIX); - char *s = sl.first(); - while (s) + const StringVector &sl = Config_getList(IGNORE_PREFIX); + for (const auto &s : sl) { - const char *ps=s; + const char *ps=s.c_str(); const char *pd=name.data(); int i=0; while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++; @@ -5217,48 +3515,30 @@ int getPrefixIndex(const QCString &name) { 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->setVisited(FALSE); - } -} -//---------------------------------------------------------------------------- - bool classHasVisibleChildren(const ClassDef *cd) { - BaseClassList *bcl; + BaseClassList bcl; if (cd->getLanguage()==SrcLangExt_VHDL) // reverse baseClass/subClass relation { - if (cd->baseClasses()==0) return FALSE; + if (cd->baseClasses().empty()) return FALSE; bcl=cd->baseClasses(); } - else + else { - if (cd->subClasses()==0) return FALSE; + if (cd->subClasses().empty()) return FALSE; bcl=cd->subClasses(); } - BaseClassListIterator bcli(*bcl); - for ( ; bcli.current() ; ++bcli) + for (const auto &bcd : bcl) { - if (bcli.current()->classDef->isVisibleInHierarchy()) + if (bcd.classDef->isVisibleInHierarchy()) { return TRUE; } @@ -5266,48 +3546,90 @@ bool classHasVisibleChildren(const ClassDef *cd) return FALSE; } - //---------------------------------------------------------------------------- -void initClassHierarchy(ClassSDict *cl) +bool hasVisibleRoot(const BaseClassList &bcl) { - ClassSDict::Iterator cli(*cl); - ClassDef *cd; - for ( ; (cd=cli.current()); ++cli) + for (const auto &bcd : bcl) { - cd->setVisited(FALSE); - initBaseClassHierarchy(cd->baseClasses()); + const ClassDef *cd=bcd.classDef; + if (cd->isVisibleInHierarchy()) return true; + if (hasVisibleRoot(cd->baseClasses())) return true; } + return false; } -//---------------------------------------------------------------------------- +//---------------------------------------------------------------------- -bool hasVisibleRoot(const BaseClassList *bcl) +// copies the next UTF8 character from input stream into buffer ids +// returns the size of the character in bytes (or 0 if it is invalid) +// the character itself will be copied as a UTF-8 encoded string to ids. +int getUtf8Char(const char *input,char ids[MAX_UTF8_CHAR_SIZE],CaseModifier modifier) { - if (bcl) + int inputLen=1; + const unsigned char uc = (unsigned char)*input; + bool validUTF8Char = false; + if (uc <= 0xf7) { - BaseClassListIterator bcli(*bcl); - for ( ; bcli.current(); ++bcli) + const char* pt = input+1; + int l = 0; + if ((uc&0x80)==0x00) + { + switch (modifier) + { + case CaseModifier::None: ids[0]=*input; break; + case CaseModifier::ToUpper: ids[0]=(char)toupper(*input); break; + case CaseModifier::ToLower: ids[0]=(char)tolower(*input); break; + } + l=1; // 0xxx.xxxx => normal single byte ascii character + } + else + { + ids[ 0 ] = *input; + if ((uc&0xE0)==0xC0) + { + l=2; // 110x.xxxx: >=2 byte character + } + if ((uc&0xF0)==0xE0) + { + l=3; // 1110.xxxx: >=3 byte character + } + if ((uc&0xF8)==0xF0) + { + l=4; // 1111.0xxx: >=4 byte character + } + } + validUTF8Char = l>0; + for (int m=1; m<l && validUTF8Char; ++m) { - const ClassDef *cd=bcli.current()->classDef; - if (cd->isVisibleInHierarchy()) return TRUE; - hasVisibleRoot(cd->baseClasses()); + unsigned char ct = (unsigned char)*pt; + if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character + { + validUTF8Char=false; + } + else + { + ids[ m ] = *pt++; + } + } + if (validUTF8Char) // got a valid unicode character + { + ids[ l ] = 0; + inputLen=l; } } - return FALSE; + return inputLen; } -//---------------------------------------------------------------------- // note that this function is not reentrant due to the use of static growBuf! QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore) { - static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); - static bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES); - static GrowBuf growBuf; - growBuf.clear(); + bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); + bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES); if (name==0) return ""; - char c; + GrowBuf growBuf; + signed char c; const char *p=name; while ((c=*p++)!=0) { @@ -5340,57 +3662,29 @@ QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscor case '@': growBuf.addStr("_0d"); break; case ']': growBuf.addStr("_0e"); break; case '[': growBuf.addStr("_0f"); break; - default: + case '#': growBuf.addStr("_0g"); break; + default: if (c<0) { - char ids[5]; - const unsigned char uc = (unsigned char)c; - bool doEscape = TRUE; - if (allowUnicodeNames && uc <= 0xf7) + char ids[MAX_UTF8_CHAR_SIZE]; + bool doEscape = true; + if (allowUnicodeNames) { - const char* pt = p; - ids[ 0 ] = c; - int l = 0; - if ((uc&0xE0)==0xC0) - { - l=2; // 11xx.xxxx: >=2 byte character - } - if ((uc&0xF0)==0xE0) - { - l=3; // 111x.xxxx: >=3 byte character - } - if ((uc&0xF8)==0xF0) + int charLen = getUtf8Char(p-1,ids); + if (charLen>0) { - l=4; // 1111.xxxx: >=4 byte character - } - doEscape = l==0; - for (int m=1; m<l && !doEscape; ++m) - { - unsigned char ct = (unsigned char)*pt; - if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character - { - doEscape=TRUE; - } - else - { - ids[ m ] = *pt++; - } - } - if ( !doEscape ) // got a valid unicode character - { - ids[ l ] = 0; - growBuf.addStr( ids ); - p += l - 1; + growBuf.addStr(ids); + p+=charLen-1; + doEscape = false; } } if (doEscape) // not a valid unicode char or escaping needed { - static char map[] = "0123456789ABCDEF"; unsigned char id = (unsigned char)c; ids[0]='_'; ids[1]='x'; - ids[2]=map[id>>4]; - ids[3]=map[id&0xF]; + ids[2]=hex[id>>4]; + ids[3]=hex[id&0xF]; ids[4]=0; growBuf.addStr(ids); } @@ -5402,7 +3696,7 @@ QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscor else { growBuf.addChar('_'); - growBuf.addChar(tolower(c)); + growBuf.addChar((char)tolower(c)); } break; } @@ -5454,6 +3748,7 @@ QCString unescapeCharsInString(const char *s) case 'd': result+='@'; p+=2; break; // _0d -> '@' case 'e': result+=']'; p+=2; break; // _0e -> ']' case 'f': result+='['; p+=2; break; // _0f -> '[' + case 'g': result+='#'; p+=2; break; // _0g -> '#' default: // unknown escape, just pass underscore character as-is result+=c; break; @@ -5462,7 +3757,7 @@ QCString unescapeCharsInString(const char *s) default: if (!caseSenseNames && c>='a' && c<='z') // lower to upper case escape, _a -> 'A' { - result+=toupper(*p); + result+=(char)toupper(*p); p++; } else // unknown escape, pass underscore character as-is @@ -5482,7 +3777,7 @@ QCString unescapeCharsInString(const char *s) } /*! This function determines the file name on disk of an item - * given its name, which could be a class name with template + * given its name, which could be a class name with template * arguments, so special characters need to be escaped. */ QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore) @@ -5508,7 +3803,7 @@ QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore) { num = *value; } - result.sprintf("a%05d",num); + result.sprintf("a%05d",num); } else // long names { @@ -5521,47 +3816,19 @@ QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore) QCString sigStr(33); MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig); MD5SigToString(md5_sig,sigStr.rawData(),33); - result=result.left(128-32)+sigStr; + result=result.left(128-32)+sigStr; } } if (createSubdirs) { int l1Dir=0,l2Dir=0; -#if MAP_ALGO==ALGO_COUNT - // old algorithm, has the problem that after regeneration the - // output can be located in a different dir. - if (Doxygen::htmlDirMap==0) - { - Doxygen::htmlDirMap=new QDict<int>(100003); - Doxygen::htmlDirMap->setAutoDelete(TRUE); - } - static int curDirNum=0; - int *dirNum = Doxygen::htmlDirMap->find(result); - if (dirNum==0) // new name - { - Doxygen::htmlDirMap->insert(result,new int(curDirNum)); - l1Dir = (curDirNum)&0xf; // bits 0-3 - l2Dir = (curDirNum>>4)&0xff; // bits 4-11 - curDirNum++; - } - else // existing name - { - l1Dir = (*dirNum)&0xf; // bits 0-3 - l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11 - } -#elif MAP_ALGO==ALGO_CRC16 - // second algorithm based on CRC-16 checksum - int dirNum = qChecksum(result,result.length()); - l1Dir = dirNum&0xf; - l2Dir = (dirNum>>4)&0xff; -#elif MAP_ALGO==ALGO_MD5 - // third algorithm based on MD5 hash + // compute md5 hash to determine sub directory to use 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()); @@ -5598,10 +3865,20 @@ void createSubDirs(QDir &d) int l1,l2; for (l1=0;l1<16;l1++) { - d.mkdir(QCString().sprintf("d%x",l1)); + QCString subdir; + subdir.sprintf("d%x",l1); + if (!d.exists(subdir) && !d.mkdir(subdir)) + { + term("Failed to create output directory '%s'\n",subdir.data()); + } for (l2=0;l2<256;l2++) { - d.mkdir(QCString().sprintf("d%x/d%02x",l1,l2)); + QCString subsubdir; + subsubdir.sprintf("d%x/d%02x",l1,l2); + if (!d.exists(subsubdir) && !d.mkdir(subsubdir)) + { + term("Failed to create output directory '%s'\n",subsubdir.data()); + } } } } @@ -5624,7 +3901,7 @@ void extractNamespaceName(const QCString &scopeName, goto done; } p=clName.length()-2; - while (p>=0 && (i=clName.findRev("::",p))!=-1) + 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()); @@ -5634,7 +3911,7 @@ void extractNamespaceName(const QCString &scopeName, 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"); @@ -5671,12 +3948,12 @@ QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &te ((cd=getClass(scope.left(si)))==0 || cd->templateArguments().empty()) ) { - //printf("Tried '%s'\n",(scope.left(si)+templ).data()); - pi=si+2; + //printf("Tried '%s'\n",(scope.left(si)+templ).data()); + pi=si+2; } if (si==-1) // not nested => append template specifier { - result+=templ; + result+=templ; } else // nested => insert template specifier before after first class name { @@ -5688,7 +3965,7 @@ QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &te return result; } -#if 0 // original version + /*! Strips the scope from a name. Examples: A::B will return A * and A<T>::B<N::C<D> > will return A<T>. */ @@ -5696,55 +3973,11 @@ 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; + int round=0; do { @@ -5754,10 +3987,13 @@ QCString stripScope(const char *name) char c=result.at(p); switch (c) { - case ':': + case ':': // only exit in the case of :: //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data()); - if (p>0 && result.at(p-1)==':') return result.right(l-p-1); + if (p>0 && result.at(p-1)==':' && (count==0 || skipBracket)) + { + return result.right(l-p-1); + } p--; break; case '>': @@ -5781,22 +4017,31 @@ QCString stripScope(const char *name) c=result.at(p--); switch (c) { - case '>': - count++; + case ')': + round++; + break; + case '(': + round--; break; - case '<': - if (p>0) + case '>': // ignore > inside (...) to support e.g. (sizeof(T)>0) inside template parameters + if (round==0) count++; + break; + case '<': + if (round==0) { - if (result.at(p-1) == '<') // skip << operator + if (p>0) { - p--; - break; + if (result.at(p-1) == '<') // skip << operator + { + p--; + break; + } } + count--; + foundMatch = count==0; } - count--; - foundMatch = count==0; break; - default: + default: //printf("c=%c count=%d\n",c,count); break; } @@ -5819,10 +4064,8 @@ QCString stripScope(const char *name) /*! Converts a string to a HTML id string */ QCString convertToId(const char *s) { - static const char hex[] = "0123456789ABCDEF"; - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; const char *p=s; char c; bool first=TRUE; @@ -5848,12 +4091,21 @@ QCString convertToId(const char *s) return growBuf.get(); } +/*! Some strings have been corrected but the requirement regarding the fact + * that an id cannot have a digit at the first position. To overcome problems + * with double labels we always place an "a" in front + */ +QCString correctId(QCString s) +{ + if (s.isEmpty()) return s; + return "a" + s; +} + /*! Converts a string to an XML-encoded string */ QCString convertToXML(const char *s, bool keepEntities) { - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; const char *p=s; char c; while ((c=*p++)) @@ -5886,7 +4138,7 @@ QCString convertToXML(const char *s, bool keepEntities) growBuf.addStr("&"); } break; - case '\'': growBuf.addStr("'"); break; + case '\'': growBuf.addStr("'"); break; case '"': growBuf.addStr("""); break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: @@ -5903,9 +4155,8 @@ QCString convertToXML(const char *s, bool keepEntities) /*! Converts a string to an DocBook-encoded string */ QCString convertToDocBook(const char *s) { - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; const unsigned char *q; int cnt; const unsigned char *p=(const unsigned char *)s; @@ -5947,13 +4198,18 @@ QCString convertToDocBook(const char *s) break; case '\'': growBuf.addStr("'"); break; case '"': growBuf.addStr("""); break; - case '\007': growBuf.addStr("␇"); break; - case 1: case 2: case 3: case 4: case 5: case 6: case 8: - case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: + case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: + case 11: case 12: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: - break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char) - default: growBuf.addChar(c); break; + growBuf.addStr("$"); + growBuf.addChar(hex[static_cast<uchar>(c)>>4]); + growBuf.addChar(hex[static_cast<uchar>(c)&0xF]); + growBuf.addChar(';'); + break; + default: + growBuf.addChar(c); + break; } } growBuf.addChar(0); @@ -5963,9 +4219,8 @@ QCString convertToDocBook(const char *s) /*! Converts a string to a HTML-encoded string */ QCString convertToHtml(const char *s,bool keepEntities) { - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; growBuf.addStr(getHtmlDirEmbeddingChar(getTextDirByConfig(s))); const char *p=s; char c; @@ -5996,12 +4251,27 @@ QCString convertToHtml(const char *s,bool keepEntities) } else { - growBuf.addStr("&"); + growBuf.addStr("&"); } break; - case '\'': growBuf.addStr("'"); break; + case '\'': growBuf.addStr("'"); break; case '"': growBuf.addStr("""); break; - default: growBuf.addChar(c); break; + default: + { + uchar uc = static_cast<uchar>(c); + if (uc<32 && !isspace(c)) + { + growBuf.addStr("$"); + growBuf.addChar(hex[uc>>4]); + growBuf.addChar(hex[uc&0xF]); + growBuf.addChar(';'); + } + else + { + growBuf.addChar(c); + } + } + break; } } growBuf.addChar(0); @@ -6010,9 +4280,8 @@ QCString convertToHtml(const char *s,bool keepEntities) QCString convertToJSString(const char *s, bool applyTextDir) { - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; if (applyTextDir) growBuf.addStr(getJsDirEmbeddingChar(getTextDirByConfig(s))); const char *p=s; @@ -6032,9 +4301,8 @@ QCString convertToJSString(const char *s, bool applyTextDir) QCString convertToPSString(const char *s) { - static GrowBuf growBuf; - growBuf.clear(); if (s==0) return ""; + GrowBuf growBuf; const char *p=s; char c; while ((c=*p++)) @@ -6054,7 +4322,7 @@ QCString convertToLaTeX(const QCString &s,bool insideTabbing,bool keepSpaces) { QGString result; FTextStream t(&result); - filterLatexString(t,s,insideTabbing,FALSE,FALSE,keepSpaces); + filterLatexString(t,s,insideTabbing,false,false,false,keepSpaces); return result.data(); } @@ -6066,8 +4334,7 @@ QCString convertCharEntitiesToUTF8(const QCString &s) static QRegExp entityPat("&[a-zA-Z]+[0-9]*;"); if (s.length()==0) return result; - static GrowBuf growBuf; - growBuf.clear(); + GrowBuf growBuf; int p,i=0,l; while ((p=entityPat.match(s,i,&l))!=-1) { @@ -6106,11 +4373,11 @@ QCString getOverloadDocs() } void addMembersToMemberGroup(MemberList *ml, - MemberGroupSDict **ppMemberGroupSDict, + MemberGroupList *pMemberGroups, const Definition *context) { ASSERT(context!=0); - //printf("addMemberToMemberGroup()\n"); + //printf("addMemberToMemberGroup() context=%s\n",context->name().data()); if (ml==0) return; MemberListIterator mli(*ml); MemberDef *md; @@ -6129,30 +4396,34 @@ void addMembersToMemberGroup(MemberList *ml, int groupId=fmd->getMemberGroupId(); if (groupId!=-1) { - MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId]; - //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId]; - //QCString *pDocs = Doxygen::memberDocDict[groupId]; - if (info) + auto it = Doxygen::memberGroupInfoMap.find(groupId); + if (it!=Doxygen::memberGroupInfoMap.end()) { - if (*ppMemberGroupSDict==0) + auto &info = it->second; + auto mg_it = std::find_if(pMemberGroups->begin(), + pMemberGroups->end(), + [&groupId](const auto &g) + { return g->groupId()==groupId; } + ); + MemberGroup *mg_ptr = 0; + if (mg_it==pMemberGroups->end()) { - *ppMemberGroupSDict = new MemberGroupSDict; - (*ppMemberGroupSDict)->setAutoDelete(TRUE); + auto mg = std::make_unique<MemberGroup>( + context, + groupId, + info->header, + info->doc, + info->docFile, + info->docLine); + mg_ptr = mg.get(); + pMemberGroups->push_back(std::move(mg)); } - MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); - if (mg==0) + else { - mg = new MemberGroup( - groupId, - info->header, - info->doc, - info->docFile, - info->docLine - ); - (*ppMemberGroupSDict)->append(groupId,mg); + mg_ptr = (*mg_it).get(); } - mg->insertMember(fmd); // insert in member group - fmd->setMemberGroup(mg); + mg_ptr->insertMember(fmd); // insert in member group + fmd->setMemberGroup(mg_ptr); } } } @@ -6161,32 +4432,36 @@ void addMembersToMemberGroup(MemberList *ml, 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) + auto it = Doxygen::memberGroupInfoMap.find(groupId); + if (it!=Doxygen::memberGroupInfoMap.end()) + { + auto &info = it->second; + auto mg_it = std::find_if(pMemberGroups->begin(), + pMemberGroups->end(), + [&groupId](const auto &g) + { return g->groupId()==groupId; } + ); + MemberGroup *mg_ptr = 0; + if (mg_it==pMemberGroups->end()) { - *ppMemberGroupSDict = new MemberGroupSDict; - (*ppMemberGroupSDict)->setAutoDelete(TRUE); + auto mg = std::make_unique<MemberGroup>( + context, + groupId, + info->header, + info->doc, + info->docFile, + info->docLine); + mg_ptr = mg.get(); + pMemberGroups->push_back(std::move(mg)); } - MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); - if (mg==0) + else { - mg = new MemberGroup( - groupId, - info->header, - info->doc, - info->docFile, - info->docLine - ); - (*ppMemberGroupSDict)->append(groupId,mg); + mg_ptr = (*mg_it).get(); } md = ml->take(index); // remove from member list - mg->insertMember(md); // insert in member group - mg->setRefItems(info->m_sli); - md->setMemberGroup(mg); + mg_ptr->insertMember(md->resolveAlias()); // insert in member group + mg_ptr->setRefItems(info->m_sli); + md->setMemberGroup(mg_ptr); continue; } } @@ -6241,11 +4516,11 @@ int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCStri int brCount=1; while (te<typeLen && brCount!=0) { - if (type.at(te)=='<') + if (type.at(te)=='<') { if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++; } - if (type.at(te)=='>') + if (type.at(te)=='>') { if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--; } @@ -6253,7 +4528,7 @@ int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCStri } } name = type.mid(i,l); - if (te>ts) + if (te>ts) { templSpec = type.mid(ts,te-ts),tl+=te-ts; pos=i+l+tl; @@ -6292,7 +4567,7 @@ QCString normalizeNonTemplateArgumentsInString( result += name.mid(p,i-p); QCString n = name.mid(i,l); bool found=FALSE; - for (const Argument formArg : formalArgs) + for (const Argument &formArg : formalArgs) { if (formArg.name == n) { @@ -6303,7 +4578,8 @@ QCString normalizeNonTemplateArgumentsInString( if (!found) { // try to resolve the type - const ClassDef *cd = getResolvedClass(context,0,n); + SymbolResolver resolver; + const ClassDef *cd = resolver.resolveClass(context,n); if (cd) { result+=cd->name(); @@ -6334,21 +4610,24 @@ QCString normalizeNonTemplateArgumentsInString( QCString substituteTemplateArgumentsInString( const QCString &name, const ArgumentList &formalArgs, - const ArgumentList &actualArgs) + const std::unique_ptr<ArgumentList> &actualArgs) { //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n", // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data()); if (formalArgs.empty()) return name; QCString result; - static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*"); + static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*"); int p=0,l,i; // for each identifier in the base class name (e.g. B<T> -> B and T) while ((i=re.match(name,p,&l))!=-1) { result += name.mid(p,i-p); QCString n = name.mid(i,l); - auto formIt = formalArgs.begin(); - auto actIt = actualArgs.begin(); + ArgumentList::iterator actIt; + if (actualArgs) + { + actIt = actualArgs->begin(); + } // if n is a template argument, then we substitute it // for its template instance argument. @@ -6360,7 +4639,7 @@ QCString substituteTemplateArgumentsInString( { Argument formArg = *formIt; Argument actArg; - if (actIt!=actualArgs.end()) + if (actualArgs && actIt!=actualArgs->end()) { actArg = *actIt; } @@ -6378,35 +4657,35 @@ QCString substituteTemplateArgumentsInString( { //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n", // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data()); - //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n", - // formArg->name.data(),actArg ? actArg->type.data() : "",actArg ? actArg->name.data() : "" + //printf(">> n='%s' formArg->name='%s' actArg->type='%s' actArg->name='%s'\n", + // n.data(),formArg.name.data(),actIt!=actualArgs.end() ? actIt->type.data() : "",actIt!=actualArgs.end() ? actIt->name.data() : "" // ); - if (formArg.name==n && actIt!=actualArgs.end() && !actArg.type.isEmpty()) // base class is a template argument + if (formArg.name==n && actualArgs && actIt!=actualArgs->end() && !actArg.type.isEmpty()) // base class is a template argument { // replace formal argument with the actual argument of the instance - if (!leftScopeMatch(actArg.type,n)) - // the scope guard is to prevent recursive lockup for - // template<class A> class C : public<A::T>, - // where A::T would become A::T::T here, + if (!leftScopeMatch(actArg.type,n)) + // the scope guard is to prevent recursive lockup for + // template<class A> class C : public<A::T>, + // where A::T would become A::T::T here, // since n==A and actArg->type==A::T // see bug595833 for an example { if (actArg.name.isEmpty()) { - result += actArg.type+" "; + result += actArg.type+" "; found=TRUE; } - else + else // for case where the actual arg is something like "unsigned int" // the "int" part is in actArg->name. { - result += actArg.type+" "+actArg.name+" "; + result += actArg.type+" "+actArg.name+" "; found=TRUE; } } } else if (formArg.name==n && - actIt==actualArgs.end() && + (actualArgs==nullptr || actIt==actualArgs->end()) && !formArg.defval.isEmpty() && formArg.defval!=name /* to prevent recursion */ ) @@ -6416,7 +4695,7 @@ QCString substituteTemplateArgumentsInString( } } else if (formArg.name==n && - actIt==actualArgs.end() && + (actualArgs==nullptr || actIt==actualArgs->end()) && !formArg.defval.isEmpty() && formArg.defval!=name /* to prevent recursion */ ) @@ -6424,7 +4703,7 @@ QCString substituteTemplateArgumentsInString( result += substituteTemplateArgumentsInString(formArg.defval,formalArgs,actualArgs)+" "; found=TRUE; } - if (actIt!=actualArgs.end()) + if (actualArgs && actIt!=actualArgs->end()) { actIt++; } @@ -6441,62 +4720,45 @@ QCString substituteTemplateArgumentsInString( return result.stripWhiteSpace(); } -#if 0 -/*! Makes a deep copy of the list of argument lists \a srcLists. - * Will allocate memory, that is owned by the caller. - */ -QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists) -{ - ASSERT(srcLists!=0); - QList<ArgumentList> *dstLists = new QList<ArgumentList>; - dstLists->setAutoDelete(TRUE); - QListIterator<ArgumentList> sli(*srcLists); - ArgumentList *sl; - for (;(sl=sli.current());++sli) - { - dstLists->append(sl->deepCopy()); - } - return dstLists; -} -#endif -/*! Strips template specifiers from scope \a fullName, except those - * that make up specialized classes. The switch \a parentOnly - * determines whether or not a template "at the end" of a scope - * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will - * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will - * strip both unless A<T> or B<S> are specialized template classes. +/*! 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, \c A<T>::B<S> will + * try to strip `<T>` and not `<S>`, while \a parentOnly is \c FALSE will + * strip both unless `A<T>` or `B<S>` are specialized template classes. */ QCString stripTemplateSpecifiersFromScope(const QCString &fullName, bool parentOnly, QCString *pLastScopeStripped) { + int i=fullName.find('<'); + if (i==-1) return fullName; QCString result; int p=0; int l=fullName.length(); - int i=fullName.find('<'); while (i!=-1) { //printf("1:result+=%s\n",fullName.mid(p,i-p).data()); int e=i+1; - bool done=FALSE; int count=1; - while (e<l && !done) + int round=0; + while (e<l && count>0) { char c=fullName.at(e++); - if (c=='<') - { - count++; - } - else if (c=='>') + switch (c) { - count--; - done = count==0; + case '(': round++; break; + case ')': if (round>0) round--; break; + case '<': if (round==0) count++; break; + case '>': if (round==0) count--; break; + default: + break; } } int si= fullName.find("::",e); - if (parentOnly && si==-1) break; + if (parentOnly && si==-1) break; // we only do the parent scope, so we stop here if needed result+=fullName.mid(p,i-p); @@ -6523,10 +4785,10 @@ QCString stripTemplateSpecifiersFromScope(const QCString &fullName, * Example1: \c A::B and \c B::C will result in \c A::B::C <br> * Example2: \c A and \c B will be \c A::B <br> * Example3: \c A::B and B will be \c A::B - * + * * @param leftScope the left hand part of the scope. * @param rightScope the right hand part of the scope. - * @returns the merged scope. + * @returns the merged scope. */ QCString mergeScopes(const QCString &leftScope,const QCString &rightScope) { @@ -6588,7 +4850,7 @@ int getScopeFragment(const QCString &s,int p,int *l) while (sp<sl && !done) { // TODO: deal with << and >> operators! - char c=s.at(sp++); + c=s.at(sp++); switch(c) { case '<': count++; break; @@ -6612,8 +4874,10 @@ found: PageDef *addRelatedPage(const char *name,const QCString &ptitle, const QCString &doc, - const char *fileName,int startLine, - const std::vector<ListItemInfo> &sli, + const char *fileName, + int docLine, + int startLine, + const RefItemVector &sli, GroupDef *gd, const TagInfo *tagInfo, bool xref, @@ -6622,12 +4886,16 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, { PageDef *pd=0; //printf("addRelatedPage(name=%s gd=%p)\n",name,gd); - if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo) + QCString title=ptitle.stripWhiteSpace(); + if ((pd=Doxygen::pageLinkedMap->find(name)) && !tagInfo) { - if (!xref) warn(fileName,startLine,"multiple use of page label '%s', (other occurrence: %s, line: %d)", - name,pd->docFile().data(),pd->docLine()); + if (!xref && !title.isEmpty() && pd->title()!=title) + { + warn(fileName,startLine,"multiple use of page label '%s', (other occurrence: %s, line: %d)", + name,pd->docFile().data(),pd->getStartBodyLine()); + } // append documentation block to the page. - pd->setDocumentation(doc,fileName,startLine); + pd->setDocumentation(doc,fileName,docLine); //printf("Adding page docs '%s' pi=%p name=%s\n",doc.data(),pd,name); // append (x)refitems to the page. pd->setRefItems(sli); @@ -6635,13 +4903,16 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, else // new page { QCString baseName=name; - if (baseName.right(4)==".tex") + 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=createPageDef(fileName,startLine,baseName,doc,title); + //printf("Appending page '%s'\n",baseName.data()); + pd = Doxygen::pageLinkedMap->add(baseName, + std::unique_ptr<PageDef>( + createPageDef(fileName,docLine,baseName,doc,title))); + pd->setBodySegment(startLine,startLine,-1); pd->setRefItems(sli); pd->setLanguage(lang); @@ -6652,8 +4923,6 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, pd->setFileName(tagInfo->fileName); } - //printf("Appending page '%s'\n",baseName.data()); - Doxygen::pageSDict->append(baseName,pd); if (gd) gd->addPage(pd); @@ -6663,36 +4932,40 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, // a page name is a label as well! QCString file; + QCString orgFile; + int line = -1; if (gd) { file=gd->getOutputFileBase(); + orgFile=gd->getOutputFileBase(); } - else + else { file=pd->getOutputFileBase(); + orgFile=pd->docFile(); + line = pd->getStartBodyLine(); } - SectionInfo *si = Doxygen::sectionDict->find(pd->name()); + const SectionInfo *si = SectionManager::instance().find(pd->name()); if (si) { - if (si->lineNr != -1) + if (si->lineNr() != -1) { - warn(file,-1,"multiple use of section label '%s', (first occurrence: %s, line %d)",pd->name().data(),si->fileName.data(),si->lineNr); + warn(orgFile,line,"multiple use of section label '%s', (first occurrence: %s, line %d)",pd->name().data(),si->fileName().data(),si->lineNr()); } else { - warn(file,-1,"multiple use of section label '%s', (first occurrence: %s)",pd->name().data(),si->fileName.data()); + warn(orgFile,line,"multiple use of section label '%s', (first occurrence: %s)",pd->name().data(),si->fileName().data()); } } else { - si=new SectionInfo( - file,-1,pd->name(),pd->title(),SectionInfo::Page,0,pd->getReference()); + SectionManager::instance().add(pd->name(), + file,-1,pd->title(),SectionType::Page,0,pd->getReference()); //printf("si->label='%s' si->definition=%s si->fileName='%s'\n", // si->label.data(),si->definition?si->definition->name().data():"<none>", // si->fileName.data()); //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data()); //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data()); - Doxygen::sectionDict->append(pd->name(),si); } } } @@ -6701,47 +4974,28 @@ PageDef *addRelatedPage(const char *name,const QCString &ptitle, //---------------------------------------------------------------------------- -void addRefItem(const std::vector<ListItemInfo> &sli, - const char *key, - const char *prefix, const char *name,const char *title,const char *args,Definition *scope) +void addRefItem(const RefItemVector &sli, + const char *key, + const char *prefix, const char *name,const char *title,const char *args,const Definition *scope) { - //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args); + //printf("addRefItem(sli=%d,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",(int)sli.size(),key,prefix,name,title,args); if (key && key[0]!='@') // check for @ to skip anonymous stuff (see bug427012) { - for (const ListItemInfo &lii : sli) - { - RefList *refList = Doxygen::xrefLists->find(lii.type); - if (refList - && - ( - // either not a built-in list or the list is enabled - (lii.type!="todo" || Config_getBool(GENERATE_TODOLIST)) && - (lii.type!="test" || Config_getBool(GENERATE_TESTLIST)) && - (lii.type!="bug" || Config_getBool(GENERATE_BUGLIST)) && - (lii.type!="deprecated" || Config_getBool(GENERATE_DEPRECATEDLIST)) - ) - ) - { - RefItem *item = refList->getRefItem(lii.itemId); - ASSERT(item!=0); - - item->prefix = prefix; - item->scope = scope; - item->name = name; - item->title = title; - item->args = args; - - refList->insertIntoList(key,item); - - } + for (RefItem *item : sli) + { + item->setPrefix(prefix); + item->setScope(scope); + item->setName(name); + item->setTitle(title); + item->setArgs(args); + item->setGroup(key); } } } bool recursivelyAddGroupListToTitle(OutputList &ol,const Definition *d,bool root) { - GroupList *groups = d->partOfGroups(); - if (groups) // write list of group to which this definition belongs + if (!d->partOfGroups().empty()) // write list of group to which this definition belongs { if (root) { @@ -6749,16 +5003,14 @@ bool recursivelyAddGroupListToTitle(OutputList &ol,const Definition *d,bool root ol.disableAllBut(OutputGenerator::Html); ol.writeString("<div class=\"ingroups\">"); } - GroupListIterator gli(*groups); - GroupDef *gd; bool first=true; - for (gli.toFirst();(gd=gli.current());++gli) + for (const auto &gd : d->partOfGroups()) { + if (!first) { ol.writeString(" | "); } else first=false; if (recursivelyAddGroupListToTitle(ol, gd, FALSE)) { ol.writeString(" » "); } - if (!first) { ol.writeString(" | "); } else first=FALSE; ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),0,gd->groupTitle()); } if (root) @@ -6777,7 +5029,7 @@ void addGroupListToTitle(OutputList &ol,const Definition *d) } void filterLatexString(FTextStream &t,const char *str, - bool insideTabbing,bool insidePre,bool insideItem,bool keepSpaces) + bool insideTabbing,bool insidePre,bool insideItem,bool insideTable,bool keepSpaces) { if (str==0) return; //if (strlen(str)<2) stackTrace(); @@ -6812,13 +5064,15 @@ void filterLatexString(FTextStream &t,const char *str, case '%': t << "\\%"; break; case '#': t << "\\#"; break; case '$': t << "\\$"; break; + case '"': t << "\"{}"; break; case '-': t << "-\\/"; break; - case '^': (usedTableLevels()>0) ? t << "\\string^" : t << (char)c; break; + case '^': insideTable ? t << "\\string^" : t << (char)c; break; case '~': t << "\\string~"; break; case ' ': if (keepSpaces) t << "~"; else t << ' '; break; default: - t << (char)c; + if (c<32) t << ' '; // non printable control character + else t << (char)c; break; } } @@ -6870,9 +5124,9 @@ void filterLatexString(FTextStream &t,const char *str, } break; case '*': t << "$\\ast$"; break; - case '_': if (!insideTabbing) t << "\\+"; - t << "\\_"; - if (!insideTabbing) t << "\\+"; + case '_': if (!insideTabbing) t << "\\+"; + t << "\\_"; + if (!insideTabbing) t << "\\+"; break; case '{': t << "\\{"; break; case '}': t << "\\}"; break; @@ -6880,8 +5134,8 @@ void filterLatexString(FTextStream &t,const char *str, case '>': t << "$>$"; break; case '|': t << "$\\vert$"; break; case '~': t << "$\\sim$"; break; - case '[': if (Config_getBool(PDF_HYPERLINKS) || insideItem) - t << "\\mbox{[}"; + case '[': if (Config_getBool(PDF_HYPERLINKS) || insideItem) + t << "\\mbox{[}"; else t << "["; break; @@ -6889,12 +5143,12 @@ void filterLatexString(FTextStream &t,const char *str, if (Config_getBool(PDF_HYPERLINKS) || insideItem) t << "\\mbox{]}"; else - t << "]"; + t << "]"; break; case '-': t << "-\\/"; break; case '\\': t << "\\textbackslash{}"; - break; + break; case '"': t << "\\char`\\\"{}"; break; case '`': t << "\\`{}"; @@ -6904,15 +5158,22 @@ void filterLatexString(FTextStream &t,const char *str, case ' ': if (keepSpaces) { if (insideTabbing) t << "\\>"; else t << '~'; } else t << ' '; break; - default: + default: //if (!insideTabbing && forceBreaks && c!=' ' && *p!=' ') - if (!insideTabbing && - ((c>='A' && c<='Z' && pc!=' ' && pc!='\0' && *p) || (c==':' && pc!=':') || (pc=='.' && isId(c))) + if (!insideTabbing && + ((c>='A' && c<='Z' && pc!=' ' && !(pc>='A' && pc <= 'Z') && pc!='\0' && *p) || (c==':' && pc!=':') || (pc=='.' && isId(c))) ) { t << "\\+"; } - t << (char)c; + if (c<32) + { + t << ' '; // non-printable control character + } + else + { + t << (char)c; + } } } pc = c; @@ -6940,7 +5201,7 @@ QCString latexEscapeLabelName(const char *s) case '}': t << "\\rcurly{}"; break; case '~': t << "````~"; break; // to get it a bit better in index together with other special characters // NOTE: adding a case here, means adding it to while below as well! - default: + default: i=0; // collect as long string as possible, before handing it to docify tmp[i++]=c; @@ -6950,7 +5211,13 @@ QCString latexEscapeLabelName(const char *s) p++; } tmp[i]=0; - filterLatexString(t,tmp,TRUE); + filterLatexString(t,tmp, + true, // insideTabbing + false, // insidePre + false, // insideItem + false, // insideTable + false // keepSpaces + ); break; } } @@ -6979,7 +5246,7 @@ QCString latexEscapeIndexChars(const char *s) case '{': t << "\\lcurly{}"; break; case '}': t << "\\rcurly{}"; break; // NOTE: adding a case here, means adding it to while below as well! - default: + default: i=0; // collect as long string as possible, before handing it to docify tmp[i++]=c; @@ -6989,7 +5256,13 @@ QCString latexEscapeIndexChars(const char *s) p++; } tmp[i]=0; - filterLatexString(t,tmp.data(),TRUE); + filterLatexString(t,tmp.data(), + true, // insideTabbing + false, // insidePre + false, // insideItem + false, // insideTable + false // keepSpaces + ); break; } } @@ -7026,7 +5299,7 @@ QCString latexFilterURL(const char *s) if (s==0) return ""; QGString result; FTextStream t(&result); - const char *p=s; + const signed char *p=(const signed char*)s; char c; while ((c=*p++)) { @@ -7036,7 +5309,15 @@ QCString latexFilterURL(const char *s) case '%': t << "\\%"; break; case '\\': t << "\\\\"; break; default: - t << c; + if (c<0) + { + unsigned char id = (unsigned char)c; + t << "\\%" << hex[id>>4] << hex[id&0xF]; + } + else + { + t << c; + } break; } } @@ -7081,6 +5362,7 @@ QCString rtfFormatBmkStr(const char *name) } } + //printf("Name = %s RTF_tag = %s\n",name,(*tag).data()); return *tag; } @@ -7091,7 +5373,9 @@ bool checkExtension(const char *fName, const char *ext) QCString addHtmlExtensionIfMissing(const char *fName) { - if (QFileInfo(fName).extension(FALSE).isEmpty()) + if (fName==0) return fName; + const char *p = strchr(fName,'.'); + if (p==nullptr) // no extension { return QCString(fName)+Doxygen::htmlFileExtension; } @@ -7118,11 +5402,14 @@ void replaceNamespaceAliases(QCString &scope,int i) while (i>0) { QCString ns = scope.left(i); - QCString *s = Doxygen::namespaceAliasDict[ns]; - if (s) + if (!ns.isEmpty()) { - scope=*s+scope.right(scope.length()-i); - i=s->length(); + auto it = Doxygen::namespaceAliasMap.find(ns.data()); + if (it!=Doxygen::namespaceAliasMap.end()) + { + scope=it->second.data()+scope.right(scope.length()-i); + i=static_cast<int>(it->second.length()); + } } if (i>0 && ns==scope.left(i)) break; } @@ -7163,9 +5450,9 @@ bool findAndRemoveWord(QCString &s,const QCString &word) int p=0,i,l; while ((i=wordExp.match(s,p,&l))!=-1) { - if (s.mid(i,l)==word) + if (s.mid(i,l)==word) { - if (i>0 && isspace((uchar)s.at(i-1))) + if (i>0 && isspace((uchar)s.at(i-1))) i--,l++; else if (i+l<(int)s.length() && isspace((uchar)s.at(i+l))) l++; @@ -7193,10 +5480,11 @@ QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine) // search for leading empty lines int i=0,li=-1,l=s.length(); char c; - while ((c=*p++)) + while ((c=*p)) { - if (c==' ' || c=='\t' || c=='\r') i++; - else if (c=='\n') i++,li=i,docLine++; + if (c==' ' || c=='\t' || c=='\r') i++,p++; + else if (c=='\\' && qstrncmp(p,"\\ilinebr",8)==0) i+=8,li=i,p+=8; + else if (c=='\n') i++,li=i,docLine++,p++; else break; } @@ -7205,9 +5493,10 @@ QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine) p=s.data()+b; while (b>=0) { - c=*p; p--; - if (c==' ' || c=='\t' || c=='\r') b--; - else if (c=='\n') bi=b,b--; + c=*p; + if (c==' ' || c=='\t' || c=='\r') b--,p--; + else if (c=='r' && b>=7 && qstrncmp(p-7,"\\ilinebr",8)==0) bi=b-7,b-=8,p-=8; + else if (c=='\n') bi=b,b--,p--; else break; } @@ -7218,6 +5507,7 @@ QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine) if (bi==-1) bi=l; if (li==-1) li=0; if (bi<=li) return 0; // only empty lines + //printf("docLine='%s' len=%d li=%d bi=%d\n",s.data(),s.length(),li,bi); return s.mid(li,bi-li); } @@ -7249,7 +5539,7 @@ static struct Lang2ExtMap const char *langName; const char *parserName; SrcLangExt parserId; -} +} g_lang2extMap[] = { // language parser parser option @@ -7270,7 +5560,6 @@ g_lang2extMap[] = { "vhdl", "vhdl", SrcLangExt_VHDL }, { "xml", "xml", SrcLangExt_XML }, { "sql", "sql", SrcLangExt_SQL }, - { "tcl", "tcl", SrcLangExt_Tcl }, { "md", "md", SrcLangExt_Markdown }, { 0, 0, (SrcLangExt)0 } }; @@ -7361,9 +5650,9 @@ void initDefaultExtensionMapping() updateLanguageMapping(".f95", "fortran"); updateLanguageMapping(".f03", "fortran"); updateLanguageMapping(".f08", "fortran"); + updateLanguageMapping(".f18", "fortran"); updateLanguageMapping(".vhd", "vhdl"); updateLanguageMapping(".vhdl", "vhdl"); - updateLanguageMapping(".tcl", "tcl"); updateLanguageMapping(".ucf", "vhdl"); updateLanguageMapping(".qsf", "vhdl"); updateLanguageMapping(".md", "md"); @@ -7404,7 +5693,7 @@ QCString getFileNameExtension(QCString fn) //-------------------------------------------------------------------------- -MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope, +static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope, const char *n) { if (scope==0 || @@ -7420,8 +5709,8 @@ MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope, if (name.isEmpty()) return 0; // no name was given - DefinitionIntf *di = Doxygen::symbolMap->find(name); - if (di==0) + auto range = Doxygen::symbolMap.find(name); + if (range.first==range.second) return 0; // could not find any matching symbols // mostly copied from getResolvedClassRec() @@ -7438,40 +5727,21 @@ MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope, int minDistance = 10000; MemberDef *bestMatch = 0; - if (di->definitionType()==DefinitionIntf::TypeSymbolList) + for (auto it=range.first; it!=range.second; ++it) { - //printf("multiple matches!\n"); - // find the closest closest matching definition - DefinitionListIterator dli(*(DefinitionList*)di); - Definition *d; - for (dli.toFirst();(d=dli.current());++dli) + Definition *d = it->second; + if (d->definitionType()==Definition::TypeMember) { - if (d->definitionType()==Definition::TypeMember) + SymbolResolver resolver(fileScope); + int distance = resolver.isAccessibleFromWithExpScope(scope,d,explicitScopePart); + if (distance!=-1 && distance<minDistance) { - g_visitedNamespaces.clear(); - int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); - if (distance!=-1 && distance<minDistance) - { - minDistance = distance; - bestMatch = dynamic_cast<MemberDef *>(d); - //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance); - } + minDistance = distance; + bestMatch = toMemberDef(d); + //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance); } } } - else if (di->definitionType()==Definition::TypeMember) - { - //printf("unique match!\n"); - Definition *d = (Definition *)di; - g_visitedNamespaces.clear(); - int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); - if (distance!=-1 && distance<minDistance) - { - minDistance = distance; - bestMatch = dynamic_cast<MemberDef *>(d); - //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance); - } - } return bestMatch; } @@ -7488,9 +5758,9 @@ bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const char const char *writeUtf8Char(FTextStream &t,const char *s) { - char c=*s++; - t << c; - if (c<0) // multibyte character + uchar c=(uchar)*s++; + t << (char)c; + if (c>=0x80) // multibyte character { if (((uchar)c&0xE0)==0xC0) { @@ -7516,12 +5786,12 @@ const char *writeUtf8Char(FTextStream &t,const char *s) return s; } -int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos) +int nextUtf8CharPosition(const QCString &utf8Str,uint len,uint startPos) { int bytes=1; if (startPos>=len) return len; - char c = utf8Str[startPos]; - if (c<0) // multibyte utf-8 character + uchar c = (uchar)utf8Str[startPos]; + if (c>=0x80) // multibyte utf-8 character { if (((uchar)c&0xE0)==0xC0) { @@ -7570,7 +5840,8 @@ QCString parseCommentAsText(const Definition *scope,const MemberDef *md, if (doc.isEmpty()) return s.data(); FTextStream t(&s); DocNode *root = validatingParseDoc(fileName,lineNr, - (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE); + (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE, + 0,FALSE,FALSE,Config_getBool(MARKDOWN_SUPPORT)); TextDocVisitor *visitor = new TextDocVisitor(t); root->accept(visitor); delete visitor; @@ -7605,9 +5876,8 @@ QCString parseCommentAsText(const Definition *scope,const MemberDef *md, //-------------------------------------------------------------------------------------- -static QDict<void> aliasesProcessed; - -static QCString expandAliasRec(const QCString s,bool allowRecursion=FALSE); +static QCString expandAliasRec(StringUnorderedSet &aliasesProcessed, + const QCString s,bool allowRecursion=FALSE); struct Marker { @@ -7617,8 +5887,8 @@ struct Marker int size; // size of the marker }; -/** For a string \a s that starts with a command name, returns the character - * offset within that string representing the first character after the +/** For a string \a s that starts with a command name, returns the character + * offset within that string representing the first character after the * command. For an alias with argument, this is the offset to the * character just after the argument list. * @@ -7640,30 +5910,30 @@ static int findEndOfCommand(const char *s) QCString args = extractAliasArgs(p,0); i+=args.length(); } - i+=p-s; + i+=(int)(p-s); } return i; } -/** Replaces the markers in an alias definition \a aliasValue - * with the corresponding values found in the comma separated argument +/** Replaces the markers in an alias definition \a aliasValue + * with the corresponding values found in the comma separated argument * list \a argList and the returns the result after recursive alias expansion. */ -static QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList) +static QCString replaceAliasArguments(StringUnorderedSet &aliasesProcessed, + const QCString &aliasValue,const QCString &argList) { //printf("----- replaceAliasArguments(val=[%s],args=[%s])\n",aliasValue.data(),argList.data()); // first make a list of arguments from the comma separated argument list - QList<QCString> args; - args.setAutoDelete(TRUE); + std::vector<QCString> args; int i,l=(int)argList.length(); int s=0; for (i=0;i<l;i++) { char c = argList.at(i); - if (c==',' && (i==0 || argList.at(i-1)!='\\')) + if (c==',' && (i==0 || argList.at(i-1)!='\\')) { - args.append(new QCString(argList.mid(s,i-s))); + args.push_back(QCString(argList.mid(s,i-s))); s=i+1; // start of next argument } else if (c=='@' || c=='\\') @@ -7672,68 +5942,58 @@ static QCString replaceAliasArguments(const QCString &aliasValue,const QCString i+=findEndOfCommand(argList.data()+i+1); } } - if (l>s) args.append(new QCString(argList.right(l-s))); + if (l>s) args.push_back(QCString(argList.right(l-s))); //printf("found %d arguments\n",args.count()); // next we look for the positions of the markers and add them to a list - QList<Marker> markerList; - markerList.setAutoDelete(TRUE); + std::vector<Marker> markerList; l = aliasValue.length(); + char pc='\0'; + bool insideMarkerId=false; int markerStart=0; - int markerEnd=0; - for (i=0;i<l;i++) + auto isDigit = [](char c) { return c>='0' && c<='9'; }; + for (i=0;i<=l;i++) { - if (markerStart==0 && aliasValue.at(i)=='\\') // start of a \xx marker + char c = i<l ? aliasValue.at(i) : '\0'; + if (insideMarkerId && !isDigit(c)) // found end of a markerId { - markerStart=i+1; + insideMarkerId = false; + int markerLen = i-markerStart; + markerList.push_back(Marker(markerStart-1, + atoi(aliasValue.mid(markerStart,markerLen)), + markerLen+1)); } - else if (markerStart>0 && aliasValue.at(i)>='0' && aliasValue.at(i)<='9') + if (c=='\\' && (pc=='@' || pc=='\\')) // found escaped backslash { - // read digit that make up the marker number - markerEnd=i+1; + // skip + pc = '\0'; } else { - if (markerStart>0 && markerEnd>markerStart) // end of marker + if (isDigit(c) && pc=='\\') // found start of a markerId { - int markerLen = markerEnd-markerStart; - markerList.append(new Marker(markerStart-1, // include backslash - atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1)); - //printf("found marker at %d with len %d and number %d\n", - // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen))); + insideMarkerId=true; + markerStart=i; } - markerStart=0; // outside marker - markerEnd=0; + pc = c; } } - if (markerStart>0) - { - markerEnd=l; - } - if (markerStart>0 && markerEnd>markerStart) - { - int markerLen = markerEnd-markerStart; - markerList.append(new Marker(markerStart-1, // include backslash - atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1)); - //printf("found marker at %d with len %d and number %d\n", - // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen))); - } // then we replace the markers with the corresponding arguments in one pass QCString result; int p=0; - for (i=0;i<(int)markerList.count();i++) + for (i=0;i<(int)markerList.size();i++) { - Marker *m = markerList.at(i); - result+=aliasValue.mid(p,m->pos-p); + const Marker &m = markerList.at(i); + result+=aliasValue.mid(p,m.pos-p); //printf("part before marker %d: '%s'\n",i,aliasValue.mid(p,m->pos-p).data()); - if (m->number>0 && m->number<=(int)args.count()) // valid number + if (m.number>0 && m.number<=(int)args.size()) // valid number { - result+=expandAliasRec(*args.at(m->number-1),TRUE); + result+=expandAliasRec(aliasesProcessed,args.at(m.number-1),TRUE); //printf("marker index=%d pos=%d number=%d size=%d replacement %s\n",i,m->pos,m->number,m->size, // args.at(m->number-1)->data()); } - p=m->pos+m->size; // continue after the marker + p=m.pos+m.size; // continue after the marker } result+=aliasValue.right(l-p); // append remainder //printf("string after replacement of markers: '%s'\n",result.data()); @@ -7741,7 +6001,7 @@ static QCString replaceAliasArguments(const QCString &aliasValue,const QCString // expand the result again result = substitute(result,"\\{","{"); result = substitute(result,"\\}","}"); - result = expandAliasRec(substitute(result,"\\,",",")); + result = expandAliasRec(aliasesProcessed,substitute(result,"\\,",",")); return result; } @@ -7768,7 +6028,7 @@ static QCString escapeCommas(const QCString &s) return result.data(); } -static QCString expandAliasRec(const QCString s,bool allowRecursion) +static QCString expandAliasRec(StringUnorderedSet &aliasesProcessed,const QCString s,bool allowRecursion) { QCString result; static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*"); @@ -7789,8 +6049,8 @@ static QCString expandAliasRec(const QCString s,bool allowRecursion) cmd += QCString().sprintf("{%d}",numArgs); // alias name + {n} } QCString *aliasText=Doxygen::aliasDict.find(cmd); - if (numArgs>1 && aliasText==0) - { // in case there is no command with numArgs parameters, but there is a command with 1 parameter, + if (numArgs>1 && aliasText==0) + { // in case there is no command with numArgs parameters, but there is a command with 1 parameter, // we also accept all text as the argument of that command (so you don't have to escape commas) aliasText=Doxygen::aliasDict.find(cmdNoArgs+"{1}"); if (aliasText) @@ -7801,19 +6061,20 @@ static QCString expandAliasRec(const QCString s,bool allowRecursion) } //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s' aliasText=%s\n", // s.data(),cmd.data(),numArgs,args.data(),aliasText?aliasText->data():"<none>"); - if ((allowRecursion || aliasesProcessed.find(cmd)==0) && aliasText) // expand the alias + if ((allowRecursion || aliasesProcessed.find(cmd.str())==aliasesProcessed.end()) && + aliasText) // expand the alias { //printf("is an alias!\n"); - if (!allowRecursion) aliasesProcessed.insert(cmd,(void *)0x8); + if (!allowRecursion) aliasesProcessed.insert(cmd.str()); QCString val = *aliasText; if (hasArgs) { - val = replaceAliasArguments(val,args); + val = replaceAliasArguments(aliasesProcessed,val,args); //printf("replace '%s'->'%s' args='%s'\n", // aliasText->data(),val.data(),args.data()); } - result+=expandAliasRec(val); - if (!allowRecursion) aliasesProcessed.remove(cmd); + result+=expandAliasRec(aliasesProcessed,val); + if (!allowRecursion) aliasesProcessed.erase(cmd.str()); p=i+l; if (hasArgs) p+=argsLen+2; } @@ -7836,7 +6097,7 @@ int countAliasArguments(const QCString argList) int count=1; int l = argList.length(); int i; - for (i=0;i<l;i++) + for (i=0;i<l;i++) { char c = argList.at(i); if (c==',' && (i==0 || argList.at(i-1)!='\\')) count++; @@ -7870,7 +6131,7 @@ QCString extractAliasArgs(const QCString &args,int pos) prevChar=0; } - if (bc==0) + if (bc==0) { //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data()); return args.mid(pos+1,i-pos-1); @@ -7883,9 +6144,9 @@ QCString extractAliasArgs(const QCString &args,int pos) QCString resolveAliasCmd(const QCString aliasCmd) { QCString result; - aliasesProcessed.clear(); + StringUnorderedSet aliasesProcessed; //printf("Expanding: '%s'\n",aliasCmd.data()); - result = expandAliasRec(aliasCmd); + result = expandAliasRec(aliasesProcessed,aliasCmd); //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data()); return result; } @@ -7893,12 +6154,12 @@ QCString resolveAliasCmd(const QCString aliasCmd) QCString expandAlias(const QCString &aliasName,const QCString &aliasValue) { QCString result; - aliasesProcessed.clear(); + StringUnorderedSet aliasesProcessed; // avoid expanding this command recursively - aliasesProcessed.insert(aliasName,(void *)0x8); + aliasesProcessed.insert(aliasName.str()); // expand embedded commands //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data()); - result = expandAliasRec(aliasValue); + result = expandAliasRec(aliasesProcessed,aliasValue); //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data()); return result; } @@ -7906,7 +6167,7 @@ QCString expandAlias(const QCString &aliasName,const QCString &aliasValue) void writeTypeConstraints(OutputList &ol,const Definition *d,const ArgumentList &al) { if (al.empty()) return; - ol.startConstraintList(theTranslator->trTypeConstraints()); + ol.startConstraintList(theTranslator->trTypeConstraints()); for (const Argument &a : al) { ol.startConstraintParam(); @@ -7916,7 +6177,8 @@ void writeTypeConstraints(OutputList &ol,const Definition *d,const ArgumentList linkifyText(TextGeneratorOLImpl(ol),d,0,0,a.type); ol.endConstraintType(); ol.startConstraintDocs(); - ol.generateDoc(d->docFile(),d->docLine(),d,0,a.docs,TRUE,FALSE); + ol.generateDoc(d->docFile(),d->docLine(),d,0,a.docs,TRUE,FALSE, + 0,FALSE,FALSE,Config_getBool(MARKDOWN_SUPPORT)); ol.endConstraintDocs(); } ol.endConstraintList(); @@ -7932,27 +6194,19 @@ void stackTrace() static char cmd[40960]; char *p = cmd; p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid()); - for (int x = 0; x < frameCount; x++) + for (int x = 0; x < frameCount; x++) { p += sprintf(p,"%p ", backtraceFrames[x]); } fprintf(stderr,"========== STACKTRACE START ==============\n"); - #if defined(_WIN32) && !defined(__CYGWIN__) - if (FILE *fp = _popen(cmd, "r")) - #else - if (FILE *fp = ::popen(cmd, "r")) - #endif + if (FILE *fp = Portable::popen(cmd, "r")) { char resBuf[512]; while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp)) { fwrite(resBuf, 1, len, stderr); } - #if defined(_WIN32) && !defined(__CYGWIN__) - _pclose(fp); - #else - ::pclose(fp); - #endif + Portable::pclose(fp); } fprintf(stderr,"============ STACKTRACE END ==============\n"); //fprintf(stderr,"%s\n", frameStrings[x]); @@ -7965,7 +6219,7 @@ static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size if (inputEncoding==0 || outputEncoding==0) return size; if (qstricmp(inputEncoding,outputEncoding)==0) return size; void *cd = portable_iconv_open(outputEncoding,inputEncoding); - if (cd==(void *)(-1)) + if (cd==(void *)(-1)) { term("unsupported character conversion: '%s'->'%s': %s\n" "Check the INPUT_ENCODING setting in the config file!\n", @@ -8111,27 +6365,26 @@ QCString filterTitle(const QCString &title) // returns TRUE if the name of the file represented by 'fi' matches // one of the file patterns in the 'patList' list. -bool patternMatch(const QFileInfo &fi,const QStrList *patList) +bool patternMatch(const QFileInfo &fi,const StringVector &patList) { - static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); + bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); bool found = FALSE; - // For Windows/Mac, always do the case insensitive match -#if defined(_WIN32) || defined(__MACOSX__) || defined(__CYGWIN__) - caseSenseNames = FALSE; -#endif - - if (patList) + // For platforms where the file system is non case sensitive overrule the setting + if (!Portable::fileSystemIsCaseSensitive()) { - QStrListIterator it(*patList); - QCString pattern; + caseSenseNames = FALSE; + } + if (!patList.empty()) + { QCString fn = fi.fileName().data(); QCString fp = fi.filePath().data(); QCString afp= fi.absFilePath().data(); - for (it.toFirst();(pattern=it.current());++it) + for (const auto &pat: patList) { + QCString pattern = pat.c_str(); if (!pattern.isEmpty()) { int i=pattern.find('='); @@ -8181,10 +6434,15 @@ void writeSummaryLink(OutputList &ol,const char *label,const char *title, } #endif -QCString externalLinkTarget() +QCString externalLinkTarget(const bool parent) { static bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW); - if (extLinksInWindow) return "target=\"_blank\" "; else return ""; + if (extLinksInWindow) + return "target=\"_blank\" "; + else if (parent) + return "target=\"_parent\" "; + else + return ""; } QCString externalRef(const QCString &relPath,const QCString &ref,bool href) @@ -8213,7 +6471,7 @@ QCString externalRef(const QCString &relPath,const QCString &ref,bool href) return result; } -/** Writes the intensity only bitmap represented by \a data as an image to +/** Writes the intensity only bitmap represented by \a data as an image to * directory \a dir using the colors defined by HTML_COLORSTYLE_*. */ void writeColoredImgData(const char *dir,ColoredImgDataItem data[]) @@ -8242,8 +6500,8 @@ void writeColoredImgData(const char *dir,ColoredImgDataItem data[]) } /** Replaces any markers of the form \#\#AA in input string \a str - * by new markers of the form \#AABBCC, where \#AABBCC represents a - * valid color, based on the intensity represented by hex number AA + * by new markers of the form \#AABBCC, where \#AABBCC represents a + * valid color, based on the intensity represented by hex number AA * and the current HTML_COLORSTYLE_* settings. */ QCString replaceColorMarkers(const char *str) @@ -8252,7 +6510,6 @@ QCString replaceColorMarkers(const char *str) QCString s=str; if (s.isEmpty()) return result; static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]"); - static const char hex[] = "0123456789ABCDEF"; static int hue = Config_getInt(HTML_COLORSTYLE_HUE); static int sat = Config_getInt(HTML_COLORSTYLE_SAT); static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA); @@ -8264,7 +6521,7 @@ QCString replaceColorMarkers(const char *str) #define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \ ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \ ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0) - + double r,g,b; int red,green,blue; int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]); @@ -8290,7 +6547,7 @@ QCString replaceColorMarkers(const char *str) return result; } -/** Copies the contents of file with name \a src to the newly created +/** Copies the contents of file with name \a src to the newly created * file with name \a dest. Returns TRUE if successful. */ bool copyFile(const QCString &src,const QCString &dest) @@ -8322,7 +6579,7 @@ bool copyFile(const QCString &src,const QCString &dest) return TRUE; } -/** Returns the section of text, in between a pair of markers. +/** Returns the section of text, in between a pair of markers. * Full lines are returned, excluding the lines on which the markers appear. * \sa routine lineBlock */ @@ -8410,7 +6667,6 @@ QCString langToString(SrcLangExt lang) case SrcLangExt_VHDL: return "VHDL"; case SrcLangExt_XML: return "XML"; case SrcLangExt_SQL: return "SQL"; - case SrcLangExt_Tcl: return "Tcl"; case SrcLangExt_Markdown: return "Markdown"; case SrcLangExt_Slice: return "Slice"; } @@ -8433,17 +6689,15 @@ QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope) return "::"; } } -QCString replaceScopeSeparator(QCString str) -{ - // we don't know about the language so we have to go for the worse - return substitute(substitute(str,"\\","::"),".","::"); // PHP and Java, CSharp, VHDL, Python -} /** Checks whether the given url starts with a supported protocol */ bool isURL(const QCString &url) { QCString loc_url = url.stripWhiteSpace(); - return loc_url.left(5)=="http:" || loc_url.left(6)=="https:" || - loc_url.left(4)=="ftp:" || loc_url.left(5)=="file:"; + return loc_url.left(5)=="http:" || loc_url.left(6)=="https:" || + loc_url.left(4)=="ftp:" || loc_url.left(5)=="ftps:" || + loc_url.left(5)=="sftp:" || loc_url.left(5)=="file:" || + loc_url.left(5)=="news:" || loc_url.left(4)=="irc:" || + loc_url.left(5)=="ircs:"; } /** Corrects URL \a url according to the relative path \a relPath. * Returns the corrected URL. For absolute URLs no correction will be done. @@ -8465,8 +6719,8 @@ bool protectionLevelVisible(Protection prot) static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE); static bool extractPackage = Config_getBool(EXTRACT_PACKAGE); - return (prot!=Private && prot!=Package) || - (prot==Private && extractPrivate) || + return (prot!=Private && prot!=Package) || + (prot==Private && extractPrivate) || (prot==Package && extractPackage); } @@ -8489,7 +6743,7 @@ QCString stripIndentation(const QCString &s) if (c=='\t') indent+=tabSize - (indent%tabSize); else if (c=='\n') indent=0,searchIndent=TRUE; else if (c==' ') indent++; - else if (searchIndent) + else if (searchIndent) { searchIndent=FALSE; if (indent<minIndent) minIndent=indent; @@ -8538,6 +6792,55 @@ QCString stripIndentation(const QCString &s) return result.data(); } +// strip up to \a indentationLevel spaces from each line in \a doc (excluding the first line) +void stripIndentation(QCString &doc,const int indentationLevel) +{ + if (indentationLevel <= 0 || doc.isEmpty()) return; // nothing to strip + + // by stripping content the string will only become shorter so we write the results + // back into the input string and then resize it at the end. + char c; + const char *src = doc.data(); + char *dst = doc.rawData(); + bool insideIndent = false; // skip the initial line from stripping + int cnt = 0; + while ((c=*src++)!=0) + { + // invariant: dst<=src + switch(c) + { + case '\n': + *dst++ = c; + insideIndent = true; + cnt = indentationLevel; + break; + case ' ': + if (insideIndent) + { + if (cnt>0) // count down the spacing until the end of the indent + { + cnt--; + } + else // reached the end of the indent, start of the part of the line to keep + { + insideIndent = false; + *dst++ = c; + } + } + else // part after indent, copy to the output + { + *dst++ = c; + } + break; + default: + insideIndent = false; + *dst++ = c; + break; + } + } + doc.resize(dst-doc.data()+1); +} + bool fileVisibleInIndex(const FileDef *fd,bool &genSourceFile) { @@ -8546,49 +6849,11 @@ bool fileVisibleInIndex(const FileDef *fd,bool &genSourceFile) genSourceFile = !isDocFile && fd->generateSourceFile(); return ( ((allExternals && fd->isLinkable()) || fd->isLinkableInProject() - ) && + ) && !isDocFile ); } -void addDocCrossReference(MemberDef *src,MemberDef *dst) -{ - //printf("--> addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); - if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types - if ((dst->hasReferencedByRelation() || dst->hasCallerGraph()) && - src->showInCallGraph() - ) - { - dst->addSourceReferencedBy(src); - MemberDef *mdDef = dst->memberDefinition(); - if (mdDef) - { - mdDef->addSourceReferencedBy(src); - } - MemberDef *mdDecl = dst->memberDeclaration(); - if (mdDecl) - { - mdDecl->addSourceReferencedBy(src); - } - } - if ((src->hasReferencesRelation() || src->hasCallGraph()) && - src->showInCallGraph() - ) - { - src->addSourceReferences(dst); - MemberDef *mdDef = src->memberDefinition(); - if (mdDef) - { - mdDef->addSourceReferences(dst); - } - MemberDef *mdDecl = src->memberDeclaration(); - if (mdDecl) - { - mdDecl->addSourceReferences(dst); - } - } -} - //-------------------------------------------------------------------------------------- /*! @brief Get one unicode character as an unsigned integer from utf-8 string @@ -8627,7 +6892,7 @@ uint getUtf8Code( const QCString& s, int idx ) } -/*! @brief Returns one unicode character as an unsigned integer +/*! @brief Returns one unicode character as an unsigned integer * from utf-8 string, making the character lower case if it was upper case. * * @param s utf-8 encoded string @@ -8642,7 +6907,7 @@ uint getUtf8CodeToLower( const QCString& s, int idx ) } -/*! @brief Returns one unicode character as an unsigned integer +/*! @brief Returns one unicode character as an unsigned integer * from utf-8 string, making the character upper case if it was lower case. * * @param s utf-8 encoded string @@ -8657,55 +6922,54 @@ uint getUtf8CodeToUpper( const QCString& s, int idx ) } //-------------------------------------------------------------------------------------- +// +bool namespaceHasNestedNamespace(const NamespaceDef *nd) +{ + for (const auto &cnd : nd->getNamespaces()) + { + if (cnd->isLinkableInProject() && !cnd->isAnonymous()) + { + return true; + } + } + return false; +} -bool namespaceHasVisibleChild(const NamespaceDef *nd,bool includeClasses,bool filterClasses,ClassDef::CompoundType ct) +bool namespaceHasNestedClass(const NamespaceDef *nd,bool filterClasses,ClassDef::CompoundType ct) { - if (nd->getNamespaceSDict()) + //printf(">namespaceHasVisibleChild(%s,includeClasses=%d)\n",nd->name().data(),includeClasses); + for (const auto &cnd : nd->getNamespaces()) { - NamespaceSDict::Iterator cnli(*nd->getNamespaceSDict()); - const NamespaceDef *cnd; - for (cnli.toFirst();(cnd=cnli.current());++cnli) + if (namespaceHasNestedClass(cnd,filterClasses,ct)) { - if (cnd->isLinkableInProject() && !cnd->isAnonymous()) - { - return TRUE; - } - else if (namespaceHasVisibleChild(cnd,includeClasses,filterClasses,ct)) - { - return TRUE; - } + //printf("<namespaceHasVisibleChild(%s,includeClasses=%d): case2\n",nd->name().data(),includeClasses); + return TRUE; } } - if (includeClasses) + + ClassLinkedRefMap list = nd->getClasses(); + if (filterClasses) { - const ClassSDict *d = nd->getClassSDict(); - if (filterClasses) + if (ct == ClassDef::Interface) { - if (ct == ClassDef::Interface) - { - d = nd->getInterfaceSDict(); - } - else if (ct == ClassDef::Struct) - { - d = nd->getStructSDict(); - } - else if (ct == ClassDef::Exception) - { - d = nd->getExceptionSDict(); - } + list = nd->getInterfaces(); + } + else if (ct == ClassDef::Struct) + { + list = nd->getStructs(); + } + else if (ct == ClassDef::Exception) + { + list = nd->getExceptions(); } + } - if (d) + for (const auto &cd : list) + { + if (cd->isLinkableInProject() && cd->templateMaster()==0) { - ClassSDict::Iterator cli(*d); - const ClassDef *cd; - for (;(cd=cli.current());++cli) - { - if (cd->isLinkableInProject() && cd->templateMaster()==0) - { - return TRUE; - } - } + //printf("<namespaceHasVisibleChild(%s,includeClasses=%d): case3\n",nd->name().data(),includeClasses); + return TRUE; } } return FALSE; @@ -8723,20 +6987,22 @@ bool classVisibleInIndex(const ClassDef *cd) QCString extractDirection(QCString &docs) { - QRegExp re("\\[[^\\]]+\\]"); // [...] + QRegExp re("\\[[ inout,]+\\]"); // [...] int l=0; - if (re.match(docs,0,&l)==0) + if (re.match(docs,0,&l)==0 && l>2) { - int inPos = docs.find("in", 1,FALSE); - int outPos = docs.find("out",1,FALSE); - bool input = inPos!=-1 && inPos<l; - bool output = outPos!=-1 && outPos<l; - if (input || output) // in,out attributes + // make dir the part inside [...] without separators + QCString dir=substitute(substitute(docs.mid(1,l-2)," ",""),",",""); + int inIndex, outIndex; + unsigned char ioMask=0; + if (( inIndex=dir.find( "in"))!=-1) dir.remove (inIndex,2),ioMask|=(1<<0); + if ((outIndex=dir.find("out"))!=-1) dir.remove(outIndex,3),ioMask|=(1<<1); + if (dir.isEmpty() && ioMask!=0) // only in and/or out attributes found { docs = docs.mid(l); // strip attributes - if (input && output) return "[in,out]"; - else if (input) return "[in]"; - else if (output) return "[out]"; + if (ioMask==((1<<0)|(1<<1))) return "[in,out]"; + else if (ioMask==(1<<0)) return "[in]"; + else if (ioMask==(1<<1)) return "[out]"; } } return QCString(); @@ -8938,7 +7204,7 @@ bool mainPageHasTitle() return Doxygen::mainPage!=0 && Doxygen::mainPage->hasTitle(); } -QCString getDotImageExtension(void) +QCString getDotImageExtension() { QCString imgExt = Config_getEnum(DOT_IMAGE_FORMAT); int i= imgExt.find(':'); // strip renderer part when using e.g. 'png:cairo:gd' as format @@ -8963,7 +7229,7 @@ bool openOutputFile(const char *outFile,QFile &f) if (backup.exists()) // remove existing backup dir.remove(backup.fileName()); dir.rename(fi.fileName(),fi.fileName()+".bak"); - } + } f.setName(outFile); fileOpened = f.open(IO_WriteOnly|IO_Translate); } @@ -8973,18 +7239,16 @@ bool openOutputFile(const char *outFile,QFile &f) void writeExtraLatexPackages(FTextStream &t) { // User-specified packages - QStrList &extraPackages = Config_getList(EXTRA_PACKAGES); - if (!extraPackages.isEmpty()) + const StringVector &extraPackages = Config_getList(EXTRA_PACKAGES); + if (!extraPackages.empty()) { t << "% Packages requested by user\n"; - const char *pkgName=extraPackages.first(); - while (pkgName) + for (const auto &pkgName : extraPackages) { if ((pkgName[0] == '[') || (pkgName[0] == '{')) - t << "\\usepackage" << pkgName << "\n"; + t << "\\usepackage" << pkgName.c_str() << "\n"; else - t << "\\usepackage{" << pkgName << "}\n"; - pkgName=extraPackages.next(); + t << "\\usepackage{" << pkgName.c_str() << "}\n"; } t << "\n"; } @@ -9017,22 +7281,56 @@ void writeLatexSpecialFormulaChars(FTextStream &t) } //------------------------------------------------------ +// simplified way to know if this is fixed form +bool recognizeFixedForm(const char* contents, FortranFormat format) +{ + int column=0; + bool skipLine=FALSE; -static int g_usedTableLevels = 0; + if (format == FortranFormat_Fixed) return TRUE; + if (format == FortranFormat_Free) return FALSE; -void incUsedTableLevels() -{ - g_usedTableLevels++; -} -void decUsedTableLevels() -{ - g_usedTableLevels--; -} -int usedTableLevels() -{ - return g_usedTableLevels; + for(int i=0;;i++) { + column++; + + switch(contents[i]) { + case '\n': + column=0; + skipLine=FALSE; + break; + case ' ': + break; + case '\000': + return FALSE; + case '#': + skipLine=TRUE; + break; + case 'C': + case 'c': + case '*': + if (column==1) return TRUE; + if (skipLine) break; + return FALSE; + case '!': + if (column>1 && column<7) return FALSE; + skipLine=TRUE; + break; + default: + if (skipLine) break; + if (column>=7) return TRUE; + return FALSE; + } + } + return FALSE; } -//------------------------------------------------------ +FortranFormat convertFileNameFortranParserCode(QCString fn) +{ + QCString ext = getFileNameExtension(fn); + QCString parserName = Doxygen::parserManager->getParserName(ext.data()); + if (parserName == "fortranfixed") return FortranFormat_Fixed; + else if (parserName == "fortranfree") return FortranFormat_Free; + return FortranFormat_Unknown; +} |