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