1/*- 2 * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h>
| 1/*- 2 * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h>
|
27__FBSDID("$FreeBSD: head/lib/libc/stdio/fmemopen.c 246206 2013-02-01 13:04:06Z gahr $");
| 27__FBSDID("$FreeBSD: head/lib/libc/stdio/fmemopen.c 266971 2014-06-02 13:48:57Z gahr $");
|
28 29#include <fcntl.h> 30#include <stdbool.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <errno.h> 35#include "local.h" 36 37struct fmemopen_cookie 38{ 39 char *buf; /* pointer to the memory region */ 40 bool own; /* did we allocate the buffer ourselves? */ 41 char bin; /* is this a binary buffer? */ 42 size_t size; /* buffer length in bytes */ 43 size_t len; /* data length in bytes */ 44 size_t off; /* current offset into the buffer */ 45}; 46 47static int fmemopen_read(void *cookie, char *buf, int nbytes); 48static int fmemopen_write(void *cookie, const char *buf, int nbytes); 49static fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); 50static int fmemopen_close(void *cookie); 51 52FILE * 53fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 54{ 55 struct fmemopen_cookie *ck; 56 FILE *f; 57 int flags, rc; 58 59 /*
| 28 29#include <fcntl.h> 30#include <stdbool.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <errno.h> 35#include "local.h" 36 37struct fmemopen_cookie 38{ 39 char *buf; /* pointer to the memory region */ 40 bool own; /* did we allocate the buffer ourselves? */ 41 char bin; /* is this a binary buffer? */ 42 size_t size; /* buffer length in bytes */ 43 size_t len; /* data length in bytes */ 44 size_t off; /* current offset into the buffer */ 45}; 46 47static int fmemopen_read(void *cookie, char *buf, int nbytes); 48static int fmemopen_write(void *cookie, const char *buf, int nbytes); 49static fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); 50static int fmemopen_close(void *cookie); 51 52FILE * 53fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 54{ 55 struct fmemopen_cookie *ck; 56 FILE *f; 57 int flags, rc; 58 59 /*
|
| 60 * POSIX says we shall return EINVAL if size is 0. 61 */ 62 if (size == 0) { 63 errno = EINVAL; 64 return (NULL); 65 } 66 67 /*
|
60 * Retrieve the flags as used by open(2) from the mode argument, and 61 * validate them. 62 */ 63 rc = __sflags(mode, &flags); 64 if (rc == 0) { 65 errno = EINVAL; 66 return (NULL); 67 } 68 69 /* 70 * There's no point in requiring an automatically allocated buffer 71 * in write-only mode. 72 */ 73 if (!(flags & O_RDWR) && buf == NULL) { 74 errno = EINVAL; 75 return (NULL); 76 } 77 78 ck = malloc(sizeof(struct fmemopen_cookie)); 79 if (ck == NULL) { 80 return (NULL); 81 } 82 83 ck->off = 0; 84 ck->size = size; 85 86 /* Check whether we have to allocate the buffer ourselves. */ 87 ck->own = ((ck->buf = buf) == NULL); 88 if (ck->own) { 89 ck->buf = malloc(size); 90 if (ck->buf == NULL) { 91 free(ck); 92 return (NULL); 93 } 94 } 95 96 /* 97 * POSIX distinguishes between w+ and r+, in that w+ is supposed to 98 * truncate the buffer. 99 */ 100 if (ck->own || mode[0] == 'w') { 101 ck->buf[0] = '\0'; 102 } 103 104 /* Check for binary mode. */ 105 ck->bin = strchr(mode, 'b') != NULL; 106 107 /* 108 * The size of the current buffer contents is set depending on the 109 * mode: 110 * 111 * for append (text-mode), the position of the first NULL byte, or the 112 * size of the buffer if none is found 113 * 114 * for append (binary-mode), the size of the buffer 115 * 116 * for read, the size of the buffer 117 * 118 * for write, 0 119 */ 120 switch (mode[0]) { 121 case 'a':
| 68 * Retrieve the flags as used by open(2) from the mode argument, and 69 * validate them. 70 */ 71 rc = __sflags(mode, &flags); 72 if (rc == 0) { 73 errno = EINVAL; 74 return (NULL); 75 } 76 77 /* 78 * There's no point in requiring an automatically allocated buffer 79 * in write-only mode. 80 */ 81 if (!(flags & O_RDWR) && buf == NULL) { 82 errno = EINVAL; 83 return (NULL); 84 } 85 86 ck = malloc(sizeof(struct fmemopen_cookie)); 87 if (ck == NULL) { 88 return (NULL); 89 } 90 91 ck->off = 0; 92 ck->size = size; 93 94 /* Check whether we have to allocate the buffer ourselves. */ 95 ck->own = ((ck->buf = buf) == NULL); 96 if (ck->own) { 97 ck->buf = malloc(size); 98 if (ck->buf == NULL) { 99 free(ck); 100 return (NULL); 101 } 102 } 103 104 /* 105 * POSIX distinguishes between w+ and r+, in that w+ is supposed to 106 * truncate the buffer. 107 */ 108 if (ck->own || mode[0] == 'w') { 109 ck->buf[0] = '\0'; 110 } 111 112 /* Check for binary mode. */ 113 ck->bin = strchr(mode, 'b') != NULL; 114 115 /* 116 * The size of the current buffer contents is set depending on the 117 * mode: 118 * 119 * for append (text-mode), the position of the first NULL byte, or the 120 * size of the buffer if none is found 121 * 122 * for append (binary-mode), the size of the buffer 123 * 124 * for read, the size of the buffer 125 * 126 * for write, 0 127 */ 128 switch (mode[0]) { 129 case 'a':
|
122 if (ck->bin) { 123 /* 124 * This isn't useful, since the buffer isn't allowed 125 * to grow. 126 */ 127 ck->off = ck->len = size; 128 } else 129 ck->off = ck->len = strnlen(ck->buf, ck->size);
| 130 ck->off = ck->len = strnlen(ck->buf, ck->size);
|
130 break; 131 case 'r': 132 ck->len = size; 133 break; 134 case 'w': 135 ck->len = 0; 136 break; 137 } 138 139 f = funopen(ck, 140 flags & O_WRONLY ? NULL : fmemopen_read, 141 flags & O_RDONLY ? NULL : fmemopen_write, 142 fmemopen_seek, fmemopen_close); 143 144 if (f == NULL) { 145 if (ck->own) 146 free(ck->buf); 147 free(ck); 148 return (NULL); 149 } 150 151 /* 152 * Turn off buffering, so a write past the end of the buffer 153 * correctly returns a short object count. 154 */ 155 setvbuf(f, NULL, _IONBF, 0); 156 157 return (f); 158} 159 160static int 161fmemopen_read(void *cookie, char *buf, int nbytes) 162{ 163 struct fmemopen_cookie *ck = cookie; 164 165 if (nbytes > ck->len - ck->off) 166 nbytes = ck->len - ck->off; 167 168 if (nbytes == 0) 169 return (0); 170 171 memcpy(buf, ck->buf + ck->off, nbytes); 172 173 ck->off += nbytes; 174 175 return (nbytes); 176} 177 178static int 179fmemopen_write(void *cookie, const char *buf, int nbytes) 180{ 181 struct fmemopen_cookie *ck = cookie; 182 183 if (nbytes > ck->size - ck->off) 184 nbytes = ck->size - ck->off; 185 186 if (nbytes == 0) 187 return (0); 188 189 memcpy(ck->buf + ck->off, buf, nbytes); 190 191 ck->off += nbytes; 192 193 if (ck->off > ck->len) 194 ck->len = ck->off; 195 196 /* 197 * We append a NULL byte if all these conditions are met: 198 * - the buffer is not binary 199 * - the buffer is not full 200 * - the data just written doesn't already end with a NULL byte 201 */ 202 if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 203 ck->buf[ck->off] = '\0'; 204 205 return (nbytes); 206} 207 208static fpos_t 209fmemopen_seek(void *cookie, fpos_t offset, int whence) 210{ 211 struct fmemopen_cookie *ck = cookie; 212 213 214 switch (whence) { 215 case SEEK_SET: 216 if (offset > ck->size) { 217 errno = EINVAL; 218 return (-1); 219 } 220 ck->off = offset; 221 break; 222 223 case SEEK_CUR: 224 if (ck->off + offset > ck->size) { 225 errno = EINVAL; 226 return (-1); 227 } 228 ck->off += offset; 229 break; 230 231 case SEEK_END: 232 if (offset > 0 || -offset > ck->len) { 233 errno = EINVAL; 234 return (-1); 235 } 236 ck->off = ck->len + offset; 237 break; 238 239 default: 240 errno = EINVAL; 241 return (-1); 242 } 243 244 return (ck->off); 245} 246 247static int 248fmemopen_close(void *cookie) 249{ 250 struct fmemopen_cookie *ck = cookie; 251 252 if (ck->own) 253 free(ck->buf); 254 255 free(ck); 256 257 return (0); 258}
| 131 break; 132 case 'r': 133 ck->len = size; 134 break; 135 case 'w': 136 ck->len = 0; 137 break; 138 } 139 140 f = funopen(ck, 141 flags & O_WRONLY ? NULL : fmemopen_read, 142 flags & O_RDONLY ? NULL : fmemopen_write, 143 fmemopen_seek, fmemopen_close); 144 145 if (f == NULL) { 146 if (ck->own) 147 free(ck->buf); 148 free(ck); 149 return (NULL); 150 } 151 152 /* 153 * Turn off buffering, so a write past the end of the buffer 154 * correctly returns a short object count. 155 */ 156 setvbuf(f, NULL, _IONBF, 0); 157 158 return (f); 159} 160 161static int 162fmemopen_read(void *cookie, char *buf, int nbytes) 163{ 164 struct fmemopen_cookie *ck = cookie; 165 166 if (nbytes > ck->len - ck->off) 167 nbytes = ck->len - ck->off; 168 169 if (nbytes == 0) 170 return (0); 171 172 memcpy(buf, ck->buf + ck->off, nbytes); 173 174 ck->off += nbytes; 175 176 return (nbytes); 177} 178 179static int 180fmemopen_write(void *cookie, const char *buf, int nbytes) 181{ 182 struct fmemopen_cookie *ck = cookie; 183 184 if (nbytes > ck->size - ck->off) 185 nbytes = ck->size - ck->off; 186 187 if (nbytes == 0) 188 return (0); 189 190 memcpy(ck->buf + ck->off, buf, nbytes); 191 192 ck->off += nbytes; 193 194 if (ck->off > ck->len) 195 ck->len = ck->off; 196 197 /* 198 * We append a NULL byte if all these conditions are met: 199 * - the buffer is not binary 200 * - the buffer is not full 201 * - the data just written doesn't already end with a NULL byte 202 */ 203 if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 204 ck->buf[ck->off] = '\0'; 205 206 return (nbytes); 207} 208 209static fpos_t 210fmemopen_seek(void *cookie, fpos_t offset, int whence) 211{ 212 struct fmemopen_cookie *ck = cookie; 213 214 215 switch (whence) { 216 case SEEK_SET: 217 if (offset > ck->size) { 218 errno = EINVAL; 219 return (-1); 220 } 221 ck->off = offset; 222 break; 223 224 case SEEK_CUR: 225 if (ck->off + offset > ck->size) { 226 errno = EINVAL; 227 return (-1); 228 } 229 ck->off += offset; 230 break; 231 232 case SEEK_END: 233 if (offset > 0 || -offset > ck->len) { 234 errno = EINVAL; 235 return (-1); 236 } 237 ck->off = ck->len + offset; 238 break; 239 240 default: 241 errno = EINVAL; 242 return (-1); 243 } 244 245 return (ck->off); 246} 247 248static int 249fmemopen_close(void *cookie) 250{ 251 struct fmemopen_cookie *ck = cookie; 252 253 if (ck->own) 254 free(ck->buf); 255 256 free(ck); 257 258 return (0); 259}
|