diff options
Diffstat (limited to 'examples/scripts/bcsh.sh')
-rwxr-xr-x | examples/scripts/bcsh.sh | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/examples/scripts/bcsh.sh b/examples/scripts/bcsh.sh new file mode 100755 index 0000000..b810cab --- /dev/null +++ b/examples/scripts/bcsh.sh @@ -0,0 +1,1254 @@ +# 1-Feb-86 09:37:35-MST,30567;000000000001 +# Return-Path: <unix-sources-request@BRL.ARPA> +# 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 <chris@globetek.uucp> +# Newsgroups: net.sources +# Subject: Improved Bcsh (Bourne Shell Cshell-Emulator) +# Message-ID: <219@globetek.UUCP> +# Date: 30 Jan 86 17:34:26 GMT +# To: unix-sources@BRL-TGR.ARPA +# +# 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 +# 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="`sed -n '$p' $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 + "") + ;; + *) + if [ -f $MAIL ]; then + mailsize=`set - \`wc -c $MAIL\`;echo $1` + else + mailsize=0 + fi + ;; +esac + +trap ':' 2 +trap exit 3 +trap "tail -n $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 || cmd="exit" + ;; + *) ;; + esac + + case "$MAIL" in + "") + ;; + *) + : check for mail every 10 commands + case "$mailcheck" in + 1111111111) + mailcheck= + if [ -f $MAIL ]; then + newsize="`set - \`wc -c $MAIL\`;echo $1`" + else + newsize=0 + fi + 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" && test "$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" && test "$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 -n $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 | sed -n '$p'`" + ;; + *) + # 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 | sed -n '$p'`" + ;; + 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`" + # counter=$[ $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 -n $savehist $histfile>/tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + ;; + login[\ \ ]*|newgrp[\ \ ]*) + tail -n $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 + $SHELL $logoutfile + fi + tail -n $savehist $histfile > /tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + exit 0 + ;; + h|history) + grep -n . $histfile | tail -n $history | sed -e 's@:@ @' | $PAGER + continue + ;; + h[\ \ ]\|*|h[\ \ ]\>*|h\|*|h\>*) + cmd="`echo \"$cmd\" | sed -e \"s@h@grep -n . $histfile | tail -n $history | sed -e 's@:@ @'@\"`" + getcmd=no + continue + ;; + history[\ \ ]*\|*|history[\ \ ]*\>*) + cmd="`echo \"$cmd\" | sed -e \"s@history@grep -n . $histfile | tail -n $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 ($SHELL /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[\ \ ]) + if [ -f $aliasfile ]; then + $PAGER $aliasfile + fi + 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; $BASH /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`" + # cmdno=$[$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 + +# Christine Robertson {linus, ihnp4, decvax}!utzoo!globetek!chris |