/* $OpenBSD: open_memstream.c,v 1.5 2015/02/05 12:59:57 millert Exp $ */ /* * Copyright (c) 2011 Martin Pieuchot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "local.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) struct state { char *string; /* actual stream */ char **pbuf; /* point to the stream */ size_t *psize; /* point to min(pos, len) */ size_t pos; /* current position */ size_t size; /* number of allocated char */ size_t len; /* length of the data */ }; static int memstream_write(void *v, const char *b, int l) { struct state *st = v; char *p; size_t i, end; end = (st->pos + l); if (end >= st->size) { /* 1.6 is (very) close to the golden ratio. */ size_t sz = st->size * 8 / 5; if (sz < end + 1) sz = end + 1; p = realloc(st->string, sz); if (!p) return (-1); bzero(p + st->size, sz - st->size); *st->pbuf = st->string = p; st->size = sz; } for (i = 0; i < l; i++) st->string[st->pos + i] = b[i]; st->pos += l; if (st->pos > st->len) { st->len = st->pos; st->string[st->len] = '\0'; } *st->psize = st->pos; return (i); } static fpos_t memstream_seek(void *v, fpos_t off, int whence) { struct state *st = v; ssize_t base = 0; switch (whence) { case SEEK_SET: break; case SEEK_CUR: base = st->pos; break; case SEEK_END: base = st->len; break; } if (off > SIZE_MAX - base || off < -base) { errno = EOVERFLOW; return (-1); } st->pos = base + off; *st->psize = MINIMUM(st->pos, st->len); return (st->pos); } static int memstream_close(void *v) { struct state *st = v; free(st); return (0); } FILE * open_memstream(char **pbuf, size_t *psize) { struct state *st; FILE *fp; if (pbuf == NULL || psize == NULL) { errno = EINVAL; return (NULL); } if ((st = malloc(sizeof(*st))) == NULL) return (NULL); if ((fp = __sfp()) == NULL) { free(st); return (NULL); } st->size = BUFSIZ; if ((st->string = calloc(1, st->size)) == NULL) { free(st); fp->_flags = 0; return (NULL); } *st->string = '\0'; st->pos = 0; st->len = 0; st->pbuf = pbuf; st->psize = psize; *pbuf = st->string; *psize = st->len; fp->_flags = __SWR; fp->_file = -1; fp->_cookie = st; fp->_read = NULL; fp->_write = memstream_write; fp->_seek = memstream_seek; fp->_close = memstream_close; _SET_ORIENTATION(fp, -1); return (fp); }