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