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