xref: /dragonfly/lib/libc/stdio/open_memstream.c (revision 2c8c2922834904924d7b42db7bd943aaa20f3452)
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
memstream_grow(struct memstream * ms,fpos_t newoff)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
memstream_update(struct memstream * ms)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
memstream_write(void * cookie,const char * buf,int len)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
memstream_seek(void * cookie,fpos_t pos,int whence)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
memstream_close(void * cookie)162 memstream_close(void *cookie)
163 {
164 
165           free(cookie);
166           return (0);
167 }
168 
169 FILE *
open_memstream(char ** bufp,size_t * sizep)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