diff options
| author | Byron Rakitzis <byron@archone.tamu.edu> | 1992-05-27 00:00:01 +0100 |
|---|---|---|
| committer | Toby Goodwin <toby@paccrat.org> | 2015-05-16 21:22:56 +0100 |
| commit | 26bb34cf036027dfa22dfae8d8fc2f6bfd6acb75 (patch) | |
| tree | 589705b2492ce4681760846d9ca45600f0af298d /input.c | |
release: rc-1.4
Diffstat (limited to 'input.c')
| -rw-r--r-- | input.c | 373 |
1 files changed, 373 insertions, 0 deletions
@@ -0,0 +1,373 @@ +/* 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) + +#ifdef READLINE +extern char *readline(char *); +extern void add_history(char *); +static char *rlinebuf; +#endif + +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 */ + +#ifdef READLINE +#ifndef SVSIGS +static char *rc_readline(char *prompt) { + char *r; + interrupt_happened = FALSE; + if (!setjmp(slowbuf.j)) { + slow = TRUE; + if (!interrupt_happened) + r = readline(prompt); + else + r = NULL; + } else + r = NULL; + slow = FALSE; + if (r == NULL) + errno = EINTR; + SIGCHK; + return r; +} +#else +#define rc_readline readline +#endif /* SVSIGS */ +#endif /* READLINE */ + +/* + 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) { +#ifdef READLINE + if (interactive && istack->fd == 0) { + rlinebuf = readline(prompt); + if (rlinebuf == NULL) { + chars_in = 0; + } else { + if (*rlinebuf != '\0') + add_history(rlinebuf); + chars_in = strlen(rlinebuf) + 1; + efree(inbuf); + inbuf = ealloc(chars_in + 3); + strcpy(inbuf+2, rlinebuf); + strcat(inbuf+2, "\n"); + } + } else +#endif + { + 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) { +#ifdef READLINE + prompt = s->w; +#else + fprint(2, "%s", s->w); +#endif + 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; + } +} |