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