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