File Coverage

tags.c
Criterion Covered Total %
statement 170 227 74.8
branch 95 150 63.3
condition n/a
subroutine n/a
pod n/a
total 265 377 70.2


line stmt bran cond sub pod time code
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);
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);
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              
51             #include "imager.h"
52             #include
53             #include
54             #include
55             #include
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              
63             =category Tags
64              
65             Initialize a tags structure. Should not be used if the tags structure
66             has been previously used.
67              
68             This should be called tags member of an i_img object on creation (in
69             i_img_*_new() functions).
70              
71             To destroy the contents use i_tags_destroy()
72              
73             =cut
74             */
75              
76 1492           void i_tags_new(i_img_tags *tags) {
77 1492           tags->count = tags->alloc = 0;
78 1492           tags->tags = NULL;
79 1492           }
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             Use i_tags_setn() instead, this function may be removed in the future.
87              
88             Returns non-zero on success.
89              
90             =cut
91             */
92              
93 662           int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
94 662           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             Use i_tags_set() instead, this function may be removed in the future.
103              
104             Returns non-zero on success.
105              
106             =cut
107             */
108              
109 1147           int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data,
110             int size, int idata) {
111 1147           i_img_tag work = {0};
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);*/
114 1147 100         if (tags->tags == NULL) {
115 273           int alloc = 10;
116 273           tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
117 273 50         if (!tags->tags)
118 0           return 0;
119 273           tags->alloc = alloc;
120             }
121 874 100         else if (tags->count == tags->alloc) {
122 8           int newalloc = tags->alloc + 10;
123 8           void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
124 8 50         if (!newtags) {
125 0           return 0;
126             }
127 8           tags->tags = newtags;
128 8           tags->alloc = newalloc;
129             }
130 1147 100         if (name) {
131 1144           work.name = mymalloc(strlen(name)+1);
132 1144 50         if (!work.name)
133 0           return 0;
134 1144           strcpy(work.name, name);
135             }
136 1147 100         if (data) {
137 484 100         if (size == -1)
138 211           size = strlen(data);
139 484           work.data = mymalloc(size+1);
140 484 50         if (!work.data) {
141 0 0         if (work.name) myfree(work.name);
142 0           return 0;
143             }
144 484           memcpy(work.data, data, size);
145 484           work.data[size] = '\0'; /* convenience */
146 484           work.size = size;
147             }
148 1147           work.code = code;
149 1147           work.idata = idata;
150 1147           tags->tags[tags->count++] = work;
151              
152             /*i_tags_print(tags);*/
153              
154 1147           return 1;
155             }
156              
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              
167 1492           void i_tags_destroy(i_img_tags *tags) {
168 1492 100         if (tags->tags) {
169             int i;
170 1406 100         for (i = 0; i < tags->count; ++i) {
171 1133 100         if (tags->tags[i].name)
172 1132           myfree(tags->tags[i].name);
173 1133 100         if (tags->tags[i].data)
174 478           myfree(tags->tags[i].data);
175             }
176 273           myfree(tags->tags);
177             }
178 1492           }
179              
180             /*
181             =item i_tags_find(tags, name, start, &entry)
182              
183             =category Tags
184              
185             Searches for a tag of the given I starting from index I.
186              
187             On success returns true and sets *I.
188              
189             On failure returns false.
190              
191             =cut
192             */
193              
194 684           int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
195 684 100         if (tags->tags) {
196 1792 100         while (start < tags->count) {
197 1563 100         if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
    100          
198 234           *entry = start;
199 234           return 1;
200             }
201 1329           ++start;
202             }
203             }
204 450           return 0;
205             }
206              
207             /*
208             =item i_tags_findn(tags, code, start, &entry)
209              
210             =category Tags
211              
212             Searches for a tag of the given I starting from index I.
213              
214             On success returns true and sets *I.
215              
216             On failure returns false.
217              
218             =cut
219             */
220              
221 5           int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
222 5 50         if (tags->tags) {
223 15 100         while (start < tags->count) {
224 14 100         if (tags->tags[start].code == code) {
225 4           *entry = start;
226 4           return 1;
227             }
228 10           ++start;
229             }
230             }
231 1           return 0;
232             }
233              
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             */
245 14           int i_tags_delete(i_img_tags *tags, int entry) {
246             /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
247             tags, tags->count, entry);*/
248 14 50         if (tags->tags && entry >= 0 && entry < tags->count) {
    50          
    50          
249 14           i_img_tag old = tags->tags[entry];
250 14           memmove(tags->tags+entry, tags->tags+entry+1,
251 14           (tags->count-entry-1) * sizeof(i_img_tag));
252 14 100         if (old.name)
253 12           myfree(old.name);
254 14 100         if (old.data)
255 6           myfree(old.data);
256 14           --tags->count;
257              
258 14           return 1;
259             }
260 0           return 0;
261             }
262              
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              
275 562           int i_tags_delbyname(i_img_tags *tags, char const *name) {
276 562           int count = 0;
277             int i;
278             /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
279             tags, tags->count, name);*/
280 562 100         if (tags->tags) {
281 1385 100         for (i = tags->count-1; i >= 0; --i) {
282 918 100         if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
    100          
283 12           ++count;
284 12           i_tags_delete(tags, i);
285             }
286             }
287             }
288             /*i_tags_print(tags);*/
289              
290 562           return count;
291             }
292              
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              
305 3           int i_tags_delbycode(i_img_tags *tags, int code) {
306 3           int count = 0;
307             int i;
308 3 100         if (tags->tags) {
309 7 100         for (i = tags->count-1; i >= 0; --i) {
310 5 100         if (tags->tags[i].code == code) {
311 1           ++count;
312 1           i_tags_delete(tags, i);
313             }
314             }
315             }
316 3           return count;
317             }
318              
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 and returns true.
330              
331             On failure returns false.
332              
333             =cut
334             */
335              
336 50           int i_tags_get_float(i_img_tags *tags, char const *name, int code,
337             double *value) {
338             int index;
339             i_img_tag *entry;
340              
341 50 50         if (name) {
342 50 100         if (!i_tags_find(tags, name, 0, &index))
343 24           return 0;
344             }
345             else {
346 0 0         if (!i_tags_findn(tags, code, 0, &index))
347 0           return 0;
348             }
349 26           entry = tags->tags+index;
350 26 100         if (entry->data)
351 25           *value = atof(entry->data);
352             else
353 1           *value = entry->idata;
354              
355 50           return 1;
356             }
357              
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              
368 0           int i_tags_set_float(i_img_tags *tags, char const *name, int code,
369             double value) {
370 0           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              
376             =category Tags
377              
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.
383              
384             =cut
385             */
386              
387 116           int i_tags_set_float2(i_img_tags *tags, char const *name, int code,
388             double value, int places) {
389             char temp[40];
390              
391 116 50         if (places < 0)
392 0           places = 30;
393 116 50         else if (places > 30)
394 0           places = 30;
395              
396 116           sprintf(temp, "%.*g", places, value);
397 116 50         if (name)
398 116           i_tags_delbyname(tags, name);
399             else
400 0           i_tags_delbycode(tags, code);
401              
402 116           return i_tags_add(tags, name, code, temp, strlen(temp), 0);
403             }
404              
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              
412             On success sets the int *I to the integer and returns true.
413              
414             On failure returns false.
415              
416             =cut
417             */
418              
419 208           int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
420             int index;
421             i_img_tag *entry;
422              
423 208 50         if (name) {
424 208 100         if (!i_tags_find(tags, name, 0, &index))
425 172           return 0;
426             }
427             else {
428 0 0         if (!i_tags_findn(tags, code, 0, &index))
429 0           return 0;
430             }
431 36           entry = tags->tags+index;
432 36 100         if (entry->data)
433 5           *value = atoi(entry->data);
434             else
435 31           *value = entry->idata;
436              
437 208           return 1;
438             }
439              
440 12           static int parse_long(char *data, char **end, long *out) {
441             long result;
442 12           int savederr = errno;
443             char *myend;
444              
445 12           errno = 0;
446 12           result = strtol(data, &myend, 10);
447 12 50         if (((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
    50          
    0          
448 12 50         || myend == data) {
449 0           errno = savederr;
450 0           return 0;
451             }
452              
453 12           errno = savederr;
454 12           *out = result;
455 12           *end = myend;
456              
457 12           return 1;
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 3           static int parse_long_list(char *data, char **end, int maxcount, long *out) {
466             int i;
467              
468 3           i = 0;
469 12 100         while (i < maxcount-1) {
470 9 50         if (!parse_long(data, &data, out))
471 0           return 0;
472 9           out++;
473 9           i++;
474 9 50         if (*data != ',')
475 0           return i;
476 9           ++data;
477             }
478 3 50         if (!parse_long(data, &data, out))
479 0           return 0;
480 3           ++i;
481 3           *end = data;
482 3           return i;
483             }
484              
485             /* parse "color(red,green,blue,alpha)" */
486 3           static int parse_color(char *data, char **end, i_color *value) {
487             long n[4];
488             int count, i;
489            
490 3 50         if (memcmp(data, "color(", 6))
491 0           return 0; /* not a color */
492 3           data += 6;
493 3           count = parse_long_list(data, &data, 4, n);
494 3 50         if (count < 3)
495 0           return 0;
496 15 100         for (i = 0; i < count; ++i)
497 12           value->channel[i] = n[i];
498 3 50         if (count < 4)
499 0           value->channel[3] = 255;
500              
501 3           return 1;
502             }
503              
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 to the color and returns true.
512              
513             On failure returns false.
514              
515             =cut
516             */
517              
518 47           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 47 50         if (name) {
525 47 100         if (!i_tags_find(tags, name, 0, &index))
526 44           return 0;
527             }
528             else {
529 0 0         if (!i_tags_findn(tags, code, 0, &index))
530 0           return 0;
531             }
532 3           entry = tags->tags+index;
533 3 50         if (!entry->data)
534 0           return 0;
535              
536 3 50         if (!parse_color(entry->data, &end, value))
537 0           return 0;
538            
539             /* for now we're sloppy about the end */
540              
541 47           return 1;
542             }
543              
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              
554 0           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 0           sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
559 0           value->channel[2], value->channel[3]);
560 0 0         if (name)
561 0           i_tags_delbyname(tags, name);
562             else
563 0           i_tags_delbycode(tags, code);
564              
565 0           return i_tags_add(tags, name, code, temp, strlen(temp), 0);
566             }
567              
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 C terminated.
584              
585             =cut
586             */
587              
588 21           int i_tags_get_string(i_img_tags *tags, char const *name, int code,
589             char *value, size_t value_size) {
590             int index;
591             i_img_tag *entry;
592              
593 21 100         if (name) {
594 20 100         if (!i_tags_find(tags, name, 0, &index))
595 17           return 0;
596             }
597             else {
598 1 50         if (!i_tags_findn(tags, code, 0, &index))
599 0           return 0;
600             }
601 4           entry = tags->tags+index;
602 4 100         if (entry->data) {
603 3           size_t cpsize = value_size < entry->size ? value_size : entry->size;
604 3           memcpy(value, entry->data, cpsize);
605 3 50         if (cpsize == value_size)
606 0           --cpsize;
607 3           value[cpsize] = '\0';
608             }
609             else {
610 1           sprintf(value, "%d", entry->idata);
611             }
612              
613 21           return 1;
614             }
615              
616             /*
617             =item i_tags_set(tags, name, data, size)
618             =synopsis i_tags_set(&img->tags, "i_comment", -1);
619             =category Tags
620              
621             Sets the given tag to the string I
622              
623             If size is -1 then the strlen(I) bytes are stored.
624              
625             Even on failure, if an existing tag I exists, it will be
626             removed.
627              
628             =cut
629             */
630              
631             int
632 104           i_tags_set(i_img_tags *tags, char const *name, char const *data, int size) {
633 104           i_tags_delbyname(tags, name);
634              
635 104           return i_tags_add(tags, name, 0, data, size, 0);
636             }
637              
638             /*
639             =item i_tags_setn(C, C, C)
640             =synopsis i_tags_setn(&img->tags, "i_xres", 204);
641             =synopsis i_tags_setn(&img->tags, "i_yres", 196);
642             =category Tags
643              
644             Sets the given tag to the integer C
645              
646             Even on failure, if an existing tag C exists, it will be
647             removed.
648              
649             =cut
650             */
651              
652             int
653 303           i_tags_setn(i_img_tags *tags, char const *name, int idata) {
654 303           i_tags_delbyname(tags, name);
655              
656 303           return i_tags_addn(tags, name, 0, idata);
657             }
658              
659 0           void i_tags_print(i_img_tags *tags) {
660             int i;
661 0           printf("Alloc %d\n", tags->alloc);
662 0           printf("Count %d\n", tags->count);
663 0 0         for (i = 0; i < tags->count; ++i) {
664 0           i_img_tag *tag = tags->tags + i;
665 0           printf("Tag %d\n", i);
666 0 0         if (tag->name)
667 0           printf(" Name : %s (%p)\n", tag->name, tag->name);
668 0           printf(" Code : %d\n", tag->code);
669 0 0         if (tag->data) {
670             int pos;
671 0           printf(" Data : %d (%p) => '", tag->size, tag->data);
672 0 0         for (pos = 0; pos < tag->size; ++pos) {
673 0 0         if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
    0          
674 0           putchar('\\');
675 0           putchar(tag->data[pos]);
676             }
677 0 0         else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
    0          
678 0           printf("\\x%02X", tag->data[pos]);
679             else
680 0           putchar(tag->data[pos]);
681             }
682 0           printf("'\n");
683 0           printf(" Idata: %d\n", tag->idata);
684             }
685             }
686 0           }
687              
688             /*
689             =back
690              
691             =head1 AUTHOR
692              
693             Tony Cook
694              
695             =head1 SEE ALSO
696              
697             Imager(3)
698              
699             =cut
700             */