rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit f8f33b2674afaa2682c4a4ccec3e20439c53c144
parent ed03d0edf6f3a34f552db79e42f99a8c624f34ad
Author: tim <tim>
Date:   Tue, 20 May 1997 15:51:18 +0000

Initial revision

Diffstat:
Ainput.c | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asignal.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 435 insertions(+), 0 deletions(-)

diff --git a/input.c b/input.c @@ -0,0 +1,362 @@ +/* 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 int (*realgchar)(void); +static void (*realugchar)(int); + +int last; + +extern int gchar() { + int c; + + if (eofread) { + eofread = FALSE; + return last = EOF; + } + + while ((c = (*realgchar)()) == '\0') + pr_error("warning: null character ignored"); + + return c; +} + +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"); + efree(rlinebuf); + } + } else +#endif + { + long /*ssize_t*/ r = rc_read(istack->fd, inbuf + 2, BUFSIZE); + sigchk(); + if (r < 0) { + 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->t == iFd && 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 if interactive && $history is set */ + +static void history() { + List *hist; + SIZE_T a; + + if (!interactive || (hist = varlookup("history")) == NULL) + return; + + for (a = 0; a < chars_in; a++) { + char c = inbuf[a+2]; + + /* skip empty lines and comments */ + if (c == '#' || c == '\n') + break; + + /* line matches [ \t]*[^#\n] so it's ok to write out */ + if (c != ' ' && c != '\t') { + char *name = hist->w; + int fd = rc_open(name, rAppend); + if (fd < 0) { + uerror(name); + varrm(name, TRUE); + } else { + writeall(fd, inbuf + 2, chars_in); + close(fd); + } + break; + } + } +} + +/* close file descriptors after a fork() */ + +extern void closefds() { + Input *i; + for (i = istack; i != itop; --i) /* close open scripts */ + if (i->t == iFd && i->fd > 2) { + close(i->fd); + i->fd = -1; + } +} diff --git a/signal.c b/signal.c @@ -0,0 +1,73 @@ +/* signal.c: a Hugh-approved signal handler. */ + +#include <signal.h> +#include <setjmp.h> +#include "rc.h" +#include "sigmsgs.h" +#include "jbwrap.h" + +Jbwrap slowbuf; +volatile SIG_ATOMIC_T slow, interrupt_happened; +void (*sighandlers[NUMOFSIGNALS])(int); + +static volatile SIG_ATOMIC_T sigcount, caught[NUMOFSIGNALS]; + +extern void catcher(int s) { + if (caught[s] == 0) { + sigcount++; + caught[s] = 1; + } + signal(s, catcher); + interrupt_happened = TRUE; +#ifndef SVSIGS + if (slow) + longjmp(slowbuf.j, 1); +#endif +} + +extern void sigchk() { + void (*h)(int); + int s, i; + if (sigcount == 0) + return; /* ho hum; life as usual */ + if (forked) + exit(1); /* exit unconditionally on a signal in a child process */ + for (i = 0, s = -1; i < NUMOFSIGNALS; i++) + if (caught[i] != 0) { + s = i; + --sigcount; + caught[s] = 0; + break; + } + if (s == -1) + panic("all-zero sig vector with nonzero sigcount"); + if ((h = sighandlers[s]) == SIG_DFL) + panic("caught signal set to SIG_DFL"); + if (h == SIG_IGN) + panic("caught signal set to SIG_IGN"); + (*h)(s); +} + +extern void (*rc_signal(int s, void (*h)(int)))(int) { + void (*old)(int); + sigchk(); + old = sighandlers[s]; + if (h == SIG_DFL || h == SIG_IGN) { + signal(s, h); + sighandlers[s] = h; + } else { + sighandlers[s] = h; + signal(s, catcher); + } + return old; +} + +extern void initsignal() { + void (*h)(int); + int i; + for (i = 1; i < NUMOFSIGNALS; i++) { + if ((h = signal(i, SIG_DFL)) != SIG_DFL) + signal(i, h); + sighandlers[i] = h; + } +}