xref: /openbsd-src/lib/libc/stdio/open_memstream.c (revision 48950c12d106c85f315112191a0228d7b83b9510)
1 /*	$OpenBSD: open_memstream.c,v 1.1 2013/01/01 17:41:13 mpi Exp $	*/
2 /*
3  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "local.h"
24 
25 struct state {
26 	char		 *string;	/* actual stream */
27 	char		**pbuf;		/* point to the stream */
28 	size_t		 *psize;	/* point to min(pos, len) */
29 	size_t		  pos;		/* current position */
30 	size_t		  size;		/* allocated size */
31 	size_t		  len;		/* length of the data */
32 };
33 
34 static int
35 memstream_write(void *v, const char *b, int l)
36 {
37 	struct state	*st = v;
38 	int		 i;
39 	char		*p;
40 
41 	if (st->pos + l >= st->size) {
42 		/* 1.6 is (very) close to the golden ratio. */
43 		size_t	sz = st->size * 8 / 5;
44 
45 		if (sz < st->pos + l + 1)
46 			sz = st->pos + l + 1;
47 		p = realloc(st->string, sz);
48 		if (!p)
49 			return (-1);
50 		bzero(p + st->size, sz - st->size);
51 		*st->pbuf = st->string = p;
52 		st->size = sz;
53 	}
54 
55 	for (i = 0; i < l; i++)
56 		st->string[st->pos + i] = b[i];
57 	st->pos += i;
58 
59 	if (st->pos > st->len) {
60 		st->len = st->pos;
61 		st->string[st->len] = '\0';
62 	}
63 
64 	*st->psize = st->pos;
65 
66 	return (i);
67 }
68 
69 static fpos_t
70 memstream_seek(void *v, fpos_t pos, int w)
71 {
72 	struct state	*st = v;
73 	char		*p;
74 
75 	switch (w) {
76 	case SEEK_SET:
77 		break;
78 	case SEEK_CUR:
79 		pos += st->pos;
80 		break;
81 	case SEEK_END:
82 		pos += st->len;
83 		break;
84 	default:
85 		errno = EINVAL;
86 		return (-1);
87 	}
88 
89 	if (pos < 0)
90 		return (-1);
91 
92 	st->pos = pos;
93 
94 	if (st->pos < st->len)
95 		*st->psize = st->pos;
96 	else
97 		*st->psize = st->len;
98 
99 	return (pos);
100 }
101 
102 static int
103 memstream_close(void *v)
104 {
105 	struct state	*st = v;
106 	char		*p;
107 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
108 
109 	*st->psize = MIN(st->pos, st->len);
110 	*st->pbuf = st->string;
111 
112 	free(st);
113 
114 	return (0);
115 #undef MIN
116 }
117 
118 FILE *
119 open_memstream(char **pbuf, size_t *psize)
120 {
121 	struct state	*st;
122 	FILE		*fp;
123 
124 	if (pbuf == NULL || psize == NULL) {
125 		errno = EINVAL;
126 		return (NULL);
127 	}
128 
129 	if ((st = malloc(sizeof(*st))) == NULL)
130 		return (NULL);
131 
132 	if ((fp = __sfp()) == NULL) {
133 		free(st);
134 		return (NULL);
135 	}
136 
137 	if (*psize < 128)
138 		st->size = 128;
139 	else
140 		st->size = *psize;
141 
142 	if ((st->string = calloc(1, st->size)) == NULL) {
143 		free(st);
144 		fp->_flags = 0;
145 		return (NULL);
146 	}
147 
148 	*st->string = '\0';
149 	st->pos = 0;
150 	st->len = 0;
151 	st->pbuf = pbuf;
152 	st->psize = psize;
153 
154 	*pbuf = st->string;
155 	*psize = st->len;
156 
157 	fp->_flags = __SWR;
158 	fp->_file = -1;
159 	fp->_cookie = st;
160 	fp->_read = NULL;
161 	fp->_write = memstream_write;
162 	fp->_seek = memstream_seek;
163 	fp->_close = memstream_close;
164 
165 	return (fp);
166 }
167