view src/ploki/IO.c @ 12292:d51f2100210c draft

<kspalaiologos> `` cat <<<"asmbf && bfi output.b" > /hackenv/ibin/asmbf
author HackEso <hackeso@esolangs.org>
date Thu, 02 Jan 2020 15:38:21 +0000
parents ac0403686959
children
line wrap: on
line source

#include "config.h"
#include "IO.h"
#include "Str.h"
#include "main.h"
#include "main_io.h"
#include "xmalloc.h"
#include "zz.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

struct IO {
	struct IO *prev, *next;
	enum io_flags mode;
	FILE *fp;
	char *name;
	size_t refs;
	String *buf;
	long told;
	enum {
		DI_NONE,
		DI_RD,
		DI_WR
	} dirct;
};

static IO *Root;

static void sanitycheck(enum io_flags m) {
	#if !DEBUG_P
		(void)m;
	#endif
	assert(
			(
			 m & IO_READ ||
			 m & IO_WRITE ||
			 m & IO_APPEND
			) &&
			!(
				m & IO_WRITE &&
				m & IO_APPEND
			 ) &&
			(
			 !(m & IO_BUFFERED) || m & IO_READ
			) &&
			(
			 !(m & IO_AUTOFLUSH) ||
			 (
			  m & IO_WRITE ||
			  m & IO_APPEND
			 )
			) &&
			(
			 !(m & IO_WRITE) || (m & IO_TRUNCATE || m & IO_READ)
			) &&
			(
			 !(m & IO_TRUNCATE) || m & IO_WRITE
			)
		  );
}

void io_init(void) {
}

static void io_delete(IO *io) {
	if (io->mode & IO_BUFFERED) {
		St_clear(io->buf);
		xfree(io->buf);
	}
	if (io->fp && io->fp != stderr) {
		if (fclose(io->fp)) {
			fprintf(io_fp(Err), "%s: %s: %s\n", Prog, io->name, strerror(errno));
		}
	}
	xfree(io->name);
	if (io->prev) {
		io->prev->next = io->next;
	} else {
		assert(io == Root);
		Root = io->next;
	}
	if (io->next) {
		io->next->prev = io->prev;
	}
	xfree(io);
}

void io_end(void) {
	while (Root) {
		io_delete(Root);
	}
}

static char *xstrdup(const char *s) {
	const size_t len = strlen(s) + 1;
	char *const tmp = xmalloc(len, sizeof *tmp);
	memcpy(tmp, s, len);
	return tmp;
}

IO *io_enter(const char *name, FILE *fp, enum io_flags mode) {
	IO *io;
	sanitycheck(mode);
	io = xmalloc(1, sizeof *io);
	io->fp = fp;
	io->mode = mode;
	if (mode & IO_BUFFERED) {
		io->buf = xmalloc(1, sizeof *io->buf);
		St_init(io->buf);
		io->told = -1;
	}
	io->name = xstrdup(name);
	io->refs = 1;

	io->dirct = DI_NONE;

	io->prev = NULL;
	io->next = Root;
	if (Root) {
		Root->prev = io;
	}
	Root = io;
	return io;
}

const char *io_name(const IO *io, String *s) {
	if (s) {
		St_cpy_s(s, io->name);
	}
	return io->name;
}

IO *io_open(const char *name, enum io_flags mode) {
	FILE *fp;
	char mbuf[4], *p = mbuf;
	sanitycheck(mode);

	if (mode & IO_APPEND) {
		*p++ = 'a';
		if (mode & IO_READ) {
			*p++ = '+';
		}
	} else if (mode & IO_WRITE) {
		if (mode & IO_TRUNCATE) {
			*p++ = 'w';
			if (mode & IO_READ) {
				*p++ = '+';
			}
		} else {
			assert(mode & IO_READ);
			*p++ = 'r';
			*p++ = '+';
		}
	} else {
		assert(mode & IO_READ);
		*p++ = 'r';
	}

	if (mode & IO_BINARY) {
		*p++ = 'b';
	}
	*p = '\0';

	if (!(fp = fopen(name, mbuf))) {
		return NULL;
	}
	return io_enter(name, fp, mode);
}

IO *io_incr(IO *io) {
	++io->refs;
	return io;
}

void io_decr(IO *io) {
	if (!--io->refs) {
		io_delete(io);
	}
}

int io_close(IO *io) {
	int ret;
	assert(io->fp != NULL);
	ret = fclose(io->fp);
	io->fp = NULL;
	if (io->mode & IO_BUFFERED) {
		St_clear(io->buf);
		xfree(io->buf);
		io->mode &= ~IO_BUFFERED;
	}
	return ret;
}

int io_bufred(const IO *io) {
	return !!(io->mode & IO_BUFFERED);
}

void io_unbuffer(IO *io) {
	assert(io->mode & IO_BUFFERED);
	if (St_len(io->buf) && io->told != -1) {
		io_seek(io, io->told, SEEK_SET);
	}
	St_clear(io->buf);
	xfree(io->buf);
	io->mode &= ~IO_BUFFERED;
}

FILE *io_fp(const IO *io) {
	return io->fp;
}

static void bufk(IO *f, size_t n) {
	String tmp;
	assert(f->mode & IO_BUFFERED);

	if (St_len(f->buf) >= n || feof(f->fp) || ferror(f->fp)) {
		return;
	}

	assert(f->dirct != DI_WR);
	#if 0
	if (f->dirct == DI_WR) {
		io_seek(f, 0, SEEK_CUR);
	}
	f->dirct = DI_RD;
	#endif

	if (!(St_len(f->buf) || f->mode & IO_BINARY)) {
		f->told = ftell(f->fp);
	}

	St_init(&tmp);
	while (
			St_len(f->buf) < n &&
			!feof(f->fp) && !ferror(f->fp) &&
			St_read(&tmp, f->fp, n - St_len(f->buf))
		  ) {
		St_cat(f->buf, &tmp);
	}
	St_clear(&tmp);
}

#ifdef EBADF
	#define BADF ((void)(errno = EBADF))
#else
	#define BADF ((void)0)
#endif

#define XMODE(c, x) \
do {                \
	if (!(c)) {     \
		BADF;       \
		return (x); \
	} \
} while (0)

#define WMODE(f, x) XMODE((f)->mode & IO_WRITE || (f)->mode & IO_APPEND, (x))
#define PMODE(f, x) XMODE((f)->mode & IO_BUFFERED, (x))
#define RMODE(f, x) XMODE((f)->mode & IO_READ, (x))

const char *io_bufptr(IO *io) {
	PMODE(io, NULL);
	return St_ptr(io->buf);
}

int io_flush(IO *f) {
	WMODE(f, EOF);
	return fflush(f->fp);
}

int io_err(const IO *f) {
	return ferror(f->fp);
}

int io_eof(const IO *f) {
	return feof(f->fp);
}

int io_peek(IO *f, size_t pos) {
	PMODE(f, EOF);
	bufk(f, pos + 1);
	if (St_len(f->buf) <= pos) {
		return EOF;
	}
	return ST_INDEX(f->buf, pos);
}

int io_cmppeek(IO *f, size_t o, const void *p, size_t n) {
	size_t i;
	PMODE(f, -1);

	for (i = 0; i < n; ++i) {
		bufk(f, o + i + 1u);
		if (ST_INDEX(f->buf, o + i) != i[(const unsigned char *)p]) {
			return 1;
		}
	}
	return 0;
}

int io_xcmp(IO *f, size_t a, size_t b, size_t n) {
	const size_t max = a > b ? a : b;
	PMODE(f, -1);
	bufk(f, max + n);
	if (St_len(f->buf) < max + n) {
		return 1;
	}
	return memcmp(St_ptr(f->buf) + a, St_ptr(f->buf) + b, n) != 0;
}

size_t io_read(IO *f, String *s, size_t n) {
	RMODE(f, -1);

	if (f->mode & IO_BUFFERED) {
		String null;
		size_t old;

		bufk(f, n);
		old = St_len(f->buf);
		St_init(&null);
		St_substr(s, f->buf, 0, n, &null);
		St_clear(&null);
		old -= St_len(f->buf);
		if (St_len(f->buf) && old && !(f->mode & IO_BINARY) && f->told != -1) {
			const long keep = ftell(f->fp);
			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
				size_t i;
				for (i = 0; i < old; ++i) {
					getc(f->fp);
				}
				f->told = ftell(f->fp);
				f->dirct = DI_RD;
				fseek(f->fp, keep, SEEK_SET);
			}
		}
		return old;
	} else {
		if (f->dirct == DI_WR) {
			fseek(f->fp, 0, SEEK_CUR);
		}
		f->dirct = DI_RD;
		return St_read(s, f->fp, n);
	}
}

int io_getc(IO *f) {
	RMODE(f, EOF);

	if (f->mode & IO_BUFFERED) {
		int c;
		bufk(f, 1);
		c = St_shift(f->buf);
		if (St_len(f->buf) && c != EOF && !(f->mode & IO_BINARY) && f->told != -1) {
			const long keep = ftell(f->fp);
			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
				getc(f->fp);
				f->told = ftell(f->fp);
				f->dirct = DI_RD;
				fseek(f->fp, keep, SEEK_SET);
			}
		}
		return c;
	}

	if (f->dirct == DI_WR) {
		fseek(f->fp, 0, SEEK_CUR);
	}
	f->dirct = DI_RD;
	return getc(f->fp);
}

size_t io_getline(IO *f, String *s) {
	RMODE(f, -1);

	if (f->mode & IO_BUFFERED) {
		size_t p;
		size_t old;

		p = St_chr(f->buf, '\n') + 1u;
		if (p) {
			old = St_len(f->buf);
			if (s) {
				St_cpy_m(s, St_ptr(f->buf), p);
			}
			St_del(f->buf, 0, p);
		} else {
			for (p = St_len(f->buf); io_peek(f, p) != EOF; ++p) {
				if (ST_LASTCHAR(f->buf) == '\n') {
					break;
				}
			}
			old = St_len(f->buf);
			if (s) {
				St_cpy(s, f->buf);
			}
			St_zero(f->buf);
		}
		old -= St_len(f->buf);
		if (St_len(f->buf) && old && !(f->mode & IO_BINARY) && f->told != -1) {
			const long keep = ftell(f->fp);
			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
				size_t i;
				for (i = 0; i < old; ++i) {
					getc(f->fp);
				}
				f->dirct = DI_RD;
				f->told = ftell(f->fp);
				fseek(f->fp, keep, SEEK_SET);
			}
		}
		return old;
	} else {
		int c;
		size_t n;

		if (f->dirct == DI_WR) {
			fseek(f->fp, 0, SEEK_CUR);
		}
		f->dirct = DI_RD;

		if (s) {
			St_zero(s);
		}
		n = 0;
		while ((c = getc(f->fp)) != EOF) {
			if (s) {
				St_cat_c(s, c);
			}
			++n;
			if (c == '\n') {
				break;
			}
		}
		if (!n && ferror(f->fp)) {
			return -1;
		}
		return n;
	}
}

size_t io_write(IO *f, const String *s) {
	size_t ret;
	WMODE(f, -1);
	if (f->dirct == DI_RD) {
		fseek(f->fp, 0, SEEK_CUR);
	}
	f->dirct = DI_WR;
	ret = ST_WRITE(s, f->fp);
	if (f->mode & IO_AUTOFLUSH) {
		fflush(f->fp);
	}
	return ret;
}

size_t io_write_m(IO *f, const void *p, size_t n) {
	size_t ret;
	WMODE(f, -1);
	if (f->dirct == DI_RD) {
		fseek(f->fp, 0, SEEK_CUR);
	}
	f->dirct = DI_WR;
	ret = fwrite(p, 1, n, f->fp);
	if (f->mode & IO_AUTOFLUSH) {
		fflush(f->fp);
	}
	return ret;
}

size_t io_write_s(IO *f, const char *s) {
	return io_write_m(f, s, strlen(s));
}

int io_putc(IO *f, int c) {
	int ret;
	WMODE(f, EOF);
	if (f->dirct == DI_RD) {
		fseek(f->fp, 0, SEEK_CUR);
	}
	f->dirct = DI_WR;
	ret = putc(c, f->fp);
	if (f->mode & IO_AUTOFLUSH) {
		fflush(f->fp);
	}
	return ret;
}

long io_tell(IO *f) {
	if (f->mode & IO_BUFFERED) {
		if (f->mode & IO_BINARY) {
			const long ret = ftell(f->fp);
			if (ret == -1) {
				return ret;
			}
			return ret - St_len(f->buf);
		}
		if (St_len(f->buf)) {
			return f->told;
		}
	}
	return ftell(f->fp);
}

int io_seek(IO *f, long off, enum io_whence w) {
	if (f->mode & IO_BUFFERED) {
		St_zero(f->buf);
	}
	f->dirct = DI_NONE;
	return fseek(f->fp, off, w);
}

void io_clearerr(IO *f) {
	clearerr(f->fp);
}