author | sandman <sandman> | 2002-04-13 00:47:20 (UTC) |
---|---|---|
committer | sandman <sandman> | 2002-04-13 00:47:20 (UTC) |
commit | 98a1e3f36567639344f12932b629e526a8783aa8 (patch) (unidiff) | |
tree | 0433d296857faceeafc54f7deabddb621f45a933 /noncore/unsupported/qpdf/xpdf/Link.cc | |
parent | 7e31b1fba119f69929d6744d7295555ff1727f4f (diff) | |
download | opie-98a1e3f36567639344f12932b629e526a8783aa8.zip opie-98a1e3f36567639344f12932b629e526a8783aa8.tar.gz opie-98a1e3f36567639344f12932b629e526a8783aa8.tar.bz2 |
CVS import of QPdf
Diffstat (limited to 'noncore/unsupported/qpdf/xpdf/Link.cc') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/unsupported/qpdf/xpdf/Link.cc | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/noncore/unsupported/qpdf/xpdf/Link.cc b/noncore/unsupported/qpdf/xpdf/Link.cc new file mode 100644 index 0000000..79a5f6e --- a/dev/null +++ b/noncore/unsupported/qpdf/xpdf/Link.cc | |||
@@ -0,0 +1,634 @@ | |||
1 | //======================================================================== | ||
2 | // | ||
3 | // Link.cc | ||
4 | // | ||
5 | // Copyright 1996 Derek B. Noonburg | ||
6 | // | ||
7 | //======================================================================== | ||
8 | |||
9 | #ifdef __GNUC__ | ||
10 | #pragma implementation | ||
11 | #endif | ||
12 | |||
13 | #include <aconf.h> | ||
14 | #include <stddef.h> | ||
15 | #include <string.h> | ||
16 | #include "gmem.h" | ||
17 | #include "GString.h" | ||
18 | #include "Error.h" | ||
19 | #include "Object.h" | ||
20 | #include "Array.h" | ||
21 | #include "Dict.h" | ||
22 | #include "Link.h" | ||
23 | |||
24 | //------------------------------------------------------------------------ | ||
25 | |||
26 | static GString *getFileSpecName(Object *fileSpecObj); | ||
27 | |||
28 | //------------------------------------------------------------------------ | ||
29 | // LinkDest | ||
30 | //------------------------------------------------------------------------ | ||
31 | |||
32 | LinkDest::LinkDest(Array *a, GBool pageIsRefA) { | ||
33 | Object obj1, obj2; | ||
34 | |||
35 | // initialize fields | ||
36 | pageIsRef = pageIsRefA; | ||
37 | left = bottom = right = top = zoom = 0; | ||
38 | ok = gFalse; | ||
39 | |||
40 | // get page | ||
41 | if (pageIsRef) { | ||
42 | if (!a->getNF(0, &obj1)->isRef()) { | ||
43 | error(-1, "Bad annotation destination"); | ||
44 | goto err2; | ||
45 | } | ||
46 | pageRef.num = obj1.getRefNum(); | ||
47 | pageRef.gen = obj1.getRefGen(); | ||
48 | obj1.free(); | ||
49 | } else { | ||
50 | if (!a->get(0, &obj1)->isInt()) { | ||
51 | error(-1, "Bad annotation destination"); | ||
52 | goto err2; | ||
53 | } | ||
54 | pageNum = obj1.getInt() + 1; | ||
55 | obj1.free(); | ||
56 | } | ||
57 | |||
58 | // get destination type | ||
59 | a->get(1, &obj1); | ||
60 | |||
61 | // XYZ link | ||
62 | if (obj1.isName("XYZ")) { | ||
63 | kind = destXYZ; | ||
64 | a->get(2, &obj2); | ||
65 | if (obj2.isNull()) { | ||
66 | changeLeft = gFalse; | ||
67 | } else if (obj2.isNum()) { | ||
68 | changeLeft = gTrue; | ||
69 | left = obj2.getNum(); | ||
70 | } else { | ||
71 | error(-1, "Bad annotation destination position"); | ||
72 | goto err1; | ||
73 | } | ||
74 | obj2.free(); | ||
75 | a->get(3, &obj2); | ||
76 | if (obj2.isNull()) { | ||
77 | changeTop = gFalse; | ||
78 | } else if (obj2.isNum()) { | ||
79 | changeTop = gTrue; | ||
80 | top = obj2.getNum(); | ||
81 | } else { | ||
82 | error(-1, "Bad annotation destination position"); | ||
83 | goto err1; | ||
84 | } | ||
85 | obj2.free(); | ||
86 | a->get(4, &obj2); | ||
87 | if (obj2.isNull()) { | ||
88 | changeZoom = gFalse; | ||
89 | } else if (obj2.isNum()) { | ||
90 | changeZoom = gTrue; | ||
91 | zoom = obj2.getNum(); | ||
92 | } else { | ||
93 | error(-1, "Bad annotation destination position"); | ||
94 | goto err1; | ||
95 | } | ||
96 | obj2.free(); | ||
97 | |||
98 | // Fit link | ||
99 | } else if (obj1.isName("Fit")) { | ||
100 | kind = destFit; | ||
101 | |||
102 | // FitH link | ||
103 | } else if (obj1.isName("FitH")) { | ||
104 | kind = destFitH; | ||
105 | if (!a->get(2, &obj2)->isNum()) { | ||
106 | error(-1, "Bad annotation destination position"); | ||
107 | goto err1; | ||
108 | } | ||
109 | top = obj2.getNum(); | ||
110 | obj2.free(); | ||
111 | |||
112 | // FitV link | ||
113 | } else if (obj1.isName("FitV")) { | ||
114 | kind = destFitV; | ||
115 | if (!a->get(2, &obj2)->isNum()) { | ||
116 | error(-1, "Bad annotation destination position"); | ||
117 | goto err1; | ||
118 | } | ||
119 | left = obj2.getNum(); | ||
120 | obj2.free(); | ||
121 | |||
122 | // FitR link | ||
123 | } else if (obj1.isName("FitR")) { | ||
124 | kind = destFitR; | ||
125 | if (!a->get(2, &obj2)->isNum()) { | ||
126 | error(-1, "Bad annotation destination position"); | ||
127 | goto err1; | ||
128 | } | ||
129 | left = obj2.getNum(); | ||
130 | obj2.free(); | ||
131 | if (!a->get(3, &obj2)->isNum()) { | ||
132 | error(-1, "Bad annotation destination position"); | ||
133 | goto err1; | ||
134 | } | ||
135 | bottom = obj2.getNum(); | ||
136 | obj2.free(); | ||
137 | if (!a->get(4, &obj2)->isNum()) { | ||
138 | error(-1, "Bad annotation destination position"); | ||
139 | goto err1; | ||
140 | } | ||
141 | right = obj2.getNum(); | ||
142 | obj2.free(); | ||
143 | if (!a->get(5, &obj2)->isNum()) { | ||
144 | error(-1, "Bad annotation destination position"); | ||
145 | goto err1; | ||
146 | } | ||
147 | top = obj2.getNum(); | ||
148 | obj2.free(); | ||
149 | |||
150 | // FitB link | ||
151 | } else if (obj1.isName("FitB")) { | ||
152 | kind = destFitB; | ||
153 | |||
154 | // FitBH link | ||
155 | } else if (obj1.isName("FitBH")) { | ||
156 | kind = destFitBH; | ||
157 | if (!a->get(2, &obj2)->isNum()) { | ||
158 | error(-1, "Bad annotation destination position"); | ||
159 | goto err1; | ||
160 | } | ||
161 | top = obj2.getNum(); | ||
162 | obj2.free(); | ||
163 | |||
164 | // FitBV link | ||
165 | } else if (obj1.isName("FitBV")) { | ||
166 | kind = destFitBV; | ||
167 | if (!a->get(2, &obj2)->isNum()) { | ||
168 | error(-1, "Bad annotation destination position"); | ||
169 | goto err1; | ||
170 | } | ||
171 | left = obj2.getNum(); | ||
172 | obj2.free(); | ||
173 | |||
174 | // unknown link kind | ||
175 | } else { | ||
176 | error(-1, "Unknown annotation destination type"); | ||
177 | goto err2; | ||
178 | } | ||
179 | |||
180 | obj1.free(); | ||
181 | ok = gTrue; | ||
182 | return; | ||
183 | |||
184 | err1: | ||
185 | obj2.free(); | ||
186 | err2: | ||
187 | obj1.free(); | ||
188 | } | ||
189 | |||
190 | LinkDest::LinkDest(LinkDest *dest) { | ||
191 | kind = dest->kind; | ||
192 | pageIsRef = dest->pageIsRef; | ||
193 | if (pageIsRef) | ||
194 | pageRef = dest->pageRef; | ||
195 | else | ||
196 | pageNum = dest->pageNum; | ||
197 | left = dest->left; | ||
198 | bottom = dest->bottom; | ||
199 | right = dest->right; | ||
200 | top = dest->top; | ||
201 | zoom = dest->zoom; | ||
202 | changeLeft = dest->changeLeft; | ||
203 | changeTop = dest->changeTop; | ||
204 | changeZoom = dest->changeZoom; | ||
205 | ok = gTrue; | ||
206 | } | ||
207 | |||
208 | //------------------------------------------------------------------------ | ||
209 | // LinkGoTo | ||
210 | //------------------------------------------------------------------------ | ||
211 | |||
212 | LinkGoTo::LinkGoTo(Object *destObj) { | ||
213 | dest = NULL; | ||
214 | namedDest = NULL; | ||
215 | |||
216 | // named destination | ||
217 | if (destObj->isName()) { | ||
218 | namedDest = new GString(destObj->getName()); | ||
219 | } else if (destObj->isString()) { | ||
220 | namedDest = destObj->getString()->copy(); | ||
221 | |||
222 | // destination dictionary | ||
223 | } else if (destObj->isArray()) { | ||
224 | dest = new LinkDest(destObj->getArray(), gTrue); | ||
225 | if (!dest->isOk()) { | ||
226 | delete dest; | ||
227 | dest = NULL; | ||
228 | } | ||
229 | |||
230 | // error | ||
231 | } else { | ||
232 | error(-1, "Illegal annotation destination"); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | LinkGoTo::~LinkGoTo() { | ||
237 | if (dest) | ||
238 | delete dest; | ||
239 | if (namedDest) | ||
240 | delete namedDest; | ||
241 | } | ||
242 | |||
243 | //------------------------------------------------------------------------ | ||
244 | // LinkGoToR | ||
245 | //------------------------------------------------------------------------ | ||
246 | |||
247 | LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { | ||
248 | dest = NULL; | ||
249 | namedDest = NULL; | ||
250 | |||
251 | // get file name | ||
252 | fileName = getFileSpecName(fileSpecObj); | ||
253 | |||
254 | // named destination | ||
255 | if (destObj->isName()) { | ||
256 | namedDest = new GString(destObj->getName()); | ||
257 | } else if (destObj->isString()) { | ||
258 | namedDest = destObj->getString()->copy(); | ||
259 | |||
260 | // destination dictionary | ||
261 | } else if (destObj->isArray()) { | ||
262 | dest = new LinkDest(destObj->getArray(), gFalse); | ||
263 | if (!dest->isOk()) { | ||
264 | delete dest; | ||
265 | dest = NULL; | ||
266 | } | ||
267 | |||
268 | // error | ||
269 | } else { | ||
270 | error(-1, "Illegal annotation destination"); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | LinkGoToR::~LinkGoToR() { | ||
275 | if (fileName) | ||
276 | delete fileName; | ||
277 | if (dest) | ||
278 | delete dest; | ||
279 | if (namedDest) | ||
280 | delete namedDest; | ||
281 | } | ||
282 | |||
283 | |||
284 | //------------------------------------------------------------------------ | ||
285 | // LinkLaunch | ||
286 | //------------------------------------------------------------------------ | ||
287 | |||
288 | LinkLaunch::LinkLaunch(Object *actionObj) { | ||
289 | Object obj1, obj2; | ||
290 | |||
291 | fileName = NULL; | ||
292 | params = NULL; | ||
293 | |||
294 | if (actionObj->isDict()) { | ||
295 | if (!actionObj->dictLookup("F", &obj1)->isNull()) { | ||
296 | fileName = getFileSpecName(&obj1); | ||
297 | } else { | ||
298 | obj1.free(); | ||
299 | //~ This hasn't been defined by Adobe yet, so assume it looks | ||
300 | //~ just like the Win dictionary until they say otherwise. | ||
301 | if (actionObj->dictLookup("Unix", &obj1)->isDict()) { | ||
302 | obj1.dictLookup("F", &obj2); | ||
303 | fileName = getFileSpecName(&obj2); | ||
304 | obj2.free(); | ||
305 | if (obj1.dictLookup("P", &obj2)->isString()) | ||
306 | params = obj2.getString()->copy(); | ||
307 | obj2.free(); | ||
308 | } else { | ||
309 | error(-1, "Bad launch-type link action"); | ||
310 | } | ||
311 | } | ||
312 | obj1.free(); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | LinkLaunch::~LinkLaunch() { | ||
317 | if (fileName) | ||
318 | delete fileName; | ||
319 | if (params) | ||
320 | delete params; | ||
321 | } | ||
322 | |||
323 | //------------------------------------------------------------------------ | ||
324 | // LinkURI | ||
325 | //------------------------------------------------------------------------ | ||
326 | |||
327 | LinkURI::LinkURI(Object *uriObj, GString *baseURI) { | ||
328 | GString *uri2; | ||
329 | int n; | ||
330 | char c; | ||
331 | |||
332 | uri = NULL; | ||
333 | if (uriObj->isString()) { | ||
334 | uri2 = uriObj->getString()->copy(); | ||
335 | if (baseURI) { | ||
336 | n = strcspn(uri2->getCString(), "/:"); | ||
337 | if (n == uri2->getLength() || uri2->getChar(n) == '/') { | ||
338 | uri = baseURI->copy(); | ||
339 | c = uri->getChar(uri->getLength() - 1); | ||
340 | if (c == '/' || c == '?') { | ||
341 | if (uri2->getChar(0) == '/') { | ||
342 | uri2->del(0); | ||
343 | } | ||
344 | } else { | ||
345 | if (uri2->getChar(0) != '/') { | ||
346 | uri->append('/'); | ||
347 | } | ||
348 | } | ||
349 | uri->append(uri2); | ||
350 | delete uri2; | ||
351 | } else { | ||
352 | uri = uri2; | ||
353 | } | ||
354 | } else { | ||
355 | uri = uri2; | ||
356 | } | ||
357 | } else { | ||
358 | error(-1, "Illegal URI-type link"); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | LinkURI::~LinkURI() { | ||
363 | if (uri) | ||
364 | delete uri; | ||
365 | } | ||
366 | |||
367 | //------------------------------------------------------------------------ | ||
368 | // LinkNamed | ||
369 | //------------------------------------------------------------------------ | ||
370 | |||
371 | LinkNamed::LinkNamed(Object *nameObj) { | ||
372 | name = NULL; | ||
373 | if (nameObj->isName()) { | ||
374 | name = new GString(nameObj->getName()); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | LinkNamed::~LinkNamed() { | ||
379 | if (name) { | ||
380 | delete name; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | //------------------------------------------------------------------------ | ||
385 | // LinkUnknown | ||
386 | //------------------------------------------------------------------------ | ||
387 | |||
388 | LinkUnknown::LinkUnknown(char *actionA) { | ||
389 | action = new GString(actionA); | ||
390 | } | ||
391 | |||
392 | LinkUnknown::~LinkUnknown() { | ||
393 | delete action; | ||
394 | } | ||
395 | |||
396 | //------------------------------------------------------------------------ | ||
397 | // Link | ||
398 | //------------------------------------------------------------------------ | ||
399 | |||
400 | Link::Link(Dict *dict, GString *baseURI) { | ||
401 | Object obj1, obj2, obj3, obj4; | ||
402 | fouble t; | ||
403 | |||
404 | action = NULL; | ||
405 | ok = gFalse; | ||
406 | |||
407 | // get rectangle | ||
408 | if (!dict->lookup("Rect", &obj1)->isArray()) { | ||
409 | error(-1, "Annotation rectangle is wrong type"); | ||
410 | goto err2; | ||
411 | } | ||
412 | if (!obj1.arrayGet(0, &obj2)->isNum()) { | ||
413 | error(-1, "Bad annotation rectangle"); | ||
414 | goto err1; | ||
415 | } | ||
416 | x1 = obj2.getNum(); | ||
417 | obj2.free(); | ||
418 | if (!obj1.arrayGet(1, &obj2)->isNum()) { | ||
419 | error(-1, "Bad annotation rectangle"); | ||
420 | goto err1; | ||
421 | } | ||
422 | y1 = obj2.getNum(); | ||
423 | obj2.free(); | ||
424 | if (!obj1.arrayGet(2, &obj2)->isNum()) { | ||
425 | error(-1, "Bad annotation rectangle"); | ||
426 | goto err1; | ||
427 | } | ||
428 | x2 = obj2.getNum(); | ||
429 | obj2.free(); | ||
430 | if (!obj1.arrayGet(3, &obj2)->isNum()) { | ||
431 | error(-1, "Bad annotation rectangle"); | ||
432 | goto err1; | ||
433 | } | ||
434 | y2 = obj2.getNum(); | ||
435 | obj2.free(); | ||
436 | obj1.free(); | ||
437 | if (x1 > x2) { | ||
438 | t = x1; | ||
439 | x1 = x2; | ||
440 | x2 = t; | ||
441 | } | ||
442 | if (y1 > y2) { | ||
443 | t = y1; | ||
444 | y1 = y2; | ||
445 | y2 = t; | ||
446 | } | ||
447 | |||
448 | // get border | ||
449 | borderW = 0; | ||
450 | if (!dict->lookup("Border", &obj1)->isNull()) { | ||
451 | if (obj1.isArray() && obj1.arrayGetLength() >= 3) { | ||
452 | if (obj1.arrayGet(2, &obj2)->isNum()) { | ||
453 | borderW = obj2.getNum(); | ||
454 | } else { | ||
455 | error(-1, "Bad annotation border"); | ||
456 | } | ||
457 | obj2.free(); | ||
458 | } | ||
459 | } | ||
460 | obj1.free(); | ||
461 | |||
462 | // look for destination | ||
463 | if (!dict->lookup("Dest", &obj1)->isNull()) { | ||
464 | action = new LinkGoTo(&obj1); | ||
465 | |||
466 | // look for action | ||
467 | } else { | ||
468 | obj1.free(); | ||
469 | if (dict->lookup("A", &obj1)->isDict()) { | ||
470 | obj1.dictLookup("S", &obj2); | ||
471 | |||
472 | // GoTo action | ||
473 | if (obj2.isName("GoTo")) { | ||
474 | obj1.dictLookup("D", &obj3); | ||
475 | action = new LinkGoTo(&obj3); | ||
476 | obj3.free(); | ||
477 | |||
478 | // GoToR action | ||
479 | } else if (obj2.isName("GoToR")) { | ||
480 | obj1.dictLookup("F", &obj3); | ||
481 | obj1.dictLookup("D", &obj4); | ||
482 | action = new LinkGoToR(&obj3, &obj4); | ||
483 | obj3.free(); | ||
484 | obj4.free(); | ||
485 | |||
486 | // Launch action | ||
487 | } else if (obj2.isName("Launch")) { | ||
488 | action = new LinkLaunch(&obj1); | ||
489 | |||
490 | // URI action | ||
491 | } else if (obj2.isName("URI")) { | ||
492 | obj1.dictLookup("URI", &obj3); | ||
493 | action = new LinkURI(&obj3, baseURI); | ||
494 | obj3.free(); | ||
495 | |||
496 | // Named action | ||
497 | } else if (obj2.isName("Named")) { | ||
498 | obj1.dictLookup("N", &obj3); | ||
499 | action = new LinkNamed(&obj3); | ||
500 | obj3.free(); | ||
501 | |||
502 | // unknown action | ||
503 | } else if (obj2.isName()) { | ||
504 | action = new LinkUnknown(obj2.getName()); | ||
505 | |||
506 | // action is missing or wrong type | ||
507 | } else { | ||
508 | error(-1, "Bad annotation action"); | ||
509 | action = NULL; | ||
510 | } | ||
511 | |||
512 | obj2.free(); | ||
513 | |||
514 | } else { | ||
515 | error(-1, "Missing annotation destination/action"); | ||
516 | action = NULL; | ||
517 | } | ||
518 | } | ||
519 | obj1.free(); | ||
520 | |||
521 | // check for bad action | ||
522 | if (action && action->isOk()) | ||
523 | ok = gTrue; | ||
524 | |||
525 | return; | ||
526 | |||
527 | err1: | ||
528 | obj2.free(); | ||
529 | err2: | ||
530 | obj1.free(); | ||
531 | } | ||
532 | |||
533 | Link::~Link() { | ||
534 | if (action) | ||
535 | delete action; | ||
536 | } | ||
537 | |||
538 | //------------------------------------------------------------------------ | ||
539 | // Links | ||
540 | //------------------------------------------------------------------------ | ||
541 | |||
542 | Links::Links(Object *annots, GString *baseURI) { | ||
543 | Link *link; | ||
544 | Object obj1, obj2; | ||
545 | int size; | ||
546 | int i; | ||
547 | |||
548 | links = NULL; | ||
549 | size = 0; | ||
550 | numLinks = 0; | ||
551 | |||
552 | if (annots->isArray()) { | ||
553 | for (i = 0; i < annots->arrayGetLength(); ++i) { | ||
554 | if (annots->arrayGet(i, &obj1)->isDict()) { | ||
555 | if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) { | ||
556 | link = new Link(obj1.getDict(), baseURI); | ||
557 | if (link->isOk()) { | ||
558 | if (numLinks >= size) { | ||
559 | size += 16; | ||
560 | links = (Link **)grealloc(links, size * sizeof(Link *)); | ||
561 | } | ||
562 | links[numLinks++] = link; | ||
563 | } else { | ||
564 | delete link; | ||
565 | } | ||
566 | } | ||
567 | obj2.free(); | ||
568 | } | ||
569 | obj1.free(); | ||
570 | } | ||
571 | } | ||
572 | } | ||
573 | |||
574 | Links::~Links() { | ||
575 | int i; | ||
576 | |||
577 | for (i = 0; i < numLinks; ++i) | ||
578 | delete links[i]; | ||
579 | gfree(links); | ||
580 | } | ||
581 | |||
582 | LinkAction *Links::find(fouble x, fouble y) { | ||
583 | int i; | ||
584 | |||
585 | for (i = 0; i < numLinks; ++i) { | ||
586 | if (links[i]->inRect(x, y)) { | ||
587 | return links[i]->getAction(); | ||
588 | } | ||
589 | } | ||
590 | return NULL; | ||
591 | } | ||
592 | |||
593 | GBool Links::onLink(fouble x, fouble y) { | ||
594 | int i; | ||
595 | |||
596 | for (i = 0; i < numLinks; ++i) { | ||
597 | if (links[i]->inRect(x, y)) | ||
598 | return gTrue; | ||
599 | } | ||
600 | return gFalse; | ||
601 | } | ||
602 | |||
603 | //------------------------------------------------------------------------ | ||
604 | |||
605 | // Extract a file name from a file specification (string or dictionary). | ||
606 | static GString *getFileSpecName(Object *fileSpecObj) { | ||
607 | GString *name; | ||
608 | Object obj1; | ||
609 | |||
610 | name = NULL; | ||
611 | |||
612 | // string | ||
613 | if (fileSpecObj->isString()) { | ||
614 | name = fileSpecObj->getString()->copy(); | ||
615 | |||
616 | // dictionary | ||
617 | } else if (fileSpecObj->isDict()) { | ||
618 | if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) { | ||
619 | obj1.free(); | ||
620 | fileSpecObj->dictLookup("F", &obj1); | ||
621 | } | ||
622 | if (obj1.isString()) | ||
623 | name = obj1.getString()->copy(); | ||
624 | else | ||
625 | error(-1, "Illegal file spec in link"); | ||
626 | obj1.free(); | ||
627 | |||
628 | // error | ||
629 | } else { | ||
630 | error(-1, "Illegal file spec in link"); | ||
631 | } | ||
632 | |||
633 | return name; | ||
634 | } | ||