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