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