1 /* $OpenBSD: open_memstream.c,v 1.10 2023/07/11 12:14:16 claudio 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 29 struct 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 38 static int 39 memstream_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 = recallocarray(st->string, st->size, sz, 1); 54 if (!p) 55 return (-1); 56 *st->pbuf = st->string = p; 57 st->size = sz; 58 } 59 60 for (i = 0; i < l; i++) 61 st->string[st->pos + i] = b[i]; 62 st->pos += l; 63 64 if (st->pos > st->len) { 65 st->len = st->pos; 66 st->string[st->len] = '\0'; 67 } 68 69 *st->psize = st->pos; 70 71 return (i); 72 } 73 74 static fpos_t 75 memstream_seek(void *v, fpos_t off, int whence) 76 { 77 struct state *st = v; 78 size_t base = 0; 79 80 switch (whence) { 81 case SEEK_SET: 82 break; 83 case SEEK_CUR: 84 base = st->pos; 85 break; 86 case SEEK_END: 87 base = st->len; 88 break; 89 } 90 91 if ((off > 0 && off > SIZE_MAX - base) || (off < 0 && base < -off)) { 92 errno = EOVERFLOW; 93 return (-1); 94 } 95 96 st->pos = base + off; 97 *st->psize = MINIMUM(st->pos, st->len); 98 99 return (st->pos); 100 } 101 102 static int 103 memstream_close(void *v) 104 { 105 struct state *st = v; 106 107 free(st); 108 109 return (0); 110 } 111 112 FILE * 113 open_memstream(char **pbuf, size_t *psize) 114 { 115 struct state *st; 116 FILE *fp; 117 118 if (pbuf == NULL || psize == NULL) { 119 errno = EINVAL; 120 return (NULL); 121 } 122 123 if ((st = malloc(sizeof(*st))) == NULL) 124 return (NULL); 125 126 if ((fp = __sfp()) == NULL) { 127 free(st); 128 return (NULL); 129 } 130 131 st->size = BUFSIZ; 132 if ((st->string = calloc(1, st->size)) == NULL) { 133 free(st); 134 fp->_flags = 0; 135 return (NULL); 136 } 137 138 st->pos = 0; 139 st->len = 0; 140 st->pbuf = pbuf; 141 st->psize = psize; 142 143 *pbuf = st->string; 144 *psize = st->len; 145 146 fp->_flags = __SWR; 147 fp->_file = -1; 148 fp->_cookie = st; 149 fp->_read = NULL; 150 fp->_write = memstream_write; 151 fp->_seek = memstream_seek; 152 fp->_close = memstream_close; 153 _SET_ORIENTATION(fp, -1); 154 155 return (fp); 156 } 157 DEF_WEAK(open_memstream); 158