1 /* $OpenBSD: open_memstream.c,v 1.10 2023/07/11 12:14:16 claudio 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 *st->pbuf = st->string = p;
57 st->size = sz;
58 }
59
60 for (i = 0; i < l; i++)
61 st->string[st->pos + i] = b[i];
62 st->pos += l;
63
64 if (st->pos > st->len) {
65 st->len = st->pos;
66 st->string[st->len] = '\0';
67 }
68
69 *st->psize = st->pos;
70
71 return (i);
72 }
73
74 static fpos_t
memstream_seek(void * v,fpos_t off,int whence)75 memstream_seek(void *v, fpos_t off, int whence)
76 {
77 struct state *st = v;
78 size_t base = 0;
79
80 switch (whence) {
81 case SEEK_SET:
82 break;
83 case SEEK_CUR:
84 base = st->pos;
85 break;
86 case SEEK_END:
87 base = st->len;
88 break;
89 }
90
91 if ((off > 0 && off > SIZE_MAX - base) || (off < 0 && base < -off)) {
92 errno = EOVERFLOW;
93 return (-1);
94 }
95
96 st->pos = base + off;
97 *st->psize = MINIMUM(st->pos, st->len);
98
99 return (st->pos);
100 }
101
102 static int
memstream_close(void * v)103 memstream_close(void *v)
104 {
105 struct state *st = v;
106
107 free(st);
108
109 return (0);
110 }
111
112 FILE *
open_memstream(char ** pbuf,size_t * psize)113 open_memstream(char **pbuf, size_t *psize)
114 {
115 struct state *st;
116 FILE *fp;
117
118 if (pbuf == NULL || psize == NULL) {
119 errno = EINVAL;
120 return (NULL);
121 }
122
123 if ((st = malloc(sizeof(*st))) == NULL)
124 return (NULL);
125
126 if ((fp = __sfp()) == NULL) {
127 free(st);
128 return (NULL);
129 }
130
131 st->size = BUFSIZ;
132 if ((st->string = calloc(1, st->size)) == NULL) {
133 free(st);
134 fp->_flags = 0;
135 return (NULL);
136 }
137
138 st->pos = 0;
139 st->len = 0;
140 st->pbuf = pbuf;
141 st->psize = psize;
142
143 *pbuf = st->string;
144 *psize = st->len;
145
146 fp->_flags = __SWR;
147 fp->_file = -1;
148 fp->_cookie = st;
149 fp->_read = NULL;
150 fp->_write = memstream_write;
151 fp->_seek = memstream_seek;
152 fp->_close = memstream_close;
153 _SET_ORIENTATION(fp, -1);
154
155 return (fp);
156 }
157 DEF_WEAK(open_memstream);
158