Home | History | Annotate | Download | only in stdio
      1 /*	$OpenBSD: open_wmemstream.c,v 1.8 2015/09/12 16:23:14 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 <stdio.h>
     22 #include <stdint.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <wchar.h>
     26 #include "local.h"
     27 
     28 #define	MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
     29 
     30 struct state {
     31 	wchar_t		 *string;	/* actual stream */
     32 	wchar_t		**pbuf;		/* point to the stream */
     33 	size_t		 *psize;	/* point to min(pos, len) */
     34 	size_t		  pos;		/* current position */
     35 	size_t		  size;		/* number of allocated wchar_t */
     36 	size_t		  len;		/* length of the data */
     37 	mbstate_t	  mbs;		/* conversion state of the stream */
     38 };
     39 
     40 static int
     41 wmemstream_write(void *v, const char *b, int l)
     42 {
     43 	struct state	*st = v;
     44 	wchar_t		*p;
     45 	size_t		 nmc, len, end;
     46 
     47 	end = (st->pos + l);
     48 
     49 	if (end >= st->size) {
     50 		/* 1.6 is (very) close to the golden ratio. */
     51 		size_t	sz = st->size * 8 / 5;
     52 
     53 		if (sz < end + 1)
     54 			sz = end + 1;
     55 		p = reallocarray(st->string, sz, sizeof(wchar_t));
     56 		if (!p)
     57 			return (-1);
     58 		bzero(p + st->size, (sz - st->size) * sizeof(wchar_t));
     59 		*st->pbuf = st->string = p;
     60 		st->size = sz;
     61 	}
     62 
     63 	nmc = (st->size - st->pos) * sizeof(wchar_t);
     64 	len = mbsnrtowcs(st->string + st->pos, &b, nmc, l, &st->mbs);
     65 	if (len == (size_t)-1)
     66 		return (-1);
     67 	st->pos += len;
     68 
     69 	if (st->pos > st->len) {
     70 		st->len = st->pos;
     71 		st->string[st->len] = L'\0';
     72 	}
     73 
     74 	*st->psize = st->pos;
     75 
     76 	return (len);
     77 }
     78 
     79 static fpos_t
     80 wmemstream_seek(void *v, fpos_t off, int whence)
     81 {
     82 	struct state	*st = v;
     83 	ssize_t		 base = 0;
     84 
     85 	switch (whence) {
     86 	case SEEK_SET:
     87 		break;
     88 	case SEEK_CUR:
     89 		base = st->pos;
     90 		break;
     91 	case SEEK_END:
     92 		base = st->len;
     93 		break;
     94 	}
     95 
     96 	if (off > (SIZE_MAX / sizeof(wchar_t)) - base || off < -base) {
     97 		errno = EOVERFLOW;
     98 		return (-1);
     99 	}
    100 
    101 	/*
    102 	 * XXX Clearing mbs here invalidates shift state for state-
    103 	 * dependent encodings, but they are not (yet) supported.
    104 	 */
    105 	bzero(&st->mbs, sizeof(st->mbs));
    106 
    107 	st->pos = base + off;
    108 	*st->psize = MINIMUM(st->pos, st->len);
    109 
    110 	return (st->pos);
    111 }
    112 
    113 static int
    114 wmemstream_close(void *v)
    115 {
    116 	struct state	*st = v;
    117 
    118 	free(st);
    119 
    120 	return (0);
    121 }
    122 
    123 FILE *
    124 open_wmemstream(wchar_t **pbuf, size_t *psize)
    125 {
    126 	struct state	*st;
    127 	FILE		*fp;
    128 
    129 	if (pbuf == NULL || psize == NULL) {
    130 		errno = EINVAL;
    131 		return (NULL);
    132 	}
    133 
    134 	if ((st = malloc(sizeof(*st))) == NULL)
    135 		return (NULL);
    136 
    137 	if ((fp = __sfp()) == NULL) {
    138 		free(st);
    139 		return (NULL);
    140 	}
    141 
    142 	st->size = BUFSIZ * sizeof(wchar_t);
    143 	if ((st->string = calloc(1, st->size)) == NULL) {
    144 		free(st);
    145 		fp->_flags = 0;
    146 		return (NULL);
    147 	}
    148 
    149 	*st->string = L'\0';
    150 	st->pos = 0;
    151 	st->len = 0;
    152 	st->pbuf = pbuf;
    153 	st->psize = psize;
    154 	bzero(&st->mbs, sizeof(st->mbs));
    155 
    156 	*pbuf = st->string;
    157 	*psize = st->len;
    158 
    159 	fp->_flags = __SWR;
    160 	fp->_file = -1;
    161 	fp->_cookie = st;
    162 	fp->_read = NULL;
    163 	fp->_write = wmemstream_write;
    164 	fp->_seek = wmemstream_seek;
    165 	fp->_close = wmemstream_close;
    166 	_SET_ORIENTATION(fp, 1);
    167 
    168 	return (fp);
    169 }
    170