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