Merge branch 'vendor/TCPDUMP'
[dragonfly.git] / lib / libc / stdio / fmemopen.c
1 /* $NetBSD: fmemopen.c,v 1.4 2010/09/27 16:50:13 tnozaki Exp $ */
2
3 /*-
4  * Copyright (c)2007, 2010 Takehiko NOZAKI,
5  * Copyright (c) 2012, Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
6  * All rights reserved.
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:
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
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/param.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include "local.h"
39 #include "priv_stdio.h"
40
41 struct fmemopen_cookie {
42         char *head, *tail, *cur, *eob;
43 };
44
45 static int
46 fmemopen_read(void *cookie, char *buf, int nbytes)
47 {
48         struct fmemopen_cookie *p;
49         char *s;
50         int len;
51
52         assert(cookie != NULL);
53         assert(buf != NULL && nbytes > 0);
54
55         p = cookie;
56         s = p->cur;
57         len = MIN(p->tail - p->cur, nbytes);
58         bcopy(p->cur, buf, len);
59         p->cur += len;
60
61         return (int)(p->cur - s);
62 }
63
64 static int
65 fmemopen_write(void *cookie, const char *buf, int nbytes)
66 {
67         struct fmemopen_cookie *p;
68         char *s;
69         int len;
70
71         assert(cookie != NULL);
72         assert(buf != NULL && nbytes > 0);
73
74         p = cookie;
75         if (p->cur >= p->tail)
76                 return 0;
77         s = p->cur;
78         
79         len = MIN(p->tail - p->cur, nbytes);
80
81         bcopy(buf, p->cur, len);
82         
83         p->cur += len - 1;
84         if (p->cur == p->tail - 1) {
85                 *p->cur = '\0';
86                 if (buf[len - 1] == '\0')
87                         p->cur++;
88         } else {
89                 *++p->cur = '\0';
90         }
91         
92         if (p->cur > p->eob)
93                 p->eob = p->cur;
94
95         return (int)(p->cur - s);
96 }
97
98 static fpos_t
99 fmemopen_seek(void *cookie, fpos_t offset, int whence)
100 {
101         struct fmemopen_cookie *p;
102  
103         assert(cookie != NULL);
104
105         p = (struct fmemopen_cookie *)cookie;
106         switch (whence) {
107         case SEEK_SET:
108                 break;
109         case SEEK_CUR:
110                 offset += p->cur - p->head;
111                 break;
112         case SEEK_END:
113                 offset += p->eob - p->head;
114                 break;
115         default:
116                 errno = EINVAL;
117                 goto error;
118         }
119         if (offset >= (fpos_t)0 && offset <= p->tail - p->head) {
120                 p->cur = p->head + (ptrdiff_t)offset;
121                 return (fpos_t)(p->cur - p->head);
122         }
123 error:
124         return (fpos_t)-1;
125 }
126
127 static int
128 fmemopen_close0(void *cookie)
129 {
130         assert(cookie != NULL);
131
132         free(cookie);
133
134         return 0;
135 }
136
137 static int
138 fmemopen_close1(void *cookie)
139 {
140         struct fmemopen_cookie *p;
141
142         assert(cookie != NULL);
143
144         p = cookie;
145         free(p->head);
146         free(p);
147
148         return 0;
149 }
150
151
152 FILE *
153 fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
154 {
155         int flags, oflags;
156         FILE *fp;
157         struct fmemopen_cookie *cookie;
158
159         if (size < (size_t)1)
160                 goto invalid;
161
162         flags = __sflags(mode, &oflags);
163         if (flags == 0)
164                 return NULL;
165
166         if ((oflags & O_RDWR) == 0 && buf == NULL)
167                 goto invalid;
168
169         fp = __sfp();
170         if (fp == NULL)
171                 return NULL;
172
173         cookie = malloc(sizeof(*cookie));
174         if (cookie == NULL)
175                 goto release;
176
177         if (buf == NULL) {
178                 cookie->head = malloc(size);
179                 if (cookie->head == NULL) {
180                         free(cookie);
181                         goto release;
182                 }
183                 *cookie->head = '\0';
184                 fp->_close = &fmemopen_close1;
185         } else {
186                 cookie->head = (char *)buf;
187                 if (oflags & O_TRUNC)
188                         *cookie->head = '\0';
189                 fp->_close = &fmemopen_close0;
190         }
191
192         cookie->tail = cookie->head + size;
193         cookie->eob  = cookie->head;
194         do {
195                 if (*cookie->eob == '\0')
196                         break;
197                 ++cookie->eob;
198         } while (--size > 0);
199
200         cookie->cur = (oflags & O_APPEND) ? cookie->eob : cookie->head;
201
202         fp->pub._flags  = flags;
203         fp->_write  = (flags & __SRD) ? NULL : &fmemopen_write;
204         fp->_read   = (flags & __SWR) ? NULL : &fmemopen_read;
205         fp->_seek   = &fmemopen_seek;
206         fp->_cookie = (void *)cookie;
207
208         return fp;
209
210 invalid:
211         errno = EINVAL;
212         return NULL;
213
214 release:
215         fp->pub._flags = 0;
216         return NULL;
217 }
218