+int
+i_io_set_buffered(io_glue *ig, int buffered) {
+ if (!buffered && ig->write_ptr) {
+ if (!i_io_flush(ig)) {
+ ig->error = 1;
+ return 0;
+ }
+ }
+ ig->buffered = buffered;
+
+ return 1;
+}
+
+/*
+=item i_io_dump(ig)
+
+Dump the base fields of an io_glue object to stdout.
+
+=cut
+*/
+void
+i_io_dump(io_glue *ig, int flags) {
+ fprintf(IOL_DEBs, "ig %p:\n", ig);
+ fprintf(IOL_DEBs, " type: %d\n", ig->type);
+ fprintf(IOL_DEBs, " exdata: %p\n", ig->exdata);
+ if (flags & I_IO_DUMP_CALLBACKS) {
+ fprintf(IOL_DEBs, " readcb: %p\n", ig->readcb);
+ fprintf(IOL_DEBs, " writecb: %p\n", ig->writecb);
+ fprintf(IOL_DEBs, " seekcb: %p\n", ig->seekcb);
+ fprintf(IOL_DEBs, " closecb: %p\n", ig->closecb);
+ fprintf(IOL_DEBs, " sizecb: %p\n", ig->sizecb);
+ }
+ if (flags & I_IO_DUMP_BUFFER) {
+ fprintf(IOL_DEBs, " buffer: %p\n", ig->buffer);
+ fprintf(IOL_DEBs, " read_ptr: %p\n", ig->read_ptr);
+ if (ig->read_ptr) {
+ fprintf(IOL_DEBs, " ");
+ dump_data(ig->read_ptr, ig->read_end, 0);
+ putc('\n', IOL_DEBs);
+ }
+ fprintf(IOL_DEBs, " read_end: %p\n", ig->read_end);
+ fprintf(IOL_DEBs, " write_ptr: %p\n", ig->write_ptr);
+ if (ig->write_ptr) {
+ fprintf(IOL_DEBs, " ");
+ dump_data(ig->buffer, ig->write_ptr, 1);
+ putc('\n', IOL_DEBs);
+ }
+ fprintf(IOL_DEBs, " write_end: %p\n", ig->write_end);
+ fprintf(IOL_DEBs, " buf_size: %u\n", (unsigned)(ig->buf_size));
+ }
+ if (flags & I_IO_DUMP_STATUS) {
+ fprintf(IOL_DEBs, " buf_eof: %d\n", ig->buf_eof);
+ fprintf(IOL_DEBs, " error: %d\n", ig->error);
+ fprintf(IOL_DEBs, " buffered: %d\n", ig->buffered);
+ }
+}
+
+/*
+=back
+
+=head1 INTERNAL FUNCTIONS
+
+=over
+
+=item my_strerror
+
+Calls strerror() and ensures we don't return NULL.
+
+On some platforms it's possible for strerror() to return NULL, this
+wrapper ensures we only get non-NULL values.
+
+=cut
+*/
+
+static
+const char *my_strerror(int err) {
+ const char *result = strerror(err);
+
+ if (!result)
+ result = "Unknown error";
+
+ return result;
+}
+
+static void
+i_io_setup_buffer(io_glue *ig) {
+ ig->buffer = mymalloc(ig->buf_size);
+}
+
+static void
+i_io_start_write(io_glue *ig) {
+ ig->write_ptr = ig->buffer;
+ ig->write_end = ig->buffer + ig->buf_size;
+}
+
+static int
+i_io_read_fill(io_glue *ig, ssize_t needed) {
+ unsigned char *buf_end = ig->buffer + ig->buf_size;
+ unsigned char *buf_start = ig->buffer;
+ unsigned char *work = ig->buffer;
+ ssize_t rc;
+ int good = 0;
+
+ IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%p, %d)\n", ig, (int)needed));
+
+ /* these conditions may be unused, callers should also be checking them */
+ if (ig->error || ig->buf_eof)
+ return 0;
+
+ if (needed > ig->buf_size)
+ needed = ig->buf_size;
+
+ if (ig->read_ptr && ig->read_ptr < ig->read_end) {
+ size_t kept = ig->read_end - ig->read_ptr;
+
+ if (needed < kept) {
+ IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%u) -> 1 (already have enough)\n", (unsigned)needed));
+ return 1;
+ }
+
+ if (ig->read_ptr != ig->buffer)
+ memmove(ig->buffer, ig->read_ptr, kept);
+
+ good = 1; /* we have *something* available to read */
+ work = buf_start + kept;
+ needed -= kept;
+ }
+ else {
+ work = ig->buffer;
+ }
+
+ /* there should always be buffer space the first time around, but
+ avoid a compiler warning here */
+ rc = -1;
+ while (work < buf_end && (rc = i_io_raw_read(ig, work, buf_end - work)) > 0) {
+ work += rc;
+ good = 1;
+ if (needed < rc)
+ break;
+
+ needed -= rc;
+ }
+
+ if (rc < 0) {
+ ig->error = 1;
+ IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc %d, setting error\n",
+ (int)rc));
+ }
+ else if (rc == 0) {
+ ig->buf_eof = 1;
+ IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc 0, setting eof\n"));
+ }
+
+ if (good) {
+ ig->read_ptr = buf_start;
+ ig->read_end = work;
+ }
+
+ IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill => %d, %u buffered\n", good,
+ (unsigned)(ig->read_end - ig->read_ptr)));
+ return good;
+}
+
+/*
+=item dump_data(start, end, bias)
+
+Hex dump the data between C<start> and C<end>.
+
+If there is more than a pleasing amount of data, either dump the
+beginning (C<bias == 0>) or dump the end C(<bias != 0>) of the range.
+
+=cut
+*/
+
+static void
+dump_data(unsigned char *start, unsigned char *end, int bias) {
+ unsigned char *p;
+ size_t count = end - start;
+
+ if (start == end) {
+ fprintf(IOL_DEBs, "(empty)");
+ return;
+ }
+
+ if (count > 15) {
+ if (bias) {
+ fprintf(IOL_DEBs, "... ");
+ start = end - 14;
+ }
+ else {
+ end = start + 14;
+ }
+
+ for (p = start; p < end; ++p) {
+ fprintf(IOL_DEBs, " %02x", *p);
+ }
+ putc(' ', IOL_DEBs);
+ putc('<', IOL_DEBs);
+ for (p = start; p < end; ++p) {
+ if (*p < ' ' || *p > '~')
+ putc('.', IOL_DEBs);
+ else
+ putc(*p, IOL_DEBs);
+ }
+ putc('>', IOL_DEBs);
+ if (!bias)
+ fprintf(IOL_DEBs, " ...");
+ }
+ else {
+ for (p = start; p < end; ++p) {
+ fprintf(IOL_DEBs, " %02x", *p);
+ }
+ putc(' ', IOL_DEBs);
+ for (p = start; p < end; ++p) {
+ if (*p < ' ' || *p > '~')
+ putc('.', IOL_DEBs);
+ else
+ putc(*p, IOL_DEBs);
+ }
+ }
+}
+
+/*
+ * Callbacks for sources that cannot seek
+ */
+
+/*
+ * Callbacks for sources that can seek
+ */
+
+/*
+=item realseek_read(ig, buf, count)
+
+Does the reading from a source that can be seeked on
+
+ ig - io_glue object
+ buf - buffer to return data in
+ count - number of bytes to read into buffer max
+
+=cut
+*/
+
+static
+ssize_t
+realseek_read(io_glue *igo, void *buf, size_t count) {
+ io_cb *ig = (io_cb *)igo;
+ void *p = ig->p;
+ ssize_t rc = 0;
+
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_read: buf = %p, count = %u\n",
+ buf, (unsigned)count) );
+ rc = ig->readcb(p,buf,count);
+
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_read: rc = %d\n", (int)rc) );
+
+ return rc;
+}
+
+
+/*
+=item realseek_write(ig, buf, count)
+
+Does the writing to a 'source' that can be seeked on
+
+ ig - io_glue object
+ buf - buffer that contains data
+ count - number of bytes to write
+
+=cut
+*/
+
+static
+ssize_t
+realseek_write(io_glue *igo, const void *buf, size_t count) {
+ io_cb *ig = (io_cb *)igo;
+ void *p = ig->p;
+ ssize_t rc = 0;
+ size_t bc = 0;
+ char *cbuf = (char*)buf;
+
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_write: ig = %p, buf = %p, "
+ "count = %u\n", ig, buf, (unsigned)count) );
+
+ /* Is this a good idea? Would it be better to handle differently?
+ skip handling? */
+ while( count!=bc && (rc = ig->writecb(p,cbuf+bc,count-bc))>0 ) {
+ bc+=rc;
+ }
+
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_write: rc = %d, bc = %u\n", (int)rc, (unsigned)bc) );
+ return rc < 0 ? rc : bc;
+}
+
+
+/*
+=item realseek_close(ig)
+
+Closes a source that can be seeked on. Not sure if this should be an
+actual close or not. Does nothing for now. Should be fixed.
+
+ ig - data source
+
+=cut */
+
+static
+int
+realseek_close(io_glue *igo) {
+ io_cb *ig = (io_cb *)igo;
+ dIMCTXio(igo);
+
+ IOL_DEB(fprintf(IOL_DEBs, "realseek_close(%p)\n", ig));
+ im_log((aIMCTX,1, "realseek_close(ig %p)\n", ig));
+ if (ig->closecb)
+ return ig->closecb(ig->p);
+ else
+ return 0;
+}
+
+
+/*
+=item realseek_seek(ig, offset, whence)
+
+Implements seeking for a source that is seekable, the purpose of having this is to be able to
+have an offset into a file that is different from what the underlying library thinks.
+
+ ig - data source
+ offset - offset into stream
+ whence - whence argument a la lseek
+
+=cut
+*/
+
+static
+off_t
+realseek_seek(io_glue *igo, off_t offset, int whence) {
+ io_cb *ig = (io_cb *)igo;
+ void *p = ig->p;
+ off_t rc;
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
+ rc = ig->seekcb(p, offset, whence);
+
+ IOL_DEB( fprintf(IOL_DEBs, "realseek_seek: rc %ld\n", (long) rc) );
+ return rc;
+ /* FIXME: How about implementing this offset handling stuff? */
+}
+
+static
+void
+realseek_destroy(io_glue *igo) {
+ io_cb *ig = (io_cb *)igo;
+
+ if (ig->destroycb)
+ ig->destroycb(ig->p);
+}
+
+/*
+ * Callbacks for sources that are a fixed size buffer
+ */
+
+/*
+=item buffer_read(ig, buf, count)
+
+Does the reading from a buffer source
+
+ ig - io_glue object
+ buf - buffer to return data in
+ count - number of bytes to read into buffer max
+
+=cut
+*/
+
+static
+ssize_t
+buffer_read(io_glue *igo, void *buf, size_t count) {
+ io_buffer *ig = (io_buffer *)igo;
+
+ IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ig->cpos = %ld, buf = %p, count = %u\n", (long) ig->cpos, buf, (unsigned)count) );
+
+ if ( ig->cpos+count > ig->len ) {
+ dIMCTXio(igo);
+ im_log((aIMCTX, 1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count));
+ count = ig->len - ig->cpos;
+ }
+
+ memcpy(buf, ig->data+ig->cpos, count);
+ ig->cpos += count;
+ IOL_DEB( fprintf(IOL_DEBs, "buffer_read: count = %ld\n", (long)count) );
+ return count;
+}
+
+
+/*
+=item buffer_write(ig, buf, count)
+
+Does nothing, returns -1
+
+ ig - io_glue object
+ buf - buffer that contains data
+ count - number of bytes to write
+
+=cut
+*/
+
+static
+ssize_t
+buffer_write(io_glue *ig, const void *buf, size_t count) {
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "buffer_write called, this method should never be called.\n"));
+ return -1;
+}
+
+
+/*
+=item buffer_close(ig)
+
+Closes a source that can be seeked on. Not sure if this should be an actual close
+or not. Does nothing for now. Should be fixed.
+
+ ig - data source
+
+=cut
+*/
+
+static
+int
+buffer_close(io_glue *ig) {
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "buffer_close(ig %p)\n", ig));
+
+ return 0;