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