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