1/* $OpenBSD: fmemopen.c,v 1.5 2020/08/17 16:17:39 millert Exp $ */ 2 3/* 4 * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org> 5 * Copyright (c) 2009 Ted Unangst 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <errno.h> 21#include <fcntl.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include "local.h" 26 27struct state { 28 char *string; /* actual stream */ 29 size_t pos; /* current position */ 30 size_t size; /* allocated size */ 31 size_t len; /* length of the data */ 32 int update; /* open for update */ 33 int append; /* open for append */ 34}; 35 36static int 37fmemopen_read(void *v, char *b, int l) 38{ 39 struct state *st = v; 40 int i; 41 42 for (i = 0; i < l && i + st->pos < st->len; i++) 43 b[i] = st->string[st->pos + i]; 44 st->pos += i; 45 46 return (i); 47} 48 49static int 50fmemopen_write(void *v, const char *b, int l) 51{ 52 struct state *st = v; 53 int i; 54 55 if (st->append) 56 st->pos = st->len; 57 58 for (i = 0; i < l && i + st->pos < st->size; i++) 59 st->string[st->pos + i] = b[i]; 60 st->pos += i; 61 62 if (st->pos >= st->len) { 63 st->len = st->pos; 64 65 if (st->len < st->size) 66 st->string[st->len] = '\0'; 67 else if (!st->update) 68 st->string[st->size - 1] = '\0'; 69 } 70 71 return (i); 72} 73 74static fpos_t 75fmemopen_seek(void *v, fpos_t off, int whence) 76{ 77 struct state *st = v; 78 ssize_t base = 0; 79 80 switch (whence) { 81 case SEEK_SET: 82 break; 83 case SEEK_CUR: 84 base = st->pos; 85 break; 86 case SEEK_END: 87 base = st->len; 88 break; 89 } 90 91 if (off > st->size - base || off < -base) { 92 errno = EOVERFLOW; 93 return (-1); 94 } 95 96 st->pos = base + off; 97 98 return (st->pos); 99} 100 101static int 102fmemopen_close(void *v) 103{ 104 free(v); 105 106 return (0); 107} 108 109static int 110fmemopen_close_free(void *v) 111{ 112 struct state *st = v; 113 114 free(st->string); 115 free(st); 116 117 return (0); 118} 119 120FILE * 121fmemopen(void *buf, size_t size, const char *mode) 122{ 123 struct state *st; 124 FILE *fp; 125 int flags, oflags; 126 127 if (size == 0) { 128 errno = EINVAL; 129 return (NULL); 130 } 131 132 if ((flags = __sflags(mode, &oflags)) == 0) { 133 errno = EINVAL; 134 return (NULL); 135 } 136 137 if (buf == NULL && ((oflags & O_RDWR) == 0)) { 138 errno = EINVAL; 139 return (NULL); 140 } 141 142 if ((st = malloc(sizeof(*st))) == NULL) 143 return (NULL); 144 145 if ((fp = __sfp()) == NULL) { 146 free(st); 147 return (NULL); 148 } 149 150 st->pos = 0; 151 st->len = (oflags & O_TRUNC) ? 0 : size; 152 st->size = size; 153 st->update = oflags & O_RDWR; 154 st->append = oflags & O_APPEND; 155 156 if (buf == NULL) { 157 if ((st->string = malloc(size)) == NULL) { 158 free(st); 159 fp->_flags = 0; 160 return (NULL); 161 } 162 *st->string = '\0'; 163 } else { 164 st->string = (char *)buf; 165 166 if (oflags & O_TRUNC) 167 *st->string = '\0'; 168 169 if (oflags & O_APPEND) { 170 char *p; 171 172 if ((p = memchr(st->string, '\0', size)) != NULL) 173 st->pos = st->len = (p - st->string); 174 else 175 st->pos = st->len = size; 176 } 177 } 178 179 fp->_flags = (short)flags; 180 fp->_file = -1; 181 fp->_cookie = st; 182 fp->_read = (flags & __SWR) ? NULL : fmemopen_read; 183 fp->_write = (flags & __SRD) ? NULL : fmemopen_write; 184 fp->_seek = fmemopen_seek; 185 fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close; 186 187 return (fp); 188} 189DEF_WEAK(fmemopen); 190