home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume23
/
rc
/
part02
< prev
next >
Wrap
Text File
|
1991-10-18
|
57KB
|
2,566 lines
Newsgroups: comp.sources.misc
From: byron@archone.tamu.edu (Byron Rakitzis)
Subject: v23i062: rc - A Plan 9 shell reimplementation, v1.2, Part02/06
Message-ID: <1991Oct18.034306.2126@sparky.imd.sterling.com>
X-Md4-Signature: e40b03bf1de892a0f76ae6f7a5d1c497
Date: Fri, 18 Oct 1991 03:43:06 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: byron@archone.tamu.edu (Byron Rakitzis)
Posting-number: Volume 23, Issue 62
Archive-name: rc/part02
Environment: UNIX
Supersedes: rc: Volume 20, Issue 10-13
#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file execve.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 2; 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 execve.c'
else
echo 'x - continuing file execve.c'
sed 's/^X//' << 'SHAR_EOF' >> 'execve.c' &&
X
/*
X NOTE: this file depends on a hack in footobar.c which places two free spots before
X av[][] so that execve does not have to call malloc.
*/
X
#include "rc.h"
#include <errno.h>
#include "lex.h"
#include "open.h"
X
#define giveupif(x) { if (x) goto fail; }
X
int my_execve(const char *path, const char **av, const char **ev) {
X int fd, len, fst, snd, end;
X boolean noarg;
X char pb[256]; /* arbitrary but generous limit */
X
X execve(path, av, ev);
X
X if (errno != ENOEXEC)
X return -1;
X
X fd = rc_open(path, FROM);
X
X giveupif(fd < 0);
X
X len = read(fd, pb, sizeof pb);
X close(fd);
X
X /*
X This execve() rejects all scripts which don't begin with #!
X This may have to be changed on some systems.
X */
X
X giveupif(len <= 0 || pb[0] != '#' || pb[1] != '!');
X
X for (fst = 2; fst < len && (pb[fst] == ' ' || pb[fst] == '\t'); fst++)
X ; /* skip leading whitespace */
X
X giveupif(fst == len);
X
X for (snd = fst; snd < len && pb[snd] != ' ' && pb[snd] != '\t' && pb[snd] != '\n'; snd++)
X ; /* skip first arg */
X
X giveupif(snd == len);
X
X noarg = (pb[snd] == '\n');
X
X pb[snd++] = '\0'; /* null terminate the first arg */
X
X if (!noarg) {
X while (snd < len && (pb[snd] == ' ' || pb[snd] == '\t'))
X snd++; /* skip whitespace to second arg */
X
X giveupif(snd == len);
X
X noarg = (pb[snd] == '\n'); /* could have trailing whitespace after only one arg */
X
X if (!noarg) {
X for (end = snd; end < len && pb[end] != ' ' && pb[end] != '\t' && pb[end] != '\n'; end++)
X ; /* skip to the end of the second arg */
X
X giveupif(end == len);
X
X if (pb[end] == '\n') {
X pb[end] = '\0'; /* null terminate the first arg */
X } else { /* else check for a spurious third arg */
X pb[end++] = '\0';
X
X while (end < len && (pb[end] == ' ' || pb[end] == '\t'))
X end++;
X
X giveupif(end == len || pb[end] != '\n');
X }
X }
X }
X
X *av = path;
X
X if (!noarg)
X *--av = pb + snd;
X *--av = pb + fst;
X
X execve(*av, av, ev);
X return -1;
X
fail: errno = ENOEXEC;
X return -1;
}
SHAR_EOF
echo 'File execve.c is complete' &&
chmod 0644 execve.c ||
echo 'restore of execve.c failed'
Wc_c="`wc -c < 'execve.c'`"
test 2017 -eq "$Wc_c" ||
echo 'execve.c: original size 2017, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= fn.c ==============
if test -f 'fn.c' -a X"$1" != X"-c"; then
echo 'x - skipping fn.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting fn.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'fn.c' &&
/*
X fn.c: functions for adding and deleting functions from the symbol table.
X Support for signal handlers is also found here.
*/
X
#include "rc.h"
#include <signal.h>
#ifdef PROTECT_JOB
#include <sys/ioctl.h>
#endif
#include "utils.h"
#include "status.h"
#include "tree.h"
#include "hash.h"
#include "footobar.h"
#include "walk.h"
#include "nalloc.h"
#include "sigmsgs.h"
#include "builtins.h"
#include "input.h"
X
static void fn_handler(int);
X
static Node *handlers[NUMOFSIGNALS], null_function;
static boolean runexit = FALSE;
X
/* set signals to default values for rc. this means that interactive shells ignore SIGQUIT, etc. */
X
void inithandler() {
X if (interactive) {
#ifdef PROTECT_JOB
X if (dashell) {
X extern int ioctl(int, long,...);
X
X setpgrp(rc_pid, rc_pid);
X ioctl(2, TIOCSPGRP, &rc_pid);
X }
#endif
X
X signal(SIGINT, sig);
X if (!dashdee) {
X signal(SIGQUIT, SIG_IGN);
X handlers[SIGQUIT] = &null_function;
X signal(SIGTERM, SIG_IGN);
X handlers[SIGTERM] = &null_function;
X }
X }
X null_function.type = BODY;
X null_function.u[0].p = null_function.u[1].p = NULL;
}
X
/* only run this in a child process! resets signals to their default values */
X
void setsigdefaults() {
X int i;
X
X /*
X General housekeeping: (setsigdefaults happens after fork(), so it's a convenient
X place to clean up)
X */
X
X interactive = FALSE;
X if (histstr != NULL) { /* Close an open history file */
X close(histfd);
X histstr = NULL; /* But prevent re-closing of the same file-descriptor */
X }
X
X /* Restore signals to SIG_DFL */
X
X for (i = 1; i < NUMOFSIGNALS; i++) { /* signal 0 is never used (bogus) */
X if (handlers[i] != NULL) {
X handlers[i] = NULL;
X signal(i, SIG_DFL);
X }
X }
X signal(SIGINT, SIG_DFL); /* sigint is special because its handler is normally */
X /* set to NULL, which doesn't get caught in the above loop */
X runexit = FALSE; /* No sigexit on subshells */
}
X
/* rc's exit. if runexit is set, run the sigexit function. */
X
void rc_exit(int stat) {
X static char *sigexit[2] = { "sigexit", NULL };
X
X if (runexit) {
X runexit = FALSE;
X funcall(sigexit);
X exit(getstatus());
X } else {
X exit(stat);
X }
}
X
/* the signal handler for all functions. calls walk() */
X
static void fn_handler(int s) {
X if (s < 0 || s >= NUMOFSIGNALS)
X rc_error("unknown signal!?");
X
X signal(s, fn_handler); /* sgi seems to require re-signalling */
X walk(handlers[s], TRUE);
}
X
/*
X assign a function in Node form. Check to see if the function is also a signal, and set the
X signal vectors appropriately.
*/
X
void fnassign(char *name, Node *def) {
X Node *newdef = treecpy(def == NULL ? &null_function : def, ealloc); /* important to do the treecopy first */
X Function *new = get_fn_place(name);
X int i;
X
X new->def = newdef;
X new->extdef = NULL;
X
X if (strncmp(name, "sig", sizeof "sig" - 3) == 0) { /* slight optimization */
#ifdef NOSIGCLD /* System V machines treat SIGCLD very specially */
X if (streq(name, "sigcld"))
X rc_error("can't trap SIGCLD");
#endif
X if (streq(name, "sigexit"))
X runexit = TRUE;
X for (i = 1; i < NUMOFSIGNALS; i++) /* zero is a bogus signal */
X if (streq(signals[i][0], name)) {
X if (newdef != NULL) {
X handlers[i] = newdef;
X signal(i, fn_handler);
X } else {
X handlers[i] = &null_function;
X signal(i, SIG_IGN);
X }
X break;
X }
X }
}
X
/* assign a function from the environment. store just the external representation */
X
void fnassign_string(char *extdef) {
X char *name = get_name(extdef+3); /* +3 to skip over "fn_" */
X Function *new;
X
X if (name == NULL)
X return;
X
X new = get_fn_place(name);
X new->def = NULL;
X new->extdef = ecpy(extdef);
}
X
/* return a function in Node form, evaluating an entry from the environment if necessary */
X
Node *fnlookup(char *name) {
X Function *look = lookup_fn(name);
X Node *ret;
X
X if (look == NULL)
X return NULL; /* not found */
X if (look->def != NULL)
X return look->def;
X if (look->extdef == NULL) /* function was set to null, e.g., fn foo {} */
X return &null_function;
X
X ret = parse_fn(name, look->extdef);
X
X if (ret == NULL) {
X efree(look->extdef);
X look->extdef = NULL;
X return &null_function;
X } else {
X return look->def = treecpy(ret, ealloc); /* Need to take it out of talloc space */
X }
}
X
/* return a function in string form (used by makeenv) */
X
char *fnlookup_string(char *name) {
X Function *look = lookup_fn(name);
X
X if (look == NULL)
X return NULL;
X if (look->extdef != NULL)
X return look->extdef;
X return look->extdef = fun2str(name, look->def);
}
X
/*
X remove a function from the symbol table. If it also defines a signal handler, restore the signal handler
X to its default value.
*/
X
void fnrm(char *name) {
X int i;
X
X for (i = 1; i < NUMOFSIGNALS; i++) /* signal 0 unused */
X if (streq(signals[i][0], name)) {
X handlers[i] = NULL;
X if (i == SIGINT)
X signal(i, sig); /* restore default signal handler for rc */
X else if ((i == SIGQUIT || i == SIGTERM) && !dashdee) {
X handlers[i] = &null_function;
X signal(i, SIG_IGN); /* ditto */
X } else {
X signal(i, SIG_DFL);
X }
X }
X
X if (streq(name, "sigexit"))
X runexit = FALSE;
X
X delete_fn(name);
}
X
void whatare_all_signals() {
X int i;
X
X for (i = 1; i < NUMOFSIGNALS; i++)
X if (*signals[i][0] != '\0')
X if (handlers[i] == NULL)
X fprint(1, "fn %s\n", signals[i][0]);
X else
X fprint(1, "fn %s {%s}\n", strprint(signals[i][0], FALSE, FALSE), ptree(handlers[i]));
}
X
void prettyprint_fn(int fd, char *name, Node *n) {
X fprint(fd, "fn %s {%s}\n", strprint(name, FALSE, FALSE), ptree(n));
}
SHAR_EOF
chmod 0644 fn.c ||
echo 'restore of fn.c failed'
Wc_c="`wc -c < 'fn.c'`"
test 5485 -eq "$Wc_c" ||
echo 'fn.c: original size 5485, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= footobar.c ==============
if test -f 'footobar.c' -a X"$1" != X"-c"; then
echo 'x - skipping footobar.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting footobar.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'footobar.c' &&
/*
X footobar.c: a collection of functions to convert internal representations of
X variables and functions to external representations, and vice versa
*/
X
#include "rc.h"
#include "utils.h"
#include "lex.h"
#include "footobar.h"
#include "nalloc.h"
#include "input.h"
#include "list.h"
X
#define FSCHAR '\1'
#define FSSTRING "\1"
X
static char *getenvw(char *, boolean);
static void funcat(char *);
static void strtree(Node *);
X
static char *fun;
static SIZE_T funsize, funpos;
X
#ifdef PROTECT_ENV
/* octalize -- protect an exported name from brain-dead shells */
X
static char hexchar[] = "0123456789abcdef";
X
static char *octalize(char *name) {
X int c;
X static char namebuf[1000];
X char *s = namebuf;
X
X while (1) {
X c = *(unsigned char *) name++;
X if (c == '\0') {
X *s++ = '\0';
X return namebuf;
X }
X if (!dnw[c] && c != '*') {
X if (c != '_')
X *s++ = c;
X else if (*name != '_')
X *s++ = c;
X else
X goto fallthrough;
X } else {
X fallthrough: *s++ = '_';
X *s++ = '_';
X *s++ = hexchar[(c >> 4) & 0xf];
X *s++ = hexchar[c & 0xf];
X }
X }
}
#endif
X
/* a specialized strcat, used in strtree */
X
static void funcat(char *s) {
X SIZE_T l = strlen(s);
X char *new;
X
X if (l + funpos > funsize) {
X new = nalloc(funsize *= 2);
X memcpy(new, fun, funpos);
X new[funpos] = 0;
X fun = new;
X }
X strcpy(fun + funpos, s);
X funpos += l;
}
X
/* used to turn a function in Node * form into something we can export to the environment */
X
char *fun2str(char *name, Node *s) {
#ifdef PROTECT_ENV
X name = octalize(name);
#endif
X fun = nalloc(funsize = 512);
X funpos = 0;
X funcat("fn_");
X funcat(name);
X funcat("={");
X strtree(s);
X funcat("}");
X return ecpy(fun); /* put into malloc space */
}
X
/* ptree is used by whatis in order to print the definition of a function to the terminal */
X
char *ptree(Node *s) {
X fun = nalloc(funsize = 512);
X funpos = 0;
X fun[0] = 0;
X strtree(s);
X return fun;
}
X
/* save some code space by gathering this operation in a function */
X
static void catredir(int i) {
X switch (i) {
X case CREATE: funcat(">"); break;
X case APPEND: funcat(">>"); break;
X case HEREDOC: funcat("<<"); break;
X case HERESTRING: funcat("<<<"); break;
X case FROM: funcat("<"); break;
X }
}
X
/* convert a function in Node * form into something rc can parse (and humans can read?) */
X
static void strtree(Node *n) {
X int defaultfd;
X char b[16];
X
X if (n == NULL) {
X funcat("()");
X return;
X }
X
X switch (n->type) {
X case rDUP:
X catredir(n->u[0].i);
X if (n->u[2].i != -1)
X sprint(b, "[%d=%d]", n->u[1].i, n->u[2].i);
X else
X sprint(b, "[%d=]", n->u[1].i);
X funcat(b);
X break;
X case rWORD:
X funcat(strprint(n->u[0].s, FALSE, FALSE));
X break;
X case QWORD:
X funcat(strprint(n->u[0].s, TRUE, FALSE));
X break;
X case BACKQ:
X if (n->u[0].p != NULL && n->u[0].p->type == VAR
X && n->u[0].p->u[0].p != NULL && n->u[0].p->u[0].p->type == rWORD
X && streq(n->u[0].p->u[0].p->u[0].s,"ifs")) {
X funcat("`{");
X } else {
X funcat("``");
X strtree(n->u[0].p);
X funcat("{");
X }
X strtree(n->u[1].p);
X funcat("}");
X break;
X case rBANG:
X funcat("! ");
X strtree(n->u[0].p);
X break;
X case rCASE:
X funcat("case ");
X strtree(n->u[0].p);
X break;
X case CBODY:
X if (n->u[0].p != NULL) {
X strtree(n->u[0].p);
X funcat(";");
X }
X if (n->u[1].p != NULL)
X strtree(n->u[1].p);
X break;
X case NOWAIT:
X strtree(n->u[0].p);
X funcat("&");
X break;
X case rCOUNT:
X funcat("$#");
X strtree(n->u[0].p);
X break;
X case rFLAT:
X funcat("$^");
X strtree(n->u[0].p);
X break;
X case RMFN:
X funcat("fn ");
X strtree(n->u[0].p);
X break;
X case rSUBSHELL:
X funcat("@ ");
X strtree(n->u[0].p);
X break;
X case VAR:
X funcat("$");
X strtree(n->u[0].p);
X break;
X case rANDAND:
X strtree(n->u[0].p);
X funcat("&&");
X strtree(n->u[1].p);
X break;
X case ASSIGN:
X strtree(n->u[0].p);
X funcat("=");
X strtree(n->u[1].p);
X break;
X case BODY:
X if (n->u[1].p != NULL)
X funcat("{");
X strtree(n->u[0].p);
X if (n->u[1].p != NULL) {
X funcat(";");
X strtree(n->u[1].p);
X funcat("}");
X }
X break;
X case BRACE:
X funcat("{");
X strtree(n->u[0].p);
X funcat("}");
X if (n->u[1].p != NULL)
X strtree(n->u[1].p);
X break;
X case CONCAT:
X strtree(n->u[0].p);
X funcat("^");
X strtree(n->u[1].p);
X break;
X case rELSE:
X funcat("{");
X strtree(n->u[0].p);
X funcat("}else ");
X strtree(n->u[1].p);
X break;
X case EPILOG:
X case PRE:
X strtree(n->u[0].p);
X if (n->u[1].p != NULL) {
X funcat(" ");
X strtree(n->u[1].p);
X }
X break;
X case NEWFN:
X funcat("fn ");
X strtree(n->u[0].p);
X funcat(" {");
X strtree(n->u[1].p);
X funcat("}");
X break;
X case rIF:
X funcat("if(");
X strtree(n->u[0].p);
X funcat(")");
X strtree(n->u[1].p);
X break;
X case rOROR:
X strtree(n->u[0].p);
X funcat("||");
X strtree(n->u[1].p);
X break;
X case ARGS:
X strtree(n->u[0].p);
X funcat(" ");
X strtree(n->u[1].p);
X break;
X case rSWITCH:
X funcat("switch(");
X strtree(n->u[0].p);
X funcat("){");
X strtree(n->u[1].p);
X funcat("}");
X break;
X case MATCH:
X funcat("~ ");
X strtree(n->u[0].p);
X funcat(" ");
X strtree(n->u[1].p);
X break;
X case VARSUB:
X funcat("$");
X strtree(n->u[0].p);
X funcat("(");
X strtree(n->u[1].p);
X funcat(")");
X break;
X case rWHILE:
X funcat("while(");
X strtree(n->u[0].p);
X funcat(")");
X strtree(n->u[1].p);
X break;
X case LAPPEND:
X funcat("(");
X strtree(n->u[0].p);
X funcat(" ");
X strtree(n->u[1].p);
X funcat(")");
X break;
X case FORIN:
X funcat("for(");
X strtree(n->u[0].p);
X funcat(" in ");
X strtree(n->u[1].p);
X funcat(")");
X strtree(n->u[2].p);
X break;
X case rPIPE:
X funcat("{");
X strtree(n->u[2].p);
X if (n->u[0].i == 1) {
X if (n->u[1].i == 0)
X sprint(b, "}|{");
X else
X sprint(b, "}|[1=%d]{", n->u[1].p);
X } else {
X if (n->u[1].i == 0)
X sprint(b, "}|[%d]{", n->u[0].p);
X else
X sprint(b, "}|[%d=%d]{", n->u[0].i, n->u[1].i);
X }
X funcat(b);
X strtree(n->u[3].p);
X funcat("}");
X break;
X case NMPIPE:
X defaultfd = (n->u[0].i == CREATE || n->u[0].i == APPEND);
X catredir(n->u[0].i);
X if (n->u[1].i != defaultfd) {
X sprint(b, "[%d]{", n->u[1].i);
X funcat(b);
X } else
X funcat("{");
X strtree(n->u[2].p);
X funcat("}");
X break;
X case rREDIR:
X defaultfd = (n->u[0].i == CREATE || n->u[0].i == APPEND);
X catredir(n->u[0].i);
X if (n->u[1].i != defaultfd) {
X sprint(b, "[%d]", n->u[1].i);
X funcat(b);
X }
X strtree(n->u[2].p);
X break;
X }
}
X
/* convert a List to a string, separating it with ^A characters. Used for exporting variables to the environment */
X
char *list2str(char *name, List *s) {
X SIZE_T size;
X List *t;
X char *w;
X
#ifdef PROTECT_ENV
X name = octalize(name);
#endif
X size = strlen(name) + listlen(s);
X
X w = ealloc(size + 2);
X t = s;
X strcpy(w, name);
X strcat(w, "=");
X strcat(w, t->w);
X for (s = s->n; s != NULL; s = s->n) {
X strcat(w, FSSTRING);
X strcat(w, s->w);
X }
X return w;
}
X
/* convert a List to an array, for execve() */
X
char **list2array(List *s, boolean print) {
X char **av;
X int i;
X
X /* 4 == 1 for the null terminator + 2 for the fake execve() + 1 for defaulting to sh */
X
X av = nalloc((listnel(s) + 4) * sizeof (char *));
X
X av += 3; /* hide the two free spots from rc (two for #! emulation, one for defaulting to sh) */
X
X if (print)
X fprint(2, "%l\n", s);
X
X for (i = 0; s != NULL; i++) {
X av[i] = s->w;
X s = s->n;
X }
X av[i] = NULL;
X return av;
}
X
/* figure out the name of a variable given an environment string. copy this into malloc space */
X
char *get_name(char *s) {
X int c;
X SIZE_T i;
X char *r, *namebuf;
#ifdef PROTECT_ENV
X char *h1, *h2;
#endif
X
X for (i = 0; s[i] != '\0' && s[i] != '='; i++)
X ;
X
X if (s[i] == '\0')
X return NULL;
X
X r = namebuf = ealloc(i + 1);
X
X while (1)
X switch (c = *s++) {
X case '=':
X *r++ = '\0';
X return namebuf;
#ifdef PROTECT_ENV
X case '_':
X if (*s == '_' && (h1 = strchr(hexchar, s[1])) != NULL && (h2 = strchr(hexchar, s[2])) != NULL) {
X *r++ = ((h1 - hexchar) << 4) | (h2 - hexchar);
X s += 3;
X break;
X }
X /* FALLTHROUGH */
#endif
X default:
X *r++ = c;
X break;
X }
}
X
/* get the next word from a variable's value as represented in the environment. */
X
static char *getenvw(char *s, boolean saw_alpha) {
X char *r;
X SIZE_T i,j;
X
X for (i = j = 0; s[i] != '\0' && s[i] != FSCHAR; i++)
X ;
X
X if (i == 0)
X if(s[i] == '\0' && !saw_alpha)
X return NULL;
X else {
X r = enew(char);
X *r = '\0';
X return r;
X }
X
X r = ealloc(i + j + 1);
X
X r[i + j] = '\0';
X
X while (i > 0) {
X --i;
X r[i + j] = s[i];
X }
X return r;
}
X
/* take an environment entry for a variable (elements ^A separated) and turn it into a List */
X
List *parse_var(char *name, char *extdef) {
X List *r, *top;
X char *f;
X boolean saw_alpha;
X
X top = r = enew(List);
X extdef = strchr(extdef, '=') + 1;
X
X
X if ((f = getenvw(extdef, FALSE)) == NULL)
X return NULL;
X
X while (1) {
X r->w = f;
X r->m = NULL;
X extdef += strlen(f);
X if (*extdef == FSCHAR) {
X extdef++;
X saw_alpha = TRUE;
X } else {
X saw_alpha = FALSE;
X }
X if ((f = getenvw(extdef, saw_alpha)) == NULL) {
X r->n = NULL;
X break;
X }
X r = r->n = enew(List);
X }
X return top;
}
X
/* get an environment entry for a function and have rc parse it. */
X
Node *parse_fn(char *name, char *extdef) {
X Node *def;
X int i;
X char *s, old[4];
X
X s = strchr(extdef, '=');
X
X if (s == NULL)
X return NULL;
X
X s -= 3;
X
X for (i = 0; i < 4; i++)
X old[i] = s[i];
X s[0] = 'f';
X s[1] = 'n';
X s[2] = ' ';
X s[3] = 'x';
X
X def = parseline(s);
X for (i = 0; i < 4; i++)
X s[i] = old[i];
X
X if (def == NULL || def->type != NEWFN)
X return NULL;
X else
X return def->u[1].p;
}
SHAR_EOF
chmod 0644 footobar.c ||
echo 'restore of footobar.c failed'
Wc_c="`wc -c < 'footobar.c'`"
test 9420 -eq "$Wc_c" ||
echo 'footobar.c: original size 9420, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= footobar.h ==============
if test -f 'footobar.h' -a X"$1" != X"-c"; then
echo 'x - skipping footobar.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting footobar.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'footobar.h' &&
extern char *fun2str(char *, Node *);
extern char *ptree(Node *);
extern char *list2str(char *, List *);
extern char **list2array(List *, boolean);
extern char *get_name(char *);
extern List *parse_var(char *, char *);
extern Node *parse_fn(char *, char *);
SHAR_EOF
chmod 0644 footobar.h ||
echo 'restore of footobar.h failed'
Wc_c="`wc -c < 'footobar.h'`"
test 258 -eq "$Wc_c" ||
echo 'footobar.h: original size 258, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= getopt.c ==============
if test -f 'getopt.c' -a X"$1" != X"-c"; then
echo 'x - skipping getopt.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting getopt.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'getopt.c' &&
/* getopt routine courtesy of David Sanderson */
#include "rc.h"
#include "utils.h"
#include "getopt.h"
X
int opterr = 1;
int optind = 1;
int optopt;
char *optarg;
X
int getopt(int argc, char **argv, char *opts) {
X static int sp = 1;
X int c;
X char *cp;
X
X if (optind == 0) /* reset getopt() */
X optind = sp = 1;
X
X if (sp == 1)
X if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') {
X return -1;
X } else if (strcmp(argv[optind], "--") == 0) {
X optind++;
X return -1;
X }
X optopt = c = argv[optind][sp];
X if (c == ':' || (cp=strchr(opts, c)) == 0) {
X fprint(2, "%s: illegal option -- %c\n", argv[0], c);
X if (argv[optind][++sp] == '\0') {
X optind++;
X sp = 1;
X }
X return '?';
X }
X if (*++cp == ':') {
X if(argv[optind][sp+1] != '\0') {
X optarg = &argv[optind++][sp+1];
X } else if(++optind >= argc) {
X fprint(2, "%s: option requires an argument -- %c\n", argv[0], c);
X sp = 1;
X return '?';
X } else
X optarg = argv[optind++];
X sp = 1;
X } else {
X if (argv[optind][++sp] == '\0') {
X sp = 1;
X optind++;
X }
X optarg = 0;
X }
X return c;
}
SHAR_EOF
chmod 0644 getopt.c ||
echo 'restore of getopt.c failed'
Wc_c="`wc -c < 'getopt.c'`"
test 1619 -eq "$Wc_c" ||
echo 'getopt.c: original size 1619, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= getopt.h ==============
if test -f 'getopt.h' -a X"$1" != X"-c"; then
echo 'x - skipping getopt.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting getopt.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'getopt.h' &&
extern int getopt(int, char **, char *);
X
extern int optind, opterr, optopt;
extern char *optarg;
SHAR_EOF
chmod 0644 getopt.h ||
echo 'restore of getopt.h failed'
Wc_c="`wc -c < 'getopt.h'`"
test 98 -eq "$Wc_c" ||
echo 'getopt.h: original size 98, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= glob.c ==============
if test -f 'glob.c' -a X"$1" != X"-c"; then
echo 'x - skipping glob.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting glob.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'glob.c' &&
/* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
X
#include <sys/types.h>
#include <sys/stat.h>
#include "rc.h"
#include "glob.h"
#include "glom.h"
#include "nalloc.h"
#include "utils.h"
#include "match.h"
#include "footobar.h"
#include "list.h"
#ifdef NODIRENT
#include <sys/dir.h>
#define dirent direct /* need to get the struct declaraction right */
#else
#include <dirent.h>
#endif
X
static List *dmatch(char *, char *, char *);
static List *doglob(char *, char *);
static List *lglob(List *, char *, char *, SIZE_T);
static List *sort(List *);
X
/*
X matches a list of words s against a list of patterns p. Returns true iff
X a pattern in p matches a word in s. null matches null, but otherwise null
X patterns match nothing.
*/
X
boolean lmatch(List *s, List *p) {
X List *q;
X int i;
X boolean okay;
X
X if (s == NULL) {
X if (p == NULL) /* null matches null */
X return TRUE;
X for (; p != NULL; p = p->n) { /* one or more stars match null */
X if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */
X okay = TRUE;
X for (i = 0; p->w[i] != '\0'; i++)
X if (p->w[i] != '*' || p->m[i] != 1) {
X okay = FALSE;
X break;
X }
X if (okay)
X return TRUE;
X }
X }
X return FALSE;
X }
X
X for (; s != NULL; s = s->n)
X for (q = p; q != NULL; q = q->n)
X if (match(q->w, q->m, s->w))
X return TRUE;
X return FALSE;
}
X
/* Matches a pattern p against the contents of directory d */
X
static List *dmatch(char *d, char *p, char *m) {
X boolean matched = FALSE;
X List *top, *r;
X DIR *dirp;
X struct dirent *dp;
X struct stat s;
X /* prototypes for XXXdir functions. comment out if necessary */
X extern DIR *opendir(const char *);
X extern struct dirent *readdir(DIR *);
X extern int closedir(DIR *);
X
X /* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */
X if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR)
X return NULL;
X if ((dirp = opendir(d)) == NULL)
X return NULL;
X
X top = r = NULL;
X
X while ((dp = readdir(dirp)) != NULL)
X if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */
X matched = TRUE;
X if (top == NULL)
X top = r = nnew(List);
X else
X r = r->n = nnew(List);
X r->w = ncpy(dp->d_name);
X r->m = NULL;
X }
X
X closedir(dirp);
X
X if (!matched)
X return NULL;
X
X r->n = NULL;
X return top;
}
X
/*
X lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
X will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
X slashcount indicates the number of slashes to stick between the directory and the
X matched name. e.g., for matching ////tmp/////foo*
*/
X
static List *lglob(List *s, char *p, char *m, SIZE_T slashcount) {
X List *q, *r, *top, foo;
X static List slash;
X static SIZE_T slashsize = 0;
X
X if (slashcount + 1 > slashsize) {
X slash.w = ealloc(slashcount + 1);
X slashsize = slashcount;
X }
X slash.w[slashcount] = '\0';
X while (slashcount > 0)
X slash.w[--slashcount] = '/';
X
X for (top = r = NULL; s != NULL; s = s->n) {
X q = dmatch(s->w, p, m);
X if (q != NULL) {
X foo.w = s->w;
X foo.m = NULL;
X foo.n = NULL;
X if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */
X q = concat(&slash, q); /* dir/name with slash */
X q = concat(&foo, q);
X if (r == NULL)
X top = r = q;
X else
X r->n = q;
X while (r->n != NULL)
X r = r->n;
X }
X }
X return top;
}
X
/*
X Doglob globs a pathname in pattern form against a unix path. Returns the original
X pattern (cleaned of metacharacters) on failure, or the globbed string(s).
*/
X
static List *doglob(char *w, char *m) {
X static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL;
X static SIZE_T dsize = 0;
X char *d, *p, *md, *mp;
X SIZE_T psize;
X char *s = w;
X List firstdir;
X List *matched;
X int slashcount;
X
X if ((psize = strlen(w) + 1) > dsize || dir == NULL) {
X efree(dir); efree(pattern); efree(metadir); efree(metapattern);
X dir = ealloc(psize);
X pattern = ealloc(psize);
X metadir = ealloc(psize);
X metapattern = ealloc(psize);
X dsize = psize;
X }
X
X d = dir;
X p = pattern;
X md = metadir;
X mp = metapattern;
X
X if (*s == '/') {
X while (*s == '/')
X *d++ = *s++, *md++ = *m++;
X } else {
X while (*s != '/' && *s != '\0')
X *d++ = *s++, *md++ = *m++; /* get first directory component */
X }
X *d = '\0';
X
X /*
X Special case: no slashes in the pattern, i.e., open the current directory.
X Remember that w cannot consist of slashes alone (the other way *s could be
X zero) since doglob gets called iff there's a metacharacter to be matched
X */
X if (*s == '\0') {
X matched = dmatch(".", dir, metadir);
X goto end;
X }
X
X if (*w == '/') {
X firstdir.w = dir;
X firstdir.m = metadir;
X firstdir.n = NULL;
X matched = &firstdir;
X } else {
X /*
X we must glob against current directory,
X since the first character is not a slash.
X */
X matched = dmatch(".", dir, metadir);
X }
X
X do {
X for (slashcount = 0; *s == '/'; s++, m++)
X slashcount++; /* skip slashes */
X
X while (*s != '/' && *s != '\0')
X *p++ = *s++, *mp++ = *m++; /* get pattern */
X *p = '\0';
X
X matched = lglob(matched, pattern, metapattern, slashcount);
X
X p = pattern, mp = metapattern;
X } while (*s != '\0');
X
end: if (matched == NULL) {
X matched = nnew(List);
X matched->w = w;
X matched->m = NULL;
X matched->n = NULL;
X }
X return matched;
}
X
/*
X Globs a list; checks to see if each element in the list has a metacharacter. If it
X does, it is globbed, and the output is sorted.
*/
X
List *glob(List *s) {
X List *top, *r;
X boolean meta;
X
X for (r = s, meta = FALSE; r != NULL; r = r->n)
X if (r->m != NULL)
X meta = TRUE;
X
X if (!meta)
X return s; /* don't copy lists with no metacharacters in them */
X
X for (top = r = NULL; s != NULL; s = s->n) {
X if (s->m == NULL) { /* no metacharacters; just tack on to the return list */
X if (top == NULL) {
X top = r = nnew(List);
X } else {
X r->n = nnew(List);
X r = r->n;
X }
X r->w = s->w;
X } else {
X if (top == NULL)
X top = r = sort(doglob(s->w, s->m));
X else
X r->n = sort(doglob(s->w, s->m));
X while (r->n != NULL)
X r = r->n;
X }
X }
X
X r->n = NULL;
X return top;
}
X
static List *sort(List *s) {
X SIZE_T nel = listnel(s);
X
X if (nel > 1) {
X char **a;
X List *t;
X
X qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp);
X
X for (t = s; t != NULL; t = t->n)
X t->w = *a++;
X }
X
X return s;
}
SHAR_EOF
chmod 0644 glob.c ||
echo 'restore of glob.c failed'
Wc_c="`wc -c < 'glob.c'`"
test 6378 -eq "$Wc_c" ||
echo 'glob.c: original size 6378, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= glob.h ==============
if test -f 'glob.h' -a X"$1" != X"-c"; then
echo 'x - skipping glob.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting glob.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'glob.h' &&
extern boolean lmatch(List *, List *);
extern List *glob(List *);
SHAR_EOF
chmod 0644 glob.h ||
echo 'restore of glob.h failed'
Wc_c="`wc -c < 'glob.h'`"
test 66 -eq "$Wc_c" ||
echo 'glob.h: original size 66, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= glom.c ==============
if test -f 'glom.c' -a X"$1" != X"-c"; then
echo 'x - skipping glom.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting glom.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'glom.c' &&
/* glom.c: builds an argument list out of words, variables, etc. */
X
#include <sys/types.h>
#include <sys/stat.h>
#ifndef S_IFIFO
#ifndef DEVFD
#define NOCMDARG
#endif
#endif
#include "rc.h"
#include "utils.h"
#include "nalloc.h"
#include "glom.h"
#include "hash.h"
#include "walk.h"
#include "status.h"
#include "exec.h"
#include "lex.h"
#include "open.h"
#include "list.h"
#include "wait.h"
X
static List *backq(Node *, Node *);
static List *bqinput(List *, int);
static List *count(List *);
static List *mkcmdarg(Node *);
X
Rq *redirq = NULL;
Fq *fifoq = NULL;
X
List *word(char *w, char *m) {
X List *s;
X
X if (w == NULL)
X return NULL;
X
X s = nnew(List);
X s->w = w;
X s->m = m;
X s->n = NULL;
X return s;
}
X
/*
X Append list s2 to list s1 by copying s1 and making the new copy
X point at s2.
*/
X
List *append(List *s1, List *s2) {
X List *r, *top;
X
X if (s1 == NULL)
X return s2;
X if (s2 == NULL)
X return s1;
X
X r = top = nnew(List);
X while (1) {
X r->w = s1->w;
X r->m = s1->m;
X if ((s1 = s1->n) == NULL)
X break;
X r = r->n = nnew(List);
X }
X
X r->n = s2;
X
X return top;
}
X
List *concat(List *s1, List *s2) {
X int n1, n2;
X SIZE_T y,z;
X List *n, *s;
X
X if (s1 == NULL)
X return s2;
X if (s2 == NULL)
X return s1;
X
X n1 = listnel(s1);
X n2 = listnel(s2);
X
X if (n1 != n2 && n1 != 1 && n2 != 1)
X rc_error("bad concatenation");
X
X n = s = nnew(List);
X
X while (1) {
X z = strlen(s1->w) + strlen(s2->w) + 1;
X n->w = nalloc(z);
X strcpy(n->w,s1->w);
X strcat(n->w,s2->w);
X if (s1->m == NULL && s2->m == NULL) {
X n->m = NULL;
X } else {
X n->m = nalloc(z);
X y = strlen(s1->w);
X if (s1->m == NULL)
X clear(n->m, y);
X else
X memcpy(n->m, s1->m, y);
X if (s2->m == NULL)
X clear(n->m + y, strlen(s2->w));
X else
X memcpy(n->m + y, s2->m, strlen(s2->w));
X n->m[z] = 0;
X }
X if (n1 > 1)
X s1 = s1->n;
X if (n2 > 1)
X s2 = s2->n;
X if (s1 == NULL || s2 == NULL || (n1 == 1 && n2 == 1)) {
X n->n = NULL;
X return s;
X }
X n->n = nnew(List);
X n = n->n;
X }
}
X
List *varsub(List *v, List *subs) {
X int i,j;
X int n;
X List *r,*s;
X List *top,*cat;
X
X n = listnel(v);
X
X top = cat = NULL;
X
X for (s = subs; s != NULL; s = s->n) {
X i = a2u(s->w);
X if (i < 1)
X rc_error("bad subscript");
X if (i <= n) {
X for (j = 1, r = v; j != i; j++, r = r->n)
X ; /* loop until r == v(i) */
X if (top == NULL) {
X top = cat = nnew(List);
X } else {
X cat->n = nnew(List);
X cat = cat->n;
X }
X cat->w = r->w;
X cat->m = r->m;
X }
X }
X
X if (top == NULL)
X return NULL;
X
X cat->n = NULL;
X return top;
}
X
List *flatten(List *s) {
X List *r;
X
X if (s == NULL || s->n == NULL)
X return s;
X
X r = nnew(List);
X r->w = nalloc(listlen(s) + 1);
X r->m = NULL; /* flattened lists come from variables, so no meta */
X r->n = NULL;
X
X strcpy(r->w, s->w);
X
X do {
X s = s->n;
X strcat(r->w, " ");
X strcat(r->w, s->w);
X } while (s->n != NULL);
X
X return r;
}
X
static List *count(List *l) {
X List *s = nnew(List);
X char buf[16];
X
X s->w = sprint(buf, "%d", listnel(l));
X s->w = ncpy(s->w);
X s->n = NULL;
X s->m = NULL;
X return s;
}
X
void assign(List *s1, List *s2, boolean stack) {
X List *val = s2;
X
X if (s1 == NULL)
X rc_error("null variable name");
X if (s1->n != NULL)
X rc_error("multi-word variable name");
X if (*s1->w == '\0')
X rc_error("zero-length variable name");
X if (a2u(s1->w) != -1)
X rc_error("numeric variable name");
X if (strchr(s1->w, '=') != NULL)
X rc_error("'=' in variable name");
X if (s1->w[0] == '*' && s1->w[1] == '\0')
X val = append(varlookup("0"), s2); /* preserve $0 when * is assigned explicitly */
X
X
X if (s2 != NULL || stack) {
X if (dashex)
X prettyprint_var(2, s1->w, val);
X varassign(s1->w, val, stack);
X alias(s1->w, val, stack);
X } else {
X if (dashex)
X prettyprint_var(2, s1->w, NULL);
X varrm(s1->w, stack);
X }
}
X
/*
X The following two functions are by the courtesy of Paul Haahr,
X who could not stand the incompetence of my own backquote implementation.
*/
X
#define BUFSIZE ((SIZE_T) 1000)
X
static List *bqinput(List *ifs, int fd) {
X char *end, *bufend, *s;
X List *r, *top, *prev;
X SIZE_T remain, bufsize;
X char isifs[256];
X int n;
X int state; /* a simple FSA is used to read in data */
X
X clear(isifs, sizeof isifs);
X isifs['\0'] = 1;
X for (r = ifs; r != NULL; r = r->n)
X for (s = r->w; *s != '\0'; s++)
X isifs[*(unsigned char *)s] = 1;
X
X remain = bufsize = BUFSIZE;
X top = r = nnew(List);
X r->w = end = nalloc(bufsize + 1);
X r->m = NULL;
X state = 0;
X prev = NULL;
X
X while (1) {
X if (remain == 0) { /* is the string bigger than the buffer? */
X SIZE_T m = end - r->w;
X char *buf;
X
X while (bufsize < m + BUFSIZE)
X bufsize *= 2;
X buf = nalloc(bufsize + 1);
X memcpy(buf, r->w, m);
X r->w = buf;
X end = &buf[m];
X remain = bufsize - m;
X }
X
X n = read(fd, end, remain);
X
X if (n <= 0) {
X if (n == -1) {
X uerror("backquote read");
X rc_error(NULL);
X }
X /* break */ break;
X }
X
X remain -= n;
X
X for (bufend = &end[n]; end < bufend; end++)
X switch (state) {
X case 0:
X if (!isifs[*(unsigned char *)end]) {
X state = 1;
X r->w = end;
X }
X break;
X case 1:
X if (isifs[*(unsigned char *)end]) {
X state = 0;
X *end = '\0';
X prev = r;
X r = r->n = nnew(List);
X r->w = end+1;
X r->m = NULL;
X }
X break;
X }
X }
X
X if (state == 1) { /* terminate last string */
X *end = '\0';
X r->n = NULL;
X } else {
X if (prev == NULL) /* no input at all? */
X top = NULL;
X else
X prev->n = NULL; /* else terminate list */
X }
X
X return top;
}
X
static List *backq(Node *ifsnode, Node *n) {
X int p[2], pid, sp;
X List *result, *ifs;
X
X if (n == NULL)
X return NULL;
X
X if (pipe(p) < 0) {
X uerror("pipe");
X rc_error(NULL);
X }
X
X if ((pid = rc_fork()) == 0) {
X setsigdefaults();
X dup2(p[1],1);
X close(p[0]);
X close(p[1]);
X redirq = NULL;
X fifoq = NULL;
X walk(n, FALSE);
X exit(getstatus());
X }
X
X ifs = glom(ifsnode);
X close(p[1]);
X result = bqinput(ifs, p[0]);
X close(p[0]);
X rc_wait4(pid, &sp);
X statprint(sp);
X return result;
}
X
void qredir(Node *n) {
X Rq *next;
X
X if (redirq == NULL) {
X next = redirq = nnew(Rq);
X } else {
X for (next = redirq; next->n != NULL; next = next->n)
X ;
X next->n = nnew(Rq);
X next = next->n;
X }
X
X next->r = n;
X next->n = NULL;
}
X
#ifdef NOCMDARG
static List *mkcmdarg(Node *n) {
X rc_error("named pipes are not supported");
X return NULL;
}
#else
#ifndef DEVFD
static List *mkcmdarg(Node *n) {
X int fd, pid;
X char *name, buf[32];
X List *ret = nnew(List);
X Fq *f = nnew(Fq);
X static int fifonumber = 0;
X
X name = sprint(buf,"/tmp/rc%d.%d", getpid(), fifonumber++);
X name = ncpy(name);
X
X if (mknod(name, S_IFIFO | 0644, 0) < 0) {
X uerror("mknod");
X return NULL;
X }
X
X if ((pid = rc_fork()) == 0) {
X setsigdefaults();
X /* fd = rc_open(name, CREATE); */
X fd = rc_open(name, (n->u[0].i != FROM) ? FROM : CREATE); /* stupid hack */
X if (fd < 0) {
X uerror("open");
X exit(1);
X }
X if (dup2(fd, (n->u[0].i == FROM)) < 0) { /* same stupid hack */
X uerror("dup2");
X exit(1);
X }
X close(fd);
X redirq = NULL;
X fifoq = NULL;
X walk(n->u[2].p, FALSE);
X exit(getstatus());
X }
X
X f->pid = pid;
X f->name = name;
X f->n = fifoq;
X fifoq = f;
X
X ret->w = name;
X ret->m = NULL;
X ret->n = NULL;
X
X return ret;
}
#else
static List *mkcmdarg(Node *n) {
X char *name, buf[32];
X List *ret = nnew(List);
X int p[2];
X
X if (pipe(p) < 0) {
X uerror("pipe");
X return NULL;
X }
X
X if (rc_fork() == 0) {
X setsigdefaults();
X
X if (dup2(p[n->u[0].i == FROM], n->u[0].i == FROM) < 0) { /* stupid hack */
X uerror("dup2");
X exit(1);
X }
X close(p[n->u[0].i != FROM]);
X
X redirq = NULL;
X walk(n->u[2].p, FALSE);
X exit(getstatus());
X }
X
X name = sprint(buf, "/dev/fd/%d", p[n->u[0].i != FROM]);
X name = ncpy(name); /* ncpy evaluates the expression twice */
X ret->w = name;
X ret->m = NULL;
X ret->n = NULL;
X
X close(p[n->u[0].i == FROM]);
X
X return ret;
}
#endif /* DEVFD */
#endif /* !NOCMDARG */
X
List *glom(Node *n) {
X Node *words;
X List *v, *first, *last;
X boolean dollarstar;
X
X if (n == NULL)
X return NULL;
X
X switch (n->type) {
X case ARGS:
X case LAPPEND:
X words = n->u[0].p;
X last = NULL;
X while (words != NULL && (words->type == ARGS || words->type == LAPPEND)) {
X if (words->u[1].p != NULL && words->u[1].p->type != rWORD && words->u[1].p->type != QWORD)
X break;
X first = glom(words->u[1].p);
X if (first != NULL) {
X first->n = last;
X last = first;
X }
X words = words->u[0].p;
X }
X v = append(glom(words), last); /* force left to right evaluation */
X return append(v, glom(n->u[1].p));
X case BACKQ:
X return backq(n->u[0].p,n->u[1].p);
X case CONCAT:
X first = glom(n->u[0].p); /* force left-to-right evaluation */
X return concat(first, glom(n->u[1].p));
X case rDUP:
X case rREDIR:
X qredir(n);
X return NULL;
X case rWORD: case QWORD:
X return word(n->u[0].s,n->u[1].s);
X case NMPIPE:
X return mkcmdarg(n);
X default:
X break;
X }
X
X /*
X the next three operations depend on the left-child of glom
X to be a variable name. Therefore they are all treated here.
X (previously each function looked up and checked the validity
X of a variable name)
X */
X
X v = glom(n->u[0].p);
X if (v == NULL)
X rc_error("null variable name");
X if (v->n != NULL)
X rc_error("multi-word variable name");
X if (*v->w == '\0')
X rc_error("zero-length variable name");
X
X dollarstar = (v->w[0] == '*' && v->w[1] == '\0');
X v = varlookup(v->w);
X if (dollarstar)
X v = v->n;
X
X switch (n->type) {
X default:
X fprint(2,"glom: this can't happen\n");
X exit(1);
X /* NOTREACHED */
X case rCOUNT:
X return count(v);
X case rFLAT:
X return flatten(v);
X case VAR:
X return v;
X case VARSUB:
X return varsub(v, glom(n->u[1].p));
X }
X
}
SHAR_EOF
chmod 0644 glom.c ||
echo 'restore of glom.c failed'
Wc_c="`wc -c < 'glom.c'`"
test 9512 -eq "$Wc_c" ||
echo 'glom.c: original size 9512, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= glom.h ==============
if test -f 'glom.h' -a X"$1" != X"-c"; then
echo 'x - skipping glom.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting glom.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'glom.h' &&
extern void assign(List *, List *, boolean);
extern void qredir(Node *);
extern List *append(List *, List*);
extern List *flatten(List *);
extern List *glom(Node *);
extern List *concat(List *, List *);
extern List *varsub(List *, List *);
extern List *word(char *, char *);
X
struct Fq {
X int pid;
X char *name;
X Fq *n;
};
SHAR_EOF
chmod 0644 glom.h ||
echo 'restore of glom.h failed'
Wc_c="`wc -c < 'glom.h'`"
test 322 -eq "$Wc_c" ||
echo 'glom.h: original size 322, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= hash.c ==============
if test -f 'hash.c' -a X"$1" != X"-c"; then
echo 'x - skipping hash.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting hash.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'hash.c' &&
/* hash.c: hash table support for functions and variables. */
X
/*
X functions and variables are cached in both internal and external
X form for performance. Thus a variable which is never "dereferenced"
X with a $ is passed on to rc's children untouched. This is not so
X important for variables, but is a big win for functions, where a call
X to yyparse() is involved.
*/
X
#include "rc.h"
#include "utils.h"
#include "hash.h"
#include "list.h"
#include "tree.h"
#include "footobar.h"
#include "sigmsgs.h"
X
static boolean var_exportable(char *);
static boolean fn_exportable(char *);
static int hash(char *, int);
static int find(char *, Htab *, int);
static void free_fn(Function *);
X
Htab *fp;
Htab *vp;
static int fused, fsize, vused, vsize;
static char **env;
static int bozosize;
static int envsize;
static boolean env_dirty = TRUE;
static char *dead = "";
X
#define HASHSIZE 64 /* rc was debugged with HASHSIZE == 2; 64 is about right for normal use */
X
void inithash() {
X int i;
X Htab *fpp, *vpp;
X
X fp = ealloc(sizeof(Htab) * HASHSIZE);
X vp = ealloc(sizeof(Htab) * HASHSIZE);
X fused = vused = 0;
X fsize = vsize = HASHSIZE;
X
X for (vpp = vp, fpp = fp, i = 0; i < HASHSIZE; i++, vpp++, fpp++)
X vpp->name = fpp->name = NULL;
}
X
#define ADV() {if ((c = *s++) == '\0') break;}
X
/* hash function courtesy of paul haahr */
X
static int hash(char *s, int size) {
X int n = 0;
X int c;
X
X while (1) {
X ADV();
X n += (c << 17) ^ (c << 11) ^ (c << 5) ^ (c >> 1);
X ADV();
X n ^= (c << 14) + (c << 7) + (c << 4) + c;
X ADV();
X n ^= (~c << 11) | ((c << 3) ^ (c >> 1));
X ADV();
X n -= (c << 16) | (c << 9) | (c << 2) | (c & 3);
X }
X
X if (n < 0)
X n = ~n;
X
X return n & (size - 1); /* need power of 2 size */
}
X
static boolean rehash(Htab *ht) {
X int i,j,size;
X int newsize,newused;
X Htab *newhtab;
X
X if (ht == fp) {
X if (fsize > 2 * fused)
X return FALSE;
X size = fsize;
X } else {
X if (vsize > 2 * vused)
X return FALSE;
X size = vsize;
X }
X
X
X newsize = 2 * size;
X newhtab = ealloc(newsize * sizeof(Htab));
X for (i = 0; i < newsize; i++)
X newhtab[i].name = NULL;
X
X for (i = newused = 0; i < size; i++)
X if (ht[i].name != NULL && ht[i].name != dead) {
X newused++;
X j = hash(ht[i].name, newsize);
X while (newhtab[j].name != NULL) {
X j++;
X j &= (newsize - 1);
X }
X newhtab[j].name = ht[i].name;
X newhtab[j].p = ht[i].p;
X }
X
X if (ht == fp) {
X fused = newused;
X fp = newhtab;
X fsize = newsize;
X } else {
X vused = newused;
X vp = newhtab;
X vsize = newsize;
X }
X
X efree(ht);
X return TRUE;
}
X
#define varfind(s) find(s,vp,vsize)
#define fnfind(s) find(s,fp,fsize)
X
static int find(char *s, Htab *ht, int size) {
X int h = hash(s, size);
X
X while (ht[h].name != NULL && !streq(ht[h].name,s)) {
X h++;
X h &= size - 1;
X }
X
X return h;
}
X
void *lookup(char *s, Htab *ht) {
X int h = find(s, ht, ht == fp ? fsize : vsize);
X
X return (ht[h].name == NULL) ? NULL : ht[h].p;
}
X
Function *get_fn_place(char *s) {
X int h = fnfind(s);
X
X env_dirty = TRUE;
X
X if (fp[h].name == NULL) {
X if (rehash(fp))
X h = fnfind(s);
X fused++;
X fp[h].name = ecpy(s);
X fp[h].p = enew(Function);
X } else
X free_fn(fp[h].p);
X
X return fp[h].p;
}
X
Variable *get_var_place(char *s, boolean stack) {
X Variable *new;
X int h = varfind(s);
X
X env_dirty = TRUE;
X
X if (vp[h].name == NULL) {
X if (rehash(vp))
X h = varfind(s);
X vused++;
X vp[h].name = ecpy(s);
X vp[h].p = enew(Variable);
X ((Variable *)vp[h].p)->n = NULL;
X return vp[h].p;
X } else {
X if (stack) { /* increase the stack by 1 */
X new = enew(Variable);
X new->n = vp[h].p;
X return vp[h].p = new;
X } else { /* trample the top of the stack */
X new = vp[h].p;
X efree(new->extdef);
X listfree(new->def);
X return new;
X }
X }
}
X
void delete_fn(char *s) {
X int h = fnfind(s);
X
X if (fp[h].name == NULL)
X return; /* not found */
X
X env_dirty = TRUE;
X
X free_fn(fp[h].p);
X efree(fp[h].p);
X efree(fp[h].name);
X if (fp[(h+1)&(fsize-1)].name == NULL) {
X --fused;
X fp[h].name = NULL;
X } else {
X fp[h].name = dead;
X }
}
X
void delete_var(char *s, boolean stack) {
X int h = varfind(s);
X Variable *v;
X
X if (vp[h].name == NULL)
X return; /* not found */
X
X env_dirty = TRUE;
X
X v = vp[h].p;
X efree(v->extdef);
X listfree(v->def);
X
X if (v->n != NULL) { /* This is the top of a stack */
X if (stack) { /* pop */
X vp[h].p = v->n;
X efree(v);
X } else { /* else just empty */
X v->extdef = NULL;
X v->def = NULL;
X }
X } else { /* needs to be removed from the hash table */
X efree(v);
X efree(vp[h].name);
X if (vp[(h+1)&(vsize-1)].name == NULL) {
X --vused;
X vp[h].name = NULL;
X } else {
X vp[h].name = dead;
X }
X }
}
X
static void free_fn(Function *f) {
X treefree(f->def);
X efree(f->extdef);
}
X
void initenv(char **envp) {
X int n;
X
X for (n = 0; envp[n] != NULL; n++)
X ;
X n++; /* one for the null terminator */
X
X if (n < HASHSIZE)
X n = HASHSIZE;
X
X env = ealloc((envsize = 2 * n) * sizeof (char *));
X
X for (; *envp != NULL; envp++)
X if (strncmp(*envp, "fn_", sizeof("fn_") - 1) == 0)
X fnassign_string(*envp);
X else
X if (!varassign_string(*envp)) /* add to bozo env */
X env[bozosize++] = *envp;
}
X
static boolean var_exportable(char *s) {
X int i;
X static char *notforexport[] = {
X "apid", "pid", "apids", "*", "ifs"
X };
X
X for (i = 0; i < arraysize(notforexport); i++)
X if (streq(s,notforexport[i]))
X return FALSE;
X
X return TRUE;
}
X
static boolean fn_exportable(char *s) {
X int i;
X
X if (strncmp(s,"sig",3) == 0) { /* small speed hack */
X for (i = 0; i < NUMOFSIGNALS; i++)
X if (streq(s, signals[i][0]))
X return FALSE;
X
X if (streq(s, "sigexit"))
X return FALSE;
X }
X
X return TRUE;
}
X
char **makeenv() {
X int ep, i;
X char *v;
X
X if (!env_dirty)
X return env;
X
X env_dirty = FALSE;
X ep = bozosize;
X
X if (vsize + fsize + 1 + bozosize > envsize) {
X envsize = 2 * (bozosize + vsize + fsize + 1);
X env = erealloc(env, envsize * sizeof(char *));
X }
X
X for (i = 0; i < vsize; i++) {
X if (vp[i].name == NULL || vp[i].name == dead || !var_exportable(vp[i].name))
X continue;
X v = varlookup_string(vp[i].name);
X if (v != NULL)
X env[ep++] = v;
X }
X for (i = 0; i < fsize; i++) {
X if (fp[i].name == NULL || fp[i].name == dead || !fn_exportable(fp[i].name))
X continue;
X env[ep++] = fnlookup_string(fp[i].name);
X }
X env[ep] = NULL;
X qsort(env, ep, sizeof(char *), starstrcmp);
X return env;
}
X
void whatare_all_vars() {
X int i;
X List *s;
X
X for (i = 0; i < vsize; i++)
X if (vp[i].name != NULL && (s = varlookup(vp[i].name)) != NULL)
X prettyprint_var(1, vp[i].name, s);
X
X for (i = 0; i < fsize; i++)
X if (fp[i].name != NULL)
X prettyprint_fn(1, fp[i].name, fnlookup(fp[i].name));
}
X
/* fake getenv() for readline() follows: */
X
#ifdef READLINE
char *getenv(const char *name) {
X List *s;
X
X if (name == NULL || vp == NULL || (s = varlookup((char *) name)) == NULL)
X return NULL;
X
X return s->w;
}
#endif
X
SHAR_EOF
chmod 0644 hash.c ||
echo 'restore of hash.c failed'
Wc_c="`wc -c < 'hash.c'`"
test 6760 -eq "$Wc_c" ||
echo 'hash.c: original size 6760, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= hash.h ==============
if test -f 'hash.h' -a X"$1" != X"-c"; then
echo 'x - skipping hash.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting hash.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'hash.h' &&
typedef struct Function {
X Node *def;
X char *extdef;
} Function;
X
typedef struct Variable {
X List *def;
X char *extdef;
X struct Variable *n;
} Variable;
X
typedef struct Htab {
X char *name;
X void *p;
} Htab;
X
extern Htab *fp, *vp;
X
#define lookup_fn(s) ((Function *) lookup(s,fp))
#define lookup_var(s) ((Variable *) lookup(s,vp))
X
extern void *lookup(char *, Htab *);
extern Function *get_fn_place(char *);
extern List *varlookup(char *);
extern Node *fnlookup(char *);
extern Variable *get_var_place(char *, boolean);
extern boolean varassign_string(char *);
extern char **makeenv(void);
extern char *fnlookup_string(char *);
extern char *varlookup_string(char *);
extern void alias(char *, List *, boolean);
extern void starassign(char *, char **, boolean);
extern void delete_fn(char *);
extern void delete_var(char *, boolean);
extern void fnassign(char *, Node *);
extern void fnassign_string(char *);
extern void fnrm(char *);
extern void initenv(char **);
extern void inithash(void);
extern void setsigdefaults(void);
extern void inithandler(void);
extern void varassign(char *, List *, boolean);
extern void varrm(char *, boolean);
extern void whatare_all_vars(void);
extern void whatare_all_signals(void);
extern void prettyprint_var(int, char *, List *);
extern void prettyprint_fn(int, char *, Node *);
SHAR_EOF
chmod 0644 hash.h ||
echo 'restore of hash.h failed'
Wc_c="`wc -c < 'hash.h'`"
test 1313 -eq "$Wc_c" ||
echo 'hash.h: original size 1313, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= heredoc.c ==============
if test -f 'heredoc.c' -a X"$1" != X"-c"; then
echo 'x - skipping heredoc.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting heredoc.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'heredoc.c' &&
/* heredoc.c: heredoc slurping is done here */
X
#include "rc.h"
#include "lex.h"
#include "utils.h"
#include "nalloc.h"
#include "heredoc.h"
#include "tree.h"
#include "input.h"
#include "hash.h"
#include "glom.h"
X
struct Hq {
X Node *doc;
X char *name;
X Hq *n;
X boolean quoted;
} *hq;
X
static boolean dead = FALSE;
X
/*
X * read in a heredocument. A clever trick: skip over any partially matched end-of-file
X * marker storing only the number of characters matched. If the whole marker is matched,
X * return from readheredoc(). If only part of the marker is matched, copy that part into
X * the heredocument.
X *
X * BUG: if the eof string contains a newline, the state can get confused, and the
X * heredoc may continue past where it should. on the other hand, /bin/sh seems to
X * never get out of its readheredoc() when the heredoc string contains a newline
X */
X
static char *readheredoc(char *eof) {
X int c;
X char *t, *buf, *bufend;
X unsigned char *s;
X SIZE_T bufsize;
X
X t = buf = nalloc(bufsize = 512);
X bufend = &buf[bufsize];
X dead = FALSE;
X
#define RESIZE(extra) { \
X char *nbuf; \
X bufsize = bufsize * 2 + extra; \
X nbuf = nalloc(bufsize); \
X memcpy(nbuf, buf, t - buf); \
X t = nbuf + (t - buf); \
X buf = nbuf; \
X bufend = &buf[bufsize]; \
X }
X
X for (;;) {
X print_prompt2();
X for (s = (unsigned char *) eof; (c = gchar()) == *s; s++)
X ;
X if (*s == '\0' && (c == '\n' || c == EOF)) {
X *t++ = '\0';
X return buf;
X }
X if (s != (unsigned char *) eof) {
X SIZE_T len = s - (unsigned char *) eof;
X if (t + len >= bufend)
X RESIZE(len);
X memcpy(t, eof, len);
X t += len;
X }
X for (;; c = gchar()) {
X if (c == EOF) {
X yyerror("EOF inside heredoc");
X dead = TRUE;
X return NULL;
X }
X if (t + 1 >= bufend)
X RESIZE(0);
X *t++ = c;
X if (c == '\n')
X break;
X }
X }
}
X
/* parseheredoc -- turn a heredoc with variable references into a node chain */
X
static Node *parseheredoc(char *s) {
X int c = *s;
X Node *result = NULL;
X
X while (TRUE) {
X Node *node;
X
X switch (c) {
X default: {
X char *begin = s;
X while ((c = *s++) != '\0' && c != '$')
X ;
X *--s = '\0';
X node = newnode(QWORD, begin, NULL);
X break;
X }
X case '$': {
X char *begin = ++s, *var;
X c = *s++;
X if (c == '$') {
X node = newnode(QWORD, "$", NULL);
X c = *s;
X } else {
X int len = 0;
X do
X len++;
X while (!dnw[c = *(unsigned char *) s++]);
X if (c == '^')
X c = *s;
X else
X s--;
X var = nalloc(len + 1);
X var[len] = '\0';
X memcpy(var, begin, len);
X node = newnode(rFLAT, newnode(rWORD, var, NULL));
X }
X break;
X }
X case '\0':
X return result;
X }
X
X result = (result == NULL) ? node : newnode(CONCAT, result, node);
X }
}
X
/* read in heredocs when yyparse hits a newline. called from yyparse */
X
int heredoc(int end) {
X Hq *here;
X
X if ((here = hq) != NULL) {
X hq = NULL;
X if (end) {
X yyerror("EOF on command line with heredoc");
X return FALSE;
X }
X do {
X Node *n = here->doc;
X char *s = readheredoc(here->name);
X if (dead)
X return FALSE;
X n->u[2].p = here->quoted ? newnode(QWORD, s, NULL) : parseheredoc(s);
X n->u[0].i = HERESTRING;
X } while ((here = here->n) != NULL);
X }
X return TRUE;
}
X
/* queue pending heredocs into a queue. called from yyparse */
X
int qdoc(Node *name, Node *n) {
X Hq *new, **prev;
X
X if (name->type != rWORD && name->type != QWORD) {
X yyerror("eof-marker must be a single literal word");
X flushu();
X return FALSE;
X }
X
X for (prev = &hq; (new = *prev) != NULL; prev = &new->n)
X ;
X *prev = new = nnew(Hq);
X
X new->name = name->u[0].s;
X new->quoted = (name->type == QWORD);
X new->doc = n;
X new->n = NULL;
X return TRUE;
}
SHAR_EOF
chmod 0644 heredoc.c ||
echo 'restore of heredoc.c failed'
Wc_c="`wc -c < 'heredoc.c'`"
test 3622 -eq "$Wc_c" ||
echo 'heredoc.c: original size 3622, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= heredoc.h ==============
if test -f 'heredoc.h' -a X"$1" != X"-c"; then
echo 'x - skipping heredoc.h (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting heredoc.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'heredoc.h' &&
extern int heredoc(int);
extern int qdoc(Node *, Node *);
X
typedef struct Hq Hq;
X
extern Hq *hq;
SHAR_EOF
chmod 0644 heredoc.h ||
echo 'restore of heredoc.h failed'
Wc_c="`wc -c < 'heredoc.h'`"
test 97 -eq "$Wc_c" ||
echo 'heredoc.h: original size 97, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= hfix.awk ==============
if test -f 'hfix.awk' -a X"$1" != X"-c"; then
echo 'x - skipping hfix.awk (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting hfix.awk (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'hfix.awk' &&
/^extern void b_exec\(char \*\*\), funcall\(char \*\*\), b_dot\(char \*\*\), b_builtin\(char \*\*\);$/ {
SHAR_EOF
true || echo 'restore of hfix.awk failed'
fi
echo 'End of part 2'
echo 'File hfix.awk is continued in part 3'
echo 3 > _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.