xref: /freebsd/lib/libc/stdio/open_memstream.c (revision c6e615506b60b2ddd504c843c9ccebc266849a39)
19240031aSJohn Baldwin /*-
2179fa75eSJohn Baldwin  * Copyright (c) 2013 Hudson River Trading LLC
39240031aSJohn Baldwin  * Written by: John H. Baldwin <jhb@FreeBSD.org>
49240031aSJohn Baldwin  * All rights reserved.
59240031aSJohn Baldwin  *
69240031aSJohn Baldwin  * Redistribution and use in source and binary forms, with or without
79240031aSJohn Baldwin  * modification, are permitted provided that the following conditions
89240031aSJohn Baldwin  * are met:
99240031aSJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
109240031aSJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
119240031aSJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
129240031aSJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
139240031aSJohn Baldwin  *    documentation and/or other materials provided with the distribution.
149240031aSJohn Baldwin  *
159240031aSJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
169240031aSJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
179240031aSJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
189240031aSJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
199240031aSJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
209240031aSJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
219240031aSJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
229240031aSJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
239240031aSJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
249240031aSJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
259240031aSJohn Baldwin  * SUCH DAMAGE.
269240031aSJohn Baldwin  */
279240031aSJohn Baldwin 
289240031aSJohn Baldwin #include <sys/cdefs.h>
299240031aSJohn Baldwin __FBSDID("$FreeBSD$");
309240031aSJohn Baldwin 
319240031aSJohn Baldwin #include "namespace.h"
329240031aSJohn Baldwin #include <assert.h>
339240031aSJohn Baldwin #include <errno.h>
349240031aSJohn Baldwin #include <limits.h>
35*c6e61550SEnji Cooper #ifdef DEBUG
36*c6e61550SEnji Cooper #include <stdint.h>
37*c6e61550SEnji Cooper #endif
389240031aSJohn Baldwin #include <stdio.h>
399240031aSJohn Baldwin #include <stdlib.h>
409240031aSJohn Baldwin #include <string.h>
419240031aSJohn Baldwin #include <wchar.h>
429240031aSJohn Baldwin #include "un-namespace.h"
439240031aSJohn Baldwin 
449240031aSJohn Baldwin /* XXX: There is no FPOS_MAX.  This assumes fpos_t is an off_t. */
459240031aSJohn Baldwin #define	FPOS_MAX	OFF_MAX
469240031aSJohn Baldwin 
479240031aSJohn Baldwin struct memstream {
489240031aSJohn Baldwin 	char **bufp;
499240031aSJohn Baldwin 	size_t *sizep;
509240031aSJohn Baldwin 	ssize_t len;
519240031aSJohn Baldwin 	fpos_t offset;
529240031aSJohn Baldwin };
539240031aSJohn Baldwin 
549240031aSJohn Baldwin static int
559240031aSJohn Baldwin memstream_grow(struct memstream *ms, fpos_t newoff)
569240031aSJohn Baldwin {
579240031aSJohn Baldwin 	char *buf;
589240031aSJohn Baldwin 	ssize_t newsize;
599240031aSJohn Baldwin 
609240031aSJohn Baldwin 	if (newoff < 0 || newoff >= SSIZE_MAX)
619240031aSJohn Baldwin 		newsize = SSIZE_MAX - 1;
629240031aSJohn Baldwin 	else
639240031aSJohn Baldwin 		newsize = newoff;
649240031aSJohn Baldwin 	if (newsize > ms->len) {
659240031aSJohn Baldwin 		buf = realloc(*ms->bufp, newsize + 1);
669240031aSJohn Baldwin 		if (buf != NULL) {
679240031aSJohn Baldwin #ifdef DEBUG
689240031aSJohn Baldwin 			fprintf(stderr, "MS: %p growing from %zd to %zd\n",
699240031aSJohn Baldwin 			    ms, ms->len, newsize);
709240031aSJohn Baldwin #endif
719240031aSJohn Baldwin 			memset(buf + ms->len + 1, 0, newsize - ms->len);
729240031aSJohn Baldwin 			*ms->bufp = buf;
739240031aSJohn Baldwin 			ms->len = newsize;
749240031aSJohn Baldwin 			return (1);
759240031aSJohn Baldwin 		}
769240031aSJohn Baldwin 		return (0);
779240031aSJohn Baldwin 	}
789240031aSJohn Baldwin 	return (1);
799240031aSJohn Baldwin }
809240031aSJohn Baldwin 
819240031aSJohn Baldwin static void
829240031aSJohn Baldwin memstream_update(struct memstream *ms)
839240031aSJohn Baldwin {
849240031aSJohn Baldwin 
859240031aSJohn Baldwin 	assert(ms->len >= 0 && ms->offset >= 0);
869240031aSJohn Baldwin 	*ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
879240031aSJohn Baldwin }
889240031aSJohn Baldwin 
899240031aSJohn Baldwin static int
909240031aSJohn Baldwin memstream_write(void *cookie, const char *buf, int len)
919240031aSJohn Baldwin {
929240031aSJohn Baldwin 	struct memstream *ms;
939240031aSJohn Baldwin 	ssize_t tocopy;
949240031aSJohn Baldwin 
959240031aSJohn Baldwin 	ms = cookie;
969240031aSJohn Baldwin 	if (!memstream_grow(ms, ms->offset + len))
979240031aSJohn Baldwin 		return (-1);
989240031aSJohn Baldwin 	tocopy = ms->len - ms->offset;
999240031aSJohn Baldwin 	if (len < tocopy)
1009240031aSJohn Baldwin 		tocopy = len;
1019240031aSJohn Baldwin 	memcpy(*ms->bufp + ms->offset, buf, tocopy);
1029240031aSJohn Baldwin 	ms->offset += tocopy;
1039240031aSJohn Baldwin 	memstream_update(ms);
1049240031aSJohn Baldwin #ifdef DEBUG
1059240031aSJohn Baldwin 	fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
1069240031aSJohn Baldwin #endif
1079240031aSJohn Baldwin 	return (tocopy);
1089240031aSJohn Baldwin }
1099240031aSJohn Baldwin 
1109240031aSJohn Baldwin static fpos_t
1119240031aSJohn Baldwin memstream_seek(void *cookie, fpos_t pos, int whence)
1129240031aSJohn Baldwin {
1139240031aSJohn Baldwin 	struct memstream *ms;
1149240031aSJohn Baldwin #ifdef DEBUG
1159240031aSJohn Baldwin 	fpos_t old;
1169240031aSJohn Baldwin #endif
1179240031aSJohn Baldwin 
1189240031aSJohn Baldwin 	ms = cookie;
1199240031aSJohn Baldwin #ifdef DEBUG
1209240031aSJohn Baldwin 	old = ms->offset;
1219240031aSJohn Baldwin #endif
1229240031aSJohn Baldwin 	switch (whence) {
1239240031aSJohn Baldwin 	case SEEK_SET:
1249240031aSJohn Baldwin 		/* _fseeko() checks for negative offsets. */
1259240031aSJohn Baldwin 		assert(pos >= 0);
1269240031aSJohn Baldwin 		ms->offset = pos;
1279240031aSJohn Baldwin 		break;
1289240031aSJohn Baldwin 	case SEEK_CUR:
1299240031aSJohn Baldwin 		/* This is only called by _ftello(). */
1309240031aSJohn Baldwin 		assert(pos == 0);
1319240031aSJohn Baldwin 		break;
1329240031aSJohn Baldwin 	case SEEK_END:
1339240031aSJohn Baldwin 		if (pos < 0) {
1349240031aSJohn Baldwin 			if (pos + ms->len < 0) {
1359240031aSJohn Baldwin #ifdef DEBUG
1369240031aSJohn Baldwin 				fprintf(stderr,
1379240031aSJohn Baldwin 				    "MS: bad SEEK_END: pos %jd, len %zd\n",
1389240031aSJohn Baldwin 				    (intmax_t)pos, ms->len);
1399240031aSJohn Baldwin #endif
1409240031aSJohn Baldwin 				errno = EINVAL;
1419240031aSJohn Baldwin 				return (-1);
1429240031aSJohn Baldwin 			}
1439240031aSJohn Baldwin 		} else {
1449240031aSJohn Baldwin 			if (FPOS_MAX - ms->len < pos) {
1459240031aSJohn Baldwin #ifdef DEBUG
1469240031aSJohn Baldwin 				fprintf(stderr,
1479240031aSJohn Baldwin 				    "MS: bad SEEK_END: pos %jd, len %zd\n",
1489240031aSJohn Baldwin 				    (intmax_t)pos, ms->len);
1499240031aSJohn Baldwin #endif
1509240031aSJohn Baldwin 				errno = EOVERFLOW;
1519240031aSJohn Baldwin 				return (-1);
1529240031aSJohn Baldwin 			}
1539240031aSJohn Baldwin 		}
1549240031aSJohn Baldwin 		ms->offset = ms->len + pos;
1559240031aSJohn Baldwin 		break;
1569240031aSJohn Baldwin 	}
1579240031aSJohn Baldwin 	memstream_update(ms);
1589240031aSJohn Baldwin #ifdef DEBUG
1599240031aSJohn Baldwin 	fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
1609240031aSJohn Baldwin 	    whence, (intmax_t)old, (intmax_t)ms->offset);
1619240031aSJohn Baldwin #endif
1629240031aSJohn Baldwin 	return (ms->offset);
1639240031aSJohn Baldwin }
1649240031aSJohn Baldwin 
1659240031aSJohn Baldwin static int
1669240031aSJohn Baldwin memstream_close(void *cookie)
1679240031aSJohn Baldwin {
1689240031aSJohn Baldwin 
1699240031aSJohn Baldwin 	free(cookie);
1709240031aSJohn Baldwin 	return (0);
1719240031aSJohn Baldwin }
1729240031aSJohn Baldwin 
1739240031aSJohn Baldwin FILE *
1749240031aSJohn Baldwin open_memstream(char **bufp, size_t *sizep)
1759240031aSJohn Baldwin {
1769240031aSJohn Baldwin 	struct memstream *ms;
1779240031aSJohn Baldwin 	int save_errno;
1789240031aSJohn Baldwin 	FILE *fp;
1799240031aSJohn Baldwin 
1809240031aSJohn Baldwin 	if (bufp == NULL || sizep == NULL) {
1819240031aSJohn Baldwin 		errno = EINVAL;
1829240031aSJohn Baldwin 		return (NULL);
1839240031aSJohn Baldwin 	}
1849240031aSJohn Baldwin 	*bufp = calloc(1, 1);
1859240031aSJohn Baldwin 	if (*bufp == NULL)
1869240031aSJohn Baldwin 		return (NULL);
1879240031aSJohn Baldwin 	ms = malloc(sizeof(*ms));
1889240031aSJohn Baldwin 	if (ms == NULL) {
1899240031aSJohn Baldwin 		save_errno = errno;
1909240031aSJohn Baldwin 		free(*bufp);
1919240031aSJohn Baldwin 		*bufp = NULL;
1929240031aSJohn Baldwin 		errno = save_errno;
1939240031aSJohn Baldwin 		return (NULL);
1949240031aSJohn Baldwin 	}
1959240031aSJohn Baldwin 	ms->bufp = bufp;
1969240031aSJohn Baldwin 	ms->sizep = sizep;
1979240031aSJohn Baldwin 	ms->len = 0;
1989240031aSJohn Baldwin 	ms->offset = 0;
1999240031aSJohn Baldwin 	memstream_update(ms);
2009240031aSJohn Baldwin 	fp = funopen(ms, NULL, memstream_write, memstream_seek,
2019240031aSJohn Baldwin 	    memstream_close);
2029240031aSJohn Baldwin 	if (fp == NULL) {
2039240031aSJohn Baldwin 		save_errno = errno;
2049240031aSJohn Baldwin 		free(ms);
2059240031aSJohn Baldwin 		free(*bufp);
2069240031aSJohn Baldwin 		*bufp = NULL;
2079240031aSJohn Baldwin 		errno = save_errno;
2089240031aSJohn Baldwin 		return (NULL);
2099240031aSJohn Baldwin 	}
2109240031aSJohn Baldwin 	fwide(fp, -1);
2119240031aSJohn Baldwin 	return (fp);
2129240031aSJohn Baldwin }
213