]> git.saurik.com Git - apple/libc.git/blob - stdio/FreeBSD/open_memstream.c
Libc-1244.30.3.tar.gz
[apple/libc.git] / stdio / FreeBSD / open_memstream.c
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <limits.h>
34 #ifdef DEBUG
35 #include <stdint.h>
36 #endif
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <wchar.h>
41
42 /* XXX: There is no FPOS_MAX. This assumes fpos_t is an off_t. */
43 #define FPOS_MAX OFF_MAX
44
45 struct memstream {
46 char **bufp;
47 size_t *sizep;
48 ssize_t len;
49 fpos_t offset;
50 };
51
52 static int
53 memstream_grow(struct memstream *ms, fpos_t newoff)
54 {
55 char *buf;
56 ssize_t newsize;
57
58 if (newoff < 0 || newoff >= SSIZE_MAX)
59 newsize = SSIZE_MAX - 1;
60 else
61 newsize = newoff;
62 if (newsize > ms->len) {
63 buf = realloc(*ms->bufp, newsize + 1);
64 if (buf != NULL) {
65 #ifdef DEBUG
66 fprintf(stderr, "MS: %p growing from %zd to %zd\n",
67 ms, ms->len, newsize);
68 #endif
69 memset(buf + ms->len + 1, 0, newsize - ms->len);
70 *ms->bufp = buf;
71 ms->len = newsize;
72 return (1);
73 }
74 return (0);
75 }
76 return (1);
77 }
78
79 static void
80 memstream_update(struct memstream *ms)
81 {
82
83 assert(ms->len >= 0 && ms->offset >= 0);
84 *ms->sizep = ms->len < ms->offset ? ms->len : ms->offset;
85 }
86
87 static int
88 memstream_write(void *cookie, const char *buf, int len)
89 {
90 struct memstream *ms;
91 ssize_t tocopy;
92
93 ms = cookie;
94 if (!memstream_grow(ms, ms->offset + len))
95 return (-1);
96 tocopy = ms->len - ms->offset;
97 if (len < tocopy)
98 tocopy = len;
99 memcpy(*ms->bufp + ms->offset, buf, tocopy);
100 ms->offset += tocopy;
101 memstream_update(ms);
102 #ifdef DEBUG
103 fprintf(stderr, "MS: write(%p, %d) = %zd\n", ms, len, tocopy);
104 #endif
105 return (tocopy);
106 }
107
108 static fpos_t
109 memstream_seek(void *cookie, fpos_t pos, int whence)
110 {
111 struct memstream *ms;
112 #ifdef DEBUG
113 fpos_t old;
114 #endif
115
116 ms = cookie;
117 #ifdef DEBUG
118 old = ms->offset;
119 #endif
120 switch (whence) {
121 case SEEK_SET:
122 /* _fseeko() checks for negative offsets. */
123 assert(pos >= 0);
124 ms->offset = pos;
125 break;
126 case SEEK_CUR:
127 /* This is only called by _ftello(). */
128 assert(pos == 0);
129 break;
130 case SEEK_END:
131 if (pos < 0) {
132 if (pos + ms->len < 0) {
133 #ifdef DEBUG
134 fprintf(stderr,
135 "MS: bad SEEK_END: pos %jd, len %zd\n",
136 (intmax_t)pos, ms->len);
137 #endif
138 errno = EINVAL;
139 return (-1);
140 }
141 } else {
142 if (FPOS_MAX - ms->len < pos) {
143 #ifdef DEBUG
144 fprintf(stderr,
145 "MS: bad SEEK_END: pos %jd, len %zd\n",
146 (intmax_t)pos, ms->len);
147 #endif
148 errno = EOVERFLOW;
149 return (-1);
150 }
151 }
152 ms->offset = ms->len + pos;
153 break;
154 }
155 memstream_update(ms);
156 #ifdef DEBUG
157 fprintf(stderr, "MS: seek(%p, %jd, %d) %jd -> %jd\n", ms, (intmax_t)pos,
158 whence, (intmax_t)old, (intmax_t)ms->offset);
159 #endif
160 return (ms->offset);
161 }
162
163 static int
164 memstream_close(void *cookie)
165 {
166
167 free(cookie);
168 return (0);
169 }
170
171 FILE *
172 open_memstream(char **bufp, size_t *sizep)
173 {
174 struct memstream *ms;
175 int save_errno;
176 FILE *fp;
177
178 if (bufp == NULL || sizep == NULL) {
179 errno = EINVAL;
180 return (NULL);
181 }
182 *bufp = calloc(1, 1);
183 if (*bufp == NULL)
184 return (NULL);
185 ms = malloc(sizeof(*ms));
186 if (ms == NULL) {
187 save_errno = errno;
188 free(*bufp);
189 *bufp = NULL;
190 errno = save_errno;
191 return (NULL);
192 }
193 ms->bufp = bufp;
194 ms->sizep = sizep;
195 ms->len = 0;
196 ms->offset = 0;
197 memstream_update(ms);
198 fp = funopen(ms, NULL, memstream_write, memstream_seek,
199 memstream_close);
200 if (fp == NULL) {
201 save_errno = errno;
202 free(ms);
203 free(*bufp);
204 *bufp = NULL;
205 errno = save_errno;
206 return (NULL);
207 }
208 fwide(fp, -1);
209 return (fp);
210 }