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