xref: /freebsd/lib/libc/stdio/open_memstream.c (revision 9240031ac655f4e3cd4bcf840f2efc5222d6fdbc)
1*9240031aSJohn Baldwin /*-
2*9240031aSJohn Baldwin  * Copyright (c) 2013 Advanced Computing Technologies LLC
3*9240031aSJohn Baldwin  * Written by: John H. Baldwin <jhb@FreeBSD.org>
4*9240031aSJohn Baldwin  * All rights reserved.
5*9240031aSJohn Baldwin  *
6*9240031aSJohn Baldwin  * Redistribution and use in source and binary forms, with or without
7*9240031aSJohn Baldwin  * modification, are permitted provided that the following conditions
8*9240031aSJohn Baldwin  * are met:
9*9240031aSJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
10*9240031aSJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
11*9240031aSJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
12*9240031aSJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
13*9240031aSJohn Baldwin  *    documentation and/or other materials provided with the distribution.
14*9240031aSJohn Baldwin  *
15*9240031aSJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*9240031aSJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*9240031aSJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*9240031aSJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*9240031aSJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*9240031aSJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*9240031aSJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*9240031aSJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*9240031aSJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*9240031aSJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*9240031aSJohn Baldwin  * SUCH DAMAGE.
26*9240031aSJohn Baldwin  */
27*9240031aSJohn Baldwin 
28*9240031aSJohn Baldwin #include <sys/cdefs.h>
29*9240031aSJohn Baldwin __FBSDID("$FreeBSD$");
30*9240031aSJohn Baldwin 
31*9240031aSJohn Baldwin #include "namespace.h"
32*9240031aSJohn Baldwin #include <assert.h>
33*9240031aSJohn Baldwin #include <errno.h>
34*9240031aSJohn Baldwin #include <limits.h>
35*9240031aSJohn Baldwin #include <stdio.h>
36*9240031aSJohn Baldwin #include <stdlib.h>
37*9240031aSJohn Baldwin #include <string.h>
38*9240031aSJohn Baldwin #include <wchar.h>
39*9240031aSJohn Baldwin #include "un-namespace.h"
40*9240031aSJohn Baldwin 
41*9240031aSJohn Baldwin /* XXX: There is no FPOS_MAX.  This assumes fpos_t is an off_t. */
42*9240031aSJohn Baldwin #define	FPOS_MAX	OFF_MAX
43*9240031aSJohn Baldwin 
44*9240031aSJohn Baldwin struct memstream {
45*9240031aSJohn Baldwin 	char **bufp;
46*9240031aSJohn Baldwin 	size_t *sizep;
47*9240031aSJohn Baldwin 	ssize_t len;
48*9240031aSJohn Baldwin 	fpos_t offset;
49*9240031aSJohn Baldwin };
50*9240031aSJohn Baldwin 
51*9240031aSJohn Baldwin static int
52*9240031aSJohn Baldwin memstream_grow(struct memstream *ms, fpos_t newoff)
53*9240031aSJohn Baldwin {
54*9240031aSJohn Baldwin 	char *buf;
55*9240031aSJohn Baldwin 	ssize_t newsize;
56*9240031aSJohn Baldwin 
57*9240031aSJohn Baldwin 	if (newoff < 0 || newoff >= SSIZE_MAX)
58*9240031aSJohn Baldwin 		newsize = SSIZE_MAX - 1;
59*9240031aSJohn Baldwin 	else
60*9240031aSJohn Baldwin 		newsize = newoff;
61*9240031aSJohn Baldwin 	if (newsize > ms->len) {
62*9240031aSJohn Baldwin 		buf = realloc(*ms->bufp, newsize + 1);
63*9240031aSJohn Baldwin 		if (buf != NULL) {
64*9240031aSJohn Baldwin #ifdef DEBUG
65*9240031aSJohn Baldwin 			fprintf(stderr, "MS: %p growing from %zd to %zd\n",
66*9240031aSJohn Baldwin 			    ms, ms->len, newsize);
67*9240031aSJohn Baldwin #endif
68*9240031aSJohn Baldwin 			memset(buf + ms->len + 1, 0, newsize - ms->len);
69*9240031aSJohn Baldwin 			*ms->bufp = buf;
70*9240031aSJohn Baldwin 			ms->len = newsize;
71*9240031aSJohn Baldwin 			return (1);
72*9240031aSJohn Baldwin 		}
73*9240031aSJohn Baldwin 		return (0);
74*9240031aSJohn Baldwin 	}
75*9240031aSJohn Baldwin 	return (1);
76*9240031aSJohn Baldwin }
77*9240031aSJohn Baldwin 
78*9240031aSJohn Baldwin static void
79*9240031aSJohn Baldwin memstream_update(struct memstream *ms)
80*9240031aSJohn Baldwin {
81*9240031aSJohn Baldwin 
82*9240031aSJohn Baldwin 	assert(ms->len >= 0 && ms->offset >= 0);
83*9240031aSJohn Baldwin 	*ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
84*9240031aSJohn Baldwin }
85*9240031aSJohn Baldwin 
86*9240031aSJohn Baldwin static int
87*9240031aSJohn Baldwin memstream_write(void *cookie, const char *buf, int len)
88*9240031aSJohn Baldwin {
89*9240031aSJohn Baldwin 	struct memstream *ms;
90*9240031aSJohn Baldwin 	ssize_t tocopy;
91*9240031aSJohn Baldwin 
92*9240031aSJohn Baldwin 	ms = cookie;
93*9240031aSJohn Baldwin 	if (!memstream_grow(ms, ms->offset + len))
94*9240031aSJohn Baldwin 		return (-1);
95*9240031aSJohn Baldwin 	tocopy = ms->len - ms->offset;
96*9240031aSJohn Baldwin 	if (len < tocopy)
97*9240031aSJohn Baldwin 		tocopy = len;
98*9240031aSJohn Baldwin 	memcpy(*ms->bufp + ms->offset, buf, tocopy);
99*9240031aSJohn Baldwin 	ms->offset += tocopy;
100*9240031aSJohn Baldwin 	memstream_update(ms);
101*9240031aSJohn Baldwin #ifdef DEBUG
102*9240031aSJohn Baldwin 	fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
103*9240031aSJohn Baldwin #endif
104*9240031aSJohn Baldwin 	return (tocopy);
105*9240031aSJohn Baldwin }
106*9240031aSJohn Baldwin 
107*9240031aSJohn Baldwin static fpos_t
108*9240031aSJohn Baldwin memstream_seek(void *cookie, fpos_t pos, int whence)
109*9240031aSJohn Baldwin {
110*9240031aSJohn Baldwin 	struct memstream *ms;
111*9240031aSJohn Baldwin #ifdef DEBUG
112*9240031aSJohn Baldwin 	fpos_t old;
113*9240031aSJohn Baldwin #endif
114*9240031aSJohn Baldwin 
115*9240031aSJohn Baldwin 	ms = cookie;
116*9240031aSJohn Baldwin #ifdef DEBUG
117*9240031aSJohn Baldwin 	old = ms->offset;
118*9240031aSJohn Baldwin #endif
119*9240031aSJohn Baldwin 	switch (whence) {
120*9240031aSJohn Baldwin 	case SEEK_SET:
121*9240031aSJohn Baldwin 		/* _fseeko() checks for negative offsets. */
122*9240031aSJohn Baldwin 		assert(pos >= 0);
123*9240031aSJohn Baldwin 		ms->offset = pos;
124*9240031aSJohn Baldwin 		break;
125*9240031aSJohn Baldwin 	case SEEK_CUR:
126*9240031aSJohn Baldwin 		/* This is only called by _ftello(). */
127*9240031aSJohn Baldwin 		assert(pos == 0);
128*9240031aSJohn Baldwin 		break;
129*9240031aSJohn Baldwin 	case SEEK_END:
130*9240031aSJohn Baldwin 		if (pos < 0) {
131*9240031aSJohn Baldwin 			if (pos + ms->len < 0) {
132*9240031aSJohn Baldwin #ifdef DEBUG
133*9240031aSJohn Baldwin 				fprintf(stderr,
134*9240031aSJohn Baldwin 				    "MS: bad SEEK_END: pos %jd, len %zd\n",
135*9240031aSJohn Baldwin 				    (intmax_t)pos, ms->len);
136*9240031aSJohn Baldwin #endif
137*9240031aSJohn Baldwin 				errno = EINVAL;
138*9240031aSJohn Baldwin 				return (-1);
139*9240031aSJohn Baldwin 			}
140*9240031aSJohn Baldwin 		} else {
141*9240031aSJohn Baldwin 			if (FPOS_MAX - ms->len < pos) {
142*9240031aSJohn Baldwin #ifdef DEBUG
143*9240031aSJohn Baldwin 				fprintf(stderr,
144*9240031aSJohn Baldwin 				    "MS: bad SEEK_END: pos %jd, len %zd\n",
145*9240031aSJohn Baldwin 				    (intmax_t)pos, ms->len);
146*9240031aSJohn Baldwin #endif
147*9240031aSJohn Baldwin 				errno = EOVERFLOW;
148*9240031aSJohn Baldwin 				return (-1);
149*9240031aSJohn Baldwin 			}
150*9240031aSJohn Baldwin 		}
151*9240031aSJohn Baldwin 		ms->offset = ms->len + pos;
152*9240031aSJohn Baldwin 		break;
153*9240031aSJohn Baldwin 	}
154*9240031aSJohn Baldwin 	memstream_update(ms);
155*9240031aSJohn Baldwin #ifdef DEBUG
156*9240031aSJohn Baldwin 	fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
157*9240031aSJohn Baldwin 	    whence, (intmax_t)old, (intmax_t)ms->offset);
158*9240031aSJohn Baldwin #endif
159*9240031aSJohn Baldwin 	return (ms->offset);
160*9240031aSJohn Baldwin }
161*9240031aSJohn Baldwin 
162*9240031aSJohn Baldwin static int
163*9240031aSJohn Baldwin memstream_close(void *cookie)
164*9240031aSJohn Baldwin {
165*9240031aSJohn Baldwin 
166*9240031aSJohn Baldwin 	free(cookie);
167*9240031aSJohn Baldwin 	return (0);
168*9240031aSJohn Baldwin }
169*9240031aSJohn Baldwin 
170*9240031aSJohn Baldwin FILE *
171*9240031aSJohn Baldwin open_memstream(char **bufp, size_t *sizep)
172*9240031aSJohn Baldwin {
173*9240031aSJohn Baldwin 	struct memstream *ms;
174*9240031aSJohn Baldwin 	int save_errno;
175*9240031aSJohn Baldwin 	FILE *fp;
176*9240031aSJohn Baldwin 
177*9240031aSJohn Baldwin 	if (bufp == NULL || sizep == NULL) {
178*9240031aSJohn Baldwin 		errno = EINVAL;
179*9240031aSJohn Baldwin 		return (NULL);
180*9240031aSJohn Baldwin 	}
181*9240031aSJohn Baldwin 	*bufp = calloc(1, 1);
182*9240031aSJohn Baldwin 	if (*bufp == NULL)
183*9240031aSJohn Baldwin 		return (NULL);
184*9240031aSJohn Baldwin 	ms = malloc(sizeof(*ms));
185*9240031aSJohn Baldwin 	if (ms == NULL) {
186*9240031aSJohn Baldwin 		save_errno = errno;
187*9240031aSJohn Baldwin 		free(*bufp);
188*9240031aSJohn Baldwin 		*bufp = NULL;
189*9240031aSJohn Baldwin 		errno = save_errno;
190*9240031aSJohn Baldwin 		return (NULL);
191*9240031aSJohn Baldwin 	}
192*9240031aSJohn Baldwin 	ms->bufp = bufp;
193*9240031aSJohn Baldwin 	ms->sizep = sizep;
194*9240031aSJohn Baldwin 	ms->len = 0;
195*9240031aSJohn Baldwin 	ms->offset = 0;
196*9240031aSJohn Baldwin 	memstream_update(ms);
197*9240031aSJohn Baldwin 	fp = funopen(ms, NULL, memstream_write, memstream_seek,
198*9240031aSJohn Baldwin 	    memstream_close);
199*9240031aSJohn Baldwin 	if (fp == NULL) {
200*9240031aSJohn Baldwin 		save_errno = errno;
201*9240031aSJohn Baldwin 		free(ms);
202*9240031aSJohn Baldwin 		free(*bufp);
203*9240031aSJohn Baldwin 		*bufp = NULL;
204*9240031aSJohn Baldwin 		errno = save_errno;
205*9240031aSJohn Baldwin 		return (NULL);
206*9240031aSJohn Baldwin 	}
207*9240031aSJohn Baldwin 	fwide(fp, -1);
208*9240031aSJohn Baldwin 	return (fp);
209*9240031aSJohn Baldwin }
210