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 40 struct fmemopen_cookie { 41 char *head, *tail, *cur, *eob; 42 }; 43 44 static int 45 fmemopen_read(void *cookie, char *buf, int nbytes) 46 { 47 struct fmemopen_cookie *p; 48 char *s; 49 int len; 50 51 assert(cookie != NULL); 52 assert(buf != NULL && nbytes > 0); 53 54 p = cookie; 55 s = p->cur; 56 len = MIN(p->tail - p->cur, nbytes); 57 bcopy(p->cur, buf, len); 58 p->cur += len; 59 60 return (int)(p->cur - s); 61 } 62 63 static int 64 fmemopen_write(void *cookie, const char *buf, int nbytes) 65 { 66 struct fmemopen_cookie *p; 67 char *s; 68 int len; 69 70 assert(cookie != NULL); 71 assert(buf != NULL && nbytes > 0); 72 73 p = cookie; 74 if (p->cur >= p->tail) 75 return 0; 76 s = p->cur; 77 78 len = MIN(p->tail - p->cur, nbytes); 79 80 bcopy(buf, p->cur, len); 81 82 p->cur += len - 1; 83 if (p->cur == p->tail - 1) { 84 *p->cur = '\0'; 85 if (buf[len - 1] == '\0') 86 p->cur++; 87 } else { 88 *++p->cur = '\0'; 89 } 90 91 if (p->cur > p->eob) 92 p->eob = p->cur; 93 94 return (int)(p->cur - s); 95 } 96 97 static fpos_t 98 fmemopen_seek(void *cookie, fpos_t offset, int whence) 99 { 100 struct fmemopen_cookie *p; 101 102 assert(cookie != NULL); 103 104 p = (struct fmemopen_cookie *)cookie; 105 switch (whence) { 106 case SEEK_SET: 107 break; 108 case SEEK_CUR: 109 offset += p->cur - p->head; 110 break; 111 case SEEK_END: 112 offset += p->eob - p->head; 113 break; 114 default: 115 errno = EINVAL; 116 goto error; 117 } 118 if (offset >= (fpos_t)0 && offset <= p->tail - p->head) { 119 p->cur = p->head + (ptrdiff_t)offset; 120 return (fpos_t)(p->cur - p->head); 121 } 122 error: 123 return (fpos_t)-1; 124 } 125 126 static int 127 fmemopen_close0(void *cookie) 128 { 129 assert(cookie != NULL); 130 131 free(cookie); 132 133 return 0; 134 } 135 136 static int 137 fmemopen_close1(void *cookie) 138 { 139 struct fmemopen_cookie *p; 140 141 assert(cookie != NULL); 142 143 p = cookie; 144 free(p->head); 145 free(p); 146 147 return 0; 148 } 149 150 151 FILE * 152 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 153 { 154 int flags, oflags; 155 FILE *fp; 156 struct fmemopen_cookie *cookie; 157 158 if (size < (size_t)1) 159 goto invalid; 160 161 flags = __sflags(mode, &oflags); 162 if (flags == 0) 163 return NULL; 164 165 if ((oflags & O_RDWR) == 0 && buf == NULL) 166 goto invalid; 167 168 fp = __sfp(); 169 if (fp == NULL) 170 return NULL; 171 172 cookie = malloc(sizeof(*cookie)); 173 if (cookie == NULL) 174 goto release; 175 176 if (buf == NULL) { 177 cookie->head = malloc(size); 178 if (cookie->head == NULL) { 179 free(cookie); 180 goto release; 181 } 182 *cookie->head = '\0'; 183 fp->_close = &fmemopen_close1; 184 } else { 185 cookie->head = (char *)buf; 186 if (oflags & O_TRUNC) 187 *cookie->head = '\0'; 188 fp->_close = &fmemopen_close0; 189 } 190 191 cookie->tail = cookie->head + size; 192 cookie->eob = cookie->head; 193 do { 194 if (*cookie->eob == '\0') 195 break; 196 ++cookie->eob; 197 } while (--size > 0); 198 199 cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head; 200 201 fp->pub._flags = flags; 202 fp->_write = (flags & __SRD) ? NULL : &fmemopen_write; 203 fp->_read = (flags & __SWR) ? NULL : &fmemopen_read; 204 fp->_seek = &fmemopen_seek; 205 fp->_cookie = (void *)cookie; 206 207 return fp; 208 209 invalid: 210 errno = EINVAL; 211 return NULL; 212 213 release: 214 fp->pub._flags = 0; 215 return NULL; 216 } 217 218