make fmemopen's w+ mode truncate the buffer
[musl] / src / stdio / fmemopen.c
1 #include "stdio_impl.h"
2 #include <errno.h>
3 #include <string.h>
4 #include <inttypes.h>
5
6 struct cookie {
7         size_t pos, len, size;
8         unsigned char *buf;
9         int mode;
10 };
11
12 struct mem_FILE {
13         FILE f;
14         struct cookie c;
15         unsigned char buf[UNGET+BUFSIZ], buf2[];
16 };
17
18 static off_t mseek(FILE *f, off_t off, int whence)
19 {
20         ssize_t base;
21         struct cookie *c = f->cookie;
22         if (whence>2U) {
23 fail:
24                 errno = EINVAL;
25                 return -1;
26         }
27         base = (size_t [3]){0, c->pos, c->len}[whence];
28         if (off < -base || off > (ssize_t)c->size-base) goto fail;
29         return c->pos = base+off;
30 }
31
32 static size_t mread(FILE *f, unsigned char *buf, size_t len)
33 {
34         struct cookie *c = f->cookie;
35         size_t rem = c->len - c->pos;
36         if (c->pos > c->len) rem = 0;
37         if (len > rem) {
38                 len = rem;
39                 f->flags |= F_EOF;
40         }
41         memcpy(buf, c->buf+c->pos, len);
42         c->pos += len;
43         rem -= len;
44         if (rem > f->buf_size) rem = f->buf_size;
45         f->rpos = f->buf;
46         f->rend = f->buf + rem;
47         memcpy(f->rpos, c->buf+c->pos, rem);
48         c->pos += rem;
49         return len;
50 }
51
52 static size_t mwrite(FILE *f, const unsigned char *buf, size_t len)
53 {
54         struct cookie *c = f->cookie;
55         size_t rem;
56         size_t len2 = f->wpos - f->wbase;
57         if (len2) {
58                 f->wpos = f->wbase;
59                 if (mwrite(f, f->wpos, len2) < len2) return 0;
60         }
61         if (c->mode == 'a') c->pos = c->len;
62         rem = c->size - c->pos;
63         if (len > rem) len = rem;
64         memcpy(c->buf+c->pos, buf, len);
65         c->pos += len;
66         if (c->pos > c->len) {
67                 c->len = c->pos;
68                 if (c->len < c->size) c->buf[c->len] = 0;
69                 else if ((f->flags&F_NORD) && c->size) c->buf[c->size-1] = 0;
70         }
71         return len;
72 }
73
74 static int mclose(FILE *m)
75 {
76         return 0;
77 }
78
79 FILE *fmemopen(void *restrict buf, size_t size, const char *restrict mode)
80 {
81         struct mem_FILE *f;
82         int plus = !!strchr(mode, '+');
83         
84         if (!size || !strchr("rwa", *mode)) {
85                 errno = EINVAL;
86                 return 0;
87         }
88
89         if (!buf && size > PTRDIFF_MAX) {
90                 errno = ENOMEM;
91                 return 0;
92         }
93
94         f = malloc(sizeof *f + (buf?0:size));
95         if (!f) return 0;
96         memset(&f->f, 0, sizeof f->f);
97         f->f.cookie = &f->c;
98         f->f.fd = -1;
99         f->f.lbf = EOF;
100         f->f.buf = f->buf + UNGET;
101         f->f.buf_size = sizeof f->buf - UNGET;
102         if (!buf) {
103                 buf = f->buf2;;
104                 memset(buf, 0, size);
105         }
106
107         memset(&f->c, 0, sizeof f->c);
108         f->c.buf = buf;
109         f->c.size = size;
110         f->c.mode = *mode;
111         
112         if (!plus) f->f.flags = (*mode == 'r') ? F_NOWR : F_NORD;
113         if (*mode == 'r') f->c.len = size;
114         else if (*mode == 'a') f->c.len = f->c.pos = strnlen(buf, size);
115         else if (plus) *f->c.buf = 0;
116
117         f->f.read = mread;
118         f->f.write = mwrite;
119         f->f.seek = mseek;
120         f->f.close = mclose;
121
122         if (!libc.threaded) f->f.lock = -1;
123
124         return __ofl_add(&f->f);
125 }