1 /*	$OpenBSD: open_memstream.c,v 1.8 2019/05/02 08:30:10 yasuoka Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Martin Pieuchot <mpi@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
memstream_write(void * v,const char * b,int l)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 = recallocarray(st->string, st->size, sz, 1);
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
memstream_seek(void * v,fpos_t off,int whence)76 memstream_seek(void *v, fpos_t off, int whence)
77 {
78 	struct state	*st = v;
79 	size_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 > 0 && off > SIZE_MAX - base) || (off < 0 && base < -off)) {
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
memstream_close(void * v)104 memstream_close(void *v)
105 {
106 	struct state	*st = v;
107 
108 	free(st);
109 
110 	return (0);
111 }
112 
113 FILE *
open_memstream(char ** pbuf,size_t * psize)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