home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-10-18 | 55.1 KB | 2,392 lines |
- Newsgroups: comp.sources.misc
- From: byron@archone.tamu.edu (Byron Rakitzis)
- Subject: v23i063: rc - A Plan 9 shell reimplementation, v1.2, Part03/06
- Message-ID: <1991Oct18.034340.2235@sparky.imd.sterling.com>
- X-Md4-Signature: 1ebbb60177aa5936f82228f7d564ba5f
- Date: Fri, 18 Oct 1991 03:43:40 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: byron@archone.tamu.edu (Byron Rakitzis)
- Posting-number: Volume 23, Issue 63
- Archive-name: rc/part03
- Environment: UNIX
- Supersedes: rc: Volume 20, Issue 10-13
-
- #!/bin/sh
- # do not concatenate these parts, unpack them in order with /bin/sh
- # file hfix.awk continued
- #
- if test ! -r _shar_seq_.tmp; then
- echo 'Please unpack part 1 first!'
- exit 1
- fi
- (read Scheck
- if test "$Scheck" != 3; then
- echo Please unpack part "$Scheck" next!
- exit 1
- else
- exit 0
- fi
- ) < _shar_seq_.tmp || exit 1
- if test ! -f _shar_wnt_.tmp; then
- echo 'x - still skipping hfix.awk'
- else
- echo 'x - continuing file hfix.awk'
- sed 's/^X//' << 'SHAR_EOF' >> 'hfix.awk' &&
- X print "extern void b_exec(), funcall(), b_dot(), b_builtin();";
- X next;
- X }
- /^extern void qsort\(void \*, SIZE_T, SIZE_T, int \(\*\)\(const void \*, const void \*\)\);$/ {
- X print "extern void qsort();";
- X next;
- X }
- /^extern Node \*treecpy\(Node \*s, void \*\(\*\)\(SIZE_T\)\);$/ {
- X print "extern Node *treecpy();";
- X next;
- X }
- /^extern .*\);/ || /^typedef .*\);/ {
- X n = split($0, tmp1, "(");
- X if (n != 2) {
- X print | "sh -c 'cat >&2'";
- X print "ERROR with left parens: ", n | "sh -c 'cat >&2'";
- X next;
- X }
- X printf "%s();\n", tmp1[1];
- X next;
- X }
- X { print; }
- SHAR_EOF
- echo 'File hfix.awk is complete' &&
- chmod 0644 hfix.awk ||
- echo 'restore of hfix.awk failed'
- Wc_c="`wc -c < 'hfix.awk'`"
- test 714 -eq "$Wc_c" ||
- echo 'hfix.awk: original size 714, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= history.1 ==============
- if test -f 'history.1' -a X"$1" != X"-c"; then
- echo 'x - skipping history.1 (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting history.1 (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'history.1' &&
- .\" rc.1
- .\"-------
- .\" Dd distance to space vertically before a "display"
- .\" These are what n/troff use for interparagraph distance
- .\"-------
- .if t .nr Dd .4v
- .if n .nr Dd 1v
- .\"-------
- .\" Ds begin a display
- .\"-------
- .de Ds
- .RS \\$1
- .sp \\n(Ddu
- .nf
- ..
- .\"-------
- .\" De end a display (no trailing vertical spacing)
- .\"-------
- .de De
- .fi
- .RE
- ..
- .\"-------
- .\" I stole the Xf macro from the -man macros on my machine (originally
- .\" "}S", I renamed it so that it won't conflict).
- .\"-------
- .\" Set Cf to the name of the constant width font.
- .\" It will be "C" or "(CW", typically.
- .\" NOTEZ BIEN the lines defining Cf must have no trailing white space:
- .\"-------
- .if t .ds Cf C
- .if n .ds Cf R
- .\"-------
- .\" Rc - Alternate Roman and Courier
- .\"-------
- .de Rc
- .Xf 1 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Ic - Alternate Italic and Courier
- .\"-------
- .de Ic
- .Xf 2 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Bc - Alternate Bold and Courier
- .\"-------
- .de Bc
- .Xf 3 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Cr - Alternate Courier and Roman
- .\"-------
- .de Cr
- .Xf \\*(Cf 1 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Ci - Alternate Courier and Italic
- .\"-------
- .de Ci
- .Xf \\*(Cf 2 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Cb - Alternate Courier and Bold
- .\"-------
- .de Cb
- .Xf \\*(Cf 3 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
- ..
- .\"-------
- .\" Xf - Alternate fonts
- .\"
- .\" \$1 - first font
- .\" \$2 - second font
- .\" \$3 - desired word with embedded font changes, built up by recursion
- .\" \$4 - text for first font
- .\" \$5 - \$9 - remaining args
- .\"
- .\" Every time we are called:
- .\"
- .\" If there is something in \$4
- .\" then Call ourself with the fonts switched,
- .\" with a new word made of the current word (\$3) and \$4
- .\" rendered in the first font,
- .\" and with the remaining args following \$4.
- .\" else We are done recursing. \$3 holds the desired output
- .\" word. We emit \$3, change to Roman font, and restore
- .\" the point size to the default.
- .\" fi
- .\"
- .\" Use Xi to add a little bit of space after italic text.
- .\"-------
- .de Xf
- .ds Xi
- .if "\\$1"2" .if !"\\$5"" .ds Xi \^
- .if "\\$1"I" .if !"\\$5"" .ds Xi \^
- .ie !"\\$4"" \{\
- . Xf \\$2 \\$1 "\\$3\\f\\$1\\$4\\*(Xi" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
- .\}
- .el \{\\$3
- . ft R \" Restore the default font, since we don't know
- . \" what the last font change was.
- . ps 10 \" Restore the default point size, since it might
- . \" have been changed by an argument to this macro.
- .\}
- ..
- .TH HISTORY 1 "30 July 1991"
- .SH NAME
- \-, \-\-, \-p, \-\-p \- shell history programs
- .SH SYNOPSIS
- .B \-
- [
- .I pattern
- ]
- .I substitutions
- ]
- .SH DESCRIPTION
- This set of programs provides a crude history mechanism for the shell
- .IR rc (1).
- It is based on the v8 UNIX programs =, ==, etc.
- .PP
- The program
- .I \-
- runs the shell on the command it is requested to find.
- The program
- .I \-\-
- edits that command first.
- .I \-p
- and
- .I \-\-p
- are similar, except that they print the final command on their standard
- output instead of running the shell.
- .PP
- The commands work by looking for a file named by the environment variable
- .Cr $history ,
- and by searching for previous commands in this file.
- Old commands can be edited, or simply re-executed according to the rules
- below:
- .PP
- A command is searched for by examining the lines in
- .Cr $history
- in reverse order. Lines which contain a previous invokation of the history
- program itself are ignored. If one or more
- .I pattern
- is supplied on the command line, then the patterns are used as a means of
- limiting the search.
- Patterns match any substring of a previous command, and if more than one
- pattern is present then all patterns must be matched before a command
- is selected.
- .PP
- Substitutions may also be specified on the command line. These have
- the syntax:
- .Ds
- .Cr old:new
- .De
- .PP
- (Note that the
- .I old
- pattern is used as a search-limiting pattern also.)
- Substitutions happen on the first match.
- .PP
- Finally, a command may be edited in a crude line-mode fashion:
- The line to be edited is printed out, and below it the user
- supplies modifications to the command:
- .TP
- .B any character
- Replaces the character above.
- .TP
- .B space or tab
- Skips over the above character(s).
- .TP
- .B #
- Deletes one character.
- .TP
- .B %
- Replaces one character with a space.
- .TP
- .B ^
- Inserts the rest of the typed line just before the character.
- .TP
- .B $
- Deletes the rest of the line from that character on, and replaces
- it with the rest of the typed line.
- .TP
- .B \-
- Backs up to a previous command satisfying the same matching
- constraints.
- .SH EXAMPLES
- The history programs work best when their output is reinterpreted by
- the shell using an
- .Cr eval
- command.
- This can be achieved by writing a shell function to perform the
- reinterpretation:
- .Ds
- .Cr "fn - -- {
- .Cr " comm = \`{$0^p $*}
- .Cr " if (! ~ $#comm 0) {
- .Cr " echo $comm
- .Cr " eval $comm
- .Cr " }
- .Cr "}
- SHAR_EOF
- chmod 0644 history.1 ||
- echo 'restore of history.1 failed'
- Wc_c="`wc -c < 'history.1'`"
- test 4951 -eq "$Wc_c" ||
- echo 'history.1: original size 4951, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= history.c ==============
- if test -f 'history.c' -a X"$1" != X"-c"; then
- echo 'x - skipping history.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting history.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'history.c' &&
- /*
- X history.c -- primitive history mechanism
- X
- X Paul Haahr & Byron Rakitzis, July 1991.
- X
- X This program mimics the att v8 = and == history programs.
- X The edit() algorithm was adapted from a similar program
- X that Boyd Roberts wrote, but otherwise all the code has
- X been written from scratch.
- X
- X edit() was subsequently redone by Hugh Redelmeier in order
- X to correctly deal with tab characters in the source line.
- X
- X BUGS:
- X There is an implicit assumption that commands are no
- X more than 1k characters long.
- */
- X
- #include <stdio.h>
- #include "stddef.h"
- #include "string.h"
- #include "stdlib.h"
- #include "unistd.h"
- X
- static char *id = "@(#) history.c 8/91";
- X
- enum chunk { CHUNKSIZE = 65536 };
- X
- #undef FALSE
- #undef TRUE
- X
- typedef enum { FALSE, TRUE } boolean;
- X
- static struct {
- X char *old, *new;
- } *replace;
- X
- static char **search, *progname, *history;
- static char me; /* typically ':' or '-' */
- static boolean editit = FALSE, printit = FALSE;
- static int nreplace = 0, nsearch = 0;
- static FILE *fp;
- X
- static void *ealloc(SIZE_T n) {
- X void *p = malloc(n);
- X if (p == NULL) {
- X perror("malloc");
- X exit(1);
- X }
- X return p;
- }
- X
- static void *erealloc(void *p, SIZE_T n) {
- X p = realloc(p, n);
- X if (p == NULL) {
- X perror("realloc");
- X exit(1);
- X }
- X return p;
- }
- X
- static char *newstr() {
- X return ealloc((SIZE_T)1024);
- }
- X
- static char *basename(char *s) {
- X char *t = strrchr(s, '/');
- X return (t == NULL) ? s : t + 1;
- }
- X
- /* stupid O(n^2) substring matching routine */
- X
- static char *isin(char *target, char *pattern) {
- X SIZE_T plen = strlen(pattern);
- X SIZE_T tlen = strlen(target);
- X for (; tlen >= plen; target++, --tlen)
- X if (strncmp(target, pattern, plen) == 0)
- X return target;
- X return NULL;
- }
- X
- /* replace the first match in the string with "new" */
- static char *sub(char *s, char *old, char *new) {
- X char *t, *u;
- X
- X t = isin(s, old);
- X u = newstr();
- X
- X *t = '\0';
- X while (*old != '\0')
- X old++, t++;
- X strcpy(u, s);
- X strcat(u, new);
- X strcat(u, t);
- X return u;
- }
- X
- static char *edit(char *s) {
- X char *final, *f, *end;
- X int col;
- X boolean ins;
- X
- start:
- X fprintf(stderr, "%s\n", s);
- X f = final = newstr();
- X end = s + strlen(s);
- X col = 0;
- X ins = FALSE;
- X
- X for (;; col++) {
- X int c = getchar();
- X
- X if (c == me && col == 0) {
- X int peekc = getchar();
- X if (peekc == '\n')
- X return NULL;
- X ungetc(peekc, stdin);
- X }
- X if (c == '\n') {
- X if (col == 0)
- X return s;
- X
- X while (s < end) /* copy remainder of string */
- X *f++ = *s++;
- X *f = '\0';
- X s = final;
- X goto start;
- X } else if (ins || s>=end) {
- X /* col need not be accurate -- tabs need not be interpreted */
- X *f++ = c;
- X } else {
- X switch (c) {
- X case '%':
- X c = ' ';
- X /* FALLTHROUGH */
- X default:
- X *f++ = c;
- X break;
- X case EOF:
- X exit(1);
- X /* NOTREACHED */
- X case ' ':
- X if (*s == '\t') {
- X int oldcol = col;
- X
- X for (;; col++) {
- X int peekc;
- X
- X if ((col&07) == 07) {
- X *f++ = '\t'; /* we spaced past a tab */
- X break;
- X }
- X peekc = getchar();
- X if (peekc != ' ') {
- X ungetc(peekc, stdin);
- X if (peekc != '\n') {
- X /* we spaced partially into a tab */
- X do {
- X *f++ = ' ';
- X oldcol++;
- X } while (oldcol <= col);
- X }
- X break;
- X }
- X }
- X } else {
- X *f++ = *s;
- X }
- X break;
- X case '#':
- X break;
- X case '$':
- X end = s; /* truncate s */
- X continue; /* skip incrementing s */
- X case '^':
- X ins = TRUE;
- X continue; /* skip incrementing s */
- X case '\t':
- X for (;; col++) {
- X if ((*f++ = s<end? *s++ : '\t') == '\t') {
- X col = col | 07; /* advance to before next tabstop */
- X }
- X if ((col&07) == 07) /* stop before tabstop */
- X break;
- X }
- X continue; /* skip incrementing s */
- X }
- X if (s<end && (*s!='\t' || (col&07)==07))
- X s++;
- X }
- X }
- }
- X
- static char *readhistoryfile(char **last) {
- X char *buf;
- X SIZE_T count, size;
- X long nread;
- X
- X if ((history = getenv("history")) == NULL) {
- X fprintf(stderr, "$history not set\n");
- X exit(1);
- X }
- X fp = fopen(history, "r+");
- X if (fp == NULL) {
- X perror(history);
- X exit(1);
- X }
- X
- X size = 0;
- X count = 0;
- X buf = ealloc(size = CHUNKSIZE);
- X while ((nread = fread(buf + count, sizeof (char), size - count, fp)) > 0) {
- X count += nread;
- X if (size - count == 0)
- X buf = erealloc(buf, size *= 4);
- X }
- X if (nread == -1) {
- X perror(history);
- X exit(1);
- X }
- X *last = buf + count;
- X return buf;
- }
- X
- static char *getcommand() {
- X char *s, *t;
- X static char *hist = NULL, *last;
- X
- X if (hist == NULL) {
- X hist = readhistoryfile(&last);
- X *--last = '\0'; /* trim final newline */
- X }
- X
- again: s = last;
- X if (s < hist)
- X return NULL;
- X while (*--s != '\n')
- X if (s <= hist) {
- X last = hist - 1;
- X return hist;
- X }
- X *s = '\0';
- X last = s++;
- X
- X /*
- X * if the command contains the "me" character at the start of the line
- X * or after any of [`{|()] then try again
- X */
- X
- X for (t = s; *t != '\0'; t++)
- X if (*t == me) {
- X char *u = t - 1;
- X while (u >= s && (*u == ' ' || *u == '\t'))
- X --u;
- X if (u < s || *u == '`' || *u == '{' || *u == '|' || *u == '(' || *u == ')')
- X goto again;
- X }
- X return s;
- }
- X
- int main(int argc, char **argv) {
- X int i;
- X char *s;
- X
- X s = progname = basename(argv[0]);
- X me = *s++;
- X if (*s == me) {
- X s++;
- X editit = TRUE;
- X }
- X if (*s == 'p') {
- X s++;
- X printit = TRUE;
- X }
- X if (*s != '\0') {
- X fprintf(stderr, "\"%s\": bad name for history program\n", progname);
- X exit(1);
- X }
- X
- X if (argc > 1) {
- X replace = ealloc((argc - 1) * sizeof *replace);
- X search = ealloc((argc - 1) * sizeof *search);
- X }
- X for (i = 1; i < argc; i++)
- X if ((s = strchr(argv[i], ':')) == NULL)
- X search[nsearch++] = argv[i];
- X else {
- X *(char *)s = '\0'; /* do we confuse ps too much? */
- X replace[nreplace].old = argv[i];
- X replace[nreplace].new = s + 1;
- X nreplace++;
- X }
- X
- next: s = getcommand();
- X if (s == NULL) {
- X fprintf(stderr, "command not matched\n");
- X return 1;
- X }
- X for (i = 0; i < nsearch; i++)
- X if (!isin(s, search[i]))
- X goto next;
- X for (i = 0; i < nreplace; i++)
- X if (!isin(s, replace[i].old))
- X goto next;
- X else
- X s = sub(s, replace[i].old, replace[i].new);
- X if (editit) {
- X s = edit(s);
- X if (s == NULL)
- X goto next;
- X }
- X fseek(fp, 0, 2); /* 2 == end of file. i.e., append command to $history */
- X fprintf(fp, "%s\n", s);
- X fclose(fp);
- X if (printit)
- X printf("%s\n", s);
- X else {
- X char *shell = getenv("SHELL");
- X
- X if (!editit)
- X fprintf(stderr, "%s\n", s);
- X if (shell == NULL)
- X shell = "/bin/sh";
- X execl(shell, basename(shell), "-c", s, NULL);
- X perror(shell);
- X exit(1);
- X }
- X return 0;
- }
- SHAR_EOF
- chmod 0644 history.c ||
- echo 'restore of history.c failed'
- Wc_c="`wc -c < 'history.c'`"
- test 6420 -eq "$Wc_c" ||
- echo 'history.c: original size 6420, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= input.c ==============
- if test -f 'input.c' -a X"$1" != X"-c"; then
- echo 'x - skipping input.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting input.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'input.c' &&
- /* input.c: i/o routines for files and pseudo-files (strings) */
- X
- #include <errno.h>
- #include "jbwrap.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"
- #include "footobar.h"
- X
- /*
- X warning, changes have been made to (fd|string)(gchar|ugchar) so that
- X you cannot unget EOF more than once. lex.c never does this, so I'm
- X safe, but if you unget EOF more than once, expect to be able to read
- X it back only once. The reason is that EOF is an int, whereas the buffers
- X are character buffers.
- */
- X
- typedef struct Input {
- X enum inputtype t;
- X char *ibuf;
- X int fd;
- X int index;
- X int read;
- X int lineno;
- X boolean saved;
- } Input;
- X
- #define BUFSIZE ((SIZE_T) 256)
- X
- #ifdef READLINE
- extern char *readline(char *);
- extern void add_history(char *);
- static char *rlinebuf;
- #endif
- X
- char *prompt, *prompt2;
- boolean rcrc;
- char *histstr;
- int histfd;
- X
- static int dead(void);
- static int fdgchar(void);
- static int stringgchar(void);
- static void history(void);
- static void ugdead(int);
- X
- static char *inbuf;
- static SIZE_T istacksize, chars_out, chars_in;
- static boolean eofread = FALSE, save_lineno = TRUE;
- static Input *istack, *itop;
- X
- static int (*realgchar)(void);
- static void (*realugchar)(int);
- X
- int last;
- X
- int gchar() {
- X if (eofread) {
- X eofread = FALSE;
- X return last = EOF;
- X }
- X return realgchar();
- }
- X
- void ugchar(int c) {
- X realugchar(c);
- }
- X
- static int dead() {
- X return last = EOF;
- }
- X
- static void ugdead(int c) {
- X return;
- }
- X
- static void ugalive(int c) {
- X if (c == EOF)
- X eofread = TRUE;
- X else
- X inbuf[--chars_out] = c;
- }
- X
- /* get the next character from a string. */
- X
- static int stringgchar() {
- X return last = (inbuf[chars_out] == '\0' ? EOF : inbuf[chars_out++]);
- }
- X
- /*
- X read a character from a file-descriptor. If GNU readline is defined, add a newline and doctor
- X the buffer to look like a regular fdgchar buffer.
- */
- X
- static int fdgchar() {
- X if (chars_out >= chars_in + 2) { /* has the buffer been exhausted? if so, replenish it */
- X while (1) {
- #ifdef READLINE
- X if (interactive && istack->fd == 0) {
- X rlinebuf = readline(prompt);
- X if (rlinebuf == NULL) {
- X chars_in = 0;
- X } else {
- X if (*rlinebuf != '\0')
- X add_history(rlinebuf);
- X chars_in = strlen(rlinebuf) + 1;
- X efree(inbuf);
- X inbuf = ealloc(chars_in + 3);
- X strcpy(inbuf+2, rlinebuf);
- X strcat(inbuf+2, "\n");
- X efree(rlinebuf);
- X }
- X } else
- #endif
- X {
- X int /* (ssize_t) */ r = read(istack->fd, inbuf + 2, BUFSIZE);
- X
- X if (r < 0) {
- X if (errno == EINTR)
- X continue; /* Suppose it was interrupted by a signal */
- X uerror("read");
- X rc_exit(1);
- X }
- X
- X chars_in = (SIZE_T) r;
- X }
- X break;
- X }
- X
- X if (chars_in == 0)
- X return last = EOF;
- X
- X chars_out = 2;
- X if (dashvee)
- X writeall(2, inbuf + 2, chars_in);
- X history();
- X }
- X return last = inbuf[chars_out++];
- }
- X
- /* set up the input stack, and put a "dead" input at the bottom, so that yyparse will always read eof */
- X
- void initinput() {
- X istack = itop = ealloc(istacksize = 256 * sizeof (Input));
- X istack->t = FD;
- X istack->fd = -1;
- X realugchar = ugalive;
- }
- X
- /* push an input source onto the stack. set up a new input buffer, and set the right getchar() */
- X
- void pushinput(int /*enum inputtype*/ t,...) {
- X SIZE_T count, idiff;
- X char **a;
- X va_list ap;
- X
- X va_start(ap, t);
- X
- X istack->index = chars_out;
- X istack->read = chars_in;
- X istack->ibuf = inbuf;
- X istack->lineno = lineno;
- X istack->saved = save_lineno;
- X istack++;
- X
- X idiff = istack - itop;
- X
- X if (idiff >= istacksize / sizeof (Input)) {
- X itop = erealloc(itop, istacksize *= 2);
- X istack = itop + idiff;
- X }
- X
- X istack->t = t;
- X if (t == FD) {
- X save_lineno = TRUE;
- X istack->fd = va_arg(ap, int);
- X realgchar = fdgchar;
- X inbuf = ealloc(BUFSIZE + 2);
- X } else {
- X count = strarraylen(a = va_arg(ap, char **));
- X save_lineno = va_arg(ap, boolean);
- X sprint((inbuf = ealloc(count + 3)) + 2, "%a", a);
- X realgchar = stringgchar;
- X }
- X
- X va_end(ap);
- X
- X realugchar = ugalive;
- X chars_out = 2;
- X chars_in = 0;
- X if (save_lineno)
- X lineno = 1;
- X else
- X --lineno;
- }
- X
- /* remove an input source from the stack. restore the right kind of getchar (string,fd) etc. */
- X
- void popinput() {
- X if (istack->t == FD)
- X close(istack->fd);
- X efree(inbuf);
- X
- X --istack;
- X
- X realgchar = (istack->t == STRING ? stringgchar : fdgchar);
- X
- X if (istack->fd == -1) { /* top of input stack */
- X realgchar = dead;
- X realugchar = ugdead;
- X }
- X
- X inbuf = istack->ibuf;
- X chars_out = istack->index;
- X chars_in = istack->read;
- X if (save_lineno)
- X lineno = istack->lineno;
- X else
- X lineno++;
- X save_lineno = istack->saved;
- }
- X
- /* flush input characters upto newline. Used by scanerror() */
- X
- void flushu() {
- X int c;
- X
- X if (last == '\n' || last == EOF)
- X return;
- X
- X while ((c = gchar()) != '\n' && c != EOF)
- X ; /* skip to newline */
- X
- X if (c == EOF)
- X ugchar(c);
- }
- X
- /* the wrapper loop in rc: prompt for commands until EOF, calling yyparse and walk() */
- X
- Node *doit(boolean execit) {
- X boolean eof;
- X jbwrap j;
- X Estack e1, e2;
- X
- X if (dashen)
- X execit = FALSE;
- X
- X setjmp(j.j);
- X except(ERROR, &j, &e1);
- X
- X for (eof = FALSE; !eof;) {
- X except(ARENA, NULL, &e2);
- X
- X if (dashell) {
- X char *fname[3];
- X
- X fname[1] = concat(varlookup("home"),word("/.rcrc",NULL))->w;
- X fname[2] = NULL;
- X rcrc = TRUE;
- X dashell = FALSE;
- X b_dot(fname);
- X }
- X
- X if (interactive) {
- X List *s;
- X
- X if (fnlookup("prompt") != NULL) {
- X static char *arglist[] = { "prompt", NULL };
- X funcall(arglist);
- X }
- X
- X if ((s = varlookup("prompt")) != NULL) {
- #ifdef READLINE
- X prompt = s->w;
- #else
- X fprint(2,"%s",s->w);
- #endif
- X prompt2 = (s->n == NULL ? "" : s->n->w);
- X }
- X }
- X
- X inityy();
- X
- X if (yyparse() == 1 && execit)
- X rc_raise(ERROR);
- X
- X eof = (last == EOF); /* "last" can be clobbered during a walk() */
- X
- X if (parsetree != NULL) {
- X if (execit)
- X walk(parsetree, TRUE);
- X else if (dashex && dashen)
- X fprint(2, "%s\n", ptree(parsetree));
- X }
- X
- X unexcept(); /* ARENA */
- X }
- X
- X popinput();
- X unexcept(); /* ERROR */
- X return parsetree;
- }
- X
- /* parse a function imported from the environment */
- X
- Node *parseline(char *extdef) {
- X char *in[2];
- X int i = interactive;
- X Node *ret;
- X
- X in[0] = extdef;
- X in[1] = NULL;
- X interactive = FALSE;
- X pushinput(STRING, in, TRUE);
- X ret = doit(FALSE);
- X interactive = i;
- X return ret;
- }
- X
- /* write last command out to a file. Check to see if $history has changed, also */
- X
- static void history() {
- X List *histlist;
- X SIZE_T a;
- X
- X if (!interactive)
- X return;
- X
- X if ((histlist = varlookup("history")) == NULL) {
- X if (histstr != NULL) {
- X efree(histstr);
- X close(histfd);
- X histstr = NULL;
- X }
- X return;
- X }
- X
- X if (histstr == NULL || !streq(histstr, histlist->w)) { /* open new file */
- X if (histstr != NULL) {
- X efree(histstr);
- X close(histfd);
- X }
- X histstr = ecpy(histlist->w);
- X histfd = rc_open(histstr, APPEND);
- X if (histfd < 0) {
- X uerror(histstr);
- X efree(histstr);
- X histstr = NULL;
- X varrm("history", FALSE);
- X }
- X }
- X
- X /*
- X small unix hack: since read() reads only up to a newline from a terminal, then
- X presumably this write() will write at most only one input line at a time.
- X */
- X
- X for (a = 2; a < chars_in + 2; a++) { /* skip empty lines and comments in history. */
- X if (inbuf[a] == '#' || inbuf[a] == '\n')
- X return;
- X if (inbuf[a] != ' ' && inbuf[a] != '\t')
- X break;
- X }
- X writeall(histfd, inbuf + 2, chars_in);
- }
- SHAR_EOF
- chmod 0644 input.c ||
- echo 'restore of input.c failed'
- Wc_c="`wc -c < 'input.c'`"
- test 7405 -eq "$Wc_c" ||
- echo 'input.c: original size 7405, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= input.h ==============
- if test -f 'input.h' -a X"$1" != X"-c"; then
- echo 'x - skipping input.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting input.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'input.h' &&
- enum inputtype { FD, STRING };
- X
- #define EOF (-1)
- X
- extern void initinput(void);
- extern Node *parseline(char *);
- extern int gchar(void);
- extern void ugchar(int);
- extern Node *doit(boolean);
- extern void flushu(void);
- extern void pushinput(int /*enum inputtype*/,...);
- extern void popinput(void);
- extern int last;
- X
- extern boolean rcrc;
- extern char *histstr;
- extern int histfd;
- SHAR_EOF
- chmod 0644 input.h ||
- echo 'restore of input.h failed'
- Wc_c="`wc -c < 'input.h'`"
- test 373 -eq "$Wc_c" ||
- echo 'input.h: original size 373, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= jbwrap.h ==============
- if test -f 'jbwrap.h' -a X"$1" != X"-c"; then
- echo 'x - skipping jbwrap.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting jbwrap.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'jbwrap.h' &&
- /* certain braindamaged compilers don't define jmp_buf as an array, so... */
- X
- #include <setjmp.h>
- X
- typedef struct jbwrap {
- X jmp_buf j;
- } jbwrap;
- X
- extern int setjmp(jmp_buf);
- extern void longjmp(jmp_buf, int);
- SHAR_EOF
- chmod 0644 jbwrap.h ||
- echo 'restore of jbwrap.h failed'
- Wc_c="`wc -c < 'jbwrap.h'`"
- test 209 -eq "$Wc_c" ||
- echo 'jbwrap.h: original size 209, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= lex.c ==============
- if test -f 'lex.c' -a X"$1" != X"-c"; then
- echo 'x - skipping lex.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting lex.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'lex.c' &&
- /* lex.c: rc's lexical analyzer */
- X
- #include "rc.h"
- #include "lex.h"
- #include "y.tab.h"
- #include "nalloc.h"
- #include "input.h"
- #include "utils.h"
- #include "hash.h"
- #include "heredoc.h"
- X
- /*
- X Special characters (i.e., "non-word") in rc:
- X \t \n # ; & | ^ $ = ~ ` ' { } @ ! ( ) < > \
- X
- X The lexical analyzer is fairly straightforward. The only really unclean part
- X concerns backslash continuation and "double backslashes". A backslash followed by
- X a newline is treated as a space, otherwise backslash is not a special characeter
- X (i.e., it can be part of a word). This introduces a host of unwanted special
- X cases. In our case, \ cannot be a word character, since we wish to read in all
- X word characters in a tight loop.
- X
- X Note: to save the trouble of declaring these arrays with TRUEs and FALSEs, I am assuming
- X that FALSE = 0, TRUE = 1. (and so is it declared in rc.h)
- */
- X
- #define BUFSIZE ((SIZE_T) 1000) /* malloc hates power of 2 buffers? */
- #define BUFMAX (8 * BUFSIZE) /* How big the buffer can get before we re-allocate the
- X space at BUFSIZE again. Premature optimization? Maybe.
- X */
- X
- enum wordstates { NW, RW, KW }; /* "nonword", "realword", "keyword" */
- X
- static void getpair(int);
- X
- int lineno;
- X
- const char nw[] = {
- X 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
- X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
- X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- X 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- X
- const char dnw[] = {
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
- X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
- X 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- X 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
- };
- X
- static SIZE_T bufsize = BUFSIZE;
- static char *realbuf = NULL;
- static boolean newline = FALSE;
- static boolean errset = FALSE;
- static boolean prerror = FALSE;
- static enum wordstates word = NW;
- static int fd_left, fd_right;
- X
- #define checkfreecaret {if (word != NW) { word = NW; ugchar(c); return '^'; }}
- X
- enum filedescriptors { UNSET = -9, CLOSED = -1 };
- X
- int yylex() {
- X static boolean dollar = FALSE;
- X boolean saw_meta = FALSE;
- X int c;
- X SIZE_T i; /* The purpose of all these local assignments is to */
- X const char *meta; /* allow optimizing compilers like gcc to load these */
- X char *buf = realbuf; /* values into registers. On a sparc this is a */
- X YYSTYPE *y = &yylval; /* win, in code size *and* execution time */
- X
- X if (errset) {
- X errset = FALSE;
- X return '\n';
- X }
- X
- X /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */
- X meta = (dollar ? dnw : nw);
- X dollar = FALSE;
- X
- X if (newline) {
- X --lineno; /* slight space optimization; print_prompt2() always increments lineno */
- X print_prompt2();
- X newline = FALSE;
- X }
- X
- top: while ((c = gchar()) == ' ' || c == '\t')
- X word = NW;
- X
- X if (c == EOF)
- X return END;
- X
- X if (!meta[(unsigned char) c]) { /* it's a word or keyword. */
- X checkfreecaret;
- X word = RW;
- X i = 0;
- X read: do {
- X buf[i++] = c;
- X if (c == '?' || c == '[' || c == '*')
- X saw_meta = TRUE;
- X if (i >= bufsize)
- X buf = realbuf = erealloc(buf, bufsize *= 2);
- X } while ((c = gchar()) != EOF && !meta[(unsigned char) c]);
- X if (c == '\\') {
- X anotherbs: if ((c = gchar()) == '\n') {
- X print_prompt2();
- X c = ' '; /* Pretend a space was read */
- X } else {
- X bs: buf[i++] = '\\';
- X if (i >= bufsize)
- X buf = realbuf = erealloc(buf, bufsize *= 2);
- X if (!meta[(unsigned char) c])
- X goto read;
- X if (c == '\\')
- X goto anotherbs;
- X }
- X }
- X ugchar(c);
- X buf[i] = '\0';
- X word = KW;
- X if (i == 2) {
- X if (*buf == 'i' && buf[1] == 'f') return IF;
- X if (*buf == 'f' && buf[1] == 'n') return FN;
- X if (*buf == 'i' && buf[1] == 'n') return IN;
- X }
- X if (streq(buf, "for")) return FOR;
- X if (streq(buf, "else")) return ELSE;
- X if (streq(buf, "switch")) return SWITCH;
- X if (streq(buf, "while")) return WHILE;
- X if (streq(buf, "case")) return CASE;
- X word = RW;
- X y->word.w = ncpy(buf);
- X if (saw_meta) {
- X char *r, *s;
- X
- X y->word.m = nalloc(strlen(buf) + 1);
- X for (r = buf, s = y->word.m; *r != '\0'; r++, s++)
- X *s = (*r == '?' || *r == '[' || *r == '*');
- X } else {
- X y->word.m = NULL;
- X }
- X return WORD;
- X }
- X
- X if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') {
- X checkfreecaret;
- X if (c == '!' || c == '@' || c == '~')
- X word = KW;
- X }
- X
- X switch (c) {
- X case '\0':
- X pr_error("warning: null character ignored");
- X goto top;
- X case '!':
- X return BANG;
- X case '@':
- X return SUBSHELL;
- X case '~':
- X return TWIDDLE;
- X case '`':
- X c = gchar();
- X if (c == '`')
- X return BACKBACK;
- X ugchar(c);
- X return '`';
- X case '$':
- X dollar = TRUE;
- X c = gchar();
- X if (c == '#')
- X return COUNT;
- X if (c == '^')
- X return FLAT;
- X ugchar(c);
- X return '$';
- X case '\'':
- X word = RW;
- X i = 0;
- X do {
- X buf[i++] = c;
- X if (c == '\n')
- X print_prompt2();
- X if (c == EOF) {
- X word = NW;
- X scanerror("eof in quoted string");
- X return HUH;
- X }
- X if (i >= bufsize)
- X buf = realbuf = erealloc(buf, bufsize *= 2);
- X } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */
- X ugchar(c);
- X buf[i] = '\0';
- X y->word.w = ncpy(buf);
- X y->word.m = NULL;
- X return WORD;
- X case '\\':
- X if ((c = gchar()) == '\n') {
- X print_prompt2();
- X goto top; /* Pretend it was just another space. */
- X }
- X ugchar(c);
- X c = '\\';
- X checkfreecaret;
- X c = gchar();
- X i = 0;
- X goto bs;
- X case '(':
- X if (word == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */
- X c = SUB;
- X word = NW;
- X return c;
- X case '#':
- X while ((c = gchar()) != '\n') /* skip comment until newline */
- X if (c == EOF)
- X return END;
- X /* FALLTHROUGH */
- X case '\n':
- X lineno++;
- X newline = TRUE;
- X /* FALLTHROUGH */
- X case ';':
- X case '^':
- X case ')':
- X case '=':
- X case '{': case '}':
- X word = NW;
- X return c;
- X case '&':
- X word = NW;
- X c = gchar();
- X if (c == '&')
- X return ANDAND;
- X ugchar(c);
- X return '&';
- X case '|':
- X word = NW;
- X c = gchar();
- X if (c == '|')
- X return OROR;
- X getpair(c);
- X if (errset)
- X return HUH;
- X if ((y->pipe.left = fd_left) == UNSET)
- X y->pipe.left = 1; /* default to fd 1 */
- X if ((y->pipe.right = fd_right) == UNSET)
- X y->pipe.right = 0; /* default to fd 0 */
- X if (y->pipe.right == CLOSED) {
- X scanerror("expected digit after '='"); /* can't close a pipe */
- X return HUH;
- X }
- X return PIPE;
- X case '>':
- X c = gchar();
- X if (c == '>') {
- X c = gchar();
- X y->redir.type = APPEND;
- X } else
- X y->redir.type = CREATE;
- X y->redir.fd = 1;
- X goto common;
- X case '<':
- X c = gchar();
- X if (c == '<') {
- X c = gchar();
- X if (c == '<') {
- X c = gchar();
- X y->redir.type = HERESTRING;
- X } else {
- X y->redir.type = HEREDOC;
- X }
- X } else
- X y->redir.type = FROM;
- X y->redir.fd = 0;
- X common:
- X word = NW;
- X getpair(c);
- X if (errset)
- X return HUH;
- X if (fd_right == UNSET) { /* redirection, not dup */
- X if (fd_left != UNSET)
- X y->redir.fd = fd_left;
- X return REDIR;
- X } else { /* dup; recast yylval */
- X y->dup.type = y->redir.type;
- X y->dup.left = fd_left;
- X y->dup.right = fd_right;
- X return DUP;
- X }
- X default:
- X word = NW;
- X return c; /* don't know what it is, let yacc barf on it */
- X }
- }
- X
- void yyerror(const char *s) {
- X char *tok;
- X char tokbuf[128];
- X
- X if (prerror) { /* don't print "syntax error" if there's a more informative scanerror */
- X prerror = FALSE;
- X return;
- X }
- X
- X if (!interactive) {
- X if (word != NW)
- X tok = realbuf;
- X else if (last == EOF)
- X tok = "end of input";
- X else if (last == '\n')
- X tok = "end of line";
- X else
- X sprint(tok = tokbuf, (last < 32 || last > 126) ? "(decimal %d)" : "'%c'",last);
- X fprint(2,"line %d: %s near %s\n", lineno - (last == '\n'), s, tok);
- X } else
- X fprint(2,"%s\n",s);
- }
- X
- void scanerror(char *s) {
- X flushu(); /* flush upto newline */
- X yyerror(s);
- X errset = prerror = TRUE;
- }
- X
- void inityy() {
- X newline = FALSE;
- X word = NW;
- X hq = NULL;
- X
- X /* return memory to the system if the buffer got too large */
- X
- X if (bufsize > BUFMAX && realbuf != NULL) {
- X efree(realbuf);
- X bufsize = BUFSIZE;
- X realbuf = ealloc(bufsize);
- X } else if (realbuf == NULL)
- X realbuf = ealloc(bufsize);
- }
- X
- void print_prompt2() {
- X lineno++;
- #ifdef READLINE
- X prompt = prompt2;
- #else
- X if (interactive)
- X fprint(2,"%s",prompt2);
- #endif
- }
- X
- /*
- X Scan in a pair of integers for redirections like >[2=1]. CLOSED represents a closed file
- X descriptor (i.e., >[2=]) and UNSET represents an undesignated file descriptor (e.g.,
- X >[2] is represented as (2,UNSET).
- X
- X This function makes use of unsigned compares to make range tests in one compare operation.
- */
- X
- static void getpair(int c) {
- X int n;
- X
- X fd_left = fd_right = UNSET;
- X
- X if (c != '[') {
- X ugchar(c);
- X return;
- X }
- X
- X if ((unsigned int) (n = gchar() - '0') > 9) {
- X scanerror("expected digit after '['");
- X return;
- X }
- X
- X while ((unsigned int) (c = gchar() - '0') <= 9)
- X n = n * 10 + c;
- X
- X fd_left = n;
- X c += '0';
- X
- X switch (c) {
- X default:
- X scanerror("expected '=' or ']' after digit");
- X return;
- X case ']':
- X return;
- X case '=':
- X if ((unsigned int) (n = gchar() - '0') > 9) {
- X if (n != ']' - '0') {
- X scanerror("expected digit or ']' after '='");
- X return;
- X }
- X fd_right = CLOSED;
- X } else {
- X while ((unsigned int) (c = gchar() - '0') <= 9)
- X n = n * 10 + c;
- X if (c != ']' - '0') {
- X scanerror("expected ']' after digit");
- X return;
- X }
- X fd_right = n;
- X }
- X }
- }
- SHAR_EOF
- chmod 0644 lex.c ||
- echo 'restore of lex.c failed'
- Wc_c="`wc -c < 'lex.c'`"
- test 10411 -eq "$Wc_c" ||
- echo 'lex.c: original size 10411, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= lex.h ==============
- if test -f 'lex.h' -a X"$1" != X"-c"; then
- echo 'x - skipping lex.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting lex.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'lex.h' &&
- enum redirtype {
- X FROM, CREATE, APPEND, HEREDOC, HERESTRING
- };
- X
- typedef struct Pipe {
- X int left,right;
- } Pipe;
- X
- typedef struct Dup {
- X enum redirtype type;
- X int left,right;
- } Dup;
- X
- typedef struct Redir {
- X enum redirtype type;
- X int fd;
- } Redir;
- X
- typedef struct Word {
- X char *w;
- X char *m;
- } Word;
- X
- extern int yylex(void);
- extern void inityy(void);
- extern void yyerror(const char *);
- extern void scanerror(char *);
- extern void print_prompt2(void);
- X
- extern const char nw[], dnw[];
- SHAR_EOF
- chmod 0644 lex.h ||
- echo 'restore of lex.h failed'
- Wc_c="`wc -c < 'lex.h'`"
- test 476 -eq "$Wc_c" ||
- echo 'lex.h: original size 476, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= list.c ==============
- if test -f 'list.c' -a X"$1" != X"-c"; then
- echo 'x - skipping list.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting list.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'list.c' &&
- /* list.c: routines for manipulating the List type */
- X
- #include "rc.h"
- #include "utils.h"
- #include "list.h"
- X
- /*
- X These list routines assign meta values of null to the resulting lists;
- X it is impossible to glob with the value of a variable unless this value
- X is rescanned with eval---therefore it is safe to throw away the meta-ness
- X of the list.
- */
- X
- /* free a list from malloc space */
- X
- void listfree(List *p) {
- X if (p == NULL)
- X return;
- X listfree(p->n);
- X efree(p->w);
- X efree(p);
- }
- X
- /* copy of list in malloc space (for storing a variable) */
- X
- List *listcpy(List *s) {
- X List *r;
- X
- X if (s == NULL)
- X return NULL;
- X
- X r = enew(List);
- X r->w = ecpy(s->w);
- X r->m = NULL;
- X r->n = listcpy(s->n);
- X
- X return r;
- }
- X
- /* length of list */
- X
- SIZE_T listlen(List *s) {
- X SIZE_T size;
- X
- X for (size = 0; s != NULL; s = s->n)
- X size += strlen(s->w) + 1;
- X
- X return size;
- }
- X
- /* number of elements in list */
- X
- int listnel(List *s) {
- X int nel;
- X
- X for (nel = 0; s != NULL; s = s->n)
- X nel++;
- X
- X return nel;
- }
- SHAR_EOF
- chmod 0644 list.c ||
- echo 'restore of list.c failed'
- Wc_c="`wc -c < 'list.c'`"
- test 984 -eq "$Wc_c" ||
- echo 'list.c: original size 984, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= list.h ==============
- if test -f 'list.h' -a X"$1" != X"-c"; then
- echo 'x - skipping list.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting list.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'list.h' &&
- extern void listfree(List *);
- extern List *listcpy(List *);
- extern SIZE_T listlen(List *);
- extern int listnel(List *);
- SHAR_EOF
- chmod 0644 list.h ||
- echo 'restore of list.h failed'
- Wc_c="`wc -c < 'list.h'`"
- test 119 -eq "$Wc_c" ||
- echo 'list.h: original size 119, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= main.c ==============
- if test -f 'main.c' -a X"$1" != X"-c"; then
- echo 'x - skipping main.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting main.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
- /* main.c: handles initialization of rc and command line options */
- X
- #include <stdarg.h>
- #include "rc.h"
- #include "utils.h"
- #include "input.h"
- #include "nalloc.h"
- #include "hash.h"
- #include "lex.h"
- #include "open.h"
- #include "tree.h"
- #include "glom.h"
- #include "builtins.h"
- #include "parse.h"
- #include "status.h"
- #include "getopt.h"
- X
- boolean dashdee, dashee, dashvee, dashex, dashell, dasheye, dashen, interactive;
- int rc_pid;
- X
- #define REALLYNULL ((void *) 0) /* used to terminate a vararg list with NULL */
- X
- static void assigndefault(char *,...);
- X
- void main(int argc, char *argv[], char *envp[]) {
- X char *dashsee[2], pid[8], *dollarzero, *null[1];
- X int c;
- X
- X dashee = dashell = dashvee = dashex = dashdee = dashen = FALSE;
- X dashsee[0] = dashsee[1] = NULL;
- X dollarzero = argv[0];
- X rc_pid = getpid();
- X
- X dashell = (*argv[0] == '-'); /* Unix tradition */
- X
- X while ((c = getopt(argc, argv, "nleivdxc:")) != -1)
- X switch (c) {
- X case 'l':
- X dashell = TRUE;
- X break;
- X case 'e':
- X dashee = TRUE;
- X break;
- X case 'i':
- X dasheye = interactive = TRUE;
- X break;
- X case 'v':
- X dashvee = TRUE;
- X break;
- X case 'x':
- X dashex = TRUE;
- X break;
- X case 'd':
- X dashdee = TRUE;
- X break;
- X case 'c':
- X dashsee[0] = optarg;
- X goto quitopts;
- X case 'n':
- X dashen = TRUE;
- X break;
- X case '?':
- X exit(1);
- X }
- X
- quitopts:
- X argv += optind;
- X
- X /* use isatty() iff -i is not set, and iff the input is not from a script or -c flag */
- X if (!dasheye && dashsee[0] == NULL && *argv == NULL)
- X interactive = isatty(0);
- X
- X if (dashsee[0] != NULL) { /* rc -c must always ignore signals, e.g., for "rc -c sh" */
- X int i = interactive;
- X
- X interactive = TRUE;
- X inithandler();
- X interactive = i;
- X } else
- X inithandler();
- X inithash();
- X initparse();
- X assigndefault("prompt", "; ", "", REALLYNULL);
- X assigndefault("path", ".", "/bin", "/usr/bin", "/usr/ucb", REALLYNULL);
- X assigndefault("ifs", " ", "\t", "\n", REALLYNULL);
- X assigndefault("pid", sprint(pid, "%d", rc_pid), REALLYNULL);
- X initenv(envp);
- X initinput();
- X null[0] = NULL;
- X starassign(dollarzero, null, FALSE); /* assign $0 to $* */
- X
- X if (dashsee[0] != NULL) { /* input from the -c flag? */
- X if (*argv != NULL)
- X starassign(dollarzero, argv, FALSE);
- X pushinput(STRING, dashsee, TRUE);
- X } else if (*argv != NULL) { /* else from a file? */
- X b_dot(--argv);
- X rc_exit(getstatus());
- X } else { /* else stdin */
- X pushinput(FD, 0);
- X }
- X
- X dasheye = FALSE;
- X doit(TRUE);
- X rc_exit(getstatus());
- }
- X
- static void assigndefault(char *name,...) {
- X va_list ap;
- X List *l;
- X char *v;
- X
- X va_start(ap, name);
- X
- X for (l = NULL; (v = va_arg(ap, char *)) != NULL;)
- X l = append(l, word(v, NULL));
- X
- X varassign(name, l, FALSE);
- X
- X if (streq(name,"path"))
- X alias(name, l, FALSE);
- X
- X va_end(ap);
- }
- SHAR_EOF
- chmod 0644 main.c ||
- echo 'restore of main.c failed'
- Wc_c="`wc -c < 'main.c'`"
- test 2703 -eq "$Wc_c" ||
- echo 'main.c: original size 2703, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= match.c ==============
- if test -f 'match.c' -a X"$1" != X"-c"; then
- echo 'x - skipping match.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting match.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'match.c' &&
- /* match.c: pattern matching routines */
- X
- #include "rc.h"
- #include "utils.h"
- #include "match.h"
- X
- static int rangematch(char *, char);
- X
- /*
- X match() matches a single pattern against a single string.
- */
- X
- boolean match(char *p, char *m, char *s) {
- X int i = 0;
- X int j;
- X
- X if (m == NULL)
- X return streq(p, s);
- X
- X while(1) {
- X if (p[i] == '\0')
- X return *s == '\0';
- X else if (m[i]) {
- X switch (p[i++]) {
- X case '?':
- X if (*s++ == '\0')
- X return FALSE;
- X break;
- X case '*':
- X /* collapse multiple stars */
- X while (p[i] == '*' && m[i] == 1)
- X i++;
- X
- X /* star at end of pattern? */
- X if (p[i] == '\0')
- X return TRUE;
- X
- X while (*s != '\0')
- X if (match(p + i, m + i, s++))
- X return TRUE;
- X return FALSE;
- X case '[':
- X if (*s == '\0' || ((j = rangematch(p + i, *s++)) < 0))
- X return FALSE;
- X i += j;
- X break;
- X default:
- X fprint(2,"%c is not a metacharacter\n", p[i-1]);
- X return FALSE;
- X }
- X } else if (p[i++] != *s++) {
- X return FALSE;
- X }
- X }
- }
- X
- /*
- X From the ed(1) man pages (on ranges):
- X
- X The `-' is treated as an ordinary character if it occurs first
- X (or first after an initial ^) or last in the string.
- X
- X The right square bracket does not terminate the enclosed string
- X if it is the first character (after an initial `^', if any), in
- X the bracketed string.
- X
- X rangematch() matches a single character against a class, and returns
- X an integer offset to the end of the range on success, or -1 on
- X failure.
- */
- X
- static int rangematch(char *p, char c) {
- X char *orig = p;
- X boolean neg = (*p == '~');
- X boolean matched = FALSE;
- X
- X if (neg)
- X p++;
- X
- X if (*p == ']') {
- X p++;
- X matched = (c == ']');
- X }
- X
- X for (; *p != ']'; p++) {
- X if (*p == '\0')
- X return -1; /* bad syntax */
- X if (p[1] == '-' && p[2] != ']') { /* check for [..-..] but ignore [..-] */
- X if (c >= *p)
- X matched |= (c <= p[2]);
- X p += 2;
- X } else {
- X matched |= (*p == c);
- X }
- X }
- X
- X if (matched ^ neg)
- X return p - orig + 1; /* skip the right-bracket */
- X else
- X return -1;
- }
- X
- SHAR_EOF
- chmod 0644 match.c ||
- echo 'restore of match.c failed'
- Wc_c="`wc -c < 'match.c'`"
- test 1992 -eq "$Wc_c" ||
- echo 'match.c: original size 1992, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= match.h ==============
- if test -f 'match.h' -a X"$1" != X"-c"; then
- echo 'x - skipping match.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting match.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'match.h' &&
- extern boolean match(char *, char *, char *);
- SHAR_EOF
- chmod 0644 match.h ||
- echo 'restore of match.h failed'
- Wc_c="`wc -c < 'match.h'`"
- test 46 -eq "$Wc_c" ||
- echo 'match.h: original size 46, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= mksignal ==============
- if test -f 'mksignal' -a X"$1" != X"-c"; then
- echo 'x - skipping mksignal (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting mksignal (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'mksignal' &&
- #!/bin/sh
- # generate rc's internal signal table from signal.h
- X
- exec > sigmsgs.c
- X
- echo '#include "sigmsgs.h"'
- echo
- echo 'char *signals[][2] = {'
- X
- sed ' s/\/\*[ ]*//
- X s/[ ]*\*\///
- X s/([@*+!]) //
- X s/[ ]*([a-zA-Z,->& ]*)[ ]*//
- X s/[ ]*signal$//' $1 |
- awk '
- X BEGIN {
- X # assign to nomesg["SIGNAME"] to suppress a long message
- X nomesg["SIGINT"] = 1
- X nomesg["SIGPIPE"] = 1
- X # assign to mesg["SIGNAME"] to override a message
- X mesg["SIGHUP"] = "hangup"
- X mesg["SIGKILL"] = "killed"
- X mesg["SIGQUIT"] = "quit"
- X mesg["SIGTERM"] = "terminated"
- X mesg["SIGURG"] = "urgent condition on i/o channel"
- X mesg["SIGSTOP"] = "stop signal not from tty"
- X mesg["SIGTSTP"] = "stopped"
- X mesg["SIGCONT"] = "continue"
- X mesg["SIGCHLD"] = "child stop or exit"
- X mesg["SIGTTIN"] = "background tty read"
- X mesg["SIGTTOU"] = "background tty write"
- X # assign to ignore["SIGNAME"] to explicitly ignore a named signal
- X ignore["SIGMAX"] = 1
- X }
- X $1 == "#define" && $2 == "NSIG" && $3 ~ /^[0-9]+$/ { nsig = $3 }
- X $1 == "#define" && $2 ~ /^SIG/ && $3 ~ /^[0-9]+$/ && sig[$3] == "" && ignore[$2] == 0 {
- X sig[$3] = $2
- X if ($3 > max)
- X max = $3
- X if (mesg[$2] == "" && nomesg[$2] == 0) {
- X str = $4
- X for (i = 5; i <= NF; i++)
- X str = str " " $i
- X mesg[$2] = str
- X }
- X }
- X END {
- X if (nsig == 0)
- X nsig = max + 1
- X printf " !!, !!,\n"
- X for (i = 1; i < nsig; i++) {
- X if (sig[i] == "")
- X printf " !!, !!,\n"
- X else
- X printf " !%s!, !%s!,\n", sig[i], mesg[sig[i]]
- X }
- X }
- ' |
- tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ!' 'abcdefghijklmnopqrstuvwxyz"'
- X
- echo '};'
- X
- exec > sigmsgs.h
- X
- echo 'extern char *signals[][2];'
- X
- grep '^ ' sigmsgs.c | # the thing in quotes is ^<tab>
- awk '
- X { sum = sum + 1; }
- X END { print "#define NUMOFSIGNALS", sum }
- '
- SHAR_EOF
- chmod 0644 mksignal ||
- echo 'restore of mksignal failed'
- Wc_c="`wc -c < 'mksignal'`"
- test 1716 -eq "$Wc_c" ||
- echo 'mksignal: original size 1716, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= nalloc.c ==============
- if test -f 'nalloc.c' -a X"$1" != X"-c"; then
- echo 'x - skipping nalloc.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting nalloc.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'nalloc.c' &&
- /* nalloc.c: a simple single-arena allocator for command-line-lifetime allocation */
- X
- #include "rc.h"
- #include "utils.h"
- #include "nalloc.h"
- X
- static struct Block {
- X SIZE_T used;
- X SIZE_T size;
- X char *mem;
- X Block *n;
- } *fl, *ul;
- X
- /* alignto() works only with power of 2 blocks and assumes 2's complement arithmetic */
- #define alignto(m, n) ((m + n - 1) & ~(n - 1))
- #define BLOCKSIZE ((SIZE_T) 4096)
- X
- /* gets a block from malloc space and places it at the head of the used-list */
- X
- static void getblock(SIZE_T n) {
- X Block *r, *p;
- X
- X for (r = fl, p = NULL; r != NULL; p = r, r = r->n)
- X if (n <= r->size)
- X break;
- X
- X if (r != NULL) {
- X if (p != NULL)
- X p->n = r->n;
- X else
- X fl = r->n;
- X } else {
- X r = enew(Block);
- X r->mem = ealloc(alignto(n, BLOCKSIZE));
- X r->size = alignto(n, BLOCKSIZE);
- X }
- X
- X r->used = 0;
- X r->n = ul;
- X ul = r;
- }
- X
- /*
- X A fast single-arena allocator. Looks at the current block, and if there is not enough room,
- X it goes to getblock() for more. "ul" stands for "used list", and the head of the list is the
- X current block.
- */
- X
- void *nalloc(SIZE_T n) {
- X char *ret;
- X
- X n = alignto(n, sizeof (ALIGN_T));
- X
- X if (ul == NULL || n + ul->used >= ul->size)
- X getblock(n);
- X
- X ret = ul->mem + ul->used;
- X ul->used += n;
- X return ret;
- }
- X
- /*
- X Frees memory from nalloc space by putting it on the freelist. Returns free blocks to the
- X system, retaining at least MAXMEM bytes worth of blocks for nalloc.
- */
- X
- #define MAXMEM 500000
- X
- void nfree() {
- X Block *r;
- X SIZE_T count;
- X
- X if (ul == NULL)
- X return;
- X
- X for (r = ul; r->n != NULL; r = r->n)
- X ;
- X
- X r->n = fl;
- X fl = ul;
- X ul = NULL;
- X
- X for (r = fl, count = r->size; r->n != NULL; r = r->n, count += r->size) {
- X if (count >= MAXMEM) {
- X Block *tmp = r;
- X
- X r = r->n;
- X tmp->n = NULL; /* terminate the freelist */
- X
- X while (r != NULL) { /* free memory off the tail of the freelist */
- X tmp = r->n;
- X efree(r->mem);
- X efree(r);
- X r = tmp;
- X }
- X return;
- X }
- X }
- }
- X
- /*
- X "allocates" a new arena by zeroing out the old one. Up to the calling routine to keep
- X the old value of the block around.
- */
- X
- Block *newblock() {
- X Block *ret = ul;
- X
- X ul = NULL;
- X return ret;
- }
- X
- /* "restores" an arena to its saved value. */
- X
- void restoreblock(Block *b) {
- X nfree();
- X ul = b;
- }
- SHAR_EOF
- chmod 0644 nalloc.c ||
- echo 'restore of nalloc.c failed'
- Wc_c="`wc -c < 'nalloc.c'`"
- test 2229 -eq "$Wc_c" ||
- echo 'nalloc.c: original size 2229, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= nalloc.h ==============
- if test -f 'nalloc.h' -a X"$1" != X"-c"; then
- echo 'x - skipping nalloc.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting nalloc.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'nalloc.h' &&
- extern Block *newblock(void);
- extern void *nalloc(SIZE_T);
- extern void nfree(void);
- extern void restoreblock(Block *);
- X
- #undef offsetof
- #define offsetof(t, m) ((SIZE_T) &((t *)0)->m)
- X
- /* memory allocation abbreviation */
- #define nnew(x) ((x *) nalloc(sizeof(x)))
- #define ncpy(x) (strcpy((char *) nalloc(strlen(x) + 1), x))
- SHAR_EOF
- chmod 0644 nalloc.h ||
- echo 'restore of nalloc.h failed'
- Wc_c="`wc -c < 'nalloc.h'`"
- test 323 -eq "$Wc_c" ||
- echo 'nalloc.h: original size 323, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= node.h ==============
- if test -f 'node.h' -a X"$1" != X"-c"; then
- echo 'x - skipping node.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting node.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'node.h' &&
- enum nodetype {
- X rANDAND, ASSIGN, BACKQ, rBANG, BODY, CBODY, NOWAIT, BRACE, CONCAT,
- X rCOUNT, rELSE, rFLAT, rDUP, EPILOG, NEWFN, FORIN, rIF, QWORD,
- X rOROR, rPIPE, PRE, rREDIR, RMFN, ARGS, rSUBSHELL, rCASE,
- X rSWITCH, MATCH, VAR, VARSUB, rWHILE, rWORD, LAPPEND, NMPIPE
- };
- X
- typedef struct Node Node;
- X
- struct Node {
- X enum nodetype type;
- X union { char *s; int i; Node *p; } u[4];
- };
- SHAR_EOF
- chmod 0644 node.h ||
- echo 'restore of node.h failed'
- Wc_c="`wc -c < 'node.h'`"
- test 377 -eq "$Wc_c" ||
- echo 'node.h: original size 377, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= open.c ==============
- if test -f 'open.c' -a X"$1" != X"-c"; then
- echo 'x - skipping open.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting open.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'open.c' &&
- /* open.c: to insulate <fcntl.h> from the rest of rc. */
- X
- #include <fcntl.h>
- #include "lex.h"
- #include "open.h"
- X
- /* prototype for open() follows. comment out if necessary */
- X
- /*extern int open(const char *, int,...);*/
- extern void rc_error(const char *);
- X
- /*
- X Opens a file with the necessary flags. Assumes the following
- X declaration for enum redirtype:
- X
- X enum redirtype {
- X FROM, CREATE, APPEND, HEREDOC, HERESTRING
- X };
- */
- X
- static const int mode_masks[] = {
- X /* read */ O_RDONLY,
- X /* create */ O_TRUNC | O_CREAT | O_WRONLY,
- X /* append */ O_APPEND | O_CREAT | O_WRONLY
- };
- X
- int rc_open(const char *name, enum redirtype m) {
- X if ((unsigned int) m >= (sizeof(mode_masks)/sizeof(int)))
- X rc_error("bad mode passed to rc_open");
- X
- X return open(name, mode_masks[m], 0644);
- }
- SHAR_EOF
- chmod 0644 open.c ||
- echo 'restore of open.c failed'
- Wc_c="`wc -c < 'open.c'`"
- test 772 -eq "$Wc_c" ||
- echo 'open.c: original size 772, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= open.h ==============
- if test -f 'open.h' -a X"$1" != X"-c"; then
- echo 'x - skipping open.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting open.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'open.h' &&
- extern int rc_open(const char *, enum redirtype);
- SHAR_EOF
- chmod 0644 open.h ||
- echo 'restore of open.h failed'
- Wc_c="`wc -c < 'open.h'`"
- test 50 -eq "$Wc_c" ||
- echo 'open.h: original size 50, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= parse.h ==============
- if test -f 'parse.h' -a X"$1" != X"-c"; then
- echo 'x - skipping parse.h (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting parse.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'parse.h' &&
- extern Node *parsetree;
- extern int yyparse(void);
- extern void initparse(void);
- SHAR_EOF
- chmod 0644 parse.h ||
- echo 'restore of parse.h failed'
- Wc_c="`wc -c < 'parse.h'`"
- test 79 -eq "$Wc_c" ||
- echo 'parse.h: original size 79, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= parse.y ==============
- if test -f 'parse.y' -a X"$1" != X"-c"; then
- echo 'x - skipping parse.y (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting parse.y (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'parse.y' &&
- /* parse.y */
- X
- /*
- X * Adapted from rc grammar, v10 manuals, volume 2.
- X */
- X
- %{
- #include "stddef.h"
- #include "node.h"
- #include "lex.h"
- #include "tree.h"
- #include "heredoc.h"
- #include "parse.h"
- #include "utils.h"
- #undef NULL
- #define NULL 0
- #undef lint
- #define lint /* hush up gcc -Wall, leave out the dumb sccsid's. */
- static Node *star, *nolist;
- Node *parsetree; /* not using yylval because bison declares it as an auto */
- %}
- X
- %term ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN
- %term OROR PIPE REDIR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD HUH
- X
- %left WHILE ')' ELSE
- %left ANDAND OROR '\n'
- %left BANG SUBSHELL
- %left PIPE
- %right '$'
- %left SUB
- /*
- */
- X
- %union {
- X struct Node *node;
- X struct Redir redir;
- X struct Pipe pipe;
- X struct Dup dup;
- X struct Word word;
- X char *keyword;
- }
- X
- %type <redir> REDIR
- %type <pipe> PIPE
- %type <dup> DUP
- %type <word> WORD
- %type <keyword> keyword
- %type <node> assign body brace case cbody cmd cmdsa cmdsan comword epilog
- X first line paren redir sword simple iftail word words
- X
- %start rc
- X
- %%
- X
- rc : line end { parsetree = $1; YYACCEPT; }
- X | error end { yyerrok; parsetree = NULL; YYABORT; }
- X
- /* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */
- end : END /* EOF */ { if (!heredoc(1)) YYABORT; } /* flag error if there is a heredoc in the queue */
- X | '\n' { if (!heredoc(0)) YYABORT; } /* get heredoc on \n */
- X
- /* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */
- cmdsa : cmd ';'
- X | cmd '&' { $$ = ($1 != NULL ? newnode(NOWAIT,$1) : $1); }
- X
- /* a line is a single command, or a command terminated by ; or & followed by a line (recursive) */
- line : cmd
- X | cmdsa line { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); }
- X
- /* a body is like a line, only commands may also be terminated by newline */
- body : cmd
- X | cmdsan body { $$ = ($1 == NULL ? $2 : $2 == NULL ? $1 : newnode(BODY,$1,$2)); }
- X
- cmdsan : cmdsa
- X | cmd '\n' { $$ = $1; if (!heredoc(0)) YYABORT; } /* get h.d. on \n */
- X
- brace : '{' body '}' { $$ = $2; }
- X
- paren : '(' body ')' { $$ = $2; }
- X
- assign : first '=' word { $$ = newnode(ASSIGN,$1,$3); }
- X
- epilog : { $$ = NULL; }
- X | redir epilog { $$ = newnode(EPILOG,$1,$2); }
- X
- /* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */
- SHAR_EOF
- true || echo 'restore of parse.y failed'
- fi
- echo 'End of part 3'
- echo 'File parse.y is continued in part 4'
- echo 4 > _shar_seq_.tmp
- exit 0
- exit 0 # Just in case...
- --
- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
- Sterling Software, IMD UUCP: uunet!sparky!kent
- Phone: (402) 291-8300 FAX: (402) 291-4362
- Please send comp.sources.misc-related mail to kent@uunet.uu.net.
-