Commit | Line | Data |
---|---|---|
faa9b3e7 TC |
1 | /* |
2 | =head1 NAME | |
3 | ||
4 | tags.c - functions for manipulating an images tags list | |
5 | ||
6 | =head1 SYNOPSIS | |
7 | ||
8 | i_img_tags tags; | |
9 | i_tags_new(&tags); | |
10 | i_tags_destroy(&tags); | |
11 | i_tags_addn(&tags, "name", code, idata); | |
12 | i_tags_add(&tags, "name", code, data, data_size, idata); | |
13 | if (i_tags_find(&tags, name, start, &entry)) { found } | |
14 | if (i_tags_findn(&tags, code, start, &entry)) { found } | |
15 | i_tags_delete(&tags, index); | |
16 | count = i_tags_delbyname(tags, name); | |
17 | count = i_tags_delbycode(tags, code); | |
2e41e30b TC |
18 | if (i_tags_get_float(&tags, name, code, &float_value)) { found } |
19 | i_tags_set_float(&tags, name, code, value); | |
20 | i_tags_set_float2(&tags, name, code, value, sig_digits); | |
21 | i_tags_get_int(&tags, name, code, &int_value); | |
faa9b3e7 TC |
22 | |
23 | =head1 DESCRIPTION | |
24 | ||
25 | Provides functions which give write access to the tags list of an image. | |
26 | ||
27 | For read access directly access the fields (do not write any fields | |
28 | directly). | |
29 | ||
30 | A tag is represented by an i_img_tag structure: | |
31 | ||
32 | typedef enum { | |
33 | itt_double, | |
34 | iit_text | |
35 | } i_tag_type; | |
36 | ||
37 | typedef struct { | |
38 | char *name; // name of a given tag, might be NULL | |
39 | int code; // number of a given tag, -1 if it has no meaning | |
40 | char *data; // value of a given tag if it's not an int, may be NULL | |
41 | int size; // size of the data | |
42 | int idata; // value of a given tag if data is NULL | |
43 | } i_img_tag; | |
44 | ||
45 | ||
46 | =over | |
47 | ||
48 | =cut | |
49 | */ | |
50 | ||
92bda632 | 51 | #include "imager.h" |
faa9b3e7 TC |
52 | #include <string.h> |
53 | #include <stdlib.h> | |
97c4effc TC |
54 | #include <errno.h> |
55 | #include <limits.h> | |
faa9b3e7 TC |
56 | |
57 | /* useful for debugging */ | |
58 | void i_tags_print(i_img_tags *tags); | |
59 | ||
60 | /* | |
61 | =item i_tags_new(i_img_tags *tags) | |
62 | ||
92bda632 TC |
63 | =category Tags |
64 | ||
faa9b3e7 TC |
65 | Initialize a tags structure. Should not be used if the tags structure |
66 | has been previously used. | |
67 | ||
92bda632 TC |
68 | This should be called tags member of an i_img object on creation (in |
69 | i_img_*_new() functions). | |
70 | ||
faa9b3e7 TC |
71 | To destroy the contents use i_tags_destroy() |
72 | ||
73 | =cut | |
74 | */ | |
75 | ||
76 | void i_tags_new(i_img_tags *tags) { | |
77 | tags->count = tags->alloc = 0; | |
78 | tags->tags = NULL; | |
79 | } | |
80 | ||
81 | /* | |
82 | =item i_tags_addn(i_img_tags *tags, char *name, int code, int idata) | |
83 | ||
84 | Adds a tag that has an integer value. A simple wrapper around i_tags_add(). | |
85 | ||
86 | Duplicate tags can be added. | |
87 | ||
88 | Returns non-zero on success. | |
89 | ||
90 | =cut | |
91 | */ | |
92 | ||
97c4effc | 93 | int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) { |
faa9b3e7 TC |
94 | return i_tags_add(tags, name, code, NULL, 0, idata); |
95 | } | |
96 | ||
97 | /* | |
98 | =item i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size, i_tag_type type, int idata) | |
99 | ||
100 | Adds a tag to the tags list. | |
101 | ||
102 | Duplicate tags can be added. | |
103 | ||
104 | Returns non-zero on success. | |
105 | ||
106 | =cut | |
107 | */ | |
108 | ||
97c4effc TC |
109 | int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data, |
110 | int size, int idata) { | |
faa9b3e7 | 111 | i_img_tag work = {0}; |
97c4effc TC |
112 | /*printf("i_tags_add(tags %p [count %d], name %s, code %d, data %p, size %d, idata %d)\n", |
113 | tags, tags->count, name, code, data, size, idata);*/ | |
faa9b3e7 TC |
114 | if (tags->tags == NULL) { |
115 | int alloc = 10; | |
4dfa5522 | 116 | tags->tags = mymalloc(sizeof(i_img_tag) * alloc); |
faa9b3e7 TC |
117 | if (!tags->tags) |
118 | return 0; | |
119 | tags->alloc = alloc; | |
120 | } | |
121 | else if (tags->count == tags->alloc) { | |
122 | int newalloc = tags->alloc + 10; | |
7f882a01 | 123 | void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc); |
faa9b3e7 TC |
124 | if (!newtags) { |
125 | return 0; | |
126 | } | |
127 | tags->tags = newtags; | |
128 | tags->alloc = newalloc; | |
129 | } | |
130 | if (name) { | |
4dfa5522 | 131 | work.name = mymalloc(strlen(name)+1); |
faa9b3e7 TC |
132 | if (!work.name) |
133 | return 0; | |
134 | strcpy(work.name, name); | |
135 | } | |
136 | if (data) { | |
403946c6 TC |
137 | if (size == -1) |
138 | size = strlen(data); | |
4dfa5522 | 139 | work.data = mymalloc(size+1); |
faa9b3e7 | 140 | if (!work.data) { |
4dfa5522 | 141 | if (work.name) myfree(work.name); |
faa9b3e7 TC |
142 | return 0; |
143 | } | |
144 | memcpy(work.data, data, size); | |
145 | work.data[size] = '\0'; /* convenience */ | |
146 | work.size = size; | |
147 | } | |
148 | work.code = code; | |
149 | work.idata = idata; | |
150 | tags->tags[tags->count++] = work; | |
151 | ||
97c4effc TC |
152 | /*i_tags_print(tags);*/ |
153 | ||
faa9b3e7 TC |
154 | return 1; |
155 | } | |
156 | ||
92bda632 TC |
157 | /* |
158 | =item i_tags_destroy(tags) | |
159 | ||
160 | =category Tags | |
161 | ||
162 | Destroys the given tags structure. Called by i_img_destroy(). | |
163 | ||
164 | =cut | |
165 | */ | |
166 | ||
faa9b3e7 TC |
167 | void i_tags_destroy(i_img_tags *tags) { |
168 | if (tags->tags) { | |
169 | int i; | |
170 | for (i = 0; i < tags->count; ++i) { | |
171 | if (tags->tags[i].name) | |
4dfa5522 | 172 | myfree(tags->tags[i].name); |
faa9b3e7 | 173 | if (tags->tags[i].data) |
4dfa5522 | 174 | myfree(tags->tags[i].data); |
faa9b3e7 | 175 | } |
4dfa5522 | 176 | myfree(tags->tags); |
faa9b3e7 TC |
177 | } |
178 | } | |
179 | ||
92bda632 TC |
180 | /* |
181 | =item i_tags_find(tags, name, start, &entry) | |
182 | ||
183 | =category Tags | |
184 | ||
185 | Searchs for a tag of the given I<name> starting from index I<start>. | |
186 | ||
187 | On success returns true and sets *I<entry>. | |
188 | ||
189 | On failure returns false. | |
190 | ||
191 | =cut | |
192 | */ | |
193 | ||
97c4effc | 194 | int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) { |
faa9b3e7 TC |
195 | if (tags->tags) { |
196 | while (start < tags->count) { | |
197 | if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) { | |
198 | *entry = start; | |
199 | return 1; | |
200 | } | |
201 | ++start; | |
202 | } | |
203 | } | |
204 | return 0; | |
205 | } | |
206 | ||
92bda632 TC |
207 | /* |
208 | =item i_tags_findn(tags, code, start, &entry) | |
209 | ||
210 | =category Tags | |
211 | ||
212 | Searchs for a tag of the given I<code> starting from index I<start>. | |
213 | ||
214 | On success returns true and sets *I<entry>. | |
215 | ||
216 | On failure returns false. | |
217 | ||
218 | =cut | |
219 | */ | |
220 | ||
faa9b3e7 TC |
221 | int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) { |
222 | if (tags->tags) { | |
223 | while (start < tags->count) { | |
224 | if (tags->tags[start].code == code) { | |
225 | *entry = start; | |
226 | return 1; | |
227 | } | |
228 | ++start; | |
229 | } | |
230 | } | |
231 | return 0; | |
232 | } | |
233 | ||
92bda632 TC |
234 | /* |
235 | =item i_tags_delete(tags, index) | |
236 | ||
237 | =category Tags | |
238 | ||
239 | Delete a tag by index. | |
240 | ||
241 | Returns true on success. | |
242 | ||
243 | =cut | |
244 | */ | |
faa9b3e7 | 245 | int i_tags_delete(i_img_tags *tags, int entry) { |
97c4effc TC |
246 | /*printf("i_tags_delete(tags %p [count %d], entry %d)\n", |
247 | tags, tags->count, entry);*/ | |
faa9b3e7 TC |
248 | if (tags->tags && entry >= 0 && entry < tags->count) { |
249 | i_img_tag old = tags->tags[entry]; | |
250 | memmove(tags->tags+entry, tags->tags+entry+1, | |
97c4effc | 251 | (tags->count-entry-1) * sizeof(i_img_tag)); |
faa9b3e7 | 252 | if (old.name) |
4dfa5522 | 253 | myfree(old.name); |
faa9b3e7 | 254 | if (old.data) |
4dfa5522 | 255 | myfree(old.data); |
faa9b3e7 | 256 | --tags->count; |
97c4effc | 257 | |
faa9b3e7 TC |
258 | return 1; |
259 | } | |
260 | return 0; | |
261 | } | |
262 | ||
92bda632 TC |
263 | /* |
264 | =item i_tags_delbyname(tags, name) | |
265 | ||
266 | =category Tags | |
267 | ||
268 | Delete any tags with the given name. | |
269 | ||
270 | Returns the number of tags deleted. | |
271 | ||
272 | =cut | |
273 | */ | |
274 | ||
97c4effc | 275 | int i_tags_delbyname(i_img_tags *tags, char const *name) { |
faa9b3e7 TC |
276 | int count = 0; |
277 | int i; | |
97c4effc TC |
278 | /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n", |
279 | tags, tags->count, name);*/ | |
faa9b3e7 TC |
280 | if (tags->tags) { |
281 | for (i = tags->count-1; i >= 0; --i) { | |
282 | if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) { | |
283 | ++count; | |
284 | i_tags_delete(tags, i); | |
285 | } | |
286 | } | |
287 | } | |
97c4effc TC |
288 | /*i_tags_print(tags);*/ |
289 | ||
faa9b3e7 TC |
290 | return count; |
291 | } | |
292 | ||
92bda632 TC |
293 | /* |
294 | =item i_tags_delbycode(tags, code) | |
295 | ||
296 | =category Tags | |
297 | ||
298 | Delete any tags with the given code. | |
299 | ||
300 | Returns the number of tags deleted. | |
301 | ||
302 | =cut | |
303 | */ | |
304 | ||
faa9b3e7 TC |
305 | int i_tags_delbycode(i_img_tags *tags, int code) { |
306 | int count = 0; | |
307 | int i; | |
308 | if (tags->tags) { | |
309 | for (i = tags->count-1; i >= 0; --i) { | |
310 | if (tags->tags[i].code == code) { | |
311 | ++count; | |
312 | i_tags_delete(tags, i); | |
313 | } | |
314 | } | |
315 | } | |
316 | return count; | |
317 | } | |
318 | ||
92bda632 TC |
319 | /* |
320 | =item i_tags_get_float(tags, name, code, value) | |
321 | ||
322 | =category Tags | |
323 | ||
324 | Retrieves a tag as a floating point value. | |
325 | ||
326 | If the tag has a string value then that is parsed as a floating point | |
327 | number, otherwise the integer value of the tag is used. | |
328 | ||
329 | On success sets *I<value> and returns true. | |
330 | ||
331 | On failure returns false. | |
332 | ||
333 | =cut | |
334 | */ | |
335 | ||
97c4effc TC |
336 | int i_tags_get_float(i_img_tags *tags, char const *name, int code, |
337 | double *value) { | |
faa9b3e7 TC |
338 | int index; |
339 | i_img_tag *entry; | |
340 | ||
341 | if (name) { | |
342 | if (!i_tags_find(tags, name, 0, &index)) | |
343 | return 0; | |
344 | } | |
345 | else { | |
346 | if (!i_tags_findn(tags, code, 0, &index)) | |
347 | return 0; | |
348 | } | |
349 | entry = tags->tags+index; | |
350 | if (entry->data) | |
351 | *value = atof(entry->data); | |
352 | else | |
353 | *value = entry->idata; | |
354 | ||
355 | return 1; | |
356 | } | |
357 | ||
92bda632 TC |
358 | /* |
359 | =item i_tags_set_float(tags, name, code, value) | |
360 | ||
361 | =category Tags | |
362 | ||
363 | Equivalent to i_tags_set_float2(tags, name, code, value, 30). | |
364 | ||
365 | =cut | |
366 | */ | |
367 | ||
97c4effc TC |
368 | int i_tags_set_float(i_img_tags *tags, char const *name, int code, |
369 | double value) { | |
2e41e30b TC |
370 | return i_tags_set_float2(tags, name, code, value, 30); |
371 | } | |
372 | ||
373 | /* | |
374 | =item i_tags_set_float2(tags, name, code, value, places) | |
375 | ||
92bda632 TC |
376 | =category Tags |
377 | ||
2e41e30b TC |
378 | Sets the tag with the given name and code to the given floating point |
379 | value. | |
380 | ||
381 | Since tags are strings or ints, we convert the value to a string before | |
382 | storage at the precision specified by C<places>. | |
383 | ||
384 | =cut | |
385 | */ | |
386 | ||
387 | int i_tags_set_float2(i_img_tags *tags, char const *name, int code, | |
388 | double value, int places) { | |
faa9b3e7 TC |
389 | char temp[40]; |
390 | ||
2e41e30b TC |
391 | if (places < 0) |
392 | places = 30; | |
393 | else if (places > 30) | |
394 | places = 30; | |
395 | ||
396 | sprintf(temp, "%.*g", places, value); | |
faa9b3e7 TC |
397 | if (name) |
398 | i_tags_delbyname(tags, name); | |
399 | else | |
400 | i_tags_delbycode(tags, code); | |
401 | ||
402 | return i_tags_add(tags, name, code, temp, strlen(temp), 0); | |
403 | } | |
404 | ||
92bda632 TC |
405 | /* |
406 | =item i_tags_get_int(tags, name, code, &value) | |
407 | ||
408 | =category Tags | |
409 | ||
410 | Retrieve a tag specified by name or code as an integer. | |
411 | ||
743e98e4 | 412 | On success sets the int *I<value> to the integer and returns true. |
92bda632 TC |
413 | |
414 | On failure returns false. | |
415 | ||
416 | =cut | |
417 | */ | |
418 | ||
97c4effc | 419 | int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) { |
faa9b3e7 TC |
420 | int index; |
421 | i_img_tag *entry; | |
422 | ||
423 | if (name) { | |
424 | if (!i_tags_find(tags, name, 0, &index)) | |
425 | return 0; | |
426 | } | |
427 | else { | |
428 | if (!i_tags_findn(tags, code, 0, &index)) | |
429 | return 0; | |
430 | } | |
431 | entry = tags->tags+index; | |
432 | if (entry->data) | |
433 | *value = atoi(entry->data); | |
434 | else | |
435 | *value = entry->idata; | |
436 | ||
437 | return 1; | |
438 | } | |
439 | ||
97c4effc | 440 | static int parse_long(char *data, char **end, long *out) { |
97c4effc TC |
441 | long result; |
442 | int savederr = errno; | |
443 | char *myend; | |
444 | ||
445 | errno = 0; | |
446 | result = strtol(data, &myend, 10); | |
af070d99 | 447 | if (((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE) |
97c4effc | 448 | || myend == data) { |
a659442a | 449 | errno = savederr; |
97c4effc TC |
450 | return 0; |
451 | } | |
452 | ||
a659442a | 453 | errno = savederr; |
97c4effc TC |
454 | *out = result; |
455 | *end = myend; | |
456 | ||
457 | return 1; | |
97c4effc TC |
458 | } |
459 | ||
460 | /* parse a comma-separated list of integers | |
461 | returns when it has maxcount numbers, finds a non-comma after a number | |
462 | or can't parse a number | |
463 | if it can't parse a number after a comma, that's considered an error | |
464 | */ | |
465 | static int parse_long_list(char *data, char **end, int maxcount, long *out) { | |
466 | int i; | |
467 | ||
26fd367b | 468 | i = 0; |
97c4effc TC |
469 | while (i < maxcount-1) { |
470 | if (!parse_long(data, &data, out)) | |
471 | return 0; | |
472 | out++; | |
473 | i++; | |
474 | if (*data != ',') | |
475 | return i; | |
476 | ++data; | |
477 | } | |
478 | if (!parse_long(data, &data, out)) | |
479 | return 0; | |
480 | ++i; | |
481 | *end = data; | |
482 | return i; | |
483 | } | |
484 | ||
485 | /* parse "color(red,green,blue,alpha)" */ | |
486 | static int parse_color(char *data, char **end, i_color *value) { | |
487 | long n[4]; | |
488 | int count, i; | |
489 | ||
490 | if (memcmp(data, "color(", 6)) | |
491 | return 0; /* not a color */ | |
492 | data += 6; | |
493 | count = parse_long_list(data, &data, 4, n); | |
494 | if (count < 3) | |
495 | return 0; | |
496 | for (i = 0; i < count; ++i) | |
497 | value->channel[i] = n[i]; | |
498 | if (count < 4) | |
499 | value->channel[3] = 255; | |
500 | ||
501 | return 1; | |
502 | } | |
503 | ||
92bda632 TC |
504 | /* |
505 | =item i_tags_get_color(tags, name, code, &value) | |
506 | ||
507 | =category Tags | |
508 | ||
509 | Retrieve a tag specified by name or code as color. | |
510 | ||
511 | On success sets the i_color *I<value> to the color and returns true. | |
512 | ||
513 | On failure returns false. | |
514 | ||
515 | =cut | |
516 | */ | |
517 | ||
97c4effc TC |
518 | int i_tags_get_color(i_img_tags *tags, char const *name, int code, |
519 | i_color *value) { | |
520 | int index; | |
521 | i_img_tag *entry; | |
522 | char *end; | |
523 | ||
524 | if (name) { | |
525 | if (!i_tags_find(tags, name, 0, &index)) | |
526 | return 0; | |
527 | } | |
528 | else { | |
529 | if (!i_tags_findn(tags, code, 0, &index)) | |
530 | return 0; | |
531 | } | |
532 | entry = tags->tags+index; | |
533 | if (!entry->data) | |
534 | return 0; | |
535 | ||
536 | if (!parse_color(entry->data, &end, value)) | |
537 | return 0; | |
538 | ||
539 | /* for now we're sloppy about the end */ | |
540 | ||
541 | return 1; | |
542 | } | |
543 | ||
92bda632 TC |
544 | /* |
545 | =item i_tags_set_color(tags, name, code, &value) | |
546 | ||
547 | =category Tags | |
548 | ||
549 | Stores the given color as a tag with the given name and code. | |
550 | ||
551 | =cut | |
552 | */ | |
553 | ||
97c4effc TC |
554 | int i_tags_set_color(i_img_tags *tags, char const *name, int code, |
555 | i_color const *value) { | |
556 | char temp[80]; | |
557 | ||
558 | sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1], | |
559 | value->channel[2], value->channel[3]); | |
560 | if (name) | |
561 | i_tags_delbyname(tags, name); | |
562 | else | |
563 | i_tags_delbycode(tags, code); | |
564 | ||
565 | return i_tags_add(tags, name, code, temp, strlen(temp), 0); | |
566 | } | |
567 | ||
92bda632 TC |
568 | /* |
569 | =item i_tags_get_string(tags, name, code, value, value_size) | |
570 | ||
571 | =category Tags | |
572 | ||
573 | Retrieves a tag by name or code as a string. | |
574 | ||
575 | On success copies the string to value for a max of value_size and | |
576 | returns true. | |
577 | ||
578 | On failure returns false. | |
579 | ||
580 | value_size must be at least large enough for a string representation | |
581 | of an integer. | |
582 | ||
583 | The copied value is always NUL terminated. | |
584 | ||
585 | =cut | |
586 | */ | |
587 | ||
97c4effc | 588 | int i_tags_get_string(i_img_tags *tags, char const *name, int code, |
faa9b3e7 TC |
589 | char *value, size_t value_size) { |
590 | int index; | |
591 | i_img_tag *entry; | |
592 | ||
593 | if (name) { | |
594 | if (!i_tags_find(tags, name, 0, &index)) | |
595 | return 0; | |
596 | } | |
597 | else { | |
598 | if (!i_tags_findn(tags, code, 0, &index)) | |
599 | return 0; | |
600 | } | |
601 | entry = tags->tags+index; | |
602 | if (entry->data) { | |
603 | size_t cpsize = value_size < entry->size ? value_size : entry->size; | |
604 | memcpy(value, entry->data, cpsize); | |
605 | if (cpsize == value_size) | |
606 | --cpsize; | |
607 | value[cpsize] = '\0'; | |
608 | } | |
609 | else { | |
241defe8 | 610 | sprintf(value, "%d", entry->idata); |
faa9b3e7 TC |
611 | } |
612 | ||
613 | return 1; | |
614 | } | |
615 | ||
92bda632 TC |
616 | /* |
617 | =item i_tags_set(tags, name, data, size) | |
618 | ||
619 | =category Tags | |
620 | ||
621 | Sets the given tag to the string I<data> | |
622 | ||
623 | =cut | |
624 | */ | |
625 | ||
626 | int | |
627 | i_tags_set(i_img_tags *tags, char const *name, char const *data, int size) { | |
628 | i_tags_delbyname(tags, name); | |
629 | ||
630 | return i_tags_add(tags, name, 0, data, size, 0); | |
631 | } | |
632 | ||
633 | /* | |
634 | =item i_tags_setn(tags, name, idata) | |
635 | ||
636 | =category Tags | |
637 | ||
638 | Sets the given tag to the integer I<idata> | |
639 | ||
640 | =cut | |
641 | */ | |
642 | ||
643 | int | |
644 | i_tags_setn(i_img_tags *tags, char const *name, int idata) { | |
645 | i_tags_delbyname(tags, name); | |
646 | ||
647 | return i_tags_addn(tags, name, 0, idata); | |
648 | } | |
649 | ||
faa9b3e7 TC |
650 | void i_tags_print(i_img_tags *tags) { |
651 | int i; | |
652 | printf("Alloc %d\n", tags->alloc); | |
653 | printf("Count %d\n", tags->count); | |
654 | for (i = 0; i < tags->count; ++i) { | |
655 | i_img_tag *tag = tags->tags + i; | |
656 | printf("Tag %d\n", i); | |
657 | if (tag->name) | |
97c4effc | 658 | printf(" Name : %s (%p)\n", tag->name, tag->name); |
faa9b3e7 TC |
659 | printf(" Code : %d\n", tag->code); |
660 | if (tag->data) { | |
661 | int pos; | |
97c4effc | 662 | printf(" Data : %d (%p) => '", tag->size, tag->data); |
faa9b3e7 TC |
663 | for (pos = 0; pos < tag->size; ++pos) { |
664 | if (tag->data[pos] == '\\' || tag->data[pos] == '\'') { | |
665 | putchar('\\'); | |
666 | putchar(tag->data[pos]); | |
667 | } | |
668 | else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E') | |
669 | printf("\\x%02X", tag->data[pos]); | |
670 | else | |
671 | putchar(tag->data[pos]); | |
672 | } | |
673 | printf("'\n"); | |
674 | printf(" Idata: %d\n", tag->idata); | |
675 | } | |
676 | } | |
677 | } | |
b8c2033e AMH |
678 | |
679 | /* | |
680 | =back | |
681 | ||
682 | =head1 AUTHOR | |
683 | ||
684 | Tony Cook <tony@develop-help.com> | |
685 | ||
686 | =head1 SEE ALSO | |
687 | ||
688 | Imager(3) | |
689 | ||
690 | =cut | |
691 | */ |