long delayed renaming of m_fatal() to i_fatal() to match Imager's
[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 int 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:  buf = %p, count = %d\n", 
161                   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 int
222 realseek_close(io_glue *ig) {
223   mm_log((1, "realseek_close(ig %p)\n", ig));
224   if (ig->source.cb.closecb)
225     return ig->source.cb.closecb(ig->source.cb.p);
226   else
227     return 0;
228 }
229
230
231 /* realseek_seek(ig, offset, whence)
232
233 Implements seeking for a source that is seekable, the purpose of having this is to be able to
234 have 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
243 static
244 off_t
245 realseek_seek(io_glue *ig, off_t offset, int whence) {
246   /*  io_ex_rseek *ier = ig->exdata; Needed later */
247   void *p = ig->source.cb.p;
248   int rc;
249   IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
250   rc = ig->source.cb.seekcb(p, offset, whence);
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
257 static
258 void
259 realseek_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
268 /*
269  * Callbacks for sources that are a fixed size buffer
270  */
271
272 /*
273 =item buffer_read(ig, buf, count)
274
275 Does 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
284 static
285 ssize_t 
286 buffer_read(io_glue *ig, void *buf, size_t count) {
287   io_ex_buffer *ieb = ig->exdata;
288
289   IOL_DEB( printf("buffer_read: ieb->cpos = %ld, buf = %p, count = %d\n", (long) ieb->cpos, buf, count) );
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;
298   IOL_DEB( printf("buffer_read: count = %d\n", count) );
299   return count;
300 }
301
302
303 /*
304 =item buffer_write(ig, buf, count)
305
306 Does 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
315 static
316 ssize_t 
317 buffer_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
326 Closes a source that can be seeked on.  Not sure if this should be an actual close
327 or not.  Does nothing for now.  Should be fixed.
328
329    ig - data source
330
331 =cut
332 */
333
334 static
335 int
336 buffer_close(io_glue *ig) {
337   mm_log((1, "buffer_close(ig %p)\n", ig));
338
339   return 0;
340 }
341
342
343 /* buffer_seek(ig, offset, whence)
344
345 Implements 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
354 static
355 off_t
356 buffer_seek(io_glue *ig, off_t offset, int whence) {
357   io_ex_buffer *ieb = ig->exdata;
358   off_t reqpos = 
359     calc_seek_offset(ieb->cpos, ig->source.buffer.len, offset, whence);
360   
361   if (reqpos > ig->source.buffer.len) {
362     mm_log((1, "seeking out of readable range\n"));
363     return (off_t)-1;
364   }
365   if (reqpos < 0) {
366     i_push_error(0, "seek before beginning of file");
367     return (off_t)-1;
368   }
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
377 static
378 void
379 buffer_destroy(io_glue *ig) {
380   io_ex_buffer *ieb = ig->exdata;
381
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 }
389
390
391
392 /*
393  * Callbacks for sources that are a chain of variable sized buffers
394  */
395
396
397
398 /* Helper functions for buffer chains */
399
400 static
401 io_blink*
402 io_blink_new(void) {
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
422 Advances the buffer chain to the next link - extending if
423 necessary.  Also adjusts the cpos and tfill counters as needed.
424
425    ieb   - buffer chain object
426
427 =cut
428 */
429
430 static
431 void
432 io_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
445
446 /*
447 =item io_bchain_destroy()
448
449 frees all resources used by a buffer chain.
450
451 =cut
452 */
453
454 void
455 io_destroy_bufchain(io_ex_bchain *ieb) {
456   io_blink *cp;
457   mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb));
458   cp = ieb->head;
459   
460   while(cp) {
461     io_blink *t = cp->next;
462     myfree(cp);
463     cp = t;
464   }
465 }
466
467
468
469
470 /*
471
472 static
473 void
474 bufchain_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 /*
492 static
493 void
494 chainlencert( 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
531 static
532 void
533 chaincert( 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
582 Does 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
591 static
592 ssize_t 
593 bufchain_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));
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
630 Does 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
639 static
640 ssize_t
641 bufchain_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
647   mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
648
649   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
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
681 Closes a source that can be seeked on.  Not sure if this should be an actual close
682 or not.  Does nothing for now.  Should be fixed.
683
684    ig - data source
685
686 =cut
687 */
688
689 static
690 int
691 bufchain_close(io_glue *ig) {
692   mm_log((1, "bufchain_close(ig %p)\n",ig));
693   IOL_DEB( printf("bufchain_close(ig %p)\n", ig) );
694
695   return 0;  
696 }
697
698
699 /* bufchain_seek(ig, offset, whence)
700
701 Implements seeking for a source that is seekable, the purpose of having this is to be able to
702 have 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
711 static
712 off_t
713 bufchain_seek(io_glue *ig, off_t offset, int whence) {
714   io_ex_bchain *ieb = ig->exdata;
715   int wrlen;
716
717   off_t scount = calc_seek_offset(ieb->gpos, ieb->length, offset, whence);
718   off_t sk;
719
720   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
721
722   if (scount < 0) {
723     i_push_error(0, "invalid whence supplied or seek before start of file");
724     return (off_t)-1;
725   }
726
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;
738     }
739     
740     sk = clen - ieb->cpos;
741     sk = sk > scount ? scount : sk;
742     
743     scount    -= sk;
744     ieb->cpos += sk;
745     ieb->gpos += sk;
746   }
747   
748   wrlen = scount;
749
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;
758     ieb->cpos = ieb->tfill;
759     
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 );
764       if (rc != wl) i_fatal(0, "bufchain_seek: Unable to extend file\n");
765       wrlen -= rc;
766     }
767   }
768
769   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
770   return ieb->gpos;
771 }
772
773 static
774 void
775 bufchain_destroy(io_glue *ig) {
776   io_ex_bchain *ieb = ig->exdata;
777
778   io_destroy_bufchain(ieb);
779
780   myfree(ieb);
781 }
782
783 /*
784  * Methods for setting up data source
785  */
786
787 /*
788 =item io_obj_setp_buffer(io, p, len)
789
790 Sets 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
799 static void
800 io_obj_setp_buffer(io_obj *io, char *p, size_t len, i_io_closebufp_t closecb, 
801                    void *closedata) {
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;
807 }
808
809
810
811 /*
812 =item io_obj_setp_cb2(io, p, readcb, writecb, seekcb, closecb, destroycb)
813
814 Sets an io_object for reading from a source that uses callbacks
815
816    io      - io object that describes a source
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
823
824 =cut
825 */
826
827 static void
828 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) {
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
838 /*
839 =item io_glue_commit_types(ig)
840
841 This is now effectively a no-op.
842
843 =cut
844 */
845
846 void
847 io_glue_commit_types(io_glue *ig) {
848   io_type      inn = ig->source.type;
849
850   mm_log((1, "io_glue_commit_types(ig %p)\n", ig));
851   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
852
853   if (ig->flags & 0x01) {
854     mm_log((1, "io_glue_commit_types: type already set up\n"));
855     return;
856   }
857
858   ig->flags |= 0x01; /* indicate source has been setup already */
859 }
860
861 /*
862 =item io_glue_gettypes(ig, reqmeth)
863
864 Returns a set of compatible interfaces to read data with.
865
866   ig      - io_glue object
867   reqmeth - request mask
868
869 The request mask is a bit mask (of something that hasn't been implemented yet)
870 of interfaces that it would like to read data from the source which the ig
871 describes.
872
873 =cut
874 */
875
876 void
877 io_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
892 returns a new io_glue object that has the 'empty' source and but can
893 be written to and read from later (like a pseudo file).
894
895 =cut
896 */
897
898 io_glue *
899 io_new_bufchain() {
900   io_glue *ig;
901   io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
902
903   mm_log((1, "io_new_bufchain()\n"));
904
905   ig = mymalloc(sizeof(io_glue));
906   memset(ig, 0, sizeof(*ig));
907   ig->source.type = BUFCHAIN;
908
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   
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;
925
926   return ig;
927 }
928
929 /*
930 =item io_new_buffer(data, len)
931
932 Returns a new io_glue object that has the source defined as reading
933 from specified buffer.  Note that the buffer is not copied.
934
935    data - buffer to read from
936    len - length of buffer
937
938 =cut
939 */
940
941 io_glue *
942 io_new_buffer(char *data, size_t len, i_io_closebufp_t closecb, void *closedata) {
943   io_glue *ig;
944   io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer));
945   
946   mm_log((1, "io_new_buffer(data %p, len %d, closecb %p, closedata %p)\n", data, len, closecb, closedata));
947
948   ig = mymalloc(sizeof(io_glue));
949   memset(ig, 0, sizeof(*ig));
950   io_obj_setp_buffer(&ig->source, data, len, closecb, closedata);
951   ig->flags = 0;
952
953   ieb->offset = 0;
954   ieb->cpos   = 0;
955   
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;
962
963   return ig;
964 }
965
966
967 /*
968 =item io_new_fd(fd)
969
970 returns a new io_glue object that has the source defined as reading
971 from specified filedescriptor.  Note that the the interface to recieving
972 data from the io_glue callbacks hasn't been done yet.
973
974    fd - file descriptor to read/write from
975
976 =cut
977 */
978
979 io_glue *
980 io_new_fd(int fd) {
981   io_glue *ig;
982
983   mm_log((1, "io_new_fd(fd %d)\n", fd));
984
985   ig = mymalloc(sizeof(io_glue));
986   memset(ig, 0, sizeof(*ig));
987   ig->source.type = FDSEEK;
988   ig->source.fdseek.fd = fd;
989   ig->flags = 0;
990
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;
998       
999   mm_log((1, "(%p) <- io_new_fd\n", ig));
1000   return ig;
1001 }
1002
1003 io_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) {
1006   io_glue *ig;
1007   io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
1008
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));
1012   memset(ig, 0, sizeof(*ig));
1013   io_obj_setp_cb2(&ig->source, p, readcb, writecb, seekcb, closecb, destroycb);
1014   mm_log((1, "(%p) <- io_new_cb\n", ig));
1015
1016   ier->offset = 0;
1017   ier->cpos   = 0;
1018   
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;
1025
1026   return ig;
1027 }
1028
1029 /*
1030 =item io_slurp(ig)
1031
1032 Takes the source that the io_glue is bound to and allocates space
1033 for a return buffer and returns the entire content in a single buffer.
1034 Note: This only works for io_glue objects that contain a bufchain.  It
1035 is 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
1043 size_t
1044 io_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 ) {
1052     i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
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)
1065     i_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
1066
1067   return rc;
1068 }
1069
1070 /*
1071 =item fd_read(ig, buf, count)
1072
1073 =cut
1074 */
1075 static ssize_t fd_read(io_glue *ig, void *buf, size_t count) {
1076   ssize_t result;
1077 #ifdef _MSC_VER
1078   result = _read(ig->source.fdseek.fd, buf, count);
1079 #else
1080   result = read(ig->source.fdseek.fd, buf, count);
1081 #endif
1082
1083   /* 0 is valid - means EOF */
1084   if (result < 0) {
1085     i_push_errorf(0, "read() failure: %s (%d)", my_strerror(errno), errno);
1086   }
1087
1088   return result;
1089 }
1090
1091 static ssize_t fd_write(io_glue *ig, const void *buf, size_t count) {
1092   ssize_t result;
1093 #ifdef _MSC_VER
1094   result = _write(ig->source.fdseek.fd, buf, count);
1095 #else
1096   result = write(ig->source.fdseek.fd, buf, count);
1097 #endif
1098
1099   if (result <= 0) {
1100     i_push_errorf(errno, "write() failure: %s (%d)", my_strerror(errno), errno);
1101   }
1102
1103   return result;
1104 }
1105
1106 static off_t fd_seek(io_glue *ig, off_t offset, int whence) {
1107   off_t result;
1108 #ifdef _MSC_VER
1109   result = _lseek(ig->source.fdseek.fd, offset, whence);
1110 #else
1111   result = lseek(ig->source.fdseek.fd, offset, whence);
1112 #endif
1113
1114   if (result == (off_t)-1) {
1115     i_push_errorf(errno, "lseek() failure: %s (%d)", my_strerror(errno), errno);
1116   }
1117
1118   return result;
1119 }
1120
1121 static int fd_close(io_glue *ig) {
1122   /* no, we don't close it */
1123   return 0;
1124 }
1125
1126 static ssize_t fd_size(io_glue *ig) {
1127   mm_log((1, "fd_size(ig %p) unimplemented\n", ig));
1128   
1129   return -1;
1130 }
1131
1132 /*
1133 =item io_glue_destroy(ig)
1134
1135 A destructor method for io_glue objects.  Should clean up all related buffers.
1136 Might leave us with a dangling pointer issue on some buffers.
1137
1138    ig - io_glue object to destroy.
1139
1140 =cut
1141 */
1142
1143 void
1144 io_glue_destroy(io_glue *ig) {
1145   mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
1146
1147   if (ig->destroycb)
1148     ig->destroycb(ig);
1149   
1150   myfree(ig);
1151 }
1152
1153 /*
1154 =back
1155
1156 =head1 INTERNAL FUNCTIONS
1157
1158 =over
1159
1160 =item my_strerror
1161
1162 Calls strerror() and ensures we don't return NULL.
1163
1164 On some platforms it's possible for strerror() to return NULL, this
1165 wrapper ensures we only get non-NULL values.
1166
1167 =cut
1168 */
1169
1170 static
1171 const char *my_strerror(int err) {
1172   const char *result = strerror(err);
1173
1174   if (!result)
1175     result = "Unknown error";
1176
1177   return result;
1178 }
1179
1180 /*
1181 =back
1182
1183 =head1 AUTHOR
1184
1185 Arnar M. Hrafnkelsson <addi@umich.edu>
1186
1187 =head1 SEE ALSO
1188
1189 Imager(3)
1190
1191 =cut
1192 */