mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
8.9 KiB
479 lines
8.9 KiB
/*
|
|
* I/O streams.
|
|
* Copyright (c) 1998 New Generation Software (NGS) Oy
|
|
*
|
|
* Author: Markku Rossi <mtr@ngs.fi>
|
|
*/
|
|
|
|
/*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library 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
|
|
*/
|
|
|
|
/*
|
|
* $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/iostream.c,v $
|
|
* $Id: iostream.c,v 1.2 2006/10/28 11:27:44 njh Exp $
|
|
*/
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#ifdef CL_EXPERIMENTAL
|
|
|
|
#include "jsint.h"
|
|
|
|
/*
|
|
* Types and definitions.
|
|
*/
|
|
|
|
#define DEFAULT_BUFFER_SIZE 4096
|
|
|
|
/*
|
|
* Global functions.
|
|
*/
|
|
|
|
JSIOStream *
|
|
js_iostream_new ()
|
|
{
|
|
JSIOStream *stream;
|
|
|
|
stream = js_calloc (NULL, 1, sizeof (*stream));
|
|
if (stream == NULL)
|
|
return NULL;
|
|
|
|
stream->buflen = DEFAULT_BUFFER_SIZE;
|
|
stream->buffer = js_malloc (NULL, stream->buflen);
|
|
if (stream->buffer == NULL)
|
|
{
|
|
js_free (stream);
|
|
return NULL;
|
|
}
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
/* The `FILE *' stream. */
|
|
|
|
static int
|
|
file_read (void *context, unsigned char *buffer, unsigned int todo,
|
|
int *error_return)
|
|
{
|
|
int got;
|
|
|
|
errno = 0;
|
|
got = fread (buffer, 1, todo, (FILE *) context);
|
|
*error_return = errno;
|
|
|
|
return got;
|
|
}
|
|
|
|
|
|
static int
|
|
file_write (void *context, unsigned char *buffer, unsigned int todo,
|
|
int *error_return)
|
|
{
|
|
int wrote;
|
|
|
|
errno = 0;
|
|
wrote = fwrite (buffer, 1, todo, (FILE *) context);
|
|
*error_return = errno;
|
|
|
|
return wrote;
|
|
}
|
|
|
|
|
|
static int
|
|
file_seek (void *context, long offset, int whence)
|
|
{
|
|
return fseek ((FILE *) context, offset, whence);
|
|
}
|
|
|
|
|
|
static long
|
|
file_get_position (void *context)
|
|
{
|
|
return ftell ((FILE *) context);
|
|
}
|
|
|
|
|
|
static long
|
|
file_get_length (void *context)
|
|
{
|
|
FILE *fp = (FILE *) context;
|
|
long cpos;
|
|
long result = -1;
|
|
|
|
/* Save current position. */
|
|
cpos = ftell (fp);
|
|
if (cpos >= 0)
|
|
{
|
|
/* Seek to the end of the file. */
|
|
if (fseek (fp, 0, SEEK_END) >= 0)
|
|
{
|
|
/* Fetch result. */
|
|
result = ftell (fp);
|
|
|
|
/* Seek back. */
|
|
if (fseek (fp, cpos, SEEK_SET) < 0)
|
|
/* Couldn't revert the fp to the original position. */
|
|
result = -1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static void
|
|
file_close (void *context)
|
|
{
|
|
fclose ((FILE *) context);
|
|
}
|
|
|
|
|
|
JSIOStream *
|
|
js_iostream_file (FILE *fp, int readp, int writep, int do_close)
|
|
{
|
|
JSIOStream *stream;
|
|
|
|
if (fp == NULL)
|
|
return NULL;
|
|
|
|
stream = js_iostream_new ();
|
|
if (stream == NULL)
|
|
return NULL;
|
|
|
|
if (readp)
|
|
stream->read = file_read;
|
|
if (writep)
|
|
stream->write = file_write;
|
|
|
|
stream->seek = file_seek;
|
|
stream->get_position = file_get_position;
|
|
stream->get_length = file_get_length;
|
|
|
|
if (do_close)
|
|
stream->close = file_close;
|
|
|
|
stream->context = fp;
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
static void
|
|
close_pipe (void *context)
|
|
{
|
|
pclose ((FILE *) context);
|
|
}
|
|
|
|
|
|
JSIOStream *
|
|
js_iostream_pipe (FILE *fp, int readp)
|
|
{
|
|
JSIOStream *stream;
|
|
|
|
if (fp == NULL)
|
|
return NULL;
|
|
|
|
stream = js_iostream_new ();
|
|
|
|
if (stream == NULL)
|
|
return NULL;
|
|
|
|
if (readp)
|
|
stream->read = file_read;
|
|
else
|
|
stream->write = file_write;
|
|
|
|
stream->seek = file_seek;
|
|
stream->get_position = file_get_position;
|
|
stream->get_length = file_get_length;
|
|
stream->close = close_pipe;
|
|
stream->context = fp;
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
size_t
|
|
js_iostream_read (JSIOStream *stream, void *v, size_t size)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)v;
|
|
size_t total = 0;
|
|
int got;
|
|
|
|
if (stream->writep)
|
|
{
|
|
/* We have buffered output data. */
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return 0;
|
|
|
|
assert (stream->writep == 0);
|
|
}
|
|
|
|
while (size > 0)
|
|
{
|
|
/* First, take everything from the buffer. */
|
|
if (stream->bufpos < stream->data_in_buf)
|
|
{
|
|
got = stream->data_in_buf - stream->bufpos;
|
|
|
|
if (size < got)
|
|
got = size;
|
|
|
|
memcpy (ptr, stream->buffer + stream->bufpos, got);
|
|
|
|
stream->bufpos += got;
|
|
size -= got;
|
|
ptr += got;
|
|
total += got;
|
|
}
|
|
else
|
|
{
|
|
if (stream->at_eof)
|
|
/* EOF seen, can't read more. */
|
|
break;
|
|
|
|
js_iostream_fill_buffer (stream);
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
size_t
|
|
js_iostream_write (JSIOStream *stream, void *v, size_t size)
|
|
{
|
|
int space;
|
|
size_t total = 0;
|
|
unsigned char *ptr = (unsigned char *)v;
|
|
|
|
if (stream->write == NULL)
|
|
{
|
|
stream->error = EBADF;
|
|
return 0;
|
|
}
|
|
|
|
if (!stream->writep && stream->bufpos < stream->data_in_buf)
|
|
{
|
|
/*
|
|
* We have some buffered data in the stream => the actual stream
|
|
* position in stream->context is not in sync with stream->bufpos.
|
|
* Seek back.
|
|
*/
|
|
|
|
if ((*stream->seek) (stream->context, SEEK_CUR,
|
|
stream->bufpos - stream->data_in_buf) < 0)
|
|
/* XXX Error value. */
|
|
return 0;
|
|
|
|
stream->bufpos = 0;
|
|
stream->data_in_buf = 0;
|
|
}
|
|
|
|
while (size > 0)
|
|
{
|
|
space = stream->buflen - stream->data_in_buf;
|
|
if (size < space)
|
|
space = size;
|
|
|
|
/* Append data to the buffer. */
|
|
memcpy (stream->buffer + stream->data_in_buf, ptr, space);
|
|
stream->data_in_buf += space;
|
|
total += space;
|
|
size -= space;
|
|
ptr += space;
|
|
|
|
/* Now the buffer contains buffered write data. */
|
|
stream->writep = 1;
|
|
|
|
if (size > 0)
|
|
{
|
|
/* Still some data left. Must flush */
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return total;
|
|
}
|
|
}
|
|
|
|
/* Autoflush. */
|
|
if (stream->autoflush && stream->writep)
|
|
if (js_iostream_flush (stream) == EOF)
|
|
/* Failed. Just return something smaller than <size> */
|
|
return total - stream->data_in_buf;
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
int
|
|
js_iostream_flush (JSIOStream *stream)
|
|
{
|
|
if (stream == NULL || stream->write == NULL || !stream->writep)
|
|
return 0;
|
|
|
|
stream->writep = 0;
|
|
assert (stream->bufpos == 0);
|
|
|
|
if (stream->data_in_buf > 0)
|
|
{
|
|
int to_write = stream->data_in_buf;
|
|
|
|
stream->data_in_buf = 0;
|
|
if ((*stream->write) (stream->context, stream->buffer,
|
|
to_write, &stream->error) < to_write)
|
|
{
|
|
stream->error = errno;
|
|
return EOF;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
js_iostream_unget (JSIOStream *stream, int byte)
|
|
{
|
|
if (stream->writep)
|
|
{
|
|
/* We have buffered output data. */
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return EOF;
|
|
|
|
assert (stream->writep == 0);
|
|
}
|
|
|
|
if (stream->bufpos > 0)
|
|
{
|
|
/* It fits. */
|
|
stream->buffer[--stream->bufpos] = byte;
|
|
}
|
|
else if (stream->data_in_buf < stream->buflen)
|
|
{
|
|
move:
|
|
memmove (stream->buffer + 1, stream->buffer, stream->data_in_buf);
|
|
stream->data_in_buf++;
|
|
stream->buffer[0] = byte;
|
|
}
|
|
else
|
|
{
|
|
/* Allocate a bigger buffer. */
|
|
unsigned char *new_buffer = js_realloc (NULL, stream->buffer,
|
|
stream->buflen + 1);
|
|
if (new_buffer == NULL)
|
|
{
|
|
stream->error = errno;
|
|
return EOF;
|
|
}
|
|
|
|
stream->buflen++;
|
|
stream->buffer = new_buffer;
|
|
goto move;
|
|
}
|
|
|
|
/* Upon successful completion, we must return the byte. */
|
|
return byte;
|
|
}
|
|
|
|
|
|
int
|
|
js_iostream_close (JSIOStream *stream)
|
|
{
|
|
int result = 0;
|
|
|
|
if (stream == NULL)
|
|
return result;
|
|
|
|
if (js_iostream_flush (stream) == EOF)
|
|
result = EOF;
|
|
|
|
if (stream->close)
|
|
(*stream->close) (stream->context);
|
|
|
|
js_free (stream->buffer);
|
|
js_free (stream);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int
|
|
js_iostream_seek (JSIOStream *stream, long offset, int whence)
|
|
{
|
|
int result;
|
|
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return -1;
|
|
|
|
result = (*stream->seek) (stream->context, offset, whence);
|
|
if (result == 0)
|
|
/* Successful. Clear the eof flag. */
|
|
stream->at_eof = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
long
|
|
js_iostream_get_position (JSIOStream *stream)
|
|
{
|
|
long pos;
|
|
|
|
/* Flush the possible buffered output. */
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return -1;
|
|
|
|
pos = (*stream->get_position) (stream->context);
|
|
if (pos < 0)
|
|
return pos;
|
|
|
|
/*
|
|
* The logical position if at <bufpos>, the context's idea is at
|
|
* <data_in_buf>. Adjust.
|
|
*/
|
|
return pos - (stream->data_in_buf - stream->bufpos);
|
|
}
|
|
|
|
|
|
long
|
|
js_iostream_get_length (JSIOStream *stream)
|
|
{
|
|
/* Flush the possible buffered output. */
|
|
if (js_iostream_flush (stream) == EOF)
|
|
return -1;
|
|
|
|
return (*stream->get_length) (stream->context);
|
|
}
|
|
|
|
|
|
void
|
|
js_iostream_fill_buffer (JSIOStream *stream)
|
|
{
|
|
if (stream->read == NULL)
|
|
{
|
|
stream->at_eof = 1;
|
|
return;
|
|
}
|
|
|
|
stream->data_in_buf = (*stream->read) (stream->context, stream->buffer,
|
|
stream->buflen, &stream->error);
|
|
stream->bufpos = 0;
|
|
if (stream->data_in_buf == 0)
|
|
stream->at_eof = 1;
|
|
}
|
|
#endif /*CL_EXPERIMENTAL*/
|
|
|