1 /* $OpenBSD: fmemopen.c,v 1.3 2015/08/31 02:53:57 guenther 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 27 struct 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 }; 34 35 static int 36 fmemopen_read(void *v, char *b, int l) 37 { 38 struct state *st = v; 39 int i; 40 41 for (i = 0; i < l && i + st->pos < st->len; i++) 42 b[i] = st->string[st->pos + i]; 43 st->pos += i; 44 45 return (i); 46 } 47 48 static int 49 fmemopen_write(void *v, const char *b, int l) 50 { 51 struct state *st = v; 52 int i; 53 54 for (i = 0; i < l && i + st->pos < st->size; i++) 55 st->string[st->pos + i] = b[i]; 56 st->pos += i; 57 58 if (st->pos >= st->len) { 59 st->len = st->pos; 60 61 if (st->len < st->size) 62 st->string[st->len] = '\0'; 63 else if (!st->update) 64 st->string[st->size - 1] = '\0'; 65 } 66 67 return (i); 68 } 69 70 static fpos_t 71 fmemopen_seek(void *v, fpos_t off, int whence) 72 { 73 struct state *st = v; 74 ssize_t base = 0; 75 76 switch (whence) { 77 case SEEK_SET: 78 break; 79 case SEEK_CUR: 80 base = st->pos; 81 break; 82 case SEEK_END: 83 base = st->len; 84 break; 85 } 86 87 if (off > st->size - base || off < -base) { 88 errno = EOVERFLOW; 89 return (-1); 90 } 91 92 st->pos = base + off; 93 94 return (st->pos); 95 } 96 97 static int 98 fmemopen_close(void *v) 99 { 100 free(v); 101 102 return (0); 103 } 104 105 static int 106 fmemopen_close_free(void *v) 107 { 108 struct state *st = v; 109 110 free(st->string); 111 free(st); 112 113 return (0); 114 } 115 116 FILE * 117 fmemopen(void *buf, size_t size, const char *mode) 118 { 119 struct state *st; 120 FILE *fp; 121 int flags, oflags; 122 123 if (size == 0) { 124 errno = EINVAL; 125 return (NULL); 126 } 127 128 if ((flags = __sflags(mode, &oflags)) == 0) { 129 errno = EINVAL; 130 return (NULL); 131 } 132 133 if (buf == NULL && ((oflags & O_RDWR) == 0)) { 134 errno = EINVAL; 135 return (NULL); 136 } 137 138 if ((st = malloc(sizeof(*st))) == NULL) 139 return (NULL); 140 141 if ((fp = __sfp()) == NULL) { 142 free(st); 143 return (NULL); 144 } 145 146 st->pos = 0; 147 st->len = (oflags & O_WRONLY) ? 0 : size; 148 st->size = size; 149 st->update = oflags & O_RDWR; 150 151 if (buf == NULL) { 152 if ((st->string = malloc(size)) == NULL) { 153 free(st); 154 fp->_flags = 0; 155 return (NULL); 156 } 157 *st->string = '\0'; 158 } else { 159 st->string = (char *)buf; 160 161 if (oflags & O_TRUNC) 162 *st->string = '\0'; 163 164 if (oflags & O_APPEND) { 165 char *p; 166 167 if ((p = memchr(st->string, '\0', size)) != NULL) 168 st->pos = st->len = (p - st->string); 169 else 170 st->pos = st->len = size; 171 } 172 } 173 174 fp->_flags = (short)flags; 175 fp->_file = -1; 176 fp->_cookie = (void *)st; 177 fp->_read = (flags & __SWR) ? NULL : fmemopen_read; 178 fp->_write = (flags & __SRD) ? NULL : fmemopen_write; 179 fp->_seek = fmemopen_seek; 180 fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close; 181 182 return (fp); 183 } 184 DEF_WEAK(fmemopen); 185