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