]> git.imager.perl.org - imager.git/blob - tags.c
update Changes
[imager.git] / tags.c
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 <string.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <limits.h>
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 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 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 int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
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 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 int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data, 
110                int size, int idata) {
111   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   if (tags->tags == NULL) {
115     int alloc = 10;
116     tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
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;
123     void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
124     if (!newtags) {
125       return 0;
126     }
127     tags->tags = newtags;
128     tags->alloc = newalloc;
129   }
130   if (name) {
131     work.name = mymalloc(strlen(name)+1);
132     if (!work.name)
133       return 0;
134     strcpy(work.name, name);
135   }
136   if (data) {
137     if (size == -1)
138       size = strlen(data);
139     work.data = mymalloc(size+1);
140     if (!work.data) {
141       if (work.name) myfree(work.name);
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
152   /*i_tags_print(tags);*/
153
154   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 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)
172         myfree(tags->tags[i].name);
173       if (tags->tags[i].data)
174         myfree(tags->tags[i].data);
175     }
176     myfree(tags->tags);
177   }
178 }
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<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
194 int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
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
207 /*
208 =item i_tags_findn(tags, code, start, &entry)
209
210 =category Tags
211
212 Searches 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
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
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 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   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,
251             (tags->count-entry-1) * sizeof(i_img_tag));
252     if (old.name)
253       myfree(old.name);
254     if (old.data)
255       myfree(old.data);
256     --tags->count;
257
258     return 1;
259   }
260   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 int i_tags_delbyname(i_img_tags *tags, char const *name) {
276   int count = 0;
277   int i;
278   /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
279     tags, tags->count, name);*/
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   }
288   /*i_tags_print(tags);*/
289
290   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 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
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
336 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   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
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 int i_tags_set_float(i_img_tags *tags, char const *name, int code, 
369                      double value) {
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
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<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) {
389   char temp[40];
390
391   if (places < 0) 
392     places = 30;
393   else if (places > 30) 
394     places = 30;
395
396   sprintf(temp, "%.*g", places, value);
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
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<value> to the integer and returns true.
413
414 On failure returns false.
415
416 =cut
417 */
418
419 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   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
440 static int parse_long(char *data, char **end, long *out) {
441   long result;
442   int savederr = errno;
443   char *myend;
444
445   errno = 0;
446   result = strtol(data, &myend, 10);
447   if (((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
448       || myend == data) {
449     errno = savederr;
450     return 0;
451   }
452
453   errno = savederr;
454   *out = result;
455   *end = myend;
456
457   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 static int parse_long_list(char *data, char **end, int maxcount, long *out) {
466   int i;
467
468   i = 0;
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
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
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
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 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
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<NUL> terminated.
584
585 =cut
586 */
587
588 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   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 {
610     sprintf(value, "%d", entry->idata);
611   }
612
613   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<data>
622
623 If size is -1 then the strlen(I<data>) bytes are stored.
624
625 Even on failure, if an existing tag I<name> exists, it will be
626 removed.
627
628 =cut
629 */
630
631 int
632 i_tags_set(i_img_tags *tags, char const *name, char const *data, int size) {
633   i_tags_delbyname(tags, name);
634
635   return i_tags_add(tags, name, 0, data, size, 0);
636 }
637
638 /*
639 =item i_tags_setn(C<tags>, C<name>, C<idata>)
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<idata>
645
646 Even on failure, if an existing tag C<name> exists, it will be
647 removed.
648
649 =cut
650 */
651
652 int
653 i_tags_setn(i_img_tags *tags, char const *name, int idata) {
654   i_tags_delbyname(tags, name);
655
656   return i_tags_addn(tags, name, 0, idata);
657 }
658
659 void i_tags_print(i_img_tags *tags) {
660   int i;
661   printf("Alloc %d\n", tags->alloc);
662   printf("Count %d\n", tags->count);
663   for (i = 0; i < tags->count; ++i) {
664     i_img_tag *tag = tags->tags + i;
665     printf("Tag %d\n", i);
666     if (tag->name)
667       printf(" Name : %s (%p)\n", tag->name, tag->name);
668     printf(" Code : %d\n", tag->code);
669     if (tag->data) {
670       int pos;
671       printf(" Data : %d (%p) => '", tag->size, tag->data);
672       for (pos = 0; pos < tag->size; ++pos) {
673         if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
674           putchar('\\');
675           putchar(tag->data[pos]);
676         }
677         else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
678           printf("\\x%02X", tag->data[pos]);
679         else
680           putchar(tag->data[pos]);
681       }
682       printf("'\n");
683       printf(" Idata: %d\n", tag->idata);
684     }
685   }
686 }
687
688 /*
689 =back
690
691 =head1 AUTHOR
692
693 Tony Cook <tony@develop-help.com>
694
695 =head1 SEE ALSO
696
697 Imager(3)
698
699 =cut
700 */