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