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