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