1246120Sgahr/*- 2246206Sgahr * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 3246206Sgahr * 4246206Sgahr * Redistribution and use in source and binary forms, with or without 5246206Sgahr * modification, are permitted provided that the following conditions 6246206Sgahr * are met: 7246206Sgahr * 1. Redistributions of source code must retain the above copyright 8246206Sgahr * notice, this list of conditions and the following disclaimer. 9246206Sgahr * 2. Redistributions in binary form must reproduce the above copyright 10246206Sgahr * notice, this list of conditions and the following disclaimer in the 11246206Sgahr * documentation and/or other materials provided with the distribution. 12246206Sgahr * 13246206Sgahr * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14246206Sgahr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15246206Sgahr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16246206Sgahr * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17246206Sgahr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18246206Sgahr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19246206Sgahr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20246206Sgahr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21246206Sgahr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22246206Sgahr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23246206Sgahr * SUCH DAMAGE. 24246206Sgahr */ 25246120Sgahr 26246120Sgahr#include <sys/cdefs.h> 27246120Sgahr__FBSDID("$FreeBSD$"); 28246120Sgahr 29246148Sgahr#include <fcntl.h> 30246206Sgahr#include <stdbool.h> 31246120Sgahr#include <stdio.h> 32246120Sgahr#include <stdlib.h> 33246120Sgahr#include <string.h> 34246120Sgahr#include <errno.h> 35246148Sgahr#include "local.h" 36246120Sgahr 37246148Sgahrstruct fmemopen_cookie 38246120Sgahr{ 39246148Sgahr char *buf; /* pointer to the memory region */ 40246206Sgahr bool own; /* did we allocate the buffer ourselves? */ 41246148Sgahr char bin; /* is this a binary buffer? */ 42246148Sgahr size_t size; /* buffer length in bytes */ 43246148Sgahr size_t len; /* data length in bytes */ 44246148Sgahr size_t off; /* current offset into the buffer */ 45246120Sgahr}; 46246120Sgahr 47246206Sgahrstatic int fmemopen_read(void *cookie, char *buf, int nbytes); 48246206Sgahrstatic int fmemopen_write(void *cookie, const char *buf, int nbytes); 49246206Sgahrstatic fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); 50246206Sgahrstatic int fmemopen_close(void *cookie); 51246120Sgahr 52246120SgahrFILE * 53246206Sgahrfmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 54246120Sgahr{ 55246148Sgahr struct fmemopen_cookie *ck; 56246148Sgahr FILE *f; 57246148Sgahr int flags, rc; 58246148Sgahr 59246206Sgahr /* 60266971Sgahr * POSIX says we shall return EINVAL if size is 0. 61266971Sgahr */ 62266971Sgahr if (size == 0) { 63266971Sgahr errno = EINVAL; 64266971Sgahr return (NULL); 65266971Sgahr } 66266971Sgahr 67266971Sgahr /* 68246148Sgahr * Retrieve the flags as used by open(2) from the mode argument, and 69246148Sgahr * validate them. 70246206Sgahr */ 71246206Sgahr rc = __sflags(mode, &flags); 72246148Sgahr if (rc == 0) { 73246148Sgahr errno = EINVAL; 74246148Sgahr return (NULL); 75246148Sgahr } 76246148Sgahr 77246206Sgahr /* 78246148Sgahr * There's no point in requiring an automatically allocated buffer 79246148Sgahr * in write-only mode. 80246148Sgahr */ 81246148Sgahr if (!(flags & O_RDWR) && buf == NULL) { 82246148Sgahr errno = EINVAL; 83246148Sgahr return (NULL); 84246148Sgahr } 85246148Sgahr 86246206Sgahr ck = malloc(sizeof(struct fmemopen_cookie)); 87246120Sgahr if (ck == NULL) { 88246120Sgahr return (NULL); 89246120Sgahr } 90246120Sgahr 91246148Sgahr ck->off = 0; 92246148Sgahr ck->size = size; 93246120Sgahr 94246148Sgahr /* Check whether we have to allocate the buffer ourselves. */ 95246120Sgahr ck->own = ((ck->buf = buf) == NULL); 96246120Sgahr if (ck->own) { 97246206Sgahr ck->buf = malloc(size); 98246120Sgahr if (ck->buf == NULL) { 99246206Sgahr free(ck); 100246120Sgahr return (NULL); 101246120Sgahr } 102246148Sgahr } 103246148Sgahr 104246148Sgahr /* 105246148Sgahr * POSIX distinguishes between w+ and r+, in that w+ is supposed to 106246148Sgahr * truncate the buffer. 107246148Sgahr */ 108246148Sgahr if (ck->own || mode[0] == 'w') { 109246120Sgahr ck->buf[0] = '\0'; 110246120Sgahr } 111246120Sgahr 112246148Sgahr /* Check for binary mode. */ 113246148Sgahr ck->bin = strchr(mode, 'b') != NULL; 114246120Sgahr 115246148Sgahr /* 116246148Sgahr * The size of the current buffer contents is set depending on the 117246148Sgahr * mode: 118246206Sgahr * 119246148Sgahr * for append (text-mode), the position of the first NULL byte, or the 120246148Sgahr * size of the buffer if none is found 121246148Sgahr * 122246148Sgahr * for append (binary-mode), the size of the buffer 123246206Sgahr * 124246148Sgahr * for read, the size of the buffer 125246206Sgahr * 126246148Sgahr * for write, 0 127246148Sgahr */ 128246148Sgahr switch (mode[0]) { 129246148Sgahr case 'a': 130266971Sgahr ck->off = ck->len = strnlen(ck->buf, ck->size); 131246148Sgahr break; 132246148Sgahr case 'r': 133246148Sgahr ck->len = size; 134246148Sgahr break; 135246148Sgahr case 'w': 136246148Sgahr ck->len = 0; 137246148Sgahr break; 138246148Sgahr } 139246148Sgahr 140246206Sgahr f = funopen(ck, 141246148Sgahr flags & O_WRONLY ? NULL : fmemopen_read, 142246148Sgahr flags & O_RDONLY ? NULL : fmemopen_write, 143246120Sgahr fmemopen_seek, fmemopen_close); 144246120Sgahr 145246120Sgahr if (f == NULL) { 146246120Sgahr if (ck->own) 147246206Sgahr free(ck->buf); 148246206Sgahr free(ck); 149246120Sgahr return (NULL); 150246120Sgahr } 151246120Sgahr 152289931Sache if (mode[0] == 'a') 153289931Sache f->_flags |= __SAPP; 154289931Sache 155246148Sgahr /* 156246148Sgahr * Turn off buffering, so a write past the end of the buffer 157246148Sgahr * correctly returns a short object count. 158246148Sgahr */ 159246206Sgahr setvbuf(f, NULL, _IONBF, 0); 160246120Sgahr 161246120Sgahr return (f); 162246120Sgahr} 163246120Sgahr 164246120Sgahrstatic int 165246206Sgahrfmemopen_read(void *cookie, char *buf, int nbytes) 166246120Sgahr{ 167246148Sgahr struct fmemopen_cookie *ck = cookie; 168246120Sgahr 169246120Sgahr if (nbytes > ck->len - ck->off) 170246120Sgahr nbytes = ck->len - ck->off; 171246120Sgahr 172246120Sgahr if (nbytes == 0) 173246120Sgahr return (0); 174246120Sgahr 175246206Sgahr memcpy(buf, ck->buf + ck->off, nbytes); 176246120Sgahr 177246120Sgahr ck->off += nbytes; 178246120Sgahr 179246120Sgahr return (nbytes); 180246120Sgahr} 181246120Sgahr 182246120Sgahrstatic int 183246206Sgahrfmemopen_write(void *cookie, const char *buf, int nbytes) 184246120Sgahr{ 185246148Sgahr struct fmemopen_cookie *ck = cookie; 186246120Sgahr 187246148Sgahr if (nbytes > ck->size - ck->off) 188246148Sgahr nbytes = ck->size - ck->off; 189246120Sgahr 190246120Sgahr if (nbytes == 0) 191246120Sgahr return (0); 192246120Sgahr 193246206Sgahr memcpy(ck->buf + ck->off, buf, nbytes); 194246120Sgahr 195246120Sgahr ck->off += nbytes; 196246120Sgahr 197246148Sgahr if (ck->off > ck->len) 198246148Sgahr ck->len = ck->off; 199246148Sgahr 200246148Sgahr /* 201246148Sgahr * We append a NULL byte if all these conditions are met: 202246148Sgahr * - the buffer is not binary 203246148Sgahr * - the buffer is not full 204246148Sgahr * - the data just written doesn't already end with a NULL byte 205246148Sgahr */ 206246148Sgahr if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 207246120Sgahr ck->buf[ck->off] = '\0'; 208246120Sgahr 209246120Sgahr return (nbytes); 210246120Sgahr} 211246120Sgahr 212246120Sgahrstatic fpos_t 213246206Sgahrfmemopen_seek(void *cookie, fpos_t offset, int whence) 214246120Sgahr{ 215246148Sgahr struct fmemopen_cookie *ck = cookie; 216246120Sgahr 217246120Sgahr 218246120Sgahr switch (whence) { 219246120Sgahr case SEEK_SET: 220246148Sgahr if (offset > ck->size) { 221246120Sgahr errno = EINVAL; 222246120Sgahr return (-1); 223246120Sgahr } 224246120Sgahr ck->off = offset; 225246120Sgahr break; 226246120Sgahr 227246120Sgahr case SEEK_CUR: 228246148Sgahr if (ck->off + offset > ck->size) { 229246120Sgahr errno = EINVAL; 230246120Sgahr return (-1); 231246120Sgahr } 232246120Sgahr ck->off += offset; 233246120Sgahr break; 234246120Sgahr 235246120Sgahr case SEEK_END: 236246120Sgahr if (offset > 0 || -offset > ck->len) { 237246120Sgahr errno = EINVAL; 238246120Sgahr return (-1); 239246120Sgahr } 240246120Sgahr ck->off = ck->len + offset; 241246120Sgahr break; 242246120Sgahr 243246120Sgahr default: 244246120Sgahr errno = EINVAL; 245246120Sgahr return (-1); 246246120Sgahr } 247246120Sgahr 248246120Sgahr return (ck->off); 249246120Sgahr} 250246120Sgahr 251246120Sgahrstatic int 252246206Sgahrfmemopen_close(void *cookie) 253246120Sgahr{ 254246148Sgahr struct fmemopen_cookie *ck = cookie; 255246120Sgahr 256246120Sgahr if (ck->own) 257246206Sgahr free(ck->buf); 258246120Sgahr 259246206Sgahr free(ck); 260246120Sgahr 261246120Sgahr return (0); 262246120Sgahr} 263