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