Commit | Line | Data |
---|---|---|
74315ca9 | 1 | #include "imageri.h" |
31a13473 | 2 | #include <stdio.h> |
74315ca9 | 3 | |
d7506bda TC |
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; | |
fc02e376 | 7 | |
74315ca9 TC |
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) { | |
cf8c77ae | 18 | im_context_t ctx = malloc(sizeof(im_context_struct)); |
74315ca9 TC |
19 | int i; |
20 | ||
fc02e376 TC |
21 | if (!slot_mutex) |
22 | slot_mutex = i_mutex_new(); | |
23 | ||
cf8c77ae TC |
24 | if (!ctx) |
25 | return NULL; | |
26 | ||
74315ca9 TC |
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 | |
44d86483 TC |
37 | ctx->max_width = 0; |
38 | ctx->max_height = 0; | |
cf8c77ae | 39 | ctx->max_bytes = DEF_BYTES_LIMIT; |
74315ca9 | 40 | |
f4ced639 TC |
41 | ctx->slot_alloc = slot_count; |
42 | ctx->slots = calloc(sizeof(void *), ctx->slot_alloc); | |
fc02e376 TC |
43 | if (!ctx->slots) { |
44 | free(ctx); | |
45 | return NULL; | |
46 | } | |
47 | ||
b7028a2e TC |
48 | ctx->file_magic = NULL; |
49 | ||
31a13473 TC |
50 | ctx->refcount = 1; |
51 | ||
52 | #ifdef IMAGER_TRACE_CONTEXT | |
53 | fprintf(stderr, "im_context: created %p\n", ctx); | |
54 | #endif | |
55 | ||
fc02e376 | 56 | |
74315ca9 TC |
57 | return ctx; |
58 | } | |
59 | ||
60 | /* | |
31a13473 | 61 | =item im_context_refinc(ctx, where) |
abffffed TC |
62 | X<im_context_refinc API> |
63 | =section Context objects | |
64 | =synopsis im_context_refinc(aIMCTX, "a description"); | |
31a13473 TC |
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 | /* | |
abffffed TC |
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"); | |
74315ca9 | 86 | |
abffffed TC |
87 | Remove a reference to the context, releasing it if all references have |
88 | been removed. | |
74315ca9 TC |
89 | |
90 | =cut | |
91 | */ | |
92 | ||
93 | void | |
31a13473 | 94 | im_context_refdec(im_context_t ctx, const char *where) { |
74315ca9 | 95 | int i; |
fc02e376 | 96 | im_slot_t slot; |
74315ca9 | 97 | |
31a13473 TC |
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 | ||
d7506bda TC |
110 | /* lock here to avoid slot_destructors from being moved under us */ |
111 | i_mutex_lock(slot_mutex); | |
fc02e376 TC |
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 | } | |
d7506bda | 116 | i_mutex_unlock(slot_mutex); |
fc02e376 TC |
117 | |
118 | free(ctx->slots); | |
119 | ||
74315ca9 TC |
120 | for (i = 0; i < IM_ERROR_COUNT; ++i) { |
121 | if (ctx->error_stack[i].msg) | |
122 | myfree(ctx->error_stack[i].msg); | |
123 | } | |
b7028a2e TC |
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 | } | |
74315ca9 | 136 | #ifdef IMAGER_LOG |
6ee0c41f | 137 | if (ctx->lg_file && ctx->own_log) |
74315ca9 TC |
138 | fclose(ctx->lg_file); |
139 | #endif | |
cf8c77ae TC |
140 | |
141 | free(ctx); | |
74315ca9 TC |
142 | } |
143 | ||
144 | /* | |
145 | =item im_context_clone(ctx) | |
146 | ||
147 | Clone an Imager context object, returning the result. | |
148 | ||
a6c8996f TC |
149 | The error stack is not copied from the original context. |
150 | ||
74315ca9 TC |
151 | =cut |
152 | */ | |
153 | ||
154 | im_context_t | |
31a13473 | 155 | im_context_clone(im_context_t ctx, const char *where) { |
cf8c77ae | 156 | im_context_t nctx = malloc(sizeof(im_context_struct)); |
74315ca9 TC |
157 | int i; |
158 | ||
cf8c77ae TC |
159 | if (!nctx) |
160 | return NULL; | |
161 | ||
d7506bda TC |
162 | nctx->slot_alloc = slot_count; |
163 | nctx->slots = calloc(sizeof(void *), nctx->slot_alloc); | |
fc02e376 TC |
164 | if (!nctx->slots) { |
165 | free(nctx); | |
166 | return NULL; | |
167 | } | |
fc02e376 | 168 | |
a6c8996f | 169 | nctx->error_sp = IM_ERROR_COUNT-1; |
74315ca9 | 170 | for (i = 0; i < IM_ERROR_COUNT; ++i) { |
a6c8996f TC |
171 | nctx->error_alloc[i] = 0; |
172 | nctx->error_stack[i].msg = NULL; | |
74315ca9 TC |
173 | } |
174 | #ifdef IMAGER_LOG | |
175 | nctx->log_level = ctx->log_level; | |
176 | if (ctx->lg_file) { | |
6ee0c41f TC |
177 | if (ctx->own_log) { |
178 | int newfd = dup(fileno(ctx->lg_file)); | |
9ca939d0 TC |
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 | } | |
6ee0c41f TC |
193 | } |
194 | else { | |
195 | /* stderr */ | |
196 | nctx->lg_file = ctx->lg_file; | |
197 | nctx->own_log = 0; | |
198 | } | |
74315ca9 TC |
199 | } |
200 | else { | |
44d86483 | 201 | nctx->lg_file = NULL; |
74315ca9 TC |
202 | } |
203 | #endif | |
44d86483 TC |
204 | nctx->max_width = ctx->max_width; |
205 | nctx->max_height = ctx->max_height; | |
206 | nctx->max_bytes = ctx->max_bytes; | |
fc02e376 | 207 | |
31a13473 | 208 | nctx->refcount = 1; |
74315ca9 | 209 | |
b7028a2e TC |
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 | ||
31a13473 TC |
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; | |
74315ca9 | 247 | } |
fc02e376 TC |
248 | |
249 | /* | |
250 | =item im_context_slot_new(destructor) | |
251 | ||
252 | Allocate a new context-local-storage slot. | |
253 | ||
c135e76b TC |
254 | C<desctructor> will be called when the context is destroyed if the |
255 | corresponding slot is non-NULL. | |
256 | ||
fc02e376 TC |
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 | } | |
b7028a2e TC |
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 | } |