diff options
author | Jari Aalto <jari.aalto@cante.net> | 1999-02-19 17:11:39 +0000 |
---|---|---|
committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:46:52 +0000 |
commit | b72432fdcc59300c6fe7c9d6c8a31ad3447933f5 (patch) | |
tree | b9899162338c2ff3fd83a8aef8831cb119e85cd7 /examples | |
parent | bc4cd23ce958feda898c618215f94d8a4e8f4ffa (diff) | |
download | bash-b72432fdcc59300c6fe7c9d6c8a31ad3447933f5.tar.gz |
Imported from ../bash-2.03.tar.gz.
Diffstat (limited to 'examples')
27 files changed, 1543 insertions, 736 deletions
diff --git a/examples/functions/autoload.v3 b/examples/functions/autoload.v3 new file mode 100644 index 00000000..a82ffe9d --- /dev/null +++ b/examples/functions/autoload.v3 @@ -0,0 +1,125 @@ +#From: Mark Kennedy <mtk@ny.ubs.com> +#Message-ID: <35E2B899.63A02DF5@ny.ubs.com> +#Date: Tue, 25 Aug 1998 09:14:01 -0400 +#To: chet@nike.ins.cwru.edu +#Subject: a newer version of the ksh-style 'autoload' + +#enclosed you'll find 'autoload.v3', a version of the autoloader +#that emulates the ksh semantics of delaying the resolution (and loading) of the function +#until its first use. i took the liberty of simplifying the code a bit although it still uses the +#same functional breakdown. i recently went through the exercise of converting +#my ksh-based environment to bash (a very, very pleasant experience) +#and this popped out. + +# the psuedo-ksh autoloader. + +# The first cut of this was by Bill Trost, trost@reed.bitnet. +# The second cut came from Chet Ramey, chet@ins.CWRU.Edu +# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25 + +unset _AUTOLOADS + +_aload() +{ + local func + for func; do + eval $func '() + { + local f=$(_autoload_resolve '$func') + if [[ $f ]]; then + . $f + '$func' "$@" + return $? + else + return 1 + fi + }' + _autoload_addlist $func + done +} + +_autoload_addlist() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $func = "$1" ]] && return + done + + _AUTOLOADS[${#_AUTOLOADS[@]}]=$1 +} + +_autoload_dump() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $1 ]] && echo -n "autoload " + echo $func + done +} + +_autoload_remove_one() +{ + local func + local -a NEW_AUTOLOADS + + for func in ${_AUTOLOADS[@]}; do + [[ $func != "$1" ]] && NEW_AUTOLOADS[${#NEW_AUTOLOADS[@]}]=$func + done + + _AUTOLOADS=( ${NEW_AUTOLOADS[@]} ) +} + +_autoload_remove() +{ + local victim func + + for victim; do + for func in ${_AUTOLOADS[@]}; do + [[ $victim = "$func" ]] && unset -f $func && continue 2 + done + echo "autoload: $func: not an autoloaded function" >&2 + done + + for func; do + _autoload_remove_one $func + done +} + +_autoload_resolve() +{ + if [[ ! "$FPATH" ]]; then + echo "autoload: FPATH not set or null" >&2 + return + fi + + local p + + for p in $( (IFS=':'; set -- ${FPATH}; echo "$@") ); do + p=${p:-.} + if [ -f $p/$1 ]; then echo $p/$1; return; fi + done + + echo "autoload: $1: function source file not found" >&2 +} + +autoload() +{ + if (( $# == 0 )) ; then _autoload_dump; return; fi + + local opt OPTIND + + while getopts pu opt + do + case $opt in + p) _autoload_dump printable; return;; + u) shift $((OPTIND-1)); _autoload_remove "$@"; return;; + *) echo "autoload: usage: autoload [-pu] [function ...]" >&2; return;; + esac + done + + shift $(($OPTIND-1)) + + _aload "$@" +} diff --git a/examples/functions/inetaddr b/examples/functions/inetaddr index 776b204a..08086aea 100644 --- a/examples/functions/inetaddr +++ b/examples/functions/inetaddr @@ -23,6 +23,17 @@ inet2hex () hex2inet () { local x1 x2 x3 x4 + local rev + + OPTIND=1 + while getopts "r" o + do + case "$o" in + r) rev=true;; + *) echo "hex2inet: usage: hex2inet [0x]XXXXXXXX" >&2 ; exit 2;; + esac + done + shift $(( $OPTIND - 1 )) case "$1" in 0x*) h=${1#??} ;; @@ -40,5 +51,10 @@ hex2inet () x3=$(( 0x${h:4:2} )) x4=$(( 0x${h:6:2} )) - printf "%d.%d.%d.%d\n" $x1 $x2 $x3 $x4 + if [ -z "$rev" ] ; then + printf "%d.%d.%d.%d\n" $x1 $x2 $x3 $x4 + else + printf "%d.%d.%d.%d\n" $x4 $x3 $x2 $x1 + fi + return 0 } diff --git a/examples/functions/lowercase b/examples/functions/lowercase index 5f4fc9d4..a0649f84 100644 --- a/examples/functions/lowercase +++ b/examples/functions/lowercase @@ -8,19 +8,19 @@ lowercase() { -for file; do - filename=${file##*/} - case "$filename" in - */*) dirname=${file%/*} ;; - *) dirname=.;; - esac - nf=$(echo $filename | tr A-Z a-z) - newname="${dirname}/${nf}" - if [ "$nf" != "$filename" ]; then - mv "$file" "$newname" - echo "$0: $file -> $newname" - else - echo "$0: $file not changed." - fi -done + for file; do + filename=${file##*/} + case "$filename" in + */*) dirname=${file%/*} ;; + *) dirname=.;; + esac + nf=$(echo $filename | tr A-Z a-z) + newname="${dirname}/${nf}" + if [ "$nf" != "$filename" ]; then + mv "$file" "$newname" + echo "lowercase: $file -> $newname" + else + echo "lowercase: $file not changed." + fi + done } diff --git a/examples/functions/repeat3 b/examples/functions/repeat3 new file mode 100644 index 00000000..65048bf9 --- /dev/null +++ b/examples/functions/repeat3 @@ -0,0 +1,12 @@ +# From psamuels@jake.niar.twsu.edu (Peter Samuelson) +# posted to usenet, Message-ID: <6rtp8j$2a0$1@jake.niar.twsu.edu> + +repeat () +{ + local i max; # note that you can use \$i in the command string + max=$1; shift; + + i=1; while ((i <= max)); do + eval "$@"; ((i = i + 1)); + done; +} diff --git a/examples/functions/seq2 b/examples/functions/seq2 new file mode 100644 index 00000000..c3ad95c4 --- /dev/null +++ b/examples/functions/seq2 @@ -0,0 +1,37 @@ +# Generate a sequence from m to n, m defaults to 1. + +seq () +{ + declare -i lo hi i # makes local + local _SEQ INIT COMPARE STEP + + case "$1" in + -r) INIT='i=$hi _SEQ=""' COMPARE='let "i >= $lo"' STEP='let i-=1' ; shift ;; + *) INIT='i=$lo _SEQ=""' COMPARE='let "i <= $hi"' STEP='let i+=1' ;; + esac + + case $# in + 1) lo=1 hi="$1" ;; + 2) lo=$1 hi=$2 ;; + *) echo seq: usage: seq [-r] [low] high 1>&2 ; return 2 ;; + esac + + # equivalent to the as-yet-unimplemented + # for (( "$INIT" ; "$COMPARE" ; "$STEP" )); do _SEQ="${_SEQ}$i "; done + eval "$INIT" + while eval "$COMPARE"; do + _SEQ="${_SEQ}$i " + eval "$STEP" + done + echo "${_SEQ# }" + return 0 +} + +# like the APL `iota' function (or at least how I remember it :-) +iota() +{ + case $# in + 1) seq 1 "$1"; return $?;; + *) echo "iota: usage: iota high" 1>&2; return 2;; + esac +} diff --git a/examples/functions/which b/examples/functions/which new file mode 100644 index 00000000..54ace771 --- /dev/null +++ b/examples/functions/which @@ -0,0 +1,44 @@ +# +# which - emulation of `which' as it appears in FreeBSD +# +# usage: which [-as] command [command...] +# + +which() +{ + local aflag sflag ES a + + OPTIND=1 + while builtin getopts as opt ; do + case "$opt" in + a) aflag=-a ;; + s) sflag=1 ;; + ?) echo "which: usage: which [-as] command [command ...]" >&2 + exit 2 ;; + esac + done + + (( $OPTIND > 1 )) && shift $(( $OPTIND - 1 )) + + # without command arguments, exit with status 1 + ES=1 + + # exit status is 0 if all commands are found, 1 if any are not found + for command; do + # if $command is a function, make sure we add -a so type + # will look in $PATH after finding the function + a=$aflag + case "$(builtin type -t $command)" in + "function") a=-a;; + esac + + if [ -n "$sflag" ]; then + builtin type -p $a $command >/dev/null 2>&1 + else + builtin type -p $a $command + fi + ES=$? + done + + return $ES +} diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index 9773144d..799b4cfc 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -1,8 +1,6 @@ # # Simple makefile for the sample loadable builtins # -# This includes some boilerplate definitions added by configure, but will -# still need hand-editing # # Include some boilerplate Gnu makefile definitions. prefix = @prefix@ @@ -22,123 +20,148 @@ VPATH = .:@srcdir@ CC = @CC@ RM = rm -f -SHELL = /bin/sh - -# SunOS 4 -#PICFLAG = -pic -# Some versions of gcc, esp. on NetBSD and FreeBSD -PICFLAG = -fpic -# Linux -- could also be -fpic -#PICFLAG = -fPIC -# SunOS 5 -#PICFLAG = -K pic -# SVR4, SVR4.2, Irix -#PICFLAG = -K PIC -# BSD/OS 2.1, BSD/OS 3.x -#PICFLAG = -# AIX 4.2 -#PICFLAG = -K - -# SunOS 4, BSD/OS 2.1, BSD/OS 3.x, SVR4.2, SVR4, Linux, AIX 4.2, etc. -LD = ld -# SunOS 5, Linux -#LD = ${CC} - -# SunOS 4 -#LDOPT = -assert pure-text -# OSF/1, Digital UNIX -#LDOPT = -shared -soname $@ -expect_unresolved '*' -# SunOS 5 using sun cc -#LDOPT = -dy -z text -G -i -h $@ -# SunOS 5 using gcc with Sun ld -#LDOPT = -shared -Wl,-dy -Wl,-G -Wl,-i -# SVR4, SVR4.2 -#LDOPT = -dy -z text -G -h $@ -# NetBSD, FreeBSD -- might also need -r -LDOPT = -x -Bshareable -# Linux -#LDOPT = -shared -# BSD/OS 2.1, BSD/OS 3.x -#LDOPT = -r -# AIX 4.2 -#LDOPT = -bdynamic -bnoentry -bexpall -G - -# other libraries to link the shared object against -# BSD/OS 2.1 -#LDLIBS = -lc_s.2.1.0 -# BSD/OS 3.0, BSD/OS 3.1 -#LDLIBS = -lc_s.3.0.0 +SHELL = @MAKE_SHELL@ +host_os = @host_os@ +host_cpu = @host_cpu@ +host_vendor = @host_vendor@ + +CFLAGS = @CFLAGS@ + +# +# These values are generated for configure by ${topdir}/support/shobj-conf. +# If your system is not supported by that script, but includes facilities for +# dynamic loading of shared objects, please update the script and send the +# changes to bash-maintainers@gnu.org. +# +SHOBJ_CC = @SHOBJ_CC@ +SHOBJ_CFLAGS = @SHOBJ_CFLAGS@ +SHOBJ_LD = @SHOBJ_LD@ +SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ +SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@ +SHOBJ_LIBS = @SHOBJ_LIBS@ +SHOBJ_STATUS = @SHOBJ_STATUS@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \ -I$(BUILD_DIR) -I$(BUILD_DIR)/lib -I$(BUILD_DIR)/builtins .c.o: - $(CC) $(PICFLAG) $(CFLAGS) $(INC) -c -o $@ $< + $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ $< ALLPROG = print truefalse sleep pushd finfo logname basename dirname \ - tty pathchk tee head rmdir sprintf + tty pathchk tee head mkdir rmdir sprintf printenv id whoami \ + uname sync push ln unlink OTHERPROG = necho getconf hello cat -all: $(ALLPROG) -others: $(OTHERPROG) +all: $(SHOBJ_STATUS) + +supported: $(ALLPROG) +others: $(OTHERPROG) + +unsupported: + @echo "Your system (${host_os}) is not supported by the" + @echo "${topdir}/support/shobj-conf script." + @echo "If your operating system provides facilities for dynamic" + @echo "loading of shared objects using the dlopen(3) interface," + @echo "please update the script and re-run configure. + @echo "Please send the changes you made to bash-maintainers@gnu.org" + @echo "for inclusion in future bash releases." -everything: all others +everything: supported others sprintf: sprintf.o - $(LD) $(LDOPT) -o $@ sprintf.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sprintf.o $(SHOBJ_LIBS) print: print.o - $(LD) $(LDOPT) -o $@ print.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ print.o $(SHOBJ_LIBS) necho: necho.o - $(LD) $(LDOPT) -o $@ necho.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ necho.o $(SHOBJ_LIBS) getconf: getconf.o - $(LD) $(LDOPT) -o $@ getconf.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ getconf.o $(SHOBJ_LIBS) hello: hello.o - $(LD) $(LDOPT) -o $@ hello.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ hello.o $(SHOBJ_LIBS) truefalse: truefalse.o - $(LD) $(LDOPT) -o $@ truefalse.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS) sleep: sleep.o - $(LD) $(LDOPT) -o $@ sleep.o $(LDLIBS) - -pushd: pushd.o - $(LD) $(LDOPT) -o $@ pushd.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS) finfo: finfo.o - $(LD) $(LDOPT) -o $@ finfo.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ finfo.o $(SHOBJ_LIBS) cat: cat.o - $(LD) $(LDOPT) -o $@ cat.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS) logname: logname.o - $(LD) $(LDOPT) -o $@ logname.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS) basename: basename.o - $(LD) $(LDOPT) -o $@ basename.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ basename.o $(SHOBJ_LIBS) dirname: dirname.o - $(LD) $(LDOPT) -o $@ dirname.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dirname.o $(SHOBJ_LIBS) tty: tty.o - $(LD) $(LDOPT) -o $@ tty.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tty.o $(SHOBJ_LIBS) pathchk: pathchk.o - $(LD) $(LDOPT) -o $@ pathchk.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pathchk.o $(SHOBJ_LIBS) tee: tee.o - $(LD) $(LDOPT) -o $@ tee.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tee.o $(SHOBJ_LIBS) + +mkdir: mkdir.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkdir.o $(SHOBJ_LIBS) rmdir: rmdir.o - $(LD) $(LDOPT) -o $@ rmdir.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS) head: head.o - $(LD) $(LDOPT) -o $@ head.o $(LDLIBS) + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS) + +printenv: printenv.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS) + +id: id.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS) + +whoami: whoami.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS) + +uname: uname.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS) + +sync: sync.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS) + +push: push.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS) + +ln: ln.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS) + +unlink: unlink.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS) + + +# pushd is a special case. We use the same source that the builtin version +# uses, with special compilation options. +# +pushd.c: ${topdir}/builtins/pushd.def + $(RM) $@ + ${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def + +pushd.o: pushd.c + $(RM) $@ + $(SHOBJ_CC) -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ $< + +pushd: pushd.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS) clean: $(RM) $(ALLPROG) $(OTHERPROG) *.o @@ -146,12 +169,11 @@ clean: mostlyclean: clean distclean maintainer-clean: clean - $(RM) Makefile + $(RM) Makefile pushd.c print.o: print.c truefalse.o: truefalse.c sleep.o: sleep.c -pushd.o: pushd.c finfo.o: finfo.c logname.o: logname.c basename.o: basename.c @@ -166,4 +188,10 @@ necho.o: necho.c getconf.o: getconf.c hello.o: hello.c cat.o: cat.c - +printenv.o: printenv.c +id.o: id.c +whoami.o: whoami.c +uname.o: uname.c +sync.o: sync.c +push.o: push.c +mkdir.o: mkdir.c diff --git a/examples/loadables/README b/examples/loadables/README index 4c02f472..db678609 100644 --- a/examples/loadables/README +++ b/examples/loadables/README @@ -10,13 +10,18 @@ of the shell. All of the new builtins in ksh93 that bash didn't already have are included here, as is the ksh `print' builtin. -Compile with cc and whatever pic options you need (look in the -Makefile for a few common settings) +The configure script in the top-level source directory uses the +support/shobj-conf script to set the right values in the Makefile, +so you should not need to change the Makefile. If your system +is not supported by support/shobj-conf, and it has the necessary +facilities for building shared objects and support for the +dlopen/dlsyn/dlclose/dlerror family of functions, please make +the necessary changes to support/shobj-conf and send the changes +to bash-maintainers@gnu.org. -load with ld and whatever shared object options you need (again, -look in the Makefile) +Loadable builtins are loaded into a running shell with -then enable -f filename builtin-name + enable -f filename builtin-name enable uses a simple reference-counting scheme to avoid unloading a shared object that implements more than one loadable builtin before @@ -24,4 +29,5 @@ all loadable builtins implemented in the object are removed. Many of the details needed by builtin writers are found in hello.c, the canonical example. There is no real `builtin writers' programming -guide'. +guide'. The file template.c provides a template to use for creating +new loadable builtins. diff --git a/examples/loadables/dirname.c b/examples/loadables/dirname.c index e8758653..6159560e 100644 --- a/examples/loadables/dirname.c +++ b/examples/loadables/dirname.c @@ -58,7 +58,7 @@ dirname_builtin (list) if (string[slen] == '/') break; - if (slen >= 0) + if (slen < 0) { fputs (".\n", stdout); return (EXECUTION_SUCCESS); diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c index c0b03652..ab3c9a41 100644 --- a/examples/loadables/finfo.c +++ b/examples/loadables/finfo.c @@ -196,6 +196,13 @@ int m; obits[i++] = 'x'; obits[i] = '\0'; + if (m & S_ISUID) + ubits[2] = (m & S_IXUSR) ? 's' : 'S'; + if (m & S_ISGID) + gbits[2] = (m & S_IXGRP) ? 's' : 'S'; + if (m & S_ISVTX) + obits[2] = (m & S_IXOTH) ? 't' : 'T'; + printf ("u=%s,g=%s,o=%s", ubits, gbits, obits); } @@ -217,6 +224,10 @@ int mode; printf("S_IFLNK "); if (S_ISSOCK(mode)) printf("S_IFSOCK "); +#ifdef S_ISWHT + if (S_ISWHT(mode)) + printf("S_ISWHT "); +#endif perms(getperm(mode)); printf("\n"); } diff --git a/examples/loadables/hello.c b/examples/loadables/hello.c index 3f371e55..f4b4a8ea 100644 --- a/examples/loadables/hello.c +++ b/examples/loadables/hello.c @@ -3,19 +3,21 @@ /* See Makefile for compilation details. */ -#include "config.h" +#include <config.h> #if defined (HAVE_UNISTD_H) # include <unistd.h> #endif #include <stdio.h> + #include "builtins.h" #include "shell.h" +#include "bashgetopt.h" /* A builtin `xxx' is normally implemented with an `xxx_builtin' function. If you're converting a command that uses the normal Unix argc/argv - calling convention, use argv = word_list_to_argv (list, &argc) and call + calling convention, use argv = make_builtin_argv (list, &argc) and call the original `main' something like `xxx_main'. Look at cat.c for an example. @@ -41,7 +43,6 @@ hello_builtin (list) which is printed by `help xxx'. It must end with a NULL. */ char *hello_doc[] = { "this is the long doc for the sample hello builtin", - "which is a bare-bones echo", (char *)NULL }; @@ -53,7 +54,7 @@ struct builtin hello_struct = { hello_builtin, /* function implementing the builtin */ BUILTIN_ENABLED, /* initial flags for builtin */ hello_doc, /* array of long documentation strings. */ - "hello [args]", /* usage synopsis; becomes short_doc */ + "hello", /* usage synopsis; becomes short_doc */ 0 /* reserved for internal use */ }; diff --git a/examples/loadables/id.c b/examples/loadables/id.c new file mode 100644 index 00000000..945190d7 --- /dev/null +++ b/examples/loadables/id.c @@ -0,0 +1,308 @@ +/* + * id - POSIX.2 user identity + * + * (INCOMPLETE -- supplementary groups for other users not yet done) + * + * usage: id [-Ggu] [-nr] [user] + * + * The default output format looks something like: + * uid=xxx(chet) gid=xx groups=aa(aname), bb(bname), cc(cname) + */ + +#include <config.h> +#include <stdio.h> +#include "bashtypes.h" +#include <pwd.h> +#include <grp.h> +#include "bashansi.h" + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#else +# include <sys/param.h> +#endif + +#if !defined (HAVE_GETPW_DECLS) +extern struct passwd *getpwuid (); +#endif +extern struct group *getgrgid (); + +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "common.h" +#include "bashgetopt.h" + +#define ID_ALLGROUPS 0x001 /* -G */ +#define ID_GIDONLY 0x002 /* -g */ +#define ID_USENAME 0x004 /* -n */ +#define ID_USEREAL 0x008 /* -r */ +#define ID_USERONLY 0x010 /* -u */ + +#define ID_FLAGSET(s) ((id_flags & (s)) != 0) + +static int id_flags; + +static uid_t ruid, euid; +static gid_t rgid, egid; + +static char *id_user; + +static int inituser (); + +static int id_pruser (); +static int id_prgrp (); +static int id_prgroups (); +static int id_prall (); + +int +id_builtin (list) + WORD_LIST *list; +{ + int opt; + char *user; + + id_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Ggnru")) != -1) + { + switch (opt) + { + case 'G': id_flags |= ID_ALLGROUPS; break; + case 'g': id_flags |= ID_GIDONLY; break; + case 'n': id_flags |= ID_USENAME; break; + case 'r': id_flags |= ID_USEREAL; break; + case 'u': id_flags |= ID_USERONLY; break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + user = list ? list->word->word : (char *)NULL; + + /* Check for some invalid option combinations */ + opt = ID_FLAGSET (ID_ALLGROUPS) + ID_FLAGSET (ID_GIDONLY) + ID_FLAGSET (ID_USERONLY); + if (opt > 1 || (opt == 0 && ((id_flags & (ID_USEREAL|ID_USENAME)) != 0))) + { + builtin_usage (); + return (EX_USAGE); + } + + if (list && list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + if (inituser (user) < 0) + return (EXECUTION_FAILURE); + + opt = 0; + if (id_flags & ID_USERONLY) + opt += id_pruser ((id_flags & ID_USEREAL) ? ruid : euid); + else if (id_flags & ID_GIDONLY) + opt += id_prgrp ((id_flags & ID_USEREAL) ? rgid : egid); + else if (id_flags & ID_ALLGROUPS) + opt += id_prgroups (user); + else + opt += id_prall (user); + putchar ('\n'); + fflush (stdout); + + return (opt == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +static int +inituser (uname) + char *uname; +{ + struct passwd *pwd; + + if (uname) + { + pwd = getpwnam (uname); + if (pwd == 0) + { + builtin_error ("%s: no such user", uname); + return -1; + } + ruid = euid = pwd->pw_uid; + rgid = egid = pwd->pw_gid; + } + else + { + ruid = current_user.uid; + euid = current_user.euid; + rgid = current_user.gid; + egid = current_user.egid; + } + return 0; +} + +/* Print the name or value of user ID UID. */ +static int +id_pruser (uid) + int uid; +{ + struct passwd *pwd = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + pwd = getpwuid (uid); + if (pwd == NULL) + r = 1; + } + if (pwd) + printf ("%s", pwd->pw_name); + else + printf ("%u", (unsigned) uid); + + return r; +} + +/* Print the name or value of group ID GID. */ + +static int +id_prgrp (gid) + int gid; +{ + struct group *grp = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + grp = getgrgid (gid); + if (grp == NULL) + r = 1; + } + + if (grp) + printf ("%s", grp->gr_name); + else + printf ("%u", (unsigned) gid); + + return r; +} + +static int +id_prgroups (uname) + char *uname; +{ + int *glist, ng, i, r; + + r = 0; + id_prgrp (rgid); + if (egid != rgid) + { + putchar (' '); + id_prgrp (egid); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + for (i = 0; i < ng; i++) + if (glist[i] != rgid && glist[i] != egid) + { + putchar (' '); + id_prgrp (glist[i]); + } + + return r; +} + +static int +id_prall (uname) + char *uname; +{ + int r, i, ng, *glist; + struct passwd *pwd; + struct group *grp; + + r = 0; + printf ("uid=%u", (unsigned) ruid); + pwd = getpwuid (ruid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + + printf (" gid=%u", (unsigned) rgid); + grp = getgrgid (rgid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + + if (euid != ruid) + { + printf (" euid=%u", (unsigned) euid); + pwd = getpwuid (euid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + } + + if (egid != rgid) + { + printf (" egid=%u", (unsigned) egid); + grp = getgrgid (egid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + if (ng > 0) + printf (" groups="); + for (i = 0; i < ng; i++) + { + if (i > 0) + printf (", "); + printf ("%u", (unsigned) glist[i]); + grp = getgrgid (glist[i]); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + return r; +} + +char *id_doc[] = { + "return information about user identity", + (char *)NULL +}; + +struct builtin id_struct = { + "id", + id_builtin, + BUILTIN_ENABLED, + id_doc, + "id [user]\n\tid -G [-n] [user]\n\tid -g [-nr] [user]\n\tid -u [-nr] [user]", + 0 +}; diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c new file mode 100644 index 00000000..9c4c91bf --- /dev/null +++ b/examples/loadables/ln.c @@ -0,0 +1,203 @@ +/* ln - make links */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "posixstat.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +#define LN_SYMLINK 0x01 +#define LN_UNLINK 0x02 + +static Function *linkfn; +static int dolink (); + +ln_builtin (list) + WORD_LIST *list; +{ + int rval, opt, flags; + WORD_LIST *l; + char *sdir; + struct stat sb; + + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fs")) != -1) + { + switch (opt) + { + case 'f': + flags |= LN_UNLINK; + break; + case 's': + flags |= LN_SYMLINK; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + linkfn = (flags & LN_SYMLINK) ? symlink : link; + + if (list->next == 0) /* ln target, equivalent to ln target . */ + return (dolink (list->word->word, ".", flags)); + + if (list->next->next == 0) /* ln target source */ + return (dolink (list->word->word, list->next->word->word, flags)); + + /* ln target1 target2 ... directory */ + + /* find last argument: target directory, and make sure it's an existing + directory. */ + for (l = list; l->next; l = l->next) + ; + sdir = l->word->word; + + if (stat(sdir, &sb) < 0) + { + builtin_error ("%s", sdir); + return (EXECUTION_FAILURE); + } + + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + for (rval = EXECUTION_SUCCESS; list != l; list = list->next) + rval += dolink (list->word->word, sdir, flags); + + return rval; +} + +static char * +mkdirpath (dir, file) + char *dir, *file; +{ + int dlen, flen; + char *ret; + + dlen = strlen (dir); + flen = strlen (file); + + ret = xmalloc (2 + dlen + flen); + + strcpy (ret, dir); + if (ret[dlen - 1] != '/') + ret[dlen++] = '/'; + strcpy (ret + dlen, file); + return ret; +} + +#if defined (HAVE_LSTAT) +# define LSTAT lstat +#else +# define LSTAT stat +#endif + +static int +dolink (src, dst, flags) + char *src, *dst; + int flags; +{ + struct stat ssb, dsb; + int exists; + char *dst_path, *p; + + /* If we're not doing symlinks, the source must exist and not be a + directory. */ + if ((flags & LN_SYMLINK) == 0) + { + if (stat (src, &ssb) != 0) + { + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + if (S_ISDIR (ssb.st_mode)) + { + errno = EISDIR; + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + } + + /* If the destination is a directory, create the final filename by appending + the basename of the source to the destination. */ + dst_path = 0; + if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode)) + { + if ((p = strrchr (src, '/')) == 0) + p = src; + else + p++; + + dst_path = mkdirpath (dst, p); + dst = dst_path; + } + + exists = LSTAT (dst, &dsb) == 0; + + /* If -f was specified, and the destination exists, unlink it. */ + if ((flags & LN_UNLINK) && exists && unlink (dst) != 0) + { + builtin_error ("%s: cannot unlink: %s", dst, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + /* Perform the link. */ + if ((*linkfn) (src, dst) != 0) + { + builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + FREE (dst_path); + return (EXECUTION_SUCCESS); +} + +char *ln_doc[] = { + "Create a new directory entry with the same modes as the original", + "file. The -f option means to unlink any existing file, permitting", + "the link to occur. The -s option means to create a symbolic link.", + "By default, ln makes hard links.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin ln_struct = { + "ln", /* builtin name */ + ln_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + ln_doc, /* array of long documentation strings. */ + "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c new file mode 100644 index 00000000..cd6e5f96 --- /dev/null +++ b/examples/loadables/mkdir.c @@ -0,0 +1,216 @@ +/* mkdir - make directories */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#include "bashtypes.h" +#include "posixstat.h" +#include <errno.h> +#include <stdio.h> +#include "bashansi.h" +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +#define ISOCTAL(c) ((c) >= '0' && (c) <= '7') + +extern int parse_symbolic_mode (); + +static int make_path (); + +static int original_umask; + +int +mkdir_builtin (list) + WORD_LIST *list; +{ + int opt, pflag, omode, rval, octal, nmode, parent_mode, um; + char *mode; + WORD_LIST *l; + + reset_internal_getopt (); + pflag = 0; + mode = (char *)NULL; + while ((opt = internal_getopt(list, "m:p")) != -1) + switch (opt) + { + case 'p': + pflag = 1; + break; + case 'm': + mode = list_optarg; + break; + default: + builtin_usage(); + return (EX_USAGE); + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (mode == NULL) + omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */ + else if (ISOCTAL (*mode)) /* octal number */ + { + omode = read_octal (mode); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + octal = 1; + } + else if (mode) + { + /* initial bits are a=rwx; the mode argument modifies them */ + omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + octal = 0; + } + + /* Make the new mode */ + original_umask = umask (0); + umask (original_umask); + + nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask; + parent_mode = nmode | (S_IWRITE|S_IEXEC); /* u+wx */ + + /* Adjust new mode based on mode argument */ + nmode &= omode; + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + if (pflag && make_path (l->word->word, nmode, parent_mode)) + { + rval = EXECUTION_FAILURE; + continue; + } + else if (pflag == 0 && mkdir (l->word->word, nmode) < 0) + { + builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + } + return rval; +} + +/* Make all the directories leading up to PATH, then create PATH. Note that + this changes the process's umask; make sure that all paths leading to a + return reset it to ORIGINAL_UMASK */ +static int +make_path (path, nmode, parent_mode) + char *path; + int nmode, parent_mode; +{ + int oumask; + struct stat sb; + char *p, *npath; + + if (stat (path, &sb) == 0) + { + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", path); + return 1; + } + + if (chmod (path, nmode)) + { + builtin_error ("%s: %s", path, strerror (errno)); + return 1; + } + + return 0; + } + + oumask = umask (0); + npath = savestring (path); /* So we can write to it. */ + + /* Check whether or not we need to do anything with intermediate dirs. */ + + /* Skip leading slashes. */ + p = npath; + while (*p == '/') + p++; + + while (p = strchr (p, '/')) + { + *p = '\0'; + if (stat (npath, &sb) != 0) + { + if (mkdir (npath, parent_mode)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + } + else if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", npath); + umask (original_umask); + free (npath); + return 1; + } + + *p++ = '/'; /* restore slash */ + while (*p == '/') + p++; + } + + /* Create the final directory component. */ + if (stat (npath, &sb) && mkdir (npath, nmode)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + + umask (original_umask); + free (npath); + return 0; +} + +char *mkdir_doc[] = { + "Make directories. Create the directories named as arguments, in", + "the order specified, using mode rwxrwxrwx as modified by the current", + "umask (see `help umask'). The -m option causes the file permission", + "bits of the final directory to be MODE. The MODE argument may be", + "an octal number or a symbolic mode like that used by chmod(1). If", + "a symbolic mode is used, the operations are interpreted relative to", + "an initial mode of \"a=rwx\". The -p option causes any required", + "intermediate directories in PATH to be created. The directories", + "are created with permssion bits of rwxrwxrwx as modified by the current", + "umask, plus write and search permissions for the owner. mkdir", + "returns 0 if the directories are created successfully, and non-zero", + "if an error occurs.", + (char *)NULL +}; + +struct builtin mkdir_struct = { + "mkdir", + mkdir_builtin, + BUILTIN_ENABLED, + mkdir_doc, + "mkdir [-p] [-m mode] directory [directory ...]", + 0 +}; diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c index 695062a3..521ee2c6 100644 --- a/examples/loadables/necho.c +++ b/examples/loadables/necho.c @@ -22,7 +22,7 @@ char *necho_doc[] = { (char *)NULL }; -struct builtin echo_struct = { +struct builtin necho_struct = { "echo", necho_builtin, BUILTIN_ENABLED, diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c index dc02fce9..2e36f8f6 100644 --- a/examples/loadables/pathchk.c +++ b/examples/loadables/pathchk.c @@ -170,8 +170,7 @@ portable_chars_only (path) for (p = path; *p; ++p) if (portable_chars[(const unsigned char) *p] == 0) { - error (0, 0, "path `%s' contains nonportable character `%c'", - path, *p); + builtin_error ("path `%s' contains nonportable character `%c'", path, *p); return 0; } return 1; @@ -212,7 +211,7 @@ dir_ok (path) if (!S_ISDIR (stats.st_mode)) { - error (0, 0, "`%s' is not a directory", path); + builtin_error ("`%s' is not a directory", path); return 0; } @@ -324,7 +323,7 @@ validate_path (path, portability) name_max = _POSIX_NAME_MAX; if (length > name_max) { - error (0, 0, "name `%s' has length %d; exceeds limit of %d", + builtin_error ("name `%s' has length %d; exceeds limit of %d", start, length, name_max); free (parent); return 1; @@ -350,7 +349,7 @@ validate_path (path, portability) free (parent); if (strlen (path) > path_max) { - error (0, 0, "path `%s' has length %d; exceeds limit of %d", + builtin_error ("path `%s' has length %d; exceeds limit of %d", path, strlen (path), path_max); return 1; } diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c new file mode 100644 index 00000000..16f398fc --- /dev/null +++ b/examples/loadables/printenv.c @@ -0,0 +1,71 @@ +/* + * printenv -- minimal builtin clone of BSD printenv(1). + * + * usage: printenv [varname] + * + */ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +extern char **export_env; + +int +printenv_builtin (list) + WORD_LIST *list; +{ + register char **envp; + int opt; + SHELL_VAR *var; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* printenv */ + if (list == 0) + { + maybe_make_export_env (); /* this allows minimal code */ + for (envp = export_env; *envp; envp++) + printf ("%s\n", *envp); + return (EXECUTION_SUCCESS); + } + + /* printenv varname */ + var = find_variable (list->word->word); + if (var == 0 || (exported_p (var) == 0)) + return (EXECUTION_FAILURE); + + if (function_p (var)) + print_var_function (var); + else + print_var_value (var, 0); + + return (EXECUTION_SUCCESS); +} + +char *printenv_doc[] = { + "print values of environment variables", + (char *)NULL +}; + +struct builtin printenv_struct = { + "printenv", + printenv_builtin, + BUILTIN_ENABLED, + printenv_doc, + "printenv [varname]", + 0 +}; diff --git a/examples/loadables/pushd.c b/examples/loadables/pushd.c deleted file mode 100644 index 2ecbcbbc..00000000 --- a/examples/loadables/pushd.c +++ /dev/null @@ -1,608 +0,0 @@ -/* pushd.c, created from pushd.def. */ -#include <config.h> - -#include <stdio.h> -#include <sys/param.h> - -#if defined (HAVE_UNISTD_H) -# include <unistd.h> -#endif - -#include "bashansi.h" - -#include <errno.h> - -#include <tilde/tilde.h> - -#include "shell.h" -#include "builtins.h" -#include "maxpath.h" -#include "common.h" - -#if !defined (errno) -extern int errno; -#endif /* !errno */ - -static char *m_badarg = "%s: bad argument"; - -/* The list of remembered directories. */ -static char **pushd_directory_list = (char **)NULL; - -/* Number of existing slots in this list. */ -static int directory_list_size; - -/* Offset to the end of the list. */ -static int directory_list_offset; - -static void pushd_error (); -static void clear_directory_stack (); -static int cd_to_string (); -static int change_to_temp (); -static int get_dirstack_index (); -static void add_dirstack_element (); - -#define NOCD 0x01 -#define ROTATE 0x02 -#define LONGFORM 0x04 -#define CLEARSTAK 0x08 - -int -pushd_builtin (list) - WORD_LIST *list; -{ - char *temp, *current_directory, *top; - int j, flags; - long num; - char direction; - - /* If there is no argument list then switch current and - top of list. */ - if (list == 0) - { - if (directory_list_offset == 0) - { - builtin_error ("no other directory"); - return (EXECUTION_FAILURE); - } - - current_directory = get_working_directory ("pushd"); - if (current_directory == 0) - return (EXECUTION_FAILURE); - - j = directory_list_offset - 1; - temp = pushd_directory_list[j]; - pushd_directory_list[j] = current_directory; - j = change_to_temp (temp); - free (temp); - return j; - } - - for (flags = 0; list; list = list->next) - { - if (ISOPTION (list->word->word, 'n')) - { - flags |= NOCD; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; - break; - } - else if (list->word->word[0] == '-' && list->word->word[1] == '\0') - /* Let `pushd -' work like it used to. */ - break; - else if (((direction = list->word->word[0]) == '+') || direction == '-') - { - if (legal_number (list->word->word + 1, &num) == 0) - { - builtin_error (m_badarg, list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - - if (direction == '-') - num = directory_list_offset - num; - - if (num > directory_list_offset || num < 0) - { - pushd_error (directory_list_offset, list->word->word); - return (EXECUTION_FAILURE); - } - flags |= ROTATE; - } - else if (*list->word->word == '-') - { - bad_option (list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - else - break; - } - - if (flags & ROTATE) - { - /* Rotate the stack num times. Remember, the current - directory acts like it is part of the stack. */ - temp = get_working_directory ("pushd"); - - if (num == 0) - { - j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS; - free (temp); - return j; - } - - do - { - top = pushd_directory_list[directory_list_offset - 1]; - - for (j = directory_list_offset - 2; j > -1; j--) - pushd_directory_list[j + 1] = pushd_directory_list[j]; - - pushd_directory_list[j + 1] = temp; - - temp = top; - num--; - } - while (num); - - j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS; - free (temp); - return j; - } - - if (list == 0) - return (EXECUTION_SUCCESS); - - /* Change to the directory in list->word->word. Save the current - directory on the top of the stack. */ - current_directory = get_working_directory ("pushd"); - if (current_directory == 0) - return (EXECUTION_FAILURE); - - j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS; - if (j == EXECUTION_SUCCESS) - { - add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory); - dirs_builtin ((WORD_LIST *)NULL); - if (flags & NOCD) - free (current_directory); - return (EXECUTION_SUCCESS); - } - else - { - free (current_directory); - return (EXECUTION_FAILURE); - } -} - -/* Pop the directory stack, and then change to the new top of the stack. - If LIST is non-null it should consist of a word +N or -N, which says - what element to delete from the stack. The default is the top one. */ -int -popd_builtin (list) - WORD_LIST *list; -{ - register int i; - long which; - int flags; - char direction; - char *which_word; - - which_word = (char *)NULL; - for (flags = 0, which = 0L, direction = '+'; list; list = list->next) - { - if (ISOPTION (list->word->word, 'n')) - { - flags |= NOCD; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; - break; - } - else if (((direction = list->word->word[0]) == '+') || direction == '-') - { - if (legal_number (list->word->word + 1, &which) == 0) - { - builtin_error (m_badarg, list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - which_word = list->word->word; - } - else if (*list->word->word == '-') - { - bad_option (list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - else - break; - } - - if (which > directory_list_offset || (directory_list_offset == 0 && which == 0)) - { - pushd_error (directory_list_offset, which_word ? which_word : ""); - return (EXECUTION_FAILURE); - } - - /* Handle case of no specification, or top of stack specification. */ - if ((direction == '+' && which == 0) || - (direction == '-' && which == directory_list_offset)) - { - i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1]) - : EXECUTION_SUCCESS; - if (i != EXECUTION_SUCCESS) - return (i); - free (pushd_directory_list[--directory_list_offset]); - } - else - { - /* Since an offset other than the top directory was specified, - remove that directory from the list and shift the remainder - of the list into place. */ - i = (direction == '+') ? directory_list_offset - which : which; - free (pushd_directory_list[i]); - directory_list_offset--; - - /* Shift the remainder of the list into place. */ - for (; i < directory_list_offset; i++) - pushd_directory_list[i] = pushd_directory_list[i + 1]; - } - - dirs_builtin ((WORD_LIST *)NULL); - return (EXECUTION_SUCCESS); -} - -/* Print the current list of directories on the directory stack. */ -int -dirs_builtin (list) - WORD_LIST *list; -{ - int flags, desired_index, index_flag, vflag; - long i; - char *temp, *w; - - for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next) - { - if (ISOPTION (list->word->word, 'l')) - { - flags |= LONGFORM; - } - else if (ISOPTION (list->word->word, 'c')) - { - flags |= CLEARSTAK; - } - else if (ISOPTION (list->word->word, 'v')) - { - vflag |= 2; - } - else if (ISOPTION (list->word->word, 'p')) - { - vflag |= 1; - } - else if (ISOPTION (list->word->word, '-')) - { - list = list->next; - break; - } - else if (*list->word->word == '+' || *list->word->word == '-') - { - int sign; - if (legal_number (w = list->word->word + 1, &i) == 0) - { - builtin_error (m_badarg, list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - sign = (*list->word->word == '+') ? 1 : -1; - desired_index = get_dirstack_index (i, sign, &index_flag); - } - else - { - bad_option (list->word->word); - builtin_usage (); - return (EXECUTION_FAILURE); - } - } - - if (flags & CLEARSTAK) - { - clear_directory_stack (); - return (EXECUTION_SUCCESS); - } - - if (index_flag && (desired_index < 0 || desired_index > directory_list_offset)) - { - pushd_error (directory_list_offset, w); - return (EXECUTION_FAILURE); - } - -#define DIRSTACK_FORMAT(temp) \ - (flags & LONGFORM) ? temp : polite_directory_format (temp) - - /* The first directory printed is always the current working directory. */ - if (index_flag == 0 || (index_flag == 1 && desired_index == 0)) - { - temp = get_working_directory ("dirs"); - if (temp == 0) - temp = savestring ("<no current directory>"); - if (vflag & 2) - printf ("%2d %s", 0, DIRSTACK_FORMAT (temp)); - else - printf ("%s", DIRSTACK_FORMAT (temp)); - free (temp); - if (index_flag) - { - putchar ('\n'); - return EXECUTION_SUCCESS; - } - } - -#define DIRSTACK_ENTRY(i) \ - (flags & LONGFORM) ? pushd_directory_list[i] \ - : polite_directory_format (pushd_directory_list[i]) - - /* Now print the requested directory stack entries. */ - if (index_flag) - { - if (vflag & 2) - printf ("%2d %s", directory_list_offset - desired_index, - DIRSTACK_ENTRY (desired_index)); - else - printf ("%s", DIRSTACK_ENTRY (desired_index)); - } - else - for (i = directory_list_offset - 1; i >= 0; i--) - if (vflag >= 2) - printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i)); - else - printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i)); - - putchar ('\n'); - fflush (stdout); - return (EXECUTION_SUCCESS); -} - -static void -pushd_error (offset, arg) - int offset; - char *arg; -{ - if (offset == 0) - builtin_error ("directory stack empty"); - else if (arg) - builtin_error ("%s: bad directory stack index", arg); - else - builtin_error ("bad directory stack index"); -} - -static void -clear_directory_stack () -{ - register int i; - - for (i = 0; i < directory_list_offset; i++) - free (pushd_directory_list[i]); - directory_list_offset = 0; -} - -/* Switch to the directory in NAME. This uses the cd_builtin to do the work, - so if the result is EXECUTION_FAILURE then an error message has already - been printed. */ -static int -cd_to_string (name) - char *name; -{ - WORD_LIST *tlist; - int result; - - tlist = make_word_list (make_word (name), NULL); - result = cd_builtin (tlist); - dispose_words (tlist); - return (result); -} - -static int -change_to_temp (temp) - char *temp; -{ - int tt; - - tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE; - - if (tt == EXECUTION_SUCCESS) - dirs_builtin ((WORD_LIST *)NULL); - - return (tt); -} - -static void -add_dirstack_element (dir) - char *dir; -{ - int j; - - if (directory_list_offset == directory_list_size) - { - j = (directory_list_size += 10) * sizeof (char *); - pushd_directory_list = (char **)xrealloc (pushd_directory_list, j); - } - pushd_directory_list[directory_list_offset++] = dir; -} - -static int -get_dirstack_index (ind, sign, indexp) - int ind, sign, *indexp; -{ - if (indexp) - *indexp = sign > 0 ? 1 : 2; - - /* dirs +0 prints the current working directory. */ - /* dirs -0 prints last element in directory stack */ - if (ind == 0 && sign > 0) - return 0; - else if (ind == directory_list_offset) - { - if (indexp) - *indexp = sign > 0 ? 2 : 1; - return 0; - } - else - return (sign > 0 ? directory_list_offset - ind : ind); -} - -char * -get_dirstack_element (ind, sign) - int ind, sign; -{ - int i; - - i = get_dirstack_index (ind, sign, (int *)NULL); - return (i < 0 || i > directory_list_offset) ? (char *)NULL - : pushd_directory_list[i]; -} - -void -set_dirstack_element (ind, sign, value) - int ind, sign; - char *value; -{ - int i; - - i = get_dirstack_index (ind, sign, (int *)NULL); - if (ind == 0 || i < 0 || i > directory_list_offset) - return; - free (pushd_directory_list[i]); - pushd_directory_list[i] = savestring (value); -} - -WORD_LIST * -get_directory_stack () -{ - register int i; - WORD_LIST *ret; - char *d, *t; - - for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++) - { - d = polite_directory_format (pushd_directory_list[i]); - ret = make_word_list (make_word (d), ret); - } - /* Now the current directory. */ - d = get_working_directory ("dirstack"); - i = 0; /* sentinel to decide whether or not to free d */ - if (d == 0) - d = "."; - else - { - t = polite_directory_format (d); - /* polite_directory_format sometimes returns its argument unchanged. - If it does not, we can free d right away. If it does, we need to - mark d to be deleted later. */ - if (t != d) - { - free (d); - d = t; - } - else /* t == d, so d is what we want */ - i = 1; - } - ret = make_word_list (make_word (d), ret); - if (i) - free (d); - return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */ -} - -static char *dirs_doc[] = { - "Display the list of currently remembered directories. Directories", - "find their way onto the list with the `pushd' command; you can get", - "back up through the list with the `popd' command.", - "", - "The -l flag specifies that `dirs' should not print shorthand versions", - "of directories which are relative to your home directory. This means", - "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag", - "causes `dirs' to print the directory stack with one entry per line,", - "prepending the directory name with its position in the stack. The -p", - "flag does the same thing, but the stack position is not prepended.", - "The -c flag clears the directory stack by deleting all of the elements.", - "", - "+N displays the Nth entry counting from the left of the list shown by", - " dirs when invoked without options, starting with zero.", - "", - "-N displays the Nth entry counting from the right of the list shown by", - " dirs when invoked without options, starting with zero.", - (char *)NULL -}; - -static char *pushd_doc[] = { - "Adds a directory to the top of the directory stack, or rotates", - "the stack, making the new top of the stack the current working", - "directory. With no arguments, exchanges the top two directories.", - "", - "+N Rotates the stack so that the Nth directory (counting", - " from the left of the list shown by `dirs', starting with" - " zero) is at the top.", - "", - "-N Rotates the stack so that the Nth directory (counting", - " from the right of the list shown by `dirs', starting with" - " zero) is at the top.", - "", - "-n suppress the normal change of directory when adding directories", - " to the stack, so only the stack is manipulated.", - "", - "dir adds DIR to the directory stack at the top, making it the", - " new current working directory.", - "", - "You can see the directory stack with the `dirs' command.", - (char *)NULL -}; - -static char *popd_doc[] = { - "Removes entries from the directory stack. With no arguments,", - "removes the top directory from the stack, and cd's to the new", - "top directory.", - "", - "+N removes the Nth entry counting from the left of the list", - " shown by `dirs', starting with zero. For example: `popd +0'", - " removes the first directory, `popd +1' the second.", - "", - "-N removes the Nth entry counting from the right of the list", - " shown by `dirs', starting with zero. For example: `popd -0'", - " removes the last directory, `popd -1' the next to last.", - "", - "-n suppress the normal change of directory when removing directories", - " from the stack, so only the stack is manipulated.", - "", - "You can see the directory stack with the `dirs' command.", - (char *)NULL -}; - -struct builtin pushd_struct = { - "pushd", - pushd_builtin, - BUILTIN_ENABLED, - pushd_doc, - "pushd [+N | -N] [-n] [dir]", - 0 -}; - -struct builtin popd_struct = { - "popd", - popd_builtin, - BUILTIN_ENABLED, - popd_doc, - "popd [+N | -N] [-n]", - 0 -}; - -struct builtin dirs_struct = { - "dirs", - dirs_builtin, - BUILTIN_ENABLED, - dirs_doc, - "dirs [-clpv] [+N] [-N]", - 0 -}; diff --git a/examples/loadables/sleep.c b/examples/loadables/sleep.c index 57f2cb60..8ae4bc9b 100644 --- a/examples/loadables/sleep.c +++ b/examples/loadables/sleep.c @@ -60,6 +60,8 @@ long sec, usec; /* * An incredibly simplistic floating point converter. */ +static int multiplier[7] = { 1, 100000, 10000, 1000, 100, 10, 1 }; + static int convert(s, sp, usp) char *s; @@ -95,18 +97,11 @@ long *sp, *usp; } /* Now convert to millionths */ - if (n == 1) - usec *= 100000; - else if (n == 2) - usec *= 10000; - else if (n == 3) - usec *= 1000; - else if (n == 4) - usec *= 100; - else if (n == 5) - usec *= 10; - else if (n == 6 && p[6] && isdigit(p[6]) && p[6] >= '5') + usec *= multiplier[n]; + + if (n == 6 && p[6] && isdigit(p[6]) && p[6] >= '5') usec++; /* round up 1 */ + RETURN(1); } diff --git a/examples/loadables/sync.c b/examples/loadables/sync.c new file mode 100644 index 00000000..44d4e095 --- /dev/null +++ b/examples/loadables/sync.c @@ -0,0 +1,32 @@ +/* sync - sync the disks by forcing pending filesystem writes to complete */ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +sync_builtin (list) + WORD_LIST *list; +{ + sync(); + return (EXECUTION_SUCCESS); +} + +char *sync_doc[] = { + "force completion of pending disk writes", + (char *)NULL +}; + +struct builtin sync_struct = { + "sync", /* builtin name */ + sync_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + sync_doc, /* array of long documentation strings. */ + "sync", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/template.c b/examples/loadables/template.c new file mode 100644 index 00000000..7bb3f9f3 --- /dev/null +++ b/examples/loadables/template.c @@ -0,0 +1,56 @@ +/* template - example template for loadable builtin */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "bashansi.h" +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +extern char *strerror (); + +template_builtin (list) + WORD_LIST *list; +{ + int opt, rval; + + rval = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + return (rval); +} + +char *template_doc[] = { + (char *)NULL +}; + +struct builtin template_struct = { + "template", /* builtin name */ + template_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + template_doc, /* array of long documentation strings. */ + "template", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c new file mode 100644 index 00000000..9f450cda --- /dev/null +++ b/examples/loadables/uname.c @@ -0,0 +1,139 @@ +/* + * uname - print system information + * + * usage: uname [-amnrsv] + * + */ + +#include <config.h> +#include <stdio.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNAME) +# include <sys/utsname.h> +#else +struct utsname { + char sysname[32]; + char nodename[32]; + char release[32]; + char version[32]; + char machine[32]; +}; +#endif + +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#define FLAG_SYSNAME 0x01 /* -s */ +#define FLAG_NODENAME 0x02 /* -n */ +#define FLAG_RELEASE 0x04 /* -r */ +#define FLAG_VERSION 0x08 /* -v */ +#define FLAG_MACHINE 0x10 /* -m, -p */ + +#define FLAG_ALL 0x1f + +#ifndef errno +extern int errno; +#endif + +static void uprint(); + +static int uname_flags; + +uname_builtin (list) + WORD_LIST *list; +{ + int opt, r; + struct utsname uninfo; + + uname_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "amnprsv")) != -1) + { + switch (opt) + { + case 'a': + uname_flags |= FLAG_ALL; + break; + case 'm': + case 'p': + uname_flags |= FLAG_MACHINE; + break; + case 'n': + uname_flags |= FLAG_NODENAME; + break; + case 'r': + uname_flags |= FLAG_RELEASE; + break; + case 's': + uname_flags |= FLAG_SYSNAME; + break; + case 'v': + uname_flags |= FLAG_VERSION; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (uname_flags == 0) + uname_flags = FLAG_SYSNAME; + + /* Only ancient systems will not have uname(2). */ +#ifdef HAVE_UNAME + if (uname (&uninfo) < 0) + { + builtin_error ("cannot get system name: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } +#else + builtin_error ("cannot get system information: uname(2) not available"); + return (EXECUTION_FAILURE); +#endif + + uprint (FLAG_SYSNAME, uninfo.sysname); + uprint (FLAG_NODENAME, uninfo.nodename); + uprint (FLAG_RELEASE, uninfo.release); + uprint (FLAG_VERSION, uninfo.version); + uprint (FLAG_MACHINE, uninfo.machine); + + return (EXECUTION_SUCCESS); +} + +static void +uprint (flag, info) + int flag; + char *info; +{ + if (uname_flags & flag) + { + uname_flags &= ~flag; + printf ("%s%c", info, uname_flags ? ' ' : '\n'); + } +} + +char *uname_doc[] = { + "display information about the system", + (char *)NULL +}; + +struct builtin uname_struct = { + "uname", + uname_builtin, + BUILTIN_ENABLED, + uname_doc, + "uname [-amnrsv]", + 0 +}; diff --git a/examples/loadables/unlink.c b/examples/loadables/unlink.c new file mode 100644 index 00000000..8c81ad02 --- /dev/null +++ b/examples/loadables/unlink.c @@ -0,0 +1,52 @@ +/* unlink - remove a directory entry */ + +/* Should only be used to remove directories by a superuser prepared to let + fsck clean up the file system. */ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +unlink_builtin (list) + WORD_LIST *list; +{ + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (unlink (list->word->word) != 0) + { + builtin_error ("%s: cannot unlink: %s", list->word->word, strerror (errno)); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +char *unlink_doc[] = { + "Remove a directory entry.", + (char *)NULL +}; + +struct builtin unlink_struct = { + "unlink", /* builtin name */ + unlink_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + unlink_doc, /* array of long documentation strings. */ + "unlink name", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c new file mode 100644 index 00000000..41fd5c48 --- /dev/null +++ b/examples/loadables/whoami.c @@ -0,0 +1,52 @@ +/* + * whoami - print out username of current user + */ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +whoami_builtin (list) + WORD_LIST *list; +{ + int opt; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (current_user.user_name == 0) + get_current_user_info (); + printf ("%s\n", current_user.user_name); + return (EXECUTION_SUCCESS); +} + +char *whoami_doc[] = { + "display name of current user", + (char *)NULL +}; + +struct builtin whoami_struct = { + "whoami", + whoami_builtin, + BUILTIN_ENABLED, + whoami_doc, + "whoami", + 0 +}; diff --git a/examples/misc/aliasconv.bash b/examples/misc/aliasconv.bash index cb92ee0a..3fbe877d 100755 --- a/examples/misc/aliasconv.bash +++ b/examples/misc/aliasconv.bash @@ -1,8 +1,8 @@ #! /bin/bash # -# alias-conv.sh - convert csh aliases to bash aliases and functions +# aliasconv.bash - convert csh aliases to bash aliases and functions # -# usage: alias-conv.sh +# usage: aliasconv.bash # # Chet Ramey # chet@po.cwru.edu @@ -27,7 +27,11 @@ mkalias () } EOF -sed "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 +# the first thing we want to do is to protect single quotes in the alias, +# since they whole thing is going to be surrounded by single quotes when +# passed to mkalias + +sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 $BASH /tmp/cb$$.1 | sed -e 's/\$cwd/\$PWD/g' \ -e 's/\$term/\$TERM/g' \ diff --git a/examples/misc/aliasconv.sh b/examples/misc/aliasconv.sh index 4cbebfb4..29e1ead9 100755 --- a/examples/misc/aliasconv.sh +++ b/examples/misc/aliasconv.sh @@ -1,8 +1,8 @@ #! /bin/bash # -# alias-conv.sh - convert csh aliases to bash aliases and functions +# aliasconv.sh - convert csh aliases to bash aliases and functions # -# usage: alias-conv.sh +# usage: aliasconv.sh # # Chet Ramey # chet@po.cwru.edu @@ -27,7 +27,11 @@ mkalias () } EOF -sed "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 +# the first thing we want to do is to protect single quotes in the alias, +# since they whole thing is going to be surrounded by single quotes when +# passed to mkalias + +sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 sh /tmp/cb$$.1 | sed -e 's/\$cwd/\$PWD/g' \ -e 's/\$term/\$TERM/g' \ diff --git a/examples/scripts/adventure.sh b/examples/scripts/adventure.sh index a6d2eaa3..4e223939 100755 --- a/examples/scripts/adventure.sh +++ b/examples/scripts/adventure.sh @@ -90,6 +90,11 @@ ash_lk(){ echo " $1 " | fgrep " $2 " >&- 2>&-; } ash_pr(){ echo $* | tr ' ' '\012' | pr -5 -t -w75 -l$[ ( $# + 4 ) / 5 ]; } ash_rm(){ echo " $1 " | sed -e "s/ $2 / /" -e 's/^ //' -e 's/ $//'; } +# enable history, bang history expansion, and emacs editing +set -o history +set -o histexpand +set -o emacs + cd LIM=.limbo # $HOME/$LIM contains "destroyed" objects mkdir $LIM >&- 2>&- @@ -164,8 +169,7 @@ do room=`pwd` prev=$room fi - echo -n '-advsh> ' # prompt - read verb obj x + read -e -p '-advsh> ' verb obj x # prompt is '-advsh> ' if [ $? != 0 ] then verb=quit # EOF fi |