view src/ploki/IO.c @ 8065:591b1467ccdf

<int-e> le/rn paste/"Paste" is a short story by Henry James. Its contents has been cut into pieces and distributed over numerous tin boxes on the World Wide Web, little pearls of wisdom buried among ordinary pastes.
author HackBot
date Sun, 15 May 2016 13:14:57 +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);
}