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