- eliminate unused variables and static functions
[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 "image.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 Initialize a tags structure.  Should not be used if the tags structure
64 has been previously used.
65
66 To destroy the contents use i_tags_destroy()
67
68 =cut
69 */
70
71 void i_tags_new(i_img_tags *tags) {
72   tags->count = tags->alloc = 0;
73   tags->tags = NULL;
74 }
75
76 /*
77 =item i_tags_addn(i_img_tags *tags, char *name, int code, int idata)
78
79 Adds a tag that has an integer value.  A simple wrapper around i_tags_add().
80
81 Duplicate tags can be added.
82
83 Returns non-zero on success.
84
85 =cut
86 */
87
88 int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
89   return i_tags_add(tags, name, code, NULL, 0, idata);
90 }
91
92 /*
93 =item i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size, i_tag_type type, int idata)
94
95 Adds a tag to the tags list.
96
97 Duplicate tags can be added.
98
99 Returns non-zero on success.
100
101 =cut
102 */
103
104 int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data, 
105                int size, int idata) {
106   i_img_tag work = {0};
107   /*printf("i_tags_add(tags %p [count %d], name %s, code %d, data %p, size %d, idata %d)\n",
108     tags, tags->count, name, code, data, size, idata);*/
109   if (tags->tags == NULL) {
110     int alloc = 10;
111     tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
112     if (!tags->tags)
113       return 0;
114     tags->alloc = alloc;
115   }
116   else if (tags->count == tags->alloc) {
117     int newalloc = tags->alloc + 10;
118     void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
119     if (!newtags) {
120       return 0;
121     }
122     tags->tags = newtags;
123     tags->alloc = newalloc;
124   }
125   if (name) {
126     work.name = mymalloc(strlen(name)+1);
127     if (!work.name)
128       return 0;
129     strcpy(work.name, name);
130   }
131   if (data) {
132     if (size == -1)
133       size = strlen(data);
134     work.data = mymalloc(size+1);
135     if (!work.data) {
136       if (work.name) myfree(work.name);
137       return 0;
138     }
139     memcpy(work.data, data, size);
140     work.data[size] = '\0'; /* convenience */
141     work.size = size;
142   }
143   work.code = code;
144   work.idata = idata;
145   tags->tags[tags->count++] = work;
146
147   /*i_tags_print(tags);*/
148
149   return 1;
150 }
151
152 void i_tags_destroy(i_img_tags *tags) {
153   if (tags->tags) {
154     int i;
155     for (i = 0; i < tags->count; ++i) {
156       if (tags->tags[i].name)
157         myfree(tags->tags[i].name);
158       if (tags->tags[i].data)
159         myfree(tags->tags[i].data);
160     }
161     myfree(tags->tags);
162   }
163 }
164
165 int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
166   if (tags->tags) {
167     while (start < tags->count) {
168       if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
169         *entry = start;
170         return 1;
171       }
172       ++start;
173     }
174   }
175   return 0;
176 }
177
178 int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
179   if (tags->tags) {
180     while (start < tags->count) {
181       if (tags->tags[start].code == code) {
182         *entry = start;
183         return 1;
184       }
185       ++start;
186     }
187   }
188   return 0;
189 }
190
191 int i_tags_delete(i_img_tags *tags, int entry) {
192   /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
193     tags, tags->count, entry);*/
194   if (tags->tags && entry >= 0 && entry < tags->count) {
195     i_img_tag old = tags->tags[entry];
196     memmove(tags->tags+entry, tags->tags+entry+1,
197             (tags->count-entry-1) * sizeof(i_img_tag));
198     if (old.name)
199       myfree(old.name);
200     if (old.data)
201       myfree(old.data);
202     --tags->count;
203
204     return 1;
205   }
206   return 0;
207 }
208
209 int i_tags_delbyname(i_img_tags *tags, char const *name) {
210   int count = 0;
211   int i;
212   /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
213     tags, tags->count, name);*/
214   if (tags->tags) {
215     for (i = tags->count-1; i >= 0; --i) {
216       if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
217         ++count;
218         i_tags_delete(tags, i);
219       }
220     }
221   }
222   /*i_tags_print(tags);*/
223
224   return count;
225 }
226
227 int i_tags_delbycode(i_img_tags *tags, int code) {
228   int count = 0;
229   int i;
230   if (tags->tags) {
231     for (i = tags->count-1; i >= 0; --i) {
232       if (tags->tags[i].code == code) {
233         ++count;
234         i_tags_delete(tags, i);
235       }
236     }
237   }
238   return count;
239 }
240
241 int i_tags_get_float(i_img_tags *tags, char const *name, int code, 
242                      double *value) {
243   int index;
244   i_img_tag *entry;
245
246   if (name) {
247     if (!i_tags_find(tags, name, 0, &index))
248       return 0;
249   }
250   else {
251     if (!i_tags_findn(tags, code, 0, &index))
252       return 0;
253   }
254   entry = tags->tags+index;
255   if (entry->data)
256     *value = atof(entry->data);
257   else
258     *value = entry->idata;
259
260   return 1;
261 }
262
263 int i_tags_set_float(i_img_tags *tags, char const *name, int code, 
264                      double value) {
265   return i_tags_set_float2(tags, name, code, value, 30);
266 }
267
268 /*
269 =item i_tags_set_float2(tags, name, code, value, places)
270
271 Sets the tag with the given name and code to the given floating point
272 value.
273
274 Since tags are strings or ints, we convert the value to a string before
275 storage at the precision specified by C<places>.
276
277 =cut
278 */
279
280 int i_tags_set_float2(i_img_tags *tags, char const *name, int code, 
281                       double value, int places) {
282   char temp[40];
283
284   if (places < 0) 
285     places = 30;
286   else if (places > 30) 
287     places = 30;
288
289   sprintf(temp, "%.*g", places, value);
290   if (name)
291     i_tags_delbyname(tags, name);
292   else
293     i_tags_delbycode(tags, code);
294
295   return i_tags_add(tags, name, code, temp, strlen(temp), 0);
296 }
297
298 int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
299   int index;
300   i_img_tag *entry;
301
302   if (name) {
303     if (!i_tags_find(tags, name, 0, &index))
304       return 0;
305   }
306   else {
307     if (!i_tags_findn(tags, code, 0, &index))
308       return 0;
309   }
310   entry = tags->tags+index;
311   if (entry->data)
312     *value = atoi(entry->data);
313   else
314     *value = entry->idata;
315
316   return 1;
317 }
318
319 static int parse_long(char *data, char **end, long *out) {
320   long result;
321   int savederr = errno;
322   char *myend;
323
324   errno = 0;
325   result = strtol(data, &myend, 10);
326   if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE
327       || myend == data) {
328     errno = savederr;
329     return 0;
330   }
331
332   errno = savederr;
333   *out = result;
334   *end = myend;
335
336   return 1;
337 }
338
339 /* parse a comma-separated list of integers
340    returns when it has maxcount numbers, finds a non-comma after a number
341    or can't parse a number
342    if it can't parse a number after a comma, that's considered an error
343 */
344 static int parse_long_list(char *data, char **end, int maxcount, long *out) {
345   int i;
346
347   i = 0;
348   while (i < maxcount-1) {
349     if (!parse_long(data, &data, out))
350       return 0;
351     out++;
352     i++;
353     if (*data != ',')
354       return i;
355     ++data;
356   }
357   if (!parse_long(data, &data, out))
358     return 0;
359   ++i;
360   *end = data;
361   return i;
362 }
363
364 /* parse "color(red,green,blue,alpha)" */
365 static int parse_color(char *data, char **end, i_color *value) {
366   long n[4];
367   int count, i;
368   
369   if (memcmp(data, "color(", 6))
370     return 0; /* not a color */
371   data += 6;
372   count = parse_long_list(data, &data, 4, n);
373   if (count < 3)
374     return 0;
375   for (i = 0; i < count; ++i)
376     value->channel[i] = n[i];
377   if (count < 4)
378     value->channel[3] = 255;
379
380   return 1;
381 }
382
383 int i_tags_get_color(i_img_tags *tags, char const *name, int code, 
384                      i_color *value) {
385   int index;
386   i_img_tag *entry;
387   char *end;
388
389   if (name) {
390     if (!i_tags_find(tags, name, 0, &index))
391       return 0;
392   }
393   else {
394     if (!i_tags_findn(tags, code, 0, &index))
395       return 0;
396   }
397   entry = tags->tags+index;
398   if (!entry->data) 
399     return 0;
400
401   if (!parse_color(entry->data, &end, value))
402     return 0;
403   
404   /* for now we're sloppy about the end */
405
406   return 1;
407 }
408
409 int i_tags_set_color(i_img_tags *tags, char const *name, int code, 
410                      i_color const *value) {
411   char temp[80];
412
413   sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
414           value->channel[2], value->channel[3]);
415   if (name)
416     i_tags_delbyname(tags, name);
417   else
418     i_tags_delbycode(tags, code);
419
420   return i_tags_add(tags, name, code, temp, strlen(temp), 0);
421 }
422
423 int i_tags_get_string(i_img_tags *tags, char const *name, int code, 
424                       char *value, size_t value_size) {
425   int index;
426   i_img_tag *entry;
427
428   if (name) {
429     if (!i_tags_find(tags, name, 0, &index))
430       return 0;
431   }
432   else {
433     if (!i_tags_findn(tags, code, 0, &index))
434       return 0;
435   }
436   entry = tags->tags+index;
437   if (entry->data) {
438     size_t cpsize = value_size < entry->size ? value_size : entry->size;
439     memcpy(value, entry->data, cpsize);
440     if (cpsize == value_size)
441       --cpsize;
442     value[cpsize] = '\0';
443   }
444   else {
445     sprintf(value, "%d", entry->data);
446   }
447
448   return 1;
449 }
450
451 void i_tags_print(i_img_tags *tags) {
452   int i;
453   printf("Alloc %d\n", tags->alloc);
454   printf("Count %d\n", tags->count);
455   for (i = 0; i < tags->count; ++i) {
456     i_img_tag *tag = tags->tags + i;
457     printf("Tag %d\n", i);
458     if (tag->name)
459       printf(" Name : %s (%p)\n", tag->name, tag->name);
460     printf(" Code : %d\n", tag->code);
461     if (tag->data) {
462       int pos;
463       printf(" Data : %d (%p) => '", tag->size, tag->data);
464       for (pos = 0; pos < tag->size; ++pos) {
465         if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
466           putchar('\\');
467           putchar(tag->data[pos]);
468         }
469         else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
470           printf("\\x%02X", tag->data[pos]);
471         else
472           putchar(tag->data[pos]);
473       }
474       printf("'\n");
475       printf(" Idata: %d\n", tag->idata);
476     }
477   }
478 }
479
480 /*
481 =back
482
483 =head1 AUTHOR
484
485 Tony Cook <tony@develop-help.com>
486
487 =head1 SEE ALSO
488
489 Imager(3)
490
491 =cut
492 */