1 /* $NetBSD: fmemopen.c,v 1.4 2010/09/27 16:50:13 tnozaki Exp $ */
2
3 /*-
4 * Copyright (c)2007, 2010 Takehiko NOZAKI,
5 * Copyright (c) 2012, Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include "local.h"
39
40 struct fmemopen_cookie {
41 char *head, *tail, *cur, *eob;
42 };
43
44 static int
fmemopen_read(void * cookie,char * buf,int nbytes)45 fmemopen_read(void *cookie, char *buf, int nbytes)
46 {
47 struct fmemopen_cookie *p;
48 char *s;
49 int len;
50
51 assert(cookie != NULL);
52 assert(buf != NULL && nbytes > 0);
53
54 p = cookie;
55 s = p->cur;
56 len = MIN(p->tail - p->cur, nbytes);
57 bcopy(p->cur, buf, len);
58 p->cur += len;
59
60 return (int)(p->cur - s);
61 }
62
63 static int
fmemopen_write(void * cookie,const char * buf,int nbytes)64 fmemopen_write(void *cookie, const char *buf, int nbytes)
65 {
66 struct fmemopen_cookie *p;
67 char *s;
68 int len;
69
70 assert(cookie != NULL);
71 assert(buf != NULL && nbytes > 0);
72
73 p = cookie;
74 if (p->cur >= p->tail)
75 return 0;
76 s = p->cur;
77
78 len = MIN(p->tail - p->cur, nbytes);
79
80 bcopy(buf, p->cur, len);
81
82 p->cur += len - 1;
83 if (p->cur == p->tail - 1) {
84 *p->cur = '\0';
85 if (buf[len - 1] == '\0')
86 p->cur++;
87 } else {
88 *++p->cur = '\0';
89 }
90
91 if (p->cur > p->eob)
92 p->eob = p->cur;
93
94 return (int)(p->cur - s);
95 }
96
97 static fpos_t
fmemopen_seek(void * cookie,fpos_t offset,int whence)98 fmemopen_seek(void *cookie, fpos_t offset, int whence)
99 {
100 struct fmemopen_cookie *p;
101
102 assert(cookie != NULL);
103
104 p = (struct fmemopen_cookie *)cookie;
105 switch (whence) {
106 case SEEK_SET:
107 break;
108 case SEEK_CUR:
109 offset += p->cur - p->head;
110 break;
111 case SEEK_END:
112 offset += p->eob - p->head;
113 break;
114 default:
115 errno = EINVAL;
116 goto error;
117 }
118 if (offset >= (fpos_t)0 && offset <= p->tail - p->head) {
119 p->cur = p->head + (ptrdiff_t)offset;
120 return (fpos_t)(p->cur - p->head);
121 }
122 error:
123 return (fpos_t)-1;
124 }
125
126 static int
fmemopen_close0(void * cookie)127 fmemopen_close0(void *cookie)
128 {
129 assert(cookie != NULL);
130
131 free(cookie);
132
133 return 0;
134 }
135
136 static int
fmemopen_close1(void * cookie)137 fmemopen_close1(void *cookie)
138 {
139 struct fmemopen_cookie *p;
140
141 assert(cookie != NULL);
142
143 p = cookie;
144 free(p->head);
145 free(p);
146
147 return 0;
148 }
149
150
151 FILE *
fmemopen(void * __restrict buf,size_t size,const char * __restrict mode)152 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
153 {
154 int flags, oflags;
155 FILE *fp;
156 struct fmemopen_cookie *cookie;
157
158 if (size < (size_t)1)
159 goto invalid;
160
161 flags = __sflags(mode, &oflags);
162 if (flags == 0)
163 return NULL;
164
165 if ((oflags & O_RDWR) == 0 && buf == NULL)
166 goto invalid;
167
168 fp = __sfp();
169 if (fp == NULL)
170 return NULL;
171
172 cookie = malloc(sizeof(*cookie));
173 if (cookie == NULL)
174 goto release;
175
176 if (buf == NULL) {
177 cookie->head = malloc(size);
178 if (cookie->head == NULL) {
179 free(cookie);
180 goto release;
181 }
182 *cookie->head = '\0';
183 fp->_close = &fmemopen_close1;
184 } else {
185 cookie->head = (char *)buf;
186 if (oflags & O_TRUNC)
187 *cookie->head = '\0';
188 fp->_close = &fmemopen_close0;
189 }
190
191 cookie->tail = cookie->head + size;
192 cookie->eob = cookie->head;
193 do {
194 if (*cookie->eob == '\0')
195 break;
196 ++cookie->eob;
197 } while (--size > 0);
198
199 cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head;
200
201 fp->pub._flags = flags;
202 fp->_write = (flags & __SRD) ? NULL : &fmemopen_write;
203 fp->_read = (flags & __SWR) ? NULL : &fmemopen_read;
204 fp->_seek = &fmemopen_seek;
205 fp->_cookie = (void *)cookie;
206
207 return fp;
208
209 invalid:
210 errno = EINVAL;
211 return NULL;
212
213 release:
214 fp->pub._flags = 0;
215 return NULL;
216 }
217
218