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