Commit | Line | Data |
---|---|---|
ed60e785 | 1 | #define IMAGER_NO_CONTEXT |
e310e5f9 | 2 | #include "imager.h" |
02d1d628 | 3 | #include "iolayer.h" |
af070d99 | 4 | #include "imerror.h" |
02d1d628 AMH |
5 | #include "log.h" |
6 | #include <stdlib.h> | |
7 | #include <stdio.h> | |
8 | #ifdef _MSC_VER | |
9 | #include <io.h> | |
10 | #endif | |
35891892 | 11 | #include <string.h> |
2691d220 | 12 | #include <errno.h> |
50c75381 | 13 | #include "imageri.h" |
02d1d628 AMH |
14 | |
15 | #define IOL_DEB(x) | |
6d5c85a2 | 16 | #define IOL_DEBs stderr |
02d1d628 | 17 | |
6d5c85a2 | 18 | #define IO_BUF_SIZE 8192 |
02d1d628 AMH |
19 | |
20 | char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" }; | |
21 | ||
299da279 TC |
22 | typedef struct io_blink { |
23 | char buf[BBSIZ]; | |
24 | /* size_t cnt; */ | |
25 | size_t len; /* How large is this buffer = BBZIS for now */ | |
26 | struct io_blink *next; | |
27 | struct io_blink *prev; | |
28 | } io_blink; | |
29 | ||
30 | ||
299da279 | 31 | typedef struct { |
6d5c85a2 TC |
32 | i_io_glue_t base; |
33 | int fd; | |
34 | } io_fdseek; | |
299da279 TC |
35 | |
36 | typedef struct { | |
6d5c85a2 TC |
37 | i_io_glue_t base; |
38 | const char *data; | |
39 | size_t len; | |
40 | i_io_closebufp_t closecb; /* free memory mapped segment or decrement refcount */ | |
41 | void *closedata; | |
299da279 | 42 | off_t cpos; |
6d5c85a2 | 43 | } io_buffer; |
299da279 | 44 | |
6d5c85a2 TC |
45 | typedef struct { |
46 | i_io_glue_t base; | |
47 | void *p; /* Callback data */ | |
48 | i_io_readl_t readcb; | |
49 | i_io_writel_t writecb; | |
50 | i_io_seekl_t seekcb; | |
51 | i_io_closel_t closecb; | |
52 | i_io_destroyl_t destroycb; | |
53 | } io_cb; | |
299da279 TC |
54 | |
55 | typedef struct { | |
56 | off_t offset; /* Offset of the source - not used */ | |
57 | off_t length; /* Total length of chain in bytes */ | |
58 | io_blink *head; /* Start of chain */ | |
59 | io_blink *tail; /* End of chain */ | |
60 | off_t tfill; /* End of stream in last link */ | |
61 | io_blink *cp; /* Current element of list */ | |
62 | off_t cpos; /* Offset within the current */ | |
63 | off_t gpos; /* Global position in stream */ | |
64 | } io_ex_bchain; | |
65 | ||
eda1622c TC |
66 | /* turn current offset, file length, whence and offset into a new offset */ |
67 | #define calc_seek_offset(curr_off, length, offset, whence) \ | |
68 | (((whence) == SEEK_SET) ? (offset) : \ | |
69 | ((whence) == SEEK_CUR) ? (curr_off) + (offset) : \ | |
70 | ((whence) == SEEK_END) ? (length) + (offset) : -1) | |
02d1d628 AMH |
71 | |
72 | /* | |
73 | =head1 NAME | |
74 | ||
75 | iolayer.c - encapsulates different source of data into a single framework. | |
76 | ||
77 | =head1 SYNOPSIS | |
78 | ||
79 | io_glue *ig = io_new_fd( fileno(stdin) ); | |
80 | method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet | |
6d5c85a2 | 81 | |
02d1d628 AMH |
82 | switch (method) { |
83 | case IOL_NOSEEK: | |
84 | code that uses ig->readcb() | |
85 | to read data goes here. | |
86 | break; | |
87 | case IOL_MMAP: | |
88 | code that uses ig->readcb() | |
89 | to read data goes here. | |
90 | break; | |
91 | } | |
92 | ||
d16420e9 | 93 | io_glue_destroy(ig); |
02d1d628 AMH |
94 | // and much more |
95 | ||
96 | =head1 DESCRIPTION | |
97 | ||
98 | iolayer.c implements the basic functions to create and destroy io_glue | |
99 | objects for Imager. The typical usage pattern for data sources is: | |
100 | ||
101 | 1. Create the source (io_new_fd) | |
102 | 2. Define how you want to get data from it (io_reqmeth) | |
103 | 3. read from it using the interface requested (ig->readdb, ig->mmapcb) | |
104 | 4. Close the source, which | |
105 | shouldn't really close the underlying source. (io_glue DESTROY) | |
106 | ||
107 | =head1 FUNCTION REFERENCE | |
108 | ||
109 | Some of these functions are internal. | |
110 | ||
b8c2033e | 111 | =over |
02d1d628 AMH |
112 | |
113 | =cut | |
114 | */ | |
115 | ||
6d5c85a2 | 116 | static void |
ed60e785 TC |
117 | i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb, |
118 | i_io_writep_t writecb, i_io_seekp_t seekcb); | |
6d5c85a2 | 119 | |
10461f9a TC |
120 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count); |
121 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count); | |
122 | static off_t fd_seek(io_glue *ig, off_t offset, int whence); | |
2b405c9e | 123 | static int fd_close(io_glue *ig); |
10461f9a | 124 | static ssize_t fd_size(io_glue *ig); |
40d75d5a | 125 | static const char *my_strerror(int err); |
6d5c85a2 TC |
126 | static void i_io_setup_buffer(io_glue *ig); |
127 | static void | |
128 | i_io_start_write(io_glue *ig); | |
129 | static int | |
130 | i_io_read_fill(io_glue *ig, ssize_t needed); | |
131 | static void | |
132 | dump_data(unsigned char *start, unsigned char *end, int bias); | |
133 | static ssize_t realseek_read(io_glue *igo, void *buf, size_t count); | |
134 | static ssize_t realseek_write(io_glue *igo, const void *buf, size_t count); | |
135 | static int realseek_close(io_glue *igo); | |
136 | static off_t realseek_seek(io_glue *igo, off_t offset, int whence); | |
137 | static void realseek_destroy(io_glue *igo); | |
138 | static ssize_t buffer_read(io_glue *igo, void *buf, size_t count); | |
139 | static ssize_t buffer_write(io_glue *ig, const void *buf, size_t count); | |
140 | static int buffer_close(io_glue *ig); | |
141 | static off_t buffer_seek(io_glue *igo, off_t offset, int whence); | |
142 | static void buffer_destroy(io_glue *igo); | |
143 | static io_blink*io_blink_new(void); | |
144 | static void io_bchain_advance(io_ex_bchain *ieb); | |
145 | static void io_destroy_bufchain(io_ex_bchain *ieb); | |
146 | static ssize_t bufchain_read(io_glue *ig, void *buf, size_t count); | |
147 | static ssize_t bufchain_write(io_glue *ig, const void *buf, size_t count); | |
148 | static int bufchain_close(io_glue *ig); | |
149 | static off_t bufchain_seek(io_glue *ig, off_t offset, int whence); | |
150 | static void bufchain_destroy(io_glue *ig); | |
02d1d628 AMH |
151 | |
152 | /* | |
6d5c85a2 | 153 | * Methods for setting up data source |
02d1d628 AMH |
154 | */ |
155 | ||
6d5c85a2 | 156 | /* |
ed60e785 | 157 | =item io_new_bufchain(ctx) |
6d5c85a2 TC |
158 | =order 10 |
159 | =category I/O Layers | |
160 | ||
d03fd5a4 | 161 | returns a new io_glue object that has the 'empty' source and but can |
6d5c85a2 TC |
162 | be written to and read from later (like a pseudo file). |
163 | ||
164 | =cut | |
165 | */ | |
166 | ||
167 | io_glue * | |
ed60e785 | 168 | im_io_new_bufchain(pIMCTX) { |
6d5c85a2 TC |
169 | io_glue *ig; |
170 | io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain)); | |
171 | ||
ed60e785 | 172 | im_log((aIMCTX, 1, "io_new_bufchain()\n")); |
6d5c85a2 TC |
173 | |
174 | ig = mymalloc(sizeof(io_glue)); | |
175 | memset(ig, 0, sizeof(*ig)); | |
ed60e785 | 176 | i_io_init(aIMCTX, ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek); |
6d5c85a2 TC |
177 | |
178 | ieb->offset = 0; | |
179 | ieb->length = 0; | |
180 | ieb->cpos = 0; | |
181 | ieb->gpos = 0; | |
182 | ieb->tfill = 0; | |
183 | ||
184 | ieb->head = io_blink_new(); | |
185 | ieb->cp = ieb->head; | |
186 | ieb->tail = ieb->head; | |
187 | ||
188 | ig->exdata = ieb; | |
189 | ig->closecb = bufchain_close; | |
190 | ig->destroycb = bufchain_destroy; | |
191 | ||
ed60e785 TC |
192 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); |
193 | ||
6d5c85a2 | 194 | return ig; |
02d1d628 | 195 | } |
6d5c85a2 TC |
196 | |
197 | /* | |
d03fd5a4 | 198 | =item io_new_buffer(data, length) |
6d5c85a2 TC |
199 | =order 10 |
200 | =category I/O Layers | |
201 | ||
202 | Returns a new io_glue object that has the source defined as reading | |
203 | from specified buffer. Note that the buffer is not copied. | |
204 | ||
205 | data - buffer to read from | |
206 | length - length of buffer | |
207 | ||
208 | =cut | |
02d1d628 AMH |
209 | */ |
210 | ||
6d5c85a2 | 211 | io_glue * |
ed60e785 | 212 | im_io_new_buffer(pIMCTX, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { |
6d5c85a2 TC |
213 | io_buffer *ig; |
214 | ||
ed60e785 | 215 | im_log((aIMCTX, 1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); |
6d5c85a2 TC |
216 | |
217 | ig = mymalloc(sizeof(io_buffer)); | |
218 | memset(ig, 0, sizeof(*ig)); | |
ed60e785 | 219 | i_io_init(aIMCTX, &ig->base, BUFFER, buffer_read, buffer_write, buffer_seek); |
6d5c85a2 TC |
220 | ig->data = data; |
221 | ig->len = len; | |
222 | ig->closecb = closecb; | |
223 | ig->closedata = closedata; | |
224 | ||
225 | ig->cpos = 0; | |
226 | ||
227 | ig->base.closecb = buffer_close; | |
228 | ig->base.destroycb = buffer_destroy; | |
229 | ||
ed60e785 TC |
230 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); |
231 | ||
6d5c85a2 TC |
232 | return (io_glue *)ig; |
233 | } | |
02d1d628 AMH |
234 | |
235 | ||
236 | /* | |
d03fd5a4 | 237 | =item io_new_fd(fd) |
6d5c85a2 TC |
238 | =order 10 |
239 | =category I/O Layers | |
240 | ||
d03fd5a4 | 241 | returns a new io_glue object that has the source defined as reading |
6d5c85a2 TC |
242 | from specified file descriptor. Note that the the interface to receiving |
243 | data from the io_glue callbacks hasn't been done yet. | |
244 | ||
d03fd5a4 | 245 | fd - file descriptor to read/write from |
6d5c85a2 TC |
246 | |
247 | =cut | |
248 | */ | |
249 | ||
250 | io_glue * | |
ed60e785 | 251 | im_io_new_fd(pIMCTX, int fd) { |
6d5c85a2 TC |
252 | io_fdseek *ig; |
253 | ||
ed60e785 | 254 | im_log((aIMCTX, 1, "io_new_fd(fd %d)\n", fd)); |
6d5c85a2 TC |
255 | |
256 | ig = mymalloc(sizeof(io_fdseek)); | |
257 | memset(ig, 0, sizeof(*ig)); | |
ed60e785 | 258 | i_io_init(aIMCTX, &ig->base, FDSEEK, fd_read, fd_write, fd_seek); |
6d5c85a2 TC |
259 | ig->fd = fd; |
260 | ||
261 | ig->base.closecb = fd_close; | |
262 | ig->base.sizecb = fd_size; | |
263 | ig->base.destroycb = NULL; | |
ed60e785 | 264 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); |
6d5c85a2 | 265 | |
ed60e785 | 266 | im_log((aIMCTX, 1, "(%p) <- io_new_fd\n", ig)); |
6d5c85a2 TC |
267 | return (io_glue *)ig; |
268 | } | |
02d1d628 AMH |
269 | |
270 | /* | |
d03fd5a4 | 271 | =item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) |
6d5c85a2 TC |
272 | =category I/O Layers |
273 | =order 10 | |
02d1d628 | 274 | |
6d5c85a2 | 275 | Create a new I/O layer object that calls your supplied callbacks. |
02d1d628 | 276 | |
6d5c85a2 TC |
277 | In general the callbacks should behave like the corresponding POSIX |
278 | primitives. | |
279 | ||
280 | =over | |
281 | ||
282 | =item * | |
283 | ||
284 | C<read_cb>(p, buffer, length) should read up to C<length> bytes into | |
285 | C<buffer> and return the number of bytes read. At end of file, return | |
286 | 0. On error, return -1. | |
287 | ||
288 | =item * | |
289 | ||
290 | C<write_cb>(p, buffer, length) should write up to C<length> bytes from | |
291 | C<buffer> and return the number of bytes written. A return value <= 0 | |
292 | will be treated as an error. | |
293 | ||
294 | =item * | |
295 | ||
296 | C<seekcb>(p, offset, whence) should seek and return the new offset. | |
297 | ||
298 | =item * | |
299 | ||
300 | C<close_cb>(p) should return 0 on success, -1 on failure. | |
301 | ||
302 | =item * | |
303 | ||
304 | C<destroy_cb>(p) should release any memory specific to your callback | |
305 | handlers. | |
306 | ||
307 | =back | |
02d1d628 AMH |
308 | |
309 | =cut | |
310 | */ | |
311 | ||
6d5c85a2 | 312 | io_glue * |
ed60e785 | 313 | im_io_new_cb(pIMCTX, void *p, i_io_readl_t readcb, i_io_writel_t writecb, |
6d5c85a2 TC |
314 | i_io_seekl_t seekcb, i_io_closel_t closecb, |
315 | i_io_destroyl_t destroycb) { | |
316 | io_cb *ig; | |
02d1d628 | 317 | |
ed60e785 | 318 | im_log((aIMCTX, 1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " |
6d5c85a2 TC |
319 | "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb)); |
320 | ig = mymalloc(sizeof(io_cb)); | |
321 | memset(ig, 0, sizeof(*ig)); | |
ed60e785 TC |
322 | i_io_init(aIMCTX, &ig->base, CBSEEK, realseek_read, realseek_write, realseek_seek); |
323 | im_log((aIMCTX, 1, "(%p) <- io_new_cb\n", ig)); | |
6d5c85a2 TC |
324 | |
325 | ig->base.closecb = realseek_close; | |
326 | ig->base.destroycb = realseek_destroy; | |
327 | ||
328 | ig->p = p; | |
329 | ig->readcb = readcb; | |
330 | ig->writecb = writecb; | |
331 | ig->seekcb = seekcb; | |
332 | ig->closecb = closecb; | |
333 | ig->destroycb = destroycb; | |
ed60e785 TC |
334 | |
335 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); | |
6d5c85a2 TC |
336 | |
337 | return (io_glue *)ig; | |
338 | } | |
339 | ||
340 | /* | |
341 | =item io_slurp(ig, c) | |
342 | =category I/O Layers | |
343 | ||
344 | Takes the source that the io_glue is bound to and allocates space for | |
345 | a return buffer and returns the entire content in a single buffer. | |
346 | Note: This only works for io_glue objects created by | |
347 | io_new_bufchain(). It is useful for saving to scalars and such. | |
348 | ||
349 | ig - io_glue object | |
350 | c - pointer to a pointer to where data should be copied to | |
351 | ||
352 | char *data; | |
353 | size_t size = io_slurp(ig, &data); | |
354 | ... do something with the data ... | |
355 | myfree(data); | |
356 | ||
357 | io_slurp() will abort the program if the supplied I/O layer is not | |
358 | from io_new_bufchain(). | |
359 | ||
360 | =cut | |
361 | */ | |
362 | ||
363 | size_t | |
364 | io_slurp(io_glue *ig, unsigned char **c) { | |
365 | ssize_t rc; | |
366 | off_t orgoff; | |
367 | io_ex_bchain *ieb; | |
368 | unsigned char *cc; | |
369 | io_type inn = ig->type; | |
370 | ||
371 | if ( inn != BUFCHAIN ) { | |
2038f6ce TC |
372 | dIMCTXio(ig); |
373 | im_fatal(aIMCTX, 0, "io_slurp: called on a source that is not from a bufchain\n"); | |
10461f9a | 374 | } |
6d5c85a2 TC |
375 | |
376 | ieb = ig->exdata; | |
377 | cc = *c = mymalloc( ieb->length ); | |
02d1d628 | 378 | |
6d5c85a2 TC |
379 | orgoff = ieb->gpos; |
380 | ||
381 | bufchain_seek(ig, 0, SEEK_SET); | |
382 | ||
383 | rc = bufchain_read(ig, cc, ieb->length); | |
384 | ||
2038f6ce TC |
385 | if (rc != ieb->length) { |
386 | dIMCTXio(ig); | |
387 | im_fatal(aIMCTX,1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length); | |
388 | } | |
6d5c85a2 TC |
389 | |
390 | return rc; | |
391 | } | |
392 | ||
393 | /* | |
394 | =item io_glue_destroy(ig) | |
395 | =category I/O Layers | |
396 | =order 90 | |
397 | =synopsis io_glue_destroy(ig); | |
398 | ||
399 | Destroy an io_glue objects. Should clean up all related buffers. | |
400 | ||
401 | ig - io_glue object to destroy. | |
402 | ||
403 | =cut | |
404 | */ | |
405 | ||
406 | void | |
407 | io_glue_destroy(io_glue *ig) { | |
ed60e785 TC |
408 | dIMCTXio(ig); |
409 | im_log((aIMCTX, 1, "io_glue_DESTROY(ig %p)\n", ig)); | |
6d5c85a2 TC |
410 | |
411 | if (ig->destroycb) | |
412 | ig->destroycb(ig); | |
413 | ||
414 | if (ig->buffer) | |
415 | myfree(ig->buffer); | |
416 | ||
417 | myfree(ig); | |
ed60e785 TC |
418 | |
419 | im_context_refdec(aIMCTX, "io_glue_destroy"); | |
6d5c85a2 TC |
420 | } |
421 | ||
422 | /* | |
423 | =item i_io_getc(ig) | |
424 | =category I/O Layers | |
425 | ||
426 | A macro to read a single byte from a buffered I/O glue object. | |
427 | ||
428 | Returns EOF on failure, or a byte. | |
429 | ||
430 | =cut | |
431 | */ | |
432 | ||
433 | int | |
434 | i_io_getc_imp(io_glue *ig) { | |
435 | if (ig->write_ptr) | |
436 | return EOF; | |
437 | ||
438 | if (ig->error || ig->buf_eof) | |
439 | return EOF; | |
440 | ||
441 | if (!ig->buffered) { | |
442 | unsigned char buf; | |
443 | ssize_t rc = i_io_raw_read(ig, &buf, 1); | |
444 | if (rc > 0) { | |
445 | return buf; | |
446 | } | |
447 | else if (rc == 0) { | |
448 | ig->buf_eof = 1; | |
449 | return EOF; | |
450 | } | |
451 | else { | |
452 | ig->error = 1; | |
453 | return EOF; | |
454 | } | |
455 | } | |
456 | ||
457 | if (!ig->buffer) | |
458 | i_io_setup_buffer(ig); | |
459 | ||
460 | if (!ig->read_ptr || ig->read_ptr == ig->read_end) { | |
461 | if (!i_io_read_fill(ig, 1)) | |
462 | return EOF; | |
463 | } | |
464 | ||
465 | return *(ig->read_ptr++); | |
466 | } | |
467 | ||
468 | /* | |
469 | =item i_io_peekc(ig) | |
470 | =category I/O Layers | |
471 | ||
472 | Read the next character from the stream without advancing the stream. | |
473 | ||
474 | On error or end of file, return EOF. | |
475 | ||
476 | For unbuffered streams a single character buffer will be setup. | |
477 | ||
478 | =cut | |
479 | */ | |
480 | ||
481 | int | |
482 | i_io_peekc_imp(io_glue *ig) { | |
483 | if (ig->write_ptr) | |
484 | return EOF; | |
485 | ||
486 | if (!ig->buffer) | |
487 | i_io_setup_buffer(ig); | |
488 | ||
489 | if (!ig->buffered) { | |
490 | ssize_t rc = i_io_raw_read(ig, ig->buffer, 1); | |
491 | if (rc > 0) { | |
492 | ig->read_ptr = ig->buffer; | |
493 | ig->read_end = ig->buffer + 1; | |
494 | return *(ig->buffer); | |
495 | } | |
496 | else if (rc == 0) { | |
497 | ig->buf_eof = 1; | |
498 | return EOF; | |
499 | } | |
500 | else { | |
501 | ig->error = 1; | |
502 | return EOF; | |
503 | } | |
504 | } | |
505 | ||
506 | if (!ig->read_ptr || ig->read_ptr == ig->read_end) { | |
507 | if (ig->error || ig->buf_eof) | |
508 | return EOF; | |
509 | ||
510 | if (!i_io_read_fill(ig, 1)) | |
511 | return EOF; | |
512 | } | |
513 | ||
514 | return *(ig->read_ptr); | |
515 | } | |
516 | ||
517 | /* | |
518 | =item i_io_peekn(ig, buffer, size) | |
519 | =category I/O Layers | |
520 | =synopsis ssize_t count = i_io_peekn(ig, buffer, sizeof(buffer)); | |
521 | ||
522 | Buffer at least C<size> (at most C<< ig->buf_size >> bytes of data | |
523 | from the stream and return C<size> bytes of it to the caller in | |
524 | C<buffer>. | |
525 | ||
526 | This ignores the buffered state of the stream, and will always setup | |
527 | buffering if needed. | |
528 | ||
529 | If no C<type> parameter is provided to Imager::read() or | |
530 | Imager::read_multi(), Imager will call C<i_io_peekn()> when probing | |
531 | for the file format. | |
532 | ||
533 | Returns -1 on error, 0 if there is no data before EOF, or the number | |
534 | of bytes read into C<buffer>. | |
535 | ||
536 | =cut | |
537 | */ | |
538 | ||
539 | ssize_t | |
540 | i_io_peekn(io_glue *ig, void *buf, size_t size) { | |
541 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn(%p, %p, %d)\n", ig, buf, (int)size)); | |
542 | ||
543 | if (size == 0) { | |
ed60e785 | 544 | dIMCTXio(ig); |
6d5c85a2 TC |
545 | i_push_error(0, "peekn size must be positive"); |
546 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (zero size)\n")); | |
547 | return -1; | |
548 | } | |
549 | ||
550 | if (ig->write_ptr) { | |
551 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (write_ptr set)\n")); | |
552 | return -1; | |
553 | } | |
554 | ||
555 | if (!ig->buffer) | |
556 | i_io_setup_buffer(ig); | |
557 | ||
558 | if ((!ig->read_ptr || size > ig->read_end - ig->read_ptr) | |
559 | && !(ig->buf_eof || ig->error)) { | |
560 | i_io_read_fill(ig, size); | |
561 | } | |
562 | ||
563 | if (size > ig->read_end - ig->read_ptr) | |
564 | size = ig->read_end - ig->read_ptr; | |
565 | ||
566 | if (size) | |
567 | memcpy(buf, ig->read_ptr, size); | |
568 | else if (ig->buf_eof) { | |
569 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => 0 (eof)\n")); | |
570 | return 0; | |
571 | } | |
572 | else if (ig->error) { | |
573 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (error)\n")); | |
574 | return -1; | |
575 | } | |
576 | else { | |
577 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() - size 0 but not eof or error!\n")); | |
578 | return -1; | |
579 | } | |
580 | ||
581 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => %d\n", (int)size)); | |
582 | ||
583 | return size; | |
584 | } | |
585 | ||
586 | /* | |
587 | =item i_io_putc(ig, c) | |
588 | =category I/O Layers | |
589 | ||
590 | Write a single character to the stream. | |
591 | ||
592 | On success return c, on error returns EOF | |
593 | ||
594 | =cut | |
595 | */ | |
596 | ||
597 | int | |
598 | i_io_putc_imp(io_glue *ig, int c) { | |
599 | IOL_DEB(fprintf(IOL_DEBs, "i_io_putc_imp(%p, %d)\n", ig, c)); | |
600 | ||
601 | if (!ig->buffered) { | |
602 | char buf = c; | |
603 | ssize_t write_result; | |
39e938a4 | 604 | int result = c; |
6d5c85a2 TC |
605 | |
606 | if (ig->error) | |
607 | return EOF; | |
608 | ||
609 | write_result = i_io_raw_write(ig, &buf, 1); | |
6d5c85a2 TC |
610 | if (write_result != 1) { |
611 | ig->error = 1; | |
612 | result = EOF; | |
613 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered putc() failed, setting error mode\n")); | |
614 | } | |
615 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered: result %d\n", result)); | |
616 | ||
617 | return result; | |
618 | } | |
619 | ||
620 | if (ig->read_ptr) | |
621 | return EOF; | |
622 | ||
623 | if (ig->error) | |
624 | return EOF; | |
625 | ||
626 | if (!ig->buffer) | |
627 | i_io_setup_buffer(ig); | |
628 | ||
629 | if (ig->write_ptr && ig->write_ptr == ig->write_end) { | |
630 | if (!i_io_flush(ig)) | |
631 | return EOF; | |
632 | } | |
633 | ||
634 | i_io_start_write(ig); | |
635 | ||
636 | *(ig->write_ptr)++ = c; | |
637 | ||
638 | return (unsigned char)c; | |
639 | } | |
640 | ||
641 | /* | |
642 | =item i_io_read(io, buffer, size) | |
643 | =category I/O Layers | |
644 | ||
645 | Read up to C<size> bytes from the stream C<io> into C<buffer>. | |
646 | ||
647 | Returns the number of bytes read. Returns 0 on end of file. Returns | |
648 | -1 on error. | |
649 | ||
650 | =cut | |
651 | */ | |
652 | ||
653 | ssize_t | |
654 | i_io_read(io_glue *ig, void *buf, size_t size) { | |
655 | unsigned char *pbuf = buf; | |
656 | ssize_t read_total = 0; | |
657 | ||
658 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read(%p, %p, %u)\n", ig, buf, (unsigned)size)); | |
659 | ||
660 | if (ig->write_ptr) { | |
661 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => -1 (write_ptr set)\n")); | |
662 | return -1; | |
663 | } | |
664 | ||
665 | if (!ig->buffer && ig->buffered) | |
666 | i_io_setup_buffer(ig); | |
667 | ||
668 | if (ig->read_ptr && ig->read_ptr < ig->read_end) { | |
669 | size_t alloc = ig->read_end - ig->read_ptr; | |
670 | ||
671 | if (alloc > size) | |
672 | alloc = size; | |
673 | ||
674 | memcpy(pbuf, ig->read_ptr, alloc); | |
675 | ig->read_ptr += alloc; | |
676 | pbuf += alloc; | |
677 | size -= alloc; | |
678 | read_total += alloc; | |
679 | } | |
680 | ||
681 | if (size > 0 && !(ig->error || ig->buf_eof)) { | |
682 | if (!ig->buffered || size > ig->buf_size) { | |
683 | ssize_t rc; | |
684 | ||
685 | while (size > 0 && (rc = i_io_raw_read(ig, pbuf, size)) > 0) { | |
686 | size -= rc; | |
687 | pbuf += rc; | |
688 | read_total += rc; | |
689 | } | |
690 | ||
691 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => %d (raw read)\n", (int)read_total)); | |
692 | ||
693 | if (rc < 0) | |
694 | ig->error = 1; | |
695 | else if (rc == 0) | |
696 | ig->buf_eof = 1; | |
697 | ||
698 | if (!read_total) | |
699 | return rc; | |
700 | } | |
701 | else { | |
702 | if (i_io_read_fill(ig, size)) { | |
703 | size_t alloc = ig->read_end - ig->read_ptr; | |
704 | if (alloc > size) | |
705 | alloc = size; | |
706 | ||
707 | memcpy(pbuf, ig->read_ptr, alloc); | |
708 | ig->read_ptr += alloc; | |
709 | pbuf += alloc; | |
710 | size -= alloc; | |
711 | read_total += alloc; | |
712 | } | |
713 | else { | |
714 | if (!read_total && ig->error) { | |
715 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => -1 (fill failure)\n")); | |
716 | return -1; | |
717 | } | |
718 | } | |
719 | } | |
720 | } | |
721 | ||
722 | if (!read_total && ig->error) | |
723 | read_total = -1; | |
724 | ||
725 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => %d\n", (int)read_total)); | |
726 | ||
727 | return read_total; | |
728 | } | |
729 | ||
730 | /* | |
731 | =item i_io_write(io, buffer, size) | |
732 | =category I/O Layers | |
733 | =synopsis ssize_t result = i_io_write(io, buffer, size) | |
734 | ||
735 | Write to the given I/O stream. | |
736 | ||
737 | Returns the number of bytes written. | |
738 | ||
739 | =cut | |
740 | */ | |
741 | ||
742 | ssize_t | |
743 | i_io_write(io_glue *ig, const void *buf, size_t size) { | |
744 | const unsigned char *pbuf = buf; | |
745 | size_t write_count = 0; | |
746 | ||
747 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write(%p, %p, %u)\n", ig, buf, (unsigned)size)); | |
748 | ||
749 | if (!ig->buffered) { | |
750 | ssize_t result; | |
751 | ||
752 | if (ig->error) { | |
753 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, error state\n")); | |
754 | return -1; | |
755 | } | |
756 | ||
757 | result = i_io_raw_write(ig, buf, size); | |
758 | ||
759 | if (result != size) { | |
760 | ig->error = 1; | |
761 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, setting error flag\n")); | |
762 | } | |
763 | ||
764 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, result: %d\n", (int)result)); | |
765 | ||
766 | return result; | |
767 | } | |
768 | ||
769 | if (ig->read_ptr) { | |
770 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (read_ptr set)\n")); | |
771 | return -1; | |
772 | } | |
773 | ||
774 | if (ig->error) { | |
775 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (error)\n")); | |
776 | return -1; | |
777 | } | |
778 | ||
779 | if (!ig->buffer) | |
780 | i_io_setup_buffer(ig); | |
781 | ||
782 | if (!ig->write_ptr) | |
783 | i_io_start_write(ig); | |
784 | ||
785 | if (ig->write_ptr && ig->write_ptr + size <= ig->write_end) { | |
786 | size_t alloc = ig->write_end - ig->write_ptr; | |
787 | if (alloc > size) | |
788 | alloc = size; | |
789 | memcpy(ig->write_ptr, pbuf, alloc); | |
790 | write_count += alloc; | |
791 | size -= alloc; | |
792 | pbuf += alloc; | |
793 | ig->write_ptr += alloc; | |
794 | } | |
795 | ||
796 | if (size) { | |
797 | if (!i_io_flush(ig)) { | |
798 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => %d (i_io_flush failure)\n", (int)write_count)); | |
799 | return write_count ? write_count : -1; | |
800 | } | |
801 | ||
802 | i_io_start_write(ig); | |
803 | ||
804 | if (size > ig->buf_size) { | |
805 | ssize_t rc; | |
806 | while (size > 0 && (rc = i_io_raw_write(ig, pbuf, size)) > 0) { | |
807 | write_count += rc; | |
808 | pbuf += rc; | |
809 | size -= rc; | |
810 | } | |
811 | if (rc <= 0) { | |
812 | ig->error = 1; | |
813 | if (!write_count) { | |
814 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (direct write failure)\n")); | |
815 | return -1; | |
816 | } | |
817 | } | |
818 | } | |
819 | else { | |
820 | memcpy(ig->write_ptr, pbuf, size); | |
821 | write_count += size; | |
822 | ig->write_ptr += size; | |
823 | } | |
824 | } | |
825 | ||
826 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => %d\n", (int)write_count)); | |
827 | ||
828 | return write_count; | |
829 | } | |
830 | ||
831 | /* | |
832 | =item i_io_seek(io, offset, whence) | |
833 | =category I/O Layers | |
834 | ||
835 | Seek within the stream. | |
836 | ||
837 | Acts like perl's seek. | |
838 | ||
839 | =cut | |
840 | */ | |
841 | ||
842 | off_t | |
843 | i_io_seek(io_glue *ig, off_t offset, int whence) { | |
844 | off_t new_off; | |
845 | ||
846 | IOL_DEB(fprintf(IOL_DEBs, "i_io_seek(%p, %ld, %d)\n", ig, (long)offset, whence)); | |
847 | ||
848 | if (ig->write_ptr && ig->write_ptr != ig->write_end) { | |
849 | if (!i_io_flush(ig)) | |
850 | return (off_t)(-1); | |
851 | } | |
852 | ||
853 | if (whence == SEEK_CUR && ig->read_ptr && ig->read_ptr != ig->read_end) | |
854 | offset -= ig->read_end - ig->read_ptr; | |
855 | ||
856 | ig->read_ptr = ig->read_end = NULL; | |
857 | ig->write_ptr = ig->write_end = NULL; | |
858 | ig->error = 0; | |
859 | ig->buf_eof = 0; | |
860 | ||
861 | new_off = i_io_raw_seek(ig, offset, whence); | |
862 | if (new_off < 0) | |
863 | ig->error = 1; | |
864 | ||
865 | IOL_DEB(fprintf(IOL_DEBs, "i_io_seek() => %ld\n", (long)new_off)); | |
866 | ||
867 | return new_off; | |
868 | } | |
869 | ||
870 | /* | |
871 | =item i_io_flush(io) | |
872 | =category I/O Layers | |
873 | ||
874 | Flush any buffered output. | |
875 | ||
876 | Returns true on success, | |
877 | ||
878 | =cut | |
879 | */ | |
880 | ||
881 | int | |
882 | i_io_flush(io_glue *ig) { | |
883 | unsigned char *bufp; | |
884 | ||
885 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush(%p)\n", ig)); | |
886 | ||
887 | if (ig->error) { | |
888 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 0 (error set)\n", ig)); | |
889 | return 0; | |
890 | } | |
891 | ||
892 | /* nothing to do */ | |
893 | if (!ig->write_ptr) | |
894 | return 1; | |
895 | ||
896 | bufp = ig->buffer; | |
897 | while (bufp < ig->write_ptr) { | |
898 | ssize_t rc = i_io_raw_write(ig, bufp, ig->write_ptr - bufp); | |
899 | if (rc <= 0) { | |
900 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 0 (write error)\n", ig)); | |
901 | ig->error = 1; | |
902 | return 0; | |
903 | } | |
904 | ||
905 | bufp += rc; | |
906 | } | |
907 | ||
908 | ig->write_ptr = ig->write_end = NULL; | |
909 | ||
910 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 1\n", ig)); | |
911 | ||
912 | return 1; | |
913 | } | |
914 | ||
915 | /* | |
916 | =item i_io_close(io) | |
917 | =category I/O Layers | |
918 | ||
919 | Flush any pending output and perform the close action for the stream. | |
920 | ||
921 | Returns 0 on success. | |
922 | ||
923 | =cut | |
924 | */ | |
925 | ||
926 | int | |
927 | i_io_close(io_glue *ig) { | |
928 | int result = 0; | |
929 | ||
930 | IOL_DEB(fprintf(IOL_DEBs, "i_io_close(%p)\n", ig)); | |
931 | if (ig->error) | |
932 | result = -1; | |
933 | ||
934 | if (ig->write_ptr && !i_io_flush(ig)) | |
935 | result = -1; | |
936 | ||
937 | if (i_io_raw_close(ig)) | |
938 | result = -1; | |
939 | ||
940 | IOL_DEB(fprintf(IOL_DEBs, "i_io_close() => %d\n", result)); | |
941 | ||
942 | return result; | |
943 | } | |
944 | ||
945 | /* | |
946 | =item i_io_gets(ig, buffer, size, end_of_line) | |
947 | =category I/O Layers | |
948 | =synopsis char buffer[BUFSIZ] | |
949 | =synopsis ssize_t len = i_io_gets(buffer, sizeof(buffer), '\n'); | |
950 | ||
951 | Read up to C<size>-1 bytes from the stream C<ig> into C<buffer>. | |
952 | ||
953 | If the byte C<end_of_line> is seen then no further bytes will be read. | |
954 | ||
955 | Returns the number of bytes read. | |
956 | ||
957 | Always C<NUL> terminates the buffer. | |
958 | ||
959 | =cut | |
960 | */ | |
961 | ||
962 | ssize_t | |
963 | i_io_gets(io_glue *ig, char *buffer, size_t size, int eol) { | |
964 | ssize_t read_count = 0; | |
965 | if (size < 2) | |
966 | return 0; | |
967 | --size; /* room for nul */ | |
968 | while (size > 0) { | |
969 | int byte = i_io_getc(ig); | |
970 | if (byte == EOF) | |
971 | break; | |
972 | *buffer++ = byte; | |
973 | ++read_count; | |
974 | if (byte == eol) | |
975 | break; | |
976 | --size; | |
977 | } | |
978 | *buffer++ = '\0'; | |
979 | ||
980 | return read_count; | |
981 | } | |
982 | ||
983 | /* | |
984 | =item i_io_init(ig, readcb, writecb, seekcb) | |
985 | ||
986 | Do common initialization for io_glue objects. | |
987 | ||
988 | =cut | |
989 | */ | |
990 | ||
991 | static void | |
ed60e785 | 992 | i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb, |
6d5c85a2 TC |
993 | i_io_seekp_t seekcb) { |
994 | ig->type = type; | |
995 | ig->exdata = NULL; | |
996 | ig->readcb = readcb; | |
997 | ig->writecb = writecb; | |
998 | ig->seekcb = seekcb; | |
999 | ig->closecb = NULL; | |
1000 | ig->sizecb = NULL; | |
1001 | ig->destroycb = NULL; | |
ed60e785 | 1002 | ig->context = aIMCTX; |
6d5c85a2 TC |
1003 | |
1004 | ig->buffer = NULL; | |
1005 | ig->read_ptr = NULL; | |
1006 | ig->read_end = NULL; | |
1007 | ig->write_ptr = NULL; | |
1008 | ig->write_end = NULL; | |
1009 | ig->buf_size = IO_BUF_SIZE; | |
1010 | ig->buf_eof = 0; | |
1011 | ig->error = 0; | |
1012 | ig->buffered = 1; | |
1013 | } | |
1014 | ||
1015 | /* | |
1016 | =item i_io_set_buffered(io, buffered) | |
1017 | =category I/O Layers | |
1018 | ||
1019 | Set the buffering mode of the stream. | |
1020 | ||
1021 | If you switch buffering off on a stream with buffering on: | |
1022 | ||
1023 | =over | |
1024 | ||
1025 | =item * | |
1026 | ||
1027 | any buffered output will be flushed. | |
1028 | ||
1029 | =item * | |
1030 | ||
1031 | any existing buffered input will be consumed before reads become | |
1032 | unbuffered. | |
1033 | ||
1034 | =back | |
1035 | ||
1036 | Returns true on success. This may fail if any buffered output cannot | |
1037 | be flushed. | |
1038 | ||
1039 | =cut | |
1040 | */ | |
1041 | ||
1042 | int | |
1043 | i_io_set_buffered(io_glue *ig, int buffered) { | |
1044 | if (!buffered && ig->write_ptr) { | |
1045 | if (!i_io_flush(ig)) { | |
1046 | ig->error = 1; | |
1047 | return 0; | |
1048 | } | |
1049 | } | |
1050 | ig->buffered = buffered; | |
1051 | ||
1052 | return 1; | |
1053 | } | |
1054 | ||
1055 | /* | |
1056 | =item i_io_dump(ig) | |
1057 | ||
1058 | Dump the base fields of an io_glue object to stdout. | |
1059 | ||
1060 | =cut | |
1061 | */ | |
1062 | void | |
1063 | i_io_dump(io_glue *ig, int flags) { | |
1064 | fprintf(IOL_DEBs, "ig %p:\n", ig); | |
1065 | fprintf(IOL_DEBs, " type: %d\n", ig->type); | |
1066 | fprintf(IOL_DEBs, " exdata: %p\n", ig->exdata); | |
1067 | if (flags & I_IO_DUMP_CALLBACKS) { | |
1068 | fprintf(IOL_DEBs, " readcb: %p\n", ig->readcb); | |
1069 | fprintf(IOL_DEBs, " writecb: %p\n", ig->writecb); | |
1070 | fprintf(IOL_DEBs, " seekcb: %p\n", ig->seekcb); | |
1071 | fprintf(IOL_DEBs, " closecb: %p\n", ig->closecb); | |
1072 | fprintf(IOL_DEBs, " sizecb: %p\n", ig->sizecb); | |
1073 | } | |
1074 | if (flags & I_IO_DUMP_BUFFER) { | |
1075 | fprintf(IOL_DEBs, " buffer: %p\n", ig->buffer); | |
1076 | fprintf(IOL_DEBs, " read_ptr: %p\n", ig->read_ptr); | |
1077 | if (ig->read_ptr) { | |
1078 | fprintf(IOL_DEBs, " "); | |
1079 | dump_data(ig->read_ptr, ig->read_end, 0); | |
1080 | putc('\n', IOL_DEBs); | |
1081 | } | |
1082 | fprintf(IOL_DEBs, " read_end: %p\n", ig->read_end); | |
1083 | fprintf(IOL_DEBs, " write_ptr: %p\n", ig->write_ptr); | |
1084 | if (ig->write_ptr) { | |
1085 | fprintf(IOL_DEBs, " "); | |
1086 | dump_data(ig->buffer, ig->write_ptr, 1); | |
1087 | putc('\n', IOL_DEBs); | |
1088 | } | |
1089 | fprintf(IOL_DEBs, " write_end: %p\n", ig->write_end); | |
1090 | fprintf(IOL_DEBs, " buf_size: %u\n", (unsigned)(ig->buf_size)); | |
1091 | } | |
1092 | if (flags & I_IO_DUMP_STATUS) { | |
1093 | fprintf(IOL_DEBs, " buf_eof: %d\n", ig->buf_eof); | |
1094 | fprintf(IOL_DEBs, " error: %d\n", ig->error); | |
1095 | fprintf(IOL_DEBs, " buffered: %d\n", ig->buffered); | |
1096 | } | |
1097 | } | |
1098 | ||
1099 | /* | |
1100 | =back | |
1101 | ||
1102 | =head1 INTERNAL FUNCTIONS | |
1103 | ||
1104 | =over | |
1105 | ||
1106 | =item my_strerror | |
1107 | ||
1108 | Calls strerror() and ensures we don't return NULL. | |
1109 | ||
1110 | On some platforms it's possible for strerror() to return NULL, this | |
1111 | wrapper ensures we only get non-NULL values. | |
1112 | ||
1113 | =cut | |
1114 | */ | |
1115 | ||
1116 | static | |
1117 | const char *my_strerror(int err) { | |
1118 | const char *result = strerror(err); | |
1119 | ||
1120 | if (!result) | |
1121 | result = "Unknown error"; | |
1122 | ||
1123 | return result; | |
1124 | } | |
1125 | ||
1126 | static void | |
1127 | i_io_setup_buffer(io_glue *ig) { | |
1128 | ig->buffer = mymalloc(ig->buf_size); | |
1129 | } | |
1130 | ||
1131 | static void | |
1132 | i_io_start_write(io_glue *ig) { | |
1133 | ig->write_ptr = ig->buffer; | |
1134 | ig->write_end = ig->buffer + ig->buf_size; | |
1135 | } | |
1136 | ||
1137 | static int | |
1138 | i_io_read_fill(io_glue *ig, ssize_t needed) { | |
1139 | unsigned char *buf_end = ig->buffer + ig->buf_size; | |
1140 | unsigned char *buf_start = ig->buffer; | |
1141 | unsigned char *work = ig->buffer; | |
1142 | ssize_t rc; | |
1143 | int good = 0; | |
1144 | ||
1145 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%p, %d)\n", ig, (int)needed)); | |
1146 | ||
1147 | /* these conditions may be unused, callers should also be checking them */ | |
1148 | if (ig->error || ig->buf_eof) | |
1149 | return 0; | |
1150 | ||
1151 | if (needed > ig->buf_size) | |
1152 | needed = ig->buf_size; | |
1153 | ||
1154 | if (ig->read_ptr && ig->read_ptr < ig->read_end) { | |
1155 | size_t kept = ig->read_end - ig->read_ptr; | |
1156 | ||
1157 | if (needed < kept) { | |
1158 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%u) -> 1 (already have enough)\n", (unsigned)needed)); | |
1159 | return 1; | |
1160 | } | |
1161 | ||
1162 | if (ig->read_ptr != ig->buffer) | |
1163 | memmove(ig->buffer, ig->read_ptr, kept); | |
1164 | ||
1165 | good = 1; /* we have *something* available to read */ | |
1166 | work = buf_start + kept; | |
1167 | needed -= kept; | |
1168 | } | |
1169 | else { | |
1170 | work = ig->buffer; | |
1171 | } | |
1172 | ||
1173 | while (work < buf_end && (rc = i_io_raw_read(ig, work, buf_end - work)) > 0) { | |
1174 | work += rc; | |
1175 | good = 1; | |
1176 | if (needed < rc) | |
1177 | break; | |
1178 | ||
1179 | needed -= rc; | |
1180 | } | |
1181 | ||
1182 | if (rc < 0) { | |
1183 | ig->error = 1; | |
1184 | IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc %d, setting error\n", | |
1185 | (int)rc)); | |
1186 | } | |
1187 | else if (rc == 0) { | |
1188 | ig->buf_eof = 1; | |
1189 | IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc 0, setting eof\n")); | |
1190 | } | |
1191 | ||
1192 | if (good) { | |
1193 | ig->read_ptr = buf_start; | |
1194 | ig->read_end = work; | |
1195 | } | |
1196 | ||
1197 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill => %d, %u buffered\n", good, | |
1198 | (unsigned)(ig->read_end - ig->read_ptr))); | |
1199 | return good; | |
1200 | } | |
1201 | ||
1202 | /* | |
1203 | =item dump_data(start, end, bias) | |
1204 | ||
1205 | Hex dump the data between C<start> and C<end>. | |
1206 | ||
1207 | If there is more than a pleasing amount of data, either dump the | |
1208 | beginning (C<bias == 0>) or dump the end C(<bias != 0>) of the range. | |
1209 | ||
1210 | =cut | |
1211 | */ | |
1212 | ||
1213 | static void | |
1214 | dump_data(unsigned char *start, unsigned char *end, int bias) { | |
1215 | unsigned char *p; | |
1216 | size_t count = end - start; | |
1217 | ||
1218 | if (start == end) { | |
1219 | fprintf(IOL_DEBs, "(empty)"); | |
1220 | return; | |
1221 | } | |
1222 | ||
1223 | if (count > 15) { | |
1224 | if (bias) { | |
1225 | fprintf(IOL_DEBs, "... "); | |
1226 | start = end - 14; | |
1227 | } | |
1228 | else { | |
1229 | end = start + 14; | |
1230 | } | |
1231 | ||
1232 | for (p = start; p < end; ++p) { | |
1233 | fprintf(IOL_DEBs, " %02x", *p); | |
1234 | } | |
1235 | putc(' ', IOL_DEBs); | |
1236 | putc('<', IOL_DEBs); | |
1237 | for (p = start; p < end; ++p) { | |
1238 | if (*p < ' ' || *p > '~') | |
1239 | putc('.', IOL_DEBs); | |
1240 | else | |
1241 | putc(*p, IOL_DEBs); | |
1242 | } | |
1243 | putc('>', IOL_DEBs); | |
1244 | if (!bias) | |
1245 | fprintf(IOL_DEBs, " ..."); | |
1246 | } | |
1247 | else { | |
1248 | for (p = start; p < end; ++p) { | |
1249 | fprintf(IOL_DEBs, " %02x", *p); | |
1250 | } | |
1251 | putc(' ', IOL_DEBs); | |
1252 | for (p = start; p < end; ++p) { | |
1253 | if (*p < ' ' || *p > '~') | |
1254 | putc('.', IOL_DEBs); | |
1255 | else | |
1256 | putc(*p, IOL_DEBs); | |
1257 | } | |
1258 | } | |
1259 | } | |
1260 | ||
1261 | /* | |
1262 | * Callbacks for sources that cannot seek | |
1263 | */ | |
1264 | ||
1265 | /* | |
1266 | * Callbacks for sources that can seek | |
1267 | */ | |
1268 | ||
1269 | /* | |
1270 | =item realseek_read(ig, buf, count) | |
1271 | ||
1272 | Does the reading from a source that can be seeked on | |
1273 | ||
1274 | ig - io_glue object | |
1275 | buf - buffer to return data in | |
1276 | count - number of bytes to read into buffer max | |
1277 | ||
1278 | =cut | |
1279 | */ | |
1280 | ||
1281 | static | |
1282 | ssize_t | |
1283 | realseek_read(io_glue *igo, void *buf, size_t count) { | |
1284 | io_cb *ig = (io_cb *)igo; | |
1285 | void *p = ig->p; | |
1286 | ssize_t rc = 0; | |
1287 | ||
1288 | IOL_DEB( fprintf(IOL_DEBs, "realseek_read: buf = %p, count = %u\n", | |
1289 | buf, (unsigned)count) ); | |
1290 | rc = ig->readcb(p,buf,count); | |
1291 | ||
1292 | IOL_DEB( fprintf(IOL_DEBs, "realseek_read: rc = %d\n", (int)rc) ); | |
1293 | ||
1294 | return rc; | |
02d1d628 AMH |
1295 | } |
1296 | ||
1297 | ||
1298 | /* | |
1299 | =item realseek_write(ig, buf, count) | |
1300 | ||
1301 | Does the writing to a 'source' that can be seeked on | |
1302 | ||
1303 | ig - io_glue object | |
1304 | buf - buffer that contains data | |
1305 | count - number of bytes to write | |
1306 | ||
1307 | =cut | |
1308 | */ | |
1309 | ||
1310 | static | |
1311 | ssize_t | |
6d5c85a2 TC |
1312 | realseek_write(io_glue *igo, const void *buf, size_t count) { |
1313 | io_cb *ig = (io_cb *)igo; | |
1314 | void *p = ig->p; | |
02d1d628 AMH |
1315 | ssize_t rc = 0; |
1316 | size_t bc = 0; | |
1317 | char *cbuf = (char*)buf; | |
1318 | ||
6d5c85a2 TC |
1319 | IOL_DEB( fprintf(IOL_DEBs, "realseek_write: ig = %p, buf = %p, " |
1320 | "count = %u\n", ig, buf, (unsigned)count) ); | |
02d1d628 | 1321 | |
10461f9a TC |
1322 | /* Is this a good idea? Would it be better to handle differently? |
1323 | skip handling? */ | |
6d5c85a2 | 1324 | while( count!=bc && (rc = ig->writecb(p,cbuf+bc,count-bc))>0 ) { |
10461f9a TC |
1325 | bc+=rc; |
1326 | } | |
02d1d628 | 1327 | |
6d5c85a2 | 1328 | IOL_DEB( fprintf(IOL_DEBs, "realseek_write: rc = %d, bc = %u\n", (int)rc, (unsigned)bc) ); |
1f6c1c10 | 1329 | return rc < 0 ? rc : bc; |
02d1d628 AMH |
1330 | } |
1331 | ||
1332 | ||
1333 | /* | |
1334 | =item realseek_close(ig) | |
1335 | ||
10461f9a TC |
1336 | Closes a source that can be seeked on. Not sure if this should be an |
1337 | actual close or not. Does nothing for now. Should be fixed. | |
02d1d628 AMH |
1338 | |
1339 | ig - data source | |
1340 | ||
10461f9a | 1341 | =cut */ |
02d1d628 AMH |
1342 | |
1343 | static | |
2b405c9e | 1344 | int |
6d5c85a2 TC |
1345 | realseek_close(io_glue *igo) { |
1346 | io_cb *ig = (io_cb *)igo; | |
ed60e785 | 1347 | dIMCTXio(igo); |
6d5c85a2 TC |
1348 | |
1349 | IOL_DEB(fprintf(IOL_DEBs, "realseek_close(%p)\n", ig)); | |
ed60e785 | 1350 | im_log((aIMCTX,1, "realseek_close(ig %p)\n", ig)); |
6d5c85a2 TC |
1351 | if (ig->closecb) |
1352 | return ig->closecb(ig->p); | |
2b405c9e TC |
1353 | else |
1354 | return 0; | |
02d1d628 AMH |
1355 | } |
1356 | ||
1357 | ||
1358 | /* realseek_seek(ig, offset, whence) | |
1359 | ||
1360 | Implements seeking for a source that is seekable, the purpose of having this is to be able to | |
1361 | have an offset into a file that is different from what the underlying library thinks. | |
1362 | ||
1363 | ig - data source | |
1364 | offset - offset into stream | |
1365 | whence - whence argument a la lseek | |
1366 | ||
1367 | =cut | |
1368 | */ | |
1369 | ||
1370 | static | |
1371 | off_t | |
6d5c85a2 TC |
1372 | realseek_seek(io_glue *igo, off_t offset, int whence) { |
1373 | io_cb *ig = (io_cb *)igo; | |
1374 | void *p = ig->p; | |
8d14daab | 1375 | off_t rc; |
6d5c85a2 TC |
1376 | IOL_DEB( fprintf(IOL_DEBs, "realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); |
1377 | rc = ig->seekcb(p, offset, whence); | |
02d1d628 | 1378 | |
6d5c85a2 | 1379 | IOL_DEB( fprintf(IOL_DEBs, "realseek_seek: rc %ld\n", (long) rc) ); |
02d1d628 AMH |
1380 | return rc; |
1381 | /* FIXME: How about implementing this offset handling stuff? */ | |
1382 | } | |
1383 | ||
d16420e9 TC |
1384 | static |
1385 | void | |
6d5c85a2 TC |
1386 | realseek_destroy(io_glue *igo) { |
1387 | io_cb *ig = (io_cb *)igo; | |
d16420e9 | 1388 | |
6d5c85a2 TC |
1389 | if (ig->destroycb) |
1390 | ig->destroycb(ig->p); | |
d16420e9 TC |
1391 | } |
1392 | ||
4dfa5522 AMH |
1393 | /* |
1394 | * Callbacks for sources that are a fixed size buffer | |
1395 | */ | |
1396 | ||
1397 | /* | |
1398 | =item buffer_read(ig, buf, count) | |
1399 | ||
1400 | Does the reading from a buffer source | |
1401 | ||
1402 | ig - io_glue object | |
1403 | buf - buffer to return data in | |
1404 | count - number of bytes to read into buffer max | |
1405 | ||
1406 | =cut | |
1407 | */ | |
1408 | ||
1409 | static | |
1410 | ssize_t | |
6d5c85a2 TC |
1411 | buffer_read(io_glue *igo, void *buf, size_t count) { |
1412 | io_buffer *ig = (io_buffer *)igo; | |
4dfa5522 | 1413 | |
6d5c85a2 | 1414 | IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ig->cpos = %ld, buf = %p, count = %u\n", (long) ig->cpos, buf, (unsigned)count) ); |
4dfa5522 | 1415 | |
6d5c85a2 | 1416 | if ( ig->cpos+count > ig->len ) { |
ed60e785 TC |
1417 | dIMCTXio(igo); |
1418 | im_log((aIMCTX, 1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count)); | |
6d5c85a2 | 1419 | count = ig->len - ig->cpos; |
4dfa5522 AMH |
1420 | } |
1421 | ||
6d5c85a2 TC |
1422 | memcpy(buf, ig->data+ig->cpos, count); |
1423 | ig->cpos += count; | |
1424 | IOL_DEB( fprintf(IOL_DEBs, "buffer_read: count = %ld\n", (long)count) ); | |
4dfa5522 AMH |
1425 | return count; |
1426 | } | |
1427 | ||
1428 | ||
1429 | /* | |
1430 | =item buffer_write(ig, buf, count) | |
1431 | ||
1432 | Does nothing, returns -1 | |
1433 | ||
1434 | ig - io_glue object | |
1435 | buf - buffer that contains data | |
1436 | count - number of bytes to write | |
1437 | ||
1438 | =cut | |
1439 | */ | |
1440 | ||
1441 | static | |
1442 | ssize_t | |
1443 | buffer_write(io_glue *ig, const void *buf, size_t count) { | |
ed60e785 TC |
1444 | dIMCTXio(ig); |
1445 | im_log((aIMCTX, 1, "buffer_write called, this method should never be called.\n")); | |
4dfa5522 AMH |
1446 | return -1; |
1447 | } | |
1448 | ||
1449 | ||
1450 | /* | |
1451 | =item buffer_close(ig) | |
1452 | ||
1453 | Closes a source that can be seeked on. Not sure if this should be an actual close | |
1454 | or not. Does nothing for now. Should be fixed. | |
1455 | ||
1456 | ig - data source | |
1457 | ||
1458 | =cut | |
1459 | */ | |
1460 | ||
1461 | static | |
2b405c9e | 1462 | int |
4dfa5522 | 1463 | buffer_close(io_glue *ig) { |
ed60e785 TC |
1464 | dIMCTXio(ig); |
1465 | im_log((aIMCTX, 1, "buffer_close(ig %p)\n", ig)); | |
2b405c9e TC |
1466 | |
1467 | return 0; | |
4dfa5522 AMH |
1468 | } |
1469 | ||
1470 | ||
1471 | /* buffer_seek(ig, offset, whence) | |
1472 | ||
1473 | Implements seeking for a buffer source. | |
1474 | ||
1475 | ig - data source | |
1476 | offset - offset into stream | |
1477 | whence - whence argument a la lseek | |
1478 | ||
1479 | =cut | |
1480 | */ | |
1481 | ||
1482 | static | |
1483 | off_t | |
6d5c85a2 TC |
1484 | buffer_seek(io_glue *igo, off_t offset, int whence) { |
1485 | io_buffer *ig = (io_buffer *)igo; | |
eda1622c | 1486 | off_t reqpos = |
6d5c85a2 | 1487 | calc_seek_offset(ig->cpos, ig->len, offset, whence); |
4dfa5522 | 1488 | |
6d5c85a2 | 1489 | if (reqpos > ig->len) { |
ed60e785 TC |
1490 | dIMCTXio(igo); |
1491 | im_log((aIMCTX, 1, "seeking out of readable range\n")); | |
4dfa5522 AMH |
1492 | return (off_t)-1; |
1493 | } | |
eda1622c | 1494 | if (reqpos < 0) { |
ed60e785 | 1495 | dIMCTXio(igo); |
eda1622c TC |
1496 | i_push_error(0, "seek before beginning of file"); |
1497 | return (off_t)-1; | |
1498 | } | |
4dfa5522 | 1499 | |
6d5c85a2 TC |
1500 | ig->cpos = reqpos; |
1501 | IOL_DEB( fprintf(IOL_DEBs, "buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
4dfa5522 AMH |
1502 | |
1503 | return reqpos; | |
1504 | /* FIXME: How about implementing this offset handling stuff? */ | |
1505 | } | |
1506 | ||
d16420e9 TC |
1507 | static |
1508 | void | |
6d5c85a2 TC |
1509 | buffer_destroy(io_glue *igo) { |
1510 | io_buffer *ig = (io_buffer *)igo; | |
4dfa5522 | 1511 | |
6d5c85a2 | 1512 | if (ig->closecb) { |
ed60e785 TC |
1513 | dIMCTXio(igo); |
1514 | im_log((aIMCTX, 1,"calling close callback %p for io_buffer\n", | |
6d5c85a2 TC |
1515 | ig->closecb)); |
1516 | ig->closecb(ig->closedata); | |
d16420e9 | 1517 | } |
d16420e9 | 1518 | } |
4dfa5522 AMH |
1519 | |
1520 | ||
1521 | ||
02d1d628 AMH |
1522 | /* |
1523 | * Callbacks for sources that are a chain of variable sized buffers | |
1524 | */ | |
1525 | ||
1526 | ||
1527 | ||
1528 | /* Helper functions for buffer chains */ | |
1529 | ||
1530 | static | |
1531 | io_blink* | |
faa9b3e7 | 1532 | io_blink_new(void) { |
02d1d628 AMH |
1533 | io_blink *ib; |
1534 | ||
ed60e785 TC |
1535 | #if 0 |
1536 | im_log((aIMCTX, 1, "io_blink_new()\n")); | |
1537 | #endif | |
02d1d628 AMH |
1538 | |
1539 | ib = mymalloc(sizeof(io_blink)); | |
1540 | ||
1541 | ib->next = NULL; | |
1542 | ib->prev = NULL; | |
1543 | ib->len = BBSIZ; | |
1544 | ||
1545 | memset(&ib->buf, 0, ib->len); | |
1546 | return ib; | |
1547 | } | |
1548 | ||
1549 | ||
1550 | ||
1551 | /* | |
1552 | =item io_bchain_advance(ieb) | |
1553 | ||
1554 | Advances the buffer chain to the next link - extending if | |
1555 | necessary. Also adjusts the cpos and tfill counters as needed. | |
1556 | ||
1557 | ieb - buffer chain object | |
1558 | ||
1559 | =cut | |
1560 | */ | |
1561 | ||
1562 | static | |
1563 | void | |
1564 | io_bchain_advance(io_ex_bchain *ieb) { | |
1565 | if (ieb->cp->next == NULL) { | |
1566 | ieb->tail = io_blink_new(); | |
1567 | ieb->tail->prev = ieb->cp; | |
1568 | ieb->cp->next = ieb->tail; | |
1569 | ||
1570 | ieb->tfill = 0; /* Only set this if we added a new slice */ | |
1571 | } | |
1572 | ieb->cp = ieb->cp->next; | |
1573 | ieb->cpos = 0; | |
1574 | } | |
1575 | ||
1576 | ||
c3cc977e AMH |
1577 | |
1578 | /* | |
1579 | =item io_bchain_destroy() | |
1580 | ||
1581 | frees all resources used by a buffer chain. | |
1582 | ||
1583 | =cut | |
1584 | */ | |
1585 | ||
6d5c85a2 | 1586 | static void |
c3cc977e | 1587 | io_destroy_bufchain(io_ex_bchain *ieb) { |
4dfa5522 | 1588 | io_blink *cp; |
ed60e785 | 1589 | #if 0 |
4dfa5522 | 1590 | mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb)); |
ed60e785 | 1591 | #endif |
4dfa5522 AMH |
1592 | cp = ieb->head; |
1593 | ||
c3cc977e AMH |
1594 | while(cp) { |
1595 | io_blink *t = cp->next; | |
4dfa5522 | 1596 | myfree(cp); |
c3cc977e AMH |
1597 | cp = t; |
1598 | } | |
1599 | } | |
1600 | ||
1601 | ||
1602 | ||
1603 | ||
02d1d628 AMH |
1604 | /* |
1605 | ||
1606 | static | |
1607 | void | |
1608 | bufchain_dump(io_ex_bchain *ieb) { | |
1609 | mm_log((1, " buf_chain_dump(ieb %p)\n")); | |
1610 | mm_log((1, " buf_chain_dump: ieb->offset = %d\n", ieb->offset)); | |
1611 | mm_log((1, " buf_chain_dump: ieb->length = %d\n", ieb->length)); | |
1612 | mm_log((1, " buf_chain_dump: ieb->head = %p\n", ieb->head )); | |
1613 | mm_log((1, " buf_chain_dump: ieb->tail = %p\n", ieb->tail )); | |
1614 | mm_log((1, " buf_chain_dump: ieb->tfill = %d\n", ieb->tfill )); | |
1615 | mm_log((1, " buf_chain_dump: ieb->cp = %p\n", ieb->cp )); | |
1616 | mm_log((1, " buf_chain_dump: ieb->cpos = %d\n", ieb->cpos )); | |
1617 | mm_log((1, " buf_chain_dump: ieb->gpos = %d\n", ieb->gpos )); | |
1618 | } | |
1619 | */ | |
1620 | ||
1621 | /* | |
1622 | * TRUE if lengths are NOT equal | |
1623 | */ | |
1624 | ||
1625 | /* | |
1626 | static | |
1627 | void | |
1628 | chainlencert( io_glue *ig ) { | |
1629 | int clen; | |
1630 | int cfl = 0; | |
1631 | size_t csize = 0; | |
1632 | size_t cpos = 0; | |
1633 | io_ex_bchain *ieb = ig->exdata; | |
1634 | io_blink *cp = ieb->head; | |
1635 | ||
1636 | ||
1637 | if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length)); | |
1638 | ||
1639 | while(cp) { | |
1640 | clen = (cp == ieb->tail) ? ieb->tfill : cp->len; | |
1641 | if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n")); | |
1642 | if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n")); | |
1643 | ||
1644 | if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n")); | |
1645 | if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n")); | |
1646 | ||
1647 | if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next)); | |
1648 | if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev)); | |
1649 | ||
1650 | if (cp == ieb->cp) { | |
1651 | cfl = 1; | |
1652 | cpos += ieb->cpos; | |
1653 | } | |
1654 | ||
1655 | if (!cfl) cpos += clen; | |
1656 | ||
1657 | csize += clen; | |
1658 | cp = cp->next; | |
1659 | } | |
1660 | if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length)); | |
1661 | if (( cpos != ieb->gpos )) mm_log((1, "BAR : cpos = %d, ieb->gpos = %d\n", cpos, ieb->gpos )); | |
1662 | } | |
1663 | ||
1664 | ||
1665 | static | |
1666 | void | |
1667 | chaincert( io_glue *ig) { | |
1668 | size_t csize = 0; | |
1669 | io_ex_bchain *ieb = ig->exdata; | |
1670 | io_blink *cp = ieb->head; | |
1671 | ||
1672 | mm_log((1, "Chain verification.\n")); | |
1673 | ||
1674 | mm_log((1, " buf_chain_dump: ieb->offset = %d\n", ieb->offset)); | |
1675 | mm_log((1, " buf_chain_dump: ieb->length = %d\n", ieb->length)); | |
1676 | mm_log((1, " buf_chain_dump: ieb->head = %p\n", ieb->head )); | |
1677 | mm_log((1, " buf_chain_dump: ieb->tail = %p\n", ieb->tail )); | |
1678 | mm_log((1, " buf_chain_dump: ieb->tfill = %d\n", ieb->tfill )); | |
1679 | mm_log((1, " buf_chain_dump: ieb->cp = %p\n", ieb->cp )); | |
1680 | mm_log((1, " buf_chain_dump: ieb->cpos = %d\n", ieb->cpos )); | |
1681 | mm_log((1, " buf_chain_dump: ieb->gpos = %d\n", ieb->gpos )); | |
1682 | ||
1683 | while(cp) { | |
1684 | int clen = cp == ieb->tail ? ieb->tfill : cp->len; | |
1685 | mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next)); | |
1686 | if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n")); | |
1687 | if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n")); | |
1688 | ||
1689 | if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n")); | |
1690 | if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n")); | |
1691 | ||
1692 | if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next)); | |
1693 | if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev)); | |
1694 | ||
1695 | csize += clen; | |
1696 | cp = cp->next; | |
1697 | } | |
1698 | ||
1699 | mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length)); | |
1700 | } | |
1701 | */ | |
1702 | ||
02d1d628 AMH |
1703 | /* |
1704 | =item bufchain_read(ig, buf, count) | |
1705 | ||
1706 | Does the reading from a source that can be seeked on | |
1707 | ||
1708 | ig - io_glue object | |
1709 | buf - buffer to return data in | |
1710 | count - number of bytes to read into buffer max | |
1711 | ||
1712 | =cut | |
1713 | */ | |
1714 | ||
1715 | static | |
1716 | ssize_t | |
1717 | bufchain_read(io_glue *ig, void *buf, size_t count) { | |
1718 | io_ex_bchain *ieb = ig->exdata; | |
1719 | size_t scount = count; | |
1720 | char *cbuf = buf; | |
1721 | size_t sk; | |
ed60e785 | 1722 | dIMCTXio(ig); |
02d1d628 | 1723 | |
ed60e785 | 1724 | im_log((aIMCTX, 1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count)); |
02d1d628 AMH |
1725 | |
1726 | while( scount ) { | |
1727 | int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
1728 | if (clen == ieb->cpos) { | |
1729 | if (ieb->cp == ieb->tail) break; /* EOF */ | |
1730 | ieb->cp = ieb->cp->next; | |
1731 | ieb->cpos = 0; | |
1732 | clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
1733 | } | |
1734 | ||
1735 | sk = clen - ieb->cpos; | |
1736 | sk = sk > scount ? scount : sk; | |
1737 | ||
1738 | memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk); | |
1739 | scount -= sk; | |
1740 | ieb->cpos += sk; | |
1741 | ieb->gpos += sk; | |
1742 | } | |
1743 | ||
ed60e785 | 1744 | im_log((aIMCTX, 1, "bufchain_read: returning %ld\n", (long)(count-scount))); |
02d1d628 AMH |
1745 | return count-scount; |
1746 | } | |
1747 | ||
1748 | ||
1749 | ||
1750 | ||
1751 | ||
1752 | /* | |
1753 | =item bufchain_write(ig, buf, count) | |
1754 | ||
1755 | Does the writing to a 'source' that can be seeked on | |
1756 | ||
1757 | ig - io_glue object | |
1758 | buf - buffer that contains data | |
1759 | count - number of bytes to write | |
1760 | ||
1761 | =cut | |
1762 | */ | |
1763 | ||
1764 | static | |
1765 | ssize_t | |
1766 | bufchain_write(io_glue *ig, const void *buf, size_t count) { | |
1767 | char *cbuf = (char *)buf; | |
1768 | io_ex_bchain *ieb = ig->exdata; | |
1769 | size_t ocount = count; | |
1770 | size_t sk; | |
ed60e785 | 1771 | dIMCTXio(ig); |
02d1d628 | 1772 | |
ed60e785 | 1773 | im_log((aIMCTX, 1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count)); |
02d1d628 | 1774 | |
6d5c85a2 | 1775 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) ); |
02d1d628 AMH |
1776 | |
1777 | while(count) { | |
ed60e785 | 1778 | im_log((aIMCTX, 2, "bufchain_write: - looping - count = %ld\n", (long)count)); |
02d1d628 | 1779 | if (ieb->cp->len == ieb->cpos) { |
ed60e785 | 1780 | im_log((aIMCTX, 1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos)); |
02d1d628 AMH |
1781 | io_bchain_advance(ieb); |
1782 | } | |
1783 | ||
1784 | sk = ieb->cp->len - ieb->cpos; | |
1785 | sk = sk > count ? count : sk; | |
1786 | memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk); | |
1787 | ||
1788 | if (ieb->cp == ieb->tail) { | |
1789 | int extend = ieb->cpos + sk - ieb->tfill; | |
ed60e785 | 1790 | im_log((aIMCTX, 2, "bufchain_write: extending tail by %d\n", extend)); |
02d1d628 AMH |
1791 | if (extend > 0) { |
1792 | ieb->length += extend; | |
1793 | ieb->tfill += extend; | |
1794 | } | |
1795 | } | |
1796 | ||
1797 | ieb->cpos += sk; | |
1798 | ieb->gpos += sk; | |
1799 | count -= sk; | |
1800 | } | |
1801 | return ocount; | |
1802 | } | |
1803 | ||
1804 | /* | |
1805 | =item bufchain_close(ig) | |
1806 | ||
1807 | Closes a source that can be seeked on. Not sure if this should be an actual close | |
1808 | or not. Does nothing for now. Should be fixed. | |
1809 | ||
1810 | ig - data source | |
1811 | ||
1812 | =cut | |
1813 | */ | |
1814 | ||
1815 | static | |
2b405c9e | 1816 | int |
02d1d628 | 1817 | bufchain_close(io_glue *ig) { |
ed60e785 TC |
1818 | dIMCTXio(ig); |
1819 | im_log((aIMCTX, 1, "bufchain_close(ig %p)\n",ig)); | |
6d5c85a2 | 1820 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_close(ig %p)\n", ig) ); |
2b405c9e TC |
1821 | |
1822 | return 0; | |
02d1d628 AMH |
1823 | } |
1824 | ||
1825 | ||
1826 | /* bufchain_seek(ig, offset, whence) | |
1827 | ||
1828 | Implements seeking for a source that is seekable, the purpose of having this is to be able to | |
1829 | have an offset into a file that is different from what the underlying library thinks. | |
1830 | ||
1831 | ig - data source | |
1832 | offset - offset into stream | |
1833 | whence - whence argument a la lseek | |
1834 | ||
1835 | =cut | |
1836 | */ | |
1837 | ||
1838 | static | |
1839 | off_t | |
1840 | bufchain_seek(io_glue *ig, off_t offset, int whence) { | |
1841 | io_ex_bchain *ieb = ig->exdata; | |
02d1d628 AMH |
1842 | int wrlen; |
1843 | ||
eda1622c | 1844 | off_t scount = calc_seek_offset(ieb->gpos, ieb->length, offset, whence); |
02d1d628 | 1845 | off_t sk; |
ed60e785 | 1846 | dIMCTXio(ig); |
02d1d628 | 1847 | |
ed60e785 | 1848 | im_log((aIMCTX, 1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence)); |
02d1d628 | 1849 | |
eda1622c TC |
1850 | if (scount < 0) { |
1851 | i_push_error(0, "invalid whence supplied or seek before start of file"); | |
1852 | return (off_t)-1; | |
1853 | } | |
02d1d628 | 1854 | |
eda1622c TC |
1855 | ieb->cp = ieb->head; |
1856 | ieb->cpos = 0; | |
1857 | ieb->gpos = 0; | |
1858 | ||
1859 | while( scount ) { | |
1860 | int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
1861 | if (clen == ieb->cpos) { | |
1862 | if (ieb->cp == ieb->tail) break; /* EOF */ | |
1863 | ieb->cp = ieb->cp->next; | |
1864 | ieb->cpos = 0; | |
1865 | clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; | |
02d1d628 AMH |
1866 | } |
1867 | ||
eda1622c TC |
1868 | sk = clen - ieb->cpos; |
1869 | sk = sk > scount ? scount : sk; | |
02d1d628 | 1870 | |
eda1622c TC |
1871 | scount -= sk; |
1872 | ieb->cpos += sk; | |
1873 | ieb->gpos += sk; | |
1874 | } | |
1875 | ||
1876 | wrlen = scount; | |
02d1d628 | 1877 | |
eda1622c TC |
1878 | if (wrlen > 0) { |
1879 | /* | |
1880 | * extending file - get ieb into consistent state and then | |
1881 | * call write which will get it to the correct position | |
1882 | */ | |
1883 | char TB[BBSIZ]; | |
1884 | memset(TB, 0, BBSIZ); | |
1885 | ieb->gpos = ieb->length; | |
02d1d628 AMH |
1886 | ieb->cpos = ieb->tfill; |
1887 | ||
eda1622c TC |
1888 | while(wrlen > 0) { |
1889 | ssize_t rc, wl = i_min(wrlen, BBSIZ); | |
ed60e785 | 1890 | im_log((aIMCTX, 1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl)); |
eda1622c | 1891 | rc = bufchain_write( ig, TB, wl ); |
2038f6ce | 1892 | if (rc != wl) im_fatal(aIMCTX, 0, "bufchain_seek: Unable to extend file\n"); |
eda1622c | 1893 | wrlen -= rc; |
02d1d628 | 1894 | } |
02d1d628 AMH |
1895 | } |
1896 | ||
ed60e785 | 1897 | im_log((aIMCTX, 2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos)); |
02d1d628 AMH |
1898 | return ieb->gpos; |
1899 | } | |
1900 | ||
d16420e9 TC |
1901 | static |
1902 | void | |
1903 | bufchain_destroy(io_glue *ig) { | |
1904 | io_ex_bchain *ieb = ig->exdata; | |
02d1d628 | 1905 | |
d16420e9 | 1906 | io_destroy_bufchain(ieb); |
02d1d628 | 1907 | |
d16420e9 TC |
1908 | myfree(ieb); |
1909 | } | |
02d1d628 | 1910 | |
10461f9a TC |
1911 | /* |
1912 | =item fd_read(ig, buf, count) | |
1913 | ||
6d5c85a2 TC |
1914 | Read callback for file descriptor IO objects. |
1915 | ||
10461f9a TC |
1916 | =cut |
1917 | */ | |
6d5c85a2 TC |
1918 | static ssize_t fd_read(io_glue *igo, void *buf, size_t count) { |
1919 | io_fdseek *ig = (io_fdseek *)igo; | |
5f8f8e17 | 1920 | ssize_t result; |
10461f9a | 1921 | #ifdef _MSC_VER |
6d5c85a2 | 1922 | result = _read(ig->fd, buf, count); |
10461f9a | 1923 | #else |
6d5c85a2 | 1924 | result = read(ig->fd, buf, count); |
10461f9a | 1925 | #endif |
5f8f8e17 | 1926 | |
6d5c85a2 TC |
1927 | IOL_DEB(fprintf(IOL_DEBs, "fd_read(%p, %p, %u) => %d\n", ig, buf, |
1928 | (unsigned)count, (int)result)); | |
1929 | ||
5f8f8e17 TC |
1930 | /* 0 is valid - means EOF */ |
1931 | if (result < 0) { | |
ed60e785 TC |
1932 | dIMCTXio(igo); |
1933 | im_push_errorf(aIMCTX, 0, "read() failure: %s (%d)", my_strerror(errno), errno); | |
5f8f8e17 TC |
1934 | } |
1935 | ||
1936 | return result; | |
10461f9a TC |
1937 | } |
1938 | ||
6d5c85a2 TC |
1939 | static ssize_t fd_write(io_glue *igo, const void *buf, size_t count) { |
1940 | io_fdseek *ig = (io_fdseek *)igo; | |
2691d220 | 1941 | ssize_t result; |
10461f9a | 1942 | #ifdef _MSC_VER |
6d5c85a2 | 1943 | result = _write(ig->fd, buf, count); |
10461f9a | 1944 | #else |
6d5c85a2 | 1945 | result = write(ig->fd, buf, count); |
10461f9a | 1946 | #endif |
2691d220 | 1947 | |
6d5c85a2 TC |
1948 | IOL_DEB(fprintf(IOL_DEBs, "fd_write(%p, %p, %u) => %d\n", ig, buf, |
1949 | (unsigned)count, (int)result)); | |
1950 | ||
2691d220 | 1951 | if (result <= 0) { |
ed60e785 TC |
1952 | dIMCTXio(igo); |
1953 | im_push_errorf(aIMCTX, errno, "write() failure: %s (%d)", my_strerror(errno), errno); | |
2691d220 TC |
1954 | } |
1955 | ||
1956 | return result; | |
10461f9a TC |
1957 | } |
1958 | ||
6d5c85a2 TC |
1959 | static off_t fd_seek(io_glue *igo, off_t offset, int whence) { |
1960 | io_fdseek *ig = (io_fdseek *)igo; | |
2691d220 | 1961 | off_t result; |
10461f9a | 1962 | #ifdef _MSC_VER |
6d5c85a2 | 1963 | result = _lseek(ig->fd, offset, whence); |
10461f9a | 1964 | #else |
6d5c85a2 | 1965 | result = lseek(ig->fd, offset, whence); |
10461f9a | 1966 | #endif |
2691d220 TC |
1967 | |
1968 | if (result == (off_t)-1) { | |
ed60e785 TC |
1969 | dIMCTXio(igo); |
1970 | im_push_errorf(aIMCTX, errno, "lseek() failure: %s (%d)", my_strerror(errno), errno); | |
2691d220 TC |
1971 | } |
1972 | ||
1973 | return result; | |
10461f9a TC |
1974 | } |
1975 | ||
2b405c9e | 1976 | static int fd_close(io_glue *ig) { |
10461f9a | 1977 | /* no, we don't close it */ |
2b405c9e | 1978 | return 0; |
10461f9a TC |
1979 | } |
1980 | ||
1981 | static ssize_t fd_size(io_glue *ig) { | |
ed60e785 TC |
1982 | dIMCTXio(ig); |
1983 | im_log((aIMCTX, 1, "fd_size(ig %p) unimplemented\n", ig)); | |
10461f9a TC |
1984 | |
1985 | return -1; | |
1986 | } | |
02d1d628 | 1987 | |
b8c2033e AMH |
1988 | |
1989 | /* | |
1990 | =back | |
1991 | ||
1992 | =head1 AUTHOR | |
1993 | ||
1994 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
1995 | ||
1996 | =head1 SEE ALSO | |
1997 | ||
1998 | Imager(3) | |
1999 | ||
2000 | =cut | |
2001 | */ |