xref: /freebsd/lib/libc/stdio/fmemopen.c (revision 96c95412cafecdd39aaf1539ce712e4a24975c52)
1*96c95412SPietro Cerutti /*-
2*96c95412SPietro Cerutti Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org>
3*96c95412SPietro Cerutti 
4*96c95412SPietro Cerutti Redistribution and use in source and binary forms, with or without
5*96c95412SPietro Cerutti modification, are permitted provided that the following conditions
6*96c95412SPietro Cerutti are met:
7*96c95412SPietro Cerutti 1. Redistributions of source code must retain the above copyright
8*96c95412SPietro Cerutti    notice, this list of conditions and the following disclaimer.
9*96c95412SPietro Cerutti 2. Redistributions in binary form must reproduce the above copyright
10*96c95412SPietro Cerutti    notice, this list of conditions and the following disclaimer in the
11*96c95412SPietro Cerutti    documentation and/or other materials provided with the distribution.
12*96c95412SPietro Cerutti 
13*96c95412SPietro Cerutti THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*96c95412SPietro Cerutti ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*96c95412SPietro Cerutti IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*96c95412SPietro Cerutti ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17*96c95412SPietro Cerutti FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*96c95412SPietro Cerutti DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*96c95412SPietro Cerutti OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*96c95412SPietro Cerutti HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*96c95412SPietro Cerutti LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*96c95412SPietro Cerutti OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*96c95412SPietro Cerutti SUCH DAMAGE.
24*96c95412SPietro Cerutti */
25*96c95412SPietro Cerutti 
26*96c95412SPietro Cerutti #include <sys/cdefs.h>
27*96c95412SPietro Cerutti __FBSDID("$FreeBSD$");
28*96c95412SPietro Cerutti 
29*96c95412SPietro Cerutti #include <stdio.h>
30*96c95412SPietro Cerutti #include <stdlib.h>
31*96c95412SPietro Cerutti #include <string.h>
32*96c95412SPietro Cerutti #include <errno.h>
33*96c95412SPietro Cerutti 
34*96c95412SPietro Cerutti struct __fmemopen_cookie
35*96c95412SPietro Cerutti {
36*96c95412SPietro Cerutti 	char *buf;	/* pointer to the memory region */
37*96c95412SPietro Cerutti 	char  own;	/* did we allocate the buffer ourselves? */
38*96c95412SPietro Cerutti 	long  len;	/* buffer length in bytes */
39*96c95412SPietro Cerutti 	long  off;	/* current offset into the buffer */
40*96c95412SPietro Cerutti };
41*96c95412SPietro Cerutti 
42*96c95412SPietro Cerutti static int	fmemopen_read  (void *cookie, char *buf, int nbytes);
43*96c95412SPietro Cerutti static int	fmemopen_write (void *cookie, const char *buf, int nbytes);
44*96c95412SPietro Cerutti static fpos_t	fmemopen_seek  (void *cookie, fpos_t offset, int whence);
45*96c95412SPietro Cerutti static int	fmemopen_close (void *cookie);
46*96c95412SPietro Cerutti 
47*96c95412SPietro Cerutti FILE *
48*96c95412SPietro Cerutti fmemopen (void * __restrict buf, size_t size, const char * __restrict mode)
49*96c95412SPietro Cerutti {
50*96c95412SPietro Cerutti 	/* allocate cookie */
51*96c95412SPietro Cerutti 	struct __fmemopen_cookie *ck = malloc (sizeof (struct __fmemopen_cookie));
52*96c95412SPietro Cerutti 	if (ck == NULL) {
53*96c95412SPietro Cerutti 		errno = ENOMEM;
54*96c95412SPietro Cerutti 		return (NULL);
55*96c95412SPietro Cerutti 	}
56*96c95412SPietro Cerutti 
57*96c95412SPietro Cerutti 	ck->off = 0;
58*96c95412SPietro Cerutti 	ck->len = size;
59*96c95412SPietro Cerutti 
60*96c95412SPietro Cerutti 	/* do we have to allocate the buffer ourselves? */
61*96c95412SPietro Cerutti 	ck->own = ((ck->buf = buf) == NULL);
62*96c95412SPietro Cerutti 	if (ck->own) {
63*96c95412SPietro Cerutti 		ck->buf = malloc (size);
64*96c95412SPietro Cerutti 		if (ck->buf == NULL) {
65*96c95412SPietro Cerutti 			free (ck);
66*96c95412SPietro Cerutti 			errno = ENOMEM;
67*96c95412SPietro Cerutti 			return (NULL);
68*96c95412SPietro Cerutti 		}
69*96c95412SPietro Cerutti 		ck->buf[0] = '\0';
70*96c95412SPietro Cerutti 	}
71*96c95412SPietro Cerutti 
72*96c95412SPietro Cerutti 	if (mode[0] == 'a')
73*96c95412SPietro Cerutti 		ck->off = strnlen(ck->buf, ck->len);
74*96c95412SPietro Cerutti 
75*96c95412SPietro Cerutti 	/* actuall wrapper */
76*96c95412SPietro Cerutti 	FILE *f = funopen ((void *)ck, fmemopen_read, fmemopen_write,
77*96c95412SPietro Cerutti 	    fmemopen_seek, fmemopen_close);
78*96c95412SPietro Cerutti 
79*96c95412SPietro Cerutti 	if (f == NULL) {
80*96c95412SPietro Cerutti 		if (ck->own)
81*96c95412SPietro Cerutti 			free (ck->buf);
82*96c95412SPietro Cerutti 		free (ck);
83*96c95412SPietro Cerutti 		return (NULL);
84*96c95412SPietro Cerutti 	}
85*96c95412SPietro Cerutti 
86*96c95412SPietro Cerutti 	/* turn off buffering, so a write past the end of the buffer
87*96c95412SPietro Cerutti 	 * correctly returns a short object count */
88*96c95412SPietro Cerutti 	setvbuf (f, (char *) NULL, _IONBF, 0);
89*96c95412SPietro Cerutti 
90*96c95412SPietro Cerutti 	return (f);
91*96c95412SPietro Cerutti }
92*96c95412SPietro Cerutti 
93*96c95412SPietro Cerutti static int
94*96c95412SPietro Cerutti fmemopen_read (void *cookie, char *buf, int nbytes)
95*96c95412SPietro Cerutti {
96*96c95412SPietro Cerutti 	struct __fmemopen_cookie *ck = cookie;
97*96c95412SPietro Cerutti 
98*96c95412SPietro Cerutti 	if (nbytes > ck->len - ck->off)
99*96c95412SPietro Cerutti 		nbytes = ck->len - ck->off;
100*96c95412SPietro Cerutti 
101*96c95412SPietro Cerutti 	if (nbytes == 0)
102*96c95412SPietro Cerutti 		return (0);
103*96c95412SPietro Cerutti 
104*96c95412SPietro Cerutti 	memcpy (buf, ck->buf + ck->off, nbytes);
105*96c95412SPietro Cerutti 
106*96c95412SPietro Cerutti 	ck->off += nbytes;
107*96c95412SPietro Cerutti 
108*96c95412SPietro Cerutti 	return (nbytes);
109*96c95412SPietro Cerutti }
110*96c95412SPietro Cerutti 
111*96c95412SPietro Cerutti static int
112*96c95412SPietro Cerutti fmemopen_write (void *cookie, const char *buf, int nbytes)
113*96c95412SPietro Cerutti {
114*96c95412SPietro Cerutti 	struct __fmemopen_cookie *ck = cookie;
115*96c95412SPietro Cerutti 
116*96c95412SPietro Cerutti 	if (nbytes > ck->len - ck->off)
117*96c95412SPietro Cerutti 		nbytes = ck->len - ck->off;
118*96c95412SPietro Cerutti 
119*96c95412SPietro Cerutti 	if (nbytes == 0)
120*96c95412SPietro Cerutti 		return (0);
121*96c95412SPietro Cerutti 
122*96c95412SPietro Cerutti 	memcpy (ck->buf + ck->off, buf, nbytes);
123*96c95412SPietro Cerutti 
124*96c95412SPietro Cerutti 	ck->off += nbytes;
125*96c95412SPietro Cerutti 
126*96c95412SPietro Cerutti 	if (ck->off < ck->len && ck->buf[ck->off - 1] != '\0')
127*96c95412SPietro Cerutti 		ck->buf[ck->off] = '\0';
128*96c95412SPietro Cerutti 
129*96c95412SPietro Cerutti 	return (nbytes);
130*96c95412SPietro Cerutti }
131*96c95412SPietro Cerutti 
132*96c95412SPietro Cerutti static fpos_t
133*96c95412SPietro Cerutti fmemopen_seek (void *cookie, fpos_t offset, int whence)
134*96c95412SPietro Cerutti {
135*96c95412SPietro Cerutti 	struct __fmemopen_cookie *ck = cookie;
136*96c95412SPietro Cerutti 
137*96c95412SPietro Cerutti 
138*96c95412SPietro Cerutti 	switch (whence) {
139*96c95412SPietro Cerutti 	case SEEK_SET:
140*96c95412SPietro Cerutti 		if (offset > ck->len) {
141*96c95412SPietro Cerutti 			errno = EINVAL;
142*96c95412SPietro Cerutti 			return (-1);
143*96c95412SPietro Cerutti 		}
144*96c95412SPietro Cerutti 		ck->off = offset;
145*96c95412SPietro Cerutti 		break;
146*96c95412SPietro Cerutti 
147*96c95412SPietro Cerutti 	case SEEK_CUR:
148*96c95412SPietro Cerutti 		if (ck->off + offset > ck->len) {
149*96c95412SPietro Cerutti 			errno = EINVAL;
150*96c95412SPietro Cerutti 			return (-1);
151*96c95412SPietro Cerutti 		}
152*96c95412SPietro Cerutti 		ck->off += offset;
153*96c95412SPietro Cerutti 		break;
154*96c95412SPietro Cerutti 
155*96c95412SPietro Cerutti 	case SEEK_END:
156*96c95412SPietro Cerutti 		if (offset > 0 || -offset > ck->len) {
157*96c95412SPietro Cerutti 			errno = EINVAL;
158*96c95412SPietro Cerutti 			return (-1);
159*96c95412SPietro Cerutti 		}
160*96c95412SPietro Cerutti 		ck->off = ck->len + offset;
161*96c95412SPietro Cerutti 		break;
162*96c95412SPietro Cerutti 
163*96c95412SPietro Cerutti 	default:
164*96c95412SPietro Cerutti 		errno = EINVAL;
165*96c95412SPietro Cerutti 		return (-1);
166*96c95412SPietro Cerutti 	}
167*96c95412SPietro Cerutti 
168*96c95412SPietro Cerutti 	return (ck->off);
169*96c95412SPietro Cerutti }
170*96c95412SPietro Cerutti 
171*96c95412SPietro Cerutti static int
172*96c95412SPietro Cerutti fmemopen_close (void *cookie)
173*96c95412SPietro Cerutti {
174*96c95412SPietro Cerutti 	struct __fmemopen_cookie *ck = cookie;
175*96c95412SPietro Cerutti 
176*96c95412SPietro Cerutti 	if (ck->own)
177*96c95412SPietro Cerutti 		free (ck->buf);
178*96c95412SPietro Cerutti 
179*96c95412SPietro Cerutti 	free (ck);
180*96c95412SPietro Cerutti 
181*96c95412SPietro Cerutti 	return (0);
182*96c95412SPietro Cerutti }
183