xref: /dragonfly/lib/libc/stdio/open_memstream.c (revision cab8bf9b)
1 /*-
2  * Copyright (c) 2011 Venkatesh Srinivas,
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <errno.h>
31 
32 struct memstream_cookie {
33 	char **pub_buf;
34 	size_t *pub_size;
35 
36 	char *head;
37 	size_t pos;
38 	size_t tail;
39 };
40 
41 static void
42 sync_pub_cookie(struct memstream_cookie *cp)
43 {
44 	*cp->pub_buf = cp->head;
45 	*cp->pub_size = cp->tail;
46 }
47 
48 static int
49 memstream_writefn(void *cookie, const char *buf, int len)
50 {
51 	struct memstream_cookie *c;
52 	size_t reqsize;
53 
54 	c = cookie;
55 
56 	/* Write is contained within valid region */
57 	if (c->pos + len < c->tail) {
58 		bcopy(buf, &c->head[c->pos], len);
59 		c->pos += len;
60 		return (len);
61 	}
62 
63 	/* Write results in resizing buffer */
64 	reqsize = c->pos + len + 1;
65 	c->head = reallocf(c->head, reqsize);
66 	if (c->head == NULL) {
67 		errno = ENOMEM;
68 		return (0);
69 	}
70 
71 	bcopy(buf, &c->head[c->pos], len);
72 
73 	c->tail = c->pos + len;
74 	c->pos = c->tail;
75 	c->head[c->tail] = '\0';
76 
77 	sync_pub_cookie(c);
78 
79 	return (len);
80 }
81 
82 static fpos_t
83 memstream_seekfn(void *cookie, fpos_t pos, int whence)
84 {
85 	struct memstream_cookie *c;
86 
87 	c = cookie;
88 
89 	/* XXX: Should validate SEEK_SET and SEEK_CUR positions */
90 	/* XXX: What to do wrt SEEK_END? Is it relative to tail? to pos? */
91 
92 	switch(whence) {
93 	case (SEEK_SET):
94 		c->pos = pos;
95 		return (c->pos);
96 		break;
97 	case (SEEK_CUR):
98 		c->pos += pos;
99 		return (c->pos);
100 		break;
101 	case (SEEK_END):
102 	default:
103 		errno = EINVAL;
104 		return (fpos_t) -1;
105 	}
106 }
107 
108 static int
109 memstream_closefn(void *cookie)
110 {
111 	struct memstream_cookie *c;
112 
113 	c = cookie;
114 
115 	sync_pub_cookie(c);
116 
117 	free(c);
118 	return (0);
119 }
120 
121 FILE *
122 open_memstream(char **bufp, size_t *sizep)
123 {
124 	FILE *fp;
125 	struct memstream_cookie *c;
126 
127 	fp = NULL;
128 	if (bufp == NULL || sizep == NULL) {
129 		errno = EINVAL;
130 		goto out;
131 	}
132 
133 	c = malloc(sizeof(struct memstream_cookie));
134 	if (c == NULL) {
135 		errno = EINVAL;
136 		goto out;
137 	}
138 
139 	fp = funopen(c,
140 		     NULL,
141 		     memstream_writefn,
142 		     memstream_seekfn,
143 		     memstream_closefn
144 		    );
145 
146 	if (fp == NULL) {
147 		free(c);
148 		errno = ENOMEM;
149 		goto out;
150 	}
151 
152 	c->pub_buf = bufp;
153 	c->pub_size = sizep;
154 	c->head = NULL;
155 	c->tail = 0;
156 	c->pos = 0;
157 
158 out:
159 	return (fp);
160 }
161