fmemopen(): Some more cleanup.
[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 struct fmemopen_cookie {
63         char *buffer;
64         int mybuffer;
65         size_t size;
66         size_t pos;
67         size_t maxpos;
68 };
69
70 static int
71 __fmemopen_readfn(void *cookie, char *buf, int len)
72 {
73         struct fmemopen_cookie *c;
74
75         c = (struct fmemopen_cookie *) cookie;
76         if (c == NULL) {
77                 errno = EBADF;
78                 return (-1);
79         }
80
81         if ((c->pos + len) > c->size) {
82                 if (c->pos == c->size)
83                         return -1;
84                 len = c->size - c->pos;
85         }
86
87         memcpy(buf, &(c->buffer[c->pos]), len);
88
89         c->pos += len;
90
91         if (c->pos > c->maxpos)
92                 c->maxpos = c->pos;
93
94         return (len);
95 }
96
97 static int
98 __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
130 __fmemopen_seekfn(void *cookie, fpos_t pos, int whence)
131 {
132         fpos_t np = 0;
133         struct fmemopen_cookie *c;
134
135         c = (struct fmemopen_cookie *) cookie;
136
137         switch(whence) {
138         case (SEEK_SET):
139                 np = pos;
140                 break;
141         case (SEEK_CUR):
142                 np = c->pos + pos;
143                 break;
144         case (SEEK_END):
145                 np = c->size - pos;
146                 break;
147         }
148
149         if ((np < 0) || (np > c->size))
150                 return (-1);
151
152         c->pos = np;
153
154         return (np);
155 }
156
157 static int
158 __fmemopen_closefn (void *cookie)
159 {
160         struct fmemopen_cookie *c;
161
162         c = (struct fmemopen_cookie*) cookie;
163
164         if (c->mybuffer)
165                 free(c->buffer);
166         free(c);
167
168         return (0);
169 }
170
171 FILE *
172 fmemopen(void *restrict buffer, size_t s, const char *restrict mode)
173 {
174         FILE *f = NULL;
175         struct fmemopen_cookie *c;
176
177         c = malloc(sizeof (struct fmemopen_cookie));
178         if (c == NULL)
179                 return NULL;
180
181         c->mybuffer = (buffer == NULL);
182
183         if (c->mybuffer) {
184                 c->buffer = malloc(s);
185                 if (c->buffer == NULL) {
186                         free(c);
187                         return NULL;
188                 }
189                 c->buffer[0] = '\0';
190         } else {
191                 c->buffer = buffer;
192         }
193         c->size = s;
194         if (mode[0] == 'w')
195                 c->buffer[0] = '\0';
196         c->maxpos = strlen(c->buffer);
197
198         if (mode[0] == 'a')
199                 c->pos = c->maxpos;
200         else
201                 c->pos = 0;
202
203         f = funopen(c,
204                     __fmemopen_readfn, /* string stream read */
205                     __fmemopen_writefn, /* string stream write */
206                     __fmemopen_seekfn, /* string stream seek */
207                     __fmemopen_closefn /* string stream close */
208                     );
209
210         if (f == NULL)
211                 free(c);
212
213         return (f);
214 }