xref: /dragonfly/lib/libc/stdio/fmemopen.c (revision 9b6c3ce0)
1 /*
2  * Copyright (c) 2011 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Venkatesh Srinivas <me@endeavour.zapto.org>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * ----------------------------------------------------------------------------
36  * "THE BEER-WARE LICENSE" (Revision 42):
37  * <hiten@uk.FreeBSD.ORG> wrote this file.  As long as you retain this notice
38  * you can do whatever you want with this stuff. If we meet some day, and you
39  * think this stuff is worth it, you can buy me a beer in return. Hiten Pandya.
40  * ----------------------------------------------------------------------------
41  *
42  * $FreeBSD: src/sys/dev/md/md.c,v 1.8.2.2 2002/08/19 17:43:34 jdp Exp $
43  */
44 
45 /*
46  * fmemopen -- Open a memory buffer stream
47  *
48  * POSIX 1003.1-2008
49  */
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sys/types.h>
55 #include <errno.h>
56 
57 static int __fmemopen_closefn (void *);
58 static int __fmemopen_readfn(void *, char *, int);
59 static fpos_t __fmemopen_seekfn (void *, fpos_t, int);
60 static int __fmemopen_writefn(void *, const char *, int);
61 
62 struct fmemopen_cookie {
63 	char *buffer;
64 	int mybuffer;
65 	size_t size;
66 	size_t pos;
67 	size_t maxpos;
68 };
69 
70 static int
71 __fmemopen_readfn(void *cookie, char *buf, int len)
72 {
73 	struct fmemopen_cookie *c;
74 
75 	c = (struct fmemopen_cookie *) cookie;
76 	if (c == NULL) {
77 		errno = EBADF;
78 		return (-1);
79 	}
80 
81 	if ((c->pos + len) > c->size) {
82 		if (c->pos == c->size)
83 			return -1;
84 		len = c->size - c->pos;
85 	}
86 
87 	memcpy(buf, &(c->buffer[c->pos]), len);
88 
89 	c->pos += len;
90 
91 	if (c->pos > c->maxpos)
92 		c->maxpos = c->pos;
93 
94 	return (len);
95 }
96 
97 static int
98 __fmemopen_writefn (void *cookie, const char *buf, int len)
99 {
100 	struct fmemopen_cookie *c;
101 	int addnullc;
102 
103 	c = (struct fmemopen_cookie *) cookie;
104 	if (c == NULL) {
105 		errno = EBADF;
106 		return (-1);
107 	}
108 
109 	addnullc = ((len == 0) || (buf[len - 1] != '\0')) ? 1 : 0;
110 
111 	if ((c->pos + len + addnullc) > c->size) {
112 		if ((c->pos + addnullc) == c->size)
113 			return -1;
114 		len = c->size - c->pos - addnullc;
115 	}
116 
117 	memcpy(&(c->buffer[c->pos]), buf, len);
118 
119 	c->pos += len;
120 	if (c->pos > c->maxpos) {
121 		c->maxpos = c->pos;
122 		if (addnullc)
123 			c->buffer[c->maxpos] = '\0';
124 	}
125 
126 	return (len);
127 }
128 
129 static fpos_t
130 __fmemopen_seekfn(void *cookie, fpos_t pos, int whence)
131 {
132 	fpos_t np = 0;
133 	struct fmemopen_cookie *c;
134 
135 	c = (struct fmemopen_cookie *) cookie;
136 
137 	switch(whence) {
138 	case (SEEK_SET):
139 		np = pos;
140 		break;
141 	case (SEEK_CUR):
142 		np = c->pos + pos;
143 		break;
144 	case (SEEK_END):
145 		np = c->size - pos;
146 		break;
147 	}
148 
149 	if ((np < 0) || (np > c->size))
150 		return (-1);
151 
152 	c->pos = np;
153 
154 	return (np);
155 }
156 
157 static int
158 __fmemopen_closefn (void *cookie)
159 {
160 	struct fmemopen_cookie *c;
161 
162 	c = (struct fmemopen_cookie*) cookie;
163 
164 	if (c->mybuffer)
165 		free(c->buffer);
166 	free(c);
167 
168 	return (0);
169 }
170 
171 FILE *
172 fmemopen(void *restrict buffer, size_t s, const char *restrict mode)
173 {
174 	FILE *f = NULL;
175 	struct fmemopen_cookie *c;
176 
177 	c = malloc(sizeof (struct fmemopen_cookie));
178 	if (c == NULL)
179 		return NULL;
180 
181 	c->mybuffer = (buffer == NULL);
182 
183 	if (c->mybuffer) {
184 		c->buffer = malloc(s);
185 		if (c->buffer == NULL) {
186 			free(c);
187 			return NULL;
188 		}
189 		c->buffer[0] = '\0';
190 	} else {
191 		c->buffer = buffer;
192 	}
193 	c->size = s;
194 	if (mode[0] == 'w')
195 		c->buffer[0] = '\0';
196 	c->maxpos = strlen(c->buffer);
197 
198 	if (mode[0] == 'a')
199 		c->pos = c->maxpos;
200 	else
201 		c->pos = 0;
202 
203 	f = funopen(c,
204 		    __fmemopen_readfn, /* string stream read */
205 		    __fmemopen_writefn, /* string stream write */
206 		    __fmemopen_seekfn, /* string stream seek */
207 		    __fmemopen_closefn /* string stream close */
208 		    );
209 
210 	if (f == NULL)
211 		free(c);
212 
213 	return (f);
214 }
215