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