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