# 1-Feb-86 09:37:35-MST,30567;000000000001 # Return-Path: # 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 # 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