196c95412SPietro Cerutti /*- 296c95412SPietro Cerutti Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 396c95412SPietro Cerutti 496c95412SPietro Cerutti Redistribution and use in source and binary forms, with or without 596c95412SPietro Cerutti modification, are permitted provided that the following conditions 696c95412SPietro Cerutti are met: 796c95412SPietro Cerutti 1. Redistributions of source code must retain the above copyright 896c95412SPietro Cerutti notice, this list of conditions and the following disclaimer. 996c95412SPietro Cerutti 2. Redistributions in binary form must reproduce the above copyright 1096c95412SPietro Cerutti notice, this list of conditions and the following disclaimer in the 1196c95412SPietro Cerutti documentation and/or other materials provided with the distribution. 1296c95412SPietro Cerutti 1396c95412SPietro Cerutti THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1496c95412SPietro Cerutti ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1596c95412SPietro Cerutti IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1696c95412SPietro Cerutti ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 1796c95412SPietro Cerutti FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1896c95412SPietro Cerutti DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1996c95412SPietro Cerutti OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2096c95412SPietro Cerutti HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2196c95412SPietro Cerutti LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2296c95412SPietro Cerutti OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2396c95412SPietro Cerutti SUCH DAMAGE. 2496c95412SPietro Cerutti */ 2596c95412SPietro Cerutti 2696c95412SPietro Cerutti #include <sys/cdefs.h> 2796c95412SPietro Cerutti __FBSDID("$FreeBSD$"); 2896c95412SPietro Cerutti 29*646b68f0SPietro Cerutti #include <fcntl.h> 3096c95412SPietro Cerutti #include <stdio.h> 3196c95412SPietro Cerutti #include <stdlib.h> 3296c95412SPietro Cerutti #include <string.h> 3396c95412SPietro Cerutti #include <errno.h> 34*646b68f0SPietro Cerutti #include "local.h" 3596c95412SPietro Cerutti 36*646b68f0SPietro Cerutti struct fmemopen_cookie 3796c95412SPietro Cerutti { 3896c95412SPietro Cerutti char *buf; /* pointer to the memory region */ 3996c95412SPietro Cerutti char own; /* did we allocate the buffer ourselves? */ 40*646b68f0SPietro Cerutti char bin; /* is this a binary buffer? */ 41*646b68f0SPietro Cerutti size_t size; /* buffer length in bytes */ 42*646b68f0SPietro Cerutti size_t len; /* data length in bytes */ 43*646b68f0SPietro Cerutti size_t off; /* current offset into the buffer */ 4496c95412SPietro Cerutti }; 4596c95412SPietro Cerutti 4696c95412SPietro Cerutti static int fmemopen_read (void *cookie, char *buf, int nbytes); 4796c95412SPietro Cerutti static int fmemopen_write (void *cookie, const char *buf, int nbytes); 4896c95412SPietro Cerutti static fpos_t fmemopen_seek (void *cookie, fpos_t offset, int whence); 4996c95412SPietro Cerutti static int fmemopen_close (void *cookie); 5096c95412SPietro Cerutti 5196c95412SPietro Cerutti FILE * 5296c95412SPietro Cerutti fmemopen (void * __restrict buf, size_t size, const char * __restrict mode) 5396c95412SPietro Cerutti { 54*646b68f0SPietro Cerutti struct fmemopen_cookie *ck; 55*646b68f0SPietro Cerutti FILE *f; 56*646b68f0SPietro Cerutti int flags, rc; 57*646b68f0SPietro Cerutti 58*646b68f0SPietro Cerutti /* 59*646b68f0SPietro Cerutti * Retrieve the flags as used by open(2) from the mode argument, and 60*646b68f0SPietro Cerutti * validate them. 61*646b68f0SPietro Cerutti * */ 62*646b68f0SPietro Cerutti rc = __sflags (mode, &flags); 63*646b68f0SPietro Cerutti if (rc == 0) { 64*646b68f0SPietro Cerutti errno = EINVAL; 65*646b68f0SPietro Cerutti return (NULL); 66*646b68f0SPietro Cerutti } 67*646b68f0SPietro Cerutti 68*646b68f0SPietro Cerutti /* 69*646b68f0SPietro Cerutti * There's no point in requiring an automatically allocated buffer 70*646b68f0SPietro Cerutti * in write-only mode. 71*646b68f0SPietro Cerutti */ 72*646b68f0SPietro Cerutti if (!(flags & O_RDWR) && buf == NULL) { 73*646b68f0SPietro Cerutti errno = EINVAL; 74*646b68f0SPietro Cerutti return (NULL); 75*646b68f0SPietro Cerutti } 76*646b68f0SPietro Cerutti 77*646b68f0SPietro Cerutti /* Allocate a cookie. */ 78*646b68f0SPietro Cerutti ck = malloc (sizeof (struct fmemopen_cookie)); 7996c95412SPietro Cerutti if (ck == NULL) { 8096c95412SPietro Cerutti return (NULL); 8196c95412SPietro Cerutti } 8296c95412SPietro Cerutti 8396c95412SPietro Cerutti ck->off = 0; 84*646b68f0SPietro Cerutti ck->size = size; 8596c95412SPietro Cerutti 86*646b68f0SPietro Cerutti /* Check whether we have to allocate the buffer ourselves. */ 8796c95412SPietro Cerutti ck->own = ((ck->buf = buf) == NULL); 8896c95412SPietro Cerutti if (ck->own) { 8996c95412SPietro Cerutti ck->buf = malloc (size); 9096c95412SPietro Cerutti if (ck->buf == NULL) { 9196c95412SPietro Cerutti free (ck); 9296c95412SPietro Cerutti return (NULL); 9396c95412SPietro Cerutti } 94*646b68f0SPietro Cerutti } 95*646b68f0SPietro Cerutti 96*646b68f0SPietro Cerutti /* 97*646b68f0SPietro Cerutti * POSIX distinguishes between w+ and r+, in that w+ is supposed to 98*646b68f0SPietro Cerutti * truncate the buffer. 99*646b68f0SPietro Cerutti */ 100*646b68f0SPietro Cerutti if (ck->own || mode[0] == 'w') { 10196c95412SPietro Cerutti ck->buf[0] = '\0'; 10296c95412SPietro Cerutti } 10396c95412SPietro Cerutti 104*646b68f0SPietro Cerutti /* Check for binary mode. */ 105*646b68f0SPietro Cerutti ck->bin = strchr(mode, 'b') != NULL; 10696c95412SPietro Cerutti 107*646b68f0SPietro Cerutti /* 108*646b68f0SPietro Cerutti * The size of the current buffer contents is set depending on the 109*646b68f0SPietro Cerutti * mode: 110*646b68f0SPietro Cerutti * 111*646b68f0SPietro Cerutti * for append (text-mode), the position of the first NULL byte, or the 112*646b68f0SPietro Cerutti * size of the buffer if none is found 113*646b68f0SPietro Cerutti * 114*646b68f0SPietro Cerutti * for append (binary-mode), the size of the buffer 115*646b68f0SPietro Cerutti * 116*646b68f0SPietro Cerutti * for read, the size of the buffer 117*646b68f0SPietro Cerutti * 118*646b68f0SPietro Cerutti * for write, 0 119*646b68f0SPietro Cerutti */ 120*646b68f0SPietro Cerutti switch (mode[0]) { 121*646b68f0SPietro Cerutti case 'a': 122*646b68f0SPietro Cerutti if (ck->bin) { 123*646b68f0SPietro Cerutti /* 124*646b68f0SPietro Cerutti * This isn't useful, since the buffer isn't 125*646b68f0SPietro Cerutti * allowed to grow. 126*646b68f0SPietro Cerutti */ 127*646b68f0SPietro Cerutti ck->off = ck->len = size; 128*646b68f0SPietro Cerutti } else 129*646b68f0SPietro Cerutti ck->off = ck->len = strnlen(ck->buf, ck->size); 130*646b68f0SPietro Cerutti break; 131*646b68f0SPietro Cerutti case 'r': 132*646b68f0SPietro Cerutti ck->len = size; 133*646b68f0SPietro Cerutti break; 134*646b68f0SPietro Cerutti case 'w': 135*646b68f0SPietro Cerutti ck->len = 0; 136*646b68f0SPietro Cerutti break; 137*646b68f0SPietro Cerutti } 138*646b68f0SPietro Cerutti 139*646b68f0SPietro Cerutti /* Actuall wrapper. */ 140*646b68f0SPietro Cerutti f = funopen ((void *)ck, 141*646b68f0SPietro Cerutti flags & O_WRONLY ? NULL : fmemopen_read, 142*646b68f0SPietro Cerutti flags & O_RDONLY ? NULL : fmemopen_write, 14396c95412SPietro Cerutti fmemopen_seek, fmemopen_close); 14496c95412SPietro Cerutti 14596c95412SPietro Cerutti if (f == NULL) { 14696c95412SPietro Cerutti if (ck->own) 14796c95412SPietro Cerutti free (ck->buf); 14896c95412SPietro Cerutti free (ck); 14996c95412SPietro Cerutti return (NULL); 15096c95412SPietro Cerutti } 15196c95412SPietro Cerutti 152*646b68f0SPietro Cerutti /* 153*646b68f0SPietro Cerutti * Turn off buffering, so a write past the end of the buffer 154*646b68f0SPietro Cerutti * correctly returns a short object count. 155*646b68f0SPietro Cerutti */ 15696c95412SPietro Cerutti setvbuf (f, (char *) NULL, _IONBF, 0); 15796c95412SPietro Cerutti 15896c95412SPietro Cerutti return (f); 15996c95412SPietro Cerutti } 16096c95412SPietro Cerutti 16196c95412SPietro Cerutti static int 16296c95412SPietro Cerutti fmemopen_read (void *cookie, char *buf, int nbytes) 16396c95412SPietro Cerutti { 164*646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 16596c95412SPietro Cerutti 16696c95412SPietro Cerutti if (nbytes > ck->len - ck->off) 16796c95412SPietro Cerutti nbytes = ck->len - ck->off; 16896c95412SPietro Cerutti 16996c95412SPietro Cerutti if (nbytes == 0) 17096c95412SPietro Cerutti return (0); 17196c95412SPietro Cerutti 17296c95412SPietro Cerutti memcpy (buf, ck->buf + ck->off, nbytes); 17396c95412SPietro Cerutti 17496c95412SPietro Cerutti ck->off += nbytes; 17596c95412SPietro Cerutti 17696c95412SPietro Cerutti return (nbytes); 17796c95412SPietro Cerutti } 17896c95412SPietro Cerutti 17996c95412SPietro Cerutti static int 18096c95412SPietro Cerutti fmemopen_write (void *cookie, const char *buf, int nbytes) 18196c95412SPietro Cerutti { 182*646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 18396c95412SPietro Cerutti 184*646b68f0SPietro Cerutti if (nbytes > ck->size - ck->off) 185*646b68f0SPietro Cerutti nbytes = ck->size - ck->off; 18696c95412SPietro Cerutti 18796c95412SPietro Cerutti if (nbytes == 0) 18896c95412SPietro Cerutti return (0); 18996c95412SPietro Cerutti 19096c95412SPietro Cerutti memcpy (ck->buf + ck->off, buf, nbytes); 19196c95412SPietro Cerutti 19296c95412SPietro Cerutti ck->off += nbytes; 19396c95412SPietro Cerutti 194*646b68f0SPietro Cerutti if (ck->off > ck->len) 195*646b68f0SPietro Cerutti ck->len = ck->off; 196*646b68f0SPietro Cerutti 197*646b68f0SPietro Cerutti /* 198*646b68f0SPietro Cerutti * We append a NULL byte if all these conditions are met: 199*646b68f0SPietro Cerutti * - the buffer is not binary 200*646b68f0SPietro Cerutti * - the buffer is not full 201*646b68f0SPietro Cerutti * - the data just written doesn't already end with a NULL byte 202*646b68f0SPietro Cerutti */ 203*646b68f0SPietro Cerutti if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 20496c95412SPietro Cerutti ck->buf[ck->off] = '\0'; 20596c95412SPietro Cerutti 20696c95412SPietro Cerutti return (nbytes); 20796c95412SPietro Cerutti } 20896c95412SPietro Cerutti 20996c95412SPietro Cerutti static fpos_t 21096c95412SPietro Cerutti fmemopen_seek (void *cookie, fpos_t offset, int whence) 21196c95412SPietro Cerutti { 212*646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 21396c95412SPietro Cerutti 21496c95412SPietro Cerutti 21596c95412SPietro Cerutti switch (whence) { 21696c95412SPietro Cerutti case SEEK_SET: 217*646b68f0SPietro Cerutti if (offset > ck->size) { 21896c95412SPietro Cerutti errno = EINVAL; 21996c95412SPietro Cerutti return (-1); 22096c95412SPietro Cerutti } 22196c95412SPietro Cerutti ck->off = offset; 22296c95412SPietro Cerutti break; 22396c95412SPietro Cerutti 22496c95412SPietro Cerutti case SEEK_CUR: 225*646b68f0SPietro Cerutti if (ck->off + offset > ck->size) { 22696c95412SPietro Cerutti errno = EINVAL; 22796c95412SPietro Cerutti return (-1); 22896c95412SPietro Cerutti } 22996c95412SPietro Cerutti ck->off += offset; 23096c95412SPietro Cerutti break; 23196c95412SPietro Cerutti 23296c95412SPietro Cerutti case SEEK_END: 23396c95412SPietro Cerutti if (offset > 0 || -offset > ck->len) { 23496c95412SPietro Cerutti errno = EINVAL; 23596c95412SPietro Cerutti return (-1); 23696c95412SPietro Cerutti } 23796c95412SPietro Cerutti ck->off = ck->len + offset; 23896c95412SPietro Cerutti break; 23996c95412SPietro Cerutti 24096c95412SPietro Cerutti default: 24196c95412SPietro Cerutti errno = EINVAL; 24296c95412SPietro Cerutti return (-1); 24396c95412SPietro Cerutti } 24496c95412SPietro Cerutti 24596c95412SPietro Cerutti return (ck->off); 24696c95412SPietro Cerutti } 24796c95412SPietro Cerutti 24896c95412SPietro Cerutti static int 24996c95412SPietro Cerutti fmemopen_close (void *cookie) 25096c95412SPietro Cerutti { 251*646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 25296c95412SPietro Cerutti 25396c95412SPietro Cerutti if (ck->own) 25496c95412SPietro Cerutti free (ck->buf); 25596c95412SPietro Cerutti 25696c95412SPietro Cerutti free (ck); 25796c95412SPietro Cerutti 25896c95412SPietro Cerutti return (0); 25996c95412SPietro Cerutti } 260