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