musl/src/stdio/fmemopen.c
Rich Felker d2e061a2bd more fmemopen null termination fixes
null termination is only added when current size grows.
in update modes, null termination is not added if it does not fit
(i.e. it is not allowed to clobber data).

these rules make very little sense, but that's how it goes..
2011-09-04 21:53:20 -04:00

116 lines
2.3 KiB
C

#include "stdio_impl.h"
struct cookie {
size_t pos, len, size;
unsigned char *buf;
int mode;
};
static off_t mseek(FILE *f, off_t off, int whence)
{
ssize_t base;
struct cookie *c = f->cookie;
if (whence>2U) {
fail:
errno = EINVAL;
return -1;
}
base = (size_t [3]){0, c->pos, c->len}[whence];
if (off < -base || off > (ssize_t)c->size-base) goto fail;
return c->pos = base+off;
}
static size_t mread(FILE *f, unsigned char *buf, size_t len)
{
struct cookie *c = f->cookie;
size_t rem = c->len - c->pos;
if (c->pos > c->len) rem = 0;
if (len > rem) {
len = rem;
f->flags |= F_EOF;
}
memcpy(buf, c->buf+c->pos, len);
c->pos += len;
rem -= len;
if (rem > f->buf_size) rem = f->buf_size;
f->rpos = f->buf;
f->rend = f->buf + rem;
memcpy(f->rpos, c->buf+c->pos, rem);
c->pos += rem;
return len;
}
static size_t mwrite(FILE *f, const unsigned char *buf, size_t len)
{
struct cookie *c = f->cookie;
size_t rem;
size_t len2 = f->wpos - f->wbase;
if (len2) {
f->wpos = f->wbase;
if (mwrite(f, f->wpos, len2) < len2) return 0;
}
if (c->mode == 'a') c->pos = c->len;
rem = c->size - c->pos;
if (len > rem) len = rem;
memcpy(c->buf+c->pos, buf, len);
c->pos += len;
if (c->pos > c->len) {
c->len = c->pos;
if (c->len < c->size) c->buf[c->len] = 0;
else if ((f->flags&F_NORD) && c->size) c->buf[c->size-1] = 0;
}
return len;
}
static int mclose(FILE *m)
{
return 0;
}
FILE *fmemopen(void *buf, size_t size, const char *mode)
{
FILE *f;
struct cookie *c;
int plus = !!strchr(mode, '+');
if (!size || !strchr("rwa", *mode)) {
errno = EINVAL;
return 0;
}
if (!buf && size > SIZE_MAX-sizeof(FILE)-BUFSIZ-UNGET) {
errno = ENOMEM;
return 0;
}
f = calloc(sizeof *f + sizeof *c + UNGET + BUFSIZ + (buf?0:size), 1);
if (!f) return 0;
f->cookie = c = (void *)(f+1);
f->fd = -1;
f->lbf = EOF;
f->buf = (unsigned char *)(c+1) + UNGET;
f->buf_size = BUFSIZ;
if (!buf) buf = f->buf + BUFSIZ;
c->buf = buf;
c->size = size;
c->mode = *mode;
if (!plus) f->flags = (*mode == 'r') ? F_NOWR : F_NORD;
if (*mode == 'r') c->len = size;
else if (*mode == 'a') c->len = c->pos = strnlen(buf, size);
f->read = mread;
f->write = mwrite;
f->seek = mseek;
f->close = mclose;
if (!libc.threaded) {
f->lock = -1;
f->next = libc.ofl_head;
if (libc.ofl_head) libc.ofl_head->prev = f;
libc.ofl_head = f;
}
return f;
}