4.4BSD/usr/src/contrib/rc-1.4/input.c

Compare this file to the similar file:
Show the results in this format:

/* input.c: i/o routines for files and pseudo-files (strings) */

#include <errno.h>
#include <setjmp.h>
#include "rc.h"
#include "jbwrap.h"

/*
   NB: character unget is supported for up to two characters, but NOT
   in the case of EOF. Since EOF does not fit in a char, it is easiest
   to support only one unget of EOF.
*/

typedef struct Input {
	inputtype t;
	char *ibuf;
	int fd, index, read, lineno, last;
	bool saved, eofread;
} Input;

#define BUFSIZE ((size_t) 256)

char *prompt, *prompt2;
bool rcrc;

static int dead(void);
static int fdgchar(void);
static int stringgchar(void);
static void history(void);
static void ugdead(int);
static void pushcommon(void);

static char *inbuf;
static size_t istacksize, chars_out, chars_in;
static bool eofread = FALSE, save_lineno = TRUE;
static Input *istack, *itop;
static char *histstr;
static int histfd;

static int (*realgchar)(void);
static void (*realugchar)(int);

int last;

extern int gchar() {
	if (eofread) {
		eofread = FALSE;
		return last = EOF;
	}
	return (*realgchar)();
}

extern void ugchar(int c) {
	(*realugchar)(c);
}

static int dead() {
	return last = EOF;
}

static void ugdead(int c) {
	return;
}

static void ugalive(int c) {
	if (c == EOF)
		eofread = TRUE;
	else
		inbuf[--chars_out] = c;
}

/* get the next character from a string. */

static int stringgchar() {
	return last = (inbuf[chars_out] == '\0' ? EOF : inbuf[chars_out++]);
}

/* signal-safe readline wrapper */

/*
   read a character from a file-descriptor. If GNU readline is defined, add a newline and doctor
   the buffer to look like a regular fdgchar buffer.
*/

static int fdgchar() {
	if (chars_out >= chars_in + 2) { /* has the buffer been exhausted? if so, replenish it */
		while (1) {
			long /*ssize_t*/ r = rc_read(istack->fd, inbuf + 2, BUFSIZE);
			if (r < 0) {
				if (errno == EINTR)
					continue; /* Suppose it was interrupted by a signal */
				uerror("read");
				rc_exit(1);
			}
			chars_in = (size_t) r;
			break;
		}
		if (chars_in == 0)
			return last = EOF;
		chars_out = 2;
		if (dashvee)
			writeall(2, inbuf + 2, chars_in);
		history();
	}
	return last = inbuf[chars_out++];
}

/* set up the input stack, and put a "dead" input at the bottom, so that yyparse will always read eof */

extern void initinput() {
	istack = itop = ealloc(istacksize = 256 * sizeof (Input));
	istack->t = iFd;
	istack->fd = -1;
	realugchar = ugalive;
}

/* push an input source onto the stack. set up a new input buffer, and set gchar() */

static void pushcommon() {
	size_t idiff;
	istack->index = chars_out;
	istack->read = chars_in;
	istack->ibuf = inbuf;
	istack->lineno = lineno;
	istack->saved = save_lineno;
	istack->last = last;
	istack->eofread = eofread;
	istack++;
	idiff = istack - itop;
	if (idiff >= istacksize / sizeof (Input)) {
		itop = erealloc(itop, istacksize *= 2);
		istack = itop + idiff;
	}
	realugchar = ugalive;
	chars_out = 2;
	chars_in = 0;
}

extern void pushfd(int fd) {
	pushcommon();
	istack->t = iFd;
	save_lineno = TRUE;
	istack->fd = fd;
	realgchar = fdgchar;
	inbuf = ealloc(BUFSIZE + 2);
	lineno = 1;
}

extern void pushstring(char **a, bool save) {
	pushcommon();
	istack->t = iString;
	save_lineno = save;
	inbuf = mprint("..%A", a);
	realgchar = stringgchar;
	if (save_lineno)
		lineno = 1;
	else
		--lineno;
}

/* remove an input source from the stack. restore the right kind of getchar (string,fd) etc. */

extern void popinput() {
	if (istack->t == iFd)
		close(istack->fd);
	efree(inbuf);
	--istack;
	realgchar = (istack->t == iString ? stringgchar : fdgchar);
	if (istack->fd == -1) { /* top of input stack */
		realgchar = dead;
		realugchar = ugdead;
	}
	last = istack->last;
	eofread = istack->eofread;
	inbuf = istack->ibuf;
	chars_out = istack->index;
	chars_in = istack->read;
	if (save_lineno)
		lineno = istack->lineno;
	else
		lineno++;
	save_lineno = istack->saved;
}

/* flush input characters upto newline. Used by scanerror() */

extern void flushu() {
	int c;
	if (last == '\n' || last == EOF)
		return;
	while ((c = gchar()) != '\n' && c != EOF)
		; /* skip to newline */
	if (c == EOF)
		ugchar(c);
}

/* the wrapper loop in rc: prompt for commands until EOF, calling yyparse and walk() */

extern Node *doit(bool execit) {
	bool eof;
	Jbwrap j;
	Estack e1, e2;
	Edata jerror;
	if (dashen)
		execit = FALSE;
	setjmp(j.j);
	jerror.jb = &j;
	except(eError, jerror, &e1);
	for (eof = FALSE; !eof;) {
		Edata block;
		block.b = newblock();
		except(eArena, block, &e2);
		SIGCHK;
		if (dashell) {
			char *fname[3];
			fname[1] = concat(varlookup("home"), word("/.rcrc", NULL))->w;
			fname[2] = NULL;
			rcrc = TRUE;
			dashell = FALSE;
			b_dot(fname);
		}
		if (interactive) {
			List *s;
			if (!dashen && fnlookup("prompt") != NULL) {
				static char *arglist[] = { "prompt", NULL };
				funcall(arglist);
			}
			if ((s = varlookup("prompt")) != NULL) {
				fprint(2, "%s", s->w);
				prompt2 = (s->n == NULL ? "" : s->n->w);
			}
		}
		inityy();
		if (yyparse() == 1 && execit)
			rc_raise(eError);
		eof = (last == EOF); /* "last" can be clobbered during a walk() */
		if (parsetree != NULL) {
			if (execit)
				walk(parsetree, TRUE);
			else if (dashex && dashen)
				fprint(2, "%T\n", parsetree);
		}
		unexcept(); /* eArena */
	}
	popinput();
	unexcept(); /* eError */
	return parsetree;
}

/* parse a function imported from the environment */

extern Node *parseline(char *extdef) {
	int i = interactive;
	char *in[2];
	Node *fun;
	in[0] = extdef;
	in[1] = NULL;
	interactive = FALSE;
	pushstring(in, TRUE);
	fun = doit(FALSE);
	interactive = i;
	return fun;
}

/* write last command out to a file. Check to see if $history has changed, also */

static void history() {
	List *histlist;
	size_t a;
	if (!interactive)
		return;
	if ((histlist = varlookup("history")) == NULL) {
		if (histstr != NULL) {
			efree(histstr);
			close(histfd);
			histstr = NULL;
		}
		return;
	}
	if (histstr == NULL || !streq(histstr, histlist->w)) { /* open new file */
		if (histstr != NULL) {
			efree(histstr);
			close(histfd);
		}
		histstr = ecpy(histlist->w);
		histfd = rc_open(histstr, rAppend);
		if (histfd < 0) {
			uerror(histstr);
			efree(histstr);
			histstr = NULL;
			varrm("history", FALSE);
		}
	}
	/*
	   Small unix hack: since read() reads only up to a newline
	   from a terminal, then presumably this write() will write at
	   most only one input line at a time.
	*/
	for (a = 2; a < chars_in + 2; a++) { /* skip empty lines and comments in history. */
		if (inbuf[a] == '#' || inbuf[a] == '\n')
			return;
		if (inbuf[a] != ' ' && inbuf[a] != '\t')
			break;
	}
	writeall(histfd, inbuf + 2, chars_in);
}

/* close file descriptors after a fork() */

extern void closefds() {
	Input *i;
	if (histstr != NULL) {			/* Close an open history file */
		close(histfd);
		histstr = NULL;			/* But prevent re-closing of the same file-descriptor */
	}
	for (i = istack; i != itop; --i)	/* close open scripts */
		if (i->t == iFd && i->fd > 2) {
			close(i->fd);
			i->fd = -1;
		}
}