libc -- Initial fmemopen support.
[dragonfly.git] / lib / libc / stdio / fmemopen.c
1 /*
2  * Copyright (c) 2011 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Venkatesh Srinivas <me@endeavour.zapto.org>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * ----------------------------------------------------------------------------
36  * "THE BEER-WARE LICENSE" (Revision 42):
37  * <hiten@uk.FreeBSD.ORG> wrote this file.  As long as you retain this notice
38  * you can do whatever you want with this stuff. If we meet some day, and you
39  * think this stuff is worth it, you can buy me a beer in return. Hiten Pandya. 
40  * ----------------------------------------------------------------------------
41  *
42  * $FreeBSD: src/sys/dev/md/md.c,v 1.8.2.2 2002/08/19 17:43:34 jdp Exp $
43  */
44
45 /*
46  * fmemopen -- Open a memory buffer stream
47  *
48  * POSIX 1003.1-2008
49  */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sys/types.h>
55 #include <errno.h>
56
57 static int __fmemopen_closefn (void *);
58 static int __fmemopen_readfn(void *, char *, int);
59 static fpos_t __fmemopen_seekfn (void *, fpos_t, int);
60 static int __fmemopen_writefn(void *, const char *, int);
61
62 FILE *fmemopen (void *, size_t, const char *);
63
64 struct fmemopen_cookie {
65         char *buffer;
66         int mybuffer;
67         size_t size;
68         size_t pos;
69         size_t maxpos;
70 };
71
72 static int __fmemopen_readfn(void *cookie, char *buf, int len)
73 {
74         struct fmemopen_cookie *c;
75         c = (struct fmemopen_cookie *) cookie;
76         
77         if (c == NULL) {
78                 errno = EBADF;
79                 return (-1);
80         }
81         
82         if ((c->pos + len) > c->size) {
83                 if (c->pos == c->size) 
84                         return -1;
85                 len = c->size - c->pos;
86         }
87         
88         memcpy(buf, &(c->buffer[c->pos]), len);
89         
90         c->pos += len;
91         
92         if (c->pos > c->maxpos) 
93                 c->maxpos = c->pos;
94         
95         return (len);
96 }
97
98 static int __fmemopen_writefn (void *cookie, const char *buf, int len)
99 {
100         struct fmemopen_cookie *c;
101         int addnullc;
102         
103         c = (struct fmemopen_cookie *) cookie;
104         if (c == NULL) {
105                 errno = EBADF;
106                 return (-1);
107         }
108         
109         addnullc = ((len == 0) || (buf[len - 1] != '\0') ) ? 1 : 0;
110         
111         if ((c->pos + len + addnullc) > c->size) {
112                 if ((c->pos + addnullc) == c->size) 
113                         return -1;
114                 len = c->size - c->pos - addnullc;
115         }
116         
117         memcpy(&(c->buffer[c->pos]), buf, len);
118         
119         c->pos += len;
120         if (c->pos > c->maxpos) {
121                 c->maxpos = c->pos;
122                 if (addnullc) 
123                         c->buffer[c->maxpos] = '\0';
124         }
125         
126         return (len);
127 }
128
129 static fpos_t __fmemopen_seekfn(void *cookie, fpos_t pos, int whence)
130 {
131         fpos_t np = 0;
132         struct fmemopen_cookie *c;
133         
134         c = (struct fmemopen_cookie *) cookie;
135         
136         switch(whence) {
137         case (SEEK_SET): 
138                 np = pos; 
139                 break;
140         case (SEEK_CUR): 
141                 np = c->pos + pos; 
142                 break;
143         case (SEEK_END): 
144                 np = c->size - pos; 
145                 break;
146         }
147         
148         if ((np < 0) || (np > c->size)) 
149                 return (-1);
150         
151         c->pos = np;
152         
153         return (np);
154 }
155
156  
157 static int __fmemopen_closefn (void *cookie)
158 {
159         struct fmemopen_cookie *c;
160         c = (struct fmemopen_cookie*) cookie;
161         
162         if (c->mybuffer) 
163                 free(c->buffer);
164         free(c);
165         
166         return (0);
167 }
168
169 FILE *fmemopen(void *restrict buffer, size_t s, const char *restrict mode)
170 {
171         FILE *f = NULL;
172         struct fmemopen_cookie *c;
173         c = malloc(sizeof (struct fmemopen_cookie));
174         
175         if (c == NULL)
176                 return NULL;
177         
178         c->mybuffer = (buffer == NULL);
179         
180         if (c->mybuffer) {
181                 c->buffer = malloc(s);
182                 if (c->buffer == NULL) {
183                         free(c);
184                         return NULL;
185                 }
186                 c->buffer[0] = '\0';
187         } else {
188                 c->buffer = buffer;
189         }
190         c->size = s;
191         if (mode[0] == 'w')
192                 c->buffer[0] = '\0';
193         c->maxpos = strlen(c->buffer);
194         
195         if (mode[0] == 'a')
196                 c->pos = c->maxpos;
197         else
198                 c->pos = 0;
199         
200         f = funopen(c,
201                     __fmemopen_readfn, /* string stream read */
202                     __fmemopen_writefn, /* string stream write */
203                     __fmemopen_seekfn, /* string stream seek */
204                     __fmemopen_closefn /* string stream close */
205                     );
206         
207         if (f == NULL)
208                 free(c);
209         
210         return (f);
211 }