1-Feb-86 09:37:35-MST,30567;000000000001
Return-Path: <[email protected]>
Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sat 1 Feb 86 09:36:16-MST
Received: from usenet by TGR.BRL.ARPA id a002623; 1 Feb 86 9:33 EST
From: chris <[email protected]>
Newsgroups: net.sources
Subject: Improved Bcsh (Bourne Shell Cshell-Emulator)
Message-ID: <[email protected]>
Date: 30 Jan 86 17:34:26 GMT
To:       [email protected]

This is a new, improved version of my Bourne shell cshell-emulator.
The code has been cleaned up quite a bit, and a couple of new features
added (now supports 'noclobber' and 'iclobber' variables).  A bug with
'eval' that caused "illegal I/O" error messages on vanilla V7 shells has
also been fixed.

I have posted the program in its entirety because a context diff of the
old and new versions was longer than the new version...

--Chris
--------------- cut here --------------------- cut here -------------------

:       Bcsh -- A Simple Cshell-Like Command Pre-Processor For The Bourne Shell
:
:       "Copyright (c) Chris Robertson, December 1985"
:
:       This software may be used for any purpose provided the original
:       copyright notice and this notice are affixed thereto.  No warranties of
:       any kind whatsoever are provided with this software, and it is hereby
:       understood that the author is not liable for any damagages arising
:       from the use of this software.
:
:       Features Which the Cshell Does Not Have:
:       ----------------------------------------
:
:       +  command history persists across bcsh sessions
:       +  global last-command editing via 'g^string1^string2^' syntax
:       +  edit any command via $EDITOR or $VISUAL editors
:       +  history file name, .bcshrc file name, alias file name, and number
:          of commands saved on termination can be set by environment variables
:       +  prompt may evaluate commands, such as `pwd`, `date`, etc.
:       +  the whole text of interactive 'for' and 'while' loops and 'if'
:          statements goes into the history list and may be re-run or edited
:       +  multiple copies of commands and requests to see command history
:          are not added to the history list
:       +  the history mechanism actually stores all commands entered in a
:          current session, not just $history of them.  This means that you
:          can increase $history on the fly and at once have a larger history.
:
:
:       Synonyms:
:       ---------
:
:       logout, exit, bye       write out history file and exit
:       h, history              show current history list
:
:
:       Aliases:
:       --------
:
:       alias NAME CMND         create an alias called NAME to run CMND
:       unalias NAME            remove the alias NAME
:
:       There are no 'current-session only' aliases -- all alias and unalias
:       commands are permanent, and stored in the $aliasfile.
:
:       If an alias contains positional variables -- $1, $2, $*, etc. -- any
:       arguments following the alias name are considered to be values for
:       those variables, and the alias is turned into a command of the form
:       'set - arguments;alias'.  Otherwise, a simple substitution is performed
:       for the alias and the rest of the command preserved.  The cshell
:       convention of using '\!:n' in an alias to get bits of the current
:       command is mercifully abandoned.
:
:       Quotes are not necessary around the commands comprising an alias;
:       in fact, any enclosing quotes are stripped when the alias is added
:       to the file.
:
:       A couple of typical aliases might be:
:
:               goto    cd $1;pwd
:               l       ls -F
:
:       Note that aliasing something to "commands;logout" will not work -- if
:       you want something to happen routinely on logout put it in the file
:       specified by $logoutfile, default = $HOME/.blogout.
:
:
:       Command Substitutions:
:       ----------------------
:
:       !!                      substitute last command from history list
:       !!:N                    substitute Nth element of last command from
:                               history list -- 0 = command name, 1 = 1st arg
:       !!:$                    substitute last element of last command from
:                               history list
:       !!:*                    substitute all arguments to last command
:                               from history list
:       !NUMBER                 substitute command NUMBER from the history list
:       !NUMBER:N               as above, but substitute Nth element, where
:                               0 = command name, 1 = 1st arg, etc.
:       !NUMBER:$               as above, but substitute last element
:       !NUMBER:*               as above, but substitute all arguments
:       !-NUMBER                substitute the command NUMBER lines from the
:                               end of the history list; 1 = last command
:       !-NUMBER:N              as above, but substitute Nth element, where
:                               0 = command name, 1 = 1st arg, etc.
:       !-NUMBER:$              as above, but substitute last element
:       !-NUMBER:*              as above, but substitute all arguments
:       !?STRING                substitute most-recent command from history list
:                               containing STRING -- STRING must be enclosed in
:                               braces if followed by any other characters
:       !?STRING:N              as above, but substitute Nth element, where
:                               0 = command name, 1 = 1st arg, etc.
:       !?STRING:$              as above, but substitute last element
:       !?STRING:*              as above, but substitute all arguments
:
:
:       Command Editing:
:       ----------------
:
:       CMND~e                  edit CMND using $EDITOR, where CMND may be found
:                               using a history substitution
:       CMND~v                  edit CMND using $VISUAL, where CMND may be found
:                               using a history substitution
: "     ^string1^string2^       substitute string2 for string1 in last command"
:                               command and run it
: "     g^string1^string2^      globally substitute string2 for string1 in  "
:                               last command and run it
:       !NUMBER:s/string1/string2/
:                               substitute string2 for string1 in
:                               command NUMBER and run it
:       !NUMBER:gs/string1/string2/
:                               globally substitute string2 for string1 in
:                               command NUMBER and run it
:       !?STRING:s/string1/string2/
:                               substitute string2 for string1 in last command
:                               containing STRING and run it
:       !?STRING:gs/string1/string2/
:                               globally substitute string2 for string1 in last
:                               command containing STRING and run it
:
:       Any command which ends in the string ":p" is treated as a normal
:       command until all substitutions have been completed.  The trailing
:       ":p" is then stripped, and the command is simply echoed and added to
:       the history list instead of being executed.
:
:       None of the other colon extensions of the cshell are supported.
:
:
:       Shell Environment Variables:
:       ----------------------------
:
:       EDITOR          editor used by ~e command, default = "ed"
:       VISUAL          editor used by ~v command, default = "vi"
:       MAIL            your system mailbox
:       PAGER           paging program used by history command, default = "more"
:       PS1             primary prompt
:       PS2             secondary prompt
:       history         number of commands in history list, default = 22
:       histfile        file history list is saved in, default = $HOME/.bhistory
:       savehist        number of commands remembered from last bcsh session
:       aliasfile       file of aliased commands, default = $HOME/.baliases
:       logoutfile      file of commands to be executed before termination
:       inc_cmdno       yes/no -- keep track of command numbers or not
:       noclobber       if set, existing files are not overwritten by '>'
:       iclobber        if both noclobber and iclobber are set, the user is
:                       prompted for confirmation before existing files are
:                       overwritten by '>'
:
:       Note:   if you are setting either noclobber or iclobber mid-session,
:               set them to 'yes'
:
:
:       Regular Shell Variables:
:       ------------------------
:
:       Shell variables may be set via Bourne or cshell syntax, e.g., both
:       "set foo=bar" and "foo=bar" set a variable called "foo" with the value
:       "bar".  However, all variables are automatically set as environment
:       variables, so there is no need to export them.  Conversely, there
:       are NO local variables.  Sorry, folks.
:
:       A cshell-style "setenv" command is turned into a regular "set" command.
:
:
:       The Prompt:
:       ----------
:
:       You may, if you wish, have a command executed in your prompt.  If
:       the variable PS1 contains a dollar sign or a backquote, it is
:       evaluated and the result used as the prompt, provided the evaluation
:       did not produce a "not found" error message.  The two special cases
:       of PS1 consisting solely of "$" or "$ " are handled correctly.  For
:       example, to have the prompt contain the current directory followed
:       by a space, enter:
:
:               PS1=\'echo "`pwd` "\'
:
:       You need the backslashed single quotes to prevent the command being
:       evaluated by the variable-setting mechanism and the shell before it
:       is assigned to PS1.
:
:       To include the command number in your prompt, enter the command:
:
:               PS1=\'echo "$cmdno "\'
:
:
:       Shell Control-Flow Syntax:
:       --------------------------
:
:       'While', 'for', 'case', and 'if' commands entered in Bourne shell
:       syntax are executed as normal.
:
:       A valiant attempt is made to convert 'foreach' loops into 'for' loops,
:       cshell-syntax 'while' loops into Bourne shell syntax, and 'switch'
:       statements into 'case' statements.  I cannot guarantee to always get it
:       right.  If you forget the 'do' in a 'while' or 'for' loop, or finish
:       them with 'end' instead of 'done', this will be corrected.
:
:       Note that cshell-to-Bourne control flow conversions do not take place
:       if control is nested -- e.g., a 'foreach' inside a 'while' will fail.
:
:       The simple-case cshell "if (condition) command" is turned into Bourne
:       syntax.  Other 'if' statements are left alone apart from making the
:       'then' a separate statement, because constructing a valid interactive
:       cshell 'if' statement is essentially an exercise in frustration anyway.
:       The cshell and Bourne shell have sufficiently different ideas about
:       conditions that if is probably best to resign yourself to learning
:       the Bourne shell conventions.
:
:       Note that since most of the testing built-ins of the cshell are
:       not available in the Bourne shell, a complex condition in a 'while'
:       loop or an 'if' statement will probably fail.
:
:
:       Bugs, Caveats, etc.:
:       --------------------
:
:       This is not a super-speedy program.  Be patient, especially on startup.
:
:       To the best of my knowledge this program should work on ANY Bourne
:       shell -- note that if your shell does not understand 'echo -n' you
:       will have to re-set the values of '$n' and '$c'.
:
:       This program may run out of stack space on a 16-bit machine where
:       /bin/sh is not split-space.
:
:       Mail checking is done every 10 commands if $MAIL is set in your
:       environment.  For anything fancier, you will have to hack the code.
:
:       Because commands are stuffed in a file before sh is invoked on them,
:       error messages from failed commands are ugly.
:
:       Failed history substitutions either give nothing at all, or a
:       "not found" style of error message.
:
:       A command history is kept whether you want it or not.  This may be
:       perceived as a bug or a feature, depending on which side of bed you
:       got out on.
:
:       If you want a real backslash in a command, you will have to type two
:       of them  because the shell swallows the first backslash in the initial
:       command pickup.  This means that to include a non-history '!' in a
:       command you need '\\!' -- a real wart, especially for net mail,
:       but unavoidable.
:
:       Commands containing an '@' will break all sorts of things.
:
:       Very complex history substitutions may fail.
:
:       File names containing numbers may break numeric history sustitutions.
:
:       Commands containing bizzare sequences of characters may conflict
:       with internal kludges.
:
:       Aliasing something to "commands;logout" will not work -- if you
:       want something to happen routinely on logout, put it in the file
:       specified by $logoutfile, default = $HOME/.blogout.
:
:       Please send all bug reports to ihnp4!utzoo!globetek!chris.
:       Flames will be posted to net.general with 'Reply-to' set to your
: '     path...  :-)                                                    '
:
:
:
:               ************* VERY IMPORTANT NOTICE *************
:
: If your shell supports # comments, then REPLACE all the colon 'comments'
: with # comments.  If it does not, then REMOVE all the 'comment' lines from the
: working copy of the file, as it will run MUCH faster -- the shell evaluates
: lines starting with a colon but does not actually execute them, so you will
: save the read-and-evaluate time by removing them.



case "`echo -n foo`" in
       -n*)
               n=
               c="\c"
               ;;
       foo)
               n=-n
               c=
               ;;
       *)
               echo "Your 'echo' command is broken."
               exit 1
               ;;
esac
history=${history-22}
savehist=${savehist-22}
histfile=${histfile-$HOME/.bhistory}
logoutfile=${logoutfile-$HOME/.blogout}
EDITOR=${EDITOR-ed}
VISUAL=${VISUAL-vi}
PAGER=${PAGER-more}

aliasfile=${aliasfile-$HOME/.baliases}

: the alias file may contain 1 blank line, so a test -s will not work

case "`cat $aliasfile 2> /dev/null`" in
       "")
               doalias=no
               ;;
       *)
               doalias=yes
               ;;
esac

if test -s "${sourcefile-$HOME/.bcshrc}"
       then
       . ${sourcefile-$HOME/.bcshrc}
fi

if test -s "$histfile"
       then
       cmdno="`set - \`wc -l $histfile\`;echo $1`"
       cmdno="`expr \"$cmdno\" + 1`"
       lastcmd="`tail -1 $histfile`"
       copy=false
       ohist=$histfile
       while test ! -w "$histfile"
               do
               echo "Cannot write to history file '$histfile'."
               echo $n "Please enter a new history filename: $c"
               read histfile
               copy=true
       done
       if $copy
               then
               cp $ohist $histfile
       fi
else
       cat /dev/null > $histfile
       cmdno=1
       lastcmd=
fi

: keep track of command number as the default

inc_cmdno=${inc_cmdo-yes}

: default prompts -- PS1 and PS2 may be SET but EMPTY, so '${PS1-% }' syntax
: is not used here

case "$PS1" in
       "")
               PS1="% "
               ;;
esac
case "$PS2" in
       "")
               PS2="> "
               ;;
esac

export histfile savehist history aliasfile EDITOR VISUAL PAGER cmdno PS1 PS2

case "$MAIL" in
       "")
               ;;
       *)
               mailsize=`set - \`wc -c $MAIL\`;echo $1`
               ;;
esac

trap ':' 2
trap exit 3
trap "tail -$savehist $histfile>/tmp/hist$$;uniq /tmp/hist$$ > $histfile;\
rm -f /tmp/*$$;exit 0" 15

getcmd=yes
mailcheck=
exclaim=
echoit=
mailprompt=

while :
       do
       run=yes
       case "$mailprompt" in
               "")
                       ;;
               *)
                       echo "$mailprompt"
                       ;;
       esac
       case "$getcmd" in
               yes)
                       : guess if the prompt should be evaluated or not

                       case "$PS1" in
                               \$|\$\ )
                                       echo $n "$PS1$c"
                                       ;;
                               *\`*|*\$*)
                                       tmp="`(eval $PS1) 2>&1`"
                                       case "$tmp" in
                                               *not\ found)
                                                       echo $n "$PS1$c"
                                                       ;;
                                               *)
                                                       echo $n "$tmp$c"
                                                       ;;
                                       esac
                                       ;;
                               *)
                                       echo $n "$PS1$c"
                                       ;;
                       esac

                       read cmd
                       ;;
       esac
       case "$MAIL" in
               "")
                       ;;
               *)
                       : check for mail every 10 commands

                       case "$mailcheck" in
                               1111111111)
                                       mailcheck=
                                       newsize="`set - \`wc -c $MAIL\`;echo $1`"
                                       if test "$newsize" -gt "$mailsize"
                                               then
                                               mailprompt="You have new mail"
                                       else
                                               mailprompt=
                                       fi
                                       mailsize=$newsize
                                       ;;
                               *)
                                       mailcheck=1$mailcheck
                                       ;;
                       esac
                       ;;
       esac
       hist=no
       case "$cmd" in
               "")
                       continue
                       ;;
               sh)
                       sh
                       run=no
                       ;;
               !!)
                       cmd=$lastcmd
                       echoit=yes
                       getcmd=no
                       continue
                       ;;
               *:p)
                       cmd="`expr \"$cmd\" : '\(.*\):p'` +~+p"
                       getcmd=no
                       continue
                       ;;
               foreach[\ \     ]*)
                       while test "$line" != "end"
                               do
                               echo $n "$PS2$c"
                               read line
                               cmd="${cmd};$line"
                       done
                       echo "$cmd" > /tmp/bcsh$$
                       ed - /tmp/bcsh$$ << ++++
                       s/end/done/
                       s/foreach[      ]\(.*\)(/for \1 in /
                       s/)//
                       s/;/;do /
                       w
++++
                       ;;
               for[\ \ ]*|while[\ \    ]*)

                       : try to catch the most common cshell-to-Bourne-shell mistakes

                       echo $n "$PS2$c"
                       read line
                       case "$line" in
                               *do)
                                       line="do :"
                                       ;;
                               *do*)
                                       ;;
                               *)
                                       line="do $line"
                                       ;;
                       esac
                       cmd="${cmd};$line"
                       while test "$line" != "done" -a "$line" != "end"
                               do
                               echo $n "$PS2$c"
                               read line
                               case "$line" in
                                       end)
                                               line=done
                                               ;;
                               esac
                               cmd="${cmd};$line"
                       done
                       echo "$cmd" > /tmp/bcsh$$
                       ;;
               if[\ \  ]*)
                       while test "$line" != "fi" -a "$line" != "endif"
                               do
                               echo $n "$PS2$c"
                               read line
                               case "$line" in
                                       *[a-z]*then)
                                               line="`expr \"$line\" : '\(.*\)then'`;then"
                                               ;;
                                       endif)
                                               line=fi
                                               ;;
                               esac
                               cmd="${cmd};$line"
                       done
                       echo "$cmd" > /tmp/bcsh$$
                       case "`grep then /tmp/bcsh$$`" in
                               "")
                                       : fix 'if foo bar' cases

                                       ed - /tmp/bcsh$$ << ++++
                                       s/)/);then/
                                       s/.*/;fi/
                                       w
++++
                                       ;;
                       esac
                       ;;
               case[\ \        ]*)
                       while test "$line" != "esac"
                               do
                               echo $n "$PS2$c"
                               read line
                               cmd="${cmd}@$line"
                       done
                       cmd="`echo \"$cmd\" | tr '@' ' '`"
                       echo "$cmd" > /tmp/bcsh$$
                       ;;
               switch[\ \      ]*)
                       while test "$line" != "endsw"
                               do
                               echo $n "$PS2$c"
                               read line
                               cmd="${cmd}@$line"
                       done
                       echo "$cmd" > /tmp/bcsh$$
                       ed - /tmp/bcsh$$ << '++++'
                       1,$s/@/\
/g
                       g/switch.*(/s//case "/
                       s/)/" in/
                       1,$s/case[       ]\(.*\):$/;;\
       \1)/
                       2d
                       1,$s/endsw/;;\
esac/
                       g/breaksw/s///
                       1,$s/default.*/;;\
       *)/
                       w
++++
                       cmd="`cat /tmp/bcsh$$`"
                       ;;
               *!*)
                       hist=yes
                       ;;
       esac
       case "$hist" in
               yes)

                       : deal with genuine exclamation marks, go back and parse again

                       case "$cmd" in
                               *\>![\ \        ]*|*\\!*)
                                       cmd="`echo \"$cmd\" | sed -e 's@\\!@REALEXCLAMATIONMARK@g'`"
                                       exclaim=yes
                                       getcmd=no
                                       continue
                                       ;;
                       esac

                       : break command into elements, parse each one

                       tmp=
                       for i in $cmd
                               do

                               : find element with !, peel off stuff up to !

                               case "$i" in
                                       !)

                                               : most likely a typo for !!, so fix it

                                               front=
                                               $i=!!
                                               ;;
                                       !!*)
                                               front=
                                               i="`expr \"$i\" : '.*\(!!.*\)'`"
                                               ;;
                                       *!!*)
                                               front="`expr \"$i\" : '\(.*\)!!.*'`"
                                               i="`expr \"$i\" : '.*\(!!.*\)'`"
                                               ;;
                                       !*)
                                               front=
                                               i="`expr \"$i\" : '.*!\(.*\)'`"
                                               ;;
                                       *)
                                               tmp="$tmp$i "
                                               continue
                                               ;;
                               esac
                               case "$i" in
                                       !!*)

                                               : want last command

                                               rest="`expr \"$i\" : '!!\(.*\)'`"
                                               i=$lastcmd
                                               ;;
                                       -*)

                                               : we want to search back through the history list

                                               case "$i" in
                                                       -)
                                                               rest="`expr \"$i\" : '-\(.*\)'`"
                                                               i=$lastcmd
                                                               ;;
                                                       -[0-9]*)
                                                               wanted="`expr \"$i\" : '-\([0-9][0-9]*\).*'`"
                                                               rest="`expr \"$i\" : '-[0-9][0-9]*\(.*\)'`"
                                                               i="`tail -$wanted $histfile | sed -e "1q"`"
                                                               ;;
                                               esac
                                               ;;
                                       [0-9]*)

                                               : find which number command is wanted

                                               wanted="`expr \"$i\" : '\([0-9][0-9]*\).*'`"
                                               rest="`expr \"$i\" : '[0-9][0-9]*\(.*\)'`"
                                               i="`grep -n . $histfile | grep \"^$wanted\"`"
                                               i="`expr \"$i\" : \"${wanted}.\(.*\)\"`"
                                               ;;
                                       \?*)

                                               : find which 'command-contains' match is wanted

                                               case "$i" in
                                                       \?{*}*)
                                                               wanted="`expr \"$i\" : '?{\(.*\)}.*'`"
                                                               rest="`expr \"$i\" : '?.*}\(.*\)'`"
                                                               ;;
                                                       \?*:*)
                                                               wanted="`expr \"$i\" : '?\(.*\):.*'`"
                                                               rest="`expr \"$i\" : '?.*\(:.*\)'`"
                                                               ;;
                                                       \?*)
                                                               wanted="`expr \"$i\" : '?\(.*\)'`"
                                                               rest=
                                                               ;;
                                               esac
                                               i="`grep \"$wanted\" $histfile | tail -1`"
                                               ;;
                                       *)

                                               : find which 'start-of-command' match is wanted

                                               case "$i" in
                                                       {*}*)
                                                               wanted="`expr \"$i\" : '{\(.*\)}.*'`"
                                                               rest="`expr \"$i\" : '.*}\(.*\)'`"
                                                               ;;
                                                       *:*)
                                                               wanted="`expr \"$i\" : '\(.*\):.*'`"
                                                               rest="`expr \"$i\" : '.*\(:.*\)'`"
                                                               ;;
                                                       *)
                                                               wanted="$i"
                                                               rest=
                                                               ;;
                                               esac
                                               i="`grep \"^$wanted\" $histfile | tail -1`"
                                               ;;
                               esac

                               : see if we actually found anything to substitute

                               case "$i" in
                                       "")
                                               badsub="Event not found"
                                               break
                                               ;;
                                       *)
                                               badsub=no
                                               ;;
                               esac

                               case "$rest" in
                                       "")
                                               tmp="$front$tmp$i "
                                               continue
                                               ;;
                                       :[0-9]*)

                                               : find which element of $i is wanted

                                               number="`expr \"$rest\" : ':\([0-9][0-9]*\).*'`"
                                               rest="`expr \"$rest\" : ':[0-9][0-9]*\(.*\)'`"

                                               : count through $i till we get to the right element

                                               counter=0
                                               for element in $i
                                                       do
                                                       case "$counter" in
                                                               $number)
                                                                       break
                                                                       ;;
                                                               *)
                                                                       counter="`expr \"$counter\" + 1`"
                                                                       ;;
                                                       esac
                                               done
                                               case "$counter" in
                                                       $number)
                                                               badsub=no
                                                               ;;
                                                       *)
                                                               badsub="Bad command element"
                                                               break
                                                               ;;
                                               esac
                                               tmp="$tmp$front$element$rest "
                                               continue
                                               ;;
                                       :\$*)

                                               : spin through $i till we hit the last element

                                               rest="`expr \"$rest\" : ':\$\(.*\)'`"
                                               for element in $i
                                                       do
                                                       :
                                               done
                                               tmp="$tmp$front$element$rest "
                                               continue
                                               ;;
                                       :\**)

                                               : we want all elements except the command itself

                                               rest="`expr \"$rest\" : ':\*\(.*\)'`"
                                               save=$i
                                               set - $i
                                               shift
                                               case "$*" in
                                                       "")
                                                               badsub="No arguments to command '$save'"
                                                               break
                                                               ;;
                                                       *)
                                                               badsub=no
                                                               ;;
                                               esac
                                               tmp="$tmp$front$*$rest "
                                               continue
                                               ;;
                                       :s*|:gs*)

                                               : we are doing a substitution -- put / on end if needed

                                               case "$rest" in
                                                       :s/*/*/*|:gs/*/*/*)
                                                               ;;
                                                       :s/*/*|:gs/*/*)
                                                               rest="${rest}/"
                                                               ;;
                                               esac

                                               : find what substitution is wanted

                                               first="`expr \"$rest\" : ':*s\/\(.*\)\/.*\/.*'`"
                                               second="`expr \"$i\" : ':*s/.*/\(.*\)/.*'`"

                                               : see if it is a global substitution

                                               case "$rest" in
                                                       :gs*)
                                                               global=g
                                                               ;;
                                                       :s*)
                                                               global=
                                                               ;;
                                               esac
                                               rest="`expr \"$rest\" : '.*/.*/.*/\(.*\)'`"
                                               i="`echo \"$i\" | sed -e \"s@$first@$second@$global\"`"

                                               : see if subsitution worked

                                               case "$i" in
                                                       "")
                                                               badsub="Substiution failed"
                                                               break
                                                               ;;
                                                       *)
                                                               badsub=no
                                                               ;;
                                               esac
                                               tmp="$tmp$front$i$rest "
                                               continue
                                               ;;
                                       *)
                                               tmp="$tmp$front$i$rest "
                                               ;;
                               esac
                       done
                       case "$badsub" in
                               no)
                                       ;;
                               *)
                                       echo "$badsub"
                                       badsub=no
                                       continue
                                       ;;
                       esac
                       cmd="$tmp"
                       echoit=yes
                       getcmd=no
                       continue
                       ;;
               *)
                       run=yes
                       ;;
       esac
       case "$cmd" in
               *\^*\^*\^*)

                       : see if the substitution is global

                       case "$cmd" in
                               g*)
                                       global=g
                                       ;;
                               *)
                                       global=
                                       ;;
                       esac

                       : put a '^' on the end if necessary

                       case "$cmd" in
                               *\^)
                                       ;;
                               *)
                                       cmd="${cmd}^"
                                       ;;
                       esac

                       : find what substitution is wanted

                       first="`expr \"$cmd\" : '*\^\(.*\)\^.*\^.*'`"
                       second="`expr \"$cmd\" : '*\^.*\^\(.*\)\^.*'`"
                       rest="`expr \"$cmd\" : '*\^.*\^.*\^\(.*\)'`"
                       cmd="`echo \"$lastcmd\" | sed -e \"s@$first@$second@$global\"`$rest"

                       : see if the substitution worked

                       case "$cmd" in
                               "")
                                       echo "Substitution failed"
                                       continue
                                       ;;
                       esac
                       echoit=yes
                       getcmd=no
                       continue
                       ;;
               *~e)
                       echo "$cmd" | sed -e "s@~e@@" > /tmp/bcsh$$
                       $EDITOR /tmp/bcsh$$
                       cmd="`cat /tmp/bcsh$$`"
                       getcmd=no
                       continue
                       ;;
               *~v)
                       echo "$cmd" | sed -e "s@~v@@" > /tmp/bcsh$$
                       echo "$lastcmd" > /tmp/bcsh$$
                       $VISUAL /tmp/bcsh$$
                       cmd="`cat /tmp/bcsh$$`"
                       getcmd=no
                       continue
                       ;;
               exec[\ \        ]*)
                       tail -$savehist $histfile>/tmp/hist$$
                       uniq /tmp/hist$$ > $histfile
                       rm -f /tmp/*$$
                       echo $cmd > /tmp/cmd$$
                       . /tmp/cmd$$
                       ;;
               login[\ \       ]*|newgrp[\ \   ]*)
                       tail -$savehist $histfile>/tmp/hist$$
                       uniq /tmp/hist$$ > $histfile
                       rm -f /tmp/*$$
                       echo $cmd > /tmp/cmd$$
                       . /tmp/cmd$$
                       ;;
               logout|exit|bye)
                       if test -s "$logoutfile"
                               then
                               sh $logoutfile
                       fi
                       tail -$savehist $histfile > /tmp/hist$$
                       uniq /tmp/hist$$ > $histfile
                       rm -f /tmp/*$$
                       exit 0
                       ;;
               h|history)
                       grep -n . $histfile | tail -$history | sed -e 's@:@     @' | $PAGER
                       continue
                       ;;
               h[\ \   ]\|*|h[\ \      ]\>*|h\|*|h\>*)
                       cmd="`echo \"$cmd\" | sed -e \"s@h@grep -n . $histfile | tail -$history | sed -e 's@:@  @'@\"`"
                       getcmd=no
                       continue
                       ;;
               history[\ \     ]*\|*|history[\ \       ]*\>*)
                       cmd="`echo \"$cmd\" | sed -e \"s@history@grep -n . $histfile | tail -$history | sed -e 's@:@ @'@\"`"
                       getcmd=no
                       continue
                       ;;
               source[\ \      ]*)
                       set - $cmd
                       shift
                       echo . $*  > /tmp/cmd$$
                       . /tmp/cmd$$
                       run=no
                       ;;
               wait)
                       wait
                       run=no
                       ;;
               .[\ \   ]*)
                       echo $cmd > /tmp/cmd$$
                       . /tmp/cmd$$
                       run=no
                       ;;
               cd|cd[\ \       ]*)

                       : check if it will work first, or else this shell will terminate
                       : if the cd dies.  If you have a built-in test, you might want
                       : to replace the try-it-and-see below with a couple of tests,
                       : but it is probably just as fast like this.

                       echo $cmd > /tmp/cmd$$
                       if (sh /tmp/cmd$$)
                               then
                               . /tmp/cmd$$
                       fi
                       run=no
                       ;;
               awk[\ \ ]*|dd[\ \       ]*|cc[\ \       ]*|make[\ \     ]*)
                       : these are the only commands I can think of whose syntax
                       : includes an equals sign.  Add others as you find them.

                       echo "$cmd" > /tmp/bcsh$$
                       ;;
               setenv*|*=*)

                       : handle setting shell variables, turning cshell syntax to Bourne
                       : syntax -- note all variables must be exported or they will not
                       : be usable in other commands

                       echo "$cmd" > /tmp/cmd$$
                       ed - /tmp/cmd$$ << ++++
                       g/^setenv[      ]/s/[   ]/@/
                       g/^setenv@/s/[  ]/=/
                       g/^setenv@/s///
                       g/^set/s///
                       .t.
                       \$s/=.*//
                       s/^/export /
                       w
++++
                       . /tmp/cmd$$
                       rm -f /tmp/cmd$$
                       run=no
                       ;;
               unset[\ \       ]*|umask[\ \    ]*|export[\ \   ]*|set[\ \      ]*)

                       : handle commands which twiddle current environment

                       $cmd
                       run=no
                       ;;
               alias|alias[\ \ ])
                       $PAGER $aliasfile
                       lastcmd=$cmd
                       run=no
                       continue
                       ;;
               alias[\ \       ]*)
                       case "$cmd" in
                               alias[\ \       ]\|*|alias[\ \  ]\>*)
                                       cmd="`echo \"$cmd\" | sed -e \"s@alias@cat $aliasfile@\"`"
                                       getcmd=no
                                       continue
                                       ;;
                               alias[\ \       ]*[\ \  ]*)
                                       ;;
                               *)
                                       echo "Syntax: alias name command"
                                       cmd=
                                       continue
                                       ;;
                       esac
                       set - $cmd
                       shift
                       cmd="$*"

                       : make sure there is always 1 blank line in file so
                       : unaliasing will always work -- ed normally refuses
                       : to write an empty file

                       echo "" >> $aliasfile
                       cat << ++++ >> $aliasfile
$cmd
++++
                       ed - $aliasfile << '++++'
                       g/alias[        ]/s///
                       g/^['"]\(.*\)['"]$/s//\1/
                       g/^/s//alias    /
                       w
++++
                       sort -u -o $aliasfile $aliasfile
                       doalias=yes
                       cmd="alias $cmd"
                       run=no
                       ;;
               unalias[\ \     ]*)
                       set - $cmd
                       case "$#" in
                               2)
                                       cmd=$2
                                       ;;
                               *)
                                       echo "Syntax: unalias alias_name"
                                       continue
                                       ;;
                       esac
                       ed - $aliasfile << ++++
                       /^$cmd[         ]/d
                       w
++++
                       case "`set - \`wc -l $aliasfile\`;echo $1``" in
                               1)
                                       : just removed last alias

                                       doalias=no
                                       ;;
                       esac
                       run=no
                       ;;
               *)
                       case "$doalias" in
                               yes)
                                       set - $cmd
                                       tmp="`grep \"^$1 \" $aliasfile`"
                                       case "$tmp" in
                                               $1[\ \  ]*)
                                                       shift
                                                       cmd=$*
                                                       set - $tmp
                                                       shift
                                                       tmp=$*
                                                       case "$tmp" in
                                                               *\$*)
                                                                       : uses positional variables

                                                                       cmd="set - $cmd ; $tmp"
                                                                       getcmd=no
                                                                       continue
                                                                       ;;
                                                               *)
                                                                       cmd="$tmp $cmd"
                                                                       getcmd=no
                                                                       continue
                                                                       ;;
                                                       esac
                                                       ;;
                                               *)
                                                       echo "$cmd" > /tmp/bcsh$$
                                                       ;;
                                       esac
                                       ;;
                               no)
                                       echo "$cmd" > /tmp/bcsh$$
                                       ;;
                       esac
                       ;;
       esac
       case "$cmd" in
               *+~+p)
                       cmd="`expr \"$cmd\" : '\(.*\)+~+p'`"
                       echoit=yes
                       run=no
                       ;;
       esac
       case "$cmd" in
               "")
                       continue
                       ;;
               *)
                       case "$exclaim" in
                               yes)
                                       cmd="`echo \"$cmd\" | sed -e 's@REALEXCLAMATIONMARK@!@g'`"
                                       echo "$cmd" > /tmp/bcsh$$
                                       ;;
                       esac
                       case "$echoit" in
                               yes)
                                       echo $cmd
                                       ;;
                       esac
                       case "$run" in
                               yes)
                                       case "${noclobber+yes}" in
                                               yes)
                                                       case "$cmd" in
                                                               *\>![\ \        ]*)
                                                                       ed - /tmp/bcsh$$ << ++++
                                                                       g/>!/s//>/
                                                                       w
++++
                                                                       ;;
                                                               *\>\>*)
                                                                       ;;
                                                               *\>*)
                                                                       outfile="`expr \"$cmd\" : '.*>\(.*\)'`"
                                                                       case "$outfile" in
                                                                               \&*)
                                                                                       ;;
                                                                               *)
                                                                                       set - $outfile
                                                                                       outfile="$1"
                                                                                       if test -s "$outfile"
                                                                                               then
                                                                                               case "${iclobber+yes}" in
                                                                                                       yes)
                                                                                                               echo $n "Overwrite ${outfile}? $c"
                                                                                                               read answer
                                                                                                               case "$answer" in
                                                                                                                       y*)
                                                                                                                               ;;
                                                                                                                       *)
                                                                                                                               echo ':' > /tmp/bcsh$$
                                                                                                                               ;;
                                                                                                               esac
                                                                                                               ;;
                                                                                                       *)
                                                                                                               echo "${outfile}: file exists"
                                                                                                               echo ':' > /tmp/bcsh$$
                                                                                                               ;;
                                                                                               esac
                                                                                       fi
                                                                                       ;;
                                                                       esac
                                                                       ;;
                                                       esac
                                                       ;;
                                               *)
                                                       case "$cmd" in
                                                               *\>![\ \        ]*)
                                                                       ed - /tmp/bcsh$$ << ++++
                                                                       g/>!/s//>/g
                                                                       w
++++
                                                                       ;;
                                                       esac
                                                       ;;
                                       esac
                                       (trap 'exit 1' 2 3;sh /tmp/bcsh$$)
                                       ;;
                       esac
                       case "$cmd" in
                               $lastcmd)
                                       ;;
                               *)
                                       case "$exclaim" in
                                               yes)
                                                       cmd="`echo \"$cmd\" | sed -e 's@!@\\\\!@g'`"
                                                       ;;
                                       esac
                                       cat << ++++ >> $histfile
$cmd
++++
                                       lastcmd=$cmd

                                       case "$inc_cmdno" in
                                               yes)
                                                       cmdno="`expr \"$cmdno\" + 1`"
                                                       ;;
                                       esac
                                       ;;
                       esac
                       ;;
       esac

       : The next commented-out line sets the prompt to include the command
       : number -- you should only un-comment this if it is the ONLY thing
       : you ever want as your prompt, because it will override attempts
       : to set PS1 from the command level.  If you want the command number
       : in your prompt without sacrificing the ability to change the prompt
       : later, replace the default setting for PS1 before the beginning of
       : the main loop with the following:  PS1='echo -n "${cmdno}% "'
       : Doing it this way is, however, slower than the simple version below.

       PS1="${cmdno}% "

       getcmd=yes
       echoit=no
       exclaim=no
done
exit 0
--------------- cut here --------------------- cut here -------------------
--

Christine Robertson  {linus, ihnp4, decvax}!utzoo!globetek!chris

Money may not buy happiness, but misery in luxury has its compensations...