00001 /***************************************************************************** 00002 * 00003 * $Id: util.cpp,v 1.74 2001/03/19 19:27:42 root Exp $ 00004 * 00005 * Copyright (C) 1997-2008 by Dimitri van Heesch. 00006 * 00007 * Permission to use, copy, modify, and distribute this software and its 00008 * documentation under the terms of the GNU General Public License is hereby 00009 * granted. No representations are made about the suitability of this software 00010 * for any purpose. It is provided "as is" without express or implied warranty. 00011 * See the GNU General Public License for more details. 00012 * 00013 * Documents produced by Doxygen are derivative works derived from the 00014 * input used in their production; they are not affected by this license. 00015 * 00016 */ 00017 00018 #include <stdlib.h> 00019 #include <ctype.h> 00020 #include <errno.h> 00021 00022 #include <md5.h> 00023 00024 #include "qtbc.h" 00025 #include <qregexp.h> 00026 #include <qfileinfo.h> 00027 #include <qdir.h> 00028 #include <qdatetime.h> 00029 #include <qcache.h> 00030 00031 #include "util.h" 00032 #include "message.h" 00033 #include "classdef.h" 00034 #include "filedef.h" 00035 #include "doxygen.h" 00036 #include "outputlist.h" 00037 #include "defargs.h" 00038 #include "language.h" 00039 #include "config.h" 00040 #include "htmlhelp.h" 00041 #include "example.h" 00042 #include "version.h" 00043 #include "groupdef.h" 00044 #include "reflist.h" 00045 #include "pagedef.h" 00046 #include "debug.h" 00047 #include "searchindex.h" 00048 #include "doxygen.h" 00049 #include "textdocvisitor.h" 00050 #include "portable.h" 00051 00052 //------------------------------------------------------------------------ 00053 00054 // selects one of the name to sub-dir mapping algorithms that is used 00055 // to select a sub directory when CREATE_SUBDIRS is set to YES. 00056 00057 #define ALGO_COUNT 1 00058 #define ALGO_CRC16 2 00059 #define ALGO_MD5 3 00060 00061 //#define MAP_ALGO ALGO_COUNT 00062 //#define MAP_ALGO ALGO_CRC16 00063 #define MAP_ALGO ALGO_MD5 00064 00065 #define REL_PATH_TO_ROOT "../../" 00066 00067 //------------------------------------------------------------------------ 00068 // TextGeneratorOLImpl implementation 00069 //------------------------------------------------------------------------ 00070 00071 TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od) 00072 { 00073 } 00074 00075 void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const 00076 { 00077 if (keepSpaces) 00078 { 00079 const char *p=s; 00080 if (p) 00081 { 00082 char cs[2]; 00083 char c; 00084 cs[1]='\0'; 00085 while ((c=*p++)) 00086 { 00087 if (c==' ') m_od.writeNonBreakableSpace(1); 00088 else cs[0]=c,m_od.docify(cs); 00089 } 00090 } 00091 } 00092 else 00093 { 00094 m_od.docify(s); 00095 } 00096 } 00097 00098 void TextGeneratorOLImpl::writeBreak() const 00099 { 00100 m_od.pushGeneratorState(); 00101 m_od.disableAllBut(OutputGenerator::Html); 00102 m_od.lineBreak(); 00103 m_od.popGeneratorState(); 00104 } 00105 00106 void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file, 00107 const char *anchor,const char *text 00108 ) const 00109 { 00110 m_od.writeObjectLink(extRef,file,anchor,text); 00111 } 00112 00113 //------------------------------------------------------------------------ 00114 //------------------------------------------------------------------------ 00115 00116 // an inheritance tree of depth of 100000 should be enough for everyone :-) 00117 const int maxInheritanceDepth = 100000; 00118 00119 bool isId(int c) 00120 { 00121 if (c<0 || c>255) return FALSE; 00122 return c=='_' || isalnum(c); 00123 } 00124 00125 00140 QCString removeAnonymousScopes(const QCString &s) 00141 { 00142 QCString result; 00143 if (s.isEmpty()) return result; 00144 static QRegExp re("[ :]*@[0-9]+[: ]*"); 00145 int i,l,sl=s.length(); 00146 int p=0; 00147 while ((i=re.match(s,p,&l))!=-1) 00148 { 00149 result+=s.mid(p,i-p); 00150 int c=i; 00151 bool b1=FALSE,b2=FALSE; 00152 while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE; 00153 c=i+l-1; 00154 while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE; 00155 if (b1 && b2) 00156 { 00157 result+="::"; 00158 } 00159 p=i+l; 00160 } 00161 result+=s.right(sl-p); 00162 //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data()); 00163 return result; 00164 } 00165 00166 // replace anonymous scopes with __anonymous__ or replacement if provided 00167 QCString replaceAnonymousScopes(const QCString &s,const char *replacement) 00168 { 00169 QCString result; 00170 if (s.isEmpty()) return result; 00171 static QRegExp re("@[0-9]+"); 00172 int i,l,sl=s.length(); 00173 int p=0; 00174 while ((i=re.match(s,p,&l))!=-1) 00175 { 00176 result+=s.mid(p,i-p); 00177 if (replacement) 00178 { 00179 result+=replacement; 00180 } 00181 else 00182 { 00183 result+="__anonymous__"; 00184 } 00185 p=i+l; 00186 } 00187 result+=s.right(sl-p); 00188 //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data()); 00189 return result; 00190 } 00191 00192 00193 // strip annonymous left hand side part of the scope 00194 QCString stripAnonymousNamespaceScope(const QCString &s) 00195 { 00196 int i,p=0,l; 00197 QCString newScope; 00198 while ((i=getScopeFragment(s,p,&l))!=-1) 00199 { 00200 //printf("Scope fragment %s\n",s.mid(i,l).data()); 00201 if (Doxygen::namespaceSDict->find(s.left(i+l))!=0) 00202 { 00203 if (s.at(i)!='@') 00204 { 00205 if (!newScope.isEmpty()) newScope+="::"; 00206 newScope+=s.mid(i,l); 00207 } 00208 } 00209 else 00210 { 00211 if (!newScope.isEmpty()) newScope+="::"; 00212 newScope+=s.right(s.length()-i); 00213 goto done; 00214 } 00215 p=i+l; 00216 } 00217 done: 00218 //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data()); 00219 return newScope; 00220 } 00221 00222 void writePageRef(OutputDocInterface &od,const char *cn,const char *mn) 00223 { 00224 od.pushGeneratorState(); 00225 00226 od.disable(OutputGenerator::Html); 00227 od.disable(OutputGenerator::Man); 00228 if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex); 00229 if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF); 00230 od.startPageRef(); 00231 od.docify(theTranslator->trPageAbbreviation()); 00232 od.endPageRef(cn,mn); 00233 00234 od.popGeneratorState(); 00235 } 00236 00241 QCString generateMarker(int id) 00242 { 00243 QCString result; 00244 result.sprintf("@%d",id); 00245 return result; 00246 } 00247 00248 static QCString stripFromPath(const QCString &path,QStrList &l) 00249 { 00250 // look at all the strings in the list and strip the longest match 00251 const char *s=l.first(); 00252 QCString potential; 00253 unsigned int length = 0; 00254 while (s) 00255 { 00256 QCString prefix = s; 00257 if (prefix.length() > length && 00258 stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare 00259 { 00260 length = prefix.length(); 00261 potential = path.right(path.length()-prefix.length()); 00262 } 00263 s = l.next(); 00264 } 00265 if (length) return potential; 00266 return path; 00267 } 00268 00272 QCString stripFromPath(const QCString &path) 00273 { 00274 return stripFromPath(path,Config_getList("STRIP_FROM_PATH")); 00275 } 00276 00280 QCString stripFromIncludePath(const QCString &path) 00281 { 00282 return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH")); 00283 } 00284 00289 int guessSection(const char *name) 00290 { 00291 QCString n=((QCString)name).lower(); 00292 if (n.right(2)==".c" || // source 00293 n.right(3)==".cc" || 00294 n.right(4)==".cxx" || 00295 n.right(4)==".cpp" || 00296 n.right(4)==".c++" || 00297 n.right(5)==".java" || 00298 n.right(3)==".ii" || // inline 00299 n.right(4)==".ixx" || 00300 n.right(4)==".ipp" || 00301 n.right(4)==".i++" || 00302 n.right(4)==".inl" 00303 ) return Entry::SOURCE_SEC; 00304 if (n.right(2)==".h" || // header 00305 n.right(3)==".hh" || 00306 n.right(4)==".hxx" || 00307 n.right(4)==".hpp" || 00308 n.right(4)==".h++" || 00309 n.right(4)==".idl" || 00310 n.right(4)==".ddl" || 00311 n.right(5)==".pidl" 00312 ) return Entry::HEADER_SEC; 00313 return 0; 00314 } 00315 00316 QCString resolveTypeDef(Definition *context,const QCString &qualifiedName, 00317 Definition **typedefContext) 00318 { 00319 //printf("<<resolveTypeDef(%s,%s)\n", 00320 // context ? context->name().data() : "<none>",qualifiedName.data()); 00321 QCString result; 00322 if (qualifiedName.isEmpty()) 00323 { 00324 //printf(" qualified name empty!\n"); 00325 return result; 00326 } 00327 00328 Definition *mContext=context; 00329 if (typedefContext) *typedefContext=context; 00330 00331 // see if the qualified name has a scope part 00332 int scopeIndex = qualifiedName.findRev("::"); 00333 QCString resName=qualifiedName; 00334 if (scopeIndex!=-1) // strip scope part for the name 00335 { 00336 resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2); 00337 if (resName.isEmpty()) 00338 { 00339 // qualifiedName was of form A:: ! 00340 //printf(" qualified name of form A::!\n"); 00341 return result; 00342 } 00343 } 00344 MemberDef *md=0; 00345 while (mContext && md==0) 00346 { 00347 // step 1: get the right scope 00348 Definition *resScope=mContext; 00349 if (scopeIndex!=-1) 00350 { 00351 // split-off scope part 00352 QCString resScopeName = qualifiedName.left(scopeIndex); 00353 //printf("resScopeName=`%s'\n",resScopeName.data()); 00354 00355 // look-up scope in context 00356 int is,ps=0; 00357 int l; 00358 while ((is=getScopeFragment(resScopeName,ps,&l))!=-1) 00359 { 00360 QCString qualScopePart = resScopeName.mid(is,l); 00361 QCString tmp = resolveTypeDef(mContext,qualScopePart); 00362 if (!tmp.isEmpty()) qualScopePart=tmp; 00363 resScope = resScope->findInnerCompound(qualScopePart); 00364 //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope); 00365 if (resScope==0) break; 00366 ps=is+l; 00367 } 00368 } 00369 //printf("resScope=%s\n",resScope?resScope->name().data():"<none>"); 00370 00371 // step 2: get the member 00372 if (resScope) // no scope or scope found in the current context 00373 { 00374 //printf("scope found: %s, look for typedef %s\n", 00375 // resScope->qualifiedName().data(),resName.data()); 00376 MemberNameSDict *mnd=0; 00377 if (resScope->definitionType()==Definition::TypeClass) 00378 { 00379 mnd=Doxygen::memberNameSDict; 00380 } 00381 else 00382 { 00383 mnd=Doxygen::functionNameSDict; 00384 } 00385 MemberName *mn=mnd->find(resName); 00386 if (mn) 00387 { 00388 MemberNameIterator mni(*mn); 00389 MemberDef *tmd=0; 00390 for (;(tmd=mni.current());++mni) 00391 { 00392 //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n", 00393 // tmd->name().data(), resScope->name().data(), 00394 // tmd->getOuterScope()->name().data(), mContext); 00395 if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/) 00396 { 00397 // look if resScope is visible within tmd->getOuterScope() 00398 Definition *d = tmd->getOuterScope(); 00399 while (d && d!=resScope) d=d->getOuterScope(); 00400 if (d) 00401 { 00402 md=tmd; 00403 } 00404 } 00405 } 00406 } 00407 } 00408 mContext=mContext->getOuterScope(); 00409 } 00410 00411 // step 3: get the member's type 00412 if (md) 00413 { 00414 //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s'\n", 00415 // qualifiedName.data(),context->name().data(),md->typeString() 00416 // ); 00417 result=md->typeString(); 00418 if (result.find("*)")!=-1) // typedef of a function/member pointer 00419 { 00420 result+=md->argsString(); 00421 } 00422 if (typedefContext) *typedefContext=md->getOuterScope(); 00423 } 00424 else 00425 { 00426 //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n", 00427 // qualifiedName.data(),context ? context->name().data() : "<global>"); 00428 } 00429 return result; 00430 00431 } 00432 00433 00437 ClassDef *getClass(const char *name) 00438 { 00439 if (name==0 || name[0]=='\0') return 0; 00440 return Doxygen::classSDict->find(name); 00441 } 00442 00443 NamespaceDef *getResolvedNamespace(const char *name) 00444 { 00445 if (name==0 || name[0]=='\0') return 0; 00446 QCString *subst = Doxygen::namespaceAliasDict[name]; 00447 if (subst) 00448 { 00449 int count=0; // recursion detection guard 00450 QCString *newSubst; 00451 while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10) 00452 { 00453 subst=newSubst; 00454 count++; 00455 } 00456 if (count==10) 00457 { 00458 warn_cont("Warning: possible recursive namespace alias detected for %s!\n",name); 00459 } 00460 return Doxygen::namespaceSDict->find(subst->data()); 00461 } 00462 else 00463 { 00464 return Doxygen::namespaceSDict->find(name); 00465 } 00466 } 00467 00468 static QDict<MemberDef> g_resolvedTypedefs; 00469 static QDict<Definition> g_visitedNamespaces; 00470 00471 // forward declaration 00472 ClassDef *getResolvedClassRec(Definition *scope, 00473 FileDef *fileScope, 00474 const char *n, 00475 MemberDef **pTypeDef, 00476 QCString *pTemplSpec, 00477 QCString *pResolvedType 00478 ); 00479 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item, 00480 const QCString &explicitScopePart); 00481 00489 ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md, 00490 MemberDef **pMemType,QCString *pTemplSpec, 00491 QCString *pResolvedType) 00492 { 00493 //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal()); 00494 bool isCached = md->isTypedefValCached(); // value already cached 00495 if (isCached) 00496 { 00497 //printf("Already cached %s->%s [%s]\n", 00498 // md->name().data(), 00499 // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>", 00500 // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>"); 00501 00502 if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec(); 00503 if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef(); 00504 return md->getCachedTypedefVal(); 00505 } 00506 //printf("new typedef\n"); 00507 QCString qname = md->qualifiedName(); 00508 if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done 00509 00510 g_resolvedTypedefs.insert(qname,md); // put on the trace list 00511 00512 QCString type = md->typeString(); // get the "value" of the typedef 00513 QCString typedefValue = type; 00514 int tl=type.length(); 00515 int ip=tl-1; // remove * and & at the end 00516 while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' ')) 00517 { 00518 ip--; 00519 } 00520 type=type.left(ip+1); 00521 type.stripPrefix("const "); // strip leading "const" 00522 type.stripPrefix("struct "); // strip leading "struct" 00523 type.stripPrefix("union "); // strip leading "union" 00524 int sp=0; 00525 tl=type.length(); // length may have been changed 00526 while (sp<tl && type.at(sp)==' ') sp++; 00527 MemberDef *memTypeDef = 0; 00528 ClassDef *result = getResolvedClassRec(md->getOuterScope(), 00529 fileScope,type,&memTypeDef,0,pResolvedType); 00530 // if type is a typedef then return what it resolves to. 00531 if (memTypeDef && memTypeDef->isTypedef()) 00532 { 00533 result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec); 00534 goto done; 00535 } 00536 else if (memTypeDef && memTypeDef->isEnumerate() && pMemType) 00537 { 00538 *pMemType = memTypeDef; 00539 } 00540 00541 //printf("type=%s result=%p\n",type.data(),result); 00542 if (result==0) 00543 { 00544 // try unspecialized version if type is template 00545 int si=type.findRev("::"); 00546 int i=type.find('<'); 00547 if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version 00548 { 00549 if (pTemplSpec) *pTemplSpec = type.mid(i); 00550 result = getResolvedClassRec(md->getOuterScope(),fileScope, 00551 type.left(i),0,0,pResolvedType); 00552 //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n", 00553 // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl); 00554 } 00555 else if (si!=-1) // A::B 00556 { 00557 i=type.find('<',si); 00558 if (i==-1) // Something like A<T>::B => lookup A::B 00559 { 00560 i=type.length(); 00561 } 00562 else // Something like A<T>::B<S> => lookup A::B, spec=<S> 00563 { 00564 if (pTemplSpec) *pTemplSpec = type.mid(i); 00565 } 00566 result = getResolvedClassRec(md->getOuterScope(),fileScope, 00567 stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0, 00568 pResolvedType); 00569 } 00570 00571 //if (result) ip=si+sp+1; 00572 } 00573 00574 done: 00575 if (pResolvedType) 00576 { 00577 if (result) 00578 { 00579 *pResolvedType=result->qualifiedName(); 00580 //printf("*pResolvedType=%s\n",pResolvedType->data()); 00581 if (sp>0) pResolvedType->prepend(typedefValue.left(sp)); 00582 if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1)); 00583 } 00584 else 00585 { 00586 *pResolvedType=typedefValue; 00587 } 00588 } 00589 00590 // remember computed value for next time 00591 if (result && result->getDefFileName()!="<code>") 00592 // this check is needed to prevent that temporary classes that are 00593 // introduced while parsing code fragments are being cached here. 00594 { 00595 //printf("setting cached typedef %p in result %p\n",md,result); 00596 //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine()); 00597 //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>"); 00598 md->cacheTypedefVal(result, 00599 pTemplSpec ? *pTemplSpec : QCString(), 00600 pResolvedType ? *pResolvedType : QCString() 00601 ); 00602 } 00603 00604 g_resolvedTypedefs.remove(qname); // remove from the trace list 00605 00606 return result; 00607 } 00608 00612 static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name, 00613 MemberDef **pTypeDef=0) 00614 { 00615 QCString result=name; 00616 if (name.isEmpty()) return result; 00617 00618 // lookup scope fragment in the symbol map 00619 DefinitionIntf *di = Doxygen::symbolMap->find(name); 00620 if (di==0) return result; // no matches 00621 00622 MemberDef *bestMatch=0; 00623 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols 00624 { 00625 // search for the best match 00626 DefinitionListIterator dli(*(DefinitionList*)di); 00627 Definition *d; 00628 int minDistance=10000; // init at "infinite" 00629 for (dli.toFirst();(d=dli.current());++dli) // foreach definition 00630 { 00631 // only look at members 00632 if (d->definitionType()==Definition::TypeMember) 00633 { 00634 // that are also typedefs 00635 MemberDef *md = (MemberDef *)d; 00636 if (md->isTypedef()) // d is a typedef 00637 { 00638 // test accessibility of typedef within scope. 00639 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,""); 00640 if (distance!=-1 && distance<minDistance) 00641 // definition is accessible and a better match 00642 { 00643 minDistance=distance; 00644 bestMatch = md; 00645 } 00646 } 00647 } 00648 } 00649 } 00650 else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol 00651 { 00652 Definition *d = (Definition*)di; 00653 // that are also typedefs 00654 MemberDef *md = (MemberDef *)di; 00655 if (md->isTypedef()) // d is a typedef 00656 { 00657 // test accessibility of typedef within scope. 00658 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,""); 00659 if (distance!=-1) // definition is accessible 00660 { 00661 bestMatch = md; 00662 } 00663 } 00664 } 00665 if (bestMatch) 00666 { 00667 result = bestMatch->typeString(); 00668 if (pTypeDef) *pTypeDef=bestMatch; 00669 } 00670 00671 //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>", 00672 // name.data(),result.data()); 00673 return result; 00674 } 00675 00676 static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName) 00677 { 00678 if (cl) 00679 { 00680 SDict<Definition>::Iterator cli(*cl); 00681 Definition *cd; 00682 for (cli.toFirst();(cd=cli.current());++cli) 00683 { 00684 if (cd->localName()==localName) 00685 { 00686 return cd; 00687 } 00688 } 00689 } 00690 return 0; 00691 } 00692 00698 static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path) 00699 { 00700 int is,ps; 00701 int l; 00702 Definition *current=start; 00703 ps=0; 00704 //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data()); 00705 // for each part of the explicit scope 00706 while ((is=getScopeFragment(path,ps,&l))!=-1) 00707 { 00708 // try to resolve the part if it is a typedef 00709 MemberDef *typeDef=0; 00710 QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef); 00711 //printf(" qualScopePart=%s\n",qualScopePart.data()); 00712 if (typeDef) 00713 { 00714 ClassDef *type = newResolveTypedef(fileScope,typeDef); 00715 if (type) 00716 { 00717 //printf("Found type %s\n",type->name().data()); 00718 return type; 00719 } 00720 } 00721 Definition *next = current->findInnerCompound(qualScopePart); 00722 //printf("++ Looking for %s inside %s result %s\n", 00723 // qualScopePart.data(), 00724 // current->name().data(), 00725 // next?next->name().data():"<null>"); 00726 if (next==0) // failed to follow the path 00727 { 00728 //printf("==> next==0!\n"); 00729 if (current->definitionType()==Definition::TypeNamespace) 00730 { 00731 next = endOfPathIsUsedClass( 00732 ((NamespaceDef *)current)->getUsedClasses(),qualScopePart); 00733 } 00734 else if (current->definitionType()==Definition::TypeFile) 00735 { 00736 next = endOfPathIsUsedClass( 00737 ((FileDef *)current)->getUsedClasses(),qualScopePart); 00738 } 00739 current = next; 00740 if (current==0) break; 00741 } 00742 else // continue to follow scope 00743 { 00744 current = next; 00745 //printf("==> current = %p\n",current); 00746 } 00747 ps=is+l; 00748 } 00749 //printf("followPath(start=%s,path=%s) result=%s\n", 00750 // start->name().data(),path.data(),current?current->name().data():"<null>"); 00751 return current; // path could be followed 00752 } 00753 00754 bool accessibleViaUsingClass(const SDict<Definition> *cl, 00755 FileDef *fileScope, 00756 Definition *item, 00757 const QCString &explicitScopePart="" 00758 ) 00759 { 00760 if (cl) // see if the class was imported via a using statement 00761 { 00762 SDict<Definition>::Iterator cli(*cl); 00763 Definition *ucd; 00764 bool explicitScopePartEmpty = explicitScopePart.isEmpty(); 00765 for (cli.toFirst();(ucd=cli.current());++cli) 00766 { 00767 //printf("Trying via used class %s\n",ucd->name().data()); 00768 Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart); 00769 if (sc && sc==item) return TRUE; 00770 //printf("Try via used class done\n"); 00771 } 00772 } 00773 return FALSE; 00774 } 00775 00776 bool accessibleViaUsingNamespace(const NamespaceSDict *nl, 00777 FileDef *fileScope, 00778 Definition *item, 00779 const QCString &explicitScopePart="") 00780 { 00781 static QDict<void> visitedDict; 00782 if (nl) // check used namespaces for the class 00783 { 00784 NamespaceSDict::Iterator nli(*nl); 00785 NamespaceDef *und; 00786 int count=0; 00787 for (nli.toFirst();(und=nli.current());++nli,count++) 00788 { 00789 //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(), 00790 // count,nl->count()); 00791 Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart); 00792 if (sc && item->getOuterScope()==sc) 00793 { 00794 //printf("] found it\n"); 00795 return TRUE; 00796 } 00797 QCString key=und->name(); 00798 if (und->getUsedNamespaces() && visitedDict.find(key)==0) 00799 { 00800 visitedDict.insert(key,(void *)0x08); 00801 00802 if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart)) 00803 { 00804 //printf("] found it via recursion\n"); 00805 return TRUE; 00806 } 00807 00808 visitedDict.remove(key); 00809 } 00810 //printf("] Try via used namespace done\n"); 00811 } 00812 } 00813 return FALSE; 00814 } 00815 00816 00817 /* Returns the "distance" (=number of levels up) from item to scope, or -1 00818 * if item in not inside scope. 00819 */ 00820 int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item) 00821 { 00822 //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n", 00823 // scope->name().data(),item->name().data(),item->getOuterScope()->name().data()); 00824 00825 QCString key(40); 00826 key.sprintf("%p:%p:%p",scope,fileScope,item); 00827 static QDict<void> visitedDict; 00828 if (visitedDict.find(key)) 00829 { 00830 //printf("> already found\n"); 00831 return -1; // already looked at this 00832 } 00833 visitedDict.insert(key,(void *)0x8); 00834 00835 int result=0; // assume we found it 00836 int i; 00837 00838 Definition *itemScope=item->getOuterScope(); 00839 00840 if ( 00841 itemScope==scope || // same thing 00842 (item->definitionType()==Definition::TypeMember && // a member 00843 itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class 00844 scope->definitionType()==Definition::TypeClass && // accessible 00845 ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope 00846 ) || 00847 (item->definitionType()==Definition::TypeClass && // a nested class 00848 itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base 00849 scope->definitionType()==Definition::TypeClass && // class of scope 00850 ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE) 00851 ) 00852 ) 00853 { 00854 //printf("> found it\n"); 00855 } 00856 else if (scope==Doxygen::globalScope) 00857 { 00858 if (fileScope) 00859 { 00860 SDict<Definition> *cl = fileScope->getUsedClasses(); 00861 if (accessibleViaUsingClass(cl,fileScope,item)) 00862 { 00863 //printf("> found via used class\n"); 00864 goto done; 00865 } 00866 NamespaceSDict *nl = fileScope->getUsedNamespaces(); 00867 if (accessibleViaUsingNamespace(nl,fileScope,item)) 00868 { 00869 //printf("> found via used namespace\n"); 00870 goto done; 00871 } 00872 } 00873 //printf("> reached global scope\n"); 00874 result=-1; // not found in path to globalScope 00875 } 00876 else // keep searching 00877 { 00878 // check if scope is a namespace, which is using other classes and namespaces 00879 if (scope->definitionType()==Definition::TypeNamespace) 00880 { 00881 NamespaceDef *nscope = (NamespaceDef*)scope; 00882 //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses()); 00883 SDict<Definition> *cl = nscope->getUsedClasses(); 00884 if (accessibleViaUsingClass(cl,fileScope,item)) 00885 { 00886 //printf("> found via used class\n"); 00887 goto done; 00888 } 00889 NamespaceSDict *nl = nscope->getUsedNamespaces(); 00890 if (accessibleViaUsingNamespace(nl,fileScope,item)) 00891 { 00892 //printf("> found via used namespace\n"); 00893 goto done; 00894 } 00895 } 00896 // repeat for the parent scope 00897 i=isAccessibleFrom(scope->getOuterScope(),fileScope,item); 00898 //printf("> result=%d\n",i); 00899 result= (i==-1) ? -1 : i+2; 00900 } 00901 done: 00902 visitedDict.remove(key); 00903 //Doxygen::lookupCache.insert(key,new int(result)); 00904 return result; 00905 } 00906 00907 00908 /* Returns the "distance" (=number of levels up) from item to scope, or -1 00909 * if item in not in this scope. The explicitScopePart limits the search 00910 * to scopes that match \a scope (or its parent scope(s)) plus the explicit part. 00911 * Example: 00912 * 00913 * class A { public: class I {}; }; 00914 * class B { public: class J {}; }; 00915 * 00916 * - Looking for item=='J' inside scope=='B' will return 0. 00917 * - Looking for item=='I' inside scope=='B' will return -1 00918 * (as it is not found in B nor in the global scope). 00919 * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but 00920 * not found and then A::I is searched in the global scope, which matches and 00921 * thus the result is 1. 00922 */ 00923 int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, 00924 Definition *item,const QCString &explicitScopePart) 00925 { 00926 if (explicitScopePart.isEmpty()) 00927 { 00928 // handle degenerate case where there is no explicit scope. 00929 return isAccessibleFrom(scope,fileScope,item); 00930 } 00931 00932 QCString key(40+explicitScopePart.length()); 00933 key.sprintf("%p:%p:%p:%s",scope,fileScope,item,explicitScopePart.data()); 00934 static QDict<void> visitedDict; 00935 if (visitedDict.find(key)) 00936 { 00937 //printf("Already visited!\n"); 00938 return -1; // already looked at this 00939 } 00940 visitedDict.insert(key,(void *)0x8); 00941 00942 //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>", 00943 // item?item->name().data():"<none>", 00944 // explicitScopePart.data()); 00945 int result=0; // assume we found it 00946 Definition *newScope = followPath(scope,fileScope,explicitScopePart); 00947 if (newScope) // explicitScope is inside scope => newScope is the result 00948 { 00949 Definition *itemScope = item->getOuterScope(); 00950 //printf(" scope traversal successful %s<->%s!\n",item->getOuterScope()->name().data(),newScope->name().data()); 00951 //if (newScope && newScope->definitionType()==Definition::TypeClass) 00952 //{ 00953 // ClassDef *cd = (ClassDef *)newScope; 00954 // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses()); 00955 //} 00956 if (itemScope==newScope) // exact match of scopes => distance==0 00957 { 00958 //printf("> found it\n"); 00959 } 00960 else if (itemScope && newScope && 00961 itemScope->definitionType()==Definition::TypeClass && 00962 newScope->definitionType()==Definition::TypeClass && 00963 ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0) 00964 ) 00965 { 00966 // inheritance is also ok. Example: looking for B::I, where 00967 // class A { public: class I {} }; 00968 // class B : public A {} 00969 // but looking for B::I, where 00970 // class A { public: class I {} }; 00971 // class B { public: class I {} }; 00972 // will find A::I, so we still prefer a direct match and give this one a distance of 1 00973 result=1; 00974 00975 //printf("scope(%s) is base class of newScope(%s)\n", 00976 // scope->name().data(),newScope->name().data()); 00977 } 00978 else 00979 { 00980 int i=-1; 00981 if (newScope->definitionType()==Definition::TypeNamespace) 00982 { 00983 g_visitedNamespaces.insert(newScope->name(),newScope); 00984 // this part deals with the case where item is a class 00985 // A::B::C but is explicit referenced as A::C, where B is imported 00986 // in A via a using directive. 00987 //printf("newScope is a namespace: %s!\n",newScope->name().data()); 00988 NamespaceDef *nscope = (NamespaceDef*)newScope; 00989 SDict<Definition> *cl = nscope->getUsedClasses(); 00990 if (cl) 00991 { 00992 SDict<Definition>::Iterator cli(*cl); 00993 Definition *cd; 00994 for (cli.toFirst();(cd=cli.current());++cli) 00995 { 00996 //printf("Trying for class %s\n",cd->name().data()); 00997 if (cd==item) 00998 { 00999 //printf("> class is used in this scope\n"); 01000 goto done; 01001 } 01002 } 01003 } 01004 NamespaceSDict *nl = nscope->getUsedNamespaces(); 01005 if (nl) 01006 { 01007 NamespaceSDict::Iterator nli(*nl); 01008 NamespaceDef *nd; 01009 for (nli.toFirst();(nd=nli.current());++nli) 01010 { 01011 if (g_visitedNamespaces.find(nd->name())==0) 01012 { 01013 //printf("Trying for namespace %s\n",nd->name().data()); 01014 i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name()); 01015 if (i!=-1) 01016 { 01017 //printf("> found via explicit scope of used namespace\n"); 01018 goto done; 01019 } 01020 } 01021 } 01022 } 01023 } 01024 // repeat for the parent scope 01025 if (scope!=Doxygen::globalScope) 01026 { 01027 i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope, 01028 item,explicitScopePart); 01029 } 01030 //printf("> result=%d\n",i); 01031 result = (i==-1) ? -1 : i+2; 01032 } 01033 } 01034 else // failed to resolve explicitScope 01035 { 01036 //printf("failed to resolve: scope=%s\n",scope->name().data()); 01037 if (scope->definitionType()==Definition::TypeNamespace) 01038 { 01039 NamespaceDef *nscope = (NamespaceDef*)scope; 01040 NamespaceSDict *nl = nscope->getUsedNamespaces(); 01041 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 01042 { 01043 //printf("> found in used namespace\n"); 01044 goto done; 01045 } 01046 } 01047 if (scope==Doxygen::globalScope) 01048 { 01049 if (fileScope) 01050 { 01051 NamespaceSDict *nl = fileScope->getUsedNamespaces(); 01052 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart)) 01053 { 01054 //printf("> found in used namespace\n"); 01055 goto done; 01056 } 01057 } 01058 //printf("> not found\n"); 01059 result=-1; 01060 } 01061 else // continue by looking into the parent scope 01062 { 01063 int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope, 01064 item,explicitScopePart); 01065 //printf("> result=%d\n",i); 01066 result= (i==-1) ? -1 : i+2; 01067 } 01068 } 01069 done: 01070 //printf(" > result=%d\n",result); 01071 visitedDict.remove(key); 01072 //Doxygen::lookupCache.insert(key,new int(result)); 01073 return result; 01074 } 01075 01076 int computeQualifiedIndex(const QCString &name) 01077 { 01078 int i = name.find('<'); 01079 return name.findRev("::",i==-1 ? name.length() : i); 01080 } 01081 01082 static void getResolvedSymbol(Definition *scope, 01083 FileDef *fileScope, 01084 Definition *d, 01085 const QCString &explicitScopePart, 01086 int &minDistance, 01087 ClassDef *&bestMatch, 01088 MemberDef *&bestTypedef, 01089 QCString &bestTemplSpec, 01090 QCString &bestResolvedType 01091 ) 01092 { 01093 //printf(" => found type %x name=%s d=%p\n", 01094 // d->definitionType(),d->name().data(),d); 01095 01096 // only look at classes and members that are enums or typedefs 01097 if (d->definitionType()==Definition::TypeClass || 01098 (d->definitionType()==Definition::TypeMember && 01099 (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate()) 01100 ) 01101 ) 01102 { 01103 g_visitedNamespaces.clear(); 01104 // test accessibility of definition within scope. 01105 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); 01106 //printf(" distance %s (%p) is %d\n",d->name().data(),d,distance); 01107 if (distance!=-1) // definition is accessible 01108 { 01109 // see if we are dealing with a class or a typedef 01110 if (d->definitionType()==Definition::TypeClass) // d is a class 01111 { 01112 ClassDef *cd = (ClassDef *)d; 01113 //printf("cd=%s\n",cd->name().data()); 01114 if (!cd->isTemplateArgument()) // skip classes that 01115 // are only there to 01116 // represent a template 01117 // argument 01118 { 01119 //printf("is not a templ arg\n"); 01120 if (distance<minDistance) // found a definition that is "closer" 01121 { 01122 minDistance=distance; 01123 bestMatch = cd; 01124 bestTypedef = 0; 01125 bestTemplSpec.resize(0); 01126 bestResolvedType = cd->qualifiedName(); 01127 } 01128 else if (distance==minDistance && 01129 fileScope && bestMatch && 01130 fileScope->getUsedNamespaces() && 01131 d->getOuterScope()->definitionType()==Definition::TypeNamespace && 01132 bestMatch->getOuterScope()==Doxygen::globalScope 01133 ) 01134 { 01135 // in case the distance is equal it could be that a class X 01136 // is defined in a namespace and in the global scope. When searched 01137 // in the global scope the distance is 0 in both cases. We have 01138 // to choose one of the definitions: we choose the one in the 01139 // namespace if the fileScope imports namespaces and the definition 01140 // found was in a namespace while the best match so far isn't. 01141 // Just a non-perfect heuristic but it could help in some situations 01142 // (kdecore code is an example). 01143 minDistance=distance; 01144 bestMatch = cd; 01145 bestTypedef = 0; 01146 bestTemplSpec.resize(0); 01147 bestResolvedType = cd->qualifiedName(); 01148 } 01149 } 01150 else 01151 { 01152 //printf(" is a template argument!\n"); 01153 } 01154 } 01155 else if (d->definitionType()==Definition::TypeMember) 01156 { 01157 MemberDef *md = (MemberDef *)d; 01158 //printf(" member isTypedef()=%d\n",md->isTypedef()); 01159 if (md->isTypedef()) // d is a typedef 01160 { 01161 QCString args=md->argsString(); 01162 if (args.isEmpty()) // do not expand "typedef t a[4];" 01163 { 01164 //printf(" found typedef!\n"); 01165 01166 // we found a symbol at this distance, but if it didn't 01167 // resolve to a class, we still have to make sure that 01168 // something at a greater distance does not match, since 01169 // that symbol is hidden by this one. 01170 if (distance<minDistance) 01171 { 01172 QCString spec; 01173 QCString type; 01174 minDistance=distance; 01175 MemberDef *enumType = 0; 01176 ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type); 01177 if (cd) // type resolves to a class 01178 { 01179 //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data()); 01180 bestMatch = cd; 01181 bestTypedef = md; 01182 bestTemplSpec = spec; 01183 bestResolvedType = type; 01184 } 01185 else if (enumType) // type resolves to a enum 01186 { 01187 //printf(" is enum\n"); 01188 bestMatch = 0; 01189 bestTypedef = enumType; 01190 bestTemplSpec = ""; 01191 bestResolvedType = enumType->qualifiedName(); 01192 } 01193 else if (md->isReference()) // external reference 01194 { 01195 bestMatch = 0; 01196 bestTypedef = md; 01197 bestTemplSpec = spec; 01198 bestResolvedType = type; 01199 } 01200 else 01201 { 01202 //printf(" no match\n"); 01203 } 01204 } 01205 else 01206 { 01207 //printf(" not the best match %d min=%d\n",distance,minDistance); 01208 } 01209 } 01210 else 01211 { 01212 //printf(" not a simple typedef\n") 01213 } 01214 } 01215 else if (md->isEnumerate()) 01216 { 01217 if (distance<minDistance) 01218 { 01219 minDistance=distance; 01220 bestMatch = 0; 01221 bestTypedef = md; 01222 bestTemplSpec = ""; 01223 bestResolvedType = md->qualifiedName(); 01224 } 01225 } 01226 } 01227 } // if definition accessible 01228 else 01229 { 01230 //printf(" Not accessible!\n"); 01231 } 01232 } // if definition is a class or member 01233 //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data()); 01234 } 01235 01236 /* Find the fully qualified class name refered to by the input class 01237 * or typedef name against the input scope. 01238 * Loops through scope and each of its parent scopes looking for a 01239 * match against the input name. Can recursively call itself when 01240 * resolving typedefs. 01241 */ 01242 ClassDef *getResolvedClassRec(Definition *scope, 01243 FileDef *fileScope, 01244 const char *n, 01245 MemberDef **pTypeDef, 01246 QCString *pTemplSpec, 01247 QCString *pResolvedType 01248 ) 01249 { 01250 //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n); 01251 QCString name=n; 01252 QCString explicitScopePart; 01253 01254 int qualifierIndex = computeQualifiedIndex(name); 01255 //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex); 01256 if (qualifierIndex!=-1) // qualified name 01257 { 01258 // split off the explicit scope part 01259 explicitScopePart=name.left(qualifierIndex); 01260 // todo: improve namespace alias substitution 01261 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length()); 01262 name=name.mid(qualifierIndex+2); 01263 } 01264 01265 if (name.isEmpty()) 01266 { 01267 //printf("] empty name\n"); 01268 return 0; // empty name 01269 } 01270 01271 DefinitionIntf *di = Doxygen::symbolMap->find(name); 01272 //printf("Looking for symbol %s result=%p\n",name.data(),di); 01273 if (di==0) 01274 { 01275 return 0; 01276 } 01277 01278 bool hasUsingStatements = 01279 (fileScope && ((fileScope->getUsedNamespaces() && 01280 fileScope->getUsedNamespaces()->count()>0) || 01281 (fileScope->getUsedClasses() && 01282 fileScope->getUsedClasses()->count()>0)) 01283 ); 01284 //printf("hasUsingStatements=%d\n",hasUsingStatements); 01285 // Since it is often the case that the same name is searched in the same 01286 // scope over an over again (especially for the linked source code generation) 01287 // we use a cache to collect previous results. This is possible since the 01288 // result of a lookup is deterministic. As the key we use the concatenated 01289 // scope, the name to search for and the explicit scope prefix. The speedup 01290 // achieved by this simple cache can be enormous. 01291 int scopeNameLen = scope->name().length()+1; 01292 int nameLen = name.length()+1; 01293 int explicitPartLen = explicitScopePart.length(); 01294 int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0; 01295 01296 // below is a more efficient coding of 01297 // QCString key=scope->name()+"+"+name+"+"+explicitScopePart; 01298 QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1); 01299 char *p=key.data(); 01300 qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+'; 01301 p+=scopeNameLen; 01302 qstrcpy(p,name); *(p+nameLen-1)='+'; 01303 p+=nameLen; 01304 qstrcpy(p,explicitScopePart); 01305 p+=explicitPartLen; 01306 01307 // if a file scope is given and it contains using statements we should 01308 // also use the file part in the key (as a class name can be in 01309 // two different namespaces and a using statement in a file can select 01310 // one of them). 01311 if (hasUsingStatements) 01312 { 01313 // below is a more efficient coding of 01314 // key+="+"+fileScope->name(); 01315 *p++='+'; 01316 qstrcpy(p,fileScope->absFilePath()); 01317 p+=fileScopeLen-1; 01318 } 01319 *p='\0'; 01320 01321 LookupInfo *pval=Doxygen::lookupCache.find(key); 01322 //printf("Searching for %s result=%p\n",key.data(),pval); 01323 if (pval) 01324 { 01325 //printf("LookupInfo %p %p '%s' %p\n", 01326 // pval->classDef, pval->typeDef, pval->templSpec.data(), 01327 // pval->resolvedType.data()); 01328 if (pTemplSpec) *pTemplSpec=pval->templSpec; 01329 if (pTypeDef) *pTypeDef=pval->typeDef; 01330 if (pResolvedType) *pResolvedType=pval->resolvedType; 01331 //printf("] cachedMatch=%s\n", 01332 // pval->classDef?pval->classDef->name().data():"<none>"); 01333 //if (pTemplSpec) 01334 // printf("templSpec=%s\n",pTemplSpec->data()); 01335 return pval->classDef; 01336 } 01337 else // not found yet; we already add a 0 to avoid the possibility of 01338 // endless recursion. 01339 { 01340 Doxygen::lookupCache.insert(key,new LookupInfo); 01341 } 01342 01343 ClassDef *bestMatch=0; 01344 MemberDef *bestTypedef=0; 01345 QCString bestTemplSpec; 01346 QCString bestResolvedType; 01347 int minDistance=10000; // init at "infinite" 01348 01349 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name 01350 { 01351 //printf(" name is not unique\n"); 01352 DefinitionListIterator dli(*(DefinitionList*)di); 01353 Definition *d; 01354 int count=0; 01355 for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition 01356 { 01357 getResolvedSymbol(scope,fileScope,d,explicitScopePart, 01358 minDistance,bestMatch,bestTypedef,bestTemplSpec, 01359 bestResolvedType); 01360 } 01361 } 01362 else // unique name 01363 { 01364 //printf(" name is unique\n"); 01365 Definition *d = (Definition *)di; 01366 getResolvedSymbol(scope,fileScope,d,explicitScopePart, 01367 minDistance,bestMatch,bestTypedef,bestTemplSpec, 01368 bestResolvedType); 01369 } 01370 01371 if (pTypeDef) 01372 { 01373 *pTypeDef = bestTypedef; 01374 } 01375 if (pTemplSpec) 01376 { 01377 *pTemplSpec = bestTemplSpec; 01378 } 01379 if (pResolvedType) 01380 { 01381 *pResolvedType = bestResolvedType; 01382 } 01383 //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n", 01384 // bestMatch,bestResolvedType.data()); 01385 01386 pval=Doxygen::lookupCache.find(key); 01387 if (pval) 01388 { 01389 pval->classDef = bestMatch; 01390 pval->typeDef = bestTypedef; 01391 pval->templSpec = bestTemplSpec; 01392 pval->resolvedType = bestResolvedType; 01393 } 01394 else 01395 { 01396 Doxygen::lookupCache.insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType)); 01397 } 01398 //printf("] bestMatch=%s distance=%d\n", 01399 // bestMatch?bestMatch->name().data():"<none>",minDistance); 01400 //if (pTemplSpec) 01401 // printf("templSpec=%s\n",pTemplSpec->data()); 01402 return bestMatch; 01403 } 01404 01405 /* Find the fully qualified class name refered to by the input class 01406 * or typedef name against the input scope. 01407 * Loops through scope and each of its parent scopes looking for a 01408 * match against the input name. 01409 */ 01410 ClassDef *getResolvedClass(Definition *scope, 01411 FileDef *fileScope, 01412 const char *n, 01413 MemberDef **pTypeDef, 01414 QCString *pTemplSpec, 01415 bool mayBeUnlinkable, 01416 bool mayBeHidden, 01417 QCString *pResolvedType 01418 ) 01419 { 01420 g_resolvedTypedefs.clear(); 01421 if (scope==0 || 01422 (scope->definitionType()!=Definition::TypeClass && 01423 scope->definitionType()!=Definition::TypeNamespace 01424 ) || 01425 (fileScope && fileScope->isJava() && QCString(n).find("::")!=-1) 01426 ) 01427 { 01428 scope=Doxygen::globalScope; 01429 } 01430 //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n", 01431 // scope?scope->name().data():"<global>", 01432 // fileScope?fileScope->name().data():"<none>", 01433 // n, 01434 // mayBeUnlinkable 01435 // ); 01436 ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType); 01437 if (!mayBeUnlinkable && result && !result->isLinkable()) 01438 { 01439 if (!mayBeHidden || !result->isHidden()) 01440 { 01441 result=0; // don't link to artifical/hidden classes unless explicitly allowed 01442 } 01443 } 01444 //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>", 01445 // n,result?result->name().data():"<none>"); 01446 return result; 01447 } 01448 01449 //------------------------------------------------------------------------- 01450 //------------------------------------------------------------------------- 01451 //------------------------------------------------------------------------- 01452 //------------------------------------------------------------------------- 01453 01454 static bool findOperator(const QCString &s,int i) 01455 { 01456 int b = s.findRev("operator",i); 01457 if (b==-1) return FALSE; // not found 01458 b+=8; 01459 while (b<i) // check if there are only spaces inbetween 01460 // the operator and the > 01461 { 01462 if (!isspace((uchar)s.at(b))) return FALSE; 01463 b++; 01464 } 01465 return TRUE; 01466 } 01467 01468 static bool findOperator2(const QCString &s,int i) 01469 { 01470 int b = s.findRev("operator",i); 01471 if (b==-1) return FALSE; // not found 01472 b+=8; 01473 while (b<i) // check if there are only non-ascii 01474 // characters in front of the operator 01475 { 01476 if (isId((uchar)s.at(b))) return FALSE; 01477 b++; 01478 } 01479 return TRUE; 01480 } 01481 01482 static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' }; 01483 static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' }; 01484 01485 QCString removeRedundantWhiteSpace(const QCString &s) 01486 { 01487 static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT"); 01488 if (s.isEmpty()) return s; 01489 QCString result; 01490 uint i; 01491 uint l=s.length(); 01492 uint csp=0; 01493 uint vsp=0; 01494 for (i=0;i<l;i++) 01495 { 01496 nextChar: 01497 char c=s.at(i); 01498 01499 // search for "const" 01500 if (csp<6 && c==constScope[csp] && // character matches substring "const" 01501 (csp>0 || // if it is the first character 01502 i==0 || // the previous may not be a digit 01503 !isId(s.at(i-1)) 01504 ) 01505 ) 01506 csp++; 01507 else // reset counter 01508 csp=0; 01509 01510 // search for "virtual" 01511 if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual" 01512 (vsp>0 || // if it is the first character 01513 i==0 || // the previous may not be a digit 01514 !isId(s.at(i-1)) 01515 ) 01516 ) 01517 vsp++; 01518 else // reset counter 01519 vsp=0; 01520 01521 if (c=='"') // quoted string 01522 { 01523 i++; 01524 result+=c; 01525 while (i<l) 01526 { 01527 char cc=s.at(i); 01528 result+=cc; 01529 if (cc=='\\') // escaped character 01530 { result+=s.at(i+1); i+=2; } 01531 else if (cc=='"') // end of string 01532 { i++; goto nextChar; } 01533 else // any other character 01534 { i++; } 01535 } 01536 } 01537 else if (i<l-2 && c=='<' && // current char is a < 01538 (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space 01539 (i<8 || !findOperator(s,i)) // string in front is not "operator" 01540 ) 01541 { 01542 result+="< "; // insert extra space for layouting (nested) templates 01543 } 01544 else if (i>0 && c=='>' && // current char is a > 01545 (isId(s.at(i-1)) || isspace((uchar)s.at(i-1)) || s.at(i-1)=='*' || s.at(i-1)=='&') && // prev char is an id char or space 01546 (i<8 || !findOperator(s,i)) // string in front is not "operator" 01547 ) 01548 { 01549 result+=" >"; // insert extra space for layouting (nested) templates 01550 } 01551 else if (i>0 && c==',' && !isspace((uchar)s.at(i-1)) 01552 && ((i<l-1 && isId(s.at(i+1))) 01553 || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) // for PHP 01554 || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))) // for PHP 01555 { 01556 result+=", "; 01557 } 01558 else if (i>0 && 01559 ((isId(s.at(i)) && s.at(i-1)==')') || 01560 (s.at(i)=='\'' && s.at(i-1)==' ') 01561 ) 01562 ) 01563 { 01564 result+=' '; 01565 result+=s.at(i); 01566 } 01567 else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ && 01568 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 01569 s.at(i+1)==')' || 01570 s.at(i+1)==',' || 01571 s.at(i+1)=='\0' 01572 ) 01573 ) 01574 // prevent const ::A from being converted to const::A 01575 { 01576 result+="t "; 01577 if (s.at(i+1)==' ') i++; 01578 csp=0; 01579 } 01580 else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/) 01581 // replace const::A by const ::A 01582 { 01583 result+=" :"; 01584 csp=0; 01585 } 01586 else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ && 01587 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ || 01588 s.at(i+1)==')' || 01589 s.at(i+1)==',' || 01590 s.at(i+1)=='\0' 01591 ) 01592 ) 01593 // prevent virtual ::A from being converted to virtual::A 01594 { 01595 result+="l "; 01596 if (s.at(i+1)==' ') i++; 01597 vsp=0; 01598 } 01599 else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/) 01600 // replace virtual::A by virtual ::A 01601 { 01602 result+=" :"; 01603 vsp=0; 01604 } 01605 else if (!isspace((uchar)c) || // not a space 01606 ( i>0 && i<l-1 && // internal character 01607 (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']') 01608 && (isId(s.at(i+1)) || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) 01609 || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3)))) 01610 ) 01611 ) 01612 { 01613 if (c=='*' || c=='&' || c=='@' || c=='$') 01614 { 01615 uint rl=result.length(); 01616 if ((rl>0 && (isId(result.at(rl-1)) || result.at(rl-1)=='>')) && 01617 (c!='*' || !findOperator2(s,i)) // avoid splitting operator* and operator->* 01618 ) 01619 { 01620 result+=' '; 01621 } 01622 } 01623 result+=c; 01624 if (cliSupport && (c=='^' || c=='%') && i>1 && isId(s.at(i-1))) result+=' '; // C++/CLI: Type^ name and Type% name 01625 } 01626 } 01627 //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data()); 01628 return result; 01629 } 01630 01631 bool rightScopeMatch(const QCString &scope, const QCString &name) 01632 { 01633 return (name==scope || // equal 01634 (scope.right(name.length())==name && // substring 01635 scope.at(scope.length()-name.length()-1)==':' // scope 01636 ) 01637 ); 01638 } 01639 01640 bool leftScopeMatch(const QCString &scope, const QCString &name) 01641 { 01642 return (name==scope || // equal 01643 (scope.left(name.length())==name && // substring 01644 scope.at(name.length())==':' // scope 01645 ) 01646 ); 01647 } 01648 01649 01650 void linkifyText(const TextGeneratorIntf &out,Definition *scope, 01651 FileDef *fileScope,const char *, 01652 const char *text, bool autoBreak,bool external, 01653 bool keepSpaces) 01654 { 01655 //printf("`%s'\n",text); 01656 static QRegExp regExp("[a-z_A-Z][~!a-z_A-Z0-9.:]*"); 01657 static QRegExp regExpSplit("(?!:),"); 01658 QCString txtStr=text; 01659 int strLen = txtStr.length(); 01660 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d\n", 01661 // scope?scope->name().data():"<none>", 01662 // fileScope?fileScope->name().data():"<none>", 01663 // txtStr.data(),strLen); 01664 int matchLen; 01665 int index=0; 01666 int newIndex; 01667 int skipIndex=0; 01668 int floatingIndex=0; 01669 if (strLen==0) return; 01670 // read a word from the text string 01671 while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 && 01672 (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers 01673 ) 01674 { 01675 // add non-word part to the result 01676 floatingIndex+=newIndex-skipIndex+matchLen; 01677 bool insideString=FALSE; 01678 int i; 01679 for (i=index;i<newIndex;i++) 01680 { 01681 if (txtStr.at(i)=='"') insideString=!insideString; 01682 } 01683 01684 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak); 01685 if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point 01686 { 01687 QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex); 01688 int splitLength = splitText.length(); 01689 int offset=1; 01690 i=splitText.find(regExpSplit,0); 01691 if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; } 01692 if (i==-1) i=splitText.find('>'); 01693 if (i==-1) i=splitText.find(' '); 01694 //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset); 01695 if (i!=-1) // add a link-break at i in case of Html output 01696 { 01697 out.writeString(splitText.left(i+offset),keepSpaces); 01698 out.writeBreak(); 01699 out.writeString(splitText.right(splitLength-i-offset),keepSpaces); 01700 floatingIndex=splitLength-i-offset+matchLen; 01701 } 01702 else 01703 { 01704 out.writeString(splitText,keepSpaces); 01705 } 01706 } 01707 else 01708 { 01709 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex)); 01710 out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces); 01711 } 01712 // get word from string 01713 QCString word=txtStr.mid(newIndex,matchLen); 01714 QCString matchWord = substitute(word,".","::"); 01715 //printf("linkifyText word=%s matchWord=%s scope=%s\n", 01716 // word.data(),matchWord.data(),scope?scope->name().data():"<none>"); 01717 bool found=FALSE; 01718 if (!insideString) 01719 { 01720 ClassDef *cd=0; 01721 FileDef *fd=0; 01722 MemberDef *md=0; 01723 NamespaceDef *nd=0; 01724 GroupDef *gd=0; 01725 //printf("** Match word '%s'\n",matchWord.data()); 01726 01727 MemberDef *typeDef=0; 01728 if ((cd=getResolvedClass(scope,fileScope,matchWord,&typeDef))) 01729 { 01730 //printf("Found class %s\n",cd->name().data()); 01731 // add link to the result 01732 if (external ? cd->isLinkable() : cd->isLinkableInProject()) 01733 { 01734 out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word); 01735 found=TRUE; 01736 } 01737 } 01738 else if (typeDef) 01739 { 01740 //printf("Found typedef %s\n",typeDef->name().data()); 01741 if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject()) 01742 { 01743 out.writeLink(typeDef->getReference(), 01744 typeDef->getOutputFileBase(), 01745 typeDef->anchor(), 01746 word); 01747 found=TRUE; 01748 } 01749 } 01750 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well 01751 { 01752 // add link to the result 01753 if (external ? cd->isLinkable() : cd->isLinkableInProject()) 01754 { 01755 out.writeLink(cd->getReference(),cd->getOutputFileBase(),0,word); 01756 found=TRUE; 01757 } 01758 } 01759 else 01760 { 01761 //printf(" -> nothing\n"); 01762 } 01763 01764 QCString scopeName; 01765 if (scope && 01766 (scope->definitionType()==Definition::TypeClass || 01767 scope->definitionType()==Definition::TypeNamespace 01768 ) 01769 ) 01770 { 01771 scopeName=scope->name(); 01772 } 01773 //printf("ScopeName=%s\n",scopeName.data()); 01774 //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data()); 01775 if (!found && 01776 getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) && 01777 (md->isTypedef() || md->isEnumerate() || 01778 md->isReference() || md->isVariable() 01779 ) && 01780 (external ? md->isLinkable() : md->isLinkableInProject()) 01781 ) 01782 { 01783 //printf("Found ref scope=%s\n",d?d->name().data():"<global>"); 01784 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(), 01785 // md->anchor(),word); 01786 out.writeLink(md->getReference(),md->getOutputFileBase(), 01787 md->anchor(),word); 01788 found=TRUE; 01789 } 01790 } 01791 01792 if (!found) // add word to the result 01793 { 01794 out.writeString(word,keepSpaces); 01795 } 01796 // set next start point in the string 01797 //printf("index=%d/%d\n",index,txtStr.length()); 01798 skipIndex=index=newIndex+matchLen; 01799 } 01800 // add last part of the string to the result. 01801 //ol.docify(txtStr.right(txtStr.length()-skipIndex)); 01802 out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces); 01803 } 01804 01805 01806 void writeExample(OutputList &ol,ExampleSDict *ed) 01807 { 01808 QCString exampleLine=theTranslator->trWriteList(ed->count()); 01809 01810 //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex); 01811 //bool manEnabled = ol.isEnabled(OutputGenerator::Man); 01812 //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html); 01813 QRegExp marker("@[0-9]+"); 01814 int index=0,newIndex,matchLen; 01815 // now replace all markers in inheritLine with links to the classes 01816 while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1) 01817 { 01818 bool ok; 01819 ol.parseText(exampleLine.mid(index,newIndex-index)); 01820 uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok); 01821 Example *e=ed->at(entryIndex); 01822 if (ok && e) 01823 { 01824 ol.pushGeneratorState(); 01825 //if (latexEnabled) ol.disable(OutputGenerator::Latex); 01826 ol.disable(OutputGenerator::Latex); 01827 ol.disable(OutputGenerator::RTF); 01828 // link for Html / man 01829 ol.writeObjectLink(0,e->file,e->anchor,e->name); 01830 ol.popGeneratorState(); 01831 01832 ol.pushGeneratorState(); 01833 //if (latexEnabled) ol.enable(OutputGenerator::Latex); 01834 ol.disable(OutputGenerator::Man); 01835 ol.disable(OutputGenerator::Html); 01836 // link for Latex / pdf with anchor because the sources 01837 // are not hyperlinked (not possible with a verbatim environment). 01838 ol.writeObjectLink(0,e->file,0,e->name); 01839 //if (manEnabled) ol.enable(OutputGenerator::Man); 01840 //if (htmlEnabled) ol.enable(OutputGenerator::Html); 01841 ol.popGeneratorState(); 01842 } 01843 index=newIndex+matchLen; 01844 } 01845 ol.parseText(exampleLine.right(exampleLine.length()-index)); 01846 ol.writeString("."); 01847 } 01848 01849 01850 QCString argListToString(ArgumentList *al,bool useCanonicalType) 01851 { 01852 QCString result; 01853 if (al==0) return result; 01854 Argument *a=al->first(); 01855 result+="("; 01856 while (a) 01857 { 01858 QCString type1 = useCanonicalType && !a->canType.isEmpty() ? 01859 a->canType : a->type; 01860 QCString type2; 01861 int i=type1.find(")("); // hack to deal with function pointers 01862 if (i!=-1) 01863 { 01864 type2=type1.mid(i); 01865 type1=type1.left(i); 01866 } 01867 if (!a->attrib.isEmpty()) 01868 { 01869 result+=a->attrib+" "; 01870 } 01871 if (!a->name.isEmpty() || !a->array.isEmpty()) 01872 { 01873 result+= type1+" "+a->name+type2+a->array; 01874 } 01875 else 01876 { 01877 result+= type1+type2; 01878 } 01879 if (!a->defval.isEmpty()) 01880 { 01881 result+="="+a->defval; 01882 } 01883 a = al->next(); 01884 if (a) result+=", "; 01885 } 01886 result+=")"; 01887 if (al->constSpecifier) result+=" const"; 01888 if (al->volatileSpecifier) result+=" volatile"; 01889 return removeRedundantWhiteSpace(result); 01890 } 01891 01892 QCString tempArgListToString(ArgumentList *al) 01893 { 01894 QCString result; 01895 if (al==0) return result; 01896 result="<"; 01897 Argument *a=al->first(); 01898 while (a) 01899 { 01900 if (!a->name.isEmpty()) // add template argument name 01901 { 01902 result+=a->name; 01903 } 01904 else // extract name from type 01905 { 01906 int i=a->type.length()-1; 01907 while (i>=0 && isId(a->type.at(i))) i--; 01908 if (i>0) 01909 { 01910 result+=a->type.right(a->type.length()-i-1); 01911 } 01912 } 01913 a=al->next(); 01914 if (a) result+=", "; 01915 } 01916 result+=">"; 01917 return removeRedundantWhiteSpace(result); 01918 } 01919 01920 01921 // compute the HTML anchors for a list of members 01922 void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId) 01923 { 01924 int count=0; 01925 if (ml==0) return; 01926 MemberListIterator mli(*ml); 01927 MemberDef *md; 01928 for (;(md=mli.current());++mli) 01929 { 01930 if (!md->isReference()) 01931 { 01932 QCString anchor; 01933 if (groupId==-1) 01934 anchor.sprintf("%c%d",id,count++); 01935 else 01936 anchor.sprintf("%c%d_%d",id,groupId,count++); 01937 if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE)); 01938 md->setAnchor(anchor); 01939 //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n", 01940 // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data()); 01941 } 01942 } 01943 } 01944 01945 //---------------------------------------------------------------------------- 01946 01952 int filterCRLF(char *buf,int len) 01953 { 01954 int src = 0; // source index 01955 int dest = 0; // destination index 01956 char c; // current character 01957 01958 while (src<len) 01959 { 01960 c = buf[src++]; // Remember the processed character. 01961 if (c == '\r') // CR to be solved (MAC, DOS) 01962 { 01963 c = '\n'; // each CR to LF 01964 if (src<len && buf[src] == '\n') 01965 ++src; // skip LF just after CR (DOS) 01966 } 01967 else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser 01968 { 01969 c = ' '; // turn into a space 01970 } 01971 buf[dest++] = c; // copy the (modified) character to dest 01972 } 01973 return dest; // length of the valid part of the buf 01974 } 01975 01976 01980 QCString getFileFilter(const char* name) 01981 { 01982 // sanity check 01983 if (name==0) return ""; 01984 01985 // first look for filter pattern list 01986 QStrList& filterList = Config_getList("FILTER_PATTERNS"); 01987 01988 if (filterList.isEmpty()) 01989 { 01990 // use INPUT_FILTER instead (For all files) 01991 return Config_getString("INPUT_FILTER"); 01992 } 01993 01994 // compare the file name to the filter pattern list 01995 QStrListIterator sli(filterList); 01996 char* filterStr; 01997 for (sli.toFirst(); (filterStr = sli.current()); ++sli) 01998 { 01999 QCString fs = filterStr; 02000 int i_equals=fs.find('='); 02001 02002 if (i_equals!=-1) 02003 { 02004 QCString filterPattern = fs.left(i_equals); 02005 QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE); 02006 if (fpat.match(name)!=-1) 02007 { 02008 // found a match! 02009 QCString filterName = fs.mid(i_equals+1); 02010 if (filterName.find(' ')!=-1) 02011 { // add quotes if the name has spaces 02012 filterName="\""+filterName+"\""; 02013 } 02014 return filterName; 02015 } 02016 } 02017 } 02018 02019 // no match 02020 return ""; 02021 } 02022 02023 QCString recodeString(const QCString &str,const char *fromEncoding,const char *toEncoding) 02024 { 02025 QCString inputEncoding = fromEncoding; 02026 QCString outputEncoding = toEncoding; 02027 if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || 02028 inputEncoding==outputEncoding) return str; 02029 int inputSize=str.length(); 02030 int outputSize=inputSize*4+1; 02031 QCString output(outputSize); 02032 void *cd = portable_iconv_open(outputEncoding,inputEncoding); 02033 if (cd==(void *)(-1)) 02034 { 02035 err("Error: unsupported character conversion: '%s'->'%s'\n", 02036 inputEncoding.data(),outputEncoding.data()); 02037 exit(1); 02038 } 02039 size_t iLeft=inputSize; 02040 size_t oLeft=outputSize; 02041 const char *inputPtr = str.data(); 02042 char *outputPtr = output.data(); 02043 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft)) 02044 { 02045 outputSize-=oLeft; 02046 output.resize(outputSize+1); 02047 output.at(outputSize)='\0'; 02048 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data()); 02049 } 02050 else 02051 { 02052 err("Error: failed to translate characters from %s to %s: %s\n", 02053 inputEncoding.data(),outputEncoding.data(),strerror(errno)); 02054 exit(1); 02055 } 02056 portable_iconv_close(cd); 02057 return output; 02058 } 02059 02060 02061 QCString transcodeCharacterStringToUTF8(const QCString &input) 02062 { 02063 static QCString inputEncoding = Config_getString("INPUT_ENCODING"); 02064 const char *outputEncoding = "UTF-8"; 02065 if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input; 02066 int inputSize=input.length(); 02067 int outputSize=inputSize*4+1; 02068 QCString output(outputSize); 02069 void *cd = portable_iconv_open(outputEncoding,inputEncoding); 02070 if (cd==(void *)(-1)) 02071 { 02072 err("Error: unsupported character conversion: '%s'->'%s'\n", 02073 inputEncoding.data(),outputEncoding); 02074 exit(1); 02075 } 02076 size_t iLeft=inputSize; 02077 size_t oLeft=outputSize; 02078 const char *inputPtr = input.data(); 02079 char *outputPtr = output.data(); 02080 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft)) 02081 { 02082 outputSize-=oLeft; 02083 output.resize(outputSize+1); 02084 output.at(outputSize)='\0'; 02085 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data()); 02086 } 02087 else 02088 { 02089 err("Error: failed to translate characters from %s to %s: check INPUT_ENCODING\n", 02090 inputEncoding.data(),outputEncoding); 02091 exit(1); 02092 } 02093 portable_iconv_close(cd); 02094 return output; 02095 } 02096 02101 QCString fileToString(const char *name,bool filter) 02102 { 02103 if (name==0 || name[0]==0) return 0; 02104 QFile f; 02105 02106 bool fileOpened=FALSE; 02107 if (name[0]=='-' && name[1]==0) // read from stdin 02108 { 02109 fileOpened=f.open(IO_ReadOnly,stdin); 02110 if (fileOpened) 02111 { 02112 const int bSize=4096; 02113 QCString contents(bSize); 02114 int totalSize=0; 02115 int size; 02116 while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize) 02117 { 02118 totalSize+=bSize; 02119 contents.resize(totalSize+bSize); 02120 } 02121 totalSize = filterCRLF(contents.data(),totalSize+size)+2; 02122 contents.resize(totalSize); 02123 contents.at(totalSize-2)='\n'; // to help the scanner 02124 contents.at(totalSize-1)='\0'; 02125 return contents; 02126 } 02127 } 02128 else // read from file 02129 { 02130 QFileInfo fi(name); 02131 if (!fi.exists() || !fi.isFile()) 02132 { 02133 err("Error: file `%s' not found\n",name); 02134 return ""; 02135 } 02136 QCString filterName = getFileFilter(name); 02137 if (filterName.isEmpty() || !filter) 02138 { 02139 f.setName(name); 02140 fileOpened=f.open(IO_ReadOnly); 02141 if (fileOpened) 02142 { 02143 int fsize=f.size(); 02144 QCString contents(fsize+2); 02145 f.readBlock(contents.data(),fsize); 02146 if (fsize==0 || contents[fsize-1]=='\n') 02147 contents[fsize]='\0'; 02148 else 02149 contents[fsize]='\n'; // to help the scanner 02150 contents[fsize+1]='\0'; 02151 f.close(); 02152 int newSize = filterCRLF(contents.data(),fsize+2); 02153 if (newSize!=fsize+2) 02154 { 02155 contents.resize(newSize); 02156 } 02157 return transcodeCharacterStringToUTF8(contents); 02158 } 02159 } 02160 else // filter the input 02161 { 02162 QCString cmd=filterName+" \""+name+"\""; 02163 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data()); 02164 FILE *f=portable_popen(cmd,"r"); 02165 if (!f) 02166 { 02167 err("Error: could not execute filter %s\n",filterName.data()); 02168 return ""; 02169 } 02170 const int bSize=4096; 02171 QCString contents(bSize); 02172 int totalSize=0; 02173 int size; 02174 while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize) 02175 { 02176 totalSize+=bSize; 02177 contents.resize(totalSize+bSize); 02178 } 02179 totalSize = filterCRLF(contents.data(),totalSize+size)+2; 02180 contents.resize(totalSize); 02181 contents.at(totalSize-2)='\n'; // to help the scanner 02182 contents.at(totalSize-1)='\0'; 02183 portable_pclose(f); 02184 return transcodeCharacterStringToUTF8(contents); 02185 } 02186 } 02187 if (!fileOpened) 02188 { 02189 err("Error: cannot open file `%s' for reading\n",name); 02190 } 02191 return ""; 02192 } 02193 02194 QCString dateToString(bool includeTime) 02195 { 02196 if (includeTime) 02197 { 02198 return convertToQCString(QDateTime::currentDateTime().toString()); 02199 } 02200 else 02201 { 02202 const QDate &d=QDate::currentDate(); 02203 QCString result; 02204 result.sprintf("%d %s %d", 02205 d.day(), 02206 convertToQCString(d.monthName(d.month())).data(), 02207 d.year()); 02208 return result; 02209 } 02210 //QDate date=dt.date(); 02211 //QTime time=dt.time(); 02212 //QCString dtString; 02213 //dtString.sprintf("%02d:%02d, %04d/%02d/%02d", 02214 // time.hour(),time.minute(),date.year(),date.month(),date.day()); 02215 //return dtString; 02216 } 02217 02218 QCString yearToString() 02219 { 02220 const QDate &d=QDate::currentDate(); 02221 QCString result; 02222 result.sprintf("%d", d.year()); 02223 return result; 02224 } 02225 02226 //---------------------------------------------------------------------- 02227 // recursive function that returns the number of branches in the 02228 // inheritance tree that the base class `bcd' is below the class `cd' 02229 02230 int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level) 02231 { 02232 if (bcd->categoryOf()) // use class that is being extended in case of 02233 // an Objective-C category 02234 { 02235 bcd=bcd->categoryOf(); 02236 } 02237 if (cd==bcd) return level; 02238 if (level==256) 02239 { 02240 err("Error: Internal inconsistency: found class %s seem to have a recursive " 02241 "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data()); 02242 return -1; 02243 } 02244 int m=maxInheritanceDepth; 02245 if (cd->baseClasses()) 02246 { 02247 BaseClassListIterator bcli(*cd->baseClasses()); 02248 for ( ; bcli.current() ; ++bcli) 02249 { 02250 //printf("class %s base class %s\n",cd->name().data(),bcli.current()->classDef->name().data()); 02251 int mc=minClassDistance(bcli.current()->classDef,bcd,level+1); 02252 if (mc<m) m=mc; 02253 if (m<0) break; 02254 } 02255 } 02256 return m; 02257 } 02258 02259 //static void printArgList(ArgumentList *al) 02260 //{ 02261 // if (al==0) return; 02262 // ArgumentListIterator ali(*al); 02263 // Argument *a; 02264 // printf("("); 02265 // for (;(a=ali.current());++ali) 02266 // { 02267 // printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():""); 02268 // } 02269 // printf(")"); 02270 //} 02271 02272 #ifndef NEWMATCH 02273 // strip any template specifiers that follow className in string s 02274 static QCString trimTemplateSpecifiers( 02275 const QCString &namespaceName, 02276 const QCString &className, 02277 const QCString &s 02278 ) 02279 { 02280 //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data()); 02281 QCString scopeName=mergeScopes(namespaceName,className); 02282 ClassDef *cd=getClass(scopeName); 02283 if (cd==0) return s; // should not happen, but guard anyway. 02284 02285 QCString result=s; 02286 02287 int i=className.length()-1; 02288 if (i>=0 && className.at(i)=='>') // template specialization 02289 { 02290 // replace unspecialized occurrences in s, with their specialized versions. 02291 int count=1; 02292 int cl=i+1; 02293 while (i>=0) 02294 { 02295 char c=className.at(i); 02296 if (c=='>') count++,i--; 02297 else if (c=='<') { count--; if (count==0) break; } 02298 else i--; 02299 } 02300 QCString unspecClassName=className.left(i); 02301 int l=i; 02302 int p=0; 02303 while ((i=result.find(unspecClassName,p))!=-1) 02304 { 02305 if (result.at(i+l)!='<') // unspecialized version 02306 { 02307 result=result.left(i)+className+result.right(result.length()-i-l); 02308 l=cl; 02309 } 02310 p=i+l; 02311 } 02312 } 02313 02314 //printf("result after specialization: %s\n",result.data()); 02315 02316 QCString qualName=cd->qualifiedNameWithTemplateParameters(); 02317 //printf("QualifiedName = %s\n",qualName.data()); 02318 // We strip the template arguments following className (if any) 02319 if (!qualName.isEmpty()) // there is a class name 02320 { 02321 int is,ps=0; 02322 int p=0,l,i; 02323 02324 while ((is=getScopeFragment(qualName,ps,&l))!=-1) 02325 { 02326 QCString qualNamePart = qualName.right(qualName.length()-is); 02327 //printf("qualNamePart=%s\n",qualNamePart.data()); 02328 while ((i=result.find(qualNamePart,p))!=-1) 02329 { 02330 int ql=qualNamePart.length(); 02331 result=result.left(i)+cd->name()+result.right(result.length()-i-ql); 02332 p=i+cd->name().length(); 02333 } 02334 ps=is+l; 02335 } 02336 } 02337 //printf("result=%s\n",result.data()); 02338 02339 return result.stripWhiteSpace(); 02340 } 02341 02349 static int findScopePattern(const QCString &pattern,const QCString &s, 02350 int p,int *len) 02351 { 02352 int sl=s.length(); 02353 int pl=pattern.length(); 02354 int sp=0; 02355 *len=0; 02356 while (p<sl) 02357 { 02358 sp=p; // start of match 02359 int pp=0; // pattern position 02360 while (p<sl && pp<pl) 02361 { 02362 if (s.at(p)=='<') // skip template arguments while matching 02363 { 02364 int bc=1; 02365 //printf("skipping pos=%d c=%c\n",p,s.at(p)); 02366 p++; 02367 while (p<sl) 02368 { 02369 if (s.at(p)=='<') bc++; 02370 else if (s.at(p)=='>') 02371 { 02372 bc--; 02373 if (bc==0) 02374 { 02375 p++; 02376 break; 02377 } 02378 } 02379 //printf("skipping pos=%d c=%c\n",p,s.at(p)); 02380 p++; 02381 } 02382 } 02383 else if (s.at(p)==pattern.at(pp)) 02384 { 02385 //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p)); 02386 p++; 02387 pp++; 02388 } 02389 else // no match 02390 { 02391 //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data()); 02392 p=sp+1; 02393 break; 02394 } 02395 } 02396 if (pp==pl) // whole pattern matches 02397 { 02398 *len=p-sp; 02399 return sp; 02400 } 02401 } 02402 return -1; 02403 } 02404 02405 static QCString trimScope(const QCString &name,const QCString &s) 02406 { 02407 int scopeOffset=name.length(); 02408 QCString result=s; 02409 do // for each scope 02410 { 02411 QCString tmp; 02412 QCString scope=name.left(scopeOffset)+"::"; 02413 //printf("Trying with scope=`%s'\n",scope.data()); 02414 02415 int i,p=0,l; 02416 while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence 02417 { 02418 tmp+=result.mid(p,i-p); // add part before pattern 02419 p=i+l; 02420 } 02421 tmp+=result.right(result.length()-p); // add trailing part 02422 02423 scopeOffset=name.findRev("::",scopeOffset-1); 02424 result = tmp; 02425 } while (scopeOffset>0); 02426 //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data()); 02427 return result; 02428 } 02429 #endif 02430 02431 void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0) 02432 { 02433 //printf("trimBaseClassScope level=%d `%s'\n",level,s.data()); 02434 BaseClassListIterator bcli(*bcl); 02435 BaseClassDef *bcd; 02436 for (;(bcd=bcli.current());++bcli) 02437 { 02438 ClassDef *cd=bcd->classDef; 02439 //printf("Trying class %s\n",cd->name().data()); 02440 int spos=s.find(cd->name()+"::"); 02441 if (spos!=-1) 02442 { 02443 s = s.left(spos)+s.right( 02444 s.length()-spos-cd->name().length()-2 02445 ); 02446 } 02447 //printf("base class `%s'\n",cd->name().data()); 02448 if (cd->baseClasses()) 02449 trimBaseClassScope(cd->baseClasses(),s,level+1); 02450 } 02451 } 02452 02453 #if 0 02454 02458 static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName) 02459 { 02460 int p1=t1.length(); 02461 int p2=t2.length(); 02462 for (;;) 02463 { 02464 int i1=p1==0 ? -1 : t1.findRev("::",p1); 02465 int i2=p2==0 ? -1 : t2.findRev("::",p2); 02466 if (i1==-1 && i2==-1) 02467 { 02468 return; 02469 } 02470 if (i1!=-1 && i2==-1) // only t1 has a scope 02471 { 02472 QCString scope=t1.left(i1); 02473 replaceNamespaceAliases(scope,i1); 02474 02475 int so=nsName.length(); 02476 do 02477 { 02478 QCString fullScope=nsName.left(so); 02479 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::"; 02480 fullScope+=scope; 02481 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace 02482 { 02483 t1 = t1.right(t1.length()-i1-2); 02484 return; 02485 } 02486 if (so==0) 02487 { 02488 so=-1; 02489 } 02490 else if ((so=nsName.findRev("::",so-1))==-1) 02491 { 02492 so=0; 02493 } 02494 } 02495 while (so>=0); 02496 } 02497 else if (i1==-1 && i2!=-1) // only t2 has a scope 02498 { 02499 QCString scope=t2.left(i2); 02500 replaceNamespaceAliases(scope,i2); 02501 02502 int so=nsName.length(); 02503 do 02504 { 02505 QCString fullScope=nsName.left(so); 02506 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::"; 02507 fullScope+=scope; 02508 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace 02509 { 02510 t2 = t2.right(t2.length()-i2-2); 02511 return; 02512 } 02513 if (so==0) 02514 { 02515 so=-1; 02516 } 02517 else if ((so=nsName.findRev("::",so-1))==-1) 02518 { 02519 so=0; 02520 } 02521 } 02522 while (so>=0); 02523 } 02524 p1 = QMAX(i1-2,0); 02525 p2 = QMAX(i2-2,0); 02526 } 02527 } 02528 #endif 02529 02530 static void stripIrrelevantString(QCString &target,const QCString &str) 02531 { 02532 if (target==str) { target.resize(0); return; } 02533 int i,p=0; 02534 int l=str.length(); 02535 bool changed=FALSE; 02536 while ((i=target.find(str,p))!=-1) 02537 { 02538 bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str 02539 (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str 02540 if (isMatch) 02541 { 02542 int i1=target.find('*',i+l); 02543 int i2=target.find('&',i+l); 02544 if (i1==-1 && i2==-1) 02545 { 02546 // strip str from target at index i 02547 target=target.left(i)+target.right(target.length()-i-l); 02548 changed=TRUE; 02549 i-=l; 02550 } 02551 else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or & 02552 { 02553 // move str to front 02554 target=str+" "+target.left(i)+target.right(target.length()-i-l); 02555 changed=TRUE; 02556 i++; 02557 } 02558 } 02559 p = i+l; 02560 } 02561 if (changed) target=target.stripWhiteSpace(); 02562 } 02563 02579 void stripIrrelevantConstVolatile(QCString &s) 02580 { 02581 //printf("stripIrrelevantConstVolatile(%s)=",s.data()); 02582 stripIrrelevantString(s,"const"); 02583 stripIrrelevantString(s,"volatile"); 02584 //printf("%s\n",s.data()); 02585 } 02586 02587 02588 // a bit of debug support for matchArguments 02589 #define MATCH 02590 #define NOMATCH 02591 //#define MATCH printf("Match at line %d\n",__LINE__); 02592 //#define NOMATCH printf("Nomatch at line %d\n",__LINE__); 02593 02594 #ifndef NEWMATCH 02595 static bool matchArgument(const Argument *srcA,const Argument *dstA, 02596 const QCString &className, 02597 const QCString &namespaceName, 02598 NamespaceSDict *usingNamespaces, 02599 SDict<Definition> *usingClasses) 02600 { 02601 //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n", 02602 // srcA->type.data(),srcA->name.data(), 02603 // dstA->type.data(),dstA->name.data(), 02604 // usingNamespaces, 02605 // usingClasses); 02606 02607 // TODO: resolve any typedefs names that are part of srcA->type 02608 // before matching. This should use className and namespaceName 02609 // and usingNamespaces and usingClass to determine which typedefs 02610 // are in-scope, so it will not be very efficient :-( 02611 02612 QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type); 02613 QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type); 02614 QCString srcAName=srcA->name.stripWhiteSpace(); 02615 QCString dstAName=dstA->name.stripWhiteSpace(); 02616 srcAType.stripPrefix("class "); 02617 dstAType.stripPrefix("class "); 02618 02619 // allow distingishing "const A" from "const B" even though 02620 // from a syntactic point of view they would be two names of the same 02621 // type "const". This is not fool prove ofcourse, but should at least 02622 // catch the most common cases. 02623 if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty()) 02624 { 02625 srcAType+=" "; 02626 srcAType+=srcAName; 02627 } 02628 if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty()) 02629 { 02630 dstAType+=" "; 02631 dstAType+=dstAName; 02632 } 02633 if (srcAName=="const" || srcAName=="volatile") 02634 { 02635 srcAType+=srcAName; 02636 srcAName.resize(0); 02637 } 02638 else if (dstA->name=="const" || dstA->name=="volatile") 02639 { 02640 dstAType+=dstA->name; 02641 dstAName.resize(0); 02642 } 02643 02644 stripIrrelevantConstVolatile(srcAType); 02645 stripIrrelevantConstVolatile(dstAType); 02646 02647 // strip typename keyword 02648 if (strncmp(srcAType,"typename ",9)==0) 02649 { 02650 srcAType = srcAType.right(srcAType.length()-9); 02651 } 02652 if (strncmp(dstAType,"typename ",9)==0) 02653 { 02654 dstAType = dstAType.right(dstAType.length()-9); 02655 } 02656 02657 srcAType = removeRedundantWhiteSpace(srcAType); 02658 dstAType = removeRedundantWhiteSpace(dstAType); 02659 02660 //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE); 02661 //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE); 02662 02663 //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(), 02664 // dstAType.data(),dstAName.data()); 02665 02666 if (srcA->array!=dstA->array) // nomatch for char[] against char 02667 { 02668 NOMATCH 02669 return FALSE; 02670 } 02671 if (srcAType!=dstAType) // check if the argument only differs on name 02672 { 02673 02674 // remove a namespace scope that is only in one type 02675 // (assuming a using statement was used) 02676 //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data()); 02677 //trimNamespaceScope(srcAType,dstAType,namespaceName); 02678 //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data()); 02679 02680 //QCString srcScope; 02681 //QCString dstScope; 02682 02683 // strip redundant scope specifiers 02684 if (!className.isEmpty()) 02685 { 02686 srcAType=trimScope(className,srcAType); 02687 dstAType=trimScope(className,dstAType); 02688 //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data()); 02689 ClassDef *cd; 02690 if (!namespaceName.isEmpty()) 02691 cd=getClass(namespaceName+"::"+className); 02692 else 02693 cd=getClass(className); 02694 if (cd && cd->baseClasses()) 02695 { 02696 trimBaseClassScope(cd->baseClasses(),srcAType); 02697 trimBaseClassScope(cd->baseClasses(),dstAType); 02698 } 02699 //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data()); 02700 } 02701 if (!namespaceName.isEmpty()) 02702 { 02703 srcAType=trimScope(namespaceName,srcAType); 02704 dstAType=trimScope(namespaceName,dstAType); 02705 } 02706 //printf("#usingNamespace=%d\n",usingNamespaces->count()); 02707 if (usingNamespaces && usingNamespaces->count()>0) 02708 { 02709 NamespaceSDict::Iterator nli(*usingNamespaces); 02710 NamespaceDef *nd; 02711 for (;(nd=nli.current());++nli) 02712 { 02713 srcAType=trimScope(nd->name(),srcAType); 02714 dstAType=trimScope(nd->name(),dstAType); 02715 } 02716 } 02717 //printf("#usingClasses=%d\n",usingClasses->count()); 02718 if (usingClasses && usingClasses->count()>0) 02719 { 02720 SDict<Definition>::Iterator cli(*usingClasses); 02721 Definition *cd; 02722 for (;(cd=cli.current());++cli) 02723 { 02724 srcAType=trimScope(cd->name(),srcAType); 02725 dstAType=trimScope(cd->name(),dstAType); 02726 } 02727 } 02728 02729 //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(), 02730 // dstAType.data(),dstAName.data()); 02731 02732 if (!srcAName.isEmpty() && !dstA->type.isEmpty() && 02733 (srcAType+" "+srcAName)==dstAType) 02734 { 02735 MATCH 02736 return TRUE; 02737 } 02738 else if (!dstAName.isEmpty() && !srcA->type.isEmpty() && 02739 (dstAType+" "+dstAName)==srcAType) 02740 { 02741 MATCH 02742 return TRUE; 02743 } 02744 02745 02746 uint srcPos=0,dstPos=0; 02747 bool equal=TRUE; 02748 while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal) 02749 { 02750 equal=srcAType.at(srcPos)==dstAType.at(dstPos); 02751 if (equal) srcPos++,dstPos++; 02752 } 02753 uint srcATypeLen=srcAType.length(); 02754 uint dstATypeLen=dstAType.length(); 02755 if (srcPos<srcATypeLen && dstPos<dstATypeLen) 02756 { 02757 // if nothing matches or the match ends in the middle or at the 02758 // end of a string then there is no match 02759 if (srcPos==0 || dstPos==0) 02760 { 02761 NOMATCH 02762 return FALSE; 02763 } 02764 if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos))) 02765 { 02766 //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos); 02767 // check if a name if already found -> if no then there is no match 02768 if (!srcAName.isEmpty() || !dstAName.isEmpty()) 02769 { 02770 NOMATCH 02771 return FALSE; 02772 } 02773 // types only 02774 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++; 02775 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++; 02776 if (srcPos<srcATypeLen || 02777 dstPos<dstATypeLen || 02778 (srcPos==srcATypeLen && dstPos==dstATypeLen) 02779 ) 02780 { 02781 NOMATCH 02782 return FALSE; 02783 } 02784 } 02785 else 02786 { 02787 // otherwise we assume that a name starts at the current position. 02788 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++; 02789 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++; 02790 02791 // if nothing more follows for both types then we assume we have 02792 // found a match. Note that now `signed int' and `signed' match, but 02793 // seeing that int is not a name can only be done by looking at the 02794 // semantics. 02795 02796 if (srcPos!=srcATypeLen || dstPos!=dstATypeLen) 02797 { 02798 NOMATCH 02799 return FALSE; 02800 } 02801 } 02802 } 02803 else if (dstPos<dstAType.length()) 02804 { 02805 if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ 02806 { 02807 if (!dstAName.isEmpty()) // dst has its name separated from its type 02808 { 02809 NOMATCH 02810 return FALSE; 02811 } 02812 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++; 02813 if (dstPos!=dstAType.length()) 02814 { 02815 NOMATCH 02816 return FALSE; // more than a difference in name -> no match 02817 } 02818 } 02819 else // maybe dst has a name while src has not 02820 { 02821 dstPos++; 02822 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++; 02823 if (dstPos!=dstAType.length() || !srcAName.isEmpty()) 02824 { 02825 NOMATCH 02826 return FALSE; // nope not a name -> no match 02827 } 02828 } 02829 } 02830 else if (srcPos<srcAType.length()) 02831 { 02832 if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ 02833 { 02834 if (!srcAName.isEmpty()) // src has its name separated from its type 02835 { 02836 NOMATCH 02837 return FALSE; 02838 } 02839 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++; 02840 if (srcPos!=srcAType.length()) 02841 { 02842 NOMATCH 02843 return FALSE; // more than a difference in name -> no match 02844 } 02845 } 02846 else // maybe src has a name while dst has not 02847 { 02848 srcPos++; 02849 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++; 02850 if (srcPos!=srcAType.length() || !dstAName.isEmpty()) 02851 { 02852 NOMATCH 02853 return FALSE; // nope not a name -> no match 02854 } 02855 } 02856 } 02857 } 02858 MATCH 02859 return TRUE; 02860 } 02861 02862 02870 bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl, 02871 const char *cl,const char *ns,bool checkCV, 02872 NamespaceSDict *usingNamespaces, 02873 SDict<Definition> *usingClasses) 02874 { 02875 QCString className=cl; 02876 QCString namespaceName=ns; 02877 02878 // strip template specialization from class name if present 02879 //int til=className.find('<'),tir=className.find('>'); 02880 //if (til!=-1 && tir!=-1 && tir>til) 02881 //{ 02882 // className=className.left(til)+className.right(className.length()-tir-1); 02883 //} 02884 02885 //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n", 02886 // srcAl ? argListToString(srcAl).data() : "", 02887 // dstAl ? argListToString(dstAl).data() : "", 02888 // cl,ns,checkCV, 02889 // usingNamespaces?usingNamespaces->count():0, 02890 // usingClasses?usingClasses->count():0 02891 // ); 02892 02893 if (srcAl==0 || dstAl==0) 02894 { 02895 bool match = srcAl==dstAl; // at least one of the members is not a function 02896 if (match) 02897 { 02898 MATCH 02899 return TRUE; 02900 } 02901 else 02902 { 02903 NOMATCH 02904 return FALSE; 02905 } 02906 } 02907 02908 // handle special case with void argument 02909 if ( srcAl->count()==0 && dstAl->count()==1 && 02910 dstAl->getFirst()->type=="void" ) 02911 { // special case for finding match between func() and func(void) 02912 Argument *a=new Argument; 02913 a->type = "void"; 02914 srcAl->append(a); 02915 MATCH 02916 return TRUE; 02917 } 02918 if ( dstAl->count()==0 && srcAl->count()==1 && 02919 srcAl->getFirst()->type=="void" ) 02920 { // special case for finding match between func(void) and func() 02921 Argument *a=new Argument; 02922 a->type = "void"; 02923 dstAl->append(a); 02924 MATCH 02925 return TRUE; 02926 } 02927 02928 if (srcAl->count() != dstAl->count()) 02929 { 02930 NOMATCH 02931 return FALSE; // different number of arguments -> no match 02932 } 02933 02934 if (checkCV) 02935 { 02936 if (srcAl->constSpecifier != dstAl->constSpecifier) 02937 { 02938 NOMATCH 02939 return FALSE; // one member is const, the other not -> no match 02940 } 02941 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier) 02942 { 02943 NOMATCH 02944 return FALSE; // one member is volatile, the other not -> no match 02945 } 02946 } 02947 02948 // so far the argument list could match, so we need to compare the types of 02949 // all arguments. 02950 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl); 02951 Argument *srcA,*dstA; 02952 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli) 02953 { 02954 if (!matchArgument(srcA,dstA,className,namespaceName, 02955 usingNamespaces,usingClasses)) 02956 { 02957 NOMATCH 02958 return FALSE; 02959 } 02960 } 02961 MATCH 02962 return TRUE; // all arguments match 02963 } 02964 02965 #endif 02966 02967 #if 0 02968 static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec) 02969 { 02970 ASSERT(symbol!=0); 02971 if (symbol->definitionType()==Definition::TypeMember && 02972 ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try 02973 // to resolve it 02974 { 02975 MemberDef *md = 0; 02976 ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec); 02977 if (cd) 02978 { 02979 return cd->qualifiedName()+templSpec; 02980 } 02981 else if (md) 02982 { 02983 return md->qualifiedName(); 02984 } 02985 } 02986 return symbol->qualifiedName(); 02987 } 02988 #endif 02989 02990 static QCString stripDeclKeywords(const QCString &s) 02991 { 02992 int i=s.find(" class "); 02993 if (i!=-1) return s.left(i)+s.mid(i+6); 02994 i=s.find(" typename "); 02995 if (i!=-1) return s.left(i)+s.mid(i+9); 02996 i=s.find(" union "); 02997 if (i!=-1) return s.left(i)+s.mid(i+6); 02998 i=s.find(" struct "); 02999 if (i!=-1) return s.left(i)+s.mid(i+7); 03000 return s; 03001 } 03002 03003 // forward decl for circular dependencies 03004 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type); 03005 03006 QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec) 03007 { 03008 03009 QCString templSpec = spec.stripWhiteSpace(); 03010 // this part had been commented out before... but it is needed to match for instance 03011 // std::list<std::string> against list<string> so it is now back again! 03012 if (!templSpec.isEmpty() && templSpec.at(0) == '<') 03013 { 03014 templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace()); 03015 } 03016 QCString resolvedType = resolveTypeDef(d,templSpec); 03017 if (!resolvedType.isEmpty()) // not known as a typedef either 03018 { 03019 templSpec = resolvedType; 03020 } 03021 //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data()); 03022 return templSpec; 03023 } 03024 03025 03026 static QCString getCanonicalTypeForIdentifier( 03027 Definition *d,FileDef *fs,const QCString &word, 03028 QCString *tSpec) 03029 { 03030 QCString symName,scope,result,templSpec,tmpName; 03031 //DefinitionList *defList=0; 03032 if (tSpec && !tSpec->isEmpty()) templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec)); 03033 03034 if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty()) 03035 { 03036 symName=tmpName; // name without scope 03037 } 03038 else 03039 { 03040 symName=word; 03041 } 03042 //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n", 03043 // word.data(),tSpec?tSpec->data():"<none>",templSpec.data()); 03044 03045 ClassDef *cd = 0; 03046 MemberDef *mType = 0; 03047 QCString ts; 03048 QCString resolvedType; 03049 03050 // lookup class / class template instance 03051 cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType); 03052 bool isTemplInst = cd && !templSpec.isEmpty(); 03053 if (!cd && !templSpec.isEmpty()) 03054 { 03055 // class template specialization not known, look up class template 03056 cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType); 03057 } 03058 if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations 03059 03060 //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n", 03061 // symName.data(), 03062 // word.data(), 03063 // cd?cd->name().data():"<none>", 03064 // d?d->name().data():"<none>", 03065 // fs?fs->name().data():"<none>", 03066 // cd?cd->isTemplate():-1 03067 // ); 03068 03069 //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n", 03070 // (word+templSpec).data(), 03071 // cd?cd->qualifiedName().data():"<none>", 03072 // templSpec.data(),ts.data(), 03073 // tSpec?tSpec->data():"<null>", 03074 // cd?cd->isTemplate():FALSE, 03075 // resolvedType.data()); 03076 03077 //printf(" mtype=%s\n",mType?mType->name().data():"<none>"); 03078 03079 if (cd) // resolves to a known class type 03080 { 03081 if (mType && mType->isTypedef()) // but via a typedef 03082 { 03083 result = resolvedType; 03084 } 03085 else 03086 { 03087 if (isTemplInst) 03088 { 03089 // spec is already part of class type 03090 templSpec=""; 03091 if (tSpec) *tSpec=""; 03092 } 03093 else if (!ts.isEmpty() && templSpec.isEmpty()) 03094 { 03095 // use formal template args for spec 03096 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts)); 03097 } 03098 03099 result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec); 03100 03101 if (cd->isTemplate() && tSpec) // 03102 { 03103 if (!templSpec.isEmpty()) // specific instance 03104 { 03105 result=cd->name()+templSpec; 03106 } 03107 else // use template type 03108 { 03109 result=cd->qualifiedNameWithTemplateParameters(); 03110 } 03111 // template class, so remove the template part (it is part of the class name) 03112 *tSpec=""; 03113 } 03114 else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec) 03115 { 03116 // obscure case, where a class is used as a template, but doxygen think it is 03117 // not (could happen when loading the class from a tag file). 03118 *tSpec=""; 03119 } 03120 } 03121 } 03122 else if (mType && mType->isEnumerate()) // an enum 03123 { 03124 result = mType->qualifiedName(); 03125 } 03126 else 03127 { 03128 resolvedType = resolveTypeDef(d,word); 03129 if (resolvedType.isEmpty()) // not known as a typedef either 03130 { 03131 result = word; 03132 } 03133 else 03134 { 03135 result = resolvedType; 03136 } 03137 } 03138 //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data()); 03139 return result; 03140 } 03141 03142 static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type) 03143 { 03144 type = type.stripWhiteSpace(); 03145 03146 // strip const and volatile keywords that are not relevant for the type 03147 stripIrrelevantConstVolatile(type); 03148 03149 // strip leading keywords 03150 type.stripPrefix("class "); 03151 type.stripPrefix("struct "); 03152 type.stripPrefix("union "); 03153 type.stripPrefix("enum "); 03154 type.stripPrefix("typename "); 03155 03156 type = removeRedundantWhiteSpace(type); 03157 //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(), 03158 // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>"); 03159 03160 static QRegExp id("[a-z_A-Z][:a-z_A-Z0-9]*"); 03161 03162 QCString canType,templSpec,word; 03163 int i,p=0,pp=0; 03164 while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1) 03165 // foreach identifier in the type 03166 { 03167 //printf(" i=%d p=%d\n",i,p); 03168 if (i>pp) canType += type.mid(pp,i-pp); 03169 03170 03171 canType += getCanonicalTypeForIdentifier(d,fs,word,&templSpec); 03172 //printf(" word=%s templSpec=%s canType=%s\n",word.data(),templSpec.data(),canType.data()); 03173 if (!templSpec.isEmpty()) // if we didn't use up the templSpec already 03174 // (i.e. type is not a template specialization) 03175 // then resolve any identifiers inside. 03176 { 03177 static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*"); 03178 int tp=0,tl,ti; 03179 // for each identifier template specifier 03180 //printf("adding resolved %s to %s\n",templSpec.data(),canType.data()); 03181 while ((ti=re.match(templSpec,tp,&tl))!=-1) 03182 { 03183 canType += templSpec.mid(tp,ti-tp); 03184 canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0); 03185 tp=ti+tl; 03186 } 03187 canType+=templSpec.right(templSpec.length()-tp); 03188 } 03189 03190 pp=p; 03191 } 03192 canType += type.right(type.length()-pp); 03193 //printf("extractCanonicalType = %s->%s\n",type.data(),canType.data()); 03194 03195 return removeRedundantWhiteSpace(canType); 03196 } 03197 03198 static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg) 03199 { 03200 QCString type = arg->type.stripWhiteSpace(); 03201 QCString name = arg->name; 03202 //printf("extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data()); 03203 if ((type=="const" || type=="volatile") && !name.isEmpty()) 03204 { // name is part of type => correct 03205 type+=" "; 03206 type+=name; 03207 } 03208 if (name=="const" || name=="volatile") 03209 { // name is part of type => correct 03210 if (!type.isEmpty()) type+=" "; 03211 type+=name; 03212 } 03213 03214 return extractCanonicalType(d,fs,type); 03215 } 03216 03217 static bool matchArgument2( 03218 Definition *srcScope,FileDef *srcFileScope,Argument *srcA, 03219 Definition *dstScope,FileDef *dstFileScope,Argument *dstA 03220 ) 03221 { 03222 //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n", 03223 // srcScope ? srcScope->name().data() : "", 03224 // srcA->type.data(),srcA->name.data(),srcA->canType.data(), 03225 // dstScope ? dstScope->name().data() : "", 03226 // dstA->type.data(),dstA->name.data(),dstA->canType.data()); 03227 03228 if (srcA->array!=dstA->array) // nomatch for char[] against char 03229 { 03230 NOMATCH 03231 return FALSE; 03232 } 03233 QCString sSrcName = " "+srcA->name; 03234 QCString sDstName = " "+dstA->name; 03235 QCString srcType = srcA->type; 03236 QCString dstType = dstA->type; 03237 stripIrrelevantConstVolatile(srcType); 03238 stripIrrelevantConstVolatile(dstType); 03239 //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data()); 03240 //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data()); 03241 if (sSrcName==dstType.right(sSrcName.length())) 03242 { // case "unsigned int" <-> "unsigned int i" 03243 srcA->type+=sSrcName; 03244 srcA->name=""; 03245 srcA->canType=""; // invalidate cached type value 03246 } 03247 else if (sDstName==srcType.right(sDstName.length())) 03248 { // case "unsigned int i" <-> "unsigned int" 03249 dstA->type+=sDstName; 03250 dstA->name=""; 03251 dstA->canType=""; // invalidate cached type value 03252 } 03253 03254 if (srcA->canType.isEmpty()) 03255 { 03256 srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA); 03257 } 03258 if (dstA->canType.isEmpty()) 03259 { 03260 dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA); 03261 } 03262 03263 if (srcA->canType==dstA->canType) 03264 { 03265 MATCH 03266 return TRUE; 03267 } 03268 else 03269 { 03270 //printf(" Canonical types do not match [%s]<->[%s]\n", 03271 // srcA->canType.data(),dstA->canType.data()); 03272 NOMATCH 03273 return FALSE; 03274 } 03275 } 03276 03277 03278 // new algorithm for argument matching 03279 bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl, 03280 Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl, 03281 bool checkCV 03282 ) 03283 { 03284 ASSERT(srcScope!=0 && dstScope!=0); 03285 03286 if (srcAl==0 || dstAl==0) 03287 { 03288 bool match = srcAl==dstAl; // at least one of the members is not a function 03289 if (match) 03290 { 03291 MATCH 03292 return TRUE; 03293 } 03294 else 03295 { 03296 NOMATCH 03297 return FALSE; 03298 } 03299 } 03300 03301 // handle special case with void argument 03302 if ( srcAl->count()==0 && dstAl->count()==1 && 03303 dstAl->getFirst()->type=="void" ) 03304 { // special case for finding match between func() and func(void) 03305 Argument *a=new Argument; 03306 a->type = "void"; 03307 srcAl->append(a); 03308 MATCH 03309 return TRUE; 03310 } 03311 if ( dstAl->count()==0 && srcAl->count()==1 && 03312 srcAl->getFirst()->type=="void" ) 03313 { // special case for finding match between func(void) and func() 03314 Argument *a=new Argument; 03315 a->type = "void"; 03316 dstAl->append(a); 03317 MATCH 03318 return TRUE; 03319 } 03320 03321 if (srcAl->count() != dstAl->count()) 03322 { 03323 NOMATCH 03324 return FALSE; // different number of arguments -> no match 03325 } 03326 03327 if (checkCV) 03328 { 03329 if (srcAl->constSpecifier != dstAl->constSpecifier) 03330 { 03331 NOMATCH 03332 return FALSE; // one member is const, the other not -> no match 03333 } 03334 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier) 03335 { 03336 NOMATCH 03337 return FALSE; // one member is volatile, the other not -> no match 03338 } 03339 } 03340 03341 // so far the argument list could match, so we need to compare the types of 03342 // all arguments. 03343 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl); 03344 Argument *srcA,*dstA; 03345 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli) 03346 { 03347 if (!matchArgument2(srcScope,srcFileScope,srcA, 03348 dstScope,dstFileScope,dstA) 03349 ) 03350 { 03351 NOMATCH 03352 return FALSE; 03353 } 03354 } 03355 MATCH 03356 return TRUE; // all arguments match 03357 } 03358 03359 03360 03361 // merges the initializer of two argument lists 03362 // pre: the types of the arguments in the list should match. 03363 void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite) 03364 { 03365 //printf("mergeArguments `%s', `%s'\n", 03366 // argListToString(srcAl).data(),argListToString(dstAl).data()); 03367 03368 if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count()) 03369 { 03370 return; // invalid argument lists -> do not merge 03371 } 03372 03373 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl); 03374 Argument *srcA,*dstA; 03375 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli) 03376 { 03377 if (srcA->defval.isEmpty() && !dstA->defval.isEmpty()) 03378 { 03379 //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data()); 03380 srcA->defval=dstA->defval.copy(); 03381 } 03382 else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty()) 03383 { 03384 //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data()); 03385 dstA->defval=srcA->defval.copy(); 03386 } 03387 03388 // fix wrongly detected const or volatile specificiers before merging. 03389 // example: "const A *const" is detected as type="const A *" name="const" 03390 if (srcA->name=="const" || srcA->name=="volatile") 03391 { 03392 srcA->type+=" "+srcA->name; 03393 srcA->name.resize(0); 03394 } 03395 if (dstA->name=="const" || dstA->name=="volatile") 03396 { 03397 dstA->type+=" "+dstA->name; 03398 dstA->name.resize(0); 03399 } 03400 03401 if (srcA->type==dstA->type) 03402 { 03403 if (srcA->name.isEmpty() && !dstA->name.isEmpty()) 03404 { 03405 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data()); 03406 //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data()); 03407 srcA->type = dstA->type.copy(); 03408 srcA->name = dstA->name.copy(); 03409 } 03410 else if (!srcA->name.isEmpty() && dstA->name.isEmpty()) 03411 { 03412 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data()); 03413 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data()); 03414 dstA->type = srcA->type.copy(); 03415 dstA->name = dstA->name.copy(); 03416 } 03417 else if (!srcA->name.isEmpty() && !dstA->name.isEmpty()) 03418 { 03419 //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data()); 03420 if (forceNameOverwrite) 03421 { 03422 srcA->name = dstA->name; 03423 } 03424 else 03425 { 03426 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty()) 03427 { 03428 srcA->name = dstA->name; 03429 } 03430 else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty()) 03431 { 03432 dstA->name = srcA->name; 03433 } 03434 } 03435 } 03436 } 03437 else 03438 { 03439 //printf("merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data()); 03440 if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla" 03441 { 03442 srcA->type+=" "+srcA->name; 03443 srcA->name=dstA->name; 03444 } 03445 else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int" 03446 { 03447 dstA->type+=" "+dstA->name; 03448 dstA->name=srcA->name; 03449 } 03450 else if (srcA->name.isEmpty() && !dstA->name.isEmpty()) 03451 { 03452 srcA->name = dstA->name; 03453 } 03454 else if (dstA->name.isEmpty() && !srcA->name.isEmpty()) 03455 { 03456 dstA->name = srcA->name; 03457 } 03458 } 03459 int i1=srcA->type.find("::"), 03460 i2=dstA->type.find("::"), 03461 j1=srcA->type.length()-i1-2, 03462 j2=dstA->type.length()-i2-2; 03463 if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type) 03464 { 03465 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data()); 03466 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data()); 03467 dstA->type = srcA->type.left(i1+2)+dstA->type; 03468 dstA->name = dstA->name.copy(); 03469 } 03470 else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type) 03471 { 03472 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data()); 03473 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data()); 03474 srcA->type = dstA->type.left(i2+2)+srcA->type; 03475 srcA->name = dstA->name.copy(); 03476 } 03477 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty()) 03478 { 03479 srcA->docs = dstA->docs.copy(); 03480 } 03481 else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty()) 03482 { 03483 dstA->docs = srcA->docs.copy(); 03484 } 03485 //printf("Merge argument `%s|%s' `%s|%s'\n", 03486 // srcA->type.data(),srcA->name.data(), 03487 // dstA->type.data(),dstA->name.data()); 03488 } 03489 } 03490 03513 bool getDefs(const QCString &scName,const QCString &memberName, 03514 const char *args, 03515 MemberDef *&md, 03516 ClassDef *&cd, FileDef *&fd, NamespaceDef *&nd, GroupDef *&gd, 03517 bool forceEmptyScope, 03518 FileDef *currentFile, 03519 bool checkCV 03520 ) 03521 { 03522 fd=0, md=0, cd=0, nd=0, gd=0; 03523 if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ 03524 03525 QCString scopeName=scName; 03526 //printf("Search for name=%s args=%s in scope=%s\n", 03527 // memberName.data(),args,scopeName.data()); 03528 03529 int is,im=0,pm=0; 03530 // strip common part of the scope from the scopeName 03531 while ((is=scopeName.findRev("::"))!=-1 && 03532 (im=memberName.find("::",pm))!=-1 && 03533 (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm)) 03534 ) 03535 { 03536 scopeName=scopeName.left(is); 03537 pm=im+2; 03538 } 03539 //printf("result after scope corrections scope=%s name=%s\n", 03540 // scopeName.data(),memberName.data()); 03541 03542 QCString mName=memberName; 03543 QCString mScope; 03544 if (memberName.left(9)!="operator " && // treat operator conversion methods 03545 // as a special case 03546 (im=memberName.findRev("::"))!=-1 && 03547 im<(int)memberName.length()-2 // not A:: 03548 ) 03549 { 03550 mScope=memberName.left(im); 03551 mName=memberName.right(memberName.length()-im-2); 03552 } 03553 03554 // handle special the case where both scope name and member scope are equal 03555 if (mScope==scopeName) scopeName.resize(0); 03556 03557 //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data()); 03558 03559 MemberName *mn = Doxygen::memberNameSDict->find(mName); 03560 //printf("mName=%s mn=%p\n",mName.data(),mn); 03561 if (!forceEmptyScope && mn && !(scopeName.isEmpty() && mScope.isEmpty())) 03562 { 03563 //printf(" >member name found\n"); 03564 int scopeOffset=scopeName.length(); 03565 do 03566 { 03567 QCString className = scopeName.left(scopeOffset); 03568 if (!className.isEmpty() && !mScope.isEmpty()) 03569 { 03570 className+="::"+mScope; 03571 } 03572 else if (!mScope.isEmpty()) 03573 { 03574 className=mScope.copy(); 03575 } 03576 //printf("Trying class scope %s\n",className.data()); 03577 03578 ClassDef *fcd=0; 03579 // todo: fill in correct fileScope! 03580 if ((fcd=getResolvedClass(Doxygen::globalScope,0,className)) && // is it a documented class 03581 fcd->isLinkable() 03582 ) 03583 { 03584 //printf(" Found fcd=%p\n",fcd); 03585 MemberListIterator mmli(*mn); 03586 MemberDef *mmd; 03587 int mdist=maxInheritanceDepth; 03588 ArgumentList *argList=0; 03589 if (args) 03590 { 03591 argList=new ArgumentList; 03592 stringToArgumentList(args,argList); 03593 } 03594 for (mmli.toFirst();(mmd=mmli.current());++mmli) 03595 { 03596 //if (mmd->isLinkable()) 03597 //{ 03598 LockingPtr<ArgumentList> mmdAl = mmd->argumentList(); 03599 bool match=args==0 || 03600 matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), 03601 fcd,fcd->getFileDef(),argList, 03602 checkCV 03603 ); 03604 //printf("match=%d\n",match); 03605 if (match) 03606 { 03607 ClassDef *mcd=mmd->getClassDef(); 03608 if (mcd) 03609 { 03610 int m=minClassDistance(fcd,mcd); 03611 if (m<mdist && mcd->isLinkable()) 03612 { 03613 mdist=m; 03614 cd=mcd; 03615 md=mmd; 03616 } 03617 } 03618 } 03619 //} 03620 } 03621 if (argList) 03622 { 03623 delete argList; argList=0; 03624 } 03625 if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0) 03626 // no exact match found, but if args="()" an arbitrary member will do 03627 { 03628 //printf(" >Searching for arbitrary member\n"); 03629 for (mmli.toFirst();(mmd=mmli.current());++mmli) 03630 { 03631 //if (mmd->isLinkable()) 03632 //{ 03633 ClassDef *mcd=mmd->getClassDef(); 03634 //printf(" >Class %s found\n",mcd->name().data()); 03635 if (mcd) 03636 { 03637 int m=minClassDistance(fcd,mcd); 03638 if (m<mdist /* && mcd->isLinkable()*/ ) 03639 { 03640 //printf("Class distance %d\n",m); 03641 mdist=m; 03642 cd=mcd; 03643 md=mmd; 03644 } 03645 } 03646 //} 03647 } 03648 } 03649 //printf(" >Succes=%d\n",mdist<maxInheritanceDepth); 03650 if (mdist<maxInheritanceDepth) 03651 { 03652 if (!md->isLinkable()) 03653 { 03654 md=0; // avoid returning things we cannot link to 03655 cd=0; 03656 return FALSE; // match found, but was not linkable 03657 } 03658 else 03659 { 03660 gd=md->getGroupDef(); 03661 if (gd) cd=0; 03662 return TRUE; /* found match */ 03663 } 03664 } 03665 } 03666 /* go to the parent scope */ 03667 03668 if (scopeOffset==0) 03669 { 03670 scopeOffset=-1; 03671 } 03672 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1) 03673 { 03674 scopeOffset=0; 03675 } 03676 } while (scopeOffset>=0); 03677 03678 // unknown or undocumented scope 03679 } 03680 if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function? 03681 { 03682 MemberListIterator mmli(*mn); 03683 MemberDef *mmd, *fuzzy_mmd = 0; 03684 ArgumentList *argList = 0; 03685 bool hasEmptyArgs = args && strcmp(args, "()") == 0; 03686 03687 if (args) 03688 stringToArgumentList(args, argList = new ArgumentList); 03689 03690 for (mmli.toFirst(); (mmd = mmli.current()); ++mmli) 03691 { 03692 if (!mmd->isLinkable() || !mmd->isRelated() || !mmd->getClassDef()) 03693 continue; 03694 03695 if (!args) break; 03696 03697 QCString className = mmd->getClassDef()->name(); 03698 03699 LockingPtr<ArgumentList> mmdAl = mmd->argumentList(); 03700 if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), 03701 Doxygen::globalScope,mmd->getFileDef(),argList, 03702 checkCV 03703 ) 03704 ) break; 03705 03706 if (!fuzzy_mmd && hasEmptyArgs) 03707 fuzzy_mmd = mmd; 03708 } 03709 03710 if (argList) delete argList, argList = 0; 03711 03712 mmd = mmd ? mmd : fuzzy_mmd; 03713 03714 if (mmd) 03715 { 03716 md = mmd; 03717 cd = mmd->getClassDef(); 03718 return TRUE; 03719 } 03720 } 03721 03722 03723 // maybe an namespace, file or group member ? 03724 //printf("Testing for global function scopeName=`%s' mScope=`%s' :: mName=`%s'\n", 03725 // scopeName.data(),mScope.data(),mName.data()); 03726 if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known 03727 { 03728 //printf(" >function name found\n"); 03729 NamespaceDef *fnd=0; 03730 int scopeOffset=scopeName.length(); 03731 do 03732 { 03733 QCString namespaceName = scopeName.left(scopeOffset); 03734 if (!namespaceName.isEmpty() && !mScope.isEmpty()) 03735 { 03736 namespaceName+="::"+mScope; 03737 } 03738 else if (!mScope.isEmpty()) 03739 { 03740 namespaceName=mScope.copy(); 03741 } 03742 //printf("Trying namespace %s\n",namespaceName.data()); 03743 if (!namespaceName.isEmpty() && 03744 (fnd=Doxygen::namespaceSDict->find(namespaceName)) && 03745 fnd->isLinkable() 03746 ) 03747 { 03748 //printf("Function inside existing namespace `%s'\n",namespaceName.data()); 03749 bool found=FALSE; 03750 MemberListIterator mmli(*mn); 03751 MemberDef *mmd; 03752 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) 03753 { 03754 //printf("mmd->getNamespaceDef()=%p fnd=%p\n", 03755 // mmd->getNamespaceDef(),fnd); 03756 if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ ) 03757 { // namespace is found 03758 bool match=TRUE; 03759 ArgumentList *argList=0; 03760 if (args && strcmp(args,"()")!=0) 03761 { 03762 argList=new ArgumentList; 03763 LockingPtr<ArgumentList> mmdAl = mmd->argumentList(); 03764 stringToArgumentList(args,argList); 03765 match=matchArguments2( 03766 mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(), 03767 fnd,mmd->getFileDef(),argList, 03768 checkCV); 03769 } 03770 if (match) 03771 { 03772 nd=fnd; 03773 md=mmd; 03774 found=TRUE; 03775 } 03776 if (args) 03777 { 03778 delete argList; argList=0; 03779 } 03780 } 03781 } 03782 if (!found && args && !strcmp(args,"()")) 03783 // no exact match found, but if args="()" an arbitrary 03784 // member will do 03785 { 03786 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) 03787 { 03788 if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ ) 03789 { 03790 nd=fnd; 03791 md=mmd; 03792 found=TRUE; 03793 } 03794 } 03795 } 03796 if (found) 03797 { 03798 if (!md->isLinkable()) 03799 { 03800 md=0; // avoid returning things we cannot link to 03801 nd=0; 03802 return FALSE; // match found but not linkable 03803 } 03804 else 03805 { 03806 gd=md->getGroupDef(); 03807 if (gd && gd->isLinkable()) nd=0; else gd=0; 03808 return TRUE; 03809 } 03810 } 03811 } 03812 if (scopeOffset==0) 03813 { 03814 scopeOffset=-1; 03815 } 03816 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1) 03817 { 03818 scopeOffset=0; 03819 } 03820 } while (scopeOffset>=0); 03821 03822 //else // no scope => global function 03823 { 03824 QList<MemberDef> members; 03825 03826 //printf(" Function with global scope name `%s' args=`%s'\n",memberName.data(),args); 03827 MemberListIterator mli(*mn); 03828 for (mli.toFirst();(md=mli.current());++mli) 03829 { 03830 fd=md->getFileDef(); 03831 gd=md->getGroupDef(); 03832 //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p\n", 03833 // md->name().data(),args,fd,gd); 03834 if ( 03835 ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) && 03836 md->getNamespaceDef()==0 03837 ) 03838 { 03839 //printf(" fd=%p gd=%p args=`%s'\n",fd,gd,args); 03840 bool match=TRUE; 03841 ArgumentList *argList=0; 03842 if (args && !md->isDefine() && strcmp(args,"()")!=0) 03843 { 03844 argList=new ArgumentList; 03845 LockingPtr<ArgumentList> mdAl = md->argumentList(); 03846 stringToArgumentList(args,argList); 03847 match=matchArguments2( 03848 md->getOuterScope(),fd,mdAl.pointer(), 03849 Doxygen::globalScope,fd,argList, 03850 checkCV); 03851 delete argList; argList=0; 03852 } 03853 if (match) 03854 { 03855 //printf("Found match!\n"); 03856 members.append(md); 03857 } 03858 } 03859 } 03860 if (members.count()!=1 && args && !strcmp(args,"()")) 03861 { 03862 // no exact match found, but if args="()" an arbitrary 03863 // member will do 03864 md=mn->last(); 03865 while (md /* && md->isLinkable()*/) 03866 { 03867 //printf("Found member `%s'\n",md->name().data()); 03868 //printf("member is linkable md->name()=`%s'\n",md->name().data()); 03869 fd=md->getFileDef(); 03870 gd=md->getGroupDef(); 03871 if ( 03872 (gd && gd->isLinkable()) || (fd && fd->isLinkable()) 03873 ) 03874 { 03875 members.append(md); 03876 } 03877 md=mn->prev(); 03878 } 03879 } 03880 //printf("found %d candidate members\n",members.count()); 03881 if (members.count()==1 || currentFile!=0) 03882 { 03883 md=members.first(); 03884 } 03885 else if (members.count()>1) 03886 { 03887 //printf("Found more than one matching member!\n"); 03888 // use some C scoping rules to determine the correct link 03889 // 1. member in current file 03890 // 2. non-static member in different file 03891 if (currentFile==0) 03892 { 03893 bool ambig; 03894 currentFile = findFileDef(Doxygen::inputNameDict,0/*namespaceName*/,ambig); 03895 } 03896 MemberDef *bmd = 0; 03897 for (md=members.first(); md; md=members.next()) 03898 { 03899 if (currentFile && md->getBodyDef()==currentFile) 03900 { 03901 bmd = 0; 03902 break; 03903 } 03904 if (!md->isStatic() || Config_getBool("EXTRACT_STATIC")) bmd = md; 03905 } 03906 if (bmd) md=bmd; 03907 } 03908 if (md && !md->isLinkable()) md=0; // ignore things we cannot link to 03909 if (md) // found a matching global member 03910 { 03911 fd=md->getFileDef(); 03912 gd=md->getGroupDef(); 03913 //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable()); 03914 if (gd && gd->isLinkable()) fd=0; else gd=0; 03915 return TRUE; 03916 } 03917 } 03918 } 03919 03920 // no nothing found 03921 return FALSE; 03922 } 03923 03938 static bool getScopeDefs(const char *docScope,const char *scope, 03939 ClassDef *&cd, NamespaceDef *&nd) 03940 { 03941 cd=0;nd=0; 03942 03943 QCString scopeName=scope; 03944 //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope); 03945 if (scopeName.isEmpty()) return FALSE; 03946 03947 bool explicitGlobalScope=FALSE; 03948 if (scopeName.at(0)==':' && scopeName.at(1)==':') 03949 { 03950 scopeName=scopeName.right(scopeName.length()-2); 03951 explicitGlobalScope=TRUE; 03952 } 03953 03954 QCString docScopeName=docScope; 03955 int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length(); 03956 03957 do // for each possible docScope (from largest to and including empty) 03958 { 03959 QCString fullName=scopeName.copy(); 03960 if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::"); 03961 03962 if ((cd=getClass(fullName)) && cd->isLinkable()) 03963 { 03964 return TRUE; // class link written => quit 03965 } 03966 else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable()) 03967 { 03968 return TRUE; // namespace link written => quit 03969 } 03970 if (scopeOffset==0) 03971 { 03972 scopeOffset=-1; 03973 } 03974 else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1) 03975 { 03976 scopeOffset=0; 03977 } 03978 } while (scopeOffset>=0); 03979 03980 return FALSE; 03981 } 03982 03983 //static bool isLowerCase(QCString &s) 03984 //{ 03985 // char *p=s.data(); 03986 // if (p==0) return TRUE; 03987 // int c; 03988 // while ((c=*p++)) if (!islower(c)) return FALSE; 03989 // return TRUE; 03990 //} 03991 03992 03993 03997 bool resolveRef(/* in */ const char *scName, 03998 /* in */ const char *name, 03999 /* in */ bool inSeeBlock, 04000 /* out */ Definition **resContext, 04001 /* out */ MemberDef **resMember 04002 ) 04003 { 04004 QCString tsName = name; 04005 bool memberScopeFirst = tsName.find('#')!=-1; 04006 QCString fullName = substitute(tsName,"#","::"); 04007 fullName = removeRedundantWhiteSpace(substitute(fullName,".","::")); 04008 04009 int bracePos=fullName.findRev('('); // reverse is needed for operator()(...) 04010 int endNamePos=bracePos!=-1 ? bracePos : fullName.length(); 04011 int scopePos=fullName.findRev("::",endNamePos); 04012 04013 // default result values 04014 *resContext=0; 04015 *resMember=0; 04016 04017 if (bracePos==-1) // simple name 04018 { 04019 ClassDef *cd=0; 04020 NamespaceDef *nd=0; 04021 04022 //if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName)) 04023 //{ // link to lower case only name => do not try to autolink 04024 // return FALSE; 04025 //} 04026 04027 //printf("scName=%s fullName=%s\n",scName,fullName.data()); 04028 04029 // check if this is a class or namespace reference 04030 if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd)) 04031 { 04032 if (cd) // scope matches that of a class 04033 { 04034 *resContext = cd; 04035 } 04036 else // scope matches that of a namespace 04037 { 04038 ASSERT(nd!=0); 04039 *resContext = nd; 04040 } 04041 return TRUE; 04042 } 04043 else if (scName==fullName || (!inSeeBlock && scopePos==-1)) 04044 // nothing to link => output plain text 04045 { 04046 //printf("found scName=%s fullName=%s scName==fullName=%d " 04047 // "inSeeBlock=%d scopePos=%d!\n", 04048 // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos); 04049 return FALSE; 04050 } 04051 // continue search... 04052 } 04053 04054 // extract userscope+name 04055 QCString nameStr=fullName.left(endNamePos); 04056 04057 // extract arguments 04058 QCString argsStr; 04059 if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos); 04060 04061 // strip template specifier 04062 // TODO: match against the correct partial template instantiation 04063 int templPos=nameStr.find('<'); 04064 if (templPos!=-1 && nameStr.find("operator")==-1) 04065 { 04066 int endTemplPos=nameStr.findRev('>'); 04067 nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1); 04068 } 04069 04070 QCString scopeStr=scName; 04071 04072 MemberDef *md = 0; 04073 ClassDef *cd = 0; 04074 FileDef *fd = 0; 04075 NamespaceDef *nd = 0; 04076 GroupDef *gd = 0; 04077 04078 // check if nameStr is a member or global. 04079 //printf("getDefs(scope=%s,name=%s,args=%s)\n",scopeStr.data(),nameStr.data(),argsStr.data()); 04080 if (getDefs(scopeStr,nameStr,argsStr, 04081 md,cd,fd,nd,gd, 04082 scopePos==0 && !memberScopeFirst, 04083 0, 04084 TRUE 04085 ) 04086 ) 04087 { 04088 //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd); 04089 if (md) { *resMember=md; *resContext=md; } 04090 else if (cd) *resContext=cd; 04091 else if (nd) *resContext=nd; 04092 else if (fd) *resContext=fd; 04093 else if (gd) *resContext=gd; 04094 else { *resContext=0; *resMember=0; return FALSE; } 04095 //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n", 04096 // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data()); 04097 return TRUE; 04098 } 04099 else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr))) 04100 { // group link 04101 *resContext=gd; 04102 return TRUE; 04103 } 04104 else if (tsName.find('.')!=-1) // maybe a link to a file 04105 { 04106 bool ambig; 04107 fd=findFileDef(Doxygen::inputNameDict,tsName,ambig); 04108 if (fd && !ambig) 04109 { 04110 *resContext=fd; 04111 return TRUE; 04112 } 04113 } 04114 04115 return FALSE; 04116 } 04117 04118 QCString linkToText(const char *link,bool isFileName) 04119 { 04120 static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA"); 04121 QCString result=link; 04122 if (!result.isEmpty()) 04123 { 04124 // replace # by :: 04125 result=substitute(result,"#","::"); 04126 // replace . by :: 04127 if (!isFileName) result=substitute(result,".","::"); 04128 // strip leading :: prefix if present 04129 if (result.at(0)==':' && result.at(1)==':') 04130 { 04131 result=result.right(result.length()-2); 04132 } 04133 if (optimizeOutputJava) 04134 { 04135 result=substitute(result,"::","."); 04136 } 04137 } 04138 return result; 04139 } 04140 04160 bool generateRef(OutputDocInterface &od,const char *scName, 04161 const char *name,bool inSeeBlock,const char *rt) 04162 { 04163 //printf("generateRef(scName=%s,name=%s,rt=%s)\n",scName,name,rt); 04164 04165 Definition *compound; 04166 MemberDef *md; 04167 04168 // create default link text 04169 QCString linkText = linkToText(rt,FALSE); 04170 04171 if (resolveRef(scName,name,inSeeBlock,&compound,&md)) 04172 { 04173 if (md && md->isLinkable()) // link to member 04174 { 04175 od.writeObjectLink(md->getReference(), 04176 md->getOutputFileBase(), 04177 md->anchor(),linkText); 04178 // generate the page reference (for LaTeX) 04179 if (!md->isReference()) 04180 { 04181 writePageRef(od,md->getOutputFileBase(),md->anchor()); 04182 } 04183 return TRUE; 04184 } 04185 else if (compound && compound->isLinkable()) // link to compound 04186 { 04187 if (rt==0 && compound->definitionType()==Definition::TypeGroup) 04188 { 04189 linkText=((GroupDef *)compound)->groupTitle(); 04190 } 04191 if (compound && compound->definitionType()==Definition::TypeFile) 04192 { 04193 linkText=linkToText(rt,TRUE); 04194 } 04195 od.writeObjectLink(compound->getReference(), 04196 compound->getOutputFileBase(), 04197 0,linkText); 04198 if (!compound->isReference()) 04199 { 04200 writePageRef(od,compound->getOutputFileBase(),0); 04201 } 04202 return TRUE; 04203 } 04204 } 04205 od.docify(linkText); 04206 return FALSE; 04207 } 04208 04209 bool resolveLink(/* in */ const char *scName, 04210 /* in */ const char *lr, 04211 /* in */ bool inSeeBlock, 04212 /* out */ Definition **resContext, 04213 /* out */ QCString &resAnchor 04214 ) 04215 { 04216 *resContext=0; 04217 04218 QCString linkRef=lr; 04219 //printf("ResolveLink linkRef=%s\n",lr); 04220 FileDef *fd; 04221 GroupDef *gd; 04222 PageDef *pd; 04223 ClassDef *cd; 04224 DirDef *dir; 04225 NamespaceDef *nd; 04226 bool ambig; 04227 if (linkRef.isEmpty()) // no reference name! 04228 { 04229 return FALSE; 04230 } 04231 else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page 04232 { 04233 GroupDef *gd = pd->getGroupDef(); 04234 if (gd) 04235 { 04236 SectionInfo *si=0; 04237 if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()]; 04238 *resContext=gd; 04239 if (si) resAnchor = si->label; 04240 } 04241 else 04242 { 04243 *resContext=pd; 04244 } 04245 return TRUE; 04246 } 04247 else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example 04248 { 04249 *resContext=pd; 04250 return TRUE; 04251 } 04252 else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group 04253 { 04254 *resContext=gd; 04255 return TRUE; 04256 } 04257 else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link 04258 && fd->isLinkable()) 04259 { 04260 *resContext=fd; 04261 return TRUE; 04262 } 04263 else if ((cd=getClass(linkRef))) // class link 04264 { 04265 *resContext=cd; 04266 return TRUE; 04267 } 04268 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link 04269 { 04270 *resContext=cd; 04271 return TRUE; 04272 } 04273 else if ((nd=Doxygen::namespaceSDict->find(linkRef))) 04274 { 04275 *resContext=nd; 04276 return TRUE; 04277 } 04278 else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath()+"/")) 04279 && dir->isLinkable()) // TODO: make this location independent like filedefs 04280 { 04281 *resContext=dir; 04282 return TRUE; 04283 } 04284 else // probably a member reference 04285 { 04286 MemberDef *md; 04287 bool res = resolveRef(scName,lr,inSeeBlock,resContext,&md); 04288 if (md) resAnchor=md->anchor(); 04289 return res; 04290 } 04291 } 04292 04293 04294 //---------------------------------------------------------------------- 04295 // General function that generates the HTML code for a reference to some 04296 // file, class or member from text `lr' within the context of class `clName'. 04297 // This link has the text 'lt' (if not 0), otherwise `lr' is used as a 04298 // basis for the link's text. 04299 // returns TRUE if a link could be generated. 04300 04301 bool generateLink(OutputDocInterface &od,const char *clName, 04302 const char *lr,bool inSeeBlock,const char *lt) 04303 { 04304 //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt); 04305 Definition *compound; 04306 //PageDef *pageDef=0; 04307 QCString anchor,linkText=linkToText(lt,FALSE); 04308 //printf("generateLink linkText=%s\n",linkText.data()); 04309 if (resolveLink(clName,lr,inSeeBlock,&compound,anchor)) 04310 { 04311 if (compound) // link to compound 04312 { 04313 if (lt==0 && anchor.isEmpty() && /* compound link */ 04314 compound->definitionType()==Definition::TypeGroup /* is group */ 04315 ) 04316 { 04317 linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link 04318 } 04319 else if (compound->definitionType()==Definition::TypeFile) 04320 { 04321 linkText=linkToText(lt,TRUE); 04322 } 04323 od.writeObjectLink(compound->getReference(), 04324 compound->getOutputFileBase(),anchor,linkText); 04325 if (!compound->isReference()) 04326 { 04327 writePageRef(od,compound->getOutputFileBase(),anchor); 04328 } 04329 } 04330 else 04331 { 04332 err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__); 04333 } 04334 return TRUE; 04335 } 04336 else // link could not be found 04337 { 04338 od.docify(linkText); 04339 return FALSE; 04340 } 04341 } 04342 04343 void generateFileRef(OutputDocInterface &od,const char *name,const char *text) 04344 { 04345 //printf("generateFileRef(%s,%s)\n",name,text); 04346 QCString linkText = text ? text : name; 04347 //FileInfo *fi; 04348 FileDef *fd; 04349 bool ambig; 04350 if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) && 04351 fd->isLinkable()) 04352 // link to documented input file 04353 od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText); 04354 else 04355 od.docify(linkText); 04356 } 04357 04358 //---------------------------------------------------------------------- 04359 04360 #if 0 04361 QCString substituteClassNames(const QCString &s) 04362 { 04363 int i=0,l,p; 04364 QCString result; 04365 if (s.isEmpty()) return result; 04366 QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*"); 04367 while ((p=r.match(s,i,&l))!=-1) 04368 { 04369 QCString *subst; 04370 if (p>i) result+=s.mid(i,p-i); 04371 if ((subst=substituteDict[s.mid(p,l)])) 04372 { 04373 result+=*subst; 04374 } 04375 else 04376 { 04377 result+=s.mid(p,l); 04378 } 04379 i=p+l; 04380 } 04381 result+=s.mid(i,s.length()-i); 04382 return result; 04383 } 04384 #endif 04385 04386 //---------------------------------------------------------------------- 04387 // substitute all occurences of `src' in `s' by `dst' 04388 04389 QCString substitute(const char *s,const char *src,const char *dst) 04390 { 04391 // TODO: optimize by using strstr() instead of find 04392 QCString input=s; 04393 QCString output; 04394 int i=0,p; 04395 while ((p=input.find(src,i))!=-1) 04396 { 04397 output+=input.mid(i,p-i); 04398 output+=dst; 04399 i=p+strlen(src); 04400 } 04401 output+=input.mid(i,input.length()-i); 04402 return output; 04403 } 04404 04405 //---------------------------------------------------------------------- 04406 04407 struct FindFileCacheElem 04408 { 04409 FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {} 04410 FileDef *fileDef; 04411 bool isAmbig; 04412 }; 04413 04414 static QCache<FindFileCacheElem> g_findFileDefCache(5000); 04415 04416 FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig) 04417 { 04418 ambig=FALSE; 04419 if (n==0) return 0; 04420 04421 QCString key; 04422 key.sprintf("%p:",fnDict); 04423 key+=n; 04424 04425 g_findFileDefCache.setAutoDelete(TRUE); 04426 FindFileCacheElem *cachedResult = g_findFileDefCache.find(key); 04427 if (cachedResult) 04428 { 04429 ambig = cachedResult->isAmbig; 04430 return cachedResult->fileDef; 04431 } 04432 else 04433 { 04434 cachedResult = new FindFileCacheElem(0,FALSE); 04435 } 04436 04437 QCString name=convertToQCString(QDir::cleanDirPath(n)); 04438 QCString path; 04439 int slashPos; 04440 FileName *fn; 04441 if (name.isEmpty()) goto exit; 04442 slashPos=QMAX(name.findRev('/'),name.findRev('\\')); 04443 if (slashPos!=-1) 04444 { 04445 path=name.left(slashPos+1); 04446 name=name.right(name.length()-slashPos-1); 04447 } 04448 //printf("findFileDef path=`%s' name=`%s'\n",path.data(),name.data()); 04449 if (name.isEmpty()) goto exit; 04450 if ((fn=(*fnDict)[name])) 04451 { 04452 if (fn->count()==1) 04453 { 04454 FileDef *fd = fn->getFirst(); 04455 if (path.isEmpty() || fd->getPath().right(path.length())==path) 04456 { 04457 cachedResult->fileDef = fd; 04458 g_findFileDefCache.insert(key,cachedResult); 04459 return fd; 04460 } 04461 } 04462 else // file name alone is ambigious 04463 { 04464 int count=0; 04465 FileNameIterator fni(*fn); 04466 FileDef *fd; 04467 FileDef *lastMatch=0; 04468 QCString pathStripped = stripFromIncludePath(path); 04469 for (fni.toFirst();(fd=fni.current());++fni) 04470 { 04471 QCString fdStripPath = stripFromIncludePath(fd->getPath()); 04472 if (path.isEmpty() || fdStripPath==pathStripped) 04473 { 04474 count++; 04475 lastMatch=fd; 04476 } 04477 } 04478 ambig=(count>1); 04479 cachedResult->isAmbig = ambig; 04480 cachedResult->fileDef = lastMatch; 04481 g_findFileDefCache.insert(key,cachedResult); 04482 return lastMatch; 04483 } 04484 } 04485 exit: 04486 g_findFileDefCache.insert(key,cachedResult); 04487 return 0; 04488 } 04489 04490 //---------------------------------------------------------------------- 04491 04492 QCString showFileDefMatches(const FileNameDict *fnDict,const char *n) 04493 { 04494 QCString result; 04495 QCString name=n; 04496 QCString path; 04497 int slashPos=QMAX(name.findRev('/'),name.findRev('\\')); 04498 if (slashPos!=-1) 04499 { 04500 path=name.left(slashPos+1); 04501 name=name.right(name.length()-slashPos-1); 04502 } 04503 FileName *fn; 04504 if ((fn=(*fnDict)[name])) 04505 { 04506 FileNameIterator fni(*fn); 04507 FileDef *fd; 04508 for (fni.toFirst();(fd=fni.current());++fni) 04509 { 04510 if (path.isEmpty() || fd->getPath().right(path.length())==path) 04511 { 04512 result+=" "+fd->absFilePath()+"\n"; 04513 } 04514 } 04515 } 04516 return result; 04517 } 04518 04519 //---------------------------------------------------------------------- 04520 04521 QCString substituteKeywords(const QCString &s,const char *title,const QCString &relPath) 04522 { 04523 QCString result = s.copy(); 04524 if (title) result = substitute(result,"$title",title); 04525 result = substitute(result,"$datetime",dateToString(TRUE)); 04526 result = substitute(result,"$date",dateToString(FALSE)); 04527 result = substitute(result,"$year",yearToString()); 04528 result = substitute(result,"$doxygenversion",versionString); 04529 result = substitute(result,"$projectname",Config_getString("PROJECT_NAME")); 04530 result = substitute(result,"$projectnumber",Config_getString("PROJECT_NUMBER")); 04531 result = substitute(result,"$relpath$",relPath); 04532 return result; 04533 } 04534 04535 //---------------------------------------------------------------------- 04536 04541 int getPrefixIndex(const QCString &name) 04542 { 04543 if (name.isEmpty()) return 0; 04544 QStrList &sl = Config_getList("IGNORE_PREFIX"); 04545 char *s = sl.first(); 04546 while (s) 04547 { 04548 const char *ps=s; 04549 const char *pd=name.data(); 04550 int i=0; 04551 while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++; 04552 if (*ps==0 && *pd!=0) 04553 { 04554 return i; 04555 } 04556 s = sl.next(); 04557 } 04558 return 0; 04559 } 04560 04561 //---------------------------------------------------------------------------- 04562 04563 static void initBaseClassHierarchy(BaseClassList *bcl) 04564 { 04565 if (bcl==0) return; 04566 BaseClassListIterator bcli(*bcl); 04567 for ( ; bcli.current(); ++bcli) 04568 { 04569 ClassDef *cd=bcli.current()->classDef; 04570 if (cd->baseClasses()==0) // no base classes => new root 04571 { 04572 initBaseClassHierarchy(cd->baseClasses()); 04573 } 04574 cd->visited=FALSE; 04575 } 04576 } 04577 04578 //---------------------------------------------------------------------------- 04579 04580 void initClassHierarchy(ClassSDict *cl) 04581 { 04582 ClassSDict::Iterator cli(*cl); 04583 ClassDef *cd; 04584 for ( ; (cd=cli.current()); ++cli) 04585 { 04586 cd->visited=FALSE; 04587 initBaseClassHierarchy(cd->baseClasses()); 04588 } 04589 } 04590 04591 //---------------------------------------------------------------------------- 04592 04593 bool hasVisibleRoot(BaseClassList *bcl) 04594 { 04595 if (bcl) 04596 { 04597 BaseClassListIterator bcli(*bcl); 04598 for ( ; bcli.current(); ++bcli) 04599 { 04600 ClassDef *cd=bcli.current()->classDef; 04601 if (cd->isVisibleInHierarchy()) return TRUE; 04602 hasVisibleRoot(cd->baseClasses()); 04603 } 04604 } 04605 return FALSE; 04606 } 04607 04608 //---------------------------------------------------------------------- 04609 04610 QCString escapeCharsInString(const char *name,bool allowDots) 04611 { 04612 static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES"); 04613 QCString result; 04614 char c; 04615 const char *p=name; 04616 while ((c=*p++)!=0) 04617 { 04618 switch(c) 04619 { 04620 case '_': result+="__"; break; 04621 case '-': result+="-"; break; 04622 case ':': result+="_1"; break; 04623 case '/': result+="_2"; break; 04624 case '<': result+="_3"; break; 04625 case '>': result+="_4"; break; 04626 case '*': result+="_5"; break; 04627 case '&': result+="_6"; break; 04628 case '|': result+="_7"; break; 04629 case '.': if (allowDots) result+="."; else result+="_8"; break; 04630 case '!': result+="_9"; break; 04631 case ',': result+="_00"; break; 04632 case ' ': result+="_01"; break; 04633 case '{': result+="_02"; break; 04634 case '}': result+="_03"; break; 04635 case '?': result+="_04"; break; 04636 case '^': result+="_05"; break; 04637 case '%': result+="_06"; break; 04638 case '(': result+="_07"; break; 04639 case ')': result+="_08"; break; 04640 case '+': result+="_09"; break; 04641 case '=': result+="_0A"; break; 04642 default: 04643 if (caseSenseNames || !isupper(c)) 04644 { 04645 result+=c; 04646 } 04647 else 04648 { 04649 result+="_"; 04650 result+=tolower(c); 04651 } 04652 break; 04653 } 04654 } 04655 return result; 04656 } 04657 04662 QCString convertNameToFile(const char *name,bool allowDots) 04663 { 04664 static bool shortNames = Config_getBool("SHORT_NAMES"); 04665 static bool createSubdirs = Config_getBool("CREATE_SUBDIRS"); 04666 QCString result; 04667 if (shortNames) // use short names only 04668 { 04669 static QDict<int> usedNames(10007); 04670 usedNames.setAutoDelete(TRUE); 04671 static int count=1; 04672 04673 int *value=usedNames.find(name); 04674 int num; 04675 if (value==0) 04676 { 04677 usedNames.insert(name,new int(count)); 04678 num = count++; 04679 } 04680 else 04681 { 04682 num = *value; 04683 } 04684 result.sprintf("a%05d",num); 04685 } 04686 else // long names 04687 { 04688 result=escapeCharsInString(name,allowDots); 04689 int resultLen = result.length(); 04690 if (resultLen>=128) // prevent names that cannot be created! 04691 { 04692 // third algorithm based on MD5 hash 04693 uchar md5_sig[16]; 04694 QCString sigStr(33); 04695 MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig); 04696 MD5SigToString(md5_sig,sigStr.data(),33); 04697 result=result.left(128-32)+sigStr; 04698 } 04699 } 04700 if (createSubdirs) 04701 { 04702 int l1Dir=0,l2Dir=0; 04703 04704 #if MAP_ALGO==ALGO_COUNT 04705 // old algorithm, has the problem that after regeneration the 04706 // output can be located in a different dir. 04707 if (Doxygen::htmlDirMap==0) 04708 { 04709 Doxygen::htmlDirMap=new QDict<int>(100003); 04710 Doxygen::htmlDirMap->setAutoDelete(TRUE); 04711 } 04712 static int curDirNum=0; 04713 int *dirNum = Doxygen::htmlDirMap->find(result); 04714 if (dirNum==0) // new name 04715 { 04716 Doxygen::htmlDirMap->insert(result,new int(curDirNum)); 04717 l1Dir = (curDirNum)&0xf; // bits 0-3 04718 l2Dir = (curDirNum>>4)&0xff; // bits 4-11 04719 curDirNum++; 04720 } 04721 else // existing name 04722 { 04723 l1Dir = (*dirNum)&0xf; // bits 0-3 04724 l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11 04725 } 04726 #elif MAP_ALGO==ALGO_CRC16 04727 // second algorithm based on CRC-16 checksum 04728 int dirNum = qChecksum(result,result.length()); 04729 l1Dir = dirNum&0xf; 04730 l2Dir = (dirNum>>4)&0xff; 04731 #elif MAP_ALGO==ALGO_MD5 04732 // third algorithm based on MD5 hash 04733 uchar md5_sig[16]; 04734 MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig); 04735 l1Dir = md5_sig[14]&0xf; 04736 l2Dir = md5_sig[15]; 04737 #endif 04738 result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir)); 04739 } 04740 //printf("*** convertNameToFile(%s)->%s\n",name,result.data()); 04741 return result; 04742 } 04743 04744 QCString relativePathToRoot(const char *name) 04745 { 04746 QCString result; 04747 if (Config_getBool("CREATE_SUBDIRS")) 04748 { 04749 if (name==0) 04750 { 04751 return REL_PATH_TO_ROOT; 04752 } 04753 else 04754 { 04755 QCString n = name; 04756 int i = n.findRev('/'); 04757 if (i!=-1) 04758 { 04759 result=REL_PATH_TO_ROOT; 04760 } 04761 } 04762 } 04763 return result; 04764 } 04765 04766 void createSubDirs(QDir &d) 04767 { 04768 if (Config_getBool("CREATE_SUBDIRS")) 04769 { 04770 // create 4096 subdirectories 04771 int l1,l2; 04772 for (l1=0;l1<16;l1++) 04773 { 04774 d.mkdir(QString().sprintf("d%x",l1)); 04775 for (l2=0;l2<256;l2++) 04776 { 04777 d.mkdir(QString().sprintf("d%x/d%02x",l1,l2)); 04778 } 04779 } 04780 } 04781 } 04782 04786 void extractNamespaceName(const QCString &scopeName, 04787 QCString &className,QCString &namespaceName, 04788 bool allowEmptyClass) 04789 { 04790 int i,p; 04791 QCString clName=scopeName; 04792 NamespaceDef *nd = 0; 04793 if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0) 04794 { // the whole name is a namespace (and not a class) 04795 namespaceName=nd->name().copy(); 04796 className.resize(0); 04797 goto done; 04798 } 04799 p=clName.length()-2; 04800 while (p>=0 && (i=clName.findRev("::",p))!=-1) 04801 // see if the first part is a namespace (and not a class) 04802 { 04803 //printf("Trying %s\n",clName.left(i).data()); 04804 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0) 04805 { 04806 //printf("found!\n"); 04807 namespaceName=nd->name().copy(); 04808 className=clName.right(clName.length()-i-2); 04809 goto done; 04810 } 04811 p=i-2; // try a smaller piece of the scope 04812 } 04813 //printf("not found!\n"); 04814 04815 // not found, so we just have to guess. 04816 className=scopeName.copy(); 04817 namespaceName.resize(0); 04818 04819 done: 04820 if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass) 04821 { 04822 // class and namespace with the same name, correct to return the class. 04823 className=namespaceName.copy(); 04824 namespaceName.resize(0); 04825 } 04826 //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(), 04827 // className.data(),namespaceName.data()); 04828 return; 04829 } 04830 04831 QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ) 04832 { 04833 QCString result=scope.copy(); 04834 if (!templ.isEmpty() && scope.find('<')==-1) 04835 { 04836 int si,pi=0; 04837 ClassDef *cd=0; 04838 while ( 04839 (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) && 04840 ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0) 04841 ) 04842 { 04843 //printf("Tried `%s'\n",(scope.left(si)+templ).data()); 04844 pi=si+2; 04845 } 04846 if (si==-1) // not nested => append template specifier 04847 { 04848 result+=templ; 04849 } 04850 else // nested => insert template specifier before after first class name 04851 { 04852 result=scope.left(si) + templ + scope.right(scope.length()-si); 04853 } 04854 } 04855 //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n", 04856 // scope.data(),templ.data(),result.data()); 04857 return result; 04858 } 04859 04860 #if 0 // original version 04861 04864 QCString stripScope(const char *name) 04865 { 04866 QCString result = name; 04867 int l=result.length(); 04868 int p=l-1; 04869 bool done; 04870 int count; 04871 04872 while (p>=0) 04873 { 04874 char c=result.at(p); 04875 switch (c) 04876 { 04877 case ':': 04878 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data()); 04879 return result.right(l-p-1); 04880 case '>': 04881 count=1; 04882 done=FALSE; 04883 //printf("pos < = %d\n",p); 04884 p--; 04885 while (p>=0 && !done) 04886 { 04887 c=result.at(p--); 04888 switch (c) 04889 { 04890 case '>': count++; break; 04891 case '<': count--; if (count<=0) done=TRUE; break; 04892 default: 04893 //printf("c=%c count=%d\n",c,count); 04894 break; 04895 } 04896 } 04897 //printf("pos > = %d\n",p+1); 04898 break; 04899 default: 04900 p--; 04901 } 04902 } 04903 //printf("stripScope(%s)=%s\n",name,name); 04904 return name; 04905 } 04906 #endif 04907 04908 // new version by Davide Cesari which also works for Fortran 04909 QCString stripScope(const char *name) 04910 { 04911 QCString result = name; 04912 int l=result.length(); 04913 int p; 04914 bool done = FALSE; 04915 bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether 04916 int count=0; 04917 04918 do 04919 { 04920 p=l-1; // start at the end of the string 04921 while (p>=0 && count>=0) 04922 { 04923 char c=result.at(p); 04924 switch (c) 04925 { 04926 case ':': 04927 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data()); 04928 return result.right(l-p-1); 04929 case '>': 04930 if (skipBracket) // we don't care about brackets 04931 { 04932 p--; 04933 } 04934 else // count open/close brackets 04935 { 04936 if (p>0 && result.at(p-1)=='>') // skip >> operator 04937 { 04938 p-=2; 04939 break; 04940 } 04941 count=1; 04942 //printf("pos < = %d\n",p); 04943 p--; 04944 while (p>=0) 04945 { 04946 c=result.at(p--); 04947 switch (c) 04948 { 04949 case '>': 04950 count++; 04951 break; 04952 case '<': 04953 if (p>0) 04954 { 04955 if (result.at(p-1) == '<') // skip << operator 04956 { 04957 p--; 04958 break; 04959 } 04960 } 04961 count--; 04962 break; 04963 default: 04964 //printf("c=%c count=%d\n",c,count); 04965 break; 04966 } 04967 } 04968 } 04969 //printf("pos > = %d\n",p+1); 04970 break; 04971 default: 04972 p--; 04973 } 04974 } 04975 done = count==0 || skipBracket; // reparse if brackets do not match 04976 skipBracket=TRUE; 04977 } 04978 while (!done); // if < > unbalanced repeat ignoring them 04979 //printf("stripScope(%s)=%s\n",name,name); 04980 return name; 04981 } 04982 04983 04985 QCString convertToXML(const char *s) 04986 { 04987 QCString result; 04988 if (s==0) return result; 04989 const char *p=s; 04990 char c; 04991 while ((c=*p++)) 04992 { 04993 switch (c) 04994 { 04995 case '<': result+="<"; break; 04996 case '>': result+=">"; break; 04997 case '&': result+="&"; break; 04998 case '\'': result+="'"; break; 04999 case '"': result+="""; break; 05000 default: result+=c; break; 05001 } 05002 } 05003 return result; 05004 } 05005 05007 QCString convertToHtml(const char *s,bool keepEntities) 05008 { 05009 QCString result; 05010 if (s==0) return result; 05011 const char *p=s; 05012 char c; 05013 while ((c=*p++)) 05014 { 05015 switch (c) 05016 { 05017 case '<': result+="<"; break; 05018 case '>': result+=">"; break; 05019 case '&': if (keepEntities) 05020 { 05021 const char *e=p; 05022 char ce; 05023 while ((ce=*e++)) 05024 { 05025 if (ce==';' || (!(isId(ce) || ce=='#'))) break; 05026 } 05027 if (ce==';') // found end of an entity 05028 { 05029 // copy entry verbatim 05030 result+=c; 05031 while (p<e) result+=*p++; 05032 } 05033 else 05034 { 05035 result+="&"; 05036 } 05037 } 05038 else 05039 { 05040 result+="&"; 05041 } 05042 break; 05043 case '\'': result+="'"; break; 05044 case '"': result+="""; break; 05045 default: result+=c; break; 05046 } 05047 } 05048 return result; 05049 } 05050 05051 QCString convertCharEntitiesToUTF8(const QCString &s) 05052 { 05053 static QDict<char> entityMap(67); 05054 static bool init=TRUE; 05055 QCString result; 05056 static QRegExp entityPat("&[a-zA-Z]+;"); 05057 05058 if (init) 05059 { 05060 entityMap.insert("copy", "\xC2\xA9"); 05061 entityMap.insert("tm", "\xE2\x84\xA2"); 05062 entityMap.insert("trade", "\xE2\x84\xA2"); 05063 entityMap.insert("reg", "\xC2\xAE"); 05064 entityMap.insert("lsquo", "\xE2\x80\x98"); 05065 entityMap.insert("rsquo", "\xE2\x80\x99"); 05066 entityMap.insert("ldquo", "\xE2\x80\x9C"); 05067 entityMap.insert("rdquo", "\xE2\x80\x9D"); 05068 entityMap.insert("ndash", "\xE2\x80\x93"); 05069 entityMap.insert("mdash", "\xE2\x80\x94"); 05070 entityMap.insert("Auml", "\xC3\x84"); 05071 entityMap.insert("Euml", "\xC3\x8B"); 05072 entityMap.insert("Iuml", "\xC3\x8F"); 05073 entityMap.insert("Ouml", "\xC3\x96"); 05074 entityMap.insert("Uuml", "\xC3\x9C"); 05075 entityMap.insert("Yuml", "\xC5\xB8"); 05076 entityMap.insert("auml", "\xC3\xA4"); 05077 entityMap.insert("euml", "\xC3\xAB"); 05078 entityMap.insert("iuml", "\xC3\xAF"); 05079 entityMap.insert("ouml", "\xC3\xB6"); 05080 entityMap.insert("uuml", "\xC3\xBC"); 05081 entityMap.insert("yuml", "\xC3\xBF"); 05082 entityMap.insert("Aacute","\xC3\x81"); 05083 entityMap.insert("Eacute","\xC3\x89"); 05084 entityMap.insert("Iacute","\xC3\x8D"); 05085 entityMap.insert("Oacute","\xC3\x93"); 05086 entityMap.insert("Uacute","\xC3\x9A"); 05087 entityMap.insert("aacute","\xC3\xA1"); 05088 entityMap.insert("eacute","\xC3\xA9"); 05089 entityMap.insert("iacute","\xC3\xAD"); 05090 entityMap.insert("oacute","\xC3\xB3"); 05091 entityMap.insert("uacute","\xC3\xBA"); 05092 entityMap.insert("Agrave","\xC3\x80"); 05093 entityMap.insert("Egrave","\xC3\x88"); 05094 entityMap.insert("Igrave","\xC3\x8C"); 05095 entityMap.insert("Ograve","\xC3\x92"); 05096 entityMap.insert("Ugrave","\xC3\x99"); 05097 entityMap.insert("agrave","\xC3\xA0"); 05098 entityMap.insert("egrave","\xC3\xA8"); 05099 entityMap.insert("igrave","\xC3\xAC"); 05100 entityMap.insert("ograve","\xC3\xB2"); 05101 entityMap.insert("ugrave","\xC3\xB9"); 05102 entityMap.insert("Acirc", "\xC3\x82"); 05103 entityMap.insert("Ecirc", "\xC3\x8A"); 05104 entityMap.insert("Icirc", "\xC3\x8E"); 05105 entityMap.insert("Ocirc", "\xC3\x94"); 05106 entityMap.insert("Ucirc", "\xC3\x9B"); 05107 entityMap.insert("acirc", "\xC3\xA2"); 05108 entityMap.insert("ecirc", "\xC3\xAA"); 05109 entityMap.insert("icirc", "\xC3\xAE"); 05110 entityMap.insert("ocirc", "\xC3\xB4"); 05111 entityMap.insert("ucirc", "\xC3\xBB"); 05112 entityMap.insert("Atilde","\xC3\x83"); 05113 entityMap.insert("Ntilde","\xC3\x91"); 05114 entityMap.insert("Otilde","\xC3\x95"); 05115 entityMap.insert("atilde","\xC3\xA3"); 05116 entityMap.insert("ntilde","\xC3\xB1"); 05117 entityMap.insert("otilde","\xC3\xB5"); 05118 entityMap.insert("szlig", "\xC3\x9F"); 05119 entityMap.insert("Ccedil","\xC3\x87"); 05120 entityMap.insert("ccedil","\xC3\xA7"); 05121 entityMap.insert("Aring", "\xC3\x85"); 05122 entityMap.insert("aring", "\xC3\xA5"); 05123 entityMap.insert("nbsp", "\xC2\xA0"); 05124 init=FALSE; 05125 } 05126 05127 if (s==0) return result; 05128 int p,i=0,l; 05129 while ((p=entityPat.match(s,i,&l))!=-1) 05130 { 05131 if (p>i) result+=s.mid(i,p-i); 05132 QCString entity = s.mid(p+1,l-2); 05133 char *code = entityMap.find(entity); 05134 if (code) 05135 { 05136 result+=code; 05137 } 05138 else 05139 { 05140 result+=s.mid(p,l); 05141 } 05142 i=p+l; 05143 } 05144 result+=s.mid(i,s.length()-i); 05145 return result; 05146 } 05147 05151 QCString getOverloadDocs() 05152 { 05153 return theTranslator->trOverloadText(); 05154 //"This is an overloaded member function, " 05155 // "provided for convenience. It differs from the above " 05156 // "function only in what argument(s) it accepts."; 05157 } 05158 05159 void addMembersToMemberGroup(MemberList *ml, 05160 MemberGroupSDict **ppMemberGroupSDict, 05161 Definition *context) 05162 { 05163 ASSERT(context!=0); 05164 //printf("addMemberToMemberGroup()\n"); 05165 if (ml==0) return; 05166 MemberListIterator mli(*ml); 05167 MemberDef *md; 05168 uint index; 05169 for (index=0;(md=mli.current());) 05170 { 05171 if (md->isEnumerate()) // insert enum value of this enum into groups 05172 { 05173 LockingPtr<MemberList> fmdl=md->enumFieldList(); 05174 if (fmdl!=0) 05175 { 05176 MemberDef *fmd=fmdl->first(); 05177 while (fmd) 05178 { 05179 int groupId=fmd->getMemberGroupId(); 05180 if (groupId!=-1) 05181 { 05182 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId]; 05183 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId]; 05184 //QCString *pDocs = Doxygen::memberDocDict[groupId]; 05185 if (info) 05186 { 05187 if (*ppMemberGroupSDict==0) 05188 { 05189 *ppMemberGroupSDict = new MemberGroupSDict; 05190 (*ppMemberGroupSDict)->setAutoDelete(TRUE); 05191 } 05192 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); 05193 if (mg==0) 05194 { 05195 mg = new MemberGroup( 05196 context, 05197 groupId, 05198 info->header, 05199 info->doc, 05200 info->docFile 05201 ); 05202 (*ppMemberGroupSDict)->append(groupId,mg); 05203 } 05204 mg->insertMember(fmd); // insert in member group 05205 fmd->setMemberGroup(mg); 05206 } 05207 } 05208 fmd=fmdl->next(); 05209 } 05210 } 05211 } 05212 int groupId=md->getMemberGroupId(); 05213 if (groupId!=-1) 05214 { 05215 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId]; 05216 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId]; 05217 //QCString *pDocs = Doxygen::memberDocDict[groupId]; 05218 if (info) 05219 { 05220 if (*ppMemberGroupSDict==0) 05221 { 05222 *ppMemberGroupSDict = new MemberGroupSDict; 05223 (*ppMemberGroupSDict)->setAutoDelete(TRUE); 05224 } 05225 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId); 05226 if (mg==0) 05227 { 05228 mg = new MemberGroup( 05229 context, 05230 groupId, 05231 info->header, 05232 info->doc, 05233 info->docFile 05234 ); 05235 (*ppMemberGroupSDict)->append(groupId,mg); 05236 } 05237 md = ml->take(index); // remove from member list 05238 mg->insertMember(md); // insert in member group 05239 md->setMemberGroup(mg); 05240 continue; 05241 } 05242 } 05243 ++mli;++index; 05244 } 05245 } 05246 05252 int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec) 05253 { 05254 static const QRegExp re("[a-z_A-Z][a-z_A-Z0-9:]*"); 05255 name.resize(0); 05256 templSpec.resize(0); 05257 int i,l; 05258 int typeLen=type.length(); 05259 if (typeLen>0) 05260 { 05261 if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type 05262 { 05263 int ts=i+l; 05264 int te=ts; 05265 int tl=0; 05266 while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace 05267 if (type.at(ts)=='<') // assume template instance 05268 { 05269 // locate end of template 05270 te=ts+1; 05271 int brCount=1; 05272 while (te<typeLen && brCount!=0) 05273 { 05274 if (type.at(te)=='<') 05275 { 05276 if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++; 05277 } 05278 if (type.at(te)=='>') 05279 { 05280 if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--; 05281 } 05282 te++; 05283 } 05284 } 05285 name = type.mid(i,l); 05286 if (te>ts) 05287 { 05288 templSpec = type.mid(ts,te-ts),tl+=te-ts; 05289 pos=i+l+tl; 05290 } 05291 else // no template part 05292 { 05293 pos=i+l; 05294 } 05295 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n", 05296 // type.data(),pos,name.data(),templSpec.data()); 05297 return i; 05298 } 05299 } 05300 pos = typeLen; 05301 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n", 05302 // type.data(),pos,name.data(),templSpec.data()); 05303 return -1; 05304 } 05305 05312 QCString substituteTemplateArgumentsInString( 05313 const QCString &name, 05314 ArgumentList *formalArgs, 05315 ArgumentList *actualArgs) 05316 { 05317 //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n", 05318 // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data()); 05319 if (formalArgs==0) return name; 05320 QCString result; 05321 static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*"); 05322 int p=0,l,i; 05323 // for each identifier in the base class name (e.g. B<T> -> B and T) 05324 while ((i=re.match(name,p,&l))!=-1) 05325 { 05326 result += name.mid(p,i-p); 05327 QCString n = name.mid(i,l); 05328 ArgumentListIterator formAli(*formalArgs); 05329 Argument *formArg; 05330 Argument *actArg=actualArgs->first(); 05331 05332 // if n is a template argument, then we substitute it 05333 // for its template instance argument. 05334 bool found=FALSE; 05335 for (formAli.toFirst(); 05336 (formArg=formAli.current()) && !found; 05337 ++formAli,actArg=actualArgs->next() 05338 ) 05339 { 05340 //printf("formArg->type=%s\n",formArg->type.data()); 05341 if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template") 05342 { 05343 //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n", 05344 // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data()); 05345 if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument 05346 { 05347 // replace formal argument with the actual argument of the instance 05348 if (actArg->name.isEmpty()) 05349 { 05350 result += actArg->type+" "; 05351 } 05352 else // for case where the actual arg is something like "unsigned int" 05353 // the "int" part is in actArg->name. 05354 { 05355 result += actArg->type+" "+actArg->name+" "; 05356 } 05357 found=TRUE; 05358 } 05359 else if (formArg->name==n && actArg==0 && !formArg->defval.isEmpty() && 05360 formArg->defval!=name /* to prevent recursion */ 05361 ) 05362 { 05363 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" "; 05364 found=TRUE; 05365 } 05366 } 05367 else if (formArg->name==n && actArg==0 && !formArg->defval.isEmpty() && 05368 formArg->defval!=name /* to prevent recursion */ 05369 ) 05370 { 05371 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" "; 05372 found=TRUE; 05373 } 05374 } 05375 if (!found) result += n; 05376 p=i+l; 05377 } 05378 result+=name.right(name.length()-p); 05379 //printf(" Inheritance relation %s -> %s\n", 05380 // name.data(),result.data()); 05381 return result; 05382 } 05383 05384 05388 ArgumentList *copyArgumentList(const ArgumentList *src) 05389 { 05390 ASSERT(src!=0); 05391 ArgumentList *dst = new ArgumentList; 05392 dst->setAutoDelete(TRUE); 05393 ArgumentListIterator tali(*src); 05394 Argument *a; 05395 for (;(a=tali.current());++tali) 05396 { 05397 dst->append(new Argument(*a)); 05398 } 05399 dst->constSpecifier = src->constSpecifier; 05400 dst->volatileSpecifier = src->volatileSpecifier; 05401 dst->pureSpecifier = src->pureSpecifier; 05402 return dst; 05403 } 05404 05408 QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists) 05409 { 05410 ASSERT(srcLists!=0); 05411 QList<ArgumentList> *dstLists = new QList<ArgumentList>; 05412 dstLists->setAutoDelete(TRUE); 05413 QListIterator<ArgumentList> sli(*srcLists); 05414 ArgumentList *sl; 05415 for (;(sl=sli.current());++sli) 05416 { 05417 dstLists->append(copyArgumentList(sl)); 05418 } 05419 return dstLists; 05420 } 05421 05429 QCString stripTemplateSpecifiersFromScope(const QCString &fullName, 05430 bool parentOnly, 05431 QCString *pLastScopeStripped) 05432 { 05433 QCString result; 05434 int p=0; 05435 int l=fullName.length(); 05436 int i=fullName.find('<'); 05437 while (i!=-1) 05438 { 05439 //printf("1:result+=%s\n",fullName.mid(p,i-p).data()); 05440 int e=i+1; 05441 bool done=FALSE; 05442 int count=1; 05443 while (e<l && !done) 05444 { 05445 char c=fullName.at(e++); 05446 if (c=='<') 05447 { 05448 count++; 05449 } 05450 else if (c=='>') 05451 { 05452 count--; 05453 done = count==0; 05454 } 05455 } 05456 int si= fullName.find("::",e); 05457 05458 if (parentOnly && si==-1) break; 05459 // we only do the parent scope, so we stop here if needed 05460 05461 result+=fullName.mid(p,i-p); 05462 //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data()); 05463 if (getClass(result+fullName.mid(i,e-i))!=0) 05464 { 05465 result+=fullName.mid(i,e-i); 05466 //printf("2:result+=%s\n",fullName.mid(i,e-i-1).data()); 05467 } 05468 else if (pLastScopeStripped) 05469 { 05470 *pLastScopeStripped=fullName.mid(i,e-i); 05471 } 05472 p=e; 05473 i=fullName.find('<',p); 05474 } 05475 result+=fullName.right(l-p); 05476 //printf("3:result+=%s\n",fullName.right(l-p).data()); 05477 return result; 05478 } 05479 05489 QCString mergeScopes(const QCString &leftScope,const QCString &rightScope) 05490 { 05491 // case leftScope=="A" rightScope=="A::B" => result = "A::B" 05492 if (leftScopeMatch(rightScope,leftScope)) return rightScope; 05493 QCString result; 05494 int i=0,p=leftScope.length(); 05495 05496 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C" 05497 // case leftScope=="A::B" rightScope=="B" => result = "A::B" 05498 bool found=FALSE; 05499 while ((i=leftScope.findRev("::",p))!=-1) 05500 { 05501 if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2))) 05502 { 05503 result = leftScope.left(i+2)+rightScope; 05504 found=TRUE; 05505 } 05506 p=i-1; 05507 } 05508 if (found) return result; 05509 05510 // case leftScope=="A" rightScope=="B" => result = "A::B" 05511 result=leftScope.copy(); 05512 if (!result.isEmpty() && !rightScope.isEmpty()) result+="::"; 05513 result+=rightScope; 05514 return result; 05515 } 05516 05524 int getScopeFragment(const QCString &s,int p,int *l) 05525 { 05526 int sl=s.length(); 05527 int sp=p; 05528 int count=0; 05529 bool done; 05530 if (sp>=sl) return -1; 05531 while (sp<sl) 05532 { 05533 char c=s.at(sp); 05534 if (c==':') sp++,p++; else break; 05535 } 05536 while (sp<sl) 05537 { 05538 char c=s.at(sp); 05539 switch (c) 05540 { 05541 case ':': // found next part 05542 goto found; 05543 case '<': // skip template specifier 05544 count=1;sp++; 05545 done=FALSE; 05546 while (sp<sl && !done) 05547 { 05548 // TODO: deal with << and >> operators! 05549 char c=s.at(sp++); 05550 switch(c) 05551 { 05552 case '<': count++; break; 05553 case '>': count--; if (count==0) done=TRUE; break; 05554 default: break; 05555 } 05556 } 05557 break; 05558 default: 05559 sp++; 05560 break; 05561 } 05562 } 05563 found: 05564 *l=sp-p; 05565 //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data()); 05566 return p; 05567 } 05568 05569 //---------------------------------------------------------------------------- 05570 05571 PageDef *addRelatedPage(const char *name,const QCString &ptitle, 05572 const QCString &doc, 05573 QList<SectionInfo> * /*anchors*/, 05574 const char *fileName,int startLine, 05575 const QList<ListItemInfo> *sli, 05576 GroupDef *gd, 05577 TagInfo *tagInfo 05578 ) 05579 { 05580 PageDef *pd=0; 05581 //printf("addRelatedPage(name=%s gd=%p)\n",name,gd); 05582 if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo) 05583 { 05584 // append documentation block to the page. 05585 pd->setDocumentation(doc,fileName,startLine); 05586 //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name); 05587 } 05588 else // new page 05589 { 05590 QCString baseName=name; 05591 if (baseName.right(4)==".tex") 05592 baseName=baseName.left(baseName.length()-4); 05593 else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension) 05594 baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length()); 05595 05596 QCString title=ptitle.stripWhiteSpace(); 05597 pd=new PageDef(fileName,startLine,baseName,doc,title); 05598 05599 pd->setRefItems(sli); 05600 05601 if (tagInfo) 05602 { 05603 pd->setReference(tagInfo->tagName); 05604 } 05605 05606 QCString pageName; 05607 if (Config_getBool("CASE_SENSE_NAMES")) 05608 pageName=pd->name(); 05609 else 05610 pageName=pd->name().lower(); 05611 pd->setFileName(pageName); 05612 05613 //printf("Appending page `%s'\n",baseName.data()); 05614 Doxygen::pageSDict->append(baseName,pd); 05615 05616 if (gd) gd->addPage(pd); 05617 05618 if (!pd->title().isEmpty()) 05619 { 05620 //outputList->writeTitle(pi->name,pi->title); 05621 05622 // a page name is a label as well! 05623 QCString file; 05624 if (gd) 05625 { 05626 file=gd->getOutputFileBase(); 05627 } 05628 else if (pd->getGroupDef()) 05629 { 05630 file=pd->getGroupDef()->getOutputFileBase().copy(); 05631 } 05632 else 05633 { 05634 file=pageName; 05635 } 05636 SectionInfo *si=new SectionInfo( 05637 file,pd->name(),pd->title(),SectionInfo::Page,pd->getReference()); 05638 //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n", 05639 // si->label.data(),si->definition?si->definition->name().data():"<none>", 05640 // si->fileName.data()); 05641 //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data()); 05642 //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data()); 05643 Doxygen::sectionDict.insert(pageName,si); 05644 } 05645 } 05646 return pd; 05647 } 05648 05649 //---------------------------------------------------------------------------- 05650 05651 void addRefItem(const QList<ListItemInfo> *sli, 05652 const char *prefix, 05653 const char *name,const char *title,const char *args) 05654 { 05655 //printf("addRefItem(sli=%p,prefix=%s,name=%s,title=%s,args=%s)\n",sli,prefix,name,title,args); 05656 if (sli) 05657 { 05658 QListIterator<ListItemInfo> slii(*sli); 05659 ListItemInfo *lii; 05660 for (slii.toFirst();(lii=slii.current());++slii) 05661 { 05662 RefList *refList = Doxygen::xrefLists->find(lii->type); 05663 if (refList && 05664 ( 05665 // either not a built-in list or the list is enabled 05666 (lii->type!="todo" || Config_getBool("GENERATE_TODOLIST")) && 05667 (lii->type!="test" || Config_getBool("GENERATE_TESTLIST")) && 05668 (lii->type!="bug" || Config_getBool("GENERATE_BUGLIST")) && 05669 (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST")) 05670 ) 05671 ) 05672 { 05673 RefItem *item = refList->getRefItem(lii->itemId); 05674 ASSERT(item!=0); 05675 //printf("anchor=%s written=%d\n",item->listAnchor.data(),item->written); 05676 if (item->written) return; 05677 05678 QCString doc(1000); 05679 doc = "\\anchor "; 05680 doc += item->listAnchor; 05681 doc += " <dl><dt>"; 05682 doc += prefix; 05683 doc += " \\_internalref "; 05684 doc += name; 05685 doc += " \""; 05686 doc += title; 05687 doc += "\""; 05688 if (args) doc += args; 05689 doc += "</dt>\n<dd>"; 05690 doc += item->text; 05691 doc += "</dd></dl>\n"; 05692 addRelatedPage(refList->listName(),refList->pageTitle(),doc,0,refList->listName(),1,0,0,0); 05693 item->written=TRUE; 05694 } 05695 } 05696 } 05697 } 05698 05699 void addGroupListToTitle(OutputList &ol,Definition *d) 05700 { 05701 LockingPtr<GroupList> groups = d->partOfGroups(); 05702 if (groups!=0) // write list of group to which this definition belongs 05703 { 05704 ol.pushGeneratorState(); 05705 ol.disableAllBut(OutputGenerator::Html); 05706 ol.lineBreak(); 05707 ol.startSmall(); 05708 ol.docify("["); 05709 GroupListIterator gli(*groups); 05710 GroupDef *gd; 05711 bool first=TRUE; 05712 for (gli.toFirst();(gd=gli.current());++gli) 05713 { 05714 if (!first) { ol.docify(","); ol.writeNonBreakableSpace(1); } else first=FALSE; 05715 ol.writeObjectLink(gd->getReference(), 05716 gd->getOutputFileBase(),0,gd->groupTitle()); 05717 } 05718 ol.docify("]"); 05719 ol.endSmall(); 05720 ol.popGeneratorState(); 05721 } 05722 } 05723 05724 #if 0 05725 05729 static void latin1ToLatex(QTextStream &t,unsigned char c) 05730 { 05731 switch (c) 05732 { 05733 // the Latin-1 characters 05734 case 161: t << "!`"; break; 05735 case 181: t << "$\\mu$"; break; 05736 case 191: t << "?`"; break; 05737 case 192: t << "\\`{A}"; break; 05738 case 193: t << "\\'{A}"; break; 05739 case 194: t << "\\^{A}"; break; 05740 case 195: t << "\\~{A}"; break; 05741 case 196: t << "\\\"{A}"; break; 05742 case 197: t << "\\AA{}"; break; 05743 case 198: t << "\\AE{}"; break; 05744 case 199: t << "\\c{C}"; break; 05745 case 200: t << "\\`{E}"; break; 05746 case 201: t << "\\'{E}"; break; 05747 case 202: t << "\\^{E}"; break; 05748 case 203: t << "\\\"{E}"; break; 05749 case 204: t << "\\`{I}"; break; 05750 case 205: t << "\\'{I}"; break; 05751 case 206: t << "\\^{I}"; break; 05752 case 207: t << "\\\"{I}"; break; 05753 case 208: t << "D "; break; // anyone know the real code? 05754 case 209: t << "\\~{N}"; break; 05755 case 210: t << "\\`{O}"; break; 05756 case 211: t << "\\'{O}"; break; 05757 case 212: t << "\\^{O}"; break; 05758 case 213: t << "\\~{O}"; break; 05759 case 214: t << "\\\"{O}"; break; 05760 case 215: t << "$\\times$"; break; 05761 case 216: t << "\\O"; break; 05762 case 217: t << "\\`{U}"; break; 05763 case 218: t << "\\'{U}"; break; 05764 case 219: t << "\\^{U}"; break; 05765 case 220: t << "\\\"{U}"; break; 05766 case 221: t << "\\'{Y}"; break; 05767 case 223: t << "\\ss{}"; break; 05768 case 224: t << "\\`{a}"; break; 05769 case 225: t << "\\'{a}"; break; 05770 case 226: t << "\\^{a}"; break; 05771 case 227: t << "\\~{a}"; break; 05772 case 228: t << "\\\"{a}"; break; 05773 case 229: t << "\\aa{}"; break; 05774 case 230: t << "\\ae{}"; break; 05775 case 231: t << "\\c{c}"; break; 05776 case 232: t << "\\`{e}"; break; 05777 case 233: t << "\\'{e}"; break; 05778 case 234: t << "\\^{e}"; break; 05779 case 235: t << "\\\"{e}"; break; 05780 case 236: t << "\\`{\\i}"; break; 05781 case 237: t << "\\'{\\i}"; break; 05782 case 238: t << "\\^{\\i}"; break; 05783 case 239: t << "\\\"{\\i}"; break; 05784 case 241: t << "\\~{n}"; break; 05785 case 242: t << "\\`{o}"; break; 05786 case 243: t << "\\'{o}"; break; 05787 case 244: t << "\\^{o}"; break; 05788 case 245: t << "\\~{o}"; break; 05789 case 246: t << "\\\"{o}"; break; 05790 case 248: t << "\\o{}"; break; 05791 case 249: t << "\\`{u}"; break; 05792 case 250: t << "\\'{u}"; break; 05793 case 251: t << "\\^{u}"; break; 05794 case 252: t << "\\\"{u}"; break; 05795 case 253: t << "\\'{y}"; break; 05796 case 255: t << "\\\"{y}"; break; 05797 default: t << (char)c; 05798 } 05799 } 05800 05805 static void latin2ToLatex(QTextStream &t,unsigned char c) 05806 { 05807 switch (c) 05808 { 05809 case 0xA1: t << "\\k{A}"; break; 05810 case 0xA2: t << (char)c; break; 05811 case 0xA3: t << "\\L{}"; break; 05812 case 0xA4: t << (char)c; break; 05813 case 0xA5: t << (char)c; break; 05814 case 0xA6: t << "\\'{S}"; break; 05815 case 0xA7: t << (char)c; break; 05816 case 0xA8: t << (char)c; break; 05817 case 0xA9: t << "\\v{S}"; break; 05818 case 0xAA: t << "\\c{S}"; break; 05819 case 0xAB: t << "\\v{T}"; break; 05820 case 0xAC: t << "\\'{Z}"; break; 05821 case 0xAD: t << (char)c; break; 05822 case 0xAE: t << "\\v{Z}"; break; 05823 case 0xAF: t << "\\.{Z}"; break; 05824 05825 case 0xB0: t << (char)c; break; 05826 case 0xB1: t << "\\k{a}"; break; 05827 case 0xB2: t << (char)c; break; 05828 case 0xB3: t << "\\l{}"; break; 05829 case 0xB4: t << (char)c; break; 05830 case 0xB5: t << (char)c; break; 05831 case 0xB6: t << "\\'{s}"; break; 05832 case 0xB7: t << (char)c; break; 05833 case 0xB8: t << (char)c; break; 05834 case 0xB9: t << "\\v{s}"; break; 05835 case 0xBA: t << "\\c{s}"; break; 05836 case 0xBB: t << "\\v{t}"; break; 05837 case 0xBC: t << "\\'{z}"; break; 05838 case 0xBD: t << (char)c; break; 05839 case 0xBE: t << "\\v{z}"; break; 05840 case 0xBF: t << "\\.{z}"; break; 05841 05842 case 0xC0: t << "\\'{R}"; break; 05843 case 0xC1: t << "\\'{A}"; break; 05844 case 0xC2: t << "\\^{A}"; break; 05845 case 0xC3: t << "\\u{A}"; break; 05846 case 0xC4: t << "\\\"{A}"; break; 05847 case 0xC5: t << "\\'{L}"; break; 05848 case 0xC6: t << "\\'{C}"; break; 05849 case 0xC7: t << "\\c{C}"; break; 05850 case 0xC8: t << "\\v{C}"; break; 05851 case 0xC9: t << "\\'{E}"; break; 05852 case 0xCA: t << "\\k{E}"; break; 05853 case 0xCB: t << "\\\"{E}"; break; 05854 case 0xCC: t << "\\v{E}"; break; 05855 case 0xCD: t << "\\'{I}"; break; 05856 case 0xCE: t << "\\^{I}"; break; 05857 case 0xCF: t << "\\v{D}"; break; 05858 05859 case 0xD0: t << "\\DJ "; break; 05860 case 0xD1: t << "\\'{N}"; break; 05861 case 0xD2: t << "\\v{N}"; break; 05862 case 0xD3: t << "\\'{O}"; break; 05863 case 0xD4: t << "\\^{O}"; break; 05864 case 0xD5: t << "\\H{O}"; break; 05865 case 0xD6: t << "\\\"{O}"; break; 05866 case 0xD7: t << (char)c; break; 05867 case 0xD8: t << "\\v{R}"; break; 05868 case 0xD9: t << (char)c; break; 05869 case 0xDA: t << "\\'{U}"; break; 05870 case 0xDB: t << "\\H{U}"; break; 05871 case 0xDC: t << "\\\"{U}"; break; 05872 case 0xDD: t << "\\'{Y}"; break; 05873 case 0xDE: t << "\\c{T}"; break; 05874 case 0xDF: t << "\\ss"; break; 05875 05876 case 0xE0: t << "\\'{r}"; break; 05877 case 0xE1: t << "\\'{a}"; break; 05878 case 0xE2: t << "\\^{a}"; break; 05879 case 0xE3: t << (char)c; break; 05880 case 0xE4: t << "\\\"{a}"; break; 05881 case 0xE5: t << "\\'{l}"; break; 05882 case 0xE6: t << "\\'{c}"; break; 05883 case 0xE7: t << "\\c{c}"; break; 05884 case 0xE8: t << "\\v{c}"; break; 05885 case 0xE9: t << "\\'{e}"; break; 05886 case 0xEA: t << "\\k{e}"; break; 05887 case 0xEB: t << "\\\"{e}"; break; 05888 case 0xEC: t << "\\v{e}"; break; 05889 case 0xED: t << "\\'{\\i}"; break; 05890 case 0xEE: t << "\\^{\\i}"; break; 05891 case 0xEF: t << "\\v{d}"; break; 05892 05893 case 0xF0: t << "\\dj "; break; 05894 case 0xF1: t << "\\'{n}"; break; 05895 case 0xF2: t << "\\v{n}"; break; 05896 case 0xF3: t << "\\'{o}"; break; 05897 case 0xF4: t << "\\^{o}"; break; 05898 case 0xF5: t << "\\H{o}"; break; 05899 case 0xF6: t << "\\\"{o}"; break; 05900 case 0xF7: t << (char)c; break; 05901 case 0xF8: t << "\\v{r}"; break; 05902 case 0xF9: t << (char)c; break; 05903 case 0xFA: t << "\\'{u}"; break; 05904 case 0xFB: t << "\\H{u}"; break; 05905 case 0xFC: t << "\\\"{u}"; break; 05906 case 0xFD: t << "\\'{y}"; break; 05907 case 0xFE: t << (char)c; break; 05908 case 0xFF: t << (char)c; break; 05909 05910 default: t << (char)c; 05911 } 05912 } 05913 #endif 05914 05915 void filterLatexString(QTextStream &t,const char *str, 05916 bool insideTabbing,bool insidePre,bool insideItem) 05917 { 05918 #if 0 05919 static bool isCzech = theTranslator->idLanguage()=="czech"; 05920 static bool isSerbian = theTranslator->idLanguage()=="serbian"; 05921 static bool isJapanese = theTranslator->idLanguage()=="japanese" || 05922 theTranslator->idLanguage()=="japanese-en"; 05923 static bool isKorean = theTranslator->idLanguage()=="korean" || 05924 theTranslator->idLanguage()=="korean-en"; 05925 static bool isRussian = theTranslator->idLanguage()=="russian"; 05926 static bool isUkrainian = theTranslator->idLanguage()=="ukrainian"; 05927 static bool isSlovene = theTranslator->idLanguage()=="solvene"; 05928 static bool isChinese = theTranslator->idLanguage()=="chinese" || 05929 theTranslator->idLanguage()=="chinese-traditional"; 05930 static bool isLatin2 = theTranslator->idLanguageCharset()=="iso-8859-2"; 05931 static bool isGreek = theTranslator->idLanguage()=="greek"; 05932 //printf("filterLatexString(%s)\n",str); 05933 #endif 05934 if (str) 05935 { 05936 const unsigned char *p=(const unsigned char *)str; 05937 unsigned char c; 05938 unsigned char pc='\0'; 05939 while (*p) 05940 { 05941 c=*p++; 05942 05943 if (insidePre) 05944 { 05945 switch(c) 05946 { 05947 case '\\': t << "\\(\\backslash\\)"; break; 05948 case '{': t << "\\{"; break; 05949 case '}': t << "\\}"; break; 05950 case '_': t << "\\_"; break; 05951 default: 05952 t << (char)c; 05953 #if 0 05954 { 05955 // Some languages use wide characters 05956 if (c>=128 && (isJapanese || isKorean || isChinese || isSerbian)) 05957 { 05958 t << (char)c; 05959 if (*p) 05960 { 05961 c = *p++; 05962 t << (char)c; 05963 } 05964 } 05965 else 05966 { 05967 t << (char)c; 05968 } 05969 break; 05970 } 05971 #endif 05972 } 05973 } 05974 else 05975 { 05976 switch(c) 05977 { 05978 case '#': t << "\\#"; break; 05979 case '$': t << "\\$"; break; 05980 case '%': t << "\\%"; break; 05981 case '^': t << "$^\\wedge$"; break; 05982 case '&': t << "\\&"; break; 05983 case '*': t << "$\\ast$"; break; 05984 case '_': t << "\\_"; 05985 if (!insideTabbing) t << "\\-"; 05986 break; 05987 case '{': t << "\\{"; break; 05988 case '}': t << "\\}"; break; 05989 case '<': t << "$<$"; break; 05990 case '>': t << "$>$"; break; 05991 case '|': t << "$|$"; break; 05992 case '~': t << "$\\sim$"; break; 05993 case '[': if (Config_getBool("PDF_HYPERLINKS") || insideItem) 05994 t << "\\mbox{[}"; 05995 else 05996 t << "["; 05997 break; 05998 case ']': if (pc=='[') t << "$\\,$"; 05999 if (Config_getBool("PDF_HYPERLINKS") || insideItem) 06000 t << "\\mbox{]}"; 06001 else 06002 t << "]"; 06003 break; 06004 case '-': if (*p=='>') 06005 { t << " $\\rightarrow$ "; p++; } 06006 else 06007 { t << (char)c; } 06008 break; 06009 case '\\': if (*p=='<') 06010 { t << "$<$"; p++; } 06011 else if (*p=='>') 06012 { t << "$>$"; p++; } 06013 else 06014 { t << "$\\backslash$"; } 06015 break; 06016 case '"': { t << "\\char`\\\"{}"; } 06017 break; 06018 06019 default: 06020 t << (char)c; 06021 #if 0 06022 { 06023 // Some languages use wide characters 06024 if (isJapanese || isKorean || isChinese || isSerbian) 06025 { 06026 if (c>=128) 06027 { 06028 t << (char)c; 06029 if (*p) 06030 { 06031 c = *p++; 06032 t << (char)c; 06033 } 06034 } 06035 else // ascii char => see if we can insert a hypenation hint 06036 { 06037 if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-"; 06038 t << (char)c; 06039 } 06040 } 06041 else if (isCzech || isRussian || isUkrainian || isSlovene) 06042 { 06043 if (c>=128) 06044 { 06045 t << (char)c; 06046 } 06047 else // ascii char => see if we can insert a hypenation hint 06048 { 06049 if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-"; 06050 t << (char)c; 06051 } 06052 } 06053 else if (isGreek) 06054 { 06055 if (c<128) 06056 { 06057 t << "\\textlatin{" << (char)c << "}"; 06058 } 06059 else 06060 { 06061 t << (char)c; 06062 } 06063 } 06064 else if (isLatin2) 06065 { 06066 if (c>=128) 06067 { 06068 latin2ToLatex(t,c); 06069 } 06070 else 06071 { 06072 // see if we can insert an hyphenation hint 06073 if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-"; 06074 t << (char)c; 06075 } 06076 } 06077 else // another language => assume latin1 charset 06078 { 06079 if (c>=128) 06080 { 06081 latin1ToLatex(t,c); 06082 } 06083 else 06084 { 06085 // see if we can insert an hyphenation hint 06086 if (isupper(c) && islower(pc) && !insideTabbing) t << "\\-"; 06087 t << (char)c; 06088 } 06089 } 06090 } 06091 #endif 06092 } 06093 } 06094 pc = c; 06095 } 06096 } 06097 } 06098 06099 06100 QCString rtfFormatBmkStr(const char *name) 06101 { 06102 static QCString g_nextTag( "AAAAAAAAAA" ); 06103 static QDict<QCString> g_tagDict( 5003 ); 06104 06105 g_tagDict.setAutoDelete(TRUE); 06106 06107 // To overcome the 40-character tag limitation, we 06108 // substitute a short arbitrary string for the name 06109 // supplied, and keep track of the correspondence 06110 // between names and strings. 06111 QCString key( name ); 06112 QCString* tag = g_tagDict.find( key ); 06113 if ( !tag ) 06114 { 06115 // This particular name has not yet been added 06116 // to the list. Add it, associating it with the 06117 // next tag value, and increment the next tag. 06118 tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy! 06119 g_tagDict.insert( key, tag ); 06120 06121 // This is the increment part 06122 char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1; 06123 for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag ) 06124 { 06125 if ( ( ++(*nxtTag) ) > 'Z' ) 06126 { 06127 *nxtTag = 'A'; 06128 } 06129 else 06130 { 06131 // Since there was no carry, we can stop now 06132 break; 06133 } 06134 } 06135 } 06136 06137 return *tag; 06138 } 06139 06140 QCString stripExtension(const char *fName) 06141 { 06142 QCString result=fName; 06143 if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension) 06144 { 06145 result=result.left(result.length()-Doxygen::htmlFileExtension.length()); 06146 } 06147 return result; 06148 } 06149 06150 06151 void replaceNamespaceAliases(QCString &scope,int i) 06152 { 06153 //printf("replaceNamespaceAliases(%s,%d)\n",scope.data(),i); 06154 while (i>0) 06155 { 06156 QCString *s = Doxygen::namespaceAliasDict[scope.left(i)]; 06157 if (s) 06158 { 06159 scope=*s+scope.right(scope.length()-i); 06160 i=s->length(); 06161 } 06162 i=scope.findRev("::",i-1); 06163 } 06164 //printf("replaceNamespaceAliases() result=%s\n",scope.data()); 06165 } 06166 06167 QCString stripPath(const char *s) 06168 { 06169 QCString result=s; 06170 int i=result.findRev('/'); 06171 if (i!=-1) 06172 { 06173 result=result.mid(i+1); 06174 } 06175 return result; 06176 } 06177 06179 bool containsWord(const QCString &s,const QCString &word) 06180 { 06181 static QRegExp wordExp("[a-z_A-Z]+"); 06182 int p=0,i,l; 06183 while ((i=wordExp.match(s,p,&l))!=-1) 06184 { 06185 if (s.mid(i,l)==word) return TRUE; 06186 p=i+l; 06187 } 06188 return FALSE; 06189 } 06190 06191 bool findAndRemoveWord(QCString &s,const QCString &word) 06192 { 06193 static QRegExp wordExp("[a-z_A-Z]+"); 06194 int p=0,i,l; 06195 while ((i=wordExp.match(s,p,&l))!=-1) 06196 { 06197 if (s.mid(i,l)==word) 06198 { 06199 if (i>0 && isspace(s.at(i-1))) 06200 i--,l++; 06201 else if (i+l<(int)s.length() && isspace(s.at(i+l))) 06202 l++; 06203 s = s.left(i)+s.mid(i+l); // remove word + spacing 06204 return TRUE; 06205 } 06206 p=i+l; 06207 } 06208 return FALSE; 06209 } 06210 06214 QCString stripLeadingAndTrailingEmptyLines(const QCString &s) 06215 { 06216 const char *p = s.data(); 06217 if (p==0) return 0; 06218 06219 // search for leading empty lines 06220 int i=0,li=-1,l=s.length(); 06221 char c; 06222 while ((c=*p++)) 06223 { 06224 if (c==' ' || c=='\t' || c=='\r') i++; 06225 else if (c=='\n') i++,li=i; 06226 else break; 06227 } 06228 06229 // search for trailing empty lines 06230 int b=l-1,bi=-1; 06231 p=s.data()+b; 06232 while (b>=0) 06233 { 06234 c=*p; p--; 06235 if (c==' ' || c=='\t' || c=='\r') b--; 06236 else if (c=='\n') bi=b,b--; 06237 else break; 06238 } 06239 06240 // return whole string if no leading or trailing lines where found 06241 if (li==-1 && bi==-1) return s; 06242 06243 // return substring 06244 if (bi==-1) bi=l; 06245 if (li==-1) li=0; 06246 if (bi<=li) return 0; // only empty lines 06247 return s.mid(li,bi-li); 06248 } 06249 06250 void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title, 06251 const QCString &str,bool priority,const QCString &anchor) 06252 { 06253 static bool searchEngine = Config_getBool("SEARCHENGINE"); 06254 if (searchEngine) 06255 { 06256 Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor); 06257 static QRegExp wordPattern("[a-z_A-Z][a-z_A-Z0-9]*"); 06258 int i,p=0,l; 06259 while ((i=wordPattern.match(str,p,&l))!=-1) 06260 { 06261 Doxygen::searchIndex->addWord(str.mid(i,l),priority); 06262 p=i+l; 06263 } 06264 } 06265 } 06266 06267 SrcLangExt getLanguageFromFileName(const QCString fileName) 06268 { 06269 int i = fileName.findRev('.'); 06270 static bool init=FALSE; 06271 static QDict<int> extLookup; 06272 extLookup.setAutoDelete(TRUE); 06273 if (!init) // one time initialization 06274 { 06275 extLookup.insert(".idl", new int(SrcLangExt_IDL)); 06276 extLookup.insert(".ddl", new int(SrcLangExt_IDL)); 06277 extLookup.insert(".odl", new int(SrcLangExt_IDL)); 06278 extLookup.insert(".ddl", new int(SrcLangExt_IDL)); 06279 extLookup.insert(".java", new int(SrcLangExt_Java)); 06280 extLookup.insert(".as", new int(SrcLangExt_JS)); 06281 extLookup.insert(".js", new int(SrcLangExt_JS)); 06282 extLookup.insert(".cs", new int(SrcLangExt_CSharp)); 06283 extLookup.insert(".d", new int(SrcLangExt_D)); 06284 extLookup.insert(".php", new int(SrcLangExt_PHP)); 06285 extLookup.insert(".php4", new int(SrcLangExt_PHP)); 06286 extLookup.insert(".php5", new int(SrcLangExt_PHP)); 06287 extLookup.insert(".inc", new int(SrcLangExt_PHP)); 06288 extLookup.insert(".phtml", new int(SrcLangExt_PHP)); 06289 extLookup.insert(".m", new int(SrcLangExt_ObjC)); 06290 extLookup.insert(".M", new int(SrcLangExt_ObjC)); 06291 extLookup.insert(".mm", new int(SrcLangExt_ObjC)); 06292 extLookup.insert(".py", new int(SrcLangExt_Python)); 06293 extLookup.insert(".f", new int(SrcLangExt_F90)); 06294 extLookup.insert(".f90", new int(SrcLangExt_F90)); 06295 extLookup.insert(".vhd", new int(SrcLangExt_VHDL)); 06296 extLookup.insert(".vhdl", new int(SrcLangExt_VHDL)); 06297 init=TRUE; 06298 } 06299 if (i!=-1) // name has an extension 06300 { 06301 QCString extStr=fileName.right(fileName.length()-i); 06302 if (!extStr.isEmpty()) // non-empty extension 06303 { 06304 int *pVal=extLookup.find(extStr); 06305 if (pVal) // listed extension 06306 { 06307 return (SrcLangExt)*pVal; // cast void* address to enum value 06308 } 06309 } 06310 } 06311 return SrcLangExt_Cpp; // not listed => assume C-ish language. 06312 } 06313 06315 bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n) 06316 { 06317 if (scope==0 || 06318 (scope->definitionType()!=Definition::TypeClass && 06319 scope->definitionType()!=Definition::TypeNamespace 06320 ) 06321 ) 06322 { 06323 scope=Doxygen::globalScope; 06324 } 06325 06326 QCString name = n; 06327 if (name.isEmpty()) 06328 return FALSE; // no name was given 06329 06330 DefinitionIntf *di = Doxygen::symbolMap->find(name); 06331 if (di==0) 06332 return FALSE; // could not find any matching symbols 06333 06334 // mostly copied from getResolvedClassRec() 06335 QCString explicitScopePart; 06336 int qualifierIndex = computeQualifiedIndex(name); 06337 if (qualifierIndex!=-1) 06338 { 06339 explicitScopePart = name.left(qualifierIndex); 06340 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length()); 06341 name = name.mid(qualifierIndex+2); 06342 } 06343 06344 int minDistance = 10000; 06345 MemberDef *bestMatch = 0; 06346 06347 if (di->definitionType()==DefinitionIntf::TypeSymbolList) 06348 { 06349 // find the closest closest matching definition 06350 DefinitionListIterator dli(*(DefinitionList*)di); 06351 Definition *d; 06352 for (dli.toFirst();(d=dli.current());++dli) 06353 { 06354 if (d->definitionType()==Definition::TypeMember) 06355 { 06356 g_visitedNamespaces.clear(); 06357 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); 06358 if (distance!=-1 && distance<minDistance) 06359 { 06360 minDistance = distance; 06361 bestMatch = (MemberDef *)d; 06362 } 06363 } 06364 } 06365 } 06366 else if (di->definitionType()==Definition::TypeMember) 06367 { 06368 Definition *d = (Definition *)di; 06369 g_visitedNamespaces.clear(); 06370 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart); 06371 if (distance!=-1 && distance<minDistance) 06372 { 06373 minDistance = distance; 06374 bestMatch = (MemberDef *)d; 06375 } 06376 } 06377 06378 if (bestMatch && bestMatch->isTypedef()) 06379 return TRUE; // closest matching symbol is a typedef 06380 else 06381 return FALSE; 06382 } 06383 06384 QCString parseCommentAsText(const QString &doc,const QCString &fileName,int lineNr) 06385 { 06386 QString result; 06387 if (doc.isEmpty()) return result.data(); 06388 QTextStream t(&result,IO_WriteOnly); 06389 DocNode *root = validatingParseDoc(fileName,lineNr,Doxygen::globalScope,0,doc,FALSE,FALSE); 06390 TextDocVisitor *visitor = new TextDocVisitor(t); 06391 root->accept(visitor); 06392 delete visitor; 06393 delete root; 06394 int i=0; 06395 if (result.length()>80) 06396 { 06397 for (i=80;i<100;i++) // search for nice truncation point 06398 { 06399 if (result.at(i).isSpace() || 06400 result.at(i)==',' || 06401 result.at(i)=='.' || 06402 result.at(i)=='?') 06403 { 06404 break; 06405 } 06406 } 06407 } 06408 if (i>0) result=result.left(i)+"..."; 06409 return result.data(); 06410 } 06411 06412 //-------------------------------------------------------------------------------------- 06413 06414 static QDict<void> aliasesProcessed; 06415 06416 QCString expandAliasRec(const QCString s) 06417 { 06418 QCString result; 06419 static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*"); 06420 QCString value=s; 06421 int i,p=0,l; 06422 while ((i=cmdPat.match(value,p,&l))!=-1) 06423 { 06424 result+=value.mid(p,i-p); 06425 QCString args = extractAliasArgs(value,i+l); 06426 bool hasArgs = !args.isEmpty(); // found directly after command 06427 QCString cmd; 06428 if (hasArgs) 06429 { 06430 int numArgs = countAliasArguments(args); 06431 cmd = value.mid(i+1,l-1)+QCString().sprintf("{%d}",numArgs); // alias name + {n} 06432 } 06433 else 06434 { 06435 cmd = value.mid(i+1,l-1); 06436 } 06437 //printf("Found command '%s' args='%s'\n",cmd.data(),args.data()); 06438 QCString *aliasText=Doxygen::aliasDict.find(cmd); 06439 if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias 06440 { 06441 //printf("is an alias!\n"); 06442 aliasesProcessed.insert(cmd,(void *)0x8); 06443 QCString val = *aliasText; 06444 if (hasArgs) 06445 { 06446 val = replaceAliasArguments(val,args); 06447 //printf("replace '%s'->'%s' args='%s'\n", 06448 // aliasText->data(),val.data(),args.data()); 06449 } 06450 result+=expandAliasRec(val); 06451 aliasesProcessed.remove(cmd); 06452 p=i+l; 06453 if (hasArgs) p+=args.length()+2; 06454 } 06455 else // command is not an alias 06456 { 06457 //printf("not an alias!\n"); 06458 result+=value.mid(i,l); 06459 p=i+l; 06460 } 06461 } 06462 result+=value.right(value.length()-p); 06463 06464 //printf("expandAliases '%s'->'%s'\n",s.data(),result.data()); 06465 return result; 06466 } 06467 06468 static QCString replaceAliasArgument(const QCString &aliasValue,int paramNum, 06469 const QCString ¶mValue) 06470 { 06471 QCString result; 06472 QCString paramMarker; 06473 paramMarker.sprintf("\\%d",paramNum); 06474 int markerLen = paramMarker.length(); 06475 int p=0,i; 06476 while ((i=aliasValue.find(paramMarker,p))!=-1) // search for marker 06477 { 06478 result+=aliasValue.mid(p,i-p); 06479 //printf("Found marker '%s' at %d len=%d for param '%s' in '%s'\n", 06480 // paramMarker.data(),i,markerLen,paramValue.data(),aliasValue.data()); 06481 if (i==0 || aliasValue.at(i-1)!='\\') // found unescaped marker 06482 { 06483 result += paramValue; 06484 p=i+markerLen; 06485 } 06486 else // ignore escaped markers 06487 { 06488 result += aliasValue.mid(i,markerLen); 06489 p=i+1; 06490 } 06491 } 06492 result+=aliasValue.right(aliasValue.length()-p); 06493 result = expandAliasRec(substitute(result,"\\,",",")); 06494 //printf("replaceAliasArgument('%s',%d,'%s')->%s\n", 06495 // aliasValue.data(),paramNum,paramValue.data(),result.data()); 06496 return result; 06497 } 06498 06499 QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList) 06500 { 06501 QCString result = aliasValue; 06502 QList<QCString> args; 06503 int p=0,i,c=1; 06504 for (i=0;i<(int)argList.length();i++) 06505 { 06506 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) 06507 { 06508 result = replaceAliasArgument(result,c,argList.mid(p,i-p)); 06509 p=i+1; 06510 c++; 06511 } 06512 } 06513 if (p<(int)argList.length()) 06514 { 06515 result = replaceAliasArgument(result,c,argList.right(argList.length()-p)); 06516 } 06517 return result; 06518 } 06519 06520 int countAliasArguments(const QCString argList) 06521 { 06522 int count=1; 06523 int l = argList.length(); 06524 int i; 06525 for (i=0;i<l;i++) 06526 { 06527 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++; 06528 } 06529 return count; 06530 } 06531 06532 QCString extractAliasArgs(const QCString &args,int pos) 06533 { 06534 int i; 06535 int bc=0; 06536 if (args.at(pos)=='{') // alias has argument 06537 { 06538 for (i=pos;i<(int)args.length();i++) 06539 { 06540 if (args.at(i)=='{') bc++; 06541 if (args.at(i)=='}') bc--; 06542 if (bc==0) 06543 { 06544 //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data()); 06545 return args.mid(pos+1,i-pos-1); 06546 } 06547 } 06548 } 06549 return ""; 06550 } 06551 06552 QCString resolveAliasCmd(const QCString aliasCmd) 06553 { 06554 QCString result; 06555 aliasesProcessed.clear(); 06556 //printf("Expanding: '%s'\n",aliasCmd.data()); 06557 result = expandAliasRec(aliasCmd); 06558 //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data()); 06559 return result; 06560 } 06561 06562 QCString expandAlias(const QCString &aliasName,const QCString &aliasValue) 06563 { 06564 QCString result; 06565 aliasesProcessed.clear(); 06566 // avoid expanding this command recursively 06567 aliasesProcessed.insert(aliasName,(void *)0x8); 06568 // expand embedded commands 06569 //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data()); 06570 result = expandAliasRec(aliasValue); 06571 //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data()); 06572 return result; 06573 } 06574 06575 void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al) 06576 { 06577 if (al==0) return; 06578 ol.startConstraintList(theTranslator->trTypeConstraints()); 06579 ArgumentListIterator ali(*al); 06580 Argument *a; 06581 for (;(a=ali.current());++ali) 06582 { 06583 ol.startConstraintParam(); 06584 ol.parseText(a->name); 06585 ol.endConstraintParam(); 06586 ol.startConstraintType(); 06587 linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type); 06588 ol.endConstraintType(); 06589 ol.startConstraintDocs(); 06590 ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE); 06591 ol.endConstraintDocs(); 06592 } 06593 ol.endConstraintList(); 06594 } 06595