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 <strings.h> 30 #include <errno.h> 31 32 struct memstream_cookie { 33 char **pub_buf; 34 size_t *pub_size; 35 36 char *head; 37 size_t pos; 38 size_t tail; 39 }; 40 41 static void 42 sync_pub_cookie(struct memstream_cookie *cp) 43 { 44 *cp->pub_buf = cp->head; 45 *cp->pub_size = cp->tail; 46 } 47 48 static int 49 memstream_writefn(void *cookie, const char *buf, int len) 50 { 51 struct memstream_cookie *c; 52 size_t reqsize; 53 54 c = cookie; 55 56 /* Write is contained within valid region */ 57 if (c->pos + len < c->tail) { 58 bcopy(buf, &c->head[c->pos], len); 59 c->pos += len; 60 return (len); 61 } 62 63 /* Write results in resizing buffer */ 64 reqsize = c->pos + len + 1; 65 c->head = reallocf(c->head, reqsize); 66 if (c->head == NULL) { 67 errno = ENOMEM; 68 return (0); 69 } 70 71 bcopy(buf, &c->head[c->pos], len); 72 73 c->tail = c->pos + len; 74 c->pos = c->tail; 75 c->head[c->tail] = '\0'; 76 77 sync_pub_cookie(c); 78 79 return (len); 80 } 81 82 static fpos_t 83 memstream_seekfn(void *cookie, fpos_t pos, int whence) 84 { 85 struct memstream_cookie *c; 86 87 c = cookie; 88 89 /* XXX: Should validate SEEK_SET and SEEK_CUR positions */ 90 /* XXX: What to do wrt SEEK_END? Is it relative to tail? to pos? */ 91 92 switch(whence) { 93 case (SEEK_SET): 94 c->pos = pos; 95 return (c->pos); 96 break; 97 case (SEEK_CUR): 98 c->pos += pos; 99 return (c->pos); 100 break; 101 case (SEEK_END): 102 default: 103 errno = EINVAL; 104 return (fpos_t) -1; 105 } 106 } 107 108 static int 109 memstream_closefn(void *cookie) 110 { 111 struct memstream_cookie *c; 112 113 c = cookie; 114 115 sync_pub_cookie(c); 116 117 free(c); 118 return (0); 119 } 120 121 FILE * 122 open_memstream(char **bufp, size_t *sizep) 123 { 124 FILE *fp; 125 struct memstream_cookie *c; 126 127 fp = NULL; 128 if (bufp == NULL || sizep == NULL) { 129 errno = EINVAL; 130 goto out; 131 } 132 133 c = malloc(sizeof(struct memstream_cookie)); 134 if (c == NULL) { 135 errno = EINVAL; 136 goto out; 137 } 138 139 fp = funopen(c, 140 NULL, 141 memstream_writefn, 142 memstream_seekfn, 143 memstream_closefn 144 ); 145 146 if (fp == NULL) { 147 free(c); 148 errno = ENOMEM; 149 goto out; 150 } 151 152 c->pub_buf = bufp; 153 c->pub_size = sizep; 154 c->head = NULL; 155 c->tail = 0; 156 c->pos = 0; 157 158 out: 159 return (fp); 160 } 161