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