1247411Sjhb/*-
2281887Sjhb * Copyright (c) 2013 Hudson River Trading 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>
34292013Sngie#include <limits.h>
35292004Sngie#ifdef DEBUG
36292013Sngie#include <stdint.h>
37292004Sngie#endif
38247411Sjhb#include <stdio.h>
39247411Sjhb#include <stdlib.h>
40247411Sjhb#include <string.h>
41247411Sjhb#include <wchar.h>
42247411Sjhb#include "un-namespace.h"
43247411Sjhb
44247411Sjhb/* XXX: There is no FPOS_MAX.  This assumes fpos_t is an off_t. */
45247411Sjhb#define	FPOS_MAX	OFF_MAX
46247411Sjhb
47247411Sjhbstruct memstream {
48247411Sjhb	char **bufp;
49247411Sjhb	size_t *sizep;
50247411Sjhb	ssize_t len;
51247411Sjhb	fpos_t offset;
52247411Sjhb};
53247411Sjhb
54247411Sjhbstatic int
55247411Sjhbmemstream_grow(struct memstream *ms, fpos_t newoff)
56247411Sjhb{
57247411Sjhb	char *buf;
58247411Sjhb	ssize_t newsize;
59247411Sjhb
60247411Sjhb	if (newoff < 0 || newoff >= SSIZE_MAX)
61247411Sjhb		newsize = SSIZE_MAX - 1;
62247411Sjhb	else
63247411Sjhb		newsize = newoff;
64247411Sjhb	if (newsize > ms->len) {
65247411Sjhb		buf = realloc(*ms->bufp, newsize + 1);
66247411Sjhb		if (buf != NULL) {
67247411Sjhb#ifdef DEBUG
68247411Sjhb			fprintf(stderr, "MS: %p growing from %zd to %zd\n",
69247411Sjhb			    ms, ms->len, newsize);
70247411Sjhb#endif
71247411Sjhb			memset(buf + ms->len + 1, 0, newsize - ms->len);
72247411Sjhb			*ms->bufp = buf;
73247411Sjhb			ms->len = newsize;
74247411Sjhb			return (1);
75247411Sjhb		}
76247411Sjhb		return (0);
77247411Sjhb	}
78247411Sjhb	return (1);
79247411Sjhb}
80247411Sjhb
81247411Sjhbstatic void
82247411Sjhbmemstream_update(struct memstream *ms)
83247411Sjhb{
84247411Sjhb
85247411Sjhb	assert(ms->len >= 0 && ms->offset >= 0);
86247411Sjhb	*ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
87247411Sjhb}
88247411Sjhb
89247411Sjhbstatic int
90247411Sjhbmemstream_write(void *cookie, const char *buf, int len)
91247411Sjhb{
92247411Sjhb	struct memstream *ms;
93247411Sjhb	ssize_t tocopy;
94247411Sjhb
95247411Sjhb	ms = cookie;
96247411Sjhb	if (!memstream_grow(ms, ms->offset + len))
97247411Sjhb		return (-1);
98247411Sjhb	tocopy = ms->len - ms->offset;
99247411Sjhb	if (len < tocopy)
100247411Sjhb		tocopy = len;
101247411Sjhb	memcpy(*ms->bufp + ms->offset, buf, tocopy);
102247411Sjhb	ms->offset += tocopy;
103247411Sjhb	memstream_update(ms);
104247411Sjhb#ifdef DEBUG
105247411Sjhb	fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
106247411Sjhb#endif
107247411Sjhb	return (tocopy);
108247411Sjhb}
109247411Sjhb
110247411Sjhbstatic fpos_t
111247411Sjhbmemstream_seek(void *cookie, fpos_t pos, int whence)
112247411Sjhb{
113247411Sjhb	struct memstream *ms;
114247411Sjhb#ifdef DEBUG
115247411Sjhb	fpos_t old;
116247411Sjhb#endif
117247411Sjhb
118247411Sjhb	ms = cookie;
119247411Sjhb#ifdef DEBUG
120247411Sjhb	old = ms->offset;
121247411Sjhb#endif
122247411Sjhb	switch (whence) {
123247411Sjhb	case SEEK_SET:
124247411Sjhb		/* _fseeko() checks for negative offsets. */
125247411Sjhb		assert(pos >= 0);
126247411Sjhb		ms->offset = pos;
127247411Sjhb		break;
128247411Sjhb	case SEEK_CUR:
129247411Sjhb		/* This is only called by _ftello(). */
130247411Sjhb		assert(pos == 0);
131247411Sjhb		break;
132247411Sjhb	case SEEK_END:
133247411Sjhb		if (pos < 0) {
134247411Sjhb			if (pos + ms->len < 0) {
135247411Sjhb#ifdef DEBUG
136247411Sjhb				fprintf(stderr,
137247411Sjhb				    "MS: bad SEEK_END: pos %jd, len %zd\n",
138247411Sjhb				    (intmax_t)pos, ms->len);
139247411Sjhb#endif
140247411Sjhb				errno = EINVAL;
141247411Sjhb				return (-1);
142247411Sjhb			}
143247411Sjhb		} else {
144247411Sjhb			if (FPOS_MAX - ms->len < pos) {
145247411Sjhb#ifdef DEBUG
146247411Sjhb				fprintf(stderr,
147247411Sjhb				    "MS: bad SEEK_END: pos %jd, len %zd\n",
148247411Sjhb				    (intmax_t)pos, ms->len);
149247411Sjhb#endif
150247411Sjhb				errno = EOVERFLOW;
151247411Sjhb				return (-1);
152247411Sjhb			}
153247411Sjhb		}
154247411Sjhb		ms->offset = ms->len + pos;
155247411Sjhb		break;
156247411Sjhb	}
157247411Sjhb	memstream_update(ms);
158247411Sjhb#ifdef DEBUG
159247411Sjhb	fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
160247411Sjhb	    whence, (intmax_t)old, (intmax_t)ms->offset);
161247411Sjhb#endif
162247411Sjhb	return (ms->offset);
163247411Sjhb}
164247411Sjhb
165247411Sjhbstatic int
166247411Sjhbmemstream_close(void *cookie)
167247411Sjhb{
168247411Sjhb
169247411Sjhb	free(cookie);
170247411Sjhb	return (0);
171247411Sjhb}
172247411Sjhb
173247411SjhbFILE *
174247411Sjhbopen_memstream(char **bufp, size_t *sizep)
175247411Sjhb{
176247411Sjhb	struct memstream *ms;
177247411Sjhb	int save_errno;
178247411Sjhb	FILE *fp;
179247411Sjhb
180247411Sjhb	if (bufp == NULL || sizep == NULL) {
181247411Sjhb		errno = EINVAL;
182247411Sjhb		return (NULL);
183247411Sjhb	}
184247411Sjhb	*bufp = calloc(1, 1);
185247411Sjhb	if (*bufp == NULL)
186247411Sjhb		return (NULL);
187247411Sjhb	ms = malloc(sizeof(*ms));
188247411Sjhb	if (ms == NULL) {
189247411Sjhb		save_errno = errno;
190247411Sjhb		free(*bufp);
191247411Sjhb		*bufp = NULL;
192247411Sjhb		errno = save_errno;
193247411Sjhb		return (NULL);
194247411Sjhb	}
195247411Sjhb	ms->bufp = bufp;
196247411Sjhb	ms->sizep = sizep;
197247411Sjhb	ms->len = 0;
198247411Sjhb	ms->offset = 0;
199247411Sjhb	memstream_update(ms);
200247411Sjhb	fp = funopen(ms, NULL, memstream_write, memstream_seek,
201247411Sjhb	    memstream_close);
202247411Sjhb	if (fp == NULL) {
203247411Sjhb		save_errno = errno;
204247411Sjhb		free(ms);
205247411Sjhb		free(*bufp);
206247411Sjhb		*bufp = NULL;
207247411Sjhb		errno = save_errno;
208247411Sjhb		return (NULL);
209247411Sjhb	}
210247411Sjhb	fwide(fp, -1);
211247411Sjhb	return (fp);
212247411Sjhb}
213