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