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