Chameleon

Chameleon Svn Source Tree

Root/branches/xZenu/src/util/doxygen/src/util.cpp

Source at commit 1322 created 12 years 8 months ago.
By meklort, Add doxygen to utils folder
1/*****************************************************************************
2 *
3 *
4 *
5 * Copyright (C) 1997-2011 by Dimitri van Heesch.
6 *
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
12 *
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
15 *
16 */
17
18#include <stdlib.h>
19#include <ctype.h>
20#include <errno.h>
21#include <math.h>
22
23#include "md5.h"
24
25#include "qtbc.h"
26#include <qregexp.h>
27#include <qfileinfo.h>
28#include <qdir.h>
29#include <qdatetime.h>
30#include <qcache.h>
31
32#include "util.h"
33#include "message.h"
34#include "classdef.h"
35#include "filedef.h"
36#include "doxygen.h"
37#include "outputlist.h"
38#include "defargs.h"
39#include "language.h"
40#include "config.h"
41#include "htmlhelp.h"
42#include "example.h"
43#include "version.h"
44#include "groupdef.h"
45#include "reflist.h"
46#include "pagedef.h"
47#include "debug.h"
48#include "searchindex.h"
49#include "doxygen.h"
50#include "textdocvisitor.h"
51#include "portable.h"
52#include "parserintf.h"
53#include "bufstr.h"
54#include "image.h"
55
56#define ENABLE_TRACINGSUPPORT 0
57
58#if defined(_OS_MAC_) && ENABLE_TRACINGSUPPORT
59#define TRACINGSUPPORT
60#endif
61
62#ifdef TRACINGSUPPORT
63#include <execinfo.h>
64#include <unistd.h>
65#endif
66
67
68//------------------------------------------------------------------------
69
70// selects one of the name to sub-dir mapping algorithms that is used
71// to select a sub directory when CREATE_SUBDIRS is set to YES.
72
73#define ALGO_COUNT 1
74#define ALGO_CRC16 2
75#define ALGO_MD5 3
76
77//#define MAP_ALGO ALGO_COUNT
78//#define MAP_ALGO ALGO_CRC16
79#define MAP_ALGO ALGO_MD5
80
81#define REL_PATH_TO_ROOT "../../"
82
83//------------------------------------------------------------------------
84// TextGeneratorOLImpl implementation
85//------------------------------------------------------------------------
86
87TextGeneratorOLImpl::TextGeneratorOLImpl(OutputDocInterface &od) : m_od(od)
88{
89}
90
91void TextGeneratorOLImpl::writeString(const char *s,bool keepSpaces) const
92{
93 if (keepSpaces)
94 {
95 const char *p=s;
96 if (p)
97 {
98 char cs[2];
99 char c;
100 cs[1]='\0';
101 while ((c=*p++))
102 {
103 if (c==' ') m_od.writeNonBreakableSpace(1);
104 else cs[0]=c,m_od.docify(cs);
105 }
106 }
107 }
108 else
109 {
110 m_od.docify(s);
111 }
112}
113
114void TextGeneratorOLImpl::writeBreak() const
115{
116 m_od.pushGeneratorState();
117 m_od.disableAllBut(OutputGenerator::Html);
118 m_od.lineBreak("typebreak");
119 m_od.popGeneratorState();
120}
121
122void TextGeneratorOLImpl::writeLink(const char *extRef,const char *file,
123 const char *anchor,const char *text
124 ) const
125{
126 m_od.writeObjectLink(extRef,file,anchor,text);
127}
128
129//------------------------------------------------------------------------
130//------------------------------------------------------------------------
131
132// an inheritance tree of depth of 100000 should be enough for everyone :-)
133const int maxInheritanceDepth = 100000;
134
135/*!
136 Removes all anonymous scopes from string s
137 Possible examples:
138\verbatim
139 "bla::@10::blep" => "bla::blep"
140 "bla::@10::@11::blep" => "bla::blep"
141 "@10::blep" => "blep"
142 " @10::blep" => "blep"
143 "@9::@10::blep" => "blep"
144 "bla::@1" => "bla"
145 "bla::@1::@2" => "bla"
146 "bla @1" => "bla"
147\endverbatim
148 */
149QCString removeAnonymousScopes(const QCString &s)
150{
151 QCString result;
152 if (s.isEmpty()) return result;
153 static QRegExp re("[ :]*@[0-9]+[: ]*");
154 int i,l,sl=s.length();
155 int p=0;
156 while ((i=re.match(s,p,&l))!=-1)
157 {
158 result+=s.mid(p,i-p);
159 int c=i;
160 bool b1=FALSE,b2=FALSE;
161 while (c<i+l && s.at(c)!='@') if (s.at(c++)==':') b1=TRUE;
162 c=i+l-1;
163 while (c>=i && s.at(c)!='@') if (s.at(c--)==':') b2=TRUE;
164 if (b1 && b2)
165 {
166 result+="::";
167 }
168 p=i+l;
169 }
170 result+=s.right(sl-p);
171 //printf("removeAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
172 return result;
173}
174
175// replace anonymous scopes with __anonymous__ or replacement if provided
176QCString replaceAnonymousScopes(const QCString &s,const char *replacement)
177{
178 QCString result;
179 if (s.isEmpty()) return result;
180 static QRegExp re("@[0-9]+");
181 int i,l,sl=s.length();
182 int p=0;
183 while ((i=re.match(s,p,&l))!=-1)
184 {
185 result+=s.mid(p,i-p);
186 if (replacement)
187 {
188 result+=replacement;
189 }
190 else
191 {
192 result+="__anonymous__";
193 }
194 p=i+l;
195 }
196 result+=s.right(sl-p);
197 //printf("replaceAnonymousScopes(`%s')=`%s'\n",s.data(),result.data());
198 return result;
199}
200
201
202// strip anonymous left hand side part of the scope
203QCString stripAnonymousNamespaceScope(const QCString &s)
204{
205 int i,p=0,l;
206 QCString newScope;
207 while ((i=getScopeFragment(s,p,&l))!=-1)
208 {
209 //printf("Scope fragment %s\n",s.mid(i,l).data());
210 if (Doxygen::namespaceSDict->find(s.left(i+l))!=0)
211 {
212 if (s.at(i)!='@')
213 {
214 if (!newScope.isEmpty()) newScope+="::";
215 newScope+=s.mid(i,l);
216 }
217 }
218 else
219 {
220 if (!newScope.isEmpty()) newScope+="::";
221 newScope+=s.right(s.length()-i);
222 goto done;
223 }
224 p=i+l;
225 }
226done:
227 //printf("stripAnonymousNamespaceScope(`%s')=`%s'\n",s.data(),newScope.data());
228 return newScope;
229}
230
231void writePageRef(OutputDocInterface &od,const char *cn,const char *mn)
232{
233 od.pushGeneratorState();
234
235 od.disable(OutputGenerator::Html);
236 od.disable(OutputGenerator::Man);
237 if (Config_getBool("PDF_HYPERLINKS")) od.disable(OutputGenerator::Latex);
238 if (Config_getBool("RTF_HYPERLINKS")) od.disable(OutputGenerator::RTF);
239 od.startPageRef();
240 od.docify(theTranslator->trPageAbbreviation());
241 od.endPageRef(cn,mn);
242
243 od.popGeneratorState();
244}
245
246/*! Generate a place holder for a position in a list. Used for
247 * translators to be able to specify different elements orders
248 * depending on whether text flows from left to right or visa versa.
249 */
250QCString generateMarker(int id)
251{
252 QCString result;
253 result.sprintf("@%d",id);
254 return result;
255}
256
257static QCString stripFromPath(const QCString &path,QStrList &l)
258{
259 // look at all the strings in the list and strip the longest match
260 const char *s=l.first();
261 QCString potential;
262 unsigned int length = 0;
263 while (s)
264 {
265 QCString prefix = s;
266 if (prefix.length() > length &&
267 stricmp(path.left(prefix.length()),prefix)==0) // case insensitive compare
268 {
269 length = prefix.length();
270 potential = path.right(path.length()-prefix.length());
271 }
272 s = l.next();
273 }
274 if (length) return potential;
275 return path;
276}
277
278/*! strip part of \a path if it matches
279 * one of the paths in the Config_getList("STRIP_FROM_PATH") list
280 */
281QCString stripFromPath(const QCString &path)
282{
283 return stripFromPath(path,Config_getList("STRIP_FROM_PATH"));
284}
285
286/*! strip part of \a path if it matches
287 * one of the paths in the Config_getList("INCLUDE_PATH") list
288 */
289QCString stripFromIncludePath(const QCString &path)
290{
291 return stripFromPath(path,Config_getList("STRIP_FROM_INC_PATH"));
292}
293
294/*! try to determine if \a name is a source or a header file name by looking
295 * at the extension. A number of variations is allowed in both upper and
296 * lower case) If anyone knows or uses another extension please let me know :-)
297 */
298int guessSection(const char *name)
299{
300 QCString n=((QCString)name).lower();
301 if (n.right(2)==".c" || // source
302 n.right(3)==".cc" ||
303 n.right(4)==".cxx" ||
304 n.right(4)==".cpp" ||
305 n.right(4)==".c++" ||
306 n.right(5)==".java" ||
307 n.right(3)==".ii" || // inline
308 n.right(4)==".ixx" ||
309 n.right(4)==".ipp" ||
310 n.right(4)==".i++" ||
311 n.right(4)==".inl" ||
312 n.right(4)==".xml"
313 ) return Entry::SOURCE_SEC;
314 if (n.right(2)==".h" || // header
315 n.right(3)==".hh" ||
316 n.right(4)==".hxx" ||
317 n.right(4)==".hpp" ||
318 n.right(4)==".h++" ||
319 n.right(4)==".idl" ||
320 n.right(4)==".ddl" ||
321 n.right(5)==".pidl"
322 ) return Entry::HEADER_SEC;
323 return 0;
324}
325
326QCString resolveTypeDef(Definition *context,const QCString &qualifiedName,
327 Definition **typedefContext)
328{
329 //printf("<<resolveTypeDef(%s,%s)\n",
330 // context ? context->name().data() : "<none>",qualifiedName.data());
331 QCString result;
332 if (qualifiedName.isEmpty())
333 {
334 //printf(" qualified name empty!\n");
335 return result;
336 }
337
338 Definition *mContext=context;
339 if (typedefContext) *typedefContext=context;
340
341 // see if the qualified name has a scope part
342 int scopeIndex = qualifiedName.findRev("::");
343 QCString resName=qualifiedName;
344 if (scopeIndex!=-1) // strip scope part for the name
345 {
346 resName=qualifiedName.right(qualifiedName.length()-scopeIndex-2);
347 if (resName.isEmpty())
348 {
349 // qualifiedName was of form A:: !
350 //printf(" qualified name of form A::!\n");
351 return result;
352 }
353 }
354 MemberDef *md=0;
355 while (mContext && md==0)
356 {
357 // step 1: get the right scope
358 Definition *resScope=mContext;
359 if (scopeIndex!=-1)
360 {
361 // split-off scope part
362 QCString resScopeName = qualifiedName.left(scopeIndex);
363 //printf("resScopeName=`%s'\n",resScopeName.data());
364
365 // look-up scope in context
366 int is,ps=0;
367 int l;
368 while ((is=getScopeFragment(resScopeName,ps,&l))!=-1)
369 {
370 QCString qualScopePart = resScopeName.mid(is,l);
371 QCString tmp = resolveTypeDef(mContext,qualScopePart);
372 if (!tmp.isEmpty()) qualScopePart=tmp;
373 resScope = resScope->findInnerCompound(qualScopePart);
374 //printf("qualScopePart=`%s' resScope=%p\n",qualScopePart.data(),resScope);
375 if (resScope==0) break;
376 ps=is+l;
377 }
378 }
379 //printf("resScope=%s\n",resScope?resScope->name().data():"<none>");
380
381 // step 2: get the member
382 if (resScope) // no scope or scope found in the current context
383 {
384 //printf("scope found: %s, look for typedef %s\n",
385 // resScope->qualifiedName().data(),resName.data());
386 MemberNameSDict *mnd=0;
387 if (resScope->definitionType()==Definition::TypeClass)
388 {
389 mnd=Doxygen::memberNameSDict;
390 }
391 else
392 {
393 mnd=Doxygen::functionNameSDict;
394 }
395 MemberName *mn=mnd->find(resName);
396 if (mn)
397 {
398 MemberNameIterator mni(*mn);
399 MemberDef *tmd=0;
400 int minDist=-1;
401 for (;(tmd=mni.current());++mni)
402 {
403 //printf("Found member %s resScope=%s outerScope=%s mContext=%p\n",
404 // tmd->name().data(), resScope->name().data(),
405 // tmd->getOuterScope()->name().data(), mContext);
406 if (tmd->isTypedef() /*&& tmd->getOuterScope()==resScope*/)
407 {
408 int dist=isAccessibleFrom(resScope,0,tmd);
409 if (dist!=-1 && (md==0 || dist<minDist))
410 {
411 md = tmd;
412 minDist = dist;
413 }
414 }
415 }
416 }
417 }
418 mContext=mContext->getOuterScope();
419 }
420
421 // step 3: get the member's type
422 if (md)
423 {
424 //printf(">>resolveTypeDef: Found typedef name `%s' in scope `%s' value=`%s' args='%s'\n",
425 // qualifiedName.data(),context->name().data(),md->typeString(),md->argsString()
426 // );
427 result=md->typeString();
428 QString args = md->argsString();
429 if (args.find(")(")!=-1) // typedef of a function/member pointer
430 {
431 result+=args;
432 }
433 else if (args.find('[')!=-1) // typedef of an array
434 {
435 result+=args;
436 }
437 if (typedefContext) *typedefContext=md->getOuterScope();
438 }
439 else
440 {
441 //printf(">>resolveTypeDef: Typedef `%s' not found in scope `%s'!\n",
442 // qualifiedName.data(),context ? context->name().data() : "<global>");
443 }
444 return result;
445
446}
447
448
449/*! Get a class definition given its name.
450 * Returns 0 if the class is not found.
451 */
452ClassDef *getClass(const char *name)
453{
454 if (name==0 || name[0]=='\0') return 0;
455 return Doxygen::classSDict->find(name);
456}
457
458NamespaceDef *getResolvedNamespace(const char *name)
459{
460 if (name==0 || name[0]=='\0') return 0;
461 QCString *subst = Doxygen::namespaceAliasDict[name];
462 if (subst)
463 {
464 int count=0; // recursion detection guard
465 QCString *newSubst;
466 while ((newSubst=Doxygen::namespaceAliasDict[*subst]) && count<10)
467 {
468 subst=newSubst;
469 count++;
470 }
471 if (count==10)
472 {
473 err("warning: possible recursive namespace alias detected for %s!\n",name);
474 }
475 return Doxygen::namespaceSDict->find(subst->data());
476 }
477 else
478 {
479 return Doxygen::namespaceSDict->find(name);
480 }
481}
482
483static QDict<MemberDef> g_resolvedTypedefs;
484static QDict<Definition> g_visitedNamespaces;
485
486// forward declaration
487ClassDef *getResolvedClassRec(Definition *scope,
488 FileDef *fileScope,
489 const char *n,
490 MemberDef **pTypeDef,
491 QCString *pTemplSpec,
492 QCString *pResolvedType
493 );
494int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,Definition *item,
495 const QCString &explicitScopePart);
496
497/*! Returns the class representing the value of the typedef represented by \a md
498 * within file \a fileScope.
499 *
500 * Example: typedef A T; will return the class representing A if it is a class.
501 *
502 * Example: typedef int T; will return 0, since "int" is not a class.
503 */
504ClassDef *newResolveTypedef(FileDef *fileScope,MemberDef *md,
505 MemberDef **pMemType,QCString *pTemplSpec,
506 QCString *pResolvedType,
507 ArgumentList *actTemplParams)
508{
509 //printf("newResolveTypedef(md=%p,cachedVal=%p)\n",md,md->getCachedTypedefVal());
510 bool isCached = md->isTypedefValCached(); // value already cached
511 if (isCached)
512 {
513 //printf("Already cached %s->%s [%s]\n",
514 // md->name().data(),
515 // md->getCachedTypedefVal()?md->getCachedTypedefVal()->name().data():"<none>",
516 // md->getCachedResolvedTypedef()?md->getCachedResolvedTypedef().data():"<none>");
517
518 if (pTemplSpec) *pTemplSpec = md->getCachedTypedefTemplSpec();
519 if (pResolvedType) *pResolvedType = md->getCachedResolvedTypedef();
520 return md->getCachedTypedefVal();
521 }
522 //printf("new typedef\n");
523 QCString qname = md->qualifiedName();
524 if (g_resolvedTypedefs.find(qname)) return 0; // typedef already done
525
526 g_resolvedTypedefs.insert(qname,md); // put on the trace list
527
528 ClassDef *typeClass = md->getClassDef();
529 QCString type = md->typeString(); // get the "value" of the typedef
530 if (typeClass && typeClass->isTemplate() &&
531 actTemplParams && actTemplParams->count()>0)
532 {
533 type = substituteTemplateArgumentsInString(type,
534 typeClass->templateArguments(),actTemplParams);
535 }
536 QCString typedefValue = type;
537 int tl=type.length();
538 int ip=tl-1; // remove * and & at the end
539 while (ip>=0 && (type.at(ip)=='*' || type.at(ip)=='&' || type.at(ip)==' '))
540 {
541 ip--;
542 }
543 type=type.left(ip+1);
544 type.stripPrefix("const "); // strip leading "const"
545 type.stripPrefix("struct "); // strip leading "struct"
546 type.stripPrefix("union "); // strip leading "union"
547 int sp=0;
548 tl=type.length(); // length may have been changed
549 while (sp<tl && type.at(sp)==' ') sp++;
550 MemberDef *memTypeDef = 0;
551 ClassDef *result = getResolvedClassRec(md->getOuterScope(),
552 fileScope,type,&memTypeDef,0,pResolvedType);
553 // if type is a typedef then return what it resolves to.
554 if (memTypeDef && memTypeDef->isTypedef())
555 {
556 result=newResolveTypedef(fileScope,memTypeDef,pMemType,pTemplSpec);
557 goto done;
558 }
559 else if (memTypeDef && memTypeDef->isEnumerate() && pMemType)
560 {
561 *pMemType = memTypeDef;
562 }
563
564 //printf("type=%s result=%p\n",type.data(),result);
565 if (result==0)
566 {
567 // try unspecialized version if type is template
568 int si=type.findRev("::");
569 int i=type.find('<');
570 if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version
571 {
572 if (pTemplSpec) *pTemplSpec = type.mid(i);
573 result = getResolvedClassRec(md->getOuterScope(),fileScope,
574 type.left(i),0,0,pResolvedType);
575 //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n",
576 // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl);
577 }
578 else if (si!=-1) // A::B
579 {
580 i=type.find('<',si);
581 if (i==-1) // Something like A<T>::B => lookup A::B
582 {
583 i=type.length();
584 }
585 else // Something like A<T>::B<S> => lookup A::B, spec=<S>
586 {
587 if (pTemplSpec) *pTemplSpec = type.mid(i);
588 }
589 result = getResolvedClassRec(md->getOuterScope(),fileScope,
590 stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,
591 pResolvedType);
592 }
593
594 //if (result) ip=si+sp+1;
595 }
596
597done:
598 if (pResolvedType)
599 {
600 if (result)
601 {
602 *pResolvedType=result->qualifiedName();
603 //printf("*pResolvedType=%s\n",pResolvedType->data());
604 if (sp>0) pResolvedType->prepend(typedefValue.left(sp));
605 if (ip<tl-1) pResolvedType->append(typedefValue.right(tl-ip-1));
606 }
607 else
608 {
609 *pResolvedType=typedefValue;
610 }
611 }
612
613 // remember computed value for next time
614 if (result && result->getDefFileName()!="<code>")
615 // this check is needed to prevent that temporary classes that are
616 // introduced while parsing code fragments are being cached here.
617 {
618 //printf("setting cached typedef %p in result %p\n",md,result);
619 //printf("==> %s (%s,%d)\n",result->name().data(),result->getDefFileName().data(),result->getDefLine());
620 //printf("*pResolvedType=%s\n",pResolvedType?pResolvedType->data():"<none>");
621 md->cacheTypedefVal(result,
622 pTemplSpec ? *pTemplSpec : QCString(),
623 pResolvedType ? *pResolvedType : QCString()
624 );
625 }
626
627 g_resolvedTypedefs.remove(qname); // remove from the trace list
628
629 return result;
630}
631
632/*! Substitutes a simple unqualified \a name within \a scope. Returns the
633 * value of the typedef or \a name if no typedef was found.
634 */
635static QCString substTypedef(Definition *scope,FileDef *fileScope,const QCString &name,
636 MemberDef **pTypeDef=0)
637{
638 QCString result=name;
639 if (name.isEmpty()) return result;
640
641 // lookup scope fragment in the symbol map
642 DefinitionIntf *di = Doxygen::symbolMap->find(name);
643 if (di==0) return result; // no matches
644
645 MemberDef *bestMatch=0;
646 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multi symbols
647 {
648 // search for the best match
649 DefinitionListIterator dli(*(DefinitionList*)di);
650 Definition *d;
651 int minDistance=10000; // init at "infinite"
652 for (dli.toFirst();(d=dli.current());++dli) // foreach definition
653 {
654 // only look at members
655 if (d->definitionType()==Definition::TypeMember)
656 {
657 // that are also typedefs
658 MemberDef *md = (MemberDef *)d;
659 if (md->isTypedef()) // d is a typedef
660 {
661 // test accessibility of typedef within scope.
662 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
663 if (distance!=-1 && distance<minDistance)
664 // definition is accessible and a better match
665 {
666 minDistance=distance;
667 bestMatch = md;
668 }
669 }
670 }
671 }
672 }
673 else if (di->definitionType()==DefinitionIntf::TypeMember) // single symbol
674 {
675 Definition *d = (Definition*)di;
676 // that are also typedefs
677 MemberDef *md = (MemberDef *)di;
678 if (md->isTypedef()) // d is a typedef
679 {
680 // test accessibility of typedef within scope.
681 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,"");
682 if (distance!=-1) // definition is accessible
683 {
684 bestMatch = md;
685 }
686 }
687 }
688 if (bestMatch)
689 {
690 result = bestMatch->typeString();
691 if (pTypeDef) *pTypeDef=bestMatch;
692 }
693
694 //printf("substTypedef(%s,%s)=%s\n",scope?scope->name().data():"<global>",
695 // name.data(),result.data());
696 return result;
697}
698
699static Definition *endOfPathIsUsedClass(SDict<Definition> *cl,const QCString &localName)
700{
701 if (cl)
702 {
703 SDict<Definition>::Iterator cli(*cl);
704 Definition *cd;
705 for (cli.toFirst();(cd=cli.current());++cli)
706 {
707 if (cd->localName()==localName)
708 {
709 return cd;
710 }
711 }
712 }
713 return 0;
714}
715
716/*! Starting with scope \a start, the string \a path is interpreted as
717 * a part of a qualified scope name (e.g. A::B::C), and the scope is
718 * searched. If found the scope definition is returned, otherwise 0
719 * is returned.
720 */
721static Definition *followPath(Definition *start,FileDef *fileScope,const QCString &path)
722{
723 int is,ps;
724 int l;
725 Definition *current=start;
726 ps=0;
727 //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"<none>",path.data());
728 // for each part of the explicit scope
729 while ((is=getScopeFragment(path,ps,&l))!=-1)
730 {
731 // try to resolve the part if it is a typedef
732 MemberDef *typeDef=0;
733 QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef);
734 //printf(" qualScopePart=%s\n",qualScopePart.data());
735 if (typeDef)
736 {
737 ClassDef *type = newResolveTypedef(fileScope,typeDef);
738 if (type)
739 {
740 //printf("Found type %s\n",type->name().data());
741 return type;
742 }
743 }
744 Definition *next = current->findInnerCompound(qualScopePart);
745 //printf("++ Looking for %s inside %s result %s\n",
746 // qualScopePart.data(),
747 // current->name().data(),
748 // next?next->name().data():"<null>");
749 if (next==0) // failed to follow the path
750 {
751 //printf("==> next==0!\n");
752 if (current->definitionType()==Definition::TypeNamespace)
753 {
754 next = endOfPathIsUsedClass(
755 ((NamespaceDef *)current)->getUsedClasses(),qualScopePart);
756 }
757 else if (current->definitionType()==Definition::TypeFile)
758 {
759 next = endOfPathIsUsedClass(
760 ((FileDef *)current)->getUsedClasses(),qualScopePart);
761 }
762 current = next;
763 if (current==0) break;
764 }
765 else // continue to follow scope
766 {
767 current = next;
768 //printf("==> current = %p\n",current);
769 }
770 ps=is+l;
771 }
772 //printf("followPath(start=%s,path=%s) result=%s\n",
773 // start->name().data(),path.data(),current?current->name().data():"<null>");
774 return current; // path could be followed
775}
776
777bool accessibleViaUsingClass(const SDict<Definition> *cl,
778 FileDef *fileScope,
779 Definition *item,
780 const QCString &explicitScopePart=""
781 )
782{
783 //printf("accessibleViaUsingClass(%p)\n",cl);
784 if (cl) // see if the class was imported via a using statement
785 {
786 SDict<Definition>::Iterator cli(*cl);
787 Definition *ucd;
788 bool explicitScopePartEmpty = explicitScopePart.isEmpty();
789 for (cli.toFirst();(ucd=cli.current());++cli)
790 {
791 //printf("Trying via used class %s\n",ucd->name().data());
792 Definition *sc = explicitScopePartEmpty ? ucd : followPath(ucd,fileScope,explicitScopePart);
793 if (sc && sc==item) return TRUE;
794 //printf("Try via used class done\n");
795 }
796 }
797 return FALSE;
798}
799
800bool accessibleViaUsingNamespace(const NamespaceSDict *nl,
801 FileDef *fileScope,
802 Definition *item,
803 const QCString &explicitScopePart="")
804{
805 static QDict<void> visitedDict;
806 if (nl) // check used namespaces for the class
807 {
808 NamespaceSDict::Iterator nli(*nl);
809 NamespaceDef *und;
810 int count=0;
811 for (nli.toFirst();(und=nli.current());++nli,count++)
812 {
813 //printf("[Trying via used namespace %s: count=%d/%d\n",und->name().data(),
814 // count,nl->count());
815 Definition *sc = explicitScopePart.isEmpty() ? und : followPath(und,fileScope,explicitScopePart);
816 if (sc && item->getOuterScope()==sc)
817 {
818 //printf("] found it\n");
819 return TRUE;
820 }
821 QCString key=und->name();
822 if (und->getUsedNamespaces() && visitedDict.find(key)==0)
823 {
824 visitedDict.insert(key,(void *)0x08);
825
826 if (accessibleViaUsingNamespace(und->getUsedNamespaces(),fileScope,item,explicitScopePart))
827 {
828 //printf("] found it via recursion\n");
829 return TRUE;
830 }
831
832 visitedDict.remove(key);
833 }
834 //printf("] Try via used namespace done\n");
835 }
836 }
837 return FALSE;
838}
839
840
841/* Returns the "distance" (=number of levels up) from item to scope, or -1
842 * if item in not inside scope.
843 */
844int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item)
845{
846 //printf("<isAccesibleFrom(scope=%s,item=%s itemScope=%s)\n",
847 // scope->name().data(),item->name().data(),item->getOuterScope()->name().data());
848
849 QCString key(40);
850 key.sprintf("%p:%p:%p",scope,fileScope,item);
851 static QDict<void> visitedDict;
852 if (visitedDict.find(key))
853 {
854 //printf("> already found\n");
855 return -1; // already looked at this
856 }
857 visitedDict.insert(key,(void *)0x8);
858
859 int result=0; // assume we found it
860 int i;
861
862 Definition *itemScope=item->getOuterScope();
863
864 if (
865 itemScope==scope || // same thing
866 (item->definitionType()==Definition::TypeMember && // a member
867 itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class
868 scope->definitionType()==Definition::TypeClass && // accessible
869 ((ClassDef*)scope)->isAccessibleMember((MemberDef *)item) // from scope
870 ) ||
871 (item->definitionType()==Definition::TypeClass && // a nested class
872 itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base
873 scope->definitionType()==Definition::TypeClass && // class of scope
874 ((ClassDef*)scope)->isBaseClass((ClassDef*)itemScope,TRUE)
875 )
876 )
877 {
878 //printf("> found it\n");
879 }
880 else if (scope==Doxygen::globalScope)
881 {
882 if (fileScope)
883 {
884 SDict<Definition> *cl = fileScope->getUsedClasses();
885 if (accessibleViaUsingClass(cl,fileScope,item))
886 {
887 //printf("> found via used class\n");
888 goto done;
889 }
890 NamespaceSDict *nl = fileScope->getUsedNamespaces();
891 if (accessibleViaUsingNamespace(nl,fileScope,item))
892 {
893 //printf("> found via used namespace\n");
894 goto done;
895 }
896 }
897 //printf("> reached global scope\n");
898 result=-1; // not found in path to globalScope
899 }
900 else // keep searching
901 {
902 // check if scope is a namespace, which is using other classes and namespaces
903 if (scope->definitionType()==Definition::TypeNamespace)
904 {
905 NamespaceDef *nscope = (NamespaceDef*)scope;
906 //printf(" %s is namespace with %d used classes\n",nscope->name().data(),nscope->getUsedClasses());
907 SDict<Definition> *cl = nscope->getUsedClasses();
908 if (accessibleViaUsingClass(cl,fileScope,item))
909 {
910 //printf("> found via used class\n");
911 goto done;
912 }
913 NamespaceSDict *nl = nscope->getUsedNamespaces();
914 if (accessibleViaUsingNamespace(nl,fileScope,item))
915 {
916 //printf("> found via used namespace\n");
917 goto done;
918 }
919 }
920 // repeat for the parent scope
921 i=isAccessibleFrom(scope->getOuterScope(),fileScope,item);
922 //printf("> result=%d\n",i);
923 result= (i==-1) ? -1 : i+2;
924 }
925done:
926 visitedDict.remove(key);
927 //Doxygen::lookupCache.insert(key,new int(result));
928 return result;
929}
930
931
932/* Returns the "distance" (=number of levels up) from item to scope, or -1
933 * if item in not in this scope. The explicitScopePart limits the search
934 * to scopes that match \a scope (or its parent scope(s)) plus the explicit part.
935 * Example:
936 *
937 * class A { public: class I {}; };
938 * class B { public: class J {}; };
939 *
940 * - Looking for item=='J' inside scope=='B' will return 0.
941 * - Looking for item=='I' inside scope=='B' will return -1
942 * (as it is not found in B nor in the global scope).
943 * - Looking for item=='A::I' inside scope=='B', first the match B::A::I is tried but
944 * not found and then A::I is searched in the global scope, which matches and
945 * thus the result is 1.
946 */
947int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope,
948 Definition *item,const QCString &explicitScopePart)
949{
950 if (explicitScopePart.isEmpty())
951 {
952 // handle degenerate case where there is no explicit scope.
953 return isAccessibleFrom(scope,fileScope,item);
954 }
955
956 QCString key(40+explicitScopePart.length());
957 key.sprintf("%p:%p:%p:%s",scope,fileScope,item,explicitScopePart.data());
958 static QDict<void> visitedDict;
959 if (visitedDict.find(key))
960 {
961 //printf("Already visited!\n");
962 return -1; // already looked at this
963 }
964 visitedDict.insert(key,(void *)0x8);
965
966 //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>",
967 // item?item->name().data():"<none>",
968 // explicitScopePart.data());
969 int result=0; // assume we found it
970 Definition *newScope = followPath(scope,fileScope,explicitScopePart);
971 if (newScope) // explicitScope is inside scope => newScope is the result
972 {
973 Definition *itemScope = item->getOuterScope();
974 //printf(" scope traversal successful %s<->%s!\n",itemScope->name().data(),newScope->name().data());
975 //if (newScope && newScope->definitionType()==Definition::TypeClass)
976 //{
977 // ClassDef *cd = (ClassDef *)newScope;
978 // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses());
979 //}
980 if (itemScope==newScope) // exact match of scopes => distance==0
981 {
982 //printf("> found it\n");
983 }
984 else if (itemScope && newScope &&
985 itemScope->definitionType()==Definition::TypeClass &&
986 newScope->definitionType()==Definition::TypeClass &&
987 ((ClassDef*)newScope)->isBaseClass((ClassDef*)itemScope,TRUE,0)
988 )
989 {
990 // inheritance is also ok. Example: looking for B::I, where
991 // class A { public: class I {} };
992 // class B : public A {}
993 // but looking for B::I, where
994 // class A { public: class I {} };
995 // class B { public: class I {} };
996 // will find A::I, so we still prefer a direct match and give this one a distance of 1
997 result=1;
998
999 //printf("scope(%s) is base class of newScope(%s)\n",
1000 // scope->name().data(),newScope->name().data());
1001 }
1002 else
1003 {
1004 int i=-1;
1005 if (newScope->definitionType()==Definition::TypeNamespace)
1006 {
1007 g_visitedNamespaces.insert(newScope->name(),newScope);
1008 // this part deals with the case where item is a class
1009 // A::B::C but is explicit referenced as A::C, where B is imported
1010 // in A via a using directive.
1011 //printf("newScope is a namespace: %s!\n",newScope->name().data());
1012 NamespaceDef *nscope = (NamespaceDef*)newScope;
1013 SDict<Definition> *cl = nscope->getUsedClasses();
1014 if (cl)
1015 {
1016 SDict<Definition>::Iterator cli(*cl);
1017 Definition *cd;
1018 for (cli.toFirst();(cd=cli.current());++cli)
1019 {
1020 //printf("Trying for class %s\n",cd->name().data());
1021 if (cd==item)
1022 {
1023 //printf("> class is used in this scope\n");
1024 goto done;
1025 }
1026 }
1027 }
1028 NamespaceSDict *nl = nscope->getUsedNamespaces();
1029 if (nl)
1030 {
1031 NamespaceSDict::Iterator nli(*nl);
1032 NamespaceDef *nd;
1033 for (nli.toFirst();(nd=nli.current());++nli)
1034 {
1035 if (g_visitedNamespaces.find(nd->name())==0)
1036 {
1037 //printf("Trying for namespace %s\n",nd->name().data());
1038 i = isAccessibleFromWithExpScope(scope,fileScope,item,nd->name());
1039 if (i!=-1)
1040 {
1041 //printf("> found via explicit scope of used namespace\n");
1042 goto done;
1043 }
1044 }
1045 }
1046 }
1047 }
1048 // repeat for the parent scope
1049 if (scope!=Doxygen::globalScope)
1050 {
1051 i = isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1052 item,explicitScopePart);
1053 }
1054 //printf(" | result=%d\n",i);
1055 result = (i==-1) ? -1 : i+2;
1056 }
1057 }
1058 else // failed to resolve explicitScope
1059 {
1060 //printf(" failed to resolve: scope=%s\n",scope->name().data());
1061 if (scope->definitionType()==Definition::TypeNamespace)
1062 {
1063 NamespaceDef *nscope = (NamespaceDef*)scope;
1064 NamespaceSDict *nl = nscope->getUsedNamespaces();
1065 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1066 {
1067 //printf("> found in used namespace\n");
1068 goto done;
1069 }
1070 }
1071 if (scope==Doxygen::globalScope)
1072 {
1073 if (fileScope)
1074 {
1075 NamespaceSDict *nl = fileScope->getUsedNamespaces();
1076 if (accessibleViaUsingNamespace(nl,fileScope,item,explicitScopePart))
1077 {
1078 //printf("> found in used namespace\n");
1079 goto done;
1080 }
1081 }
1082 //printf("> not found\n");
1083 result=-1;
1084 }
1085 else // continue by looking into the parent scope
1086 {
1087 int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope,
1088 item,explicitScopePart);
1089 //printf("> result=%d\n",i);
1090 result= (i==-1) ? -1 : i+2;
1091 }
1092 }
1093done:
1094 //printf(" > result=%d\n",result);
1095 visitedDict.remove(key);
1096 //Doxygen::lookupCache.insert(key,new int(result));
1097 return result;
1098}
1099
1100int computeQualifiedIndex(const QCString &name)
1101{
1102 int i = name.find('<');
1103 return name.findRev("::",i==-1 ? name.length() : i);
1104}
1105
1106static void getResolvedSymbol(Definition *scope,
1107 FileDef *fileScope,
1108 Definition *d,
1109 const QCString &explicitScopePart,
1110 ArgumentList *actTemplParams,
1111 int &minDistance,
1112 ClassDef *&bestMatch,
1113 MemberDef *&bestTypedef,
1114 QCString &bestTemplSpec,
1115 QCString &bestResolvedType
1116 )
1117{
1118 //printf(" => found type %x name=%s d=%p\n",
1119 // d->definitionType(),d->name().data(),d);
1120
1121 // only look at classes and members that are enums or typedefs
1122 if (d->definitionType()==Definition::TypeClass ||
1123 (d->definitionType()==Definition::TypeMember &&
1124 (((MemberDef*)d)->isTypedef() || ((MemberDef*)d)->isEnumerate())
1125 )
1126 )
1127 {
1128 g_visitedNamespaces.clear();
1129 // test accessibility of definition within scope.
1130 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
1131 //printf(" %s; distance %s (%p) is %d\n",scope->name().data(),d->name().data(),d,distance);
1132 if (distance!=-1) // definition is accessible
1133 {
1134 // see if we are dealing with a class or a typedef
1135 if (d->definitionType()==Definition::TypeClass) // d is a class
1136 {
1137 ClassDef *cd = (ClassDef *)d;
1138 //printf("cd=%s\n",cd->name().data());
1139 if (!cd->isTemplateArgument()) // skip classes that
1140 // are only there to
1141 // represent a template
1142 // argument
1143 {
1144 //printf("is not a templ arg\n");
1145 if (distance<minDistance) // found a definition that is "closer"
1146 {
1147 minDistance=distance;
1148 bestMatch = cd;
1149 bestTypedef = 0;
1150 bestTemplSpec.resize(0);
1151 bestResolvedType = cd->qualifiedName();
1152 }
1153 else if (distance==minDistance &&
1154 fileScope && bestMatch &&
1155 fileScope->getUsedNamespaces() &&
1156 d->getOuterScope()->definitionType()==Definition::TypeNamespace &&
1157 bestMatch->getOuterScope()==Doxygen::globalScope
1158 )
1159 {
1160 // in case the distance is equal it could be that a class X
1161 // is defined in a namespace and in the global scope. When searched
1162 // in the global scope the distance is 0 in both cases. We have
1163 // to choose one of the definitions: we choose the one in the
1164 // namespace if the fileScope imports namespaces and the definition
1165 // found was in a namespace while the best match so far isn't.
1166 // Just a non-perfect heuristic but it could help in some situations
1167 // (kdecore code is an example).
1168 minDistance=distance;
1169 bestMatch = cd;
1170 bestTypedef = 0;
1171 bestTemplSpec.resize(0);
1172 bestResolvedType = cd->qualifiedName();
1173 }
1174 }
1175 else
1176 {
1177 //printf(" is a template argument!\n");
1178 }
1179 }
1180 else if (d->definitionType()==Definition::TypeMember)
1181 {
1182 MemberDef *md = (MemberDef *)d;
1183 //printf(" member isTypedef()=%d\n",md->isTypedef());
1184 if (md->isTypedef()) // d is a typedef
1185 {
1186 QCString args=md->argsString();
1187 if (args.isEmpty()) // do not expand "typedef t a[4];"
1188 {
1189 //printf(" found typedef!\n");
1190
1191 // we found a symbol at this distance, but if it didn't
1192 // resolve to a class, we still have to make sure that
1193 // something at a greater distance does not match, since
1194 // that symbol is hidden by this one.
1195 if (distance<minDistance)
1196 {
1197 QCString spec;
1198 QCString type;
1199 minDistance=distance;
1200 MemberDef *enumType = 0;
1201 ClassDef *cd = newResolveTypedef(fileScope,md,&enumType,&spec,&type,actTemplParams);
1202 if (cd) // type resolves to a class
1203 {
1204 //printf(" bestTypeDef=%p spec=%s type=%s\n",md,spec.data(),type.data());
1205 bestMatch = cd;
1206 bestTypedef = md;
1207 bestTemplSpec = spec;
1208 bestResolvedType = type;
1209 }
1210 else if (enumType) // type resolves to a enum
1211 {
1212 //printf(" is enum\n");
1213 bestMatch = 0;
1214 bestTypedef = enumType;
1215 bestTemplSpec = "";
1216 bestResolvedType = enumType->qualifiedName();
1217 }
1218 else if (md->isReference()) // external reference
1219 {
1220 bestMatch = 0;
1221 bestTypedef = md;
1222 bestTemplSpec = spec;
1223 bestResolvedType = type;
1224 }
1225 else
1226 {
1227 bestMatch = 0;
1228 bestTypedef = md;
1229 bestTemplSpec.resize(0);
1230 bestResolvedType.resize(0);
1231 //printf(" no match\n");
1232 }
1233 }
1234 else
1235 {
1236 //printf(" not the best match %d min=%d\n",distance,minDistance);
1237 }
1238 }
1239 else
1240 {
1241 //printf(" not a simple typedef\n")
1242 }
1243 }
1244 else if (md->isEnumerate())
1245 {
1246 if (distance<minDistance)
1247 {
1248 minDistance=distance;
1249 bestMatch = 0;
1250 bestTypedef = md;
1251 bestTemplSpec = "";
1252 bestResolvedType = md->qualifiedName();
1253 }
1254 }
1255 }
1256 } // if definition accessible
1257 else
1258 {
1259 //printf(" Not accessible!\n");
1260 }
1261 } // if definition is a class or member
1262 //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,bestResolvedType.data());
1263}
1264
1265/* Find the fully qualified class name referred to by the input class
1266 * or typedef name against the input scope.
1267 * Loops through scope and each of its parent scopes looking for a
1268 * match against the input name. Can recursively call itself when
1269 * resolving typedefs.
1270 */
1271ClassDef *getResolvedClassRec(Definition *scope,
1272 FileDef *fileScope,
1273 const char *n,
1274 MemberDef **pTypeDef,
1275 QCString *pTemplSpec,
1276 QCString *pResolvedType
1277 )
1278{
1279 //printf("[getResolvedClassRec(%s,%s)\n",scope?scope->name().data():"<global>",n);
1280 QCString name;
1281 QCString explicitScopePart;
1282 QCString strippedTemplateParams;
1283 name=stripTemplateSpecifiersFromScope
1284 (removeRedundantWhiteSpace(n),TRUE,
1285 &strippedTemplateParams);
1286 ArgumentList actTemplParams;
1287 if (!strippedTemplateParams.isEmpty()) // template part that was stripped
1288 {
1289 stringToArgumentList(strippedTemplateParams,&actTemplParams);
1290 }
1291
1292 int qualifierIndex = computeQualifiedIndex(name);
1293 //printf("name=%s qualifierIndex=%d\n",name.data(),qualifierIndex);
1294 if (qualifierIndex!=-1) // qualified name
1295 {
1296 // split off the explicit scope part
1297 explicitScopePart=name.left(qualifierIndex);
1298 // todo: improve namespace alias substitution
1299 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
1300 name=name.mid(qualifierIndex+2);
1301 }
1302
1303 if (name.isEmpty())
1304 {
1305 //printf("] empty name\n");
1306 return 0; // empty name
1307 }
1308
1309 //printf("Looking for symbol %s\n",name.data());
1310 DefinitionIntf *di = Doxygen::symbolMap->find(name);
1311 if (di==0)
1312 {
1313 di = Doxygen::symbolMap->find(name+"-g");
1314 if (di==0)
1315 {
1316 di = Doxygen::symbolMap->find(name+"-p");
1317 if (di==0)
1318 {
1319 //printf("no such symbol!\n");
1320 return 0;
1321 }
1322 }
1323 }
1324 //printf("found symbol!\n");
1325
1326 bool hasUsingStatements =
1327 (fileScope && ((fileScope->getUsedNamespaces() &&
1328 fileScope->getUsedNamespaces()->count()>0) ||
1329 (fileScope->getUsedClasses() &&
1330 fileScope->getUsedClasses()->count()>0))
1331 );
1332 //printf("hasUsingStatements=%d\n",hasUsingStatements);
1333 // Since it is often the case that the same name is searched in the same
1334 // scope over an over again (especially for the linked source code generation)
1335 // we use a cache to collect previous results. This is possible since the
1336 // result of a lookup is deterministic. As the key we use the concatenated
1337 // scope, the name to search for and the explicit scope prefix. The speedup
1338 // achieved by this simple cache can be enormous.
1339 int scopeNameLen = scope->name().length()+1;
1340 int nameLen = name.length()+1;
1341 int explicitPartLen = explicitScopePart.length();
1342 int fileScopeLen = hasUsingStatements ? 1+fileScope->absFilePath().length() : 0;
1343
1344 // below is a more efficient coding of
1345 // QCString key=scope->name()+"+"+name+"+"+explicitScopePart;
1346 QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1);
1347 char *p=key.data();
1348 qstrcpy(p,scope->name()); *(p+scopeNameLen-1)='+';
1349 p+=scopeNameLen;
1350 qstrcpy(p,name); *(p+nameLen-1)='+';
1351 p+=nameLen;
1352 qstrcpy(p,explicitScopePart);
1353 p+=explicitPartLen;
1354
1355 // if a file scope is given and it contains using statements we should
1356 // also use the file part in the key (as a class name can be in
1357 // two different namespaces and a using statement in a file can select
1358 // one of them).
1359 if (hasUsingStatements)
1360 {
1361 // below is a more efficient coding of
1362 // key+="+"+fileScope->name();
1363 *p++='+';
1364 qstrcpy(p,fileScope->absFilePath());
1365 p+=fileScopeLen-1;
1366 }
1367 *p='\0';
1368
1369 LookupInfo *pval=Doxygen::lookupCache.find(key);
1370 //printf("Searching for %s result=%p\n",key.data(),pval);
1371 if (pval)
1372 {
1373 //printf("LookupInfo %p %p '%s' %p\n",
1374 // pval->classDef, pval->typeDef, pval->templSpec.data(),
1375 // pval->resolvedType.data());
1376 if (pTemplSpec) *pTemplSpec=pval->templSpec;
1377 if (pTypeDef) *pTypeDef=pval->typeDef;
1378 if (pResolvedType) *pResolvedType=pval->resolvedType;
1379 //printf("] cachedMatch=%s\n",
1380 // pval->classDef?pval->classDef->name().data():"<none>");
1381 //if (pTemplSpec)
1382 // printf("templSpec=%s\n",pTemplSpec->data());
1383 return pval->classDef;
1384 }
1385 else // not found yet; we already add a 0 to avoid the possibility of
1386 // endless recursion.
1387 {
1388 Doxygen::lookupCache.insert(key,new LookupInfo);
1389 }
1390
1391 ClassDef *bestMatch=0;
1392 MemberDef *bestTypedef=0;
1393 QCString bestTemplSpec;
1394 QCString bestResolvedType;
1395 int minDistance=10000; // init at "infinite"
1396
1397 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // not a unique name
1398 {
1399 //printf(" name is not unique\n");
1400 DefinitionListIterator dli(*(DefinitionList*)di);
1401 Definition *d;
1402 int count=0;
1403 for (dli.toFirst();(d=dli.current());++dli,++count) // foreach definition
1404 {
1405 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1406 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1407 bestResolvedType);
1408 }
1409 }
1410 else // unique name
1411 {
1412 //printf(" name is unique\n");
1413 Definition *d = (Definition *)di;
1414 getResolvedSymbol(scope,fileScope,d,explicitScopePart,&actTemplParams,
1415 minDistance,bestMatch,bestTypedef,bestTemplSpec,
1416 bestResolvedType);
1417 }
1418
1419 if (pTypeDef)
1420 {
1421 *pTypeDef = bestTypedef;
1422 }
1423 if (pTemplSpec)
1424 {
1425 *pTemplSpec = bestTemplSpec;
1426 }
1427 if (pResolvedType)
1428 {
1429 *pResolvedType = bestResolvedType;
1430 }
1431 //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n",
1432 // bestMatch,bestResolvedType.data());
1433
1434 pval=Doxygen::lookupCache.find(key);
1435 if (pval)
1436 {
1437 pval->classDef = bestMatch;
1438 pval->typeDef = bestTypedef;
1439 pval->templSpec = bestTemplSpec;
1440 pval->resolvedType = bestResolvedType;
1441 }
1442 else
1443 {
1444 Doxygen::lookupCache.insert(key,new LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType));
1445 }
1446 //printf("] bestMatch=%s distance=%d\n",
1447 // bestMatch?bestMatch->name().data():"<none>",minDistance);
1448 //if (pTemplSpec)
1449 // printf("templSpec=%s\n",pTemplSpec->data());
1450 return bestMatch;
1451}
1452
1453/* Find the fully qualified class name referred to by the input class
1454 * or typedef name against the input scope.
1455 * Loops through scope and each of its parent scopes looking for a
1456 * match against the input name.
1457 */
1458ClassDef *getResolvedClass(Definition *scope,
1459 FileDef *fileScope,
1460 const char *n,
1461 MemberDef **pTypeDef,
1462 QCString *pTemplSpec,
1463 bool mayBeUnlinkable,
1464 bool mayBeHidden,
1465 QCString *pResolvedType
1466 )
1467{
1468 g_resolvedTypedefs.clear();
1469 if (scope==0 ||
1470 (scope->definitionType()!=Definition::TypeClass &&
1471 scope->definitionType()!=Definition::TypeNamespace
1472 ) ||
1473 (fileScope && fileScope->isJava() && QCString(n).find("::")!=-1)
1474 )
1475 {
1476 scope=Doxygen::globalScope;
1477 }
1478 //printf("------------ getResolvedClass(scope=%s,file=%s,name=%s,mayUnlinkable=%d)\n",
1479 // scope?scope->name().data():"<global>",
1480 // fileScope?fileScope->name().data():"<none>",
1481 // n,
1482 // mayBeUnlinkable
1483 // );
1484 ClassDef *result = getResolvedClassRec(scope,fileScope,n,pTypeDef,pTemplSpec,pResolvedType);
1485 if (!mayBeUnlinkable && result && !result->isLinkable())
1486 {
1487 if (!mayBeHidden || !result->isHidden())
1488 {
1489 result=0; // don't link to artificial/hidden classes unless explicitly allowed
1490 }
1491 }
1492 //printf("getResolvedClass(%s,%s)=%s\n",scope?scope->name().data():"<global>",
1493 // n,result?result->name().data():"<none>");
1494 return result;
1495}
1496
1497//-------------------------------------------------------------------------
1498//-------------------------------------------------------------------------
1499//-------------------------------------------------------------------------
1500//-------------------------------------------------------------------------
1501
1502static bool findOperator(const QCString &s,int i)
1503{
1504 int b = s.findRev("operator",i);
1505 if (b==-1) return FALSE; // not found
1506 b+=8;
1507 while (b<i) // check if there are only spaces in between
1508 // the operator and the >
1509 {
1510 if (!isspace((uchar)s.at(b))) return FALSE;
1511 b++;
1512 }
1513 return TRUE;
1514}
1515
1516static bool findOperator2(const QCString &s,int i)
1517{
1518 int b = s.findRev("operator",i);
1519 if (b==-1) return FALSE; // not found
1520 b+=8;
1521 while (b<i) // check if there are only non-ascii
1522 // characters in front of the operator
1523 {
1524 if (isId((uchar)s.at(b))) return FALSE;
1525 b++;
1526 }
1527 return TRUE;
1528}
1529
1530static const char constScope[] = { 'c', 'o', 'n', 's', 't', ':' };
1531static const char virtualScope[] = { 'v', 'i', 'r', 't', 'u', 'a', 'l', ':' };
1532
1533
1534class StrBuf
1535{
1536 public:
1537 StrBuf() : str(0), pos(0), len(0) {}
1538 ~StrBuf() { free(str); str=0; pos=0; len=0; }
1539 void clear() { pos=0; }
1540 void addChar(char c) { if (pos>=len) { len+=1024; str = (char*)realloc(str,len); }
1541 str[pos++]=c;
1542 }
1543 void addStr(const char *s) {
1544 int l=strlen(s);
1545 if (pos+l>=len) { len+=l+1024; str = (char*)realloc(str,len); }
1546 strcpy(&str[pos],s);
1547 pos+=l;
1548 }
1549 const char *get() { return str; }
1550 int getPos() const { return pos; }
1551 char at(int i) const { return str[i]; }
1552 private:
1553 char *str;
1554 int pos;
1555 int len;
1556};
1557
1558// Note: this function is not reentrant due to the use of static buffer!
1559QCString removeRedundantWhiteSpace(const QCString &s)
1560{
1561 static bool cliSupport = Config_getBool("CPP_CLI_SUPPORT");
1562 if (s.isEmpty()) return s;
1563 static StrBuf strBuf;
1564 //int resultLen = 1024;
1565 //int resultPos = 0;
1566 //QCString result(resultLen);
1567 // we use strBuf.addChar(c) instead of result+=c to
1568 // improve the performance of this function
1569 strBuf.clear();
1570 uint i;
1571 uint l=s.length();
1572 uint csp=0;
1573 uint vsp=0;
1574 for (i=0;i<l;i++)
1575 {
1576nextChar:
1577 char c=s.at(i);
1578
1579 // search for "const"
1580 if (csp<6 && c==constScope[csp] && // character matches substring "const"
1581 (csp>0 || // if it is the first character
1582 i==0 || // the previous may not be a digit
1583 !isId(s.at(i-1))
1584 )
1585 )
1586 csp++;
1587 else // reset counter
1588 csp=0;
1589
1590 // search for "virtual"
1591 if (vsp<8 && c==virtualScope[vsp] && // character matches substring "virtual"
1592 (vsp>0 || // if it is the first character
1593 i==0 || // the previous may not be a digit
1594 !isId(s.at(i-1))
1595 )
1596 )
1597 vsp++;
1598 else // reset counter
1599 vsp=0;
1600
1601 if (c=='"') // quoted string
1602 {
1603 i++;
1604 strBuf.addChar(c);
1605 while (i<l)
1606 {
1607 char cc=s.at(i);
1608 strBuf.addChar(cc);
1609 if (cc=='\\') // escaped character
1610 {
1611 strBuf.addChar(s.at(i+1));
1612 i+=2;
1613 }
1614 else if (cc=='"') // end of string
1615 { i++; goto nextChar; }
1616 else // any other character
1617 { i++; }
1618 }
1619 }
1620 else if (i<l-2 && c=='<' && // current char is a <
1621 (isId(s.at(i+1)) || isspace((uchar)s.at(i+1))) && // next char is an id char or space
1622 (i<8 || !findOperator(s,i)) // string in front is not "operator"
1623 )
1624 {
1625 strBuf.addChar('<');
1626 strBuf.addChar(' ');
1627 }
1628 else if (i>0 && c=='>' && // current char is a >
1629 (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
1630 (i<8 || !findOperator(s,i)) // string in front is not "operator"
1631 )
1632 {
1633 strBuf.addChar(' ');
1634 strBuf.addChar('>');
1635 }
1636 else if (i>0 && c==',' && !isspace((uchar)s.at(i-1))
1637 && ((i<l-1 && isId(s.at(i+1)))
1638 || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2))) // for PHP
1639 || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))) // for PHP
1640 {
1641 strBuf.addChar(',');
1642 strBuf.addChar(' ');
1643 }
1644 else if (i>0 &&
1645 ((isId(s.at(i)) && s.at(i-1)==')') ||
1646 (s.at(i)=='\'' && s.at(i-1)==' ')
1647 )
1648 )
1649 {
1650 strBuf.addChar(' ');
1651 strBuf.addChar(s.at(i));
1652 }
1653 else if (c=='t' && csp==5 /*&& (i<5 || !isId(s.at(i-5)))*/ &&
1654 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
1655 s.at(i+1)==')' ||
1656 s.at(i+1)==',' ||
1657 s.at(i+1)=='\0'
1658 )
1659 )
1660 // prevent const ::A from being converted to const::A
1661 {
1662 strBuf.addChar('t');
1663 strBuf.addChar(' ');
1664 if (s.at(i+1)==' ') i++;
1665 csp=0;
1666 }
1667 else if (c==':' && csp==6 /*&& (i<6 || !isId(s.at(i-6)))*/)
1668 // replace const::A by const ::A
1669 {
1670 strBuf.addChar(' ');
1671 strBuf.addChar(':');
1672 csp=0;
1673 }
1674 else if (c=='l' && vsp==7 /*&& (i<7 || !isId(s.at(i-7)))*/ &&
1675 !(isId(s.at(i+1)) /*|| s.at(i+1)==' '*/ ||
1676 s.at(i+1)==')' ||
1677 s.at(i+1)==',' ||
1678 s.at(i+1)=='\0'
1679 )
1680 )
1681 // prevent virtual ::A from being converted to virtual::A
1682 {
1683 strBuf.addChar('l');
1684 strBuf.addChar(' ');
1685 if (s.at(i+1)==' ') i++;
1686 vsp=0;
1687 }
1688 else if (c==':' && vsp==8 /*&& (i<8 || !isId(s.at(i-8)))*/)
1689 // replace virtual::A by virtual ::A
1690 {
1691 strBuf.addChar(' ');
1692 strBuf.addChar(':');
1693 vsp=0;
1694 }
1695 else if (!isspace((uchar)c) || // not a space
1696 ( i>0 && i<l-1 && // internal character
1697 (isId(s.at(i-1)) || s.at(i-1)==')' || s.at(i-1)==',' || s.at(i-1)=='>' || s.at(i-1)==']')
1698 && (isId(s.at(i+1)) || (i<l-2 && s.at(i+1)=='$' && isId(s.at(i+2)))
1699 || (i<l-3 && s.at(i+1)=='&' && s.at(i+2)=='$' && isId(s.at(i+3))))
1700 )
1701 )
1702 {
1703 if (c=='*' || c=='&' || c=='@' || c=='$')
1704 {
1705 //uint rl=result.length();
1706 uint rl=strBuf.getPos();
1707 if ((rl>0 && (isId(strBuf.at(rl-1)) || strBuf.at(rl-1)=='>')) &&
1708 ((c!='*' && c!='&') || !findOperator2(s,i)) // avoid splitting operator* and operator->* and operator&
1709 )
1710 {
1711 strBuf.addChar(' ');
1712 }
1713 }
1714 strBuf.addChar(c);
1715 if (cliSupport &&
1716 (c=='^' || c=='%') && i>1 && isId(s.at(i-1)) &&
1717 !findOperator(s,i)
1718 )
1719 {
1720 strBuf.addChar(' '); // C++/CLI: Type^ name and Type% name
1721 }
1722 }
1723 }
1724 //printf("removeRedundantWhiteSpace(`%s')=`%s'\n",s.data(),result.data());
1725 strBuf.addChar(0);
1726 //result.resize(resultPos);
1727 return strBuf.get();
1728}
1729
1730bool rightScopeMatch(const QCString &scope, const QCString &name)
1731{
1732 return (name==scope || // equal
1733 (scope.right(name.length())==name && // substring
1734 scope.at(scope.length()-name.length()-1)==':' // scope
1735 )
1736 );
1737}
1738
1739bool leftScopeMatch(const QCString &scope, const QCString &name)
1740{
1741 return (name==scope || // equal
1742 (scope.left(name.length())==name && // substring
1743 scope.at(name.length())==':' // scope
1744 )
1745 );
1746}
1747
1748
1749void linkifyText(const TextGeneratorIntf &out,Definition *scope,
1750 FileDef *fileScope,const char *,
1751 const char *text, bool autoBreak,bool external,
1752 bool keepSpaces)
1753{
1754 //printf("`%s'\n",text);
1755 static QRegExp regExp("[a-z_A-Z\\x80-\\xFF][~!a-z_A-Z0-9.:\\x80-\\xFF]*");
1756 static QRegExp regExpSplit("(?!:),");
1757 QCString txtStr=text;
1758 int strLen = txtStr.length();
1759 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%d\n",
1760 // scope?scope->name().data():"<none>",
1761 // fileScope?fileScope->name().data():"<none>",
1762 // txtStr.data(),strLen);
1763 int matchLen;
1764 int index=0;
1765 int newIndex;
1766 int skipIndex=0;
1767 int floatingIndex=0;
1768 if (strLen==0) return;
1769 // read a word from the text string
1770 while ((newIndex=regExp.match(txtStr,index,&matchLen))!=-1 &&
1771 (newIndex==0 || !(txtStr.at(newIndex-1)>='0' && txtStr.at(newIndex-1)<='9')) // avoid matching part of hex numbers
1772 )
1773 {
1774 // add non-word part to the result
1775 floatingIndex+=newIndex-skipIndex+matchLen;
1776 bool insideString=FALSE;
1777 int i;
1778 for (i=index;i<newIndex;i++)
1779 {
1780 if (txtStr.at(i)=='"') insideString=!insideString;
1781 }
1782
1783 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak);
1784 if (strLen>35 && floatingIndex>30 && autoBreak) // try to insert a split point
1785 {
1786 QCString splitText = txtStr.mid(skipIndex,newIndex-skipIndex);
1787 int splitLength = splitText.length();
1788 int offset=1;
1789 i=splitText.find(regExpSplit,0);
1790 if (i==-1) { i=splitText.find('<'); if (i!=-1) offset=0; }
1791 if (i==-1) i=splitText.find('>');
1792 if (i==-1) i=splitText.find(' ');
1793 //printf("splitText=[%s] len=%d i=%d offset=%d\n",splitText.data(),splitLength,i,offset);
1794 if (i!=-1) // add a link-break at i in case of Html output
1795 {
1796 out.writeString(splitText.left(i+offset),keepSpaces);
1797 out.writeBreak();
1798 out.writeString(splitText.right(splitLength-i-offset),keepSpaces);
1799 floatingIndex=splitLength-i-offset+matchLen;
1800 }
1801 else
1802 {
1803 out.writeString(splitText,keepSpaces);
1804 }
1805 }
1806 else
1807 {
1808 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
1809 out.writeString(txtStr.mid(skipIndex,newIndex-skipIndex),keepSpaces);
1810 }
1811 // get word from string
1812 QCString word=txtStr.mid(newIndex,matchLen);
1813 QCString matchWord = substitute(word,".","::");
1814 //printf("linkifyText word=%s matchWord=%s scope=%s\n",
1815 // word.data(),matchWord.data(),scope?scope->name().data():"<none>");
1816 bool found=FALSE;
1817 if (!insideString)
1818 {
1819 ClassDef *cd=0;
1820 FileDef *fd=0;
1821 MemberDef *md=0;
1822 NamespaceDef *nd=0;
1823 GroupDef *gd=0;
1824 //printf("** Match word '%s'\n",matchWord.data());
1825
1826 MemberDef *typeDef=0;
1827 if ((cd=getResolvedClass(scope,fileScope,matchWord,&typeDef)))
1828 {
1829 //printf("Found class %s\n",cd->name().data());
1830 // add link to the result
1831 if (external ? cd->isLinkable() : cd->isLinkableInProject())
1832 {
1833 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1834 found=TRUE;
1835 }
1836 }
1837 else if (typeDef)
1838 {
1839 //printf("Found typedef %s\n",typeDef->name().data());
1840 if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject())
1841 {
1842 out.writeLink(typeDef->getReference(),
1843 typeDef->getOutputFileBase(),
1844 typeDef->anchor(),
1845 word);
1846 found=TRUE;
1847 }
1848 }
1849 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
1850 {
1851 // add link to the result
1852 if (external ? cd->isLinkable() : cd->isLinkableInProject())
1853 {
1854 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1855 found=TRUE;
1856 }
1857 }
1858 else if ((cd=getClass(matchWord+"-g"))) // C# generic as well
1859 {
1860 // add link to the result
1861 if (external ? cd->isLinkable() : cd->isLinkableInProject())
1862 {
1863 out.writeLink(cd->getReference(),cd->getOutputFileBase(),cd->anchor(),word);
1864 found=TRUE;
1865 }
1866 }
1867 else
1868 {
1869 //printf(" -> nothing\n");
1870 }
1871
1872 QCString scopeName;
1873 if (scope &&
1874 (scope->definitionType()==Definition::TypeClass ||
1875 scope->definitionType()==Definition::TypeNamespace
1876 )
1877 )
1878 {
1879 scopeName=scope->name();
1880 }
1881 //printf("ScopeName=%s\n",scopeName.data());
1882 //if (!found) printf("Trying to link %s in %s\n",word.data(),scopeName.data());
1883 if (!found &&
1884 getDefs(scopeName,matchWord,0,md,cd,fd,nd,gd) &&
1885 (md->isTypedef() || md->isEnumerate() ||
1886 md->isReference() || md->isVariable()
1887 ) &&
1888 (external ? md->isLinkable() : md->isLinkableInProject())
1889 )
1890 {
1891 //printf("Found ref scope=%s\n",d?d->name().data():"<global>");
1892 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
1893 // md->anchor(),word);
1894 out.writeLink(md->getReference(),md->getOutputFileBase(),
1895 md->anchor(),word);
1896 found=TRUE;
1897 }
1898 }
1899
1900 if (!found) // add word to the result
1901 {
1902 out.writeString(word,keepSpaces);
1903 }
1904 // set next start point in the string
1905 //printf("index=%d/%d\n",index,txtStr.length());
1906 skipIndex=index=newIndex+matchLen;
1907 }
1908 // add last part of the string to the result.
1909 //ol.docify(txtStr.right(txtStr.length()-skipIndex));
1910 out.writeString(txtStr.right(txtStr.length()-skipIndex),keepSpaces);
1911}
1912
1913
1914void writeExample(OutputList &ol,ExampleSDict *ed)
1915{
1916 QCString exampleLine=theTranslator->trWriteList(ed->count());
1917
1918 //bool latexEnabled = ol.isEnabled(OutputGenerator::Latex);
1919 //bool manEnabled = ol.isEnabled(OutputGenerator::Man);
1920 //bool htmlEnabled = ol.isEnabled(OutputGenerator::Html);
1921 QRegExp marker("@[0-9]+");
1922 int index=0,newIndex,matchLen;
1923 // now replace all markers in inheritLine with links to the classes
1924 while ((newIndex=marker.match(exampleLine,index,&matchLen))!=-1)
1925 {
1926 bool ok;
1927 ol.parseText(exampleLine.mid(index,newIndex-index));
1928 uint entryIndex = exampleLine.mid(newIndex+1,matchLen-1).toUInt(&ok);
1929 Example *e=ed->at(entryIndex);
1930 if (ok && e)
1931 {
1932 ol.pushGeneratorState();
1933 //if (latexEnabled) ol.disable(OutputGenerator::Latex);
1934 ol.disable(OutputGenerator::Latex);
1935 ol.disable(OutputGenerator::RTF);
1936 // link for Html / man
1937 //printf("writeObjectLink(file=%s)\n",e->file.data());
1938 ol.writeObjectLink(0,e->file,e->anchor,e->name);
1939 ol.popGeneratorState();
1940
1941 ol.pushGeneratorState();
1942 //if (latexEnabled) ol.enable(OutputGenerator::Latex);
1943 ol.disable(OutputGenerator::Man);
1944 ol.disable(OutputGenerator::Html);
1945 // link for Latex / pdf with anchor because the sources
1946 // are not hyperlinked (not possible with a verbatim environment).
1947 ol.writeObjectLink(0,e->file,0,e->name);
1948 //if (manEnabled) ol.enable(OutputGenerator::Man);
1949 //if (htmlEnabled) ol.enable(OutputGenerator::Html);
1950 ol.popGeneratorState();
1951 }
1952 index=newIndex+matchLen;
1953 }
1954 ol.parseText(exampleLine.right(exampleLine.length()-index));
1955 ol.writeString(".");
1956}
1957
1958
1959QCString argListToString(ArgumentList *al,bool useCanonicalType,bool showDefVals)
1960{
1961 QCString result;
1962 if (al==0) return result;
1963 Argument *a=al->first();
1964 result+="(";
1965 while (a)
1966 {
1967 QCString type1 = useCanonicalType && !a->canType.isEmpty() ?
1968 a->canType : a->type;
1969 QCString type2;
1970 int i=type1.find(")("); // hack to deal with function pointers
1971 if (i!=-1)
1972 {
1973 type2=type1.mid(i);
1974 type1=type1.left(i);
1975 }
1976 if (!a->attrib.isEmpty())
1977 {
1978 result+=a->attrib+" ";
1979 }
1980 if (!a->name.isEmpty() || !a->array.isEmpty())
1981 {
1982 result+= type1+" "+a->name+type2+a->array;
1983 }
1984 else
1985 {
1986 result+= type1+type2;
1987 }
1988 if (!a->defval.isEmpty() && showDefVals)
1989 {
1990 result+="="+a->defval;
1991 }
1992 a = al->next();
1993 if (a) result+=", ";
1994 }
1995 result+=")";
1996 if (al->constSpecifier) result+=" const";
1997 if (al->volatileSpecifier) result+=" volatile";
1998 return removeRedundantWhiteSpace(result);
1999}
2000
2001QCString tempArgListToString(ArgumentList *al)
2002{
2003 QCString result;
2004 if (al==0) return result;
2005 result="<";
2006 Argument *a=al->first();
2007 while (a)
2008 {
2009 if (!a->name.isEmpty()) // add template argument name
2010 {
2011 result+=a->name;
2012 }
2013 else // extract name from type
2014 {
2015 int i=a->type.length()-1;
2016 while (i>=0 && isId(a->type.at(i))) i--;
2017 if (i>0)
2018 {
2019 result+=a->type.right(a->type.length()-i-1);
2020 }
2021 else // nothing found -> take whole name
2022 {
2023 result+=a->type;
2024 }
2025 }
2026 a=al->next();
2027 if (a) result+=", ";
2028 }
2029 result+=">";
2030 return removeRedundantWhiteSpace(result);
2031}
2032
2033
2034// compute the HTML anchors for a list of members
2035void setAnchors(ClassDef *cd,char id,MemberList *ml,int groupId)
2036{
2037 int count=0;
2038 if (ml==0) return;
2039 MemberListIterator mli(*ml);
2040 MemberDef *md;
2041 for (;(md=mli.current());++mli)
2042 {
2043 if (!md->isReference())
2044 {
2045 QCString anchor;
2046 if (groupId==-1)
2047 anchor.sprintf("%c%d",id,count++);
2048 else
2049 anchor.sprintf("%c%d_%d",id,groupId,count++);
2050 if (cd) anchor.prepend(escapeCharsInString(cd->name(),FALSE));
2051 md->setAnchor(anchor);
2052 //printf("setAnchors(): Member %s outputFileBase=%s anchor %s result %s\n",
2053 // md->name().data(),md->getOutputFileBase().data(),anchor.data(),md->anchor().data());
2054 }
2055 }
2056}
2057
2058//----------------------------------------------------------------------------
2059
2060/*! takes the \a buf of the given length \a len and converts CR LF (DOS)
2061 * or CR (MAC) line ending to LF (Unix). Returns the length of the
2062 * converted content (i.e. the same as \a len (Unix, MAC) or
2063 * smaller (DOS).
2064 */
2065int filterCRLF(char *buf,int len)
2066{
2067 int src = 0; // source index
2068 int dest = 0; // destination index
2069 char c; // current character
2070
2071 while (src<len)
2072 {
2073 c = buf[src++]; // Remember the processed character.
2074 if (c == '\r') // CR to be solved (MAC, DOS)
2075 {
2076 c = '\n'; // each CR to LF
2077 if (src<len && buf[src] == '\n')
2078 ++src; // skip LF just after CR (DOS)
2079 }
2080 else if ( c == '\0' && src<len-1) // filter out internal \0 characters, as it will confuse the parser
2081 {
2082 c = ' '; // turn into a space
2083 }
2084 buf[dest++] = c; // copy the (modified) character to dest
2085 }
2086 return dest; // length of the valid part of the buf
2087}
2088
2089static QCString getFilterFromList(const char *name,const QStrList &filterList,bool &found)
2090{
2091 found=FALSE;
2092 // compare the file name to the filter pattern list
2093 QStrListIterator sli(filterList);
2094 char* filterStr;
2095 for (sli.toFirst(); (filterStr = sli.current()); ++sli)
2096 {
2097 QCString fs = filterStr;
2098 int i_equals=fs.find('=');
2099 if (i_equals!=-1)
2100 {
2101 QCString filterPattern = fs.left(i_equals);
2102 QRegExp fpat(filterPattern,portable_fileSystemIsCaseSensitive(),TRUE);
2103 if (fpat.match(name)!=-1)
2104 {
2105 // found a match!
2106 QCString filterName = fs.mid(i_equals+1);
2107 if (filterName.find(' ')!=-1)
2108 { // add quotes if the name has spaces
2109 filterName="\""+filterName+"\"";
2110 }
2111 found=TRUE;
2112 return filterName;
2113 }
2114 }
2115 }
2116
2117 // no match
2118 return "";
2119}
2120
2121/*! looks for a filter for the file \a name. Returns the name of the filter
2122 * if there is a match for the file name, otherwise an empty string.
2123 * In case \a inSourceCode is TRUE then first the source filter list is
2124 * considered.
2125 */
2126QCString getFileFilter(const char* name,bool isSourceCode)
2127{
2128 // sanity check
2129 if (name==0) return "";
2130
2131 QStrList& filterSrcList = Config_getList("FILTER_SOURCE_PATTERNS");
2132 QStrList& filterList = Config_getList("FILTER_PATTERNS");
2133
2134 QCString filterName;
2135 bool found=FALSE;
2136 if (isSourceCode && !filterSrcList.isEmpty())
2137 { // first look for source filter pattern list
2138 filterName = getFilterFromList(name,filterSrcList,found);
2139 }
2140 if (!found && filterName.isEmpty())
2141 { // then look for filter pattern list
2142 filterName = getFilterFromList(name,filterList,found);
2143 }
2144 if (!found)
2145 { // then use the generic input filter
2146 return Config_getString("INPUT_FILTER");
2147 }
2148 else
2149 {
2150 return filterName;
2151 }
2152}
2153
2154
2155QCString transcodeCharacterStringToUTF8(const QCString &input)
2156{
2157 bool error=FALSE;
2158 static QCString inputEncoding = Config_getString("INPUT_ENCODING");
2159 const char *outputEncoding = "UTF-8";
2160 if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input;
2161 int inputSize=input.length();
2162 int outputSize=inputSize*4+1;
2163 QCString output(outputSize);
2164 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
2165 if (cd==(void *)(-1))
2166 {
2167 err("error: unsupported character conversion: '%s'->'%s'\n",
2168 inputEncoding.data(),outputEncoding);
2169 error=TRUE;
2170 }
2171 if (!error)
2172 {
2173 size_t iLeft=inputSize;
2174 size_t oLeft=outputSize;
2175 const char *inputPtr = input.data();
2176 char *outputPtr = output.data();
2177 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
2178 {
2179 outputSize-=oLeft;
2180 output.resize(outputSize+1);
2181 output.at(outputSize)='\0';
2182 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
2183 }
2184 else
2185 {
2186 err("error: failed to translate characters from %s to %s: check INPUT_ENCODING\ninput=[%s]\n",
2187 inputEncoding.data(),outputEncoding,input.data());
2188 error=TRUE;
2189 }
2190 }
2191 portable_iconv_close(cd);
2192 return error ? input : output;
2193}
2194
2195/*! reads a file with name \a name and returns it as a string. If \a filter
2196 * is TRUE the file will be filtered by any user specified input filter.
2197 * If \a name is "-" the string will be read from standard input.
2198 */
2199QCString fileToString(const char *name,bool filter,bool isSourceCode)
2200{
2201 if (name==0 || name[0]==0) return 0;
2202 QFile f;
2203
2204 bool fileOpened=FALSE;
2205 if (name[0]=='-' && name[1]==0) // read from stdin
2206 {
2207 fileOpened=f.open(IO_ReadOnly,stdin);
2208 if (fileOpened)
2209 {
2210 const int bSize=4096;
2211 QCString contents(bSize);
2212 int totalSize=0;
2213 int size;
2214 while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
2215 {
2216 totalSize+=bSize;
2217 contents.resize(totalSize+bSize);
2218 }
2219 totalSize = filterCRLF(contents.data(),totalSize+size)+2;
2220 contents.resize(totalSize);
2221 contents.at(totalSize-2)='\n'; // to help the scanner
2222 contents.at(totalSize-1)='\0';
2223 return contents;
2224 }
2225 }
2226 else // read from file
2227 {
2228 QFileInfo fi(name);
2229 if (!fi.exists() || !fi.isFile())
2230 {
2231 err("error: file `%s' not found\n",name);
2232 return "";
2233 }
2234 QCString filterName = getFileFilter(name,isSourceCode);
2235 if (filterName.isEmpty() || !filter)
2236 {
2237 f.setName(name);
2238 fileOpened=f.open(IO_ReadOnly);
2239 if (fileOpened)
2240 {
2241 int fsize=f.size();
2242 QCString contents(fsize+2);
2243 f.readBlock(contents.data(),fsize);
2244 if (fsize==0 || contents[fsize-1]=='\n')
2245 contents[fsize]='\0';
2246 else
2247 contents[fsize]='\n'; // to help the scanner
2248 contents[fsize+1]='\0';
2249 f.close();
2250 int newSize = filterCRLF(contents.data(),fsize+2);
2251 if (newSize!=fsize+2)
2252 {
2253 contents.resize(newSize);
2254 }
2255 return transcodeCharacterStringToUTF8(contents);
2256 }
2257 }
2258 else // filter the input
2259 {
2260 QCString cmd=filterName+" \""+name+"\"";
2261 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
2262 FILE *f=portable_popen(cmd,"r");
2263 if (!f)
2264 {
2265 err("error: could not execute filter %s\n",filterName.data());
2266 return "";
2267 }
2268 const int bSize=4096;
2269 QCString contents(bSize);
2270 int totalSize=0;
2271 int size;
2272 while ((size=fread(contents.data()+totalSize,1,bSize,f))==bSize)
2273 {
2274 totalSize+=bSize;
2275 contents.resize(totalSize+bSize);
2276 }
2277 totalSize = filterCRLF(contents.data(),totalSize+size)+2;
2278 contents.resize(totalSize);
2279 contents.at(totalSize-2)='\n'; // to help the scanner
2280 contents.at(totalSize-1)='\0';
2281 portable_pclose(f);
2282 return transcodeCharacterStringToUTF8(contents);
2283 }
2284 }
2285 if (!fileOpened)
2286 {
2287 err("error: cannot open file `%s' for reading\n",name);
2288 }
2289 return "";
2290}
2291
2292QCString dateToString(bool includeTime)
2293{
2294 QDateTime current = QDateTime::currentDateTime();
2295 return theTranslator->trDateTime(current.date().year(),
2296 current.date().month(),
2297 current.date().day(),
2298 current.date().dayOfWeek(),
2299 current.time().hour(),
2300 current.time().minute(),
2301 current.time().second(),
2302 includeTime);
2303}
2304
2305QCString yearToString()
2306{
2307 const QDate &d=QDate::currentDate();
2308 QCString result;
2309 result.sprintf("%d", d.year());
2310 return result;
2311}
2312
2313//----------------------------------------------------------------------
2314// recursive function that returns the number of branches in the
2315// inheritance tree that the base class `bcd' is below the class `cd'
2316
2317int minClassDistance(const ClassDef *cd,const ClassDef *bcd,int level)
2318{
2319 if (bcd->categoryOf()) // use class that is being extended in case of
2320 // an Objective-C category
2321 {
2322 bcd=bcd->categoryOf();
2323 }
2324 if (cd==bcd) return level;
2325 if (level==256)
2326 {
2327 err("error: Internal inconsistency: found class %s seem to have a recursive "
2328 "inheritance relation! Please send a bug report to dimitri@stack.nl\n",cd->name().data());
2329 return -1;
2330 }
2331 int m=maxInheritanceDepth;
2332 if (cd->baseClasses())
2333 {
2334#if 0
2335 BaseClassListIterator bcli(*cd->baseClasses());
2336 for ( ; bcli.current() ; ++bcli)
2337 {
2338 //printf("class %s base class %s\n",cd->name().data(),bcli.current()->classDef->name().data());
2339 int mc=minClassDistance(bcli.current()->classDef,bcd,level+1);
2340 if (mc<m) m=mc;
2341 if (m<0) break;
2342 }
2343#endif
2344 BaseClassDef *bcdi = cd->baseClasses()->first();
2345 while (bcdi)
2346 {
2347 int mc=minClassDistance(bcdi->classDef,bcd,level+1);
2348 if (mc<m) m=mc;
2349 if (m<0) break;
2350 bcdi = cd->baseClasses()->next();
2351 }
2352 }
2353 return m;
2354}
2355
2356//static void printArgList(ArgumentList *al)
2357//{
2358// if (al==0) return;
2359// ArgumentListIterator ali(*al);
2360// Argument *a;
2361// printf("(");
2362// for (;(a=ali.current());++ali)
2363// {
2364// printf("t=`%s' n=`%s' v=`%s' ",a->type.data(),!a->name.isEmpty()>0?a->name.data():"",!a->defval.isEmpty()>0?a->defval.data():"");
2365// }
2366// printf(")");
2367//}
2368
2369#ifndef NEWMATCH
2370// strip any template specifiers that follow className in string s
2371static QCString trimTemplateSpecifiers(
2372 const QCString &namespaceName,
2373 const QCString &className,
2374 const QCString &s
2375 )
2376{
2377 //printf("trimTemplateSpecifiers(%s,%s,%s)\n",namespaceName.data(),className.data(),s.data());
2378 QCString scopeName=mergeScopes(namespaceName,className);
2379 ClassDef *cd=getClass(scopeName);
2380 if (cd==0) return s; // should not happen, but guard anyway.
2381
2382 QCString result=s;
2383
2384 int i=className.length()-1;
2385 if (i>=0 && className.at(i)=='>') // template specialization
2386 {
2387 // replace unspecialized occurrences in s, with their specialized versions.
2388 int count=1;
2389 int cl=i+1;
2390 while (i>=0)
2391 {
2392 char c=className.at(i);
2393 if (c=='>') count++,i--;
2394 else if (c=='<') { count--; if (count==0) break; }
2395 else i--;
2396 }
2397 QCString unspecClassName=className.left(i);
2398 int l=i;
2399 int p=0;
2400 while ((i=result.find(unspecClassName,p))!=-1)
2401 {
2402 if (result.at(i+l)!='<') // unspecialized version
2403 {
2404 result=result.left(i)+className+result.right(result.length()-i-l);
2405 l=cl;
2406 }
2407 p=i+l;
2408 }
2409 }
2410
2411 //printf("result after specialization: %s\n",result.data());
2412
2413 QCString qualName=cd->qualifiedNameWithTemplateParameters();
2414 //printf("QualifiedName = %s\n",qualName.data());
2415 // We strip the template arguments following className (if any)
2416 if (!qualName.isEmpty()) // there is a class name
2417 {
2418 int is,ps=0;
2419 int p=0,l,i;
2420
2421 while ((is=getScopeFragment(qualName,ps,&l))!=-1)
2422 {
2423 QCString qualNamePart = qualName.right(qualName.length()-is);
2424 //printf("qualNamePart=%s\n",qualNamePart.data());
2425 while ((i=result.find(qualNamePart,p))!=-1)
2426 {
2427 int ql=qualNamePart.length();
2428 result=result.left(i)+cd->name()+result.right(result.length()-i-ql);
2429 p=i+cd->name().length();
2430 }
2431 ps=is+l;
2432 }
2433 }
2434 //printf("result=%s\n",result.data());
2435
2436 return result.stripWhiteSpace();
2437}
2438
2439/*!
2440 * @param pattern pattern to look for
2441 * @param s string to search in
2442 * @param p position to start
2443 * @param len resulting pattern length
2444 * @returns position on which string is found, or -1 if not found
2445 */
2446static int findScopePattern(const QCString &pattern,const QCString &s,
2447 int p,int *len)
2448{
2449 int sl=s.length();
2450 int pl=pattern.length();
2451 int sp=0;
2452 *len=0;
2453 while (p<sl)
2454 {
2455 sp=p; // start of match
2456 int pp=0; // pattern position
2457 while (p<sl && pp<pl)
2458 {
2459 if (s.at(p)=='<') // skip template arguments while matching
2460 {
2461 int bc=1;
2462 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2463 p++;
2464 while (p<sl)
2465 {
2466 if (s.at(p)=='<') bc++;
2467 else if (s.at(p)=='>')
2468 {
2469 bc--;
2470 if (bc==0)
2471 {
2472 p++;
2473 break;
2474 }
2475 }
2476 //printf("skipping pos=%d c=%c\n",p,s.at(p));
2477 p++;
2478 }
2479 }
2480 else if (s.at(p)==pattern.at(pp))
2481 {
2482 //printf("match at position p=%d pp=%d c=%c\n",p,pp,s.at(p));
2483 p++;
2484 pp++;
2485 }
2486 else // no match
2487 {
2488 //printf("restarting at %d c=%c pat=%s\n",p,s.at(p),pattern.data());
2489 p=sp+1;
2490 break;
2491 }
2492 }
2493 if (pp==pl) // whole pattern matches
2494 {
2495 *len=p-sp;
2496 return sp;
2497 }
2498 }
2499 return -1;
2500}
2501
2502static QCString trimScope(const QCString &name,const QCString &s)
2503{
2504 int scopeOffset=name.length();
2505 QCString result=s;
2506 do // for each scope
2507 {
2508 QCString tmp;
2509 QCString scope=name.left(scopeOffset)+"::";
2510 //printf("Trying with scope=`%s'\n",scope.data());
2511
2512 int i,p=0,l;
2513 while ((i=findScopePattern(scope,result,p,&l))!=-1) // for each occurrence
2514 {
2515 tmp+=result.mid(p,i-p); // add part before pattern
2516 p=i+l;
2517 }
2518 tmp+=result.right(result.length()-p); // add trailing part
2519
2520 scopeOffset=name.findRev("::",scopeOffset-1);
2521 result = tmp;
2522 } while (scopeOffset>0);
2523 //printf("trimScope(name=%s,scope=%s)=%s\n",name.data(),s.data(),result.data());
2524 return result;
2525}
2526#endif
2527
2528void trimBaseClassScope(BaseClassList *bcl,QCString &s,int level=0)
2529{
2530 //printf("trimBaseClassScope level=%d `%s'\n",level,s.data());
2531 BaseClassListIterator bcli(*bcl);
2532 BaseClassDef *bcd;
2533 for (;(bcd=bcli.current());++bcli)
2534 {
2535 ClassDef *cd=bcd->classDef;
2536 //printf("Trying class %s\n",cd->name().data());
2537 int spos=s.find(cd->name()+"::");
2538 if (spos!=-1)
2539 {
2540 s = s.left(spos)+s.right(
2541 s.length()-spos-cd->name().length()-2
2542 );
2543 }
2544 //printf("base class `%s'\n",cd->name().data());
2545 if (cd->baseClasses())
2546 trimBaseClassScope(cd->baseClasses(),s,level+1);
2547 }
2548}
2549
2550#if 0
2551/*! if either t1 or t2 contains a namespace scope, then remove that
2552 * scope. If neither or both have a namespace scope, t1 and t2 remain
2553 * unchanged.
2554 */
2555static void trimNamespaceScope(QCString &t1,QCString &t2,const QCString &nsName)
2556{
2557 int p1=t1.length();
2558 int p2=t2.length();
2559 for (;;)
2560 {
2561 int i1=p1==0 ? -1 : t1.findRev("::",p1);
2562 int i2=p2==0 ? -1 : t2.findRev("::",p2);
2563 if (i1==-1 && i2==-1)
2564 {
2565 return;
2566 }
2567 if (i1!=-1 && i2==-1) // only t1 has a scope
2568 {
2569 QCString scope=t1.left(i1);
2570 replaceNamespaceAliases(scope,i1);
2571
2572 int so=nsName.length();
2573 do
2574 {
2575 QCString fullScope=nsName.left(so);
2576 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2577 fullScope+=scope;
2578 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2579 {
2580 t1 = t1.right(t1.length()-i1-2);
2581 return;
2582 }
2583 if (so==0)
2584 {
2585 so=-1;
2586 }
2587 else if ((so=nsName.findRev("::",so-1))==-1)
2588 {
2589 so=0;
2590 }
2591 }
2592 while (so>=0);
2593 }
2594 else if (i1==-1 && i2!=-1) // only t2 has a scope
2595 {
2596 QCString scope=t2.left(i2);
2597 replaceNamespaceAliases(scope,i2);
2598
2599 int so=nsName.length();
2600 do
2601 {
2602 QCString fullScope=nsName.left(so);
2603 if (!fullScope.isEmpty() && !scope.isEmpty()) fullScope+="::";
2604 fullScope+=scope;
2605 if (!fullScope.isEmpty() && Doxygen::namespaceSDict[fullScope]!=0) // scope is a namespace
2606 {
2607 t2 = t2.right(t2.length()-i2-2);
2608 return;
2609 }
2610 if (so==0)
2611 {
2612 so=-1;
2613 }
2614 else if ((so=nsName.findRev("::",so-1))==-1)
2615 {
2616 so=0;
2617 }
2618 }
2619 while (so>=0);
2620 }
2621 p1 = QMAX(i1-2,0);
2622 p2 = QMAX(i2-2,0);
2623 }
2624}
2625#endif
2626
2627static void stripIrrelevantString(QCString &target,const QCString &str)
2628{
2629 if (target==str) { target.resize(0); return; }
2630 int i,p=0;
2631 int l=str.length();
2632 bool changed=FALSE;
2633 while ((i=target.find(str,p))!=-1)
2634 {
2635 bool isMatch = (i==0 || !isId(target.at(i-1))) && // not a character before str
2636 (i+l==(int)target.length() || !isId(target.at(i+l))); // not a character after str
2637 if (isMatch)
2638 {
2639 int i1=target.find('*',i+l);
2640 int i2=target.find('&',i+l);
2641 if (i1==-1 && i2==-1)
2642 {
2643 // strip str from target at index i
2644 target=target.left(i)+target.right(target.length()-i-l);
2645 changed=TRUE;
2646 i-=l;
2647 }
2648 else if ((i1!=-1 && i<i1) || (i2!=-1 && i<i2)) // str before * or &
2649 {
2650 // move str to front
2651 target=str+" "+target.left(i)+target.right(target.length()-i-l);
2652 changed=TRUE;
2653 i++;
2654 }
2655 }
2656 p = i+l;
2657 }
2658 if (changed) target=target.stripWhiteSpace();
2659}
2660
2661/*! According to the C++ spec and Ivan Vecerina:
2662
2663 Parameter declarations that differ only in the presence or absence
2664 of const and/or volatile are equivalent.
2665
2666 So the following example, show what is stripped by this routine
2667 for const. The same is done for volatile.
2668
2669 \code
2670 const T param -> T param // not relevant
2671 const T& param -> const T& param // const needed
2672 T* const param -> T* param // not relevant
2673 const T* param -> const T* param // const needed
2674 \endcode
2675 */
2676void stripIrrelevantConstVolatile(QCString &s)
2677{
2678 //printf("stripIrrelevantConstVolatile(%s)=",s.data());
2679 stripIrrelevantString(s,"const");
2680 stripIrrelevantString(s,"volatile");
2681 //printf("%s\n",s.data());
2682}
2683
2684
2685// a bit of debug support for matchArguments
2686#define MATCH
2687#define NOMATCH
2688//#define MATCH printf("Match at line %d\n",__LINE__);
2689//#define NOMATCH printf("Nomatch at line %d\n",__LINE__);
2690
2691#ifndef NEWMATCH
2692static bool matchArgument(const Argument *srcA,const Argument *dstA,
2693 const QCString &className,
2694 const QCString &namespaceName,
2695 NamespaceSDict *usingNamespaces,
2696 SDict<Definition> *usingClasses)
2697{
2698 //printf("match argument start `%s|%s' <-> `%s|%s' using nsp=%p class=%p\n",
2699 // srcA->type.data(),srcA->name.data(),
2700 // dstA->type.data(),dstA->name.data(),
2701 // usingNamespaces,
2702 // usingClasses);
2703
2704 // TODO: resolve any typedefs names that are part of srcA->type
2705 // before matching. This should use className and namespaceName
2706 // and usingNamespaces and usingClass to determine which typedefs
2707 // are in-scope, so it will not be very efficient :-(
2708
2709 QCString srcAType=trimTemplateSpecifiers(namespaceName,className,srcA->type);
2710 QCString dstAType=trimTemplateSpecifiers(namespaceName,className,dstA->type);
2711 QCString srcAName=srcA->name.stripWhiteSpace();
2712 QCString dstAName=dstA->name.stripWhiteSpace();
2713 srcAType.stripPrefix("class ");
2714 dstAType.stripPrefix("class ");
2715
2716 // allow distinguishing "const A" from "const B" even though
2717 // from a syntactic point of view they would be two names of the same
2718 // type "const". This is not fool prove of course, but should at least
2719 // catch the most common cases.
2720 if ((srcAType=="const" || srcAType=="volatile") && !srcAName.isEmpty())
2721 {
2722 srcAType+=" ";
2723 srcAType+=srcAName;
2724 }
2725 if ((dstAType=="const" || dstAType=="volatile") && !dstAName.isEmpty())
2726 {
2727 dstAType+=" ";
2728 dstAType+=dstAName;
2729 }
2730 if (srcAName=="const" || srcAName=="volatile")
2731 {
2732 srcAType+=srcAName;
2733 srcAName.resize(0);
2734 }
2735 else if (dstA->name=="const" || dstA->name=="volatile")
2736 {
2737 dstAType+=dstA->name;
2738 dstAName.resize(0);
2739 }
2740
2741 stripIrrelevantConstVolatile(srcAType);
2742 stripIrrelevantConstVolatile(dstAType);
2743
2744 // strip typename keyword
2745 if (strncmp(srcAType,"typename ",9)==0)
2746 {
2747 srcAType = srcAType.right(srcAType.length()-9);
2748 }
2749 if (strncmp(dstAType,"typename ",9)==0)
2750 {
2751 dstAType = dstAType.right(dstAType.length()-9);
2752 }
2753
2754 srcAType = removeRedundantWhiteSpace(srcAType);
2755 dstAType = removeRedundantWhiteSpace(dstAType);
2756
2757 //srcAType=stripTemplateSpecifiersFromScope(srcAType,FALSE);
2758 //dstAType=stripTemplateSpecifiersFromScope(dstAType,FALSE);
2759
2760 //printf("srcA=`%s|%s' dstA=`%s|%s'\n",srcAType.data(),srcAName.data(),
2761 // dstAType.data(),dstAName.data());
2762
2763 if (srcA->array!=dstA->array) // nomatch for char[] against char
2764 {
2765 NOMATCH
2766 return FALSE;
2767 }
2768 if (srcAType!=dstAType) // check if the argument only differs on name
2769 {
2770
2771 // remove a namespace scope that is only in one type
2772 // (assuming a using statement was used)
2773 //printf("Trimming %s<->%s: %s\n",srcAType.data(),dstAType.data(),namespaceName.data());
2774 //trimNamespaceScope(srcAType,dstAType,namespaceName);
2775 //printf("After Trimming %s<->%s\n",srcAType.data(),dstAType.data());
2776
2777 //QCString srcScope;
2778 //QCString dstScope;
2779
2780 // strip redundant scope specifiers
2781 if (!className.isEmpty())
2782 {
2783 srcAType=trimScope(className,srcAType);
2784 dstAType=trimScope(className,dstAType);
2785 //printf("trimScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
2786 ClassDef *cd;
2787 if (!namespaceName.isEmpty())
2788 cd=getClass(namespaceName+"::"+className);
2789 else
2790 cd=getClass(className);
2791 if (cd && cd->baseClasses())
2792 {
2793 trimBaseClassScope(cd->baseClasses(),srcAType);
2794 trimBaseClassScope(cd->baseClasses(),dstAType);
2795 }
2796 //printf("trimBaseClassScope: `%s' <=> `%s'\n",srcAType.data(),dstAType.data());
2797 }
2798 if (!namespaceName.isEmpty())
2799 {
2800 srcAType=trimScope(namespaceName,srcAType);
2801 dstAType=trimScope(namespaceName,dstAType);
2802 }
2803 //printf("#usingNamespace=%d\n",usingNamespaces->count());
2804 if (usingNamespaces && usingNamespaces->count()>0)
2805 {
2806 NamespaceSDict::Iterator nli(*usingNamespaces);
2807 NamespaceDef *nd;
2808 for (;(nd=nli.current());++nli)
2809 {
2810 srcAType=trimScope(nd->name(),srcAType);
2811 dstAType=trimScope(nd->name(),dstAType);
2812 }
2813 }
2814 //printf("#usingClasses=%d\n",usingClasses->count());
2815 if (usingClasses && usingClasses->count()>0)
2816 {
2817 SDict<Definition>::Iterator cli(*usingClasses);
2818 Definition *cd;
2819 for (;(cd=cli.current());++cli)
2820 {
2821 srcAType=trimScope(cd->name(),srcAType);
2822 dstAType=trimScope(cd->name(),dstAType);
2823 }
2824 }
2825
2826 //printf("2. srcA=%s|%s dstA=%s|%s\n",srcAType.data(),srcAName.data(),
2827 // dstAType.data(),dstAName.data());
2828
2829 if (!srcAName.isEmpty() && !dstA->type.isEmpty() &&
2830 (srcAType+" "+srcAName)==dstAType)
2831 {
2832 MATCH
2833 return TRUE;
2834 }
2835 else if (!dstAName.isEmpty() && !srcA->type.isEmpty() &&
2836 (dstAType+" "+dstAName)==srcAType)
2837 {
2838 MATCH
2839 return TRUE;
2840 }
2841
2842
2843 uint srcPos=0,dstPos=0;
2844 bool equal=TRUE;
2845 while (srcPos<srcAType.length() && dstPos<dstAType.length() && equal)
2846 {
2847 equal=srcAType.at(srcPos)==dstAType.at(dstPos);
2848 if (equal) srcPos++,dstPos++;
2849 }
2850 uint srcATypeLen=srcAType.length();
2851 uint dstATypeLen=dstAType.length();
2852 if (srcPos<srcATypeLen && dstPos<dstATypeLen)
2853 {
2854 // if nothing matches or the match ends in the middle or at the
2855 // end of a string then there is no match
2856 if (srcPos==0 || dstPos==0)
2857 {
2858 NOMATCH
2859 return FALSE;
2860 }
2861 if (isId(srcAType.at(srcPos)) && isId(dstAType.at(dstPos)))
2862 {
2863 //printf("partial match srcPos=%d dstPos=%d!\n",srcPos,dstPos);
2864 // check if a name if already found -> if no then there is no match
2865 if (!srcAName.isEmpty() || !dstAName.isEmpty())
2866 {
2867 NOMATCH
2868 return FALSE;
2869 }
2870 // types only
2871 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
2872 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
2873 if (srcPos<srcATypeLen ||
2874 dstPos<dstATypeLen ||
2875 (srcPos==srcATypeLen && dstPos==dstATypeLen)
2876 )
2877 {
2878 NOMATCH
2879 return FALSE;
2880 }
2881 }
2882 else
2883 {
2884 // otherwise we assume that a name starts at the current position.
2885 while (srcPos<srcATypeLen && isId(srcAType.at(srcPos))) srcPos++;
2886 while (dstPos<dstATypeLen && isId(dstAType.at(dstPos))) dstPos++;
2887
2888 // if nothing more follows for both types then we assume we have
2889 // found a match. Note that now `signed int' and `signed' match, but
2890 // seeing that int is not a name can only be done by looking at the
2891 // semantics.
2892
2893 if (srcPos!=srcATypeLen || dstPos!=dstATypeLen)
2894 {
2895 NOMATCH
2896 return FALSE;
2897 }
2898 }
2899 }
2900 else if (dstPos<dstAType.length())
2901 {
2902 if (!isspace((uchar)dstAType.at(dstPos))) // maybe the names differ
2903 {
2904 if (!dstAName.isEmpty()) // dst has its name separated from its type
2905 {
2906 NOMATCH
2907 return FALSE;
2908 }
2909 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
2910 if (dstPos!=dstAType.length())
2911 {
2912 NOMATCH
2913 return FALSE; // more than a difference in name -> no match
2914 }
2915 }
2916 else // maybe dst has a name while src has not
2917 {
2918 dstPos++;
2919 while (dstPos<dstAType.length() && isId(dstAType.at(dstPos))) dstPos++;
2920 if (dstPos!=dstAType.length() || !srcAName.isEmpty())
2921 {
2922 NOMATCH
2923 return FALSE; // nope not a name -> no match
2924 }
2925 }
2926 }
2927 else if (srcPos<srcAType.length())
2928 {
2929 if (!isspace((uchar)srcAType.at(srcPos))) // maybe the names differ
2930 {
2931 if (!srcAName.isEmpty()) // src has its name separated from its type
2932 {
2933 NOMATCH
2934 return FALSE;
2935 }
2936 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
2937 if (srcPos!=srcAType.length())
2938 {
2939 NOMATCH
2940 return FALSE; // more than a difference in name -> no match
2941 }
2942 }
2943 else // maybe src has a name while dst has not
2944 {
2945 srcPos++;
2946 while (srcPos<srcAType.length() && isId(srcAType.at(srcPos))) srcPos++;
2947 if (srcPos!=srcAType.length() || !dstAName.isEmpty())
2948 {
2949 NOMATCH
2950 return FALSE; // nope not a name -> no match
2951 }
2952 }
2953 }
2954 }
2955 MATCH
2956 return TRUE;
2957}
2958
2959
2960/*!
2961 * Matches the arguments list srcAl with the argument list dstAl
2962 * Returns TRUE if the argument lists are equal. Two argument list are
2963 * considered equal if the number of arguments is equal and the types of all
2964 * arguments are equal. Furthermore the const and volatile specifiers
2965 * stored in the list should be equal.
2966 */
2967bool matchArguments(ArgumentList *srcAl,ArgumentList *dstAl,
2968 const char *cl,const char *ns,bool checkCV,
2969 NamespaceSDict *usingNamespaces,
2970 SDict<Definition> *usingClasses)
2971{
2972 QCString className=cl;
2973 QCString namespaceName=ns;
2974
2975 // strip template specialization from class name if present
2976 //int til=className.find('<'),tir=className.find('>');
2977 //if (til!=-1 && tir!=-1 && tir>til)
2978 //{
2979 // className=className.left(til)+className.right(className.length()-tir-1);
2980 //}
2981
2982 //printf("matchArguments(%s,%s) className=%s namespaceName=%s checkCV=%d usingNamespaces=%d usingClasses=%d\n",
2983 // srcAl ? argListToString(srcAl).data() : "",
2984 // dstAl ? argListToString(dstAl).data() : "",
2985 // cl,ns,checkCV,
2986 // usingNamespaces?usingNamespaces->count():0,
2987 // usingClasses?usingClasses->count():0
2988 // );
2989
2990 if (srcAl==0 || dstAl==0)
2991 {
2992 bool match = srcAl==dstAl; // at least one of the members is not a function
2993 if (match)
2994 {
2995 MATCH
2996 return TRUE;
2997 }
2998 else
2999 {
3000 NOMATCH
3001 return FALSE;
3002 }
3003 }
3004
3005 // handle special case with void argument
3006 if ( srcAl->count()==0 && dstAl->count()==1 &&
3007 dstAl->getFirst()->type=="void" )
3008 { // special case for finding match between func() and func(void)
3009 Argument *a=new Argument;
3010 a->type = "void";
3011 srcAl->append(a);
3012 MATCH
3013 return TRUE;
3014 }
3015 if ( dstAl->count()==0 && srcAl->count()==1 &&
3016 srcAl->getFirst()->type=="void" )
3017 { // special case for finding match between func(void) and func()
3018 Argument *a=new Argument;
3019 a->type = "void";
3020 dstAl->append(a);
3021 MATCH
3022 return TRUE;
3023 }
3024
3025 if (srcAl->count() != dstAl->count())
3026 {
3027 NOMATCH
3028 return FALSE; // different number of arguments -> no match
3029 }
3030
3031 if (checkCV)
3032 {
3033 if (srcAl->constSpecifier != dstAl->constSpecifier)
3034 {
3035 NOMATCH
3036 return FALSE; // one member is const, the other not -> no match
3037 }
3038 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3039 {
3040 NOMATCH
3041 return FALSE; // one member is volatile, the other not -> no match
3042 }
3043 }
3044
3045 // so far the argument list could match, so we need to compare the types of
3046 // all arguments.
3047 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3048 Argument *srcA,*dstA;
3049 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3050 {
3051 if (!matchArgument(srcA,dstA,className,namespaceName,
3052 usingNamespaces,usingClasses))
3053 {
3054 NOMATCH
3055 return FALSE;
3056 }
3057 }
3058 MATCH
3059 return TRUE; // all arguments match
3060}
3061
3062#endif
3063
3064#if 0
3065static QCString resolveSymbolName(FileDef *fs,Definition *symbol,QCString &templSpec)
3066{
3067 ASSERT(symbol!=0);
3068 if (symbol->definitionType()==Definition::TypeMember &&
3069 ((MemberDef*)symbol)->isTypedef()) // if symbol is a typedef then try
3070 // to resolve it
3071 {
3072 MemberDef *md = 0;
3073 ClassDef *cd = newResolveTypedef(fs,(MemberDef*)symbol,&md,&templSpec);
3074 if (cd)
3075 {
3076 return cd->qualifiedName()+templSpec;
3077 }
3078 else if (md)
3079 {
3080 return md->qualifiedName();
3081 }
3082 }
3083 return symbol->qualifiedName();
3084}
3085#endif
3086
3087static QCString stripDeclKeywords(const QCString &s)
3088{
3089 int i=s.find(" class ");
3090 if (i!=-1) return s.left(i)+s.mid(i+6);
3091 i=s.find(" typename ");
3092 if (i!=-1) return s.left(i)+s.mid(i+9);
3093 i=s.find(" union ");
3094 if (i!=-1) return s.left(i)+s.mid(i+6);
3095 i=s.find(" struct ");
3096 if (i!=-1) return s.left(i)+s.mid(i+7);
3097 return s;
3098}
3099
3100// forward decl for circular dependencies
3101static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type);
3102
3103QCString getCanonicalTemplateSpec(Definition *d,FileDef *fs,const QCString& spec)
3104{
3105
3106 QCString templSpec = spec.stripWhiteSpace();
3107 // this part had been commented out before... but it is needed to match for instance
3108 // std::list<std::string> against list<string> so it is now back again!
3109 if (!templSpec.isEmpty() && templSpec.at(0) == '<')
3110 {
3111 templSpec = "< " + extractCanonicalType(d,fs,templSpec.right(templSpec.length()-1).stripWhiteSpace());
3112 }
3113 QCString resolvedType = resolveTypeDef(d,templSpec);
3114 if (!resolvedType.isEmpty()) // not known as a typedef either
3115 {
3116 templSpec = resolvedType;
3117 }
3118 //printf("getCanonicalTemplateSpec(%s)=%s\n",spec.data(),templSpec.data());
3119 return templSpec;
3120}
3121
3122
3123static QCString getCanonicalTypeForIdentifier(
3124 Definition *d,FileDef *fs,const QCString &word,
3125 QCString *tSpec,int count=0)
3126{
3127 if (count>10) return word; // oops recursion
3128
3129 QCString symName,scope,result,templSpec,tmpName;
3130 //DefinitionList *defList=0;
3131 if (tSpec && !tSpec->isEmpty())
3132 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,*tSpec));
3133
3134 if (word.findRev("::")!=-1 && !(tmpName=stripScope(word)).isEmpty())
3135 {
3136 symName=tmpName; // name without scope
3137 }
3138 else
3139 {
3140 symName=word;
3141 }
3142 //printf("getCanonicalTypeForIdentifier(%s,[%s->%s]) start\n",
3143 // word.data(),tSpec?tSpec->data():"<none>",templSpec.data());
3144
3145 ClassDef *cd = 0;
3146 MemberDef *mType = 0;
3147 QCString ts;
3148 QCString resolvedType;
3149
3150 // lookup class / class template instance
3151 cd = getResolvedClass(d,fs,word+templSpec,&mType,&ts,TRUE,TRUE,&resolvedType);
3152 bool isTemplInst = cd && !templSpec.isEmpty();
3153 if (!cd && !templSpec.isEmpty())
3154 {
3155 // class template specialization not known, look up class template
3156 cd = getResolvedClass(d,fs,word,&mType,&ts,TRUE,TRUE,&resolvedType);
3157 }
3158 if (cd && cd->isUsedOnly()) cd=0; // ignore types introduced by usage relations
3159
3160 //printf("cd=%p mtype=%p\n",cd,mType);
3161 //printf(" getCanonicalTypeForIdentifer: symbol=%s word=%s cd=%s d=%s fs=%s cd->isTemplate=%d\n",
3162 // symName.data(),
3163 // word.data(),
3164 // cd?cd->name().data():"<none>",
3165 // d?d->name().data():"<none>",
3166 // fs?fs->name().data():"<none>",
3167 // cd?cd->isTemplate():-1
3168 // );
3169
3170 //printf(" >>>> word '%s' => '%s' templSpec=%s ts=%s tSpec=%s isTemplate=%d resolvedType=%s\n",
3171 // (word+templSpec).data(),
3172 // cd?cd->qualifiedName().data():"<none>",
3173 // templSpec.data(),ts.data(),
3174 // tSpec?tSpec->data():"<null>",
3175 // cd?cd->isTemplate():FALSE,
3176 // resolvedType.data());
3177
3178 //printf(" mtype=%s\n",mType?mType->name().data():"<none>");
3179
3180 if (cd) // resolves to a known class type
3181 {
3182 if (cd==d && tSpec) *tSpec="";
3183
3184 if (mType && mType->isTypedef()) // but via a typedef
3185 {
3186 result = resolvedType;
3187 }
3188 else
3189 {
3190 if (isTemplInst)
3191 {
3192 // spec is already part of class type
3193 templSpec="";
3194 if (tSpec) *tSpec="";
3195 }
3196 else if (!ts.isEmpty() && templSpec.isEmpty())
3197 {
3198 // use formal template args for spec
3199 templSpec = stripDeclKeywords(getCanonicalTemplateSpec(d,fs,ts));
3200 }
3201
3202 result = removeRedundantWhiteSpace(cd->qualifiedName() + templSpec);
3203
3204 if (cd->isTemplate() && tSpec) //
3205 {
3206 if (!templSpec.isEmpty()) // specific instance
3207 {
3208 result=cd->name()+templSpec;
3209 }
3210 else // use template type
3211 {
3212 result=cd->qualifiedNameWithTemplateParameters();
3213 }
3214 // template class, so remove the template part (it is part of the class name)
3215 *tSpec="";
3216 }
3217 else if (ts.isEmpty() && !templSpec.isEmpty() && cd && !cd->isTemplate() && tSpec)
3218 {
3219 // obscure case, where a class is used as a template, but doxygen think it is
3220 // not (could happen when loading the class from a tag file).
3221 *tSpec="";
3222 }
3223 }
3224 }
3225 else if (mType && mType->isEnumerate()) // an enum
3226 {
3227 result = mType->qualifiedName();
3228 }
3229 else if (mType && mType->isTypedef()) // a typedef
3230 {
3231 //result = mType->qualifiedName(); // changed after 1.7.2
3232 //result = mType->typeString();
3233 //printf("word=%s typeString=%s\n",word.data(),mType->typeString());
3234 if (word!=mType->typeString())
3235 {
3236 result = getCanonicalTypeForIdentifier(d,fs,mType->typeString(),tSpec,count+1);
3237 }
3238 else
3239 {
3240 result = mType->typeString();
3241 }
3242 }
3243 else // fallback
3244 {
3245 resolvedType = resolveTypeDef(d,word);
3246 //printf("typedef [%s]->[%s]\n",word.data(),resolvedType.data());
3247 if (resolvedType.isEmpty()) // not known as a typedef either
3248 {
3249 result = word;
3250 }
3251 else
3252 {
3253 result = resolvedType;
3254 }
3255 }
3256 //printf("getCanonicalTypeForIdentifier [%s]->[%s]\n",word.data(),result.data());
3257 return result;
3258}
3259
3260static QCString extractCanonicalType(Definition *d,FileDef *fs,QCString type)
3261{
3262 type = type.stripWhiteSpace();
3263
3264 // strip const and volatile keywords that are not relevant for the type
3265 stripIrrelevantConstVolatile(type);
3266
3267 // strip leading keywords
3268 type.stripPrefix("class ");
3269 type.stripPrefix("struct ");
3270 type.stripPrefix("union ");
3271 type.stripPrefix("enum ");
3272 type.stripPrefix("typename ");
3273
3274 type = removeRedundantWhiteSpace(type);
3275 //printf("extractCanonicalType(type=%s) start: def=%s file=%s\n",type.data(),
3276 // d ? d->name().data() : "<null>",fs ? fs->name().data() : "<null>");
3277
3278 //static QRegExp id("[a-z_A-Z\\x80-\\xFF][:a-z_A-Z0-9\\x80-\\xFF]*");
3279
3280 QCString canType;
3281 QCString templSpec,word;
3282 int i,p=0,pp=0;
3283 while ((i=extractClassNameFromType(type,p,word,templSpec))!=-1)
3284 // foreach identifier in the type
3285 {
3286 //printf(" i=%d p=%d\n",i,p);
3287 if (i>pp) canType += type.mid(pp,i-pp);
3288
3289
3290 QCString ct = getCanonicalTypeForIdentifier(d,fs,word,&templSpec);
3291
3292 // in case the ct is empty it means that "word" represents scope "d"
3293 // and this does not need to be added to the canonical
3294 // type (it is redundant), so/ we skip it. This solves problem 589616.
3295 if (ct.isEmpty() && type.mid(p,2)=="::")
3296 {
3297 p+=2;
3298 }
3299 else
3300 {
3301 canType += ct;
3302 }
3303 //printf(" word=%s templSpec=%s canType=%s ct=%s\n",
3304 // word.data(),templSpec.data(),canType.data(),ct.data());
3305 if (!templSpec.isEmpty()) // if we didn't use up the templSpec already
3306 // (i.e. type is not a template specialization)
3307 // then resolve any identifiers inside.
3308 {
3309 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
3310 int tp=0,tl,ti;
3311 // for each identifier template specifier
3312 //printf("adding resolved %s to %s\n",templSpec.data(),canType.data());
3313 while ((ti=re.match(templSpec,tp,&tl))!=-1)
3314 {
3315 canType += templSpec.mid(tp,ti-tp);
3316 canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0);
3317 tp=ti+tl;
3318 }
3319 canType+=templSpec.right(templSpec.length()-tp);
3320 }
3321
3322 pp=p;
3323 }
3324 canType += type.right(type.length()-pp);
3325 //printf("extractCanonicalType = '%s'->'%s'\n",type.data(),canType.data());
3326
3327 return removeRedundantWhiteSpace(canType);
3328}
3329
3330static QCString extractCanonicalArgType(Definition *d,FileDef *fs,const Argument *arg)
3331{
3332 QCString type = arg->type.stripWhiteSpace();
3333 QCString name = arg->name;
3334 //printf("----- extractCanonicalArgType(type=%s,name=%s)\n",type.data(),name.data());
3335 if ((type=="const" || type=="volatile") && !name.isEmpty())
3336 { // name is part of type => correct
3337 type+=" ";
3338 type+=name;
3339 }
3340 if (name=="const" || name=="volatile")
3341 { // name is part of type => correct
3342 if (!type.isEmpty()) type+=" ";
3343 type+=name;
3344 }
3345 if (!arg->array.isEmpty())
3346 {
3347 type+=arg->array;
3348 }
3349
3350 return extractCanonicalType(d,fs,type);
3351}
3352
3353static bool matchArgument2(
3354 Definition *srcScope,FileDef *srcFileScope,Argument *srcA,
3355 Definition *dstScope,FileDef *dstFileScope,Argument *dstA
3356 )
3357{
3358 //printf(">> match argument: %s::`%s|%s' (%s) <-> %s::`%s|%s' (%s)\n",
3359 // srcScope ? srcScope->name().data() : "",
3360 // srcA->type.data(),srcA->name.data(),srcA->canType.data(),
3361 // dstScope ? dstScope->name().data() : "",
3362 // dstA->type.data(),dstA->name.data(),dstA->canType.data());
3363
3364 //if (srcA->array!=dstA->array) // nomatch for char[] against char
3365 //{
3366 // NOMATCH
3367 // return FALSE;
3368 //}
3369 QCString sSrcName = " "+srcA->name;
3370 QCString sDstName = " "+dstA->name;
3371 QCString srcType = srcA->type;
3372 QCString dstType = dstA->type;
3373 stripIrrelevantConstVolatile(srcType);
3374 stripIrrelevantConstVolatile(dstType);
3375 //printf("'%s'<->'%s'\n",sSrcName.data(),dstType.right(sSrcName.length()).data());
3376 //printf("'%s'<->'%s'\n",sDstName.data(),srcType.right(sDstName.length()).data());
3377 if (sSrcName==dstType.right(sSrcName.length()))
3378 { // case "unsigned int" <-> "unsigned int i"
3379 srcA->type+=sSrcName;
3380 srcA->name="";
3381 srcA->canType=""; // invalidate cached type value
3382 }
3383 else if (sDstName==srcType.right(sDstName.length()))
3384 { // case "unsigned int i" <-> "unsigned int"
3385 dstA->type+=sDstName;
3386 dstA->name="";
3387 dstA->canType=""; // invalidate cached type value
3388 }
3389
3390 if (srcA->canType.isEmpty())
3391 {
3392 srcA->canType = extractCanonicalArgType(srcScope,srcFileScope,srcA);
3393 }
3394 if (dstA->canType.isEmpty())
3395 {
3396 dstA->canType = extractCanonicalArgType(dstScope,dstFileScope,dstA);
3397 }
3398
3399 if (srcA->canType==dstA->canType)
3400 {
3401 MATCH
3402 return TRUE;
3403 }
3404 else
3405 {
3406 //printf(" Canonical types do not match [%s]<->[%s]\n",
3407 // srcA->canType.data(),dstA->canType.data());
3408 NOMATCH
3409 return FALSE;
3410 }
3411}
3412
3413
3414// new algorithm for argument matching
3415bool matchArguments2(Definition *srcScope,FileDef *srcFileScope,ArgumentList *srcAl,
3416 Definition *dstScope,FileDef *dstFileScope,ArgumentList *dstAl,
3417 bool checkCV
3418 )
3419{
3420 //printf("*** matchArguments2\n");
3421 ASSERT(srcScope!=0 && dstScope!=0);
3422
3423 if (srcAl==0 || dstAl==0)
3424 {
3425 bool match = srcAl==dstAl; // at least one of the members is not a function
3426 if (match)
3427 {
3428 MATCH
3429 return TRUE;
3430 }
3431 else
3432 {
3433 NOMATCH
3434 return FALSE;
3435 }
3436 }
3437
3438 // handle special case with void argument
3439 if ( srcAl->count()==0 && dstAl->count()==1 &&
3440 dstAl->getFirst()->type=="void" )
3441 { // special case for finding match between func() and func(void)
3442 Argument *a=new Argument;
3443 a->type = "void";
3444 srcAl->append(a);
3445 MATCH
3446 return TRUE;
3447 }
3448 if ( dstAl->count()==0 && srcAl->count()==1 &&
3449 srcAl->getFirst()->type=="void" )
3450 { // special case for finding match between func(void) and func()
3451 Argument *a=new Argument;
3452 a->type = "void";
3453 dstAl->append(a);
3454 MATCH
3455 return TRUE;
3456 }
3457
3458 if (srcAl->count() != dstAl->count())
3459 {
3460 NOMATCH
3461 return FALSE; // different number of arguments -> no match
3462 }
3463
3464 if (checkCV)
3465 {
3466 if (srcAl->constSpecifier != dstAl->constSpecifier)
3467 {
3468 NOMATCH
3469 return FALSE; // one member is const, the other not -> no match
3470 }
3471 if (srcAl->volatileSpecifier != dstAl->volatileSpecifier)
3472 {
3473 NOMATCH
3474 return FALSE; // one member is volatile, the other not -> no match
3475 }
3476 }
3477
3478 // so far the argument list could match, so we need to compare the types of
3479 // all arguments.
3480 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3481 Argument *srcA,*dstA;
3482 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3483 {
3484 if (!matchArgument2(srcScope,srcFileScope,srcA,
3485 dstScope,dstFileScope,dstA)
3486 )
3487 {
3488 NOMATCH
3489 return FALSE;
3490 }
3491 }
3492 MATCH
3493 return TRUE; // all arguments match
3494}
3495
3496
3497
3498// merges the initializer of two argument lists
3499// pre: the types of the arguments in the list should match.
3500void mergeArguments(ArgumentList *srcAl,ArgumentList *dstAl,bool forceNameOverwrite)
3501{
3502 //printf("mergeArguments `%s', `%s'\n",
3503 // argListToString(srcAl).data(),argListToString(dstAl).data());
3504
3505 if (srcAl==0 || dstAl==0 || srcAl->count()!=dstAl->count())
3506 {
3507 return; // invalid argument lists -> do not merge
3508 }
3509
3510 ArgumentListIterator srcAli(*srcAl),dstAli(*dstAl);
3511 Argument *srcA,*dstA;
3512 for (;(srcA=srcAli.current(),dstA=dstAli.current());++srcAli,++dstAli)
3513 {
3514 if (srcA->defval.isEmpty() && !dstA->defval.isEmpty())
3515 {
3516 //printf("Defval changing `%s'->`%s'\n",srcA->defval.data(),dstA->defval.data());
3517 srcA->defval=dstA->defval.copy();
3518 }
3519 else if (!srcA->defval.isEmpty() && dstA->defval.isEmpty())
3520 {
3521 //printf("Defval changing `%s'->`%s'\n",dstA->defval.data(),srcA->defval.data());
3522 dstA->defval=srcA->defval.copy();
3523 }
3524
3525 // fix wrongly detected const or volatile specifiers before merging.
3526 // example: "const A *const" is detected as type="const A *" name="const"
3527 if (srcA->name=="const" || srcA->name=="volatile")
3528 {
3529 srcA->type+=" "+srcA->name;
3530 srcA->name.resize(0);
3531 }
3532 if (dstA->name=="const" || dstA->name=="volatile")
3533 {
3534 dstA->type+=" "+dstA->name;
3535 dstA->name.resize(0);
3536 }
3537
3538 if (srcA->type==dstA->type)
3539 {
3540 //printf("1. merging %s:%s <-> %s:%s\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3541 if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3542 {
3543 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3544 //printf("name: `%s':=`%s'\n",srcA->name.data(),dstA->name.data());
3545 srcA->type = dstA->type.copy();
3546 srcA->name = dstA->name.copy();
3547 }
3548 else if (!srcA->name.isEmpty() && dstA->name.isEmpty())
3549 {
3550 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3551 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3552 dstA->type = srcA->type.copy();
3553 dstA->name = dstA->name.copy();
3554 }
3555 else if (!srcA->name.isEmpty() && !dstA->name.isEmpty())
3556 {
3557 //printf("srcA->name=%s dstA->name=%s\n",srcA->name.data(),dstA->name.data());
3558 if (forceNameOverwrite)
3559 {
3560 srcA->name = dstA->name;
3561 }
3562 else
3563 {
3564 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3565 {
3566 srcA->name = dstA->name;
3567 }
3568 else if (!srcA->docs.isEmpty() && dstA->docs.isEmpty())
3569 {
3570 dstA->name = srcA->name;
3571 }
3572 }
3573 }
3574 }
3575 else
3576 {
3577 //printf("2. merging '%s':'%s' <-> '%s':'%s'\n",srcA->type.data(),srcA->name.data(),dstA->type.data(),dstA->name.data());
3578 srcA->type=srcA->type.stripWhiteSpace();
3579 dstA->type=dstA->type.stripWhiteSpace();
3580 if (srcA->type+" "+srcA->name==dstA->type) // "unsigned long:int" <-> "unsigned long int:bla"
3581 {
3582 srcA->type+=" "+srcA->name;
3583 srcA->name=dstA->name;
3584 }
3585 else if (dstA->type+" "+dstA->name==srcA->type) // "unsigned long int bla" <-> "unsigned long int"
3586 {
3587 dstA->type+=" "+dstA->name;
3588 dstA->name=srcA->name;
3589 }
3590 else if (srcA->name.isEmpty() && !dstA->name.isEmpty())
3591 {
3592 srcA->name = dstA->name;
3593 }
3594 else if (dstA->name.isEmpty() && !srcA->name.isEmpty())
3595 {
3596 dstA->name = srcA->name;
3597 }
3598 }
3599 int i1=srcA->type.find("::"),
3600 i2=dstA->type.find("::"),
3601 j1=srcA->type.length()-i1-2,
3602 j2=dstA->type.length()-i2-2;
3603 if (i1!=-1 && i2==-1 && srcA->type.right(j1)==dstA->type)
3604 {
3605 //printf("type: `%s':=`%s'\n",dstA->type.data(),srcA->type.data());
3606 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3607 dstA->type = srcA->type.left(i1+2)+dstA->type;
3608 dstA->name = dstA->name.copy();
3609 }
3610 else if (i1==-1 && i2!=-1 && dstA->type.right(j2)==srcA->type)
3611 {
3612 //printf("type: `%s':=`%s'\n",srcA->type.data(),dstA->type.data());
3613 //printf("name: `%s':=`%s'\n",dstA->name.data(),srcA->name.data());
3614 srcA->type = dstA->type.left(i2+2)+srcA->type;
3615 srcA->name = dstA->name.copy();
3616 }
3617 if (srcA->docs.isEmpty() && !dstA->docs.isEmpty())
3618 {
3619 srcA->docs = dstA->docs.copy();
3620 }
3621 else if (dstA->docs.isEmpty() && !srcA->docs.isEmpty())
3622 {
3623 dstA->docs = srcA->docs.copy();
3624 }
3625 //printf("Merge argument `%s|%s' `%s|%s'\n",
3626 // srcA->type.data(),srcA->name.data(),
3627 // dstA->type.data(),dstA->name.data());
3628 }
3629}
3630
3631static void findMembersWithSpecificName(MemberName *mn,
3632 const char *args,
3633 bool checkStatics,
3634 FileDef *currentFile,
3635 bool checkCV,
3636 QList<MemberDef> &members)
3637{
3638 //printf(" Function with global scope name `%s' args=`%s'\n",
3639 // mn->memberName(),args);
3640 MemberListIterator mli(*mn);
3641 MemberDef *md;
3642 for (mli.toFirst();(md=mli.current());++mli)
3643 {
3644 FileDef *fd=md->getFileDef();
3645 GroupDef *gd=md->getGroupDef();
3646 //printf(" md->name()=`%s' md->args=`%s' fd=%p gd=%p current=%p\n",
3647 // md->name().data(),args,fd,gd,currentFile);
3648 if (
3649 ((gd && gd->isLinkable()) || (fd && fd->isLinkable()) || md->isReference()) &&
3650 md->getNamespaceDef()==0 && md->isLinkable() &&
3651 (!checkStatics || (!md->isStatic() && !md->isDefine()) ||
3652 currentFile==0 || fd==currentFile) // statics must appear in the same file
3653 )
3654 {
3655 //printf(" fd=%p gd=%p args=`%s'\n",fd,gd,args);
3656 bool match=TRUE;
3657 ArgumentList *argList=0;
3658 if (args && !md->isDefine() && strcmp(args,"()")!=0)
3659 {
3660 argList=new ArgumentList;
3661 LockingPtr<ArgumentList> mdAl = md->argumentList();
3662 stringToArgumentList(args,argList);
3663 match=matchArguments2(
3664 md->getOuterScope(),fd,mdAl.pointer(),
3665 Doxygen::globalScope,fd,argList,
3666 checkCV);
3667 delete argList; argList=0;
3668 }
3669 if (match)
3670 {
3671 //printf("Found match!\n");
3672 members.append(md);
3673 }
3674 }
3675 }
3676}
3677
3678/*!
3679 * Searches for a member definition given its name `memberName' as a string.
3680 * memberName may also include a (partial) scope to indicate the scope
3681 * in which the member is located.
3682 *
3683 * The parameter `scName' is a string representing the name of the scope in
3684 * which the link was found.
3685 *
3686 * In case of a function args contains a string representation of the
3687 * argument list. Passing 0 means the member has no arguments.
3688 * Passing "()" means any argument list will do, but "()" is preferred.
3689 *
3690 * The function returns TRUE if the member is known and documented or
3691 * FALSE if it is not.
3692 * If TRUE is returned parameter `md' contains a pointer to the member
3693 * definition. Furthermore exactly one of the parameter `cd', `nd', or `fd'
3694 * will be non-zero:
3695 * - if `cd' is non zero, the member was found in a class pointed to by cd.
3696 * - if `nd' is non zero, the member was found in a namespace pointed to by nd.
3697 * - if `fd' is non zero, the member was found in the global namespace of
3698 * file fd.
3699 */
3700bool getDefs(const QCString &scName,const QCString &memberName,
3701 const char *args,
3702 MemberDef *&md,
3703 ClassDef *&cd, FileDef *&fd, NamespaceDef *&nd, GroupDef *&gd,
3704 bool forceEmptyScope,
3705 FileDef *currentFile,
3706 bool checkCV
3707 )
3708{
3709 fd=0, md=0, cd=0, nd=0, gd=0;
3710 if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
3711
3712 QCString scopeName=scName;
3713 //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n",
3714 // memberName.data(),args,scopeName.data(),forceEmptyScope);
3715
3716 int is,im=0,pm=0;
3717 // strip common part of the scope from the scopeName
3718 while ((is=scopeName.findRev("::"))!=-1 &&
3719 (im=memberName.find("::",pm))!=-1 &&
3720 (scopeName.right(scopeName.length()-is-2)==memberName.mid(pm,im-pm))
3721 )
3722 {
3723 scopeName=scopeName.left(is);
3724 pm=im+2;
3725 }
3726 //printf("result after scope corrections scope=%s name=%s\n",
3727 // scopeName.data(),memberName.data());
3728
3729 QCString mName=memberName;
3730 QCString mScope;
3731 if (memberName.left(9)!="operator " && // treat operator conversion methods
3732 // as a special case
3733 (im=memberName.findRev("::"))!=-1 &&
3734 im<(int)memberName.length()-2 // not A::
3735 )
3736 {
3737 mScope=memberName.left(im);
3738 mName=memberName.right(memberName.length()-im-2);
3739 }
3740
3741 // handle special the case where both scope name and member scope are equal
3742 if (mScope==scopeName) scopeName.resize(0);
3743
3744 //printf("mScope=`%s' mName=`%s'\n",mScope.data(),mName.data());
3745
3746 MemberName *mn = Doxygen::memberNameSDict->find(mName);
3747 //printf("mName=%s mn=%p\n",mName.data(),mn);
3748
3749 if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName
3750 mn && !(scopeName.isEmpty() && mScope.isEmpty()))
3751 {
3752 //printf(" >member name '%s' found\n",mName.data());
3753 int scopeOffset=scopeName.length();
3754 do
3755 {
3756 QCString className = scopeName.left(scopeOffset);
3757 if (!className.isEmpty() && !mScope.isEmpty())
3758 {
3759 className+="::"+mScope;
3760 }
3761 else if (!mScope.isEmpty())
3762 {
3763 className=mScope;
3764 }
3765
3766 ClassDef *fcd=getResolvedClass(Doxygen::globalScope,0,className);
3767 //printf("Trying class scope %s: %p\n",className.data(),fcd);
3768 // todo: fill in correct fileScope!
3769 if (fcd && // is it a documented class
3770 fcd->isLinkable()
3771 )
3772 {
3773 //printf(" Found fcd=%p\n",fcd);
3774 MemberListIterator mmli(*mn);
3775 MemberDef *mmd;
3776 int mdist=maxInheritanceDepth;
3777 ArgumentList *argList=0;
3778 if (args)
3779 {
3780 argList=new ArgumentList;
3781 stringToArgumentList(args,argList);
3782 }
3783 for (mmli.toFirst();(mmd=mmli.current());++mmli)
3784 {
3785 //if (mmd->isLinkable())
3786 //{
3787 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
3788 bool match=args==0 ||
3789 matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
3790 fcd,fcd->getFileDef(),argList,
3791 checkCV
3792 );
3793 //printf("match=%d\n",match);
3794 if (match)
3795 {
3796 ClassDef *mcd=mmd->getClassDef();
3797 if (mcd)
3798 {
3799 int m=minClassDistance(fcd,mcd);
3800 if (m<mdist && mcd->isLinkable())
3801 {
3802 mdist=m;
3803 cd=mcd;
3804 md=mmd;
3805 }
3806 }
3807 }
3808 //}
3809 }
3810 if (argList)
3811 {
3812 delete argList; argList=0;
3813 }
3814 if (mdist==maxInheritanceDepth && args && strcmp(args,"()")==0)
3815 // no exact match found, but if args="()" an arbitrary member will do
3816 {
3817 //printf(" >Searching for arbitrary member\n");
3818 for (mmli.toFirst();(mmd=mmli.current());++mmli)
3819 {
3820 //if (mmd->isLinkable())
3821 //{
3822 ClassDef *mcd=mmd->getClassDef();
3823 //printf(" >Class %s found\n",mcd->name().data());
3824 if (mcd)
3825 {
3826 int m=minClassDistance(fcd,mcd);
3827 if (m<mdist /* && mcd->isLinkable()*/ )
3828 {
3829 //printf("Class distance %d\n",m);
3830 mdist=m;
3831 cd=mcd;
3832 md=mmd;
3833 }
3834 }
3835 //}
3836 }
3837 }
3838 //printf(" >Succes=%d\n",mdist<maxInheritanceDepth);
3839 if (mdist<maxInheritanceDepth)
3840 {
3841 if (!md->isLinkable())
3842 {
3843 md=0; // avoid returning things we cannot link to
3844 cd=0;
3845 return FALSE; // match found, but was not linkable
3846 }
3847 else
3848 {
3849 gd=md->getGroupDef();
3850 if (gd) cd=0;
3851 return TRUE; /* found match */
3852 }
3853 }
3854 }
3855 /* go to the parent scope */
3856 if (scopeOffset==0)
3857 {
3858 scopeOffset=-1;
3859 }
3860 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
3861 {
3862 scopeOffset=0;
3863 }
3864 } while (scopeOffset>=0);
3865
3866 }
3867 if (mn && scopeName.isEmpty() && mScope.isEmpty()) // Maybe a related function?
3868 {
3869 MemberListIterator mmli(*mn);
3870 MemberDef *mmd, *fuzzy_mmd = 0;
3871 ArgumentList *argList = 0;
3872 bool hasEmptyArgs = args && strcmp(args, "()") == 0;
3873
3874 if (args)
3875 stringToArgumentList(args, argList = new ArgumentList);
3876
3877 for (mmli.toFirst(); (mmd = mmli.current()); ++mmli)
3878 {
3879 if (!mmd->isLinkable() || (!mmd->isRelated() && !mmd->isForeign()) ||
3880 !mmd->getClassDef())
3881 continue;
3882
3883 if (!args) break;
3884
3885 QCString className = mmd->getClassDef()->name();
3886
3887 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
3888 if (matchArguments2(mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
3889 Doxygen::globalScope,mmd->getFileDef(),argList,
3890 checkCV
3891 )
3892 ) break;
3893
3894 if (!fuzzy_mmd && hasEmptyArgs)
3895 fuzzy_mmd = mmd;
3896 }
3897
3898 if (argList) delete argList, argList = 0;
3899
3900 mmd = mmd ? mmd : fuzzy_mmd;
3901
3902 if (mmd)
3903 {
3904 md = mmd;
3905 cd = mmd->getClassDef();
3906 return TRUE;
3907 }
3908 }
3909
3910
3911 // maybe an namespace, file or group member ?
3912 //printf("Testing for global symbol scopeName=`%s' mScope=`%s' :: mName=`%s'\n",
3913 // scopeName.data(),mScope.data(),mName.data());
3914 if ((mn=Doxygen::functionNameSDict->find(mName))) // name is known
3915 {
3916 //printf(" >symbol name found\n");
3917 NamespaceDef *fnd=0;
3918 int scopeOffset=scopeName.length();
3919 do
3920 {
3921 QCString namespaceName = scopeName.left(scopeOffset);
3922 if (!namespaceName.isEmpty() && !mScope.isEmpty())
3923 {
3924 namespaceName+="::"+mScope;
3925 }
3926 else if (!mScope.isEmpty())
3927 {
3928 namespaceName=mScope.copy();
3929 }
3930 //printf("Trying namespace %s\n",namespaceName.data());
3931 if (!namespaceName.isEmpty() &&
3932 (fnd=Doxygen::namespaceSDict->find(namespaceName)) &&
3933 fnd->isLinkable()
3934 )
3935 {
3936 //printf("Function inside existing namespace `%s'\n",namespaceName.data());
3937 bool found=FALSE;
3938 MemberListIterator mmli(*mn);
3939 MemberDef *mmd;
3940 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
3941 {
3942 //printf("mmd->getNamespaceDef()=%p fnd=%p\n",
3943 // mmd->getNamespaceDef(),fnd);
3944 if (mmd->getNamespaceDef()==fnd /* && mmd->isLinkable() */ )
3945 { // namespace is found
3946 bool match=TRUE;
3947 ArgumentList *argList=0;
3948 if (args && strcmp(args,"()")!=0)
3949 {
3950 argList=new ArgumentList;
3951 LockingPtr<ArgumentList> mmdAl = mmd->argumentList();
3952 stringToArgumentList(args,argList);
3953 match=matchArguments2(
3954 mmd->getOuterScope(),mmd->getFileDef(),mmdAl.pointer(),
3955 fnd,mmd->getFileDef(),argList,
3956 checkCV);
3957 }
3958 if (match)
3959 {
3960 nd=fnd;
3961 md=mmd;
3962 found=TRUE;
3963 }
3964 if (args)
3965 {
3966 delete argList; argList=0;
3967 }
3968 }
3969 }
3970 if (!found && args && !strcmp(args,"()"))
3971 // no exact match found, but if args="()" an arbitrary
3972 // member will do
3973 {
3974 for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli)
3975 {
3976 if (mmd->getNamespaceDef()==fnd /*&& mmd->isLinkable() */ )
3977 {
3978 nd=fnd;
3979 md=mmd;
3980 found=TRUE;
3981 }
3982 }
3983 }
3984 if (found)
3985 {
3986 if (!md->isLinkable())
3987 {
3988 md=0; // avoid returning things we cannot link to
3989 nd=0;
3990 return FALSE; // match found but not linkable
3991 }
3992 else
3993 {
3994 gd=md->getGroupDef();
3995 if (gd && gd->isLinkable()) nd=0; else gd=0;
3996 return TRUE;
3997 }
3998 }
3999 }
4000 if (scopeOffset==0)
4001 {
4002 scopeOffset=-1;
4003 }
4004 else if ((scopeOffset=scopeName.findRev("::",scopeOffset-1))==-1)
4005 {
4006 scopeOffset=0;
4007 }
4008 } while (scopeOffset>=0);
4009
4010 //else // no scope => global function
4011 {
4012 QList<MemberDef> members;
4013 // search for matches with strict static checking
4014 findMembersWithSpecificName(mn,args,TRUE,currentFile,checkCV,members);
4015 if (members.count()==0) // nothing found
4016 {
4017 // search again without strict static checking
4018 findMembersWithSpecificName(mn,args,FALSE,currentFile,checkCV,members);
4019 }
4020 if (members.count()!=1 && args && !strcmp(args,"()"))
4021 {
4022 // no exact match found, but if args="()" an arbitrary
4023 // member will do
4024 md=mn->last();
4025 while (md /* && md->isLinkable()*/)
4026 {
4027 //printf("Found member `%s'\n",md->name().data());
4028 //printf("member is linkable md->name()=`%s'\n",md->name().data());
4029 fd=md->getFileDef();
4030 gd=md->getGroupDef();
4031 if (
4032 (gd && gd->isLinkable()) || (fd && fd->isLinkable())
4033 )
4034 {
4035 members.append(md);
4036 }
4037 md=mn->prev();
4038 }
4039 }
4040 //printf("found %d candidate members\n",members.count());
4041 if (members.count()>0) // at least one match
4042 {
4043 md=members.last();
4044 }
4045 if (md) // found a matching global member
4046 {
4047 fd=md->getFileDef();
4048 gd=md->getGroupDef();
4049 //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable());
4050 if (gd && gd->isLinkable()) fd=0; else gd=0;
4051 return TRUE;
4052 }
4053 }
4054 }
4055
4056 // no nothing found
4057 return FALSE;
4058}
4059
4060/*!
4061 * Searches for a scope definition given its name as a string via parameter
4062 * `scope'.
4063 *
4064 * The parameter `docScope' is a string representing the name of the scope in
4065 * which the `scope' string was found.
4066 *
4067 * The function returns TRUE if the scope is known and documented or
4068 * FALSE if it is not.
4069 * If TRUE is returned exactly one of the parameter `cd', `nd'
4070 * will be non-zero:
4071 * - if `cd' is non zero, the scope was a class pointed to by cd.
4072 * - if `nd' is non zero, the scope was a namespace pointed to by nd.
4073 */
4074static bool getScopeDefs(const char *docScope,const char *scope,
4075 ClassDef *&cd, NamespaceDef *&nd)
4076{
4077 cd=0;nd=0;
4078
4079 QCString scopeName=scope;
4080 //printf("getScopeDefs: docScope=`%s' scope=`%s'\n",docScope,scope);
4081 if (scopeName.isEmpty()) return FALSE;
4082
4083 bool explicitGlobalScope=FALSE;
4084 if (scopeName.at(0)==':' && scopeName.at(1)==':')
4085 {
4086 scopeName=scopeName.right(scopeName.length()-2);
4087 explicitGlobalScope=TRUE;
4088 }
4089
4090 QCString docScopeName=docScope;
4091 int scopeOffset=explicitGlobalScope ? 0 : docScopeName.length();
4092
4093 do // for each possible docScope (from largest to and including empty)
4094 {
4095 QCString fullName=scopeName.copy();
4096 if (scopeOffset>0) fullName.prepend(docScopeName.left(scopeOffset)+"::");
4097
4098 if (((cd=getClass(fullName)) || // normal class
4099 (cd=getClass(fullName+"-p")) || // ObjC protocol
4100 (cd=getClass(fullName+"-g")) // C# generic
4101 ) && cd->isLinkable())
4102 {
4103 return TRUE; // class link written => quit
4104 }
4105 else if ((nd=Doxygen::namespaceSDict->find(fullName)) && nd->isLinkable())
4106 {
4107 return TRUE; // namespace link written => quit
4108 }
4109 if (scopeOffset==0)
4110 {
4111 scopeOffset=-1;
4112 }
4113 else if ((scopeOffset=docScopeName.findRev("::",scopeOffset-1))==-1)
4114 {
4115 scopeOffset=0;
4116 }
4117 } while (scopeOffset>=0);
4118
4119 return FALSE;
4120}
4121
4122static bool isLowerCase(QCString &s)
4123{
4124 uchar *p=(uchar*)s.data();
4125 if (p==0) return TRUE;
4126 int c;
4127 while ((c=*p++)) if (!islower(c)) return FALSE;
4128 return TRUE;
4129}
4130
4131/*! Returns an object to reference to given its name and context
4132 * @post return value TRUE implies *resContext!=0 or *resMember!=0
4133 */
4134bool resolveRef(/* in */ const char *scName,
4135 /* in */ const char *name,
4136 /* in */ bool inSeeBlock,
4137 /* out */ Definition **resContext,
4138 /* out */ MemberDef **resMember,
4139 bool lookForSpecialization,
4140 FileDef *currentFile,
4141 bool checkScope
4142 )
4143{
4144 QCString tsName = name;
4145 //bool memberScopeFirst = tsName.find('#')!=-1;
4146 QCString fullName = substitute(tsName,"#","::");
4147 fullName = removeRedundantWhiteSpace(substitute(fullName,".","::"));
4148
4149 int bracePos=fullName.findRev('('); // reverse is needed for operator()(...)
4150 int endNamePos=bracePos!=-1 ? bracePos : fullName.length();
4151 int scopePos=fullName.findRev("::",endNamePos);
4152 bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope
4153 (scopePos>2 || // ::N::A
4154 tsName.left(2)=="::" || // ::foo in local scope
4155 scName==0 // #foo in global scope
4156 );
4157
4158 // default result values
4159 *resContext=0;
4160 *resMember=0;
4161
4162 if (bracePos==-1) // simple name
4163 {
4164 ClassDef *cd=0;
4165 NamespaceDef *nd=0;
4166
4167 // the following if() was commented out for releases in the range
4168 // 1.5.2 to 1.6.1, but has been restored as a result of bug report 594787.
4169 if (!inSeeBlock && scopePos==-1 && isLowerCase(tsName))
4170 { // link to lower case only name => do not try to autolink
4171 return FALSE;
4172 }
4173
4174 //printf("scName=%s fullName=%s\n",scName,fullName.data());
4175
4176 // check if this is a class or namespace reference
4177 if (scName!=fullName && getScopeDefs(scName,fullName,cd,nd))
4178 {
4179 if (cd) // scope matches that of a class
4180 {
4181 *resContext = cd;
4182 }
4183 else // scope matches that of a namespace
4184 {
4185 ASSERT(nd!=0);
4186 *resContext = nd;
4187 }
4188 return TRUE;
4189 }
4190 else if (scName==fullName || (!inSeeBlock && scopePos==-1))
4191 // nothing to link => output plain text
4192 {
4193 //printf("found scName=%s fullName=%s scName==fullName=%d "
4194 // "inSeeBlock=%d scopePos=%d!\n",
4195 // scName,fullName.data(),scName==fullName,inSeeBlock,scopePos);
4196 return FALSE;
4197 }
4198 // continue search...
4199 }
4200
4201 // extract userscope+name
4202 QCString nameStr=fullName.left(endNamePos);
4203 if (explicitScope) nameStr=nameStr.mid(2);
4204
4205 // extract arguments
4206 QCString argsStr;
4207 if (bracePos!=-1) argsStr=fullName.right(fullName.length()-bracePos);
4208
4209 // strip template specifier
4210 // TODO: match against the correct partial template instantiation
4211 int templPos=nameStr.find('<');
4212 bool tryUnspecializedVersion = FALSE;
4213 if (templPos!=-1 && nameStr.find("operator")==-1)
4214 {
4215 int endTemplPos=nameStr.findRev('>');
4216 if (endTemplPos!=-1)
4217 {
4218 if (!lookForSpecialization)
4219 {
4220 nameStr=nameStr.left(templPos)+nameStr.right(nameStr.length()-endTemplPos-1);
4221 }
4222 else
4223 {
4224 tryUnspecializedVersion = TRUE;
4225 }
4226 }
4227 }
4228
4229 QCString scopeStr=scName;
4230
4231 MemberDef *md = 0;
4232 ClassDef *cd = 0;
4233 FileDef *fd = 0;
4234 NamespaceDef *nd = 0;
4235 GroupDef *gd = 0;
4236
4237 // check if nameStr is a member or global.
4238 //printf("getDefs(scope=%s,name=%s,args=%s checkScope=%d)\n",
4239 // scopeStr.data(),nameStr.data(),argsStr.data(),checkScope);
4240 if (getDefs(scopeStr,nameStr,argsStr,
4241 md,cd,fd,nd,gd,
4242 //scopePos==0 && !memberScopeFirst, // forceEmptyScope
4243 explicitScope, // replaces prev line due to bug 600829
4244 currentFile,
4245 TRUE // checkCV
4246 )
4247 )
4248 {
4249 //printf("after getDefs checkScope=%d nameStr=%s cd=%p nd=%p\n",checkScope,nameStr.data(),cd,nd);
4250 if (checkScope && md && md->getOuterScope()==Doxygen::globalScope &&
4251 (!scopeStr.isEmpty() || nameStr.find("::")>0))
4252 {
4253 // we did find a member, but it is a global one while we were explicitly
4254 // looking for a scoped variable. See bug 616387 for an example why this check is needed.
4255 // note we do need to support autolinking to "::symbol" hence the >0
4256 //printf("not global member!\n");
4257 *resContext=0;
4258 *resMember=0;
4259 return FALSE;
4260 }
4261 //printf("after getDefs md=%p cd=%p fd=%p nd=%p gd=%p\n",md,cd,fd,nd,gd);
4262 if (md) { *resMember=md; *resContext=md; }
4263 else if (cd) *resContext=cd;
4264 else if (nd) *resContext=nd;
4265 else if (fd) *resContext=fd;
4266 else if (gd) *resContext=gd;
4267 else { *resContext=0; *resMember=0; return FALSE; }
4268 //printf("member=%s (md=%p) anchor=%s linkable()=%d context=%s\n",
4269 // md->name().data(),md,md->anchor().data(),md->isLinkable(),(*resContext)->name().data());
4270 return TRUE;
4271 }
4272 else if (inSeeBlock && !nameStr.isEmpty() && (gd=Doxygen::groupSDict->find(nameStr)))
4273 { // group link
4274 *resContext=gd;
4275 return TRUE;
4276 }
4277 else if (tsName.find('.')!=-1) // maybe a link to a file
4278 {
4279 bool ambig;
4280 fd=findFileDef(Doxygen::inputNameDict,tsName,ambig);
4281 if (fd && !ambig)
4282 {
4283 *resContext=fd;
4284 return TRUE;
4285 }
4286 }
4287
4288 if (tryUnspecializedVersion)
4289 {
4290 return resolveRef(scName,name,inSeeBlock,resContext,resMember,FALSE,0,checkScope);
4291 }
4292 //printf("resolveRef: %s not found!\n",name);
4293
4294 return FALSE;
4295}
4296
4297QCString linkToText(const char *link,bool isFileName)
4298{
4299 static bool optimizeOutputJava = Config_getBool("OPTIMIZE_OUTPUT_JAVA");
4300 QCString result=link;
4301 if (!result.isEmpty())
4302 {
4303 // replace # by ::
4304 result=substitute(result,"#","::");
4305 // replace . by ::
4306 if (!isFileName) result=substitute(result,".","::");
4307 // strip leading :: prefix if present
4308 if (result.at(0)==':' && result.at(1)==':')
4309 {
4310 result=result.right(result.length()-2);
4311 }
4312 if (optimizeOutputJava)
4313 {
4314 result=substitute(result,"::",".");
4315 }
4316 }
4317 return result;
4318}
4319
4320#if 0
4321/*
4322 * generate a reference to a class, namespace or member.
4323 * `scName' is the name of the scope that contains the documentation
4324 * string that is returned.
4325 * `name' is the name that we want to link to.
4326 * `name' may have five formats:
4327 * 1) "ScopeName"
4328 * 2) "memberName()" one of the (overloaded) function or define
4329 * with name memberName.
4330 * 3) "memberName(...)" a specific (overloaded) function or define
4331 * with name memberName
4332 * 4) "::name a global variable or define
4333 * 4) "\#memberName member variable, global variable or define
4334 * 5) ("ScopeName::")+"memberName()"
4335 * 6) ("ScopeName::")+"memberName(...)"
4336 * 7) ("ScopeName::")+"memberName"
4337 * instead of :: the \# symbol may also be used.
4338 */
4339
4340bool generateRef(OutputDocInterface &od,const char *scName,
4341 const char *name,bool inSeeBlock,const char *rt)
4342{
4343 //printf("generateRef(scName=%s,name=%s,inSee=%d,rt=%s)\n",scName,name,inSeeBlock,rt);
4344
4345 Definition *compound;
4346 MemberDef *md;
4347
4348 // create default link text
4349 QCString linkText = linkToText(rt,FALSE);
4350
4351 if (resolveRef(scName,name,inSeeBlock,&compound,&md))
4352 {
4353 if (md && md->isLinkable()) // link to member
4354 {
4355 od.writeObjectLink(md->getReference(),
4356 md->getOutputFileBase(),
4357 md->anchor(),linkText);
4358 // generate the page reference (for LaTeX)
4359 if (!md->isReference())
4360 {
4361 writePageRef(od,md->getOutputFileBase(),md->anchor());
4362 }
4363 return TRUE;
4364 }
4365 else if (compound && compound->isLinkable()) // link to compound
4366 {
4367 if (rt==0 && compound->definitionType()==Definition::TypeGroup)
4368 {
4369 linkText=((GroupDef *)compound)->groupTitle();
4370 }
4371 if (compound && compound->definitionType()==Definition::TypeFile)
4372 {
4373 linkText=linkToText(rt,TRUE);
4374 }
4375 od.writeObjectLink(compound->getReference(),
4376 compound->getOutputFileBase(),
4377 0,linkText);
4378 if (!compound->isReference())
4379 {
4380 writePageRef(od,compound->getOutputFileBase(),0);
4381 }
4382 return TRUE;
4383 }
4384 }
4385 od.docify(linkText);
4386 return FALSE;
4387}
4388#endif
4389
4390bool resolveLink(/* in */ const char *scName,
4391 /* in */ const char *lr,
4392 /* in */ bool /*inSeeBlock*/,
4393 /* out */ Definition **resContext,
4394 /* out */ QCString &resAnchor
4395 )
4396{
4397 *resContext=0;
4398
4399 QCString linkRef=lr;
4400 //printf("ResolveLink linkRef=%s inSee=%d\n",lr,inSeeBlock);
4401 FileDef *fd;
4402 GroupDef *gd;
4403 PageDef *pd;
4404 ClassDef *cd;
4405 DirDef *dir;
4406 NamespaceDef *nd;
4407 bool ambig;
4408 if (linkRef.isEmpty()) // no reference name!
4409 {
4410 return FALSE;
4411 }
4412 else if ((pd=Doxygen::pageSDict->find(linkRef))) // link to a page
4413 {
4414 GroupDef *gd = pd->getGroupDef();
4415 if (gd)
4416 {
4417 SectionInfo *si=0;
4418 if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()];
4419 *resContext=gd;
4420 if (si) resAnchor = si->label;
4421 }
4422 else
4423 {
4424 *resContext=pd;
4425 }
4426 return TRUE;
4427 }
4428 else if ((pd=Doxygen::exampleSDict->find(linkRef))) // link to an example
4429 {
4430 *resContext=pd;
4431 return TRUE;
4432 }
4433 else if ((gd=Doxygen::groupSDict->find(linkRef))) // link to a group
4434 {
4435 *resContext=gd;
4436 return TRUE;
4437 }
4438 else if ((fd=findFileDef(Doxygen::inputNameDict,linkRef,ambig)) // file link
4439 && fd->isLinkable())
4440 {
4441 *resContext=fd;
4442 return TRUE;
4443 }
4444 else if ((cd=getClass(linkRef))) // class link
4445 {
4446 *resContext=cd;
4447 resAnchor=cd->anchor();
4448 return TRUE;
4449 }
4450 else if ((cd=getClass(linkRef+"-p"))) // Obj-C protocol link
4451 {
4452 *resContext=cd;
4453 resAnchor=cd->anchor();
4454 return TRUE;
4455 }
4456 else if ((cd=getClass(linkRef+"-g"))) // C# generic link
4457 {
4458 *resContext=cd;
4459 resAnchor=cd->anchor();
4460 return TRUE;
4461 }
4462 else if ((nd=Doxygen::namespaceSDict->find(linkRef)))
4463 {
4464 *resContext=nd;
4465 return TRUE;
4466 }
4467 else if ((dir=Doxygen::directories->find(QFileInfo(linkRef).absFilePath()+"/"))
4468 && dir->isLinkable()) // TODO: make this location independent like filedefs
4469 {
4470 *resContext=dir;
4471 return TRUE;
4472 }
4473 else // probably a member reference
4474 {
4475 MemberDef *md;
4476 bool res = resolveRef(scName,lr,TRUE,resContext,&md);
4477 if (md) resAnchor=md->anchor();
4478 return res;
4479 }
4480}
4481
4482
4483//----------------------------------------------------------------------
4484// General function that generates the HTML code for a reference to some
4485// file, class or member from text `lr' within the context of class `clName'.
4486// This link has the text 'lt' (if not 0), otherwise `lr' is used as a
4487// basis for the link's text.
4488// returns TRUE if a link could be generated.
4489
4490bool generateLink(OutputDocInterface &od,const char *clName,
4491 const char *lr,bool inSeeBlock,const char *lt)
4492{
4493 //printf("generateLink(clName=%s,lr=%s,lr=%s)\n",clName,lr,lt);
4494 Definition *compound;
4495 //PageDef *pageDef=0;
4496 QCString anchor,linkText=linkToText(lt,FALSE);
4497 //printf("generateLink linkText=%s\n",linkText.data());
4498 if (resolveLink(clName,lr,inSeeBlock,&compound,anchor))
4499 {
4500 if (compound) // link to compound
4501 {
4502 if (lt==0 && anchor.isEmpty() && /* compound link */
4503 compound->definitionType()==Definition::TypeGroup /* is group */
4504 )
4505 {
4506 linkText=((GroupDef *)compound)->groupTitle(); // use group's title as link
4507 }
4508 else if (compound->definitionType()==Definition::TypeFile)
4509 {
4510 linkText=linkToText(lt,TRUE);
4511 }
4512 od.writeObjectLink(compound->getReference(),
4513 compound->getOutputFileBase(),anchor,linkText);
4514 if (!compound->isReference())
4515 {
4516 writePageRef(od,compound->getOutputFileBase(),anchor);
4517 }
4518 }
4519 else
4520 {
4521 err("%s:%d: Internal error: resolveLink successful but no compound found!",__FILE__,__LINE__);
4522 }
4523 return TRUE;
4524 }
4525 else // link could not be found
4526 {
4527 od.docify(linkText);
4528 return FALSE;
4529 }
4530}
4531
4532void generateFileRef(OutputDocInterface &od,const char *name,const char *text)
4533{
4534 //printf("generateFileRef(%s,%s)\n",name,text);
4535 QCString linkText = text ? text : name;
4536 //FileInfo *fi;
4537 FileDef *fd;
4538 bool ambig;
4539 if ((fd=findFileDef(Doxygen::inputNameDict,name,ambig)) &&
4540 fd->isLinkable())
4541 // link to documented input file
4542 od.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,linkText);
4543 else
4544 od.docify(linkText);
4545}
4546
4547//----------------------------------------------------------------------
4548
4549#if 0
4550QCString substituteClassNames(const QCString &s)
4551{
4552 int i=0,l,p;
4553 QCString result;
4554 if (s.isEmpty()) return result;
4555 QRegExp r("[a-z_A-Z][a-z_A-Z0-9]*");
4556 while ((p=r.match(s,i,&l))!=-1)
4557 {
4558 QCString *subst;
4559 if (p>i) result+=s.mid(i,p-i);
4560 if ((subst=substituteDict[s.mid(p,l)]))
4561 {
4562 result+=*subst;
4563 }
4564 else
4565 {
4566 result+=s.mid(p,l);
4567 }
4568 i=p+l;
4569 }
4570 result+=s.mid(i,s.length()-i);
4571 return result;
4572}
4573#endif
4574
4575//----------------------------------------------------------------------
4576
4577struct FindFileCacheElem
4578{
4579 FindFileCacheElem(FileDef *fd,bool ambig) : fileDef(fd), isAmbig(ambig) {}
4580 FileDef *fileDef;
4581 bool isAmbig;
4582};
4583
4584static QCache<FindFileCacheElem> g_findFileDefCache(5000);
4585
4586FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
4587{
4588 ambig=FALSE;
4589 if (n==0) return 0;
4590
4591 QCString key;
4592 key.sprintf("%p:",fnDict);
4593 key+=n;
4594
4595 g_findFileDefCache.setAutoDelete(TRUE);
4596 FindFileCacheElem *cachedResult = g_findFileDefCache.find(key);
4597 //printf("key=%s cachedResult=%p\n",key.data(),cachedResult);
4598 if (cachedResult)
4599 {
4600 ambig = cachedResult->isAmbig;
4601 //printf("cached: fileDef=%p\n",cachedResult->fileDef);
4602 return cachedResult->fileDef;
4603 }
4604 else
4605 {
4606 cachedResult = new FindFileCacheElem(0,FALSE);
4607 }
4608
4609 QCString name=convertToQCString(QDir::cleanDirPath(n));
4610 QCString path;
4611 int slashPos;
4612 FileName *fn;
4613 if (name.isEmpty()) goto exit;
4614 slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4615 if (slashPos!=-1)
4616 {
4617 path=name.left(slashPos+1);
4618 name=name.right(name.length()-slashPos-1);
4619 //printf("path=%s name=%s\n",path.data(),name.data());
4620 }
4621 if (name.isEmpty()) goto exit;
4622 if ((fn=(*fnDict)[name]))
4623 {
4624 //printf("fn->count()=%d\n",fn->count());
4625 if (fn->count()==1)
4626 {
4627 FileDef *fd = fn->getFirst();
4628 if (path.isEmpty() || fd->getPath().right(path.length())==path)
4629 {
4630 cachedResult->fileDef = fd;
4631 g_findFileDefCache.insert(key,cachedResult);
4632 //printf("=1 ===> add to cache %p\n",fd);
4633 return fd;
4634 }
4635 }
4636 else // file name alone is ambiguous
4637 {
4638 int count=0;
4639 FileNameIterator fni(*fn);
4640 FileDef *fd;
4641 FileDef *lastMatch=0;
4642 QCString pathStripped = stripFromIncludePath(path);
4643 for (fni.toFirst();(fd=fni.current());++fni)
4644 {
4645 QCString fdStripPath = stripFromIncludePath(fd->getPath());
4646 if (path.isEmpty() || fdStripPath.right(pathStripped.length())==pathStripped)
4647 {
4648 count++;
4649 lastMatch=fd;
4650 }
4651 }
4652 //printf(">1 ===> add to cache %p\n",fd);
4653
4654 ambig=(count>1);
4655 cachedResult->isAmbig = ambig;
4656 cachedResult->fileDef = lastMatch;
4657 g_findFileDefCache.insert(key,cachedResult);
4658 return lastMatch;
4659 }
4660 }
4661 else
4662 {
4663 //printf("not found!\n");
4664 }
4665exit:
4666 //printf("0 ===> add to cache %p: %s\n",cachedResult,n);
4667 g_findFileDefCache.insert(key,cachedResult);
4668 //delete cachedResult;
4669 return 0;
4670}
4671
4672//----------------------------------------------------------------------
4673
4674QCString showFileDefMatches(const FileNameDict *fnDict,const char *n)
4675{
4676 QCString result;
4677 QCString name=n;
4678 QCString path;
4679 int slashPos=QMAX(name.findRev('/'),name.findRev('\\'));
4680 if (slashPos!=-1)
4681 {
4682 path=name.left(slashPos+1);
4683 name=name.right(name.length()-slashPos-1);
4684 }
4685 FileName *fn;
4686 if ((fn=(*fnDict)[name]))
4687 {
4688 FileNameIterator fni(*fn);
4689 FileDef *fd;
4690 for (fni.toFirst();(fd=fni.current());++fni)
4691 {
4692 if (path.isEmpty() || fd->getPath().right(path.length())==path)
4693 {
4694 result+=" "+fd->absFilePath()+"\n";
4695 }
4696 }
4697 }
4698 return result;
4699}
4700
4701//----------------------------------------------------------------------
4702
4703QCString substituteKeywords(const QCString &s,const char *title)
4704{
4705 QCString result = s;
4706 if (title) result = substitute(result,"$title",title);
4707 result = substitute(result,"$datetime",dateToString(TRUE));
4708 result = substitute(result,"$date",dateToString(FALSE));
4709 result = substitute(result,"$year",yearToString());
4710 result = substitute(result,"$doxygenversion",versionString);
4711 result = substitute(result,"$projectname",Config_getString("PROJECT_NAME"));
4712 result = substitute(result,"$projectnumber",Config_getString("PROJECT_NUMBER"));
4713 result = substitute(result,"$projectbrief",Config_getString("PROJECT_BRIEF"));
4714 result = substitute(result,"$projectlogo",Config_getString("PROJECT_LOGO"));
4715 return result;
4716}
4717
4718//----------------------------------------------------------------------
4719
4720/*! Returns the character index within \a name of the first prefix
4721 * in Config_getList("IGNORE_PREFIX") that matches \a name at the left hand side,
4722 * or zero if no match was found
4723 */
4724int getPrefixIndex(const QCString &name)
4725{
4726 if (name.isEmpty()) return 0;
4727 static QStrList &sl = Config_getList("IGNORE_PREFIX");
4728 char *s = sl.first();
4729 while (s)
4730 {
4731 const char *ps=s;
4732 const char *pd=name.data();
4733 int i=0;
4734 while (*ps!=0 && *pd!=0 && *ps==*pd) ps++,pd++,i++;
4735 if (*ps==0 && *pd!=0)
4736 {
4737 return i;
4738 }
4739 s = sl.next();
4740 }
4741 return 0;
4742}
4743
4744//----------------------------------------------------------------------------
4745
4746static void initBaseClassHierarchy(BaseClassList *bcl)
4747{
4748 if (bcl==0) return;
4749 BaseClassListIterator bcli(*bcl);
4750 for ( ; bcli.current(); ++bcli)
4751 {
4752 ClassDef *cd=bcli.current()->classDef;
4753 if (cd->baseClasses()==0) // no base classes => new root
4754 {
4755 initBaseClassHierarchy(cd->baseClasses());
4756 }
4757 cd->visited=FALSE;
4758 }
4759}
4760
4761//----------------------------------------------------------------------------
4762
4763void initClassHierarchy(ClassSDict *cl)
4764{
4765 ClassSDict::Iterator cli(*cl);
4766 ClassDef *cd;
4767 for ( ; (cd=cli.current()); ++cli)
4768 {
4769 cd->visited=FALSE;
4770 initBaseClassHierarchy(cd->baseClasses());
4771 }
4772}
4773
4774//----------------------------------------------------------------------------
4775
4776bool hasVisibleRoot(BaseClassList *bcl)
4777{
4778 if (bcl)
4779 {
4780 BaseClassListIterator bcli(*bcl);
4781 for ( ; bcli.current(); ++bcli)
4782 {
4783 ClassDef *cd=bcli.current()->classDef;
4784 if (cd->isVisibleInHierarchy()) return TRUE;
4785 hasVisibleRoot(cd->baseClasses());
4786 }
4787 }
4788 return FALSE;
4789}
4790
4791//----------------------------------------------------------------------
4792
4793// note that this function is not reentrant due to the use of static strBuf!
4794QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscore)
4795{
4796 static bool caseSenseNames = Config_getBool("CASE_SENSE_NAMES");
4797 static StrBuf strBuf;
4798 strBuf.clear();
4799 char c;
4800 const char *p=name;
4801 while ((c=*p++)!=0)
4802 {
4803 switch(c)
4804 {
4805 case '_': if (allowUnderscore) strBuf.addChar('_'); else strBuf.addStr("__"); break;
4806 case '-': strBuf.addChar('-'); break;
4807 case ':': strBuf.addStr("_1"); break;
4808 case '/': strBuf.addStr("_2"); break;
4809 case '<': strBuf.addStr("_3"); break;
4810 case '>': strBuf.addStr("_4"); break;
4811 case '*': strBuf.addStr("_5"); break;
4812 case '&': strBuf.addStr("_6"); break;
4813 case '|': strBuf.addStr("_7"); break;
4814 case '.': if (allowDots) strBuf.addChar('.'); else strBuf.addStr("_8"); break;
4815 case '!': strBuf.addStr("_9"); break;
4816 case ',': strBuf.addStr("_00"); break;
4817 case ' ': strBuf.addStr("_01"); break;
4818 case '{': strBuf.addStr("_02"); break;
4819 case '}': strBuf.addStr("_03"); break;
4820 case '?': strBuf.addStr("_04"); break;
4821 case '^': strBuf.addStr("_05"); break;
4822 case '%': strBuf.addStr("_06"); break;
4823 case '(': strBuf.addStr("_07"); break;
4824 case ')': strBuf.addStr("_08"); break;
4825 case '+': strBuf.addStr("_09"); break;
4826 case '=': strBuf.addStr("_0A"); break;
4827 default:
4828 if (c<0)
4829 {
4830 static char map[] = "0123456789ABCDEF";
4831 char ids[5];
4832 unsigned char id = (unsigned char)c;
4833 ids[0]='_';
4834 ids[1]='x';
4835 ids[2]=map[id>>4];
4836 ids[3]=map[id&0xF];
4837 ids[4]=0;
4838 strBuf.addStr(ids);
4839 }
4840 else if (caseSenseNames || !isupper(c))
4841 {
4842 strBuf.addChar(c);
4843 }
4844 else
4845 {
4846 strBuf.addChar('_');
4847 strBuf.addChar(tolower(c));
4848 }
4849 break;
4850 }
4851 }
4852 strBuf.addChar(0);
4853 return strBuf.get();
4854}
4855
4856/*! This function determines the file name on disk of an item
4857 * given its name, which could be a class name with template
4858 * arguments, so special characters need to be escaped.
4859 */
4860QCString convertNameToFile(const char *name,bool allowDots,bool allowUnderscore)
4861{
4862 static bool shortNames = Config_getBool("SHORT_NAMES");
4863 static bool createSubdirs = Config_getBool("CREATE_SUBDIRS");
4864 QCString result;
4865 if (shortNames) // use short names only
4866 {
4867 static QDict<int> usedNames(10007);
4868 usedNames.setAutoDelete(TRUE);
4869 static int count=1;
4870
4871 int *value=usedNames.find(name);
4872 int num;
4873 if (value==0)
4874 {
4875 usedNames.insert(name,new int(count));
4876 num = count++;
4877 }
4878 else
4879 {
4880 num = *value;
4881 }
4882 result.sprintf("a%05d",num);
4883 }
4884 else // long names
4885 {
4886 result=escapeCharsInString(name,allowDots,allowUnderscore);
4887 int resultLen = result.length();
4888 if (resultLen>=128) // prevent names that cannot be created!
4889 {
4890 // third algorithm based on MD5 hash
4891 uchar md5_sig[16];
4892 QCString sigStr(33);
4893 MD5Buffer((const unsigned char *)result.data(),resultLen,md5_sig);
4894 MD5SigToString(md5_sig,sigStr.data(),33);
4895 result=result.left(128-32)+sigStr;
4896 }
4897 }
4898 if (createSubdirs)
4899 {
4900 int l1Dir=0,l2Dir=0;
4901
4902#if MAP_ALGO==ALGO_COUNT
4903 // old algorithm, has the problem that after regeneration the
4904 // output can be located in a different dir.
4905 if (Doxygen::htmlDirMap==0)
4906 {
4907 Doxygen::htmlDirMap=new QDict<int>(100003);
4908 Doxygen::htmlDirMap->setAutoDelete(TRUE);
4909 }
4910 static int curDirNum=0;
4911 int *dirNum = Doxygen::htmlDirMap->find(result);
4912 if (dirNum==0) // new name
4913 {
4914 Doxygen::htmlDirMap->insert(result,new int(curDirNum));
4915 l1Dir = (curDirNum)&0xf; // bits 0-3
4916 l2Dir = (curDirNum>>4)&0xff; // bits 4-11
4917 curDirNum++;
4918 }
4919 else // existing name
4920 {
4921 l1Dir = (*dirNum)&0xf; // bits 0-3
4922 l2Dir = ((*dirNum)>>4)&0xff; // bits 4-11
4923 }
4924#elif MAP_ALGO==ALGO_CRC16
4925 // second algorithm based on CRC-16 checksum
4926 int dirNum = qChecksum(result,result.length());
4927 l1Dir = dirNum&0xf;
4928 l2Dir = (dirNum>>4)&0xff;
4929#elif MAP_ALGO==ALGO_MD5
4930 // third algorithm based on MD5 hash
4931 uchar md5_sig[16];
4932 MD5Buffer((const unsigned char *)result.data(),result.length(),md5_sig);
4933 l1Dir = md5_sig[14]&0xf;
4934 l2Dir = md5_sig[15];
4935#endif
4936 result.prepend(QCString().sprintf("d%x/d%02x/",l1Dir,l2Dir));
4937 }
4938 //printf("*** convertNameToFile(%s)->%s\n",name,result.data());
4939 return result;
4940}
4941
4942QCString relativePathToRoot(const char *name)
4943{
4944 QCString result;
4945 if (Config_getBool("CREATE_SUBDIRS"))
4946 {
4947 if (name==0)
4948 {
4949 return REL_PATH_TO_ROOT;
4950 }
4951 else
4952 {
4953 QCString n = name;
4954 int i = n.findRev('/');
4955 if (i!=-1)
4956 {
4957 result=REL_PATH_TO_ROOT;
4958 }
4959 }
4960 }
4961 return result;
4962}
4963
4964void createSubDirs(QDir &d)
4965{
4966 if (Config_getBool("CREATE_SUBDIRS"))
4967 {
4968 // create 4096 subdirectories
4969 int l1,l2;
4970 for (l1=0;l1<16;l1++)
4971 {
4972 d.mkdir(QString().sprintf("d%x",l1));
4973 for (l2=0;l2<256;l2++)
4974 {
4975 d.mkdir(QString().sprintf("d%x/d%02x",l1,l2));
4976 }
4977 }
4978 }
4979}
4980
4981/*! Input is a scopeName, output is the scopename split into a
4982 * namespace part (as large as possible) and a classname part.
4983 */
4984void extractNamespaceName(const QCString &scopeName,
4985 QCString &className,QCString &namespaceName,
4986 bool allowEmptyClass)
4987{
4988 int i,p;
4989 QCString clName=scopeName;
4990 NamespaceDef *nd = 0;
4991 if (!clName.isEmpty() && (nd=getResolvedNamespace(clName)) && getClass(clName)==0)
4992 { // the whole name is a namespace (and not a class)
4993 namespaceName=nd->name().copy();
4994 className.resize(0);
4995 goto done;
4996 }
4997 p=clName.length()-2;
4998 while (p>=0 && (i=clName.findRev("::",p))!=-1)
4999 // see if the first part is a namespace (and not a class)
5000 {
5001 //printf("Trying %s\n",clName.left(i).data());
5002 if (i>0 && (nd=getResolvedNamespace(clName.left(i))) && getClass(clName.left(i))==0)
5003 {
5004 //printf("found!\n");
5005 namespaceName=nd->name().copy();
5006 className=clName.right(clName.length()-i-2);
5007 goto done;
5008 }
5009 p=i-2; // try a smaller piece of the scope
5010 }
5011 //printf("not found!\n");
5012
5013 // not found, so we just have to guess.
5014 className=scopeName.copy();
5015 namespaceName.resize(0);
5016
5017done:
5018 if (className.isEmpty() && !namespaceName.isEmpty() && !allowEmptyClass)
5019 {
5020 // class and namespace with the same name, correct to return the class.
5021 className=namespaceName.copy();
5022 namespaceName.resize(0);
5023 }
5024 //printf("extractNamespace `%s' => `%s|%s'\n",scopeName.data(),
5025 // className.data(),namespaceName.data());
5026 return;
5027}
5028
5029QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &templ)
5030{
5031 QCString result=scope.copy();
5032 if (!templ.isEmpty() && scope.find('<')==-1)
5033 {
5034 int si,pi=0;
5035 ClassDef *cd=0;
5036 while (
5037 (si=scope.find("::",pi))!=-1 && !getClass(scope.left(si)+templ) &&
5038 ((cd=getClass(scope.left(si)))==0 || cd->templateArguments()==0)
5039 )
5040 {
5041 //printf("Tried `%s'\n",(scope.left(si)+templ).data());
5042 pi=si+2;
5043 }
5044 if (si==-1) // not nested => append template specifier
5045 {
5046 result+=templ;
5047 }
5048 else // nested => insert template specifier before after first class name
5049 {
5050 result=scope.left(si) + templ + scope.right(scope.length()-si);
5051 }
5052 }
5053 //printf("insertTemplateSpecifierInScope(`%s',`%s')=%s\n",
5054 // scope.data(),templ.data(),result.data());
5055 return result;
5056}
5057
5058#if 0 // original version
5059/*! Strips the scope from a name. Examples: A::B will return A
5060 * and A<T>::B<N::C<D> > will return A<T>.
5061 */
5062QCString stripScope(const char *name)
5063{
5064 QCString result = name;
5065 int l=result.length();
5066 int p=l-1;
5067 bool done;
5068 int count;
5069
5070 while (p>=0)
5071 {
5072 char c=result.at(p);
5073 switch (c)
5074 {
5075 case ':':
5076 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5077 return result.right(l-p-1);
5078 case '>':
5079 count=1;
5080 done=FALSE;
5081 //printf("pos < = %d\n",p);
5082 p--;
5083 while (p>=0 && !done)
5084 {
5085 c=result.at(p--);
5086 switch (c)
5087 {
5088 case '>': count++; break;
5089 case '<': count--; if (count<=0) done=TRUE; break;
5090 default:
5091 //printf("c=%c count=%d\n",c,count);
5092 break;
5093 }
5094 }
5095 //printf("pos > = %d\n",p+1);
5096 break;
5097 default:
5098 p--;
5099 }
5100 }
5101 //printf("stripScope(%s)=%s\n",name,name);
5102 return name;
5103}
5104#endif
5105
5106// new version by Davide Cesari which also works for Fortran
5107QCString stripScope(const char *name)
5108{
5109 QCString result = name;
5110 int l=result.length();
5111 int p;
5112 bool done = FALSE;
5113 bool skipBracket=FALSE; // if brackets do not match properly, ignore them altogether
5114 int count=0;
5115
5116 do
5117 {
5118 p=l-1; // start at the end of the string
5119 while (p>=0 && count>=0)
5120 {
5121 char c=result.at(p);
5122 switch (c)
5123 {
5124 case ':':
5125 //printf("stripScope(%s)=%s\n",name,result.right(l-p-1).data());
5126 return result.right(l-p-1);
5127 case '>':
5128 if (skipBracket) // we don't care about brackets
5129 {
5130 p--;
5131 }
5132 else // count open/close brackets
5133 {
5134 if (p>0 && result.at(p-1)=='>') // skip >> operator
5135 {
5136 p-=2;
5137 break;
5138 }
5139 count=1;
5140 //printf("pos < = %d\n",p);
5141 p--;
5142 bool foundMatch=false;
5143 while (p>=0 && !foundMatch)
5144 {
5145 c=result.at(p--);
5146 switch (c)
5147 {
5148 case '>':
5149 count++;
5150 break;
5151 case '<':
5152 if (p>0)
5153 {
5154 if (result.at(p-1) == '<') // skip << operator
5155 {
5156 p--;
5157 break;
5158 }
5159 }
5160 count--;
5161 foundMatch = count==0;
5162 break;
5163 default:
5164 //printf("c=%c count=%d\n",c,count);
5165 break;
5166 }
5167 }
5168 }
5169 //printf("pos > = %d\n",p+1);
5170 break;
5171 default:
5172 p--;
5173 }
5174 }
5175 done = count==0 || skipBracket; // reparse if brackets do not match
5176 skipBracket=TRUE;
5177 }
5178 while (!done); // if < > unbalanced repeat ignoring them
5179 //printf("stripScope(%s)=%s\n",name,name);
5180 return name;
5181}
5182
5183
5184/*! Converts a string to an XML-encoded string */
5185QCString convertToXML(const char *s)
5186{
5187 static StrBuf strBuf;
5188 strBuf.clear();
5189 if (s==0) return "";
5190 const char *p=s;
5191 char c;
5192 while ((c=*p++))
5193 {
5194 switch (c)
5195 {
5196 case '<': strBuf.addStr("&lt;"); break;
5197 case '>': strBuf.addStr("&gt;"); break;
5198 case '&': strBuf.addStr("&amp;"); break;
5199 case '\'': strBuf.addStr("&apos;"); break;
5200 case '"': strBuf.addStr("&quot;"); break;
5201 default: strBuf.addChar(c); break;
5202 }
5203 }
5204 strBuf.addChar(0);
5205 return strBuf.get();
5206}
5207
5208/*! Converts a string to a HTML-encoded string */
5209QCString convertToHtml(const char *s,bool keepEntities)
5210{
5211 static StrBuf strBuf;
5212 strBuf.clear();
5213 if (s==0) return "";
5214 const char *p=s;
5215 char c;
5216 while ((c=*p++))
5217 {
5218 switch (c)
5219 {
5220 case '<': strBuf.addStr("&lt;"); break;
5221 case '>': strBuf.addStr("&gt;"); break;
5222 case '&': if (keepEntities)
5223 {
5224 const char *e=p;
5225 char ce;
5226 while ((ce=*e++))
5227 {
5228 if (ce==';' || (!(isId(ce) || ce=='#'))) break;
5229 }
5230 if (ce==';') // found end of an entity
5231 {
5232 // copy entry verbatim
5233 strBuf.addChar(c);
5234 while (p<e) strBuf.addChar(*p++);
5235 }
5236 else
5237 {
5238 strBuf.addStr("&amp;");
5239 }
5240 }
5241 else
5242 {
5243 strBuf.addStr("&amp;");
5244 }
5245 break;
5246 case '\'': strBuf.addStr("&#39;"); break;
5247 case '"': strBuf.addStr("&quot;"); break;
5248 default: strBuf.addChar(c); break;
5249 }
5250 }
5251 strBuf.addChar(0);
5252 return strBuf.get();
5253}
5254
5255QCString convertToJSString(const char *s)
5256{
5257 static StrBuf strBuf;
5258 strBuf.clear();
5259 if (s==0) return "";
5260 const char *p=s;
5261 char c;
5262 while ((c=*p++))
5263 {
5264 switch (c)
5265 {
5266 case '"': strBuf.addStr("\\\""); break;
5267 default: strBuf.addChar(c); break;
5268 }
5269 }
5270 strBuf.addChar(0);
5271 return strBuf.get();
5272}
5273
5274
5275QCString convertCharEntitiesToUTF8(const QCString &s)
5276{
5277 static QDict<char> entityMap(67);
5278 static bool init=TRUE;
5279 QCString result;
5280 static QRegExp entityPat("&[a-zA-Z]+;");
5281
5282 if (init)
5283 {
5284 entityMap.insert("copy", "\xC2\xA9");
5285 entityMap.insert("tm", "\xE2\x84\xA2");
5286 entityMap.insert("trade", "\xE2\x84\xA2");
5287 entityMap.insert("reg", "\xC2\xAE");
5288 entityMap.insert("lsquo", "\xE2\x80\x98");
5289 entityMap.insert("rsquo", "\xE2\x80\x99");
5290 entityMap.insert("ldquo", "\xE2\x80\x9C");
5291 entityMap.insert("rdquo", "\xE2\x80\x9D");
5292 entityMap.insert("ndash", "\xE2\x80\x93");
5293 entityMap.insert("mdash", "\xE2\x80\x94");
5294 entityMap.insert("Auml", "\xC3\x84");
5295 entityMap.insert("Euml", "\xC3\x8B");
5296 entityMap.insert("Iuml", "\xC3\x8F");
5297 entityMap.insert("Ouml", "\xC3\x96");
5298 entityMap.insert("Uuml", "\xC3\x9C");
5299 entityMap.insert("Yuml", "\xC5\xB8");
5300 entityMap.insert("auml", "\xC3\xA4");
5301 entityMap.insert("euml", "\xC3\xAB");
5302 entityMap.insert("iuml", "\xC3\xAF");
5303 entityMap.insert("ouml", "\xC3\xB6");
5304 entityMap.insert("uuml", "\xC3\xBC");
5305 entityMap.insert("yuml", "\xC3\xBF");
5306 entityMap.insert("Aacute","\xC3\x81");
5307 entityMap.insert("Eacute","\xC3\x89");
5308 entityMap.insert("Iacute","\xC3\x8D");
5309 entityMap.insert("Oacute","\xC3\x93");
5310 entityMap.insert("Uacute","\xC3\x9A");
5311 entityMap.insert("aacute","\xC3\xA1");
5312 entityMap.insert("eacute","\xC3\xA9");
5313 entityMap.insert("iacute","\xC3\xAD");
5314 entityMap.insert("oacute","\xC3\xB3");
5315 entityMap.insert("uacute","\xC3\xBA");
5316 entityMap.insert("Agrave","\xC3\x80");
5317 entityMap.insert("Egrave","\xC3\x88");
5318 entityMap.insert("Igrave","\xC3\x8C");
5319 entityMap.insert("Ograve","\xC3\x92");
5320 entityMap.insert("Ugrave","\xC3\x99");
5321 entityMap.insert("agrave","\xC3\xA0");
5322 entityMap.insert("egrave","\xC3\xA8");
5323 entityMap.insert("igrave","\xC3\xAC");
5324 entityMap.insert("ograve","\xC3\xB2");
5325 entityMap.insert("ugrave","\xC3\xB9");
5326 entityMap.insert("Acirc", "\xC3\x82");
5327 entityMap.insert("Ecirc", "\xC3\x8A");
5328 entityMap.insert("Icirc", "\xC3\x8E");
5329 entityMap.insert("Ocirc", "\xC3\x94");
5330 entityMap.insert("Ucirc", "\xC3\x9B");
5331 entityMap.insert("acirc", "\xC3\xA2");
5332 entityMap.insert("ecirc", "\xC3\xAA");
5333 entityMap.insert("icirc", "\xC3\xAE");
5334 entityMap.insert("ocirc", "\xC3\xB4");
5335 entityMap.insert("ucirc", "\xC3\xBB");
5336 entityMap.insert("Atilde","\xC3\x83");
5337 entityMap.insert("Ntilde","\xC3\x91");
5338 entityMap.insert("Otilde","\xC3\x95");
5339 entityMap.insert("atilde","\xC3\xA3");
5340 entityMap.insert("ntilde","\xC3\xB1");
5341 entityMap.insert("otilde","\xC3\xB5");
5342 entityMap.insert("szlig", "\xC3\x9F");
5343 entityMap.insert("Ccedil","\xC3\x87");
5344 entityMap.insert("ccedil","\xC3\xA7");
5345 entityMap.insert("Aring", "\xC3\x85");
5346 entityMap.insert("aring", "\xC3\xA5");
5347 entityMap.insert("nbsp", "\xC2\xA0");
5348 init=FALSE;
5349 }
5350
5351 if (s==0) return result;
5352 int p,i=0,l;
5353 while ((p=entityPat.match(s,i,&l))!=-1)
5354 {
5355 if (p>i) result+=s.mid(i,p-i);
5356 QCString entity = s.mid(p+1,l-2);
5357 char *code = entityMap.find(entity);
5358 if (code)
5359 {
5360 result+=code;
5361 }
5362 else
5363 {
5364 result+=s.mid(p,l);
5365 }
5366 i=p+l;
5367 }
5368 result+=s.mid(i,s.length()-i);
5369 return result;
5370}
5371
5372/*! Returns the standard string that is generated when the \\overload
5373 * command is used.
5374 */
5375QCString getOverloadDocs()
5376{
5377 return theTranslator->trOverloadText();
5378 //"This is an overloaded member function, "
5379 // "provided for convenience. It differs from the above "
5380 // "function only in what argument(s) it accepts.";
5381}
5382
5383void addMembersToMemberGroup(MemberList *ml,
5384 MemberGroupSDict **ppMemberGroupSDict,
5385 Definition *context)
5386{
5387 ASSERT(context!=0);
5388 //printf("addMemberToMemberGroup()\n");
5389 if (ml==0) return;
5390 MemberListIterator mli(*ml);
5391 MemberDef *md;
5392 uint index;
5393 for (index=0;(md=mli.current());)
5394 {
5395 if (md->isEnumerate()) // insert enum value of this enum into groups
5396 {
5397 LockingPtr<MemberList> fmdl=md->enumFieldList();
5398 if (fmdl!=0)
5399 {
5400 MemberDef *fmd=fmdl->first();
5401 while (fmd)
5402 {
5403 int groupId=fmd->getMemberGroupId();
5404 if (groupId!=-1)
5405 {
5406 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5407 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5408 //QCString *pDocs = Doxygen::memberDocDict[groupId];
5409 if (info)
5410 {
5411 if (*ppMemberGroupSDict==0)
5412 {
5413 *ppMemberGroupSDict = new MemberGroupSDict;
5414 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5415 }
5416 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5417 if (mg==0)
5418 {
5419 mg = new MemberGroup(
5420 context,
5421 groupId,
5422 info->header,
5423 info->doc,
5424 info->docFile
5425 );
5426 (*ppMemberGroupSDict)->append(groupId,mg);
5427 }
5428 mg->insertMember(fmd); // insert in member group
5429 fmd->setMemberGroup(mg);
5430 }
5431 }
5432 fmd=fmdl->next();
5433 }
5434 }
5435 }
5436 int groupId=md->getMemberGroupId();
5437 if (groupId!=-1)
5438 {
5439 MemberGroupInfo *info = Doxygen::memGrpInfoDict[groupId];
5440 //QCString *pGrpHeader = Doxygen::memberHeaderDict[groupId];
5441 //QCString *pDocs = Doxygen::memberDocDict[groupId];
5442 if (info)
5443 {
5444 if (*ppMemberGroupSDict==0)
5445 {
5446 *ppMemberGroupSDict = new MemberGroupSDict;
5447 (*ppMemberGroupSDict)->setAutoDelete(TRUE);
5448 }
5449 MemberGroup *mg = (*ppMemberGroupSDict)->find(groupId);
5450 if (mg==0)
5451 {
5452 mg = new MemberGroup(
5453 context,
5454 groupId,
5455 info->header,
5456 info->doc,
5457 info->docFile
5458 );
5459 (*ppMemberGroupSDict)->append(groupId,mg);
5460 }
5461 md = ml->take(index); // remove from member list
5462 mg->insertMember(md); // insert in member group
5463 mg->setRefItems(info->m_sli);
5464 md->setMemberGroup(mg);
5465 continue;
5466 }
5467 }
5468 ++mli;++index;
5469 }
5470}
5471
5472/*! Extracts a (sub-)string from \a type starting at \a pos that
5473 * could form a class. The index of the match is returned and the found
5474 * class \a name and a template argument list \a templSpec. If -1 is returned
5475 * there are no more matches.
5476 */
5477int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCString &templSpec)
5478{
5479 static const QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9:\\x80-\\xFF]*");
5480 name.resize(0);
5481 templSpec.resize(0);
5482 int i,l;
5483 int typeLen=type.length();
5484 if (typeLen>0)
5485 {
5486 if ((i=re.match(type,pos,&l))!=-1) // for each class name in the type
5487 {
5488 int ts=i+l;
5489 int te=ts;
5490 int tl=0;
5491 while (type.at(ts)==' ' && ts<typeLen) ts++,tl++; // skip any whitespace
5492 if (type.at(ts)=='<') // assume template instance
5493 {
5494 // locate end of template
5495 te=ts+1;
5496 int brCount=1;
5497 while (te<typeLen && brCount!=0)
5498 {
5499 if (type.at(te)=='<')
5500 {
5501 if (te<typeLen-1 && type.at(te+1)=='<') te++; else brCount++;
5502 }
5503 if (type.at(te)=='>')
5504 {
5505 if (te<typeLen-1 && type.at(te+1)=='>') te++; else brCount--;
5506 }
5507 te++;
5508 }
5509 }
5510 name = type.mid(i,l);
5511 if (te>ts)
5512 {
5513 templSpec = type.mid(ts,te-ts),tl+=te-ts;
5514 pos=i+l+tl;
5515 }
5516 else // no template part
5517 {
5518 pos=i+l;
5519 }
5520 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=TRUE\n",
5521 // type.data(),pos,name.data(),templSpec.data());
5522 return i;
5523 }
5524 }
5525 pos = typeLen;
5526 //printf("extractClassNameFromType([in] type=%s,[out] pos=%d,[out] name=%s,[out] templ=%s)=FALSE\n",
5527 // type.data(),pos,name.data(),templSpec.data());
5528 return -1;
5529}
5530
5531/*! Substitutes any occurrence of a formal argument from argument list
5532 * \a formalArgs in \a name by the corresponding actual argument in
5533 * argument list \a actualArgs. The result after substitution
5534 * is returned as a string. The argument \a name is used to
5535 * prevent recursive substitution.
5536 */
5537QCString substituteTemplateArgumentsInString(
5538 const QCString &name,
5539 ArgumentList *formalArgs,
5540 ArgumentList *actualArgs)
5541{
5542 //printf("substituteTemplateArgumentsInString(name=%s formal=%s actualArg=%s)\n",
5543 // name.data(),argListToString(formalArgs).data(),argListToString(actualArgs).data());
5544 if (formalArgs==0) return name;
5545 QCString result;
5546 static QRegExp re("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
5547 int p=0,l,i;
5548 // for each identifier in the base class name (e.g. B<T> -> B and T)
5549 while ((i=re.match(name,p,&l))!=-1)
5550 {
5551 result += name.mid(p,i-p);
5552 QCString n = name.mid(i,l);
5553 ArgumentListIterator formAli(*formalArgs);
5554 Argument *formArg;
5555 Argument *actArg=actualArgs->first();
5556
5557 // if n is a template argument, then we substitute it
5558 // for its template instance argument.
5559 bool found=FALSE;
5560 for (formAli.toFirst();
5561 (formArg=formAli.current()) && !found;
5562 ++formAli,actArg=actualArgs->next()
5563 )
5564 {
5565 if (formArg->type.left(6)=="class " && formArg->name.isEmpty())
5566 {
5567 formArg->name = formArg->type.mid(6);
5568 formArg->type = "class";
5569 }
5570 if (formArg->type.left(9)=="typename " && formArg->name.isEmpty())
5571 {
5572 formArg->name = formArg->type.mid(9);
5573 formArg->type = "typename";
5574 }
5575 if (formArg->type=="class" || formArg->type=="typename" || formArg->type.left(8)=="template")
5576 {
5577 //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n",
5578 // n.data(),formArg->type.data(),formArg->name.data(),formArg->defval.data());
5579 //printf(">> formArg->name='%s' actArg->type='%s' actArg->name='%s'\n",
5580 // formArg->name.data(),actArg->type.data(),actArg->name.data()
5581 // );
5582 if (formArg->name==n && actArg && !actArg->type.isEmpty()) // base class is a template argument
5583 {
5584 // replace formal argument with the actual argument of the instance
5585 if (!leftScopeMatch(actArg->type,n))
5586 // the scope guard is to prevent recursive lockup for
5587 // template<class A> class C : public<A::T>,
5588 // where A::T would become A::T::T here,
5589 // since n==A and actArg->type==A::T
5590 // see bug595833 for an example
5591 {
5592 if (actArg->name.isEmpty())
5593 {
5594 result += actArg->type+" ";
5595 found=TRUE;
5596 }
5597 else
5598 // for case where the actual arg is something like "unsigned int"
5599 // the "int" part is in actArg->name.
5600 {
5601 result += actArg->type+" "+actArg->name+" ";
5602 found=TRUE;
5603 }
5604 }
5605 }
5606 else if (formArg->name==n &&
5607 actArg==0 &&
5608 !formArg->defval.isEmpty() &&
5609 formArg->defval!=name /* to prevent recursion */
5610 )
5611 {
5612 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
5613 found=TRUE;
5614 }
5615 }
5616 else if (formArg->name==n &&
5617 actArg==0 &&
5618 !formArg->defval.isEmpty() &&
5619 formArg->defval!=name /* to prevent recursion */
5620 )
5621 {
5622 result += substituteTemplateArgumentsInString(formArg->defval,formalArgs,actualArgs)+" ";
5623 found=TRUE;
5624 }
5625 }
5626 if (!found) result += n;
5627 p=i+l;
5628 }
5629 result+=name.right(name.length()-p);
5630 //printf(" Inheritance relation %s -> %s\n",
5631 // name.data(),result.data());
5632 return result.stripWhiteSpace();
5633}
5634
5635
5636/*! Makes a deep copy of argument list \a src. Will allocate memory, that
5637 * is owned by the caller.
5638 */
5639ArgumentList *copyArgumentList(const ArgumentList *src)
5640{
5641 ASSERT(src!=0);
5642 ArgumentList *dst = new ArgumentList;
5643 dst->setAutoDelete(TRUE);
5644 ArgumentListIterator tali(*src);
5645 Argument *a;
5646 for (;(a=tali.current());++tali)
5647 {
5648 dst->append(new Argument(*a));
5649 }
5650 dst->constSpecifier = src->constSpecifier;
5651 dst->volatileSpecifier = src->volatileSpecifier;
5652 dst->pureSpecifier = src->pureSpecifier;
5653 return dst;
5654}
5655
5656/*! Makes a deep copy of the list of argument lists \a srcLists.
5657 * Will allocate memory, that is owned by the caller.
5658 */
5659QList<ArgumentList> *copyArgumentLists(const QList<ArgumentList> *srcLists)
5660{
5661 ASSERT(srcLists!=0);
5662 QList<ArgumentList> *dstLists = new QList<ArgumentList>;
5663 dstLists->setAutoDelete(TRUE);
5664 QListIterator<ArgumentList> sli(*srcLists);
5665 ArgumentList *sl;
5666 for (;(sl=sli.current());++sli)
5667 {
5668 dstLists->append(copyArgumentList(sl));
5669 }
5670 return dstLists;
5671}
5672
5673/*! Strips template specifiers from scope \a fullName, except those
5674 * that make up specialized classes. The switch \a parentOnly
5675 * determines whether or not a template "at the end" of a scope
5676 * should be considered, e.g. with \a parentOnly is \c TRUE, A<T>::B<S> will
5677 * try to strip \<T\> and not \<S\>, while \a parentOnly is \c FALSE will
5678 * strip both unless A<T> or B<S> are specialized template classes.
5679 */
5680QCString stripTemplateSpecifiersFromScope(const QCString &fullName,
5681 bool parentOnly,
5682 QCString *pLastScopeStripped)
5683{
5684 QCString result;
5685 int p=0;
5686 int l=fullName.length();
5687 int i=fullName.find('<');
5688 while (i!=-1)
5689 {
5690 //printf("1:result+=%s\n",fullName.mid(p,i-p).data());
5691 int e=i+1;
5692 bool done=FALSE;
5693 int count=1;
5694 while (e<l && !done)
5695 {
5696 char c=fullName.at(e++);
5697 if (c=='<')
5698 {
5699 count++;
5700 }
5701 else if (c=='>')
5702 {
5703 count--;
5704 done = count==0;
5705 }
5706 }
5707 int si= fullName.find("::",e);
5708
5709 if (parentOnly && si==-1) break;
5710 // we only do the parent scope, so we stop here if needed
5711
5712 result+=fullName.mid(p,i-p);
5713 //printf(" trying %s\n",(result+fullName.mid(i,e-i)).data());
5714 if (getClass(result+fullName.mid(i,e-i))!=0)
5715 {
5716 result+=fullName.mid(i,e-i);
5717 //printf(" 2:result+=%s cd=%s\n",fullName.mid(i,e-i-1).data(),cd->name().data());
5718 }
5719 else if (pLastScopeStripped)
5720 {
5721 //printf(" last stripped scope '%s'\n",fullName.mid(i,e-i).data());
5722 *pLastScopeStripped=fullName.mid(i,e-i);
5723 }
5724 p=e;
5725 i=fullName.find('<',p);
5726 }
5727 result+=fullName.right(l-p);
5728 //printf("3:result+=%s\n",fullName.right(l-p).data());
5729 return result;
5730}
5731
5732/*! Merges two scope parts together. The parts may (partially) overlap.
5733 * Example1: \c A::B and \c B::C will result in \c A::B::C <br>
5734 * Example2: \c A and \c B will be \c A::B <br>
5735 * Example3: \c A::B and B will be \c A::B
5736 *
5737 * @param leftScope the left hand part of the scope.
5738 * @param rightScope the right hand part of the scope.
5739 * @returns the merged scope.
5740 */
5741QCString mergeScopes(const QCString &leftScope,const QCString &rightScope)
5742{
5743 // case leftScope=="A" rightScope=="A::B" => result = "A::B"
5744 if (leftScopeMatch(rightScope,leftScope)) return rightScope;
5745 QCString result;
5746 int i=0,p=leftScope.length();
5747
5748 // case leftScope=="A::B" rightScope=="B::C" => result = "A::B::C"
5749 // case leftScope=="A::B" rightScope=="B" => result = "A::B"
5750 bool found=FALSE;
5751 while ((i=leftScope.findRev("::",p))!=-1)
5752 {
5753 if (leftScopeMatch(rightScope,leftScope.right(leftScope.length()-i-2)))
5754 {
5755 result = leftScope.left(i+2)+rightScope;
5756 found=TRUE;
5757 }
5758 p=i-1;
5759 }
5760 if (found) return result;
5761
5762 // case leftScope=="A" rightScope=="B" => result = "A::B"
5763 result=leftScope.copy();
5764 if (!result.isEmpty() && !rightScope.isEmpty()) result+="::";
5765 result+=rightScope;
5766 return result;
5767}
5768
5769/*! Returns a fragment from scope \a s, starting at position \a p.
5770 *
5771 * @param s the scope name as a string.
5772 * @param p the start position (0 is the first).
5773 * @param l the resulting length of the fragment.
5774 * @returns the location of the fragment, or -1 if non is found.
5775 */
5776int getScopeFragment(const QCString &s,int p,int *l)
5777{
5778 int sl=s.length();
5779 int sp=p;
5780 int count=0;
5781 bool done;
5782 if (sp>=sl) return -1;
5783 while (sp<sl)
5784 {
5785 char c=s.at(sp);
5786 if (c==':') sp++,p++; else break;
5787 }
5788 while (sp<sl)
5789 {
5790 char c=s.at(sp);
5791 switch (c)
5792 {
5793 case ':': // found next part
5794 goto found;
5795 case '<': // skip template specifier
5796 count=1;sp++;
5797 done=FALSE;
5798 while (sp<sl && !done)
5799 {
5800 // TODO: deal with << and >> operators!
5801 char c=s.at(sp++);
5802 switch(c)
5803 {
5804 case '<': count++; break;
5805 case '>': count--; if (count==0) done=TRUE; break;
5806 default: break;
5807 }
5808 }
5809 break;
5810 default:
5811 sp++;
5812 break;
5813 }
5814 }
5815found:
5816 *l=sp-p;
5817 //printf("getScopeFragment(%s,%d)=%s\n",s.data(),p,s.mid(p,*l).data());
5818 return p;
5819}
5820
5821//----------------------------------------------------------------------------
5822
5823PageDef *addRelatedPage(const char *name,const QCString &ptitle,
5824 const QCString &doc,
5825 QList<SectionInfo> * /*anchors*/,
5826 const char *fileName,int startLine,
5827 const QList<ListItemInfo> *sli,
5828 GroupDef *gd,
5829 TagInfo *tagInfo
5830 )
5831{
5832 PageDef *pd=0;
5833 //printf("addRelatedPage(name=%s gd=%p)\n",name,gd);
5834 if ((pd=Doxygen::pageSDict->find(name)) && !tagInfo)
5835 {
5836 // append documentation block to the page.
5837 pd->setDocumentation(doc,fileName,startLine);
5838 //printf("Adding page docs `%s' pi=%p name=%s\n",doc.data(),pi,name);
5839 }
5840 else // new page
5841 {
5842 QCString baseName=name;
5843 if (baseName.right(4)==".tex")
5844 baseName=baseName.left(baseName.length()-4);
5845 else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
5846 baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length());
5847
5848 QCString title=ptitle.stripWhiteSpace();
5849 pd=new PageDef(fileName,startLine,baseName,doc,title);
5850
5851 pd->setRefItems(sli);
5852
5853 if (tagInfo)
5854 {
5855 pd->setReference(tagInfo->tagName);
5856 }
5857
5858 pd->setFileName(convertNameToFile(pd->name(),FALSE,TRUE));
5859
5860 //printf("Appending page `%s'\n",baseName.data());
5861 Doxygen::pageSDict->append(baseName,pd);
5862
5863 if (gd) gd->addPage(pd);
5864
5865 if (!pd->title().isEmpty())
5866 {
5867 //outputList->writeTitle(pi->name,pi->title);
5868
5869 // a page name is a label as well!
5870 QCString file;
5871 if (gd)
5872 {
5873 file=gd->getOutputFileBase();
5874 }
5875 else
5876 {
5877 file=pd->getOutputFileBase();
5878 }
5879 SectionInfo *si=new SectionInfo(
5880 file,pd->name(),pd->title(),SectionInfo::Page,pd->getReference());
5881 //printf("si->label=`%s' si->definition=%s si->fileName=`%s'\n",
5882 // si->label.data(),si->definition?si->definition->name().data():"<none>",
5883 // si->fileName.data());
5884 //printf(" SectionInfo: sec=%p sec->fileName=%s\n",si,si->fileName.data());
5885 //printf("Adding section key=%s si->fileName=%s\n",pageName.data(),si->fileName.data());
5886 Doxygen::sectionDict.insert(pd->name(),si);
5887 }
5888 }
5889 return pd;
5890}
5891
5892//----------------------------------------------------------------------------
5893
5894void addRefItem(const QList<ListItemInfo> *sli,
5895 const char *key,
5896 const char *prefix, const char *name,const char *title,const char *args)
5897{
5898 //printf("addRefItem(sli=%p,key=%s,prefix=%s,name=%s,title=%s,args=%s)\n",sli,key,prefix,name,title,args);
5899 if (sli)
5900 {
5901 QListIterator<ListItemInfo> slii(*sli);
5902 ListItemInfo *lii;
5903 for (slii.toFirst();(lii=slii.current());++slii)
5904 {
5905 RefList *refList = Doxygen::xrefLists->find(lii->type);
5906 if (refList
5907 &&
5908 (
5909 // either not a built-in list or the list is enabled
5910 (lii->type!="todo" || Config_getBool("GENERATE_TODOLIST")) &&
5911 (lii->type!="test" || Config_getBool("GENERATE_TESTLIST")) &&
5912 (lii->type!="bug" || Config_getBool("GENERATE_BUGLIST")) &&
5913 (lii->type!="deprecated" || Config_getBool("GENERATE_DEPRECATEDLIST"))
5914 )
5915 )
5916 {
5917 RefItem *item = refList->getRefItem(lii->itemId);
5918 ASSERT(item!=0);
5919
5920 item->prefix = prefix;
5921 item->name = name;
5922 item->title = title;
5923 item->args = args;
5924
5925 refList->insertIntoList(key,item);
5926
5927 }
5928 }
5929 }
5930}
5931
5932void addGroupListToTitle(OutputList &ol,Definition *d)
5933{
5934 LockingPtr<GroupList> groups = d->partOfGroups();
5935 if (groups!=0) // write list of group to which this definition belongs
5936 {
5937 ol.pushGeneratorState();
5938 ol.disableAllBut(OutputGenerator::Html);
5939 ol.writeString("<div class=\"ingroups\">");
5940 GroupListIterator gli(*groups);
5941 GroupDef *gd;
5942 bool first=TRUE;
5943 for (gli.toFirst();(gd=gli.current());++gli)
5944 {
5945 if (!first) { ol.writeString(" &#124; "); } else first=FALSE;
5946 ol.writeObjectLink(gd->getReference(),
5947 gd->getOutputFileBase(),0,gd->groupTitle());
5948 }
5949 ol.writeString("</div>");
5950 ol.popGeneratorState();
5951 }
5952}
5953
5954void filterLatexString(FTextStream &t,const char *str,
5955 bool insideTabbing,bool insidePre,bool insideItem)
5956{
5957 if (str==0) return;
5958 const unsigned char *p=(const unsigned char *)str;
5959 unsigned char c;
5960 unsigned char pc='\0';
5961 while (*p)
5962 {
5963 c=*p++;
5964
5965 if (insidePre)
5966 {
5967 switch(c)
5968 {
5969 case '\\': t << "\\(\\backslash\\)"; break;
5970 case '{': t << "\\{"; break;
5971 case '}': t << "\\}"; break;
5972 case '_': t << "\\_"; break;
5973 default:
5974 t << (char)c;
5975 }
5976 }
5977 else
5978 {
5979 switch(c)
5980 {
5981 case '#': t << "\\#"; break;
5982 case '$': t << "\\$"; break;
5983 case '%': t << "\\%"; break;
5984 case '^': t << "$^\\wedge$"; break;
5985 case '&': t << "\\&"; break;
5986 case '*': t << "$\\ast$"; break;
5987 case '_': t << "\\_";
5988 if (!insideTabbing) t << "\\-";
5989 break;
5990 case '{': t << "\\{"; break;
5991 case '}': t << "\\}"; break;
5992 case '<': t << "$<$"; break;
5993 case '>': t << "$>$"; break;
5994 case '|': t << "$|$"; break;
5995 case '~': t << "$\\sim$"; break;
5996 case '[': if (Config_getBool("PDF_HYPERLINKS") || insideItem)
5997 t << "\\mbox{[}";
5998 else
5999 t << "[";
6000 break;
6001 case ']': if (pc=='[') t << "$\\,$";
6002 if (Config_getBool("PDF_HYPERLINKS") || insideItem)
6003 t << "\\mbox{]}";
6004 else
6005 t << "]";
6006 break;
6007 case '-': t << "-\\/";
6008 break;
6009 case '\\': if (*p=='<')
6010 { t << "$<$"; p++; }
6011 else if (*p=='>')
6012 { t << "$>$"; p++; }
6013 else
6014 { t << "$\\backslash$"; }
6015 break;
6016 case '"': { t << "\\char`\\\"{}"; }
6017 break;
6018
6019 default:
6020 t << (char)c;
6021 }
6022 }
6023 pc = c;
6024 }
6025}
6026
6027
6028QCString rtfFormatBmkStr(const char *name)
6029{
6030 static QCString g_nextTag( "AAAAAAAAAA" );
6031 static QDict<QCString> g_tagDict( 5003 );
6032
6033 g_tagDict.setAutoDelete(TRUE);
6034
6035 // To overcome the 40-character tag limitation, we
6036 // substitute a short arbitrary string for the name
6037 // supplied, and keep track of the correspondence
6038 // between names and strings.
6039 QCString key( name );
6040 QCString* tag = g_tagDict.find( key );
6041 if ( !tag )
6042 {
6043 // This particular name has not yet been added
6044 // to the list. Add it, associating it with the
6045 // next tag value, and increment the next tag.
6046 tag = new QCString( g_nextTag.copy() ); // Make sure to use a deep copy!
6047 g_tagDict.insert( key, tag );
6048
6049 // This is the increment part
6050 char* nxtTag = g_nextTag.data() + g_nextTag.length() - 1;
6051 for ( unsigned int i = 0; i < g_nextTag.length(); ++i, --nxtTag )
6052 {
6053 if ( ( ++(*nxtTag) ) > 'Z' )
6054 {
6055 *nxtTag = 'A';
6056 }
6057 else
6058 {
6059 // Since there was no carry, we can stop now
6060 break;
6061 }
6062 }
6063 }
6064
6065 return *tag;
6066}
6067
6068QCString stripExtension(const char *fName)
6069{
6070 QCString result=fName;
6071 if (result.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension)
6072 {
6073 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
6074 }
6075 return result;
6076}
6077
6078
6079void replaceNamespaceAliases(QCString &scope,int i)
6080{
6081 while (i>0)
6082 {
6083 QCString ns = scope.left(i);
6084 QCString *s = Doxygen::namespaceAliasDict[ns];
6085 if (s)
6086 {
6087 scope=*s+scope.right(scope.length()-i);
6088 i=s->length();
6089 }
6090 if (i>0 && ns==scope.left(i)) break;
6091 }
6092}
6093
6094QCString stripPath(const char *s)
6095{
6096 QCString result=s;
6097 int i=result.findRev('/');
6098 if (i!=-1)
6099 {
6100 result=result.mid(i+1);
6101 }
6102 return result;
6103}
6104
6105/** returns \c TRUE iff string \a s contains word \a w */
6106bool containsWord(const QCString &s,const QCString &word)
6107{
6108 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6109 int p=0,i,l;
6110 while ((i=wordExp.match(s,p,&l))!=-1)
6111 {
6112 if (s.mid(i,l)==word) return TRUE;
6113 p=i+l;
6114 }
6115 return FALSE;
6116}
6117
6118bool findAndRemoveWord(QCString &s,const QCString &word)
6119{
6120 static QRegExp wordExp("[a-z_A-Z\\x80-\\xFF]+");
6121 int p=0,i,l;
6122 while ((i=wordExp.match(s,p,&l))!=-1)
6123 {
6124 if (s.mid(i,l)==word)
6125 {
6126 if (i>0 && isspace((uchar)s.at(i-1)))
6127 i--,l++;
6128 else if (i+l<(int)s.length() && isspace(s.at(i+l)))
6129 l++;
6130 s = s.left(i)+s.mid(i+l); // remove word + spacing
6131 return TRUE;
6132 }
6133 p=i+l;
6134 }
6135 return FALSE;
6136}
6137
6138/** Special version of QCString::stripWhiteSpace() that only strips
6139 * empty lines.
6140 */
6141QCString stripLeadingAndTrailingEmptyLines(const QCString &s)
6142{
6143 const char *p = s.data();
6144 if (p==0) return 0;
6145
6146 // search for leading empty lines
6147 int i=0,li=-1,l=s.length();
6148 char c;
6149 while ((c=*p++))
6150 {
6151 if (c==' ' || c=='\t' || c=='\r') i++;
6152 else if (c=='\n') i++,li=i;
6153 else break;
6154 }
6155
6156 // search for trailing empty lines
6157 int b=l-1,bi=-1;
6158 p=s.data()+b;
6159 while (b>=0)
6160 {
6161 c=*p; p--;
6162 if (c==' ' || c=='\t' || c=='\r') b--;
6163 else if (c=='\n') bi=b,b--;
6164 else break;
6165 }
6166
6167 // return whole string if no leading or trailing lines where found
6168 if (li==-1 && bi==-1) return s;
6169
6170 // return substring
6171 if (bi==-1) bi=l;
6172 if (li==-1) li=0;
6173 if (bi<=li) return 0; // only empty lines
6174 return s.mid(li,bi-li);
6175}
6176
6177#if 0
6178void stringToSearchIndex(const QCString &docBaseUrl,const QCString &title,
6179 const QCString &str,bool priority,const QCString &anchor)
6180{
6181 static bool searchEngine = Config_getBool("SEARCHENGINE");
6182 if (searchEngine)
6183 {
6184 Doxygen::searchIndex->setCurrentDoc(title,docBaseUrl,anchor);
6185 static QRegExp wordPattern("[a-z_A-Z\\x80-\\xFF][a-z_A-Z0-9\\x80-\\xFF]*");
6186 int i,p=0,l;
6187 while ((i=wordPattern.match(str,p,&l))!=-1)
6188 {
6189 Doxygen::searchIndex->addWord(str.mid(i,l),priority);
6190 p=i+l;
6191 }
6192 }
6193}
6194#endif
6195
6196//--------------------------------------------------------------------------
6197
6198static QDict<int> g_extLookup;
6199
6200static struct Lang2ExtMap
6201{
6202 const char *langName;
6203 const char *parserName;
6204 SrcLangExt parserId;
6205}
6206g_lang2extMap[] =
6207{
6208// language parser parser option
6209 { "idl", "c", SrcLangExt_IDL },
6210 { "java", "c", SrcLangExt_Java },
6211 { "javascript", "c", SrcLangExt_JS },
6212 { "csharp", "c", SrcLangExt_CSharp },
6213 { "d", "c", SrcLangExt_D },
6214 { "php", "c", SrcLangExt_PHP },
6215 { "objective-c", "c", SrcLangExt_ObjC },
6216 { "c", "c", SrcLangExt_Cpp },
6217 { "c++", "c", SrcLangExt_Cpp },
6218 { "python", "python", SrcLangExt_Python },
6219 { "fortran", "fortran", SrcLangExt_F90 },
6220 { "vhdl", "vhdl", SrcLangExt_VHDL },
6221 { "dbusxml", "dbusxml", SrcLangExt_XML },
6222 { 0, 0, (SrcLangExt)0 }
6223};
6224
6225bool updateLanguageMapping(const QCString &extension,const QCString &language)
6226{
6227 //getLanguageFromFileName("dummy"); // force initialization of the g_extLookup map
6228 const Lang2ExtMap *p = g_lang2extMap;
6229 QCString langName = language.lower();
6230 while (p->langName)
6231 {
6232 if (langName==p->langName) break;
6233 p++;
6234 }
6235 if (!p->langName) return FALSE;
6236
6237 // found the language
6238 SrcLangExt parserId = p->parserId;
6239 QCString extName = extension.lower();
6240 if (extName.isEmpty()) return FALSE;
6241 if (extName.at(0)!='.') extName.prepend(".");
6242 if (g_extLookup.find(extension)!=0) // language was already register for this ext
6243 {
6244 g_extLookup.remove(extension);
6245 }
6246 //printf("registering extension %s\n",extName.data());
6247 g_extLookup.insert(extName,new int(parserId));
6248 if (!Doxygen::parserManager->registerExtension(extName,p->parserName))
6249 {
6250 err("Failed to assign extension %s to parser %s for language %s\n",
6251 extName.data(),p->parserName,language.data());
6252 }
6253 else
6254 {
6255 //msg("Registered extension %s to language parser %s...\n",
6256 // extName.data(),language.data());
6257 }
6258 return TRUE;
6259}
6260
6261void initDefaultExtensionMapping()
6262{
6263 g_extLookup.setAutoDelete(TRUE);
6264 updateLanguageMapping(".idl", "idl");
6265 updateLanguageMapping(".ddl", "idl");
6266 updateLanguageMapping(".odl", "idl");
6267 updateLanguageMapping(".java", "java");
6268 updateLanguageMapping(".as", "javascript");
6269 updateLanguageMapping(".js", "javascript");
6270 updateLanguageMapping(".cs", "csharp");
6271 updateLanguageMapping(".d", "d");
6272 updateLanguageMapping(".php", "php");
6273 updateLanguageMapping(".php4", "php");
6274 updateLanguageMapping(".php5", "php");
6275 updateLanguageMapping(".inc", "php");
6276 updateLanguageMapping(".phtml", "php");
6277 updateLanguageMapping(".m", "objective-c");
6278 updateLanguageMapping(".M", "objective-c");
6279 updateLanguageMapping(".mm", "objective-c");
6280 updateLanguageMapping(".py", "python");
6281 updateLanguageMapping(".f", "fortran");
6282 updateLanguageMapping(".for", "fortran");
6283 updateLanguageMapping(".f90", "fortran");
6284 updateLanguageMapping(".vhd", "vhdl");
6285 updateLanguageMapping(".vhdl", "vhdl");
6286 //updateLanguageMapping(".xml", "dbusxml");
6287}
6288
6289SrcLangExt getLanguageFromFileName(const QCString fileName)
6290{
6291 int i = fileName.findRev('.');
6292 if (i!=-1) // name has an extension
6293 {
6294 QCString extStr=fileName.right(fileName.length()-i).lower();
6295 if (!extStr.isEmpty()) // non-empty extension
6296 {
6297 int *pVal=g_extLookup.find(extStr);
6298 if (pVal) // listed extension
6299 {
6300 //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
6301 return (SrcLangExt)*pVal;
6302 }
6303 }
6304 }
6305 //printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
6306 return SrcLangExt_Cpp; // not listed => assume C-ish language.
6307}
6308
6309//--------------------------------------------------------------------------
6310
6311/*! Returns true iff the given name string appears to be a typedef in scope. */
6312bool checkIfTypedef(Definition *scope,FileDef *fileScope,const char *n)
6313{
6314 if (scope==0 ||
6315 (scope->definitionType()!=Definition::TypeClass &&
6316 scope->definitionType()!=Definition::TypeNamespace
6317 )
6318 )
6319 {
6320 scope=Doxygen::globalScope;
6321 }
6322
6323 QCString name = n;
6324 if (name.isEmpty())
6325 return FALSE; // no name was given
6326
6327 DefinitionIntf *di = Doxygen::symbolMap->find(name);
6328 if (di==0)
6329 return FALSE; // could not find any matching symbols
6330
6331 // mostly copied from getResolvedClassRec()
6332 QCString explicitScopePart;
6333 int qualifierIndex = computeQualifiedIndex(name);
6334 if (qualifierIndex!=-1)
6335 {
6336 explicitScopePart = name.left(qualifierIndex);
6337 replaceNamespaceAliases(explicitScopePart,explicitScopePart.length());
6338 name = name.mid(qualifierIndex+2);
6339 }
6340
6341 int minDistance = 10000;
6342 MemberDef *bestMatch = 0;
6343
6344 if (di->definitionType()==DefinitionIntf::TypeSymbolList)
6345 {
6346 // find the closest closest matching definition
6347 DefinitionListIterator dli(*(DefinitionList*)di);
6348 Definition *d;
6349 for (dli.toFirst();(d=dli.current());++dli)
6350 {
6351 if (d->definitionType()==Definition::TypeMember)
6352 {
6353 g_visitedNamespaces.clear();
6354 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
6355 if (distance!=-1 && distance<minDistance)
6356 {
6357 minDistance = distance;
6358 bestMatch = (MemberDef *)d;
6359 }
6360 }
6361 }
6362 }
6363 else if (di->definitionType()==Definition::TypeMember)
6364 {
6365 Definition *d = (Definition *)di;
6366 g_visitedNamespaces.clear();
6367 int distance = isAccessibleFromWithExpScope(scope,fileScope,d,explicitScopePart);
6368 if (distance!=-1 && distance<minDistance)
6369 {
6370 minDistance = distance;
6371 bestMatch = (MemberDef *)d;
6372 }
6373 }
6374
6375 if (bestMatch && bestMatch->isTypedef())
6376 return TRUE; // closest matching symbol is a typedef
6377 else
6378 return FALSE;
6379}
6380
6381int nextUtf8CharPosition(const QCString &utf8Str,int len,int startPos)
6382{
6383 int bytes=1;
6384 if (startPos>=len) return len;
6385 char c = utf8Str[startPos];
6386 if (c<0) // multibyte utf-8 character
6387 {
6388 bytes++; // 1xxx.xxxx: >=2 byte character
6389 if (((uchar)c&0xE0)==0xE0)
6390 {
6391 bytes++; // 111x.xxxx: >=3 byte character
6392 }
6393 if (((uchar)c&0xF0)==0xF0)
6394 {
6395 bytes++; // 1111.xxxx: 4 byte character
6396 }
6397 }
6398 else if (c=='&') // skip over character entities
6399 {
6400 static QRegExp re1("&#[0-9]+;"); // numerical entity
6401 static QRegExp re2("&[A-Z_a-z]+;"); // named entity
6402 int l1,l2;
6403 int i1 = re1.match(utf8Str,startPos,&l1);
6404 int i2 = re2.match(utf8Str,startPos,&l2);
6405 if (i1!=-1)
6406 {
6407 bytes=l1;
6408 }
6409 else if (i2!=-1)
6410 {
6411 bytes=l2;
6412 }
6413 }
6414 return startPos+bytes;
6415}
6416
6417QCString parseCommentAsText(const Definition *scope,const MemberDef *md,
6418 const QCString &doc,const QCString &fileName,int lineNr)
6419{
6420 QGString s;
6421 if (doc.isEmpty()) return s.data();
6422 FTextStream t(&s);
6423 DocNode *root = validatingParseDoc(fileName,lineNr,
6424 (Definition*)scope,(MemberDef*)md,doc,FALSE,FALSE);
6425 TextDocVisitor *visitor = new TextDocVisitor(t);
6426 root->accept(visitor);
6427 delete visitor;
6428 delete root;
6429 QCString result = s.data();
6430 int i=0;
6431 int charCnt=0;
6432 int l=result.length();
6433 bool addEllipsis=FALSE;
6434 while ((i=nextUtf8CharPosition(result,l,i))<l)
6435 {
6436 charCnt++;
6437 if (charCnt>=80) break;
6438 }
6439 if (charCnt>=80) // try to truncate the string
6440 {
6441 while ((i=nextUtf8CharPosition(result,l,i))<l && charCnt<100)
6442 {
6443 charCnt++;
6444 if (result.at(i)>=0 && isspace(result.at(i)))
6445 {
6446 addEllipsis=TRUE;
6447 }
6448 else if (result.at(i)==',' ||
6449 result.at(i)=='.' ||
6450 result.at(i)=='?')
6451 {
6452 break;
6453 }
6454 }
6455 }
6456 if (addEllipsis || charCnt==100) result=result.left(i)+"...";
6457 return result.data();
6458}
6459
6460//--------------------------------------------------------------------------------------
6461
6462static QDict<void> aliasesProcessed;
6463
6464QCString expandAliasRec(const QCString s)
6465{
6466 QCString result;
6467 static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*");
6468 QCString value=s;
6469 int i,p=0,l;
6470 while ((i=cmdPat.match(value,p,&l))!=-1)
6471 {
6472 result+=value.mid(p,i-p);
6473 QCString args = extractAliasArgs(value,i+l);
6474 bool hasArgs = !args.isEmpty(); // found directly after command
6475 QCString cmd;
6476 if (hasArgs)
6477 {
6478 int numArgs = countAliasArguments(args);
6479 cmd = value.mid(i+1,l-1)+QCString().sprintf("{%d}",numArgs); // alias name + {n}
6480 }
6481 else
6482 {
6483 cmd = value.mid(i+1,l-1);
6484 }
6485 //printf("Found command '%s' args='%s'\n",cmd.data(),args.data());
6486 QCString *aliasText=Doxygen::aliasDict.find(cmd);
6487 if (aliasesProcessed.find(cmd)==0 && aliasText) // expand the alias
6488 {
6489 //printf("is an alias!\n");
6490 aliasesProcessed.insert(cmd,(void *)0x8);
6491 QCString val = *aliasText;
6492 if (hasArgs)
6493 {
6494 val = replaceAliasArguments(val,args);
6495 //printf("replace '%s'->'%s' args='%s'\n",
6496 // aliasText->data(),val.data(),args.data());
6497 }
6498 result+=expandAliasRec(val);
6499 aliasesProcessed.remove(cmd);
6500 p=i+l;
6501 if (hasArgs) p+=args.length()+2;
6502 }
6503 else // command is not an alias
6504 {
6505 //printf("not an alias!\n");
6506 result+=value.mid(i,l);
6507 p=i+l;
6508 }
6509 }
6510 result+=value.right(value.length()-p);
6511
6512 //printf("expandAliases '%s'->'%s'\n",s.data(),result.data());
6513 return result;
6514}
6515
6516static QCString replaceAliasArgument(const QCString &aliasValue,int paramNum,
6517 const QCString &paramValue)
6518{
6519 QCString result;
6520 QCString paramMarker;
6521 paramMarker.sprintf("\\%d",paramNum);
6522 int markerLen = paramMarker.length();
6523 int p=0,i;
6524 while ((i=aliasValue.find(paramMarker,p))!=-1) // search for marker
6525 {
6526 result+=aliasValue.mid(p,i-p);
6527 //printf("Found marker '%s' at %d len=%d for param '%s' in '%s'\n",
6528 // paramMarker.data(),i,markerLen,paramValue.data(),aliasValue.data());
6529 if (i==0 || aliasValue.at(i-1)!='\\') // found unescaped marker
6530 {
6531 result += paramValue;
6532 p=i+markerLen;
6533 }
6534 else // ignore escaped markers
6535 {
6536 result += aliasValue.mid(i,markerLen);
6537 p=i+1;
6538 }
6539 }
6540 result+=aliasValue.right(aliasValue.length()-p);
6541 result = substitute(result,"\\{","{");
6542 result = substitute(result,"\\}","}");
6543 result = expandAliasRec(substitute(result,"\\,",","));
6544 //printf("replaceAliasArgument('%s',%d,'%s')->%s\n",
6545 // aliasValue.data(),paramNum,paramValue.data(),result.data());
6546 return result;
6547}
6548
6549QCString replaceAliasArguments(const QCString &aliasValue,const QCString &argList)
6550{
6551 QCString result = aliasValue;
6552 QList<QCString> args;
6553 int p=0,i,c=1;
6554 for (i=0;i<(int)argList.length();i++)
6555 {
6556 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\'))
6557 {
6558 result = replaceAliasArgument(result,c,argList.mid(p,i-p));
6559 p=i+1;
6560 c++;
6561 }
6562 }
6563 if (p<(int)argList.length())
6564 {
6565 result = replaceAliasArgument(result,c,argList.right(argList.length()-p));
6566 }
6567 return result;
6568}
6569
6570int countAliasArguments(const QCString argList)
6571{
6572 int count=1;
6573 int l = argList.length();
6574 int i;
6575 for (i=0;i<l;i++)
6576 {
6577 if (argList.at(i)==',' && (i==0 || argList.at(i-1)!='\\')) count++;
6578 }
6579 return count;
6580}
6581
6582QCString extractAliasArgs(const QCString &args,int pos)
6583{
6584 int i;
6585 int bc=0;
6586 char prevChar=0;
6587 if (args.at(pos)=='{') // alias has argument
6588 {
6589 for (i=pos;i<(int)args.length();i++)
6590 {
6591 if (prevChar!='\\')
6592 {
6593 if (args.at(i)=='{') bc++;
6594 if (args.at(i)=='}') bc--;
6595 prevChar=args.at(i);
6596 }
6597 else
6598 {
6599 prevChar=0;
6600 }
6601
6602 if (bc==0)
6603 {
6604 //printf("extractAliasArgs('%s')->'%s'\n",args.data(),args.mid(pos+1,i-pos-1).data());
6605 return args.mid(pos+1,i-pos-1);
6606 }
6607 }
6608 }
6609 return "";
6610}
6611
6612QCString resolveAliasCmd(const QCString aliasCmd)
6613{
6614 QCString result;
6615 aliasesProcessed.clear();
6616 //printf("Expanding: '%s'\n",aliasCmd.data());
6617 result = expandAliasRec(aliasCmd);
6618 //printf("Expanding result: '%s'->'%s'\n",aliasCmd.data(),result.data());
6619 return result;
6620}
6621
6622QCString expandAlias(const QCString &aliasName,const QCString &aliasValue)
6623{
6624 QCString result;
6625 aliasesProcessed.clear();
6626 // avoid expanding this command recursively
6627 aliasesProcessed.insert(aliasName,(void *)0x8);
6628 // expand embedded commands
6629 //printf("Expanding: '%s'->'%s'\n",aliasName.data(),aliasValue.data());
6630 result = expandAliasRec(aliasValue);
6631 //printf("Expanding result: '%s'->'%s'\n",aliasName.data(),result.data());
6632 return result;
6633}
6634
6635void writeTypeConstraints(OutputList &ol,Definition *d,ArgumentList *al)
6636{
6637 if (al==0) return;
6638 ol.startConstraintList(theTranslator->trTypeConstraints());
6639 ArgumentListIterator ali(*al);
6640 Argument *a;
6641 for (;(a=ali.current());++ali)
6642 {
6643 ol.startConstraintParam();
6644 ol.parseText(a->name);
6645 ol.endConstraintParam();
6646 ol.startConstraintType();
6647 linkifyText(TextGeneratorOLImpl(ol),d,0,0,a->type);
6648 ol.endConstraintType();
6649 ol.startConstraintDocs();
6650 ol.parseDoc(d->docFile(),d->docLine(),d,0,a->docs,TRUE,FALSE);
6651 ol.endConstraintDocs();
6652 }
6653 ol.endConstraintList();
6654}
6655
6656bool usingTreeIndex()
6657{
6658 static bool treeView = Config_getBool("USE_INLINE_TREES");
6659 return treeView;
6660}
6661
6662void stackTrace()
6663{
6664#ifdef TRACINGSUPPORT
6665 void *backtraceFrames[128];
6666 int frameCount = backtrace(backtraceFrames, 128);
6667 static char cmd[40960];
6668 char *p = cmd;
6669 p += sprintf(p,"/usr/bin/atos -p %d ", (int)getpid());
6670 for (int x = 0; x < frameCount; x++)
6671 {
6672 p += sprintf(p,"%p ", backtraceFrames[x]);
6673 }
6674 fprintf(stderr,"========== STACKTRACE START ==============\n");
6675 if (FILE *fp = popen(cmd, "r"))
6676 {
6677 char resBuf[512];
6678 while (size_t len = fread(resBuf, 1, sizeof(resBuf), fp))
6679 {
6680 fwrite(resBuf, 1, len, stderr);
6681 }
6682 pclose(fp);
6683 }
6684 fprintf(stderr,"============ STACKTRACE END ==============\n");
6685 //fprintf(stderr,"%s\n", frameStrings[x]);
6686#endif
6687}
6688
6689static int transcodeCharacterBuffer(const char *fileName,BufStr &srcBuf,int size,
6690 const char *inputEncoding,const char *outputEncoding)
6691{
6692 if (inputEncoding==0 || outputEncoding==0) return size;
6693 if (qstricmp(inputEncoding,outputEncoding)==0) return size;
6694 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
6695 if (cd==(void *)(-1))
6696 {
6697 err("error: unsupported character conversion: '%s'->'%s': %s\n"
6698 "Check the INPUT_ENCODING setting in the config file!\n",
6699 inputEncoding,outputEncoding,strerror(errno));
6700 exit(1);
6701 }
6702 int tmpBufSize=size*4+1;
6703 BufStr tmpBuf(tmpBufSize);
6704 size_t iLeft=size;
6705 size_t oLeft=tmpBufSize;
6706 const char *srcPtr = srcBuf.data();
6707 char *dstPtr = tmpBuf.data();
6708 uint newSize=0;
6709 if (!portable_iconv(cd, &srcPtr, &iLeft, &dstPtr, &oLeft))
6710 {
6711 newSize = tmpBufSize-oLeft;
6712 srcBuf.shrink(newSize);
6713 strncpy(srcBuf.data(),tmpBuf.data(),newSize);
6714 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
6715 }
6716 else
6717 {
6718 err("%s: error: failed to translate characters from %s to %s: check INPUT_ENCODING\n",
6719 fileName,inputEncoding,outputEncoding);
6720 exit(1);
6721 }
6722 portable_iconv_close(cd);
6723 return newSize;
6724}
6725
6726//! read a file name \a fileName and optionally filter and transcode it
6727bool readInputFile(const char *fileName,BufStr &inBuf)
6728{
6729 // try to open file
6730 int size=0;
6731 //uint oldPos = dest.curPos();
6732 //printf(".......oldPos=%d\n",oldPos);
6733
6734 QFileInfo fi(fileName);
6735 if (!fi.exists()) return FALSE;
6736 QCString filterName = getFileFilter(fileName,FALSE);
6737 if (filterName.isEmpty())
6738 {
6739 QFile f(fileName);
6740 if (!f.open(IO_ReadOnly))
6741 {
6742 err("error: could not open file %s\n",fileName);
6743 return FALSE;
6744 }
6745 size=fi.size();
6746 // read the file
6747 inBuf.skip(size);
6748 if (f.readBlock(inBuf.data()/*+oldPos*/,size)!=size)
6749 {
6750 err("error: problems while reading file %s\n",fileName);
6751 return FALSE;
6752 }
6753 }
6754 else
6755 {
6756 QCString cmd=filterName+" \""+fileName+"\"";
6757 Debug::print(Debug::ExtCmd,0,"Executing popen(`%s`)\n",cmd.data());
6758 FILE *f=portable_popen(cmd,"r");
6759 if (!f)
6760 {
6761 err("error: could not execute filter %s\n",filterName.data());
6762 return FALSE;
6763 }
6764 const int bufSize=1024;
6765 char buf[bufSize];
6766 int numRead;
6767 while ((numRead=fread(buf,1,bufSize,f))>0)
6768 {
6769 //printf(">>>>>>>>Reading %d bytes\n",numRead);
6770 inBuf.addArray(buf,numRead),size+=numRead;
6771 }
6772 portable_pclose(f);
6773 }
6774
6775 int start=0;
6776 if (inBuf.size()>=2 &&
6777 ((inBuf.at(0)==-1 && inBuf.at(1)==-2) || // Litte endian BOM
6778 (inBuf.at(0)==-2 && inBuf.at(1)==-1) // big endian BOM
6779 )
6780 ) // UCS-2 encoded file
6781 {
6782 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
6783 "UCS-2","UTF-8");
6784 }
6785 else if (inBuf.size()>=3 &&
6786 (uchar)inBuf.at(0)==0xEF &&
6787 (uchar)inBuf.at(1)==0xBB &&
6788 (uchar)inBuf.at(2)==0xBF
6789 )
6790 {
6791 // UTF-8 encoded file
6792 inBuf.dropFromStart(3); // remove UTF-8 BOM: no translation needed
6793 }
6794 else // transcode according to the INPUT_ENCODING setting
6795 {
6796 // do character transcoding if needed.
6797 transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(),
6798 Config_getString("INPUT_ENCODING"),"UTF-8");
6799 }
6800
6801 inBuf.addChar('\n'); /* to prevent problems under Windows ? */
6802
6803 // and translate CR's
6804 size=inBuf.curPos()-start;
6805 int newSize=filterCRLF(inBuf.data()+start,size);
6806 //printf("filter char at %p size=%d newSize=%d\n",dest.data()+oldPos,size,newSize);
6807 if (newSize!=size) // we removed chars
6808 {
6809 inBuf.shrink(newSize); // resize the array
6810 //printf(".......resizing from %d to %d result=[%s]\n",oldPos+size,oldPos+newSize,dest.data());
6811 }
6812 inBuf.at(inBuf.curPos())='\0';
6813 return TRUE;
6814}
6815
6816// Replace %word by word in title
6817QCString filterTitle(const QCString &title)
6818{
6819 QCString tf;
6820 static QRegExp re("%[A-Z_a-z]");
6821 int p=0,i,l;
6822 while ((i=re.match(title,p,&l))!=-1)
6823 {
6824 tf+=title.mid(p,i-p);
6825 tf+=title.mid(i+1,l-1); // skip %
6826 p=i+l;
6827 }
6828 tf+=title.right(title.length()-p);
6829 return tf;
6830}
6831
6832//----------------------------------------------------------------------------
6833// returns TRUE if the name of the file represented by `fi' matches
6834// one of the file patterns in the `patList' list.
6835
6836bool patternMatch(const QFileInfo &fi,const QStrList *patList)
6837{
6838 bool found=FALSE;
6839 if (patList)
6840 {
6841 QStrListIterator it(*patList);
6842 QCString pattern;
6843 for (it.toFirst();(pattern=it.current());++it)
6844 {
6845 if (!pattern.isEmpty() && !found)
6846 {
6847 int i=pattern.find('=');
6848 if (i!=-1) pattern=pattern.left(i); // strip of the extension specific filter name
6849
6850#if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
6851 QRegExp re(pattern,FALSE,TRUE); // case insensitive match
6852#else // unix
6853 QRegExp re(pattern,TRUE,TRUE); // case sensitive match
6854#endif
6855 found = found || re.match(fi.fileName().data())!=-1 ||
6856 re.match(fi.filePath().data())!=-1 ||
6857 re.match(fi.absFilePath().data())!=-1;
6858 //printf("Matching `%s' against pattern `%s' found=%d\n",
6859 // fi->fileName().data(),pattern.data(),found);
6860 }
6861 }
6862 }
6863 return found;
6864}
6865
6866void writeSummaryLink(OutputList &ol,const char *label,const char *title,
6867 bool &first)
6868{
6869 if (first)
6870 {
6871 ol.writeString(" <div class=\"summary\">\n");
6872 first=FALSE;
6873 }
6874 else
6875 {
6876 ol.writeString(" &#124;\n");
6877 }
6878 ol.writeString("<a href=\"#");
6879 ol.writeString(label);
6880 ol.writeString("\">");
6881 ol.writeString(title);
6882 ol.writeString("</a>");
6883}
6884
6885QCString externalLinkTarget()
6886{
6887 static bool extLinksInWindow = Config_getBool("EXT_LINKS_IN_WINDOW");
6888 if (extLinksInWindow) return "target=\"_blank\" "; else return "";
6889}
6890
6891QCString externalRef(const QCString &relPath,const QCString &ref,bool href)
6892{
6893 QCString result;
6894 if (!ref.isEmpty())
6895 {
6896 QCString *dest = Doxygen::tagDestinationDict[ref];
6897 if (dest)
6898 {
6899 result = *dest;
6900 int l = result.length();
6901 if (!relPath.isEmpty() && l>0 && result.at(0)=='.')
6902 { // relative path -> prepend relPath.
6903 result.prepend(relPath);
6904 }
6905 if (!href) result.prepend("doxygen=\""+ref+":");
6906 if (l>0 && result.at(l-1)!='/') result+='/';
6907 if (!href) result.append("\" ");
6908 }
6909 }
6910 else
6911 {
6912 result = relPath;
6913 }
6914 return result;
6915}
6916
6917void writeColoredImgData(const char *dir,ColoredImgDataItem data[])
6918{
6919 static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
6920 static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
6921 static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
6922 while (data->name)
6923 {
6924 QCString fileName;
6925 fileName=(QCString)dir+"/"+data->name;
6926 QFile f(fileName);
6927 if (f.open(IO_WriteOnly))
6928 {
6929 ColoredImage img(data->width,data->height,data->content,data->alpha,
6930 sat,hue,gamma);
6931 img.save(fileName);
6932 }
6933 else
6934 {
6935 fprintf(stderr,"Warning: Cannot open file %s for writing\n",data->name);
6936 }
6937 Doxygen::indexList.addImageFile(data->name);
6938 data++;
6939 }
6940}
6941
6942QCString replaceColorMarkers(const char *str)
6943{
6944 QCString result;
6945 QCString s=str;
6946 if (s.isEmpty()) return result;
6947 static QRegExp re("##[0-9A-Fa-f][0-9A-Fa-f]");
6948 static const char hex[] = "0123456789ABCDEF";
6949 static int hue = Config_getInt("HTML_COLORSTYLE_HUE");
6950 static int sat = Config_getInt("HTML_COLORSTYLE_SAT");
6951 static int gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
6952 int i,l,sl=s.length(),p=0;
6953 while ((i=re.match(s,p,&l))!=-1)
6954 {
6955 result+=s.mid(p,i-p);
6956 QCString lumStr = s.mid(i+2,l-2);
6957#define HEXTONUM(x) (((x)>='0' && (x)<='9') ? ((x)-'0') : \
6958 ((x)>='a' && (x)<='f') ? ((x)-'a'+10) : \
6959 ((x)>='A' && (x)<='F') ? ((x)-'A'+10) : 0)
6960
6961 double r,g,b;
6962 int red,green,blue;
6963 int level = HEXTONUM(lumStr[0])*16+HEXTONUM(lumStr[1]);
6964 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
6965 pow(level/255.0,gamma/100.0),&r,&g,&b);
6966 red = (int)(r*255.0);
6967 green = (int)(g*255.0);
6968 blue = (int)(b*255.0);
6969 char colStr[8];
6970 colStr[0]='#';
6971 colStr[1]=hex[red>>4];
6972 colStr[2]=hex[red&0xf];
6973 colStr[3]=hex[green>>4];
6974 colStr[4]=hex[green&0xf];
6975 colStr[5]=hex[blue>>4];
6976 colStr[6]=hex[blue&0xf];
6977 colStr[7]=0;
6978 //printf("replacing %s->%s (level=%d)\n",lumStr.data(),colStr,level);
6979 result+=colStr;
6980 p=i+l;
6981 }
6982 result+=s.right(sl-p);
6983 return result;
6984}
6985
6986

Archive Download this file

Revision: 1322