Initial revision
[imager.git] / iolayer.c
1 #include "io.h"
2 #include "iolayer.h"
3 #include "log.h"
4 #include <stdlib.h>
5 #include <stdio.h>
6 #ifdef _MSC_VER
7 #include <io.h>
8 #endif
9
10 #define IOL_DEB(x)
11
12
13 char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" };
14
15
16 /*
17 =head1 NAME
18
19 iolayer.c - encapsulates different source of data into a single framework.
20
21 =head1 SYNOPSIS
22
23   io_glue *ig = io_new_fd( fileno(stdin) );
24   method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet
25   io_glue_commit_types(ig);                     // always assume IOL_SEEK for now
26   switch (method) {
27   case IOL_NOSEEK:
28     code that uses ig->readcb()
29     to read data goes here.
30     break;
31   case IOL_MMAP:
32     code that uses ig->readcb()
33     to read data goes here.
34     break;
35   }  
36
37   io_glue_DESTROY(ig);
38   // and much more
39
40 =head1 DESCRIPTION
41
42 iolayer.c implements the basic functions to create and destroy io_glue
43 objects for Imager.  The typical usage pattern for data sources is:
44
45    1. Create the source (io_new_fd)
46    2. Define how you want to get data from it (io_reqmeth)
47    3. read from it using the interface requested (ig->readdb, ig->mmapcb)
48    4. Close the source, which 
49       shouldn't really close the underlying source. (io_glue DESTROY)
50
51 =head1 FUNCTION REFERENCE
52
53 Some of these functions are internal.
54
55 =over 4
56
57 =cut
58 */
59
60
61
62
63 /*
64  * Callbacks for sources that cannot seek
65  */
66
67 /* fakeseek_read: read method for when emulating a seekable source
68 static
69 ssize_t
70 fakeseek_read(io_glue *ig, void *buf, size_t count) {
71   io_ex_fseek *exdata = ig->exdata; 
72   return 0;
73 }
74 */
75
76
77
78 /*
79  * Callbacks for sources that can seek 
80  */
81
82 /*
83 =item realseek_read(ig, buf, count)
84
85 Does the reading from a source that can be seeked on
86
87    ig    - io_glue object
88    buf   - buffer to return data in
89    count - number of bytes to read into buffer max
90
91 =cut
92 */
93
94 static
95 ssize_t 
96 realseek_read(io_glue *ig, void *buf, size_t count) {
97   io_ex_rseek *ier = ig->exdata;
98   int fd           = (int)ig->source.cb.p;
99   ssize_t       rc = 0;
100   size_t        bc = 0;
101   char       *cbuf = buf;
102
103   IOL_DEB( printf("realseek_read: fd = %d, ier->cpos = %ld, buf = 0x%p, count = %d\n", fd, (long) ier->cpos, buf, count) );
104   /* Is this a good idea? Would it be better to handle differently? skip handling? */
105   while( count!=bc && (rc = ig->source.cb.readcb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
106   
107   ier->cpos += bc;
108   IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) );
109   return bc;
110 }
111
112
113 /*
114 =item realseek_write(ig, buf, count)
115
116 Does the writing to a 'source' that can be seeked on
117
118    ig    - io_glue object
119    buf   - buffer that contains data
120    count - number of bytes to write
121
122 =cut
123 */
124
125 static
126 ssize_t 
127 realseek_write(io_glue *ig, const void *buf, size_t count) {
128   io_ex_rseek *ier = ig->exdata;
129   int           fd = (int)ig->source.cb.p;
130   ssize_t       rc = 0;
131   size_t        bc = 0;
132   char       *cbuf = (char*)buf; 
133   
134   IOL_DEB( printf("realseek_write: fd = %d, ier->cpos = %ld, buf = 0x%p, count = %d\n", fd, (long) ier->cpos, buf, count) );
135   /* Is this a good idea? Would it be better to handle differently? skip handling? */
136
137   while( count!=bc && (rc = ig->source.cb.writecb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
138
139   ier->cpos += bc;
140   IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) );
141   return bc;
142 }
143
144
145 /*
146 =item realseek_close(ig)
147
148 Closes a source that can be seeked on.  Not sure if this should be an actual close
149 or not.  Does nothing for now.  Should be fixed.
150
151    ig - data source
152
153 =cut
154 */
155
156 static
157 void
158 realseek_close(io_glue *ig) {
159   mm_log((1, "realseek_close(ig %p)\n", ig));
160   /* FIXME: Do stuff here */
161 }
162
163
164 /* realseek_seek(ig, offset, whence)
165
166 Implements seeking for a source that is seekable, the purpose of having this is to be able to
167 have an offset into a file that is different from what the underlying library thinks.
168
169    ig     - data source
170    offset - offset into stream
171    whence - whence argument a la lseek
172
173 =cut
174 */
175
176 static
177 off_t
178 realseek_seek(io_glue *ig, off_t offset, int whence) {
179   /*  io_ex_rseek *ier = ig->exdata; Needed later */
180   int fd           = (int)ig->source.cb.p;
181   int rc;
182   IOL_DEB( printf("realseek_seek(ig 0x%p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
183   rc = lseek(fd, offset, whence);
184
185   IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) );
186   return rc;
187   /* FIXME: How about implementing this offset handling stuff? */
188 }
189
190
191
192
193
194
195
196 /*
197  * Callbacks for sources that are a chain of variable sized buffers
198  */
199
200
201
202 /* Helper functions for buffer chains */
203
204 static
205 io_blink*
206 io_blink_new() {
207   io_blink *ib;
208
209   mm_log((1, "io_blink_new()\n"));
210
211   ib = mymalloc(sizeof(io_blink));
212
213   ib->next = NULL;
214   ib->prev = NULL;
215   ib->len  = BBSIZ;
216
217   memset(&ib->buf, 0, ib->len);
218   return ib;
219 }
220
221
222
223 /*
224 =item io_bchain_advance(ieb)
225
226 Advances the buffer chain to the next link - extending if
227 necessary.  Also adjusts the cpos and tfill counters as needed.
228
229    ieb   - buffer chain object
230
231 =cut
232 */
233
234 static
235 void
236 io_bchain_advance(io_ex_bchain *ieb) {
237   if (ieb->cp->next == NULL) {
238     ieb->tail = io_blink_new();
239     ieb->tail->prev = ieb->cp;
240     ieb->cp->next   = ieb->tail;
241
242     ieb->tfill = 0; /* Only set this if we added a new slice */
243   }
244   ieb->cp    = ieb->cp->next;
245   ieb->cpos  = 0;
246 }
247
248
249 /*
250
251 static
252 void
253 bufchain_dump(io_ex_bchain *ieb) {
254   mm_log((1, "  buf_chain_dump(ieb %p)\n"));
255   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
256   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
257   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
258   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
259   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
260   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
261   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
262   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
263 }
264 */
265
266 /*
267  * TRUE if lengths are NOT equal
268  */
269
270 /*
271 static
272 void
273 chainlencert( io_glue *ig ) {
274   int clen;
275   int cfl           = 0;
276   size_t csize      = 0;
277   size_t cpos       = 0;
278   io_ex_bchain *ieb = ig->exdata;
279   io_blink *cp      = ieb->head;
280   
281
282   if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length));
283
284   while(cp) {
285     clen = (cp == ieb->tail) ? ieb->tfill : cp->len;
286     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
287     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
288     
289     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
290     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
291     
292     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
293     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
294
295     if (cp == ieb->cp) {
296       cfl = 1;
297       cpos += ieb->cpos;
298     }
299
300     if (!cfl) cpos += clen;
301
302     csize += clen;
303     cp     = cp->next;
304   }
305   if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length));
306   if (( cpos  != ieb->gpos   )) mm_log((1, "BAR : cpos  = %d, ieb->gpos   = %d\n", cpos,  ieb->gpos  ));
307 }
308
309
310 static
311 void
312 chaincert( io_glue *ig) {
313   size_t csize   = 0;
314   io_ex_bchain *ieb = ig->exdata;
315   io_blink *cp   = ieb->head;
316   
317   mm_log((1, "Chain verification.\n"));
318
319   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
320   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
321   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
322   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
323   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
324   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
325   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
326   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
327
328   while(cp) {
329     int clen = cp == ieb->tail ? ieb->tfill : cp->len;
330     mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next));
331     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
332     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
333     
334     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
335     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
336     
337     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
338     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
339
340     csize += clen;
341     cp     = cp->next;
342   }
343
344   mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length));
345 }
346 */
347
348
349
350
351
352
353
354
355
356
357
358 /*
359 =item bufchain_read(ig, buf, count)
360
361 Does the reading from a source that can be seeked on
362
363    ig    - io_glue object
364    buf   - buffer to return data in
365    count - number of bytes to read into buffer max
366
367 =cut
368 */
369
370 static
371 ssize_t 
372 bufchain_read(io_glue *ig, void *buf, size_t count) {
373   io_ex_bchain *ieb = ig->exdata;
374   size_t     scount = count;
375   char        *cbuf = buf;
376   size_t         sk;
377
378   mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, count));
379   IOL_DEB( printf("bufchain_read: fd = %d, ier->cpos = %ld, buf = 0x%p, count = %d\n", fd, (long) ier->cpos, buf, count) );
380
381   while( scount ) {
382     int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
383     if (clen == ieb->cpos) {
384       if (ieb->cp == ieb->tail) break; /* EOF */
385       ieb->cp = ieb->cp->next;
386       ieb->cpos = 0;
387       clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
388     }
389
390     sk = clen - ieb->cpos;
391     sk = sk > scount ? scount : sk;
392
393     memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
394     scount    -= sk;
395     ieb->cpos += sk;
396     ieb->gpos += sk;
397   }
398
399   mm_log((1, "bufchain_read: returning %d\n", count-scount));
400   return count-scount;
401 }
402
403
404
405
406
407 /*
408 =item bufchain_write(ig, buf, count)
409
410 Does the writing to a 'source' that can be seeked on
411
412    ig    - io_glue object
413    buf   - buffer that contains data
414    count - number of bytes to write
415
416 =cut
417 */
418
419 static
420 ssize_t
421 bufchain_write(io_glue *ig, const void *buf, size_t count) {
422   char *cbuf = (char *)buf;
423   io_ex_bchain *ieb = ig->exdata;
424   size_t         ocount = count;
425   size_t         sk;
426
427   mm_log((1, "bufchain_write: ig = %p, buf = 0x%p, count = %d\n", ig, buf, count));
428
429   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = 0x%p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
430   
431   while(count) {
432     mm_log((2, "bufchain_write: - looping - count = %d\n", count));
433     if (ieb->cp->len == ieb->cpos) {
434       mm_log((1, "bufchain_write: cp->len == ieb->cpos = %d - advancing chain\n", (long) ieb->cpos));
435       io_bchain_advance(ieb);
436     }
437
438     sk = ieb->cp->len - ieb->cpos;
439     sk = sk > count ? count : sk;
440     memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
441
442     if (ieb->cp == ieb->tail) {
443       int extend = ieb->cpos + sk - ieb->tfill;
444       mm_log((2, "bufchain_write: extending tail by %d\n", extend));
445       if (extend > 0) {
446         ieb->length += extend;
447         ieb->tfill  += extend;
448       }
449     }
450
451     ieb->cpos += sk;
452     ieb->gpos += sk;
453     count     -= sk;
454   }
455   return ocount;
456 }
457
458 /*
459 =item bufchain_close(ig)
460
461 Closes a source that can be seeked on.  Not sure if this should be an actual close
462 or not.  Does nothing for now.  Should be fixed.
463
464    ig - data source
465
466 =cut
467 */
468
469 static
470 void
471 bufchain_close(io_glue *ig) {
472   mm_log((1, "bufchain_close(ig %p)\n",ig));
473   IOL_DEB( printf("bufchain_close(ig 0x%p)\n", ig) );
474   /* FIXME: Commit a seek point here */
475   
476 }
477
478
479 /* bufchain_seek(ig, offset, whence)
480
481 Implements seeking for a source that is seekable, the purpose of having this is to be able to
482 have an offset into a file that is different from what the underlying library thinks.
483
484    ig     - data source
485    offset - offset into stream
486    whence - whence argument a la lseek
487
488 =cut
489 */
490
491 static
492 off_t
493 bufchain_seek(io_glue *ig, off_t offset, int whence) {
494   io_ex_bchain *ieb = ig->exdata;
495   io_blink *ib      = NULL;
496   int wrlen;
497
498   off_t cof = 0;
499   off_t scount = offset;
500   off_t sk;
501
502   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
503
504   switch (whence) {
505   case SEEK_SET: /* SEEK_SET = 0, From the top */
506     ieb->cp   = ieb->head;
507     ieb->cpos = 0;
508     ieb->gpos = 0;
509
510     while( scount ) {
511       int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
512       if (clen == ieb->cpos) {
513         if (ieb->cp == ieb->tail) break; /* EOF */
514         ieb->cp = ieb->cp->next;
515         ieb->cpos = 0;
516         clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
517       }
518       
519       sk = clen - ieb->cpos;
520       sk = sk > scount ? scount : sk;
521       
522       scount    -= sk;
523       ieb->cpos += sk;
524       ieb->gpos += sk;
525     }
526
527     wrlen = scount;
528
529     if (wrlen > 0) { 
530       /*
531        * extending file - get ieb into consistent state and then
532        * call write which will get it to the correct position 
533        */
534       char TB[BBSIZ];
535       memset(TB, 0, BBSIZ);
536       ieb->gpos = ieb->length;
537       ieb->cpos = ieb->tfill;
538
539       while(wrlen > 0) {
540         ssize_t rc, wl = min(wrlen, BBSIZ);
541         mm_log((1, "bufchain_seek: wrlen = %d, wl = %d\n", wrlen, wl));
542         rc = bufchain_write( ig, TB, wl );
543         if (rc != wl) m_fatal(0, "bufchain_seek: Unable to extend file\n");
544         wrlen -= rc;
545       }
546     }
547     
548     break;
549
550   case SEEK_CUR:
551     m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
552
553     /*
554       case SEEK_CUR: 
555       ib = ieb->cp;
556       if (cof < 0) {
557       cof += ib->cpos;
558       cpos = 0;
559       while(cof < 0 && ib->prev) {
560       ib = ib->prev;
561       cof += ib->len;
562       }
563     */
564     
565   case SEEK_END: /* SEEK_END = 2 */
566     if (cof>0) m_fatal(0, "bufchain_seek: SEEK_END + %d : Extending files via seek not supported!\n", cof);
567
568     ieb->cp   = ieb->tail;
569     ieb->cpos = ieb->tfill;
570     
571     if (cof<0) {
572       cof      += ieb->cpos;
573       ieb->cpos = 0;
574
575       while(cof<0 && ib->prev) {
576         ib   = ib->prev;
577         cof += ib->len;
578       }
579     
580       if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
581       ieb->gpos = ieb->length+offset;
582       ieb->cpos = cof;
583     }
584     break;
585   default:
586     m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
587   }
588
589   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
590   return ieb->gpos;
591 }
592
593
594
595
596
597
598 /*
599  * Methods for setting up data source
600  */
601
602 /*
603 =item io_obj_setp_buffer(io, p, len)
604
605 Sets an io_object for reading from a buffer source
606
607    io  - io object that describes a source
608    p   - pointer to buffer
609    len - length of buffer
610
611 =cut
612 */
613
614 void
615 io_obj_setp_buffer(io_obj *io, void *p, size_t len) {
616   io->buffer.type = BUFFER;
617   io->buffer.c    = (char*) p;
618   io->buffer.len  = len;
619 }
620
621
622 /*
623 =item io_obj_setp_cbuf(io, p)
624
625 Sets an io_object for reading/writing from a buffer source
626
627    io  - io object that describes a source
628    p   - pointer to buffer
629    len - length of buffer
630
631 =cut
632 */
633
634 void
635 io_obj_setp_bufchain(io_obj *io) {
636   io->type = BUFCHAIN;
637 }
638
639
640 /*
641 =item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
642
643 Sets an io_object for reading from a source that uses callbacks
644
645    io      - io object that describes a source
646    p       - pointer to data for callbacks
647    readcb  - read callback to read from source
648    writecb - write callback to write to source
649    seekcb  - seek callback to seek on source
650
651 =cut
652 */
653
654 void
655 io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
656   io->cb.type    = CBSEEK;
657   io->cb.p       = p;
658   io->cb.readcb  = readcb;
659   io->cb.writecb = writecb;
660   io->cb.seekcb  = seekcb;
661 }
662
663 /*
664 =item io_glue_commit_types(ig)
665
666 Creates buffers and initializes structures to read with the chosen interface.
667
668    ig - io_glue object
669
670 =cut
671 */
672
673 void
674 io_glue_commit_types(io_glue *ig) {
675   io_type      inn = ig->source.type;
676   
677   mm_log((1, "io_glue_commit_types(ig 0x%p)\n", ig));
678   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
679   
680   switch (inn) {
681   case BUFCHAIN:
682     {
683       io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
684       
685       ieb->offset = 0;
686       ieb->length = 0;
687       ieb->cpos   = 0;
688       ieb->gpos   = 0;
689       ieb->tfill  = 0;
690       
691       ieb->head   = io_blink_new();
692       ieb->cp     = ieb->head;
693       ieb->tail   = ieb->head;
694
695       ig->exdata  = ieb;
696       ig->readcb  = bufchain_read;
697       ig->writecb = bufchain_write;
698       ig->seekcb  = bufchain_seek;
699       ig->closecb = bufchain_close;
700     }
701     break;
702   case CBSEEK:
703   default:
704     {
705       io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
706       
707       ier->offset = 0;
708       ier->cpos   = 0;
709       
710       ig->exdata  = ier;
711       ig->readcb  = realseek_read;
712       ig->writecb = realseek_write;
713       ig->seekcb  = realseek_seek;
714       ig->closecb = realseek_close;
715     }
716   }
717 }
718
719 /*
720 =item io_glue_gettypes(ig, reqmeth)
721
722 Returns a set of compatible interfaces to read data with.
723
724   ig      - io_glue object
725   reqmeth - request mask
726
727 The request mask is a bit mask (of something that hasn't been implemented yet)
728 of interfaces that it would like to read data from the source which the ig
729 describes.
730
731 =cut
732 */
733
734 void
735 io_glue_gettypes(io_glue *ig, int reqmeth) {
736
737   ig = NULL;
738   reqmeth = 0;
739   
740   /* FIXME: Implement this function! */
741   /* if (ig->source.type = 
742      if (reqmeth & IO_BUFF) */ 
743
744 }
745
746
747 /*
748 =item io_new_bufchain()
749
750 returns a new io_glue object that has the 'empty' source and but can
751 be written to and read from later (like a pseudo file).
752
753 =cut
754 */
755
756 io_glue *
757 io_new_bufchain() {
758   io_glue *ig = mymalloc(sizeof(io_glue));
759   io_obj_setp_bufchain(&ig->source);
760   return ig;
761 }
762
763
764 /*
765 =item io_new_fd(fd)
766
767 returns a new io_glue object that has the source defined as reading
768 from specified filedescriptor.  Note that the the interface to recieving
769 data from the io_glue callbacks hasn't been done yet.
770
771    fd - file descriptor to read/write from
772
773 =cut
774 */
775
776 io_glue *
777 io_new_fd(int fd) {
778   io_glue *ig = mymalloc(sizeof(io_glue));
779 #ifdef _MSC_VER
780   io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
781 #else
782   io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
783 #endif
784   return ig;
785 }
786
787
788
789 /*
790 =item io_slurp(ig)
791
792 Takes the source that the io_glue is bound to and allocates space
793 for a return buffer and returns the entire content in a single buffer.
794 Note: This only works for io_glue objects that contain a bufchain.  It
795 is usefull for saving to scalars and such.
796
797    ig - io_glue object
798    c  - pointer to a pointer to where data should be copied to
799
800 =cut
801 */
802
803 size_t
804 io_slurp(io_glue *ig, unsigned char **c) {
805   ssize_t rc;
806   off_t orgoff;
807   io_ex_bchain *ieb;
808   unsigned char *cc;
809   io_type inn = ig->source.type;
810   
811   if ( inn != BUFCHAIN ) {
812     m_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
813   }
814
815   ieb = ig->exdata;
816   cc = *c = mymalloc( ieb->length );
817   
818   orgoff = ieb->gpos;
819   
820   bufchain_seek(ig, 0, SEEK_SET);
821   
822   rc = bufchain_read(ig, cc, ieb->length);
823
824   if (rc != ieb->length)
825     m_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
826
827   return rc;
828 }
829
830
831 /*
832 =item io_glue_DESTROY(ig)
833
834 A destructor method for io_glue objects.  Should clean up all related buffers.
835 Might leave us with a dangling pointer issue on some buffers.
836
837    ig - io_glue object to destroy.
838
839 =cut
840 */
841
842 void
843 io_glue_DESTROY(io_glue *ig) {
844   free(ig);
845   /* FIXME: Handle extradata and such */
846 }