1 /* $OpenBSD: fmemopen.c,v 1.2 2013/03/27 15:06:25 mpi 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 };
34
35 static int
fmemopen_read(void * v,char * b,int l)36 fmemopen_read(void *v, char *b, int l)
37 {
38 struct state *st = v;
39 int i;
40
41 for (i = 0; i < l && i + st->pos < st->len; i++)
42 b[i] = st->string[st->pos + i];
43 st->pos += i;
44
45 return (i);
46 }
47
48 static int
fmemopen_write(void * v,const char * b,int l)49 fmemopen_write(void *v, const char *b, int l)
50 {
51 struct state *st = v;
52 int i;
53
54 for (i = 0; i < l && i + st->pos < st->size; i++)
55 st->string[st->pos + i] = b[i];
56 st->pos += i;
57
58 if (st->pos >= st->len) {
59 st->len = st->pos;
60
61 if (st->len < st->size)
62 st->string[st->len] = '\0';
63 else if (!st->update)
64 st->string[st->size - 1] = '\0';
65 }
66
67 return (i);
68 }
69
70 static fpos_t
fmemopen_seek(void * v,fpos_t off,int whence)71 fmemopen_seek(void *v, fpos_t off, int whence)
72 {
73 struct state *st = v;
74 ssize_t base = 0;
75
76 switch (whence) {
77 case SEEK_SET:
78 break;
79 case SEEK_CUR:
80 base = st->pos;
81 break;
82 case SEEK_END:
83 base = st->len;
84 break;
85 }
86
87 if (off > st->size - base || off < -base) {
88 errno = EOVERFLOW;
89 return (-1);
90 }
91
92 st->pos = base + off;
93
94 return (st->pos);
95 }
96
97 static int
fmemopen_close(void * v)98 fmemopen_close(void *v)
99 {
100 free(v);
101
102 return (0);
103 }
104
105 static int
fmemopen_close_free(void * v)106 fmemopen_close_free(void *v)
107 {
108 struct state *st = v;
109
110 free(st->string);
111 free(st);
112
113 return (0);
114 }
115
116 FILE *
fmemopen(void * buf,size_t size,const char * mode)117 fmemopen(void *buf, size_t size, const char *mode)
118 {
119 struct state *st;
120 FILE *fp;
121 int flags, oflags;
122
123 if (size == 0) {
124 errno = EINVAL;
125 return (NULL);
126 }
127
128 if ((flags = __sflags(mode, &oflags)) == 0) {
129 errno = EINVAL;
130 return (NULL);
131 }
132
133 if (buf == NULL && ((oflags & O_RDWR) == 0)) {
134 errno = EINVAL;
135 return (NULL);
136 }
137
138 if ((st = malloc(sizeof(*st))) == NULL)
139 return (NULL);
140
141 if ((fp = __sfp()) == NULL) {
142 free(st);
143 return (NULL);
144 }
145
146 st->pos = 0;
147 st->len = (oflags & O_WRONLY) ? 0 : size;
148 st->size = size;
149 st->update = oflags & O_RDWR;
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_TRUNC)
162 *st->string = '\0';
163
164 if (oflags & O_APPEND) {
165 char *p;
166
167 if ((p = memchr(st->string, '\0', size)) != NULL)
168 st->pos = st->len = (p - st->string);
169 else
170 st->pos = st->len = size;
171 }
172 }
173
174 fp->_flags = (short)flags;
175 fp->_file = -1;
176 fp->_cookie = (void *)st;
177 fp->_read = (flags & __SWR) ? NULL : fmemopen_read;
178 fp->_write = (flags & __SRD) ? NULL : fmemopen_write;
179 fp->_seek = fmemopen_seek;
180 fp->_close = (buf == NULL) ? fmemopen_close_free : fmemopen_close;
181
182 return (fp);
183 }
184