xref: /netbsd-src/lib/libc/stdio/fmemopen.c (revision b66595809f8c1acc286376f8c6f4d1fce8bac08b)
1*b6659580Schristos /* $NetBSD: fmemopen.c,v 1.8 2012/03/29 14:27:33 christos Exp $ */
293b7363cStnozaki 
393b7363cStnozaki /*-
493b7363cStnozaki  * Copyright (c)2007, 2010 Takehiko NOZAKI,
593b7363cStnozaki  * All rights reserved.
693b7363cStnozaki  *
793b7363cStnozaki  * Redistribution and use in source and binary forms, with or without
893b7363cStnozaki  * modification, are permitted provided that the following conditions
993b7363cStnozaki  * are met:
1093b7363cStnozaki  * 1. Redistributions of source code must retain the above copyright
1193b7363cStnozaki  *    notice, this list of conditions and the following disclaimer.
1293b7363cStnozaki  * 2. Redistributions in binary form must reproduce the above copyright
1393b7363cStnozaki  *    notice, this list of conditions and the following disclaimer in the
1493b7363cStnozaki  *    documentation and/or other materials provided with the distribution.
1593b7363cStnozaki  *
1693b7363cStnozaki  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1793b7363cStnozaki  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1893b7363cStnozaki  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1993b7363cStnozaki  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2093b7363cStnozaki  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2193b7363cStnozaki  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2293b7363cStnozaki  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2393b7363cStnozaki  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2493b7363cStnozaki  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2593b7363cStnozaki  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2693b7363cStnozaki  * SUCH DAMAGE.
2793b7363cStnozaki  *
2893b7363cStnozaki  */
2993b7363cStnozaki 
3093b7363cStnozaki #include <sys/cdefs.h>
3193b7363cStnozaki #if defined(LIBC_SCCS) && !defined(lint)
32*b6659580Schristos __RCSID("$NetBSD: fmemopen.c,v 1.8 2012/03/29 14:27:33 christos Exp $");
3393b7363cStnozaki #endif /* LIBC_SCCS and not lint */
3493b7363cStnozaki 
3593b7363cStnozaki #include <assert.h>
3693b7363cStnozaki #include <errno.h>
3793b7363cStnozaki #include <fcntl.h>
38ac440cccStron #include <stddef.h>
3993b7363cStnozaki #include <stdio.h>
4093b7363cStnozaki #include <stdlib.h>
4193b7363cStnozaki 
4293b7363cStnozaki #include "reentrant.h"
4393b7363cStnozaki #include "local.h"
4493b7363cStnozaki 
4593b7363cStnozaki struct fmemopen_cookie {
4693b7363cStnozaki 	char *head, *tail, *cur, *eob;
4793b7363cStnozaki };
4893b7363cStnozaki 
49de001ba2Schristos static ssize_t
fmemopen_read(void * cookie,void * buf,size_t nbytes)50de001ba2Schristos fmemopen_read(void *cookie, void *buf, size_t nbytes)
5193b7363cStnozaki {
5293b7363cStnozaki 	struct fmemopen_cookie *p;
53de001ba2Schristos 	char *s, *b = buf;
5493b7363cStnozaki 
5593b7363cStnozaki 	_DIAGASSERT(cookie != NULL);
5693b7363cStnozaki 	_DIAGASSERT(buf != NULL && nbytes > 0);
5793b7363cStnozaki 
5893b7363cStnozaki 	p = (struct fmemopen_cookie *)cookie;
5993b7363cStnozaki 	s = p->cur;
6093b7363cStnozaki 	do {
6193b7363cStnozaki 		if (p->cur == p->tail)
6293b7363cStnozaki 			break;
63de001ba2Schristos 		*b++ = *p->cur++;
6493b7363cStnozaki 	} while (--nbytes > 0);
6593b7363cStnozaki 
66de001ba2Schristos 	return (ssize_t)(p->cur - s);
6793b7363cStnozaki }
6893b7363cStnozaki 
69de001ba2Schristos static ssize_t
fmemopen_write(void * cookie,const void * buf,size_t nbytes)70de001ba2Schristos fmemopen_write(void *cookie, const void *buf, size_t nbytes)
7193b7363cStnozaki {
7293b7363cStnozaki 	struct fmemopen_cookie *p;
736d102a32Stnozaki 	char *s;
74de001ba2Schristos 	const char *b = buf;
7593b7363cStnozaki 
7693b7363cStnozaki 	_DIAGASSERT(cookie != NULL);
7793b7363cStnozaki 	_DIAGASSERT(buf != NULL && nbytes > 0);
7893b7363cStnozaki 
7993b7363cStnozaki 	p = (struct fmemopen_cookie *)cookie;
8093b7363cStnozaki 	if (p->cur >= p->tail)
8193b7363cStnozaki 		return 0;
8293b7363cStnozaki 	s = p->cur;
8393b7363cStnozaki 	do {
846d102a32Stnozaki 		if (p->cur == p->tail - 1) {
85de001ba2Schristos 			if (*b == '\0') {
866d102a32Stnozaki 				*p->cur++ = '\0';
876d102a32Stnozaki 				goto ok;
886d102a32Stnozaki 			}
8993b7363cStnozaki 			break;
9093b7363cStnozaki 		}
91de001ba2Schristos 		*p->cur++ = *b++;
9293b7363cStnozaki 	} while (--nbytes > 0);
9393b7363cStnozaki 	*p->cur = '\0';
946d102a32Stnozaki ok:
9593b7363cStnozaki 	if (p->cur > p->eob)
9693b7363cStnozaki 		p->eob = p->cur;
9793b7363cStnozaki 
98de001ba2Schristos 	return (ssize_t)(p->cur - s);
99de001ba2Schristos }
100de001ba2Schristos 
101*b6659580Schristos #ifdef notyet
102de001ba2Schristos static int
fmemopen_flush(void * cookie)103de001ba2Schristos fmemopen_flush(void *cookie)
104de001ba2Schristos {
105de001ba2Schristos 	struct fmemopen_cookie *p;
106de001ba2Schristos 
107de001ba2Schristos 	_DIAGASSERT(cookie != NULL);
108de001ba2Schristos 
109de001ba2Schristos 	p = (struct fmemopen_cookie *)cookie;
110de001ba2Schristos 	if (p->cur >= p->tail)
111de001ba2Schristos 		return -1;
112de001ba2Schristos 	*p->cur = '\0';
113de001ba2Schristos 	return 0;
11493b7363cStnozaki }
115*b6659580Schristos #endif
11693b7363cStnozaki 
1171897181aSchristos static off_t
fmemopen_seek(void * cookie,off_t offset,int whence)1181897181aSchristos fmemopen_seek(void *cookie, off_t offset, int whence)
11993b7363cStnozaki {
12093b7363cStnozaki 	struct fmemopen_cookie *p;
12193b7363cStnozaki 
12293b7363cStnozaki 	_DIAGASSERT(cookie != NULL);
12393b7363cStnozaki 
12493b7363cStnozaki 	p = (struct fmemopen_cookie *)cookie;
12593b7363cStnozaki 	switch (whence) {
12693b7363cStnozaki 	case SEEK_SET:
12793b7363cStnozaki 		break;
12893b7363cStnozaki 	case SEEK_CUR:
12993b7363cStnozaki 		offset += p->cur - p->head;
13093b7363cStnozaki 		break;
13193b7363cStnozaki 	case SEEK_END:
13293b7363cStnozaki 		offset += p->eob - p->head;
13393b7363cStnozaki 		break;
13493b7363cStnozaki 	default:
13593b7363cStnozaki 		errno = EINVAL;
13693b7363cStnozaki 		goto error;
13793b7363cStnozaki 	}
1381897181aSchristos 	if (offset >= (off_t)0 && offset <= p->tail - p->head) {
139ac440cccStron 		p->cur = p->head + (ptrdiff_t)offset;
1401897181aSchristos 		return (off_t)(p->cur - p->head);
14193b7363cStnozaki 	}
14293b7363cStnozaki error:
1431897181aSchristos 	return (off_t)-1;
14493b7363cStnozaki }
14593b7363cStnozaki 
14693b7363cStnozaki static int
fmemopen_close0(void * cookie)14793b7363cStnozaki fmemopen_close0(void *cookie)
14893b7363cStnozaki {
14993b7363cStnozaki 	_DIAGASSERT(cookie != NULL);
15093b7363cStnozaki 
15193b7363cStnozaki 	free(cookie);
15293b7363cStnozaki 
15393b7363cStnozaki 	return 0;
15493b7363cStnozaki }
15593b7363cStnozaki 
15693b7363cStnozaki static int
fmemopen_close1(void * cookie)15793b7363cStnozaki fmemopen_close1(void *cookie)
15893b7363cStnozaki {
15993b7363cStnozaki 	struct fmemopen_cookie *p;
16093b7363cStnozaki 
16193b7363cStnozaki 	_DIAGASSERT(cookie != NULL);
16293b7363cStnozaki 
16393b7363cStnozaki 	p = (struct fmemopen_cookie *)cookie;
16493b7363cStnozaki 	free(p->head);
16593b7363cStnozaki 	free(p);
16693b7363cStnozaki 
16793b7363cStnozaki 	return 0;
16893b7363cStnozaki }
16993b7363cStnozaki 
17093b7363cStnozaki 
17193b7363cStnozaki FILE *
fmemopen(void * __restrict buf,size_t size,const char * __restrict mode)17293b7363cStnozaki fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
17393b7363cStnozaki {
1749ef74dfbStnozaki 	int flags, oflags;
17593b7363cStnozaki 	FILE *fp;
17693b7363cStnozaki 	struct fmemopen_cookie *cookie;
17793b7363cStnozaki 
1789ef74dfbStnozaki 	if (size < (size_t)1)
1799ef74dfbStnozaki 		goto invalid;
1809ef74dfbStnozaki 
1819ef74dfbStnozaki 	flags = __sflags(mode, &oflags);
1829ef74dfbStnozaki 	if (flags == 0)
18393b7363cStnozaki 		return NULL;
1849ef74dfbStnozaki 
1859ef74dfbStnozaki 	if ((oflags & O_RDWR) == 0 && buf == NULL)
1869ef74dfbStnozaki 		goto invalid;
18793b7363cStnozaki 
18893b7363cStnozaki 	fp = __sfp();
18993b7363cStnozaki 	if (fp == NULL)
19093b7363cStnozaki 		return NULL;
19193b7363cStnozaki 	fp->_file = -1;
19293b7363cStnozaki 
19393b7363cStnozaki 	cookie = malloc(sizeof(*cookie));
19493b7363cStnozaki 	if (cookie == NULL)
19593b7363cStnozaki 		goto release;
19693b7363cStnozaki 
19793b7363cStnozaki 	if (buf == NULL) {
19893b7363cStnozaki 		cookie->head = malloc(size);
19993b7363cStnozaki 		if (cookie->head == NULL) {
20093b7363cStnozaki 			free(cookie);
20193b7363cStnozaki 			goto release;
20293b7363cStnozaki 		}
20393b7363cStnozaki 		*cookie->head = '\0';
204de001ba2Schristos 		fp->_close = fmemopen_close1;
20593b7363cStnozaki 	} else {
20693b7363cStnozaki 		cookie->head = (char *)buf;
20793b7363cStnozaki 		if (oflags & O_TRUNC)
20893b7363cStnozaki 			*cookie->head = '\0';
209de001ba2Schristos 		fp->_close = fmemopen_close0;
21093b7363cStnozaki 	}
21193b7363cStnozaki 
21293b7363cStnozaki 	cookie->tail = cookie->head + size;
21393b7363cStnozaki 	cookie->eob  = cookie->head;
21493b7363cStnozaki 	do {
21593b7363cStnozaki 		if (*cookie->eob == '\0')
21693b7363cStnozaki 			break;
21793b7363cStnozaki 		++cookie->eob;
21893b7363cStnozaki 	} while (--size > 0);
21993b7363cStnozaki 
22093b7363cStnozaki 	cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head;
22193b7363cStnozaki 
2229ef74dfbStnozaki 	fp->_flags  = flags;
223de001ba2Schristos 	fp->_write  = (flags & __SRD) ? NULL : fmemopen_write;
224de001ba2Schristos 	fp->_read   = (flags & __SWR) ? NULL : fmemopen_read;
225de001ba2Schristos 	fp->_seek   = fmemopen_seek;
226*b6659580Schristos #ifdef notyet
227de001ba2Schristos 	fp->_flush  = fmemopen_flush;
228*b6659580Schristos #endif
22993b7363cStnozaki 	fp->_cookie = (void *)cookie;
23093b7363cStnozaki 
23193b7363cStnozaki 	return fp;
23293b7363cStnozaki 
2339ef74dfbStnozaki invalid:
2349ef74dfbStnozaki 	errno = EINVAL;
2359ef74dfbStnozaki 	return NULL;
2369ef74dfbStnozaki 
23793b7363cStnozaki release:
23893b7363cStnozaki 	fp->_flags = 0;
23993b7363cStnozaki 	return NULL;
24093b7363cStnozaki }
241