1 /*- 2 * Copyright (c) 2013 Hudson River Trading LLC 3 * Written by: John H. Baldwin <jhb@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/lib/libc/stdio/open_memstream.c 281887 2015-04-23 14:22:20Z jhb $ 28 */ 29 30 #include "namespace.h" 31 #include <assert.h> 32 #include <errno.h> 33 #include <limits.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <wchar.h> 38 #include "un-namespace.h" 39 40 /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ 41 #define FPOS_MAX OFF_MAX 42 43 struct memstream { 44 char **bufp; 45 size_t *sizep; 46 ssize_t len; 47 fpos_t offset; 48 }; 49 50 static int 51 memstream_grow(struct memstream *ms, fpos_t newoff) 52 { 53 char *buf; 54 ssize_t newsize; 55 56 if (newoff < 0 || newoff >= SSIZE_MAX) 57 newsize = SSIZE_MAX - 1; 58 else 59 newsize = newoff; 60 if (newsize > ms->len) { 61 buf = realloc(*ms->bufp, newsize + 1); 62 if (buf != NULL) { 63 #ifdef DEBUG 64 fprintf(stderr, "MS: %p growing from %zd to %zd\n", 65 ms, ms->len, newsize); 66 #endif 67 memset(buf + ms->len + 1, 0, newsize - ms->len); 68 *ms->bufp = buf; 69 ms->len = newsize; 70 return (1); 71 } 72 return (0); 73 } 74 return (1); 75 } 76 77 static void 78 memstream_update(struct memstream *ms) 79 { 80 81 assert(ms->len >= 0 && ms->offset >= 0); 82 *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; 83 } 84 85 static int 86 memstream_write(void *cookie, const char *buf, int len) 87 { 88 struct memstream *ms; 89 ssize_t tocopy; 90 91 ms = cookie; 92 if (!memstream_grow(ms, ms->offset + len)) 93 return (-1); 94 tocopy = ms->len - ms->offset; 95 if (len < tocopy) 96 tocopy = len; 97 memcpy(*ms->bufp + ms->offset, buf, tocopy); 98 ms->offset += tocopy; 99 memstream_update(ms); 100 #ifdef DEBUG 101 fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy); 102 #endif 103 return (tocopy); 104 } 105 106 static fpos_t 107 memstream_seek(void *cookie, fpos_t pos, int whence) 108 { 109 struct memstream *ms; 110 #ifdef DEBUG 111 fpos_t old; 112 #endif 113 114 ms = cookie; 115 #ifdef DEBUG 116 old = ms->offset; 117 #endif 118 switch (whence) { 119 case SEEK_SET: 120 /* _fseeko() checks for negative offsets. */ 121 assert(pos >= 0); 122 ms->offset = pos; 123 break; 124 case SEEK_CUR: 125 /* This is only called by _ftello(). */ 126 assert(pos == 0); 127 break; 128 case SEEK_END: 129 if (pos < 0) { 130 if (pos + ms->len < 0) { 131 #ifdef DEBUG 132 fprintf(stderr, 133 "MS: bad SEEK_END: pos %jd, len %zd\n", 134 (intmax_t)pos, ms->len); 135 #endif 136 errno = EINVAL; 137 return (-1); 138 } 139 } else { 140 if (FPOS_MAX - ms->len < pos) { 141 #ifdef DEBUG 142 fprintf(stderr, 143 "MS: bad SEEK_END: pos %jd, len %zd\n", 144 (intmax_t)pos, ms->len); 145 #endif 146 errno = EOVERFLOW; 147 return (-1); 148 } 149 } 150 ms->offset = ms->len + pos; 151 break; 152 } 153 memstream_update(ms); 154 #ifdef DEBUG 155 fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos, 156 whence, (intmax_t)old, (intmax_t)ms->offset); 157 #endif 158 return (ms->offset); 159 } 160 161 static int 162 memstream_close(void *cookie) 163 { 164 165 free(cookie); 166 return (0); 167 } 168 169 FILE * 170 open_memstream(char **bufp, size_t *sizep) 171 { 172 struct memstream *ms; 173 int save_errno; 174 FILE *fp; 175 176 if (bufp == NULL || sizep == NULL) { 177 errno = EINVAL; 178 return (NULL); 179 } 180 *bufp = calloc(1, 1); 181 if (*bufp == NULL) 182 return (NULL); 183 ms = malloc(sizeof(*ms)); 184 if (ms == NULL) { 185 save_errno = errno; 186 free(*bufp); 187 *bufp = NULL; 188 errno = save_errno; 189 return (NULL); 190 } 191 ms->bufp = bufp; 192 ms->sizep = sizep; 193 ms->len = 0; 194 ms->offset = 0; 195 memstream_update(ms); 196 fp = funopen(ms, NULL, memstream_write, memstream_seek, 197 memstream_close); 198 if (fp == NULL) { 199 save_errno = errno; 200 free(ms); 201 free(*bufp); 202 *bufp = NULL; 203 errno = save_errno; 204 return (NULL); 205 } 206 fwide(fp, -1); 207 return (fp); 208 } 209