xref: /openbsd-src/lib/libc/stdio/fmemopen.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 /*	$OpenBSD: fmemopen.c,v 1.1 2013/01/01 17:41:13 mpi Exp $	*/
2 /*
3  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
4  * Copyright (c) 2009 Ted Unangst
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 <stdio.h>
22 #include <stdlib.h>
23 #include "local.h"
24 
25 struct state {
26 	char		*string;	/* actual stream */
27 	size_t		 pos;		/* current position */
28 	size_t		 size;		/* allocated size */
29 	size_t		 len;		/* length of the data */
30 };
31 
32 static int
33 fmemopen_read(void *v, char *b, int l)
34 {
35 	struct state	*st = v;
36 	int		 i;
37 
38 	for (i = 0; i < l && i + st->pos < st->len; i++)
39 		b[i] = st->string[st->pos + i];
40 	st->pos += i;
41 
42 	return (i);
43 }
44 
45 static int
46 fmemopen_write(void *v, const char *b, int l)
47 {
48 	struct state	*st = v;
49 	int		i;
50 
51 	for (i = 0; i < l && i + st->pos < st->size; i++)
52 		st->string[st->pos + i] = b[i];
53 	st->pos += i;
54 
55 	if (st->pos >= st->len) {
56 		st->len = st->pos;
57 
58 		if (st->len == st->size)
59 			st->string[st->len - 1] = '\0';
60 		else
61 			st->string[st->len] = '\0';
62 	}
63 
64 	return (i);
65 }
66 
67 static fpos_t
68 fmemopen_seek(void *v, fpos_t pos, int whence)
69 {
70 	struct state	*st = v;
71 
72 	switch (whence) {
73 	case SEEK_SET:
74 		break;
75 	case SEEK_CUR:
76 		pos += st->pos;
77 		break;
78 	case SEEK_END:
79 		/*
80 		 * XXX The standard is not clear about where to seek
81 		 * from the end of the data or the end of the buffer.
82 		 */
83 		pos += st->len;
84 		break;
85 	default:
86 		errno = EINVAL;
87 		return (-1);
88 	}
89 
90 	if (pos < 0 || pos > st->size)
91 		return (-1);
92 
93 	st->pos = pos;
94 
95 	return (pos);
96 }
97 
98 static int
99 fmemopen_close(void *v)
100 {
101 	free(v);
102 
103 	return (0);
104 }
105 
106 static int
107 fmemopen_close_free(void *v)
108 {
109 	struct state	*st = v;
110 
111 	free(st->string);
112 	free(st);
113 
114 	return (0);
115 }
116 
117 FILE *
118 fmemopen(void *buf, size_t size, const char *mode)
119 {
120 	struct state	*st;
121 	FILE		*fp;
122 	int		 flags, oflags;
123 
124 	if (size == 0) {
125 		errno = EINVAL;
126 		return (NULL);
127 	}
128 
129 	if ((flags = __sflags(mode, &oflags)) == 0) {
130 		errno = EINVAL;
131 		return (NULL);
132 	}
133 
134 	if (buf == NULL && ((oflags & O_RDWR) == 0)) {
135 		errno = EINVAL;
136 		return (NULL);
137 	}
138 
139 	if ((st = malloc(sizeof(*st))) == NULL)
140 		return (NULL);
141 
142 	if ((fp = __sfp()) == NULL) {
143 		free(st);
144 		return (NULL);
145 	}
146 
147 	st->pos = 0;
148 	st->len = 0;
149 	st->size = size;
150 
151 	if (buf == NULL) {
152 		if ((st->string = malloc(size)) == NULL) {
153 			free(st);
154 			fp->_flags = 0;
155 			return (NULL);
156 		}
157 		*st->string = '\0';
158 	} else {
159 		st->string = (char *)buf;
160 
161 		if ((oflags & O_WRONLY) == 0)
162 			st->len = size;
163 
164 		if (oflags & O_TRUNC)
165 			*st->string = '\0';
166 
167 		if (oflags & O_APPEND) {
168 			char	*p;
169 
170 			if ((p = memchr(st->string, '\0', size)) != NULL)
171 				st->pos = st->len = (p - st->string);
172 			else
173 				st->pos = st->len = size;
174 		}
175 	}
176 
177 	fp->_flags = (short)flags;
178 	fp->_file = -1;
179 	fp->_cookie = (void *)st;
180 	fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
181 	fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
182 	fp->_seek = fmemopen_seek;
183 	fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
184 
185 	return (fp);
186 }
187