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