1 /* $OpenBSD: fmemopen.c,v 1.5 2020/08/17 16:17:39 millert Exp $ */
2
3 /*
4 * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
5 * Copyright (c) 2009 Ted Unangst
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "local.h"
26
27 struct state {
28 char *string; /* actual stream */
29 size_t pos; /* current position */
30 size_t size; /* allocated size */
31 size_t len; /* length of the data */
32 int update; /* open for update */
33 int append; /* open for append */
34 };
35
36 static int
fmemopen_read(void * v,char * b,int l)37 fmemopen_read(void *v, char *b, int l)
38 {
39 struct state *st = v;
40 int i;
41
42 for (i = 0; i < l && i + st->pos < st->len; i++)
43 b[i] = st->string[st->pos + i];
44 st->pos += i;
45
46 return (i);
47 }
48
49 static int
fmemopen_write(void * v,const char * b,int l)50 fmemopen_write(void *v, const char *b, int l)
51 {
52 struct state *st = v;
53 int i;
54
55 if (st->append)
56 st->pos = st->len;
57
58 for (i = 0; i < l && i + st->pos < st->size; i++)
59 st->string[st->pos + i] = b[i];
60 st->pos += i;
61
62 if (st->pos >= st->len) {
63 st->len = st->pos;
64
65 if (st->len < st->size)
66 st->string[st->len] = '\0';
67 else if (!st->update)
68 st->string[st->size - 1] = '\0';
69 }
70
71 return (i);
72 }
73
74 static fpos_t
fmemopen_seek(void * v,fpos_t off,int whence)75 fmemopen_seek(void *v, fpos_t off, int whence)
76 {
77 struct state *st = v;
78 ssize_t base = 0;
79
80 switch (whence) {
81 case SEEK_SET:
82 break;
83 case SEEK_CUR:
84 base = st->pos;
85 break;
86 case SEEK_END:
87 base = st->len;
88 break;
89 }
90
91 if (off > st->size - base || off < -base) {
92 errno = EOVERFLOW;
93 return (-1);
94 }
95
96 st->pos = base + off;
97
98 return (st->pos);
99 }
100
101 static int
fmemopen_close(void * v)102 fmemopen_close(void *v)
103 {
104 free(v);
105
106 return (0);
107 }
108
109 static int
fmemopen_close_free(void * v)110 fmemopen_close_free(void *v)
111 {
112 struct state *st = v;
113
114 free(st->string);
115 free(st);
116
117 return (0);
118 }
119
120 FILE *
fmemopen(void * buf,size_t size,const char * mode)121 fmemopen(void *buf, size_t size, const char *mode)
122 {
123 struct state *st;
124 FILE *fp;
125 int flags, oflags;
126
127 if (size == 0) {
128 errno = EINVAL;
129 return (NULL);
130 }
131
132 if ((flags = __sflags(mode, &oflags)) == 0) {
133 errno = EINVAL;
134 return (NULL);
135 }
136
137 if (buf == NULL && ((oflags & O_RDWR) == 0)) {
138 errno = EINVAL;
139 return (NULL);
140 }
141
142 if ((st = malloc(sizeof(*st))) == NULL)
143 return (NULL);
144
145 if ((fp = __sfp()) == NULL) {
146 free(st);
147 return (NULL);
148 }
149
150 st->pos = 0;
151 st->len = (oflags & O_TRUNC) ? 0 : size;
152 st->size = size;
153 st->update = oflags & O_RDWR;
154 st->append = oflags & O_APPEND;
155
156 if (buf == NULL) {
157 if ((st->string = malloc(size)) == NULL) {
158 free(st);
159 fp->_flags = 0;
160 return (NULL);
161 }
162 *st->string = '\0';
163 } else {
164 st->string = (char *)buf;
165
166 if (oflags & O_TRUNC)
167 *st->string = '\0';
168
169 if (oflags & O_APPEND) {
170 char *p;
171
172 if ((p = memchr(st->string, '\0', size)) != NULL)
173 st->pos = st->len = (p - st->string);
174 else
175 st->pos = st->len = size;
176 }
177 }
178
179 fp->_flags = (short)flags;
180 fp->_file = -1;
181 fp->_cookie = st;
182 fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
183 fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
184 fp->_seek = fmemopen_seek;
185 fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
186
187 return (fp);
188 }
189 DEF_WEAK(fmemopen);
190