16b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes/* $OpenBSD: fmemopen.c,v 1.2 2013/03/27 15:06:25 mpi Exp $ */ 26b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 36b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes/* 46b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org> 56b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * Copyright (c) 2009 Ted Unangst 66b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * 76b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * Permission to use, copy, modify, and distribute this software for any 86b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * purpose with or without fee is hereby granted, provided that the above 96b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * copyright notice and this permission notice appear in all copies. 106b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * 116b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 126b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 136b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 146b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 156b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 166b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 176b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 186b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes */ 196b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 206b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include <errno.h> 216b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include <fcntl.h> 226b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include <stdio.h> 236b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include <stdlib.h> 246b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include <string.h> 256b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes#include "local.h" 266b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 276b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstruct state { 286b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes char *string; /* actual stream */ 296b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes size_t pos; /* current position */ 306b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes size_t size; /* allocated size */ 316b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes size_t len; /* length of the data */ 326b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes int update; /* open for update */ 336b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes}; 346b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 356b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstatic int 366b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen_read(void *v, char *b, int l) 376b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 386b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes struct state *st = v; 396b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes int i; 406b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 416b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes for (i = 0; i < l && i + st->pos < st->len; i++) 426b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes b[i] = st->string[st->pos + i]; 436b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos += i; 446b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 456b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (i); 466b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 476b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 486b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstatic int 496b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen_write(void *v, const char *b, int l) 506b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 516b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes struct state *st = v; 526b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes int i; 536b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 546b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes for (i = 0; i < l && i + st->pos < st->size; i++) 556b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->string[st->pos + i] = b[i]; 566b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos += i; 576b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 586b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (st->pos >= st->len) { 596b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->len = st->pos; 606b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 616b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (st->len < st->size) 626b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->string[st->len] = '\0'; 636b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes else if (!st->update) 646b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->string[st->size - 1] = '\0'; 656b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 666b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 676b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (i); 686b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 696b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 706b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstatic fpos_t 716b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen_seek(void *v, fpos_t off, int whence) 726b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 736b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes struct state *st = v; 746b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes ssize_t base = 0; 756b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 766b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes switch (whence) { 776b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes case SEEK_SET: 786b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes break; 796b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes case SEEK_CUR: 806b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes base = st->pos; 816b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes break; 826b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes case SEEK_END: 836b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes base = st->len; 846b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes break; 856b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 866b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 876b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (off > st->size - base || off < -base) { 886b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes errno = EOVERFLOW; 896b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (-1); 906b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 916b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 926b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos = base + off; 936b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 946b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (st->pos); 956b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 966b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 976b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstatic int 986b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen_close(void *v) 996b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 1006b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes free(v); 1016b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1026b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (0); 1036b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 1046b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1056b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesstatic int 1066b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen_close_free(void *v) 1076b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 1086b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes struct state *st = v; 1096b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1106b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes free(st->string); 1116b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes free(st); 1126b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1136b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (0); 1146b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 1156b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1166b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott HughesFILE * 1176b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughesfmemopen(void *buf, size_t size, const char *mode) 1186b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes{ 1196b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes struct state *st; 1206b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes FILE *fp; 1216b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes int flags, oflags; 1226b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1236b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (size == 0) { 1246b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes errno = EINVAL; 1256b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1266b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1276b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1286b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if ((flags = __sflags(mode, &oflags)) == 0) { 1296b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes errno = EINVAL; 1306b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1316b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1326b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1336b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (buf == NULL && ((oflags & O_RDWR) == 0)) { 1346b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes errno = EINVAL; 1356b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1366b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1376b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1386b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if ((st = malloc(sizeof(*st))) == NULL) 1396b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1406b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1416b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if ((fp = __sfp()) == NULL) { 1426b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes free(st); 1436b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1446b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1456b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1466b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos = 0; 1476b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->len = (oflags & O_WRONLY) ? 0 : size; 1486b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->size = size; 1496b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->update = oflags & O_RDWR; 1506b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1516b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (buf == NULL) { 1526b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if ((st->string = malloc(size)) == NULL) { 1536b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes free(st); 1546b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_flags = 0; 1556b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (NULL); 1566b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1576b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes *st->string = '\0'; 1586b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } else { 1596b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->string = (char *)buf; 1606b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1616b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (oflags & O_TRUNC) 1626b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes *st->string = '\0'; 1636b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1646b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if (oflags & O_APPEND) { 1656b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes char *p; 1666b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1676b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes if ((p = memchr(st->string, '\0', size)) != NULL) 1686b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos = st->len = (p - st->string); 1696b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes else 1706b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes st->pos = st->len = size; 1716b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1726b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes } 1736b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1746b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_flags = (short)flags; 1756b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_file = -1; 1766b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_cookie = (void *)st; 1776b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_read = (flags & __SWR) ? NULL : fmemopen_read; 1786b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_write = (flags & __SRD) ? NULL : fmemopen_write; 1796b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_seek = fmemopen_seek; 1806b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close; 1816b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes 1826b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes return (fp); 1836b841db2baa24ffcf2a4e5f975d1d07f1699b918Elliott Hughes} 184