xref: /dragonfly/lib/libc/stdio/fmemopen.c (revision 650094e1)
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 #include "priv_stdio.h"
40 
41 struct fmemopen_cookie {
42 	char *head, *tail, *cur, *eob;
43 };
44 
45 static int
46 fmemopen_read(void *cookie, char *buf, int nbytes)
47 {
48 	struct fmemopen_cookie *p;
49 	char *s;
50 	int len;
51 
52 	assert(cookie != NULL);
53 	assert(buf != NULL && nbytes > 0);
54 
55 	p = cookie;
56 	s = p->cur;
57 	len = MIN(p->tail - p->cur, nbytes);
58 	bcopy(p->cur, buf, len);
59 	p->cur += len;
60 
61 	return (int)(p->cur - s);
62 }
63 
64 static int
65 fmemopen_write(void *cookie, const char *buf, int nbytes)
66 {
67 	struct fmemopen_cookie *p;
68 	char *s;
69 	int len;
70 
71 	assert(cookie != NULL);
72 	assert(buf != NULL && nbytes > 0);
73 
74 	p = cookie;
75 	if (p->cur >= p->tail)
76 		return 0;
77 	s = p->cur;
78 
79 	len = MIN(p->tail - p->cur, nbytes);
80 
81 	bcopy(buf, p->cur, len);
82 
83 	p->cur += len - 1;
84 	if (p->cur == p->tail - 1) {
85 		*p->cur = '\0';
86 		if (buf[len - 1] == '\0')
87 			p->cur++;
88 	} else {
89 		*++p->cur = '\0';
90 	}
91 
92 	if (p->cur > p->eob)
93 		p->eob = p->cur;
94 
95 	return (int)(p->cur - s);
96 }
97 
98 static fpos_t
99 fmemopen_seek(void *cookie, fpos_t offset, int whence)
100 {
101 	struct fmemopen_cookie *p;
102 
103 	assert(cookie != NULL);
104 
105 	p = (struct fmemopen_cookie *)cookie;
106 	switch (whence) {
107 	case SEEK_SET:
108 		break;
109 	case SEEK_CUR:
110 		offset += p->cur - p->head;
111 		break;
112 	case SEEK_END:
113 		offset += p->eob - p->head;
114 		break;
115 	default:
116 		errno = EINVAL;
117 		goto error;
118 	}
119 	if (offset >= (fpos_t)0 && offset <= p->tail - p->head) {
120 		p->cur = p->head + (ptrdiff_t)offset;
121 		return (fpos_t)(p->cur - p->head);
122 	}
123 error:
124 	return (fpos_t)-1;
125 }
126 
127 static int
128 fmemopen_close0(void *cookie)
129 {
130 	assert(cookie != NULL);
131 
132 	free(cookie);
133 
134 	return 0;
135 }
136 
137 static int
138 fmemopen_close1(void *cookie)
139 {
140 	struct fmemopen_cookie *p;
141 
142 	assert(cookie != NULL);
143 
144 	p = cookie;
145 	free(p->head);
146 	free(p);
147 
148 	return 0;
149 }
150 
151 
152 FILE *
153 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
154 {
155 	int flags, oflags;
156 	FILE *fp;
157 	struct fmemopen_cookie *cookie;
158 
159 	if (size < (size_t)1)
160 		goto invalid;
161 
162 	flags = __sflags(mode, &oflags);
163 	if (flags == 0)
164 		return NULL;
165 
166 	if ((oflags & O_RDWR) == 0 && buf == NULL)
167 		goto invalid;
168 
169 	fp = __sfp();
170 	if (fp == NULL)
171 		return NULL;
172 
173 	cookie = malloc(sizeof(*cookie));
174 	if (cookie == NULL)
175 		goto release;
176 
177 	if (buf == NULL) {
178 		cookie->head = malloc(size);
179 		if (cookie->head == NULL) {
180 			free(cookie);
181 			goto release;
182 		}
183 		*cookie->head = '\0';
184 		fp->_close = &fmemopen_close1;
185 	} else {
186 		cookie->head = (char *)buf;
187 		if (oflags & O_TRUNC)
188 			*cookie->head = '\0';
189 		fp->_close = &fmemopen_close0;
190 	}
191 
192 	cookie->tail = cookie->head + size;
193 	cookie->eob  = cookie->head;
194 	do {
195 		if (*cookie->eob == '\0')
196 			break;
197 		++cookie->eob;
198 	} while (--size > 0);
199 
200 	cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head;
201 
202 	fp->pub._flags  = flags;
203 	fp->_write  = (flags & __SRD) ? NULL : &fmemopen_write;
204 	fp->_read   = (flags & __SWR) ? NULL : &fmemopen_read;
205 	fp->_seek   = &fmemopen_seek;
206 	fp->_cookie = (void *)cookie;
207 
208 	return fp;
209 
210 invalid:
211 	errno = EINVAL;
212 	return NULL;
213 
214 release:
215 	fp->pub._flags = 0;
216 	return NULL;
217 }
218 
219