]>
Commit | Line | Data |
---|---|---|
e310e5f9 | 1 | #include "imager.h" |
02d1d628 | 2 | #include "iolayer.h" |
af070d99 | 3 | #include "imerror.h" |
02d1d628 AMH |
4 | #include "log.h" |
5 | #include <stdlib.h> | |
6 | #include <stdio.h> | |
7 | #ifdef _MSC_VER | |
8 | #include <io.h> | |
9 | #endif | |
35891892 | 10 | #include <string.h> |
2691d220 | 11 | #include <errno.h> |
50c75381 | 12 | #include "imageri.h" |
02d1d628 AMH |
13 | |
14 | #define IOL_DEB(x) | |
15 | ||
16 | ||
17 | char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" }; | |
18 | ||
299da279 TC |
19 | typedef struct io_blink { |
20 | char buf[BBSIZ]; | |
21 | /* size_t cnt; */ | |
22 | size_t len; /* How large is this buffer = BBZIS for now */ | |
23 | struct io_blink *next; | |
24 | struct io_blink *prev; | |
25 | } io_blink; | |
26 | ||
27 | ||
28 | /* Structures that describe callback interfaces */ | |
29 | ||
30 | typedef struct { | |
31 | off_t offset; | |
32 | off_t cpos; | |
33 | } io_ex_rseek; | |
34 | ||
35 | ||
36 | typedef struct { | |
37 | off_t offset; | |
38 | off_t cpos; | |
39 | io_blink *head; | |
40 | io_blink *tail; | |
41 | io_blink *cp; | |
42 | } io_ex_fseek; | |
43 | ||
44 | ||
45 | typedef struct { | |
46 | off_t offset; /* Offset of the source - not used */ | |
47 | off_t length; /* Total length of chain in bytes */ | |
48 | io_blink *head; /* Start of chain */ | |
49 | io_blink *tail; /* End of chain */ | |
50 | off_t tfill; /* End of stream in last link */ | |
51 | io_blink *cp; /* Current element of list */ | |
52 | off_t cpos; /* Offset within the current */ | |
53 | off_t gpos; /* Global position in stream */ | |
54 | } io_ex_bchain; | |
55 | ||
56 | typedef struct { | |
57 | off_t offset; /* Offset of the source - not used */ | |
58 | off_t cpos; /* Offset within the current */ | |
59 | } io_ex_buffer; | |
60 | ||
0778adbf TC |
61 | static void io_obj_setp_buffer(io_obj *io, char *p, size_t len, i_io_closebufp_t closecb, void *closedata); |
62 | static void io_obj_setp_cb2 (io_obj *io, void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb); | |
299da279 | 63 | |
eda1622c TC |
64 | /* turn current offset, file length, whence and offset into a new offset */ |
65 | #define calc_seek_offset(curr_off, length, offset, whence) \ | |
66 | (((whence) == SEEK_SET) ? (offset) : \ | |
67 | ((whence) == SEEK_CUR) ? (curr_off) + (offset) : \ | |
68 | ((whence) == SEEK_END) ? (length) + (offset) : -1) | |
02d1d628 AMH |
69 | |
70 | /* | |
71 | =head1 NAME | |
72 | ||
73 | iolayer.c - encapsulates different source of data into a single framework. | |
74 | ||
75 | =head1 SYNOPSIS | |
76 | ||
77 | io_glue *ig = io_new_fd( fileno(stdin) ); | |
78 | method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet | |
79 | io_glue_commit_types(ig); // always assume IOL_SEEK for now | |
80 | switch (method) { | |
81 | case IOL_NOSEEK: | |
82 | code that uses ig->readcb() | |
83 | to read data goes here. | |
84 | break; | |
85 | case IOL_MMAP: | |
86 | code that uses ig->readcb() | |
87 | to read data goes here. | |
88 | break; | |
89 | } | |
90 | ||
d16420e9 | 91 | io_glue_destroy(ig); |
02d1d628 AMH |
92 | // and much more |
93 | ||
94 | =head1 DESCRIPTION | |
95 | ||
96 | iolayer.c implements the basic functions to create and destroy io_glue | |
97 | objects for Imager. The typical usage pattern for data sources is: | |
98 | ||
99 | 1. Create the source (io_new_fd) | |
100 | 2. Define how you want to get data from it (io_reqmeth) | |
101 | 3. read from it using the interface requested (ig->readdb, ig->mmapcb) | |
102 | 4. Close the source, which | |
103 | shouldn't really close the underlying source. (io_glue DESTROY) | |
104 | ||
105 | =head1 FUNCTION REFERENCE | |
106 | ||
107 | Some of these functions are internal. | |
108 | ||
b8c2033e | 109 | =over |
02d1d628 AMH |
110 | |
111 | =cut | |
112 | */ | |
113 | ||
10461f9a TC |
114 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count); |
115 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count); | |
116 | static off_t fd_seek(io_glue *ig, off_t offset, int whence); | |
2b405c9e | 117 | static int fd_close(io_glue *ig); |
10461f9a | 118 | static ssize_t fd_size(io_glue *ig); |
40d75d5a | 119 | static const char *my_strerror(int err); |
02d1d628 AMH |
120 | |
121 | /* | |
122 | * Callbacks for sources that cannot seek | |
123 | */ | |
124 | ||
125 | /* fakeseek_read: read method for when emulating a seekable source | |
126 | static | |
127 | ssize_t | |
128 | fakeseek_read(io_glue *ig, void *buf, size_t count) { | |
129 | io_ex_fseek *exdata = ig->exdata; | |
130 | return 0; | |
131 | } | |
132 | */ | |
133 | ||
134 | ||
135 | ||
136 | /* | |
137 | * Callbacks for sources that can seek | |
138 | */ | |
139 | ||
140 | /* | |
141 | =item realseek_read(ig, buf, count) | |
142 | ||
143 | Does the reading from a source that can be seeked on | |
144 | ||
145 | ig - io_glue object | |
146 | buf - buffer to return data in | |
147 | count - number of bytes to read into buffer max | |
148 | ||
149 | =cut | |
150 | */ | |
151 | ||
152 | static | |
153 | ssize_t | |
154 | realseek_read(io_glue *ig, void *buf, size_t count) { | |
155 | io_ex_rseek *ier = ig->exdata; | |
10461f9a | 156 | void *p = ig->source.cb.p; |
02d1d628 AMH |
157 | ssize_t rc = 0; |
158 | size_t bc = 0; | |
159 | char *cbuf = buf; | |
160 | ||
2b405c9e TC |
161 | IOL_DEB( printf("realseek_read: buf = %p, count = %d\n", |
162 | buf, count) ); | |
10461f9a TC |
163 | /* Is this a good idea? Would it be better to handle differently? |
164 | skip handling? */ | |
165 | while( count!=bc && (rc = ig->source.cb.readcb(p,cbuf+bc,count-bc))>0 ) { | |
166 | bc+=rc; | |
167 | } | |
02d1d628 AMH |
168 | |
169 | ier->cpos += bc; | |
170 | IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) ); | |
1f6c1c10 | 171 | return rc < 0 ? rc : bc; |
02d1d628 AMH |
172 | } |
173 | ||
174 | ||
175 | /* | |
176 | =item realseek_write(ig, buf, count) | |
177 | ||
178 | Does the writing to a 'source' that can be seeked on | |
179 | ||
180 | ig - io_glue object | |
181 | buf - buffer that contains data | |
182 | count - number of bytes to write | |
183 | ||
184 | =cut | |
185 | */ | |
186 | ||
187 | static | |
188 | ssize_t | |
189 | realseek_write(io_glue *ig, const void *buf, size_t count) { | |
190 | io_ex_rseek *ier = ig->exdata; | |
10461f9a | 191 | void *p = ig->source.cb.p; |
02d1d628 AMH |
192 | ssize_t rc = 0; |
193 | size_t bc = 0; | |
194 | char *cbuf = (char*)buf; | |
195 | ||
10461f9a TC |
196 | IOL_DEB( printf("realseek_write: ig = %p, ier->cpos = %ld, buf = %p, " |
197 | "count = %d\n", ig, (long) ier->cpos, buf, count) ); | |
02d1d628 | 198 | |
10461f9a TC |
199 | /* Is this a good idea? Would it be better to handle differently? |
200 | skip handling? */ | |
201 | while( count!=bc && (rc = ig->source.cb.writecb(p,cbuf+bc,count-bc))>0 ) { | |
202 | bc+=rc; | |
203 | } | |
02d1d628 AMH |
204 | |
205 | ier->cpos += bc; | |
206 | IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) ); | |
1f6c1c10 | 207 | return rc < 0 ? rc : bc; |
02d1d628 AMH |
208 | } |
209 | ||
210 | ||
211 | /* | |
212 | =item realseek_close(ig) | |
213 | ||
10461f9a TC |
214 | Closes a source that can be seeked on. Not sure if this should be an |
215 | actual close or not. Does nothing for now. Should be fixed. | |
02d1d628 AMH |
216 | |
217 | ig - data source | |
218 | ||
10461f9a | 219 | =cut */ |
02d1d628 AMH |
220 | |
221 | static | |
2b405c9e | 222 | int |
02d1d628 AMH |
223 | realseek_close(io_glue *ig) { |
224 | mm_log((1, "realseek_close(ig %p)\n", ig)); | |
10461f9a | 225 | if (ig->source.cb.closecb) |
2b405c9e TC |
226 | return ig->source.cb.closecb(ig->source.cb.p); |
227 | else | |
228 | return 0; | |
02d1d628 AMH |
229 | } |
230 | ||
231 | ||
232 | /* realseek_seek(ig, offset, whence) | |
233 | ||
234 | Implements seeking for a source that is seekable, the purpose of having this is to be able to | |
235 | have an offset into a file that is different from what the underlying library thinks. | |
236 | ||
237 | ig - data source | |
238 | offset - offset into stream | |
239 | whence - whence argument a la lseek | |
240 | ||
241 | =cut | |
242 | */ | |
243 | ||
244 | static | |
245 | off_t | |
246 | realseek_seek(io_glue *ig, off_t offset, int whence) { | |
247 | /* io_ex_rseek *ier = ig->exdata; Needed later */ | |
10461f9a | 248 | void *p = ig->source.cb.p; |
8d14daab | 249 | off_t rc; |
930c67c8 | 250 | IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); |
10461f9a | 251 | rc = ig->source.cb.seekcb(p, offset, whence); |
02d1d628 AMH |
252 | |
253 | IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) ); | |
254 | return rc; | |
255 | /* FIXME: How about implementing this offset handling stuff? */ | |
256 | } | |
257 | ||
d16420e9 TC |
258 | static |
259 | void | |
260 | realseek_destroy(io_glue *ig) { | |
261 | io_ex_rseek *ier = ig->exdata; | |
262 | ||
263 | if (ig->source.cb.destroycb) | |
264 | ig->source.cb.destroycb(ig->source.cb.p); | |
265 | ||
266 | myfree(ier); | |
267 | } | |
268 | ||
4dfa5522 AMH |
269 | /* |
270 | * Callbacks for sources that are a fixed size buffer | |
271 | */ | |
272 | ||
273 | /* | |
274 | =item buffer_read(ig, buf, count) | |
275 | ||
276 | Does the reading from a buffer source | |
277 | ||
278 | ig - io_glue object | |
279 | buf - buffer to return data in | |
280 | count - number of bytes to read into buffer max | |
281 | ||
282 | =cut | |
283 | */ | |
284 | ||
285 | static | |
286 | ssize_t | |
287 | buffer_read(io_glue *ig, void *buf, size_t count) { | |
288 | io_ex_buffer *ieb = ig->exdata; | |
4dfa5522 | 289 | |
2b405c9e | 290 | IOL_DEB( printf("buffer_read: ieb->cpos = %ld, buf = %p, count = %d\n", (long) ieb->cpos, buf, count) ); |
4dfa5522 AMH |
291 | |
292 | if ( ieb->cpos+count > ig->source.buffer.len ) { | |
8d14daab | 293 | mm_log((1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ieb->cpos, (long)ig->source.buffer.len, (long)count)); |
4dfa5522 AMH |
294 | count = ig->source.buffer.len - ieb->cpos; |
295 | } | |
296 | ||
297 | memcpy(buf, ig->source.buffer.data+ieb->cpos, count); | |
298 | ieb->cpos += count; | |
8d14daab | 299 | IOL_DEB( printf("buffer_read: count = %ld\n", (long)count) ); |
4dfa5522 AMH |
300 | return count; |
301 | } | |
302 | ||
303 | ||
304 | /* | |
305 | =item buffer_write(ig, buf, count) | |
306 | ||
307 | Does nothing, returns -1 | |
308 | ||
309 | ig - io_glue object | |
310 | buf - buffer that contains data | |
311 | count - number of bytes to write | |
312 | ||
313 | =cut | |
314 | */ | |
315 | ||
316 | static | |
317 | ssize_t | |
318 | buffer_write(io_glue *ig, const void *buf, size_t count) { | |
319 | mm_log((1, "buffer_write called, this method should never be called.\n")); | |
320 | return -1; | |
321 | } | |
322 | ||
323 | ||
324 | /* | |
325 | =item buffer_close(ig) | |
326 | ||
327 | Closes a source that can be seeked on. Not sure if this should be an actual close | |
328 | or not. Does nothing for now. Should be fixed. | |
329 | ||
330 | ig - data source | |
331 | ||
332 | =cut | |
333 | */ | |
334 | ||
335 | static | |
2b405c9e | 336 | int |
4dfa5522 AMH |
337 | buffer_close(io_glue *ig) { |
338 | mm_log((1, "buffer_close(ig %p)\n", ig)); | |
2b405c9e TC |
339 | |
340 | return 0; | |
4dfa5522 AMH |
341 | } |
342 | ||
343 | ||
344 | /* buffer_seek(ig, offset, whence) | |
345 | ||
346 | Implements seeking for a buffer source. | |
347 | ||
348 | ig - data source | |
349 | offset - offset into stream | |
350 | whence - whence argument a la lseek | |
351 | ||
352 | =cut | |
353 | */ | |
354 | ||
355 | static | |
356 | off_t | |
357 | buffer_seek(io_glue *ig, off_t offset, int whence) { | |
358 | io_ex_buffer *ieb = ig->exdata; | |
eda1622c TC |
359 | off_t reqpos = |
360 | calc_seek_offset(ieb->cpos, ig->source.buffer.len, offset, whence); | |
4dfa5522 AMH |
361 | |
362 | if (reqpos > ig->source.buffer.len) { | |
363 | mm_log((1, "seeking out of readable range\n")); | |
364 | return (off_t)-1; | |
365 | } | |
eda1622c TC |
366 | if (reqpos < 0) { |
367 | i_push_error(0, "seek before beginning of file"); | |
368 | return (off_t)-1; | |
369 | } | |
4dfa5522 AMH |
370 | |
371 | ieb->cpos = reqpos; | |
372 | IOL_DEB( printf("buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
373 | ||
374 | return reqpos; | |
375 | /* FIXME: How about implementing this offset handling stuff? */ | |
376 | } | |
377 | ||
d16420e9 TC |
378 | static |
379 | void | |
380 | buffer_destroy(io_glue *ig) { | |
381 | io_ex_buffer *ieb = ig->exdata; | |
4dfa5522 | 382 | |
d16420e9 TC |
383 | if (ig->source.buffer.closecb) { |
384 | mm_log((1,"calling close callback %p for io_buffer\n", | |
385 | ig->source.buffer.closecb)); | |
386 | ig->source.buffer.closecb(ig->source.buffer.closedata); | |
387 | } | |
388 | myfree(ieb); | |
389 | } | |
4dfa5522 AMH |
390 | |
391 | ||
392 | ||
02d1d628 AMH |
393 | /* |
394 | * Callbacks for sources that are a chain of variable sized buffers | |
395 | */ | |
396 | ||
397 | ||
398 | ||
399 | /* Helper functions for buffer chains */ | |
400 | ||
401 | static | |
402 | io_blink* | |
faa9b3e7 | 403 | io_blink_new(void) { |
02d1d628 AMH |
404 | io_blink *ib; |
405 | ||
406 | mm_log((1, "io_blink_new()\n")); | |
407 | ||
408 | ib = mymalloc(sizeof(io_blink)); | |
409 | ||
410 | ib->next = NULL; | |
411 | ib->prev = NULL; | |
412 | ib->len = BBSIZ; | |
413 | ||
414 | memset(&ib->buf, 0, ib->len); | |
415 | return ib; | |
416 | } | |
417 | ||
418 | ||
419 | ||
420 | /* | |
421 | =item io_bchain_advance(ieb) | |
422 | ||
423 | Advances the buffer chain to the next link - extending if | |
424 | necessary. Also adjusts the cpos and tfill counters as needed. | |
425 | ||
426 | ieb - buffer chain object | |
427 | ||
428 | =cut | |
429 | */ | |
430 | ||
431 | static | |
432 | void | |
433 | io_bchain_advance(io_ex_bchain *ieb) { | |
434 | if (ieb->cp->next == NULL) { | |
435 | ieb->tail = io_blink_new(); | |
436 | ieb->tail->prev = ieb->cp; | |
437 | ieb->cp->next = ieb->tail; | |
438 | ||
439 | ieb->tfill = 0; /* Only set this if we added a new slice */ | |
440 | } | |
441 | ieb->cp = ieb->cp->next; | |
442 | ieb->cpos = 0; | |
443 | } | |
444 | ||
445 | ||
c3cc977e AMH |
446 | |
447 | /* | |
448 | =item io_bchain_destroy() | |
449 | ||
450 | frees all resources used by a buffer chain. | |
451 | ||
452 | =cut | |
453 | */ | |
454 | ||
455 | void | |
456 | io_destroy_bufchain(io_ex_bchain *ieb) { | |
4dfa5522 AMH |
457 | io_blink *cp; |
458 | mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb)); | |
459 | cp = ieb->head; | |
460 | ||
c3cc977e AMH |
461 | while(cp) { |
462 | io_blink *t = cp->next; | |
4dfa5522 | 463 | myfree(cp); |
c3cc977e AMH |
464 | cp = t; |
465 | } | |
466 | } | |
467 | ||
468 | ||
469 | ||
470 | ||
02d1d628 AMH |
471 | /* |
472 | ||
473 | static | |
474 | void | |
475 | bufchain_dump(io_ex_bchain *ieb) { | |
476 | mm_log((1, " buf_chain_dump(ieb %p)\n")); | |
477 | mm_log((1, " buf_chain_dump: ieb->offset = %d\n", ieb->offset)); | |
478 | mm_log((1, " buf_chain_dump: ieb->length = %d\n", ieb->length)); | |
479 | mm_log((1, " buf_chain_dump: ieb->head = %p\n", ieb->head )); | |
480 | mm_log((1, " buf_chain_dump: ieb->tail = %p\n", ieb->tail )); | |
481 | mm_log((1, " buf_chain_dump: ieb->tfill = %d\n", ieb->tfill )); | |
482 | mm_log((1, " buf_chain_dump: ieb->cp = %p\n", ieb->cp )); | |
483 | mm_log((1, " buf_chain_dump: ieb->cpos = %d\n", ieb->cpos )); | |
484 | mm_log((1, " buf_chain_dump: ieb->gpos = %d\n", ieb->gpos )); | |
485 | } | |
486 | */ | |
487 | ||
488 | /* | |
489 | * TRUE if lengths are NOT equal | |
490 | */ | |
491 | ||
492 | /* | |
493 | static | |
494 | void | |
495 | chainlencert( io_glue *ig ) { | |
496 | int clen; | |
497 | int cfl = 0; | |
498 | size_t csize = 0; | |
499 | size_t cpos = 0; | |
500 | io_ex_bchain *ieb = ig->exdata; | |
501 | io_blink *cp = ieb->head; | |
502 | ||
503 | ||
504 | if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length)); | |
505 | ||
506 | while(cp) { | |
507 | clen = (cp == ieb->tail) ? ieb->tfill : cp->len; | |
508 | if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n")); | |
509 | if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n")); | |
510 | ||
511 | if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n")); | |
512 | if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n")); | |
513 | ||
514 | if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next)); | |
515 | if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev)); | |
516 | ||
517 | if (cp == ieb->cp) { | |
518 | cfl = 1; | |
519 | cpos += ieb->cpos; | |
520 | } | |
521 | ||
522 | if (!cfl) cpos += clen; | |
523 | ||
524 | csize += clen; | |
525 | cp = cp->next; | |
526 | } | |
527 | if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length)); | |
528 | if (( cpos != ieb->gpos )) mm_log((1, "BAR : cpos = %d, ieb->gpos = %d\n", cpos, ieb->gpos )); | |
529 | } | |
530 | ||
531 | ||
532 | static | |
533 | void | |
534 | chaincert( io_glue *ig) { | |
535 | size_t csize = 0; | |
536 | io_ex_bchain *ieb = ig->exdata; | |
537 | io_blink *cp = ieb->head; | |
538 | ||
539 | mm_log((1, "Chain verification.\n")); | |
540 | ||
541 | mm_log((1, " buf_chain_dump: ieb->offset = %d\n", ieb->offset)); | |
542 | mm_log((1, " buf_chain_dump: ieb->length = %d\n", ieb->length)); | |
543 | mm_log((1, " buf_chain_dump: ieb->head = %p\n", ieb->head )); | |
544 | mm_log((1, " buf_chain_dump: ieb->tail = %p\n", ieb->tail )); | |
545 | mm_log((1, " buf_chain_dump: ieb->tfill = %d\n", ieb->tfill )); | |
546 | mm_log((1, " buf_chain_dump: ieb->cp = %p\n", ieb->cp )); | |
547 | mm_log((1, " buf_chain_dump: ieb->cpos = %d\n", ieb->cpos )); | |
548 | mm_log((1, " buf_chain_dump: ieb->gpos = %d\n", ieb->gpos )); | |
549 | ||
550 | while(cp) { | |
551 | int clen = cp == ieb->tail ? ieb->tfill : cp->len; | |
552 | mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next)); | |
553 | if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n")); | |
554 | if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n")); | |
555 | ||
556 | if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n")); | |
557 | if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n")); | |
558 | ||
559 | if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next)); | |
560 | if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev)); | |
561 | ||
562 | csize += clen; | |
563 | cp = cp->next; | |
564 | } | |
565 | ||
566 | mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length)); | |
567 | } | |
568 | */ | |
569 | ||
570 | ||
571 | ||
572 | ||
573 | ||
574 | ||
575 | ||
576 | ||
577 | ||
578 | ||
579 | ||
580 | /* | |
581 | =item bufchain_read(ig, buf, count) | |
582 | ||
583 | Does the reading from a source that can be seeked on | |
584 | ||
585 | ig - io_glue object | |
586 | buf - buffer to return data in | |
587 | count - number of bytes to read into buffer max | |
588 | ||
589 | =cut | |
590 | */ | |
591 | ||
592 | static | |
593 | ssize_t | |
594 | bufchain_read(io_glue *ig, void *buf, size_t count) { | |
595 | io_ex_bchain *ieb = ig->exdata; | |
596 | size_t scount = count; | |
597 | char *cbuf = buf; | |
598 | size_t sk; | |
599 | ||
8d14daab | 600 | mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count)); |
02d1d628 AMH |
601 | |
602 | while( scount ) { | |
603 | int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
604 | if (clen == ieb->cpos) { | |
605 | if (ieb->cp == ieb->tail) break; /* EOF */ | |
606 | ieb->cp = ieb->cp->next; | |
607 | ieb->cpos = 0; | |
608 | clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
609 | } | |
610 | ||
611 | sk = clen - ieb->cpos; | |
612 | sk = sk > scount ? scount : sk; | |
613 | ||
614 | memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk); | |
615 | scount -= sk; | |
616 | ieb->cpos += sk; | |
617 | ieb->gpos += sk; | |
618 | } | |
619 | ||
8d14daab | 620 | mm_log((1, "bufchain_read: returning %ld\n", (long)(count-scount))); |
02d1d628 AMH |
621 | return count-scount; |
622 | } | |
623 | ||
624 | ||
625 | ||
626 | ||
627 | ||
628 | /* | |
629 | =item bufchain_write(ig, buf, count) | |
630 | ||
631 | Does the writing to a 'source' that can be seeked on | |
632 | ||
633 | ig - io_glue object | |
634 | buf - buffer that contains data | |
635 | count - number of bytes to write | |
636 | ||
637 | =cut | |
638 | */ | |
639 | ||
640 | static | |
641 | ssize_t | |
642 | bufchain_write(io_glue *ig, const void *buf, size_t count) { | |
643 | char *cbuf = (char *)buf; | |
644 | io_ex_bchain *ieb = ig->exdata; | |
645 | size_t ocount = count; | |
646 | size_t sk; | |
647 | ||
8d14daab | 648 | mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count)); |
02d1d628 | 649 | |
8d14daab | 650 | IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) ); |
02d1d628 AMH |
651 | |
652 | while(count) { | |
8d14daab | 653 | mm_log((2, "bufchain_write: - looping - count = %ld\n", (long)count)); |
02d1d628 | 654 | if (ieb->cp->len == ieb->cpos) { |
8d14daab | 655 | mm_log((1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos)); |
02d1d628 AMH |
656 | io_bchain_advance(ieb); |
657 | } | |
658 | ||
659 | sk = ieb->cp->len - ieb->cpos; | |
660 | sk = sk > count ? count : sk; | |
661 | memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk); | |
662 | ||
663 | if (ieb->cp == ieb->tail) { | |
664 | int extend = ieb->cpos + sk - ieb->tfill; | |
665 | mm_log((2, "bufchain_write: extending tail by %d\n", extend)); | |
666 | if (extend > 0) { | |
667 | ieb->length += extend; | |
668 | ieb->tfill += extend; | |
669 | } | |
670 | } | |
671 | ||
672 | ieb->cpos += sk; | |
673 | ieb->gpos += sk; | |
674 | count -= sk; | |
675 | } | |
676 | return ocount; | |
677 | } | |
678 | ||
679 | /* | |
680 | =item bufchain_close(ig) | |
681 | ||
682 | Closes a source that can be seeked on. Not sure if this should be an actual close | |
683 | or not. Does nothing for now. Should be fixed. | |
684 | ||
685 | ig - data source | |
686 | ||
687 | =cut | |
688 | */ | |
689 | ||
690 | static | |
2b405c9e | 691 | int |
02d1d628 AMH |
692 | bufchain_close(io_glue *ig) { |
693 | mm_log((1, "bufchain_close(ig %p)\n",ig)); | |
930c67c8 | 694 | IOL_DEB( printf("bufchain_close(ig %p)\n", ig) ); |
2b405c9e TC |
695 | |
696 | return 0; | |
02d1d628 AMH |
697 | } |
698 | ||
699 | ||
700 | /* bufchain_seek(ig, offset, whence) | |
701 | ||
702 | Implements seeking for a source that is seekable, the purpose of having this is to be able to | |
703 | have an offset into a file that is different from what the underlying library thinks. | |
704 | ||
705 | ig - data source | |
706 | offset - offset into stream | |
707 | whence - whence argument a la lseek | |
708 | ||
709 | =cut | |
710 | */ | |
711 | ||
712 | static | |
713 | off_t | |
714 | bufchain_seek(io_glue *ig, off_t offset, int whence) { | |
715 | io_ex_bchain *ieb = ig->exdata; | |
02d1d628 AMH |
716 | int wrlen; |
717 | ||
eda1622c | 718 | off_t scount = calc_seek_offset(ieb->gpos, ieb->length, offset, whence); |
02d1d628 AMH |
719 | off_t sk; |
720 | ||
8d14daab | 721 | mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence)); |
02d1d628 | 722 | |
eda1622c TC |
723 | if (scount < 0) { |
724 | i_push_error(0, "invalid whence supplied or seek before start of file"); | |
725 | return (off_t)-1; | |
726 | } | |
02d1d628 | 727 | |
eda1622c TC |
728 | ieb->cp = ieb->head; |
729 | ieb->cpos = 0; | |
730 | ieb->gpos = 0; | |
731 | ||
732 | while( scount ) { | |
733 | int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
734 | if (clen == ieb->cpos) { | |
735 | if (ieb->cp == ieb->tail) break; /* EOF */ | |
736 | ieb->cp = ieb->cp->next; | |
737 | ieb->cpos = 0; | |
738 | clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
02d1d628 AMH |
739 | } |
740 | ||
eda1622c TC |
741 | sk = clen - ieb->cpos; |
742 | sk = sk > scount ? scount : sk; | |
02d1d628 | 743 | |
eda1622c TC |
744 | scount -= sk; |
745 | ieb->cpos += sk; | |
746 | ieb->gpos += sk; | |
747 | } | |
748 | ||
749 | wrlen = scount; | |
02d1d628 | 750 | |
eda1622c TC |
751 | if (wrlen > 0) { |
752 | /* | |
753 | * extending file - get ieb into consistent state and then | |
754 | * call write which will get it to the correct position | |
755 | */ | |
756 | char TB[BBSIZ]; | |
757 | memset(TB, 0, BBSIZ); | |
758 | ieb->gpos = ieb->length; | |
02d1d628 AMH |
759 | ieb->cpos = ieb->tfill; |
760 | ||
eda1622c TC |
761 | while(wrlen > 0) { |
762 | ssize_t rc, wl = i_min(wrlen, BBSIZ); | |
8d14daab | 763 | mm_log((1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl)); |
eda1622c | 764 | rc = bufchain_write( ig, TB, wl ); |
b1e96952 | 765 | if (rc != wl) i_fatal(0, "bufchain_seek: Unable to extend file\n"); |
eda1622c | 766 | wrlen -= rc; |
02d1d628 | 767 | } |
02d1d628 AMH |
768 | } |
769 | ||
8d14daab | 770 | mm_log((2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos)); |
02d1d628 AMH |
771 | return ieb->gpos; |
772 | } | |
773 | ||
d16420e9 TC |
774 | static |
775 | void | |
776 | bufchain_destroy(io_glue *ig) { | |
777 | io_ex_bchain *ieb = ig->exdata; | |
02d1d628 | 778 | |
d16420e9 | 779 | io_destroy_bufchain(ieb); |
02d1d628 | 780 | |
d16420e9 TC |
781 | myfree(ieb); |
782 | } | |
02d1d628 AMH |
783 | |
784 | /* | |
785 | * Methods for setting up data source | |
786 | */ | |
787 | ||
788 | /* | |
789 | =item io_obj_setp_buffer(io, p, len) | |
790 | ||
791 | Sets an io_object for reading from a buffer source | |
792 | ||
793 | io - io object that describes a source | |
794 | p - pointer to buffer | |
795 | len - length of buffer | |
796 | ||
797 | =cut | |
798 | */ | |
799 | ||
299da279 | 800 | static void |
0778adbf | 801 | io_obj_setp_buffer(io_obj *io, char *p, size_t len, i_io_closebufp_t closecb, |
299da279 | 802 | void *closedata) { |
4dfa5522 AMH |
803 | io->buffer.type = BUFFER; |
804 | io->buffer.data = p; | |
805 | io->buffer.len = len; | |
806 | io->buffer.closecb = closecb; | |
807 | io->buffer.closedata = closedata; | |
02d1d628 AMH |
808 | } |
809 | ||
810 | ||
02d1d628 AMH |
811 | |
812 | /* | |
10461f9a | 813 | =item io_obj_setp_cb2(io, p, readcb, writecb, seekcb, closecb, destroycb) |
02d1d628 AMH |
814 | |
815 | Sets an io_object for reading from a source that uses callbacks | |
816 | ||
817 | io - io object that describes a source | |
10461f9a TC |
818 | p - pointer to data for callbacks |
819 | readcb - read callback to read from source | |
820 | writecb - write callback to write to source | |
821 | seekcb - seek callback to seek on source | |
822 | closecb - flush any pending data | |
823 | destroycb - release any extra resources | |
02d1d628 AMH |
824 | |
825 | =cut | |
826 | */ | |
827 | ||
299da279 | 828 | static void |
0778adbf | 829 | io_obj_setp_cb2(io_obj *io, void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb) { |
10461f9a TC |
830 | io->cb.type = CBSEEK; |
831 | io->cb.p = p; | |
832 | io->cb.readcb = readcb; | |
833 | io->cb.writecb = writecb; | |
834 | io->cb.seekcb = seekcb; | |
835 | io->cb.closecb = closecb; | |
836 | io->cb.destroycb = destroycb; | |
837 | } | |
838 | ||
02d1d628 AMH |
839 | /* |
840 | =item io_glue_commit_types(ig) | |
841 | ||
299da279 | 842 | This is now effectively a no-op. |
02d1d628 AMH |
843 | |
844 | =cut | |
845 | */ | |
846 | ||
847 | void | |
848 | io_glue_commit_types(io_glue *ig) { | |
849 | io_type inn = ig->source.type; | |
527c0c3e | 850 | |
930c67c8 | 851 | mm_log((1, "io_glue_commit_types(ig %p)\n", ig)); |
02d1d628 | 852 | mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn])); |
527c0c3e AMH |
853 | |
854 | if (ig->flags & 0x01) { | |
855 | mm_log((1, "io_glue_commit_types: type already set up\n")); | |
856 | return; | |
857 | } | |
858 | ||
527c0c3e | 859 | ig->flags |= 0x01; /* indicate source has been setup already */ |
02d1d628 AMH |
860 | } |
861 | ||
862 | /* | |
863 | =item io_glue_gettypes(ig, reqmeth) | |
864 | ||
865 | Returns a set of compatible interfaces to read data with. | |
866 | ||
867 | ig - io_glue object | |
868 | reqmeth - request mask | |
869 | ||
870 | The request mask is a bit mask (of something that hasn't been implemented yet) | |
871 | of interfaces that it would like to read data from the source which the ig | |
872 | describes. | |
873 | ||
874 | =cut | |
875 | */ | |
876 | ||
877 | void | |
878 | io_glue_gettypes(io_glue *ig, int reqmeth) { | |
879 | ||
880 | ig = NULL; | |
881 | reqmeth = 0; | |
882 | ||
883 | /* FIXME: Implement this function! */ | |
884 | /* if (ig->source.type = | |
885 | if (reqmeth & IO_BUFF) */ | |
886 | ||
887 | } | |
888 | ||
889 | ||
890 | /* | |
891 | =item io_new_bufchain() | |
892 | ||
893 | returns a new io_glue object that has the 'empty' source and but can | |
894 | be written to and read from later (like a pseudo file). | |
895 | ||
896 | =cut | |
897 | */ | |
898 | ||
899 | io_glue * | |
900 | io_new_bufchain() { | |
4dfa5522 | 901 | io_glue *ig; |
299da279 TC |
902 | io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain)); |
903 | ||
4dfa5522 | 904 | mm_log((1, "io_new_bufchain()\n")); |
299da279 | 905 | |
4dfa5522 | 906 | ig = mymalloc(sizeof(io_glue)); |
527c0c3e | 907 | memset(ig, 0, sizeof(*ig)); |
299da279 | 908 | ig->source.type = BUFCHAIN; |
c3cc977e | 909 | |
299da279 TC |
910 | ieb->offset = 0; |
911 | ieb->length = 0; | |
912 | ieb->cpos = 0; | |
913 | ieb->gpos = 0; | |
914 | ieb->tfill = 0; | |
915 | ||
916 | ieb->head = io_blink_new(); | |
917 | ieb->cp = ieb->head; | |
918 | ieb->tail = ieb->head; | |
919 | ||
d16420e9 TC |
920 | ig->exdata = ieb; |
921 | ig->readcb = bufchain_read; | |
922 | ig->writecb = bufchain_write; | |
923 | ig->seekcb = bufchain_seek; | |
924 | ig->closecb = bufchain_close; | |
925 | ig->destroycb = bufchain_destroy; | |
c3cc977e | 926 | |
299da279 TC |
927 | return ig; |
928 | } | |
c3cc977e | 929 | |
4dfa5522 AMH |
930 | /* |
931 | =item io_new_buffer(data, len) | |
932 | ||
933 | Returns a new io_glue object that has the source defined as reading | |
934 | from specified buffer. Note that the buffer is not copied. | |
935 | ||
936 | data - buffer to read from | |
937 | len - length of buffer | |
c3cc977e | 938 | |
4dfa5522 AMH |
939 | =cut |
940 | */ | |
941 | ||
942 | io_glue * | |
0778adbf | 943 | io_new_buffer(char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { |
4dfa5522 | 944 | io_glue *ig; |
299da279 TC |
945 | io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer)); |
946 | ||
8d14daab | 947 | mm_log((1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); |
299da279 | 948 | |
4dfa5522 AMH |
949 | ig = mymalloc(sizeof(io_glue)); |
950 | memset(ig, 0, sizeof(*ig)); | |
951 | io_obj_setp_buffer(&ig->source, data, len, closecb, closedata); | |
527c0c3e | 952 | ig->flags = 0; |
299da279 TC |
953 | |
954 | ieb->offset = 0; | |
955 | ieb->cpos = 0; | |
956 | ||
d16420e9 TC |
957 | ig->exdata = ieb; |
958 | ig->readcb = buffer_read; | |
959 | ig->writecb = buffer_write; | |
960 | ig->seekcb = buffer_seek; | |
961 | ig->closecb = buffer_close; | |
962 | ig->destroycb = buffer_destroy; | |
299da279 | 963 | |
4dfa5522 AMH |
964 | return ig; |
965 | } | |
c3cc977e AMH |
966 | |
967 | ||
02d1d628 AMH |
968 | /* |
969 | =item io_new_fd(fd) | |
970 | ||
971 | returns a new io_glue object that has the source defined as reading | |
972 | from specified filedescriptor. Note that the the interface to recieving | |
973 | data from the io_glue callbacks hasn't been done yet. | |
974 | ||
975 | fd - file descriptor to read/write from | |
976 | ||
977 | =cut | |
978 | */ | |
979 | ||
980 | io_glue * | |
981 | io_new_fd(int fd) { | |
4dfa5522 | 982 | io_glue *ig; |
299da279 | 983 | |
4dfa5522 | 984 | mm_log((1, "io_new_fd(fd %d)\n", fd)); |
299da279 | 985 | |
4dfa5522 | 986 | ig = mymalloc(sizeof(io_glue)); |
261f91c5 | 987 | memset(ig, 0, sizeof(*ig)); |
10461f9a TC |
988 | ig->source.type = FDSEEK; |
989 | ig->source.fdseek.fd = fd; | |
527c0c3e | 990 | ig->flags = 0; |
299da279 | 991 | |
d16420e9 TC |
992 | ig->exdata = NULL; |
993 | ig->readcb = fd_read; | |
994 | ig->writecb = fd_write; | |
995 | ig->seekcb = fd_seek; | |
996 | ig->closecb = fd_close; | |
997 | ig->sizecb = fd_size; | |
998 | ig->destroycb = NULL; | |
299da279 | 999 | |
4dfa5522 | 1000 | mm_log((1, "(%p) <- io_new_fd\n", ig)); |
02d1d628 AMH |
1001 | return ig; |
1002 | } | |
1003 | ||
0778adbf TC |
1004 | io_glue *io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, |
1005 | i_io_seekl_t seekcb, i_io_closel_t closecb, | |
1006 | i_io_destroyl_t destroycb) { | |
10461f9a | 1007 | io_glue *ig; |
299da279 | 1008 | io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek)); |
02d1d628 | 1009 | |
10461f9a TC |
1010 | mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " |
1011 | "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb)); | |
1012 | ig = mymalloc(sizeof(io_glue)); | |
527c0c3e | 1013 | memset(ig, 0, sizeof(*ig)); |
10461f9a TC |
1014 | io_obj_setp_cb2(&ig->source, p, readcb, writecb, seekcb, closecb, destroycb); |
1015 | mm_log((1, "(%p) <- io_new_cb\n", ig)); | |
1016 | ||
299da279 TC |
1017 | ier->offset = 0; |
1018 | ier->cpos = 0; | |
1019 | ||
d16420e9 TC |
1020 | ig->exdata = ier; |
1021 | ig->readcb = realseek_read; | |
1022 | ig->writecb = realseek_write; | |
1023 | ig->seekcb = realseek_seek; | |
1024 | ig->closecb = realseek_close; | |
1025 | ig->destroycb = realseek_destroy; | |
299da279 | 1026 | |
10461f9a TC |
1027 | return ig; |
1028 | } | |
02d1d628 AMH |
1029 | |
1030 | /* | |
1031 | =item io_slurp(ig) | |
1032 | ||
1033 | Takes the source that the io_glue is bound to and allocates space | |
1034 | for a return buffer and returns the entire content in a single buffer. | |
1035 | Note: This only works for io_glue objects that contain a bufchain. It | |
1036 | is usefull for saving to scalars and such. | |
1037 | ||
1038 | ig - io_glue object | |
1039 | c - pointer to a pointer to where data should be copied to | |
1040 | ||
1041 | =cut | |
1042 | */ | |
1043 | ||
1044 | size_t | |
1045 | io_slurp(io_glue *ig, unsigned char **c) { | |
1046 | ssize_t rc; | |
1047 | off_t orgoff; | |
1048 | io_ex_bchain *ieb; | |
1049 | unsigned char *cc; | |
1050 | io_type inn = ig->source.type; | |
1051 | ||
1052 | if ( inn != BUFCHAIN ) { | |
b1e96952 | 1053 | i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n"); |
02d1d628 AMH |
1054 | } |
1055 | ||
1056 | ieb = ig->exdata; | |
1057 | cc = *c = mymalloc( ieb->length ); | |
1058 | ||
1059 | orgoff = ieb->gpos; | |
1060 | ||
1061 | bufchain_seek(ig, 0, SEEK_SET); | |
1062 | ||
1063 | rc = bufchain_read(ig, cc, ieb->length); | |
1064 | ||
1065 | if (rc != ieb->length) | |
b1e96952 | 1066 | i_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length); |
02d1d628 AMH |
1067 | |
1068 | return rc; | |
1069 | } | |
1070 | ||
10461f9a TC |
1071 | /* |
1072 | =item fd_read(ig, buf, count) | |
1073 | ||
1074 | =cut | |
1075 | */ | |
1076 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count) { | |
5f8f8e17 | 1077 | ssize_t result; |
10461f9a | 1078 | #ifdef _MSC_VER |
5f8f8e17 | 1079 | result = _read(ig->source.fdseek.fd, buf, count); |
10461f9a | 1080 | #else |
5f8f8e17 | 1081 | result = read(ig->source.fdseek.fd, buf, count); |
10461f9a | 1082 | #endif |
5f8f8e17 TC |
1083 | |
1084 | /* 0 is valid - means EOF */ | |
1085 | if (result < 0) { | |
40d75d5a | 1086 | i_push_errorf(0, "read() failure: %s (%d)", my_strerror(errno), errno); |
5f8f8e17 TC |
1087 | } |
1088 | ||
1089 | return result; | |
10461f9a TC |
1090 | } |
1091 | ||
1092 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count) { | |
2691d220 | 1093 | ssize_t result; |
10461f9a | 1094 | #ifdef _MSC_VER |
2691d220 | 1095 | result = _write(ig->source.fdseek.fd, buf, count); |
10461f9a | 1096 | #else |
2691d220 | 1097 | result = write(ig->source.fdseek.fd, buf, count); |
10461f9a | 1098 | #endif |
2691d220 TC |
1099 | |
1100 | if (result <= 0) { | |
40d75d5a | 1101 | i_push_errorf(errno, "write() failure: %s (%d)", my_strerror(errno), errno); |
2691d220 TC |
1102 | } |
1103 | ||
1104 | return result; | |
10461f9a TC |
1105 | } |
1106 | ||
1107 | static off_t fd_seek(io_glue *ig, off_t offset, int whence) { | |
2691d220 | 1108 | off_t result; |
10461f9a | 1109 | #ifdef _MSC_VER |
2691d220 | 1110 | result = _lseek(ig->source.fdseek.fd, offset, whence); |
10461f9a | 1111 | #else |
2691d220 | 1112 | result = lseek(ig->source.fdseek.fd, offset, whence); |
10461f9a | 1113 | #endif |
2691d220 TC |
1114 | |
1115 | if (result == (off_t)-1) { | |
40d75d5a | 1116 | i_push_errorf(errno, "lseek() failure: %s (%d)", my_strerror(errno), errno); |
2691d220 TC |
1117 | } |
1118 | ||
1119 | return result; | |
10461f9a TC |
1120 | } |
1121 | ||
2b405c9e | 1122 | static int fd_close(io_glue *ig) { |
10461f9a | 1123 | /* no, we don't close it */ |
2b405c9e | 1124 | return 0; |
10461f9a TC |
1125 | } |
1126 | ||
1127 | static ssize_t fd_size(io_glue *ig) { | |
1128 | mm_log((1, "fd_size(ig %p) unimplemented\n", ig)); | |
1129 | ||
1130 | return -1; | |
1131 | } | |
02d1d628 AMH |
1132 | |
1133 | /* | |
d16420e9 | 1134 | =item io_glue_destroy(ig) |
02d1d628 AMH |
1135 | |
1136 | A destructor method for io_glue objects. Should clean up all related buffers. | |
1137 | Might leave us with a dangling pointer issue on some buffers. | |
1138 | ||
1139 | ig - io_glue object to destroy. | |
1140 | ||
1141 | =cut | |
1142 | */ | |
1143 | ||
1144 | void | |
d16420e9 | 1145 | io_glue_destroy(io_glue *ig) { |
c3cc977e | 1146 | mm_log((1, "io_glue_DESTROY(ig %p)\n", ig)); |
d16420e9 TC |
1147 | |
1148 | if (ig->destroycb) | |
1149 | ig->destroycb(ig); | |
c3cc977e | 1150 | |
c3cc977e | 1151 | myfree(ig); |
02d1d628 | 1152 | } |
b8c2033e | 1153 | |
40d75d5a TC |
1154 | /* |
1155 | =back | |
1156 | ||
1157 | =head1 INTERNAL FUNCTIONS | |
1158 | ||
1159 | =over | |
1160 | ||
1161 | =item my_strerror | |
1162 | ||
1163 | Calls strerror() and ensures we don't return NULL. | |
1164 | ||
1165 | On some platforms it's possible for strerror() to return NULL, this | |
1166 | wrapper ensures we only get non-NULL values. | |
1167 | ||
1168 | =cut | |
1169 | */ | |
1170 | ||
1171 | static | |
1172 | const char *my_strerror(int err) { | |
1173 | const char *result = strerror(err); | |
1174 | ||
1175 | if (!result) | |
1176 | result = "Unknown error"; | |
1177 | ||
1178 | return result; | |
1179 | } | |
b8c2033e AMH |
1180 | |
1181 | /* | |
1182 | =back | |
1183 | ||
1184 | =head1 AUTHOR | |
1185 | ||
1186 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
1187 | ||
1188 | =head1 SEE ALSO | |
1189 | ||
1190 | Imager(3) | |
1191 | ||
1192 | =cut | |
1193 | */ |