1 /* $NetBSD: fmemopen.c,v 1.4 2010/09/27 16:50:13 tnozaki Exp $ */ 2 3 /*- 4 * Copyright (c)2007, 2010 Takehiko NOZAKI, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 #include <assert.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stddef.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 38 #include "local.h" 39 #include "priv_stdio.h" 40 41 struct fmemopen_cookie { 42 char *head, *tail, *cur, *eob; 43 }; 44 45 static int min(int x, int y) { 46 return (x > y) ? (y) : (x); 47 } 48 49 static int 50 fmemopen_read(void *cookie, char *buf, int nbytes) 51 { 52 struct fmemopen_cookie *p; 53 char *s; 54 int len; 55 56 assert(cookie != NULL); 57 assert(buf != NULL && nbytes > 0); 58 59 p = (struct fmemopen_cookie *)cookie; 60 s = p->cur; 61 len = min(p->tail - p->cur, nbytes); 62 bcopy(p->cur, buf, len); 63 p->cur += len; 64 65 return (int)(p->cur - s); 66 } 67 68 static int 69 fmemopen_write(void *cookie, const char *buf, int nbytes) 70 { 71 struct fmemopen_cookie *p; 72 char *s; 73 74 assert(cookie != NULL); 75 assert(buf != NULL && nbytes > 0); 76 77 p = (struct fmemopen_cookie *)cookie; 78 if (p->cur >= p->tail) 79 return 0; 80 s = p->cur; 81 do { 82 if (p->cur == p->tail - 1) { 83 if (*buf == '\0') { 84 *p->cur++ = '\0'; 85 goto ok; 86 } 87 break; 88 } 89 *p->cur++ = *buf++; 90 } while (--nbytes > 0); 91 *p->cur = '\0'; 92 ok: 93 if (p->cur > p->eob) 94 p->eob = p->cur; 95 96 return (int)(p->cur - s); 97 } 98 99 static fpos_t 100 fmemopen_seek(void *cookie, fpos_t offset, int whence) 101 { 102 struct fmemopen_cookie *p; 103 104 assert(cookie != NULL); 105 106 p = (struct fmemopen_cookie *)cookie; 107 switch (whence) { 108 case SEEK_SET: 109 break; 110 case SEEK_CUR: 111 offset += p->cur - p->head; 112 break; 113 case SEEK_END: 114 offset += p->eob - p->head; 115 break; 116 default: 117 errno = EINVAL; 118 goto error; 119 } 120 if (offset >= (fpos_t)0 && offset <= p->tail - p->head) { 121 p->cur = p->head + (ptrdiff_t)offset; 122 return (fpos_t)(p->cur - p->head); 123 } 124 error: 125 return (fpos_t)-1; 126 } 127 128 static int 129 fmemopen_close0(void *cookie) 130 { 131 assert(cookie != NULL); 132 133 free(cookie); 134 135 return 0; 136 } 137 138 static int 139 fmemopen_close1(void *cookie) 140 { 141 struct fmemopen_cookie *p; 142 143 assert(cookie != NULL); 144 145 p = (struct fmemopen_cookie *)cookie; 146 free(p->head); 147 free(p); 148 149 return 0; 150 } 151 152 153 FILE * 154 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 155 { 156 int flags, oflags; 157 FILE *fp; 158 struct fmemopen_cookie *cookie; 159 160 if (size < (size_t)1) 161 goto invalid; 162 163 flags = __sflags(mode, &oflags); 164 if (flags == 0) 165 return NULL; 166 167 if ((oflags & O_RDWR) == 0 && buf == NULL) 168 goto invalid; 169 170 fp = __sfp(); 171 if (fp == NULL) 172 return NULL; 173 174 cookie = malloc(sizeof(*cookie)); 175 if (cookie == NULL) 176 goto release; 177 178 if (buf == NULL) { 179 cookie->head = malloc(size); 180 if (cookie->head == NULL) { 181 free(cookie); 182 goto release; 183 } 184 *cookie->head = '\0'; 185 fp->_close = &fmemopen_close1; 186 } else { 187 cookie->head = (char *)buf; 188 if (oflags & O_TRUNC) 189 *cookie->head = '\0'; 190 fp->_close = &fmemopen_close0; 191 } 192 193 cookie->tail = cookie->head + size; 194 cookie->eob = cookie->head; 195 do { 196 if (*cookie->eob == '\0') 197 break; 198 ++cookie->eob; 199 } while (--size > 0); 200 201 cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head; 202 203 fp->pub._flags = flags; 204 fp->_write = (flags & __SRD) ? NULL : &fmemopen_write; 205 fp->_read = (flags & __SWR) ? NULL : &fmemopen_read; 206 fp->_seek = &fmemopen_seek; 207 fp->_cookie = (void *)cookie; 208 209 return fp; 210 211 invalid: 212 errno = EINVAL; 213 return NULL; 214 215 release: 216 fp->pub._flags = 0; 217 return NULL; 218 } 219 220