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