1 /*****************************************************************************
4 * Copyright (C) 1997-2015 by Dimitri van Heesch.
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation under the terms of the GNU General Public License is hereby
8 * granted. No representations are made about the suitability of this software
9 * for any purpose. It is provided "as is" without express or implied warranty.
10 * See the GNU General Public License for more details.
12 * Documents produced by Doxygen are derivative works derived from the
13 * input used in their production; they are not affected by this license.
26 #include <qfileinfo.h>
28 #include <qdatetime.h>
36 #include "outputlist.h"
47 #include "searchindex.h"
49 #include "textdocvisitor.h"
50 #include "latexdocvisitor.h"
52 #include "parserintf.h"
57 #include "arguments.h"
58 #include "memberlist.h"
59 #include "classlist.h"
60 #include "namespacedef.h"
61 #include "membername.h"
63 #include "membergroup.h"
65 #include "htmlentity.h"
67 #define ENABLE_TRACINGSUPPORT 0
69 #if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
70 #define TRACINGSUPPORT
79 //------------------------------------------------------------------------
81 // selects one of the name to sub-dir mapping algorithms that is used
82 // to select a sub directory when CREATE_SUBDIRS is set to YES.
88 //#define MAP_ALGO ALGO_COUNT
89 //#define MAP_ALGO ALGO_CRC16
90 #define MAP_ALGO ALGO_MD5
92 #define REL_PATH_TO_ROOT "../../"
94 //------------------------------------------------------------------------
95 // TextGeneratorOLImpl implementation
96 //------------------------------------------------------------------------
98 TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od)
102 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
105 //printf("TextGeneratorOlImpl::writeString('%s',%d)\n",s,keepSpaces);
116 if (c==' ') m_od.writeNonBreakableSpace(1);
117 else cs[0]=c,m_od.docify(cs);
127 void TextGeneratorOLImpl::writeBreak(int indent) const
129 m_od.lineBreak("typebreak");
131 for (i=0;i<indent;i++)
133 m_od.writeNonBreakableSpace(3);
137 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
138 const char *anchor,const char *text
141 //printf("TextGeneratorOlImpl::writeLink('%s')\n",text);
142 m_od.writeObjectLink(extRef,file,anchor,text);
145 //------------------------------------------------------------------------
146 //------------------------------------------------------------------------
148 // an inheritance tree of depth of 100000 should be enough for everyone :-)
149 const int maxInheritanceDepth = 100000;
152 Removes all anonymous scopes from string s
155 "bla::@10::blep" => "bla::blep"
156 "bla::@10::@11::blep" => "bla::blep"
157 "@10::blep" => "blep"
158 " @10::blep" => "blep"
159 "@9::@10::blep" => "blep"
161 "bla::@1::@2" => "bla"
165 QCString removeAnonymousScopes(const QCString &s)
168 if (s.isEmpty()) return result;
169 static QRegExp re("[ :]*@[0-9]+[: ]*");
170 int i,l,sl=s.length();
172 while ((i=re.match(s,p,&l))!=-1)
174 result+=s.mid(p,i-p);
176 bool b1=FALSE,b2=FALSE;
177 while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
179 while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
186 result+=s.right(sl-p);
187 //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
191 // replace anonymous scopes with __anonymous__ or replacement if provided
192 QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
195 if (s.isEmpty()) return result;
196 static QRegExp re("@[0-9]+");
197 int i,l,sl=s.length();
199 while ((i=re.match(s,p,&l))!=-1)
201 result+=s.mid(p,i-p);
208 result+="__anonymous__";
212 result+=s.right(sl-p);
213 //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
218 // strip anonymous left hand side part of the scope
219 QCString stripAnonymousNamespaceScope(const QCString &s)
224 while ((i=getScopeFragment(s,p,&l))!=-1)
226 //printf("Scope fragment %s\n",s.mid(i,l).data());
227 if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
231 if (!newScope.isEmpty()) newScope+="::";
232 newScope+=s.mid(i,l);
237 if (!newScope.isEmpty()) newScope+="::";
238 newScope+=s.right(sl-i);
244 //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
248 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
250 od.pushGeneratorState();
252 od.disable(OutputGenerator::Html);
253 od.disable(OutputGenerator::Man);
254 od.disable(OutputGenerator::Docbook);
255 if (Config_getBool(PDF_HYPERLINKS)) od.disable(OutputGenerator::Latex);
256 if (Config_getBool(RTF_HYPERLINKS)) od.disable(OutputGenerator::RTF);
258 od.docify(theTranslator->trPageAbbreviation());
259 od.endPageRef(cn,mn);
261 od.popGeneratorState();
264 /*! Generate a place holder for a position in a list. Used for
265 * translators to be able to specify different elements orders
266 * depending on whether text flows from left to right or visa versa.
268 QCString generateMarker(int id)
270 const int maxMarkerStrLen = 20;
271 char result[maxMarkerStrLen];
272 qsnprintf(result,maxMarkerStrLen,"@%d",id);
276 static QCString stripFromPath(const QCString &path,QStrList &l)
278 // look at all the strings in the list and strip the longest match
279 const char *s=l.first();
281 unsigned int length = 0;
285 if (prefix.length() > length &&
286 qstricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
288 length = prefix.length();
289 potential = path.right(path.length()-prefix.length());
293 if (length) return potential;
297 /*! strip part of \a path if it matches
298 * one of the paths in the Config_getList(STRIP_FROM_PATH) list
300 QCString stripFromPath(const QCString &path)
302 return stripFromPath(path,Config_getList(STRIP_FROM_PATH));
305 /*! strip part of \a path if it matches
306 * one of the paths in the Config_getList(INCLUDE_PATH) list
308 QCString stripFromIncludePath(const QCString &path)
310 return stripFromPath(path,Config_getList(STRIP_FROM_INC_PATH));
313 /*! try to determine if \a name is a source or a header file name by looking
314 * at the extension. A number of variations is allowed in both upper and
315 * lower case) If anyone knows or uses another extension please let me know :-)
317 int guessSection(const char *name)
319 QCString n=((QCString)name).lower();
320 if (n.right(2)==".c" || // source
322 n.right(4)==".cxx" ||
323 n.right(4)==".cpp" ||
324 n.right(4)==".c++" ||
325 n.right(5)==".java" ||
328 n.right(3)==".ii" || // inline
329 n.right(4)==".ixx" ||
330 n.right(4)==".ipp" ||
331 n.right(4)==".i++" ||
332 n.right(4)==".inl" ||
333 n.right(4)==".xml" ||
335 ) return Entry::SOURCE_SEC;
336 if (n.right(2)==".h" || // header
338 n.right(4)==".hxx" ||
339 n.right(4)==".hpp" ||
340 n.right(4)==".h++" ||
341 n.right(4)==".idl" ||
342 n.right(4)==".ddl" ||
343 n.right(5)==".pidl" ||
345 ) return Entry::HEADER_SEC;
349 QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
350 Definition **typedefContext)
352 //printf("<<resolveTypeDef(%s,%s)\n",
353 // context ? context->name().data() : "<none>",qualifiedName.data());
355 if (qualifiedName.isEmpty())
357 //printf(" qualified name empty!\n");
361 Definition *mContext=context;
362 if (typedefContext) *typedefContext=context;
364 // see if the qualified name has a scope part
365 int scopeIndex = qualifiedName.findRev("::");
366 QCString resName=qualifiedName;
367 if (scopeIndex!=-1) // strip scope part for the name
369 resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
370 if (resName.isEmpty())
372 // qualifiedName was of form A:: !
373 //printf(" qualified name of form A::!\n");
378 while (mContext && md==0)
380 // step 1: get the right scope
381 Definition *resScope=mContext;
384 // split-off scope part
385 QCString resScopeName = qualifiedName.left(scopeIndex);
386 //printf("resScopeName=`%s'\n",resScopeName.data());
388 // look-up scope in context
391 while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
393 QCString qualScopePart = resScopeName.mid(is,l);
394 QCString tmp = resolveTypeDef(mContext,qualScopePart);
395 if (!tmp.isEmpty()) qualScopePart=tmp;
396 resScope = resScope->findInnerCompound(qualScopePart);
397 //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
398 if (resScope==0) break;
402 //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
404 // step 2: get the member
405 if (resScope) // no scope or scope found in the current context
407 //printf("scope found: %s, look for typedef %s\n",
408 // resScope->qualifiedName().data(),resName.data());
409 MemberNameSDict *mnd=0;
410 if (resScope->definitionType()==Definition::TypeClass)
412 mnd=Doxygen::memberNameSDict;
416 mnd=Doxygen::functionNameSDict;
418 MemberName *mn=mnd->find(resName);
421 MemberNameIterator mni(*mn);
424 for (;(tmd=mni.current());++mni)
426 //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
427 // tmd->name().data(), resScope->name().data(),
428 // tmd->getOuterScope()->name().data(), mContext);
429 if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
431 int dist=isAccessibleFrom(resScope,0,tmd);
432 if (dist!=-1 && (md==0 || dist<minDist))
441 mContext=mContext->getOuterScope();
444 // step 3: get the member's type
447 //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s' args='%s'\n",
448 // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
450 result=md->typeString();
451 QCString args = md->argsString();
452 if (args.find(")(")!=-1) // typedef of a function/member pointer
456 else if (args.find('[')!=-1) // typedef of an array
460 if (typedefContext) *typedefContext=md->getOuterScope();
464 //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
465 // qualifiedName.data(),context ? context->name().data() : "<global>");
472 /*! Get a class definition given its name.
473 * Returns 0 if the class is not found.
475 ClassDef *getClass(const char *n)
477 if (n==0 || n[0]=='\0') return 0;
479 ClassDef *result = Doxygen::classSDict->find(name);
480 //if (result==0 && !exact) // also try generic and protocol versions
482 // result = Doxygen::classSDict->find(name+"-g");
485 // result = Doxygen::classSDict->find(name+"-p");
488 //printf("getClass(%s)=%s\n",n,result?result->name().data():"<none>");
492 NamespaceDef *getResolvedNamespace(const char *name)
494 if (name==0 || name[0]=='\0') return 0;
495 QCString *subst = Doxygen::namespaceAliasDict[name];
498 int count=0; // recursion detection guard
500 while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
507 warn_uncond("possible recursive namespace alias detected for %s!\n",name);
509 return Doxygen::namespaceSDict->find(subst->data());
513 return Doxygen::namespaceSDict->find(name);
517 static QDict<MemberDef> g_resolvedTypedefs;
518 static QDict<Definition> g_visitedNamespaces;
520 // forward declaration
521 static ClassDef *getResolvedClassRec(const Definition *scope,
522 const FileDef *fileScope,
524 MemberDef **pTypeDef,
525 QCString *pTemplSpec,
526 QCString *pResolvedType
528 int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope,const Definition *item,
529 const QCString &explicitScopePart);
531 /*! Returns the class representing the value of the typedef represented by \a md
532 * within file \a fileScope.
534 * Example: typedef A T; will return the class representing A if it is a class.
536 * Example: typedef int T; will return 0, since "int" is not a class.
538 ClassDef *newResolveTypedef(const FileDef *fileScope,MemberDef *md,
539 MemberDef **pMemType,QCString *pTemplSpec,
540 QCString *pResolvedType,
541 ArgumentList *actTemplParams)
543 //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
544 bool isCached = md->isTypedefValCached(); // value already cached
547 //printf("Already cached %s->%s [%s]\n",
548 // md->name().data(),
549 // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
550 // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
552 if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
553 if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
554 return md->getCachedTypedefVal();
556 //printf("new typedef\n");
557 QCString qname = md->qualifiedName();
558 if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
560 g_resolvedTypedefs.insert(qname,md); // put on the trace list
562 ClassDef *typeClass = md->getClassDef();
563 QCString type = md->typeString(); // get the "value" of the typedef
564 if (typeClass && typeClass->isTemplate() &&
565 actTemplParams && actTemplParams->count()>0)
567 type = substituteTemplateArgumentsInString(type,
568 typeClass->templateArguments(),actTemplParams);
570 QCString typedefValue = type;
571 int tl=type.length();
572 int ip=tl-1; // remove * and & at the end
573 while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
577 type=type.left(ip+1);
578 type.stripPrefix("const "); // strip leading "const"
579 type.stripPrefix("struct "); // strip leading "struct"
580 type.stripPrefix("union "); // strip leading "union"
582 tl=type.length(); // length may have been changed
583 while (sp<tl && type.at(sp)==' ') sp++;
584 MemberDef *memTypeDef = 0;
585 ClassDef *result = getResolvedClassRec(md->getOuterScope(),
586 fileScope,type,&memTypeDef,0,pResolvedType);
587 // if type is a typedef then return what it resolves to.
588 if (memTypeDef && memTypeDef->isTypedef())
590 result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
593 else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
595 *pMemType = memTypeDef;
598 //printf("type=%s result=%p\n",type.data(),result);
601 // try unspecialized version if type is template
602 int si=type.findRev("::");
603 int i=type.find('<');
604 if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
606 if (pTemplSpec) *pTemplSpec = type.mid(i);
607 result = getResolvedClassRec(md->getOuterScope(),fileScope,
608 type.left(i),0,0,pResolvedType);
609 //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
610 // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
612 else if (si!=-1) // A::B
615 if (i==-1) // Something like A<T>::B => lookup A::B
619 else // Something like A<T>::B<S> => lookup A::B, spec=<S>
621 if (pTemplSpec) *pTemplSpec = type.mid(i);
623 result = getResolvedClassRec(md->getOuterScope(),fileScope,
624 stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
628 //if (result) ip=si+sp+1;
636 *pResolvedType=result->qualifiedName();
637 //printf("*pResolvedType=%s\n",pResolvedType->data());
638 if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
639 if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
643 *pResolvedType=typedefValue;
647 // remember computed value for next time
648 if (result && result->getDefFileName()!="<code>")
649 // this check is needed to prevent that temporary classes that are
650 // introduced while parsing code fragments are being cached here.
652 //printf("setting cached typedef %p in result %p\n",md,result);
653 //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
654 //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
655 md->cacheTypedefVal(result,
656 pTemplSpec ? *pTemplSpec : QCString(),
657 pResolvedType ? *pResolvedType : QCString()
661 g_resolvedTypedefs.remove(qname); // remove from the trace list
666 /*! Substitutes a simple unqualified \a name within \a scope. Returns the
667 * value of the typedef or \a name if no typedef was found.
669 static QCString substTypedef(const Definition *scope,const FileDef *fileScope,const QCString &name,
670 MemberDef **pTypeDef=0)
672 QCString result=name;
673 if (name.isEmpty()) return result;
675 // lookup scope fragment in the symbol map
676 DefinitionIntf *di = Doxygen::symbolMap->find(name);
677 if (di==0) return result; // no matches
679 MemberDef *bestMatch=0;
680 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
682 // search for the best match
683 DefinitionListIterator dli(*(DefinitionList*)di);
685 int minDistance=10000; // init at "infinite"
686 for (dli.toFirst();(d=dli.current());++dli) // foreach definition
688 // only look at members
689 if (d->definitionType()==Definition::TypeMember)
691 // that are also typedefs
692 MemberDef *md = (MemberDef *)d;
693 if (md->isTypedef()) // d is a typedef
695 // test accessibility of typedef within scope.
696 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
697 if (distance!=-1 && distance<minDistance)
698 // definition is accessible and a better match
700 minDistance=distance;
707 else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
709 Definition *d = (Definition*)di;
710 // that are also typedefs
711 MemberDef *md = (MemberDef *)di;
712 if (md->isTypedef()) // d is a typedef
714 // test accessibility of typedef within scope.
715 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
716 if (distance!=-1) // definition is accessible
724 result = bestMatch->typeString();
725 if (pTypeDef) *pTypeDef=bestMatch;
728 //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
729 // name.data(),result.data());
733 static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
737 SDict<Definition>::Iterator cli(*cl);
739 for (cli.toFirst();(cd=cli.current());++cli)
741 if (cd->localName()==localName)
750 /*! Starting with scope \a start, the string \a path is interpreted as
751 * a part of a qualified scope name (e.g. A::B::C), and the scope is
752 * searched. If found the scope definition is returned, otherwise 0
755 static const Definition *followPath(const Definition *start,const FileDef *fileScope,const QCString &path)
759 const Definition *current=start;
761 //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
762 // for each part of the explicit scope
763 while ((is=getScopeFragment(path,ps,&l))!=-1)
765 // try to resolve the part if it is a typedef
766 MemberDef *typeDef=0;
767 QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
768 //printf(" qualScopePart=%s\n",qualScopePart.data());
771 ClassDef *type = newResolveTypedef(fileScope,typeDef);
774 //printf("Found type %s\n",type->name().data());
778 const Definition *next = current->findInnerCompound(qualScopePart);
779 //printf("++ Looking for %s inside %s result %s\n",
780 // qualScopePart.data(),
781 // current->name().data(),
782 // next?next->name().data():"<null>");
783 if (next==0) // failed to follow the path
785 //printf("==> next==0!\n");
786 if (current->definitionType()==Definition::TypeNamespace)
788 next = endOfPathIsUsedClass(
789 ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
791 else if (current->definitionType()==Definition::TypeFile)
793 next = endOfPathIsUsedClass(
794 ((FileDef *)current)->getUsedClasses(),qualScopePart);
797 if (current==0) break;
799 else // continue to follow scope
802 //printf("==> current = %p\n",current);
806 //printf("followPath(start=%s,path=%s) result=%s\n",
807 // start->name().data(),path.data(),current?current->name().data():"<null>");
808 return current; // path could be followed
811 bool accessibleViaUsingClass(const SDict<Definition> *cl,
812 const FileDef *fileScope,
813 const Definition *item,
814 const QCString &explicitScopePart=""
817 //printf("accessibleViaUsingClass(%p)\n",cl);
818 if (cl) // see if the class was imported via a using statement
820 SDict<Definition>::Iterator cli(*cl);
822 bool explicitScopePartEmpty = explicitScopePart.isEmpty();
823 for (cli.toFirst();(ucd=cli.current());++cli)
825 //printf("Trying via used class %s\n",ucd->name().data());
826 const Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
827 if (sc && sc==item) return TRUE;
828 //printf("Try via used class done\n");
834 bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
835 const FileDef *fileScope,
836 const Definition *item,
837 const QCString &explicitScopePart="")
839 static QDict<void> visitedDict;
840 if (nl) // check used namespaces for the class
842 NamespaceSDict::Iterator nli(*nl);
845 for (nli.toFirst();(und=nli.current());++nli,count++)
847 //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
848 // count,nl->count());
849 const Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
850 if (sc && item->getOuterScope()==sc)
852 //printf("] found it\n");
855 QCString key=und->name();
856 if (und->getUsedNamespaces() && visitedDict.find(key)==0)
858 visitedDict.insert(key,(void *)0x08);
860 if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
862 //printf("] found it via recursion\n");
866 visitedDict.remove(key);
868 //printf("] Try via used namespace done\n");
874 const int MAX_STACK_SIZE = 1000;
876 /** Helper class representing the stack of items considered while resolving
882 AccessStack() : m_index(0) {}
883 void push(const Definition *scope,const FileDef *fileScope,const Definition *item)
885 if (m_index<MAX_STACK_SIZE)
887 m_elements[m_index].scope = scope;
888 m_elements[m_index].fileScope = fileScope;
889 m_elements[m_index].item = item;
893 void push(const Definition *scope,const FileDef *fileScope,const Definition *item,const QCString &expScope)
895 if (m_index<MAX_STACK_SIZE)
897 m_elements[m_index].scope = scope;
898 m_elements[m_index].fileScope = fileScope;
899 m_elements[m_index].item = item;
900 m_elements[m_index].expScope = expScope;
906 if (m_index>0) m_index--;
908 bool find(const Definition *scope,const FileDef *fileScope, const Definition *item)
911 for (i=0;i<m_index;i++)
913 AccessElem *e = &m_elements[i];
914 if (e->scope==scope && e->fileScope==fileScope && e->item==item)
921 bool find(const Definition *scope,const FileDef *fileScope, const Definition *item,const QCString &expScope)
924 for (i=0;i<m_index;i++)
926 AccessElem *e = &m_elements[i];
927 if (e->scope==scope && e->fileScope==fileScope && e->item==item && e->expScope==expScope)
936 /** Element in the stack. */
939 const Definition *scope;
940 const FileDef *fileScope;
941 const Definition *item;
945 AccessElem m_elements[MAX_STACK_SIZE];
948 /* Returns the "distance" (=number of levels up) from item to scope, or -1
949 * if item in not inside scope.
951 int isAccessibleFrom(const Definition *scope,const FileDef *fileScope,const Definition *item)
953 //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
954 // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
956 static AccessStack accessStack;
957 if (accessStack.find(scope,fileScope,item))
961 accessStack.push(scope,fileScope,item);
963 int result=0; // assume we found it
966 Definition *itemScope=item->getOuterScope();
967 bool memberAccessibleFromScope =
968 (item->definitionType()==Definition::TypeMember && // a member
969 itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
970 scope->definitionType()==Definition::TypeClass && // accessible
971 ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope
973 bool nestedClassInsideBaseClass =
974 (item->definitionType()==Definition::TypeClass && // a nested class
975 itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
976 scope->definitionType()==Definition::TypeClass && // class of scope
977 ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)
980 if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass)
982 //printf("> found it\n");
983 if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent
984 // this is preferred over nested class in this class
987 else if (scope==Doxygen::globalScope)
991 SDict<Definition> *cl = fileScope->getUsedClasses();
992 if (accessibleViaUsingClass(cl,fileScope,item))
994 //printf("> found via used class\n");
997 NamespaceSDict *nl = fileScope->getUsedNamespaces();
998 if (accessibleViaUsingNamespace(nl,fileScope,item))
1000 //printf("> found via used namespace\n");
1004 //printf("> reached global scope\n");
1005 result=-1; // not found in path to globalScope
1007 else // keep searching
1009 // check if scope is a namespace, which is using other classes and namespaces
1010 if (scope->definitionType()==Definition::TypeNamespace)
1012 NamespaceDef *nscope = (NamespaceDef*)scope;
1013 //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
1014 SDict<Definition> *cl = nscope->getUsedClasses();
1015 if (accessibleViaUsingClass(cl,fileScope,item))
1017 //printf("> found via used class\n");
1020 NamespaceSDict *nl = nscope->getUsedNamespaces();
1021 if (accessibleViaUsingNamespace(nl,fileScope,item))
1023 //printf("> found via used namespace\n");
1027 // repeat for the parent scope
1028 i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
1029 //printf("> result=%d\n",i);
1030 result= (i==-1) ? -1 : i+2;
1034 //Doxygen::lookupCache.insert(key,new int(result));
1039 /* Returns the "distance" (=number of levels up) from item to scope, or -1
1040 * if item in not in this scope. The explicitScopePart limits the search
1041 * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
1044 * class A { public: class I {}; };
1045 * class B { public: class J {}; };
1047 * - Looking for item=='J' inside scope=='B' will return 0.
1048 * - Looking for item=='I' inside scope=='B' will return -1
1049 * (as it is not found in B nor in the global scope).
1050 * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
1051 * not found and then A::I is searched in the global scope, which matches and
1052 * thus the result is 1.
1054 int isAccessibleFromWithExpScope(const Definition *scope,const FileDef *fileScope,
1055 const Definition *item,const QCString &explicitScopePart)
1057 if (explicitScopePart.isEmpty())
1059 // handle degenerate case where there is no explicit scope.
1060 return isAccessibleFrom(scope,fileScope,item);
1063 static AccessStack accessStack;
1064 if (accessStack.find(scope,fileScope,item,explicitScopePart))
1068 accessStack.push(scope,fileScope,item,explicitScopePart);
1071 //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
1072 // item?item->name().data():"<none>",
1073 // explicitScopePart.data());
1074 int result=0; // assume we found it
1075 const Definition *newScope = followPath(scope,fileScope,explicitScopePart);
1076 if (newScope) // explicitScope is inside scope => newScope is the result
1078 Definition *itemScope = item->getOuterScope();
1079 //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
1080 //if (newScope && newScope->definitionType()==Definition::TypeClass)
1082 // ClassDef *cd = (ClassDef *)newScope;
1083 // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
1085 if (itemScope==newScope) // exact match of scopes => distance==0
1087 //printf("> found it\n");
1089 else if (itemScope && newScope &&
1090 itemScope->definitionType()==Definition::TypeClass &&
1091 newScope->definitionType()==Definition::TypeClass &&
1092 ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
1095 // inheritance is also ok. Example: looking for B::I, where
1096 // class A { public: class I {} };
1097 // class B : public A {}
1098 // but looking for B::I, where
1099 // class A { public: class I {} };
1100 // class B { public: class I {} };
1101 // will find A::I, so we still prefer a direct match and give this one a distance of 1
1104 //printf("scope(%s) is base class of newScope(%s)\n",
1105 // scope->name().data(),newScope->name().data());
1110 if (newScope->definitionType()==Definition::TypeNamespace)
1112 g_visitedNamespaces.insert(newScope->name(),newScope);
1113 // this part deals with the case where item is a class
1114 // A::B::C but is explicit referenced as A::C, where B is imported
1115 // in A via a using directive.
1116 //printf("newScope is a namespace: %s!\n",newScope->name().data());
1117 NamespaceDef *nscope = (NamespaceDef*)newScope;
1118 SDict<Definition> *cl = nscope->getUsedClasses();
1121 SDict<Definition>::Iterator cli(*cl);
1123 for (cli.toFirst();(cd=cli.current());++cli)
1125 //printf("Trying for class %s\n",cd->name().data());
1128 //printf("> class is used in this scope\n");
1133 NamespaceSDict *nl = nscope->getUsedNamespaces();
1136 NamespaceSDict::Iterator nli(*nl);
1138 for (nli.toFirst();(nd=nli.current());++nli)
1140 if (g_visitedNamespaces.find(nd->name())==0)
1142 //printf("Trying for namespace %s\n",nd->name().data());
1143 i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
1146 //printf("> found via explicit scope of used namespace\n");
1153 // repeat for the parent scope
1154 if (scope!=Doxygen::globalScope)
1156 i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1157 item,explicitScopePart);
1159 //printf(" | result=%d\n",i);
1160 result = (i==-1) ? -1 : i+2;
1163 else // failed to resolve explicitScope
1165 //printf(" failed to resolve: scope=%s\n",scope->name().data());
1166 if (scope->definitionType()==Definition::TypeNamespace)
1168 NamespaceDef *nscope = (NamespaceDef*)scope;
1169 NamespaceSDict *nl = nscope->getUsedNamespaces();
1170 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1172 //printf("> found in used namespace\n");
1176 if (scope==Doxygen::globalScope)
1180 NamespaceSDict *nl = fileScope->getUsedNamespaces();
1181 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1183 //printf("> found in used namespace\n");
1187 //printf("> not found\n");
1190 else // continue by looking into the parent scope
1192 int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1193 item,explicitScopePart);
1194 //printf("> result=%d\n",i);
1195 result= (i==-1) ? -1 : i+2;
1200 //printf(" > result=%d\n",result);
1202 //Doxygen::lookupCache.insert(key,new int(result));
1206 int computeQualifiedIndex(const QCString &name)
1208 int i = name.find('<');
1209 return name.findRev("::",i==-1 ? name.length() : i);
1212 static void getResolvedSymbol(const Definition *scope,
1213 const FileDef *fileScope,
1215 const QCString &explicitScopePart,
1216 ArgumentList *actTemplParams,
1218 ClassDef *&bestMatch,
1219 MemberDef *&bestTypedef,
1220 QCString &bestTemplSpec,
1221 QCString &bestResolvedType
1224 //printf(" => found type %x name=%s d=%p\n",
1225 // d->definitionType(),d->name().data(),d);
1227 // only look at classes and members that are enums or typedefs
1228 if (d->definitionType()==Definition::TypeClass ||
1229 (d->definitionType()==Definition::TypeMember &&
1230 (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate())
1234 g_visitedNamespaces.clear();
1235 // test accessibility of definition within scope.
1236 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
1237 //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
1238 if (distance!=-1) // definition is accessible
1240 // see if we are dealing with a class or a typedef
1241 if (d->definitionType()==Definition::TypeClass) // d is a class
1243 ClassDef *cd = (ClassDef *)d;
1244 //printf("cd=%s\n",cd->name().data());
1245 if (!cd->isTemplateArgument()) // skip classes that
1246 // are only there to
1247 // represent a template
1250 //printf("is not a templ arg\n");
1251 if (distance<minDistance) // found a definition that is "closer"
1253 minDistance=distance;
1256 bestTemplSpec.resize(0);
1257 bestResolvedType = cd->qualifiedName();
1259 else if (distance==minDistance &&
1260 fileScope && bestMatch &&
1261 fileScope->getUsedNamespaces() &&
1262 d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
1263 bestMatch->getOuterScope()==Doxygen::globalScope
1266 // in case the distance is equal it could be that a class X
1267 // is defined in a namespace and in the global scope. When searched
1268 // in the global scope the distance is 0 in both cases. We have
1269 // to choose one of the definitions: we choose the one in the
1270 // namespace if the fileScope imports namespaces and the definition
1271 // found was in a namespace while the best match so far isn't.
1272 // Just a non-perfect heuristic but it could help in some situations
1273 // (kdecore code is an example).
1274 minDistance=distance;
1277 bestTemplSpec.resize(0);
1278 bestResolvedType = cd->qualifiedName();
1283 //printf(" is a template argument!\n");
1286 else if (d->definitionType()==Definition::TypeMember)
1288 MemberDef *md = (MemberDef *)d;
1289 //printf(" member isTypedef()=%d\n",md->isTypedef());
1290 if (md->isTypedef()) // d is a typedef
1292 QCString args=md->argsString();
1293 if (args.isEmpty()) // do not expand "typedef t a[4];"
1295 //printf(" found typedef!\n");
1297 // we found a symbol at this distance, but if it didn't
1298 // resolve to a class, we still have to make sure that
1299 // something at a greater distance does not match, since
1300 // that symbol is hidden by this one.
1301 if (distance<minDistance)
1305 minDistance=distance;
1306 MemberDef *enumType = 0;
1307 ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
1308 if (cd) // type resolves to a class
1310 //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
1313 bestTemplSpec = spec;
1314 bestResolvedType = type;
1316 else if (enumType) // type resolves to a enum
1318 //printf(" is enum\n");
1320 bestTypedef = enumType;
1322 bestResolvedType = enumType->qualifiedName();
1324 else if (md->isReference()) // external reference
1328 bestTemplSpec = spec;
1329 bestResolvedType = type;
1335 bestTemplSpec.resize(0);
1336 bestResolvedType.resize(0);
1337 //printf(" no match\n");
1342 //printf(" not the best match %d min=%d\n",distance,minDistance);
1347 //printf(" not a simple typedef\n")
1350 else if (md->isEnumerate())
1352 if (distance<minDistance)
1354 minDistance=distance;
1358 bestResolvedType = md->qualifiedName();
1362 } // if definition accessible
1365 //printf(" Not accessible!\n");
1367 } // if definition is a class or member
1368 //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
1371 /* Find the fully qualified class name referred to by the input class
1372 * or typedef name against the input scope.
1373 * Loops through scope and each of its parent scopes looking for a
1374 * match against the input name. Can recursively call itself when
1375 * resolving typedefs.
1377 static ClassDef *getResolvedClassRec(const Definition *scope,
1378 const FileDef *fileScope,
1380 MemberDef **pTypeDef,
1381 QCString *pTemplSpec,
1382 QCString *pResolvedType
1385 //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
1387 QCString explicitScopePart;
1388 QCString strippedTemplateParams;
1389 name=stripTemplateSpecifiersFromScope
1390 (removeRedundantWhiteSpace(n),TRUE,
1391 &strippedTemplateParams);
1392 ArgumentList actTemplParams;
1393 if (!strippedTemplateParams.isEmpty()) // template part that was stripped
1395 stringToArgumentList(strippedTemplateParams,&actTemplParams);
1398 int qualifierIndex = computeQualifiedIndex(name);
1399 //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
1400 if (qualifierIndex!=-1) // qualified name
1402 // split off the explicit scope part
1403 explicitScopePart=name.left(qualifierIndex);
1404 // todo: improve namespace alias substitution
1405 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
1406 name=name.mid(qualifierIndex+2);
1411 //printf("] empty name\n");
1412 return 0; // empty name
1415 //printf("Looking for symbol %s\n",name.data());
1416 DefinitionIntf *di = Doxygen::symbolMap->find(name);
1417 // the -g (for C# generics) and -p (for ObjC protocols) are now already
1418 // stripped from the key used in the symbolMap, so that is not needed here.
1421 //di = Doxygen::symbolMap->find(name+"-g");
1424 di = Doxygen::symbolMap->find(name+"-p");
1427 //printf("no such symbol!\n");
1432 //printf("found symbol!\n");
1434 bool hasUsingStatements =
1435 (fileScope && ((fileScope->getUsedNamespaces() &&
1436 fileScope->getUsedNamespaces()->count()>0) ||
1437 (fileScope->getUsedClasses() &&
1438 fileScope->getUsedClasses()->count()>0))
1440 //printf("hasUsingStatements=%d\n",hasUsingStatements);
1441 // Since it is often the case that the same name is searched in the same
1442 // scope over an over again (especially for the linked source code generation)
1443 // we use a cache to collect previous results. This is possible since the
1444 // result of a lookup is deterministic. As the key we use the concatenated
1445 // scope, the name to search for and the explicit scope prefix. The speedup
1446 // achieved by this simple cache can be enormous.
1447 int scopeNameLen = scope->name().length()+1;
1448 int nameLen = name.length()+1;
1449 int explicitPartLen = explicitScopePart.length();
1450 int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
1452 // below is a more efficient coding of
1453 // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
1454 QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
1455 char *p=key.rawData();
1456 qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
1458 qstrcpy(p,name); *(p+nameLen-1)='+';
1460 qstrcpy(p,explicitScopePart);
1463 // if a file scope is given and it contains using statements we should
1464 // also use the file part in the key (as a class name can be in
1465 // two different namespaces and a using statement in a file can select
1467 if (hasUsingStatements)
1469 // below is a more efficient coding of
1470 // key+="+"+fileScope->name();
1472 qstrcpy(p,fileScope->absFilePath());
1477 LookupInfo *pval=Doxygen::lookupCache->find(key);
1478 //printf("Searching for %s result=%p\n",key.data(),pval);
1481 //printf("LookupInfo %p %p '%s' %p\n",
1482 // pval->classDef, pval->typeDef, pval->templSpec.data(),
1483 // pval->resolvedType.data());
1484 if (pTemplSpec) *pTemplSpec=pval->templSpec;
1485 if (pTypeDef) *pTypeDef=pval->typeDef;
1486 if (pResolvedType) *pResolvedType=pval->resolvedType;
1487 //printf("] cachedMatch=%s\n",
1488 // pval->classDef?pval->classDef->name().data():"<none>");
1490 // printf("templSpec=%s\n",pTemplSpec->data());
1491 return pval->classDef;
1493 else // not found yet; we already add a 0 to avoid the possibility of
1494 // endless recursion.
1496 Doxygen::lookupCache->insert(key,new LookupInfo);
1499 ClassDef *bestMatch=0;
1500 MemberDef *bestTypedef=0;
1501 QCString bestTemplSpec;
1502 QCString bestResolvedType;
1503 int minDistance=10000; // init at "infinite"
1505 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
1507 //printf(" name is not unique\n");
1508 DefinitionListIterator dli(*(DefinitionList*)di);
1511 for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
1513 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1514 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1520 //printf(" name is unique\n");
1521 Definition *d = (Definition *)di;
1522 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1523 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1529 *pTypeDef = bestTypedef;
1533 *pTemplSpec = bestTemplSpec;
1537 *pResolvedType = bestResolvedType;
1539 //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
1540 // bestMatch,bestResolvedType.data());
1542 pval=Doxygen::lookupCache->find(key);
1545 pval->classDef = bestMatch;
1546 pval->typeDef = bestTypedef;
1547 pval->templSpec = bestTemplSpec;
1548 pval->resolvedType = bestResolvedType;
1552 Doxygen::lookupCache->insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
1554 //printf("] bestMatch=%s distance=%d\n",
1555 // bestMatch?bestMatch->name().data():"<none>",minDistance);
1557 // printf("templSpec=%s\n",pTemplSpec->data());
1561 /* Find the fully qualified class name referred to by the input class
1562 * or typedef name against the input scope.
1563 * Loops through scope and each of its parent scopes looking for a
1564 * match against the input name.
1566 ClassDef *getResolvedClass(const Definition *scope,
1567 const FileDef *fileScope,
1569 MemberDef **pTypeDef,
1570 QCString *pTemplSpec,
1571 bool mayBeUnlinkable,
1573 QCString *pResolvedType
1576 static bool optimizeOutputVhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1577 g_resolvedTypedefs.clear();
1579 (scope->definitionType()!=Definition::TypeClass &&
1580 scope->definitionType()!=Definition::TypeNamespace
1582 (scope->getLanguage()==SrcLangExt_Java && QCString(n).find("::")!=-1)
1585 scope=Doxygen::globalScope;
1587 //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
1588 // scope?scope->name().data():"<global>",
1589 // fileScope?fileScope->name().data():"<none>",
1594 if (optimizeOutputVhdl)
1596 result = getClass(n);
1600 result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
1602 if (result==0) // for nested classes imported via tag files, the scope may not
1603 // present, so we check the class name directly as well.
1604 // See also bug701314
1606 result = getClass(n);
1608 if (!mayBeUnlinkable && result && !result->isLinkable())
1610 if (!mayBeHidden || !result->isHidden())
1612 //printf("result was %s\n",result?result->name().data():"<none>");
1613 result=0; // don't link to artificial/hidden classes unless explicitly allowed
1616 //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
1617 // n,result?result->name().data():"<none>");
1621 //-------------------------------------------------------------------------
1622 //-------------------------------------------------------------------------
1623 //-------------------------------------------------------------------------
1624 //-------------------------------------------------------------------------
1626 static bool findOperator(const QCString &s,int i)
1628 int b = s.findRev("operator",i);
1629 if (b==-1) return FALSE; // not found
1631 while (b<i) // check if there are only spaces in between
1632 // the operator and the >
1634 if (!isspace((uchar)s.at(b))) return FALSE;
1640 static bool findOperator2(const QCString &s,int i)
1642 int b = s.findRev("operator",i);
1643 if (b==-1) return FALSE; // not found
1645 while (b<i) // check if there are only non-ascii
1646 // characters in front of the operator
1648 if (isId((uchar)s.at(b))) return FALSE;
1654 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
1655 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
1656 static const char operatorScope[] = { 'o', 'p', 'e', 'r', 'a', 't', 'o', 'r', '?', '?', '?' };
1658 struct CharAroundSpace
1662 charMap['('].before=FALSE;
1663 charMap['='].before=FALSE;
1664 charMap['&'].before=FALSE;
1665 charMap['*'].before=FALSE;
1666 charMap['['].before=FALSE;
1667 charMap['|'].before=FALSE;
1668 charMap['+'].before=FALSE;
1669 charMap[';'].before=FALSE;
1670 charMap[':'].before=FALSE;
1671 charMap['/'].before=FALSE;
1673 charMap['='].after=FALSE;
1674 charMap[' '].after=FALSE;
1675 charMap[']'].after=FALSE;
1676 charMap['\t'].after=FALSE;
1677 charMap['\n'].after=FALSE;
1678 charMap[')'].after=FALSE;
1679 charMap[','].after=FALSE;
1680 charMap['<'].after=FALSE;
1681 charMap['|'].after=FALSE;
1682 charMap['+'].after=FALSE;
1683 charMap['('].after=FALSE;
1684 charMap['/'].after=FALSE;
1688 CharElem() : before(TRUE), after(TRUE) {}
1693 CharElem charMap[256];
1696 static CharAroundSpace g_charAroundSpace;
1698 // Note: this function is not reentrant due to the use of static buffer!
1699 QCString removeRedundantWhiteSpace(const QCString &s)
1701 static bool cliSupport = Config_getBool(CPP_CLI_SUPPORT);
1702 static bool vhdl = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
1704 if (s.isEmpty() || vhdl) return s;
1706 // We use a static character array to
1707 // improve the performance of this function
1708 static char *growBuf = 0;
1709 static int growBufLen = 0;
1710 if (s.length()*3>growBufLen) // For input character we produce at most 3 output characters,
1712 growBufLen = s.length()*3;
1713 growBuf = (char *)realloc(growBuf,growBufLen+1); // add 1 for 0-terminator
1715 if (growBuf==0) return s; // should not happen, only we run out of memory
1717 char *src=s.rawData();
1727 // skip leading whitespace
1728 while (i<l && isspace((uchar)src[i]))
1735 char nc=i<l-1 ? src[i+1] : ' ';
1737 // search for "const"
1738 if (csp<6 && c==constScope[csp] && // character matches substring "const"
1739 (csp>0 || // inside search string
1740 i==0 || // if it is the first character
1741 !isId(pc) // the previous may not be a digit
1745 else // reset counter
1748 // search for "virtual"
1749 if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
1750 (vsp>0 || // inside search string
1751 i==0 || // if it is the first character
1752 !isId(pc) // the previous may not be a digit
1756 else // reset counter
1759 // search for "operator"
1760 if (osp<11 && (osp>=8 || c==operatorScope[osp]) && // character matches substring "operator" followed by 3 arbitrary characters
1761 (osp>0 || // inside search string
1762 i==0 || // if it is the first character
1763 !isId(pc) // the previous may not be a digit
1767 else // reset counter
1772 case '"': // quoted string
1777 for (;i<l;i++) // find end of string
1781 if (c=='\\' && i+1<l)
1796 case '<': // current char is a <
1799 (isId(nc)) && // next char is an id char
1800 (osp<8) // string in front is not "operator"
1803 *dst++=' '; // add extra space
1806 case '>': // current char is a >
1807 if (i>0 && !isspace((uchar)pc) &&
1808 (isId(pc) || pc=='*' || pc=='&' || pc=='.') && // prev char is an id char or space or *&.
1809 (osp<8 || (osp==8 && pc!='-')) // string in front is not "operator>" or "operator->"
1812 *dst++=' '; // add extra space in front
1815 if (i<l-1 && (nc=='-' || nc=='&')) // '>-' -> '> -'
1817 *dst++=' '; // add extra space after
1820 case ',': // current char is a ,
1822 if (i>0 && !isspace((uchar)pc) &&
1823 ((i<l-1 && (isId(nc) || nc=='[')) || // the [ is for attributes (see bug702170)
1824 (i<l-2 && nc=='$' && isId(src[i+2])) || // for PHP: ',$name' -> ', $name'
1825 (i<l-3 && nc=='&' && src[i+2]=='$' && isId(src[i+3])) // for PHP: ',&$name' -> ', &$name'
1829 *dst++=' '; // add extra space after
1832 case '^': // CLI 'Type^name' -> 'Type^ name'
1833 case '%': // CLI 'Type%name' -> 'Type% name'
1835 if (cliSupport && i<l-1 && (isId(nc) || nc=='-'))
1837 *dst++=' '; // add extra space after
1840 case ')': // current char is a ) -> ')name' -> ') name'
1842 if (i<l-1 && (isId(nc) || nc=='-'))
1844 *dst++=' '; // add extra space after
1848 if (i>0 && pc!=' ' && pc!='\t' && pc!=':' &&
1849 pc!='*' && pc!='&' && pc!='(' && pc!='/' &&
1850 pc!='.' && (osp<9 || !(pc=='>' && osp==11)))
1851 // avoid splitting &&, **, .*, operator*, operator->*
1858 if (i>0 && isId(pc))
1861 // avoid splitting operator&=
1868 case '@': // '@name' -> ' @name'
1869 case '$': // '$name' -> ' $name'
1870 case '\'': // ''name' -> '' name'
1871 if (i>0 && i<l-1 && pc!='=' && pc!=':' && !isspace(pc) &&
1872 isId(nc) && osp<8) // ")id" -> ") id"
1878 case ':': // current char is a :
1879 if (csp==6) // replace const::A by const ::A
1884 else if (vsp==8) // replace virtual::A by virtual ::A
1891 case ' ': // fallthrough
1892 case '\n': // fallthrough
1895 if (g_charAroundSpace.charMap[(uchar)pc].before &&
1896 g_charAroundSpace.charMap[(uchar)nc].after &&
1897 !(pc==',' && nc=='.') &&
1898 (osp<8 || (osp>=8 && pc!='"' && isId(nc)) || (osp>=8 && pc!='"' && nc!='"'))
1899 // e.g. 'operator >>' -> 'operator>>',
1900 // 'operator "" _x' -> 'operator""_x',
1901 // but not 'operator int' -> 'operatorint'
1906 else if ((pc=='*' || pc=='&' || pc=='.') && nc=='>')
1914 if (c=='t' && csp==5 && i<l-1 && // found 't' in 'const'
1915 !(isId(nc) || nc==')' || nc==',' || isspace(nc))
1916 ) // prevent const ::A from being converted to const::A
1921 else if (c=='l' && vsp==7 && i<l-1 && // found 'l' in 'virtual'
1922 !(isId(nc) || nc==')' || nc==',' || isspace(nc))
1923 ) // prevent virtual ::A from being converted to virtual::A
1933 //printf("removeRedundantWhitespace(%s)->%s\n",s.data(),growBuf);
1938 * Returns the position in the string where a function parameter list
1939 * begins, or -1 if one is not found.
1941 int findParameterList(const QCString &name)
1944 int templateDepth=0;
1947 if (templateDepth > 0)
1949 int nextOpenPos=name.findRev('>', pos);
1950 int nextClosePos=name.findRev('<', pos);
1951 if (nextOpenPos!=-1 && nextOpenPos>nextClosePos)
1956 else if (nextClosePos!=-1)
1961 else // more >'s than <'s, see bug701295
1968 int lastAnglePos=name.findRev('>', pos);
1969 int bracePos=name.findRev('(', pos);
1970 if (lastAnglePos!=-1 && lastAnglePos>bracePos)
1977 int bp = bracePos>0 ? name.findRev('(',bracePos-1) : -1;
1978 // bp test is to allow foo(int(&)[10]), but we need to make an exception for operator()
1979 return bp==-1 || (bp>=8 && name.mid(bp-8,10)=="operator()") ? bracePos : bp;
1986 bool rightScopeMatch(const QCString &scope, const QCString &name)
1988 int sl=scope.length();
1989 int nl=name.length();
1990 return (name==scope || // equal
1991 (scope.right(nl)==name && // substring
1992 sl-nl>1 && scope.at(sl-nl-1)==':' && scope.at(sl-nl-2)==':' // scope
1997 bool leftScopeMatch(const QCString &scope, const QCString &name)
1999 int sl=scope.length();
2000 int nl=name.length();
2001 return (name==scope || // equal
2002 (scope.left(nl)==name && // substring
2003 sl>nl+1 && scope.at(nl)==':' && scope.at(nl+1)==':' // scope
2009 void linkifyText(const TextGeneratorIntf &out, const Definition *scope,
2010 const FileDef *fileScope,const Definition *self,
2011 const char *text, bool autoBreak,bool external,
2012 bool keepSpaces,int indentLevel)
2014 //printf("linkify=`%s'\n",text);
2015 static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9$\\\\.:\\x80-\\xFF]*");
2016 static QRegExp regExpSplit("(?!:),");
2017 QCString txtStr=text;
2018 int strLen = txtStr.length();
2019 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d external=%d\n",
2020 // scope?scope->name().data():"<none>",
2021 // fileScope?fileScope->name().data():"<none>",
2022 // txtStr.data(),strLen,external);
2027 int floatingIndex=0;
2028 if (strLen==0) return;
2029 // read a word from the text string
2030 while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 &&
2031 (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
2034 // add non-word part to the result
2035 floatingIndex+=newIndex-skipIndex+matchLen;
2036 bool insideString=FALSE;
2038 for (i=index;i<newIndex;i++)
2040 if (txtStr.at(i)=='"') insideString=!insideString;
2043 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
2044 if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
2046 QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
2047 int splitLength = splitText.length();
2049 i=splitText.find(regExpSplit,0);
2050 if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
2051 if (i==-1) i=splitText.find('>');
2052 if (i==-1) i=splitText.find(' ');
2053 //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
2054 if (i!=-1) // add a link-break at i in case of Html output
2056 out.writeString(splitText.left(i+offset),keepSpaces);
2057 out.writeBreak(indentLevel==0 ? 0 : indentLevel+1);
2058 out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
2059 floatingIndex=splitLength-i-offset+matchLen;
2063 out.writeString(splitText,keepSpaces);
2068 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
2069 out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces);
2071 // get word from string
2072 QCString word=txtStr.mid(newIndex,matchLen);
2073 QCString matchWord = substitute(substitute(word,"\\","::"),".","::");
2074 //printf("linkifyText word=%s matchWord=%s scope=%s\n",
2075 // word.data(),matchWord.data(),scope?scope->name().data():"<none>");
2084 //printf("** Match word '%s'\n",matchWord.data());
2086 MemberDef *typeDef=0;
2087 cd=getResolvedClass(scope,fileScope,matchWord,&typeDef);
2088 if (typeDef) // First look at typedef then class, see bug 584184.
2090 //printf("Found typedef %s\n",typeDef->name().data());
2091 if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
2093 if (typeDef->getOuterScope()!=self)
2095 out.writeLink(typeDef->getReference(),
2096 typeDef->getOutputFileBase(),
2103 if (!found && (cd || (cd=getClass(matchWord))))
2105 //printf("Found class %s\n",cd->name().data());
2106 // add link to the result
2107 if (external ? cd->isLinkable() : cd->isLinkableInProject())
2111 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2116 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
2118 // add link to the result
2119 if (external ? cd->isLinkable() : cd->isLinkableInProject())
2123 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2128 // else if ((cd=getClass(matchWord+"-g"))) // C# generic as well
2130 // // add link to the result
2131 // if (external ? cd->isLinkable() : cd->isLinkableInProject())
2135 // out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
2142 //printf(" -> nothing\n");
2145 int m = matchWord.findRev("::");
2148 (scope->definitionType()==Definition::TypeClass ||
2149 scope->definitionType()==Definition::TypeNamespace
2153 scopeName=scope->name();
2157 scopeName = matchWord.left(m);
2158 matchWord = matchWord.mid(m+2);
2161 //printf("ScopeName=%s\n",scopeName.data());
2162 //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data());
2164 getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) &&
2165 //(md->isTypedef() || md->isEnumerate() ||
2166 // md->isReference() || md->isVariable()
2168 (external ? md->isLinkable() : md->isLinkableInProject())
2171 //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
2172 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
2173 // md->anchor(),word);
2174 if (md!=self && (self==0 || md->name()!=self->name()))
2175 // name check is needed for overloaded members, where getDefs just returns one
2177 /* in case of Fortran scop and the variable is a non Fortran variable: don't link,
2178 * see also getLink in fortrancode.l
2180 if (!(scope && (scope->getLanguage() == SrcLangExt_Fortran) && md->isVariable() && (md->getLanguage() != SrcLangExt_Fortran)))
2182 out.writeLink(md->getReference(),md->getOutputFileBase(),
2184 //printf("found symbol %s\n",matchWord.data());
2191 if (!found) // add word to the result
2193 out.writeString(word,keepSpaces);
2195 // set next start point in the string
2196 //printf("index=%d/%d\n",index,txtStr.length());
2197 skipIndex=index=newIndex+matchLen;
2199 // add last part of the string to the result.
2200 //ol.docify(txtStr.right(txtStr.length()-skipIndex));
2201 out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
2205 void writeExample(OutputList &ol,ExampleSDict *ed)
2207 QCString exampleLine=theTranslator->trWriteList(ed->count());
2209 //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
2210 //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
2211 //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
2212 QRegExp marker("@[0-9]+");
2213 int index=0,newIndex,matchLen;
2214 // now replace all markers in inheritLine with links to the classes
2215 while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
2218 ol.parseText(exampleLine.mid(index,newIndex-index));
2219 uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
2220 Example *e=ed->at(entryIndex);
2223 ol.pushGeneratorState();
2224 //if (latexEnabled) ol.disable(OutputGenerator::Latex);
2225 ol.disable(OutputGenerator::Latex);
2226 ol.disable(OutputGenerator::RTF);
2227 ol.disable(OutputGenerator::Docbook);
2228 // link for Html / man
2229 //printf("writeObjectLink(file=%s)\n",e->file.data());
2230 ol.writeObjectLink(0,e->file,e->anchor,e->name);
2231 ol.popGeneratorState();
2233 ol.pushGeneratorState();
2234 //if (latexEnabled) ol.enable(OutputGenerator::Latex);
2235 ol.disable(OutputGenerator::Man);
2236 ol.disable(OutputGenerator::Html);
2237 // link for Latex / pdf with anchor because the sources
2238 // are not hyperlinked (not possible with a verbatim environment).
2239 ol.writeObjectLink(0,e->file,0,e->name);
2240 //if (manEnabled) ol.enable(OutputGenerator::Man);
2241 //if (htmlEnabled) ol.enable(OutputGenerator::Html);
2242 ol.popGeneratorState();
2244 index=newIndex+matchLen;
2246 ol.parseText(exampleLine.right(exampleLine.length()-index));
2247 ol.writeString(".");
2251 QCString argListToString(ArgumentList *al,bool useCanonicalType,bool showDefVals)
2254 if (al==0) return result;
2255 ArgumentListIterator ali(*al);
2256 Argument *a=ali.current();
2260 QCString type1 = useCanonicalType && !a->canType.isEmpty() ?
2261 a->canType : a->type;
2263 int i=type1.find(")("); // hack to deal with function pointers
2267 type1=type1.left(i);
2269 if (!a->attrib.isEmpty())
2271 result+=a->attrib+" ";
2273 if (!a->name.isEmpty() || !a->array.isEmpty())
2275 result+= type1+" "+a->name+type2+a->array;
2279 result+= type1+type2;
2281 if (!a->defval.isEmpty() && showDefVals)
2283 result+="="+a->defval;
2287 if (a) result+=", ";
2290 if (al->constSpecifier) result+=" const";
2291 if (al->volatileSpecifier) result+=" volatile";
2292 if (al->refQualifier==RefQualifierLValue) result+=" &";
2293 else if (al->refQualifier==RefQualifierRValue) result+=" &&";
2294 if (!al->trailingReturnType.isEmpty()) result+=" -> "+al->trailingReturnType;
2295 if (al->pureSpecifier) result+=" =0";
2296 return removeRedundantWhiteSpace(result);
2299 QCString tempArgListToString(ArgumentList *al,SrcLangExt lang)
2302 if (al==0) return result;
2304 ArgumentListIterator ali(*al);
2305 Argument *a=ali.current();
2308 if (!a->name.isEmpty()) // add template argument name
2310 if (a->type.left(4)=="out") // C# covariance
2314 else if (a->type.left(3)=="in") // C# contravariance
2318 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
2320 result+=a->type+" ";
2324 else // extract name from type
2326 int i=a->type.length()-1;
2327 while (i>=0 && isId(a->type.at(i))) i--;
2330 result+=a->type.right(a->type.length()-i-1);
2331 if (a->type.find("...")!=-1)
2336 else // nothing found -> take whole name
2341 if (!a->typeConstraint.isEmpty() && lang==SrcLangExt_Java)
2343 result+=" extends "; // TODO: now Java specific, C# has where...
2344 result+=a->typeConstraint;
2348 if (a) result+=", ";
2351 return removeRedundantWhiteSpace(result);
2355 // compute the HTML anchors for a list of members
2356 void setAnchors(MemberList *ml)
2360 MemberListIterator mli(*ml);
2362 for (;(md=mli.current());++mli)
2364 if (!md->isReference())
2368 // anchor.sprintf("%c%d",id,count++);
2370 // anchor.sprintf("%c%d_%d",id,groupId,count++);
2371 //if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
2373 //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
2374 // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
2379 //----------------------------------------------------------------------------
2381 /*! takes the \a buf of the given length \a len and converts CR LF (DOS)
2382 * or CR (MAC) line ending to LF (Unix). Returns the length of the
2383 * converted content (i.e. the same as \a len (Unix, MAC) or
2386 int filterCRLF(char *buf,int len)
2388 int src = 0; // source index
2389 int dest = 0; // destination index
2390 char c; // current character
2394 c = buf[src++]; // Remember the processed character.
2395 if (c == '\r') // CR to be solved (MAC, DOS)
2397 c = '\n'; // each CR to LF
2398 if (src<len && buf[src] == '\n')
2399 ++src; // skip LF just after CR (DOS)
2401 else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
2403 c = ' '; // turn into a space
2405 buf[dest++] = c; // copy the (modified) character to dest
2407 return dest; // length of the valid part of the buf
2410 static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
2413 // compare the file name to the filter pattern list
2414 QStrListIterator sli(filterList);
2416 for (sli.toFirst(); (filterStr = sli.current()); ++sli)
2418 QCString fs = filterStr;
2419 int i_equals=fs.find('=');
2422 QCString filterPattern = fs.left(i_equals);
2423 QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE);
2424 if (fpat.match(name)!=-1)
2427 QCString filterName = fs.mid(i_equals+1);
2428 if (filterName.find(' ')!=-1)
2429 { // add quotes if the name has spaces
2430 filterName="\""+filterName+"\"";
2442 /*! looks for a filter for the file \a name. Returns the name of the filter
2443 * if there is a match for the file name, otherwise an empty string.
2444 * In case \a inSourceCode is TRUE then first the source filter list is
2447 QCString getFileFilter(const char* name,bool isSourceCode)
2450 if (name==0) return "";
2452 QStrList& filterSrcList = Config_getList(FILTER_SOURCE_PATTERNS);
2453 QStrList& filterList = Config_getList(FILTER_PATTERNS);
2455 QCString filterName;
2457 if (isSourceCode && !filterSrcList.isEmpty())
2458 { // first look for source filter pattern list
2459 filterName = getFilterFromList(name,filterSrcList,found);
2461 if (!found && filterName.isEmpty())
2462 { // then look for filter pattern list
2463 filterName = getFilterFromList(name,filterList,found);
2466 { // then use the generic input filter
2467 return Config_getString(INPUT_FILTER);
2471 /* remove surrounding double quotes */
2472 if ((filterName.right(1) == "\"") && (filterName.left(1) == "\""))
2474 filterName.remove(filterName.length() - 1, 1);
2475 filterName.remove(0, 1);
2482 QCString transcodeCharacterStringToUTF8(const QCString &input)
2485 static QCString inputEncoding = Config_getString(INPUT_ENCODING);
2486 const char *outputEncoding = "UTF-8";
2487 if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
2488 int inputSize=input.length();
2489 int outputSize=inputSize*4+1;
2490 QCString output(outputSize);
2491 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
2492 if (cd==(void *)(-1))
2494 err("unsupported character conversion: '%s'->'%s'\n",
2495 inputEncoding.data(),outputEncoding);
2500 size_t iLeft=inputSize;
2501 size_t oLeft=outputSize;
2502 char *inputPtr = input.rawData();
2503 char *outputPtr = output.rawData();
2504 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
2506 outputSize-=(int)oLeft;
2507 output.resize(outputSize+1);
2508 output.at(outputSize)='\0';
2509 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
2513 err("failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
2514 inputEncoding.data(),outputEncoding,input.data());
2518 portable_iconv_close(cd);
2519 return error ? input : output;
2522 /*! reads a file with name \a name and returns it as a string. If \a filter
2523 * is TRUE the file will be filtered by any user specified input filter.
2524 * If \a name is "-" the string will be read from standard input.
2526 QCString fileToString(const char *name,bool filter,bool isSourceCode)
2528 if (name==0 || name[0]==0) return 0;
2531 bool fileOpened=FALSE;
2532 if (name[0]=='-' && name[1]==0) // read from stdin
2534 fileOpened=f.open(IO_ReadOnly,stdin);
2537 const int bSize=4096;
2538 QCString contents(bSize);
2541 while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize)
2544 contents.resize(totalSize+bSize);
2546 totalSize = filterCRLF(contents.rawData(),totalSize+size)+2;
2547 contents.resize(totalSize);
2548 contents.at(totalSize-2)='\n'; // to help the scanner
2549 contents.at(totalSize-1)='\0';
2553 else // read from file
2556 if (!fi.exists() || !fi.isFile())
2558 err("file `%s' not found\n",name);
2561 BufStr buf(fi.size());
2562 fileOpened=readInputFile(name,buf,filter,isSourceCode);
2566 if (s>1 && buf.at(s-2)!='\n')
2576 err("cannot open file `%s' for reading\n",name);
2581 QCString dateToString(bool includeTime)
2583 QDateTime current = QDateTime::currentDateTime();
2584 QCString sourceDateEpoch = portable_getenv("SOURCE_DATE_EPOCH");
2585 if (!sourceDateEpoch.isEmpty())
2588 uint64 epoch = sourceDateEpoch.toUInt64(&ok);
2591 static bool warnedOnce=FALSE;
2594 warn_uncond("Environment variable SOURCE_DATE_EPOCH does not contain a valid number; value is '%s'\n",
2595 sourceDateEpoch.data());
2599 else if (epoch>UINT_MAX)
2601 static bool warnedOnce=FALSE;
2604 warn_uncond("Environment variable SOURCE_DATE_EPOCH must have a value smaller than or equal to %llu; actual value %llu\n",UINT_MAX,epoch);
2608 else // all ok, replace current time with epoch value
2610 current.setTimeUtc_t((ulong)epoch); // TODO: add support for 64bit epoch value
2613 return theTranslator->trDateTime(current.date().year(),
2614 current.date().month(),
2615 current.date().day(),
2616 current.date().dayOfWeek(),
2617 current.time().hour(),
2618 current.time().minute(),
2619 current.time().second(),
2623 QCString yearToString()
2625 const QDate &d=QDate::currentDate();
2627 result.sprintf("%d", d.year());
2631 //----------------------------------------------------------------------
2632 // recursive function that returns the number of branches in the
2633 // inheritance tree that the base class `bcd' is below the class `cd'
2635 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
2637 if (bcd->categoryOf()) // use class that is being extended in case of
2638 // an Objective-C category
2640 bcd=bcd->categoryOf();
2642 if (cd==bcd) return level;
2645 warn_uncond("class %s seem to have a recursive "
2646 "inheritance relation!\n",cd->name().data());
2649 int m=maxInheritanceDepth;
2650 if (cd->baseClasses())
2652 BaseClassListIterator bcli(*cd->baseClasses());
2654 for (;(bcdi=bcli.current());++bcli)
2656 int mc=minClassDistance(bcdi->classDef,bcd,level+1);
2664 Protection classInheritedProtectionLevel(ClassDef *cd,ClassDef *bcd,Protection prot,int level)
2666 if (bcd->categoryOf()) // use class that is being extended in case of
2667 // an Objective-C category
2669 bcd=bcd->categoryOf();
2677 err("Internal inconsistency: found class %s seem to have a recursive "
2678 "inheritance relation! Please send a bug report to doxygen@gmail.com\n",cd->name().data());
2680 else if (cd->baseClasses())
2682 BaseClassListIterator bcli(*cd->baseClasses());
2684 for (;(bcdi=bcli.current()) && prot!=Private;++bcli)
2686 Protection baseProt = classInheritedProtectionLevel(bcdi->classDef,bcd,bcdi->prot,level+1);
2687 if (baseProt==Private) prot=Private;
2688 else if (baseProt==Protected) prot=Protected;
2692 //printf("classInheritedProtectionLevel(%s,%s)=%d\n",cd->name().data(),bcd->name().data(),prot);
2696 //static void printArgList(ArgumentList *al)
2698 // if (al==0) return;
2699 // ArgumentListIterator ali(*al);
2702 // for (;(a=ali.current());++ali)
2704 // printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():"");
2710 // strip any template specifiers that follow className in string s
2711 static QCString trimTemplateSpecifiers(
2712 const QCString &namespaceName,
2713 const QCString &className,
2717 //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
2718 QCString scopeName=mergeScopes(namespaceName,className);
2719 ClassDef *cd=getClass(scopeName);
2720 if (cd==0) return s; // should not happen, but guard anyway.
2724 int i=className.length()-1;
2725 if (i>=0 && className.at(i)=='>') // template specialization
2727 // replace unspecialized occurrences in s, with their specialized versions.
2732 char c=className.at(i);
2733 if (c=='>') count++,i--;
2734 else if (c=='<') { count--; if (count==0) break; }
2737 QCString unspecClassName=className.left(i);
2740 while ((i=result.find(unspecClassName,p))!=-1)
2742 if (result.at(i+l)!='<') // unspecialized version
2744 result=result.left(i)+className+result.right(result.length()-i-l);
2751 //printf("result after specialization: %s\n",result.data());
2753 QCString qualName=cd->qualifiedNameWithTemplateParameters();
2754 //printf("QualifiedName = %s\n",qualName.data());
2755 // We strip the template arguments following className (if any)
2756 if (!qualName.isEmpty()) // there is a class name
2761 while ((is=getScopeFragment(qualName,ps,&l))!=-1)
2763 QCString qualNamePart = qualName.right(qualName.length()-is);
2764 //printf("qualNamePart=%s\n",qualNamePart.data());
2765 while ((i=result.find(qualNamePart,p))!=-1)
2767 int ql=qualNamePart.length();
2768 result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
2769 p=i+cd->name().length();
2774 //printf("result=%s\n",result.data());
2776 return result.stripWhiteSpace();
2780 * @param pattern pattern to look for
2781 * @param s string to search in
2782 * @param p position to start
2783 * @param len resulting pattern length
2784 * @returns position on which string is found, or -1 if not found
2786 static int findScopePattern(const QCString &pattern,const QCString &s,
2790 int pl=pattern.length();
2795 sp=p; // start of match
2796 int pp=0; // pattern position
2797 while (p<sl && pp<pl)
2799 if (s.at(p)=='<') // skip template arguments while matching
2802 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2806 if (s.at(p)=='<') bc++;
2807 else if (s.at(p)=='>')
2816 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2820 else if (s.at(p)==pattern.at(pp))
2822 //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
2828 //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
2833 if (pp==pl) // whole pattern matches
2842 static QCString trimScope(const QCString &name,const QCString &s)
2844 int scopeOffset=name.length();
2846 do // for each scope
2849 QCString scope=name.left(scopeOffset)+"::";
2850 //printf("Trying with scope=`%s'\n",scope.data());
2853 while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
2855 tmp+=result.mid(p,i-p); // add part before pattern
2858 tmp+=result.right(result.length()-p); // add trailing part
2860 scopeOffset=name.findRev("::",scopeOffset-1);
2862 } while (scopeOffset>0);
2863 //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
2868 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
2870 //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
2871 BaseClassListIterator bcli(*bcl);
2873 for (;(bcd=bcli.current());++bcli)
2875 ClassDef *cd=bcd->classDef;
2876 //printf("Trying class %s\n",cd->name().data());
2877 int spos=s.find(cd->name()+"::");
2880 s = s.left(spos)+s.right(
2881 s.length()-spos-cd->name().length()-2
2884 //printf("base class `%s'\n",cd->name().data());
2885 if (cd->baseClasses())
2886 trimBaseClassScope(cd->baseClasses(),s,level+1);
2891 /*! if either t1 or t2 contains a namespace scope, then remove that
2892 * scope. If neither or both have a namespace scope, t1 and t2 remain
2895 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
2901 int i1=p1==0 ? -1 : t1.findRev("::",p1);
2902 int i2=p2==0 ? -1 : t2.findRev("::",p2);
2903 if (i1==-1 && i2==-1)
2907 if (i1!=-1 && i2==-1) // only t1 has a scope
2909 QCString scope=t1.left(i1);
2910 replaceNamespaceAliases(scope,i1);
2912 int so=nsName.length();
2915 QCString fullScope=nsName.left(so);
2916 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2918 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2920 t1 = t1.right(t1.length()-i1-2);
2927 else if ((so=nsName.findRev("::",so-1))==-1)
2934 else if (i1==-1 && i2!=-1) // only t2 has a scope
2936 QCString scope=t2.left(i2);
2937 replaceNamespaceAliases(scope,i2);
2939 int so=nsName.length();
2942 QCString fullScope=nsName.left(so);
2943 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2945 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2947 t2 = t2.right(t2.length()-i2-2);
2954 else if ((so=nsName.findRev("::",so-1))==-1)
2967 static void stripIrrelevantString(QCString &target,const QCString &str)
2969 if (target==str) { target.resize(0); return; }
2973 while ((i=target.find(str,p))!=-1)
2975 bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
2976 (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
2979 int i1=target.find('*',i+l);
2980 int i2=target.find('&',i+l);
2981 if (i1==-1 && i2==-1)
2983 // strip str from target at index i
2984 target=target.left(i)+target.right(target.length()-i-l);
2988 else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
2990 // move str to front
2991 target=str+" "+target.left(i)+target.right(target.length()-i-l);
2998 if (changed) target=target.stripWhiteSpace();
3001 /*! According to the C++ spec and Ivan Vecerina:
3003 Parameter declarations that differ only in the presence or absence
3004 of const and/or volatile are equivalent.
3006 So the following example, show what is stripped by this routine
3007 for const. The same is done for volatile.
3009 For Java code we also strip the "final" keyword, see bug 765070.
3012 const T param -> T param // not relevant
3013 const T& param -> const T& param // const needed
3014 T* const param -> T* param // not relevant
3015 const T* param -> const T* param // const needed
3018 void stripIrrelevantConstVolatile(QCString &s)
3020 //printf("stripIrrelevantConstVolatile(%s)=",s.data());
3021 stripIrrelevantString(s,"const");
3022 stripIrrelevantString(s,"volatile");
3023 stripIrrelevantString(s,"final");
3024 //printf("%s\n",s.data());
3028 // a bit of debug support for matchArguments
3031 //#define MATCH printf("Match at line %d\n",__LINE__);
3032 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
3035 static bool matchArgument(const Argument *srcA,const Argument *dstA,
3036 const QCString &className,
3037 const QCString &namespaceName,
3038 NamespaceSDict *usingNamespaces,
3039 SDict<Definition> *usingClasses)
3041 //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
3042 // srcA->type.data(),srcA->name.data(),
3043 // dstA->type.data(),dstA->name.data(),
3047 // TODO: resolve any typedefs names that are part of srcA->type
3048 // before matching. This should use className and namespaceName
3049 // and usingNamespaces and usingClass to determine which typedefs
3050 // are in-scope, so it will not be very efficient :-(
3052 QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
3053 QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
3054 QCString srcAName=srcA->name.stripWhiteSpace();
3055 QCString dstAName=dstA->name.stripWhiteSpace();
3056 srcAType.stripPrefix("class ");
3057 dstAType.stripPrefix("class ");
3059 // allow distinguishing "const A" from "const B" even though
3060 // from a syntactic point of view they would be two names of the same
3061 // type "const". This is not fool prove of course, but should at least
3062 // catch the most common cases.
3063 if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
3068 if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
3073 if (srcAName=="const" || srcAName=="volatile")
3078 else if (dstA->name=="const" || dstA->name=="volatile")
3080 dstAType+=dstA->name;
3084 stripIrrelevantConstVolatile(srcAType);
3085 stripIrrelevantConstVolatile(dstAType);
3087 // strip typename keyword
3088 if (qstrncmp(srcAType,"typename ",9)==0)
3090 srcAType = srcAType.right(srcAType.length()-9);
3092 if (qstrncmp(dstAType,"typename ",9)==0)
3094 dstAType = dstAType.right(dstAType.length()-9);
3097 srcAType = removeRedundantWhiteSpace(srcAType);
3098 dstAType = removeRedundantWhiteSpace(dstAType);
3100 //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
3101 //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
3103 //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
3104 // dstAType.data(),dstAName.data());
3106 if (srcA->array!=dstA->array) // nomatch for char[] against char
3111 if (srcAType!=dstAType) // check if the argument only differs on name
3114 // remove a namespace scope that is only in one type
3115 // (assuming a using statement was used)
3116 //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
3117 //trimNamespaceScope(srcAType,dstAType,namespaceName);
3118 //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
3120 //QCString srcScope;
3121 //QCString dstScope;
3123 // strip redundant scope specifiers
3124 if (!className.isEmpty())
3126 srcAType=trimScope(className,srcAType);
3127 dstAType=trimScope(className,dstAType);
3128 //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
3130 if (!namespaceName.isEmpty())
3131 cd=getClass(namespaceName+"::"+className);
3133 cd=getClass(className);
3134 if (cd && cd->baseClasses())
3136 trimBaseClassScope(cd->baseClasses(),srcAType);
3137 trimBaseClassScope(cd->baseClasses(),dstAType);
3139 //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
3141 if (!namespaceName.isEmpty())
3143 srcAType=trimScope(namespaceName,srcAType);
3144 dstAType=trimScope(namespaceName,dstAType);
3146 //printf("#usingNamespace=%d\n",usingNamespaces->count());
3147 if (usingNamespaces && usingNamespaces->count()>0)
3149 NamespaceSDict::Iterator nli(*usingNamespaces);
3151 for (;(nd=nli.current());++nli)
3153 srcAType=trimScope(nd->name(),srcAType);
3154 dstAType=trimScope(nd->name(),dstAType);
3157 //printf("#usingClasses=%d\n",usingClasses->count());
3158 if (usingClasses && usingClasses->count()>0)
3160 SDict<Definition>::Iterator cli(*usingClasses);
3162 for (;(cd=cli.current());++cli)
3164 srcAType=trimScope(cd->name(),srcAType);
3165 dstAType=trimScope(cd->name(),dstAType);
3169 //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
3170 // dstAType.data(),dstAName.data());
3172 if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
3173 (srcAType+" "+srcAName)==dstAType)
3178 else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
3179 (dstAType+" "+dstAName)==srcAType)
3186 uint srcPos=0,dstPos=0;
3188 while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
3190 equal=srcAType.at(srcPos)==dstAType.at(dstPos);
3191 if (equal) srcPos++,dstPos++;
3193 uint srcATypeLen=srcAType.length();
3194 uint dstATypeLen=dstAType.length();
3195 if (srcPos<srcATypeLen && dstPos<dstATypeLen)
3197 // if nothing matches or the match ends in the middle or at the
3198 // end of a string then there is no match
3199 if (srcPos==0 || dstPos==0)
3204 if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
3206 //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
3207 // check if a name if already found -> if no then there is no match
3208 if (!srcAName.isEmpty() || !dstAName.isEmpty())
3214 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3215 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3216 if (srcPos<srcATypeLen ||
3217 dstPos<dstATypeLen ||
3218 (srcPos==srcATypeLen && dstPos==dstATypeLen)
3227 // otherwise we assume that a name starts at the current position.
3228 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
3229 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
3231 // if nothing more follows for both types then we assume we have
3232 // found a match. Note that now `signed int' and `signed' match, but
3233 // seeing that int is not a name can only be done by looking at the
3236 if (srcPos!=srcATypeLen || dstPos!=dstATypeLen)
3243 else if (dstPos<dstAType.length())
3245 if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
3247 if (!dstAName.isEmpty()) // dst has its name separated from its type
3252 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3253 if (dstPos!=dstAType.length())
3256 return FALSE; // more than a difference in name -> no match
3259 else // maybe dst has a name while src has not
3262 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
3263 if (dstPos!=dstAType.length() || !srcAName.isEmpty())
3266 return FALSE; // nope not a name -> no match
3270 else if (srcPos<srcAType.length())
3272 if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
3274 if (!srcAName.isEmpty()) // src has its name separated from its type
3279 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3280 if (srcPos!=srcAType.length())
3283 return FALSE; // more than a difference in name -> no match
3286 else // maybe src has a name while dst has not
3289 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
3290 if (srcPos!=srcAType.length() || !dstAName.isEmpty())
3293 return FALSE; // nope not a name -> no match
3304 * Matches the arguments list srcAl with the argument list dstAl
3305 * Returns TRUE if the argument lists are equal. Two argument list are
3306 * considered equal if the number of arguments is equal and the types of all
3307 * arguments are equal. Furthermore the const and volatile specifiers
3308 * stored in the list should be equal.
3310 bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
3311 const char *cl,const char *ns,bool checkCV,
3312 NamespaceSDict *usingNamespaces,
3313 SDict<Definition> *usingClasses)
3315 QCString className=cl;
3316 QCString namespaceName=ns;
3318 // strip template specialization from class name if present
3319 //int til=className.find('<'),tir=className.find('>');
3320 //if (til!=-1 && tir!=-1 && tir>til)
3322 // className=className.left(til)+className.right(className.length()-tir-1);
3325 //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
3326 // srcAl ? argListToString(srcAl).data() : "",
3327 // dstAl ? argListToString(dstAl).data() : "",
3329 // usingNamespaces?usingNamespaces->count():0,
3330 // usingClasses?usingClasses->count():0
3333 if (srcAl==0 || dstAl==0)
3335 bool match = srcAl==dstAl; // at least one of the members is not a function
3348 // handle special case with void argument
3349 if ( srcAl->count()==0 && dstAl->count()==1 &&
3350 dstAl->getFirst()->type=="void" )
3351 { // special case for finding match between func() and func(void)
3352 Argument *a=new Argument;
3358 if ( dstAl->count()==0 && srcAl->count()==1 &&
3359 srcAl->getFirst()->type=="void" )
3360 { // special case for finding match between func(void) and func()
3361 Argument *a=new Argument;
3368 if (srcAl->count() != dstAl->count())
3371 return FALSE; // different number of arguments -> no match
3376 if (srcAl->constSpecifier != dstAl->constSpecifier)
3379 return FALSE; // one member is const, the other not -> no match
3381 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3384 return FALSE; // one member is volatile, the other not -> no match
3388 if (srcAl->refQualifier != dstAl->refQualifier)
3391 return FALSE; // one member is has a different ref-qualifier than the other
3394 // so far the argument list could match, so we need to compare the types of
3396 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3397 Argument *srcA,*dstA;
3398 for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3400 if (!matchArgument(srcA,dstA,className,namespaceName,
3401 usingNamespaces,usingClasses))
3408 return TRUE; // all arguments match
3414 static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
3417 if (symbol->definitionType()==Definition::TypeMember &&
3418 ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
3422 ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
3425 return cd->qualifiedName()+templSpec;
3429 return md->qualifiedName();
3432 return symbol->qualifiedName();
3436 static QCString stripDeclKeywords(const QCString &s)
3438 int i=s.find(" class ");
3439 if (i!=-1) return s.left(i)+s.mid(i+6);
3440 i=s.find(" typename ");
3441 if (i!=-1) return s.left(i)+s.mid(i+9);
3442 i=s.find(" union ");
3443 if (i!=-1) return s.left(i)+s.mid(i+6);
3444 i=s.find(" struct ");
3445 if (i!=-1) return s.left(i)+s.mid(i+7);
3449 // forward decl for circular dependencies
3450 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
3452 QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
3455 QCString templSpec = spec.stripWhiteSpace();
3456 // this part had been commented out before... but it is needed to match for instance
3457 // std::list<std::string> against list<string> so it is now back again!
3458 if (!templSpec.isEmpty() && templSpec.at(0) == '<')
3460 templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
3462 QCString resolvedType = resolveTypeDef(d,templSpec);
3463 if (!resolvedType.isEmpty()) // not known as a typedef either
3465 templSpec = resolvedType;
3467 //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
3472 static QCString getCanonicalTypeForIdentifier(
3473 Definition *d,FileDef *fs,const QCString &word,
3474 QCString *tSpec,int count=0)
3476 if (count>10) return word; // oops recursion
3478 QCString symName,result,templSpec,tmpName;
3479 //DefinitionList *defList=0;
3480 if (tSpec && !tSpec->isEmpty())
3481 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
3483 if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
3485 symName=tmpName; // name without scope
3491 //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
3492 // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
3495 MemberDef *mType = 0;
3497 QCString resolvedType;
3499 // lookup class / class template instance
3500 cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
3501 bool isTemplInst = cd && !templSpec.isEmpty();
3502 if (!cd && !templSpec.isEmpty())
3504 // class template specialization not known, look up class template
3505 cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
3507 if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
3509 //printf("cd=%p mtype=%p\n",cd,mType);
3510 //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
3513 // cd?cd->name().data():"<none>",
3514 // d?d->name().data():"<none>",
3515 // fs?fs->name().data():"<none>",
3516 // cd?cd->isTemplate():-1
3519 //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
3520 // (word+templSpec).data(),
3521 // cd?cd->qualifiedName().data():"<none>",
3522 // templSpec.data(),ts.data(),
3523 // tSpec?tSpec->data():"<null>",
3524 // cd?cd->isTemplate():FALSE,
3525 // resolvedType.data());
3527 //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
3529 if (cd) // resolves to a known class type
3531 if (cd==d && tSpec) *tSpec="";
3533 if (mType && mType->isTypedef()) // but via a typedef
3535 result = resolvedType+ts; // the +ts was added for bug 685125
3541 // spec is already part of class type
3543 if (tSpec) *tSpec="";
3545 else if (!ts.isEmpty() && templSpec.isEmpty())
3547 // use formal template args for spec
3548 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
3551 result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
3553 if (cd->isTemplate() && tSpec) //
3555 if (!templSpec.isEmpty()) // specific instance
3557 result=cd->name()+templSpec;
3559 else // use template type
3561 result=cd->qualifiedNameWithTemplateParameters();
3563 // template class, so remove the template part (it is part of the class name)
3566 else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
3568 // obscure case, where a class is used as a template, but doxygen think it is
3569 // not (could happen when loading the class from a tag file).
3574 else if (mType && mType->isEnumerate()) // an enum
3576 result = mType->qualifiedName();
3578 else if (mType && mType->isTypedef()) // a typedef
3580 //result = mType->qualifiedName(); // changed after 1.7.2
3581 //result = mType->typeString();
3582 //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
3583 if (word!=mType->typeString())
3585 result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
3589 result = mType->typeString();
3594 resolvedType = resolveTypeDef(d,word);
3595 //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
3596 if (resolvedType.isEmpty()) // not known as a typedef either
3602 result = resolvedType;
3605 //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
3609 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
3611 type = type.stripWhiteSpace();
3613 // strip const and volatile keywords that are not relevant for the type
3614 stripIrrelevantConstVolatile(type);
3616 // strip leading keywords
3617 type.stripPrefix("class ");
3618 type.stripPrefix("struct ");
3619 type.stripPrefix("union ");
3620 type.stripPrefix("enum ");
3621 type.stripPrefix("typename ");
3623 type = removeRedundantWhiteSpace(type);
3624 //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
3625 // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
3627 //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
3630 QCString templSpec,word;
3632 while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
3633 // foreach identifier in the type
3635 //printf(" i=%d p=%d\n",i,p);
3636 if (i>pp) canType += type.mid(pp,i-pp);
3639 QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
3641 // in case the ct is empty it means that "word" represents scope "d"
3642 // and this does not need to be added to the canonical
3643 // type (it is redundant), so/ we skip it. This solves problem 589616.
3644 if (ct.isEmpty() && type.mid(p,2)=="::")
3652 //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
3653 // word.data(),templSpec.data(),canType.data(),ct.data());
3654 if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
3655 // (i.e. type is not a template specialization)
3656 // then resolve any identifiers inside.
3658 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
3660 // for each identifier template specifier
3661 //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
3662 while ((ti=re.match(templSpec,tp,&tl))!=-1)
3664 canType += templSpec.mid(tp,ti-tp);
3665 canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
3668 canType+=templSpec.right(templSpec.length()-tp);
3673 canType += type.right(type.length()-pp);
3674 //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
3676 return removeRedundantWhiteSpace(canType);
3679 static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
3681 QCString type = arg->type.stripWhiteSpace();
3682 QCString name = arg->name;
3683 //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
3684 if ((type=="const" || type=="volatile") && !name.isEmpty())
3685 { // name is part of type => correct
3689 if (name=="const" || name=="volatile")
3690 { // name is part of type => correct
3691 if (!type.isEmpty()) type+=" ";
3694 if (!arg->array.isEmpty())
3699 return extractCanonicalType(d,fs,type);
3702 static bool matchArgument2(
3703 Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
3704 Definition *dstScope,FileDef *dstFileScope,Argument *dstA
3707 //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
3708 // srcScope ? srcScope->name().data() : "",
3709 // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
3710 // dstScope ? dstScope->name().data() : "",
3711 // dstA->type.data(),dstA->name.data(),dstA->canType.data());
3713 //if (srcA->array!=dstA->array) // nomatch for char[] against char
3718 QCString sSrcName = " "+srcA->name;
3719 QCString sDstName = " "+dstA->name;
3720 QCString srcType = srcA->type;
3721 QCString dstType = dstA->type;
3722 stripIrrelevantConstVolatile(srcType);
3723 stripIrrelevantConstVolatile(dstType);
3724 //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
3725 //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
3726 if (sSrcName==dstType.right(sSrcName.length()))
3727 { // case "unsigned int" <-> "unsigned int i"
3728 srcA->type+=sSrcName;
3730 srcA->canType=""; // invalidate cached type value
3732 else if (sDstName==srcType.right(sDstName.length()))
3733 { // case "unsigned int i" <-> "unsigned int"
3734 dstA->type+=sDstName;
3736 dstA->canType=""; // invalidate cached type value
3739 if (srcA->canType.isEmpty())
3741 srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
3743 if (dstA->canType.isEmpty())
3745 dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
3748 if (srcA->canType==dstA->canType)
3755 //printf(" Canonical types do not match [%s]<->[%s]\n",
3756 // srcA->canType.data(),dstA->canType.data());
3763 // new algorithm for argument matching
3764 bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
3765 Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
3769 //printf("*** matchArguments2\n");
3770 ASSERT(srcScope!=0 && dstScope!=0);
3772 if (srcAl==0 || dstAl==0)
3774 bool match = srcAl==dstAl; // at least one of the members is not a function
3787 // handle special case with void argument
3788 if ( srcAl->count()==0 && dstAl->count()==1 &&
3789 dstAl->getFirst()->type=="void" )
3790 { // special case for finding match between func() and func(void)
3791 Argument *a=new Argument;
3797 if ( dstAl->count()==0 && srcAl->count()==1 &&
3798 srcAl->getFirst()->type=="void" )
3799 { // special case for finding match between func(void) and func()
3800 Argument *a=new Argument;
3807 if (srcAl->count() != dstAl->count())
3810 return FALSE; // different number of arguments -> no match
3815 if (srcAl->constSpecifier != dstAl->constSpecifier)
3818 return FALSE; // one member is const, the other not -> no match
3820 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3823 return FALSE; // one member is volatile, the other not -> no match
3827 if (srcAl->refQualifier != dstAl->refQualifier)
3830 return FALSE; // one member is has a different ref-qualifier than the other
3833 // so far the argument list could match, so we need to compare the types of
3835 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3836 Argument *srcA,*dstA;
3837 for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3839 if (!matchArgument2(srcScope,srcFileScope,srcA,
3840 dstScope,dstFileScope,dstA)
3848 return TRUE; // all arguments match
3853 // merges the initializer of two argument lists
3854 // pre: the types of the arguments in the list should match.
3855 void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
3857 //printf("mergeArguments `%s', `%s'\n",
3858 // argListToString(srcAl).data(),argListToString(dstAl).data());
3860 if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
3862 return; // invalid argument lists -> do not merge
3865 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3866 Argument *srcA,*dstA;
3867 for (;(srcA=srcAli.current()) && (dstA=dstAli.current());++srcAli,++dstAli)
3869 if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
3871 //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
3872 srcA->defval=dstA->defval.copy();
3874 else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
3876 //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
3877 dstA->defval=srcA->defval.copy();
3880 // fix wrongly detected const or volatile specifiers before merging.
3881 // example: "const A *const" is detected as type="const A *" name="const"
3882 if (srcA->name=="const" || srcA->name=="volatile")
3884 srcA->type+=" "+srcA->name;
3885 srcA->name.resize(0);
3887 if (dstA->name=="const" || dstA->name=="volatile")
3889 dstA->type+=" "+dstA->name;
3890 dstA->name.resize(0);
3893 if (srcA->type==dstA->type)
3895 //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3896 if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3898 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3899 //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
3900 srcA->type = dstA->type.copy();
3901 srcA->name = dstA->name.copy();
3903 else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
3905 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3906 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3907 dstA->type = srcA->type.copy();
3908 dstA->name = dstA->name.copy();
3910 else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
3912 //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
3913 if (forceNameOverwrite)
3915 srcA->name = dstA->name;
3919 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3921 srcA->name = dstA->name;
3923 else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
3925 dstA->name = srcA->name;
3932 //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3933 srcA->type=srcA->type.stripWhiteSpace();
3934 dstA->type=dstA->type.stripWhiteSpace();
3935 if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
3937 srcA->type+=" "+srcA->name;
3938 srcA->name=dstA->name;
3940 else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
3942 dstA->type+=" "+dstA->name;
3943 dstA->name=srcA->name;
3945 else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3947 srcA->name = dstA->name;
3949 else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
3951 dstA->name = srcA->name;
3954 int i1=srcA->type.find("::"),
3955 i2=dstA->type.find("::"),
3956 j1=srcA->type.length()-i1-2,
3957 j2=dstA->type.length()-i2-2;
3958 if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
3960 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3961 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3962 dstA->type = srcA->type.left(i1+2)+dstA->type;
3963 dstA->name = dstA->name.copy();
3965 else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
3967 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3968 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3969 srcA->type = dstA->type.left(i2+2)+srcA->type;
3970 srcA->name = dstA->name.copy();
3972 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3974 srcA->docs = dstA->docs.copy();
3976 else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
3978 dstA->docs = srcA->docs.copy();
3980 //printf("Merge argument `%s|%s' `%s|%s'\n",
3981 // srcA->type.data(),srcA->name.data(),
3982 // dstA->type.data(),dstA->name.data());
3986 static void findMembersWithSpecificName(MemberName *mn,
3989 FileDef *currentFile,
3991 const char *forceTagFile,
3992 QList<MemberDef> &members)
3994 //printf(" Function with global scope name `%s' args=`%s'\n",
3995 // mn->memberName(),args);
3996 MemberNameIterator mli(*mn);
3998 for (mli.toFirst();(md=mli.current());++mli)
4000 FileDef *fd=md->getFileDef();
4001 GroupDef *gd=md->getGroupDef();
4002 //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p current=%p ref=%s\n",
4003 // md->name().data(),args,fd,gd,currentFile,md->getReference().data());
4005 ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
4006 md->getNamespaceDef()==0 && md->isLinkable() &&
4007 (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
4008 currentFile==0 || fd==currentFile) // statics must appear in the same file
4012 ArgumentList *argList=0;
4013 if (args && !md->isDefine() && qstrcmp(args,"()")!=0)
4015 argList=new ArgumentList;
4016 ArgumentList *mdAl = md->argumentList();
4017 stringToArgumentList(args,argList);
4018 match=matchArguments2(
4019 md->getOuterScope(),fd,mdAl,
4020 Doxygen::globalScope,fd,argList,
4022 delete argList; argList=0;
4024 if (match && (forceTagFile==0 || md->getReference()==forceTagFile))
4026 //printf("Found match!\n");
4034 * Searches for a member definition given its name `memberName' as a string.
4035 * memberName may also include a (partial) scope to indicate the scope
4036 * in which the member is located.
4038 * The parameter `scName' is a string representing the name of the scope in
4039 * which the link was found.
4041 * In case of a function args contains a string representation of the
4042 * argument list. Passing 0 means the member has no arguments.
4043 * Passing "()" means any argument list will do, but "()" is preferred.
4045 * The function returns TRUE if the member is known and documented or
4046 * FALSE if it is not.
4047 * If TRUE is returned parameter `md' contains a pointer to the member
4048 * definition. Furthermore exactly one of the parameter `cd', `nd', or `fd'
4050 * - if `cd' is non zero, the member was found in a class pointed to by cd.
4051 * - if `nd' is non zero, the member was found in a namespace pointed to by nd.
4052 * - if `fd' is non zero, the member was found in the global namespace of
4055 bool getDefs(const QCString &scName,
4056 const QCString &mbName,
4063 bool forceEmptyScope,
4064 FileDef *currentFile,
4066 const char *forceTagFile
4069 fd=0, md=0, cd=0, nd=0, gd=0;
4070 if (mbName.isEmpty()) return FALSE; /* empty name => nothing to link */
4072 QCString scopeName=scName;
4073 QCString memberName=mbName;
4074 scopeName = substitute(scopeName,"\\","::"); // for PHP
4075 memberName = substitute(memberName,"\\","::"); // for PHP
4076 //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
4077 // memberName.data(),args,scopeName.data(),forceEmptyScope);
4080 // strip common part of the scope from the scopeName
4081 while ((is=scopeName.findRev("::"))!=-1 &&
4082 (im=memberName.find("::",pm))!=-1 &&
4083 (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
4086 scopeName=scopeName.left(is);
4089 //printf("result after scope corrections scope=%s name=%s\n",
4090 // scopeName.data(),memberName.data());
4092 QCString mName=memberName;
4094 if (memberName.left(9)!="operator " && // treat operator conversion methods
4095 // as a special case
4096 (im=memberName.findRev("::"))!=-1 &&
4097 im<(int)memberName.length()-2 // not A::
4100 mScope=memberName.left(im);
4101 mName=memberName.right(memberName.length()-im-2);
4104 // handle special the case where both scope name and member scope are equal
4105 if (mScope==scopeName) scopeName.resize(0);
4107 //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
4109 MemberName *mn = Doxygen::memberNameSDict->find(mName);
4110 //printf("mName=%s mn=%p\n",mName.data(),mn);
4112 if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
4113 mn && !(scopeName.isEmpty() && mScope.isEmpty()))
4115 //printf(" >member name '%s' found\n",mName.data());
4116 int scopeOffset=scopeName.length();
4119 QCString className = scopeName.left(scopeOffset);
4120 if (!className.isEmpty() && !mScope.isEmpty())
4122 className+="::"+mScope;
4124 else if (!mScope.isEmpty())
4130 ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className,&tmd);
4131 if (fcd==0 && className.find('<')!=-1) // try without template specifiers as well
4133 QCString nameWithoutTemplates = stripTemplateSpecifiersFromScope(className,FALSE);
4134 fcd=getResolvedClass(Doxygen::globalScope,0,nameWithoutTemplates,&tmd);
4136 //printf("Trying class scope %s: fcd=%p tmd=%p\n",className.data(),fcd,tmd);
4137 // todo: fill in correct fileScope!
4138 if (fcd && // is it a documented class
4142 //printf(" Found fcd=%p\n",fcd);
4143 MemberNameIterator mmli(*mn);
4145 int mdist=maxInheritanceDepth;
4146 ArgumentList *argList=0;
4149 argList=new ArgumentList;
4150 stringToArgumentList(args,argList);
4152 for (mmli.toFirst();(mmd=mmli.current());++mmli)
4154 if (!mmd->isStrongEnumValue())
4156 ArgumentList *mmdAl = mmd->argumentList();
4157 bool match=args==0 ||
4158 matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4159 fcd,fcd->getFileDef(),argList,
4162 //printf("match=%d\n",match);
4165 ClassDef *mcd=mmd->getClassDef();
4168 int m=minClassDistance(fcd,mcd);
4169 if (m<mdist && mcd->isLinkable())
4181 delete argList; argList=0;
4183 if (mdist==maxInheritanceDepth && args && qstrcmp(args,"()")==0)
4184 // no exact match found, but if args="()" an arbitrary member will do
4186 //printf(" >Searching for arbitrary member\n");
4187 for (mmli.toFirst();(mmd=mmli.current());++mmli)
4189 //if (mmd->isLinkable())
4191 ClassDef *mcd=mmd->getClassDef();
4192 //printf(" >Class %s found\n",mcd->name().data());
4195 int m=minClassDistance(fcd,mcd);
4196 if (m<mdist /* && mcd->isLinkable()*/ )
4198 //printf("Class distance %d\n",m);
4207 //printf(" >Success=%d\n",mdist<maxInheritanceDepth);
4208 if (mdist<maxInheritanceDepth)
4210 if (!md->isLinkable() || md->isStrongEnumValue())
4212 md=0; // avoid returning things we cannot link to
4214 return FALSE; // match found, but was not linkable
4218 gd=md->getGroupDef();
4220 return TRUE; /* found match */
4224 if (tmd && tmd->isEnumerate() && tmd->isStrong()) // scoped enum
4226 //printf("Found scoped enum!\n");
4227 MemberList *tml = tmd->enumFieldList();
4230 MemberListIterator tmi(*tml);
4232 for (;(emd=tmi.current());++tmi)
4234 if (emd->localName()==mName)
4236 if (emd->isLinkable())
4238 cd=tmd->getClassDef();
4252 /* go to the parent scope */
4257 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4261 } while (scopeOffset>=0);
4264 if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
4266 //printf("Global symbol\n");
4267 MemberNameIterator mmli(*mn);
4268 MemberDef *mmd, *fuzzy_mmd = 0;
4269 ArgumentList *argList = 0;
4270 bool hasEmptyArgs = args && qstrcmp(args, "()") == 0;
4273 stringToArgumentList(args, argList = new ArgumentList);
4275 for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
4277 if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
4278 !mmd->getClassDef())
4283 ArgumentList *mmdAl = mmd->argumentList();
4284 if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4285 Doxygen::globalScope,mmd->getFileDef(),argList,
4290 if (!fuzzy_mmd && hasEmptyArgs)
4294 if (argList) delete argList, argList = 0;
4296 mmd = mmd ? mmd : fuzzy_mmd;
4298 if (mmd && !mmd->isStrongEnumValue())
4301 cd = mmd->getClassDef();
4307 // maybe an namespace, file or group member ?
4308 //printf("Testing for global symbol scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
4309 // scopeName.data(),mScope.data(),mName.data());
4310 if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
4312 //printf(" >symbol name found\n");
4313 NamespaceDef *fnd=0;
4314 int scopeOffset=scopeName.length();
4317 QCString namespaceName = scopeName.left(scopeOffset);
4318 if (!namespaceName.isEmpty() && !mScope.isEmpty())
4320 namespaceName+="::"+mScope;
4322 else if (!mScope.isEmpty())
4324 namespaceName=mScope.copy();
4326 //printf("Trying namespace %s\n",namespaceName.data());
4327 if (!namespaceName.isEmpty() &&
4328 (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
4332 //printf("Symbol inside existing namespace `%s' count=%d\n",
4333 // namespaceName.data(),mn->count());
4335 MemberNameIterator mmli(*mn);
4337 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4339 //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
4340 // mmd->getNamespaceDef(),fnd);
4341 MemberDef *emd = mmd->getEnumScope();
4342 if (emd && emd->isStrong())
4344 //printf("yes match %s<->%s!\n",mScope.data(),emd->localName().data());
4345 if (emd->getNamespaceDef()==fnd &&
4346 rightScopeMatch(mScope,emd->localName()))
4348 //printf("found it!\n");
4360 else if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
4361 { // namespace is found
4363 ArgumentList *argList=0;
4364 if (args && qstrcmp(args,"()")!=0)
4366 argList=new ArgumentList;
4367 ArgumentList *mmdAl = mmd->argumentList();
4368 stringToArgumentList(args,argList);
4369 match=matchArguments2(
4370 mmd->getOuterScope(),mmd->getFileDef(),mmdAl,
4371 fnd,mmd->getFileDef(),argList,
4382 delete argList; argList=0;
4386 if (!found && args && !qstrcmp(args,"()"))
4387 // no exact match found, but if args="()" an arbitrary
4390 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
4392 if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
4402 if (!md->isLinkable())
4404 md=0; // avoid returning things we cannot link to
4406 return FALSE; // match found but not linkable
4410 gd=md->getGroupDef();
4411 if (gd && gd->isLinkable()) nd=0; else gd=0;
4418 //printf("not a namespace\n");
4419 MemberNameIterator mmli(*mn);
4421 for (mmli.toFirst();(mmd=mmli.current());++mmli)
4423 MemberDef *tmd = mmd->getEnumScope();
4424 //printf("try member %s tmd=%s\n",mmd->name().data(),tmd?tmd->name().data():"<none>");
4425 int ni=namespaceName.findRev("::");
4426 //printf("namespaceName=%s ni=%d\n",namespaceName.data(),ni);
4427 bool notInNS = tmd && ni==-1 && tmd->getNamespaceDef()==0 && (mScope.isEmpty() || mScope==tmd->name());
4428 bool sameNS = tmd && tmd->getNamespaceDef() && namespaceName.left(ni)==tmd->getNamespaceDef()->name();
4429 //printf("notInNS=%d sameNS=%d\n",notInNS,sameNS);
4430 if (tmd && tmd->isStrong() && // C++11 enum class
4431 (notInNS || sameNS) &&
4432 namespaceName.length()>0 // enum is part of namespace so this should not be empty
4436 fd=mmd->getFileDef();
4437 gd=mmd->getGroupDef();
4438 if (gd && gd->isLinkable()) fd=0; else gd=0;
4439 //printf("Found scoped enum %s fd=%p gd=%p\n",
4440 // mmd->name().data(),fd,gd);
4449 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4453 } while (scopeOffset>=0);
4455 //else // no scope => global function
4457 QList<MemberDef> members;
4458 // search for matches with strict static checking
4459 findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,forceTagFile,members);
4460 if (members.count()==0) // nothing found
4462 // search again without strict static checking
4463 findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,forceTagFile,members);
4465 //printf("found %d members\n",members.count());
4466 if (members.count()!=1 && args && !qstrcmp(args,"()"))
4468 // no exact match found, but if args="()" an arbitrary
4470 MemberNameIterator mni(*mn);
4471 for (mni.toLast();(md=mni.current());--mni)
4473 //printf("Found member `%s'\n",md->name().data());
4474 //printf("member is linkable md->name()=`%s'\n",md->name().data());
4475 fd=md->getFileDef();
4476 gd=md->getGroupDef();
4477 MemberDef *tmd = md->getEnumScope();
4479 (gd && gd->isLinkable()) || (fd && fd->isLinkable()) ||
4480 (tmd && tmd->isStrong())
4487 //printf("found %d candidate members\n",members.count());
4488 if (members.count()>0) // at least one match
4492 //printf("multiple results; pick one from file:%s\n", currentFile->name().data());
4493 QListIterator<MemberDef> mit(members);
4494 for (mit.toFirst();(md=mit.current());++mit)
4496 if (md->getFileDef() && md->getFileDef()->name() == currentFile->name())
4498 break; // found match in the current file
4501 if (!md) // member not in the current file
4503 md=members.getLast();
4508 md=members.getLast();
4511 if (md && (md->getEnumScope()==0 || !md->getEnumScope()->isStrong()))
4512 // found a matching global member, that is not a scoped enum value (or uniquely matches)
4514 fd=md->getFileDef();
4515 gd=md->getGroupDef();
4516 //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
4517 if (gd && gd->isLinkable()) fd=0; else gd=0;
4528 * Searches for a scope definition given its name as a string via parameter
4531 * The parameter `docScope` is a string representing the name of the scope in
4532 * which the `scope` string was found.
4534 * The function returns TRUE if the scope is known and documented or
4535 * FALSE if it is not.
4536 * If TRUE is returned exactly one of the parameter `cd`, `nd`
4538 * - if `cd` is non zero, the scope was a class pointed to by cd.
4539 * - if `nd` is non zero, the scope was a namespace pointed to by nd.
4541 static bool getScopeDefs(const char *docScope,const char *scope,
4542 ClassDef *&cd, NamespaceDef *&nd)
4546 QCString scopeName=scope;
4547 //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
4548 if (scopeName.isEmpty()) return FALSE;
4550 bool explicitGlobalScope=FALSE;
4551 if (scopeName.at(0)==':' && scopeName.at(1)==':')
4553 scopeName=scopeName.right(scopeName.length()-2);
4554 explicitGlobalScope=TRUE;
4557 QCString docScopeName=docScope;
4558 int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
4560 do // for each possible docScope (from largest to and including empty)
4562 QCString fullName=scopeName.copy();
4563 if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
4565 if (((cd=getClass(fullName)) || // normal class
4566 (cd=getClass(fullName+"-p")) //|| // ObjC protocol
4567 //(cd=getClass(fullName+"-g")) // C# generic
4568 ) && cd->isLinkable())
4570 return TRUE; // class link written => quit
4572 else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
4574 return TRUE; // namespace link written => quit
4580 else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
4584 } while (scopeOffset>=0);
4589 static bool isLowerCase(QCString &s)
4591 uchar *p=(uchar*)s.data();
4592 if (p==0) return TRUE;
4594 while ((c=*p++)) if (!islower(c)) return FALSE;
4598 /*! Returns an object to reference to given its name and context
4599 * @post return value TRUE implies *resContext!=0 or *resMember!=0
4601 bool resolveRef(/* in */ const char *scName,
4602 /* in */ const char *name,
4603 /* in */ bool inSeeBlock,
4604 /* out */ Definition **resContext,
4605 /* out */ MemberDef **resMember,
4606 bool lookForSpecialization,
4607 FileDef *currentFile,
4611 //printf("resolveRef(scope=%s,name=%s,inSeeBlock=%d)\n",scName,name,inSeeBlock);
4612 QCString tsName = name;
4613 //bool memberScopeFirst = tsName.find('#')!=-1;
4614 QCString fullName = substitute(tsName,"#","::");
4615 if (fullName.find("anonymous_namespace{")==-1)
4617 fullName = removeRedundantWhiteSpace(substitute(fullName,".","::",3));
4621 fullName = removeRedundantWhiteSpace(fullName);
4624 int bracePos=findParameterList(fullName);
4625 int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
4626 int scopePos=fullName.findRev("::",endNamePos);
4627 bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
4628 (scopePos>2 || // ::N::A
4629 tsName.left(2)=="::" || // ::foo in local scope
4630 scName==0 // #foo in global scope
4633 // default result values
4637 if (bracePos==-1) // simple name
4642 // the following if() was commented out for releases in the range
4643 // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
4644 if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
4645 { // link to lower case only name => do not try to autolink
4649 //printf("scName=%s fullName=%s\n",scName,fullName.data());
4651 // check if this is a class or namespace reference
4652 if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
4654 if (cd) // scope matches that of a class
4658 else // scope matches that of a namespace
4665 else if (scName==fullName || (!inSeeBlock && scopePos==-1))
4666 // nothing to link => output plain text
4668 //printf("found scName=%s fullName=%s scName==fullName=%d "
4669 // "inSeeBlock=%d scopePos=%d!\n",
4670 // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
4673 // continue search...
4676 // extract userscope+name
4677 QCString nameStr=fullName.left(endNamePos);
4678 if (explicitScope) nameStr=nameStr.mid(2);
4680 // extract arguments
4682 if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
4684 // strip template specifier
4685 // TODO: match against the correct partial template instantiation
4686 int templPos=nameStr.find('<');
4687 bool tryUnspecializedVersion = FALSE;
4688 if (templPos!=-1 && nameStr.find("operator")==-1)
4690 int endTemplPos=nameStr.findRev('>');
4691 if (endTemplPos!=-1)
4693 if (!lookForSpecialization)
4695 nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
4699 tryUnspecializedVersion = TRUE;
4704 QCString scopeStr=scName;
4709 NamespaceDef *nd = 0;
4712 // check if nameStr is a member or global.
4713 //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
4714 // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
4715 if (getDefs(scopeStr,nameStr,argsStr,
4717 //scopePos==0 && !memberScopeFirst, // forceEmptyScope
4718 explicitScope, // replaces prev line due to bug 600829
4724 //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
4725 if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
4726 !md->isStrongEnumValue() &&
4727 (!scopeStr.isEmpty() || nameStr.find("::")>0))
4729 // we did find a member, but it is a global one while we were explicitly
4730 // looking for a scoped variable. See bug 616387 for an example why this check is needed.
4731 // note we do need to support autolinking to "::symbol" hence the >0
4732 //printf("not global member!\n");
4737 //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
4738 if (md) { *resMember=md; *resContext=md; }
4739 else if (cd) *resContext=cd;
4740 else if (nd) *resContext=nd;
4741 else if (fd) *resContext=fd;
4742 else if (gd) *resContext=gd;
4743 else { *resContext=0; *resMember=0; return FALSE; }
4744 //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
4745 // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
4748 else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
4753 else if (tsName.find('.')!=-1) // maybe a link to a file
4756 fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
4764 if (tryUnspecializedVersion)
4766 return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
4768 if (bracePos!=-1) // Try without parameters as well, could be a constructor invocation
4770 *resContext=getClass(fullName.left(bracePos));
4776 //printf("resolveRef: %s not found!\n",name);
4781 QCString linkToText(SrcLangExt lang,const char *link,bool isFileName)
4783 //static bool optimizeOutputJava = Config_getBool(OPTIMIZE_OUTPUT_JAVA);
4784 QCString result=link;
4785 if (!result.isEmpty())
4788 result=substitute(result,"#","::");
4790 if (!isFileName && result.find('<')==-1) result=substitute(result,".","::",3);
4791 // strip leading :: prefix if present
4792 if (result.at(0)==':' && result.at(1)==':')
4794 result=result.right(result.length()-2);
4796 QCString sep = getLanguageSpecificSeparator(lang);
4799 result=substitute(result,"::",sep);
4807 * generate a reference to a class, namespace or member.
4808 * `scName' is the name of the scope that contains the documentation
4809 * string that is returned.
4810 * `name' is the name that we want to link to.
4811 * `name' may have five formats:
4813 * 2) "memberName()" one of the (overloaded) function or define
4814 * with name memberName.
4815 * 3) "memberName(...)" a specific (overloaded) function or define
4816 * with name memberName
4817 * 4) "::name a global variable or define
4818 * 4) "\#memberName member variable, global variable or define
4819 * 5) ("ScopeName::")+"memberName()"
4820 * 6) ("ScopeName::")+"memberName(...)"
4821 * 7) ("ScopeName::")+"memberName"
4822 * instead of :: the \# symbol may also be used.
4825 bool generateRef(OutputDocInterface &od,const char *scName,
4826 const char *name,bool inSeeBlock,const char *rt)
4828 //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
4830 Definition *compound;
4833 // create default link text
4834 QCString linkText = linkToText(rt,FALSE);
4836 if (resolveRef(scName,name,inSeeBlock,&compound,&md))
4838 if (md && md->isLinkable()) // link to member
4840 od.writeObjectLink(md->getReference(),
4841 md->getOutputFileBase(),
4842 md->anchor(),linkText);
4843 // generate the page reference (for LaTeX)
4844 if (!md->isReference())
4846 writePageRef(od,md->getOutputFileBase(),md->anchor());
4850 else if (compound && compound->isLinkable()) // link to compound
4852 if (rt==0 && compound->definitionType()==Definition::TypeGroup)
4854 linkText=((GroupDef *)compound)->groupTitle();
4856 if (compound && compound->definitionType()==Definition::TypeFile)
4858 linkText=linkToText(rt,TRUE);
4860 od.writeObjectLink(compound->getReference(),
4861 compound->getOutputFileBase(),
4863 if (!compound->isReference())
4865 writePageRef(od,compound->getOutputFileBase(),0);
4870 od.docify(linkText);
4875 bool resolveLink(/* in */ const char *scName,
4876 /* in */ const char *lr,
4877 /* in */ bool /*inSeeBlock*/,
4878 /* out */ Definition **resContext,
4879 /* out */ QCString &resAnchor
4884 QCString linkRef=lr;
4885 QCString linkRefWithoutTemplates = stripTemplateSpecifiersFromScope(linkRef,FALSE);
4886 //printf("ResolveLink linkRef=%s\n",lr);
4895 if (linkRef.isEmpty()) // no reference name!
4899 else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
4901 GroupDef *gd = pd->getGroupDef();
4904 if (!pd->name().isEmpty()) si=Doxygen::sectionDict->find(pd->name());
4906 if (si) resAnchor = si->label;
4914 else if ((si=Doxygen::sectionDict->find(linkRef)))
4916 *resContext=si->definition;
4917 resAnchor = si->label;
4920 else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
4925 else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
4930 else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
4931 && fd->isLinkable())
4936 else if ((cd=getClass(linkRef))) // class link
4939 resAnchor=cd->anchor();
4942 else if ((cd=getClass(linkRefWithoutTemplates))) // C#/Java generic class link
4945 resAnchor=cd->anchor();
4948 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
4951 resAnchor=cd->anchor();
4954 // else if ((cd=getClass(linkRef+"-g"))) // C# generic link
4957 // resAnchor=cd->anchor();
4960 else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
4965 else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath().utf8()+"/"))
4966 && dir->isLinkable()) // TODO: make this location independent like filedefs
4971 else // probably a member reference
4974 bool res = resolveRef(scName,lr,TRUE,resContext,&md);
4975 if (md) resAnchor=md->anchor();
4981 //----------------------------------------------------------------------
4982 // General function that generates the HTML code for a reference to some
4983 // file, class or member from text `lr' within the context of class `clName'.
4984 // This link has the text 'lt' (if not 0), otherwise `lr' is used as a
4985 // basis for the link's text.
4986 // returns TRUE if a link could be generated.
4988 bool generateLink(OutputDocInterface &od,const char *clName,
4989 const char *lr,bool inSeeBlock,const char *lt)
4991 //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
4992 Definition *compound;
4993 //PageDef *pageDef=0;
4994 QCString anchor,linkText=linkToText(SrcLangExt_Unknown,lt,FALSE);
4995 //printf("generateLink linkText=%s\n",linkText.data());
4996 if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
4998 if (compound) // link to compound
5000 if (lt==0 && anchor.isEmpty() && /* compound link */
5001 compound->definitionType()==Definition::TypeGroup /* is group */
5004 linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
5006 else if (compound->definitionType()==Definition::TypeFile)
5008 linkText=linkToText(compound->getLanguage(),lt,TRUE);
5010 od.writeObjectLink(compound->getReference(),
5011 compound->getOutputFileBase(),anchor,linkText);
5012 if (!compound->isReference())
5014 writePageRef(od,compound->getOutputFileBase(),anchor);
5019 err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
5023 else // link could not be found
5025 od.docify(linkText);
5030 void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
5032 //printf("generateFileRef(%s,%s)\n",name,text);
5033 QCString linkText = text ? text : name;
5037 if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) &&
5039 // link to documented input file
5040 od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
5042 od.docify(linkText);
5045 //----------------------------------------------------------------------
5048 QCString substituteClassNames(const QCString &s)
5052 if (s.isEmpty()) return result;
5053 QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
5054 while ((p=r.match(s,i,&l))!=-1)
5057 if (p>i) result+=s.mid(i,p-i);
5058 if ((subst=substituteDict[s.mid(p,l)]))
5068 result+=s.mid(i,s.length()-i);
5073 //----------------------------------------------------------------------
5075 /** Cache element for the file name to FileDef mapping cache. */
5076 struct FindFileCacheElem
5078 FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
5083 static QCache<FindFileCacheElem> g_findFileDefCache(5000);
5085 FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
5090 const int maxAddrSize = 20;
5091 char addr[maxAddrSize];
5092 qsnprintf(addr,maxAddrSize,"%p:",fnDict);
5093 QCString key = addr;
5096 g_findFileDefCache.setAutoDelete(TRUE);
5097 FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
5098 //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
5101 ambig = cachedResult->isAmbig;
5102 //printf("cached: fileDef=%p\n",cachedResult->fileDef);
5103 return cachedResult->fileDef;
5107 cachedResult = new FindFileCacheElem(0,FALSE);
5110 QCString name=QDir::cleanDirPath(n).utf8();
5114 if (name.isEmpty()) goto exit;
5115 slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
5118 path=name.left(slashPos+1);
5119 name=name.right(name.length()-slashPos-1);
5120 //printf("path=%s name=%s\n",path.data(),name.data());
5122 if (name.isEmpty()) goto exit;
5123 if ((fn=(*fnDict)[name]))
5125 //printf("fn->count()=%d\n",fn->count());
5128 FileDef *fd = fn->getFirst();
5129 #if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
5130 bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
5132 bool isSamePath = fd->getPath().right(path.length())==path;
5134 if (path.isEmpty() || isSamePath)
5136 cachedResult->fileDef = fd;
5137 g_findFileDefCache.insert(key,cachedResult);
5138 //printf("=1 ===> add to cache %p\n",fd);
5142 else // file name alone is ambiguous
5145 FileNameIterator fni(*fn);
5147 FileDef *lastMatch=0;
5148 QCString pathStripped = stripFromIncludePath(path);
5149 for (fni.toFirst();(fd=fni.current());++fni)
5151 QCString fdStripPath = stripFromIncludePath(fd->getPath());
5152 if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
5158 //printf(">1 ===> add to cache %p\n",fd);
5161 cachedResult->isAmbig = ambig;
5162 cachedResult->fileDef = lastMatch;
5163 g_findFileDefCache.insert(key,cachedResult);
5169 //printf("not found!\n");
5172 //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
5173 g_findFileDefCache.insert(key,cachedResult);
5174 //delete cachedResult;
5178 //----------------------------------------------------------------------
5180 QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
5185 int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
5188 path=name.left(slashPos+1);
5189 name=name.right(name.length()-slashPos-1);
5192 if ((fn=(*fnDict)[name]))
5194 FileNameIterator fni(*fn);
5196 for (fni.toFirst();(fd=fni.current());++fni)
5198 if (path.isEmpty() || fd->getPath().right(path.length())==path)
5200 result+=" "+fd->absFilePath()+"\n";
5207 //----------------------------------------------------------------------
5209 /// substitute all occurrences of \a src in \a s by \a dst
5210 QCString substitute(const QCString &s,const QCString &src,const QCString &dst)
5212 if (s.isEmpty() || src.isEmpty()) return s;
5214 int srcLen = src.length();
5215 int dstLen = dst.length();
5220 for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++;
5221 resLen = s.length()+count*(dstLen-srcLen);
5223 else // result has same size as s
5225 resLen = s.length();
5227 QCString result(resLen+1);
5229 for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
5235 if (dst) memcpy(r,dst,dstLen);
5239 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
5244 /// substitute all occurrences of \a src in \a s by \a dst, but skip
5245 /// each consecutive sequence of \a src where the number consecutive
5246 /// \a src matches \a skip_seq; if \a skip_seq is negative, skip any
5247 /// number of consecutive \a src
5248 QCString substitute(const QCString &s,const QCString &src,const QCString &dst,int skip_seq)
5250 if (s.isEmpty() || src.isEmpty()) return s;
5252 int srcLen = src.length();
5253 int dstLen = dst.length();
5258 for (count=0, p=s.data(); (q=strstr(p,src))!=0; p=q+srcLen) count++;
5259 resLen = s.length()+count*(dstLen-srcLen);
5261 else // result has same size as s
5263 resLen = s.length();
5265 QCString result(resLen+1);
5267 for (r=result.rawData(), p=s; (q=strstr(p,src))!=0; p=q+srcLen)
5269 // search a consecutive sequence of src
5270 int seq = 0, skip = 0;
5273 for (const char *n=q+srcLen; qstrncmp(n,src,srcLen)==0; seq=1+skip, n+=srcLen)
5274 ++skip; // number of consecutive src after the current one
5276 // verify the allowed number of consecutive src to skip
5277 if (skip_seq > 0 && skip_seq != seq)
5281 // skip a consecutive sequence of src when necessary
5282 int l = (int)((q + seq * srcLen)-p);
5288 // skip only the consecutive src found after the current one
5290 // the next loop will skip the current src, aka (p=q+srcLen)
5294 if (dst) memcpy(r,dst,dstLen);
5298 result.resize(strlen(result.data())+1);
5299 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
5303 /// substitute all occurrences of \a srcChar in \a s by \a dstChar
5304 QCString substitute(const QCString &s,char srcChar,char dstChar)
5307 QCString result(l+1);
5308 char *q=result.rawData();
5311 const char *p=s.data();
5313 while ((c=*p++)) *q++ = (c==srcChar) ? dstChar : c;
5319 //----------------------------------------------------------------------
5321 QCString substituteKeywords(const QCString &s,const char *title,
5322 const char *projName,const char *projNum,const char *projBrief)
5324 QCString result = s;
5325 if (title) result = substitute(result,"$title",title);
5326 result = substitute(result,"$datetime",dateToString(TRUE));
5327 result = substitute(result,"$date",dateToString(FALSE));
5328 result = substitute(result,"$year",yearToString());
5329 result = substitute(result,"$doxygenversion",versionString);
5330 result = substitute(result,"$projectname",projName);
5331 result = substitute(result,"$projectnumber",projNum);
5332 result = substitute(result,"$projectbrief",projBrief);
5333 result = substitute(result,"$projectlogo",stripPath(Config_getString(PROJECT_LOGO)));
5337 //----------------------------------------------------------------------
5339 /*! Returns the character index within \a name of the first prefix
5340 * in Config_getList(IGNORE_PREFIX) that matches \a name at the left hand side,
5341 * or zero if no match was found
5343 int getPrefixIndex(const QCString &name)
5345 if (name.isEmpty()) return 0;
5346 static QStrList &sl = Config_getList(IGNORE_PREFIX);
5347 char *s = sl.first();
5351 const char *pd=name.data();
5353 while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
5354 if (*ps==0 && *pd!=0)
5363 //----------------------------------------------------------------------------
5365 static void initBaseClassHierarchy(BaseClassList *bcl)
5368 BaseClassListIterator bcli(*bcl);
5369 for ( ; bcli.current(); ++bcli)
5371 ClassDef *cd=bcli.current()->classDef;
5372 if (cd->baseClasses()==0) // no base classes => new root
5374 initBaseClassHierarchy(cd->baseClasses());
5379 //----------------------------------------------------------------------------
5381 bool classHasVisibleChildren(ClassDef *cd)
5385 if (cd->getLanguage()==SrcLangExt_VHDL) // reverse baseClass/subClass relation
5387 if (cd->baseClasses()==0) return FALSE;
5388 bcl=cd->baseClasses();
5392 if (cd->subClasses()==0) return FALSE;
5393 bcl=cd->subClasses();
5396 BaseClassListIterator bcli(*bcl);
5397 for ( ; bcli.current() ; ++bcli)
5399 if (bcli.current()->classDef->isVisibleInHierarchy())
5408 //----------------------------------------------------------------------------
5410 void initClassHierarchy(ClassSDict *cl)
5412 ClassSDict::Iterator cli(*cl);
5414 for ( ; (cd=cli.current()); ++cli)
5417 initBaseClassHierarchy(cd->baseClasses());
5421 //----------------------------------------------------------------------------
5423 bool hasVisibleRoot(BaseClassList *bcl)
5427 BaseClassListIterator bcli(*bcl);
5428 for ( ; bcli.current(); ++bcli)
5430 ClassDef *cd=bcli.current()->classDef;
5431 if (cd->isVisibleInHierarchy()) return TRUE;
5432 hasVisibleRoot(cd->baseClasses());
5438 //----------------------------------------------------------------------
5440 // note that this function is not reentrant due to the use of static growBuf!
5441 QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
5443 static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES);
5444 static bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES);
5445 static GrowBuf growBuf;
5453 case '_': if (allowUnderscore) growBuf.addChar('_'); else growBuf.addStr("__"); break;
5454 case '-': growBuf.addChar('-'); break;
5455 case ':': growBuf.addStr("_1"); break;
5456 case '/': growBuf.addStr("_2"); break;
5457 case '<': growBuf.addStr("_3"); break;
5458 case '>': growBuf.addStr("_4"); break;
5459 case '*': growBuf.addStr("_5"); break;
5460 case '&': growBuf.addStr("_6"); break;
5461 case '|': growBuf.addStr("_7"); break;
5462 case '.': if (allowDots) growBuf.addChar('.'); else growBuf.addStr("_8"); break;
5463 case '!': growBuf.addStr("_9"); break;
5464 case ',': growBuf.addStr("_00"); break;
5465 case ' ': growBuf.addStr("_01"); break;
5466 case '{': growBuf.addStr("_02"); break;
5467 case '}': growBuf.addStr("_03"); break;
5468 case '?': growBuf.addStr("_04"); break;
5469 case '^': growBuf.addStr("_05"); break;
5470 case '%': growBuf.addStr("_06"); break;
5471 case '(': growBuf.addStr("_07"); break;
5472 case ')': growBuf.addStr("_08"); break;
5473 case '+': growBuf.addStr("_09"); break;
5474 case '=': growBuf.addStr("_0A"); break;
5475 case '$': growBuf.addStr("_0B"); break;
5476 case '\\': growBuf.addStr("_0C"); break;
5477 case '@': growBuf.addStr("_0D"); break;
5482 const unsigned char uc = (unsigned char)c;
5483 bool doEscape = TRUE;
5484 if (allowUnicodeNames && uc <= 0xf7)
5489 if ((uc&0xE0)==0xC0)
5491 l=2; // 11xx.xxxx: >=2 byte character
5493 if ((uc&0xF0)==0xE0)
5495 l=3; // 111x.xxxx: >=3 byte character
5497 if ((uc&0xF8)==0xF0)
5499 l=4; // 1111.xxxx: >=4 byte character
5502 for (int m=1; m<l && !doEscape; ++m)
5504 unsigned char ct = (unsigned char)*pt;
5505 if (ct==0 || (ct&0xC0)!=0x80) // invalid unicode character
5514 if ( !doEscape ) // got a valid unicode character
5517 growBuf.addStr( ids );
5521 if (doEscape) // not a valid unicode char or escaping needed
5523 static char map[] = "0123456789ABCDEF";
5524 unsigned char id = (unsigned char)c;
5530 growBuf.addStr(ids);
5533 else if (caseSenseNames || !isupper(c))
5539 growBuf.addChar('_');
5540 growBuf.addChar(tolower(c));
5546 return growBuf.get();
5549 /*! This function determines the file name on disk of an item
5550 * given its name, which could be a class name with template
5551 * arguments, so special characters need to be escaped.
5553 QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
5555 if (name==0 || name[0]=='\0') return "";
5556 static bool shortNames = Config_getBool(SHORT_NAMES);
5557 static bool createSubdirs = Config_getBool(CREATE_SUBDIRS);
5559 if (shortNames) // use short names only
5561 static QDict<int> usedNames(10007);
5562 usedNames.setAutoDelete(TRUE);
5565 int *value=usedNames.find(name);
5569 usedNames.insert(name,new int(count));
5576 result.sprintf("a%05d",num);
5580 result=escapeCharsInString(name,allowDots,allowUnderscore);
5581 int resultLen = result.length();
5582 if (resultLen>=128) // prevent names that cannot be created!
5584 // third algorithm based on MD5 hash
5586 QCString sigStr(33);
5587 MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
5588 MD5SigToString(md5_sig,sigStr.rawData(),33);
5589 result=result.left(128-32)+sigStr;
5594 int l1Dir=0,l2Dir=0;
5596 #if MAP_ALGO==ALGO_COUNT
5597 // old algorithm, has the problem that after regeneration the
5598 // output can be located in a different dir.
5599 if (Doxygen::htmlDirMap==0)
5601 Doxygen::htmlDirMap=new QDict<int>(100003);
5602 Doxygen::htmlDirMap->setAutoDelete(TRUE);
5604 static int curDirNum=0;
5605 int *dirNum = Doxygen::htmlDirMap->find(result);
5606 if (dirNum==0) // new name
5608 Doxygen::htmlDirMap->insert(result,new int(curDirNum));
5609 l1Dir = (curDirNum)&0xf; // bits 0-3
5610 l2Dir = (curDirNum>>4)&0xff; // bits 4-11
5613 else // existing name
5615 l1Dir = (*dirNum)&0xf; // bits 0-3
5616 l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
5618 #elif MAP_ALGO==ALGO_CRC16
5619 // second algorithm based on CRC-16 checksum
5620 int dirNum = qChecksum(result,result.length());
5622 l2Dir = (dirNum>>4)&0xff;
5623 #elif MAP_ALGO==ALGO_MD5
5624 // third algorithm based on MD5 hash
5626 MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
5627 l1Dir = md5_sig[14]&0xf;
5628 l2Dir = md5_sig[15];
5630 result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
5632 //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
5636 QCString relativePathToRoot(const char *name)
5639 if (Config_getBool(CREATE_SUBDIRS))
5643 return REL_PATH_TO_ROOT;
5648 int i = n.findRev('/');
5651 result=REL_PATH_TO_ROOT;
5658 void createSubDirs(QDir &d)
5660 if (Config_getBool(CREATE_SUBDIRS))
5662 // create 4096 subdirectories
5664 for (l1=0;l1<16;l1++)
5666 d.mkdir(QCString().sprintf("d%x",l1));
5667 for (l2=0;l2<256;l2++)
5669 d.mkdir(QCString().sprintf("d%x/d%02x",l1,l2));
5675 /*! Input is a scopeName, output is the scopename split into a
5676 * namespace part (as large as possible) and a classname part.
5678 void extractNamespaceName(const QCString &scopeName,
5679 QCString &className,QCString &namespaceName,
5680 bool allowEmptyClass)
5683 QCString clName=scopeName;
5684 NamespaceDef *nd = 0;
5685 if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
5686 { // the whole name is a namespace (and not a class)
5687 namespaceName=nd->name().copy();
5688 className.resize(0);
5691 p=clName.length()-2;
5692 while (p>=0 && (i=clName.findRev("::",p))!=-1)
5693 // see if the first part is a namespace (and not a class)
5695 //printf("Trying %s\n",clName.left(i).data());
5696 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
5698 //printf("found!\n");
5699 namespaceName=nd->name().copy();
5700 className=clName.right(clName.length()-i-2);
5703 p=i-2; // try a smaller piece of the scope
5705 //printf("not found!\n");
5707 // not found, so we just have to guess.
5708 className=scopeName.copy();
5709 namespaceName.resize(0);
5712 if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
5714 // class and namespace with the same name, correct to return the class.
5715 className=namespaceName.copy();
5716 namespaceName.resize(0);
5718 //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
5719 // className.data(),namespaceName.data());
5720 if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p")
5722 className = className.left(className.length()-2);
5727 QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
5729 QCString result=scope.copy();
5730 if (!templ.isEmpty() && scope.find('<')==-1)
5735 (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
5736 ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0)
5739 //printf("Tried `%s'\n",(scope.left(si)+templ).data());
5742 if (si==-1) // not nested => append template specifier
5746 else // nested => insert template specifier before after first class name
5748 result=scope.left(si) + templ + scope.right(scope.length()-si);
5751 //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
5752 // scope.data(),templ.data(),result.data());
5756 #if 0 // original version
5757 /*! Strips the scope from a name. Examples: A::B will return A
5758 * and A<T>::B<N::C<D> > will return A<T>.
5760 QCString stripScope(const char *name)
5762 QCString result = name;
5763 int l=result.length();
5770 char c=result.at(p);
5774 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5775 return result.right(l-p-1);
5779 //printf("pos < = %d\n",p);
5781 while (p>=0 && !done)
5786 case '>': count++; break;
5787 case '<': count--; if (count<=0) done=TRUE; break;
5789 //printf("c=%c count=%d\n",c,count);
5793 //printf("pos > = %d\n",p+1);
5799 //printf("stripScope(%s)=%s\n",name,name);
5804 // new version by Davide Cesari which also works for Fortran
5805 QCString stripScope(const char *name)
5807 QCString result = name;
5808 int l=result.length();
5811 bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
5816 p=l-1; // start at the end of the string
5817 while (p>=0 && count>=0)
5819 char c=result.at(p);
5823 // only exit in the case of ::
5824 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5825 if (p>0 && result.at(p-1)==':') return result.right(l-p-1);
5829 if (skipBracket) // we don't care about brackets
5833 else // count open/close brackets
5835 if (p>0 && result.at(p-1)=='>') // skip >> operator
5841 //printf("pos < = %d\n",p);
5843 bool foundMatch=false;
5844 while (p>=0 && !foundMatch)
5855 if (result.at(p-1) == '<') // skip << operator
5862 foundMatch = count==0;
5865 //printf("c=%c count=%d\n",c,count);
5870 //printf("pos > = %d\n",p+1);
5876 done = count==0 || skipBracket; // reparse if brackets do not match
5879 while (!done); // if < > unbalanced repeat ignoring them
5880 //printf("stripScope(%s)=%s\n",name,name);
5884 /*! Converts a string to a HTML id string */
5885 QCString convertToId(const char *s)
5887 static const char hex[] = "0123456789ABCDEF";
5888 static GrowBuf growBuf;
5890 if (s==0) return "";
5897 if ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='-' || c==':' || c=='.')
5898 { // any permissive character except _
5899 if (first && c>='0' && c<='9') growBuf.addChar('a'); // don't start with a digit
5905 encChar[1]=hex[((unsigned char)c)>>4];
5906 encChar[2]=hex[((unsigned char)c)&0xF];
5908 growBuf.addStr(encChar);
5913 return growBuf.get();
5916 /*! Converts a string to an XML-encoded string */
5917 QCString convertToXML(const char *s)
5919 static GrowBuf growBuf;
5921 if (s==0) return "";
5928 case '<': growBuf.addStr("<"); break;
5929 case '>': growBuf.addStr(">"); break;
5930 case '&': growBuf.addStr("&"); break;
5931 case '\'': growBuf.addStr("'"); break;
5932 case '"': growBuf.addStr("""); break;
5933 case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
5934 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
5935 case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
5936 case 27: case 28: case 29: case 30: case 31:
5937 break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
5938 default: growBuf.addChar(c); break;
5942 return growBuf.get();
5945 /*! Converts a string to an DocBook-encoded string */
5946 QCString convertToDocBook(const char *s)
5948 static GrowBuf growBuf;
5950 if (s==0) return "";
5951 const unsigned char *q;
5953 const unsigned char *p=(const unsigned char *)s;
5959 case '<': growBuf.addStr("<"); break;
5960 case '>': growBuf.addStr(">"); break;
5961 case '&': // possibility to have a special symbol
5963 cnt = 2; // we have to count & and ; as well
5964 while ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') || (*q >= '0' && *q <= '9'))
5971 --p; // we need & as well
5972 DocSymbol::SymType res = HtmlEntityMapper::instance()->name2sym(QCString((char *)p).left(cnt));
5973 if (res == DocSymbol::Sym_Unknown)
5976 growBuf.addStr("&");
5980 growBuf.addStr(HtmlEntityMapper::instance()->docbook(res));
5987 growBuf.addStr("&");
5990 case '\'': growBuf.addStr("'"); break;
5991 case '"': growBuf.addStr("""); break;
5992 case '\007': growBuf.addStr("␇"); break;
5993 case 1: case 2: case 3: case 4: case 5: case 6: case 8:
5994 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18:
5995 case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26:
5996 case 27: case 28: case 29: case 30: case 31:
5997 break; // skip invalid XML characters (see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char)
5998 default: growBuf.addChar(c); break;
6002 return growBuf.get();
6005 /*! Converts a string to a HTML-encoded string */
6006 QCString convertToHtml(const char *s,bool keepEntities)
6008 static GrowBuf growBuf;
6010 if (s==0) return "";
6011 growBuf.addStr(getHtmlDirEmbedingChar(getTextDirByConfig(s)));
6018 case '<': growBuf.addStr("<"); break;
6019 case '>': growBuf.addStr(">"); break;
6020 case '&': if (keepEntities)
6026 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
6028 if (ce==';') // found end of an entity
6030 // copy entry verbatim
6032 while (p<e) growBuf.addChar(*p++);
6036 growBuf.addStr("&");
6041 growBuf.addStr("&");
6044 case '\'': growBuf.addStr("'"); break;
6045 case '"': growBuf.addStr("""); break;
6046 default: growBuf.addChar(c); break;
6050 return growBuf.get();
6053 QCString convertToJSString(const char *s, bool applyTextDir)
6055 static GrowBuf growBuf;
6057 if (s==0) return "";
6059 growBuf.addStr(getJsDirEmbedingChar(getTextDirByConfig(s)));
6066 case '"': growBuf.addStr("\\\""); break;
6067 case '\\': growBuf.addStr("\\\\"); break;
6068 default: growBuf.addChar(c); break;
6072 return convertCharEntitiesToUTF8(growBuf.get());
6075 QCString convertToLaTeX(const QCString &s,bool insideTabbing,bool keepSpaces)
6078 FTextStream t(&result);
6079 filterLatexString(t,s,insideTabbing,FALSE,FALSE,keepSpaces);
6080 return result.data();
6085 QCString convertCharEntitiesToUTF8(const QCString &s)
6088 static QRegExp entityPat("&[a-zA-Z]+[0-9]*;");
6090 if (s.length()==0) return result;
6091 static GrowBuf growBuf;
6094 while ((p=entityPat.match(s,i,&l))!=-1)
6098 growBuf.addStr(s.mid(i,p-i));
6100 QCString entity = s.mid(p,l);
6101 DocSymbol::SymType symType = HtmlEntityMapper::instance()->name2sym(entity);
6103 if (symType!=DocSymbol::Sym_Unknown && (code=HtmlEntityMapper::instance()->utf8(symType)))
6105 growBuf.addStr(code);
6109 growBuf.addStr(s.mid(p,l));
6113 growBuf.addStr(s.mid(i,s.length()-i));
6115 //printf("convertCharEntitiesToUTF8(%s)->%s\n",s.data(),growBuf.get());
6116 return growBuf.get();
6119 /*! Returns the standard string that is generated when the \\overload
6122 QCString getOverloadDocs()
6124 return theTranslator->trOverloadText();
6125 //"This is an overloaded member function, "
6126 // "provided for convenience. It differs from the above "
6127 // "function only in what argument(s) it accepts.";
6130 void addMembersToMemberGroup(MemberList *ml,
6131 MemberGroupSDict **ppMemberGroupSDict,
6132 Definition *context)
6135 //printf("addMemberToMemberGroup()\n");
6137 MemberListIterator mli(*ml);
6140 for (index=0;(md=mli.current());)
6142 if (md->isEnumerate()) // insert enum value of this enum into groups
6144 MemberList *fmdl=md->enumFieldList();
6147 MemberListIterator fmli(*fmdl);
6149 for (fmli.toFirst();(fmd=fmli.current());++fmli)
6151 int groupId=fmd->getMemberGroupId();
6154 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
6155 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
6156 //QCString *pDocs = Doxygen::memberDocDict[groupId];
6159 if (*ppMemberGroupSDict==0)
6161 *ppMemberGroupSDict = new MemberGroupSDict;
6162 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
6164 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
6167 mg = new MemberGroup(
6175 (*ppMemberGroupSDict)->append(groupId,mg);
6177 mg->insertMember(fmd); // insert in member group
6178 fmd->setMemberGroup(mg);
6184 int groupId=md->getMemberGroupId();
6187 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
6188 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
6189 //QCString *pDocs = Doxygen::memberDocDict[groupId];
6192 if (*ppMemberGroupSDict==0)
6194 *ppMemberGroupSDict = new MemberGroupSDict;
6195 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
6197 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
6200 mg = new MemberGroup(
6208 (*ppMemberGroupSDict)->append(groupId,mg);
6210 md = ml->take(index); // remove from member list
6211 mg->insertMember(md); // insert in member group
6212 mg->setRefItems(info->m_sli);
6213 md->setMemberGroup(mg);
6221 /*! Extracts a (sub-)string from \a type starting at \a pos that
6222 * could form a class. The index of the match is returned and the found
6223 * class \a name and a template argument list \a templSpec. If -1 is returned
6224 * there are no more matches.
6226 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec,SrcLangExt lang)
6228 static const QRegExp re_norm("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
6229 static const QRegExp re_ftn("[a-z_A-Z\\x80-\\xFF][()=_a-z_A-Z0-9:\\x80-\\xFF]*");
6233 templSpec.resize(0);
6235 int typeLen=type.length();
6238 if (lang == SrcLangExt_Fortran)
6240 if (type.at(pos)==',') return -1;
6241 if (type.left(4).lower()=="type")
6255 if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
6260 while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
6261 if (type.at(ts)=='<') // assume template instance
6263 // locate end of template
6266 while (te<typeLen && brCount!=0)
6268 if (type.at(te)=='<')
6270 if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
6272 if (type.at(te)=='>')
6274 if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
6279 name = type.mid(i,l);
6282 templSpec = type.mid(ts,te-ts),tl+=te-ts;
6285 else // no template part
6289 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
6290 // type.data(),pos,name.data(),templSpec.data());
6295 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
6296 // type.data(),pos,name.data(),templSpec.data());
6300 QCString normalizeNonTemplateArgumentsInString(
6301 const QCString &name,
6302 Definition *context,
6303 const ArgumentList * formalArgs)
6306 int p=name.find('<');
6307 if (p==-1) return name;
6309 QCString result = name.left(p);
6311 static QRegExp re("[a-z:_A-Z\\x80-\\xFF][a-z:_A-Z0-9\\x80-\\xFF]*");
6313 // for each identifier in the template part (e.g. B<T> -> T)
6314 while ((i=re.match(name,p,&l))!=-1)
6316 result += name.mid(p,i-p);
6317 QCString n = name.mid(i,l);
6319 if (formalArgs) // check that n is not a formal template argument
6321 ArgumentListIterator formAli(*formalArgs);
6323 for (formAli.toFirst();
6324 (formArg=formAli.current()) && !found;
6328 found = formArg->name==n;
6333 // try to resolve the type
6334 ClassDef *cd = getResolvedClass(context,0,n);
6350 result+=name.right(name.length()-p);
6351 //printf("normalizeNonTemplateArgumentInString(%s)=%s\n",name.data(),result.data());
6352 return removeRedundantWhiteSpace(result);
6356 /*! Substitutes any occurrence of a formal argument from argument list
6357 * \a formalArgs in \a name by the corresponding actual argument in
6358 * argument list \a actualArgs. The result after substitution
6359 * is returned as a string. The argument \a name is used to
6360 * prevent recursive substitution.
6362 QCString substituteTemplateArgumentsInString(
6363 const QCString &name,
6364 ArgumentList *formalArgs,
6365 ArgumentList *actualArgs)
6367 //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
6368 // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
6369 if (formalArgs==0) return name;
6371 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
6373 // for each identifier in the base class name (e.g. B<T> -> B and T)
6374 while ((i=re.match(name,p,&l))!=-1)
6376 result += name.mid(p,i-p);
6377 QCString n = name.mid(i,l);
6378 ArgumentListIterator formAli(*formalArgs);
6379 ArgumentListIterator actAli(*actualArgs);
6383 // if n is a template argument, then we substitute it
6384 // for its template instance argument.
6386 for (formAli.toFirst();
6387 (formArg=formAli.current()) && !found;
6391 actArg = actAli.current();
6392 if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
6394 formArg->name = formArg->type.mid(6);
6395 formArg->type = "class";
6397 if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
6399 formArg->name = formArg->type.mid(9);
6400 formArg->type = "typename";
6402 if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
6404 //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
6405 // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
6406 //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
6407 // formArg->name.data(),actArg ? actArg->type.data() : "",actArg ? actArg->name.data() : ""
6409 if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
6411 // replace formal argument with the actual argument of the instance
6412 if (!leftScopeMatch(actArg->type,n))
6413 // the scope guard is to prevent recursive lockup for
6414 // template<class A> class C : public<A::T>,
6415 // where A::T would become A::T::T here,
6416 // since n==A and actArg->type==A::T
6417 // see bug595833 for an example
6419 if (actArg->name.isEmpty())
6421 result += actArg->type+" ";
6425 // for case where the actual arg is something like "unsigned int"
6426 // the "int" part is in actArg->name.
6428 result += actArg->type+" "+actArg->name+" ";
6433 else if (formArg->name==n &&
6435 !formArg->defval.isEmpty() &&
6436 formArg->defval!=name /* to prevent recursion */
6439 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
6443 else if (formArg->name==n &&
6445 !formArg->defval.isEmpty() &&
6446 formArg->defval!=name /* to prevent recursion */
6449 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
6459 result+=name.right(name.length()-p);
6460 //printf(" Inheritance relation %s -> %s\n",
6461 // name.data(),result.data());
6462 return result.stripWhiteSpace();
6465 /*! Makes a deep copy of the list of argument lists \a srcLists.
6466 * Will allocate memory, that is owned by the caller.
6468 QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
6470 ASSERT(srcLists!=0);
6471 QList<ArgumentList> *dstLists = new QList<ArgumentList>;
6472 dstLists->setAutoDelete(TRUE);
6473 QListIterator<ArgumentList> sli(*srcLists);
6475 for (;(sl=sli.current());++sli)
6477 dstLists->append(sl->deepCopy());
6482 /*! Strips template specifiers from scope \a fullName, except those
6483 * that make up specialized classes. The switch \a parentOnly
6484 * determines whether or not a template "at the end" of a scope
6485 * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will
6486 * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will
6487 * strip both unless A<T> or B<S> are specialized template classes.
6489 QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
6491 QCString *pLastScopeStripped)
6495 int l=fullName.length();
6496 int i=fullName.find('<');
6499 //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
6503 while (e<l && !done)
6505 char c=fullName.at(e++);
6516 int si= fullName.find("::",e);
6518 if (parentOnly && si==-1) break;
6519 // we only do the parent scope, so we stop here if needed
6521 result+=fullName.mid(p,i-p);
6522 //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data());
6523 if (getClass(result+fullName.mid(i,e-i))!=0)
6525 result+=fullName.mid(i,e-i);
6526 //printf(" 2:result+=%s\n",fullName.mid(i,e-i-1).data());
6528 else if (pLastScopeStripped)
6530 //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data());
6531 *pLastScopeStripped=fullName.mid(i,e-i);
6534 i=fullName.find('<',p);
6536 result+=fullName.right(l-p);
6537 //printf("3:result+=%s\n",fullName.right(l-p).data());
6541 /*! Merges two scope parts together. The parts may (partially) overlap.
6542 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
6543 * Example2: \c A and \c B will be \c A::B <br>
6544 * Example3: \c A::B and B will be \c A::B
6546 * @param leftScope the left hand part of the scope.
6547 * @param rightScope the right hand part of the scope.
6548 * @returns the merged scope.
6550 QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
6552 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
6553 if (leftScopeMatch(rightScope,leftScope)) return rightScope;
6555 int i=0,p=leftScope.length();
6557 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
6558 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
6560 while ((i=leftScope.findRev("::",p))>0)
6562 if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
6564 result = leftScope.left(i+2)+rightScope;
6569 if (found) return result;
6571 // case leftScope=="A" rightScope=="B" => result = "A::B"
6572 result=leftScope.copy();
6573 if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
6578 /*! Returns a fragment from scope \a s, starting at position \a p.
6580 * @param s the scope name as a string.
6581 * @param p the start position (0 is the first).
6582 * @param l the resulting length of the fragment.
6583 * @returns the location of the fragment, or -1 if non is found.
6585 int getScopeFragment(const QCString &s,int p,int *l)
6591 if (sp>=sl) return -1;
6595 if (c==':') sp++,p++; else break;
6602 case ':': // found next part
6604 case '<': // skip template specifier
6607 while (sp<sl && !done)
6609 // TODO: deal with << and >> operators!
6613 case '<': count++; break;
6614 case '>': count--; if (count==0) done=TRUE; break;
6626 //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
6630 //----------------------------------------------------------------------------
6632 PageDef *addRelatedPage(const char *name,const QCString &ptitle,
6633 const QCString &doc,
6634 QList<SectionInfo> * /*anchors*/,
6635 const char *fileName,int startLine,
6636 const QList<ListItemInfo> *sli,
6643 //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
6644 if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
6646 // append documentation block to the page.
6647 pd->setDocumentation(doc,fileName,startLine);
6648 //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pd,name);
6649 // append (x)refitems to the page.
6650 pd->setRefItems(sli);
6654 QCString baseName=name;
6655 if (baseName.right(4)==".tex")
6656 baseName=baseName.left(baseName.length()-4);
6657 else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
6658 baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
6660 QCString title=ptitle.stripWhiteSpace();
6661 pd=new PageDef(fileName,startLine,baseName,doc,title);
6663 pd->setRefItems(sli);
6664 pd->setLanguage(lang);
6668 pd->setReference(tagInfo->tagName);
6669 pd->setFileName(tagInfo->fileName);
6672 //printf("Appending page `%s'\n",baseName.data());
6673 Doxygen::pageSDict->append(baseName,pd);
6675 if (gd) gd->addPage(pd);
6677 if (!pd->title().isEmpty())
6679 //outputList->writeTitle(pi->name,pi->title);
6681 // a page name is a label as well!
6685 file=gd->getOutputFileBase();
6689 file=pd->getOutputFileBase();
6691 SectionInfo *si = Doxygen::sectionDict->find(pd->name());
6694 if (si->lineNr != -1)
6696 warn(file,-1,"multiple use of section label '%s', (first occurrence: %s, line %d)",pd->name().data(),si->fileName.data(),si->lineNr);
6700 warn(file,-1,"multiple use of section label '%s', (first occurrence: %s)",pd->name().data(),si->fileName.data());
6706 file,-1,pd->name(),pd->title(),SectionInfo::Page,0,pd->getReference());
6707 //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
6708 // si->label.data(),si->definition?si->definition->name().data():"<none>",
6709 // si->fileName.data());
6710 //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
6711 //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
6712 Doxygen::sectionDict->append(pd->name(),si);
6719 //----------------------------------------------------------------------------
6721 void addRefItem(const QList<ListItemInfo> *sli,
6723 const char *prefix, const char *name,const char *title,const char *args,Definition *scope)
6725 //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args);
6726 if (sli && key && key[0]!='@') // check for @ to skip anonymous stuff (see bug427012)
6728 QListIterator<ListItemInfo> slii(*sli);
6730 for (slii.toFirst();(lii=slii.current());++slii)
6732 RefList *refList = Doxygen::xrefLists->find(lii->type);
6736 // either not a built-in list or the list is enabled
6737 (lii->type!="todo" || Config_getBool(GENERATE_TODOLIST)) &&
6738 (lii->type!="test" || Config_getBool(GENERATE_TESTLIST)) &&
6739 (lii->type!="bug" || Config_getBool(GENERATE_BUGLIST)) &&
6740 (lii->type!="deprecated" || Config_getBool(GENERATE_DEPRECATEDLIST))
6744 RefItem *item = refList->getRefItem(lii->itemId);
6747 item->prefix = prefix;
6748 item->scope = scope;
6750 item->title = title;
6753 refList->insertIntoList(key,item);
6760 bool recursivelyAddGroupListToTitle(OutputList &ol,Definition *d,bool root)
6762 GroupList *groups = d->partOfGroups();
6763 if (groups) // write list of group to which this definition belongs
6767 ol.pushGeneratorState();
6768 ol.disableAllBut(OutputGenerator::Html);
6769 ol.writeString("<div class=\"ingroups\">");
6771 GroupListIterator gli(*groups);
6774 for (gli.toFirst();(gd=gli.current());++gli)
6776 if (recursivelyAddGroupListToTitle(ol, gd, FALSE))
6778 ol.writeString(" » ");
6780 if (!first) { ol.writeString(" | "); } else first=FALSE;
6781 ol.writeObjectLink(gd->getReference(),gd->getOutputFileBase(),0,gd->groupTitle());
6785 ol.writeString("</div>");
6786 ol.popGeneratorState();
6793 void addGroupListToTitle(OutputList &ol,Definition *d)
6795 recursivelyAddGroupListToTitle(ol,d,TRUE);
6798 void filterLatexString(FTextStream &t,const char *str,
6799 bool insideTabbing,bool insidePre,bool insideItem,bool keepSpaces)
6802 //if (strlen(str)<2) stackTrace();
6803 const unsigned char *p=(const unsigned char *)str;
6804 const unsigned char *q;
6807 unsigned char pc='\0';
6816 case 0xef: // handle U+FFFD i.e. "Replacement character" caused by octal: 357 277 275 / hexadecimal 0xef 0xbf 0xbd
6817 // the LaTeX command \ucr has been defined in doxygen.sty
6818 if ((unsigned char)*(p) == 0xbf && (unsigned char)*(p+1) == 0xbd)
6826 case '\\': t << "\\(\\backslash\\)"; break;
6827 case '{': t << "\\{"; break;
6828 case '}': t << "\\}"; break;
6829 case '_': t << "\\_"; break;
6830 case '&': t << "\\&"; break;
6831 case '%': t << "\\%"; break;
6832 case '#': t << "\\#"; break;
6833 case '$': t << "\\$"; break;
6834 case '^': (usedTableLevels()>0) ? t << "\\string^" : t << (char)c; break;
6835 case '~': (usedTableLevels()>0) ? t << "\\string~" : t << (char)c; break;
6836 case ' ': if (keepSpaces) t << "~"; else t << ' ';
6847 case 0xef: // handle U+FFFD i.e. "Replacement character" caused by octal: 357 277 275 / hexadecimal 0xef 0xbf 0xbd
6848 // the LaTeX command \ucr has been defined in doxygen.sty
6849 if ((unsigned char)*(p) == 0xbf && (unsigned char)*(p+1) == 0xbd)
6857 case '#': t << "\\#"; break;
6858 case '$': t << "\\$"; break;
6859 case '%': t << "\\%"; break;
6860 case '^': t << "$^\\wedge$"; break;
6861 case '&': // possibility to have a special symbol
6863 cnt = 2; // we have to count & and ; as well
6864 while ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') || (*q >= '0' && *q <= '9'))
6871 --p; // we need & as well
6872 DocSymbol::SymType res = HtmlEntityMapper::instance()->name2sym(QCString((char *)p).left(cnt));
6873 if (res == DocSymbol::Sym_Unknown)
6880 t << HtmlEntityMapper::instance()->latex(res);
6890 case '*': t << "$\\ast$"; break;
6891 case '_': if (!insideTabbing) t << "\\+";
6893 if (!insideTabbing) t << "\\+";
6895 case '{': t << "\\{"; break;
6896 case '}': t << "\\}"; break;
6897 case '<': t << "$<$"; break;
6898 case '>': t << "$>$"; break;
6899 case '|': t << "$\\vert$"; break;
6900 case '~': t << "$\\sim$"; break;
6901 case '[': if (Config_getBool(PDF_HYPERLINKS) || insideItem)
6906 case ']': if (pc=='[') t << "$\\,$";
6907 if (Config_getBool(PDF_HYPERLINKS) || insideItem)
6912 case '-': t << "-\\/";
6914 case '\\': t << "\\textbackslash{}";
6916 case '"': t << "\\char`\\\"{}";
6918 case '`': t << "\\`{}";
6920 case '\'': t << "\\textquotesingle{}";
6922 case ' ': if (keepSpaces) { if (insideTabbing) t << "\\>"; else t << '~'; } else t << ' ';
6926 //if (!insideTabbing && forceBreaks && c!=' ' && *p!=' ')
6927 if (!insideTabbing &&
6928 ((c>='A' && c<='Z' && pc!=' ' && pc!='\0' && *p) || (c==':' && pc!=':') || (pc=='.' && isId(c)))
6940 QCString latexEscapeLabelName(const char *s)
6943 QCString tmp(qstrlen(s)+1);
6944 FTextStream t(&result);
6952 case '|': t << "\\texttt{\"|}"; break;
6953 case '!': t << "\"!"; break;
6954 case '@': t << "\"@"; break;
6955 case '%': t << "\\%"; break;
6956 case '{': t << "\\lcurly{}"; break;
6957 case '}': t << "\\rcurly{}"; break;
6958 case '~': t << "````~"; break; // to get it a bit better in index together with other special characters
6959 // NOTE: adding a case here, means adding it to while below as well!
6962 // collect as long string as possible, before handing it to docify
6964 while ((c=*p) && c!='@' && c!='[' && c!=']' && c!='!' && c!='{' && c!='}' && c!='|')
6970 filterLatexString(t,tmp,TRUE);
6974 return result.data();
6977 QCString latexEscapeIndexChars(const char *s)
6980 QCString tmp(qstrlen(s)+1);
6981 FTextStream t(&result);
6989 case '!': t << "\"!"; break;
6990 case '"': t << "\"\""; break;
6991 case '@': t << "\"@"; break;
6992 case '|': t << "\\texttt{\"|}"; break;
6993 case '[': t << "["; break;
6994 case ']': t << "]"; break;
6995 case '{': t << "\\lcurly{}"; break;
6996 case '}': t << "\\rcurly{}"; break;
6997 // NOTE: adding a case here, means adding it to while below as well!
7000 // collect as long string as possible, before handing it to docify
7002 while ((c=*p) && c!='"' && c!='@' && c!='[' && c!=']' && c!='!' && c!='{' && c!='}' && c!='|')
7008 filterLatexString(t,tmp.data(),TRUE);
7012 return result.data();
7015 QCString latexEscapePDFString(const char *s)
7018 FTextStream t(&result);
7025 case '\\': t << "\\textbackslash{}"; break;
7026 case '{': t << "\\{"; break;
7027 case '}': t << "\\}"; break;
7028 case '_': t << "\\_"; break;
7029 case '%': t << "\\%"; break;
7030 case '&': t << "\\&"; break;
7036 return result.data();
7039 QCString latexFilterURL(const char *s)
7042 FTextStream t(&result);
7049 case '#': t << "\\#"; break;
7055 return result.data();
7059 QCString rtfFormatBmkStr(const char *name)
7061 static QCString g_nextTag( "AAAAAAAAAA" );
7062 static QDict<QCString> g_tagDict( 5003 );
7064 g_tagDict.setAutoDelete(TRUE);
7066 // To overcome the 40-character tag limitation, we
7067 // substitute a short arbitrary string for the name
7068 // supplied, and keep track of the correspondence
7069 // between names and strings.
7070 QCString key( name );
7071 QCString* tag = g_tagDict.find( key );
7074 // This particular name has not yet been added
7075 // to the list. Add it, associating it with the
7076 // next tag value, and increment the next tag.
7077 tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
7078 g_tagDict.insert( key, tag );
7080 // This is the increment part
7081 char* nxtTag = g_nextTag.rawData() + g_nextTag.length() - 1;
7082 for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
7084 if ( ( ++(*nxtTag) ) > 'Z' )
7090 // Since there was no carry, we can stop now
7099 bool checkExtension(const char *fName, const char *ext)
7101 return (QCString(fName).right(QCString(ext).length())==ext);
7104 QCString stripExtensionGeneral(const char *fName, const char *ext)
7106 QCString result=fName;
7107 if (result.right(QCString(ext).length())==QCString(ext))
7109 result=result.left(result.length()-QCString(ext).length());
7114 QCString stripExtension(const char *fName)
7116 return stripExtensionGeneral(fName, Doxygen::htmlFileExtension);
7119 void replaceNamespaceAliases(QCString &scope,int i)
7123 QCString ns = scope.left(i);
7124 QCString *s = Doxygen::namespaceAliasDict[ns];
7127 scope=*s+scope.right(scope.length()-i);
7130 if (i>0 && ns==scope.left(i)) break;
7134 QCString stripPath(const char *s)
7137 int i=result.findRev('/');
7140 result=result.mid(i+1);
7142 i=result.findRev('\\');
7145 result=result.mid(i+1);
7150 /** returns \c TRUE iff string \a s contains word \a w */
7151 bool containsWord(const QCString &s,const QCString &word)
7153 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
7155 while ((i=wordExp.match(s,p,&l))!=-1)
7157 if (s.mid(i,l)==word) return TRUE;
7163 bool findAndRemoveWord(QCString &s,const QCString &word)
7165 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
7167 while ((i=wordExp.match(s,p,&l))!=-1)
7169 if (s.mid(i,l)==word)
7171 if (i>0 && isspace((uchar)s.at(i-1)))
7173 else if (i+l<(int)s.length() && isspace(s.at(i+l)))
7175 s = s.left(i)+s.mid(i+l); // remove word + spacing
7183 /** Special version of QCString::stripWhiteSpace() that only strips
7184 * completely blank lines.
7185 * @param s the string to be stripped
7186 * @param docLine the line number corresponding to the start of the
7187 * string. This will be adjusted based on the number of lines stripped
7189 * @returns The stripped string.
7191 QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine)
7193 const char *p = s.data();
7196 // search for leading empty lines
7197 int i=0,li=-1,l=s.length();
7201 if (c==' ' || c=='\t' || c=='\r') i++;
7202 else if (c=='\n') i++,li=i,docLine++;
7206 // search for trailing empty lines
7212 if (c==' ' || c=='\t' || c=='\r') b--;
7213 else if (c=='\n') bi=b,b--;
7217 // return whole string if no leading or trailing lines where found
7218 if (li==-1 && bi==-1) return s;
7223 if (bi<=li) return 0; // only empty lines
7224 return s.mid(li,bi-li);
7228 void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
7229 const QCString &str,bool priority,const QCString &anchor)
7231 static bool searchEngine = Config_getBool(SEARCHENGINE);
7234 Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
7235 static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
7237 while ((i=wordPattern.match(str,p,&l))!=-1)
7239 Doxygen::searchIndex->addWord(str.mid(i,l),priority);
7246 //--------------------------------------------------------------------------
7248 static QDict<int> g_extLookup;
7250 static struct Lang2ExtMap
7252 const char *langName;
7253 const char *parserName;
7254 SrcLangExt parserId;
7258 // language parser parser option
7259 { "idl", "c", SrcLangExt_IDL },
7260 { "java", "c", SrcLangExt_Java },
7261 { "javascript", "c", SrcLangExt_JS },
7262 { "csharp", "c", SrcLangExt_CSharp },
7263 { "d", "c", SrcLangExt_D },
7264 { "php", "c", SrcLangExt_PHP },
7265 { "objective-c", "c", SrcLangExt_ObjC },
7266 { "c", "c", SrcLangExt_Cpp },
7267 { "c++", "c", SrcLangExt_Cpp },
7268 { "slice", "c", SrcLangExt_Slice },
7269 { "python", "python", SrcLangExt_Python },
7270 { "fortran", "fortran", SrcLangExt_Fortran },
7271 { "fortranfree", "fortranfree", SrcLangExt_Fortran },
7272 { "fortranfixed", "fortranfixed", SrcLangExt_Fortran },
7273 { "vhdl", "vhdl", SrcLangExt_VHDL },
7274 { "xml", "xml", SrcLangExt_XML },
7275 { "sql", "sql", SrcLangExt_SQL },
7276 { "tcl", "tcl", SrcLangExt_Tcl },
7277 { "md", "md", SrcLangExt_Markdown },
7278 { 0, 0, (SrcLangExt)0 }
7281 bool updateLanguageMapping(const QCString &extension,const QCString &language)
7283 const Lang2ExtMap *p = g_lang2extMap;
7284 QCString langName = language.lower();
7287 if (langName==p->langName) break;
7290 if (!p->langName) return FALSE;
7292 // found the language
7293 SrcLangExt parserId = p->parserId;
7294 QCString extName = extension.lower();
7295 if (extName.isEmpty()) return FALSE;
7296 if (extName.at(0)!='.') extName.prepend(".");
7297 if (g_extLookup.find(extension)!=0) // language was already register for this ext
7299 g_extLookup.remove(extension);
7301 //printf("registering extension %s\n",extName.data());
7302 g_extLookup.insert(extName,new int(parserId));
7303 if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
7305 err("Failed to assign extension %s to parser %s for language %s\n",
7306 extName.data(),p->parserName,language.data());
7310 //msg("Registered extension %s to language parser %s...\n",
7311 // extName.data(),language.data());
7316 void initDefaultExtensionMapping()
7318 // NOTE: when adding an extension, also add the extension in config.xml
7319 g_extLookup.setAutoDelete(TRUE);
7320 // extension parser id
7321 updateLanguageMapping(".dox", "c");
7322 updateLanguageMapping(".txt", "c"); // see bug 760836
7323 updateLanguageMapping(".doc", "c");
7324 updateLanguageMapping(".c", "c");
7325 updateLanguageMapping(".C", "c");
7326 updateLanguageMapping(".cc", "c");
7327 updateLanguageMapping(".CC", "c");
7328 updateLanguageMapping(".cxx", "c");
7329 updateLanguageMapping(".cpp", "c");
7330 updateLanguageMapping(".c++", "c");
7331 updateLanguageMapping(".ii", "c");
7332 updateLanguageMapping(".ixx", "c");
7333 updateLanguageMapping(".ipp", "c");
7334 updateLanguageMapping(".i++", "c");
7335 updateLanguageMapping(".inl", "c");
7336 updateLanguageMapping(".h", "c");
7337 updateLanguageMapping(".H", "c");
7338 updateLanguageMapping(".hh", "c");
7339 updateLanguageMapping(".HH", "c");
7340 updateLanguageMapping(".hxx", "c");
7341 updateLanguageMapping(".hpp", "c");
7342 updateLanguageMapping(".h++", "c");
7343 updateLanguageMapping(".idl", "idl");
7344 updateLanguageMapping(".ddl", "idl");
7345 updateLanguageMapping(".odl", "idl");
7346 updateLanguageMapping(".java", "java");
7347 //updateLanguageMapping(".as", "javascript"); // not officially supported
7348 //updateLanguageMapping(".js", "javascript"); // not officially supported
7349 updateLanguageMapping(".cs", "csharp");
7350 updateLanguageMapping(".d", "d");
7351 updateLanguageMapping(".php", "php");
7352 updateLanguageMapping(".php4", "php");
7353 updateLanguageMapping(".php5", "php");
7354 updateLanguageMapping(".inc", "php");
7355 updateLanguageMapping(".phtml", "php");
7356 updateLanguageMapping(".m", "objective-c");
7357 updateLanguageMapping(".M", "objective-c");
7358 updateLanguageMapping(".mm", "c"); // see bug746361
7359 updateLanguageMapping(".py", "python");
7360 updateLanguageMapping(".pyw", "python");
7361 updateLanguageMapping(".f", "fortran");
7362 updateLanguageMapping(".for", "fortran");
7363 updateLanguageMapping(".f90", "fortran");
7364 updateLanguageMapping(".f95", "fortran");
7365 updateLanguageMapping(".f03", "fortran");
7366 updateLanguageMapping(".f08", "fortran");
7367 updateLanguageMapping(".vhd", "vhdl");
7368 updateLanguageMapping(".vhdl", "vhdl");
7369 updateLanguageMapping(".tcl", "tcl");
7370 updateLanguageMapping(".ucf", "vhdl");
7371 updateLanguageMapping(".qsf", "vhdl");
7372 updateLanguageMapping(".md", "md");
7373 updateLanguageMapping(".markdown", "md");
7374 updateLanguageMapping(".ice", "slice");
7377 void addCodeOnlyMappings()
7379 updateLanguageMapping(".xml", "xml");
7380 updateLanguageMapping(".sql", "sql");
7383 SrcLangExt getLanguageFromFileName(const QCString& fileName)
7385 int i = fileName.findRev('.');
7386 if (i!=-1) // name has an extension
7388 QCString extStr=fileName.right(fileName.length()-i).lower();
7389 if (!extStr.isEmpty()) // non-empty extension
7391 int *pVal=g_extLookup.find(extStr);
7392 if (pVal) // listed extension
7394 //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
7395 return (SrcLangExt)*pVal;
7399 //printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
7400 return SrcLangExt_Cpp; // not listed => assume C-ish language.
7403 //--------------------------------------------------------------------------
7405 MemberDef *getMemberFromSymbol(Definition *scope,FileDef *fileScope,
7409 (scope->definitionType()!=Definition::TypeClass &&
7410 scope->definitionType()!=Definition::TypeNamespace
7414 scope=Doxygen::globalScope;
7419 return 0; // no name was given
7421 DefinitionIntf *di = Doxygen::symbolMap->find(name);
7423 return 0; // could not find any matching symbols
7425 // mostly copied from getResolvedClassRec()
7426 QCString explicitScopePart;
7427 int qualifierIndex = computeQualifiedIndex(name);
7428 if (qualifierIndex!=-1)
7430 explicitScopePart = name.left(qualifierIndex);
7431 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
7432 name = name.mid(qualifierIndex+2);
7434 //printf("explicitScopePart=%s\n",explicitScopePart.data());
7436 int minDistance = 10000;
7437 MemberDef *bestMatch = 0;
7439 if (di->definitionType()==DefinitionIntf::TypeSymbolList)
7441 //printf("multiple matches!\n");
7442 // find the closest closest matching definition
7443 DefinitionListIterator dli(*(DefinitionList*)di);
7445 for (dli.toFirst();(d=dli.current());++dli)
7447 if (d->definitionType()==Definition::TypeMember)
7449 g_visitedNamespaces.clear();
7450 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
7451 if (distance!=-1 && distance<minDistance)
7453 minDistance = distance;
7454 bestMatch = (MemberDef *)d;
7455 //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
7460 else if (di->definitionType()==Definition::TypeMember)
7462 //printf("unique match!\n");
7463 Definition *d = (Definition *)di;
7464 g_visitedNamespaces.clear();
7465 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
7466 if (distance!=-1 && distance<minDistance)
7468 minDistance = distance;
7469 bestMatch = (MemberDef *)d;
7470 //printf("new best match %s distance=%d\n",bestMatch->qualifiedName().data(),distance);
7476 /*! Returns true iff the given name string appears to be a typedef in scope. */
7477 bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
7479 MemberDef *bestMatch = getMemberFromSymbol(scope,fileScope,n);
7481 if (bestMatch && bestMatch->isTypedef())
7482 return TRUE; // closest matching symbol is a typedef
7487 const char *writeUtf8Char(FTextStream &t,const char *s)
7491 if (c<0) // multibyte character
7493 if (((uchar)c&0xE0)==0xC0)
7495 t << *s++; // 11xx.xxxx: >=2 byte character
7497 if (((uchar)c&0xF0)==0xE0)
7499 t << *s++; // 111x.xxxx: >=3 byte character
7501 if (((uchar)c&0xF8)==0xF0)
7503 t << *s++; // 1111.xxxx: >=4 byte character
7505 if (((uchar)c&0xFC)==0xF8)
7507 t << *s++; // 1111.1xxx: >=5 byte character
7509 if (((uchar)c&0xFE)==0xFC)
7511 t << *s++; // 1111.1xxx: 6 byte character
7517 int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos)
7520 if (startPos>=len) return len;
7521 char c = utf8Str[startPos];
7522 if (c<0) // multibyte utf-8 character
7524 if (((uchar)c&0xE0)==0xC0)
7526 bytes+=1; // 11xx.xxxx: >=2 byte character
7528 if (((uchar)c&0xF0)==0xE0)
7530 bytes+=2; // 111x.xxxx: >=3 byte character
7532 if (((uchar)c&0xF8)==0xF0)
7534 bytes+=3; // 1111.xxxx: >=4 byte character
7536 if (((uchar)c&0xFC)==0xF8)
7538 bytes+=4; // 1111.1xxx: >=5 byte character
7540 if (((uchar)c&0xFE)==0xFC)
7542 bytes+=5; // 1111.1xxx: 6 byte character
7545 else if (c=='&') // skip over character entities
7547 static QRegExp re1("&#[0-9]+;"); // numerical entity
7548 static QRegExp re2("&[A-Z_a-z]+;"); // named entity
7550 int i1 = re1.match(utf8Str,startPos,&l1);
7551 int i2 = re2.match(utf8Str,startPos,&l2);
7561 return startPos+bytes;
7564 QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
7565 const QCString &doc,const QCString &fileName,int lineNr)
7568 if (doc.isEmpty()) return s.data();
7570 DocNode *root = validatingParseDoc(fileName,lineNr,
7571 (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
7572 TextDocVisitor *visitor = new TextDocVisitor(t);
7573 root->accept(visitor);
7576 QCString result = convertCharEntitiesToUTF8(s.data()).stripWhiteSpace();
7579 int l=result.length();
7580 while ((i=nextUtf8CharPosition(result,l,i))<l)
7583 if (charCnt>=80) break;
7585 if (charCnt>=80) // try to truncate the string
7587 while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100)
7590 if (result.at(i)==',' ||
7591 result.at(i)=='.' ||
7592 result.at(i)=='!' ||
7595 i++; // we want to be "behind" last inspected character
7600 if ( i < l) result=result.left(i)+"...";
7601 return result.data();
7604 //--------------------------------------------------------------------------------------
7606 static QDict<void> aliasesProcessed;
7608 static QCString expandAliasRec(const QCString s,bool allowRecursion=FALSE);
7612 Marker(int p, int n,int s) : pos(p),number(n),size(s) {}
7613 int pos; // position in the string
7614 int number; // argument number
7615 int size; // size of the marker
7618 /** For a string \a s that starts with a command name, returns the character
7619 * offset within that string representing the first character after the
7620 * command. For an alias with argument, this is the offset to the
7621 * character just after the argument list.
7624 * - s=="a b" returns 1
7625 * - s=="a{2,3} b" returns 6
7626 * = s=="#" returns 0
7628 static int findEndOfCommand(const char *s)
7635 while ((c=*p) && isId(c)) p++;
7638 QCString args = extractAliasArgs(p,0);
7646 /** Replaces the markers in an alias definition \a aliasValue
7647 * with the corresponding values found in the comma separated argument
7648 * list \a argList and the returns the result after recursive alias expansion.
7650 static QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
7652 //printf("----- replaceAliasArguments(val=[%s],args=[%s])\n",aliasValue.data(),argList.data());
7654 // first make a list of arguments from the comma separated argument list
7655 QList<QCString> args;
7656 args.setAutoDelete(TRUE);
7657 int i,l=(int)argList.length();
7661 char c = argList.at(i);
7662 if (c==',' && (i==0 || argList.at(i-1)!='\\'))
7664 args.append(new QCString(argList.mid(s,i-s)));
7665 s=i+1; // start of next argument
7667 else if (c=='@' || c=='\\')
7669 // check if this is the start of another aliased command (see bug704172)
7670 i+=findEndOfCommand(argList.data()+i+1);
7673 if (l>s) args.append(new QCString(argList.right(l-s)));
7674 //printf("found %d arguments\n",args.count());
7676 // next we look for the positions of the markers and add them to a list
7677 QList<Marker> markerList;
7678 markerList.setAutoDelete(TRUE);
7679 l = aliasValue.length();
7684 if (markerStart==0 && aliasValue.at(i)=='\\') // start of a \xx marker
7688 else if (markerStart>0 && aliasValue.at(i)>='0' && aliasValue.at(i)<='9')
7690 // read digit that make up the marker number
7695 if (markerStart>0 && markerEnd>markerStart) // end of marker
7697 int markerLen = markerEnd-markerStart;
7698 markerList.append(new Marker(markerStart-1, // include backslash
7699 atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
7700 //printf("found marker at %d with len %d and number %d\n",
7701 // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
7703 markerStart=0; // outside marker
7711 if (markerStart>0 && markerEnd>markerStart)
7713 int markerLen = markerEnd-markerStart;
7714 markerList.append(new Marker(markerStart-1, // include backslash
7715 atoi(aliasValue.mid(markerStart,markerLen)),markerLen+1));
7716 //printf("found marker at %d with len %d and number %d\n",
7717 // markerStart-1,markerLen+1,atoi(aliasValue.mid(markerStart,markerLen)));
7720 // then we replace the markers with the corresponding arguments in one pass
7723 for (i=0;i<(int)markerList.count();i++)
7725 Marker *m = markerList.at(i);
7726 result+=aliasValue.mid(p,m->pos-p);
7727 //printf("part before marker %d: '%s'\n",i,aliasValue.mid(p,m->pos-p).data());
7728 if (m->number>0 && m->number<=(int)args.count()) // valid number
7730 result+=expandAliasRec(*args.at(m->number-1),TRUE);
7731 //printf("marker index=%d pos=%d number=%d size=%d replacement %s\n",i,m->pos,m->number,m->size,
7732 // args.at(m->number-1)->data());
7734 p=m->pos+m->size; // continue after the marker
7736 result+=aliasValue.right(l-p); // append remainder
7737 //printf("string after replacement of markers: '%s'\n",result.data());
7739 // expand the result again
7740 result = substitute(result,"\\{","{");
7741 result = substitute(result,"\\}","}");
7742 result = expandAliasRec(substitute(result,"\\,",","));
7747 static QCString escapeCommas(const QCString &s)
7750 const char *p = s.data();
7754 if (c==',' && pc!='\\')
7765 //printf("escapeCommas: '%s'->'%s'\n",s.data(),result.data());
7766 return result.data();
7769 static QCString expandAliasRec(const QCString s,bool allowRecursion)
7772 static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
7775 while ((i=cmdPat.match(value,p,&l))!=-1)
7777 result+=value.mid(p,i-p);
7778 QCString args = extractAliasArgs(value,i+l);
7779 bool hasArgs = !args.isEmpty(); // found directly after command
7780 int argsLen = args.length();
7781 QCString cmd = value.mid(i+1,l-1);
7782 QCString cmdNoArgs = cmd;
7786 numArgs = countAliasArguments(args);
7787 cmd += QCString().sprintf("{%d}",numArgs); // alias name + {n}
7789 QCString *aliasText=Doxygen::aliasDict.find(cmd);
7790 if (numArgs>1 && aliasText==0)
7791 { // in case there is no command with numArgs parameters, but there is a command with 1 parameter,
7792 // we also accept all text as the argument of that command (so you don't have to escape commas)
7793 aliasText=Doxygen::aliasDict.find(cmdNoArgs+"{1}");
7796 cmd = cmdNoArgs+"{1}";
7797 args = escapeCommas(args); // escape , so that everything is seen as one argument
7800 //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s' aliasText=%s\n",
7801 // s.data(),cmd.data(),numArgs,args.data(),aliasText?aliasText->data():"<none>");
7802 if ((allowRecursion || aliasesProcessed.find(cmd)==0) && aliasText) // expand the alias
7804 //printf("is an alias!\n");
7805 if (!allowRecursion) aliasesProcessed.insert(cmd,(void *)0x8);
7806 QCString val = *aliasText;
7809 val = replaceAliasArguments(val,args);
7810 //printf("replace '%s'->'%s' args='%s'\n",
7811 // aliasText->data(),val.data(),args.data());
7813 result+=expandAliasRec(val);
7814 if (!allowRecursion) aliasesProcessed.remove(cmd);
7816 if (hasArgs) p+=argsLen+2;
7818 else // command is not an alias
7820 //printf("not an alias!\n");
7821 result+=value.mid(i,l);
7825 result+=value.right(value.length()-p);
7827 //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
7832 int countAliasArguments(const QCString argList)
7835 int l = argList.length();
7839 char c = argList.at(i);
7840 if (c==',' && (i==0 || argList.at(i-1)!='\\')) count++;
7841 else if (c=='@' || c=='\\')
7843 // check if this is the start of another aliased command (see bug704172)
7844 i+=findEndOfCommand(argList.data()+i+1);
7847 //printf("countAliasArguments=%d\n",count);
7851 QCString extractAliasArgs(const QCString &args,int pos)
7856 if (args.at(pos)=='{') // alias has argument
7858 for (i=pos;i<(int)args.length();i++)
7862 if (args.at(i)=='{') bc++;
7863 if (args.at(i)=='}') bc--;
7864 prevChar=args.at(i);
7873 //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
7874 return args.mid(pos+1,i-pos-1);
7881 QCString resolveAliasCmd(const QCString aliasCmd)
7884 aliasesProcessed.clear();
7885 //printf("Expanding: '%s'\n",aliasCmd.data());
7886 result = expandAliasRec(aliasCmd);
7887 //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
7891 QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
7894 aliasesProcessed.clear();
7895 // avoid expanding this command recursively
7896 aliasesProcessed.insert(aliasName,(void *)0x8);
7897 // expand embedded commands
7898 //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
7899 result = expandAliasRec(aliasValue);
7900 //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
7904 void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
7907 ol.startConstraintList(theTranslator->trTypeConstraints());
7908 ArgumentListIterator ali(*al);
7910 for (;(a=ali.current());++ali)
7912 ol.startConstraintParam();
7913 ol.parseText(a->name);
7914 ol.endConstraintParam();
7915 ol.startConstraintType();
7916 linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
7917 ol.endConstraintType();
7918 ol.startConstraintDocs();
7919 ol.generateDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
7920 ol.endConstraintDocs();
7922 ol.endConstraintList();
7925 //----------------------------------------------------------------------------
7929 #ifdef TRACINGSUPPORT
7930 void *backtraceFrames[128];
7931 int frameCount = backtrace(backtraceFrames, 128);
7932 static char cmd[40960];
7934 p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
7935 for (int x = 0; x < frameCount; x++)
7937 p += sprintf(p,"%p ", backtraceFrames[x]);
7939 fprintf(stderr,"========== STACKTRACE START ==============\n");
7940 if (FILE *fp = popen(cmd, "r"))
7943 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
7945 fwrite(resBuf, 1, len, stderr);
7949 fprintf(stderr,"============ STACKTRACE END ==============\n");
7950 //fprintf(stderr,"%s\n", frameStrings[x]);
7954 static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size,
7955 const char *inputEncoding,const char *outputEncoding)
7957 if (inputEncoding==0 || outputEncoding==0) return size;
7958 if (qstricmp(inputEncoding,outputEncoding)==0) return size;
7959 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
7960 if (cd==(void *)(-1))
7962 err("unsupported character conversion: '%s'->'%s': %s\n"
7963 "Check the INPUT_ENCODING setting in the config file!\n",
7964 inputEncoding,outputEncoding,strerror(errno));
7967 int tmpBufSize=size*4+1;
7968 BufStr tmpBuf(tmpBufSize);
7970 size_t oLeft=tmpBufSize;
7971 char *srcPtr = srcBuf.data();
7972 char *dstPtr = tmpBuf.data();
7974 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
7976 newSize = tmpBufSize-(int)oLeft;
7977 srcBuf.shrink(newSize);
7978 strncpy(srcBuf.data(),tmpBuf.data(),newSize);
7979 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
7983 err("%s: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
7984 fileName,inputEncoding,outputEncoding);
7987 portable_iconv_close(cd);
7991 //! read a file name \a fileName and optionally filter and transcode it
7992 bool readInputFile(const char *fileName,BufStr &inBuf,bool filter,bool isSourceCode)
7996 //uint oldPos = dest.curPos();
7997 //printf(".......oldPos=%d\n",oldPos);
7999 QFileInfo fi(fileName);
8000 if (!fi.exists()) return FALSE;
8001 QCString filterName = getFileFilter(fileName,isSourceCode);
8002 if (filterName.isEmpty() || !filter)
8005 if (!f.open(IO_ReadOnly))
8007 err("could not open file %s\n",fileName);
8013 if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
8015 err("problems while reading file %s\n",fileName);
8021 QCString cmd=filterName+" \""+fileName+"\"";
8022 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",qPrint(cmd));
8023 FILE *f=portable_popen(cmd,"r");
8026 err("could not execute filter %s\n",filterName.data());
8029 const int bufSize=1024;
8032 while ((numRead=(int)fread(buf,1,bufSize,f))>0)
8034 //printf(">>>>>>>>Reading %d bytes\n",numRead);
8035 inBuf.addArray(buf,numRead),size+=numRead;
8038 inBuf.at(inBuf.curPos()) ='\0';
8039 Debug::print(Debug::FilterOutput, 0, "Filter output\n");
8040 Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",qPrint(inBuf));
8045 (((uchar)inBuf.at(0)==0xFF && (uchar)inBuf.at(1)==0xFE) || // Little endian BOM
8046 ((uchar)inBuf.at(0)==0xFE && (uchar)inBuf.at(1)==0xFF) // big endian BOM
8048 ) // UCS-2 encoded file
8050 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
8054 (uchar)inBuf.at(0)==0xEF &&
8055 (uchar)inBuf.at(1)==0xBB &&
8056 (uchar)inBuf.at(2)==0xBF
8057 ) // UTF-8 encoded file
8059 inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
8061 else // transcode according to the INPUT_ENCODING setting
8063 // do character transcoding if needed.
8064 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
8065 Config_getString(INPUT_ENCODING),"UTF-8");
8068 //inBuf.addChar('\n'); /* to prevent problems under Windows ? */
8070 // and translate CR's
8071 size=inBuf.curPos()-start;
8072 int newSize=filterCRLF(inBuf.data()+start,size);
8073 //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
8074 if (newSize!=size) // we removed chars
8076 inBuf.shrink(newSize); // resize the array
8077 //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
8083 // Replace %word by word in title
8084 QCString filterTitle(const QCString &title)
8087 static QRegExp re("%[A-Z_a-z]");
8089 while ((i=re.match(title,p,&l))!=-1)
8091 tf+=title.mid(p,i-p);
8092 tf+=title.mid(i+1,l-1); // skip %
8095 tf+=title.right(title.length()-p);
8099 //----------------------------------------------------------------------------
8100 // returns TRUE if the name of the file represented by `fi' matches
8101 // one of the file patterns in the `patList' list.
8103 bool patternMatch(const QFileInfo &fi,const QStrList *patList)
8105 static bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES);
8108 // For Windows/Mac, always do the case insensitive match
8109 #if defined(_WIN32) || defined(__MACOSX__)
8110 caseSenseNames = FALSE;
8115 QStrListIterator it(*patList);
8118 QCString fn = fi.fileName().data();
8119 QCString fp = fi.filePath().data();
8120 QCString afp= fi.absFilePath().data();
8122 for (it.toFirst();(pattern=it.current());++it)
8124 if (!pattern.isEmpty())
8126 int i=pattern.find('=');
8127 if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
8129 QRegExp re(pattern,caseSenseNames,TRUE);
8131 found = re.match(fn)!=-1 ||
8135 //printf("Matching `%s' against pattern `%s' found=%d\n",
8136 // fi->fileName().data(),pattern.data(),found);
8143 #if 0 // move to HtmlGenerator::writeSummaryLink
8144 void writeSummaryLink(OutputList &ol,const char *label,const char *title,
8145 bool &first,const char *file)
8149 ol.writeString(" <div class=\"summary\">\n");
8154 ol.writeString(" |\n");
8158 ol.writeString("<a href=\"");
8159 ol.writeString(file);
8160 ol.writeString(Doxygen::htmlFileExtension);
8164 ol.writeString("<a href=\"#");
8165 ol.writeString(label);
8167 ol.writeString("\">");
8168 ol.writeString(title);
8169 ol.writeString("</a>");
8173 QCString externalLinkTarget()
8175 static bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW);
8176 if (extLinksInWindow) return "target=\"_blank\" "; else return "";
8179 QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
8184 QCString *dest = Doxygen::tagDestinationDict[ref];
8188 int l = result.length();
8189 if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
8190 { // relative path -> prepend relPath.
8191 result.prepend(relPath);
8192 l+=relPath.length();
8195 result.prepend("doxygen=\""+ref+":");
8198 if (l>0 && result.at(l-1)!='/') result+='/';
8199 if (!href) result.append("\" ");
8209 /** Writes the intensity only bitmap represented by \a data as an image to
8210 * directory \a dir using the colors defined by HTML_COLORSTYLE_*.
8212 void writeColoredImgData(const char *dir,ColoredImgDataItem data[])
8214 static int hue = Config_getInt(HTML_COLORSTYLE_HUE);
8215 static int sat = Config_getInt(HTML_COLORSTYLE_SAT);
8216 static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
8220 fileName=(QCString)dir+"/"+data->name;
8222 if (f.open(IO_WriteOnly))
8224 ColoredImage img(data->width,data->height,data->content,data->alpha,
8230 fprintf(stderr,"Warning: Cannot open file %s for writing\n",data->name);
8232 Doxygen::indexList->addImageFile(data->name);
8237 /** Replaces any markers of the form \#\#AA in input string \a str
8238 * by new markers of the form \#AABBCC, where \#AABBCC represents a
8239 * valid color, based on the intensity represented by hex number AA
8240 * and the current HTML_COLORSTYLE_* settings.
8242 QCString replaceColorMarkers(const char *str)
8246 if (s.isEmpty()) return result;
8247 static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]");
8248 static const char hex[] = "0123456789ABCDEF";
8249 static int hue = Config_getInt(HTML_COLORSTYLE_HUE);
8250 static int sat = Config_getInt(HTML_COLORSTYLE_SAT);
8251 static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
8252 int i,l,sl=s.length(),p=0;
8253 while ((i=re.match(s,p,&l))!=-1)
8255 result+=s.mid(p,i-p);
8256 QCString lumStr = s.mid(i+2,l-2);
8257 #define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
8258 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
8259 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
8263 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
8264 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
8265 pow(level/255.0,gamma/100.0),&r,&g,&b);
8266 red = (int)(r*255.0);
8267 green = (int)(g*255.0);
8268 blue = (int)(b*255.0);
8271 colStr[1]=hex[red>>4];
8272 colStr[2]=hex[red&0xf];
8273 colStr[3]=hex[green>>4];
8274 colStr[4]=hex[green&0xf];
8275 colStr[5]=hex[blue>>4];
8276 colStr[6]=hex[blue&0xf];
8278 //printf("replacing %s->%s (level=%d)\n",lumStr.data(),colStr,level);
8282 result+=s.right(sl-p);
8286 /** Copies the contents of file with name \a src to the newly created
8287 * file with name \a dest. Returns TRUE if successful.
8289 bool copyFile(const QCString &src,const QCString &dest)
8292 if (sf.open(IO_ReadOnly))
8296 if (df.open(IO_WriteOnly))
8298 char *buffer = new char[fi.size()];
8299 sf.readBlock(buffer,fi.size());
8300 df.writeBlock(buffer,fi.size());
8306 err("could not write to file %s\n",dest.data());
8312 err("could not open user specified file %s\n",src.data());
8318 /** Returns the section of text, in between a pair of markers.
8319 * Full lines are returned, excluding the lines on which the markers appear.
8320 * \sa routine lineBlock
8322 QCString extractBlock(const QCString text,const QCString marker)
8328 // find the character positions of the markers
8329 int m1 = text.find(marker);
8330 if (m1==-1) return result;
8331 int m2 = text.find(marker,m1+marker.length());
8332 if (m2==-1) return result;
8334 // find start and end line positions for the markers
8336 while (!found && (i=text.find('\n',p))!=-1)
8338 found = (p<=m1 && m1<i); // found the line with the start marker
8345 while ((i=text.find('\n',p))!=-1)
8347 if (p<=m2 && m2<i) // found the line with the end marker
8356 if (l2==-1) // marker at last line without newline (see bug706874)
8360 //printf("text=[%s]\n",text.mid(l1,l2-l1).data());
8361 return l2>l1 ? text.mid(l1,l2-l1) : QCString();
8364 /** Returns the line number of the line following the line with the marker.
8365 * \sa routine extractBlock
8367 int lineBlock(const QCString text,const QCString marker)
8373 // find the character positions of the first marker
8374 int m1 = text.find(marker);
8375 if (m1==-1) return result;
8377 // find start line positions for the markers
8378 while (!found && (i=text.find('\n',p))!=-1)
8380 found = (p<=m1 && m1<i); // found the line with the start marker
8387 /** Returns a string representation of \a lang. */
8388 QCString langToString(SrcLangExt lang)
8392 case SrcLangExt_Unknown: return "Unknown";
8393 case SrcLangExt_IDL: return "IDL";
8394 case SrcLangExt_Java: return "Java";
8395 case SrcLangExt_CSharp: return "C#";
8396 case SrcLangExt_D: return "D";
8397 case SrcLangExt_PHP: return "PHP";
8398 case SrcLangExt_ObjC: return "Objective-C";
8399 case SrcLangExt_Cpp: return "C++";
8400 case SrcLangExt_JS: return "Javascript";
8401 case SrcLangExt_Python: return "Python";
8402 case SrcLangExt_Fortran: return "Fortran";
8403 case SrcLangExt_VHDL: return "VHDL";
8404 case SrcLangExt_XML: return "XML";
8405 case SrcLangExt_SQL: return "SQL";
8406 case SrcLangExt_Tcl: return "Tcl";
8407 case SrcLangExt_Markdown: return "Markdown";
8408 case SrcLangExt_Slice: return "Slice";
8413 /** Returns the scope separator to use given the programming language \a lang */
8414 QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope)
8416 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp || lang==SrcLangExt_VHDL || lang==SrcLangExt_Python)
8420 else if (lang==SrcLangExt_PHP && !classScope)
8430 /** Corrects URL \a url according to the relative path \a relPath.
8431 * Returns the corrected URL. For absolute URLs no correction will be done.
8433 QCString correctURL(const QCString &url,const QCString &relPath)
8435 QCString result = url;
8436 if (!relPath.isEmpty() &&
8437 url.left(5)!="http:" && url.left(6)!="https:" &&
8438 url.left(4)!="ftp:" && url.left(5)!="file:")
8440 result.prepend(relPath);
8445 //---------------------------------------------------------------------------
8447 bool protectionLevelVisible(Protection prot)
8449 static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
8450 static bool extractPackage = Config_getBool(EXTRACT_PACKAGE);
8452 return (prot!=Private && prot!=Package) ||
8453 (prot==Private && extractPrivate) ||
8454 (prot==Package && extractPackage);
8457 //---------------------------------------------------------------------------
8459 QCString stripIndentation(const QCString &s)
8461 if (s.isEmpty()) return s; // empty string -> we're done
8463 //printf("stripIndentation:\n%s\n------\n",s.data());
8464 // compute minimum indentation over all lines
8465 const char *p=s.data();
8468 int minIndent=1000000; // "infinite"
8469 bool searchIndent=TRUE;
8470 static int tabSize=Config_getInt(TAB_SIZE);
8473 if (c=='\t') indent+=tabSize - (indent%tabSize);
8474 else if (c=='\n') indent=0,searchIndent=TRUE;
8475 else if (c==' ') indent++;
8476 else if (searchIndent)
8479 if (indent<minIndent) minIndent=indent;
8483 // no indent to remove -> we're done
8484 if (minIndent==0) return s;
8486 // remove minimum indentation for each line
8492 if (c=='\n') // start of new line
8497 else if (indent<minIndent) // skip until we reach minIndent
8501 int newIndent = indent+tabSize-(indent%tabSize);
8503 while (i>minIndent) // if a tab crosses the minIndent boundary fill the rest with spaces
8515 else // copy anything until the end of the line
8522 return result.data();
8526 bool fileVisibleInIndex(FileDef *fd,bool &genSourceFile)
8528 static bool allExternals = Config_getBool(ALLEXTERNALS);
8529 bool isDocFile = fd->isDocumentationFile();
8530 genSourceFile = !isDocFile && fd->generateSourceFile();
8531 return ( ((allExternals && fd->isLinkable()) ||
8532 fd->isLinkableInProject()
8538 void addDocCrossReference(MemberDef *src,MemberDef *dst)
8540 //printf("--> addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data());
8541 if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types
8542 if ((dst->hasReferencedByRelation() || dst->hasCallerGraph()) &&
8543 src->showInCallGraph()
8546 dst->addSourceReferencedBy(src);
8547 MemberDef *mdDef = dst->memberDefinition();
8550 mdDef->addSourceReferencedBy(src);
8552 MemberDef *mdDecl = dst->memberDeclaration();
8555 mdDecl->addSourceReferencedBy(src);
8558 if ((src->hasReferencesRelation() || src->hasCallGraph()) &&
8559 src->showInCallGraph()
8562 src->addSourceReferences(dst);
8563 MemberDef *mdDef = src->memberDefinition();
8566 mdDef->addSourceReferences(dst);
8568 MemberDef *mdDecl = src->memberDeclaration();
8571 mdDecl->addSourceReferences(dst);
8576 //--------------------------------------------------------------------------------------
8578 /*! @brief Get one unicode character as an unsigned integer from utf-8 string
8580 * @param s utf-8 encoded string
8581 * @param idx byte position of given string \a s.
8582 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT
8583 * @see getNextUtf8OrToLower()
8584 * @see getNextUtf8OrToUpper()
8586 uint getUtf8Code( const QCString& s, int idx )
8588 const int length = s.length();
8589 if (idx >= length) { return 0; }
8590 const uint c0 = (uchar)s.at(idx);
8591 if ( c0 < 0xC2 || c0 >= 0xF8 ) // 1 byte character
8595 if (idx+1 >= length) { return 0; }
8596 const uint c1 = ((uchar)s.at(idx+1)) & 0x3f;
8597 if ( c0 < 0xE0 ) // 2 byte character
8599 return ((c0 & 0x1f) << 6) | c1;
8601 if (idx+2 >= length) { return 0; }
8602 const uint c2 = ((uchar)s.at(idx+2)) & 0x3f;
8603 if ( c0 < 0xF0 ) // 3 byte character
8605 return ((c0 & 0x0f) << 12) | (c1 << 6) | c2;
8607 if (idx+3 >= length) { return 0; }
8609 const uint c3 = ((uchar)s.at(idx+3)) & 0x3f;
8610 return ((c0 & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
8614 /*! @brief Returns one unicode character as an unsigned integer
8615 * from utf-8 string, making the character lower case if it was upper case.
8617 * @param s utf-8 encoded string
8618 * @param idx byte position of given string \a s.
8619 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'A'-'Z'
8620 * @see getNextUtf8Code()
8622 uint getUtf8CodeToLower( const QCString& s, int idx )
8624 const uint v = getUtf8Code( s, idx );
8625 return v < 0x7f ? tolower( v ) : v;
8629 /*! @brief Returns one unicode character as an unsigned integer
8630 * from utf-8 string, making the character upper case if it was lower case.
8632 * @param s utf-8 encoded string
8633 * @param idx byte position of given string \a s.
8634 * @return the unicode codepoint, 0 - MAX_UNICODE_CODEPOINT, excludes 'A'-'Z'
8635 * @see getNextUtf8Code()
8637 uint getUtf8CodeToUpper( const QCString& s, int idx )
8639 const uint v = getUtf8Code( s, idx );
8640 return v < 0x7f ? toupper( v ) : v;
8643 //--------------------------------------------------------------------------------------
8645 bool namespaceHasVisibleChild(NamespaceDef *nd,bool includeClasses,bool filterClasses,ClassDef::CompoundType ct)
8647 if (nd->getNamespaceSDict())
8649 NamespaceSDict::Iterator cnli(*nd->getNamespaceSDict());
8651 for (cnli.toFirst();(cnd=cnli.current());++cnli)
8653 if (cnd->isLinkableInProject() && cnd->localName().find('@')==-1)
8657 else if (namespaceHasVisibleChild(cnd,includeClasses,filterClasses,ct))
8665 ClassSDict *d = nd->getClassSDict();
8668 if (ct == ClassDef::Interface)
8670 d = nd->getInterfaceSDict();
8672 else if (ct == ClassDef::Struct)
8674 d = nd->getStructSDict();
8676 else if (ct == ClassDef::Exception)
8678 d = nd->getExceptionSDict();
8684 ClassSDict::Iterator cli(*d);
8686 for (;(cd=cli.current());++cli)
8688 if (cd->isLinkableInProject() && cd->templateMaster()==0)
8698 //----------------------------------------------------------------------------
8700 bool classVisibleInIndex(ClassDef *cd)
8702 static bool allExternals = Config_getBool(ALLEXTERNALS);
8703 return (allExternals && cd->isLinkable()) || cd->isLinkableInProject();
8706 //----------------------------------------------------------------------------
8708 QCString extractDirection(QCString &docs)
8710 QRegExp re("\\[[^\\]]+\\]"); // [...]
8712 if (re.match(docs,0,&l)==0)
8714 int inPos = docs.find("in", 1,FALSE);
8715 int outPos = docs.find("out",1,FALSE);
8716 bool input = inPos!=-1 && inPos<l;
8717 bool output = outPos!=-1 && outPos<l;
8718 if (input || output) // in,out attributes
8720 docs = docs.mid(l); // strip attributes
8721 if (input && output) return "[in,out]";
8722 else if (input) return "[in]";
8723 else if (output) return "[out]";
8729 //-----------------------------------------------------------
8731 /** Computes for a given list type \a inListType, which are the
8732 * the corresponding list type(s) in the base class that are to be
8733 * added to this list.
8735 * So for public inheritance, the mapping is 1-1, so outListType1=inListType
8736 * Private members are to be hidden completely.
8738 * For protected inheritance, both protected and public members of the
8739 * base class should be joined in the protected member section.
8741 * For private inheritance, both protected and public members of the
8742 * base class should be joined in the private member section.
8744 void convertProtectionLevel(
8745 MemberListType inListType,
8751 static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
8752 // default representing 1-1 mapping
8753 *outListType1=inListType;
8757 switch (inListType) // in the private section of the derived class,
8758 // the private section of the base class should not
8761 case MemberListType_priMethods:
8762 case MemberListType_priStaticMethods:
8763 case MemberListType_priSlots:
8764 case MemberListType_priAttribs:
8765 case MemberListType_priStaticAttribs:
8766 case MemberListType_priTypes:
8774 else if (inProt==Protected) // Protected inheritance
8776 switch (inListType) // in the protected section of the derived class,
8777 // both the public and protected members are shown
8780 case MemberListType_pubMethods:
8781 case MemberListType_pubStaticMethods:
8782 case MemberListType_pubSlots:
8783 case MemberListType_pubAttribs:
8784 case MemberListType_pubStaticAttribs:
8785 case MemberListType_pubTypes:
8786 case MemberListType_priMethods:
8787 case MemberListType_priStaticMethods:
8788 case MemberListType_priSlots:
8789 case MemberListType_priAttribs:
8790 case MemberListType_priStaticAttribs:
8791 case MemberListType_priTypes:
8796 case MemberListType_proMethods:
8797 *outListType2=MemberListType_pubMethods;
8799 case MemberListType_proStaticMethods:
8800 *outListType2=MemberListType_pubStaticMethods;
8802 case MemberListType_proSlots:
8803 *outListType2=MemberListType_pubSlots;
8805 case MemberListType_proAttribs:
8806 *outListType2=MemberListType_pubAttribs;
8808 case MemberListType_proStaticAttribs:
8809 *outListType2=MemberListType_pubStaticAttribs;
8811 case MemberListType_proTypes:
8812 *outListType2=MemberListType_pubTypes;
8818 else if (inProt==Private)
8820 switch (inListType) // in the private section of the derived class,
8821 // both the public and protected members are shown
8824 case MemberListType_pubMethods:
8825 case MemberListType_pubStaticMethods:
8826 case MemberListType_pubSlots:
8827 case MemberListType_pubAttribs:
8828 case MemberListType_pubStaticAttribs:
8829 case MemberListType_pubTypes:
8830 case MemberListType_proMethods:
8831 case MemberListType_proStaticMethods:
8832 case MemberListType_proSlots:
8833 case MemberListType_proAttribs:
8834 case MemberListType_proStaticAttribs:
8835 case MemberListType_proTypes:
8840 case MemberListType_priMethods:
8843 *outListType1=MemberListType_pubMethods;
8844 *outListType2=MemberListType_proMethods;
8852 case MemberListType_priStaticMethods:
8855 *outListType1=MemberListType_pubStaticMethods;
8856 *outListType2=MemberListType_proStaticMethods;
8864 case MemberListType_priSlots:
8867 *outListType1=MemberListType_pubSlots;
8868 *outListType2=MemberListType_proSlots;
8876 case MemberListType_priAttribs:
8879 *outListType1=MemberListType_pubAttribs;
8880 *outListType2=MemberListType_proAttribs;
8888 case MemberListType_priStaticAttribs:
8891 *outListType1=MemberListType_pubStaticAttribs;
8892 *outListType2=MemberListType_proStaticAttribs;
8900 case MemberListType_priTypes:
8903 *outListType1=MemberListType_pubTypes;
8904 *outListType2=MemberListType_proTypes;
8916 //printf("convertProtectionLevel(type=%d prot=%d): %d,%d\n",
8917 // inListType,inProt,*outListType1,*outListType2);
8920 bool mainPageHasTitle()
8922 if (Doxygen::mainPage==0) return FALSE;
8923 if (Doxygen::mainPage->title().isEmpty()) return FALSE;
8924 if (Doxygen::mainPage->title().lower()=="notitle") return FALSE;
8928 QCString getDotImageExtension(void)
8930 QCString imgExt = Config_getEnum(DOT_IMAGE_FORMAT);
8931 imgExt = imgExt.replace( QRegExp(":.*"), "" );
8935 bool openOutputFile(const char *outFile,QFile &f)
8937 bool fileOpened=FALSE;
8938 bool writeToStdout=(outFile[0]=='-' && outFile[1]=='\0');
8939 if (writeToStdout) // write to stdout
8941 fileOpened = f.open(IO_WriteOnly,stdout);
8943 else // write to file
8945 QFileInfo fi(outFile);
8946 if (fi.exists()) // create a backup
8949 QFileInfo backup(fi.fileName()+".bak");
8950 if (backup.exists()) // remove existing backup
8951 dir.remove(backup.fileName());
8952 dir.rename(fi.fileName(),fi.fileName()+".bak");
8955 fileOpened = f.open(IO_WriteOnly|IO_Translate);
8960 void writeExtraLatexPackages(FTextStream &t)
8962 // User-specified packages
8963 QStrList &extraPackages = Config_getList(EXTRA_PACKAGES);
8964 if (!extraPackages.isEmpty())
8966 t << "% Packages requested by user\n";
8967 const char *pkgName=extraPackages.first();
8970 if ((pkgName[0] == '[') || (pkgName[0] == '{'))
8971 t << "\\usepackage" << pkgName << "\n";
8973 t << "\\usepackage{" << pkgName << "}\n";
8974 pkgName=extraPackages.next();
8980 void writeLatexSpecialFormulaChars(FTextStream &t)
8982 unsigned char minus[4]; // Superscript minus
8983 char *pminus = (char *)minus;
8984 unsigned char sup2[3]; // Superscript two
8985 char *psup2 = (char *)sup2;
8986 unsigned char sup3[3];
8987 char *psup3 = (char *)sup3; // Superscript three
8999 t << "\\usepackage{newunicodechar}\n"
9000 " \\newunicodechar{" << pminus << "}{${}^{-}$}% Superscript minus\n"
9001 " \\newunicodechar{" << psup2 << "}{${}^{2}$}% Superscript two\n"
9002 " \\newunicodechar{" << psup3 << "}{${}^{3}$}% Superscript three\n"
9006 //------------------------------------------------------
9008 static int g_usedTableLevels = 0;
9010 void incUsedTableLevels()
9012 g_usedTableLevels++;
9014 void decUsedTableLevels()
9016 g_usedTableLevels--;
9018 int usedTableLevels()
9020 return g_usedTableLevels;
9023 //------------------------------------------------------