Egads
[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 = %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 = %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 %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(void) {
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
380   while( scount ) {
381     int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
382     if (clen == ieb->cpos) {
383       if (ieb->cp == ieb->tail) break; /* EOF */
384       ieb->cp = ieb->cp->next;
385       ieb->cpos = 0;
386       clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
387     }
388
389     sk = clen - ieb->cpos;
390     sk = sk > scount ? scount : sk;
391
392     memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
393     scount    -= sk;
394     ieb->cpos += sk;
395     ieb->gpos += sk;
396   }
397
398   mm_log((1, "bufchain_read: returning %d\n", count-scount));
399   return count-scount;
400 }
401
402
403
404
405
406 /*
407 =item bufchain_write(ig, buf, count)
408
409 Does the writing to a 'source' that can be seeked on
410
411    ig    - io_glue object
412    buf   - buffer that contains data
413    count - number of bytes to write
414
415 =cut
416 */
417
418 static
419 ssize_t
420 bufchain_write(io_glue *ig, const void *buf, size_t count) {
421   char *cbuf = (char *)buf;
422   io_ex_bchain *ieb = ig->exdata;
423   size_t         ocount = count;
424   size_t         sk;
425
426   mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
427
428   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
429   
430   while(count) {
431     mm_log((2, "bufchain_write: - looping - count = %d\n", count));
432     if (ieb->cp->len == ieb->cpos) {
433       mm_log((1, "bufchain_write: cp->len == ieb->cpos = %d - advancing chain\n", (long) ieb->cpos));
434       io_bchain_advance(ieb);
435     }
436
437     sk = ieb->cp->len - ieb->cpos;
438     sk = sk > count ? count : sk;
439     memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
440
441     if (ieb->cp == ieb->tail) {
442       int extend = ieb->cpos + sk - ieb->tfill;
443       mm_log((2, "bufchain_write: extending tail by %d\n", extend));
444       if (extend > 0) {
445         ieb->length += extend;
446         ieb->tfill  += extend;
447       }
448     }
449
450     ieb->cpos += sk;
451     ieb->gpos += sk;
452     count     -= sk;
453   }
454   return ocount;
455 }
456
457 /*
458 =item bufchain_close(ig)
459
460 Closes a source that can be seeked on.  Not sure if this should be an actual close
461 or not.  Does nothing for now.  Should be fixed.
462
463    ig - data source
464
465 =cut
466 */
467
468 static
469 void
470 bufchain_close(io_glue *ig) {
471   mm_log((1, "bufchain_close(ig %p)\n",ig));
472   IOL_DEB( printf("bufchain_close(ig %p)\n", ig) );
473   /* FIXME: Commit a seek point here */
474   
475 }
476
477
478 /* bufchain_seek(ig, offset, whence)
479
480 Implements seeking for a source that is seekable, the purpose of having this is to be able to
481 have an offset into a file that is different from what the underlying library thinks.
482
483    ig     - data source
484    offset - offset into stream
485    whence - whence argument a la lseek
486
487 =cut
488 */
489
490 static
491 off_t
492 bufchain_seek(io_glue *ig, off_t offset, int whence) {
493   io_ex_bchain *ieb = ig->exdata;
494   io_blink *ib      = NULL;
495   int wrlen;
496
497   off_t cof = 0;
498   off_t scount = offset;
499   off_t sk;
500
501   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
502
503   switch (whence) {
504   case SEEK_SET: /* SEEK_SET = 0, From the top */
505     ieb->cp   = ieb->head;
506     ieb->cpos = 0;
507     ieb->gpos = 0;
508
509     while( scount ) {
510       int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
511       if (clen == ieb->cpos) {
512         if (ieb->cp == ieb->tail) break; /* EOF */
513         ieb->cp = ieb->cp->next;
514         ieb->cpos = 0;
515         clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
516       }
517       
518       sk = clen - ieb->cpos;
519       sk = sk > scount ? scount : sk;
520       
521       scount    -= sk;
522       ieb->cpos += sk;
523       ieb->gpos += sk;
524     }
525
526     wrlen = scount;
527
528     if (wrlen > 0) { 
529       /*
530        * extending file - get ieb into consistent state and then
531        * call write which will get it to the correct position 
532        */
533       char TB[BBSIZ];
534       memset(TB, 0, BBSIZ);
535       ieb->gpos = ieb->length;
536       ieb->cpos = ieb->tfill;
537
538       while(wrlen > 0) {
539         ssize_t rc, wl = min(wrlen, BBSIZ);
540         mm_log((1, "bufchain_seek: wrlen = %d, wl = %d\n", wrlen, wl));
541         rc = bufchain_write( ig, TB, wl );
542         if (rc != wl) m_fatal(0, "bufchain_seek: Unable to extend file\n");
543         wrlen -= rc;
544       }
545     }
546     
547     break;
548
549   case SEEK_CUR:
550     m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
551
552     /*
553       case SEEK_CUR: 
554       ib = ieb->cp;
555       if (cof < 0) {
556       cof += ib->cpos;
557       cpos = 0;
558       while(cof < 0 && ib->prev) {
559       ib = ib->prev;
560       cof += ib->len;
561       }
562     */
563     
564   case SEEK_END: /* SEEK_END = 2 */
565     if (cof>0) m_fatal(0, "bufchain_seek: SEEK_END + %d : Extending files via seek not supported!\n", cof);
566
567     ieb->cp   = ieb->tail;
568     ieb->cpos = ieb->tfill;
569     
570     if (cof<0) {
571       cof      += ieb->cpos;
572       ieb->cpos = 0;
573
574       while(cof<0 && ib->prev) {
575         ib   = ib->prev;
576         cof += ib->len;
577       }
578     
579       if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
580       ieb->gpos = ieb->length+offset;
581       ieb->cpos = cof;
582     }
583     break;
584   default:
585     m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
586   }
587
588   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
589   return ieb->gpos;
590 }
591
592
593
594
595
596
597 /*
598  * Methods for setting up data source
599  */
600
601 /*
602 =item io_obj_setp_buffer(io, p, len)
603
604 Sets an io_object for reading from a buffer source
605
606    io  - io object that describes a source
607    p   - pointer to buffer
608    len - length of buffer
609
610 =cut
611 */
612
613 void
614 io_obj_setp_buffer(io_obj *io, void *p, size_t len) {
615   io->buffer.type = BUFFER;
616   io->buffer.c    = (char*) p;
617   io->buffer.len  = len;
618 }
619
620
621 /*
622 =item io_obj_setp_cbuf(io, p)
623
624 Sets an io_object for reading/writing from a buffer source
625
626    io  - io object that describes a source
627    p   - pointer to buffer
628    len - length of buffer
629
630 =cut
631 */
632
633 void
634 io_obj_setp_bufchain(io_obj *io) {
635   io->type = BUFCHAIN;
636 }
637
638
639 /*
640 =item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
641
642 Sets an io_object for reading from a source that uses callbacks
643
644    io      - io object that describes a source
645    p       - pointer to data for callbacks
646    readcb  - read callback to read from source
647    writecb - write callback to write to source
648    seekcb  - seek callback to seek on source
649
650 =cut
651 */
652
653 void
654 io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
655   io->cb.type    = CBSEEK;
656   io->cb.p       = p;
657   io->cb.readcb  = readcb;
658   io->cb.writecb = writecb;
659   io->cb.seekcb  = seekcb;
660 }
661
662 /*
663 =item io_glue_commit_types(ig)
664
665 Creates buffers and initializes structures to read with the chosen interface.
666
667    ig - io_glue object
668
669 =cut
670 */
671
672 void
673 io_glue_commit_types(io_glue *ig) {
674   io_type      inn = ig->source.type;
675   
676   mm_log((1, "io_glue_commit_types(ig %p)\n", ig));
677   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
678   
679   switch (inn) {
680   case BUFCHAIN:
681     {
682       io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
683       
684       ieb->offset = 0;
685       ieb->length = 0;
686       ieb->cpos   = 0;
687       ieb->gpos   = 0;
688       ieb->tfill  = 0;
689       
690       ieb->head   = io_blink_new();
691       ieb->cp     = ieb->head;
692       ieb->tail   = ieb->head;
693
694       ig->exdata  = ieb;
695       ig->readcb  = bufchain_read;
696       ig->writecb = bufchain_write;
697       ig->seekcb  = bufchain_seek;
698       ig->closecb = bufchain_close;
699     }
700     break;
701   case CBSEEK:
702   default:
703     {
704       io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
705       
706       ier->offset = 0;
707       ier->cpos   = 0;
708       
709       ig->exdata  = ier;
710       ig->readcb  = realseek_read;
711       ig->writecb = realseek_write;
712       ig->seekcb  = realseek_seek;
713       ig->closecb = realseek_close;
714     }
715   }
716 }
717
718 /*
719 =item io_glue_gettypes(ig, reqmeth)
720
721 Returns a set of compatible interfaces to read data with.
722
723   ig      - io_glue object
724   reqmeth - request mask
725
726 The request mask is a bit mask (of something that hasn't been implemented yet)
727 of interfaces that it would like to read data from the source which the ig
728 describes.
729
730 =cut
731 */
732
733 void
734 io_glue_gettypes(io_glue *ig, int reqmeth) {
735
736   ig = NULL;
737   reqmeth = 0;
738   
739   /* FIXME: Implement this function! */
740   /* if (ig->source.type = 
741      if (reqmeth & IO_BUFF) */ 
742
743 }
744
745
746 /*
747 =item io_new_bufchain()
748
749 returns a new io_glue object that has the 'empty' source and but can
750 be written to and read from later (like a pseudo file).
751
752 =cut
753 */
754
755 io_glue *
756 io_new_bufchain() {
757   io_glue *ig = mymalloc(sizeof(io_glue));
758   io_obj_setp_bufchain(&ig->source);
759   return ig;
760 }
761
762
763 /*
764 =item io_new_fd(fd)
765
766 returns a new io_glue object that has the source defined as reading
767 from specified filedescriptor.  Note that the the interface to recieving
768 data from the io_glue callbacks hasn't been done yet.
769
770    fd - file descriptor to read/write from
771
772 =cut
773 */
774
775 io_glue *
776 io_new_fd(int fd) {
777   io_glue *ig = mymalloc(sizeof(io_glue));
778 #ifdef _MSC_VER
779   io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
780 #else
781   io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
782 #endif
783   return ig;
784 }
785
786
787
788 /*
789 =item io_slurp(ig)
790
791 Takes the source that the io_glue is bound to and allocates space
792 for a return buffer and returns the entire content in a single buffer.
793 Note: This only works for io_glue objects that contain a bufchain.  It
794 is usefull for saving to scalars and such.
795
796    ig - io_glue object
797    c  - pointer to a pointer to where data should be copied to
798
799 =cut
800 */
801
802 size_t
803 io_slurp(io_glue *ig, unsigned char **c) {
804   ssize_t rc;
805   off_t orgoff;
806   io_ex_bchain *ieb;
807   unsigned char *cc;
808   io_type inn = ig->source.type;
809   
810   if ( inn != BUFCHAIN ) {
811     m_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
812   }
813
814   ieb = ig->exdata;
815   cc = *c = mymalloc( ieb->length );
816   
817   orgoff = ieb->gpos;
818   
819   bufchain_seek(ig, 0, SEEK_SET);
820   
821   rc = bufchain_read(ig, cc, ieb->length);
822
823   if (rc != ieb->length)
824     m_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
825
826   return rc;
827 }
828
829
830 /*
831 =item io_glue_DESTROY(ig)
832
833 A destructor method for io_glue objects.  Should clean up all related buffers.
834 Might leave us with a dangling pointer issue on some buffers.
835
836    ig - io_glue object to destroy.
837
838 =cut
839 */
840
841 void
842 io_glue_DESTROY(io_glue *ig) {
843   free(ig);
844   /* FIXME: Handle extradata and such */
845 }