iolayer modifications:
[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, closebufp closecb, void *closedata);
61 static void io_obj_setp_cb2     (io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb, closel closecb, destroyl destroycb);
62
63
64 /*
65 =head1 NAME
66
67 iolayer.c - encapsulates different source of data into a single framework.
68
69 =head1 SYNOPSIS
70
71   io_glue *ig = io_new_fd( fileno(stdin) );
72   method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet
73   io_glue_commit_types(ig);                     // always assume IOL_SEEK for now
74   switch (method) {
75   case IOL_NOSEEK:
76     code that uses ig->readcb()
77     to read data goes here.
78     break;
79   case IOL_MMAP:
80     code that uses ig->readcb()
81     to read data goes here.
82     break;
83   }  
84
85   io_glue_DESTROY(ig);
86   // and much more
87
88 =head1 DESCRIPTION
89
90 iolayer.c implements the basic functions to create and destroy io_glue
91 objects for Imager.  The typical usage pattern for data sources is:
92
93    1. Create the source (io_new_fd)
94    2. Define how you want to get data from it (io_reqmeth)
95    3. read from it using the interface requested (ig->readdb, ig->mmapcb)
96    4. Close the source, which 
97       shouldn't really close the underlying source. (io_glue DESTROY)
98
99 =head1 FUNCTION REFERENCE
100
101 Some of these functions are internal.
102
103 =over
104
105 =cut
106 */
107
108 static ssize_t fd_read(io_glue *ig, void *buf, size_t count);
109 static ssize_t fd_write(io_glue *ig, const void *buf, size_t count);
110 static off_t fd_seek(io_glue *ig, off_t offset, int whence);
111 static void fd_close(io_glue *ig);
112 static ssize_t fd_size(io_glue *ig);
113 static const char *my_strerror(int err);
114
115 /*
116  * Callbacks for sources that cannot seek
117  */
118
119 /* fakeseek_read: read method for when emulating a seekable source
120 static
121 ssize_t
122 fakeseek_read(io_glue *ig, void *buf, size_t count) {
123   io_ex_fseek *exdata = ig->exdata; 
124   return 0;
125 }
126 */
127
128
129
130 /*
131  * Callbacks for sources that can seek 
132  */
133
134 /*
135 =item realseek_read(ig, buf, count)
136
137 Does the reading from a source that can be seeked on
138
139    ig    - io_glue object
140    buf   - buffer to return data in
141    count - number of bytes to read into buffer max
142
143 =cut
144 */
145
146 static
147 ssize_t 
148 realseek_read(io_glue *ig, void *buf, size_t count) {
149   io_ex_rseek *ier = ig->exdata;
150   void *p          = ig->source.cb.p;
151   ssize_t       rc = 0;
152   size_t        bc = 0;
153   char       *cbuf = buf;
154
155   IOL_DEB( printf("realseek_read: fd = %d, ier->cpos = %ld, buf = %p, "
156                   "count = %d\n", fd, (long) ier->cpos, buf, count) );
157   /* Is this a good idea? Would it be better to handle differently?
158      skip handling? */
159   while( count!=bc && (rc = ig->source.cb.readcb(p,cbuf+bc,count-bc))>0 ) {
160     bc+=rc;
161   }
162   
163   ier->cpos += bc;
164   IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) );
165   return bc;
166 }
167
168
169 /*
170 =item realseek_write(ig, buf, count)
171
172 Does the writing to a 'source' that can be seeked on
173
174    ig    - io_glue object
175    buf   - buffer that contains data
176    count - number of bytes to write
177
178 =cut
179 */
180
181 static
182 ssize_t 
183 realseek_write(io_glue *ig, const void *buf, size_t count) {
184   io_ex_rseek *ier = ig->exdata;
185   void          *p = ig->source.cb.p;
186   ssize_t       rc = 0;
187   size_t        bc = 0;
188   char       *cbuf = (char*)buf; 
189   
190   IOL_DEB( printf("realseek_write: ig = %p, ier->cpos = %ld, buf = %p, "
191                   "count = %d\n", ig, (long) ier->cpos, buf, count) );
192
193   /* Is this a good idea? Would it be better to handle differently? 
194      skip handling? */
195   while( count!=bc && (rc = ig->source.cb.writecb(p,cbuf+bc,count-bc))>0 ) {
196     bc+=rc;
197   }
198
199   ier->cpos += bc;
200   IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) );
201   return bc;
202 }
203
204
205 /*
206 =item realseek_close(ig)
207
208 Closes a source that can be seeked on.  Not sure if this should be an
209 actual close or not.  Does nothing for now.  Should be fixed.
210
211    ig - data source
212
213 =cut */
214
215 static
216 void
217 realseek_close(io_glue *ig) {
218   mm_log((1, "realseek_close(ig %p)\n", ig));
219   if (ig->source.cb.closecb)
220     ig->source.cb.closecb(ig->source.cb.p);
221 }
222
223
224 /* realseek_seek(ig, offset, whence)
225
226 Implements seeking for a source that is seekable, the purpose of having this is to be able to
227 have an offset into a file that is different from what the underlying library thinks.
228
229    ig     - data source
230    offset - offset into stream
231    whence - whence argument a la lseek
232
233 =cut
234 */
235
236 static
237 off_t
238 realseek_seek(io_glue *ig, off_t offset, int whence) {
239   /*  io_ex_rseek *ier = ig->exdata; Needed later */
240   void *p = ig->source.cb.p;
241   int rc;
242   IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
243   rc = ig->source.cb.seekcb(p, offset, whence);
244
245   IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) );
246   return rc;
247   /* FIXME: How about implementing this offset handling stuff? */
248 }
249
250 /*
251  * Callbacks for sources that are a fixed size buffer
252  */
253
254 /*
255 =item buffer_read(ig, buf, count)
256
257 Does the reading from a buffer source
258
259    ig    - io_glue object
260    buf   - buffer to return data in
261    count - number of bytes to read into buffer max
262
263 =cut
264 */
265
266 static
267 ssize_t 
268 buffer_read(io_glue *ig, void *buf, size_t count) {
269   io_ex_buffer *ieb = ig->exdata;
270
271   IOL_DEB( printf("buffer_read: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
272
273   if ( ieb->cpos+count > ig->source.buffer.len ) {
274     mm_log((1,"buffer_read: short read: cpos=%d, len=%d, count=%d\n", ieb->cpos, ig->source.buffer.len));
275     count = ig->source.buffer.len - ieb->cpos;
276   }
277   
278   memcpy(buf, ig->source.buffer.data+ieb->cpos, count);
279   ieb->cpos += count;
280   IOL_DEB( printf("buffer_read: rc = %d, count = %d\n", rc, count) );
281   return count;
282 }
283
284
285 /*
286 =item buffer_write(ig, buf, count)
287
288 Does nothing, returns -1
289
290    ig    - io_glue object
291    buf   - buffer that contains data
292    count - number of bytes to write
293
294 =cut
295 */
296
297 static
298 ssize_t 
299 buffer_write(io_glue *ig, const void *buf, size_t count) {
300   mm_log((1, "buffer_write called, this method should never be called.\n"));
301   return -1;
302 }
303
304
305 /*
306 =item buffer_close(ig)
307
308 Closes a source that can be seeked on.  Not sure if this should be an actual close
309 or not.  Does nothing for now.  Should be fixed.
310
311    ig - data source
312
313 =cut
314 */
315
316 static
317 void
318 buffer_close(io_glue *ig) {
319   mm_log((1, "buffer_close(ig %p)\n", ig));
320   /* FIXME: Do stuff here */
321 }
322
323
324 /* buffer_seek(ig, offset, whence)
325
326 Implements seeking for a buffer source.
327
328    ig     - data source
329    offset - offset into stream
330    whence - whence argument a la lseek
331
332 =cut
333 */
334
335 static
336 off_t
337 buffer_seek(io_glue *ig, off_t offset, int whence) {
338   io_ex_buffer *ieb = ig->exdata;
339   off_t reqpos = offset 
340     + (whence == SEEK_CUR)*ieb->cpos 
341     + (whence == SEEK_END)*ig->source.buffer.len;
342   
343   if (reqpos > ig->source.buffer.len) {
344     mm_log((1, "seeking out of readable range\n"));
345     return (off_t)-1;
346   }
347   
348   ieb->cpos = reqpos;
349   IOL_DEB( printf("buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
350
351   return reqpos;
352   /* FIXME: How about implementing this offset handling stuff? */
353 }
354
355
356
357
358
359 /*
360  * Callbacks for sources that are a chain of variable sized buffers
361  */
362
363
364
365 /* Helper functions for buffer chains */
366
367 static
368 io_blink*
369 io_blink_new(void) {
370   io_blink *ib;
371
372   mm_log((1, "io_blink_new()\n"));
373
374   ib = mymalloc(sizeof(io_blink));
375
376   ib->next = NULL;
377   ib->prev = NULL;
378   ib->len  = BBSIZ;
379
380   memset(&ib->buf, 0, ib->len);
381   return ib;
382 }
383
384
385
386 /*
387 =item io_bchain_advance(ieb)
388
389 Advances the buffer chain to the next link - extending if
390 necessary.  Also adjusts the cpos and tfill counters as needed.
391
392    ieb   - buffer chain object
393
394 =cut
395 */
396
397 static
398 void
399 io_bchain_advance(io_ex_bchain *ieb) {
400   if (ieb->cp->next == NULL) {
401     ieb->tail = io_blink_new();
402     ieb->tail->prev = ieb->cp;
403     ieb->cp->next   = ieb->tail;
404
405     ieb->tfill = 0; /* Only set this if we added a new slice */
406   }
407   ieb->cp    = ieb->cp->next;
408   ieb->cpos  = 0;
409 }
410
411
412
413 /*
414 =item io_bchain_destroy()
415
416 frees all resources used by a buffer chain.
417
418 =cut
419 */
420
421 void
422 io_destroy_bufchain(io_ex_bchain *ieb) {
423   io_blink *cp;
424   mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb));
425   cp = ieb->head;
426   
427   while(cp) {
428     io_blink *t = cp->next;
429     myfree(cp);
430     cp = t;
431   }
432 }
433
434
435
436
437 /*
438
439 static
440 void
441 bufchain_dump(io_ex_bchain *ieb) {
442   mm_log((1, "  buf_chain_dump(ieb %p)\n"));
443   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
444   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
445   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
446   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
447   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
448   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
449   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
450   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
451 }
452 */
453
454 /*
455  * TRUE if lengths are NOT equal
456  */
457
458 /*
459 static
460 void
461 chainlencert( io_glue *ig ) {
462   int clen;
463   int cfl           = 0;
464   size_t csize      = 0;
465   size_t cpos       = 0;
466   io_ex_bchain *ieb = ig->exdata;
467   io_blink *cp      = ieb->head;
468   
469
470   if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length));
471
472   while(cp) {
473     clen = (cp == ieb->tail) ? ieb->tfill : cp->len;
474     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
475     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
476     
477     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
478     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
479     
480     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
481     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
482
483     if (cp == ieb->cp) {
484       cfl = 1;
485       cpos += ieb->cpos;
486     }
487
488     if (!cfl) cpos += clen;
489
490     csize += clen;
491     cp     = cp->next;
492   }
493   if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length));
494   if (( cpos  != ieb->gpos   )) mm_log((1, "BAR : cpos  = %d, ieb->gpos   = %d\n", cpos,  ieb->gpos  ));
495 }
496
497
498 static
499 void
500 chaincert( io_glue *ig) {
501   size_t csize   = 0;
502   io_ex_bchain *ieb = ig->exdata;
503   io_blink *cp   = ieb->head;
504   
505   mm_log((1, "Chain verification.\n"));
506
507   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
508   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
509   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
510   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
511   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
512   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
513   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
514   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
515
516   while(cp) {
517     int clen = cp == ieb->tail ? ieb->tfill : cp->len;
518     mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next));
519     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
520     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
521     
522     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
523     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
524     
525     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
526     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
527
528     csize += clen;
529     cp     = cp->next;
530   }
531
532   mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length));
533 }
534 */
535
536
537
538
539
540
541
542
543
544
545
546 /*
547 =item bufchain_read(ig, buf, count)
548
549 Does the reading from a source that can be seeked on
550
551    ig    - io_glue object
552    buf   - buffer to return data in
553    count - number of bytes to read into buffer max
554
555 =cut
556 */
557
558 static
559 ssize_t 
560 bufchain_read(io_glue *ig, void *buf, size_t count) {
561   io_ex_bchain *ieb = ig->exdata;
562   size_t     scount = count;
563   char        *cbuf = buf;
564   size_t         sk;
565
566   mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, count));
567
568   while( scount ) {
569     int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
570     if (clen == ieb->cpos) {
571       if (ieb->cp == ieb->tail) break; /* EOF */
572       ieb->cp = ieb->cp->next;
573       ieb->cpos = 0;
574       clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
575     }
576
577     sk = clen - ieb->cpos;
578     sk = sk > scount ? scount : sk;
579
580     memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
581     scount    -= sk;
582     ieb->cpos += sk;
583     ieb->gpos += sk;
584   }
585
586   mm_log((1, "bufchain_read: returning %d\n", count-scount));
587   return count-scount;
588 }
589
590
591
592
593
594 /*
595 =item bufchain_write(ig, buf, count)
596
597 Does the writing to a 'source' that can be seeked on
598
599    ig    - io_glue object
600    buf   - buffer that contains data
601    count - number of bytes to write
602
603 =cut
604 */
605
606 static
607 ssize_t
608 bufchain_write(io_glue *ig, const void *buf, size_t count) {
609   char *cbuf = (char *)buf;
610   io_ex_bchain *ieb = ig->exdata;
611   size_t         ocount = count;
612   size_t         sk;
613
614   mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
615
616   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
617   
618   while(count) {
619     mm_log((2, "bufchain_write: - looping - count = %d\n", count));
620     if (ieb->cp->len == ieb->cpos) {
621       mm_log((1, "bufchain_write: cp->len == ieb->cpos = %d - advancing chain\n", (long) ieb->cpos));
622       io_bchain_advance(ieb);
623     }
624
625     sk = ieb->cp->len - ieb->cpos;
626     sk = sk > count ? count : sk;
627     memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
628
629     if (ieb->cp == ieb->tail) {
630       int extend = ieb->cpos + sk - ieb->tfill;
631       mm_log((2, "bufchain_write: extending tail by %d\n", extend));
632       if (extend > 0) {
633         ieb->length += extend;
634         ieb->tfill  += extend;
635       }
636     }
637
638     ieb->cpos += sk;
639     ieb->gpos += sk;
640     count     -= sk;
641   }
642   return ocount;
643 }
644
645 /*
646 =item bufchain_close(ig)
647
648 Closes a source that can be seeked on.  Not sure if this should be an actual close
649 or not.  Does nothing for now.  Should be fixed.
650
651    ig - data source
652
653 =cut
654 */
655
656 static
657 void
658 bufchain_close(io_glue *ig) {
659   mm_log((1, "bufchain_close(ig %p)\n",ig));
660   IOL_DEB( printf("bufchain_close(ig %p)\n", ig) );
661   /* FIXME: Commit a seek point here */
662   
663 }
664
665
666 /* bufchain_seek(ig, offset, whence)
667
668 Implements seeking for a source that is seekable, the purpose of having this is to be able to
669 have an offset into a file that is different from what the underlying library thinks.
670
671    ig     - data source
672    offset - offset into stream
673    whence - whence argument a la lseek
674
675 =cut
676 */
677
678 static
679 off_t
680 bufchain_seek(io_glue *ig, off_t offset, int whence) {
681   io_ex_bchain *ieb = ig->exdata;
682   io_blink *ib      = NULL;
683   int wrlen;
684
685   off_t cof = 0;
686   off_t scount = offset;
687   off_t sk;
688
689   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
690
691   switch (whence) {
692   case SEEK_SET: /* SEEK_SET = 0, From the top */
693     ieb->cp   = ieb->head;
694     ieb->cpos = 0;
695     ieb->gpos = 0;
696
697     while( scount ) {
698       int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
699       if (clen == ieb->cpos) {
700         if (ieb->cp == ieb->tail) break; /* EOF */
701         ieb->cp = ieb->cp->next;
702         ieb->cpos = 0;
703         clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
704       }
705       
706       sk = clen - ieb->cpos;
707       sk = sk > scount ? scount : sk;
708       
709       scount    -= sk;
710       ieb->cpos += sk;
711       ieb->gpos += sk;
712     }
713
714     wrlen = scount;
715
716     if (wrlen > 0) { 
717       /*
718        * extending file - get ieb into consistent state and then
719        * call write which will get it to the correct position 
720        */
721       char TB[BBSIZ];
722       memset(TB, 0, BBSIZ);
723       ieb->gpos = ieb->length;
724       ieb->cpos = ieb->tfill;
725
726       while(wrlen > 0) {
727         ssize_t rc, wl = i_min(wrlen, BBSIZ);
728         mm_log((1, "bufchain_seek: wrlen = %d, wl = %d\n", wrlen, wl));
729         rc = bufchain_write( ig, TB, wl );
730         if (rc != wl) m_fatal(0, "bufchain_seek: Unable to extend file\n");
731         wrlen -= rc;
732       }
733     }
734     
735     break;
736
737   case SEEK_CUR:
738     m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
739
740     /*
741       case SEEK_CUR: 
742       ib = ieb->cp;
743       if (cof < 0) {
744       cof += ib->cpos;
745       cpos = 0;
746       while(cof < 0 && ib->prev) {
747       ib = ib->prev;
748       cof += ib->len;
749       }
750     */
751     
752   case SEEK_END: /* SEEK_END = 2 */
753     if (cof>0) m_fatal(0, "bufchain_seek: SEEK_END + %d : Extending files via seek not supported!\n", cof);
754
755     ieb->cp   = ieb->tail;
756     ieb->cpos = ieb->tfill;
757     
758     if (cof<0) {
759       cof      += ieb->cpos;
760       ieb->cpos = 0;
761
762       while(cof<0 && ib->prev) {
763         ib   = ib->prev;
764         cof += ib->len;
765       }
766     
767       if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
768       ieb->gpos = ieb->length+offset;
769       ieb->cpos = cof;
770     }
771     break;
772   default:
773     m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
774   }
775
776   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
777   return ieb->gpos;
778 }
779
780
781
782
783
784
785 /*
786  * Methods for setting up data source
787  */
788
789 /*
790 =item io_obj_setp_buffer(io, p, len)
791
792 Sets an io_object for reading from a buffer source
793
794    io  - io object that describes a source
795    p   - pointer to buffer
796    len - length of buffer
797
798 =cut
799 */
800
801 static void
802 io_obj_setp_buffer(io_obj *io, char *p, size_t len, closebufp closecb, 
803                    void *closedata) {
804   io->buffer.type      = BUFFER;
805   io->buffer.data      = p;
806   io->buffer.len       = len;
807   io->buffer.closecb   = closecb;
808   io->buffer.closedata = closedata;
809 }
810
811
812
813 /*
814 =item io_obj_setp_cb2(io, p, readcb, writecb, seekcb, closecb, destroycb)
815
816 Sets an io_object for reading from a source that uses callbacks
817
818    io      - io object that describes a source
819    p         - pointer to data for callbacks
820    readcb    - read callback to read from source
821    writecb   - write callback to write to source
822    seekcb    - seek callback to seek on source
823    closecb   - flush any pending data
824    destroycb - release any extra resources
825
826 =cut
827 */
828
829 static void
830 io_obj_setp_cb2(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb, closel closecb, destroyl destroycb) {
831   io->cb.type      = CBSEEK;
832   io->cb.p         = p;
833   io->cb.readcb    = readcb;
834   io->cb.writecb   = writecb;
835   io->cb.seekcb    = seekcb;
836   io->cb.closecb   = closecb;
837   io->cb.destroycb = destroycb;
838 }
839
840 /*
841 =item io_glue_commit_types(ig)
842
843 This is now effectively a no-op.
844
845 =cut
846 */
847
848 void
849 io_glue_commit_types(io_glue *ig) {
850   io_type      inn = ig->source.type;
851
852   mm_log((1, "io_glue_commit_types(ig %p)\n", ig));
853   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
854
855   if (ig->flags & 0x01) {
856     mm_log((1, "io_glue_commit_types: type already set up\n"));
857     return;
858   }
859
860   ig->flags |= 0x01; /* indicate source has been setup already */
861 }
862
863 /*
864 =item io_glue_gettypes(ig, reqmeth)
865
866 Returns a set of compatible interfaces to read data with.
867
868   ig      - io_glue object
869   reqmeth - request mask
870
871 The request mask is a bit mask (of something that hasn't been implemented yet)
872 of interfaces that it would like to read data from the source which the ig
873 describes.
874
875 =cut
876 */
877
878 void
879 io_glue_gettypes(io_glue *ig, int reqmeth) {
880
881   ig = NULL;
882   reqmeth = 0;
883   
884   /* FIXME: Implement this function! */
885   /* if (ig->source.type = 
886      if (reqmeth & IO_BUFF) */ 
887
888 }
889
890
891 /*
892 =item io_new_bufchain()
893
894 returns a new io_glue object that has the 'empty' source and but can
895 be written to and read from later (like a pseudo file).
896
897 =cut
898 */
899
900 io_glue *
901 io_new_bufchain() {
902   io_glue *ig;
903   io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
904
905   mm_log((1, "io_new_bufchain()\n"));
906
907   ig = mymalloc(sizeof(io_glue));
908   memset(ig, 0, sizeof(*ig));
909   ig->source.type = BUFCHAIN;
910
911   ieb->offset = 0;
912   ieb->length = 0;
913   ieb->cpos   = 0;
914   ieb->gpos   = 0;
915   ieb->tfill  = 0;
916   
917   ieb->head   = io_blink_new();
918   ieb->cp     = ieb->head;
919   ieb->tail   = ieb->head;
920   
921   ig->exdata  = ieb;
922   ig->readcb  = bufchain_read;
923   ig->writecb = bufchain_write;
924   ig->seekcb  = bufchain_seek;
925   ig->closecb = bufchain_close;
926
927   return ig;
928 }
929
930 /*
931 =item io_new_buffer(data, len)
932
933 Returns a new io_glue object that has the source defined as reading
934 from specified buffer.  Note that the buffer is not copied.
935
936    data - buffer to read from
937    len - length of buffer
938
939 =cut
940 */
941
942 io_glue *
943 io_new_buffer(char *data, size_t len, closebufp closecb, void *closedata) {
944   io_glue *ig;
945   io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer));
946   
947   mm_log((1, "io_new_buffer(data %p, len %d, closecb %p, closedata %p)\n", data, len, closecb, closedata));
948
949   ig = mymalloc(sizeof(io_glue));
950   memset(ig, 0, sizeof(*ig));
951   io_obj_setp_buffer(&ig->source, data, len, closecb, closedata);
952   ig->flags = 0;
953
954   ieb->offset = 0;
955   ieb->cpos   = 0;
956   
957   ig->exdata  = ieb;
958   ig->readcb  = buffer_read;
959   ig->writecb = buffer_write;
960   ig->seekcb  = buffer_seek;
961   ig->closecb = buffer_close;
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       
998   mm_log((1, "(%p) <- io_new_fd\n", ig));
999   return ig;
1000 }
1001
1002 io_glue *io_new_cb(void *p, readl readcb, writel writecb, seekl seekcb, 
1003                    closel closecb, destroyl destroycb) {
1004   io_glue *ig;
1005   io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
1006
1007   mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, "
1008           "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb));
1009   ig = mymalloc(sizeof(io_glue));
1010   memset(ig, 0, sizeof(*ig));
1011   io_obj_setp_cb2(&ig->source, p, readcb, writecb, seekcb, closecb, destroycb);
1012   mm_log((1, "(%p) <- io_new_cb\n", ig));
1013
1014   ier->offset = 0;
1015   ier->cpos   = 0;
1016   
1017   ig->exdata  = ier;
1018   ig->readcb  = realseek_read;
1019   ig->writecb = realseek_write;
1020   ig->seekcb  = realseek_seek;
1021   ig->closecb = realseek_close;
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   io_type      inn = ig->source.type;
1142   mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
1143   
1144   switch (inn) {
1145   case BUFCHAIN:
1146     {
1147       io_ex_bchain *ieb = ig->exdata;
1148       io_destroy_bufchain(ieb);
1149       myfree(ieb);
1150     }
1151     break;
1152   case CBSEEK:
1153     {
1154       io_ex_rseek *ier = ig->exdata;
1155       if (ig->source.cb.destroycb)
1156         ig->source.cb.destroycb(ig->source.cb.p);
1157       myfree(ier);
1158     }
1159     break;
1160   case BUFFER:
1161     {
1162       io_ex_buffer *ieb = ig->exdata;
1163       if (ig->source.buffer.closecb) {
1164         mm_log((1,"calling close callback %p for io_buffer\n", ig->source.buffer.closecb));
1165         ig->source.buffer.closecb(ig->source.buffer.closedata);
1166       }
1167       myfree(ieb);
1168     }
1169     break;
1170   default:
1171     break;
1172   }
1173   myfree(ig);
1174 }
1175
1176 /*
1177 =back
1178
1179 =head1 INTERNAL FUNCTIONS
1180
1181 =over
1182
1183 =item my_strerror
1184
1185 Calls strerror() and ensures we don't return NULL.
1186
1187 On some platforms it's possible for strerror() to return NULL, this
1188 wrapper ensures we only get non-NULL values.
1189
1190 =cut
1191 */
1192
1193 static
1194 const char *my_strerror(int err) {
1195   const char *result = strerror(err);
1196
1197   if (!result)
1198     result = "Unknown error";
1199
1200   return result;
1201 }
1202
1203 /*
1204 =back
1205
1206 =head1 AUTHOR
1207
1208 Arnar M. Hrafnkelsson <addi@umich.edu>
1209
1210 =head1 SEE ALSO
1211
1212 Imager(3)
1213
1214 =cut
1215 */