1247411Sjhb/*-
2247411Sjhb * Copyright (c) 2013 Advanced Computing Technologies LLC
3247411Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org>
4247411Sjhb * All rights reserved.
5247411Sjhb *
6247411Sjhb * Redistribution and use in source and binary forms, with or without
7247411Sjhb * modification, are permitted provided that the following conditions
8247411Sjhb * are met:
9247411Sjhb * 1. Redistributions of source code must retain the above copyright
10247411Sjhb *    notice, this list of conditions and the following disclaimer.
11247411Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12247411Sjhb *    notice, this list of conditions and the following disclaimer in the
13247411Sjhb *    documentation and/or other materials provided with the distribution.
14247411Sjhb *
15247411Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16247411Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17247411Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18247411Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19247411Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20247411Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21247411Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22247411Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23247411Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24247411Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25247411Sjhb * SUCH DAMAGE.
26247411Sjhb */
27247411Sjhb
28247411Sjhb#include <sys/cdefs.h>
29247411Sjhb__FBSDID("$FreeBSD$");
30247411Sjhb
31247411Sjhb#include "namespace.h"
32247411Sjhb#include <assert.h>
33247411Sjhb#include <errno.h>
34247411Sjhb#include <limits.h>
35247411Sjhb#include <stdio.h>
36247411Sjhb#include <stdlib.h>
37247411Sjhb#include <string.h>
38247411Sjhb#include <wchar.h>
39247411Sjhb#include "un-namespace.h"
40247411Sjhb
41247411Sjhb/* XXX: There is no FPOS_MAX.  This assumes fpos_t is an off_t. */
42247411Sjhb#define	FPOS_MAX	OFF_MAX
43247411Sjhb
44247411Sjhbstruct memstream {
45247411Sjhb	char **bufp;
46247411Sjhb	size_t *sizep;
47247411Sjhb	ssize_t len;
48247411Sjhb	fpos_t offset;
49247411Sjhb};
50247411Sjhb
51247411Sjhbstatic int
52247411Sjhbmemstream_grow(struct memstream *ms, fpos_t newoff)
53247411Sjhb{
54247411Sjhb	char *buf;
55247411Sjhb	ssize_t newsize;
56247411Sjhb
57247411Sjhb	if (newoff < 0 || newoff >= SSIZE_MAX)
58247411Sjhb		newsize = SSIZE_MAX - 1;
59247411Sjhb	else
60247411Sjhb		newsize = newoff;
61247411Sjhb	if (newsize > ms->len) {
62247411Sjhb		buf = realloc(*ms->bufp, newsize + 1);
63247411Sjhb		if (buf != NULL) {
64247411Sjhb#ifdef DEBUG
65247411Sjhb			fprintf(stderr, "MS: %p growing from %zd to %zd\n",
66247411Sjhb			    ms, ms->len, newsize);
67247411Sjhb#endif
68247411Sjhb			memset(buf + ms->len + 1, 0, newsize - ms->len);
69247411Sjhb			*ms->bufp = buf;
70247411Sjhb			ms->len = newsize;
71247411Sjhb			return (1);
72247411Sjhb		}
73247411Sjhb		return (0);
74247411Sjhb	}
75247411Sjhb	return (1);
76247411Sjhb}
77247411Sjhb
78247411Sjhbstatic void
79247411Sjhbmemstream_update(struct memstream *ms)
80247411Sjhb{
81247411Sjhb
82247411Sjhb	assert(ms->len >= 0 && ms->offset >= 0);
83247411Sjhb	*ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
84247411Sjhb}
85247411Sjhb
86247411Sjhbstatic int
87247411Sjhbmemstream_write(void *cookie, const char *buf, int len)
88247411Sjhb{
89247411Sjhb	struct memstream *ms;
90247411Sjhb	ssize_t tocopy;
91247411Sjhb
92247411Sjhb	ms = cookie;
93247411Sjhb	if (!memstream_grow(ms, ms->offset + len))
94247411Sjhb		return (-1);
95247411Sjhb	tocopy = ms->len - ms->offset;
96247411Sjhb	if (len < tocopy)
97247411Sjhb		tocopy = len;
98247411Sjhb	memcpy(*ms->bufp + ms->offset, buf, tocopy);
99247411Sjhb	ms->offset += tocopy;
100247411Sjhb	memstream_update(ms);
101247411Sjhb#ifdef DEBUG
102247411Sjhb	fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
103247411Sjhb#endif
104247411Sjhb	return (tocopy);
105247411Sjhb}
106247411Sjhb
107247411Sjhbstatic fpos_t
108247411Sjhbmemstream_seek(void *cookie, fpos_t pos, int whence)
109247411Sjhb{
110247411Sjhb	struct memstream *ms;
111247411Sjhb#ifdef DEBUG
112247411Sjhb	fpos_t old;
113247411Sjhb#endif
114247411Sjhb
115247411Sjhb	ms = cookie;
116247411Sjhb#ifdef DEBUG
117247411Sjhb	old = ms->offset;
118247411Sjhb#endif
119247411Sjhb	switch (whence) {
120247411Sjhb	case SEEK_SET:
121247411Sjhb		/* _fseeko() checks for negative offsets. */
122247411Sjhb		assert(pos >= 0);
123247411Sjhb		ms->offset = pos;
124247411Sjhb		break;
125247411Sjhb	case SEEK_CUR:
126247411Sjhb		/* This is only called by _ftello(). */
127247411Sjhb		assert(pos == 0);
128247411Sjhb		break;
129247411Sjhb	case SEEK_END:
130247411Sjhb		if (pos < 0) {
131247411Sjhb			if (pos + ms->len < 0) {
132247411Sjhb#ifdef DEBUG
133247411Sjhb				fprintf(stderr,
134247411Sjhb				    "MS: bad SEEK_END: pos %jd, len %zd\n",
135247411Sjhb				    (intmax_t)pos, ms->len);
136247411Sjhb#endif
137247411Sjhb				errno = EINVAL;
138247411Sjhb				return (-1);
139247411Sjhb			}
140247411Sjhb		} else {
141247411Sjhb			if (FPOS_MAX - ms->len < pos) {
142247411Sjhb#ifdef DEBUG
143247411Sjhb				fprintf(stderr,
144247411Sjhb				    "MS: bad SEEK_END: pos %jd, len %zd\n",
145247411Sjhb				    (intmax_t)pos, ms->len);
146247411Sjhb#endif
147247411Sjhb				errno = EOVERFLOW;
148247411Sjhb				return (-1);
149247411Sjhb			}
150247411Sjhb		}
151247411Sjhb		ms->offset = ms->len + pos;
152247411Sjhb		break;
153247411Sjhb	}
154247411Sjhb	memstream_update(ms);
155247411Sjhb#ifdef DEBUG
156247411Sjhb	fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
157247411Sjhb	    whence, (intmax_t)old, (intmax_t)ms->offset);
158247411Sjhb#endif
159247411Sjhb	return (ms->offset);
160247411Sjhb}
161247411Sjhb
162247411Sjhbstatic int
163247411Sjhbmemstream_close(void *cookie)
164247411Sjhb{
165247411Sjhb
166247411Sjhb	free(cookie);
167247411Sjhb	return (0);
168247411Sjhb}
169247411Sjhb
170247411SjhbFILE *
171247411Sjhbopen_memstream(char **bufp, size_t *sizep)
172247411Sjhb{
173247411Sjhb	struct memstream *ms;
174247411Sjhb	int save_errno;
175247411Sjhb	FILE *fp;
176247411Sjhb
177247411Sjhb	if (bufp == NULL || sizep == NULL) {
178247411Sjhb		errno = EINVAL;
179247411Sjhb		return (NULL);
180247411Sjhb	}
181247411Sjhb	*bufp = calloc(1, 1);
182247411Sjhb	if (*bufp == NULL)
183247411Sjhb		return (NULL);
184247411Sjhb	ms = malloc(sizeof(*ms));
185247411Sjhb	if (ms == NULL) {
186247411Sjhb		save_errno = errno;
187247411Sjhb		free(*bufp);
188247411Sjhb		*bufp = NULL;
189247411Sjhb		errno = save_errno;
190247411Sjhb		return (NULL);
191247411Sjhb	}
192247411Sjhb	ms->bufp = bufp;
193247411Sjhb	ms->sizep = sizep;
194247411Sjhb	ms->len = 0;
195247411Sjhb	ms->offset = 0;
196247411Sjhb	memstream_update(ms);
197247411Sjhb	fp = funopen(ms, NULL, memstream_write, memstream_seek,
198247411Sjhb	    memstream_close);
199247411Sjhb	if (fp == NULL) {
200247411Sjhb		save_errno = errno;
201247411Sjhb		free(ms);
202247411Sjhb		free(*bufp);
203247411Sjhb		*bufp = NULL;
204247411Sjhb		errno = save_errno;
205247411Sjhb		return (NULL);
206247411Sjhb	}
207247411Sjhb	fwide(fp, -1);
208247411Sjhb	return (fp);
209247411Sjhb}
210