1/* $NetBSD: fmemopen.c,v 1.7 2012/03/27 15:05:42 christos 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.7 2012/03/27 15:05:42 christos 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 45struct fmemopen_cookie { 46 char *head, *tail, *cur, *eob; 47}; 48 49static ssize_t 50fmemopen_read(void *cookie, void *buf, size_t nbytes) 51{ 52 struct fmemopen_cookie *p; 53 char *s, *b = buf; 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 *b++ = *p->cur++; 64 } while (--nbytes > 0); 65 66 return (ssize_t)(p->cur - s); 67} 68 69static ssize_t 70fmemopen_write(void *cookie, const void *buf, size_t nbytes) 71{ 72 struct fmemopen_cookie *p; 73 char *s; 74 const char *b = buf; 75 76 _DIAGASSERT(cookie != NULL); 77 _DIAGASSERT(buf != NULL && nbytes > 0); 78 79 p = (struct fmemopen_cookie *)cookie; 80 if (p->cur >= p->tail) 81 return 0; 82 s = p->cur; 83 do { 84 if (p->cur == p->tail - 1) { 85 if (*b == '\0') { 86 *p->cur++ = '\0'; 87 goto ok; 88 } 89 break; 90 } 91 *p->cur++ = *b++; 92 } while (--nbytes > 0); 93 *p->cur = '\0'; 94ok: 95 if (p->cur > p->eob) 96 p->eob = p->cur; 97 98 return (ssize_t)(p->cur - s); 99} 100 101#ifdef notyet 102static int 103fmemopen_flush(void *cookie) 104{ 105 struct fmemopen_cookie *p; 106 107 _DIAGASSERT(cookie != NULL); 108 109 p = (struct fmemopen_cookie *)cookie; 110 if (p->cur >= p->tail) 111 return -1; 112 *p->cur = '\0'; 113 return 0; 114} 115#endif 116 117static off_t 118fmemopen_seek(void *cookie, off_t offset, int whence) 119{ 120 struct fmemopen_cookie *p; 121 122 _DIAGASSERT(cookie != NULL); 123 124 p = (struct fmemopen_cookie *)cookie; 125 switch (whence) { 126 case SEEK_SET: 127 break; 128 case SEEK_CUR: 129 offset += p->cur - p->head; 130 break; 131 case SEEK_END: 132 offset += p->eob - p->head; 133 break; 134 default: 135 errno = EINVAL; 136 goto error; 137 } 138 if (offset >= (off_t)0 && offset <= p->tail - p->head) { 139 p->cur = p->head + (ptrdiff_t)offset; 140 return (off_t)(p->cur - p->head); 141 } 142error: 143 return (off_t)-1; 144} 145 146static int 147fmemopen_close0(void *cookie) 148{ 149 _DIAGASSERT(cookie != NULL); 150 151 free(cookie); 152 153 return 0; 154} 155 156static int 157fmemopen_close1(void *cookie) 158{ 159 struct fmemopen_cookie *p; 160 161 _DIAGASSERT(cookie != NULL); 162 163 p = (struct fmemopen_cookie *)cookie; 164 free(p->head); 165 free(p); 166 167 return 0; 168} 169 170 171FILE * 172fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 173{ 174 int flags, oflags; 175 FILE *fp; 176 struct fmemopen_cookie *cookie; 177 178 if (size < (size_t)1) 179 goto invalid; 180 181 flags = __sflags(mode, &oflags); 182 if (flags == 0) 183 return NULL; 184 185 if ((oflags & O_RDWR) == 0 && buf == NULL) 186 goto invalid; 187 188 fp = __sfp(); 189 if (fp == NULL) 190 return NULL; 191 fp->_file = -1; 192 193 cookie = malloc(sizeof(*cookie)); 194 if (cookie == NULL) 195 goto release; 196 197 if (buf == NULL) { 198 cookie->head = malloc(size); 199 if (cookie->head == NULL) { 200 free(cookie); 201 goto release; 202 } 203 *cookie->head = '\0'; 204 fp->_close = fmemopen_close1; 205 } else { 206 cookie->head = (char *)buf; 207 if (oflags & O_TRUNC) 208 *cookie->head = '\0'; 209 fp->_close = fmemopen_close0; 210 } 211 212 cookie->tail = cookie->head + size; 213 cookie->eob = cookie->head; 214 do { 215 if (*cookie->eob == '\0') 216 break; 217 ++cookie->eob; 218 } while (--size > 0); 219 220 cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head; 221 222 fp->_flags = flags; 223 fp->_write = (flags & __SRD) ? NULL : fmemopen_write; 224 fp->_read = (flags & __SWR) ? NULL : fmemopen_read; 225 fp->_seek = fmemopen_seek; 226#ifdef notyet 227 fp->_flush = fmemopen_flush; 228#endif 229 fp->_cookie = (void *)cookie; 230 231 return fp; 232 233invalid: 234 errno = EINVAL; 235 return NULL; 236 237release: 238 fp->_flags = 0; 239 return NULL; 240} 241