shithub: pprolog

ref: c85de58a2047c4858825d03977e490db6168fbe3
dir: /streams.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>

#include "dat.h"
#include "fns.h"

typedef struct Stream Stream;

struct Stream
{
	ulong fd;
	Biobuf *bio;
	int type;
	int mode;
	int reposition;
	int eofaction;
	int nalias;
	Rune **aliases;
	Rune *filename;
	Stream *next;
};

enum {
	TextStream,
	BinaryStream,
};

enum {
	ReadStream,
	WriteStream,
	AppendStream,
};

enum {
	EofActionError,
	EofActionEof,
	EofActionReset,
};

static Stream *streams;
static Stream *currentinput;
static Stream *currentoutput;

Stream *openstreamfd(int, Biobuf *, int, int);
Stream *getstreambyfd(int);
Stream *getstreambyalias(Rune *);
Stream *getstream(Term *);
Term *streamproperties(Stream *);
void addstreamalias(int, Rune *);

void
initstreams(void)
{
	int infd = dup(0, -1);
	int outfd = dup(1, -1);

	Biobuf *bioin = Bfdopen(infd, OREAD);
	Biobuf *bioout = Bfdopen(outfd, OWRITE);
	
	currentinput = openstreamfd(infd, bioin, TextStream, ReadStream);
	currentoutput = openstreamfd(outfd, bioout, TextStream, AppendStream);

	addstreamalias(infd, L"user_input");
	addstreamalias(outfd, L"user_output");
}

int
openstream(Rune *sourcesink, Rune *mode, Term *options, Term **stream)
{
	USED(options);
	int omode;
	int smode;
	if(runestrcmp(mode, L"read") == 0){
		omode = OREAD;
		smode = ReadStream;
	}else if(runestrcmp(mode, L"write") == 0){
		omode = OWRITE;
		smode = WriteStream;
	}else if(runestrcmp(mode, L"append") == 0){
		omode = OWRITE; /* Is this correct? */
		smode = AppendStream;
	}else{
		*stream = existenceerror(L"source_sink", mkatom(sourcesink));
		return 1;
	}

	char *filename = smprint("%S", sourcesink);
	int fd = open(filename, omode);
	if(fd < 0){
		*stream = permissionerror(L"open", L"source_sink", mkatom(sourcesink));
		return 1;
	}
	Biobuf *bio = Bfdopen(fd, omode);
	if(bio == nil){
		*stream = permissionerror(L"open", L"source_sink", mkatom(sourcesink));
		return 1;
	}

	Stream *s = openstreamfd(fd, bio, TextStream, smode);
	s->filename = sourcesink;
	*stream = mkinteger(s->fd);
	return 0;
}

void
closestream(Term *t)
{
	Stream *s = getstream(t);
	if(s == nil)
		return;

	Bterm(s->bio);
	close(s->fd);

	Stream *tmp;
	Stream *prev = nil;
	for(tmp = streams; tmp != nil; tmp = tmp->next){
		if(tmp == s){
			if(prev == nil)
				streams = tmp->next;
			else
				prev->next = tmp->next;
			break;
		}
		if(prev == nil)
			prev = tmp;
	}
}

Term *
currentinputstream(void)
{
	return mkinteger(currentinput->fd);
}

Term *
currentoutputstream(void)
{
	return mkinteger(currentoutput->fd);
}

void
setcurrentinputstream(Term *t)
{
	Stream *s = getstream(t);
	if(s)
		currentinput = s;
}

void
setcurrentoutputstream(Term *t)
{
	Stream *s = getstream(t);
	if(s)
		currentoutput = s;
}

int
isopenstream(Term *t)
{
	Stream *s = getstream(t);
	if(s)
		return 1;
	else
		return 0;
}

int
isinputstream(Term *t)
{
	Stream *s = getstream(t);
	if(s && s->mode == ReadStream)
		return 1;
	else
		return 0;
}

int
isoutputstream(Term *t)
{
	Stream *s = getstream(t);
	if(s && (s->mode == WriteStream || s->mode == AppendStream))
		return 1;
	else
		return 0;
}

int
istextstream(Term *t)
{
	Stream *s = getstream(t);
	if(s && s->type == TextStream)
		return 1;
	else
		return 0;
}

int
isbinarystream(Term *t)
{
	Stream *s = getstream(t);
	if(s && s->type == BinaryStream)
		return 1;
	else
		return 0;
}

int
canreposition(Term *t)
{
	Stream *s = getstream(t);
	if(s && s->reposition)
		return 1;
	else
		return 0;
}

int
readterm(Term *stream, Term **term, Module *mod)
{
	Stream *s = getstream(stream);
	if(s == nil){
		*term = existenceerror(L"stream", stream);
		return 1;
	}
	*term = parse(s->bio, mod);

	return 0;
}

void
writeterm(Term *stream, Term *options, Term *term, Module *mod)
{
	Stream *s = getstream(stream);
	if(s == nil)
		return;

	int quoted = 0;
	int ignoreops = 0;
	int numbervars = 0;

	Term *op;
	for(op = options; op->tag == CompoundTerm; op = op->children->next){
		Term *opkey = op->children->children;
		Term *opval = opkey->next;
		if(runestrcmp(opkey->text, L"quoted") == 0)
			quoted = opval->ival;
		else if(runestrcmp(opkey->text, L"ignore_ops") == 0)
			ignoreops = opval->ival;
		else if(runestrcmp(opkey->text, L"numbervars") == 0)
			numbervars = opval->ival;
	}

	Rune *output = prettyprint(term, quoted, ignoreops, numbervars, mod);
	Bprint(s->bio, "%S", output);
	Bflush(s->bio);
}

Stream *
openstreamfd(int fd, Biobuf *bio, int type, int mode)
{
	/* streams are not garbage collected for now */
	Stream *s = malloc(sizeof(Stream));
	s->fd = fd;
	s->bio = bio;
	s->type = type;
	s->mode = mode;
	s->reposition = 0;
	s->eofaction = EofActionEof;
	s->nalias = 0;
	s->aliases = nil;
	s->filename = nil;
	s->next = streams;
	streams = s;
	return s;
}

Stream *
getstreambyfd(int fd)
{
	Stream *s;
	for(s = streams; s != nil; s = s->next)
		if(s->fd == fd)
			return s;
	return nil;
}

Stream *
getstreambyalias(Rune *alias)
{
	Stream *s;
	for(s = streams; s != nil; s = s->next){
		int i;
		for(i = 0; i < s->nalias; i++){
			if(runestrcmp(alias, s->aliases[i]) == 0)
				return s;
		}
	}
	return nil;
}

Stream *
getstream(Term *t)
{
	Stream *s = nil;
	if(t->tag == IntegerTerm)
		s = getstreambyfd(t->ival);
	else if(t->tag == AtomTerm)
		s = getstreambyalias(t->text);
	return s;
}

Rune
getchar(Term *t)
{
	Stream *s = getstream(t);
	return Bgetrune(s->bio);
}

Rune
peekchar(Term *t)
{
	Stream *s = getstream(t);
	Rune r = Bgetrune(s->bio);
	if(r != Beof)
		Bungetrune(s->bio);
	return r;
}

void
putchar(Term *t, Rune r)
{
	Stream *s = getstream(t);
	Bprint(s->bio, "%C", r);
}

void
flushstream(Term *t)
{
	Stream *s = getstream(t);
	Bflush(s->bio);
}

Term *
streamsproperties(void)
{
	Term *list = nil;
	Stream *s;
	for(s = streams; s != nil; s = s->next){
		Term *props = streamproperties(s);
		list = appendterm(list, props);
	}
	return list;
}

Term *streamproperties(Stream *s)
{
	Term *props = nil;
	Term *stream = mkinteger(s->fd);
	Term *arg = nil;
	Term *data;
	Term *prop;

	/* file_name(F) */
	if(s->filename){
		arg = mkatom(s->filename);
		data = copyterm(stream, nil);
		data->next = mkcompound(L"file_name", 1, arg);
		prop = mkcompound(L"prop", 2, data);
		props = appendterm(props, prop);
	}

	/* mode(M) */
	switch(s->mode){
	case ReadStream: arg = mkatom(L"read"); break;
	case WriteStream: arg = mkatom(L"write"); break;
	case AppendStream: arg = mkatom(L"append"); break;
	}
	data = copyterm(stream, nil);
	data->next = mkcompound(L"mode", 1, arg);
	prop = mkcompound(L"prop", 2, data);
	props = appendterm(props, prop);

	/* input or output */
	data = copyterm(stream, nil);
	if(s->mode == ReadStream)
		data->next = mkatom(L"input");
	else
		data->next = mkatom(L"output");
	prop = mkcompound(L"prop", 2, data);
	props = appendterm(props, prop);

	/* alias(A) */
	int i;
	for(i = 0; i < s->nalias; i++){
		arg = mkatom(s->aliases[i]);
		data = copyterm(stream, nil);
		data->next = mkcompound(L"alias", 1, arg);
		prop = mkcompound(L"prop", 2, data);
		props = appendterm(props, prop);
	}

	/* position(P) */
	if(s->reposition){
		arg = mkinteger(Boffset(s->bio));
		data = copyterm(stream, nil);
		data->next = mkcompound(L"position", 1, arg);
		prop = mkcompound(L"prop", 2, data);
		props = appendterm(props, prop);
	}

	/* end_of_stream(E) */
	if(s->mode == ReadStream){
		Rune r = Bgetrune(s->bio);
		if(r == Beof)
			arg = mkatom(L"at");
		else{
			Bungetrune(s->bio);
			arg = mkatom(L"not");
		}
		data = copyterm(stream, nil);
		data->next = mkcompound(L"end_of_stream", 1, arg);
		prop = mkcompound(L"prop", 2, data);
		props = appendterm(props, prop);
	}
	
	/* eof_action(A) */
	switch(s->eofaction){
	case EofActionError: arg = mkatom(L"error"); break;
	case EofActionEof: arg = mkatom(L"eof_code"); break;
	case EofActionReset: arg = mkatom(L"reset"); break;
	}
	data = copyterm(stream, nil);
	data->next = mkcompound(L"eof_action", 1, arg);
	prop = mkcompound(L"prop", 2, data);
	props = appendterm(props, prop);

	/* reposition(Bool) */
	if(s->reposition)
		arg = mkatom(L"true");
	else
		arg = mkatom(L"false");
	data = copyterm(stream, nil);
	data->next = mkcompound(L"reposition", 1, arg);
	prop = mkcompound(L"prop", 2, data);
	props = appendterm(props, prop);

	/* type(T) */
	if(s->type == TextStream)
		arg = mkatom(L"text");
	else
		arg = mkatom(L"binary");
	data = copyterm(stream, nil);
	data->next = mkcompound(L"type", 1, arg);
	prop = mkcompound(L"prop", 2, data);
	props = appendterm(props, prop);

	return props;
}

void
reposition(Term *t, vlong pos)
{
	Stream *s = getstream(t);
	Bseek(s->bio, pos, 0);
}

void
addstreamalias(int fd, Rune *alias)
{
	Stream *s;
	for(s = streams; s != nil; s = s->next){
		if(s->fd == fd){
			s->nalias++;
			s->aliases = realloc(s->aliases, sizeof(Rune *) * s->nalias);
			s->aliases[s->nalias-1] = alias;
		}
	}
}