1 /*- 2 * Copyright (c) 2011 Venkatesh Srinivas, 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <errno.h> 30 31 struct memstream_cookie { 32 char **pub_buf; 33 size_t *pub_size; 34 35 char *head; 36 size_t pos; 37 size_t tail; 38 }; 39 40 static void 41 sync_pub_cookie(struct memstream_cookie *cp) 42 { 43 *cp->pub_buf = cp->head; 44 *cp->pub_size = cp->tail; 45 } 46 47 static int 48 memstream_writefn(void *cookie, const char *buf, int len) 49 { 50 struct memstream_cookie *c; 51 size_t reqsize; 52 53 c = cookie; 54 55 /* Write is contained within valid region */ 56 if (c->pos + len < c->tail) { 57 bcopy(buf, &c->head[c->pos], len); 58 c->pos += len; 59 return (len); 60 } 61 62 /* Write results in resizing buffer */ 63 reqsize = c->pos + len + 1; 64 c->head = reallocf(c->head, reqsize); 65 if (c->head == NULL) { 66 errno = ENOMEM; 67 return (0); 68 } 69 70 bcopy(buf, &c->head[c->pos], len); 71 72 c->tail = c->pos + len; 73 c->pos = c->tail; 74 c->head[c->tail] = '\0'; 75 76 sync_pub_cookie(c); 77 78 return (len); 79 } 80 81 static fpos_t 82 memstream_seekfn(void *cookie, fpos_t pos, int whence) 83 { 84 struct memstream_cookie *c; 85 86 c = cookie; 87 88 /* XXX: Should validate SEEK_SET and SEEK_CUR positions */ 89 /* XXX: What to do wrt SEEK_END? Is it relative to tail? to pos? */ 90 91 switch(whence) { 92 case (SEEK_SET): 93 c->pos = pos; 94 return (c->pos); 95 break; 96 case (SEEK_CUR): 97 c->pos += pos; 98 return (c->pos); 99 break; 100 case (SEEK_END): 101 default: 102 errno = EINVAL; 103 return (fpos_t) -1; 104 } 105 } 106 107 static int 108 memstream_closefn(void *cookie) 109 { 110 struct memstream_cookie *c; 111 112 c = cookie; 113 114 sync_pub_cookie(c); 115 116 free(c); 117 } 118 119 FILE * 120 open_memstream(char **bufp, size_t *sizep) 121 { 122 FILE *fp; 123 struct memstream_cookie *c; 124 125 fp = NULL; 126 if (bufp == NULL || sizep == NULL) { 127 errno = EINVAL; 128 goto out; 129 } 130 131 c = malloc(sizeof(struct memstream_cookie)); 132 if (c == NULL) { 133 errno = EINVAL; 134 goto out; 135 } 136 137 fp = funopen(c, 138 NULL, 139 memstream_writefn, 140 memstream_seekfn, 141 memstream_closefn 142 ); 143 144 if (fp == NULL) { 145 free(c); 146 errno = ENOMEM; 147 goto out; 148 } 149 150 c->pub_buf = bufp; 151 c->pub_size = sizep; 152 c->head = NULL; 153 c->tail = 0; 154 c->pos = 0; 155 156 out: 157 return (fp); 158 } 159