fmemopen.c revision 246148
1246120Sgahr/*- 2246120SgahrCopyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 3246120Sgahr 4246120SgahrRedistribution and use in source and binary forms, with or without 5246120Sgahrmodification, are permitted provided that the following conditions 6246120Sgahrare met: 7246120Sgahr1. Redistributions of source code must retain the above copyright 8246120Sgahr notice, this list of conditions and the following disclaimer. 9246120Sgahr2. Redistributions in binary form must reproduce the above copyright 10246120Sgahr notice, this list of conditions and the following disclaimer in the 11246120Sgahr documentation and/or other materials provided with the distribution. 12246120Sgahr 13246120SgahrTHIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14246120SgahrANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15246120SgahrIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16246120SgahrARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17246120SgahrFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18246120SgahrDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19246120SgahrOR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20246120SgahrHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21246120SgahrLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22246120SgahrOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23246120SgahrSUCH DAMAGE. 24246120Sgahr*/ 25246120Sgahr 26246120Sgahr#include <sys/cdefs.h> 27246120Sgahr__FBSDID("$FreeBSD: head/lib/libc/stdio/fmemopen.c 246148 2013-01-31 16:39:50Z gahr $"); 28246120Sgahr 29246148Sgahr#include <fcntl.h> 30246120Sgahr#include <stdio.h> 31246120Sgahr#include <stdlib.h> 32246120Sgahr#include <string.h> 33246120Sgahr#include <errno.h> 34246148Sgahr#include "local.h" 35246120Sgahr 36246148Sgahrstruct fmemopen_cookie 37246120Sgahr{ 38246148Sgahr char *buf; /* pointer to the memory region */ 39246148Sgahr char own; /* did we allocate the buffer ourselves? */ 40246148Sgahr char bin; /* is this a binary buffer? */ 41246148Sgahr size_t size; /* buffer length in bytes */ 42246148Sgahr size_t len; /* data length in bytes */ 43246148Sgahr size_t off; /* current offset into the buffer */ 44246120Sgahr}; 45246120Sgahr 46246120Sgahrstatic int fmemopen_read (void *cookie, char *buf, int nbytes); 47246120Sgahrstatic int fmemopen_write (void *cookie, const char *buf, int nbytes); 48246120Sgahrstatic fpos_t fmemopen_seek (void *cookie, fpos_t offset, int whence); 49246120Sgahrstatic int fmemopen_close (void *cookie); 50246120Sgahr 51246120SgahrFILE * 52246120Sgahrfmemopen (void * __restrict buf, size_t size, const char * __restrict mode) 53246120Sgahr{ 54246148Sgahr struct fmemopen_cookie *ck; 55246148Sgahr FILE *f; 56246148Sgahr int flags, rc; 57246148Sgahr 58246148Sgahr /* 59246148Sgahr * Retrieve the flags as used by open(2) from the mode argument, and 60246148Sgahr * validate them. 61246148Sgahr * */ 62246148Sgahr rc = __sflags (mode, &flags); 63246148Sgahr if (rc == 0) { 64246148Sgahr errno = EINVAL; 65246148Sgahr return (NULL); 66246148Sgahr } 67246148Sgahr 68246148Sgahr /* 69246148Sgahr * There's no point in requiring an automatically allocated buffer 70246148Sgahr * in write-only mode. 71246148Sgahr */ 72246148Sgahr if (!(flags & O_RDWR) && buf == NULL) { 73246148Sgahr errno = EINVAL; 74246148Sgahr return (NULL); 75246148Sgahr } 76246148Sgahr 77246148Sgahr /* Allocate a cookie. */ 78246148Sgahr ck = malloc (sizeof (struct fmemopen_cookie)); 79246120Sgahr if (ck == NULL) { 80246120Sgahr return (NULL); 81246120Sgahr } 82246120Sgahr 83246148Sgahr ck->off = 0; 84246148Sgahr ck->size = size; 85246120Sgahr 86246148Sgahr /* Check whether we have to allocate the buffer ourselves. */ 87246120Sgahr ck->own = ((ck->buf = buf) == NULL); 88246120Sgahr if (ck->own) { 89246120Sgahr ck->buf = malloc (size); 90246120Sgahr if (ck->buf == NULL) { 91246120Sgahr free (ck); 92246120Sgahr return (NULL); 93246120Sgahr } 94246148Sgahr } 95246148Sgahr 96246148Sgahr /* 97246148Sgahr * POSIX distinguishes between w+ and r+, in that w+ is supposed to 98246148Sgahr * truncate the buffer. 99246148Sgahr */ 100246148Sgahr if (ck->own || mode[0] == 'w') { 101246120Sgahr ck->buf[0] = '\0'; 102246120Sgahr } 103246120Sgahr 104246148Sgahr /* Check for binary mode. */ 105246148Sgahr ck->bin = strchr(mode, 'b') != NULL; 106246120Sgahr 107246148Sgahr /* 108246148Sgahr * The size of the current buffer contents is set depending on the 109246148Sgahr * mode: 110246148Sgahr * 111246148Sgahr * for append (text-mode), the position of the first NULL byte, or the 112246148Sgahr * size of the buffer if none is found 113246148Sgahr * 114246148Sgahr * for append (binary-mode), the size of the buffer 115246148Sgahr * 116246148Sgahr * for read, the size of the buffer 117246148Sgahr * 118246148Sgahr * for write, 0 119246148Sgahr */ 120246148Sgahr switch (mode[0]) { 121246148Sgahr case 'a': 122246148Sgahr if (ck->bin) { 123246148Sgahr /* 124246148Sgahr * This isn't useful, since the buffer isn't 125246148Sgahr * allowed to grow. 126246148Sgahr */ 127246148Sgahr ck->off = ck->len = size; 128246148Sgahr } else 129246148Sgahr ck->off = ck->len = strnlen(ck->buf, ck->size); 130246148Sgahr break; 131246148Sgahr case 'r': 132246148Sgahr ck->len = size; 133246148Sgahr break; 134246148Sgahr case 'w': 135246148Sgahr ck->len = 0; 136246148Sgahr break; 137246148Sgahr } 138246148Sgahr 139246148Sgahr /* Actuall wrapper. */ 140246148Sgahr f = funopen ((void *)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) 147246120Sgahr free (ck->buf); 148246120Sgahr 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 */ 156246120Sgahr setvbuf (f, (char *) NULL, _IONBF, 0); 157246120Sgahr 158246120Sgahr return (f); 159246120Sgahr} 160246120Sgahr 161246120Sgahrstatic int 162246120Sgahrfmemopen_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 172246120Sgahr memcpy (buf, ck->buf + ck->off, nbytes); 173246120Sgahr 174246120Sgahr ck->off += nbytes; 175246120Sgahr 176246120Sgahr return (nbytes); 177246120Sgahr} 178246120Sgahr 179246120Sgahrstatic int 180246120Sgahrfmemopen_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 190246120Sgahr 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 210246120Sgahrfmemopen_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 249246120Sgahrfmemopen_close (void *cookie) 250246120Sgahr{ 251246148Sgahr struct fmemopen_cookie *ck = cookie; 252246120Sgahr 253246120Sgahr if (ck->own) 254246120Sgahr free (ck->buf); 255246120Sgahr 256246120Sgahr free (ck); 257246120Sgahr 258246120Sgahr return (0); 259246120Sgahr} 260