commit cb75624ddc0826e049781a124b53baf05585f0d1
parent 91f267363bbbf99ed9353af44ccb07748ea880dd
Author: tjg <tjg>
Date: Tue, 19 Jan 1999 12:17:39 +0000
Initial revision
Diffstat:
A | EXAMPLES | | | 749 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | getgroups.h | | | 10 | ++++++++++ |
A | heredoc.c | | | 156 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | list.c | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | match.c | | | 99 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | redir.c | | | 77 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tree.c | | | 172 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | wait.h | | | 23 | +++++++++++++++++++++++ |
8 files changed, 1343 insertions(+), 0 deletions(-)
diff --git a/EXAMPLES b/EXAMPLES
@@ -0,0 +1,749 @@
+There is no repository for useful rc code snippets as yet, so I'm including
+a (short) file in the distribution with some helpful/intriguing pieces of
+rc code.
+
+A sample .rcrc
+--------------
+Here is the .rcrc I use on archone:
+
+umask 022
+path=(/bin /usr/bin /usr/ucb)
+ht=`/usr/arch/bin/hosttype
+h=$home
+history=$h/.history
+bin=$h/bin/$ht
+lib=$h/lib/$ht
+sh=$h/bin/sh
+include=$h/lib/include
+
+switch ($ht) {
+case sun*
+ OBERON='. '$h/other/oberon
+ p=/usr/ucb
+ compiler='gcc -Wall -O -g'
+ MANPATH=$h/man:/usr/arch/man:/usr/man
+ if (! ~ $TERM ()) {
+ stty dec
+ /usr/arch/bin/msgs -q
+ }
+case next
+ p=(/usr/ucb /usr/bin /NextApps)
+ compiler='cc -Wall -O -g -DNODIRENT'
+ MANPATH=$h/man:/usr/arch/man:/usr/man
+ if (! ~ $TERM ())
+ stty dec
+case sgi
+ p=(/usr/ucb /usr/sbin /usr/bin)
+ compiler='gcc -Wall -O -g -DNOSIGCLD'
+ MANPATH=$h/man:/usr/arch/man:/usr/catman
+ if (!{~ $TERM () || ~ $TERM *iris*})
+ stty line 1 intr '' erase '' kill ''
+case *
+ echo .rcrc not configured for this machine
+}
+
+path=(. $sh $bin /usr/arch/bin $p /bin /usr/bin/X11 /etc /usr/etc)
+cdpath=(. .. $h $h/src $h/misc $h/other $h/adm)
+RNINIT=-d$h' -t -M -2400-h -2400+hfrom'; DOTDIR=$h/misc/news
+PRINTER=lw
+
+fn s {
+ echo $status
+}
+fn cd {
+ builtin cd $1 && \
+ switch ($1) {
+ case ()
+ dir=$home
+ case *
+ dir=()
+ }
+}
+fn pwd {
+ if (~ $dir ())
+ dir=`/bin/pwd
+ echo $dir
+}
+fn x {
+ if (~ `tty /dev/console)
+ clear_colormap
+ clear
+ exit
+}
+fn p {
+ if (~ $history ()) {
+ echo '$history not set' >[1=2]
+ return 1
+ }
+
+ if (! ~ $#* 0 1 2) {
+ echo usage: $0 '[egrep pattern] [sed command]' >[1=2]
+ return 1
+ }
+
+ command=`{
+ egrep -v '^[ ]*p([ ]+|$)' $history | switch ($#*) {
+ case 0
+ cat
+ case 1
+ egrep $1
+ case 2
+ egrep $1 | sed $2
+ } | tail -1
+ }
+
+ echo $command
+ eval $command
+}
+
+if (~ $TERM dialup network) {
+ TERM=vt100
+ biff y
+}
+
+A front-end to NeXT's "openfile"
+--------------------------------
+
+Named after the sam "B" command for opening a file, this script was written
+by Paul Haahr. (Assumes the "pick" command from Kernighan and Pike is also
+in your path.)
+
+#!/bin/rc
+if (~ $#* 0)
+ exec openfile
+create = ()
+files = ()
+for (i in $*)
+ if (test -f $i) {
+ files = ($files $i)
+ } else {
+ create = ($create $i)
+ }
+create = `{ pick $create }
+files = ($files $create)
+for (i in $create)
+ > $i
+if (! ~ $#files 0)
+ openfile $files
+
+A read function
+---------------
+
+Unlike sh, rc doesn't have a read. This clever alternative returns an
+exit status as well as fetch a variable. Use as
+
+ read foo
+
+to set $foo to a single line from the terminal.
+
+(due to John Mackin <john@syd.dit.csiro.au>)
+
+fn read {
+ x=() {
+ x = `` ($nl) { awk '{print; print 0; exit}' ^ $nl ^ \
+ 'END {print 1; print 1}' }
+ $1 = $x(1)
+ return $x(2)
+ }
+}
+
+From cs.wisc.edu!dws Fri Aug 2 18:16:14 1991
+
+#-------
+# ls front end
+#-------
+fn ls \
+{
+ test -t 1 && * = (-FCb $*)
+ builtin ls $*
+}
+#-------
+# nl - holds a newline, useful in certain command substitutions
+#-------
+nl='
+'
+#-------
+# show - tell me about a name
+#
+# Runs possibly dangerous things through cat -v in order to protect
+# me from the effects of control characters I might have in the
+# environment.
+#-------
+fn show \
+{
+ * = `` $nl {whatis -- $*}
+ for(itis)
+ {
+ switch($^itis)
+ {
+ case 'fn '* ; echo $itis | cat -v -t
+ case builtin* ; echo $itis
+ case /* ; file $itis; ls -ld $itis
+ case *'='* ; echo $itis | cat -v -t
+ case * ; echo $itis: UNKNOWN: update show
+ }
+ }
+ itis = ()
+}
+#-------
+# Tell me automatically when a command has a nonzero status.
+#-------
+fn prompt \
+{
+ Status = $status
+ ~ $Status 0 || echo '[status '$Status']'
+}
+
+#-------
+# chop - echo the given list, less its final member
+#
+# e.g. chop (a b c) -> (a b)
+#-------
+fn chop {
+ ~ $#* 0 1 && return 0
+ ans = '' { # local variable
+ ans = ()
+ while(! ~ $#* 1)
+ {
+ ans = ($ans $1)
+ shift
+ }
+ echo $ans
+ }
+}
+
+From arnold@audiofax.com Thu May 30 08:49:51 1991
+
+# cd.rc --- souped up version of cd
+
+# this is designed to emulate the fancy version of cd in ksh,
+# so if you're a purist, feel free to gag
+
+_cwd=$home
+_oldcwd=$home
+
+fn cd {
+ if (~ $#* 0) {
+ if (~ $_cwd $home) { # do nothing
+ } else {
+ builtin cd && { _oldcwd=$_cwd ; _cwd=$home }
+ }
+ } else if (~ $#* 1) {
+ if (~ $1 -) {
+ _t=$_cwd
+ builtin cd $_oldcwd && {
+ _cwd=$_oldcwd
+ _oldcwd=$_t
+ echo $_cwd
+ }
+ _t=()
+ } else {
+ # if a cd happens through the cdpath, rc echos
+ # the directory on its own. all we have to do
+ # is track where we end up
+ _dopwd = 1
+ { ~ $1 /* } && _dopwd = 0 # absolute path
+ builtin cd $1 && {
+ _oldcwd=$_cwd
+ _cwd=$1
+ { ~ $_dopwd 1 } && _cwd=`/bin/pwd
+ }
+ _dopwd=()
+ }
+ } else if (~ $#* 2) {
+ _t=`{ echo $_cwd | sed 's<'$1'<'$2'<' }
+ builtin cd $_t && {
+ _oldcwd=$_cwd
+ _cwd=$_t
+ echo $_cwd
+ }
+ _t=()
+ } else {
+ echo cd: takes 0, 1, or 2 arguments >[1=2]
+ builtin cd $1 && { _oldcwd=$_cwd ; _cwd=`/bin/pwd ; echo $_cwd }
+ }
+}
+
+fn pwd { echo $_cwd }
+
+From vlsi.cs.caltech.edu!drazen Tue Jan 21 16:03:14 1992
+
+# A kill builtin.
+
+#ifdef B_KILL
+#include <ctype.h>
+static void b_kill(char **av)
+{
+ int signal = SIGTERM;
+ int n = 1;
+ pid_t pid;
+ boolean res;
+
+ if (!av[1]) {
+ set(TRUE);
+ return;
+ }
+#undef STRCMP
+#define STRCMP strcmp
+ if ( '-' == av[1][0]) {
+ char *p = 1+av[1];
+ if (0 == strcmp(av[1], "-l")){
+ int r; const int nsig = NUMOFSIGNALS;
+ const int C = 4, R = 1 + (int)((nsig-2)/C);
+ for (r=1; r<=R; r++){
+ int j;
+ for (j=r; j<nsig; j+=R){
+ fprint(1, "%s%d. %s\t", j<10?" ":"", j, signals[j][0]);
+ }
+ fprint(1,"\n");
+ }
+ set(TRUE);
+ return;
+ }
+ n++;
+ if ( (signal=a2u(p)) < 0){
+ int i;
+ for (i = 1; i < NUMOFSIGNALS; i++){
+ char UPPER[31], *u=UPPER, *q;
+ for (q=signals[i][0]; *q; q++, u++) *u = toupper(*q);
+ *u = '\0';
+
+ if (*signals[i][0] &&
+ (!STRCMP(signals[i][0], p) || !STRCMP(3+signals[i][0],p)
+ || !STRCMP(UPPER, p) || !STRCMP(3+UPPER, p) ) )
+ {
+ signal = i;
+ break;
+ }
+ }
+ if (signal < 0){
+ fprint(2,"kill: bad signal %s\n", av[1]);
+ set(FALSE);
+ return;
+ }
+ }
+ }
+#undef STRCMP
+
+ for (res=TRUE; av[n]; n++){
+ if( (pid = (pid_t) a2u(av[n])) < 0){
+ fprint(2, "kill: bad process number %s\n", av[n]);
+ res = FALSE;
+ continue;
+ }
+ if (kill(pid,signal)){
+ uerror("kill");
+ res = FALSE;
+ continue;
+ }
+ }
+ set(res);
+}
+#endif
+From acc.stolaf.edu!quanstro Thu Apr 2 02:51:10 1992
+Received: from thor.acc.stolaf.edu ([130.71.192.1]) by archone.tamu.edu with SMTP id <45339>; Thu, 2 Apr 1992 02:50:56 -0600
+Received: by thor.acc.stolaf.edu; Thu, 2 Apr 92 02:49:31 -0600
+Date: Thu, 2 Apr 1992 02:49:31 -0600
+From: quanstro@acc.stolaf.edu
+Message-Id: <9204020849.AA26566@thor.acc.stolaf.edu>
+To: byron@archone.tamu.edu
+Subject: EXAMPLES in 1.4beta
+Status: RO
+
+
+I have a little bit of code which might be a little more general than
+the souped-up version that is already there. Here it is, if you are
+interested.
+
+# directory functions ###################################################
+fn pwd { echo $PWD; }
+
+fn pushd {
+ dirs = ($PWD $dirs);
+ builtin cd $*;
+ PWD = `{builtin pwd};
+}
+
+fn popd {
+ switch ($#*)
+ {
+ case 0
+ ;
+ case 1
+ echo 'popd: argument '^$1^' ignored.' >[1=2];
+ case *
+ echo 'popd: usage: popd [n].';
+ }
+
+ if (! ~ $dirs ())
+ {
+ builtin cd $dirs(1);
+ PWD = $dirs(1);
+ echo $PWD;
+ * = $dirs;
+ shift
+ dirs = $*;
+ }
+}
+
+fn cd {
+ ~ $* () && * = $home;
+ !~ $#* 1 && echo 'cd: too many arguments' >[1=2] && return 1;
+
+ if (test -r $* ) {
+ pushd $*;
+ } else {
+ echo cd: $* does not exist. >[1=2]
+ return 1;
+ }
+}
+
+fn back { popd $*; }
+
+fn Back {
+ cd $home;
+ PWD = $home;
+ dirs = ();
+}
+
+fn dirs {
+ echo $dirs;
+}
+
+PWD = `{builtin pwd} ; dirs = $PWD # kickstart
+
+
+
+
+
+From acc.stolaf.edu!quanstro Thu Apr 2 02:53:40 1992
+Received: from thor.acc.stolaf.edu ([130.71.192.1]) by archone.tamu.edu with SMTP id <45339>; Thu, 2 Apr 1992 02:53:38 -0600
+Received: by thor.acc.stolaf.edu; Thu, 2 Apr 92 02:51:46 -0600
+Date: Thu, 2 Apr 1992 02:51:46 -0600
+From: quanstro@acc.stolaf.edu
+Message-Id: <9204020851.AA26573@thor.acc.stolaf.edu>
+To: byron@archone.tamu.edu
+Subject: EXAMPLES
+Status: RO
+
+
+Little yp hack which act's like ~ w/o syntatic sugar (for those who do
+not have the luxury of seting up symbolic links to all user's homes
+
+# user function #########################################################
+fn u user {
+ info = ()
+ info = `` (':') {ypmatch $1 passwd >[2] /dev/null }
+
+ if (~ $info ()) {
+ echo user $1 unknown >[1=2];
+ return 1;
+ } else {
+ echo $info(6)
+ if (~ $0 user)
+ cd $info(6)
+ }
+}
+
+
+From stolaf.edu!quanstro Sun Apr 5 04:53:34 1992
+Date: Sun, 5 Apr 1992 04:53:08 -0500
+From: quanstro@stolaf.edu (Erik Quanstrom)
+To: byron@archone.tamu.edu
+Subject: man written in rc
+Status: RO
+
+I whipped this up because the NeXTs here insist on using MANPAGER
+instead of PAGER and otherwise being obnoxious . . .
+
+Anyway ... I hope you approve
+
+#!/bin/rc
+#########################################################################
+# file: man #
+# #
+# object: display man pages #
+# #
+# bugs: * slow #
+# * does not know about fmt files #
+# #
+# Erik Quanstrom #
+# 11. Februar 1992 #
+#########################################################################
+PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:$PATH ;
+TROFF = (nroff -hq -Tcrt);
+macros=an;
+sections=(cat1 cat2 cat3 cat4 cat5 cat6 cat7 cat8 catl man1 man2 man3 man4 \
+ man5 man6 man7 man8 manl)
+$nl='
+'
+fn sigint sighup sigquit sigalrm sigterm { rm -f $Bat; exit 2;}
+
+fn usage {
+ echo usage: $0 [-] [-t] [-M path] [-T macros] [[section] title] ...>[1=2];
+ exit 1;
+}
+
+n=();
+fn shiftn {
+ n=($n 1)
+}
+
+~ $PAGER () && test -t 1 && PAGER=more; #default pager
+
+while (~ $1 -*)
+ {
+ switch ($1)
+ {
+ case -
+ if (~ $PAGER troff)
+ echo bad combination of flags >[1=2] && usage;
+ PAGER=cat;
+ case -t
+ ~ TROFF () && TROFF = (troff -t);
+ ~ TCAT () && PAGER=(lpr -t);
+ case -M
+ shift;
+ ~ $1 () && usage;
+
+ MANPATH=$1;
+ case -T
+ shift;
+ ~ $1 () && usage;
+ macros=$1;
+ case -k
+ fflag=(); kflag=1;
+ shift;
+ break;
+ case -f
+ # locate related too filenames
+ kflag=(); fflag=1;
+ shift;
+ break;
+ case -*
+ echo bad flag '`'^$1^'''' >[1=2];
+ usage;
+ }
+ shift;
+ }
+
+if (!~ $#* 1) {
+ ~ $1 [l1-8] && { sname=$1 ; sections=(cat man)^$1 ; shift }
+ #hack for sun-style man pages
+ ~ $1 [l1-8]? && { sname=$1 ; sections=(cat man)^`{echo $1| cut -c1} ; shift }
+}
+
+if (~ 1 $fflag $kflag) {
+ dirlist=();
+ for (d in ``(:$nl) {echo $MANPATH})
+ test -s $d^/whatis && dirlist=($dirlist $d^/whatis);
+
+ ~ $1 () && usage;
+
+ if (~ $fflag 1) {
+ while (!~ $1 ()) {
+ cmd=`{echo $1 | sed 's/^.*\///g'}
+ egrep -h '^'^$cmd' ' $dirlist;
+ shift;
+ }
+ } else {
+ while (!~ $1 ()) {
+ grep -h $1 $dirlist;
+ shift;
+ }
+ }
+ exit 0;
+}
+
+s=0;
+while (!~ $1 ()) {
+ for (dir in ``(:$nl) {echo $MANPATH}) {
+ filelist=($filelist `{echo $dir/^$sections^/$1^.* |\
+ tr ' ' '\12' | grep -v '*'})
+
+ # coment this out if you don't care about speed, but
+ # would rather find all the pages.
+ ~ $filelist () || break;
+ }
+
+ if (~ $filelist ()) {
+ if (~ $#sections 2) {
+ echo no entry for $1 in section '`'$sname'''' of the manual >[1=2];
+ } else {
+ echo no entry for '`'$1'''' found. >[1=2];
+ }
+ s=1;
+ } else {
+
+ echo $filelist '(' $#filelist ')' >[1=2];
+
+ for (file in $filelist) {
+ if (~ $file */cat[1-8l]/*) {
+ Cat = ($Cat $file);
+ } else {
+ # search for dups
+ dont=()
+ for (x in $Cat) {
+ if (~ `{echo $x | sed 's/\/[mc]a[nt][1-8l]//'} \
+ `{echo $file | sed 's/\/[mc]a[nt][1-8l]//'}) {
+ dont=1;
+ break;
+ }
+ }
+
+ if (~ $dont ()) {
+ cd `{echo $file | sed 's/man[1-8].*//'}
+ echo -n Formatting ...
+ $TROFF -m^$macros $file > /tmp/man^$pid^$#n && \
+ Bat = ($Bat /tmp/man^$pid^$#n)
+
+ shiftn;
+ echo ' 'done.
+ }
+ }
+ }
+ }
+ shift;
+}
+
+{ !~ () $Cat || !~ () $Bat } && $PAGER $Cat $Bat;
+
+rm -f $Bat;
+~ $s () || exit $s;
+
+exit 0;
+
+
+
+From osf.org!rsalz Thu Apr 23 16:22:32 1992
+Date: Thu, 23 Apr 1992 16:22:07 -0500
+From: rsalz@osf.org
+To: byron@archone.tamu.edu
+Subject: One for your EXAMPLES file
+Status: RO
+
+Use
+ trimhist [-#lines]
+trims your history file back; useful for folks with dis quota's :-)
+fn trimhist { p1=-100 {
+ cp $history $history^'~'
+ ~ $#* 1 && p1=$1
+ tail $p1 <$history^'~' >$history
+ rm $history^'~'
+} }
+
+From Pa.dec.com!uucp Mon Apr 27 12:25:02 1992
+Date: Mon, 27 Apr 1992 12:15:18 -0500
+From: haahr@adobe.com
+To: Byron Rakitzis <byron@archone.tamu.edu>
+Subject: a neat little rc script
+
+what you have to know to understand this:
+ $md for me is usually obj.$machine
+ my mkfiles build *.o, *.a, and the a.outs in $md
+ this is my acc script, which i use for compiling adobe routines
+---
+#! /user/haahr/bin/next/rc
+
+cc = cc
+proto = '-DPROTOTYPES=1'
+
+switch ($md) {
+case noproto.$machine; proto = '-DPROTOTYPES=0'
+case gprof.$machine; cc = ($cc -pg)
+case prof.$machine; cc = ($cc -p)
+case lcomp.$machine; cc = lcomp
+}
+exec $cc $proto '-DPACKAGE_SPECS="package.h"' '-DISP=isp_mc68020' '-DOS=os_mach' $*
+
+From rc-owner Tue May 12 14:54:10 1992
+Received: from postman.osf.org ([130.105.1.152]) by archone.tamu.edu with SMTP id <45337>; Tue, 12 May 1992 14:38:16 -0500
+Received: from earth.osf.org by postman.osf.org (5.64+/OSF 1.0)
+ id AA14480; Tue, 12 May 92 13:25:03 -0400
+Received: by earth.osf.org (5.64/4.7) id AA03499; Tue, 12 May 92 13:25:02 -0400
+Date: Tue, 12 May 1992 12:25:02 -0500
+From: rsalz@osf.org
+Message-Id: <9205121725.AA03499@earth.osf.org>
+To: rc@archone.tamu.edu
+Subject: Useful function
+Status: R
+
+It looks like line noise, but it turns things like
+ /home/rsalz/foo/bar
+into
+ ~/foo/bar
+
+Useful for when you put your current directory up in your icon title.
+By duplicating the $home section you can make things like
+ /project/dce/build/dce1.0.1/src/rpc
+become
+ $MYBT/src/rpc
+
+## If a pathname starts with $home, turn $home into ~. Uses all built-ins.
+fn tildepath { p1=() i=() {
+ p1=$1
+ switch ($p1) {
+ case $home $home/*
+ # Split arg into components
+ *=`` (/) { echo -n $p1 }
+ # Shift down by number of components in $home
+ for (i in `` (/) { echo -n $home } ) shift
+ # Glue back together
+ p1='~'
+ for (i) p1=$p1 ^ '/' ^ $i
+ echo $p1
+ case *
+ echo $p1
+ }
+ return 0
+} }
+
+From osf.org!rsalz Tue May 12 15:47:12 1992
+Received: from postman.osf.org ([130.105.1.152]) by archone.tamu.edu with SMTP id <45316>; Tue, 12 May 1992 15:47:06 -0500
+Received: from earth.osf.org by postman.osf.org (5.64+/OSF 1.0)
+ id AA21070; Tue, 12 May 92 16:46:58 -0400
+Received: by earth.osf.org (5.64/4.7) id AA09396; Tue, 12 May 92 16:46:56 -0400
+Date: Tue, 12 May 1992 15:46:56 -0500
+From: rsalz@osf.org
+Message-Id: <9205122046.AA09396@earth.osf.org>
+To: byron@archone.tamu.edu
+Subject: Re: Useful function
+Status: R
+
+>wow. thanks, i'll add it to EXAMPLES.
+Glad you like. Getting something added to EXAMPLES has been a goal of mine...
+
+I've been thinking, a bit, about a more general way of doing it. I want
+a "prefix-sub" function, like this
+ prefix $some_path var1 var2 var3 var4 var5
+That would take some_path and replace any leading $var1 (etc) values
+with the variable name. Return on the first match.
+
+Hmm -- come to think of it, that's very easy to do:
+
+# Use pathprefix filename var1 var2 var3
+# returns filename, with leading prefixes (in $var1...) turned into the
+# string $var1...
+fn pathprefix { p1=() i=() j=() {
+ p1=$1 ; shift
+ for (i) {
+ ~ $p1 $$i $$i^/* && {
+ *=`` (/) { echo -n $p1 }
+ for (j in `` (/) { echo -n $$i } ) shift
+ p1='$'^$i
+ for (j) p1=$p1 ^ '/' ^ $j
+ echo $p1
+ return 0
+ }
+ }
+ echo $p1
+ return 0
+} }
+
+home=/user/users/rsalz
+z=/usr/users
+pathprefix /usr/users/rsalz home usr # --> $home
+pathprefix /usr/users/rsalz z # --> $z/rsalz
+pathprefix /usr/users/rsalz/foo z home # --> $z/rsalz/foo
+pathprefix /usr/users/rsalz/foo home # --> $home/foo
+
diff --git a/getgroups.h b/getgroups.h
@@ -0,0 +1,10 @@
+#if HAVE_GETGROUPS
+#if HAVE_POSIX_GETGROUPS
+/* We love POSIX. */
+#else
+/* OK, so you've got getgroups, but you don't have the POSIX semantics
+of a zero first argument. The conclusion is that you're on a reasonably
+pure BSD system, and we can include <sys/param.h> for NGROUPS. */
+#include <sys/param.h>
+#endif
+#endif
diff --git a/heredoc.c b/heredoc.c
@@ -0,0 +1,156 @@
+/* heredoc.c: heredoc slurping is done here */
+
+#include "rc.h"
+
+struct Hq {
+ Node *doc;
+ char *name;
+ Hq *n;
+ bool quoted;
+} *hq;
+
+static bool dead = FALSE;
+
+/*
+ * read in a heredocument. A clever trick: skip over any partially matched end-of-file
+ * marker storing only the number of characters matched. If the whole marker is matched,
+ * return from readheredoc(). If only part of the marker is matched, copy that part into
+ * the heredocument.
+ *
+ * BUG: if the eof string contains a newline, the state can get confused, and the
+ * heredoc may continue past where it should. on the other hand, /bin/sh seems to
+ * never get out of its readheredoc() when the heredoc string contains a newline
+ */
+
+static char *readheredoc(char *eof) {
+ int c;
+ char *t, *buf, *bufend;
+ unsigned char *s;
+ size_t bufsize;
+ t = buf = nalloc(bufsize = 512);
+ bufend = &buf[bufsize];
+ dead = FALSE;
+#define RESIZE(extra) { \
+ char *nbuf; \
+ bufsize = bufsize * 2 + extra; \
+ nbuf = nalloc(bufsize); \
+ memcpy(nbuf, buf, (size_t) (t - buf)); \
+ t = nbuf + (t - buf); \
+ buf = nbuf; \
+ bufend = &buf[bufsize]; \
+ }
+ for (;;) {
+ print_prompt2();
+ for (s = (unsigned char *) eof; (c = gchar()) == *s; s++)
+ ;
+ if (*s == '\0' && (c == '\n' || c == EOF)) {
+ *t++ = '\0';
+ return buf;
+ }
+ if (s != (unsigned char *) eof) {
+ size_t len = s - (unsigned char *) eof;
+ if (t + len >= bufend)
+ RESIZE(len);
+ memcpy(t, eof, len);
+ t += len;
+ }
+ for (;; c = gchar()) {
+ if (c == EOF) {
+ yyerror("heredoc incomplete");
+ dead = TRUE;
+ return NULL;
+ }
+ if (t + 1 >= bufend)
+ RESIZE(0);
+ *t++ = c;
+ if (c == '\n')
+ break;
+ }
+ }
+}
+
+/* parseheredoc -- turn a heredoc with variable references into a node chain */
+
+static Node *parseheredoc(char *s) {
+ int c = *s;
+ Node *result = NULL;
+ while (TRUE) {
+ Node *node;
+ switch (c) {
+ default: {
+ char *begin = s;
+ while ((c = *s++) != '\0' && c != '$')
+ ;
+ *--s = '\0';
+ node = mk(nQword, begin, NULL);
+ break;
+ }
+ case '$': {
+ char *begin = ++s, *var;
+ c = *s++;
+ if (c == '$') {
+ node = mk(nQword, "$", NULL);
+ c = *s;
+ } else {
+ size_t len = 0;
+ do
+ len++;
+ while (!dnw[c = *(unsigned char *) s++]);
+ if (c == '^')
+ c = *s;
+ else
+ s--;
+ var = nalloc(len + 1);
+ var[len] = '\0';
+ memcpy(var, begin, len);
+ node = mk(nFlat, mk(nWord, var, NULL));
+ }
+ break;
+ }
+ case '\0':
+ return result;
+ }
+ result = (result == NULL) ? node : mk(nConcat, result, node);
+ }
+}
+
+/* read in heredocs when yyparse hits a newline. called from yyparse */
+
+extern int heredoc(int end) {
+ Hq *here;
+ if ((here = hq) != NULL) {
+ hq = NULL;
+ if (end) {
+ yyerror("heredoc incomplete");
+ return FALSE;
+ }
+ do {
+ Node *n = here->doc;
+ char *s = readheredoc(here->name);
+ if (dead)
+ return FALSE;
+ n->u[2].p = here->quoted ? mk(nQword, s, NULL) : parseheredoc(s);
+ n->u[0].i = rHerestring;
+ } while ((here = here->n) != NULL);
+ }
+ return TRUE;
+}
+
+/* queue pending heredocs into a queue. called from yyparse */
+
+extern int qdoc(Node *name, Node *n) {
+ Hq *new, **prev;
+ if (name->type != nWord && name->type != nQword) {
+ yyerror("eof-marker not a single literal word");
+ flushu();
+ return FALSE;
+ }
+ for (prev = &hq; (new = *prev) != NULL; prev = &new->n)
+ ;
+ *prev = new = nnew(Hq);
+ new->name = name->u[0].s;
+ new->quoted = (name->type == nQword);
+ new->doc = n;
+ new->n = NULL;
+ return TRUE;
+}
diff --git a/list.c b/list.c
@@ -0,0 +1,57 @@
+/* list.c: routines for manipulating the List type */
+
+#include "rc.h"
+
+/*
+ These list routines assign meta values of null to the resulting lists;
+ it is impossible to glob with the value of a variable unless this value
+ is rescanned with eval---therefore it is safe to throw away the meta-ness
+ of the list.
+*/
+
+/* free a list from malloc space */
+
+extern void listfree(List *p) {
+ while (p != NULL) {
+ List *n = p->n;
+ efree(p->w);
+ efree(p);
+ p = n;
+ }
+}
+
+/* Copy list into malloc space (for storing a variable) */
+
+extern List *listcpy(List *s, void *(*alloc)(size_t)) {
+ List *top, *r;
+ for (top = r = NULL; s != NULL; s = s->n) {
+ if (top == NULL)
+ r = top = (*alloc)(sizeof (List));
+ else
+ r = r->n = (*alloc)(sizeof (List));
+ r->w = (*alloc)(strlen(s->w) + 1);
+ strcpy(r->w, s->w);
+ r->m = NULL;
+ }
+ if (r != NULL)
+ r->n = NULL;
+ return top;
+}
+
+/* Length of list */
+
+extern size_t listlen(List *s) {
+ size_t size;
+ for (size = 0; s != NULL; s = s->n)
+ size += strlen(s->w) + 1;
+ return size;
+}
+
+/* Number of elements in list */
+
+extern int listnel(List *s) {
+ int nel;
+ for (nel = 0; s != NULL; s = s->n)
+ nel++;
+ return nel;
+}
diff --git a/match.c b/match.c
@@ -0,0 +1,99 @@
+/* match.c: pattern matching routines */
+
+#include "rc.h"
+
+static int rangematch(char *, char);
+
+enum { RANGE_FAIL = -1, RANGE_ERROR = -2 };
+
+/* match() matches a single pattern against a single string. */
+
+extern bool match(char *p, char *m, char *s) {
+ int i, j;
+ if (m == NULL)
+ return streq(p, s);
+ i = 0;
+ while (1) {
+ if (p[i] == '\0')
+ return *s == '\0';
+ else if (m[i]) {
+ switch (p[i++]) {
+ case '?':
+ if (*s++ == '\0')
+ return FALSE;
+ break;
+ case '*':
+ while (p[i] == '*' && m[i] == 1) /* collapse multiple stars */
+ i++;
+ if (p[i] == '\0') /* star at end of pattern? */
+ return TRUE;
+ while (*s != '\0')
+ if (match(p + i, m + i, s++))
+ return TRUE;
+ return FALSE;
+ case '[':
+ if (*s == '\0')
+ return FALSE;
+ switch (j = rangematch(p + i, *s)) {
+ default:
+ i += j;
+ break;
+ case RANGE_FAIL:
+ return FALSE;
+ case RANGE_ERROR:
+ if (*s != '[')
+ return FALSE;
+ }
+ s++;
+ break;
+ default:
+ panic("bad metacharacter in match");
+ /* NOTREACHED */
+ return FALSE; /* hush up gcc -Wall */
+ }
+ } else if (p[i++] != *s++)
+ return FALSE;
+ }
+}
+
+/*
+ From the ed(1) man pages (on ranges):
+
+ The `-' is treated as an ordinary character if it occurs first
+ (or first after an initial ^) or last in the string.
+
+ The right square bracket does not terminate the enclosed string
+ if it is the first character (after an initial `^', if any), in
+ the bracketed string.
+
+ rangematch() matches a single character against a class, and returns
+ an integer offset to the end of the range on success, or -1 on
+ failure.
+*/
+
+static int rangematch(char *p, char c) {
+ char *orig = p;
+ bool neg = (*p == '~');
+ bool matched = FALSE;
+ if (neg)
+ p++;
+ if (*p == ']') {
+ p++;
+ matched = (c == ']');
+ }
+ for (; *p != ']'; p++) {
+ if (*p == '\0')
+ return RANGE_ERROR; /* bad syntax */
+ if (p[1] == '-' && p[2] != ']') { /* check for [..-..] but ignore [..-] */
+ if (c >= *p)
+ matched |= (c <= p[2]);
+ p += 2;
+ } else {
+ matched |= (*p == c);
+ }
+ }
+ if (matched ^ neg)
+ return p - orig + 1; /* skip the right-bracket */
+ else
+ return RANGE_FAIL;
+}
diff --git a/redir.c b/redir.c
@@ -0,0 +1,77 @@
+/* redir.c: code for opening files and piping heredocs after fork but before exec. */
+
+#include "rc.h"
+
+/*
+ Walk the redirection queue, and open files and dup2 to them. Also,
+ here-documents are treated here by dumping them down a pipe. (this
+ should make here-documents fast on systems with lots of memory which
+ do pipes right. Under sh, a file is copied to /tmp, and then read
+ out of /tmp again. I'm interested in knowing how much faster, say,
+ shar runs when unpacking when invoked with rc instead of sh. On my
+ sun4/280, it runs in about 60-75% of the time of sh for unpacking
+ the rc source distribution.)
+*/
+
+extern void doredirs() {
+ List *fname;
+ int fd, p[2];
+ Rq *r;
+ for (r = redirq; r != NULL; r = r->n) {
+ switch(r->r->type) {
+ default:
+ panic("unexpected node in doredirs");
+ /* NOTREACHED */
+ case nRedir:
+ if (r->r->u[0].i == rHerestring) {
+ fname = flatten(glom(r->r->u[2].p)); /* fname is really a string */
+ if (pipe(p) < 0) {
+ uerror("pipe");
+ rc_error(NULL);
+ }
+ if (rc_fork() == 0) { /* child writes to pipe */
+ setsigdefaults(FALSE);
+ close(p[0]);
+ if (fname != NULL)
+ writeall(p[1], fname->w, strlen(fname->w));
+ exit(0);
+ } else {
+ close(p[1]);
+ if (mvfd(p[0], r->r->u[1].i) < 0)
+ rc_error(NULL);
+ }
+ } else {
+ fname = glob(glom(r->r->u[2].p));
+ if (fname == NULL)
+ rc_error("null filename in redirection");
+ if (fname->n != NULL)
+ rc_error("multi-word filename in redirection");
+ switch (r->r->u[0].i) {
+ default:
+ panic("unexpected node in doredirs");
+ /* NOTREACHED */
+ case rCreate: case rAppend: case rFrom:
+ fd = rc_open(fname->w, r->r->u[0].i);
+ break;
+ }
+ if (fd < 0) {
+ uerror(fname->w);
+ rc_error(NULL);
+ }
+ if (mvfd(fd, r->r->u[1].i) < 0)
+ rc_error(NULL);
+ }
+ break;
+ case nDup:
+ if (r->r->u[2].i == -1)
+ close(r->r->u[1].i);
+ else if (r->r->u[2].i != r->r->u[1].i) {
+ if (dup2(r->r->u[2].i, r->r->u[1].i) < 0) {
+ uerror("dup2");
+ rc_error(NULL);
+ }
+ }
+ }
+ }
+ redirq = NULL;
+}
diff --git a/tree.c b/tree.c
@@ -0,0 +1,172 @@
+/* tree.c: functions for manipulating parse-trees. (create, copy, delete) */
+
+#include "rc.h"
+
+/* make a new node, pass it back to yyparse. Used to generate the parsetree. */
+
+extern Node *mk(int /*nodetype*/ t,...) {
+ va_list ap;
+ Node *n;
+ va_start(ap, t);
+ switch (t) {
+ default:
+ panic("unexpected node in mk");
+ /* NOTREACHED */
+ case nDup:
+ n = nalloc(offsetof(Node, u[3]));
+ n->u[0].i = va_arg(ap, int);
+ n->u[1].i = va_arg(ap, int);
+ n->u[2].i = va_arg(ap, int);
+ break;
+ case nWord: case nQword:
+ n = nalloc(offsetof(Node, u[2]));
+ n->u[0].s = va_arg(ap, char *);
+ n->u[1].s = va_arg(ap, char *);
+ break;
+ case nBang: case nNowait:
+ case nCount: case nFlat: case nRmfn: case nSubshell:
+ case nVar: case nCase:
+ n = nalloc(offsetof(Node, u[1]));
+ n->u[0].p = va_arg(ap, Node *);
+ break;
+ case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+ case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
+ case nOrelse: case nPre: case nArgs: case nSwitch:
+ case nMatch: case nVarsub: case nWhile: case nLappend:
+ n = nalloc(offsetof(Node, u[2]));
+ n->u[0].p = va_arg(ap, Node *);
+ n->u[1].p = va_arg(ap, Node *);
+ break;
+ case nForin:
+ n = nalloc(offsetof(Node, u[3]));
+ n->u[0].p = va_arg(ap, Node *);
+ n->u[1].p = va_arg(ap, Node *);
+ n->u[2].p = va_arg(ap, Node *);
+ break;
+ case nPipe:
+ n = nalloc(offsetof(Node, u[4]));
+ n->u[0].i = va_arg(ap, int);
+ n->u[1].i = va_arg(ap, int);
+ n->u[2].p = va_arg(ap, Node *);
+ n->u[3].p = va_arg(ap, Node *);
+ break;
+ case nRedir:
+ case nNmpipe:
+ n = nalloc(offsetof(Node, u[3]));
+ n->u[0].i = va_arg(ap, int);
+ n->u[1].i = va_arg(ap, int);
+ n->u[2].p = va_arg(ap, Node *);
+ break;
+ }
+ n->type = t;
+ va_end(ap);
+ return n;
+}
+
+/* copy a tree to malloc space. Used when storing the definition of a function */
+
+extern Node *treecpy(Node *s, void *(*alloc)(size_t)) {
+ Node *n;
+ if (s == NULL)
+ return NULL;
+ switch (s->type) {
+ default:
+ panic("unexpected node in treecpy");
+ /* NOTREACHED */
+ case nDup:
+ n = (*alloc)(offsetof(Node, u[3]));
+ n->u[0].i = s->u[0].i;
+ n->u[1].i = s->u[1].i;
+ n->u[2].i = s->u[2].i;
+ break;
+ case nWord: case nQword:
+ n = (*alloc)(offsetof(Node, u[2]));
+ n->u[0].s = strcpy((char *) (*alloc)(strlen(s->u[0].s) + 1), s->u[0].s);
+ if (s->u[1].s != NULL) {
+ size_t i = strlen(s->u[0].s);
+ n->u[1].s = (*alloc)(i);
+ memcpy(n->u[1].s, s->u[1].s, i);
+ } else
+ n->u[1].s = NULL;
+ break;
+ case nBang: case nNowait: case nCase:
+ case nCount: case nFlat: case nRmfn: case nSubshell: case nVar:
+ n = (*alloc)(offsetof(Node, u[1]));
+ n->u[0].p = treecpy(s->u[0].p, alloc);
+ break;
+ case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+ case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
+ case nOrelse: case nPre: case nArgs: case nSwitch:
+ case nMatch: case nVarsub: case nWhile: case nLappend:
+ n = (*alloc)(offsetof(Node, u[2]));
+ n->u[0].p = treecpy(s->u[0].p, alloc);
+ n->u[1].p = treecpy(s->u[1].p, alloc);
+ break;
+ case nForin:
+ n = (*alloc)(offsetof(Node, u[3]));
+ n->u[0].p = treecpy(s->u[0].p, alloc);
+ n->u[1].p = treecpy(s->u[1].p, alloc);
+ n->u[2].p = treecpy(s->u[2].p, alloc);
+ break;
+ case nPipe:
+ n = (*alloc)(offsetof(Node, u[4]));
+ n->u[0].i = s->u[0].i;
+ n->u[1].i = s->u[1].i;
+ n->u[2].p = treecpy(s->u[2].p, alloc);
+ n->u[3].p = treecpy(s->u[3].p, alloc);
+ break;
+ case nRedir:
+ case nNmpipe:
+ n = (*alloc)(offsetof(Node, u[3]));
+ n->u[0].i = s->u[0].i;
+ n->u[1].i = s->u[1].i;
+ n->u[2].p = treecpy(s->u[2].p, alloc);
+ break;
+ }
+ n->type = s->type;
+ return n;
+}
+
+/* free a function definition that is no longer needed */
+
+extern void treefree(Node *s) {
+ if (s == NULL)
+ return;
+ switch (s->type) {
+ default:
+ panic("unexpected node in treefree");
+ /* NOTREACHED */
+ case nDup:
+ break;
+ case nWord: case nQword:
+ efree(s->u[0].s);
+ efree(s->u[1].s);
+ break;
+ case nBang: case nNowait:
+ case nCount: case nFlat: case nRmfn:
+ case nSubshell: case nVar: case nCase:
+ treefree(s->u[0].p);
+ break;
+ case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+ case nElse: case nEpilog: case nIf: case nNewfn:
+ case nOrelse: case nPre: case nArgs: case nCbody:
+ case nSwitch: case nMatch: case nVarsub: case nWhile:
+ case nLappend:
+ treefree(s->u[1].p);
+ treefree(s->u[0].p);
+ break;
+ case nForin:
+ treefree(s->u[2].p);
+ treefree(s->u[1].p);
+ treefree(s->u[0].p);
+ break;
+ case nPipe:
+ treefree(s->u[2].p);
+ treefree(s->u[3].p);
+ break;
+ case nRedir:
+ case nNmpipe:
+ treefree(s->u[2].p);
+ }
+ efree(s);
+}
diff --git a/wait.h b/wait.h
@@ -0,0 +1,23 @@
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+/* Fake the POSIX wait() macros if we don't have them. */
+#ifndef WIFEXITED
+#define WIFEXITED(s) (((s) & 0xFF) == 0)
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(s) (((unsigned)(s) >> 8) && 0xFF)
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(s) (((s) & 0xFF) != 0)
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(s) ((s) & 0x7F)
+#endif
+
+/* These don't exist in POSIX. */
+#define myWIFDUMPED(s) (((s) & 0x80) != 0)
+
+
+