1/* $OpenBSD: open_memstream.c,v 1.6 2015/08/31 02:53:57 guenther Exp $ */ 2 3/* 4 * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <errno.h> 20#include <fcntl.h> 21#include <stdint.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include "local.h" 26 27#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 28 29struct state { 30 char *string; /* actual stream */ 31 char **pbuf; /* point to the stream */ 32 size_t *psize; /* point to min(pos, len) */ 33 size_t pos; /* current position */ 34 size_t size; /* number of allocated char */ 35 size_t len; /* length of the data */ 36}; 37 38static int 39memstream_write(void *v, const char *b, int l) 40{ 41 struct state *st = v; 42 char *p; 43 size_t i, end; 44 45 end = (st->pos + l); 46 47 if (end >= st->size) { 48 /* 1.6 is (very) close to the golden ratio. */ 49 size_t sz = st->size * 8 / 5; 50 51 if (sz < end + 1) 52 sz = end + 1; 53 p = realloc(st->string, sz); 54 if (!p) 55 return (-1); 56 bzero(p + st->size, sz - st->size); 57 *st->pbuf = st->string = p; 58 st->size = sz; 59 } 60 61 for (i = 0; i < l; i++) 62 st->string[st->pos + i] = b[i]; 63 st->pos += l; 64 65 if (st->pos > st->len) { 66 st->len = st->pos; 67 st->string[st->len] = '\0'; 68 } 69 70 *st->psize = st->pos; 71 72 return (i); 73} 74 75static fpos_t 76memstream_seek(void *v, fpos_t off, int whence) 77{ 78 struct state *st = v; 79 ssize_t base = 0; 80 81 switch (whence) { 82 case SEEK_SET: 83 break; 84 case SEEK_CUR: 85 base = st->pos; 86 break; 87 case SEEK_END: 88 base = st->len; 89 break; 90 } 91 92 if (off > SIZE_MAX - base || off < -base) { 93 errno = EOVERFLOW; 94 return (-1); 95 } 96 97 st->pos = base + off; 98 *st->psize = MINIMUM(st->pos, st->len); 99 100 return (st->pos); 101} 102 103static int 104memstream_close(void *v) 105{ 106 struct state *st = v; 107 108 free(st); 109 110 return (0); 111} 112 113FILE * 114open_memstream(char **pbuf, size_t *psize) 115{ 116 struct state *st; 117 FILE *fp; 118 119 if (pbuf == NULL || psize == NULL) { 120 errno = EINVAL; 121 return (NULL); 122 } 123 124 if ((st = malloc(sizeof(*st))) == NULL) 125 return (NULL); 126 127 if ((fp = __sfp()) == NULL) { 128 free(st); 129 return (NULL); 130 } 131 132 st->size = BUFSIZ; 133 if ((st->string = calloc(1, st->size)) == NULL) { 134 free(st); 135 fp->_flags = 0; 136 return (NULL); 137 } 138 139 *st->string = '\0'; 140 st->pos = 0; 141 st->len = 0; 142 st->pbuf = pbuf; 143 st->psize = psize; 144 145 *pbuf = st->string; 146 *psize = st->len; 147 148 fp->_flags = __SWR; 149 fp->_file = -1; 150 fp->_cookie = st; 151 fp->_read = NULL; 152 fp->_write = memstream_write; 153 fp->_seek = memstream_seek; 154 fp->_close = memstream_close; 155 _SET_ORIENTATION(fp, -1); 156 157 return (fp); 158} 159DEF_WEAK(open_memstream); 160