]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdio/fmemopen.c
Merge bmake-20201117
[FreeBSD/FreeBSD.git] / lib / libc / stdio / fmemopen.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org>
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 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 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 <fcntl.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include "local.h"
38
39 struct fmemopen_cookie
40 {
41         char    *buf;   /* pointer to the memory region */
42         bool     own;   /* did we allocate the buffer ourselves? */
43         char     bin;   /* is this a binary buffer? */
44         size_t   size;  /* buffer length in bytes */
45         size_t   len;   /* data length in bytes */
46         size_t   off;   /* current offset into the buffer */
47 };
48
49 static int      fmemopen_read(void *cookie, char *buf, int nbytes);
50 static int      fmemopen_write(void *cookie, const char *buf, int nbytes);
51 static fpos_t   fmemopen_seek(void *cookie, fpos_t offset, int whence);
52 static int      fmemopen_close(void *cookie);
53
54 FILE *
55 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
56 {
57         struct fmemopen_cookie *ck;
58         FILE *f;
59         int flags, rc;
60
61         /*
62          * POSIX says we shall return EINVAL if size is 0.
63          */
64         if (size == 0) {
65                 errno = EINVAL;
66                 return (NULL);
67         }
68
69         /*
70          * Retrieve the flags as used by open(2) from the mode argument, and
71          * validate them.
72          */
73         rc = __sflags(mode, &flags);
74         if (rc == 0) {
75                 errno = EINVAL;
76                 return (NULL);
77         }
78
79         /*
80          * There's no point in requiring an automatically allocated buffer
81          * in write-only mode.
82          */
83         if (!(flags & O_RDWR) && buf == NULL) {
84                 errno = EINVAL;
85                 return (NULL);
86         }
87         
88         ck = malloc(sizeof(struct fmemopen_cookie));
89         if (ck == NULL) {
90                 return (NULL);
91         }
92
93         ck->off  = 0;
94         ck->size = size;
95
96         /* Check whether we have to allocate the buffer ourselves. */
97         ck->own = ((ck->buf = buf) == NULL);
98         if (ck->own) {
99                 ck->buf = malloc(size);
100                 if (ck->buf == NULL) {
101                         free(ck);
102                         return (NULL);
103                 }
104         }
105
106         /*
107          * POSIX distinguishes between w+ and r+, in that w+ is supposed to
108          * truncate the buffer.
109          */
110         if (ck->own || mode[0] == 'w') {
111                 ck->buf[0] = '\0';
112         }
113
114         /* Check for binary mode. */
115         ck->bin = strchr(mode, 'b') != NULL;
116
117         /*
118          * The size of the current buffer contents is set depending on the
119          * mode:
120          * 
121          * for append (text-mode), the position of the first NULL byte, or the
122          * size of the buffer if none is found
123          *
124          * for append (binary-mode), the size of the buffer
125          * 
126          * for read, the size of the buffer
127          * 
128          * for write, 0
129          */
130         switch (mode[0]) {
131         case 'a':
132                 ck->off = ck->len = strnlen(ck->buf, ck->size);
133                 break;
134         case 'r':
135                 ck->len = size;
136                 break;
137         case 'w':
138                 ck->len = 0;
139                 break;
140         }
141
142         f = funopen(ck,
143             flags & O_WRONLY ? NULL : fmemopen_read, 
144             flags & O_RDONLY ? NULL : fmemopen_write,
145             fmemopen_seek, fmemopen_close);
146
147         if (f == NULL) {
148                 if (ck->own)
149                         free(ck->buf);
150                 free(ck);
151                 return (NULL);
152         }
153
154         if (mode[0] == 'a')
155                 f->_flags |= __SAPP;
156
157         /*
158          * Turn off buffering, so a write past the end of the buffer
159          * correctly returns a short object count.
160          */
161         setvbuf(f, NULL, _IONBF, 0);
162
163         return (f);
164 }
165
166 static int
167 fmemopen_read(void *cookie, char *buf, int nbytes)
168 {
169         struct fmemopen_cookie *ck = cookie;
170
171         if (nbytes > ck->len - ck->off)
172                 nbytes = ck->len - ck->off;
173
174         if (nbytes == 0)
175                 return (0);
176
177         memcpy(buf, ck->buf + ck->off, nbytes);
178
179         ck->off += nbytes;
180
181         return (nbytes);
182 }
183
184 static int
185 fmemopen_write(void *cookie, const char *buf, int nbytes)
186 {
187         struct fmemopen_cookie *ck = cookie;
188
189         if (nbytes > ck->size - ck->off)
190                 nbytes = ck->size - ck->off;
191
192         if (nbytes == 0)
193                 return (0);
194
195         memcpy(ck->buf + ck->off, buf, nbytes);
196
197         ck->off += nbytes;
198
199         if (ck->off > ck->len)
200                 ck->len = ck->off;
201
202         /*
203          * We append a NULL byte if all these conditions are met:
204          * - the buffer is not binary
205          * - the buffer is not full
206          * - the data just written doesn't already end with a NULL byte
207          */
208         if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0')
209                 ck->buf[ck->off] = '\0';
210
211         return (nbytes);
212 }
213
214 static fpos_t
215 fmemopen_seek(void *cookie, fpos_t offset, int whence)
216 {
217         struct fmemopen_cookie *ck = cookie;
218
219
220         switch (whence) {
221         case SEEK_SET:
222                 if (offset > ck->size) {
223                         errno = EINVAL;
224                         return (-1);
225                 }
226                 ck->off = offset;
227                 break;
228
229         case SEEK_CUR:
230                 if (ck->off + offset > ck->size) {
231                         errno = EINVAL;
232                         return (-1);
233                 }
234                 ck->off += offset;
235                 break;
236
237         case SEEK_END:
238                 if (offset > 0 || -offset > ck->len) {
239                         errno = EINVAL;
240                         return (-1);
241                 }
242                 ck->off = ck->len + offset;
243                 break;
244
245         default:
246                 errno = EINVAL;
247                 return (-1);
248         }
249
250         return (ck->off);
251 }
252
253 static int
254 fmemopen_close(void *cookie)
255 {
256         struct fmemopen_cookie *ck = cookie;
257
258         if (ck->own)
259                 free(ck->buf);
260
261         free(ck);
262
263         return (0);
264 }