home *** CD-ROM | disk | FTP | other *** search
- /* input.c: i/o routines for files and pseudo-files (strings) */
-
- #include <errno.h>
- #include <setjmp.h>
- #include <stdarg.h>
- #include "rc.h"
- #include "input.h"
- #include "utils.h"
- #include "walk.h"
- #include "hash.h"
- #include "lex.h"
- #include "open.h"
- #include "nalloc.h"
- #include "except.h"
- #include "glom.h"
- #include "builtins.h"
- #include "parse.h"
- #include "tree.h"
-
- /*
- warning, changes have been made to (fd|string)(gchar|ugchar) so that
- you cannot unget EOF more than once. lex.c never does this, so I'm
- safe, but if you unget EOF more than once, expect to be able to read
- it back only once. The reason is that EOF is an int, whereas the buffers
- are character buffers.
- */
-
- typedef struct Input {
- enum inputtype t;
- char *ibuf;
- int fd;
- int index;
- int read;
- int lineno;
- } Input;
-
- #define BUFSIZE ((SIZE_T) 256)
-
- #ifdef READLINE
- extern char *readline(char *);
- extern void add_history(char *);
- static char *rlinebuf;
- #endif
-
- char *prompt, *prompt2;
- boolean rcrc;
- char *histstr;
- int histfd;
-
- static int dead(void);
- static int fdgchar(void);
- static int stringgchar(void);
- static void history(void);
- static void ugdead(int);
-
- static char *inbuf;
- static SIZE_T istacksize, chars_out, chars_in;
- static boolean eofread = FALSE;
- static Input *istack, *itop;
-
- static int (*realgchar)(void);
- static void (*realugchar)(int);
-
- int last;
-
- int gchar(void) {
- if (eofread) {
- eofread = FALSE;
- return last = EOF;
- }
- return realgchar();
- }
-
- 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++]);
- }
-
- /*
- 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 */
- do {
- #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");
- efree(rlinebuf);
- }
- } else
- #endif
- chars_in = read(istack->fd, inbuf + 2, BUFSIZE);
- } while (chars_in == -1 && errno == EINTR); /* Suppose it was interrupted by a signal */
-
- switch (chars_in) {
- case 0:
- return last = EOF;
- case -1:
- uerror("read");
- rc_exit(1);
- /* NOTREACHED */
- default:
- 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 */
-
- void initinput() {
- istack = itop = ealloc(istacksize = 256 * sizeof (Input));
- istack->t = FD;
- istack->fd = -1;
- realugchar = ugalive;
- }
-
- /* push an input source onto the stack. set up a new input buffer, and set the right getchar() */
-
- void pushinput(int /*enum inputtype*/ t,...) {
- SIZE_T count, idiff;
- char **a;
- va_list ap;
-
- va_start(ap, t);
-
- istack->index = chars_out;
- istack->read = chars_in;
- istack->ibuf = inbuf;
- istack->lineno = lineno;
- istack++;
-
- idiff = istack - itop;
-
- if (idiff >= istacksize / sizeof (Input)) {
- itop = erealloc(itop, istacksize *= 2);
- istack = itop + idiff;
- }
-
- istack->t = t;
- if (t == FD) {
- istack->fd = va_arg(ap, int);
- realgchar = fdgchar;
- inbuf = ealloc(BUFSIZE + 2);
- } else {
- count = strarraylen(a = va_arg(ap, char **));
- sprint((inbuf = ealloc(count + 3)) + 2, "%a", a);
- realgchar = stringgchar;
- }
-
- va_end(ap);
-
- realugchar = ugalive;
- chars_out = 2;
- chars_in = 0;
- lineno = 1;
- }
-
- /* remove an input source from the stack. restore the right kind of getchar (string,fd) etc. */
-
- void popinput() {
- if (istack->t == FD)
- close(istack->fd);
- efree(inbuf);
-
- --istack;
-
- realgchar = (istack->t == STRING ? stringgchar : fdgchar);
-
- if (istack->fd == -1) { /* top of input stack */
- realgchar = dead;
- realugchar = ugdead;
- }
-
- inbuf = istack->ibuf;
- chars_out = istack->index;
- chars_in = istack->read;
- lineno = istack->lineno;
- }
-
- /* flush input characters upto newline. Used by scanerror() */
-
- 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() */
-
- Node *doit(boolean execit) {
- boolean eof;
- jmp_buf j;
- Estack e1, e2;
-
- setjmp(j);
- except(ERROR, j, &e1);
-
- for (eof = FALSE; !eof;) {
- except(ARENA, NULL, &e2);
-
- 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) {
- Node *f;
- List *s;
-
- if ((f = fnlookup("prompt")) != NULL)
- walk(treecpy(f, nalloc), TRUE);
-
- 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() < 0)
- rc_raise(ERROR);
-
- eof = (last == EOF); /* "last" can be clobbered during a walk() */
-
- if (execit && parsetree != NULL)
- walk(parsetree, TRUE);
-
- unexcept(); /* ARENA */
- }
-
- popinput();
- unexcept(); /* ERROR */
- return parsetree;
- }
-
- /* parse a function imported from the environment */
-
- Node *parseline(char *extdef) {
- char *in[2];
- int i = interactive;
- Node *ret;
-
- in[0] = extdef;
- in[1] = NULL;
- interactive = FALSE;
- pushinput(STRING, in);
- ret = doit(FALSE);
- interactive = i;
- return ret;
- }
-
- /* 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, APPEND);
- if (histfd < 0) {
- uerror(histstr);
- efree(histstr);
- histstr = NULL;
- }
- }
-
- /*
- 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);
- }
-