Commit | Line | Data |
---|---|---|
395d17df VS |
1 | /* $NetBSD: fmemopen.c,v 1.4 2010/09/27 16:50:13 tnozaki Exp $ */ |
2 | ||
3 | /*- | |
4 | * Copyright (c)2007, 2010 Takehiko NOZAKI, | |
dbd7c185 | 5 | * Copyright (c) 2012, Venkatesh Srinivas <vsrinivas@dragonflybsd.org> |
395d17df | 6 | * All rights reserved. |
e5afb31f VS |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
e5afb31f VS |
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 | |
395d17df VS |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. | |
e5afb31f | 16 | * |
395d17df VS |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
e5afb31f | 27 | * SUCH DAMAGE. |
e5afb31f VS |
28 | */ |
29 | ||
dbd7c185 | 30 | #include <sys/param.h> |
395d17df VS |
31 | #include <assert.h> |
32 | #include <errno.h> | |
33 | #include <fcntl.h> | |
34 | #include <stddef.h> | |
e5afb31f VS |
35 | #include <stdio.h> |
36 | #include <stdlib.h> | |
e5afb31f | 37 | |
395d17df | 38 | #include "local.h" |
e5afb31f | 39 | |
e5afb31f | 40 | struct fmemopen_cookie { |
395d17df | 41 | char *head, *tail, *cur, *eob; |
e5afb31f VS |
42 | }; |
43 | ||
c43830c0 | 44 | static int |
395d17df | 45 | fmemopen_read(void *cookie, char *buf, int nbytes) |
e5afb31f | 46 | { |
395d17df VS |
47 | struct fmemopen_cookie *p; |
48 | char *s; | |
49 | int len; | |
c43830c0 | 50 | |
395d17df VS |
51 | assert(cookie != NULL); |
52 | assert(buf != NULL && nbytes > 0); | |
c43830c0 | 53 | |
dbd7c185 | 54 | p = cookie; |
395d17df | 55 | s = p->cur; |
dbd7c185 | 56 | len = MIN(p->tail - p->cur, nbytes); |
395d17df VS |
57 | bcopy(p->cur, buf, len); |
58 | p->cur += len; | |
c43830c0 | 59 | |
395d17df | 60 | return (int)(p->cur - s); |
e5afb31f VS |
61 | } |
62 | ||
c43830c0 | 63 | static int |
395d17df | 64 | fmemopen_write(void *cookie, const char *buf, int nbytes) |
e5afb31f | 65 | { |
395d17df VS |
66 | struct fmemopen_cookie *p; |
67 | char *s; | |
dbd7c185 | 68 | int len; |
395d17df VS |
69 | |
70 | assert(cookie != NULL); | |
71 | assert(buf != NULL && nbytes > 0); | |
72 | ||
dbd7c185 | 73 | p = cookie; |
395d17df VS |
74 | if (p->cur >= p->tail) |
75 | return 0; | |
76 | s = p->cur; | |
dbd7c185 VS |
77 | |
78 | len = MIN(p->tail - p->cur, nbytes); | |
79 | ||
80 | bcopy(buf, p->cur, len); | |
81 | ||
82 | p->cur += len - 1; | |
83 | if (p->cur == p->tail - 1) { | |
84 | *p->cur = '\0'; | |
85 | if (buf[len - 1] == '\0') | |
86 | p->cur++; | |
87 | } else { | |
88 | *++p->cur = '\0'; | |
89 | } | |
90 | ||
395d17df VS |
91 | if (p->cur > p->eob) |
92 | p->eob = p->cur; | |
93 | ||
94 | return (int)(p->cur - s); | |
e5afb31f VS |
95 | } |
96 | ||
c43830c0 | 97 | static fpos_t |
395d17df | 98 | fmemopen_seek(void *cookie, fpos_t offset, int whence) |
e5afb31f | 99 | { |
395d17df VS |
100 | struct fmemopen_cookie *p; |
101 | ||
102 | assert(cookie != NULL); | |
c43830c0 | 103 | |
395d17df VS |
104 | p = (struct fmemopen_cookie *)cookie; |
105 | switch (whence) { | |
106 | case SEEK_SET: | |
e5afb31f | 107 | break; |
395d17df VS |
108 | case SEEK_CUR: |
109 | offset += p->cur - p->head; | |
e5afb31f | 110 | break; |
395d17df VS |
111 | case SEEK_END: |
112 | offset += p->eob - p->head; | |
e5afb31f | 113 | break; |
395d17df VS |
114 | default: |
115 | errno = EINVAL; | |
116 | goto error; | |
117 | } | |
118 | if (offset >= (fpos_t)0 && offset <= p->tail - p->head) { | |
119 | p->cur = p->head + (ptrdiff_t)offset; | |
120 | return (fpos_t)(p->cur - p->head); | |
e5afb31f | 121 | } |
395d17df VS |
122 | error: |
123 | return (fpos_t)-1; | |
124 | } | |
c43830c0 | 125 | |
395d17df VS |
126 | static int |
127 | fmemopen_close0(void *cookie) | |
128 | { | |
129 | assert(cookie != NULL); | |
c43830c0 | 130 | |
395d17df | 131 | free(cookie); |
c43830c0 | 132 | |
395d17df | 133 | return 0; |
e5afb31f VS |
134 | } |
135 | ||
c43830c0 | 136 | static int |
395d17df | 137 | fmemopen_close1(void *cookie) |
e5afb31f | 138 | { |
395d17df | 139 | struct fmemopen_cookie *p; |
4ad99553 | 140 | |
395d17df | 141 | assert(cookie != NULL); |
c43830c0 | 142 | |
dbd7c185 | 143 | p = cookie; |
395d17df VS |
144 | free(p->head); |
145 | free(p); | |
c43830c0 | 146 | |
395d17df | 147 | return 0; |
e5afb31f VS |
148 | } |
149 | ||
395d17df | 150 | |
c43830c0 | 151 | FILE * |
395d17df | 152 | fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) |
e5afb31f | 153 | { |
395d17df VS |
154 | int flags, oflags; |
155 | FILE *fp; | |
156 | struct fmemopen_cookie *cookie; | |
157 | ||
158 | if (size < (size_t)1) | |
159 | goto invalid; | |
c43830c0 | 160 | |
395d17df VS |
161 | flags = __sflags(mode, &oflags); |
162 | if (flags == 0) | |
e5afb31f | 163 | return NULL; |
c43830c0 | 164 | |
395d17df VS |
165 | if ((oflags & O_RDWR) == 0 && buf == NULL) |
166 | goto invalid; | |
c43830c0 | 167 | |
395d17df VS |
168 | fp = __sfp(); |
169 | if (fp == NULL) | |
170 | return NULL; | |
171 | ||
172 | cookie = malloc(sizeof(*cookie)); | |
173 | if (cookie == NULL) | |
174 | goto release; | |
175 | ||
176 | if (buf == NULL) { | |
177 | cookie->head = malloc(size); | |
178 | if (cookie->head == NULL) { | |
179 | free(cookie); | |
180 | goto release; | |
e5afb31f | 181 | } |
395d17df VS |
182 | *cookie->head = '\0'; |
183 | fp->_close = &fmemopen_close1; | |
e5afb31f | 184 | } else { |
395d17df VS |
185 | cookie->head = (char *)buf; |
186 | if (oflags & O_TRUNC) | |
187 | *cookie->head = '\0'; | |
188 | fp->_close = &fmemopen_close0; | |
e5afb31f | 189 | } |
395d17df VS |
190 | |
191 | cookie->tail = cookie->head + size; | |
192 | cookie->eob = cookie->head; | |
193 | do { | |
194 | if (*cookie->eob == '\0') | |
195 | break; | |
196 | ++cookie->eob; | |
197 | } while (--size > 0); | |
198 | ||
199 | cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head; | |
200 | ||
201 | fp->pub._flags = flags; | |
202 | fp->_write = (flags & __SRD) ? NULL : &fmemopen_write; | |
203 | fp->_read = (flags & __SWR) ? NULL : &fmemopen_read; | |
204 | fp->_seek = &fmemopen_seek; | |
205 | fp->_cookie = (void *)cookie; | |
206 | ||
207 | return fp; | |
208 | ||
209 | invalid: | |
210 | errno = EINVAL; | |
211 | return NULL; | |
212 | ||
213 | release: | |
214 | fp->pub._flags = 0; | |
215 | return NULL; | |
e5afb31f | 216 | } |
395d17df | 217 |