xref: /openbsd/lib/libc/stdio/fmemopen.c (revision d35e2a8f)
1 /*	$OpenBSD: fmemopen.c,v 1.5 2020/08/17 16:17:39 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
5  * Copyright (c) 2009 Ted Unangst
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "local.h"
26 
27 struct state {
28 	char		*string;	/* actual stream */
29 	size_t		 pos;		/* current position */
30 	size_t		 size;		/* allocated size */
31 	size_t		 len;		/* length of the data */
32 	int		 update;	/* open for update */
33 	int		 append;	/* open for append */
34 };
35 
36 static int
fmemopen_read(void * v,char * b,int l)37 fmemopen_read(void *v, char *b, int l)
38 {
39 	struct state	*st = v;
40 	int		 i;
41 
42 	for (i = 0; i < l && i + st->pos < st->len; i++)
43 		b[i] = st->string[st->pos + i];
44 	st->pos += i;
45 
46 	return (i);
47 }
48 
49 static int
fmemopen_write(void * v,const char * b,int l)50 fmemopen_write(void *v, const char *b, int l)
51 {
52 	struct state	*st = v;
53 	int		i;
54 
55 	if (st->append)
56 		st->pos = st->len;
57 
58 	for (i = 0; i < l && i + st->pos < st->size; i++)
59 		st->string[st->pos + i] = b[i];
60 	st->pos += i;
61 
62 	if (st->pos >= st->len) {
63 		st->len = st->pos;
64 
65 		if (st->len < st->size)
66 			st->string[st->len] = '\0';
67 		else if (!st->update)
68 			st->string[st->size - 1] = '\0';
69 	}
70 
71 	return (i);
72 }
73 
74 static fpos_t
fmemopen_seek(void * v,fpos_t off,int whence)75 fmemopen_seek(void *v, fpos_t off, int whence)
76 {
77 	struct state	*st = v;
78 	ssize_t		 base = 0;
79 
80 	switch (whence) {
81 	case SEEK_SET:
82 		break;
83 	case SEEK_CUR:
84 		base = st->pos;
85 		break;
86 	case SEEK_END:
87 		base = st->len;
88 		break;
89 	}
90 
91 	if (off > st->size - base || off < -base) {
92 		errno = EOVERFLOW;
93 		return (-1);
94 	}
95 
96 	st->pos = base + off;
97 
98 	return (st->pos);
99 }
100 
101 static int
fmemopen_close(void * v)102 fmemopen_close(void *v)
103 {
104 	free(v);
105 
106 	return (0);
107 }
108 
109 static int
fmemopen_close_free(void * v)110 fmemopen_close_free(void *v)
111 {
112 	struct state	*st = v;
113 
114 	free(st->string);
115 	free(st);
116 
117 	return (0);
118 }
119 
120 FILE *
fmemopen(void * buf,size_t size,const char * mode)121 fmemopen(void *buf, size_t size, const char *mode)
122 {
123 	struct state	*st;
124 	FILE		*fp;
125 	int		 flags, oflags;
126 
127 	if (size == 0) {
128 		errno = EINVAL;
129 		return (NULL);
130 	}
131 
132 	if ((flags = __sflags(mode, &oflags)) == 0) {
133 		errno = EINVAL;
134 		return (NULL);
135 	}
136 
137 	if (buf == NULL && ((oflags & O_RDWR) == 0)) {
138 		errno = EINVAL;
139 		return (NULL);
140 	}
141 
142 	if ((st = malloc(sizeof(*st))) == NULL)
143 		return (NULL);
144 
145 	if ((fp = __sfp()) == NULL) {
146 		free(st);
147 		return (NULL);
148 	}
149 
150 	st->pos = 0;
151 	st->len = (oflags & O_TRUNC) ? 0 : size;
152 	st->size = size;
153 	st->update = oflags & O_RDWR;
154 	st->append = oflags & O_APPEND;
155 
156 	if (buf == NULL) {
157 		if ((st->string = malloc(size)) == NULL) {
158 			free(st);
159 			fp->_flags = 0;
160 			return (NULL);
161 		}
162 		*st->string = '\0';
163 	} else {
164 		st->string = (char *)buf;
165 
166 		if (oflags & O_TRUNC)
167 			*st->string = '\0';
168 
169 		if (oflags & O_APPEND) {
170 			char	*p;
171 
172 			if ((p = memchr(st->string, '\0', size)) != NULL)
173 				st->pos = st->len = (p - st->string);
174 			else
175 				st->pos = st->len = size;
176 		}
177 	}
178 
179 	fp->_flags = (short)flags;
180 	fp->_file = -1;
181 	fp->_cookie = st;
182 	fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
183 	fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
184 	fp->_seek = fmemopen_seek;
185 	fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
186 
187 	return (fp);
188 }
189 DEF_WEAK(fmemopen);
190