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