1 /*****************************************************************************
5 * Copyright (C) 1997-2012 by Dimitri van Heesch.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
27 #include <qfileinfo.h>
29 #include <qdatetime.h>
37 #include "outputlist.h"
48 #include "searchindex.h"
50 #include "textdocvisitor.h"
52 #include "parserintf.h"
57 #include "arguments.h"
59 #define ENABLE_TRACINGSUPPORT 0
61 #if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
62 #define TRACINGSUPPORT
71 //------------------------------------------------------------------------
73 // selects one of the name to sub-dir mapping algorithms that is used
74 // to select a sub directory when CREATE_SUBDIRS is set to YES.
80 //#define MAP_ALGO ALGO_COUNT
81 //#define MAP_ALGO ALGO_CRC16
82 #define MAP_ALGO ALGO_MD5
84 #define REL_PATH_TO_ROOT "../../"
86 //------------------------------------------------------------------------
87 // TextGeneratorOLImpl implementation
88 //------------------------------------------------------------------------
90 TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od)
94 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
97 //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces);
108 if (c==' ') m_od.writeNonBreakableSpace(1);
109 else cs[0]=c,m_od.docify(cs);
119 void TextGeneratorOLImpl::writeBreak(int indent) const
121 m_od.lineBreak("typebreak");
123 for (i=0;i<indent;i++)
125 m_od.writeNonBreakableSpace(3);
129 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
130 const char *anchor,const char *text
133 //printf("TextGeneratorOlImpl::writeLink('%s')\n",text);
134 m_od.writeObjectLink(extRef,file,anchor,text);
137 //------------------------------------------------------------------------
138 //------------------------------------------------------------------------
140 // an inheritance tree of depth of 100000 should be enough for everyone :-)
141 const int maxInheritanceDepth = 100000;
144 Removes all anonymous scopes from string s
147 "bla::@10::blep" => "bla::blep"
148 "bla::@10::@11::blep" => "bla::blep"
149 "@10::blep" => "blep"
150 " @10::blep" => "blep"
151 "@9::@10::blep" => "blep"
153 "bla::@1::@2" => "bla"
157 QCString removeAnonymousScopes(const QCString &s)
160 if (s.isEmpty()) return result;
161 static QRegExp re("[ :]*@[0-9]+[: ]*");
162 int i,l,sl=s.length();
164 while ((i=re.match(s,p,&l))!=-1)
166 result+=s.mid(p,i-p);
168 bool b1=FALSE,b2=FALSE;
169 while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
171 while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
178 result+=s.right(sl-p);
179 //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
183 // replace anonymous scopes with __anonymous__ or replacement if provided
184 QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
187 if (s.isEmpty()) return result;
188 static QRegExp re("@[0-9]+");
189 int i,l,sl=s.length();
191 while ((i=re.match(s,p,&l))!=-1)
193 result+=s.mid(p,i-p);
200 result+="__anonymous__";
204 result+=s.right(sl-p);
205 //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
210 // strip anonymous left hand side part of the scope
211 QCString stripAnonymousNamespaceScope(const QCString &s)
215 while ((i=getScopeFragment(s,p,&l))!=-1)
217 //printf("Scope fragment %s\n",s.mid(i,l).data());
218 if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
222 if (!newScope.isEmpty()) newScope+="::";
223 newScope+=s.mid(i,l);
228 if (!newScope.isEmpty()) newScope+="::";
229 newScope+=s.right(s.length()-i);
235 //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
239 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
241 od.pushGeneratorState();
243 od.disable(OutputGenerator::Html);
244 od.disable(OutputGenerator::Man);
245 if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex);
246 if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF);
248 od.docify(theTranslator->trPageAbbreviation());
249 od.endPageRef(cn,mn);
251 od.popGeneratorState();
254 /*! Generate a place holder for a position in a list. Used for
255 * translators to be able to specify different elements orders
256 * depending on whether text flows from left to right or visa versa.
258 QCString generateMarker(int id)
261 result.sprintf("@%d",id);
265 static QCString stripFromPath(const QCString &path,QStrList &l)
267 // look at all the strings in the list and strip the longest match
268 const char *s=l.first();
270 unsigned int length = 0;
274 if (prefix.length() > length &&
275 stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
277 length = prefix.length();
278 potential = path.right(path.length()-prefix.length());
282 if (length) return potential;
286 /*! strip part of \a path if it matches
287 * one of the paths in the Config_getList("STRIP_FROM_PATH") list
289 QCString stripFromPath(const QCString &path)
291 return stripFromPath(path,Config_getList("STRIP_FROM_PATH"));
294 /*! strip part of \a path if it matches
295 * one of the paths in the Config_getList("INCLUDE_PATH") list
297 QCString stripFromIncludePath(const QCString &path)
299 return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH"));
302 /*! try to determine if \a name is a source or a header file name by looking
303 * at the extension. A number of variations is allowed in both upper and
304 * lower case) If anyone knows or uses another extension please let me know :-)
306 int guessSection(const char *name)
308 QCString n=((QCString)name).lower();
309 if (n.right(2)==".c" || // source
311 n.right(4)==".cxx" ||
312 n.right(4)==".cpp" ||
313 n.right(4)==".c++" ||
314 n.right(5)==".java" ||
318 n.right(3)==".ii" || // inline
319 n.right(4)==".ixx" ||
320 n.right(4)==".ipp" ||
321 n.right(4)==".i++" ||
322 n.right(4)==".inl" ||
324 ) return Entry::SOURCE_SEC;
325 if (n.right(2)==".h" || // header
327 n.right(4)==".hxx" ||
328 n.right(4)==".hpp" ||
329 n.right(4)==".h++" ||
330 n.right(4)==".idl" ||
331 n.right(4)==".ddl" ||
333 ) return Entry::HEADER_SEC;
337 QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
338 Definition **typedefContext)
340 //printf("<<resolveTypeDef(%s,%s)\n",
341 // context ? context->name().data() : "<none>",qualifiedName.data());
343 if (qualifiedName.isEmpty())
345 //printf(" qualified name empty!\n");
349 Definition *mContext=context;
350 if (typedefContext) *typedefContext=context;
352 // see if the qualified name has a scope part
353 int scopeIndex = qualifiedName.findRev("::");
354 QCString resName=qualifiedName;
355 if (scopeIndex!=-1) // strip scope part for the name
357 resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
358 if (resName.isEmpty())
360 // qualifiedName was of form A:: !
361 //printf(" qualified name of form A::!\n");
366 while (mContext && md==0)
368 // step 1: get the right scope
369 Definition *resScope=mContext;
372 // split-off scope part
373 QCString resScopeName = qualifiedName.left(scopeIndex);
374 //printf("resScopeName=`%s'\n",resScopeName.data());
376 // look-up scope in context
379 while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
381 QCString qualScopePart = resScopeName.mid(is,l);
382 QCString tmp = resolveTypeDef(mContext,qualScopePart);
383 if (!tmp.isEmpty()) qualScopePart=tmp;
384 resScope = resScope->findInnerCompound(qualScopePart);
385 //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
386 if (resScope==0) break;
390 //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
392 // step 2: get the member
393 if (resScope) // no scope or scope found in the current context
395 //printf("scope found: %s, look for typedef %s\n",
396 // resScope->qualifiedName().data(),resName.data());
397 MemberNameSDict *mnd=0;
398 if (resScope->definitionType()==Definition::TypeClass)
400 mnd=Doxygen::memberNameSDict;
404 mnd=Doxygen::functionNameSDict;
406 MemberName *mn=mnd->find(resName);
409 MemberNameIterator mni(*mn);
412 for (;(tmd=mni.current());++mni)
414 //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
415 // tmd->name().data(), resScope->name().data(),
416 // tmd->getOuterScope()->name().data(), mContext);
417 if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
419 int dist=isAccessibleFrom(resScope,0,tmd);
420 if (dist!=-1 && (md==0 || dist<minDist))
429 mContext=mContext->getOuterScope();
432 // step 3: get the member's type
435 //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s' args='%s'\n",
436 // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
438 result=md->typeString();
439 QCString args = md->argsString();
440 if (args.find(")(")!=-1) // typedef of a function/member pointer
444 else if (args.find('[')!=-1) // typedef of an array
448 if (typedefContext) *typedefContext=md->getOuterScope();
452 //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
453 // qualifiedName.data(),context ? context->name().data() : "<global>");
460 /*! Get a class definition given its name.
461 * Returns 0 if the class is not found.
463 ClassDef *getClass(const char *n)
465 if (n==0 || n[0]=='\0') return 0;
467 ClassDef *result = Doxygen::classSDict->find(name);
468 //if (result==0 && !exact) // also try generic and protocol versions
470 // result = Doxygen::classSDict->find(name+"-g");
473 // result = Doxygen::classSDict->find(name+"-p");
476 //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>");
480 NamespaceDef *getResolvedNamespace(const char *name)
482 if (name==0 || name[0]=='\0') return 0;
483 QCString *subst = Doxygen::namespaceAliasDict[name];
486 int count=0; // recursion detection guard
488 while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
495 err("warning: possible recursive namespace alias detected for %s!\n",name);
497 return Doxygen::namespaceSDict->find(subst->data());
501 return Doxygen::namespaceSDict->find(name);
505 static QDict<MemberDef> g_resolvedTypedefs;
506 static QDict<Definition> g_visitedNamespaces;
508 // forward declaration
509 static ClassDef *getResolvedClassRec(Definition *scope,
512 MemberDef **pTypeDef,
513 QCString *pTemplSpec,
514 QCString *pResolvedType
516 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
517 const QCString &explicitScopePart);
519 /*! Returns the class representing the value of the typedef represented by \a md
520 * within file \a fileScope.
522 * Example: typedef A T; will return the class representing A if it is a class.
524 * Example: typedef int T; will return 0, since "int" is not a class.
526 ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md,
527 MemberDef **pMemType,QCString *pTemplSpec,
528 QCString *pResolvedType,
529 ArgumentList *actTemplParams)
531 //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
532 bool isCached = md->isTypedefValCached(); // value already cached
535 //printf("Already cached %s->%s [%s]\n",
536 // md->name().data(),
537 // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
538 // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
540 if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
541 if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
542 return md->getCachedTypedefVal();
544 //printf("new typedef\n");
545 QCString qname = md->qualifiedName();
546 if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
548 g_resolvedTypedefs.insert(qname,md); // put on the trace list
550 ClassDef *typeClass = md->getClassDef();
551 QCString type = md->typeString(); // get the "value" of the typedef
552 if (typeClass && typeClass->isTemplate() &&
553 actTemplParams && actTemplParams->count()>0)
555 type = substituteTemplateArgumentsInString(type,
556 typeClass->templateArguments(),actTemplParams);
558 QCString typedefValue = type;
559 int tl=type.length();
560 int ip=tl-1; // remove * and & at the end
561 while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
565 type=type.left(ip+1);
566 type.stripPrefix("const "); // strip leading "const"
567 type.stripPrefix("struct "); // strip leading "struct"
568 type.stripPrefix("union "); // strip leading "union"
570 tl=type.length(); // length may have been changed
571 while (sp<tl && type.at(sp)==' ') sp++;
572 MemberDef *memTypeDef = 0;
573 ClassDef *result = getResolvedClassRec(md->getOuterScope(),
574 fileScope,type,&memTypeDef,0,pResolvedType);
575 // if type is a typedef then return what it resolves to.
576 if (memTypeDef && memTypeDef->isTypedef())
578 result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
581 else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
583 *pMemType = memTypeDef;
586 //printf("type=%s result=%p\n",type.data(),result);
589 // try unspecialized version if type is template
590 int si=type.findRev("::");
591 int i=type.find('<');
592 if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
594 if (pTemplSpec) *pTemplSpec = type.mid(i);
595 result = getResolvedClassRec(md->getOuterScope(),fileScope,
596 type.left(i),0,0,pResolvedType);
597 //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
598 // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
600 else if (si!=-1) // A::B
603 if (i==-1) // Something like A<T>::B => lookup A::B
607 else // Something like A<T>::B<S> => lookup A::B, spec=<S>
609 if (pTemplSpec) *pTemplSpec = type.mid(i);
611 result = getResolvedClassRec(md->getOuterScope(),fileScope,
612 stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
616 //if (result) ip=si+sp+1;
624 *pResolvedType=result->qualifiedName();
625 //printf("*pResolvedType=%s\n",pResolvedType->data());
626 if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
627 if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
631 *pResolvedType=typedefValue;
635 // remember computed value for next time
636 if (result && result->getDefFileName()!="<code>")
637 // this check is needed to prevent that temporary classes that are
638 // introduced while parsing code fragments are being cached here.
640 //printf("setting cached typedef %p in result %p\n",md,result);
641 //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
642 //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
643 md->cacheTypedefVal(result,
644 pTemplSpec ? *pTemplSpec : QCString(),
645 pResolvedType ? *pResolvedType : QCString()
649 g_resolvedTypedefs.remove(qname); // remove from the trace list
654 /*! Substitutes a simple unqualified \a name within \a scope. Returns the
655 * value of the typedef or \a name if no typedef was found.
657 static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
658 MemberDef **pTypeDef=0)
660 QCString result=name;
661 if (name.isEmpty()) return result;
663 // lookup scope fragment in the symbol map
664 DefinitionIntf *di = Doxygen::symbolMap->find(name);
665 if (di==0) return result; // no matches
667 MemberDef *bestMatch=0;
668 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
670 // search for the best match
671 DefinitionListIterator dli(*(DefinitionList*)di);
673 int minDistance=10000; // init at "infinite"
674 for (dli.toFirst();(d=dli.current());++dli) // foreach definition
676 // only look at members
677 if (d->definitionType()==Definition::TypeMember)
679 // that are also typedefs
680 MemberDef *md = (MemberDef *)d;
681 if (md->isTypedef()) // d is a typedef
683 // test accessibility of typedef within scope.
684 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
685 if (distance!=-1 && distance<minDistance)
686 // definition is accessible and a better match
688 minDistance=distance;
695 else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
697 Definition *d = (Definition*)di;
698 // that are also typedefs
699 MemberDef *md = (MemberDef *)di;
700 if (md->isTypedef()) // d is a typedef
702 // test accessibility of typedef within scope.
703 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
704 if (distance!=-1) // definition is accessible
712 result = bestMatch->typeString();
713 if (pTypeDef) *pTypeDef=bestMatch;
716 //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
717 // name.data(),result.data());
721 static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
725 SDict<Definition>::Iterator cli(*cl);
727 for (cli.toFirst();(cd=cli.current());++cli)
729 if (cd->localName()==localName)
738 /*! Starting with scope \a start, the string \a path is interpreted as
739 * a part of a qualified scope name (e.g. A::B::C), and the scope is
740 * searched. If found the scope definition is returned, otherwise 0
743 static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
747 Definition *current=start;
749 //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
750 // for each part of the explicit scope
751 while ((is=getScopeFragment(path,ps,&l))!=-1)
753 // try to resolve the part if it is a typedef
754 MemberDef *typeDef=0;
755 QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
756 //printf(" qualScopePart=%s\n",qualScopePart.data());
759 ClassDef *type = newResolveTypedef(fileScope,typeDef);
762 //printf("Found type %s\n",type->name().data());
766 Definition *next = current->findInnerCompound(qualScopePart);
767 //printf("++ Looking for %s inside %s result %s\n",
768 // qualScopePart.data(),
769 // current->name().data(),
770 // next?next->name().data():"<null>");
771 if (next==0) // failed to follow the path
773 //printf("==> next==0!\n");
774 if (current->definitionType()==Definition::TypeNamespace)
776 next = endOfPathIsUsedClass(
777 ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
779 else if (current->definitionType()==Definition::TypeFile)
781 next = endOfPathIsUsedClass(
782 ((FileDef *)current)->getUsedClasses(),qualScopePart);
785 if (current==0) break;
787 else // continue to follow scope
790 //printf("==> current = %p\n",current);
794 //printf("followPath(start=%s,path=%s) result=%s\n",
795 // start->name().data(),path.data(),current?current->name().data():"<null>");
796 return current; // path could be followed
799 bool accessibleViaUsingClass(const SDict<Definition> *cl,
802 const QCString &explicitScopePart=""
805 //printf("accessibleViaUsingClass(%p)\n",cl);
806 if (cl) // see if the class was imported via a using statement
808 SDict<Definition>::Iterator cli(*cl);
810 bool explicitScopePartEmpty = explicitScopePart.isEmpty();
811 for (cli.toFirst();(ucd=cli.current());++cli)
813 //printf("Trying via used class %s\n",ucd->name().data());
814 Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
815 if (sc && sc==item) return TRUE;
816 //printf("Try via used class done\n");
822 bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
825 const QCString &explicitScopePart="")
827 static QDict<void> visitedDict;
828 if (nl) // check used namespaces for the class
830 NamespaceSDict::Iterator nli(*nl);
833 for (nli.toFirst();(und=nli.current());++nli,count++)
835 //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
836 // count,nl->count());
837 Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
838 if (sc && item->getOuterScope()==sc)
840 //printf("] found it\n");
843 QCString key=und->name();
844 if (und->getUsedNamespaces() && visitedDict.find(key)==0)
846 visitedDict.insert(key,(void *)0x08);
848 if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
850 //printf("] found it via recursion\n");
854 visitedDict.remove(key);
856 //printf("] Try via used namespace done\n");
862 const int MAX_STACK_SIZE = 1000;
864 /** Helper class representing the stack of items considered while resolving
870 AccessStack() : m_index(0) {}
871 void push(Definition *scope,FileDef *fileScope,Definition *item)
873 if (m_index<MAX_STACK_SIZE)
875 m_elements[m_index].scope = scope;
876 m_elements[m_index].fileScope = fileScope;
877 m_elements[m_index].item = item;
881 void push(Definition *scope,FileDef *fileScope,Definition *item,const QCString &expScope)
883 if (m_index<MAX_STACK_SIZE)
885 m_elements[m_index].scope = scope;
886 m_elements[m_index].fileScope = fileScope;
887 m_elements[m_index].item = item;
888 m_elements[m_index].expScope = expScope;
894 if (m_index>0) m_index--;
896 bool find(Definition *scope,FileDef *fileScope, Definition *item)
899 for (i=0;i<m_index;i++)
901 AccessElem *e = &m_elements[i];
902 if (e->scope==scope && e->fileScope==fileScope && e->item==item)
909 bool find(Definition *scope,FileDef *fileScope, Definition *item,const QCString &expScope)
912 for (i=0;i<m_index;i++)
914 AccessElem *e = &m_elements[i];
915 if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope)
924 /** Element in the stack. */
933 AccessElem m_elements[MAX_STACK_SIZE];
936 /* Returns the "distance" (=number of levels up) from item to scope, or -1
937 * if item in not inside scope.
939 int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
941 //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
942 // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
944 static AccessStack accessStack;
945 if (accessStack.find(scope,fileScope,item))
949 accessStack.push(scope,fileScope,item);
951 int result=0; // assume we found it
954 Definition *itemScope=item->getOuterScope();
957 itemScope==scope || // same thing
958 (item->definitionType()==Definition::TypeMember && // a member
959 itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
960 scope->definitionType()==Definition::TypeClass && // accessible
961 ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope
963 (item->definitionType()==Definition::TypeClass && // a nested class
964 itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
965 scope->definitionType()==Definition::TypeClass && // class of scope
966 ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)
970 //printf("> found it\n");
972 else if (scope==Doxygen::globalScope)
976 SDict<Definition> *cl = fileScope->getUsedClasses();
977 if (accessibleViaUsingClass(cl,fileScope,item))
979 //printf("> found via used class\n");
982 NamespaceSDict *nl = fileScope->getUsedNamespaces();
983 if (accessibleViaUsingNamespace(nl,fileScope,item))
985 //printf("> found via used namespace\n");
989 //printf("> reached global scope\n");
990 result=-1; // not found in path to globalScope
992 else // keep searching
994 // check if scope is a namespace, which is using other classes and namespaces
995 if (scope->definitionType()==Definition::TypeNamespace)
997 NamespaceDef *nscope = (NamespaceDef*)scope;
998 //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
999 SDict<Definition> *cl = nscope->getUsedClasses();
1000 if (accessibleViaUsingClass(cl,fileScope,item))
1002 //printf("> found via used class\n");
1005 NamespaceSDict *nl = nscope->getUsedNamespaces();
1006 if (accessibleViaUsingNamespace(nl,fileScope,item))
1008 //printf("> found via used namespace\n");
1012 // repeat for the parent scope
1013 i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
1014 //printf("> result=%d\n",i);
1015 result= (i==-1) ? -1 : i+2;
1019 //Doxygen::lookupCache.insert(key,new int(result));
1024 /* Returns the "distance" (=number of levels up) from item to scope, or -1
1025 * if item in not in this scope. The explicitScopePart limits the search
1026 * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
1029 * class A { public: class I {}; };
1030 * class B { public: class J {}; };
1032 * - Looking for item=='J' inside scope=='B' will return 0.
1033 * - Looking for item=='I' inside scope=='B' will return -1
1034 * (as it is not found in B nor in the global scope).
1035 * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
1036 * not found and then A::I is searched in the global scope, which matches and
1037 * thus the result is 1.
1039 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,
1040 Definition *item,const QCString &explicitScopePart)
1042 if (explicitScopePart.isEmpty())
1044 // handle degenerate case where there is no explicit scope.
1045 return isAccessibleFrom(scope,fileScope,item);
1048 static AccessStack accessStack;
1049 if (accessStack.find(scope,fileScope,item,explicitScopePart))
1053 accessStack.push(scope,fileScope,item,explicitScopePart);
1056 //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
1057 // item?item->name().data():"<none>",
1058 // explicitScopePart.data());
1059 int result=0; // assume we found it
1060 Definition *newScope = followPath(scope,fileScope,explicitScopePart);
1061 if (newScope) // explicitScope is inside scope => newScope is the result
1063 Definition *itemScope = item->getOuterScope();
1064 //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
1065 //if (newScope && newScope->definitionType()==Definition::TypeClass)
1067 // ClassDef *cd = (ClassDef *)newScope;
1068 // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
1070 if (itemScope==newScope) // exact match of scopes => distance==0
1072 //printf("> found it\n");
1074 else if (itemScope && newScope &&
1075 itemScope->definitionType()==Definition::TypeClass &&
1076 newScope->definitionType()==Definition::TypeClass &&
1077 ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
1080 // inheritance is also ok. Example: looking for B::I, where
1081 // class A { public: class I {} };
1082 // class B : public A {}
1083 // but looking for B::I, where
1084 // class A { public: class I {} };
1085 // class B { public: class I {} };
1086 // will find A::I, so we still prefer a direct match and give this one a distance of 1
1089 //printf("scope(%s) is base class of newScope(%s)\n",
1090 // scope->name().data(),newScope->name().data());
1095 if (newScope->definitionType()==Definition::TypeNamespace)
1097 g_visitedNamespaces.insert(newScope->name(),newScope);
1098 // this part deals with the case where item is a class
1099 // A::B::C but is explicit referenced as A::C, where B is imported
1100 // in A via a using directive.
1101 //printf("newScope is a namespace: %s!\n",newScope->name().data());
1102 NamespaceDef *nscope = (NamespaceDef*)newScope;
1103 SDict<Definition> *cl = nscope->getUsedClasses();
1106 SDict<Definition>::Iterator cli(*cl);
1108 for (cli.toFirst();(cd=cli.current());++cli)
1110 //printf("Trying for class %s\n",cd->name().data());
1113 //printf("> class is used in this scope\n");
1118 NamespaceSDict *nl = nscope->getUsedNamespaces();
1121 NamespaceSDict::Iterator nli(*nl);
1123 for (nli.toFirst();(nd=nli.current());++nli)
1125 if (g_visitedNamespaces.find(nd->name())==0)
1127 //printf("Trying for namespace %s\n",nd->name().data());
1128 i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
1131 //printf("> found via explicit scope of used namespace\n");
1138 // repeat for the parent scope
1139 if (scope!=Doxygen::globalScope)
1141 i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1142 item,explicitScopePart);
1144 //printf(" | result=%d\n",i);
1145 result = (i==-1) ? -1 : i+2;
1148 else // failed to resolve explicitScope
1150 //printf(" failed to resolve: scope=%s\n",scope->name().data());
1151 if (scope->definitionType()==Definition::TypeNamespace)
1153 NamespaceDef *nscope = (NamespaceDef*)scope;
1154 NamespaceSDict *nl = nscope->getUsedNamespaces();
1155 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1157 //printf("> found in used namespace\n");
1161 if (scope==Doxygen::globalScope)
1165 NamespaceSDict *nl = fileScope->getUsedNamespaces();
1166 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1168 //printf("> found in used namespace\n");
1172 //printf("> not found\n");
1175 else // continue by looking into the parent scope
1177 int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1178 item,explicitScopePart);
1179 //printf("> result=%d\n",i);
1180 result= (i==-1) ? -1 : i+2;
1185 //printf(" > result=%d\n",result);
1187 //Doxygen::lookupCache.insert(key,new int(result));
1191 int computeQualifiedIndex(const QCString &name)
1193 int i = name.find('<');
1194 return name.findRev("::",i==-1 ? name.length() : i);
1197 static void getResolvedSymbol(Definition *scope,
1200 const QCString &explicitScopePart,
1201 ArgumentList *actTemplParams,
1203 ClassDef *&bestMatch,
1204 MemberDef *&bestTypedef,
1205 QCString &bestTemplSpec,
1206 QCString &bestResolvedType
1209 //printf(" => found type %x name=%s d=%p\n",
1210 // d->definitionType(),d->name().data(),d);
1212 // only look at classes and members that are enums or typedefs
1213 if (d->definitionType()==Definition::TypeClass ||
1214 (d->definitionType()==Definition::TypeMember &&
1215 (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate())
1219 g_visitedNamespaces.clear();
1220 // test accessibility of definition within scope.
1221 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
1222 //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
1223 if (distance!=-1) // definition is accessible
1225 // see if we are dealing with a class or a typedef
1226 if (d->definitionType()==Definition::TypeClass) // d is a class
1228 ClassDef *cd = (ClassDef *)d;
1229 //printf("cd=%s\n",cd->name().data());
1230 if (!cd->isTemplateArgument()) // skip classes that
1231 // are only there to
1232 // represent a template
1235 //printf("is not a templ arg\n");
1236 if (distance<minDistance) // found a definition that is "closer"
1238 minDistance=distance;
1241 bestTemplSpec.resize(0);
1242 bestResolvedType = cd->qualifiedName();
1244 else if (distance==minDistance &&
1245 fileScope && bestMatch &&
1246 fileScope->getUsedNamespaces() &&
1247 d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
1248 bestMatch->getOuterScope()==Doxygen::globalScope
1251 // in case the distance is equal it could be that a class X
1252 // is defined in a namespace and in the global scope. When searched
1253 // in the global scope the distance is 0 in both cases. We have
1254 // to choose one of the definitions: we choose the one in the
1255 // namespace if the fileScope imports namespaces and the definition
1256 // found was in a namespace while the best match so far isn't.
1257 // Just a non-perfect heuristic but it could help in some situations
1258 // (kdecore code is an example).
1259 minDistance=distance;
1262 bestTemplSpec.resize(0);
1263 bestResolvedType = cd->qualifiedName();
1268 //printf(" is a template argument!\n");
1271 else if (d->definitionType()==Definition::TypeMember)
1273 MemberDef *md = (MemberDef *)d;
1274 //printf(" member isTypedef()=%d\n",md->isTypedef());
1275 if (md->isTypedef()) // d is a typedef
1277 QCString args=md->argsString();
1278 if (args.isEmpty()) // do not expand "typedef t a[4];"
1280 //printf(" found typedef!\n");
1282 // we found a symbol at this distance, but if it didn't
1283 // resolve to a class, we still have to make sure that
1284 // something at a greater distance does not match, since
1285 // that symbol is hidden by this one.
1286 if (distance<minDistance)
1290 minDistance=distance;
1291 MemberDef *enumType = 0;
1292 ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
1293 if (cd) // type resolves to a class
1295 //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
1298 bestTemplSpec = spec;
1299 bestResolvedType = type;
1301 else if (enumType) // type resolves to a enum
1303 //printf(" is enum\n");
1305 bestTypedef = enumType;
1307 bestResolvedType = enumType->qualifiedName();
1309 else if (md->isReference()) // external reference
1313 bestTemplSpec = spec;
1314 bestResolvedType = type;
1320 bestTemplSpec.resize(0);
1321 bestResolvedType.resize(0);
1322 //printf(" no match\n");
1327 //printf(" not the best match %d min=%d\n",distance,minDistance);
1332 //printf(" not a simple typedef\n")
1335 else if (md->isEnumerate())
1337 if (distance<minDistance)
1339 minDistance=distance;
1343 bestResolvedType = md->qualifiedName();
1347 } // if definition accessible
1350 //printf(" Not accessible!\n");
1352 } // if definition is a class or member
1353 //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
1356 /* Find the fully qualified class name referred to by the input class
1357 * or typedef name against the input scope.
1358 * Loops through scope and each of its parent scopes looking for a
1359 * match against the input name. Can recursively call itself when
1360 * resolving typedefs.
1362 static ClassDef *getResolvedClassRec(Definition *scope,
1365 MemberDef **pTypeDef,
1366 QCString *pTemplSpec,
1367 QCString *pResolvedType
1370 //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
1372 QCString explicitScopePart;
1373 QCString strippedTemplateParams;
1374 name=stripTemplateSpecifiersFromScope
1375 (removeRedundantWhiteSpace(n),TRUE,
1376 &strippedTemplateParams);
1377 ArgumentList actTemplParams;
1378 if (!strippedTemplateParams.isEmpty()) // template part that was stripped
1380 stringToArgumentList(strippedTemplateParams,&actTemplParams);
1383 int qualifierIndex = computeQualifiedIndex(name);
1384 //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
1385 if (qualifierIndex!=-1) // qualified name
1387 // split off the explicit scope part
1388 explicitScopePart=name.left(qualifierIndex);
1389 // todo: improve namespace alias substitution
1390 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
1391 name=name.mid(qualifierIndex+2);
1396 //printf("] empty name\n");
1397 return 0; // empty name
1400 //printf("Looking for symbol %s\n",name.data());
1401 DefinitionIntf *di = Doxygen::symbolMap->find(name);
1402 // the -g (for C# generics) and -p (for ObjC protocols) are now already
1403 // stripped from the key used in the symbolMap, so that is not needed here.
1406 //di = Doxygen::symbolMap->find(name+"-g");
1409 di = Doxygen::symbolMap->find(name+"-p");
1412 //printf("no such symbol!\n");
1417 //printf("found symbol!\n");
1419 bool hasUsingStatements =
1420 (fileScope && ((fileScope->getUsedNamespaces() &&
1421 fileScope->getUsedNamespaces()->count()>0) ||
1422 (fileScope->getUsedClasses() &&
1423 fileScope->getUsedClasses()->count()>0))
1425 //printf("hasUsingStatements=%d\n",hasUsingStatements);
1426 // Since it is often the case that the same name is searched in the same
1427 // scope over an over again (especially for the linked source code generation)
1428 // we use a cache to collect previous results. This is possible since the
1429 // result of a lookup is deterministic. As the key we use the concatenated
1430 // scope, the name to search for and the explicit scope prefix. The speedup
1431 // achieved by this simple cache can be enormous.
1432 int scopeNameLen = scope->name().length()+1;
1433 int nameLen = name.length()+1;
1434 int explicitPartLen = explicitScopePart.length();
1435 int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
1437 // below is a more efficient coding of
1438 // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
1439 QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
1441 qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
1443 qstrcpy(p,name); *(p+nameLen-1)='+';
1445 qstrcpy(p,explicitScopePart);
1448 // if a file scope is given and it contains using statements we should
1449 // also use the file part in the key (as a class name can be in
1450 // two different namespaces and a using statement in a file can select
1452 if (hasUsingStatements)
1454 // below is a more efficient coding of
1455 // key+="+"+fileScope->name();
1457 qstrcpy(p,fileScope->absFilePath());
1462 LookupInfo *pval=Doxygen::lookupCache->find(key);
1463 //printf("Searching for %s result=%p\n",key.data(),pval);
1466 //printf("LookupInfo %p %p '%s' %p\n",
1467 // pval->classDef, pval->typeDef, pval->templSpec.data(),
1468 // pval->resolvedType.data());
1469 if (pTemplSpec) *pTemplSpec=pval->templSpec;
1470 if (pTypeDef) *pTypeDef=pval->typeDef;
1471 if (pResolvedType) *pResolvedType=pval->resolvedType;
1472 //printf("] cachedMatch=%s\n",
1473 // pval->classDef?pval->classDef->name().data():"<none>");
1475 // printf("templSpec=%s\n",pTemplSpec->data());
1476 return pval->classDef;
1478 else // not found yet; we already add a 0 to avoid the possibility of
1479 // endless recursion.
1481 Doxygen::lookupCache->insert(key,new LookupInfo);
1484 ClassDef *bestMatch=0;
1485 MemberDef *bestTypedef=0;
1486 QCString bestTemplSpec;
1487 QCString bestResolvedType;
1488 int minDistance=10000; // init at "infinite"
1490 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
1492 //printf(" name is not unique\n");
1493 DefinitionListIterator dli(*(DefinitionList*)di);
1496 for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
1498 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1499 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1505 //printf(" name is unique\n");
1506 Definition *d = (Definition *)di;
1507 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1508 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1514 *pTypeDef = bestTypedef;
1518 *pTemplSpec = bestTemplSpec;
1522 *pResolvedType = bestResolvedType;
1524 //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
1525 // bestMatch,bestResolvedType.data());
1527 pval=Doxygen::lookupCache->find(key);
1530 pval->classDef = bestMatch;
1531 pval->typeDef = bestTypedef;
1532 pval->templSpec = bestTemplSpec;
1533 pval->resolvedType = bestResolvedType;
1537 Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
1539 //printf("] bestMatch=%s distance=%d\n",
1540 // bestMatch?bestMatch->name().data():"<none>",minDistance);
1542 // printf("templSpec=%s\n",pTemplSpec->data());
1546 /* Find the fully qualified class name referred to by the input class
1547 * or typedef name against the input scope.
1548 * Loops through scope and each of its parent scopes looking for a
1549 * match against the input name.
1551 ClassDef *getResolvedClass(Definition *scope,
1554 MemberDef **pTypeDef,
1555 QCString *pTemplSpec,
1556 bool mayBeUnlinkable,
1558 QCString *pResolvedType
1561 g_resolvedTypedefs.clear();
1563 (scope->definitionType()!=Definition::TypeClass &&
1564 scope->definitionType()!=Definition::TypeNamespace
1566 (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
1569 scope=Doxygen::globalScope;
1571 //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
1572 // scope?scope->name().data():"<global>",
1573 // fileScope?fileScope->name().data():"<none>",
1577 ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
1578 if (!mayBeUnlinkable && result && !result->isLinkable())
1580 if (!mayBeHidden || !result->isHidden())
1582 //printf("result was %s\n",result?result->name().data():"<none>");
1583 result=0; // don't link to artificial/hidden classes unless explicitly allowed
1586 //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
1587 // n,result?result->name().data():"<none>");
1591 //-------------------------------------------------------------------------
1592 //-------------------------------------------------------------------------
1593 //-------------------------------------------------------------------------
1594 //-------------------------------------------------------------------------
1596 static bool findOperator(const QCString &s,int i)
1598 int b = s.findRev("operator",i);
1599 if (b==-1) return FALSE; // not found
1601 while (b<i) // check if there are only spaces in between
1602 // the operator and the >
1604 if (!isspace((uchar)s.at(b))) return FALSE;
1610 static bool findOperator2(const QCString &s,int i)
1612 int b = s.findRev("operator",i);
1613 if (b==-1) return FALSE; // not found
1615 while (b<i) // check if there are only non-ascii
1616 // characters in front of the operator
1618 if (isId((uchar)s.at(b))) return FALSE;
1624 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
1625 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
1627 // Note: this function is not reentrant due to the use of static buffer!
1628 QCString removeRedundantWhiteSpace(const QCString &s)
1630 static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT");
1631 if (s.isEmpty()) return s;
1632 static GrowBuf growBuf;
1633 //int resultLen = 1024;
1634 //int resultPos = 0;
1635 //QCString result(resultLen);
1636 // we use growBuf.addChar(c) instead of result+=c to
1637 // improve the performance of this function
1648 // search for "const"
1649 if (csp<6 && c==constScope[csp] && // character matches substring "const"
1650 (csp>0 || // if it is the first character
1651 i==0 || // the previous may not be a digit
1656 else // reset counter
1659 // search for "virtual"
1660 if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
1661 (vsp>0 || // if it is the first character
1662 i==0 || // the previous may not be a digit
1667 else // reset counter
1670 if (c=='"') // quoted string
1677 growBuf.addChar(cc);
1678 if (cc=='\\') // escaped character
1680 growBuf.addChar(s.at(i+1));
1683 else if (cc=='"') // end of string
1684 { i++; goto nextChar; }
1685 else // any other character
1689 else if (i<l-2 && c=='<' && // current char is a <
1690 (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space
1691 (i<8 || !findOperator(s,i)) // string in front is not "operator"
1694 growBuf.addChar('<');
1695 growBuf.addChar(' ');
1697 else if (i>0 && c=='>' && // current char is a >
1698 (isId(s.at(i-1)) || isspace((uchar)s.at(i-1)) || s.at(i-1)=='*' || s.at(i-1)=='&') && // prev char is an id char or space
1699 (i<8 || !findOperator(s,i)) // string in front is not "operator"
1702 growBuf.addChar(' ');
1703 growBuf.addChar('>');
1705 else if (i>0 && c==',' && !isspace((uchar)s.at(i-1))
1706 && ((i<l-1 && isId(s.at(i+1)))
1707 || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) // for PHP
1708 || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))) // for PHP
1710 growBuf.addChar(',');
1711 growBuf.addChar(' ');
1714 ((isId(s.at(i)) && s.at(i-1)==')') ||
1715 (s.at(i)=='\'' && s.at(i-1)==' ')
1719 growBuf.addChar(' ');
1720 growBuf.addChar(s.at(i));
1722 else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ &&
1723 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
1729 // prevent const ::A from being converted to const::A
1731 growBuf.addChar('t');
1732 growBuf.addChar(' ');
1733 if (s.at(i+1)==' ') i++;
1736 else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/)
1737 // replace const::A by const ::A
1739 growBuf.addChar(' ');
1740 growBuf.addChar(':');
1743 else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ &&
1744 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
1750 // prevent virtual ::A from being converted to virtual::A
1752 growBuf.addChar('l');
1753 growBuf.addChar(' ');
1754 if (s.at(i+1)==' ') i++;
1757 else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/)
1758 // replace virtual::A by virtual ::A
1760 growBuf.addChar(' ');
1761 growBuf.addChar(':');
1764 else if (!isspace((uchar)c) || // not a space
1765 ( i>0 && i<l-1 && // internal character
1766 (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']') &&
1768 (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) ||
1769 (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3)))
1774 if (c=='*' || c=='&' || c=='@' || c=='$')
1776 //uint rl=result.length();
1777 uint rl=growBuf.getPos();
1778 if ((rl>0 && (isId(growBuf.at(rl-1)) || growBuf.at(rl-1)=='>')) &&
1779 ((c!='*' && c!='&') || !findOperator2(s,i)) // avoid splitting operator* and operator->* and operator&
1782 growBuf.addChar(' ');
1787 uint rl=growBuf.getPos();
1788 if (rl>0 && growBuf.at(rl-1)==')' && i<l-1 && s.at(i+1)=='>') // trailing return type ')->' => ') ->'
1790 growBuf.addChar(' ');
1795 (c=='^' || c=='%') && i>1 && isId(s.at(i-1)) &&
1799 growBuf.addChar(' '); // C++/CLI: Type^ name and Type% name
1803 //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data());
1805 //result.resize(resultPos);
1806 return growBuf.get();
1809 bool rightScopeMatch(const QCString &scope, const QCString &name)
1811 return (name==scope || // equal
1812 (scope.right(name.length())==name && // substring
1813 scope.at(scope.length()-name.length()-1)==':' // scope
1818 bool leftScopeMatch(const QCString &scope, const QCString &name)
1820 return (name==scope || // equal
1821 (scope.left(name.length())==name && // substring
1822 scope.at(name.length())==':' // scope
1828 void linkifyText(const TextGeneratorIntf &out,Definition *scope,
1829 FileDef *fileScope,Definition *self,
1830 const char *text, bool autoBreak,bool external,
1831 bool keepSpaces,int indentLevel)
1833 //printf("linkify=`%s'\n",text);
1834 static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*");
1835 static QRegExp regExpSplit("(?!:),");
1836 QCString txtStr=text;
1837 int strLen = txtStr.length();
1838 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n",
1839 // scope?scope->name().data():"<none>",
1840 // fileScope?fileScope->name().data():"<none>",
1841 // txtStr.data(),strLen,external);
1846 int floatingIndex=0;
1847 if (strLen==0) return;
1848 // read a word from the text string
1849 while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 &&
1850 (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
1853 // add non-word part to the result
1854 floatingIndex+=newIndex-skipIndex+matchLen;
1855 bool insideString=FALSE;
1857 for (i=index;i<newIndex;i++)
1859 if (txtStr.at(i)=='"') insideString=!insideString;
1862 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
1863 if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
1865 QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
1866 int splitLength = splitText.length();
1868 i=splitText.find(regExpSplit,0);
1869 if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
1870 if (i==-1) i=splitText.find('>');
1871 if (i==-1) i=splitText.find(' ');
1872 //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
1873 if (i!=-1) // add a link-break at i in case of Html output
1875 out.writeString(splitText.left(i+offset),keepSpaces);
1876 out.writeBreak(indentLevel==0 ? 0 : indentLevel+1);
1877 out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
1878 floatingIndex=splitLength-i-offset+matchLen;
1882 out.writeString(splitText,keepSpaces);
1887 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
1888 out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces);
1890 // get word from string
1891 QCString word=txtStr.mid(newIndex,matchLen);
1892 QCString matchWord = substitute(substitute(word,"\\","::"),".","::");
1893 //printf("linkifyText word=%s matchWord=%s scope=%s\n",
1894 // word.data(),matchWord.data(),scope?scope->name().data():"<none>");
1903 //printf("** Match word '%s'\n",matchWord.data());
1905 MemberDef *typeDef=0;
1906 cd=getResolvedClass(scope,fileScope,matchWord,&typeDef);
1907 if (typeDef) // First look at typedef then class, see bug 584184.
1909 //printf("Found typedef %s\n",typeDef->name().data());
1910 if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
1912 if (typeDef->getOuterScope()!=self)
1914 out.writeLink(typeDef->getReference(),
1915 typeDef->getOutputFileBase(),
1922 if (!found && (cd || (cd=getClass(matchWord))))
1924 //printf("Found class %s\n",cd->name().data());
1925 // add link to the result
1926 if (external ? cd->isLinkable() : cd->isLinkableInProject())
1930 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1935 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
1937 // add link to the result
1938 if (external ? cd->isLinkable() : cd->isLinkableInProject())
1942 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1947 // else if ((cd=getClass(matchWord+"-g"))) // C# generic as well
1949 // // add link to the result
1950 // if (external ? cd->isLinkable() : cd->isLinkableInProject())
1954 // out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1961 //printf(" -> nothing\n");
1964 int m = matchWord.findRev("::");
1967 (scope->definitionType()==Definition::TypeClass ||
1968 scope->definitionType()==Definition::TypeNamespace
1972 scopeName=scope->name();
1976 scopeName = matchWord.left(m);
1977 matchWord = matchWord.mid(m+2);
1980 //printf("ScopeName=%s\n",scopeName.data());
1981 //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data());
1983 getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) &&
1984 //(md->isTypedef() || md->isEnumerate() ||
1985 // md->isReference() || md->isVariable()
1987 (external ? md->isLinkable() : md->isLinkableInProject())
1990 //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
1991 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
1992 // md->anchor(),word);
1993 if (md!=self && (self==0 || md->name()!=self->name()))
1994 // name check is needed for overloaded members, where getDefs just returns one
1996 out.writeLink(md->getReference(),md->getOutputFileBase(),
1998 //printf("found symbol %s\n",matchWord.data());
2004 if (!found) // add word to the result
2006 out.writeString(word,keepSpaces);
2008 // set next start point in the string
2009 //printf("index=%d/%d\n",index,txtStr.length());
2010 skipIndex=index=newIndex+matchLen;
2012 // add last part of the string to the result.
2013 //ol.docify(txtStr.right(txtStr.length()-skipIndex));
2014 out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
2018 void writeExample(OutputList &ol,ExampleSDict *ed)
2020 QCString exampleLine=theTranslator->trWriteList(ed->count());
2022 //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
2023 //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
2024 //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
2025 QRegExp marker("@[0-9]+");
2026 int index=0,newIndex,matchLen;
2027 // now replace all markers in inheritLine with links to the classes
2028 while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
2031 ol.parseText(exampleLine.mid(index,newIndex-index));
2032 uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
2033 Example *e=ed->at(entryIndex);
2036 ol.pushGeneratorState();
2037 //if (latexEnabled) ol.disable(OutputGenerator::Latex);
2038 ol.disable(OutputGenerator::Latex);
2039 ol.disable(OutputGenerator::RTF);
2040 // link for Html / man
2041 //printf("writeObjectLink(file=%s)\n",e->file.data());
2042 ol.writeObjectLink(0,e->file,e->anchor,e->name);
2043 ol.popGeneratorState();
2045 ol.pushGeneratorState();
2046 //if (latexEnabled) ol.enable(OutputGenerator::Latex);
2047 ol.disable(OutputGenerator::Man);
2048 ol.disable(OutputGenerator::Html);
2049 // link for Latex / pdf with anchor because the sources
2050 // are not hyperlinked (not possible with a verbatim environment).
2051 ol.writeObjectLink(0,e->file,0,e->name);
2052 //if (manEnabled) ol.enable(OutputGenerator::Man);
2053 //if (htmlEnabled) ol.enable(OutputGenerator::Html);
2054 ol.popGeneratorState();
2056 index=newIndex+matchLen;
2058 ol.parseText(exampleLine.right(exampleLine.length()-index));
2059 ol.writeString(".");
2063 QCString argListToString(ArgumentList *al,bool useCanonicalType,bool showDefVals)
2066 if (al==0) return result;
2067 Argument *a=al->first();
2071 QCString type1 = useCanonicalType && !a->canType.isEmpty() ?
2072 a->canType : a->type;
2074 int i=type1.find(")("); // hack to deal with function pointers
2078 type1=type1.left(i);
2080 if (!a->attrib.isEmpty())
2082 result+=a->attrib+" ";
2084 if (!a->name.isEmpty() || !a->array.isEmpty())
2086 result+= type1+" "+a->name+type2+a->array;
2090 result+= type1+type2;
2092 if (!a->defval.isEmpty() && showDefVals)
2094 result+="="+a->defval;
2097 if (a) result+=", ";
2100 if (al->constSpecifier) result+=" const";
2101 if (al->volatileSpecifier) result+=" volatile";
2102 if (!al->trailingReturnType.isEmpty()) result+=" -> "+al->trailingReturnType;
2103 if (al->pureSpecifier) result+=" =0";
2104 return removeRedundantWhiteSpace(result);
2107 QCString tempArgListToString(ArgumentList *al)
2110 if (al==0) return result;
2112 Argument *a=al->first();
2115 if (!a->name.isEmpty()) // add template argument name
2117 if (a->type.left(4)=="out") // C# covariance
2121 else if (a->type.left(3)=="in") // C# contravariance
2127 else // extract name from type
2129 int i=a->type.length()-1;
2130 while (i>=0 && isId(a->type.at(i))) i--;
2133 result+=a->type.right(a->type.length()-i-1);
2135 else // nothing found -> take whole name
2141 if (a) result+=", ";
2144 return removeRedundantWhiteSpace(result);
2148 // compute the HTML anchors for a list of members
2149 void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId)
2153 MemberListIterator mli(*ml);
2155 for (;(md=mli.current());++mli)
2157 if (!md->isReference())
2161 anchor.sprintf("%c%d",id,count++);
2163 anchor.sprintf("%c%d_%d",id,groupId,count++);
2164 if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
2165 md->setAnchor(anchor);
2166 //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
2167 // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
2172 //----------------------------------------------------------------------------
2174 /*! takes the \a buf of the given length \a len and converts CR LF (DOS)
2175 * or CR (MAC) line ending to LF (Unix). Returns the length of the
2176 * converted content (i.e. the same as \a len (Unix, MAC) or
2179 int filterCRLF(char *buf,int len)
2181 int src = 0; // source index
2182 int dest = 0; // destination index
2183 char c; // current character
2187 c = buf[src++]; // Remember the processed character.
2188 if (c == '\r') // CR to be solved (MAC, DOS)
2190 c = '\n'; // each CR to LF
2191 if (src<len && buf[src] == '\n')
2192 ++src; // skip LF just after CR (DOS)
2194 else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
2196 c = ' '; // turn into a space
2198 buf[dest++] = c; // copy the (modified) character to dest
2200 return dest; // length of the valid part of the buf
2203 static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
2206 // compare the file name to the filter pattern list
2207 QStrListIterator sli(filterList);
2209 for (sli.toFirst(); (filterStr = sli.current()); ++sli)
2211 QCString fs = filterStr;
2212 int i_equals=fs.find('=');
2215 QCString filterPattern = fs.left(i_equals);
2216 QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE);
2217 if (fpat.match(name)!=-1)
2220 QCString filterName = fs.mid(i_equals+1);
2221 if (filterName.find(' ')!=-1)
2222 { // add quotes if the name has spaces
2223 filterName="\""+filterName+"\"";
2235 /*! looks for a filter for the file \a name. Returns the name of the filter
2236 * if there is a match for the file name, otherwise an empty string.
2237 * In case \a inSourceCode is TRUE then first the source filter list is
2240 QCString getFileFilter(const char* name,bool isSourceCode)
2243 if (name==0) return "";
2245 QStrList& filterSrcList = Config_getList("FILTER_SOURCE_PATTERNS");
2246 QStrList& filterList = Config_getList("FILTER_PATTERNS");
2248 QCString filterName;
2250 if (isSourceCode && !filterSrcList.isEmpty())
2251 { // first look for source filter pattern list
2252 filterName = getFilterFromList(name,filterSrcList,found);
2254 if (!found && filterName.isEmpty())
2255 { // then look for filter pattern list
2256 filterName = getFilterFromList(name,filterList,found);
2259 { // then use the generic input filter
2260 return Config_getString("INPUT_FILTER");
2269 QCString transcodeCharacterStringToUTF8(const QCString &input)
2272 static QCString inputEncoding = Config_getString("INPUT_ENCODING");
2273 const char *outputEncoding = "UTF-8";
2274 if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
2275 int inputSize=input.length();
2276 int outputSize=inputSize*4+1;
2277 QCString output(outputSize);
2278 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
2279 if (cd==(void *)(-1))
2281 err("error: unsupported character conversion: '%s'->'%s'\n",
2282 inputEncoding.data(),outputEncoding);
2287 size_t iLeft=inputSize;
2288 size_t oLeft=outputSize;
2289 char *inputPtr = input.data();
2290 char *outputPtr = output.data();
2291 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
2294 output.resize(outputSize+1);
2295 output.at(outputSize)='\0';
2296 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
2300 err("error: failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
2301 inputEncoding.data(),outputEncoding,input.data());
2305 portable_iconv_close(cd);
2306 return error ? input : output;
2309 /*! reads a file with name \a name and returns it as a string. If \a filter
2310 * is TRUE the file will be filtered by any user specified input filter.
2311 * If \a name is "-" the string will be read from standard input.
2313 QCString fileToString(const char *name,bool filter,bool isSourceCode)
2315 if (name==0 || name[0]==0) return 0;
2318 bool fileOpened=FALSE;
2319 if (name[0]=='-' && name[1]==0) // read from stdin
2321 fileOpened=f.open(IO_ReadOnly,stdin);
2324 const int bSize=4096;
2325 QCString contents(bSize);
2328 while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
2331 contents.resize(totalSize+bSize);
2333 totalSize = filterCRLF(contents.data(),totalSize+size)+2;
2334 contents.resize(totalSize);
2335 contents.at(totalSize-2)='\n'; // to help the scanner
2336 contents.at(totalSize-1)='\0';
2340 else // read from file
2343 if (!fi.exists() || !fi.isFile())
2345 err("error: file `%s' not found\n",name);
2348 QCString filterName = getFileFilter(name,isSourceCode);
2349 if (filterName.isEmpty() || !filter)
2352 fileOpened=f.open(IO_ReadOnly);
2356 QCString contents(fsize+2);
2357 f.readBlock(contents.data(),fsize);
2358 if (fsize==0 || contents[fsize-1]=='\n')
2359 contents[fsize]='\0';
2361 contents[fsize]='\n'; // to help the scanner
2362 contents[fsize+1]='\0';
2364 int newSize = filterCRLF(contents.data(),fsize+2);
2365 if (newSize!=fsize+2)
2367 contents.resize(newSize);
2369 return transcodeCharacterStringToUTF8(contents);
2372 else // filter the input
2374 QCString cmd=filterName+" \""+name+"\"";
2375 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
2376 FILE *f=portable_popen(cmd,"r");
2379 err("error: could not execute filter %s\n",filterName.data());
2382 const int bSize=4096;
2383 QCString contents(bSize);
2386 while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize)
2389 contents.resize(totalSize+bSize);
2391 totalSize = filterCRLF(contents.data(),totalSize+size)+2;
2392 contents.resize(totalSize);
2393 contents.at(totalSize-2)='\n'; // to help the scanner
2394 contents.at(totalSize-1)='\0';
2396 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
2397 Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",contents.data());
2398 return transcodeCharacterStringToUTF8(contents);
2403 err("error: cannot open file `%s' for reading\n",name);
2408 QCString dateToString(bool includeTime)
2410 QDateTime current = QDateTime::currentDateTime();
2411 return theTranslator->trDateTime(current.date().year(),
2412 current.date().month(),
2413 current.date().day(),
2414 current.date().dayOfWeek(),
2415 current.time().hour(),
2416 current.time().minute(),
2417 current.time().second(),
2421 QCString yearToString()
2423 const QDate &d=QDate::currentDate();
2425 result.sprintf("%d", d.year());
2429 //----------------------------------------------------------------------
2430 // recursive function that returns the number of branches in the
2431 // inheritance tree that the base class `bcd' is below the class `cd'
2433 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
2435 if (bcd->categoryOf()) // use class that is being extended in case of
2436 // an Objective-C category
2438 bcd=bcd->categoryOf();
2440 if (cd==bcd) return level;
2443 err("error: Internal inconsistency: found class %s seem to have a recursive "
2444 "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
2447 int m=maxInheritanceDepth;
2448 if (cd->baseClasses())
2450 BaseClassDef *bcdi = cd->baseClasses()->first();
2453 int mc=minClassDistance(bcdi->classDef,bcd,level+1);
2456 bcdi = cd->baseClasses()->next();
2462 Protection classInheritedProtectionLevel(ClassDef *cd,ClassDef *bcd,Protection prot,int level)
2464 if (bcd->categoryOf()) // use class that is being extended in case of
2465 // an Objective-C category
2467 bcd=bcd->categoryOf();
2475 err("error: Internal inconsistency: found class %s seem to have a recursive "
2476 "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
2478 else if (cd->baseClasses())
2480 BaseClassDef *bcdi = cd->baseClasses()->first();
2481 while (bcdi && prot!=Private)
2483 Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1);
2484 if (baseProt==Private) prot=Private;
2485 else if (baseProt==Protected) prot=Protected;
2486 bcdi = cd->baseClasses()->next();
2490 //printf("classInheritedProtectionLevel(%s,%s)=%d\n",cd->name().data(),bcd->name().data(),prot);
2494 //static void printArgList(ArgumentList *al)
2496 // if (al==0) return;
2497 // ArgumentListIterator ali(*al);
2500 // for (;(a=ali.current());++ali)
2502 // printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():"");
2508 // strip any template specifiers that follow className in string s
2509 static QCString trimTemplateSpecifiers(
2510 const QCString &namespaceName,
2511 const QCString &className,
2515 //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
2516 QCString scopeName=mergeScopes(namespaceName,className);
2517 ClassDef *cd=getClass(scopeName);
2518 if (cd==0) return s; // should not happen, but guard anyway.
2522 int i=className.length()-1;
2523 if (i>=0 && className.at(i)=='>') // template specialization
2525 // replace unspecialized occurrences in s, with their specialized versions.
2530 char c=className.at(i);
2531 if (c=='>') count++,i--;
2532 else if (c=='<') { count--; if (count==0) break; }
2535 QCString unspecClassName=className.left(i);
2538 while ((i=result.find(unspecClassName,p))!=-1)
2540 if (result.at(i+l)!='<') // unspecialized version
2542 result=result.left(i)+className+result.right(result.length()-i-l);
2549 //printf("result after specialization: %s\n",result.data());
2551 QCString qualName=cd->qualifiedNameWithTemplateParameters();
2552 //printf("QualifiedName = %s\n",qualName.data());
2553 // We strip the template arguments following className (if any)
2554 if (!qualName.isEmpty()) // there is a class name
2559 while ((is=getScopeFragment(qualName,ps,&l))!=-1)
2561 QCString qualNamePart = qualName.right(qualName.length()-is);
2562 //printf("qualNamePart=%s\n",qualNamePart.data());
2563 while ((i=result.find(qualNamePart,p))!=-1)
2565 int ql=qualNamePart.length();
2566 result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
2567 p=i+cd->name().length();
2572 //printf("result=%s\n",result.data());
2574 return result.stripWhiteSpace();
2578 * @param pattern pattern to look for
2579 * @param s string to search in
2580 * @param p position to start
2581 * @param len resulting pattern length
2582 * @returns position on which string is found, or -1 if not found
2584 static int findScopePattern(const QCString &pattern,const QCString &s,
2588 int pl=pattern.length();
2593 sp=p; // start of match
2594 int pp=0; // pattern position
2595 while (p<sl && pp<pl)
2597 if (s.at(p)=='<') // skip template arguments while matching
2600 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2604 if (s.at(p)=='<') bc++;
2605 else if (s.at(p)=='>')
2614 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2618 else if (s.at(p)==pattern.at(pp))
2620 //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
2626 //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
2631 if (pp==pl) // whole pattern matches
2640 static QCString trimScope(const QCString &name,const QCString &s)
2642 int scopeOffset=name.length();
2644 do // for each scope
2647 QCString scope=name.left(scopeOffset)+"::";
2648 //printf("Trying with scope=`%s'\n",scope.data());
2651 while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
2653 tmp+=result.mid(p,i-p); // add part before pattern
2656 tmp+=result.right(result.length()-p); // add trailing part
2658 scopeOffset=name.findRev("::",scopeOffset-1);
2660 } while (scopeOffset>0);
2661 //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
2666 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
2668 //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
2669 BaseClassListIterator bcli(*bcl);
2671 for (;(bcd=bcli.current());++bcli)
2673 ClassDef *cd=bcd->classDef;
2674 //printf("Trying class %s\n",cd->name().data());
2675 int spos=s.find(cd->name()+"::");
2678 s = s.left(spos)+s.right(
2679 s.length()-spos-cd->name().length()-2
2682 //printf("base class `%s'\n",cd->name().data());
2683 if (cd->baseClasses())
2684 trimBaseClassScope(cd->baseClasses(),s,level+1);
2689 /*! if either t1 or t2 contains a namespace scope, then remove that
2690 * scope. If neither or both have a namespace scope, t1 and t2 remain
2693 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
2699 int i1=p1==0 ? -1 : t1.findRev("::",p1);
2700 int i2=p2==0 ? -1 : t2.findRev("::",p2);
2701 if (i1==-1 && i2==-1)
2705 if (i1!=-1 && i2==-1) // only t1 has a scope
2707 QCString scope=t1.left(i1);
2708 replaceNamespaceAliases(scope,i1);
2710 int so=nsName.length();
2713 QCString fullScope=nsName.left(so);
2714 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2716 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2718 t1 = t1.right(t1.length()-i1-2);
2725 else if ((so=nsName.findRev("::",so-1))==-1)
2732 else if (i1==-1 && i2!=-1) // only t2 has a scope
2734 QCString scope=t2.left(i2);
2735 replaceNamespaceAliases(scope,i2);
2737 int so=nsName.length();
2740 QCString fullScope=nsName.left(so);
2741 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2743 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2745 t2 = t2.right(t2.length()-i2-2);
2752 else if ((so=nsName.findRev("::",so-1))==-1)
2765 static void stripIrrelevantString(QCString &target,const QCString &str)
2767 if (target==str) { target.resize(0); return; }
2771 while ((i=target.find(str,p))!=-1)
2773 bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
2774 (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
2777 int i1=target.find('*',i+l);
2778 int i2=target.find('&',i+l);
2779 if (i1==-1 && i2==-1)
2781 // strip str from target at index i
2782 target=target.left(i)+target.right(target.length()-i-l);
2786 else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
2788 // move str to front
2789 target=str+" "+target.left(i)+target.right(target.length()-i-l);
2796 if (changed) target=target.stripWhiteSpace();
2799 /*! According to the C++ spec and Ivan Vecerina:
2801 Parameter declarations that differ only in the presence or absence
2802 of const and/or volatile are equivalent.
2804 So the following example, show what is stripped by this routine
2805 for const. The same is done for volatile.
2808 const T param -> T param // not relevant
2809 const T& param -> const T& param // const needed
2810 T* const param -> T* param // not relevant
2811 const T* param -> const T* param // const needed
2814 void stripIrrelevantConstVolatile(QCString &s)
2816 //printf("stripIrrelevantConstVolatile(%s)=",s.data());
2817 stripIrrelevantString(s,"const");
2818 stripIrrelevantString(s,"volatile");
2819 //printf("%s\n",s.data());
2823 // a bit of debug support for matchArguments
2826 //#define MATCH printf("Match at line %d\n",__LINE__);
2827 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
2830 static bool matchArgument(const Argument *srcA,const Argument *dstA,
2831 const QCString &className,
2832 const QCString &namespaceName,
2833 NamespaceSDict *usingNamespaces,
2834 SDict<Definition> *usingClasses)
2836 //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
2837 // srcA->type.data(),srcA->name.data(),
2838 // dstA->type.data(),dstA->name.data(),
2842 // TODO: resolve any typedefs names that are part of srcA->type
2843 // before matching. This should use className and namespaceName
2844 // and usingNamespaces and usingClass to determine which typedefs
2845 // are in-scope, so it will not be very efficient :-(
2847 QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
2848 QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
2849 QCString srcAName=srcA->name.stripWhiteSpace();
2850 QCString dstAName=dstA->name.stripWhiteSpace();
2851 srcAType.stripPrefix("class ");
2852 dstAType.stripPrefix("class ");
2854 // allow distinguishing "const A" from "const B" even though
2855 // from a syntactic point of view they would be two names of the same
2856 // type "const". This is not fool prove of course, but should at least
2857 // catch the most common cases.
2858 if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
2863 if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
2868 if (srcAName=="const" || srcAName=="volatile")
2873 else if (dstA->name=="const" || dstA->name=="volatile")
2875 dstAType+=dstA->name;
2879 stripIrrelevantConstVolatile(srcAType);
2880 stripIrrelevantConstVolatile(dstAType);
2882 // strip typename keyword
2883 if (strncmp(srcAType,"typename ",9)==0)
2885 srcAType = srcAType.right(srcAType.length()-9);
2887 if (strncmp(dstAType,"typename ",9)==0)
2889 dstAType = dstAType.right(dstAType.length()-9);
2892 srcAType = removeRedundantWhiteSpace(srcAType);
2893 dstAType = removeRedundantWhiteSpace(dstAType);
2895 //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
2896 //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
2898 //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
2899 // dstAType.data(),dstAName.data());
2901 if (srcA->array!=dstA->array) // nomatch for char[] against char
2906 if (srcAType!=dstAType) // check if the argument only differs on name
2909 // remove a namespace scope that is only in one type
2910 // (assuming a using statement was used)
2911 //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
2912 //trimNamespaceScope(srcAType,dstAType,namespaceName);
2913 //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
2915 //QCString srcScope;
2916 //QCString dstScope;
2918 // strip redundant scope specifiers
2919 if (!className.isEmpty())
2921 srcAType=trimScope(className,srcAType);
2922 dstAType=trimScope(className,dstAType);
2923 //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
2925 if (!namespaceName.isEmpty())
2926 cd=getClass(namespaceName+"::"+className);
2928 cd=getClass(className);
2929 if (cd && cd->baseClasses())
2931 trimBaseClassScope(cd->baseClasses(),srcAType);
2932 trimBaseClassScope(cd->baseClasses(),dstAType);
2934 //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
2936 if (!namespaceName.isEmpty())
2938 srcAType=trimScope(namespaceName,srcAType);
2939 dstAType=trimScope(namespaceName,dstAType);
2941 //printf("#usingNamespace=%d\n",usingNamespaces->count());
2942 if (usingNamespaces && usingNamespaces->count()>0)
2944 NamespaceSDict::Iterator nli(*usingNamespaces);
2946 for (;(nd=nli.current());++nli)
2948 srcAType=trimScope(nd->name(),srcAType);
2949 dstAType=trimScope(nd->name(),dstAType);
2952 //printf("#usingClasses=%d\n",usingClasses->count());
2953 if (usingClasses && usingClasses->count()>0)
2955 SDict<Definition>::Iterator cli(*usingClasses);
2957 for (;(cd=cli.current());++cli)
2959 srcAType=trimScope(cd->name(),srcAType);
2960 dstAType=trimScope(cd->name(),dstAType);
2964 //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
2965 // dstAType.data(),dstAName.data());
2967 if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
2968 (srcAType+" "+srcAName)==dstAType)
2973 else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
2974 (dstAType+" "+dstAName)==srcAType)
2981 uint srcPos=0,dstPos=0;
2983 while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
2985 equal=srcAType.at(srcPos)==dstAType.at(dstPos);
2986 if (equal) srcPos++,dstPos++;
2988 uint srcATypeLen=srcAType.length();
2989 uint dstATypeLen=dstAType.length();
2990 if (srcPos<srcATypeLen && dstPos<dstATypeLen)
2992 // if nothing matches or the match ends in the middle or at the
2993 // end of a string then there is no match
2994 if (srcPos==0 || dstPos==0)
2999 if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
3001 //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
3002 // check if a name if already found -> if no then there is no match
3003 if (!srcAName.isEmpty() || !dstAName.isEmpty())
3009 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3010 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3011 if (srcPos<srcATypeLen ||
3012 dstPos<dstATypeLen ||
3013 (srcPos==srcATypeLen && dstPos==dstATypeLen)
3022 // otherwise we assume that a name starts at the current position.
3023 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3024 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3026 // if nothing more follows for both types then we assume we have
3027 // found a match. Note that now `signed int' and `signed' match, but
3028 // seeing that int is not a name can only be done by looking at the
3031 if (srcPos!=srcATypeLen || dstPos!=dstATypeLen)
3038 else if (dstPos<dstAType.length())
3040 if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
3042 if (!dstAName.isEmpty()) // dst has its name separated from its type
3047 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3048 if (dstPos!=dstAType.length())
3051 return FALSE; // more than a difference in name -> no match
3054 else // maybe dst has a name while src has not
3057 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3058 if (dstPos!=dstAType.length() || !srcAName.isEmpty())
3061 return FALSE; // nope not a name -> no match
3065 else if (srcPos<srcAType.length())
3067 if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
3069 if (!srcAName.isEmpty()) // src has its name separated from its type
3074 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3075 if (srcPos!=srcAType.length())
3078 return FALSE; // more than a difference in name -> no match
3081 else // maybe src has a name while dst has not
3084 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3085 if (srcPos!=srcAType.length() || !dstAName.isEmpty())
3088 return FALSE; // nope not a name -> no match
3099 * Matches the arguments list srcAl with the argument list dstAl
3100 * Returns TRUE if the argument lists are equal. Two argument list are
3101 * considered equal if the number of arguments is equal and the types of all
3102 * arguments are equal. Furthermore the const and volatile specifiers
3103 * stored in the list should be equal.
3105 bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
3106 const char *cl,const char *ns,bool checkCV,
3107 NamespaceSDict *usingNamespaces,
3108 SDict<Definition> *usingClasses)
3110 QCString className=cl;
3111 QCString namespaceName=ns;
3113 // strip template specialization from class name if present
3114 //int til=className.find('<'),tir=className.find('>');
3115 //if (til!=-1 && tir!=-1 && tir>til)
3117 // className=className.left(til)+className.right(className.length()-tir-1);
3120 //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
3121 // srcAl ? argListToString(srcAl).data() : "",
3122 // dstAl ? argListToString(dstAl).data() : "",
3124 // usingNamespaces?usingNamespaces->count():0,
3125 // usingClasses?usingClasses->count():0
3128 if (srcAl==0 || dstAl==0)
3130 bool match = srcAl==dstAl; // at least one of the members is not a function
3143 // handle special case with void argument
3144 if ( srcAl->count()==0 && dstAl->count()==1 &&
3145 dstAl->getFirst()->type=="void" )
3146 { // special case for finding match between func() and func(void)
3147 Argument *a=new Argument;
3153 if ( dstAl->count()==0 && srcAl->count()==1 &&
3154 srcAl->getFirst()->type=="void" )
3155 { // special case for finding match between func(void) and func()
3156 Argument *a=new Argument;
3163 if (srcAl->count() != dstAl->count())
3166 return FALSE; // different number of arguments -> no match
3171 if (srcAl->constSpecifier != dstAl->constSpecifier)
3174 return FALSE; // one member is const, the other not -> no match
3176 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3179 return FALSE; // one member is volatile, the other not -> no match
3183 // so far the argument list could match, so we need to compare the types of
3185 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3186 Argument *srcA,*dstA;
3187 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3189 if (!matchArgument(srcA,dstA,className,namespaceName,
3190 usingNamespaces,usingClasses))
3197 return TRUE; // all arguments match
3203 static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
3206 if (symbol->definitionType()==Definition::TypeMember &&
3207 ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
3211 ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
3214 return cd->qualifiedName()+templSpec;
3218 return md->qualifiedName();
3221 return symbol->qualifiedName();
3225 static QCString stripDeclKeywords(const QCString &s)
3227 int i=s.find(" class ");
3228 if (i!=-1) return s.left(i)+s.mid(i+6);
3229 i=s.find(" typename ");
3230 if (i!=-1) return s.left(i)+s.mid(i+9);
3231 i=s.find(" union ");
3232 if (i!=-1) return s.left(i)+s.mid(i+6);
3233 i=s.find(" struct ");
3234 if (i!=-1) return s.left(i)+s.mid(i+7);
3238 // forward decl for circular dependencies
3239 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
3241 QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
3244 QCString templSpec = spec.stripWhiteSpace();
3245 // this part had been commented out before... but it is needed to match for instance
3246 // std::list<std::string> against list<string> so it is now back again!
3247 if (!templSpec.isEmpty() && templSpec.at(0) == '<')
3249 templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
3251 QCString resolvedType = resolveTypeDef(d,templSpec);
3252 if (!resolvedType.isEmpty()) // not known as a typedef either
3254 templSpec = resolvedType;
3256 //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
3261 static QCString getCanonicalTypeForIdentifier(
3262 Definition *d,FileDef *fs,const QCString &word,
3263 QCString *tSpec,int count=0)
3265 if (count>10) return word; // oops recursion
3267 QCString symName,scope,result,templSpec,tmpName;
3268 //DefinitionList *defList=0;
3269 if (tSpec && !tSpec->isEmpty())
3270 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
3272 if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
3274 symName=tmpName; // name without scope
3280 //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
3281 // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
3284 MemberDef *mType = 0;
3286 QCString resolvedType;
3288 // lookup class / class template instance
3289 cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
3290 bool isTemplInst = cd && !templSpec.isEmpty();
3291 if (!cd && !templSpec.isEmpty())
3293 // class template specialization not known, look up class template
3294 cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
3296 if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
3298 //printf("cd=%p mtype=%p\n",cd,mType);
3299 //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
3302 // cd?cd->name().data():"<none>",
3303 // d?d->name().data():"<none>",
3304 // fs?fs->name().data():"<none>",
3305 // cd?cd->isTemplate():-1
3308 //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
3309 // (word+templSpec).data(),
3310 // cd?cd->qualifiedName().data():"<none>",
3311 // templSpec.data(),ts.data(),
3312 // tSpec?tSpec->data():"<null>",
3313 // cd?cd->isTemplate():FALSE,
3314 // resolvedType.data());
3316 //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
3318 if (cd) // resolves to a known class type
3320 if (cd==d && tSpec) *tSpec="";
3322 if (mType && mType->isTypedef()) // but via a typedef
3324 result = resolvedType;
3330 // spec is already part of class type
3332 if (tSpec) *tSpec="";
3334 else if (!ts.isEmpty() && templSpec.isEmpty())
3336 // use formal template args for spec
3337 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
3340 result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
3342 if (cd->isTemplate() && tSpec) //
3344 if (!templSpec.isEmpty()) // specific instance
3346 result=cd->name()+templSpec;
3348 else // use template type
3350 result=cd->qualifiedNameWithTemplateParameters();
3352 // template class, so remove the template part (it is part of the class name)
3355 else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
3357 // obscure case, where a class is used as a template, but doxygen think it is
3358 // not (could happen when loading the class from a tag file).
3363 else if (mType && mType->isEnumerate()) // an enum
3365 result = mType->qualifiedName();
3367 else if (mType && mType->isTypedef()) // a typedef
3369 //result = mType->qualifiedName(); // changed after 1.7.2
3370 //result = mType->typeString();
3371 //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
3372 if (word!=mType->typeString())
3374 result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
3378 result = mType->typeString();
3383 resolvedType = resolveTypeDef(d,word);
3384 //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
3385 if (resolvedType.isEmpty()) // not known as a typedef either
3391 result = resolvedType;
3394 //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
3398 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
3400 type = type.stripWhiteSpace();
3402 // strip const and volatile keywords that are not relevant for the type
3403 stripIrrelevantConstVolatile(type);
3405 // strip leading keywords
3406 type.stripPrefix("class ");
3407 type.stripPrefix("struct ");
3408 type.stripPrefix("union ");
3409 type.stripPrefix("enum ");
3410 type.stripPrefix("typename ");
3412 type = removeRedundantWhiteSpace(type);
3413 //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
3414 // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
3416 //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
3419 QCString templSpec,word;
3421 while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
3422 // foreach identifier in the type
3424 //printf(" i=%d p=%d\n",i,p);
3425 if (i>pp) canType += type.mid(pp,i-pp);
3428 QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
3430 // in case the ct is empty it means that "word" represents scope "d"
3431 // and this does not need to be added to the canonical
3432 // type (it is redundant), so/ we skip it. This solves problem 589616.
3433 if (ct.isEmpty() && type.mid(p,2)=="::")
3441 //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
3442 // word.data(),templSpec.data(),canType.data(),ct.data());
3443 if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
3444 // (i.e. type is not a template specialization)
3445 // then resolve any identifiers inside.
3447 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
3449 // for each identifier template specifier
3450 //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
3451 while ((ti=re.match(templSpec,tp,&tl))!=-1)
3453 canType += templSpec.mid(tp,ti-tp);
3454 canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
3457 canType+=templSpec.right(templSpec.length()-tp);
3462 canType += type.right(type.length()-pp);
3463 //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
3465 return removeRedundantWhiteSpace(canType);
3468 static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
3470 QCString type = arg->type.stripWhiteSpace();
3471 QCString name = arg->name;
3472 //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
3473 if ((type=="const" || type=="volatile") && !name.isEmpty())
3474 { // name is part of type => correct
3478 if (name=="const" || name=="volatile")
3479 { // name is part of type => correct
3480 if (!type.isEmpty()) type+=" ";
3483 if (!arg->array.isEmpty())
3488 return extractCanonicalType(d,fs,type);
3491 static bool matchArgument2(
3492 Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
3493 Definition *dstScope,FileDef *dstFileScope,Argument *dstA
3496 //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
3497 // srcScope ? srcScope->name().data() : "",
3498 // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
3499 // dstScope ? dstScope->name().data() : "",
3500 // dstA->type.data(),dstA->name.data(),dstA->canType.data());
3502 //if (srcA->array!=dstA->array) // nomatch for char[] against char
3507 QCString sSrcName = " "+srcA->name;
3508 QCString sDstName = " "+dstA->name;
3509 QCString srcType = srcA->type;
3510 QCString dstType = dstA->type;
3511 stripIrrelevantConstVolatile(srcType);
3512 stripIrrelevantConstVolatile(dstType);
3513 //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
3514 //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
3515 if (sSrcName==dstType.right(sSrcName.length()))
3516 { // case "unsigned int" <-> "unsigned int i"
3517 srcA->type+=sSrcName;
3519 srcA->canType=""; // invalidate cached type value
3521 else if (sDstName==srcType.right(sDstName.length()))
3522 { // case "unsigned int i" <-> "unsigned int"
3523 dstA->type+=sDstName;
3525 dstA->canType=""; // invalidate cached type value
3528 if (srcA->canType.isEmpty())
3530 srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
3532 if (dstA->canType.isEmpty())
3534 dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
3537 if (srcA->canType==dstA->canType)
3544 //printf(" Canonical types do not match [%s]<->[%s]\n",
3545 // srcA->canType.data(),dstA->canType.data());
3552 // new algorithm for argument matching
3553 bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
3554 Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
3558 //printf("*** matchArguments2\n");
3559 ASSERT(srcScope!=0 && dstScope!=0);
3561 if (srcAl==0 || dstAl==0)
3563 bool match = srcAl==dstAl; // at least one of the members is not a function
3576 // handle special case with void argument
3577 if ( srcAl->count()==0 && dstAl->count()==1 &&
3578 dstAl->getFirst()->type=="void" )
3579 { // special case for finding match between func() and func(void)
3580 Argument *a=new Argument;
3586 if ( dstAl->count()==0 && srcAl->count()==1 &&
3587 srcAl->getFirst()->type=="void" )
3588 { // special case for finding match between func(void) and func()
3589 Argument *a=new Argument;
3596 if (srcAl->count() != dstAl->count())
3599 return FALSE; // different number of arguments -> no match
3604 if (srcAl->constSpecifier != dstAl->constSpecifier)
3607 return FALSE; // one member is const, the other not -> no match
3609 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3612 return FALSE; // one member is volatile, the other not -> no match
3616 // so far the argument list could match, so we need to compare the types of
3618 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3619 Argument *srcA,*dstA;
3620 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3622 if (!matchArgument2(srcScope,srcFileScope,srcA,
3623 dstScope,dstFileScope,dstA)
3631 return TRUE; // all arguments match
3636 // merges the initializer of two argument lists
3637 // pre: the types of the arguments in the list should match.
3638 void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
3640 //printf("mergeArguments `%s', `%s'\n",
3641 // argListToString(srcAl).data(),argListToString(dstAl).data());
3643 if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
3645 return; // invalid argument lists -> do not merge
3648 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3649 Argument *srcA,*dstA;
3650 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3652 if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
3654 //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
3655 srcA->defval=dstA->defval.copy();
3657 else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
3659 //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
3660 dstA->defval=srcA->defval.copy();
3663 // fix wrongly detected const or volatile specifiers before merging.
3664 // example: "const A *const" is detected as type="const A *" name="const"
3665 if (srcA->name=="const" || srcA->name=="volatile")
3667 srcA->type+=" "+srcA->name;
3668 srcA->name.resize(0);
3670 if (dstA->name=="const" || dstA->name=="volatile")
3672 dstA->type+=" "+dstA->name;
3673 dstA->name.resize(0);
3676 if (srcA->type==dstA->type)
3678 //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3679 if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3681 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3682 //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
3683 srcA->type = dstA->type.copy();
3684 srcA->name = dstA->name.copy();
3686 else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
3688 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3689 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3690 dstA->type = srcA->type.copy();
3691 dstA->name = dstA->name.copy();
3693 else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
3695 //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
3696 if (forceNameOverwrite)
3698 srcA->name = dstA->name;
3702 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3704 srcA->name = dstA->name;
3706 else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
3708 dstA->name = srcA->name;
3715 //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3716 srcA->type=srcA->type.stripWhiteSpace();
3717 dstA->type=dstA->type.stripWhiteSpace();
3718 if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
3720 srcA->type+=" "+srcA->name;
3721 srcA->name=dstA->name;
3723 else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
3725 dstA->type+=" "+dstA->name;
3726 dstA->name=srcA->name;
3728 else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3730 srcA->name = dstA->name;
3732 else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
3734 dstA->name = srcA->name;
3737 int i1=srcA->type.find("::"),
3738 i2=dstA->type.find("::"),
3739 j1=srcA->type.length()-i1-2,
3740 j2=dstA->type.length()-i2-2;
3741 if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
3743 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3744 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3745 dstA->type = srcA->type.left(i1+2)+dstA->type;
3746 dstA->name = dstA->name.copy();
3748 else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
3750 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3751 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3752 srcA->type = dstA->type.left(i2+2)+srcA->type;
3753 srcA->name = dstA->name.copy();
3755 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3757 srcA->docs = dstA->docs.copy();
3759 else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
3761 dstA->docs = srcA->docs.copy();
3763 //printf("Merge argument `%s|%s' `%s|%s'\n",
3764 // srcA->type.data(),srcA->name.data(),
3765 // dstA->type.data(),dstA->name.data());
3769 static void findMembersWithSpecificName(MemberName *mn,
3772 FileDef *currentFile,
3774 const char *forceTagFile,
3775 QList<MemberDef> &members)
3777 //printf(" Function with global scope name `%s' args=`%s'\n",
3778 // mn->memberName(),args);
3779 MemberListIterator mli(*mn);
3781 for (mli.toFirst();(md=mli.current());++mli)
3783 FileDef *fd=md->getFileDef();
3784 GroupDef *gd=md->getGroupDef();
3785 //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p current=%p ref=%s\n",
3786 // md->name().data(),args,fd,gd,currentFile,md->getReference().data());
3788 ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
3789 md->getNamespaceDef()==0 && md->isLinkable() &&
3790 (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
3791 currentFile==0 || fd==currentFile) // statics must appear in the same file
3795 ArgumentList *argList=0;
3796 if (args && !md->isDefine() && strcmp(args,"()")!=0)
3798 argList=new ArgumentList;
3799 LockingPtr<ArgumentList> mdAl = md->argumentList();
3800 stringToArgumentList(args,argList);
3801 match=matchArguments2(
3802 md->getOuterScope(),fd,mdAl.pointer(),
3803 Doxygen::globalScope,fd,argList,
3805 delete argList; argList=0;
3807 if (match && (forceTagFile==0 || md->getReference()==forceTagFile))
3809 //printf("Found match!\n");
3817 * Searches for a member definition given its name `memberName' as a string.
3818 * memberName may also include a (partial) scope to indicate the scope
3819 * in which the member is located.
3821 * The parameter `scName' is a string representing the name of the scope in
3822 * which the link was found.
3824 * In case of a function args contains a string representation of the
3825 * argument list. Passing 0 means the member has no arguments.
3826 * Passing "()" means any argument list will do, but "()" is preferred.
3828 * The function returns TRUE if the member is known and documented or
3829 * FALSE if it is not.
3830 * If TRUE is returned parameter `md' contains a pointer to the member
3831 * definition. Furthermore exactly one of the parameter `cd', `nd', or `fd'
3833 * - if `cd' is non zero, the member was found in a class pointed to by cd.
3834 * - if `nd' is non zero, the member was found in a namespace pointed to by nd.
3835 * - if `fd' is non zero, the member was found in the global namespace of
3838 bool getDefs(const QCString &scName,
3839 const QCString &memberName,
3846 bool forceEmptyScope,
3847 FileDef *currentFile,
3849 const char *forceTagFile
3852 fd=0, md=0, cd=0, nd=0, gd=0;
3853 if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
3855 QCString scopeName=scName;
3856 scopeName = substitute(scopeName,"\\","::"); // for PHP
3857 //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
3858 // memberName.data(),args,scopeName.data(),forceEmptyScope);
3861 // strip common part of the scope from the scopeName
3862 while ((is=scopeName.findRev("::"))!=-1 &&
3863 (im=memberName.find("::",pm))!=-1 &&
3864 (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
3867 scopeName=scopeName.left(is);
3870 //printf("result after scope corrections scope=%s name=%s\n",
3871 // scopeName.data(),memberName.data());
3873 QCString mName=memberName;
3875 if (memberName.left(9)!="operator " && // treat operator conversion methods
3876 // as a special case
3877 (im=memberName.findRev("::"))!=-1 &&
3878 im<(int)memberName.length()-2 // not A::
3881 mScope=memberName.left(im);
3882 mName=memberName.right(memberName.length()-im-2);
3885 // handle special the case where both scope name and member scope are equal
3886 if (mScope==scopeName) scopeName.resize(0);
3888 //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
3890 MemberName *mn = Doxygen::memberNameSDict->find(mName);
3891 //printf("mName=%s mn=%p\n",mName.data(),mn);
3893 if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
3894 mn && !(scopeName.isEmpty() && mScope.isEmpty()))
3896 //printf(" >member name '%s' found\n",mName.data());
3897 int scopeOffset=scopeName.length();
3900 QCString className = scopeName.left(scopeOffset);
3901 if (!className.isEmpty() && !mScope.isEmpty())
3903 className+="::"+mScope;
3905 else if (!mScope.isEmpty())
3910 ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className);
3911 //printf("Trying class scope %s: %p\n",className.data(),fcd);
3912 // todo: fill in correct fileScope!
3913 if (fcd && // is it a documented class
3917 //printf(" Found fcd=%p\n",fcd);
3918 MemberListIterator mmli(*mn);
3920 int mdist=maxInheritanceDepth;
3921 ArgumentList *argList=0;
3924 argList=new ArgumentList;
3925 stringToArgumentList(args,argList);
3927 for (mmli.toFirst();(mmd=mmli.current());++mmli)
3929 //if (mmd->isLinkable())
3931 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
3932 bool match=args==0 ||
3933 matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
3934 fcd,fcd->getFileDef(),argList,
3937 //printf("match=%d\n",match);
3940 ClassDef *mcd=mmd->getClassDef();
3943 int m=minClassDistance(fcd,mcd);
3944 if (m<mdist && mcd->isLinkable())
3956 delete argList; argList=0;
3958 if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0)
3959 // no exact match found, but if args="()" an arbitrary member will do
3961 //printf(" >Searching for arbitrary member\n");
3962 for (mmli.toFirst();(mmd=mmli.current());++mmli)
3964 //if (mmd->isLinkable())
3966 ClassDef *mcd=mmd->getClassDef();
3967 //printf(" >Class %s found\n",mcd->name().data());
3970 int m=minClassDistance(fcd,mcd);
3971 if (m<mdist /* && mcd->isLinkable()*/ )
3973 //printf("Class distance %d\n",m);
3982 //printf(" >Succes=%d\n",mdist<maxInheritanceDepth);
3983 if (mdist<maxInheritanceDepth)
3985 if (!md->isLinkable())
3987 md=0; // avoid returning things we cannot link to
3989 return FALSE; // match found, but was not linkable
3993 gd=md->getGroupDef();
3995 return TRUE; /* found match */
3999 /* go to the parent scope */
4004 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4008 } while (scopeOffset>=0);
4011 if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
4013 MemberListIterator mmli(*mn);
4014 MemberDef *mmd, *fuzzy_mmd = 0;
4015 ArgumentList *argList = 0;
4016 bool hasEmptyArgs = args && strcmp(args, "()") == 0;
4019 stringToArgumentList(args, argList = new ArgumentList);
4021 for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
4023 if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
4024 !mmd->getClassDef())
4029 QCString className = mmd->getClassDef()->name();
4031 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
4032 if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
4033 Doxygen::globalScope,mmd->getFileDef(),argList,
4038 if (!fuzzy_mmd && hasEmptyArgs)
4042 if (argList) delete argList, argList = 0;
4044 mmd = mmd ? mmd : fuzzy_mmd;
4049 cd = mmd->getClassDef();
4055 // maybe an namespace, file or group member ?
4056 //printf("Testing for global symbol scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
4057 // scopeName.data(),mScope.data(),mName.data());
4058 if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
4060 //printf(" >symbol name found\n");
4061 NamespaceDef *fnd=0;
4062 int scopeOffset=scopeName.length();
4065 QCString namespaceName = scopeName.left(scopeOffset);
4066 if (!namespaceName.isEmpty() && !mScope.isEmpty())
4068 namespaceName+="::"+mScope;
4070 else if (!mScope.isEmpty())
4072 namespaceName=mScope.copy();
4074 //printf("Trying namespace %s\n",namespaceName.data());
4075 if (!namespaceName.isEmpty() &&
4076 (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
4080 //printf("Function inside existing namespace `%s'\n",namespaceName.data());
4082 MemberListIterator mmli(*mn);
4084 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4086 //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
4087 // mmd->getNamespaceDef(),fnd);
4088 if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
4089 { // namespace is found
4091 ArgumentList *argList=0;
4092 if (args && strcmp(args,"()")!=0)
4094 argList=new ArgumentList;
4095 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
4096 stringToArgumentList(args,argList);
4097 match=matchArguments2(
4098 mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
4099 fnd,mmd->getFileDef(),argList,
4110 delete argList; argList=0;
4114 if (!found && args && !strcmp(args,"()"))
4115 // no exact match found, but if args="()" an arbitrary
4118 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4120 if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
4130 if (!md->isLinkable())
4132 md=0; // avoid returning things we cannot link to
4134 return FALSE; // match found but not linkable
4138 gd=md->getGroupDef();
4139 if (gd && gd->isLinkable()) nd=0; else gd=0;
4148 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4152 } while (scopeOffset>=0);
4154 //else // no scope => global function
4156 QList<MemberDef> members;
4157 // search for matches with strict static checking
4158 findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members);
4159 if (members.count()==0) // nothing found
4161 // search again without strict static checking
4162 findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members);
4164 //printf("found %d members\n",members.count());
4165 if (members.count()!=1 && args && !strcmp(args,"()"))
4167 // no exact match found, but if args="()" an arbitrary
4170 while (md /* && md->isLinkable()*/)
4172 //printf("Found member `%s'\n",md->name().data());
4173 //printf("member is linkable md->name()=`%s'\n",md->name().data());
4174 fd=md->getFileDef();
4175 gd=md->getGroupDef();
4177 (gd && gd->isLinkable()) || (fd && fd->isLinkable())
4185 //printf("found %d candidate members\n",members.count());
4186 if (members.count()>0) // at least one match
4190 if (md) // found a matching global member
4192 fd=md->getFileDef();
4193 gd=md->getGroupDef();
4194 //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
4195 if (gd && gd->isLinkable()) fd=0; else gd=0;
4206 * Searches for a scope definition given its name as a string via parameter
4209 * The parameter `docScope` is a string representing the name of the scope in
4210 * which the `scope` string was found.
4212 * The function returns TRUE if the scope is known and documented or
4213 * FALSE if it is not.
4214 * If TRUE is returned exactly one of the parameter `cd`, `nd`
4216 * - if `cd` is non zero, the scope was a class pointed to by cd.
4217 * - if `nd` is non zero, the scope was a namespace pointed to by nd.
4219 static bool getScopeDefs(const char *docScope,const char *scope,
4220 ClassDef *&cd, NamespaceDef *&nd)
4224 QCString scopeName=scope;
4225 //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
4226 if (scopeName.isEmpty()) return FALSE;
4228 bool explicitGlobalScope=FALSE;
4229 if (scopeName.at(0)==':' && scopeName.at(1)==':')
4231 scopeName=scopeName.right(scopeName.length()-2);
4232 explicitGlobalScope=TRUE;
4235 QCString docScopeName=docScope;
4236 int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
4238 do // for each possible docScope (from largest to and including empty)
4240 QCString fullName=scopeName.copy();
4241 if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
4243 if (((cd=getClass(fullName)) || // normal class
4244 (cd=getClass(fullName+"-p")) //|| // ObjC protocol
4245 //(cd=getClass(fullName+"-g")) // C# generic
4246 ) && cd->isLinkable())
4248 return TRUE; // class link written => quit
4250 else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
4252 return TRUE; // namespace link written => quit
4258 else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
4262 } while (scopeOffset>=0);
4267 static bool isLowerCase(QCString &s)
4269 uchar *p=(uchar*)s.data();
4270 if (p==0) return TRUE;
4272 while ((c=*p++)) if (!islower(c)) return FALSE;
4276 /*! Returns an object to reference to given its name and context
4277 * @post return value TRUE implies *resContext!=0 or *resMember!=0
4279 bool resolveRef(/* in */ const char *scName,
4280 /* in */ const char *name,
4281 /* in */ bool inSeeBlock,
4282 /* out */ Definition **resContext,
4283 /* out */ MemberDef **resMember,
4284 bool lookForSpecialization,
4285 FileDef *currentFile,
4289 QCString tsName = name;
4290 //bool memberScopeFirst = tsName.find('#')!=-1;
4291 QCString fullName = substitute(tsName,"#","::");
4292 fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
4294 int bracePos=fullName.findRev('('); // reverse is needed for operator()(...)
4295 int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
4296 int scopePos=fullName.findRev("::",endNamePos);
4297 bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
4298 (scopePos>2 || // ::N::A
4299 tsName.left(2)=="::" || // ::foo in local scope
4300 scName==0 // #foo in global scope
4303 // default result values
4307 if (bracePos==-1) // simple name
4312 // the following if() was commented out for releases in the range
4313 // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
4314 if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
4315 { // link to lower case only name => do not try to autolink
4319 //printf("scName=%s fullName=%s\n",scName,fullName.data());
4321 // check if this is a class or namespace reference
4322 if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
4324 if (cd) // scope matches that of a class
4328 else // scope matches that of a namespace
4335 else if (scName==fullName || (!inSeeBlock && scopePos==-1))
4336 // nothing to link => output plain text
4338 //printf("found scName=%s fullName=%s scName==fullName=%d "
4339 // "inSeeBlock=%d scopePos=%d!\n",
4340 // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
4343 // continue search...
4346 // extract userscope+name
4347 QCString nameStr=fullName.left(endNamePos);
4348 if (explicitScope) nameStr=nameStr.mid(2);
4350 // extract arguments
4352 if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
4354 // strip template specifier
4355 // TODO: match against the correct partial template instantiation
4356 int templPos=nameStr.find('<');
4357 bool tryUnspecializedVersion = FALSE;
4358 if (templPos!=-1 && nameStr.find("operator")==-1)
4360 int endTemplPos=nameStr.findRev('>');
4361 if (endTemplPos!=-1)
4363 if (!lookForSpecialization)
4365 nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
4369 tryUnspecializedVersion = TRUE;
4374 QCString scopeStr=scName;
4379 NamespaceDef *nd = 0;
4382 // check if nameStr is a member or global.
4383 //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
4384 // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
4385 if (getDefs(scopeStr,nameStr,argsStr,
4387 //scopePos==0 && !memberScopeFirst, // forceEmptyScope
4388 explicitScope, // replaces prev line due to bug 600829
4394 //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
4395 if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
4396 (!scopeStr.isEmpty() || nameStr.find("::")>0))
4398 // we did find a member, but it is a global one while we were explicitly
4399 // looking for a scoped variable. See bug 616387 for an example why this check is needed.
4400 // note we do need to support autolinking to "::symbol" hence the >0
4401 //printf("not global member!\n");
4406 //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
4407 if (md) { *resMember=md; *resContext=md; }
4408 else if (cd) *resContext=cd;
4409 else if (nd) *resContext=nd;
4410 else if (fd) *resContext=fd;
4411 else if (gd) *resContext=gd;
4412 else { *resContext=0; *resMember=0; return FALSE; }
4413 //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
4414 // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
4417 else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
4422 else if (tsName.find('.')!=-1) // maybe a link to a file
4425 fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
4433 if (tryUnspecializedVersion)
4435 return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
4437 //printf("resolveRef: %s not found!\n",name);
4442 QCString linkToText(SrcLangExt lang,const char *link,bool isFileName)
4444 //static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
4445 QCString result=link;
4446 if (!result.isEmpty())
4449 result=substitute(result,"#","::");
4451 if (!isFileName) result=substitute(result,".","::");
4452 // strip leading :: prefix if present
4453 if (result.at(0)==':' && result.at(1)==':')
4455 result=result.right(result.length()-2);
4457 QCString sep = getLanguageSpecificSeparator(lang);
4460 result=substitute(result,"::",sep);
4468 * generate a reference to a class, namespace or member.
4469 * `scName' is the name of the scope that contains the documentation
4470 * string that is returned.
4471 * `name' is the name that we want to link to.
4472 * `name' may have five formats:
4474 * 2) "memberName()" one of the (overloaded) function or define
4475 * with name memberName.
4476 * 3) "memberName(...)" a specific (overloaded) function or define
4477 * with name memberName
4478 * 4) "::name a global variable or define
4479 * 4) "\#memberName member variable, global variable or define
4480 * 5) ("ScopeName::")+"memberName()"
4481 * 6) ("ScopeName::")+"memberName(...)"
4482 * 7) ("ScopeName::")+"memberName"
4483 * instead of :: the \# symbol may also be used.
4486 bool generateRef(OutputDocInterface &od,const char *scName,
4487 const char *name,bool inSeeBlock,const char *rt)
4489 //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
4491 Definition *compound;
4494 // create default link text
4495 QCString linkText = linkToText(rt,FALSE);
4497 if (resolveRef(scName,name,inSeeBlock,&compound,&md))
4499 if (md && md->isLinkable()) // link to member
4501 od.writeObjectLink(md->getReference(),
4502 md->getOutputFileBase(),
4503 md->anchor(),linkText);
4504 // generate the page reference (for LaTeX)
4505 if (!md->isReference())
4507 writePageRef(od,md->getOutputFileBase(),md->anchor());
4511 else if (compound && compound->isLinkable()) // link to compound
4513 if (rt==0 && compound->definitionType()==Definition::TypeGroup)
4515 linkText=((GroupDef *)compound)->groupTitle();
4517 if (compound && compound->definitionType()==Definition::TypeFile)
4519 linkText=linkToText(rt,TRUE);
4521 od.writeObjectLink(compound->getReference(),
4522 compound->getOutputFileBase(),
4524 if (!compound->isReference())
4526 writePageRef(od,compound->getOutputFileBase(),0);
4531 od.docify(linkText);
4536 bool resolveLink(/* in */ const char *scName,
4537 /* in */ const char *lr,
4538 /* in */ bool /*inSeeBlock*/,
4539 /* out */ Definition **resContext,
4540 /* out */ QCString &resAnchor
4545 QCString linkRef=lr;
4546 //printf("ResolveLink linkRef=%s inSee=%d\n",lr,inSeeBlock);
4554 if (linkRef.isEmpty()) // no reference name!
4558 else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
4560 GroupDef *gd = pd->getGroupDef();
4564 if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()];
4566 if (si) resAnchor = si->label;
4574 else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
4579 else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
4584 else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
4585 && fd->isLinkable())
4590 else if ((cd=getClass(linkRef))) // class link
4593 resAnchor=cd->anchor();
4596 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
4599 resAnchor=cd->anchor();
4602 // else if ((cd=getClass(linkRef+"-g"))) // C# generic link
4605 // resAnchor=cd->anchor();
4608 else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
4613 else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/"))
4614 && dir->isLinkable()) // TODO: make this location independent like filedefs
4619 else // probably a member reference
4622 bool res = resolveRef(scName,lr,TRUE,resContext,&md);
4623 if (md) resAnchor=md->anchor();
4629 //----------------------------------------------------------------------
4630 // General function that generates the HTML code for a reference to some
4631 // file, class or member from text `lr' within the context of class `clName'.
4632 // This link has the text 'lt' (if not 0), otherwise `lr' is used as a
4633 // basis for the link's text.
4634 // returns TRUE if a link could be generated.
4636 bool generateLink(OutputDocInterface &od,const char *clName,
4637 const char *lr,bool inSeeBlock,const char *lt)
4639 //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
4640 Definition *compound;
4641 //PageDef *pageDef=0;
4642 QCString anchor,linkText=linkToText(SrcLangExt_Unknown,lt,FALSE);
4643 //printf("generateLink linkText=%s\n",linkText.data());
4644 if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
4646 if (compound) // link to compound
4648 if (lt==0 && anchor.isEmpty() && /* compound link */
4649 compound->definitionType()==Definition::TypeGroup /* is group */
4652 linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
4654 else if (compound->definitionType()==Definition::TypeFile)
4656 linkText=linkToText(compound->getLanguage(),lt,TRUE);
4658 od.writeObjectLink(compound->getReference(),
4659 compound->getOutputFileBase(),anchor,linkText);
4660 if (!compound->isReference())
4662 writePageRef(od,compound->getOutputFileBase(),anchor);
4667 err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
4671 else // link could not be found
4673 od.docify(linkText);
4678 void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
4680 //printf("generateFileRef(%s,%s)\n",name,text);
4681 QCString linkText = text ? text : name;
4685 if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) &&
4687 // link to documented input file
4688 od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
4690 od.docify(linkText);
4693 //----------------------------------------------------------------------
4696 QCString substituteClassNames(const QCString &s)
4700 if (s.isEmpty()) return result;
4701 QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
4702 while ((p=r.match(s,i,&l))!=-1)
4705 if (p>i) result+=s.mid(i,p-i);
4706 if ((subst=substituteDict[s.mid(p,l)]))
4716 result+=s.mid(i,s.length()-i);
4721 //----------------------------------------------------------------------
4723 /** Cache element for the file name to FileDef mapping cache. */
4724 struct FindFileCacheElem
4726 FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
4731 static QCache<FindFileCacheElem> g_findFileDefCache(5000);
4733 FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
4739 key.sprintf("%p:",fnDict);
4742 g_findFileDefCache.setAutoDelete(TRUE);
4743 FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
4744 //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
4747 ambig = cachedResult->isAmbig;
4748 //printf("cached: fileDef=%p\n",cachedResult->fileDef);
4749 return cachedResult->fileDef;
4753 cachedResult = new FindFileCacheElem(0,FALSE);
4756 QCString name=convertToQCString(QDir::cleanDirPath(n));
4760 if (name.isEmpty()) goto exit;
4761 slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4764 path=name.left(slashPos+1);
4765 name=name.right(name.length()-slashPos-1);
4766 //printf("path=%s name=%s\n",path.data(),name.data());
4768 if (name.isEmpty()) goto exit;
4769 if ((fn=(*fnDict)[name]))
4771 //printf("fn->count()=%d\n",fn->count());
4774 FileDef *fd = fn->getFirst();
4775 #if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
4776 bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
4778 bool isSamePath = fd->getPath().right(path.length())==path;
4780 if (path.isEmpty() || isSamePath)
4782 cachedResult->fileDef = fd;
4783 g_findFileDefCache.insert(key,cachedResult);
4784 //printf("=1 ===> add to cache %p\n",fd);
4788 else // file name alone is ambiguous
4791 FileNameIterator fni(*fn);
4793 FileDef *lastMatch=0;
4794 QCString pathStripped = stripFromIncludePath(path);
4795 for (fni.toFirst();(fd=fni.current());++fni)
4797 QCString fdStripPath = stripFromIncludePath(fd->getPath());
4798 if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
4804 //printf(">1 ===> add to cache %p\n",fd);
4807 cachedResult->isAmbig = ambig;
4808 cachedResult->fileDef = lastMatch;
4809 g_findFileDefCache.insert(key,cachedResult);
4815 //printf("not found!\n");
4818 //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
4819 g_findFileDefCache.insert(key,cachedResult);
4820 //delete cachedResult;
4824 //----------------------------------------------------------------------
4826 QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
4831 int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4834 path=name.left(slashPos+1);
4835 name=name.right(name.length()-slashPos-1);
4838 if ((fn=(*fnDict)[name]))
4840 FileNameIterator fni(*fn);
4842 for (fni.toFirst();(fd=fni.current());++fni)
4844 if (path.isEmpty() || fd->getPath().right(path.length())==path)
4846 result+=" "+fd->absFilePath()+"\n";
4853 //----------------------------------------------------------------------
4855 QCString substituteKeywords(const QCString &s,const char *title,
4856 const char *projName,const char *projNum,const char *projBrief)
4858 QCString result = s;
4859 if (title) result = substitute(result,"$title",title);
4860 result = substitute(result,"$datetime",dateToString(TRUE));
4861 result = substitute(result,"$date",dateToString(FALSE));
4862 result = substitute(result,"$year",yearToString());
4863 result = substitute(result,"$doxygenversion",versionString);
4864 result = substitute(result,"$projectname",projName);
4865 result = substitute(result,"$projectnumber",projNum);
4866 result = substitute(result,"$projectbrief",projBrief);
4867 result = substitute(result,"$projectlogo",stripPath(Config_getString("PROJECT_LOGO")));
4871 //----------------------------------------------------------------------
4873 /*! Returns the character index within \a name of the first prefix
4874 * in Config_getList("IGNORE_PREFIX") that matches \a name at the left hand side,
4875 * or zero if no match was found
4877 int getPrefixIndex(const QCString &name)
4879 if (name.isEmpty()) return 0;
4880 static QStrList &sl = Config_getList("IGNORE_PREFIX");
4881 char *s = sl.first();
4885 const char *pd=name.data();
4887 while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
4888 if (*ps==0 && *pd!=0)
4897 //----------------------------------------------------------------------------
4899 static void initBaseClassHierarchy(BaseClassList *bcl)
4902 BaseClassListIterator bcli(*bcl);
4903 for ( ; bcli.current(); ++bcli)
4905 ClassDef *cd=bcli.current()->classDef;
4906 if (cd->baseClasses()==0) // no base classes => new root
4908 initBaseClassHierarchy(cd->baseClasses());
4914 //----------------------------------------------------------------------------
4916 void initClassHierarchy(ClassSDict *cl)
4918 ClassSDict::Iterator cli(*cl);
4920 for ( ; (cd=cli.current()); ++cli)
4923 initBaseClassHierarchy(cd->baseClasses());
4927 //----------------------------------------------------------------------------
4929 bool hasVisibleRoot(BaseClassList *bcl)
4933 BaseClassListIterator bcli(*bcl);
4934 for ( ; bcli.current(); ++bcli)
4936 ClassDef *cd=bcli.current()->classDef;
4937 if (cd->isVisibleInHierarchy()) return TRUE;
4938 hasVisibleRoot(cd->baseClasses());
4944 //----------------------------------------------------------------------
4946 // note that this function is not reentrant due to the use of static growBuf!
4947 QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
4949 static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES");
4950 static GrowBuf growBuf;
4958 case '_': if (allowUnderscore) growBuf.addChar('_'); else growBuf.addStr("__"); break;
4959 case '-': growBuf.addChar('-'); break;
4960 case ':': growBuf.addStr("_1"); break;
4961 case '/': growBuf.addStr("_2"); break;
4962 case '<': growBuf.addStr("_3"); break;
4963 case '>': growBuf.addStr("_4"); break;
4964 case '*': growBuf.addStr("_5"); break;
4965 case '&': growBuf.addStr("_6"); break;
4966 case '|': growBuf.addStr("_7"); break;
4967 case '.': if (allowDots) growBuf.addChar('.'); else growBuf.addStr("_8"); break;
4968 case '!': growBuf.addStr("_9"); break;
4969 case ',': growBuf.addStr("_00"); break;
4970 case ' ': growBuf.addStr("_01"); break;
4971 case '{': growBuf.addStr("_02"); break;
4972 case '}': growBuf.addStr("_03"); break;
4973 case '?': growBuf.addStr("_04"); break;
4974 case '^': growBuf.addStr("_05"); break;
4975 case '%': growBuf.addStr("_06"); break;
4976 case '(': growBuf.addStr("_07"); break;
4977 case ')': growBuf.addStr("_08"); break;
4978 case '+': growBuf.addStr("_09"); break;
4979 case '=': growBuf.addStr("_0A"); break;
4980 case '$': growBuf.addStr("_0B"); break;
4981 case '\\': growBuf.addStr("_0C"); break;
4985 static char map[] = "0123456789ABCDEF";
4987 unsigned char id = (unsigned char)c;
4993 growBuf.addStr(ids);
4995 else if (caseSenseNames || !isupper(c))
5001 growBuf.addChar('_');
5002 growBuf.addChar(tolower(c));
5008 return growBuf.get();
5011 /*! This function determines the file name on disk of an item
5012 * given its name, which could be a class name with template
5013 * arguments, so special characters need to be escaped.
5015 QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
5017 static bool shortNames = Config_getBool("SHORT_NAMES");
5018 static bool createSubdirs = Config_getBool("CREATE_SUBDIRS");
5020 if (shortNames) // use short names only
5022 static QDict<int> usedNames(10007);
5023 usedNames.setAutoDelete(TRUE);
5024 static long int count=1;
5026 int *value=usedNames.find(name);
5030 usedNames.insert(name,new int(count));
5037 result.sprintf("a%05ld",num);
5041 result=escapeCharsInString(name,allowDots,allowUnderscore);
5042 int resultLen = result.length();
5043 if (resultLen>=128) // prevent names that cannot be created!
5045 // third algorithm based on MD5 hash
5047 QCString sigStr(33);
5048 MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
5049 MD5SigToString(md5_sig,sigStr.data(),33);
5050 result=result.left(128-32)+sigStr;
5055 int l1Dir=0,l2Dir=0;
5057 #if MAP_ALGO==ALGO_COUNT
5058 // old algorithm, has the problem that after regeneration the
5059 // output can be located in a different dir.
5060 if (Doxygen::htmlDirMap==0)
5062 Doxygen::htmlDirMap=new QDict<int>(100003);
5063 Doxygen::htmlDirMap->setAutoDelete(TRUE);
5065 static int curDirNum=0;
5066 int *dirNum = Doxygen::htmlDirMap->find(result);
5067 if (dirNum==0) // new name
5069 Doxygen::htmlDirMap->insert(result,new int(curDirNum));
5070 l1Dir = (curDirNum)&0xf; // bits 0-3
5071 l2Dir = (curDirNum>>4)&0xff; // bits 4-11
5074 else // existing name
5076 l1Dir = (*dirNum)&0xf; // bits 0-3
5077 l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
5079 #elif MAP_ALGO==ALGO_CRC16
5080 // second algorithm based on CRC-16 checksum
5081 int dirNum = qChecksum(result,result.length());
5083 l2Dir = (dirNum>>4)&0xff;
5084 #elif MAP_ALGO==ALGO_MD5
5085 // third algorithm based on MD5 hash
5087 MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
5088 l1Dir = md5_sig[14]&0xf;
5089 l2Dir = md5_sig[15];
5091 result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
5093 //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
5097 QCString relativePathToRoot(const char *name)
5100 if (Config_getBool("CREATE_SUBDIRS"))
5104 return REL_PATH_TO_ROOT;
5109 int i = n.findRev('/');
5112 result=REL_PATH_TO_ROOT;
5119 void createSubDirs(QDir &d)
5121 if (Config_getBool("CREATE_SUBDIRS"))
5123 // create 4096 subdirectories
5125 for (l1=0;l1<16;l1++)
5127 d.mkdir(QString().sprintf("d%x",l1));
5128 for (l2=0;l2<256;l2++)
5130 d.mkdir(QString().sprintf("d%x/d%02x",l1,l2));
5136 /*! Input is a scopeName, output is the scopename split into a
5137 * namespace part (as large as possible) and a classname part.
5139 void extractNamespaceName(const QCString &scopeName,
5140 QCString &className,QCString &namespaceName,
5141 bool allowEmptyClass)
5144 QCString clName=scopeName;
5145 NamespaceDef *nd = 0;
5146 if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
5147 { // the whole name is a namespace (and not a class)
5148 namespaceName=nd->name().copy();
5149 className.resize(0);
5152 p=clName.length()-2;
5153 while (p>=0 && (i=clName.findRev("::",p))!=-1)
5154 // see if the first part is a namespace (and not a class)
5156 //printf("Trying %s\n",clName.left(i).data());
5157 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
5159 //printf("found!\n");
5160 namespaceName=nd->name().copy();
5161 className=clName.right(clName.length()-i-2);
5164 p=i-2; // try a smaller piece of the scope
5166 //printf("not found!\n");
5168 // not found, so we just have to guess.
5169 className=scopeName.copy();
5170 namespaceName.resize(0);
5173 if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
5175 // class and namespace with the same name, correct to return the class.
5176 className=namespaceName.copy();
5177 namespaceName.resize(0);
5179 //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
5180 // className.data(),namespaceName.data());
5181 if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p")
5183 className = className.left(className.length()-2);
5188 QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
5190 QCString result=scope.copy();
5191 if (!templ.isEmpty() && scope.find('<')==-1)
5196 (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
5197 ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0)
5200 //printf("Tried `%s'\n",(scope.left(si)+templ).data());
5203 if (si==-1) // not nested => append template specifier
5207 else // nested => insert template specifier before after first class name
5209 result=scope.left(si) + templ + scope.right(scope.length()-si);
5212 //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
5213 // scope.data(),templ.data(),result.data());
5217 #if 0 // original version
5218 /*! Strips the scope from a name. Examples: A::B will return A
5219 * and A<T>::B<N::C<D> > will return A<T>.
5221 QCString stripScope(const char *name)
5223 QCString result = name;
5224 int l=result.length();
5231 char c=result.at(p);
5235 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5236 return result.right(l-p-1);
5240 //printf("pos < = %d\n",p);
5242 while (p>=0 && !done)
5247 case '>': count++; break;
5248 case '<': count--; if (count<=0) done=TRUE; break;
5250 //printf("c=%c count=%d\n",c,count);
5254 //printf("pos > = %d\n",p+1);
5260 //printf("stripScope(%s)=%s\n",name,name);
5265 // new version by Davide Cesari which also works for Fortran
5266 QCString stripScope(const char *name)
5268 QCString result = name;
5269 int l=result.length();
5272 bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
5277 p=l-1; // start at the end of the string
5278 while (p>=0 && count>=0)
5280 char c=result.at(p);
5284 // only exit in the case of ::
5285 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5286 if (p>0 && result.at(p-1)==':') return result.right(l-p-1);
5290 if (skipBracket) // we don't care about brackets
5294 else // count open/close brackets
5296 if (p>0 && result.at(p-1)=='>') // skip >> operator
5302 //printf("pos < = %d\n",p);
5304 bool foundMatch=false;
5305 while (p>=0 && !foundMatch)
5316 if (result.at(p-1) == '<') // skip << operator
5323 foundMatch = count==0;
5326 //printf("c=%c count=%d\n",c,count);
5331 //printf("pos > = %d\n",p+1);
5337 done = count==0 || skipBracket; // reparse if brackets do not match
5340 while (!done); // if < > unbalanced repeat ignoring them
5341 //printf("stripScope(%s)=%s\n",name,name);
5346 /*! Converts a string to an XML-encoded string */
5347 QCString convertToXML(const char *s)
5349 static GrowBuf growBuf;
5351 if (s==0) return "";
5358 case '<': growBuf.addStr("<"); break;
5359 case '>': growBuf.addStr(">"); break;
5360 case '&': growBuf.addStr("&"); break;
5361 case '\'': growBuf.addStr("'"); break;
5362 case '"': growBuf.addStr("""); break;
5363 default: growBuf.addChar(c); break;
5367 return growBuf.get();
5370 /*! Converts a string to a HTML-encoded string */
5371 QCString convertToHtml(const char *s,bool keepEntities)
5373 static GrowBuf growBuf;
5375 if (s==0) return "";
5382 case '<': growBuf.addStr("<"); break;
5383 case '>': growBuf.addStr(">"); break;
5384 case '&': if (keepEntities)
5390 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
5392 if (ce==';') // found end of an entity
5394 // copy entry verbatim
5396 while (p<e) growBuf.addChar(*p++);
5400 growBuf.addStr("&");
5405 growBuf.addStr("&");
5408 case '\'': growBuf.addStr("'"); break;
5409 case '"': growBuf.addStr("""); break;
5410 default: growBuf.addChar(c); break;
5414 return growBuf.get();
5417 QCString convertToJSString(const char *s)
5419 static GrowBuf growBuf;
5421 if (s==0) return "";
5428 case '"': growBuf.addStr("\\\""); break;
5429 case '\\': growBuf.addStr("\\\\"); break;
5430 default: growBuf.addChar(c); break;
5434 return growBuf.get();
5438 QCString convertCharEntitiesToUTF8(const QCString &s)
5440 static QDict<char> entityMap(127);
5441 static bool init=TRUE;
5443 static QRegExp entityPat("&[a-zA-Z]+;");
5447 entityMap.insert("copy", "\xC2\xA9");
5448 entityMap.insert("tm", "\xE2\x84\xA2");
5449 entityMap.insert("trade", "\xE2\x84\xA2");
5450 entityMap.insert("reg", "\xC2\xAE");
5451 entityMap.insert("lsquo", "\xE2\x80\x98");
5452 entityMap.insert("rsquo", "\xE2\x80\x99");
5453 entityMap.insert("ldquo", "\xE2\x80\x9C");
5454 entityMap.insert("rdquo", "\xE2\x80\x9D");
5455 entityMap.insert("ndash", "\xE2\x80\x93");
5456 entityMap.insert("mdash", "\xE2\x80\x94");
5457 entityMap.insert("Auml", "\xC3\x84");
5458 entityMap.insert("Euml", "\xC3\x8B");
5459 entityMap.insert("Iuml", "\xC3\x8F");
5460 entityMap.insert("Ouml", "\xC3\x96");
5461 entityMap.insert("Uuml", "\xC3\x9C");
5462 entityMap.insert("Yuml", "\xC5\xB8");
5463 entityMap.insert("auml", "\xC3\xA4");
5464 entityMap.insert("euml", "\xC3\xAB");
5465 entityMap.insert("iuml", "\xC3\xAF");
5466 entityMap.insert("ouml", "\xC3\xB6");
5467 entityMap.insert("uuml", "\xC3\xBC");
5468 entityMap.insert("yuml", "\xC3\xBF");
5469 entityMap.insert("Aacute", "\xC3\x81");
5470 entityMap.insert("Eacute", "\xC3\x89");
5471 entityMap.insert("Iacute", "\xC3\x8D");
5472 entityMap.insert("Oacute", "\xC3\x93");
5473 entityMap.insert("Uacute", "\xC3\x9A");
5474 entityMap.insert("aacute", "\xC3\xA1");
5475 entityMap.insert("eacute", "\xC3\xA9");
5476 entityMap.insert("iacute", "\xC3\xAD");
5477 entityMap.insert("oacute", "\xC3\xB3");
5478 entityMap.insert("uacute", "\xC3\xBA");
5479 entityMap.insert("Agrave", "\xC3\x80");
5480 entityMap.insert("Egrave", "\xC3\x88");
5481 entityMap.insert("Igrave", "\xC3\x8C");
5482 entityMap.insert("Ograve", "\xC3\x92");
5483 entityMap.insert("Ugrave", "\xC3\x99");
5484 entityMap.insert("agrave", "\xC3\xA0");
5485 entityMap.insert("egrave", "\xC3\xA8");
5486 entityMap.insert("igrave", "\xC3\xAC");
5487 entityMap.insert("ograve", "\xC3\xB2");
5488 entityMap.insert("ugrave", "\xC3\xB9");
5489 entityMap.insert("Acirc", "\xC3\x82");
5490 entityMap.insert("Ecirc", "\xC3\x8A");
5491 entityMap.insert("Icirc", "\xC3\x8E");
5492 entityMap.insert("Ocirc", "\xC3\x94");
5493 entityMap.insert("Ucirc", "\xC3\x9B");
5494 entityMap.insert("acirc", "\xC3\xA2");
5495 entityMap.insert("ecirc", "\xC3\xAA");
5496 entityMap.insert("icirc", "\xC3\xAE");
5497 entityMap.insert("ocirc", "\xC3\xB4");
5498 entityMap.insert("ucirc", "\xC3\xBB");
5499 entityMap.insert("Atilde", "\xC3\x83");
5500 entityMap.insert("Ntilde", "\xC3\x91");
5501 entityMap.insert("Otilde", "\xC3\x95");
5502 entityMap.insert("atilde", "\xC3\xA3");
5503 entityMap.insert("ntilde", "\xC3\xB1");
5504 entityMap.insert("otilde", "\xC3\xB5");
5505 entityMap.insert("szlig", "\xC3\x9F");
5506 entityMap.insert("Ccedil", "\xC3\x87");
5507 entityMap.insert("ccedil", "\xC3\xA7");
5508 entityMap.insert("Aring", "\xC3\x85");
5509 entityMap.insert("aring", "\xC3\xA5");
5510 entityMap.insert("nbsp", "\xC2\xA0");
5511 entityMap.insert("Gamma", "\xCE\x93");
5512 entityMap.insert("Delta", "\xCE\x94");
5513 entityMap.insert("Theta", "\xCE\x98");
5514 entityMap.insert("Lambda", "\xCE\x9B");
5515 entityMap.insert("Xi", "\xCE\x9E");
5516 entityMap.insert("Pi", "\xCE\xA0");
5517 entityMap.insert("Sigma", "\xCE\xA3");
5518 entityMap.insert("Upsilon", "\xCE\xA5");
5519 entityMap.insert("Phi", "\xCE\xA6");
5520 entityMap.insert("Psi", "\xCE\xA8");
5521 entityMap.insert("Omega", "\xCE\xA9");
5522 entityMap.insert("alpha", "\xCE\xB1");
5523 entityMap.insert("beta", "\xCE\xB2");
5524 entityMap.insert("gamma", "\xCE\xB3");
5525 entityMap.insert("delta", "\xCE\xB4");
5526 entityMap.insert("epsilon", "\xCE\xB5");
5527 entityMap.insert("zeta", "\xCE\xB6");
5528 entityMap.insert("eta", "\xCE\xB8");
5529 entityMap.insert("theta", "\xCE\xB8");
5530 entityMap.insert("iota", "\xCE\xB9");
5531 entityMap.insert("kappa", "\xCE\xBA");
5532 entityMap.insert("lambda", "\xCE\xBB");
5533 entityMap.insert("mu", "\xCE\xBC");
5534 entityMap.insert("nu", "\xCE\xBD");
5535 entityMap.insert("xi", "\xCE\xBE");
5536 entityMap.insert("pi", "\xCF\x80");
5537 entityMap.insert("rho", "\xCF\x81");
5538 entityMap.insert("sigma", "\xCF\x83");
5539 entityMap.insert("tau", "\xCF\x84");
5540 entityMap.insert("upsilon", "\xCF\x85");
5541 entityMap.insert("phi", "\xCF\x86");
5542 entityMap.insert("chi", "\xCF\x87");
5543 entityMap.insert("psi", "\xCF\x88");
5544 entityMap.insert("omega", "\xCF\x89");
5545 entityMap.insert("sigmaf", "\xCF\x82");
5546 entityMap.insert("sect", "\xC2\xA7");
5547 entityMap.insert("deg", "\xC2\xB0");
5548 entityMap.insert("prime", "\xE2\x80\xB2");
5549 entityMap.insert("Prime", "\xE2\x80\xB2");
5550 entityMap.insert("infin", "\xE2\x88\x9E");
5551 entityMap.insert("empty", "\xE2\x88\x85");
5552 entityMap.insert("plusmn", "\xC2\xB1");
5553 entityMap.insert("times", "\xC3\x97");
5554 entityMap.insert("minus", "\xE2\x88\x92");
5555 entityMap.insert("sdot", "\xE2\x8B\x85");
5556 entityMap.insert("part", "\xE2\x88\x82");
5557 entityMap.insert("nabla", "\xE2\x88\x87");
5558 entityMap.insert("radic", "\xE2\x88\x9A");
5559 entityMap.insert("perp", "\xE2\x8A\xA5");
5560 entityMap.insert("sum", "\xE2\x88\x91");
5561 entityMap.insert("int", "\xE2\x88\xAB");
5562 entityMap.insert("prod", "\xE2\x88\x8F");
5563 entityMap.insert("sim", "\xE2\x88\xBC");
5564 entityMap.insert("asymp", "\xE2\x89\x88");
5565 entityMap.insert("ne", "\xE2\x89\xA0");
5566 entityMap.insert("equiv", "\xE2\x89\xA1");
5567 entityMap.insert("prop", "\xE2\x88\x9D");
5568 entityMap.insert("le", "\xE2\x89\xA4");
5569 entityMap.insert("ge", "\xE2\x89\xA5");
5570 entityMap.insert("larr", "\xE2\x86\x90");
5571 entityMap.insert("rarr", "\xE2\x86\x92");
5572 entityMap.insert("isin", "\xE2\x88\x88");
5573 entityMap.insert("notin", "\xE2\x88\x89");
5574 entityMap.insert("lceil", "\xE2\x8C\x88");
5575 entityMap.insert("rceil", "\xE2\x8C\x89");
5576 entityMap.insert("lfloor", "\xE2\x8C\x8A");
5577 entityMap.insert("rfloor", "\xE2\x8C\x8B");
5581 if (s==0) return result;
5583 while ((p=entityPat.match(s,i,&l))!=-1)
5585 if (p>i) result+=s.mid(i,p-i);
5586 QCString entity = s.mid(p+1,l-2);
5587 char *code = entityMap.find(entity);
5598 result+=s.mid(i,s.length()-i);
5602 /*! Returns the standard string that is generated when the \\overload
5605 QCString getOverloadDocs()
5607 return theTranslator->trOverloadText();
5608 //"This is an overloaded member function, "
5609 // "provided for convenience. It differs from the above "
5610 // "function only in what argument(s) it accepts.";
5613 void addMembersToMemberGroup(MemberList *ml,
5614 MemberGroupSDict **ppMemberGroupSDict,
5615 Definition *context)
5618 //printf("addMemberToMemberGroup()\n");
5620 MemberListIterator mli(*ml);
5623 for (index=0;(md=mli.current());)
5625 if (md->isEnumerate()) // insert enum value of this enum into groups
5627 LockingPtr<MemberList> fmdl=md->enumFieldList();
5630 MemberDef *fmd=fmdl->first();
5633 int groupId=fmd->getMemberGroupId();
5636 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5637 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5638 //QCString *pDocs = Doxygen::memberDocDict[groupId];
5641 if (*ppMemberGroupSDict==0)
5643 *ppMemberGroupSDict = new MemberGroupSDict;
5644 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5646 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5649 mg = new MemberGroup(
5656 (*ppMemberGroupSDict)->append(groupId,mg);
5658 mg->insertMember(fmd); // insert in member group
5659 fmd->setMemberGroup(mg);
5666 int groupId=md->getMemberGroupId();
5669 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5670 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5671 //QCString *pDocs = Doxygen::memberDocDict[groupId];
5674 if (*ppMemberGroupSDict==0)
5676 *ppMemberGroupSDict = new MemberGroupSDict;
5677 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5679 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5682 mg = new MemberGroup(
5689 (*ppMemberGroupSDict)->append(groupId,mg);
5691 md = ml->take(index); // remove from member list
5692 mg->insertMember(md); // insert in member group
5693 mg->setRefItems(info->m_sli);
5694 md->setMemberGroup(mg);
5702 /*! Extracts a (sub-)string from \a type starting at \a pos that
5703 * could form a class. The index of the match is returned and the found
5704 * class \a name and a template argument list \a templSpec. If -1 is returned
5705 * there are no more matches.
5707 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
5709 static const QRegExp re_norm("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
5710 static const QRegExp re_ftn("[a-z_A-Z\\x80-\\xFF][()=_a-z_A-Z0-9:\\x80-\\xFF]*");
5713 if (lang == SrcLangExt_Fortran)
5715 if (type.at(pos)==',') return -1;
5716 if (type.left(4).lower()=="type")
5731 templSpec.resize(0);
5733 int typeLen=type.length();
5736 if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
5741 while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
5742 if (type.at(ts)=='<') // assume template instance
5744 // locate end of template
5747 while (te<typeLen && brCount!=0)
5749 if (type.at(te)=='<')
5751 if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
5753 if (type.at(te)=='>')
5755 if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
5760 name = type.mid(i,l);
5763 templSpec = type.mid(ts,te-ts),tl+=te-ts;
5766 else // no template part
5770 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
5771 // type.data(),pos,name.data(),templSpec.data());
5776 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
5777 // type.data(),pos,name.data(),templSpec.data());
5781 /*! Substitutes any occurrence of a formal argument from argument list
5782 * \a formalArgs in \a name by the corresponding actual argument in
5783 * argument list \a actualArgs. The result after substitution
5784 * is returned as a string. The argument \a name is used to
5785 * prevent recursive substitution.
5787 QCString substituteTemplateArgumentsInString(
5788 const QCString &name,
5789 ArgumentList *formalArgs,
5790 ArgumentList *actualArgs)
5792 //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
5793 // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
5794 if (formalArgs==0) return name;
5796 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
5798 // for each identifier in the base class name (e.g. B<T> -> B and T)
5799 while ((i=re.match(name,p,&l))!=-1)
5801 result += name.mid(p,i-p);
5802 QCString n = name.mid(i,l);
5803 ArgumentListIterator formAli(*formalArgs);
5805 Argument *actArg=actualArgs->first();
5807 // if n is a template argument, then we substitute it
5808 // for its template instance argument.
5810 for (formAli.toFirst();
5811 (formArg=formAli.current()) && !found;
5812 ++formAli,actArg=actualArgs->next()
5815 if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
5817 formArg->name = formArg->type.mid(6);
5818 formArg->type = "class";
5820 if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
5822 formArg->name = formArg->type.mid(9);
5823 formArg->type = "typename";
5825 if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
5827 //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
5828 // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
5829 //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
5830 // formArg->name.data(),actArg->type.data(),actArg->name.data()
5832 if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
5834 // replace formal argument with the actual argument of the instance
5835 if (!leftScopeMatch(actArg->type,n))
5836 // the scope guard is to prevent recursive lockup for
5837 // template<class A> class C : public<A::T>,
5838 // where A::T would become A::T::T here,
5839 // since n==A and actArg->type==A::T
5840 // see bug595833 for an example
5842 if (actArg->name.isEmpty())
5844 result += actArg->type+" ";
5848 // for case where the actual arg is something like "unsigned int"
5849 // the "int" part is in actArg->name.
5851 result += actArg->type+" "+actArg->name+" ";
5856 else if (formArg->name==n &&
5858 !formArg->defval.isEmpty() &&
5859 formArg->defval!=name /* to prevent recursion */
5862 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
5866 else if (formArg->name==n &&
5868 !formArg->defval.isEmpty() &&
5869 formArg->defval!=name /* to prevent recursion */
5872 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
5876 if (!found) result += n;
5879 result+=name.right(name.length()-p);
5880 //printf(" Inheritance relation %s -> %s\n",
5881 // name.data(),result.data());
5882 return result.stripWhiteSpace();
5885 /*! Makes a deep copy of the list of argument lists \a srcLists.
5886 * Will allocate memory, that is owned by the caller.
5888 QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
5890 ASSERT(srcLists!=0);
5891 QList<ArgumentList> *dstLists = new QList<ArgumentList>;
5892 dstLists->setAutoDelete(TRUE);
5893 QListIterator<ArgumentList> sli(*srcLists);
5895 for (;(sl=sli.current());++sli)
5897 dstLists->append(sl->deepCopy());
5902 /*! Strips template specifiers from scope \a fullName, except those
5903 * that make up specialized classes. The switch \a parentOnly
5904 * determines whether or not a template "at the end" of a scope
5905 * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will
5906 * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will
5907 * strip both unless A<T> or B<S> are specialized template classes.
5909 QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
5911 QCString *pLastScopeStripped)
5915 int l=fullName.length();
5916 int i=fullName.find('<');
5919 //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
5923 while (e<l && !done)
5925 char c=fullName.at(e++);
5936 int si= fullName.find("::",e);
5938 if (parentOnly && si==-1) break;
5939 // we only do the parent scope, so we stop here if needed
5941 result+=fullName.mid(p,i-p);
5942 //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data());
5943 if (getClass(result+fullName.mid(i,e-i))!=0)
5945 result+=fullName.mid(i,e-i);
5946 //printf(" 2:result+=%s\n",fullName.mid(i,e-i-1).data());
5948 else if (pLastScopeStripped)
5950 //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data());
5951 *pLastScopeStripped=fullName.mid(i,e-i);
5954 i=fullName.find('<',p);
5956 result+=fullName.right(l-p);
5957 //printf("3:result+=%s\n",fullName.right(l-p).data());
5961 /*! Merges two scope parts together. The parts may (partially) overlap.
5962 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
5963 * Example2: \c A and \c B will be \c A::B <br>
5964 * Example3: \c A::B and B will be \c A::B
5966 * @param leftScope the left hand part of the scope.
5967 * @param rightScope the right hand part of the scope.
5968 * @returns the merged scope.
5970 QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
5972 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
5973 if (leftScopeMatch(rightScope,leftScope)) return rightScope;
5975 int i=0,p=leftScope.length();
5977 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
5978 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
5980 while ((i=leftScope.findRev("::",p))!=-1)
5982 if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
5984 result = leftScope.left(i+2)+rightScope;
5989 if (found) return result;
5991 // case leftScope=="A" rightScope=="B" => result = "A::B"
5992 result=leftScope.copy();
5993 if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
5998 /*! Returns a fragment from scope \a s, starting at position \a p.
6000 * @param s the scope name as a string.
6001 * @param p the start position (0 is the first).
6002 * @param l the resulting length of the fragment.
6003 * @returns the location of the fragment, or -1 if non is found.
6005 int getScopeFragment(const QCString &s,int p,int *l)
6011 if (sp>=sl) return -1;
6015 if (c==':') sp++,p++; else break;
6022 case ':': // found next part
6024 case '<': // skip template specifier
6027 while (sp<sl && !done)
6029 // TODO: deal with << and >> operators!
6033 case '<': count++; break;
6034 case '>': count--; if (count==0) done=TRUE; break;
6046 //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
6050 //----------------------------------------------------------------------------
6052 PageDef *addRelatedPage(const char *name,const QCString &ptitle,
6053 const QCString &doc,
6054 QList<SectionInfo> * /*anchors*/,
6055 const char *fileName,int startLine,
6056 const QList<ListItemInfo> *sli,
6063 //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
6064 if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
6066 // append documentation block to the page.
6067 pd->setDocumentation(doc,fileName,startLine);
6068 //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name);
6072 QCString baseName=name;
6073 if (baseName.right(4)==".tex")
6074 baseName=baseName.left(baseName.length()-4);
6075 else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
6076 baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
6078 QCString title=ptitle.stripWhiteSpace();
6079 pd=new PageDef(fileName,startLine,baseName,doc,title);
6081 pd->setRefItems(sli);
6082 pd->setLanguage(lang);
6086 pd->setReference(tagInfo->tagName);
6089 pd->setFileName(convertNameToFile(pd->name(),FALSE,TRUE));
6091 //printf("Appending page `%s'\n",baseName.data());
6092 Doxygen::pageSDict->append(baseName,pd);
6094 if (gd) gd->addPage(pd);
6096 if (!pd->title().isEmpty())
6098 //outputList->writeTitle(pi->name,pi->title);
6100 // a page name is a label as well!
6104 file=gd->getOutputFileBase();
6108 file=pd->getOutputFileBase();
6110 SectionInfo *si=new SectionInfo(
6111 file,pd->name(),pd->title(),SectionInfo::Page,0,pd->getReference());
6112 //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
6113 // si->label.data(),si->definition?si->definition->name().data():"<none>",
6114 // si->fileName.data());
6115 //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
6116 //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
6117 Doxygen::sectionDict.append(pd->name(),si);
6123 //----------------------------------------------------------------------------
6125 void addRefItem(const QList<ListItemInfo> *sli,
6127 const char *prefix, const char *name,const char *title,const char *args)
6129 //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args);
6132 QListIterator<ListItemInfo> slii(*sli);
6134 for (slii.toFirst();(lii=slii.current());++slii)
6136 RefList *refList = Doxygen::xrefLists->find(lii->type);
6140 // either not a built-in list or the list is enabled
6141 (lii->type!="todo" || Config_getBool("GENERATE_TODOLIST")) &&
6142 (lii->type!="test" || Config_getBool("GENERATE_TESTLIST")) &&
6143 (lii->type!="bug" || Config_getBool("GENERATE_BUGLIST")) &&
6144 (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
6148 RefItem *item = refList->getRefItem(lii->itemId);
6151 item->prefix = prefix;
6153 item->title = title;
6156 refList->insertIntoList(key,item);
6163 void addGroupListToTitle(OutputList &ol,Definition *d)
6165 LockingPtr<GroupList> groups = d->partOfGroups();
6166 if (groups!=0) // write list of group to which this definition belongs
6168 ol.pushGeneratorState();
6169 ol.disableAllBut(OutputGenerator::Html);
6170 ol.writeString("<div class=\"ingroups\">");
6171 GroupListIterator gli(*groups);
6174 for (gli.toFirst();(gd=gli.current());++gli)
6176 if (!first) { ol.writeString(" | "); } else first=FALSE;
6177 ol.writeObjectLink(gd->getReference(),
6178 gd->getOutputFileBase(),0,gd->groupTitle());
6180 ol.writeString("</div>");
6181 ol.popGeneratorState();
6185 void filterLatexString(FTextStream &t,const char *str,
6186 bool insideTabbing,bool insidePre,bool insideItem)
6189 //printf("filterLatexString(%s)\n",str);
6190 //if (strlen(str)<2) stackTrace();
6191 const unsigned char *p=(const unsigned char *)str;
6193 unsigned char pc='\0';
6202 case '\\': t << "\\(\\backslash\\)"; break;
6203 case '{': t << "\\{"; break;
6204 case '}': t << "\\}"; break;
6205 case '_': t << "\\_"; break;
6214 case '#': t << "\\#"; break;
6215 case '$': t << "\\$"; break;
6216 case '%': t << "\\%"; break;
6217 case '^': t << "$^\\wedge$"; break;
6218 case '&': t << "\\&"; break;
6219 case '*': t << "$\\ast$"; break;
6220 case '_': if (!insideTabbing) t << "\\-";
6222 if (!insideTabbing) t << "\\-";
6224 case '{': t << "\\{"; break;
6225 case '}': t << "\\}"; break;
6226 case '<': t << "$<$"; break;
6227 case '>': t << "$>$"; break;
6228 case '|': t << "$|$"; break;
6229 case '~': t << "$\\sim$"; break;
6230 case '[': if (Config_getBool("PDF_HYPERLINKS") || insideItem)
6235 case ']': if (pc=='[') t << "$\\,$";
6236 if (Config_getBool("PDF_HYPERLINKS") || insideItem)
6241 case '-': t << "-\\/";
6243 case '\\': if (*p=='<')
6244 { t << "$<$"; p++; }
6246 { t << "$>$"; p++; }
6248 { t << "\\textbackslash{}"; }
6250 case '"': { t << "\\char`\\\"{}"; }
6254 //if (!insideTabbing && forceBreaks && c!=' ' && *p!=' ')
6255 if (!insideTabbing &&
6256 ((c>='A' && c<='Z' && pc!=' ' && pc!='\0') || (c==':' && pc!=':') || (pc=='.' && isId(c)))
6269 QCString rtfFormatBmkStr(const char *name)
6271 static QCString g_nextTag( "AAAAAAAAAA" );
6272 static QDict<QCString> g_tagDict( 5003 );
6274 g_tagDict.setAutoDelete(TRUE);
6276 // To overcome the 40-character tag limitation, we
6277 // substitute a short arbitrary string for the name
6278 // supplied, and keep track of the correspondence
6279 // between names and strings.
6280 QCString key( name );
6281 QCString* tag = g_tagDict.find( key );
6284 // This particular name has not yet been added
6285 // to the list. Add it, associating it with the
6286 // next tag value, and increment the next tag.
6287 tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
6288 g_tagDict.insert( key, tag );
6290 // This is the increment part
6291 char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1;
6292 for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
6294 if ( ( ++(*nxtTag) ) > 'Z' )
6300 // Since there was no carry, we can stop now
6309 QCString stripExtension(const char *fName)
6311 QCString result=fName;
6312 if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
6314 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
6320 void replaceNamespaceAliases(QCString &scope,int i)
6324 QCString ns = scope.left(i);
6325 QCString *s = Doxygen::namespaceAliasDict[ns];
6328 scope=*s+scope.right(scope.length()-i);
6331 if (i>0 && ns==scope.left(i)) break;
6335 QCString stripPath(const char *s)
6338 int i=result.findRev('/');
6341 result=result.mid(i+1);
6346 /** returns \c TRUE iff string \a s contains word \a w */
6347 bool containsWord(const QCString &s,const QCString &word)
6349 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6351 while ((i=wordExp.match(s,p,&l))!=-1)
6353 if (s.mid(i,l)==word) return TRUE;
6359 bool findAndRemoveWord(QCString &s,const QCString &word)
6361 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6363 while ((i=wordExp.match(s,p,&l))!=-1)
6365 if (s.mid(i,l)==word)
6367 if (i>0 && isspace((uchar)s.at(i-1)))
6369 else if (i+l<(int)s.length() && isspace(s.at(i+l)))
6371 s = s.left(i)+s.mid(i+l); // remove word + spacing
6379 /** Special version of QCString::stripWhiteSpace() that only strips
6380 * completely blank lines.
6381 * @param s the string to be stripped
6382 * @param docLine the line number corresponding to the start of the
6383 * string. This will be adjusted based on the number of lines stripped
6385 * @returns The stripped string.
6387 QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine)
6389 const char *p = s.data();
6392 // search for leading empty lines
6393 int i=0,li=-1,l=s.length();
6397 if (c==' ' || c=='\t' || c=='\r') i++;
6398 else if (c=='\n') i++,li=i,docLine++;
6402 // search for trailing empty lines
6408 if (c==' ' || c=='\t' || c=='\r') b--;
6409 else if (c=='\n') bi=b,b--;
6413 // return whole string if no leading or trailing lines where found
6414 if (li==-1 && bi==-1) return s;
6419 if (bi<=li) return 0; // only empty lines
6420 return s.mid(li,bi-li);
6424 void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
6425 const QCString &str,bool priority,const QCString &anchor)
6427 static bool searchEngine = Config_getBool("SEARCHENGINE");
6430 Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
6431 static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
6433 while ((i=wordPattern.match(str,p,&l))!=-1)
6435 Doxygen::searchIndex->addWord(str.mid(i,l),priority);
6442 //--------------------------------------------------------------------------
6444 static QDict<int> g_extLookup;
6446 static struct Lang2ExtMap
6448 const char *langName;
6449 const char *parserName;
6450 SrcLangExt parserId;
6454 // language parser parser option
6455 { "idl", "c", SrcLangExt_IDL },
6456 { "java", "c", SrcLangExt_Java },
6457 { "javascript", "c", SrcLangExt_JS },
6458 { "csharp", "c", SrcLangExt_CSharp },
6459 { "d", "c", SrcLangExt_D },
6460 { "php", "c", SrcLangExt_PHP },
6461 { "objective-c", "c", SrcLangExt_ObjC },
6462 { "c", "c", SrcLangExt_Cpp },
6463 { "c++", "c", SrcLangExt_Cpp },
6464 { "python", "python", SrcLangExt_Python },
6465 { "fortran", "fortran", SrcLangExt_Fortran },
6466 { "vhdl", "vhdl", SrcLangExt_VHDL },
6467 { "dbusxml", "dbusxml", SrcLangExt_XML },
6468 { "tcl", "tcl", SrcLangExt_Tcl },
6469 { "md", "md", SrcLangExt_Markdown },
6470 { 0, 0, (SrcLangExt)0 }
6473 bool updateLanguageMapping(const QCString &extension,const QCString &language)
6475 const Lang2ExtMap *p = g_lang2extMap;
6476 QCString langName = language.lower();
6479 if (langName==p->langName) break;
6482 if (!p->langName) return FALSE;
6484 // found the language
6485 SrcLangExt parserId = p->parserId;
6486 QCString extName = extension.lower();
6487 if (extName.isEmpty()) return FALSE;
6488 if (extName.at(0)!='.') extName.prepend(".");
6489 if (g_extLookup.find(extension)!=0) // language was already register for this ext
6491 g_extLookup.remove(extension);
6493 //printf("registering extension %s\n",extName.data());
6494 g_extLookup.insert(extName,new int(parserId));
6495 if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
6497 err("Failed to assign extension %s to parser %s for language %s\n",
6498 extName.data(),p->parserName,language.data());
6502 //msg("Registered extension %s to language parser %s...\n",
6503 // extName.data(),language.data());
6508 void initDefaultExtensionMapping()
6510 g_extLookup.setAutoDelete(TRUE);
6511 // extension parser id
6512 updateLanguageMapping(".idl", "idl");
6513 updateLanguageMapping(".ddl", "idl");
6514 updateLanguageMapping(".odl", "idl");
6515 updateLanguageMapping(".java", "java");
6516 updateLanguageMapping(".as", "javascript");
6517 updateLanguageMapping(".js", "javascript");
6518 updateLanguageMapping(".cs", "csharp");
6519 updateLanguageMapping(".d", "d");
6520 updateLanguageMapping(".php", "php");
6521 updateLanguageMapping(".php4", "php");
6522 updateLanguageMapping(".php5", "php");
6523 updateLanguageMapping(".inc", "php");
6524 updateLanguageMapping(".phtml", "php");
6525 updateLanguageMapping(".m", "objective-c");
6526 updateLanguageMapping(".M", "objective-c");
6527 updateLanguageMapping(".mm", "objective-c");
6528 updateLanguageMapping(".py", "python");
6529 updateLanguageMapping(".f", "fortran");
6530 updateLanguageMapping(".for", "fortran");
6531 updateLanguageMapping(".f90", "fortran");
6532 updateLanguageMapping(".vhd", "vhdl");
6533 updateLanguageMapping(".vhdl", "vhdl");
6534 updateLanguageMapping(".tcl", "tcl");
6535 updateLanguageMapping(".ucf", "vhdl");
6536 updateLanguageMapping(".qsf", "vhdl");
6537 updateLanguageMapping(".md", "md");
6538 updateLanguageMapping(".markdown", "md");
6540 //updateLanguageMapping(".xml", "dbusxml");
6543 SrcLangExt getLanguageFromFileName(const QCString fileName)
6545 int i = fileName.findRev('.');
6546 if (i!=-1) // name has an extension
6548 QCString extStr=fileName.right(fileName.length()-i).lower();
6549 if (!extStr.isEmpty()) // non-empty extension
6551 int *pVal=g_extLookup.find(extStr);
6552 if (pVal) // listed extension
6554 //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
6555 return (SrcLangExt)*pVal;
6559 //printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
6560 return SrcLangExt_Cpp; // not listed => assume C-ish language.
6563 //--------------------------------------------------------------------------
6565 MemberDef *getMemberFromSymbol(Definition *scope,FileDef *fileScope,
6569 (scope->definitionType()!=Definition::TypeClass &&
6570 scope->definitionType()!=Definition::TypeNamespace
6574 scope=Doxygen::globalScope;
6579 return 0; // no name was given
6581 DefinitionIntf *di = Doxygen::symbolMap->find(name);
6583 return 0; // could not find any matching symbols
6585 // mostly copied from getResolvedClassRec()
6586 QCString explicitScopePart;
6587 int qualifierIndex = computeQualifiedIndex(name);
6588 if (qualifierIndex!=-1)
6590 explicitScopePart = name.left(qualifierIndex);
6591 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
6592 name = name.mid(qualifierIndex+2);
6594 //printf("explicitScopePart=%s\n",explicitScopePart.data());
6596 int minDistance = 10000;
6597 MemberDef *bestMatch = 0;
6599 if (di->definitionType()==DefinitionIntf::TypeSymbolList)
6601 //printf("multiple matches!\n");
6602 // find the closest closest matching definition
6603 DefinitionListIterator dli(*(DefinitionList*)di);
6605 for (dli.toFirst();(d=dli.current());++dli)
6607 if (d->definitionType()==Definition::TypeMember)
6609 g_visitedNamespaces.clear();
6610 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
6611 if (distance!=-1 && distance<minDistance)
6613 minDistance = distance;
6614 bestMatch = (MemberDef *)d;
6615 //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
6620 else if (di->definitionType()==Definition::TypeMember)
6622 //printf("unique match!\n");
6623 Definition *d = (Definition *)di;
6624 g_visitedNamespaces.clear();
6625 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
6626 if (distance!=-1 && distance<minDistance)
6628 minDistance = distance;
6629 bestMatch = (MemberDef *)d;
6630 //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
6636 /*! Returns true iff the given name string appears to be a typedef in scope. */
6637 bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
6639 MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
6641 if (bestMatch && bestMatch->isTypedef())
6642 return TRUE; // closest matching symbol is a typedef
6648 int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos)
6651 if (startPos>=len) return len;
6652 char c = utf8Str[startPos];
6653 if (c<0) // multibyte utf-8 character
6655 bytes++; // 1xxx.xxxx: >=2 byte character
6656 if (((uchar)c&0xE0)==0xE0)
6658 bytes++; // 111x.xxxx: >=3 byte character
6660 if (((uchar)c&0xF0)==0xF0)
6662 bytes++; // 1111.xxxx: 4 byte character
6665 else if (c=='&') // skip over character entities
6667 static QRegExp re1("&#[0-9]+;"); // numerical entity
6668 static QRegExp re2("&[A-Z_a-z]+;"); // named entity
6670 int i1 = re1.match(utf8Str,startPos,&l1);
6671 int i2 = re2.match(utf8Str,startPos,&l2);
6681 return startPos+bytes;
6684 QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
6685 const QCString &doc,const QCString &fileName,int lineNr)
6688 if (doc.isEmpty()) return s.data();
6690 DocNode *root = validatingParseDoc(fileName,lineNr,
6691 (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
6692 TextDocVisitor *visitor = new TextDocVisitor(t);
6693 root->accept(visitor);
6696 QCString result = convertCharEntitiesToUTF8(s.data());
6699 int l=result.length();
6700 bool addEllipsis=FALSE;
6701 while ((i=nextUtf8CharPosition(result,l,i))<l)
6704 if (charCnt>=80) break;
6706 if (charCnt>=80) // try to truncate the string
6708 while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100)
6711 if (result.at(i)>=0 && isspace(result.at(i)))
6715 else if (result.at(i)==',' ||
6716 result.at(i)=='.' ||
6723 if (addEllipsis || charCnt==100) result=result.left(i)+"...";
6724 return result.data();
6727 //--------------------------------------------------------------------------------------
6729 static QDict<void> aliasesProcessed;
6731 static QCString expandAliasRec(const QCString s);
6735 Marker(int p, int n,int s) : pos(p),number(n),size(s) {}
6736 int pos; // position in the string
6737 int number; // argument number
6738 int size; // size of the marker
6741 /** Replaces the markers in an alias definition \a aliasValue
6742 * with the corresponding values found in the comma separated argument
6743 * list \a argList and the returns the result after recursive alias expansion.
6745 static QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
6747 //printf("----- replaceAliasArguments(val=[%s],args=[%s])\n",aliasValue.data(),argList.data());
6749 // first make a list of arguments from the comma separated argument list
6750 QList<QCString> args;
6751 args.setAutoDelete(TRUE);
6752 int i,l=(int)argList.length();
6756 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\'))
6758 args.append(new QCString(argList.mid(s,i-s)));
6759 s=i+1; // start of next argument
6762 if (l>s) args.append(new QCString(argList.right(l-s)));
6763 //printf("found %d arguments\n",args.count());
6765 // next we look for the positions of the markers and add them to a list
6766 QList<Marker> markerList;
6767 markerList.setAutoDelete(TRUE);
6768 l = aliasValue.length();
6773 if (markerStart==0 && aliasValue.at(i)=='\\') // start of a \xx marker
6777 else if (markerStart>0 && aliasValue.at(i)>='0' && aliasValue.at(i)<='9')
6779 // read digit that make up the marker number
6784 if (markerStart>0 && markerEnd>markerStart) // end of marker
6786 int markerLen = markerEnd-markerStart;
6787 markerList.append(new Marker(markerStart-1, // include backslash
6788 atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
6789 //printf("found marker at %d with len %d and number %d\n",
6790 // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
6792 markerStart=0; // outside marker
6800 if (markerStart>0 && markerEnd>markerStart)
6802 int markerLen = markerEnd-markerStart;
6803 markerList.append(new Marker(markerStart-1, // include backslash
6804 atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
6805 //printf("found marker at %d with len %d and number %d\n",
6806 // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
6809 // then we replace the markers with the corresponding arguments in one pass
6812 for (i=0;i<(int)markerList.count();i++)
6814 Marker *m = markerList.at(i);
6815 result+=aliasValue.mid(p,m->pos-p);
6816 //printf("part before marker %d: '%s'\n",i,aliasValue.mid(p,m->pos-p).data());
6817 if (m->number>0 && m->number<=(int)args.count()) // valid number
6819 result+=*args.at(m->number-1);
6820 //printf("marker index=%d pos=%d number=%d size=%d replacement %s\n",i,m->pos,m->number,m->size,
6821 // args.at(m->number-1)->data());
6823 p=m->pos+m->size; // continue after the marker
6825 result+=aliasValue.right(l-p); // append remainder
6826 //printf("string after replacement of markers: '%s'\n",result.data());
6828 // expand the result again
6829 result = substitute(result,"\\{","{");
6830 result = substitute(result,"\\}","}");
6831 result = expandAliasRec(substitute(result,"\\,",","));
6836 static QCString escapeCommas(const QCString &s)
6839 const char *p = s.data();
6843 if (c==',' && pc!='\\')
6854 //printf("escapeCommas: '%s'->'%s'\n",s.data(),result.data());
6855 return result.data();
6858 static QCString expandAliasRec(const QCString s)
6861 static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
6864 while ((i=cmdPat.match(value,p,&l))!=-1)
6866 result+=value.mid(p,i-p);
6867 QCString args = extractAliasArgs(value,i+l);
6868 bool hasArgs = !args.isEmpty(); // found directly after command
6869 int argsLen = args.length();
6870 QCString cmd = value.mid(i+1,l-1);
6871 QCString cmdNoArgs = cmd;
6875 numArgs = countAliasArguments(args);
6876 cmd += QCString().sprintf("{%d}",numArgs); // alias name + {n}
6878 QCString *aliasText=Doxygen::aliasDict.find(cmd);
6879 if (numArgs>1 && aliasText==0)
6880 { // in case there is no command with numArgs parameters, but there is a command with 1 parameter,
6881 // we also accept all text as the argument of that command (so you don't have to escape commas)
6882 aliasText=Doxygen::aliasDict.find(cmdNoArgs+"{1}");
6885 cmd = cmdNoArgs+"{1}";
6886 args = escapeCommas(args); // escape , so that everything is seen as one argument
6889 //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s' aliasText=%s\n",
6890 // s.data(),cmd.data(),numArgs,args.data(),aliasText?aliasText->data():"<none>");
6891 if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias
6893 //printf("is an alias!\n");
6894 aliasesProcessed.insert(cmd,(void *)0x8);
6895 QCString val = *aliasText;
6898 val = replaceAliasArguments(val,args);
6899 //printf("replace '%s'->'%s' args='%s'\n",
6900 // aliasText->data(),val.data(),args.data());
6902 result+=expandAliasRec(val);
6903 aliasesProcessed.remove(cmd);
6905 if (hasArgs) p+=argsLen+2;
6907 else // command is not an alias
6909 //printf("not an alias!\n");
6910 result+=value.mid(i,l);
6914 result+=value.right(value.length()-p);
6916 //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
6920 int countAliasArguments(const QCString argList)
6923 int l = argList.length();
6927 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++;
6932 QCString extractAliasArgs(const QCString &args,int pos)
6937 if (args.at(pos)=='{') // alias has argument
6939 for (i=pos;i<(int)args.length();i++)
6943 if (args.at(i)=='{') bc++;
6944 if (args.at(i)=='}') bc--;
6945 prevChar=args.at(i);
6954 //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
6955 return args.mid(pos+1,i-pos-1);
6962 QCString resolveAliasCmd(const QCString aliasCmd)
6965 aliasesProcessed.clear();
6966 //printf("Expanding: '%s'\n",aliasCmd.data());
6967 result = expandAliasRec(aliasCmd);
6968 //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
6972 QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
6975 aliasesProcessed.clear();
6976 // avoid expanding this command recursively
6977 aliasesProcessed.insert(aliasName,(void *)0x8);
6978 // expand embedded commands
6979 //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
6980 result = expandAliasRec(aliasValue);
6981 //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
6985 void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
6988 ol.startConstraintList(theTranslator->trTypeConstraints());
6989 ArgumentListIterator ali(*al);
6991 for (;(a=ali.current());++ali)
6993 ol.startConstraintParam();
6994 ol.parseText(a->name);
6995 ol.endConstraintParam();
6996 ol.startConstraintType();
6997 linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
6998 ol.endConstraintType();
6999 ol.startConstraintDocs();
7000 ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
7001 ol.endConstraintDocs();
7003 ol.endConstraintList();
7006 //----------------------------------------------------------------------------
7010 #ifdef TRACINGSUPPORT
7011 void *backtraceFrames[128];
7012 int frameCount = backtrace(backtraceFrames, 128);
7013 static char cmd[40960];
7015 p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
7016 for (int x = 0; x < frameCount; x++)
7018 p += sprintf(p,"%p ", backtraceFrames[x]);
7020 fprintf(stderr,"========== STACKTRACE START ==============\n");
7021 if (FILE *fp = popen(cmd, "r"))
7024 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
7026 fwrite(resBuf, 1, len, stderr);
7030 fprintf(stderr,"============ STACKTRACE END ==============\n");
7031 //fprintf(stderr,"%s\n", frameStrings[x]);
7035 static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size,
7036 const char *inputEncoding,const char *outputEncoding)
7038 if (inputEncoding==0 || outputEncoding==0) return size;
7039 if (qstricmp(inputEncoding,outputEncoding)==0) return size;
7040 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
7041 if (cd==(void *)(-1))
7043 err("error: unsupported character conversion: '%s'->'%s': %s\n"
7044 "Check the INPUT_ENCODING setting in the config file!\n",
7045 inputEncoding,outputEncoding,strerror(errno));
7048 int tmpBufSize=size*4+1;
7049 BufStr tmpBuf(tmpBufSize);
7051 size_t oLeft=tmpBufSize;
7052 char *srcPtr = srcBuf.data();
7053 char *dstPtr = tmpBuf.data();
7055 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
7057 newSize = tmpBufSize-oLeft;
7058 srcBuf.shrink(newSize);
7059 strncpy(srcBuf.data(),tmpBuf.data(),newSize);
7060 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
7064 err("%s: error: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
7065 fileName,inputEncoding,outputEncoding);
7068 portable_iconv_close(cd);
7072 //! read a file name \a fileName and optionally filter and transcode it
7073 bool readInputFile(const char *fileName,BufStr &inBuf)
7077 //uint oldPos = dest.curPos();
7078 //printf(".......oldPos=%d\n",oldPos);
7080 QFileInfo fi(fileName);
7081 if (!fi.exists()) return FALSE;
7082 QCString filterName = getFileFilter(fileName,FALSE);
7083 if (filterName.isEmpty())
7086 if (!f.open(IO_ReadOnly))
7088 err("error: could not open file %s\n",fileName);
7094 if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
7096 err("error: problems while reading file %s\n",fileName);
7102 QCString cmd=filterName+" \""+fileName+"\"";
7103 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
7104 FILE *f=portable_popen(cmd,"r");
7107 err("error: could not execute filter %s\n",filterName.data());
7110 const int bufSize=1024;
7113 while ((numRead=fread(buf,1,bufSize,f))>0)
7115 //printf(">>>>>>>>Reading %d bytes\n",numRead);
7116 inBuf.addArray(buf,numRead),size+=numRead;
7119 inBuf.at(inBuf.curPos()) ='\0';
7120 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
7121 Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",inBuf.data());
7126 ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM
7127 (inBuf.at(0)==-2 && inBuf.at(1)==-1) // big endian BOM
7129 ) // UCS-2 encoded file
7131 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
7135 (uchar)inBuf.at(0)==0xEF &&
7136 (uchar)inBuf.at(1)==0xBB &&
7137 (uchar)inBuf.at(2)==0xBF
7138 ) // UTF-8 encoded file
7140 inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
7142 else // transcode according to the INPUT_ENCODING setting
7144 // do character transcoding if needed.
7145 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
7146 Config_getString("INPUT_ENCODING"),"UTF-8");
7149 inBuf.addChar('\n'); /* to prevent problems under Windows ? */
7151 // and translate CR's
7152 size=inBuf.curPos()-start;
7153 int newSize=filterCRLF(inBuf.data()+start,size);
7154 //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
7155 if (newSize!=size) // we removed chars
7157 inBuf.shrink(newSize); // resize the array
7158 //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
7160 inBuf.at(inBuf.curPos())='\0';
7164 // Replace %word by word in title
7165 QCString filterTitle(const QCString &title)
7168 static QRegExp re("%[A-Z_a-z]");
7170 while ((i=re.match(title,p,&l))!=-1)
7172 tf+=title.mid(p,i-p);
7173 tf+=title.mid(i+1,l-1); // skip %
7176 tf+=title.right(title.length()-p);
7180 //----------------------------------------------------------------------------
7181 // returns TRUE if the name of the file represented by `fi' matches
7182 // one of the file patterns in the `patList' list.
7184 bool patternMatch(const QFileInfo &fi,const QStrList *patList)
7189 QStrListIterator it(*patList);
7191 for (it.toFirst();(pattern=it.current());++it)
7193 if (!pattern.isEmpty() && !found)
7195 int i=pattern.find('=');
7196 if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
7198 #if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
7199 QRegExp re(pattern,FALSE,TRUE); // case insensitive match
7201 QRegExp re(pattern,TRUE,TRUE); // case sensitive match
7203 found = found || re.match(fi.fileName().data())!=-1 ||
7204 re.match(fi.filePath().data())!=-1 ||
7205 re.match(fi.absFilePath().data())!=-1;
7206 //printf("Matching `%s' against pattern `%s' found=%d\n",
7207 // fi->fileName().data(),pattern.data(),found);
7214 #if 0 // move to HtmlGenerator::writeSummaryLink
7215 void writeSummaryLink(OutputList &ol,const char *label,const char *title,
7216 bool &first,const char *file)
7220 ol.writeString(" <div class=\"summary\">\n");
7225 ol.writeString(" |\n");
7229 ol.writeString("<a href=\"");
7230 ol.writeString(file);
7231 ol.writeString(Doxygen::htmlFileExtension);
7235 ol.writeString("<a href=\"#");
7236 ol.writeString(label);
7238 ol.writeString("\">");
7239 ol.writeString(title);
7240 ol.writeString("</a>");
7244 QCString externalLinkTarget()
7246 static bool extLinksInWindow = Config_getBool("EXT_LINKS_IN_WINDOW");
7247 if (extLinksInWindow) return "target=\"_blank\" "; else return "";
7250 QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
7255 QCString *dest = Doxygen::tagDestinationDict[ref];
7259 int l = result.length();
7260 if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
7261 { // relative path -> prepend relPath.
7262 result.prepend(relPath);
7264 if (!href) result.prepend("doxygen=\""+ref+":");
7265 if (l>0 && result.at(l-1)!='/') result+='/';
7266 if (!href) result.append("\" ");
7276 /** Writes the intensity only bitmap representated by \a data as an image to
7277 * directory \a dir using the colors defined by HTML_COLORSTYLE_*.
7279 void writeColoredImgData(const char *dir,ColoredImgDataItem data[])
7281 static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
7282 static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
7283 static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
7287 fileName=(QCString)dir+"/"+data->name;
7289 if (f.open(IO_WriteOnly))
7291 ColoredImage img(data->width,data->height,data->content,data->alpha,
7297 fprintf(stderr,"Warning: Cannot open file %s for writing\n",data->name);
7299 Doxygen::indexList.addImageFile(data->name);
7304 /** Replaces any markers of the form \#\#AA in input string \a str
7305 * by new markers of the form \#AABBCC, where \#AABBCC represents a
7306 * valid color, based on the intensity represented by hex number AA
7307 * and the current HTML_COLORSTYLE_* settings.
7309 QCString replaceColorMarkers(const char *str)
7313 if (s.isEmpty()) return result;
7314 static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]");
7315 static const char hex[] = "0123456789ABCDEF";
7316 static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
7317 static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
7318 static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
7319 int i,l,sl=s.length(),p=0;
7320 while ((i=re.match(s,p,&l))!=-1)
7322 result+=s.mid(p,i-p);
7323 QCString lumStr = s.mid(i+2,l-2);
7324 #define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
7325 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
7326 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
7330 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
7331 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
7332 pow(level/255.0,gamma/100.0),&r,&g,&b);
7333 red = (int)(r*255.0);
7334 green = (int)(g*255.0);
7335 blue = (int)(b*255.0);
7338 colStr[1]=hex[red>>4];
7339 colStr[2]=hex[red&0xf];
7340 colStr[3]=hex[green>>4];
7341 colStr[4]=hex[green&0xf];
7342 colStr[5]=hex[blue>>4];
7343 colStr[6]=hex[blue&0xf];
7345 //printf("replacing %s->%s (level=%d)\n",lumStr.data(),colStr,level);
7349 result+=s.right(sl-p);
7353 /** Copies the contents of file with name \a src to the newly created
7354 * file with name \a dest. Returns TRUE if successful.
7356 bool copyFile(const QCString &src,const QCString &dest)
7359 if (sf.open(IO_ReadOnly))
7363 if (df.open(IO_WriteOnly))
7365 char *buffer = new char[fi.size()];
7366 sf.readBlock(buffer,fi.size());
7367 df.writeBlock(buffer,fi.size());
7373 err("error: could not write to file %s\n",dest.data());
7379 err("error: could not open user specified file %s\n",src.data());
7385 /** Returns the section of text, in between a pair of markers.
7386 * Full lines are returned, excluding the lines on which the markers appear.
7388 QCString extractBlock(const QCString text,const QCString marker)
7394 // find the character positions of the markers
7395 int m1 = text.find(marker);
7396 if (m1==-1) return result;
7397 int m2 = text.find(marker,m1+marker.length());
7398 if (m2==-1) return result;
7400 // find start and end line positions for the markers
7402 while (!found && (i=text.find('\n',p))!=-1)
7404 found = (p<=m1 && m1<i); // found the line with the start marker
7410 while ((i=text.find('\n',p))!=-1)
7412 if (p<=m2 && m2<i) // found the line with the end marker
7420 //printf("text=[%s]\n",text.mid(l1,l2-l1).data());
7421 return text.mid(l1,l2-l1);
7424 /** Returns a string representation of \a lang. */
7425 QCString langToString(SrcLangExt lang)
7429 case SrcLangExt_Unknown: return "Unknown";
7430 case SrcLangExt_IDL: return "IDL";
7431 case SrcLangExt_Java: return "Java";
7432 case SrcLangExt_CSharp: return "C#";
7433 case SrcLangExt_D: return "D";
7434 case SrcLangExt_PHP: return "PHP";
7435 case SrcLangExt_ObjC: return "Objective-C";
7436 case SrcLangExt_Cpp: return "C++";
7437 case SrcLangExt_JS: return "Javascript";
7438 case SrcLangExt_Python: return "Python";
7439 case SrcLangExt_Fortran: return "Fortran";
7440 case SrcLangExt_VHDL: return "VHDL";
7441 case SrcLangExt_XML: return "XML";
7442 case SrcLangExt_Tcl: return "Tcl";
7443 case SrcLangExt_Markdown: return "Markdown";
7448 /** Returns the scope separator to use given the programming language \a lang */
7449 QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope)
7451 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp || lang==SrcLangExt_VHDL || lang==SrcLangExt_Python)
7455 else if (lang==SrcLangExt_PHP && !classScope)
7465 /** Corrects URL \a url according to the relative path \a relPath.
7466 * Returns the corrected URL. For absolute URLs no correction will be done.
7468 QCString correctURL(const QCString &url,const QCString &relPath)
7470 QCString result = url;
7471 if (!relPath.isEmpty() &&
7472 url.left(5)!="http:" && url.left(6)!="https:" &&
7473 url.left(4)!="ftp:" && url.left(5)!="file:")
7475 result.prepend(relPath);
7480 //---------------------------------------------------------------------------
7482 bool protectionLevelVisible(Protection prot)
7484 static bool extractPrivate = Config_getBool("EXTRACT_PRIVATE");
7485 static bool extractPackage = Config_getBool("EXTRACT_PACKAGE");
7487 return (prot!=Private && prot!=Package) ||
7488 (prot==Private && extractPrivate) ||
7489 (prot==Package && extractPackage);
7492 //---------------------------------------------------------------------------
7494 QCString stripIndentation(const QCString &s)
7496 if (s.isEmpty()) return s; // empty string -> we're done
7498 //printf("stripIndentation:\n%s\n------\n",s.data());
7499 // compute minimum indentation over all lines
7500 const char *p=s.data();
7503 int minIndent=1000000; // "infinite"
7504 bool searchIndent=TRUE;
7505 static int tabSize=Config_getInt("TAB_SIZE");
7508 if (c=='\t') indent+=tabSize - (indent%tabSize);
7509 else if (c=='\n') indent=0,searchIndent=TRUE;
7510 else if (c==' ') indent++;
7511 else if (searchIndent)
7514 if (indent<minIndent) minIndent=indent;
7518 // no indent to remove -> we're done
7519 if (minIndent==0) return s;
7521 // remove minimum indentation for each line
7527 if (c=='\n') // start of new line
7532 else if (indent<minIndent) // skip until we reach minIndent
7536 int newIndent = indent+tabSize-(indent%tabSize);
7538 while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
7550 else // copy anything until the end of the line
7557 return result.data();
7561 bool fileVisibleInIndex(FileDef *fd,bool &genSourceFile)
7563 static bool allExternals = Config_getBool("ALLEXTERNALS");
7564 bool isDocFile = fd->isDocumentationFile();
7565 genSourceFile = !isDocFile && fd->generateSourceFile();
7566 return ( ((allExternals && fd->isLinkable()) ||
7567 fd->isLinkableInProject()