rc

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

commit cb75624ddc0826e049781a124b53baf05585f0d1
parent 91f267363bbbf99ed9353af44ccb07748ea880dd
Author: tjg <tjg>
Date:   Tue, 19 Jan 1999 12:17:39 +0000

Initial revision

Diffstat:
AEXAMPLES | 749+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agetgroups.h | 10++++++++++
Aheredoc.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alist.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amatch.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aredir.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atree.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Await.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) + + +