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