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