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