Home | History | Annotate | Download | only in stdio
      1 /*	$OpenBSD: open_memstream.c,v 1.6 2015/08/31 02:53:57 guenther Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2011 Martin Pieuchot <mpi (at) openbsd.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <stdint.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include "local.h"
     26 
     27 #define	MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
     28 
     29 struct state {
     30 	char		 *string;	/* actual stream */
     31 	char		**pbuf;		/* point to the stream */
     32 	size_t		 *psize;	/* point to min(pos, len) */
     33 	size_t		  pos;		/* current position */
     34 	size_t		  size;		/* number of allocated char */
     35 	size_t		  len;		/* length of the data */
     36 };
     37 
     38 static int
     39 memstream_write(void *v, const char *b, int l)
     40 {
     41 	struct state	*st = v;
     42 	char		*p;
     43 	size_t		 i, end;
     44 
     45 	end = (st->pos + l);
     46 
     47 	if (end >= st->size) {
     48 		/* 1.6 is (very) close to the golden ratio. */
     49 		size_t	sz = st->size * 8 / 5;
     50 
     51 		if (sz < end + 1)
     52 			sz = end + 1;
     53 		p = realloc(st->string, sz);
     54 		if (!p)
     55 			return (-1);
     56 		bzero(p + st->size, sz - st->size);
     57 		*st->pbuf = st->string = p;
     58 		st->size = sz;
     59 	}
     60 
     61 	for (i = 0; i < l; i++)
     62 		st->string[st->pos + i] = b[i];
     63 	st->pos += l;
     64 
     65 	if (st->pos > st->len) {
     66 		st->len = st->pos;
     67 		st->string[st->len] = '\0';
     68 	}
     69 
     70 	*st->psize = st->pos;
     71 
     72 	return (i);
     73 }
     74 
     75 static fpos_t
     76 memstream_seek(void *v, fpos_t off, int whence)
     77 {
     78 	struct state	*st = v;
     79 	ssize_t		 base = 0;
     80 
     81 	switch (whence) {
     82 	case SEEK_SET:
     83 		break;
     84 	case SEEK_CUR:
     85 		base = st->pos;
     86 		break;
     87 	case SEEK_END:
     88 		base = st->len;
     89 		break;
     90 	}
     91 
     92 	if (off > SIZE_MAX - base || off < -base) {
     93 		errno = EOVERFLOW;
     94 		return (-1);
     95 	}
     96 
     97 	st->pos = base + off;
     98 	*st->psize = MINIMUM(st->pos, st->len);
     99 
    100 	return (st->pos);
    101 }
    102 
    103 static int
    104 memstream_close(void *v)
    105 {
    106 	struct state	*st = v;
    107 
    108 	free(st);
    109 
    110 	return (0);
    111 }
    112 
    113 FILE *
    114 open_memstream(char **pbuf, size_t *psize)
    115 {
    116 	struct state	*st;
    117 	FILE		*fp;
    118 
    119 	if (pbuf == NULL || psize == NULL) {
    120 		errno = EINVAL;
    121 		return (NULL);
    122 	}
    123 
    124 	if ((st = malloc(sizeof(*st))) == NULL)
    125 		return (NULL);
    126 
    127 	if ((fp = __sfp()) == NULL) {
    128 		free(st);
    129 		return (NULL);
    130 	}
    131 
    132 	st->size = BUFSIZ;
    133 	if ((st->string = calloc(1, st->size)) == NULL) {
    134 		free(st);
    135 		fp->_flags = 0;
    136 		return (NULL);
    137 	}
    138 
    139 	*st->string = '\0';
    140 	st->pos = 0;
    141 	st->len = 0;
    142 	st->pbuf = pbuf;
    143 	st->psize = psize;
    144 
    145 	*pbuf = st->string;
    146 	*psize = st->len;
    147 
    148 	fp->_flags = __SWR;
    149 	fp->_file = -1;
    150 	fp->_cookie = st;
    151 	fp->_read = NULL;
    152 	fp->_write = memstream_write;
    153 	fp->_seek = memstream_seek;
    154 	fp->_close = memstream_close;
    155 	_SET_ORIENTATION(fp, -1);
    156 
    157 	return (fp);
    158 }
    159 DEF_WEAK(open_memstream);
    160