fmemopen.c revision 266971
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: head/lib/libc/stdio/fmemopen.c 266971 2014-06-02 13:48:57Z gahr $"); 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 152246148Sgahr /* 153246148Sgahr * Turn off buffering, so a write past the end of the buffer 154246148Sgahr * correctly returns a short object count. 155246148Sgahr */ 156246206Sgahr setvbuf(f, NULL, _IONBF, 0); 157246120Sgahr 158246120Sgahr return (f); 159246120Sgahr} 160246120Sgahr 161246120Sgahrstatic int 162246206Sgahrfmemopen_read(void *cookie, char *buf, int nbytes) 163246120Sgahr{ 164246148Sgahr struct fmemopen_cookie *ck = cookie; 165246120Sgahr 166246120Sgahr if (nbytes > ck->len - ck->off) 167246120Sgahr nbytes = ck->len - ck->off; 168246120Sgahr 169246120Sgahr if (nbytes == 0) 170246120Sgahr return (0); 171246120Sgahr 172246206Sgahr memcpy(buf, ck->buf + ck->off, nbytes); 173246120Sgahr 174246120Sgahr ck->off += nbytes; 175246120Sgahr 176246120Sgahr return (nbytes); 177246120Sgahr} 178246120Sgahr 179246120Sgahrstatic int 180246206Sgahrfmemopen_write(void *cookie, const char *buf, int nbytes) 181246120Sgahr{ 182246148Sgahr struct fmemopen_cookie *ck = cookie; 183246120Sgahr 184246148Sgahr if (nbytes > ck->size - ck->off) 185246148Sgahr nbytes = ck->size - ck->off; 186246120Sgahr 187246120Sgahr if (nbytes == 0) 188246120Sgahr return (0); 189246120Sgahr 190246206Sgahr memcpy(ck->buf + ck->off, buf, nbytes); 191246120Sgahr 192246120Sgahr ck->off += nbytes; 193246120Sgahr 194246148Sgahr if (ck->off > ck->len) 195246148Sgahr ck->len = ck->off; 196246148Sgahr 197246148Sgahr /* 198246148Sgahr * We append a NULL byte if all these conditions are met: 199246148Sgahr * - the buffer is not binary 200246148Sgahr * - the buffer is not full 201246148Sgahr * - the data just written doesn't already end with a NULL byte 202246148Sgahr */ 203246148Sgahr if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 204246120Sgahr ck->buf[ck->off] = '\0'; 205246120Sgahr 206246120Sgahr return (nbytes); 207246120Sgahr} 208246120Sgahr 209246120Sgahrstatic fpos_t 210246206Sgahrfmemopen_seek(void *cookie, fpos_t offset, int whence) 211246120Sgahr{ 212246148Sgahr struct fmemopen_cookie *ck = cookie; 213246120Sgahr 214246120Sgahr 215246120Sgahr switch (whence) { 216246120Sgahr case SEEK_SET: 217246148Sgahr if (offset > ck->size) { 218246120Sgahr errno = EINVAL; 219246120Sgahr return (-1); 220246120Sgahr } 221246120Sgahr ck->off = offset; 222246120Sgahr break; 223246120Sgahr 224246120Sgahr case SEEK_CUR: 225246148Sgahr if (ck->off + offset > ck->size) { 226246120Sgahr errno = EINVAL; 227246120Sgahr return (-1); 228246120Sgahr } 229246120Sgahr ck->off += offset; 230246120Sgahr break; 231246120Sgahr 232246120Sgahr case SEEK_END: 233246120Sgahr if (offset > 0 || -offset > ck->len) { 234246120Sgahr errno = EINVAL; 235246120Sgahr return (-1); 236246120Sgahr } 237246120Sgahr ck->off = ck->len + offset; 238246120Sgahr break; 239246120Sgahr 240246120Sgahr default: 241246120Sgahr errno = EINVAL; 242246120Sgahr return (-1); 243246120Sgahr } 244246120Sgahr 245246120Sgahr return (ck->off); 246246120Sgahr} 247246120Sgahr 248246120Sgahrstatic int 249246206Sgahrfmemopen_close(void *cookie) 250246120Sgahr{ 251246148Sgahr struct fmemopen_cookie *ck = cookie; 252246120Sgahr 253246120Sgahr if (ck->own) 254246206Sgahr free(ck->buf); 255246120Sgahr 256246206Sgahr free(ck); 257246120Sgahr 258246120Sgahr return (0); 259246120Sgahr} 260