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