picrin/xfile.c

446 lines
6.7 KiB
C

#include "picrin/xfile.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define min(a,b) (((a)>(b))?(b):(a))
#define max(a,b) (((a)<(b))?(b):(a))
#define XF_EOF 1
#define XF_ERR 2
xFILE *
xfunopen(void *cookie, int (*read)(void *, char *, int), int (*write)(void *, const char *, int), long (*seek)(void *, long, int), int (*flush)(void *), int (*close)(void *))
{
xFILE *file;
file = (xFILE *)malloc(sizeof(xFILE));
if (! file) {
return NULL;
}
file->ungot = -1;
file->flags = 0;
/* set vtable */
file->vtable.cookie = cookie;
file->vtable.read = read;
file->vtable.write = write;
file->vtable.seek = seek;
file->vtable.flush = flush;
file->vtable.close = close;
return file;
}
xFILE *
xfopen(const char *filename, const char *mode)
{
FILE *fp;
xFILE *file;
fp = fopen(filename, mode);
if (! fp) {
return NULL;
}
file = xfpopen(fp);
if (! file) {
return NULL;
}
return file;
}
int
xfclose(xFILE *file)
{
int r;
r = file->vtable.close(file->vtable.cookie);
if (r == EOF) {
return -1;
}
free(file);
return 0;
}
int
xfflush(xFILE *file)
{
return file->vtable.flush(file->vtable.cookie);
}
size_t
xfread(void *ptr, size_t block, size_t nitems, xFILE *file)
{
char *dst = (char *)ptr;
char buf[block];
size_t i, offset;
int n;
for (i = 0; i < nitems; ++i) {
offset = 0;
if (file->ungot != -1 && block > 0) {
buf[0] = file->ungot;
offset += 1;
file->ungot = -1;
}
while (offset < block) {
n = file->vtable.read(file->vtable.cookie, buf + offset, block - offset);
if (n < 0) {
file->flags |= XF_ERR;
goto exit;
}
if (n == 0) {
file->flags |= XF_EOF;
goto exit;
}
offset += n;
}
memcpy(dst, buf, block);
dst += block;
}
exit:
return i;
}
size_t
xfwrite(const void *ptr, size_t block, size_t nitems, xFILE *file)
{
char *dst = (char *)ptr;
size_t i, offset;
int n;
for (i = 0; i < nitems; ++i) {
offset = 0;
while (offset < block) {
n = file->vtable.write(file->vtable.cookie, dst + offset, block - offset);
if (n < 0) {
file->flags |= XF_ERR;
goto exit;
}
offset += n;
}
dst += block;
}
exit:
return i;
}
long
xfseek(xFILE *file, long offset, int whence)
{
file->ungot = -1;
return file->vtable.seek(file->vtable.cookie, offset, whence);
}
long
xftell(xFILE *file)
{
return xfseek(file, 0, SEEK_CUR);
}
void
xrewind(xFILE *file)
{
xfseek(file, 0, SEEK_SET);
}
void
xclearerr(xFILE *file)
{
file->flags = 0;
}
int
xfeof(xFILE *file)
{
return file->flags & XF_EOF;
}
int
xferror(xFILE *file)
{
return file->flags & XF_ERR;
}
int
xfgetc(xFILE *file)
{
char buf[1];
xfread(buf, 1, 1, file);
if (xfeof(file)) {
return EOF;
}
return buf[0];
}
int
xungetc(int c, xFILE *file)
{
file->ungot = c;
if (c != EOF) {
file->flags &= ~XF_EOF;
}
return c;
}
int
xgetchar(void)
{
return xfgetc(xstdin);
}
int
xfputc(int c, xFILE *file)
{
char buf[1];
buf[0] = c;
xfwrite(buf, 1, 1, file);
return buf[0];
}
int
xputchar(int c)
{
return xfputc(c, xstdout);
}
int
xfputs(const char *str, xFILE *file)
{
int len;
len = strlen(str);
xfwrite(str, len, 1, file);
return 0;
}
int
xprintf(const char *fmt, ...)
{
va_list ap;
int n;
va_start(ap, fmt);
n = xvfprintf(xstdout, fmt, ap);
va_end(ap);
return n;
}
int
xfprintf(xFILE *stream, const char *fmt, ...)
{
va_list ap;
int n;
va_start(ap, fmt);
n = xvfprintf(stream, fmt, ap);
va_end(ap);
return n;
}
int
xvfprintf(xFILE *stream, const char *fmt, va_list ap)
{
va_list ap2;
va_copy(ap2, ap);
{
char buf[vsnprintf(NULL, 0, fmt, ap2)];
vsnprintf(buf, sizeof buf + 1, fmt, ap);
if (xfwrite(buf, sizeof buf, 1, stream) < 1) {
return -1;
}
va_end(ap2);
return sizeof buf;
}
}
/*
* Derieved xFILE Classes
*/
static FILE *
unpack(void *cookie)
{
switch ((long)cookie) {
default: return cookie;
case 0: return stdin;
case 1: return stdout;
case -1: return stderr;
}
}
static int
file_read(void *cookie, char *ptr, int size)
{
FILE *file = unpack(cookie);
int r;
r = fread(ptr, 1, size, file);
if (r < size && ferror(file)) {
return -1;
}
if (r == 0 && feof(file)) {
clearerr(file);
}
return r;
}
static int
file_write(void *cookie, const char *ptr, int size)
{
FILE *file = unpack(cookie);
int r;
r = fwrite(ptr, 1, size, file);
if (r < size) {
return -1;
}
return r;
}
static long
file_seek(void *cookie, long pos, int whence)
{
return fseek(unpack(cookie), pos, whence);
}
static int
file_flush(void *cookie)
{
return fflush(unpack(cookie));
}
static int
file_close(void *cookie)
{
return fclose(unpack(cookie));
}
xFILE *
xfpopen(FILE *fp)
{
xFILE *file;
file = xfunopen(fp, file_read, file_write, file_seek, file_flush, file_close);
if (! file) {
return NULL;
}
return file;
}
#define FILE_VTABLE file_read, file_write, file_seek, file_flush, file_close
static xFILE xfile_stdin = { -1, 0, { (void *)0, FILE_VTABLE } };
static xFILE xfile_stdout = { -1, 0, { (void *)1, FILE_VTABLE } };
static xFILE xfile_stderr = { -1, 0, { (void *)-1, FILE_VTABLE } };
xFILE *xstdin = &xfile_stdin;
xFILE *xstdout = &xfile_stdout;
xFILE *xstderr = &xfile_stderr;
struct membuf {
char *buf;
long pos, end, capa;
};
static int
mem_read(void *cookie, char *ptr, int size)
{
struct membuf *mem;
mem = (struct membuf *)cookie;
size = min(size, mem->end - mem->pos);
memcpy(ptr, mem->buf + mem->pos, size);
mem->pos += size;
return size;
}
static int
mem_write(void *cookie, const char *ptr, int size)
{
struct membuf *mem;
mem = (struct membuf *)cookie;
if (mem->pos + size >= mem->capa) {
mem->capa = (mem->pos + size) * 2;
mem->buf = realloc(mem->buf, mem->capa);
}
memcpy(mem->buf + mem->pos, ptr, size);
mem->pos += size;
mem->end = max(mem->pos, mem->end);
return size;
}
static long
mem_seek(void *cookie, long pos, int whence)
{
struct membuf *mem;
mem = (struct membuf *)cookie;
switch (whence) {
case SEEK_SET:
mem->pos = pos;
break;
case SEEK_CUR:
mem->pos += pos;
break;
case SEEK_END:
mem->pos = mem->end + pos;
break;
}
return mem->pos;
}
static int
mem_flush(void *cookie)
{
(void)cookie;
return 0;
}
static int
mem_close(void *cookie)
{
struct membuf *mem;
mem = (struct membuf *)cookie;
free(mem->buf);
free(mem);
return 0;
}
xFILE *
xmopen()
{
struct membuf *mem;
mem = (struct membuf *)malloc(sizeof(struct membuf));
mem->buf = (char *)malloc(BUFSIZ);
mem->pos = 0;
mem->end = 0;
mem->capa = BUFSIZ;
return xfunopen(mem, mem_read, mem_write, mem_seek, mem_flush, mem_close);
}