xref: /openbsd-src/lib/libc/stdio/open_memstream.c (revision eafe02333fabde69dc8cc7692884e2b75b82e041)
1*eafe0233Sclaudio /*	$OpenBSD: open_memstream.c,v 1.10 2023/07/11 12:14:16 claudio Exp $	*/
203222a19Smpi 
31b5be6a0Smpi /*
41b5be6a0Smpi  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
51b5be6a0Smpi  *
61b5be6a0Smpi  * Permission to use, copy, modify, and distribute this software for any
71b5be6a0Smpi  * purpose with or without fee is hereby granted, provided that the above
81b5be6a0Smpi  * copyright notice and this permission notice appear in all copies.
91b5be6a0Smpi  *
101b5be6a0Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111b5be6a0Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121b5be6a0Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131b5be6a0Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141b5be6a0Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151b5be6a0Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161b5be6a0Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171b5be6a0Smpi  */
181b5be6a0Smpi 
191b5be6a0Smpi #include <errno.h>
201b5be6a0Smpi #include <fcntl.h>
211357284aSmillert #include <stdint.h>
221b5be6a0Smpi #include <stdio.h>
231b5be6a0Smpi #include <stdlib.h>
241b5be6a0Smpi #include <string.h>
251b5be6a0Smpi #include "local.h"
261b5be6a0Smpi 
27aea60beeSderaadt #define	MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
28aea60beeSderaadt 
291b5be6a0Smpi struct state {
301b5be6a0Smpi 	char		 *string;	/* actual stream */
311b5be6a0Smpi 	char		**pbuf;		/* point to the stream */
321b5be6a0Smpi 	size_t		 *psize;	/* point to min(pos, len) */
331b5be6a0Smpi 	size_t		  pos;		/* current position */
3403222a19Smpi 	size_t		  size;		/* number of allocated char */
351b5be6a0Smpi 	size_t		  len;		/* length of the data */
361b5be6a0Smpi };
371b5be6a0Smpi 
381b5be6a0Smpi static int
memstream_write(void * v,const char * b,int l)391b5be6a0Smpi memstream_write(void *v, const char *b, int l)
401b5be6a0Smpi {
411b5be6a0Smpi 	struct state	*st = v;
421b5be6a0Smpi 	char		*p;
4303222a19Smpi 	size_t		 i, end;
441b5be6a0Smpi 
4503222a19Smpi 	end = (st->pos + l);
4603222a19Smpi 
4703222a19Smpi 	if (end >= st->size) {
481b5be6a0Smpi 		/* 1.6 is (very) close to the golden ratio. */
491b5be6a0Smpi 		size_t	sz = st->size * 8 / 5;
501b5be6a0Smpi 
5103222a19Smpi 		if (sz < end + 1)
5203222a19Smpi 			sz = end + 1;
53977aa0f2Sderaadt 		p = recallocarray(st->string, st->size, sz, 1);
541b5be6a0Smpi 		if (!p)
551b5be6a0Smpi 			return (-1);
561b5be6a0Smpi 		*st->pbuf = st->string = p;
571b5be6a0Smpi 		st->size = sz;
581b5be6a0Smpi 	}
591b5be6a0Smpi 
601b5be6a0Smpi 	for (i = 0; i < l; i++)
611b5be6a0Smpi 		st->string[st->pos + i] = b[i];
6203222a19Smpi 	st->pos += l;
631b5be6a0Smpi 
641b5be6a0Smpi 	if (st->pos > st->len) {
651b5be6a0Smpi 		st->len = st->pos;
661b5be6a0Smpi 		st->string[st->len] = '\0';
671b5be6a0Smpi 	}
681b5be6a0Smpi 
691b5be6a0Smpi 	*st->psize = st->pos;
701b5be6a0Smpi 
711b5be6a0Smpi 	return (i);
721b5be6a0Smpi }
731b5be6a0Smpi 
741b5be6a0Smpi static fpos_t
memstream_seek(void * v,fpos_t off,int whence)7503222a19Smpi memstream_seek(void *v, fpos_t off, int whence)
761b5be6a0Smpi {
771b5be6a0Smpi 	struct state	*st = v;
7850054d86Syasuoka 	size_t		 base = 0;
791b5be6a0Smpi 
8003222a19Smpi 	switch (whence) {
811b5be6a0Smpi 	case SEEK_SET:
821b5be6a0Smpi 		break;
831b5be6a0Smpi 	case SEEK_CUR:
8403222a19Smpi 		base = st->pos;
851b5be6a0Smpi 		break;
861b5be6a0Smpi 	case SEEK_END:
8703222a19Smpi 		base = st->len;
881b5be6a0Smpi 		break;
8903222a19Smpi 	}
9003222a19Smpi 
9150054d86Syasuoka 	if ((off > 0 && off > SIZE_MAX - base) || (off < 0 && base < -off)) {
9203222a19Smpi 		errno = EOVERFLOW;
931b5be6a0Smpi 		return (-1);
941b5be6a0Smpi 	}
951b5be6a0Smpi 
9603222a19Smpi 	st->pos = base + off;
97aea60beeSderaadt 	*st->psize = MINIMUM(st->pos, st->len);
981b5be6a0Smpi 
9903222a19Smpi 	return (st->pos);
1001b5be6a0Smpi }
1011b5be6a0Smpi 
1021b5be6a0Smpi static int
memstream_close(void * v)1031b5be6a0Smpi memstream_close(void *v)
1041b5be6a0Smpi {
1051b5be6a0Smpi 	struct state	*st = v;
1061b5be6a0Smpi 
1071b5be6a0Smpi 	free(st);
1081b5be6a0Smpi 
1091b5be6a0Smpi 	return (0);
1101b5be6a0Smpi }
1111b5be6a0Smpi 
1121b5be6a0Smpi FILE *
open_memstream(char ** pbuf,size_t * psize)1131b5be6a0Smpi open_memstream(char **pbuf, size_t *psize)
1141b5be6a0Smpi {
1151b5be6a0Smpi 	struct state	*st;
1161b5be6a0Smpi 	FILE		*fp;
1171b5be6a0Smpi 
1181b5be6a0Smpi 	if (pbuf == NULL || psize == NULL) {
1191b5be6a0Smpi 		errno = EINVAL;
1201b5be6a0Smpi 		return (NULL);
1211b5be6a0Smpi 	}
1221b5be6a0Smpi 
1231b5be6a0Smpi 	if ((st = malloc(sizeof(*st))) == NULL)
1241b5be6a0Smpi 		return (NULL);
1251b5be6a0Smpi 
1261b5be6a0Smpi 	if ((fp = __sfp()) == NULL) {
1271b5be6a0Smpi 		free(st);
1281b5be6a0Smpi 		return (NULL);
1291b5be6a0Smpi 	}
1301b5be6a0Smpi 
13103222a19Smpi 	st->size = BUFSIZ;
1321b5be6a0Smpi 	if ((st->string = calloc(1, st->size)) == NULL) {
1331b5be6a0Smpi 		free(st);
1341b5be6a0Smpi 		fp->_flags = 0;
1351b5be6a0Smpi 		return (NULL);
1361b5be6a0Smpi 	}
1371b5be6a0Smpi 
1381b5be6a0Smpi 	st->pos = 0;
1391b5be6a0Smpi 	st->len = 0;
1401b5be6a0Smpi 	st->pbuf = pbuf;
1411b5be6a0Smpi 	st->psize = psize;
1421b5be6a0Smpi 
1431b5be6a0Smpi 	*pbuf = st->string;
1441b5be6a0Smpi 	*psize = st->len;
1451b5be6a0Smpi 
1461b5be6a0Smpi 	fp->_flags = __SWR;
1471b5be6a0Smpi 	fp->_file = -1;
1481b5be6a0Smpi 	fp->_cookie = st;
1491b5be6a0Smpi 	fp->_read = NULL;
1501b5be6a0Smpi 	fp->_write = memstream_write;
1511b5be6a0Smpi 	fp->_seek = memstream_seek;
1521b5be6a0Smpi 	fp->_close = memstream_close;
15341a2a03bSguenther 	_SET_ORIENTATION(fp, -1);
1541b5be6a0Smpi 
1551b5be6a0Smpi 	return (fp);
1561b5be6a0Smpi }
1579b9d2a55Sguenther DEF_WEAK(open_memstream);
158