freetype-config might not be available, allow pkg-config to work
[imager.git] / context.c
1 #include "imageri.h"
2 #include <stdio.h>
3
4 static volatile im_slot_t slot_count = 1;
5 static im_slot_destroy_t *volatile slot_destructors;
6 static volatile i_mutex_t slot_mutex;
7
8 /*
9 =item im_context_new()
10
11 Create a new Imager context object.
12
13 =cut
14 */
15
16 im_context_t
17 im_context_new(void) {
18   im_context_t ctx = malloc(sizeof(im_context_struct));
19   int i;
20
21   if (!slot_mutex)
22     slot_mutex = i_mutex_new();
23
24   if (!ctx)
25     return NULL;
26   
27   ctx->error_sp = IM_ERROR_COUNT-1;
28   for (i = 0; i < IM_ERROR_COUNT; ++i) {
29     ctx->error_alloc[i] = 0;
30     ctx->error_stack[i].msg = NULL;
31     ctx->error_stack[i].code = 0;
32   }
33 #ifdef IMAGER_LOG
34   ctx->log_level = 0;
35   ctx->lg_file = NULL;
36 #endif
37   ctx->max_width = 0;
38   ctx->max_height = 0;
39   ctx->max_bytes = DEF_BYTES_LIMIT;
40
41   ctx->slot_alloc = slot_count;
42   ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
43   if (!ctx->slots) {
44     free(ctx);
45     return NULL;
46   }
47
48   ctx->file_magic = NULL;
49
50   ctx->refcount = 1;
51
52 #ifdef IMAGER_TRACE_CONTEXT
53   fprintf(stderr, "im_context: created %p\n", ctx);
54 #endif
55
56
57   return ctx;
58 }
59
60 /*
61 =item im_context_refinc(ctx, where)
62 X<im_context_refinc API>
63 =section Context objects
64 =synopsis im_context_refinc(aIMCTX, "a description");
65
66 Add a new reference to the context.
67
68 =cut
69 */
70
71 void
72 im_context_refinc(im_context_t ctx, const char *where) {
73   ++ctx->refcount;
74
75 #ifdef IMAGER_TRACE_CONTEXT
76   fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
77           ctx, (unsigned long)ctx->refcount);
78 #endif
79 }
80
81 /*
82 =item im_context_refdec(ctx, where)
83 X<im_context_refdec API>
84 =section Context objects
85 =synopsis im_context_refdec(aIMCTX, "a description");
86
87 Remove a reference to the context, releasing it if all references have
88 been removed.
89
90 =cut
91 */
92
93 void
94 im_context_refdec(im_context_t ctx, const char *where) {
95   int i;
96   im_slot_t slot;
97
98   im_assert(ctx->refcount > 0);
99
100   --ctx->refcount;
101
102 #ifdef IMAGER_TRACE_CONTEXT
103   fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
104           ctx, (unsigned long)ctx->refcount);
105 #endif
106
107   if (ctx->refcount != 0)
108     return;
109
110   /* lock here to avoid slot_destructors from being moved under us */
111   i_mutex_lock(slot_mutex);
112   for (slot = 0; slot < ctx->slot_alloc; ++slot) {
113     if (ctx->slots[slot] && slot_destructors[slot])
114       slot_destructors[slot](ctx->slots[slot]);
115   }
116   i_mutex_unlock(slot_mutex);
117
118   free(ctx->slots);
119
120   for (i = 0; i < IM_ERROR_COUNT; ++i) {
121     if (ctx->error_stack[i].msg)
122       myfree(ctx->error_stack[i].msg);
123   }
124
125   {
126     im_file_magic *p = ctx->file_magic;
127     while (p != NULL) {
128       im_file_magic *n = p->next;
129       free(p->m.name);
130       free(p->m.magic);
131       free(p->m.mask);
132       free(p);
133       p = n;
134     }
135   }
136 #ifdef IMAGER_LOG
137   if (ctx->lg_file && ctx->own_log)
138     fclose(ctx->lg_file);
139 #endif
140
141   free(ctx);
142 }
143
144 /*
145 =item im_context_clone(ctx)
146
147 Clone an Imager context object, returning the result.
148
149 The error stack is not copied from the original context.
150
151 =cut
152 */
153
154 im_context_t
155 im_context_clone(im_context_t ctx, const char *where) {
156   im_context_t nctx = malloc(sizeof(im_context_struct));
157   int i;
158
159   if (!nctx)
160     return NULL;
161
162   nctx->slot_alloc = slot_count;
163   nctx->slots = calloc(sizeof(void *), nctx->slot_alloc);
164   if (!nctx->slots) {
165     free(nctx);
166     return NULL;
167   }
168
169   nctx->error_sp = IM_ERROR_COUNT-1;
170   for (i = 0; i < IM_ERROR_COUNT; ++i) {
171     nctx->error_alloc[i] = 0;
172     nctx->error_stack[i].msg = NULL;
173   }
174 #ifdef IMAGER_LOG
175   nctx->log_level = ctx->log_level;
176   if (ctx->lg_file) {
177     if (ctx->own_log) {
178       int newfd = dup(fileno(ctx->lg_file));
179       if (newfd >= 0) {
180         nctx->own_log = 1;
181         nctx->lg_file = fdopen(newfd, "w");
182         if (nctx->lg_file)
183           setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ);
184       }
185       else {
186 #ifdef IMAGER_TRACE_CONTEXT
187         perror("im_context:failed to clone log");
188 #endif
189         free(nctx->slots);
190         free(nctx);
191         return NULL;
192       }
193     }
194     else {
195       /* stderr */
196       nctx->lg_file = ctx->lg_file;
197       nctx->own_log = 0;
198     }
199   }
200   else {
201     nctx->lg_file = NULL;
202   }
203 #endif
204   nctx->max_width = ctx->max_width;
205   nctx->max_height = ctx->max_height;
206   nctx->max_bytes = ctx->max_bytes;
207
208   nctx->refcount = 1;
209
210   {
211     im_file_magic *inp = ctx->file_magic;
212     im_file_magic **outpp = &nctx->file_magic;
213     *outpp = NULL;
214     while (inp) {
215       im_file_magic *m = malloc(sizeof(im_file_magic));
216       if (!m) {
217         /* should free any parts of the list already allocated */
218         im_context_refdec(nctx, "failed cloning");
219         return NULL;
220       }
221       m->next = NULL;
222       m->m.name = strdup(inp->m.name);
223       m->m.magic_size = inp->m.magic_size;
224       m->m.magic = malloc(inp->m.magic_size);
225       m->m.mask = malloc(inp->m.magic_size);
226       if (m->m.name == NULL || m->m.magic == NULL || m->m.mask == NULL) {
227         free(m->m.name);
228         free(m->m.magic);
229         free(m->m.mask);
230         free(m);
231         im_context_refdec(nctx, "failed cloning");
232         return NULL;
233       }
234       memcpy(m->m.magic, inp->m.magic, m->m.magic_size);
235       memcpy(m->m.mask, inp->m.mask, m->m.magic_size);
236       *outpp = m;
237       outpp = &m->next;
238       inp = inp->next;
239     }
240   }
241
242 #ifdef IMAGER_TRACE_CONTEXT
243   fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
244 #endif
245
246   return nctx;
247 }
248
249 /*
250 =item im_context_slot_new(destructor)
251
252 Allocate a new context-local-storage slot.
253
254 C<desctructor> will be called when the context is destroyed if the
255 corresponding slot is non-NULL.
256
257 =cut
258 */
259
260 im_slot_t
261 im_context_slot_new(im_slot_destroy_t destructor) {
262   im_slot_t new_slot;
263   im_slot_destroy_t *new_destructors;
264   if (!slot_mutex)
265     slot_mutex = i_mutex_new();
266
267   i_mutex_lock(slot_mutex);
268
269   new_slot = slot_count++;
270   new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
271   if (!new_destructors)
272     i_fatal(1, "Cannot allocate memory for slot destructors");
273   slot_destructors = new_destructors;
274
275   slot_destructors[new_slot] = destructor;
276
277   i_mutex_unlock(slot_mutex);
278
279   return new_slot;
280 }
281
282 /*
283 =item im_context_slot_set(slot, value)
284
285 Set the value of a slot.
286
287 Returns true on success.
288
289 Aborts if the slot supplied is invalid.
290
291 If reallocation of slot storage fails, returns false.
292
293 =cut
294 */
295
296 int
297 im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
298   if (slot < 0 || slot >= slot_count) {
299     fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
300             (int)slot, (int)slot_count-1);
301     abort();
302   }
303
304   if (slot >= ctx->slot_alloc) {
305     ssize_t i;
306     size_t new_alloc = slot_count;
307     void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
308
309     if (!new_slots)
310       return 0;
311
312     for (i = ctx->slot_alloc; i < new_alloc; ++i)
313       new_slots[i] = NULL;
314
315     ctx->slots = new_slots;
316     ctx->slot_alloc = new_alloc;
317   }
318
319   ctx->slots[slot] = value;
320
321   return 1;
322 }
323
324 /*
325 =item im_context_slot_get(ctx, slot)
326
327 Retrieve the value previously stored in the given slot of the context
328 object.
329
330 =cut
331 */
332
333 void *
334 im_context_slot_get(im_context_t ctx, im_slot_t slot) {
335   if (slot < 0 || slot >= slot_count) {
336     fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
337             (int)slot, (int)slot_count-1);
338     abort();
339   }
340
341   if (slot >= ctx->slot_alloc)
342     return NULL;
343
344   return ctx->slots[slot];
345 }
346
347 /*
348 =item im_add_file_magic(ctx, name, bits, mask, length)
349
350 Add file type magic to the given context.
351
352 =cut
353 */
354
355 int
356 im_add_file_magic(im_context_t ctx, const char *name,
357                   const unsigned char *bits, const unsigned char *mask,
358                   size_t length) {
359   im_file_magic *m = malloc(sizeof(im_file_magic));
360
361   if (m == NULL)
362     return 0;
363
364   if (length > 512)
365     length = 512;
366
367   m->m.name = strdup(name);
368   m->m.magic = malloc(length);
369   m->m.mask = malloc(length);
370   m->m.magic_size = length;
371
372   if (name == NULL || bits == NULL || mask == NULL) {
373     free(m->m.name);
374     free(m->m.magic);
375     free(m->m.mask);
376     free(m);
377     return 0;
378   }
379   memcpy(m->m.magic, bits, length);
380   memcpy(m->m.mask, mask, length);
381   m->next = ctx->file_magic;
382   ctx->file_magic = m;
383
384   return 1;
385 }