home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume23
/
rc
/
part03
< prev
next >
Wrap
Text File
|
1991-10-18
|
56KB
|
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.