222 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			222 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*  $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 <SWI-Stream.h>
 | ||
|  | #include <SWI-Prolog.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <assert.h>
 | ||
|  | #include <time.h>
 | ||
|  | #include <errno.h>
 | ||
|  | 
 | ||
|  | 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); | ||
|  | } |