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