various BMP test files added
[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) {
4dfa5522 128 work.data = mymalloc(size+1);
faa9b3e7 129 if (!work.data) {
4dfa5522 130 if (work.name) myfree(work.name);
faa9b3e7
TC
131 return 0;
132 }
133 memcpy(work.data, data, size);
134 work.data[size] = '\0'; /* convenience */
135 work.size = size;
136 }
137 work.code = code;
138 work.idata = idata;
139 tags->tags[tags->count++] = work;
140
97c4effc
TC
141 /*i_tags_print(tags);*/
142
faa9b3e7
TC
143 return 1;
144}
145
146void i_tags_destroy(i_img_tags *tags) {
147 if (tags->tags) {
148 int i;
149 for (i = 0; i < tags->count; ++i) {
150 if (tags->tags[i].name)
4dfa5522 151 myfree(tags->tags[i].name);
faa9b3e7 152 if (tags->tags[i].data)
4dfa5522 153 myfree(tags->tags[i].data);
faa9b3e7 154 }
4dfa5522 155 myfree(tags->tags);
faa9b3e7
TC
156 }
157}
158
97c4effc 159int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
faa9b3e7
TC
160 if (tags->tags) {
161 while (start < tags->count) {
162 if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
163 *entry = start;
164 return 1;
165 }
166 ++start;
167 }
168 }
169 return 0;
170}
171
172int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
173 if (tags->tags) {
174 while (start < tags->count) {
175 if (tags->tags[start].code == code) {
176 *entry = start;
177 return 1;
178 }
179 ++start;
180 }
181 }
182 return 0;
183}
184
185int i_tags_delete(i_img_tags *tags, int entry) {
97c4effc
TC
186 /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
187 tags, tags->count, entry);*/
faa9b3e7
TC
188 if (tags->tags && entry >= 0 && entry < tags->count) {
189 i_img_tag old = tags->tags[entry];
190 memmove(tags->tags+entry, tags->tags+entry+1,
97c4effc 191 (tags->count-entry-1) * sizeof(i_img_tag));
faa9b3e7 192 if (old.name)
4dfa5522 193 myfree(old.name);
faa9b3e7 194 if (old.data)
4dfa5522 195 myfree(old.data);
faa9b3e7 196 --tags->count;
97c4effc 197
faa9b3e7
TC
198 return 1;
199 }
200 return 0;
201}
202
97c4effc 203int i_tags_delbyname(i_img_tags *tags, char const *name) {
faa9b3e7
TC
204 int count = 0;
205 int i;
97c4effc
TC
206 /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
207 tags, tags->count, name);*/
faa9b3e7
TC
208 if (tags->tags) {
209 for (i = tags->count-1; i >= 0; --i) {
210 if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
211 ++count;
212 i_tags_delete(tags, i);
213 }
214 }
215 }
97c4effc
TC
216 /*i_tags_print(tags);*/
217
faa9b3e7
TC
218 return count;
219}
220
221int i_tags_delbycode(i_img_tags *tags, int code) {
222 int count = 0;
223 int i;
224 if (tags->tags) {
225 for (i = tags->count-1; i >= 0; --i) {
226 if (tags->tags[i].code == code) {
227 ++count;
228 i_tags_delete(tags, i);
229 }
230 }
231 }
232 return count;
233}
234
97c4effc
TC
235int i_tags_get_float(i_img_tags *tags, char const *name, int code,
236 double *value) {
faa9b3e7
TC
237 int index;
238 i_img_tag *entry;
239
240 if (name) {
241 if (!i_tags_find(tags, name, 0, &index))
242 return 0;
243 }
244 else {
245 if (!i_tags_findn(tags, code, 0, &index))
246 return 0;
247 }
248 entry = tags->tags+index;
249 if (entry->data)
250 *value = atof(entry->data);
251 else
252 *value = entry->idata;
253
254 return 1;
255}
256
97c4effc
TC
257int i_tags_set_float(i_img_tags *tags, char const *name, int code,
258 double value) {
faa9b3e7
TC
259 char temp[40];
260
261 sprintf(temp, "%.30g", value);
262 if (name)
263 i_tags_delbyname(tags, name);
264 else
265 i_tags_delbycode(tags, code);
266
267 return i_tags_add(tags, name, code, temp, strlen(temp), 0);
268}
269
97c4effc 270int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
faa9b3e7
TC
271 int index;
272 i_img_tag *entry;
273
274 if (name) {
275 if (!i_tags_find(tags, name, 0, &index))
276 return 0;
277 }
278 else {
279 if (!i_tags_findn(tags, code, 0, &index))
280 return 0;
281 }
282 entry = tags->tags+index;
283 if (entry->data)
284 *value = atoi(entry->data);
285 else
286 *value = entry->idata;
287
288 return 1;
289}
290
97c4effc
TC
291static int parse_long(char *data, char **end, long *out) {
292#if 0
293 /* I wrote this without thinking about strtol */
294 long x = 0;
295 int neg = *data == '-';
296
297 if (neg)
298 ++data;
299 if (!isdigit(*data))
300 return 0;
301 while (isdigit(*data)) {
302 /* this check doesn't guarantee we don't overflow, but it helps */
303 if (x > LONG_MAX / 10)
304 return 0;
305 x = x * 10 + *data - '0';
306 ++data;
307 }
308 if (neg)
309 x = -x;
310
311 *end = data;
312
313 return 1;
314#else
315 long result;
316 int savederr = errno;
317 char *myend;
318
319 errno = 0;
320 result = strtol(data, &myend, 10);
321 if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE
322 || myend == data) {
323 return 0;
324 }
325
326 *out = result;
327 *end = myend;
328
329 return 1;
330#endif
331}
332
333/* parse a comma-separated list of integers
334 returns when it has maxcount numbers, finds a non-comma after a number
335 or can't parse a number
336 if it can't parse a number after a comma, that's considered an error
337*/
338static int parse_long_list(char *data, char **end, int maxcount, long *out) {
339 int i;
340
26fd367b 341 i = 0;
97c4effc
TC
342 while (i < maxcount-1) {
343 if (!parse_long(data, &data, out))
344 return 0;
345 out++;
346 i++;
347 if (*data != ',')
348 return i;
349 ++data;
350 }
351 if (!parse_long(data, &data, out))
352 return 0;
353 ++i;
354 *end = data;
355 return i;
356}
357
358/* parse "color(red,green,blue,alpha)" */
359static int parse_color(char *data, char **end, i_color *value) {
360 long n[4];
361 int count, i;
362
363 if (memcmp(data, "color(", 6))
364 return 0; /* not a color */
365 data += 6;
366 count = parse_long_list(data, &data, 4, n);
367 if (count < 3)
368 return 0;
369 for (i = 0; i < count; ++i)
370 value->channel[i] = n[i];
371 if (count < 4)
372 value->channel[3] = 255;
373
374 return 1;
375}
376
377int i_tags_get_color(i_img_tags *tags, char const *name, int code,
378 i_color *value) {
379 int index;
380 i_img_tag *entry;
381 char *end;
382
383 if (name) {
384 if (!i_tags_find(tags, name, 0, &index))
385 return 0;
386 }
387 else {
388 if (!i_tags_findn(tags, code, 0, &index))
389 return 0;
390 }
391 entry = tags->tags+index;
392 if (!entry->data)
393 return 0;
394
395 if (!parse_color(entry->data, &end, value))
396 return 0;
397
398 /* for now we're sloppy about the end */
399
400 return 1;
401}
402
403int i_tags_set_color(i_img_tags *tags, char const *name, int code,
404 i_color const *value) {
405 char temp[80];
406
407 sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
408 value->channel[2], value->channel[3]);
409 if (name)
410 i_tags_delbyname(tags, name);
411 else
412 i_tags_delbycode(tags, code);
413
414 return i_tags_add(tags, name, code, temp, strlen(temp), 0);
415}
416
417int i_tags_get_string(i_img_tags *tags, char const *name, int code,
faa9b3e7
TC
418 char *value, size_t value_size) {
419 int index;
420 i_img_tag *entry;
421
422 if (name) {
423 if (!i_tags_find(tags, name, 0, &index))
424 return 0;
425 }
426 else {
427 if (!i_tags_findn(tags, code, 0, &index))
428 return 0;
429 }
430 entry = tags->tags+index;
431 if (entry->data) {
432 size_t cpsize = value_size < entry->size ? value_size : entry->size;
433 memcpy(value, entry->data, cpsize);
434 if (cpsize == value_size)
435 --cpsize;
436 value[cpsize] = '\0';
437 }
438 else {
439 sprintf(value, "%d", entry->data);
440 }
441
442 return 1;
443}
444
445void i_tags_print(i_img_tags *tags) {
446 int i;
447 printf("Alloc %d\n", tags->alloc);
448 printf("Count %d\n", tags->count);
449 for (i = 0; i < tags->count; ++i) {
450 i_img_tag *tag = tags->tags + i;
451 printf("Tag %d\n", i);
452 if (tag->name)
97c4effc 453 printf(" Name : %s (%p)\n", tag->name, tag->name);
faa9b3e7
TC
454 printf(" Code : %d\n", tag->code);
455 if (tag->data) {
456 int pos;
97c4effc 457 printf(" Data : %d (%p) => '", tag->size, tag->data);
faa9b3e7
TC
458 for (pos = 0; pos < tag->size; ++pos) {
459 if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
460 putchar('\\');
461 putchar(tag->data[pos]);
462 }
463 else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
464 printf("\\x%02X", tag->data[pos]);
465 else
466 putchar(tag->data[pos]);
467 }
468 printf("'\n");
469 printf(" Idata: %d\n", tag->idata);
470 }
471 }
472}
b8c2033e
AMH
473
474/*
475=back
476
477=head1 AUTHOR
478
479Tony Cook <tony@develop-help.com>
480
481=head1 SEE ALSO
482
483Imager(3)
484
485=cut
486*/