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