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