1 /*- 2 * Copyright (c) 2013 Advanced Computing Technologies 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 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include "namespace.h" 32 #include <assert.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <wchar.h> 39 #include "un-namespace.h" 40 41 /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */ 42 #define FPOS_MAX OFF_MAX 43 44 struct memstream { 45 char **bufp; 46 size_t *sizep; 47 ssize_t len; 48 fpos_t offset; 49 }; 50 51 static int 52 memstream_grow(struct memstream *ms, fpos_t newoff) 53 { 54 char *buf; 55 ssize_t newsize; 56 57 if (newoff < 0 || newoff >= SSIZE_MAX) 58 newsize = SSIZE_MAX - 1; 59 else 60 newsize = newoff; 61 if (newsize > ms->len) { 62 buf = realloc(*ms->bufp, newsize + 1); 63 if (buf != NULL) { 64 #ifdef DEBUG 65 fprintf(stderr, "MS: %p growing from %zd to %zd\n", 66 ms, ms->len, newsize); 67 #endif 68 memset(buf + ms->len + 1, 0, newsize - ms->len); 69 *ms->bufp = buf; 70 ms->len = newsize; 71 return (1); 72 } 73 return (0); 74 } 75 return (1); 76 } 77 78 static void 79 memstream_update(struct memstream *ms) 80 { 81 82 assert(ms->len >= 0 && ms->offset >= 0); 83 *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset; 84 } 85 86 static int 87 memstream_write(void *cookie, const char *buf, int len) 88 { 89 struct memstream *ms; 90 ssize_t tocopy; 91 92 ms = cookie; 93 if (!memstream_grow(ms, ms->offset + len)) 94 return (-1); 95 tocopy = ms->len - ms->offset; 96 if (len < tocopy) 97 tocopy = len; 98 memcpy(*ms->bufp + ms->offset, buf, tocopy); 99 ms->offset += tocopy; 100 memstream_update(ms); 101 #ifdef DEBUG 102 fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy); 103 #endif 104 return (tocopy); 105 } 106 107 static fpos_t 108 memstream_seek(void *cookie, fpos_t pos, int whence) 109 { 110 struct memstream *ms; 111 #ifdef DEBUG 112 fpos_t old; 113 #endif 114 115 ms = cookie; 116 #ifdef DEBUG 117 old = ms->offset; 118 #endif 119 switch (whence) { 120 case SEEK_SET: 121 /* _fseeko() checks for negative offsets. */ 122 assert(pos >= 0); 123 ms->offset = pos; 124 break; 125 case SEEK_CUR: 126 /* This is only called by _ftello(). */ 127 assert(pos == 0); 128 break; 129 case SEEK_END: 130 if (pos < 0) { 131 if (pos + ms->len < 0) { 132 #ifdef DEBUG 133 fprintf(stderr, 134 "MS: bad SEEK_END: pos %jd, len %zd\n", 135 (intmax_t)pos, ms->len); 136 #endif 137 errno = EINVAL; 138 return (-1); 139 } 140 } else { 141 if (FPOS_MAX - ms->len < pos) { 142 #ifdef DEBUG 143 fprintf(stderr, 144 "MS: bad SEEK_END: pos %jd, len %zd\n", 145 (intmax_t)pos, ms->len); 146 #endif 147 errno = EOVERFLOW; 148 return (-1); 149 } 150 } 151 ms->offset = ms->len + pos; 152 break; 153 } 154 memstream_update(ms); 155 #ifdef DEBUG 156 fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos, 157 whence, (intmax_t)old, (intmax_t)ms->offset); 158 #endif 159 return (ms->offset); 160 } 161 162 static int 163 memstream_close(void *cookie) 164 { 165 166 free(cookie); 167 return (0); 168 } 169 170 FILE * 171 open_memstream(char **bufp, size_t *sizep) 172 { 173 struct memstream *ms; 174 int save_errno; 175 FILE *fp; 176 177 if (bufp == NULL || sizep == NULL) { 178 errno = EINVAL; 179 return (NULL); 180 } 181 *bufp = calloc(1, 1); 182 if (*bufp == NULL) 183 return (NULL); 184 ms = malloc(sizeof(*ms)); 185 if (ms == NULL) { 186 save_errno = errno; 187 free(*bufp); 188 *bufp = NULL; 189 errno = save_errno; 190 return (NULL); 191 } 192 ms->bufp = bufp; 193 ms->sizep = sizep; 194 ms->len = 0; 195 ms->offset = 0; 196 memstream_update(ms); 197 fp = funopen(ms, NULL, memstream_write, memstream_seek, 198 memstream_close); 199 if (fp == NULL) { 200 save_errno = errno; 201 free(ms); 202 free(*bufp); 203 *bufp = NULL; 204 errno = save_errno; 205 return (NULL); 206 } 207 fwide(fp, -1); 208 return (fp); 209 } 210