- extra concept index entries
[imager.git] / tags.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4tags.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
25Provides functions which give write access to the tags list of an image.
26
27For read access directly access the fields (do not write any fields
28directly).
29
30A 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>
97c4effc
TC
54#include <errno.h>
55#include <limits.h>
faa9b3e7
TC
56
57/* useful for debugging */
58void i_tags_print(i_img_tags *tags);
59
60/*
61=item i_tags_new(i_img_tags *tags)
62
63Initialize a tags structure. Should not be used if the tags structure
64has been previously used.
65
66To destroy the contents use i_tags_destroy()
67
68=cut
69*/
70
71void 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
79Adds a tag that has an integer value. A simple wrapper around i_tags_add().
80
81Duplicate tags can be added.
82
83Returns non-zero on success.
84
85=cut
86*/
87
97c4effc 88int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
faa9b3e7
TC
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
95Adds a tag to the tags list.
96
97Duplicate tags can be added.
98
99Returns non-zero on success.
100
101=cut
102*/
103
97c4effc
TC
104int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data,
105 int size, int idata) {
faa9b3e7 106 i_img_tag work = {0};
97c4effc
TC
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);*/
faa9b3e7
TC
109 if (tags->tags == NULL) {
110 int alloc = 10;
4dfa5522 111 tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
faa9b3e7
TC
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;
7f882a01 118 void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
faa9b3e7
TC
119 if (!newtags) {
120 return 0;
121 }
122 tags->tags = newtags;
123 tags->alloc = newalloc;
124 }
125 if (name) {
4dfa5522 126 work.name = mymalloc(strlen(name)+1);
faa9b3e7
TC
127 if (!work.name)
128 return 0;
129 strcpy(work.name, name);
130 }
131 if (data) {
403946c6
TC
132 if (size == -1)
133 size = strlen(data);
4dfa5522 134 work.data = mymalloc(size+1);
faa9b3e7 135 if (!work.data) {
4dfa5522 136 if (work.name) myfree(work.name);
faa9b3e7
TC
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
97c4effc
TC
147 /*i_tags_print(tags);*/
148
faa9b3e7
TC
149 return 1;
150}
151
152void 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)
4dfa5522 157 myfree(tags->tags[i].name);
faa9b3e7 158 if (tags->tags[i].data)
4dfa5522 159 myfree(tags->tags[i].data);
faa9b3e7 160 }
4dfa5522 161 myfree(tags->tags);
faa9b3e7
TC
162 }
163}
164
97c4effc 165int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
faa9b3e7
TC
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
178int 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
191int i_tags_delete(i_img_tags *tags, int entry) {
97c4effc
TC
192 /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
193 tags, tags->count, entry);*/
faa9b3e7
TC
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,
97c4effc 197 (tags->count-entry-1) * sizeof(i_img_tag));
faa9b3e7 198 if (old.name)
4dfa5522 199 myfree(old.name);
faa9b3e7 200 if (old.data)
4dfa5522 201 myfree(old.data);
faa9b3e7 202 --tags->count;
97c4effc 203
faa9b3e7
TC
204 return 1;
205 }
206 return 0;
207}
208
97c4effc 209int i_tags_delbyname(i_img_tags *tags, char const *name) {
faa9b3e7
TC
210 int count = 0;
211 int i;
97c4effc
TC
212 /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
213 tags, tags->count, name);*/
faa9b3e7
TC
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 }
97c4effc
TC
222 /*i_tags_print(tags);*/
223
faa9b3e7
TC
224 return count;
225}
226
227int 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
97c4effc
TC
241int i_tags_get_float(i_img_tags *tags, char const *name, int code,
242 double *value) {
faa9b3e7
TC
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
97c4effc
TC
263int i_tags_set_float(i_img_tags *tags, char const *name, int code,
264 double value) {
2e41e30b
TC
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
271Sets the tag with the given name and code to the given floating point
272value.
273
274Since tags are strings or ints, we convert the value to a string before
275storage at the precision specified by C<places>.
276
277=cut
278*/
279
280int i_tags_set_float2(i_img_tags *tags, char const *name, int code,
281 double value, int places) {
faa9b3e7
TC
282 char temp[40];
283
2e41e30b
TC
284 if (places < 0)
285 places = 30;
286 else if (places > 30)
287 places = 30;
288
289 sprintf(temp, "%.*g", places, value);
faa9b3e7
TC
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
97c4effc 298int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
faa9b3e7
TC
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
97c4effc 319static int parse_long(char *data, char **end, long *out) {
97c4effc
TC
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) {
a659442a 328 errno = savederr;
97c4effc
TC
329 return 0;
330 }
331
a659442a 332 errno = savederr;
97c4effc
TC
333 *out = result;
334 *end = myend;
335
336 return 1;
97c4effc
TC
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*/
344static int parse_long_list(char *data, char **end, int maxcount, long *out) {
345 int i;
346
26fd367b 347 i = 0;
97c4effc
TC
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)" */
365static 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
383int 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
409int 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
423int i_tags_get_string(i_img_tags *tags, char const *name, int code,
faa9b3e7
TC
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 {
241defe8 445 sprintf(value, "%d", entry->idata);
faa9b3e7
TC
446 }
447
448 return 1;
449}
450
451void 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)
97c4effc 459 printf(" Name : %s (%p)\n", tag->name, tag->name);
faa9b3e7
TC
460 printf(" Code : %d\n", tag->code);
461 if (tag->data) {
462 int pos;
97c4effc 463 printf(" Data : %d (%p) => '", tag->size, tag->data);
faa9b3e7
TC
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}
b8c2033e
AMH
479
480/*
481=back
482
483=head1 AUTHOR
484
485Tony Cook <tony@develop-help.com>
486
487=head1 SEE ALSO
488
489Imager(3)
490
491=cut
492*/