| /* |
| This file was retrieved from |
| http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/lib/libc/stdio/fmemopen.c |
| on 26/04/2013 by Abel Luck for inclusion in tlsdate for the Android port. |
| */ |
| /* $NetBSD: fmemopen.c,v 1.4 2010/09/27 16:50:13 tnozaki Exp $ */ |
| |
| /*- |
| * Copyright (c)2007, 2010 Takehiko NOZAKI, |
| * Copyright (c) 2012, Venkatesh Srinivas <vsrinivas@dragonflybsd.org> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/param.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| //#include "local.h" |
| //#include "priv_stdio.h" |
| |
| int MIN(int a, int b) { |
| return a < b ? a : b; |
| } |
| |
| struct fmemopen_cookie { |
| char *head, *tail, *cur, *eob; |
| }; |
| |
| static int |
| fmemopen_read(void *cookie, char *buf, int nbytes) |
| { |
| struct fmemopen_cookie *p; |
| char *s; |
| int len; |
| |
| assert(cookie != NULL); |
| assert(buf != NULL && nbytes > 0); |
| |
| p = cookie; |
| s = p->cur; |
| len = MIN(p->tail - p->cur, nbytes); |
| bcopy(p->cur, buf, len); |
| p->cur += len; |
| |
| return (int)(p->cur - s); |
| } |
| |
| static int |
| fmemopen_write(void *cookie, const char *buf, int nbytes) |
| { |
| struct fmemopen_cookie *p; |
| char *s; |
| int len; |
| |
| assert(cookie != NULL); |
| assert(buf != NULL && nbytes > 0); |
| |
| p = cookie; |
| if (p->cur >= p->tail) |
| return 0; |
| s = p->cur; |
| |
| len = MIN(p->tail - p->cur, nbytes); |
| |
| bcopy(buf, p->cur, len); |
| |
| p->cur += len - 1; |
| if (p->cur == p->tail - 1) { |
| *p->cur = '\0'; |
| if (buf[len - 1] == '\0') |
| p->cur++; |
| } else { |
| *++p->cur = '\0'; |
| } |
| |
| if (p->cur > p->eob) |
| p->eob = p->cur; |
| |
| return (int)(p->cur - s); |
| } |
| |
| static fpos_t |
| fmemopen_seek(void *cookie, fpos_t offset, int whence) |
| { |
| struct fmemopen_cookie *p; |
| |
| assert(cookie != NULL); |
| |
| p = (struct fmemopen_cookie *)cookie; |
| switch (whence) { |
| case SEEK_SET: |
| break; |
| case SEEK_CUR: |
| offset += p->cur - p->head; |
| break; |
| case SEEK_END: |
| offset += p->eob - p->head; |
| break; |
| default: |
| errno = EINVAL; |
| goto error; |
| } |
| if (offset >= (fpos_t)0 && offset <= p->tail - p->head) { |
| p->cur = p->head + (ptrdiff_t)offset; |
| return (fpos_t)(p->cur - p->head); |
| } |
| error: |
| return (fpos_t)-1; |
| } |
| |
| static int |
| fmemopen_close0(void *cookie) |
| { |
| assert(cookie != NULL); |
| |
| free(cookie); |
| |
| return 0; |
| } |
| |
| static int |
| fmemopen_close1(void *cookie) |
| { |
| struct fmemopen_cookie *p; |
| |
| assert(cookie != NULL); |
| |
| p = cookie; |
| free(p->head); |
| free(p); |
| |
| return 0; |
| } |
| |
| |
| FILE * |
| fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) |
| { |
| int flags, oflags; |
| FILE *fp; |
| struct fmemopen_cookie *cookie; |
| |
| if (size < (size_t)1) |
| goto invalid; |
| |
| flags = __sflags(mode, &oflags); |
| if (flags == 0) |
| return NULL; |
| |
| if ((oflags & O_RDWR) == 0 && buf == NULL) |
| goto invalid; |
| |
| fp = __sfp(); |
| if (fp == NULL) |
| return NULL; |
| |
| cookie = malloc(sizeof(*cookie)); |
| if (cookie == NULL) |
| goto release; |
| |
| if (buf == NULL) { |
| cookie->head = malloc(size); |
| if (cookie->head == NULL) { |
| free(cookie); |
| goto release; |
| } |
| *cookie->head = '\0'; |
| fp->_close = &fmemopen_close1; |
| } else { |
| cookie->head = (char *)buf; |
| if (oflags & O_TRUNC) |
| *cookie->head = '\0'; |
| fp->_close = &fmemopen_close0; |
| } |
| |
| cookie->tail = cookie->head + size; |
| cookie->eob = cookie->head; |
| do { |
| if (*cookie->eob == '\0') |
| break; |
| ++cookie->eob; |
| } while (--size > 0); |
| |
| cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head; |
| |
| // fp->pub._flags = flags; |
| fp->_write = (flags & __SRD) ? NULL : &fmemopen_write; |
| fp->_read = (flags & __SWR) ? NULL : &fmemopen_read; |
| fp->_seek = &fmemopen_seek; |
| fp->_cookie = (void *)cookie; |
| |
| return fp; |
| |
| invalid: |
| errno = EINVAL; |
| return NULL; |
| |
| release: |
| //fp->pub._flags = 0; |
| return NULL; |
| } |
| |
| |