1/* 2 * POSIX 2008 fmemopen(3) implemented in terms of BSD funopen(3) 3 */ 4 5/* 6 * Copyright (c) 2013 Taylor R. Campbell 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#define _BSD_SOURCE 32#define _NETBSD_SOURCE 33 34#include <errno.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38 39#include "fmemopen.h" 40 41struct fmem_cookie { 42 void *fmc_buffer; 43 size_t fmc_index; 44 size_t fmc_limit; 45}; 46 47static int 48fmem_read(void *cookie, char *buffer, int n) 49{ 50 struct fmem_cookie *const fmc = cookie; 51 52 if (n < 0) { /* paranoia */ 53 errno = EINVAL; 54 return -1; 55 } 56 57 if (n > (fmc->fmc_limit - fmc->fmc_index)) 58 n = (fmc->fmc_limit - fmc->fmc_index); 59 60 (void)memcpy(buffer, (char *)fmc->fmc_buffer + fmc->fmc_index, n); 61 fmc->fmc_index += n; 62 return n; 63} 64 65static int 66fmem_write(void *cookie, const char *buffer, int n) 67{ 68 struct fmem_cookie *const fmc = cookie; 69 70 if (n < 0) { /* paranoia */ 71 errno = EINVAL; 72 return -1; 73 } 74 75 if (n > (fmc->fmc_limit - fmc->fmc_index)) 76 n = (fmc->fmc_limit - fmc->fmc_index); 77 78 (void)memcpy((char *)fmc->fmc_buffer + fmc->fmc_index, buffer, n); 79 fmc->fmc_index += n; 80 return n; 81} 82 83static fpos_t 84fmem_seek(void *cookie, fpos_t offset, int cmd) 85{ 86 struct fmem_cookie *const fmc = cookie; 87 88 switch (cmd) { 89 case SEEK_SET: 90 if ((offset < 0) || (fmc->fmc_limit < offset)) 91 goto einval; 92 fmc->fmc_index = offset; 93 return 0; 94 95 case SEEK_CUR: 96 if (offset < 0) { 97 /* Assume two's-complement arithmetic. */ 98 if ((offset == ~(fpos_t)0) || (-offset > fmc->fmc_index)) 99 goto einval; 100 } else { 101 if (offset > (fmc->fmc_limit - fmc->fmc_index)) 102 goto einval; 103 } 104 fmc->fmc_index += offset; 105 return 0; 106 107 case SEEK_END: 108 /* Assume two's-complement arithmetic. */ 109 if ((offset >= 0) || (offset == ~(fpos_t)0) || (fmc->fmc_limit < -offset)) 110 goto einval; 111 fmc->fmc_index = (fmc->fmc_limit + offset); 112 return 0; 113 114 default: 115 goto einval; 116 } 117 118einval: 119 errno = EINVAL; 120 return -1; 121} 122 123static int 124fmem_close(void *cookie) 125{ 126 struct fmem_cookie *const fmc = cookie; 127 128 free(fmc); 129 130 return 0; 131} 132 133FILE * 134fmemopen(void *buffer, size_t len, const char *mode) 135{ 136 struct fmem_cookie *fmc; 137 FILE *file; 138 139 fmc = malloc(sizeof(*fmc)); 140 if (fmc == NULL) 141 goto fail0; 142 143 (void)memset(fmc, 0, sizeof(*fmc)); 144 fmc->fmc_buffer = buffer; 145 fmc->fmc_index = 0; 146 fmc->fmc_limit = len; 147 148 file = funopen(fmc, &fmem_read, &fmem_write, &fmem_seek, &fmem_close); 149 if (file == NULL) 150 goto fail1; 151 152 return file; 153 154fail1: 155 free(fmc); 156fail0: 157 return NULL; 158} 159