/* $Id$ Part of SWI-Prolog Author: Jan Wielemaker E-mail: wielemak@science.uva.nl WWW: http://www.swi-prolog.org Copyright (C): 2007, University of Amsterdam This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include static atom_t ATOM_size; /* size(Int) */ /******************************* * TYPES * *******************************/ #define BUFSIZE SIO_BUFSIZE /* raw I/O buffer */ typedef struct range_context { IOSTREAM *stream; /* Original stream */ IOSTREAM *range_stream; /* Stream I'm handle of */ IOENC parent_encoding; /* Saved encoding of parent */ size_t read; /* data already read */ size_t size; /* #bytes of data available */ } range_context; static range_context* alloc_range_context(IOSTREAM *s) { range_context *ctx = PL_malloc(sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx)); ctx->stream = s; return ctx; } static void free_range_context(range_context *ctx) { if ( ctx->stream->upstream ) Sset_filter(ctx->stream, NULL); else PL_release_stream(ctx->stream); PL_free(ctx); } /******************************* * RANGE LIMITED INPUT * *******************************/ static ssize_t /* range-limited read */ range_read(void *handle, char *buf, size_t size) { range_context *ctx = handle; size_t max_rd; ssize_t rd; if ( ctx->read == ctx->size ) return 0; if ( ctx->size - ctx->read < size ) max_rd = ctx->size - ctx->read; else max_rd = size; if ( (rd = Sfread(buf, sizeof(char), max_rd, ctx->stream)) >= 0 ) { ctx->read += rd; return rd; } return rd; } static ssize_t /* no writing! */ range_write(void *handle, char *buf, size_t size) { return -1; } static int range_control(void *handle, int op, void *data) { range_context *ctx = handle; switch(op) { case SIO_FLUSHOUTPUT: case SIO_SETENCODING: return 0; /* allow switching encoding */ case SIO_GETSIZE: { size_t *rval = data; *rval = ctx->size; return 0; } default: if ( ctx->stream->functions->control ) return (*ctx->stream->functions->control)(ctx->stream->handle, op, data); return -1; } } static int range_close(void *handle) { range_context *ctx = handle; ctx->stream->encoding = ctx->parent_encoding; free_range_context(ctx); return 0; } static IOFUNCTIONS range_functions = { range_read, range_write, NULL, /* seek */ range_close, range_control, /* zcontrol */ NULL, /* seek64 */ }; /******************************* * PROLOG CONNECTION * *******************************/ #define COPY_FLAGS (SIO_INPUT|SIO_OUTPUT| \ SIO_TEXT| \ SIO_REPXML|SIO_REPPL|\ SIO_RECORDPOS) static foreign_t pl_stream_range_open(term_t org, term_t new, term_t options) { term_t tail = PL_copy_term_ref(options); term_t head = PL_new_term_ref(); range_context *ctx; IOSTREAM *s, *s2; int size = 0; while(PL_get_list(tail, head, tail)) { atom_t name; int arity; term_t arg = PL_new_term_ref(); if ( !PL_get_name_arity(head, &name, &arity) || arity != 1 ) return type_error(head, "option"); _PL_get_arg(1, head, arg); if ( name == ATOM_size ) { if ( !get_int_ex(arg, &size) ) return FALSE; if ( size <= 0 ) return domain_error(arg, "nonneg"); } } if ( !PL_get_nil(tail) ) return type_error(tail, "list"); if ( !PL_get_stream_handle(org, &s) ) return FALSE; /* Error */ ctx = alloc_range_context(s); ctx->size = size; if ( !(s2 = Snew(ctx, (s->flags©_FLAGS)|SIO_FBUF, &range_functions)) ) { free_range_context(ctx); /* no memory */ return FALSE; } s2->encoding = s->encoding; ctx->parent_encoding = s->encoding; s->encoding = ENC_OCTET; ctx->range_stream = s2; if ( PL_unify_stream(new, s2) ) { Sset_filter(s, s2); PL_release_stream(s); return TRUE; } else { return instantiation_error(); } } /******************************* * INSTALL * *******************************/ static void install_stream_range() { ATOM_size = PL_new_atom("size"); PL_register_foreign("stream_range_open", 3, pl_stream_range_open, 0); }