diff --git a/agent/ChangeLog b/agent/ChangeLog index 39888663c..5a1d4611e 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -2,7 +2,7 @@ **MERGED FROM MAIN BRANCH, RE-PATCHED** - * command-ssh.c: New file. + * command-ssh.c, estream.c, estream.h: New file. * findkey.c (modify_description): New function. (agent_key_from_file): New variables: comment, comment_sexp, diff --git a/agent/estream.c b/agent/estream.c new file mode 100644 index 000000000..0bfc9ef72 --- /dev/null +++ b/agent/estream.c @@ -0,0 +1,2376 @@ +/* estream.c - Extended stream I/O/ Library + Copyright (C) 2004 g10 Code GmbH + + This file is part of Libestream. + + Libestream is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + Libestream 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 General Public License + along with Libestream; 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 +#include +#include +#include + +#include + +#include "estream.h" + + + +/* Generally used types. */ + +typedef void *(*func_realloc_t) (void *mem, size_t size); +typedef void (*func_free_t) (void *mem); + + + +/* Buffer management layer. */ + +#define BUFFER_BLOCK_SIZE BUFSIZ + +#define BUFFER_UNREAD_SIZE 16 + + + +/* Macros. */ + +#define BUFFER_ROUND_TO_BLOCK(size, block_size) \ + (((size) + (block_size - 1)) / block_size) + + + +/* Locking. */ + +typedef pth_mutex_t estream_mutex_t; +#define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT +#define ESTREAM_MUTEX_LOCK(mutex) \ + pth_mutex_acquire (&(mutex), 0, NULL) +#define ESTREAM_MUTEX_UNLOCK(mutex) \ + pth_mutex_release (&(mutex)) +#define ESTREAM_MUTEX_TRYLOCK(mutex) \ + ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1) +#define ESTREAM_MUTEX_INITIALIZE(mutex) \ + pth_mutex_init (&(mutex)) + +#define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->internal->lock) +#define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->internal->lock) +#define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->internal->lock) + +/* Primitive system I/O. */ + +#define ESTREAM_SYS_READ pth_read +#define ESTREAM_SYS_WRITE pth_write + +/* Misc definitions. */ + +#define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR) + +/* An internal stream object. */ + +struct estream_internal +{ + estream_mutex_t lock; /* Lock. */ + void *cookie; /* Cookie. */ + void *opaque; /* Opaque data. */ + unsigned int flags; /* Flags. */ + off_t offset; + es_cookie_read_function_t func_read; + es_cookie_write_function_t func_write; + es_cookie_seek_function_t func_seek; + es_cookie_close_function_t func_close; + int strategy; + int fd; + struct + { + unsigned int err: 1; + unsigned int eof: 1; + } indicators; + unsigned int deallocate_buffer: 1; +}; + +/* Stream list. */ + +typedef struct estream_list *estream_list_t; + +struct estream_list +{ + estream_t car; + estream_list_t cdr; + estream_list_t *prev_cdr; +}; + +static estream_list_t estream_list; +static estream_mutex_t estream_list_lock = ESTREAM_MUTEX_INITIALIZER; + +#define ESTREAM_LIST_LOCK ESTREAM_MUTEX_LOCK (estream_list_lock) +#define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock) + + + +/* Macros. */ + +#define ESTREAM_TMPFILE_TEMPLATE "/tmp/estream.XXXXXX" + +/* Calculate array dimension. */ +#define DIM(array) (sizeof (array) / sizeof (*array)) + +/* Evaluate EXPRESSION, setting VARIABLE to the return code, if + VARIABLE is zero. */ +#define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \ + do \ + { \ + tmp_variable = expression; \ + if ((! variable) && tmp_variable) \ + variable = tmp_variable; \ + } \ + while (0) + +/* List manipulation. */ + +static int +es_list_add (estream_t estream) +{ + estream_list_t list_obj; + int ret; + + list_obj = malloc (sizeof (*list_obj)); + if (! list_obj) + ret = -1; + else + { + ESTREAM_LIST_LOCK; + list_obj->car = estream; + list_obj->cdr = estream_list; + list_obj->prev_cdr = &estream_list; + estream_list = list_obj; + ESTREAM_LIST_UNLOCK; + ret = 0; + } + + return ret; +} + +static void +es_list_remove (estream_t estream) +{ + estream_list_t list_obj; + + ESTREAM_LIST_LOCK; + for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr) + if (list_obj->car == estream) + { + *list_obj->prev_cdr = list_obj->cdr; + if (list_obj->cdr) + list_obj->cdr->prev_cdr = list_obj->prev_cdr; + free (list_obj); + break; + } + ESTREAM_LIST_UNLOCK; +} + +typedef int (*estream_iterator_t) (estream_t stream); + +static int +es_list_iterate (estream_iterator_t iterator) +{ + estream_list_t list_obj; + int ret = 0; + + ESTREAM_LIST_LOCK; + for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr) + ret |= (*iterator) (list_obj->car); + ESTREAM_LIST_UNLOCK; + + return ret; +} + + + +static int +es_init_do (void) +{ + int err, ret; + + ret = pth_init (); + if (ret == TRUE) + err = 0; + else + err = -1; + + return err; +} + + + +/* Implementation of Memory I/O. */ + +typedef struct estream_cookie_mem +{ + unsigned int flags; /* Open flags. */ + char *memory; /* Data. */ + size_t memory_size; /* Size of MEMORY. */ + size_t offset; /* Current offset in MEMORY. */ + size_t data_size; /* Size of data in MEMORY. */ + size_t block_size; /* Block size. */ + unsigned int grow: 1; /* MEMORY is allowed to grow. */ + unsigned int append_zero: 1; /* Append zero after data. */ + unsigned int dont_free: 1; /* Append zero after data. */ + char **ptr; + size_t *size; + func_realloc_t func_realloc; + func_free_t func_free; +} *estream_cookie_mem_t; + +static int +es_func_mem_create (void **cookie, + unsigned char *data, size_t data_n, size_t data_size, + size_t block_size, unsigned int grow, + unsigned int append_zero, unsigned int dont_free, + char **ptr, size_t *size, + func_realloc_t func_realloc, func_free_t func_free, + unsigned int flags) +{ + estream_cookie_mem_t mem_cookie; + int err; + + mem_cookie = malloc (sizeof (*mem_cookie)); + if (! mem_cookie) + err = -1; + else + { + mem_cookie->flags = flags; + mem_cookie->memory = data; + mem_cookie->memory_size = data_n; + mem_cookie->offset = 0; + mem_cookie->data_size = data_size; + mem_cookie->block_size = block_size; + mem_cookie->grow = grow ? 1 : 0; + mem_cookie->append_zero = append_zero ? 1 : 0; + mem_cookie->dont_free = dont_free ? 1 : 0; + mem_cookie->ptr = ptr; + mem_cookie->size = size; + mem_cookie->func_realloc = func_realloc ? func_realloc : realloc; + mem_cookie->func_free = func_free ? func_free : free; + mem_cookie->offset = 0; + *cookie = mem_cookie; + err = 0; + } + + return err; +} + +static ssize_t +es_func_mem_read (void *cookie, char *buffer, size_t size) +{ + estream_cookie_mem_t mem_cookie = cookie; + ssize_t ret; + + if (size > mem_cookie->data_size - mem_cookie->offset) + size = mem_cookie->data_size - mem_cookie->offset; + + if (size) + { + memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size); + mem_cookie->offset += size; + } + + ret = size; + + return ret; +} + +static ssize_t +es_func_mem_write (void *cookie, const char *buffer, size_t size) +{ + estream_cookie_mem_t mem_cookie = cookie; + func_realloc_t func_realloc = mem_cookie->func_realloc; + char *memory_new; + size_t newsize; + ssize_t ret; + int err; + + if (size) + { + /* Regular write. */ + + if (mem_cookie->flags & O_APPEND) + /* Append to data. */ + mem_cookie->offset = mem_cookie->data_size; + + if (! mem_cookie->grow) + if (size > mem_cookie->memory_size - mem_cookie->offset) + size = mem_cookie->memory_size - mem_cookie->offset; + + err = 0; + + while (size > (mem_cookie->memory_size - mem_cookie->offset)) + { + memory_new = (*func_realloc) (mem_cookie->memory, + mem_cookie->memory_size + + mem_cookie->block_size); + if (! memory_new) + { + err = -1; + break; + } + else + { + if (mem_cookie->memory != memory_new) + mem_cookie->memory = memory_new; + mem_cookie->memory_size += mem_cookie->block_size; + } + } + if (err) + goto out; + + if (size) + { + memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size); + if (mem_cookie->offset + size > mem_cookie->data_size) + mem_cookie->data_size = mem_cookie->offset + size; + mem_cookie->offset += size; + } + } + else + { + /* Flush. */ + + err = 0; + if (mem_cookie->append_zero) + { + if (mem_cookie->data_size >= mem_cookie->memory_size) + { + newsize = BUFFER_ROUND_TO_BLOCK (mem_cookie->data_size + 1, + mem_cookie->block_size) * mem_cookie->block_size; + + memory_new = (*func_realloc) (mem_cookie->memory, newsize); + if (! memory_new) + { + err = -1; + goto out; + } + + if (mem_cookie->memory != memory_new) + mem_cookie->memory = memory_new; + mem_cookie->memory_size = newsize; + } + + mem_cookie->memory[mem_cookie->data_size + 1] = 0; + } + + /* Return information to user if necessary. */ + if (mem_cookie->ptr) + *mem_cookie->ptr = (char *) mem_cookie->memory; + if (mem_cookie->size) + *mem_cookie->size = mem_cookie->data_size; + } + + out: + + if (err) + ret = -1; + else + ret = size; + + return ret; +} + +static int +es_func_mem_seek (void *cookie, off_t *offset, int whence) +{ + estream_cookie_mem_t mem_cookie = cookie; + off_t pos_new; + int err = 0; + + switch (whence) + { + case SEEK_SET: + pos_new = *offset; + break; + + case SEEK_CUR: + pos_new = mem_cookie->offset += *offset; + break; + + case SEEK_END: + pos_new = mem_cookie->data_size += *offset; + break; + + default: + /* Never reached. */ + pos_new = 0; + } + + if (pos_new >= mem_cookie->memory_size) + { + /* Grow buffer if possible. */ + + if (mem_cookie->grow) + { + func_realloc_t func_realloc = mem_cookie->func_realloc; + size_t newsize; + void *p; + + newsize = BUFFER_ROUND_TO_BLOCK (pos_new, mem_cookie->block_size); + p = (*func_realloc) (mem_cookie->memory, newsize); + if (! p) + { + err = -1; + goto out; + } + else + { + if (mem_cookie->memory != p) + mem_cookie->memory = p; + mem_cookie->memory_size = newsize; + } + } + else + { + errno = EINVAL; + err = -1; + goto out; + } + } + + if (pos_new > mem_cookie->data_size) + /* Fill spare space with zeroes. */ + memset (mem_cookie->memory + mem_cookie->data_size, + 0, pos_new - mem_cookie->data_size); + + mem_cookie->offset = pos_new; + *offset = pos_new; + + out: + + return err; +} + +static int +es_func_mem_destroy (void *cookie) +{ + estream_cookie_mem_t mem_cookie = cookie; + func_free_t func_free = mem_cookie->func_free; + + if (! mem_cookie->dont_free) + (*func_free) (mem_cookie->memory); + free (mem_cookie); + + return 0; +} + +static es_cookie_io_functions_t estream_functions_mem = + { + es_func_mem_read, + es_func_mem_write, + es_func_mem_seek, + es_func_mem_destroy, + }; + +/* Implementation of FD I/O. */ + +typedef struct estream_cookie_fd +{ + int fd; +} *estream_cookie_fd_t; + +static int +es_func_fd_create (void **cookie, int fd, unsigned int flags) +{ + estream_cookie_fd_t fd_cookie; + int err; + + fd_cookie = malloc (sizeof (*fd_cookie)); + if (! fd_cookie) + err = -1; + else + { + fd_cookie->fd = fd; + *cookie = fd_cookie; + err = 0; + } + + return err; +} + +static ssize_t +es_func_fd_read (void *cookie, char *buffer, size_t size) + +{ + estream_cookie_fd_t file_cookie = cookie; + ssize_t bytes_read; + + bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size); + + return bytes_read; +} + +static ssize_t +es_func_fd_write (void *cookie, const char *buffer, size_t size) + +{ + estream_cookie_fd_t file_cookie = cookie; + ssize_t bytes_written; + + bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size); + + return bytes_written; +} + +static int +es_func_fd_seek (void *cookie, off_t *offset, int whence) +{ + estream_cookie_fd_t file_cookie = cookie; + off_t offset_new; + int err; + + offset_new = lseek (file_cookie->fd, *offset, whence); + if (offset_new == -1) + err = -1; + else + { + *offset = offset_new; + err = 0; + } + + return err; +} + +static int +es_func_fd_destroy (void *cookie) +{ + estream_cookie_fd_t fd_cookie = cookie; + int err; + + if (fd_cookie) + { + err = close (fd_cookie->fd); + free (fd_cookie); + } + else + err = 0; + + return err; +} + +static es_cookie_io_functions_t estream_functions_fd = + { + es_func_fd_read, + es_func_fd_write, + es_func_fd_seek, + es_func_fd_destroy + }; + +/* Implementation of File I/O. */ + +static int +es_func_file_create (void **cookie, const char *path, unsigned int flags) +{ + estream_cookie_fd_t file_cookie; + int err; + int fd; + + err = 0; + fd = -1; + + file_cookie = malloc (sizeof (*file_cookie)); + if (! file_cookie) + { + err = -1; + goto out; + } + + fd = open (path, flags, ES_DEFAULT_OPEN_MODE); + if (fd == -1) + { + err = -1; + goto out; + } + + file_cookie->fd = fd; + *cookie = file_cookie; + + out: + + if (err) + free (file_cookie); + + return err; +} + +static es_cookie_io_functions_t estream_functions_file = + { + es_func_fd_read, + es_func_fd_write, + es_func_fd_seek, + es_func_fd_destroy + }; + + + +/* Stream primitives. */ + +static int +es_convert_mode (const char *mode, unsigned int *flags) +{ + struct + { + const char *mode; + unsigned int flags; + } mode_flags[] = { { "r", + O_RDONLY }, + { "rb", + O_RDONLY }, + { "w", + O_WRONLY | O_TRUNC | O_CREAT }, + { "wb", + O_WRONLY | O_TRUNC | O_CREAT }, + { "a", + O_WRONLY | O_APPEND | O_CREAT }, + { "ab", + O_WRONLY | O_APPEND | O_CREAT }, + { "r+", + O_RDWR }, + { "rb+", + O_RDWR }, + { "r+b", + O_RDONLY | O_WRONLY }, + { "w+", + O_RDWR | O_TRUNC | O_CREAT }, + { "wb+", + O_RDWR | O_TRUNC | O_CREAT }, + { "w+b", + O_RDWR | O_TRUNC | O_CREAT }, + { "a+", + O_RDWR | O_CREAT | O_APPEND }, + { "ab+", + O_RDWR | O_CREAT | O_APPEND }, + { "a+b", + O_RDWR | O_CREAT | O_APPEND } }; + unsigned int i; + int err; + + for (i = 0; i < DIM (mode_flags); i++) + if (! strcmp (mode_flags[i].mode, mode)) + break; + if (i == DIM (mode_flags)) + { + errno = EINVAL; + err = -1; + } + else + { + err = 0; + *flags = mode_flags[i].flags; + } + + return err; +} + +static void +es_initialize (estream_t stream, + void *cookie, es_cookie_io_functions_t functions) +{ + stream->internal->cookie = cookie; + stream->internal->opaque = NULL; + stream->internal->offset = 0; + stream->internal->func_read = functions.func_read; + stream->internal->func_write = functions.func_write; + stream->internal->func_seek = functions.func_seek; + stream->internal->func_close = functions.func_close; + stream->internal->strategy = _IOFBF; + stream->internal->indicators.err = 0; + stream->internal->indicators.eof = 0; + stream->internal->deallocate_buffer = 0; + + stream->data_size = 0; + stream->data_offset = 0; + stream->data_flushed = 0; + stream->unread_data_offset = 0; + stream->dirty = 0; +} + +static int +es_flush (estream_t stream) +{ + es_cookie_write_function_t func_write = stream->internal->func_write; + int err; + + if (! func_write) + { + errno = EOPNOTSUPP; + err = -1; + } + else if (stream->dirty && stream->data_offset) + { + size_t data_flushed = 0; + size_t bytes_written; + ssize_t ret; + + err = 0; + /* Note: actually it should be enough to check for + "stream->data_offset - data_flushed" instead of + "(stream->data_offset - data_flushed) > 0", since a write + callback function should never write more than it is asked to + write. But obviously glibc does it this way - FIXME?*/ + while (((stream->data_offset - data_flushed) > 0) && (! err)) + { + ret = (*func_write) (stream->internal->cookie, + stream->buffer + data_flushed, + stream->data_offset - data_flushed); + if (ret == -1) + { + bytes_written = 0; + err = -1; + } + else + bytes_written = ret; + + data_flushed += bytes_written; + if (err) + break; + } + + stream->data_flushed += data_flushed; + if (stream->data_offset == data_flushed) + { + stream->internal->offset += stream->data_offset; + stream->data_offset = 0; + stream->data_flushed = 0; + stream->dirty = 0; + + /* Propagate flush event. */ + (*func_write) (stream->internal->cookie, NULL, 0); + } + } + else + err = 0; + + if (err) + stream->internal->indicators.err = 1; + + return err; +} + +static int +es_deinitialize (estream_t stream) +{ + es_cookie_close_function_t func_close; + int err, tmp_err; + + func_close = stream->internal->func_close; + + err = 0; + SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream)); + if (func_close) + SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->internal->cookie)); + + /* Do not zero out "lock". */ + memset (((unsigned char *) stream->internal) + offsetof (struct estream_internal, cookie), 0, + sizeof (struct estream_internal) - offsetof (struct estream_internal, cookie)); + + /* Do not zero out "internal". */ + memset (((unsigned char *) stream) + offsetof (struct estream_public, buffer), 0, + sizeof (struct estream_public) - offsetof (struct estream_public, buffer)); + + return err; +} + +static int +es_create (estream_t *stream, void *cookie, es_cookie_io_functions_t functions) +{ + estream_t stream_new = NULL; + int initialized = 0; + int err; + + stream_new = malloc (sizeof (*stream_new) + + sizeof (struct estream_internal) + + BUFFER_BLOCK_SIZE + + BUFFER_UNREAD_SIZE); + if (! stream_new) + { + err = -1; + goto out; + } + + stream_new->internal = (void *) (((unsigned char *) stream_new) + + sizeof (*stream_new)); + stream_new->buffer = (void *) (((unsigned char *) stream_new) + + sizeof (*stream_new) + + sizeof (struct estream_internal)); + stream_new->buffer_size = BUFFER_BLOCK_SIZE; + stream_new->unread_buffer = (void *) (((unsigned char *) stream_new) + + sizeof (*stream_new) + + sizeof (struct estream_internal) + + BUFFER_BLOCK_SIZE); + stream_new->unread_buffer_size = BUFFER_UNREAD_SIZE; + + ESTREAM_MUTEX_INITIALIZE (stream_new->internal->lock); + es_initialize (stream_new, cookie, functions); + initialized = 1; + + err = es_list_add (stream_new); + if (err) + goto out; + + *stream = stream_new; + + out: + + if (err) + { + if (stream_new) + { + if (initialized) + es_deinitialize (stream_new); + free (stream_new); + } + } + + return err; +} + +static int +es_destroy (estream_t stream) +{ + int err = 0; + + if (stream) + { + es_list_remove (stream); + err = es_deinitialize (stream); + free (stream); + } + + return err; +} + +static void +es_empty (estream_t stream) +{ + stream->data_size = 0; + stream->data_offset = 0; + stream->unread_data_offset = 0; +} + +static int +es_fill (estream_t stream) +{ + size_t bytes_read = 0; + int err; + + if (! stream->internal->func_read) + { + errno = EOPNOTSUPP; + err = -1; + } + else + { + es_cookie_read_function_t func_read = stream->internal->func_read; + ssize_t ret; + + ret = (*func_read) (stream->internal->cookie, + stream->buffer, stream->buffer_size); + if (ret == -1) + { + bytes_read = 0; + err = -1; + } + else + { + bytes_read = ret; + err = 0; + } + } + + if (err) + stream->internal->indicators.err = 1; + else if (! bytes_read) + stream->internal->indicators.eof = 1; + + stream->internal->offset += stream->data_size; + stream->data_size = bytes_read; + stream->data_offset = 0; + + return err; +} + +static int +es_read_nbf (estream_t stream, + unsigned char *buffer, + size_t bytes_to_read, size_t *bytes_read) +{ + es_cookie_read_function_t func_read = stream->internal->func_read; + size_t data_read; + ssize_t ret; + int err; + + data_read = 0; + err = 0; + + while (bytes_to_read - data_read) + { + ret = (*func_read) (stream->internal->cookie, + buffer + data_read, bytes_to_read - data_read); + if (ret == -1) + { + err = -1; + break; + } + else if (ret) + data_read += ret; + else + break; + } + + stream->internal->offset += data_read; + *bytes_read = data_read; + + return err; +} + +static int +es_read_fbf (estream_t stream, + unsigned char *buffer, + size_t bytes_to_read, size_t *bytes_read) +{ + size_t data_available; + size_t data_to_read; + size_t data_read; + int err; + + data_read = 0; + err = 0; + + while ((bytes_to_read - data_read) && (! err)) + { + if (stream->data_offset == stream->data_size) + { + /* Nothing more to read in current container, try to + fill container with new data. */ + err = es_fill (stream); + if (! err) + if (! stream->data_size) + /* Filling did not result in any data read. */ + break; + } + + if (! err) + { + /* Filling resulted in some new data. */ + + data_to_read = bytes_to_read - data_read; + data_available = stream->data_size - stream->data_offset; + if (data_to_read > data_available) + data_to_read = data_available; + + memcpy (buffer + data_read, + stream->buffer + stream->data_offset, data_to_read); + stream->data_offset += data_to_read; + data_read += data_to_read; + } + } + + *bytes_read = data_read; + + return err; +} + +static int +es_read_lbf (estream_t stream, + unsigned char *buffer, + size_t bytes_to_read, size_t *bytes_read) +{ + int err; + + err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read); + + return err; +} + +static int +es_readn (estream_t stream, + unsigned char *buffer, + size_t bytes_to_read, size_t *bytes_read) +{ + size_t data_read_unread, data_read; + int err; + + data_read_unread = 0; + data_read = 0; + err = 0; + + if (stream->dirty) + /* Switching to reading mode -> flush output. */ + es_flush (stream); + + /* Read unread data first. */ + while ((bytes_to_read - data_read_unread) && stream->unread_data_offset) + { + buffer[data_read_unread] = stream->unread_buffer[stream->unread_data_offset - 1]; + stream->unread_data_offset--; + data_read_unread++; + } + + switch (stream->internal->strategy) + { + case _IONBF: + err = es_read_nbf (stream, + buffer + data_read_unread, + bytes_to_read - data_read_unread, &data_read); + break; + case _IOLBF: + err = es_read_lbf (stream, + buffer + data_read_unread, + bytes_to_read - data_read_unread, &data_read); + break; + case _IOFBF: + err = es_read_fbf (stream, + buffer + data_read_unread, + bytes_to_read - data_read_unread, &data_read); + break; + } + + if (bytes_read) + *bytes_read = data_read_unread + data_read; + + return err; +} + +static void +es_unreadn (estream_t stream, + const unsigned char *data, size_t data_n, size_t *bytes_unread) +{ + size_t space_left; + + space_left = stream->unread_buffer_size - stream->unread_data_offset; + + if (data_n > space_left) + data_n = space_left; + + if (! data_n) + goto out; + + memcpy (stream->unread_buffer + stream->unread_data_offset, data, data_n); + stream->unread_data_offset += data_n; + stream->internal->indicators.eof = 0; + + out: + + if (bytes_unread) + *bytes_unread = data_n; +} + +static int +es_seek (estream_t stream, off_t offset, int whence, off_t *offset_new) +{ + es_cookie_seek_function_t func_seek = stream->internal->func_seek; + int err, ret; + off_t off; + + if (! func_seek) + { + errno = EOPNOTSUPP; + err = -1; + goto out; + } + + /* Flush data first in order to prevent flushing it to the wrong + offset. */ + err = es_flush (stream); + if (err) + goto out; + + off = offset; + + if (whence == SEEK_CUR) + /* FIXME: this seems to be correct, but I'm not so happy with this + exception - isn't there a nicer way? */ + off = off - stream->data_size + stream->data_offset; + + ret = (*func_seek) (stream->internal->cookie, &off, whence); + if (ret == -1) + { + err = -1; + goto out; + } + else + err = 0; + + if (offset_new) + *offset_new = off; + + /* FIXME: verify. */ + + /* Discard input data. */ + es_empty (stream); + + stream->internal->indicators.eof = 0; + stream->internal->offset = off; + + out: + + if (err) + stream->internal->indicators.err = 1; + + return err; +} + +static int +es_write_nbf (estream_t stream, + const unsigned char *buffer, + size_t bytes_to_write, size_t *bytes_written) +{ + es_cookie_write_function_t func_write = stream->internal->func_write; + size_t data_written; + ssize_t ret; + int err; + + data_written = 0; + err = 0; + + while (bytes_to_write - data_written) + { + ret = (*func_write) (stream->internal->cookie, + buffer + data_written, bytes_to_write - data_written); + if (ret == -1) + { + err = -1; + break; + } + else + data_written += ret; + } + + stream->internal->offset += data_written; + *bytes_written = data_written; + + return err; +} + +static int +es_write_fbf (estream_t stream, + const unsigned char *buffer, + size_t bytes_to_write, size_t *bytes_written) +{ + size_t space_available; + size_t data_to_write; + size_t data_written; + int err; + + data_written = 0; + err = 0; + + while ((bytes_to_write - data_written) && (! err)) + { + if (stream->data_offset == stream->buffer_size) + /* Container full, flush buffer. */ + err = es_flush (stream); + + if (! err) + { + /* Flushing resulted in empty container. */ + + data_to_write = bytes_to_write - data_written; + space_available = stream->buffer_size - stream->data_offset; + if (data_to_write > space_available) + data_to_write = space_available; + + memcpy (stream->buffer + stream->data_offset, + buffer + data_written, data_to_write); + stream->data_offset += data_to_write; + data_written += data_to_write; + if (! stream->dirty) + stream->dirty = 1; + } + } + + *bytes_written = data_written; + + return err; +} + +static int +es_write_lbf (estream_t stream, + const unsigned char *buffer, + size_t bytes_to_write, size_t *bytes_written) +{ + size_t data_flushed; + size_t data_buffered; + unsigned int i; + int err; + + err = 0; + data_flushed = 0; + data_buffered = 0; + + for (i = bytes_to_write; i > 0; i--) + if (buffer[i - 1] == '\n') + break; + + /* Found a newline, directly write up to (including) this + character. */ + err = es_flush (stream); + if (err) + goto out; + err = es_write_nbf (stream, buffer, i, &data_flushed); + if (err) + goto out; + + /* Write remaining data fully buffered. */ + err = es_write_fbf (stream, buffer + data_flushed, + bytes_to_write - data_flushed, &data_buffered); + if (err) + goto out; + + out: + + *bytes_written = data_flushed + data_buffered; + + return err; +} + + +static int +es_writen (estream_t stream, + const unsigned char *buffer, + size_t bytes_to_write, size_t *bytes_written) +{ + size_t data_written; + int err; + + data_written = 0; + err = 0; + + if ((! stream->dirty) && stream->data_size) + { + /* Switching to writing mode -> discard input data and seek to + position at which reading has stopped. */ + + err = es_seek (stream, 0, SEEK_CUR, NULL); + if (err) + goto out; + } + + switch (stream->internal->strategy) + { + case _IONBF: + err = es_write_nbf (stream, buffer, bytes_to_write, &data_written); + break; + + case _IOLBF: + err = es_write_lbf (stream, buffer, bytes_to_write, &data_written); + break; + + case _IOFBF: + err = es_write_fbf (stream, buffer, bytes_to_write, &data_written); + break; + } + + out: + + if (bytes_written) + *bytes_written = data_written; + + return err; +} + +static int +es_peek (estream_t stream, unsigned char **data, size_t *data_size) +{ + int err; + + if (stream->data_offset == stream->data_size) + /* Refill container. */ + err = es_fill (stream); + else + err = 0; + + if (! err) + { + if (data) + *data = stream->buffer + stream->data_offset; + if (data_size) + *data_size = stream->data_size - stream->data_offset; + } + + return err; +} + +/* Skip SIZE bytes of input data contained in buffer. */ +static int +es_skip (estream_t stream, size_t size) +{ + int err; + + if (stream->data_offset + size > stream->data_size) + { + errno = EINVAL; + err = -1; + } + else + { + stream->data_offset += size; + err = 0; + } + + return err; +} + +static int +es_read_line (estream_t stream, size_t max_length, + char **line, size_t *line_length) +{ + size_t space_left; + size_t line_size; + estream_t line_stream; + char *line_new; + void *line_stream_cookie; + char *newline; + unsigned char *data; + size_t data_size; + int err; + + line_new = NULL; + line_stream = NULL; + line_stream_cookie = NULL; + + err = es_func_mem_create (&line_stream_cookie, NULL, 0, 0, BUFFER_BLOCK_SIZE, + 1, 0, 0, NULL, 0, realloc, free, O_RDWR); + if (err) + goto out; + + err = es_create (&line_stream, line_stream_cookie, estream_functions_mem); + if (err) + goto out; + + space_left = max_length; + line_size = 0; + while (1) + { + if (max_length && (space_left == 1)) + break; + + err = es_peek (stream, &data, &data_size); + if (err || (! data_size)) + break; + + if (data_size > (space_left - 1)) + data_size = space_left - 1; + + newline = memchr (data, '\n', data_size); + if (newline) + { + data_size = (newline - (char *) data) + 1; + err = es_write (line_stream, data, data_size, NULL); + if (! err) + { + space_left -= data_size; + line_size += data_size; + es_skip (stream, data_size); + break; + } + } + else + { + err = es_write (line_stream, data, data_size, NULL); + if (! err) + { + space_left -= data_size; + line_size += data_size; + es_skip (stream, data_size); + } + } + if (err) + break; + } + if (err) + goto out; + + /* Complete line has been written to line_stream. */ + + if ((max_length > 1) && (! line_size)) + { + stream->internal->indicators.eof = 1; + goto out; + } + + err = es_seek (line_stream, 0, SEEK_SET, NULL); + if (err) + goto out; + + if (! *line) + { + line_new = malloc (line_size + 1); + if (! line_new) + { + err = -1; + goto out; + } + } + else + line_new = *line; + + err = es_read (line_stream, line_new, line_size, NULL); + if (err) + goto out; + + line_new[line_size] = '\0'; + + if (! *line) + *line = line_new; + if (line_length) + *line_length = line_size; + + out: + + if (line_stream) + es_destroy (line_stream); + else if (line_stream_cookie) + es_func_mem_destroy (line_stream_cookie); + + if (err) + { + if (! *line) + free (line_new); + stream->internal->indicators.err = 1; + } + + return err; +} + +static int +es_print (estream_t restrict stream, const char * restrict format, va_list ap) +{ + char data[BUFFER_BLOCK_SIZE]; + size_t bytes_written; + size_t bytes_read; + FILE *tmp_stream; + int err; + + bytes_written = 0; + tmp_stream = NULL; + err = 0; + + tmp_stream = tmpfile (); + if (err) + goto out; + + err = vfprintf (tmp_stream, format, ap); + if (err < 0) + goto out; + + err = fseek (tmp_stream, 0, SEEK_SET); + if (err) + goto out; + + while (1) + { + bytes_read = fread (data, 1, sizeof (data), tmp_stream); + if (ferror (tmp_stream)) + { + err = -1; + break; + } + + err = es_writen (stream, data, bytes_read, NULL); + if (err) + break; + else + bytes_written += bytes_read; + if (feof (tmp_stream)) + break; + } + if (err) + goto out; + + out: + + if (tmp_stream) + fclose (tmp_stream); + + return err ? -1 : bytes_written; +} + +static void +es_set_indicators (estream_t stream, int err, int eof) +{ + if (err != -1) + stream->internal->indicators.err = err ? 1 : 0; + if (eof != -1) + stream->internal->indicators.eof = eof ? 1 : 0; +} + +static int +es_get_indicator (estream_t stream, int err, int eof) +{ + int ret = 0; + + if (err) + ret = stream->internal->indicators.err; + else if (eof) + ret = stream->internal->indicators.eof; + + return ret; +} + +static int +es_set_buffering (estream_t stream, + char * restrict buffer, int mode, size_t size) +{ + int err; + + /* Flush buffer or discard input data. */ + if (stream->dirty) + err = es_flush (stream); + else + { + es_empty (stream); + es_set_indicators (stream, -1, 0); + err = 0; + } + if (err) + goto out; + + /* Free old buffer in case that was allocated by this function. */ + if (stream->internal->deallocate_buffer) + { + stream->internal->deallocate_buffer = 0; + free (stream->buffer); + stream->buffer = NULL; + } + + if (mode == _IONBF) + stream->buffer_size = 0; + else + { + void *buffer_new; + + if (buffer) + buffer_new = buffer; + else + { + buffer_new = malloc (size); + if (! buffer_new) + { + err = -1; + goto out; + } + } + + stream->buffer = buffer_new; + stream->buffer_size = size; + if (! buffer) + stream->internal->deallocate_buffer = 1; + } + stream->internal->strategy = mode; + + out: + + return err; +} + +static off_t +es_offset_calculate (estream_t stream) +{ + off_t offset; + + offset = stream->internal->offset + stream->data_offset; + + return offset; +} + +static void +es_opaque_ctrl (estream_t stream, void *opaque_new, void **opaque_old) +{ + if (opaque_old) + *opaque_old = stream->internal->opaque; + if (opaque_new) + stream->internal->opaque = opaque_new; +} + + + +/* API. */ + +int +es_init (void) +{ + int err; + + err = es_init_do (); + + return err; +} + +estream_t +es_fopen (const char * restrict path, const char * restrict mode) +{ + unsigned int flags; + int create_called; + estream_t stream; + void *cookie; + int err; + + stream = NULL; + cookie = NULL; + create_called = 0; + + err = es_convert_mode (mode, &flags); + if (err) + goto out; + + err = es_func_file_create (&cookie, path, flags); + if (err) + goto out; + + create_called = 1; + err = es_create (&stream, cookie, estream_functions_file); + if (err) + goto out; + + out: + + if (err && create_called) + (*estream_functions_file.func_close) (cookie); + + return stream; +} + +estream_t +es_mopen (unsigned char *data, size_t data_n, size_t data_size, + unsigned int grow, + func_realloc_t func_realloc, func_free_t func_free, + const char * restrict mode) +{ + unsigned int flags; + int create_called; + estream_t stream; + void *cookie; + int err; + + /* FIXME: check for sanity. */ + + cookie = 0; + stream = NULL; + create_called = 0; + + err = es_convert_mode (mode, &flags); + if (err) + goto out; + + err = es_func_mem_create (&cookie, data, data_n, data_size, + BUFFER_BLOCK_SIZE, grow, 0, 0, + NULL, 0, func_realloc, func_free, flags); + if (err) + goto out; + + create_called = 1; + err = es_create (&stream, cookie, estream_functions_mem); + if (err) + goto out; + + out: + + if (err && create_called) + (*estream_functions_mem.func_close) (cookie); + + return stream; +} + +estream_t +es_open_memstream (char **ptr, size_t *size) +{ + unsigned int flags; + int create_called; + estream_t stream; + void *cookie; + int err; + + /* FIXME: check for sanity. */ + + flags = O_RDWR; + create_called = 0; + stream = NULL; + cookie = 0; + + err = es_func_mem_create (&cookie, NULL, 0, 0, + BUFFER_BLOCK_SIZE, 1, 1, 1, + ptr, size, realloc, free, flags); + if (err) + goto out; + + create_called = 1; + err = es_create (&stream, cookie, estream_functions_mem); + if (err) + goto out; + + out: + + if (err && create_called) + (*estream_functions_mem.func_close) (cookie); + + return stream; +} + +estream_t +es_fopencookie (void *cookie, + const char * restrict mode, es_cookie_io_functions_t functions) +{ + unsigned int flags; + estream_t stream; + int err; + + stream = NULL; + flags = 0; + + err = es_convert_mode (mode, &flags); + if (err) + goto out; + + err = es_create (&stream, cookie, functions); + if (err) + goto out; + + out: + + return stream; +} + +estream_t +es_fdopen (int filedes, const char *mode) +{ + unsigned int flags; + int create_called; + estream_t stream; + void *cookie; + int err; + + stream = NULL; + cookie = NULL; + create_called = 0; + + err = es_convert_mode (mode, &flags); + if (err) + goto out; + + err = es_func_fd_create (&cookie, filedes, flags); + if (err) + goto out; + + create_called = 1; + err = es_create (&stream, cookie, estream_functions_fd); + if (err) + goto out; + + out: + + if (err && create_called) + (*estream_functions_fd.func_close) (cookie); + + return stream; +} + +estream_t +es_freopen (const char *path, const char *mode, estream_t stream) +{ + int err; + + if (stream) + { + unsigned int flags; + int create_called; + void *cookie; + + cookie = NULL; + create_called = 0; + + ESTREAM_LOCK (stream); + + es_deinitialize (stream); + + err = es_convert_mode (mode, &flags); + if (err) + goto leave; + + err = es_func_file_create (&cookie, path, flags); + if (err) + goto leave; + + create_called = 1; + es_initialize (stream, cookie, estream_functions_file); + + leave: + + if (err) + { + if (create_called) + es_func_fd_destroy (cookie); + + es_destroy (stream); + stream = NULL; + } + else + ESTREAM_UNLOCK (stream); + } + else + { + /* FIXME? We don't support re-opening at the moment. */ + errno = EINVAL; + es_deinitialize (stream); + es_destroy (stream); + stream = NULL; + } + + return stream; +} + +int +es_fclose (estream_t stream) +{ + int err; + + err = es_destroy (stream); + + return err; +} + +int +es_fileno_unlocked (estream_t stream) +{ + int fd; + + /* FIXME: this is ugly. */ + if (stream->internal->func_read == es_func_fd_read) + fd = ((estream_cookie_fd_t) stream->internal->cookie)->fd; + else + fd = -1; + + return fd; +} + +void +es_flockfile (estream_t stream) +{ + ESTREAM_LOCK (stream); +} + +int +es_ftrylockfile (estream_t stream) +{ + int ret; + + ret = ESTREAM_TRYLOCK (stream); + + return ret; +} + +void +es_funlockfile (estream_t stream) +{ + ESTREAM_UNLOCK (stream); +} + +int +es_fileno (estream_t stream) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_fileno_unlocked (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +int +es_feof_unlocked (estream_t stream) +{ + return es_get_indicator (stream, 0, 1); +} + +int +es_feof (estream_t stream) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_feof_unlocked (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +int +es_ferror_unlocked (estream_t stream) +{ + return es_get_indicator (stream, 1, 0); +} + +int +es_ferror (estream_t stream) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_ferror_unlocked (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +void +es_clearerr_unlocked (estream_t stream) +{ + es_set_indicators (stream, 0, 0); +} + +void +es_clearerr (estream_t stream) +{ + ESTREAM_LOCK (stream); + es_clearerr_unlocked (stream); + ESTREAM_UNLOCK (stream); +} + +int +es_fflush (estream_t stream) +{ + int err; + + if (stream) + { + ESTREAM_LOCK (stream); + err = es_flush (stream); + ESTREAM_UNLOCK (stream); + } + else + err = es_list_iterate (es_fflush); + + return err ? EOF : 0; +} + +int +es_fseek (estream_t stream, long int offset, int whence) +{ + int err; + + ESTREAM_LOCK (stream); + err = es_seek (stream, offset, whence, NULL); + ESTREAM_UNLOCK (stream); + + return err; +} + +int +es_fseeko (estream_t stream, off_t offset, int whence) +{ + int err; + + ESTREAM_LOCK (stream); + err = es_seek (stream, offset, whence, NULL); + ESTREAM_UNLOCK (stream); + + return err; +} + +long int +es_ftell (estream_t stream) +{ + long int ret; + + ESTREAM_LOCK (stream); + ret = es_offset_calculate (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +off_t +es_ftello (estream_t stream) +{ + off_t ret = -1; + + ESTREAM_LOCK (stream); + ret = es_offset_calculate (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +void +es_rewind (estream_t stream) +{ + ESTREAM_LOCK (stream); + es_seek (stream, 0L, SEEK_SET, NULL); + es_set_indicators (stream, 0, -1); + ESTREAM_UNLOCK (stream); +} + +int +_es_getc_underflow (estream_t stream) +{ + int err; + unsigned char c; + size_t bytes_read; + + err = es_readn (stream, &c, 1, &bytes_read); + + return (err || (! bytes_read)) ? EOF : c; +} + +int +_es_putc_overflow (int c, estream_t stream) +{ + unsigned char d = c; + int err; + + err = es_writen (stream, &d, 1, NULL); + + return err ? EOF : c; +} + +int +es_fgetc (estream_t stream) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_getc_unlocked (stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +int +es_fputc (int c, estream_t stream) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_putc_unlocked (c, stream); + ESTREAM_UNLOCK (stream); + + return ret; +} + +int +es_ungetc (int c, estream_t stream) +{ + unsigned char data[] = { c }; + size_t data_unread; + + ESTREAM_LOCK (stream); + es_unreadn (stream, data, sizeof (data), &data_unread); + ESTREAM_UNLOCK (stream); + + return data_unread ? c : EOF; +} + +int +es_read (estream_t stream, + char *buffer, size_t bytes_to_read, size_t *bytes_read) +{ + int err; + + if (bytes_to_read) + { + ESTREAM_LOCK (stream); + err = es_readn (stream, buffer, bytes_to_read, bytes_read); + ESTREAM_UNLOCK (stream); + } + else + err = 0; + + return err; +} + +int +es_write (estream_t stream, + const char *buffer, size_t bytes_to_write, size_t *bytes_written) +{ + int err; + + if (bytes_to_write) + { + ESTREAM_LOCK (stream); + err = es_writen (stream, buffer, bytes_to_write, bytes_written); + ESTREAM_UNLOCK (stream); + } + else + err = 0; + + return err; +} + +size_t +es_fread (void * restrict ptr, size_t size, size_t nitems, + estream_t restrict stream) +{ + int err; + size_t ret, bytes; + + if (size * nitems) + { + ESTREAM_LOCK (stream); + err = es_readn (stream, ptr, size * nitems, &bytes); + ESTREAM_UNLOCK (stream); + + ret = bytes / size; + } + else + ret = 0; + + return ret; +} + +size_t +es_fwrite (const void * restrict ptr, size_t size, size_t nitems, + estream_t restrict stream) +{ + int err; + size_t ret, bytes; + + if (size * nitems) + { + ESTREAM_LOCK (stream); + err = es_writen (stream, ptr, size * nitems, &bytes); + ESTREAM_UNLOCK (stream); + + ret = bytes / size; + } + else + ret = 0; + + return ret; +} + +char * +es_fgets (char * restrict s, int n, estream_t restrict stream) +{ + char *ret = NULL; + + if (n) + { + size_t length; + int err; + + ESTREAM_LOCK (stream); + err = es_read_line (stream, n, &s, &length); + ESTREAM_UNLOCK (stream); + if (! err) + ret = s; + } + + return ret; +} + +int +es_fputs (const char * restrict s, estream_t restrict stream) +{ + size_t length; + int err; + + length = strlen (s); + ESTREAM_LOCK (stream); + err = es_writen (stream, s, length, NULL); + ESTREAM_UNLOCK (stream); + + return err ? EOF : 0; +} + +ssize_t +es_getline (char **lineptr, size_t *n, estream_t stream) +{ + char *line = NULL; + size_t line_n = 0; + int err; + + ESTREAM_LOCK (stream); + err = es_read_line (stream, 0, &line, &line_n); + ESTREAM_UNLOCK (stream); + if (err) + goto out; + + if (*lineptr || *n) + { + if (*n < (line_n + 1)) + { + void *p; + + /* FIXME: align? */ + p = realloc (*lineptr, line_n); + if (! p) + { + free (line); + err = -1; + goto out; + } + else + { + if (*lineptr != p) + *lineptr = p; + } + } + + memcpy (*lineptr, line, line_n + 1); + if (*n != line_n) + *n = line_n; + } + else + { + *lineptr = line; + *n = line_n; + } + + out: + + return err ? err : line_n; +} + +int +es_vfprintf (estream_t restrict stream, const char *restrict format, + va_list ap) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_print (stream, format, ap); + ESTREAM_UNLOCK (stream); + + return ret; +} + +int +es_fprintf (estream_t restrict stream, const char * restrict format, ...) +{ + int ret; + + va_list ap; + va_start (ap, format); + ESTREAM_LOCK (stream); + ret = es_print (stream, format, ap); + ESTREAM_UNLOCK (stream); + va_end (ap); + + return ret; +} + +estream_t +es_tmpfile (void) +{ + char template[] = ESTREAM_TMPFILE_TEMPLATE; + unsigned int flags; + int create_called; + estream_t stream; + void *cookie; + int err; + int fd; + + create_called = 0; + stream = NULL; + flags = O_RDWR | O_TRUNC | O_CREAT; /* FIXME - correct? */ + cookie = NULL; + fd = -1; + + fd = mkstemp (template); + if (fd == -1) + { + err = -1; + goto out; + } + + err = unlink (template); + if (err == -1) + goto out; + + err = es_func_fd_create (&cookie, fd, flags); + if (err) + goto out; + + create_called = 1; + err = es_create (&stream, cookie, estream_functions_fd); + if (err) + goto out; + + out: + + if (err) + { + if (create_called) + es_func_fd_destroy (cookie); + else if (fd != -1) + close (fd); + stream = NULL; + } + + return stream; +} + +int +es_setvbuf (estream_t restrict stream, + char * restrict buf, int type, size_t size) +{ + int err; + + if (((type == _IOFBF) || (type == _IOLBF) || (type == _IONBF)) + && (! ((! size) && (type != _IONBF)))) + { + ESTREAM_LOCK (stream); + err = es_set_buffering (stream, buf, type, size); + ESTREAM_UNLOCK (stream); + } + else + { + errno = EINVAL; + err = -1; + } + + return err; +} + +void +es_setbuf (estream_t restrict stream, char * restrict buf) +{ + ESTREAM_LOCK (stream); + es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); + ESTREAM_UNLOCK (stream); +} + +void +es_opaque_set (estream_t stream, void *opaque) +{ + ESTREAM_LOCK (stream); + es_opaque_ctrl (stream, opaque, NULL); + ESTREAM_UNLOCK (stream); +} + +void * +es_opaque_get (estream_t stream) +{ + void *opaque; + + ESTREAM_LOCK (stream); + es_opaque_ctrl (stream, NULL, &opaque); + ESTREAM_UNLOCK (stream); + + return opaque; +} diff --git a/agent/estream.h b/agent/estream.h new file mode 100644 index 000000000..d5a1d2e5f --- /dev/null +++ b/agent/estream.h @@ -0,0 +1,159 @@ +/* estream.h - Extended stream I/O/ Library + Copyright (C) 2004 g10 Code GmbH + + This file is part of Libestream. + + Libestream is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + Libestream 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 General Public License + along with Libestream; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef ESTREAM_H +#define ESTREAM_H + +#include +#include +#include + + + +typedef struct estream_public *estream_t; +typedef struct estream_internal *estream_internal_t; + +/* This struct is entirely private - use it and you will shoot + yourself in the foot. */ +struct estream_public +{ + estream_internal_t internal; + unsigned char *buffer; + size_t buffer_size; + size_t data_size; + off_t data_offset; + size_t data_flushed; + unsigned char *unread_buffer; + size_t unread_buffer_size; + off_t unread_data_offset; + unsigned int dirty: 1; +}; + +typedef ssize_t (*es_cookie_read_function_t) (void *cookie, + char *buffer, size_t size); +typedef ssize_t (*es_cookie_write_function_t) (void *cookie, + const char *buffer, size_t size); +typedef int (*es_cookie_seek_function_t) (void *cookie, + off_t *pos, int whence); +typedef int (*es_cookie_close_function_t) (void *cookie); + +typedef struct es_cookie_io_functions +{ + es_cookie_read_function_t func_read; + es_cookie_write_function_t func_write; + es_cookie_seek_function_t func_seek; + es_cookie_close_function_t func_close; +} es_cookie_io_functions_t; + +#define restrict + +int es_init (void); + +estream_t es_fopen (const char * restrict path, const char * restrict mode); +estream_t es_mopen (unsigned char *data, size_t data_n, size_t data_size, + unsigned int grow, + void *(*func_realloc) (void *mem, size_t size), + void (*func_free) (void *mem), + const char * restrict mode); +estream_t es_open_memstream (char **ptr, size_t *size); +estream_t es_fdopen (int filedes, const char *mode); +estream_t es_freopen (const char *path, const char *mode, estream_t stream); +estream_t es_fopencookie (void *cookie, + const char * restrict mode, es_cookie_io_functions_t functions); +int es_fclose (estream_t stream); +int es_fileno (estream_t stream); +int es_fileno_unlocked (estream_t stream); + +void es_flockfile (estream_t stream); +int es_ftrylockfile (estream_t stream); +void es_funlockfile (estream_t stream); + +int es_feof (estream_t stream); +int es_feof_unlocked (estream_t stream); +int es_ferror (estream_t stream); +int es_ferror_unlocked (estream_t stream); +void es_clearerr (estream_t stream); +void es_clearerr_unlocked (estream_t stream); + +int es_fflush (estream_t stream); +int es_fseek (estream_t stream, long int offset, int whence); +int es_fseeko (estream_t stream, off_t offset, int whence); +long int es_ftell (estream_t stream); +off_t es_ftello (estream_t stream); +void es_rewind (estream_t stream); + +int es_fgetc (estream_t stream); +int es_fputc (int c, estream_t stream); + +int _es_getc_underflow (estream_t stream); +int _es_putc_overflow (int c, estream_t stream); + +#define es_getc_unlocked(stream) \ + (((! (stream)->dirty) \ + && ((stream)->data_offset < (stream)->data_size) \ + && (! (stream)->unread_data_offset)) ? \ + ((int) (unsigned char) \ + (stream)->buffer[((stream)->data_offset)++]) : \ + _es_getc_underflow ((stream))) + +#define es_putc_unlocked(c, stream) \ + (((stream)->dirty \ + && ((stream)->data_offset < (stream)->buffer_size) \ + && (c != '\n')) ? \ + ((int) (unsigned char) \ + (stream)->buffer[((stream)->data_offset)++] = (c)) : \ + _es_putc_overflow ((c), (stream))) + +#define es_getc(stream) \ + es_fgetc (stream) +#define es_putc(c, stream) \ + es_fputc (c, stream) + +int es_ungetc (int c, estream_t stream); + +int es_read (estream_t stream, + char *buffer, size_t bytes_to_read, size_t *bytes_read); +int es_write (estream_t stream, + const char *buffer, size_t bytes_to_write, size_t *bytes_written); + +size_t es_fread (void * restrict ptr, size_t size, size_t nitems, + estream_t restrict stream); +size_t es_fwrite (const void * restrict ptr, size_t size, size_t memb, + estream_t restrict stream); + +char *es_fgets (char * restrict s, int n, estream_t restrict stream); +int es_fputs (const char * restrict s, estream_t restrict stream); + +ssize_t es_getline (char **lineptr, size_t *n, estream_t stream); + +int es_fprintf (estream_t restrict stream, const char * restrict format, ...); +int es_vfprintf (estream_t restrict stream, const char *restrict format, + va_list ap); + +int es_setvbuf (estream_t restrict stream, + char * restrict buf, int mode, size_t size); +void es_setbuf (estream_t restrict stream, char * restrict buf); + +estream_t es_tmpfile (void); + +void es_opaque_set (estream_t stream, void *opaque); +void *es_opaque_get (estream_t stream); + +#endif diff --git a/agent/gpg-stream-config.h b/agent/gpg-stream-config.h deleted file mode 100644 index 80353aff9..000000000 --- a/agent/gpg-stream-config.h +++ /dev/null @@ -1,41 +0,0 @@ -/* stream.c - Stream I/O/ layer - Copyright (C) 2004 g10 Code GmbH - - This file is part of libgpg-stream. - - libgpg-stream is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - libgpg-stream 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 General Public License - along with libgpg-stream; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef GPG_STREAM_CONFIG_H -#define GPG_STREAM_CONFIG_H - -#define USE_PTH - -#ifdef USE_PTH -#include - -#define READ pth_read -#define WRITE pth_write - -#else - -#include - -#define READ read -#define WRITE write - -#endif - -#endif diff --git a/agent/gpg-stream.c b/agent/gpg-stream.c deleted file mode 100644 index e8baf0e32..000000000 --- a/agent/gpg-stream.c +++ /dev/null @@ -1,1440 +0,0 @@ -/* stream.c - Stream I/O/ layer - Copyright (C) 2004 g10 Code GmbH - - This file is part of libgpg-stream. - - libgpg-stream is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - libgpg-stream 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 General Public License - along with libgpg-stream; 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 -#include -#include -#include - -#include - -#include "gpg-stream.h" -#include "gpg-stream-config.h" - - - -/* Generally used types. */ - -typedef void *(*func_realloc_t) (void *mem, size_t size); -typedef void (*func_free_t) (void *mem); - - - -/* Buffer management layer. */ - -#define BUFFER_BLOCK_SIZE 1024 - - - -typedef struct buffer *buffer_t; - -/* Callbacks, necessary for filling/flushing/seeking. */ -typedef gpg_error_t (*buffer_func_read_t) (void *handle, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read); -typedef gpg_error_t (*buffer_func_write_t) (void *handle, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written); -typedef gpg_error_t (*buffer_func_seek_t) (void *handle, - off_t offset, - int whence); - -typedef gpg_error_t (*buffer_func_stat_t) (void *handle, - size_t *size); - -typedef struct buffer_functions -{ - buffer_func_read_t func_read; /* Read callback. */ - buffer_func_write_t func_write; /* Write callback. */ - buffer_func_seek_t func_seek; /* Seek callback. */ - buffer_func_stat_t func_stat; /* Stat callback. */ -} buffer_functions_t; - -typedef struct buffer_conspec -{ - size_t block_size; - func_realloc_t func_realloc; - func_free_t func_free; -} buffer_conspec_t; - -/* Buffer context. */ -struct buffer -{ - void *handle; /* Handle, passed to callbacks. */ - buffer_conspec_t conspec; /* Container spec. */ - buffer_functions_t functions; /* Callback functions. */ - unsigned int flags; /* General flags. */ - struct buffer_in - { - char *container; /* Container holding data. */ - size_t container_size; /* Size of CONTAINER. */ - size_t data_size; /* Size of data in CONTAINER. */ - off_t data_offset; /* Offset inside of CONTAINER. */ - } buffer_in; - struct buffer_out - { - char *container; /* Container holding data. */ - size_t container_size; /* Size of CONTAINER. */ - size_t data_size; /* Size of data in CONTAINER. */ - off_t data_offset; /* Offset inside of CONTAINER. */ - size_t data_flushed; /* Amount of data already flushed. */ - } buffer_out; -}; - - - -/* Buffer contains unflushed data. */ -#define BUFFER_FLAG_DIRTY (1 << 0) - -/* Buffer is secure. */ -#define BUFFER_FLAG_SECURE (1 << 1) - - - -/* Fill buffer. */ -static gpg_error_t -buffer_fill_do (buffer_t buffer) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - size_t bytes_read = 0; - - if (! buffer->functions.func_read) - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - else - { - buffer_func_read_t func_read = buffer->functions.func_read; - - err = (*func_read) (buffer->handle, - buffer->buffer_in.container, - buffer->buffer_in.container_size, - &bytes_read); - } - - buffer->buffer_in.data_offset = 0; - buffer->buffer_in.data_size = bytes_read; - - return err; -} - -/* Empty buffer input. */ -static gpg_error_t -buffer_empty (buffer_t buffer) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - buffer->buffer_in.data_size = 0; - buffer->buffer_in.data_offset = 0; - - return err; -} - -/* Flush data contained in buffer. */ -static gpg_error_t -buffer_flush_do (buffer_t buffer) -{ - buffer_func_write_t func_write = buffer->functions.func_write; - gpg_error_t err = GPG_ERR_NO_ERROR; - size_t bytes_written = 0; - - if (! func_write) - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - else if (buffer->flags & BUFFER_FLAG_DIRTY) - while ((buffer->buffer_out.data_size - - buffer->buffer_out.data_flushed) && (! err)) - { - - err = (*func_write) (buffer->handle, - buffer->buffer_out.container - + buffer->buffer_out.data_flushed, - buffer->buffer_out.data_size - - buffer->buffer_out.data_flushed, - &bytes_written); - if (! err) - { - buffer->buffer_out.data_size = 0; - buffer->buffer_out.data_offset = 0; - buffer->buffer_out.data_flushed = 0; - buffer->flags &= ~BUFFER_FLAG_DIRTY; - } - else - buffer->buffer_out.data_flushed += bytes_written; - } - - return err; -} - -static gpg_error_t -buffer_stat_do (buffer_t buffer, - size_t *size) -{ - buffer_func_stat_t func_stat = buffer->functions.func_stat; - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = (*func_stat) (buffer->handle, size); - - return err; -} - - - -/* Read from a buffer. */ -gpg_error_t -buffer_read (buffer_t buffer, - char *data, - size_t bytes_to_read, - size_t *bytes_read) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - size_t data_read = 0; - size_t data_to_copy = 0; - - if (! (buffer->flags & BUFFER_FLAG_DIRTY)) - err = buffer_flush_do (buffer); - - while ((bytes_to_read - data_read) && (! err)) - { - if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size) - { - /* Nothing more to read in current container, try to - fill container with new data. */ - err = buffer_fill_do (buffer); - if (! err) - if (! buffer->buffer_in.data_size) - /* Filling did not result in any data read. */ - break; - } - - if (! err) - { - if ((bytes_to_read - - data_read) > (buffer->buffer_in.data_size - - buffer->buffer_in.data_offset)) - data_to_copy = (buffer->buffer_in.data_size - - buffer->buffer_in.data_offset); - else - data_to_copy = bytes_to_read - data_read; - - memcpy (data + data_read, - buffer->buffer_in.container + buffer->buffer_in.data_offset, - data_to_copy); - buffer->buffer_in.data_offset += data_to_copy; - data_read += data_to_copy; - } - } - - if (bytes_read) - *bytes_read = data_read; - - return err; -} - -/* Write to a buffer. */ -gpg_error_t -buffer_write (buffer_t buffer, - const char *data, - size_t bytes_to_write, - size_t *bytes_written) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - size_t data_written = 0; - size_t data_to_copy = 0; - - while ((bytes_to_write - data_written) && (! err)) - { - if (buffer->buffer_out.data_offset == buffer->buffer_out.container_size) - /* Container full, flush buffer. */ - err = buffer_flush_do (buffer); - - if (! err) - { - if ((bytes_to_write - - data_written) > (buffer->buffer_out.container_size - - buffer->buffer_out.data_offset)) - data_to_copy = (buffer->buffer_out.container_size - - buffer->buffer_out.data_offset); - else - data_to_copy = bytes_to_write - data_written; - - memcpy (buffer->buffer_out.container - + buffer->buffer_out.data_offset, - data + data_written, - data_to_copy); - if ((buffer->buffer_out.data_offset - + data_to_copy) > buffer->buffer_out.data_size) - buffer->buffer_out.data_size = (buffer->buffer_out.data_offset - + data_to_copy); - buffer->buffer_out.data_offset += data_to_copy; - data_written += data_to_copy; - - if (data_written) - if (! (buffer->flags & BUFFER_FLAG_DIRTY)) - buffer->flags |= BUFFER_FLAG_DIRTY; - } - } - - if (bytes_written) - *bytes_written = data_written; - - return err; -} - -/* Seek in a buffer. */ -gpg_error_t -buffer_seek (buffer_t buffer, - off_t offset, - int whence) -{ - buffer_func_seek_t func_seek = buffer->functions.func_seek; - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (! func_seek) - err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - else - { - if (buffer->flags & BUFFER_FLAG_DIRTY) - /* Flush data first in order to prevent flushing it to the - wrong offset. */ - err = buffer_flush_do (buffer); - - if (! err) - err = (*func_seek) (buffer->handle, offset, whence); - - if (! err) - err = buffer_empty (buffer); - } - - return err; -} - -/* Return the unread data contained in a buffer. */ -gpg_error_t -buffer_peek (buffer_t buffer, - char **data, - size_t *data_size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size) - /* Refill container. */ - err = buffer_fill_do (buffer); - - if (! err) - { - if (data) - *data = buffer->buffer_in.container + buffer->buffer_in.data_offset; - if (data_size) - *data_size = buffer->buffer_in.data_size - buffer->buffer_in.data_offset; - } - - return err; -} - -/* Skip SIZE bytes of input data contained in buffer. */ -gpg_error_t -buffer_skip (buffer_t buffer, - size_t size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (buffer->buffer_in.data_offset + size > buffer->buffer_in.data_size) - err = gpg_error (GPG_ERR_INV_ARG); - else - buffer->buffer_in.data_offset += size; - - return err; -} - - - -/* Create a new buffer. */ -gpg_error_t -buffer_create (buffer_t *buffer, - buffer_conspec_t *conspec, - void *handle, - buffer_functions_t functions) -{ - func_realloc_t func_realloc = realloc; - func_free_t func_free = free; - size_t block_size = BUFFER_BLOCK_SIZE; - gpg_error_t err = GPG_ERR_NO_ERROR; - buffer_t buffer_new = NULL; - char *container_in_new = NULL; - char *container_out_new = NULL; - - if (conspec) - { - if (conspec->func_realloc) - func_realloc = conspec->func_realloc; - if (conspec->func_free) - func_free = conspec->func_free; - if (conspec->block_size) - block_size = conspec->block_size; - } - - buffer_new = malloc (sizeof (*buffer_new)); - if (! buffer_new) - err = gpg_error_from_errno (errno); - - if (! err) - { - container_in_new = (*func_realloc) (NULL, block_size); - if (! container_in_new) - err = gpg_error_from_errno (errno); - } - if (! err) - { - container_out_new = (*func_realloc) (NULL, block_size); - if (! container_out_new) - err = gpg_error_from_errno (errno); - } - - if (! err) - { - buffer_new->handle = handle; - buffer_new->conspec.func_realloc = func_realloc; - buffer_new->conspec.func_free = func_free; - buffer_new->conspec.block_size = block_size; - buffer_new->flags = 0; - buffer_new->functions = functions; - buffer_new->buffer_in.container = container_in_new; - buffer_new->buffer_in.container_size = block_size; - buffer_new->buffer_in.data_size = 0; - buffer_new->buffer_in.data_offset = 0; - buffer_new->buffer_out.container = container_out_new; - buffer_new->buffer_out.container_size = block_size; - buffer_new->buffer_out.data_size = 0; - buffer_new->buffer_out.data_offset = 0; - buffer_new->buffer_out.data_flushed = 0; - *buffer = buffer_new; - } - else - { - if (container_in_new) - free (container_in_new); - if (container_out_new) - free (container_out_new); - if (buffer_new) - free (buffer_new); - } - - return err; -} - -/* Destroy a buffer. */ -gpg_error_t -buffer_destroy (buffer_t buffer) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (buffer) - { - err = buffer_flush_do (buffer); - (*buffer->conspec.func_free) (buffer->buffer_in.container); - (*buffer->conspec.func_free) (buffer->buffer_out.container); - free (buffer); - } - - return err; -} - -/* Write out unwritten data contained in buffer. */ -gpg_error_t -buffer_flush (buffer_t buffer) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_flush_do (buffer); - - return err; -} - -/* Stat buffer. */ -gpg_error_t -buffer_stat (buffer_t buffer, - size_t *size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_stat_do (buffer, size); - - return err; -} - -gpg_error_t -buffer_conspec_get (buffer_t buffer, - buffer_conspec_t *buffer_conspec) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - buffer_conspec->block_size = buffer->conspec.block_size; - buffer_conspec->func_realloc = buffer->conspec.func_realloc; - buffer_conspec->func_free = buffer->conspec.func_free; - - return err; -} - - - -/* Stream layer. */ - -/* A Stream Context. */ -struct gpg_stream -{ - void *handle; /* Handle. */ - unsigned int flags; /* Flags. */ - buffer_t buffer; /* Buffer used for I/O. */ - gpg_stream_functions_t functions; /* Callbacks. */ -}; - - - -/* Macros. */ - -/* Standard permissions used for creating new files. */ -#define GPG_STREAM_FILE_PERMISSIONS 0600 - -/* Evaluate EXPRESSION, setting VARIABLE to the return code, if - VARIABLE is zero. */ -#define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \ - do \ - { \ - tmp_variable = expression; \ - if ((! variable) && tmp_variable) \ - variable = tmp_variable; \ - } \ - while (0) - -/* Implementation of Memory I/O. */ - -typedef struct gpg_stream_handle_mem -{ - char *memory; /* Data. */ - size_t memory_size; /* Size of MEMORY. */ - size_t data_size; /* Size of data in MEMORY. */ - size_t block_size; /* Block size. */ - unsigned int grow: 1; /* MEMORY is allowed to grow. */ - size_t offset; /* Current offset in MEMORY. */ - func_realloc_t func_realloc; - func_free_t func_free; -} *gpg_stream_handle_mem_t; - -static gpg_error_t -gpg_stream_func_mem_create (void **handle, - void *spec, - unsigned int flags) -{ - gpg_stream_handle_mem_t mem_handle = NULL; - gpg_stream_spec_mem_t *mem_spec = spec; - gpg_error_t err = GPG_ERR_NO_ERROR; - - mem_handle = malloc (sizeof (*mem_handle)); - if (! mem_handle) - err = gpg_error_from_errno (errno); - else - { - mem_handle->memory = mem_spec ? mem_spec->memory : 0; - mem_handle->memory_size = mem_spec ? mem_spec->memory_size : 0; - mem_handle->data_size = 0; - mem_handle->block_size = ((mem_spec && mem_spec->block_size) - ? mem_spec->block_size : BUFFER_BLOCK_SIZE); - mem_handle->grow = mem_spec ? mem_spec->grow : 1; - mem_handle->offset = 0; - mem_handle->func_realloc = ((mem_spec && mem_spec->func_realloc) - ? mem_spec->func_realloc : realloc); - mem_handle->func_free = ((mem_spec && mem_spec->func_free) - ? mem_spec->func_free : free); - *handle = mem_handle; - } - - return err; -} - -static gpg_error_t -gpg_stream_func_mem_read (void *handle, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read) -{ - gpg_stream_handle_mem_t mem_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (bytes_to_read > mem_handle->data_size - mem_handle->offset) - bytes_to_read = mem_handle->data_size - mem_handle->offset; - - memcpy (buffer, mem_handle->memory + mem_handle->offset, - bytes_to_read); - mem_handle->offset += bytes_to_read; - *bytes_read = bytes_to_read; - - return err; -} - -static gpg_error_t -gpg_stream_func_mem_write (void *handle, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written) -{ - gpg_stream_handle_mem_t mem_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - char *memory_new = NULL; - - if (! mem_handle->grow) - if (bytes_to_write > mem_handle->memory_size - mem_handle->offset) - bytes_to_write = mem_handle->memory_size - mem_handle->offset; - - while (bytes_to_write > mem_handle->memory_size - mem_handle->offset) - { - memory_new = (*mem_handle->func_realloc) - (mem_handle->memory, - mem_handle->memory_size + mem_handle->block_size); - if (! memory_new) - { - err = gpg_error_from_errno (errno); - break; - } - else - { - if (mem_handle->memory != memory_new) - mem_handle->memory = memory_new; - mem_handle->memory_size += mem_handle->block_size; - } - } - - if (! err) - { - memcpy (mem_handle->memory + mem_handle->offset, buffer, - bytes_to_write); - if (mem_handle->offset + bytes_to_write > mem_handle->data_size) - mem_handle->data_size = mem_handle->offset + bytes_to_write; - mem_handle->offset += bytes_to_write; - } - *bytes_written = bytes_to_write; - - return err; -} - -gpg_error_t -gpg_stream_func_mem_seek (void *handle, - off_t offset, - int whence) -{ - gpg_stream_handle_mem_t mem_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - - switch (whence) - { - case SEEK_SET: - if ((offset < 0) || (offset > mem_handle->data_size)) - err = gpg_error (GPG_ERR_INV_ARG); - else - mem_handle->offset = offset; - break; - - case SEEK_CUR: - if ((mem_handle->offset + offset < 0) - || (mem_handle->offset + offset > mem_handle->data_size)) - err = gpg_error (GPG_ERR_INV_ARG); - else - mem_handle->offset += offset; - break; - - case SEEK_END: - if ((mem_handle->data_size + offset < 0) - || (mem_handle->data_size + offset > mem_handle->data_size)) - err = gpg_error (GPG_ERR_INV_ARG); - else - mem_handle->offset += offset; - } - - return err; -} - -static gpg_error_t -gpg_stream_func_mem_stat (void *handle, - size_t *size) -{ - gpg_stream_handle_mem_t mem_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - - *size = mem_handle->data_size; - - return err; -} - -static gpg_error_t -gpg_stream_func_mem_destroy (void *handle) -{ - gpg_stream_handle_mem_t mem_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (mem_handle->memory) - (*mem_handle->func_free) (mem_handle->memory); - free (mem_handle); - - return err; -} - -gpg_stream_functions_t gpg_stream_functions_mem = - { - gpg_stream_func_mem_create, - gpg_stream_func_mem_read, - gpg_stream_func_mem_write, - gpg_stream_func_mem_seek, - gpg_stream_func_mem_stat, - gpg_stream_func_mem_destroy - }; - -/* Implementation of FD I/O. */ - -typedef struct gpg_stream_handle_fd -{ - int fd; -} *gpg_stream_handle_fd_t; - -static gpg_error_t -gpg_stream_func_fd_create (void **handle, - void *spec, - unsigned int flags) -{ - gpg_stream_handle_fd_t fd_handle = NULL; - gpg_stream_spec_fd_t *fd_spec = spec; - gpg_error_t err = GPG_ERR_NO_ERROR; - - fd_handle = malloc (sizeof (*fd_handle)); - if (! fd_handle) - err = gpg_error_from_errno (errno); - else - { - fd_handle->fd = fd_spec->fd; - *handle = fd_handle; - } - - return err; -} - -static gpg_error_t -gpg_stream_func_fd_read (void *handle, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read) - -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - ssize_t ret = -1; - - ret = READ (file_handle->fd, buffer, bytes_to_read); - if (ret == -1) - err = gpg_error_from_errno (errno); - else - *bytes_read = ret; - - return err; -} - -static gpg_error_t -gpg_stream_func_fd_write (void *handle, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written) - -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - ssize_t ret = -1; - - ret = WRITE (file_handle->fd, buffer, bytes_to_write); - if (ret == -1) - err = gpg_error_from_errno (errno); - else - *bytes_written = ret; - - return err; -} - -static gpg_error_t -gpg_stream_func_fd_seek (void *handle, - off_t pos, - int whence) -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - off_t ret = -1; - - ret = lseek (file_handle->fd, pos, whence); - if (ret == -1) - err = gpg_error_from_errno (errno); - - return err; -} - -static gpg_error_t -gpg_stream_func_fd_stat (void *handle, - size_t *size) -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - struct stat statbuf; - int ret = 0; - - ret = fstat (file_handle->fd, &statbuf); - if (ret == -1) - err = gpg_error_from_errno (errno); - else - *size = statbuf.st_size; - - return err; -} - -static gpg_error_t -gpg_stream_func_fd_destroy (void *handle) -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - - free (file_handle); - - return err; -} - -gpg_stream_functions_t gpg_stream_functions_fd = - { - gpg_stream_func_fd_create, - gpg_stream_func_fd_read, - gpg_stream_func_fd_write, - gpg_stream_func_fd_seek, - gpg_stream_func_fd_stat, - gpg_stream_func_fd_destroy - }; - - -/* Implementation of File I/O. */ - -static gpg_error_t -gpg_stream_func_file_create (void **handle, - void *spec, - unsigned int flags) -{ - gpg_stream_handle_fd_t file_handle = NULL; - gpg_stream_spec_file_t *file_spec = spec; - gpg_error_t err = GPG_ERR_NO_ERROR; - int open_flags = 0; - int fd = -1; - - file_handle = malloc (sizeof (*file_handle)); - if (! file_handle) - err = gpg_error_from_errno (errno); - - if (! err) - { - struct flag_mapping - { - unsigned int gpg_stream; - unsigned int sys; - } flag_mappings[] = { { GPG_STREAM_FLAG_READ, - O_RDONLY }, - { GPG_STREAM_FLAG_WRITE, - O_WRONLY }, - { GPG_STREAM_FLAG_EXCLUSIVE, - O_EXCL }, - { GPG_STREAM_FLAG_APPEND, - O_APPEND }, - { GPG_STREAM_FLAG_CREATE, - O_CREAT }, - { GPG_STREAM_FLAG_NONBLOCK, - O_NONBLOCK }, - { GPG_STREAM_FLAG_TRUNCATE, - O_TRUNC } }; - unsigned int i = 0; - - for (i = 0; i < (sizeof (flag_mappings) / sizeof (*flag_mappings)); i++) - if (flags & flag_mappings[i].gpg_stream) - open_flags |= flag_mappings[i].sys; - - fd = open (file_spec->filename, open_flags, file_spec->mode); - if (fd == -1) - err = gpg_error_from_errno (errno); - } - - if (! err) - { - file_handle->fd = fd; - *handle = file_handle; - } - else - { - if (file_handle) - free (file_handle); - if (fd != -1) - close (fd); - } - - return err; -} - -static gpg_error_t -gpg_stream_func_file_destroy (void *handle) -{ - gpg_stream_handle_fd_t file_handle = handle; - gpg_error_t err = GPG_ERR_NO_ERROR; - int ret = 0; - - if (file_handle) - { - ret = close (file_handle->fd); - if (ret == -1) - err = gpg_error_from_errno (errno); - free (file_handle); - } - - return err; -} - -gpg_stream_functions_t gpg_stream_functions_file = - { - gpg_stream_func_file_create, - gpg_stream_func_fd_read, - gpg_stream_func_fd_write, - gpg_stream_func_fd_seek, - gpg_stream_func_fd_stat, - gpg_stream_func_file_destroy - }; - - - -static gpg_error_t -gpg_stream_create_do (gpg_stream_t *stream, - gpg_stream_buffer_spec_t *buffer_spec, - void *spec, - unsigned int flags, - gpg_stream_functions_t functions) -{ - buffer_conspec_t conspec = { NULL }; - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (buffer_spec) - { - conspec.func_realloc = buffer_spec->func_realloc; - conspec.func_free = buffer_spec->func_free; - conspec.block_size = buffer_spec->block_size; - } - - if (! (1 - && (0 - || ((flags & GPG_STREAM_FLAG_READ) && functions.func_read) - || ((flags & GPG_STREAM_FLAG_WRITE) && functions.func_write)))) - err = gpg_error (GPG_ERR_INV_ARG); - else - { - buffer_functions_t buffer_fncs = { functions.func_read, - functions.func_write, - functions.func_seek, - functions.func_stat }; - gpg_stream_t stream_new = NULL; - buffer_t buffer = NULL; - void *handle = NULL; - - stream_new = malloc (sizeof (*stream_new)); - if (! stream_new) - err = gpg_error_from_errno (errno); - - if (! err) - if (functions.func_create) - err = (*functions.func_create) (&handle, spec, flags); - - if (! err) - err = buffer_create (&buffer, buffer_spec ? &conspec : NULL, - handle, buffer_fncs); - - if (! err) - { - stream_new->handle = handle; - stream_new->flags = flags; - stream_new->buffer = buffer; - stream_new->functions = functions; - *stream = stream_new; - } - else - { - if (functions.func_destroy) - (*functions.func_destroy) (handle); - if (buffer) - buffer_destroy (buffer); - } - } - - return err; -} - -gpg_error_t -gpg_stream_create (gpg_stream_t *stream, - gpg_stream_buffer_spec_t *buffer_spec, - void *spec, - unsigned int flags, - gpg_stream_functions_t functions) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_create_do (stream, buffer_spec, spec, flags, functions); - - return err; -} - -gpg_error_t -gpg_stream_create_file (gpg_stream_t *stream, - const char *filename, - unsigned int flags) -{ - gpg_stream_spec_file_t spec = { filename, GPG_STREAM_FILE_PERMISSIONS }; - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_create_do (stream, NULL, &spec, flags, gpg_stream_functions_file); - - return err; -} - -gpg_error_t -gpg_stream_create_fd (gpg_stream_t *stream, - int fd, - unsigned int flags) -{ - gpg_stream_spec_fd_t spec = { fd }; - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_create_do (stream, NULL, &spec, flags, gpg_stream_functions_fd); - - return err; -} - - -gpg_error_t -gpg_stream_destroy (gpg_stream_t stream) -{ - gpg_error_t err = GPG_ERR_NO_ERROR, tmp_err = GPG_ERR_NO_ERROR; - - if (stream) - { - SET_UNLESS_NONZERO (err, tmp_err, buffer_destroy (stream->buffer)); - if (stream->functions.func_destroy) - SET_UNLESS_NONZERO (err, tmp_err, \ - (*stream->functions.func_destroy) (stream->handle)); - free (stream); - } - - return err; -} - -static gpg_error_t -gpg_stream_read_do (gpg_stream_t stream, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (! (stream->flags & GPG_STREAM_FLAG_READ)) - err = gpg_error (GPG_ERR_NOT_SUPPORTED); - else - err = buffer_read (stream->buffer, - buffer, bytes_to_read, bytes_read); - - return err; -} - -gpg_error_t -gpg_stream_read (gpg_stream_t stream, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_read_do (stream, buffer, bytes_to_read, bytes_read); - - return err; -} - -static gpg_error_t -gpg_stream_write_do (gpg_stream_t stream, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (! (stream->flags & GPG_STREAM_FLAG_WRITE)) - err = GPG_ERR_NOT_SUPPORTED; - else - err = buffer_write (stream->buffer, - buffer, bytes_to_write, bytes_written); - - return err; -} - -gpg_error_t -gpg_stream_write (gpg_stream_t stream, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_write_do (stream, buffer, bytes_to_write, bytes_written); - - return err; -} - -static gpg_error_t -gpg_stream_read_line_do (gpg_stream_t stream, - char **line, - size_t *line_length) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - if (! (stream->flags & GPG_STREAM_FLAG_READ)) - err = gpg_error (GPG_ERR_NOT_SUPPORTED); - else - { - buffer_functions_t buffer_fncs_mem = { gpg_stream_func_mem_read, - gpg_stream_func_mem_write, - gpg_stream_func_mem_seek }; - buffer_conspec_t buffer_conspec = { NULL }; - void *handle = NULL; - char *line_new = NULL; - buffer_t line_buffer = NULL; - char *newline = NULL; - size_t data_size = 0; - char *data = NULL; - size_t line_size = 0; - - buffer_conspec_get (stream->buffer, &buffer_conspec); - - err = gpg_stream_func_mem_create (&handle, NULL, 0); - if (! err) - err = buffer_create (&line_buffer, &buffer_conspec, handle, buffer_fncs_mem); - if (! err) - do - { - err = buffer_peek (stream->buffer, &data, &data_size); - if (! err) - { - size_t bytes_written = 0; - - newline = memchr (data, '\n', data_size); - if (newline) - { - /* Write until newline. */ - line_size += newline - data + 1; - err = buffer_write (line_buffer, data, newline - data + 1, - &bytes_written); - if (! err) - err = buffer_skip (stream->buffer, bytes_written); - break; - } - else - { - /* Write whole block. */ - line_size += data_size; - err = buffer_write (line_buffer, data, data_size, - &bytes_written); - if (! err) - err = buffer_skip (stream->buffer, bytes_written); - } - } - } - while ((! err) && data_size); - - if (! err) - { - /* Complete line has been written to line_buffer. */ - if (line_size) - { - err = buffer_seek (line_buffer, 0, SEEK_SET); - if (! err) - { - line_new = malloc (line_size + 1); - if (! line_new) - err = gpg_error_from_errno (errno); - } - if (! err) - { - size_t bytes_written = 0, written = 0; - - while ((bytes_written < line_size) && (! err)) - { - err = buffer_read (line_buffer, line_new + bytes_written, - line_size - bytes_written, &written); - bytes_written += written; - } - if (! err) - line_new[line_size] = 0; - } - } - } - - if (line_buffer) - buffer_destroy (line_buffer); - if (handle) - gpg_stream_func_mem_destroy (handle); - - if (! err) - { - *line = line_new; - if (line_length) - *line_length = line_size; - } - else - { - if (line_new) - free (line_new); - } - } - - return err; -} - -gpg_error_t -gpg_stream_read_line (gpg_stream_t stream, - char **line, - size_t *line_length) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_read_line_do (stream, line, line_length); - - return err; -} - -static gpg_error_t -gpg_stream_print_va_do (gpg_stream_t stream, - const char *format, - va_list ap) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - FILE *tmp_stream = NULL; - int ret = 0; - - if (! (stream->flags & GPG_STREAM_FLAG_WRITE)) - err = gpg_error (GPG_ERR_NOT_SUPPORTED); - else - { - tmp_stream = tmpfile (); - if (! tmp_stream) - err = gpg_error_from_errno (errno); - - if (! err) - { - ret = vfprintf (tmp_stream, format, ap); - if (ret == -1) - err = gpg_error_from_errno (errno); - } - - if (! err) - { - ret = fseek (tmp_stream, 0, SEEK_SET); - if (ret == -1) - err = gpg_error_from_errno (errno); - } - - if (! err) - { - size_t bytes_read = 0, bytes_written = 0; - char data[BUFFER_BLOCK_SIZE]; - - while (! err) - { - bytes_read = fread (data, 1, sizeof (data), tmp_stream); - if (ferror (tmp_stream)) - err = gpg_error_from_errno (errno); - if (! err) - err = gpg_stream_write_do (stream, data, - bytes_read, &bytes_written); - if (! err) - if (feof (tmp_stream)) - break; - } - } - - if (tmp_stream) - fclose (tmp_stream); - } - - return err; -} - -gpg_error_t -gpg_stream_print_va (gpg_stream_t stream, - const char *format, - va_list ap) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_print_va_do (stream, format, ap); - - return err; -} - -gpg_error_t -gpg_stream_print (gpg_stream_t stream, - const char *format, - ...) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - va_list ap; - - va_start (ap, format); - err = gpg_stream_print_va (stream, format, ap); - va_end (ap); - - return err; -} - -static gpg_error_t -gpg_stream_flush_do (gpg_stream_t stream) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_flush (stream->buffer); - - return err; -} - -gpg_error_t -gpg_stream_flush (gpg_stream_t stream) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_flush_do (stream); - - return err; -} - -static gpg_error_t -gpg_stream_peek_do (gpg_stream_t stream, - char **buffer, - size_t *size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_peek (stream->buffer, buffer, size); - - return err; -} - -gpg_error_t -gpg_stream_peek (gpg_stream_t stream, - char **buffer, - size_t *size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_peek_do (stream, buffer, size); - - return err; -} - -static gpg_error_t -gpg_stream_seek_do (gpg_stream_t stream, - off_t offset, - int whence) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_seek (stream->buffer, offset, whence); - - return err; -} - -gpg_error_t -gpg_stream_seek (gpg_stream_t stream, - off_t offset, - int whence) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_seek_do (stream, offset, whence); - - return err; -} - -static gpg_error_t -gpg_stream_stat_do (gpg_stream_t stream, - size_t *size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = buffer_stat (stream->buffer, size); - - return err; -} - -gpg_error_t -gpg_stream_stat (gpg_stream_t stream, - size_t *size) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_stat_do (stream, size); - - return err; -} - - -static gpg_error_t -gpg_stream_copy_do (gpg_stream_t dst, - gpg_stream_t src) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - unsigned char buffer[STREAM_BLOCK_SIZE]; - size_t bytes_read = 0; - - while (1) - { - err = gpg_stream_read (src, buffer, sizeof (buffer), &bytes_read); - if (err || (! bytes_read)) - break; - - err = gpg_stream_write (dst, buffer, bytes_read, NULL); - if (err) - break; - } - - return err; -} - -gpg_error_t -gpg_stream_copy (gpg_stream_t dst, - gpg_stream_t src) -{ - gpg_error_t err = GPG_ERR_NO_ERROR; - - err = gpg_stream_copy_do (dst, src); - - return err; -} diff --git a/agent/gpg-stream.h b/agent/gpg-stream.h deleted file mode 100644 index 3bea06dbc..000000000 --- a/agent/gpg-stream.h +++ /dev/null @@ -1,166 +0,0 @@ -/* stream.h - Stream I/O layer - Copyright (C) 2004 g10 Code GmbH - - This file is part of libgpg-stream. - - libgpg-stream is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - libgpg-stream 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 General Public License - along with libgpg-stream; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ - -#ifndef GPG_STREAM_H -#define GPG_STREAM_H - -#include -#include - -#include - - - -#define STREAM_BLOCK_SIZE 1024 - - - -typedef struct gpg_stream *gpg_stream_t; - -typedef gpg_error_t (*gpg_stream_func_create_t) (void **handle, - void *spec, - unsigned int flags); -typedef gpg_error_t (*gpg_stream_func_read_t) (void *handle, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read); -typedef gpg_error_t (*gpg_stream_func_write_t) (void *handle, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written); -typedef gpg_error_t (*gpg_stream_func_seek_t) (void *handle, - off_t pos, - int whence); -typedef gpg_error_t (*gpg_stream_func_stat_t) (void *handle, - size_t *size); -typedef gpg_error_t (*gpg_stream_func_destroy_t) (void *handle); - -typedef struct gpg_stream_functions -{ - gpg_stream_func_create_t func_create; - gpg_stream_func_read_t func_read; - gpg_stream_func_write_t func_write; - gpg_stream_func_seek_t func_seek; - gpg_stream_func_stat_t func_stat; - gpg_stream_func_destroy_t func_destroy; -} gpg_stream_functions_t; - -typedef void *(*gpg_stream_func_realloc_t) (void *mem, size_t size); -typedef void (*gpg_stream_func_free_t) (void *mem); - -typedef struct gpg_stream_buffer_spec -{ - size_t block_size; - gpg_stream_func_realloc_t func_realloc; - gpg_stream_func_free_t func_free; -} gpg_stream_buffer_spec_t; - - -#define GPG_STREAM_FLAG_READ (1 << 0) -#define GPG_STREAM_FLAG_WRITE (1 << 1) -#define GPG_STREAM_FLAG_EXCLUSIVE (1 << 2) -#define GPG_STREAM_FLAG_APPEND (1 << 3) -#define GPG_STREAM_FLAG_CREATE (1 << 4) -#define GPG_STREAM_FLAG_NONBLOCK (1 << 5) -#define GPG_STREAM_FLAG_TRUNCATE (1 << 6) -#define GPG_STREAM_FLAG_BINARY (1 << 7) - -gpg_error_t gpg_stream_create (gpg_stream_t *stream, - gpg_stream_buffer_spec_t *buffer_spec, - void *spec, - unsigned int flags, - gpg_stream_functions_t functions); - -gpg_error_t gpg_stream_create_file (gpg_stream_t *stream, - const char *filename, - unsigned int flags); - -gpg_error_t gpg_stream_create_fd (gpg_stream_t *stream, - int fd, - unsigned int flags); - -gpg_error_t gpg_stream_destroy (gpg_stream_t stream); - -gpg_error_t gpg_stream_read (gpg_stream_t stream, - char *buffer, - size_t bytes_to_read, - size_t *bytes_read); - -gpg_error_t gpg_stream_write (gpg_stream_t stream, - const char *buffer, - size_t bytes_to_write, - size_t *bytes_written); - -gpg_error_t gpg_stream_read_line (gpg_stream_t stream, - char **line, - size_t *line_length); - -gpg_error_t gpg_stream_print_va (gpg_stream_t stream, - const char *format, - va_list ap); - -gpg_error_t gpg_stream_print (gpg_stream_t stream, - const char *format, - ...); - -gpg_error_t gpg_stream_flush (gpg_stream_t stream); - -gpg_error_t gpg_stream_peek (gpg_stream_t stream, - char **buffer, - size_t *size); - -gpg_error_t gpg_stream_seek (gpg_stream_t stream, - off_t offset, - int whence); - -gpg_error_t gpg_stream_stat (gpg_stream_t stream, - size_t *size); - -gpg_error_t gpg_stream_copy (gpg_stream_t dst, - gpg_stream_t src); - -typedef struct gpg_stream_spec_mem -{ - char *memory; - size_t memory_size; - unsigned int grow: 1; - size_t block_size; - void *(*func_realloc) (void *mem, size_t size); - void (*func_free) (void *mem); -} gpg_stream_spec_mem_t; - -extern gpg_stream_functions_t gpg_stream_functions_mem; - -typedef struct gpg_stream_spec_file -{ - const char *filename; - mode_t mode; -} gpg_stream_spec_file_t; - -extern gpg_stream_functions_t gpg_stream_functions_file; - -typedef struct gpg_stream_spec_fd -{ - int fd; -} gpg_stream_spec_fd_t; - -extern gpg_stream_functions_t gpg_stream_functions_fd; - -#endif