xref: /openbsd/lib/libc/stdio/fmemopen.c (revision d35e2a8f)
1*d35e2a8fSmillert /*	$OpenBSD: fmemopen.c,v 1.5 2020/08/17 16:17:39 millert Exp $	*/
203222a19Smpi 
31b5be6a0Smpi /*
41b5be6a0Smpi  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
51b5be6a0Smpi  * Copyright (c) 2009 Ted Unangst
61b5be6a0Smpi  *
71b5be6a0Smpi  * Permission to use, copy, modify, and distribute this software for any
81b5be6a0Smpi  * purpose with or without fee is hereby granted, provided that the above
91b5be6a0Smpi  * copyright notice and this permission notice appear in all copies.
101b5be6a0Smpi  *
111b5be6a0Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
121b5be6a0Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
131b5be6a0Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
141b5be6a0Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
151b5be6a0Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
161b5be6a0Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
171b5be6a0Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
181b5be6a0Smpi  */
191b5be6a0Smpi 
201b5be6a0Smpi #include <errno.h>
211b5be6a0Smpi #include <fcntl.h>
221b5be6a0Smpi #include <stdio.h>
231b5be6a0Smpi #include <stdlib.h>
2403222a19Smpi #include <string.h>
251b5be6a0Smpi #include "local.h"
261b5be6a0Smpi 
271b5be6a0Smpi struct state {
281b5be6a0Smpi 	char		*string;	/* actual stream */
291b5be6a0Smpi 	size_t		 pos;		/* current position */
301b5be6a0Smpi 	size_t		 size;		/* allocated size */
311b5be6a0Smpi 	size_t		 len;		/* length of the data */
3203222a19Smpi 	int		 update;	/* open for update */
33*d35e2a8fSmillert 	int		 append;	/* open for append */
341b5be6a0Smpi };
351b5be6a0Smpi 
361b5be6a0Smpi static int
fmemopen_read(void * v,char * b,int l)371b5be6a0Smpi fmemopen_read(void *v, char *b, int l)
381b5be6a0Smpi {
391b5be6a0Smpi 	struct state	*st = v;
401b5be6a0Smpi 	int		 i;
411b5be6a0Smpi 
421b5be6a0Smpi 	for (i = 0; i < l && i + st->pos < st->len; i++)
431b5be6a0Smpi 		b[i] = st->string[st->pos + i];
441b5be6a0Smpi 	st->pos += i;
451b5be6a0Smpi 
461b5be6a0Smpi 	return (i);
471b5be6a0Smpi }
481b5be6a0Smpi 
491b5be6a0Smpi static int
fmemopen_write(void * v,const char * b,int l)501b5be6a0Smpi fmemopen_write(void *v, const char *b, int l)
511b5be6a0Smpi {
521b5be6a0Smpi 	struct state	*st = v;
531b5be6a0Smpi 	int		i;
541b5be6a0Smpi 
55*d35e2a8fSmillert 	if (st->append)
56*d35e2a8fSmillert 		st->pos = st->len;
57*d35e2a8fSmillert 
581b5be6a0Smpi 	for (i = 0; i < l && i + st->pos < st->size; i++)
591b5be6a0Smpi 		st->string[st->pos + i] = b[i];
601b5be6a0Smpi 	st->pos += i;
611b5be6a0Smpi 
621b5be6a0Smpi 	if (st->pos >= st->len) {
631b5be6a0Smpi 		st->len = st->pos;
641b5be6a0Smpi 
6503222a19Smpi 		if (st->len < st->size)
661b5be6a0Smpi 			st->string[st->len] = '\0';
6703222a19Smpi 		else if (!st->update)
6803222a19Smpi 			st->string[st->size - 1] = '\0';
691b5be6a0Smpi 	}
701b5be6a0Smpi 
711b5be6a0Smpi 	return (i);
721b5be6a0Smpi }
731b5be6a0Smpi 
741b5be6a0Smpi static fpos_t
fmemopen_seek(void * v,fpos_t off,int whence)7503222a19Smpi fmemopen_seek(void *v, fpos_t off, int whence)
761b5be6a0Smpi {
771b5be6a0Smpi 	struct state	*st = v;
7803222a19Smpi 	ssize_t		 base = 0;
791b5be6a0Smpi 
801b5be6a0Smpi 	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 
9103222a19Smpi 	if (off > st->size - base || off < -base) {
9203222a19Smpi 		errno = EOVERFLOW;
931b5be6a0Smpi 		return (-1);
941b5be6a0Smpi 	}
951b5be6a0Smpi 
9603222a19Smpi 	st->pos = base + off;
971b5be6a0Smpi 
9803222a19Smpi 	return (st->pos);
991b5be6a0Smpi }
1001b5be6a0Smpi 
1011b5be6a0Smpi static int
fmemopen_close(void * v)1021b5be6a0Smpi fmemopen_close(void *v)
1031b5be6a0Smpi {
1041b5be6a0Smpi 	free(v);
1051b5be6a0Smpi 
1061b5be6a0Smpi 	return (0);
1071b5be6a0Smpi }
1081b5be6a0Smpi 
1091b5be6a0Smpi static int
fmemopen_close_free(void * v)1101b5be6a0Smpi fmemopen_close_free(void *v)
1111b5be6a0Smpi {
1121b5be6a0Smpi 	struct state	*st = v;
1131b5be6a0Smpi 
1141b5be6a0Smpi 	free(st->string);
1151b5be6a0Smpi 	free(st);
1161b5be6a0Smpi 
1171b5be6a0Smpi 	return (0);
1181b5be6a0Smpi }
1191b5be6a0Smpi 
1201b5be6a0Smpi FILE *
fmemopen(void * buf,size_t size,const char * mode)1211b5be6a0Smpi fmemopen(void *buf, size_t size, const char *mode)
1221b5be6a0Smpi {
1231b5be6a0Smpi 	struct state	*st;
1241b5be6a0Smpi 	FILE		*fp;
1251b5be6a0Smpi 	int		 flags, oflags;
1261b5be6a0Smpi 
1271b5be6a0Smpi 	if (size == 0) {
1281b5be6a0Smpi 		errno = EINVAL;
1291b5be6a0Smpi 		return (NULL);
1301b5be6a0Smpi 	}
1311b5be6a0Smpi 
1321b5be6a0Smpi 	if ((flags = __sflags(mode, &oflags)) == 0) {
1331b5be6a0Smpi 		errno = EINVAL;
1341b5be6a0Smpi 		return (NULL);
1351b5be6a0Smpi 	}
1361b5be6a0Smpi 
1371b5be6a0Smpi 	if (buf == NULL && ((oflags & O_RDWR) == 0)) {
1381b5be6a0Smpi 		errno = EINVAL;
1391b5be6a0Smpi 		return (NULL);
1401b5be6a0Smpi 	}
1411b5be6a0Smpi 
1421b5be6a0Smpi 	if ((st = malloc(sizeof(*st))) == NULL)
1431b5be6a0Smpi 		return (NULL);
1441b5be6a0Smpi 
1451b5be6a0Smpi 	if ((fp = __sfp()) == NULL) {
1461b5be6a0Smpi 		free(st);
1471b5be6a0Smpi 		return (NULL);
1481b5be6a0Smpi 	}
1491b5be6a0Smpi 
1501b5be6a0Smpi 	st->pos = 0;
151ac593df8Smillert 	st->len = (oflags & O_TRUNC) ? 0 : size;
1521b5be6a0Smpi 	st->size = size;
15303222a19Smpi 	st->update = oflags & O_RDWR;
154*d35e2a8fSmillert 	st->append = oflags & O_APPEND;
1551b5be6a0Smpi 
1561b5be6a0Smpi 	if (buf == NULL) {
1571b5be6a0Smpi 		if ((st->string = malloc(size)) == NULL) {
1581b5be6a0Smpi 			free(st);
1591b5be6a0Smpi 			fp->_flags = 0;
1601b5be6a0Smpi 			return (NULL);
1611b5be6a0Smpi 		}
1621b5be6a0Smpi 		*st->string = '\0';
1631b5be6a0Smpi 	} else {
1641b5be6a0Smpi 		st->string = (char *)buf;
1651b5be6a0Smpi 
1661b5be6a0Smpi 		if (oflags & O_TRUNC)
1671b5be6a0Smpi 			*st->string = '\0';
1681b5be6a0Smpi 
1691b5be6a0Smpi 		if (oflags & O_APPEND) {
1701b5be6a0Smpi 			char	*p;
1711b5be6a0Smpi 
1721b5be6a0Smpi 			if ((p = memchr(st->string, '\0', size)) != NULL)
1731b5be6a0Smpi 				st->pos = st->len = (p - st->string);
1741b5be6a0Smpi 			else
1751b5be6a0Smpi 				st->pos = st->len = size;
1761b5be6a0Smpi 		}
1771b5be6a0Smpi 	}
1781b5be6a0Smpi 
1791b5be6a0Smpi 	fp->_flags = (short)flags;
1801b5be6a0Smpi 	fp->_file = -1;
181*d35e2a8fSmillert 	fp->_cookie = st;
1821b5be6a0Smpi 	fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
1831b5be6a0Smpi 	fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
1841b5be6a0Smpi 	fp->_seek = fmemopen_seek;
1851b5be6a0Smpi 	fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
1861b5be6a0Smpi 
1871b5be6a0Smpi 	return (fp);
1881b5be6a0Smpi }
1899b9d2a55Sguenther DEF_WEAK(fmemopen);
190