home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / unix_c / utils / bcsh.sh < prev    next >
Encoding:
Text File  |  1989-03-21  |  28.7 KB  |  1,261 lines

  1.  1-Feb-86 09:37:35-MST,30567;000000000001
  2. Return-Path: <unix-sources-request@BRL.ARPA>
  3. Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sat 1 Feb 86 09:36:16-MST
  4. Received: from usenet by TGR.BRL.ARPA id a002623; 1 Feb 86 9:33 EST
  5. From: chris <chris@globetek.uucp>
  6. Newsgroups: net.sources
  7. Subject: Improved Bcsh (Bourne Shell Cshell-Emulator)
  8. Message-ID: <219@globetek.UUCP>
  9. Date: 30 Jan 86 17:34:26 GMT
  10. To:       unix-sources@BRL-TGR.ARPA
  11.  
  12. This is a new, improved version of my Bourne shell cshell-emulator.
  13. The code has been cleaned up quite a bit, and a couple of new features
  14. added (now supports 'noclobber' and 'iclobber' variables).  A bug with
  15. 'eval' that caused "illegal I/O" error messages on vanilla V7 shells has
  16. also been fixed.
  17.  
  18. I have posted the program in its entirety because a context diff of the
  19. old and new versions was longer than the new version...
  20.  
  21. --Chris
  22. --------------- cut here --------------------- cut here -------------------
  23.  
  24. :    Bcsh -- A Simple Cshell-Like Command Pre-Processor For The Bourne Shell
  25. :
  26. :    "Copyright (c) Chris Robertson, December 1985"
  27. :
  28. :    This software may be used for any purpose provided the original
  29. :    copyright notice and this notice are affixed thereto.  No warranties of
  30. :    any kind whatsoever are provided with this software, and it is hereby
  31. :    understood that the author is not liable for any damagages arising
  32. :    from the use of this software.
  33. :
  34. :    Features Which the Cshell Does Not Have:
  35. :    ----------------------------------------
  36. :
  37. :    +  command history persists across bcsh sessions
  38. :     +  global last-command editing via 'g^string1^string2^' syntax
  39. :    +  edit any command via $EDITOR or $VISUAL editors
  40. :    +  history file name, .bcshrc file name, alias file name, and number
  41. :       of commands saved on termination can be set by environment variables
  42. :    +  prompt may evaluate commands, such as `pwd`, `date`, etc.
  43. :    +  the whole text of interactive 'for' and 'while' loops and 'if'
  44. :       statements goes into the history list and may be re-run or edited
  45. :    +  multiple copies of commands and requests to see command history
  46. :       are not added to the history list
  47. :    +  the history mechanism actually stores all commands entered in a
  48. :       current session, not just $history of them.  This means that you
  49. :       can increase $history on the fly and at once have a larger history.
  50. :
  51. :
  52. :    Synonyms:
  53. :    ---------
  54. :
  55. :    logout, exit, bye    write out history file and exit
  56. :    h, history        show current history list
  57. :    
  58. :    
  59. :    Aliases:
  60. :    --------
  61. :
  62. :    alias NAME CMND        create an alias called NAME to run CMND
  63. :    unalias NAME        remove the alias NAME
  64. :
  65. :    There are no 'current-session only' aliases -- all alias and unalias
  66. :    commands are permanent, and stored in the $aliasfile.
  67. :
  68. :    If an alias contains positional variables -- $1, $2, $*, etc. -- any
  69. :    arguments following the alias name are considered to be values for
  70. :    those variables, and the alias is turned into a command of the form
  71. :    'set - arguments;alias'.  Otherwise, a simple substitution is performed
  72. :    for the alias and the rest of the command preserved.  The cshell
  73. :    convention of using '\!:n' in an alias to get bits of the current
  74. :    command is mercifully abandoned.
  75. :
  76. :    Quotes are not necessary around the commands comprising an alias;
  77. :    in fact, any enclosing quotes are stripped when the alias is added
  78. :    to the file.
  79. :
  80. :    A couple of typical aliases might be:
  81. :
  82. :        goto    cd $1;pwd
  83. :        l    ls -F
  84. :
  85. :    Note that aliasing something to "commands;logout" will not work -- if
  86. :    you want something to happen routinely on logout put it in the file
  87. :    specified by $logoutfile, default = $HOME/.blogout.
  88. :
  89. :
  90. :    Command Substitutions:
  91. :    ----------------------
  92. :
  93. :    !!            substitute last command from history list
  94. :    !!:N            substitute Nth element of last command from
  95. :                history list -- 0 = command name, 1 = 1st arg
  96. :     !!:$            substitute last element of last command from
  97. :                history list
  98. :     !!:*            substitute all arguments to last command
  99. :                from history list
  100. :    !NUMBER            substitute command NUMBER from the history list
  101. :    !NUMBER:N        as above, but substitute Nth element, where
  102. :                0 = command name, 1 = 1st arg, etc.
  103. :     !NUMBER:$        as above, but substitute last element
  104. :     !NUMBER:*        as above, but substitute all arguments
  105. :    !-NUMBER        substitute the command NUMBER lines from the
  106. :                end of the history list; 1 = last command
  107. :    !-NUMBER:N        as above, but substitute Nth element, where
  108. :                0 = command name, 1 = 1st arg, etc.
  109. :     !-NUMBER:$        as above, but substitute last element
  110. :     !-NUMBER:*        as above, but substitute all arguments
  111. :    !?STRING        substitute most-recent command from history list
  112. :                containing STRING -- STRING must be enclosed in
  113. :                braces if followed by any other characters
  114. :    !?STRING:N        as above, but substitute Nth element, where
  115. :                0 = command name, 1 = 1st arg, etc.
  116. :     !?STRING:$        as above, but substitute last element    
  117. :     !?STRING:*        as above, but substitute all arguments
  118. :
  119. :
  120. :    Command Editing:
  121. :    ----------------
  122. :
  123. :    CMND~e            edit CMND using $EDITOR, where CMND may be found
  124. :                using a history substitution
  125. :    CMND~v            edit CMND using $VISUAL, where CMND may be found
  126. :                using a history substitution
  127. : "    ^string1^string2^    substitute string2 for string1 in last command"
  128. :                command and run it
  129. : "    g^string1^string2^    globally substitute string2 for string1 in  "
  130. :                last command and run it
  131. :     !NUMBER:s/string1/string2/
  132. :                substitute string2 for string1 in
  133. :                command NUMBER and run it
  134. :     !NUMBER:gs/string1/string2/
  135. :                globally substitute string2 for string1 in
  136. :                command NUMBER and run it
  137. :     !?STRING:s/string1/string2/
  138. :                substitute string2 for string1 in last command
  139. :                containing STRING and run it
  140. :     !?STRING:gs/string1/string2/
  141. :                globally substitute string2 for string1 in last
  142. :                command containing STRING and run it
  143. :    
  144. :    Any command which ends in the string ":p" is treated as a normal
  145. :    command until all substitutions have been completed.  The trailing
  146. :    ":p" is then stripped, and the command is simply echoed and added to
  147. :    the history list instead of being executed.
  148. :
  149. :    None of the other colon extensions of the cshell are supported.
  150. :
  151. :
  152. :    Shell Environment Variables:
  153. :    ----------------------------
  154. :
  155. :    EDITOR        editor used by ~e command, default = "ed"
  156. :    VISUAL        editor used by ~v command, default = "vi"
  157. :    MAIL        your system mailbox
  158. :    PAGER        paging program used by history command, default = "more"
  159. :    PS1        primary prompt
  160. :    PS2        secondary prompt
  161. :    history        number of commands in history list, default = 22
  162. :    histfile    file history list is saved in, default = $HOME/.bhistory
  163. :    savehist    number of commands remembered from last bcsh session
  164. :    aliasfile    file of aliased commands, default = $HOME/.baliases
  165. :    logoutfile    file of commands to be executed before termination
  166. :    inc_cmdno    yes/no -- keep track of command numbers or not
  167. :    noclobber    if set, existing files are not overwritten by '>'
  168. :    iclobber    if both noclobber and iclobber are set, the user is
  169. :            prompted for confirmation before existing files are
  170. :            overwritten by '>'
  171. :
  172. :    Note:    if you are setting either noclobber or iclobber mid-session,
  173. :        set them to 'yes'
  174. :
  175. :
  176. :    Regular Shell Variables:
  177. :    ------------------------
  178. :
  179. :    Shell variables may be set via Bourne or cshell syntax, e.g., both
  180. :    "set foo=bar" and "foo=bar" set a variable called "foo" with the value
  181. :    "bar".  However, all variables are automatically set as environment
  182. :    variables, so there is no need to export them.  Conversely, there
  183. :    are NO local variables.  Sorry, folks.
  184. :
  185. :    A cshell-style "setenv" command is turned into a regular "set" command.
  186. :
  187. :
  188. :    The Prompt:
  189. :    ----------
  190. :
  191. :    You may, if you wish, have a command executed in your prompt.  If
  192. :    the variable PS1 contains a dollar sign or a backquote, it is
  193. :    evaluated and the result used as the prompt, provided the evaluation
  194. :    did not produce a "not found" error message.  The two special cases
  195. :    of PS1 consisting solely of "$" or "$ " are handled correctly.  For
  196. :    example, to have the prompt contain the current directory followed
  197. :    by a space, enter:
  198. :
  199. :        PS1=\'echo "`pwd` "\'
  200. :
  201. :    You need the backslashed single quotes to prevent the command being
  202. :    evaluated by the variable-setting mechanism and the shell before it
  203. :    is assigned to PS1.
  204. :
  205. :    To include the command number in your prompt, enter the command:
  206. :
  207. :        PS1=\'echo "$cmdno "\'
  208. :
  209. :
  210. :    Shell Control-Flow Syntax:
  211. :    --------------------------
  212. :
  213. :    'While', 'for', 'case', and 'if' commands entered in Bourne shell
  214. :    syntax are executed as normal.
  215. :
  216. :    A valiant attempt is made to convert 'foreach' loops into 'for' loops,
  217. :    cshell-syntax 'while' loops into Bourne shell syntax, and 'switch'
  218. :    statements into 'case' statements.  I cannot guarantee to always get it
  219. :    right.  If you forget the 'do' in a 'while' or 'for' loop, or finish
  220. :    them with 'end' instead of 'done', this will be corrected.
  221. :
  222. :    Note that cshell-to-Bourne control flow conversions do not take place
  223. :    if control is nested -- e.g., a 'foreach' inside a 'while' will fail.
  224. :
  225. :    The simple-case cshell "if (condition) command" is turned into Bourne
  226. :    syntax.  Other 'if' statements are left alone apart from making the
  227. :    'then' a separate statement, because constructing a valid interactive
  228. :    cshell 'if' statement is essentially an exercise in frustration anyway.
  229. :    The cshell and Bourne shell have sufficiently different ideas about
  230. :    conditions that if is probably best to resign yourself to learning
  231. :    the Bourne shell conventions.
  232. :
  233. :    Note that since most of the testing built-ins of the cshell are
  234. :    not available in the Bourne shell, a complex condition in a 'while'
  235. :    loop or an 'if' statement will probably fail.
  236. :    
  237. :
  238. :    Bugs, Caveats, etc.:
  239. :    --------------------
  240. :
  241. :    This is not a super-speedy program.  Be patient, especially on startup.
  242. :
  243. :    To the best of my knowledge this program should work on ANY Bourne
  244. :    shell -- note that if your shell does not understand 'echo -n' you
  245. :    will have to re-set the values of '$n' and '$c'.
  246. :
  247. :    This program may run out of stack space on a 16-bit machine where
  248. :    /bin/sh is not split-space.
  249. :
  250. :    Mail checking is done every 10 commands if $MAIL is set in your
  251. :    environment.  For anything fancier, you will have to hack the code.
  252. :
  253. :    Because commands are stuffed in a file before sh is invoked on them,
  254. :    error messages from failed commands are ugly.
  255. :
  256. :    Failed history substitutions either give nothing at all, or a
  257. :    "not found" style of error message.
  258. :
  259. :    A command history is kept whether you want it or not.  This may be
  260. :    perceived as a bug or a feature, depending on which side of bed you
  261. :    got out on.
  262. :
  263. :    If you want a real backslash in a command, you will have to type two
  264. :     of them  because the shell swallows the first backslash in the initial
  265. :     command pickup.  This means that to include a non-history '!' in a
  266. :    command you need '\\!' -- a real wart, especially for net mail,
  267. :    but unavoidable.
  268. :
  269. :    Commands containing an '@' will break all sorts of things.
  270. :
  271. :    Very complex history substitutions may fail.
  272. :
  273. :    File names containing numbers may break numeric history sustitutions.
  274. :
  275. :    Commands containing bizzare sequences of characters may conflict
  276. :    with internal kludges.
  277. :
  278. :    Aliasing something to "commands;logout" will not work -- if you
  279. :    want something to happen routinely on logout, put it in the file
  280. :    specified by $logoutfile, default = $HOME/.blogout.
  281. :
  282. :    Please send all bug reports to ihnp4!utzoo!globetek!chris.
  283. :    Flames will be posted to net.general with 'Reply-to' set to your
  284. : '    path...  :-)                            '
  285. :
  286. :
  287. :
  288. :        ************* VERY IMPORTANT NOTICE *************
  289. :
  290. : If your shell supports # comments, then REPLACE all the colon 'comments'
  291. : with # comments.  If it does not, then REMOVE all the 'comment' lines from the
  292. : working copy of the file, as it will run MUCH faster -- the shell evaluates
  293. : lines starting with a colon but does not actually execute them, so you will
  294. : save the read-and-evaluate time by removing them.
  295.  
  296.  
  297.  
  298. case "`echo -n foo`" in
  299.     -n*)
  300.         n=
  301.         c="\c"
  302.         ;;
  303.     foo)
  304.         n=-n
  305.         c=
  306.         ;;
  307.     *)
  308.         echo "Your 'echo' command is broken."
  309.         exit 1
  310.         ;;
  311. esac
  312. history=${history-22}
  313. savehist=${savehist-22}
  314. histfile=${histfile-$HOME/.bhistory}
  315. logoutfile=${logoutfile-$HOME/.blogout}
  316. EDITOR=${EDITOR-ed}
  317. VISUAL=${VISUAL-vi}
  318. PAGER=${PAGER-more}
  319.  
  320. aliasfile=${aliasfile-$HOME/.baliases}
  321.  
  322. : the alias file may contain 1 blank line, so a test -s will not work
  323.  
  324. case "`cat $aliasfile 2> /dev/null`" in
  325.     "")
  326.         doalias=no
  327.         ;;
  328.     *)
  329.         doalias=yes
  330.         ;;
  331. esac
  332.  
  333. if test -s "${sourcefile-$HOME/.bcshrc}"
  334.     then
  335.     . ${sourcefile-$HOME/.bcshrc}
  336. fi
  337.  
  338. if test -s "$histfile"
  339.     then
  340.     cmdno="`set - \`wc -l $histfile\`;echo $1`"
  341.     cmdno="`expr \"$cmdno\" + 1`"
  342.     lastcmd="`tail -1 $histfile`"
  343.     copy=false
  344.     ohist=$histfile
  345.     while test ! -w "$histfile"
  346.         do
  347.         echo "Cannot write to history file '$histfile'."
  348.         echo $n "Please enter a new history filename: $c"
  349.         read histfile
  350.         copy=true
  351.     done
  352.     if $copy
  353.         then
  354.         cp $ohist $histfile
  355.     fi
  356. else
  357.     cat /dev/null > $histfile
  358.     cmdno=1
  359.     lastcmd=
  360. fi
  361.  
  362. : keep track of command number as the default
  363.  
  364. inc_cmdno=${inc_cmdo-yes}
  365.  
  366. : default prompts -- PS1 and PS2 may be SET but EMPTY, so '${PS1-% }' syntax
  367. : is not used here
  368.  
  369. case "$PS1" in
  370.     "")                    
  371.         PS1="% "
  372.         ;;                
  373. esac
  374. case "$PS2" in
  375.     "")                    
  376.         PS2="> "
  377.         ;;                
  378. esac
  379.  
  380. export histfile savehist history aliasfile EDITOR VISUAL PAGER cmdno PS1 PS2
  381.  
  382. case "$MAIL" in
  383.     "")
  384.         ;;
  385.     *)
  386.         mailsize=`set - \`wc -c $MAIL\`;echo $1`
  387.         ;;
  388. esac
  389.  
  390. trap ':' 2
  391. trap exit 3
  392. trap "tail -$savehist $histfile>/tmp/hist$$;uniq /tmp/hist$$ > $histfile;\
  393. rm -f /tmp/*$$;exit 0" 15
  394.  
  395. getcmd=yes
  396. mailcheck=
  397. exclaim=
  398. echoit=
  399. mailprompt=
  400.  
  401. while :
  402.     do
  403.     run=yes
  404.     case "$mailprompt" in
  405.         "")
  406.             ;;
  407.         *)
  408.             echo "$mailprompt"
  409.             ;;
  410.     esac
  411.     case "$getcmd" in
  412.         yes)
  413.             : guess if the prompt should be evaluated or not
  414.  
  415.             case "$PS1" in
  416.                 \$|\$\ )
  417.                     echo $n "$PS1$c"
  418.                     ;;
  419.                 *\`*|*\$*)
  420.                     tmp="`(eval $PS1) 2>&1`"
  421.                     case "$tmp" in
  422.                         *not\ found)            
  423.                             echo $n "$PS1$c"
  424.                             ;;            
  425.                         *)                
  426.                             echo $n "$tmp$c"
  427.                             ;;            
  428.                     esac
  429.                     ;;
  430.                 *)
  431.                     echo $n "$PS1$c"
  432.                     ;;
  433.             esac
  434.  
  435.             read cmd
  436.             ;;
  437.     esac
  438.     case "$MAIL" in
  439.         "")
  440.             ;;
  441.         *)
  442.             : check for mail every 10 commands
  443.  
  444.             case "$mailcheck" in
  445.                 1111111111)
  446.                     mailcheck=
  447.                     newsize="`set - \`wc -c $MAIL\`;echo $1`"
  448.                     if test "$newsize" -gt "$mailsize"
  449.                         then
  450.                         mailprompt="You have new mail"
  451.                     else
  452.                         mailprompt=
  453.                     fi
  454.                     mailsize=$newsize
  455.                     ;;
  456.                 *)
  457.                     mailcheck=1$mailcheck
  458.                     ;;
  459.             esac
  460.             ;;
  461.     esac
  462.     hist=no
  463.     case "$cmd" in
  464.         "")
  465.             continue
  466.             ;;
  467.         sh)
  468.             sh
  469.             run=no
  470.             ;;
  471.         !!)
  472.             cmd=$lastcmd
  473.             echoit=yes
  474.             getcmd=no
  475.             continue
  476.             ;;
  477.         *:p)
  478.             cmd="`expr \"$cmd\" : '\(.*\):p'` +~+p"
  479.             getcmd=no
  480.             continue
  481.             ;;
  482.         foreach[\ \    ]*)
  483.             while test "$line" != "end"
  484.                 do
  485.                 echo $n "$PS2$c"
  486.                 read line
  487.                 cmd="${cmd};$line"
  488.             done
  489.             echo "$cmd" > /tmp/bcsh$$
  490.             ed - /tmp/bcsh$$ << ++++
  491.             s/end/done/
  492.             s/foreach[     ]\(.*\)(/for \1 in /
  493.             s/)//
  494.             s/;/;do /
  495.             w
  496. ++++
  497.             ;;
  498.         for[\ \    ]*|while[\ \    ]*)
  499.  
  500.             : try to catch the most common cshell-to-Bourne-shell mistakes
  501.  
  502.             echo $n "$PS2$c"
  503.             read line
  504.             case "$line" in
  505.                 *do)
  506.                     line="do :"
  507.                     ;;
  508.                 *do*)
  509.                     ;;
  510.                 *)
  511.                     line="do $line"
  512.                     ;;
  513.             esac
  514.             cmd="${cmd};$line"
  515.             while test "$line" != "done" -a "$line" != "end"
  516.                 do
  517.                 echo $n "$PS2$c"
  518.                 read line
  519.                 case "$line" in
  520.                     end)
  521.                         line=done
  522.                         ;;
  523.                 esac
  524.                 cmd="${cmd};$line"
  525.             done
  526.             echo "$cmd" > /tmp/bcsh$$
  527.             ;;
  528.         if[\ \    ]*)
  529.             while test "$line" != "fi" -a "$line" != "endif"
  530.                 do
  531.                 echo $n "$PS2$c"
  532.                 read line
  533.                 case "$line" in
  534.                     *[a-z]*then)
  535.                         line="`expr \"$line\" : '\(.*\)then'`;then"
  536.                         ;;
  537.                     endif)
  538.                         line=fi
  539.                         ;;
  540.                 esac
  541.                 cmd="${cmd};$line"
  542.             done
  543.             echo "$cmd" > /tmp/bcsh$$
  544.             case "`grep then /tmp/bcsh$$`" in
  545.                 "")
  546.                     : fix 'if foo bar' cases
  547.  
  548.                     ed - /tmp/bcsh$$ << ++++
  549.                     s/)/);then/
  550.                     s/.*/;fi/
  551.                     w
  552. ++++
  553.                     ;;
  554.             esac
  555.             ;;
  556.         case[\ \    ]*)
  557.             while test "$line" != "esac"
  558.                 do
  559.                 echo $n "$PS2$c"
  560.                 read line
  561.                 cmd="${cmd}@$line"
  562.             done
  563.             cmd="`echo \"$cmd\" | tr '@' ' '`"
  564.             echo "$cmd" > /tmp/bcsh$$
  565.             ;;
  566.         switch[\ \    ]*)
  567.             while test "$line" != "endsw"
  568.                 do
  569.                 echo $n "$PS2$c"
  570.                 read line
  571.                 cmd="${cmd}@$line"
  572.             done
  573.             echo "$cmd" > /tmp/bcsh$$
  574.             ed - /tmp/bcsh$$ << '++++'
  575.             1,$s/@/\
  576. /g
  577.             g/switch.*(/s//case "/
  578.             s/)/" in/
  579.             1,$s/case[     ]\(.*\):$/;;\
  580.     \1)/
  581.             2d
  582.             1,$s/endsw/;;\
  583. esac/
  584.             g/breaksw/s///
  585.             1,$s/default.*/;;\
  586.     *)/
  587.             w
  588. ++++
  589.             cmd="`cat /tmp/bcsh$$`"
  590.             ;;
  591.         *!*)
  592.             hist=yes
  593.             ;;
  594.     esac
  595.     case "$hist" in
  596.         yes)
  597.  
  598.             : deal with genuine exclamation marks, go back and parse again
  599.  
  600.             case "$cmd" in
  601.                 *\>![\ \    ]*|*\\!*)
  602.                     cmd="`echo \"$cmd\" | sed -e 's@\\!@REALEXCLAMATIONMARK@g'`"
  603.                     exclaim=yes
  604.                     getcmd=no
  605.                     continue
  606.                     ;;
  607.             esac
  608.  
  609.             : break command into elements, parse each one
  610.  
  611.             tmp=
  612.             for i in $cmd
  613.                 do
  614.  
  615.                 : find element with !, peel off stuff up to !
  616.  
  617.                 case "$i" in
  618.                     !)
  619.  
  620.                         : most likely a typo for !!, so fix it
  621.  
  622.                         front=
  623.                         $i=!!
  624.                         ;;
  625.                     !!*)
  626.                         front=
  627.                         i="`expr \"$i\" : '.*\(!!.*\)'`"
  628.                         ;;
  629.                     *!!*)
  630.                         front="`expr \"$i\" : '\(.*\)!!.*'`"
  631.                         i="`expr \"$i\" : '.*\(!!.*\)'`"
  632.                         ;;
  633.                     !*)
  634.                         front=
  635.                         i="`expr \"$i\" : '.*!\(.*\)'`"
  636.                         ;;
  637.                     *)
  638.                         tmp="$tmp$i "
  639.                         continue
  640.                         ;;
  641.                 esac
  642.                 case "$i" in
  643.                     !!*)
  644.  
  645.                         : want last command
  646.  
  647.                         rest="`expr \"$i\" : '!!\(.*\)'`"
  648.                         i=$lastcmd
  649.                         ;;
  650.                     -*)
  651.                         
  652.                         : we want to search back through the history list
  653.  
  654.                         case "$i" in
  655.                             -)
  656.                                 rest="`expr \"$i\" : '-\(.*\)'`"
  657.                                 i=$lastcmd
  658.                                 ;;
  659.                             -[0-9]*)
  660.                                 wanted="`expr \"$i\" : '-\([0-9][0-9]*\).*'`"
  661.                                 rest="`expr \"$i\" : '-[0-9][0-9]*\(.*\)'`"
  662.                                 i="`tail -$wanted $histfile | sed -e "1q"`"
  663.                                 ;;
  664.                         esac
  665.                         ;;
  666.                     [0-9]*)
  667.  
  668.                         : find which number command is wanted
  669.  
  670.                         wanted="`expr \"$i\" : '\([0-9][0-9]*\).*'`"
  671.                         rest="`expr \"$i\" : '[0-9][0-9]*\(.*\)'`"
  672.                         i="`grep -n . $histfile | grep \"^$wanted\"`"
  673.                         i="`expr \"$i\" : \"${wanted}.\(.*\)\"`"
  674.                         ;;
  675.                     \?*)
  676.  
  677.                         : find which 'command-contains' match is wanted
  678.  
  679.                         case "$i" in
  680.                             \?{*}*)
  681.                                 wanted="`expr \"$i\" : '?{\(.*\)}.*'`"
  682.                                 rest="`expr \"$i\" : '?.*}\(.*\)'`"
  683.                                 ;;
  684.                             \?*:*)
  685.                                 wanted="`expr \"$i\" : '?\(.*\):.*'`"
  686.                                 rest="`expr \"$i\" : '?.*\(:.*\)'`"
  687.                                 ;;
  688.                             \?*)
  689.                                 wanted="`expr \"$i\" : '?\(.*\)'`"
  690.                                 rest=
  691.                                 ;;
  692.                         esac
  693.                         i="`grep \"$wanted\" $histfile | tail -1`"
  694.                         ;;
  695.                     *)
  696.  
  697.                         : find which 'start-of-command' match is wanted
  698.  
  699.                         case "$i" in
  700.                             {*}*)
  701.                                 wanted="`expr \"$i\" : '{\(.*\)}.*'`"
  702.                                 rest="`expr \"$i\" : '.*}\(.*\)'`"
  703.                                 ;;
  704.                             *:*)
  705.                                 wanted="`expr \"$i\" : '\(.*\):.*'`"
  706.                                 rest="`expr \"$i\" : '.*\(:.*\)'`"
  707.                                 ;;
  708.                             *)
  709.                                 wanted="$i"
  710.                                 rest=
  711.                                 ;;
  712.                         esac
  713.                         i="`grep \"^$wanted\" $histfile | tail -1`"
  714.                         ;;
  715.                 esac
  716.  
  717.                 : see if we actually found anything to substitute
  718.  
  719.                 case "$i" in
  720.                     "")
  721.                         badsub="Event not found"
  722.                         break
  723.                         ;;
  724.                     *)
  725.                         badsub=no
  726.                         ;;
  727.                 esac
  728.  
  729.                 case "$rest" in
  730.                     "")
  731.                         tmp="$front$tmp$i "
  732.                         continue
  733.                         ;;
  734.                     :[0-9]*)
  735.  
  736.                         : find which element of $i is wanted
  737.  
  738.                         number="`expr \"$rest\" : ':\([0-9][0-9]*\).*'`"
  739.                         rest="`expr \"$rest\" : ':[0-9][0-9]*\(.*\)'`"
  740.  
  741.                         : count through $i till we get to the right element
  742.  
  743.                         counter=0
  744.                         for element in $i
  745.                             do
  746.                             case "$counter" in
  747.                                 $number)
  748.                                     break
  749.                                     ;;
  750.                                 *)
  751.                                     counter="`expr \"$counter\" + 1`"
  752.                                     ;;
  753.                             esac
  754.                         done
  755.                         case "$counter" in
  756.                             $number)
  757.                                 badsub=no
  758.                                 ;;
  759.                             *)
  760.                                 badsub="Bad command element"
  761.                                 break
  762.                                 ;;
  763.                         esac
  764.                         tmp="$tmp$front$element$rest "
  765.                         continue
  766.                         ;;
  767.                     :\$*)
  768.  
  769.                         : spin through $i till we hit the last element
  770.  
  771.                         rest="`expr \"$rest\" : ':\$\(.*\)'`"
  772.                         for element in $i
  773.                             do
  774.                             :
  775.                         done
  776.                         tmp="$tmp$front$element$rest "
  777.                         continue
  778.                         ;;
  779.                     :\**)
  780.  
  781.                         : we want all elements except the command itself
  782.  
  783.                         rest="`expr \"$rest\" : ':\*\(.*\)'`"
  784.                         save=$i
  785.                         set - $i
  786.                         shift
  787.                         case "$*" in
  788.                             "")
  789.                                 badsub="No arguments to command '$save'"
  790.                                 break
  791.                                 ;;
  792.                             *)
  793.                                 badsub=no
  794.                                 ;;
  795.                         esac
  796.                         tmp="$tmp$front$*$rest "
  797.                         continue
  798.                         ;;
  799.                     :s*|:gs*)
  800.  
  801.                         : we are doing a substitution -- put / on end if needed
  802.  
  803.                         case "$rest" in
  804.                             :s/*/*/*|:gs/*/*/*)
  805.                                 ;;
  806.                             :s/*/*|:gs/*/*)
  807.                                 rest="${rest}/"
  808.                                 ;;
  809.                         esac
  810.  
  811.                         : find what substitution is wanted
  812.  
  813.                         first="`expr \"$rest\" : ':*s\/\(.*\)\/.*\/.*'`"
  814.                         second="`expr \"$i\" : ':*s/.*/\(.*\)/.*'`"
  815.  
  816.                         : see if it is a global substitution
  817.  
  818.                         case "$rest" in
  819.                             :gs*)
  820.                                 global=g
  821.                                 ;;
  822.                             :s*)
  823.                                 global=
  824.                                 ;;
  825.                         esac
  826.                         rest="`expr \"$rest\" : '.*/.*/.*/\(.*\)'`"
  827.                         i="`echo \"$i\" | sed -e \"s@$first@$second@$global\"`"
  828.  
  829.                         : see if subsitution worked
  830.  
  831.                         case "$i" in
  832.                             "")
  833.                                 badsub="Substiution failed"
  834.                                 break
  835.                                 ;;
  836.                             *)
  837.                                 badsub=no
  838.                                 ;;
  839.                         esac
  840.                         tmp="$tmp$front$i$rest "
  841.                         continue
  842.                         ;;
  843.                     *)
  844.                         tmp="$tmp$front$i$rest "
  845.                         ;;
  846.                 esac
  847.             done
  848.             case "$badsub" in
  849.                 no)
  850.                     ;;
  851.                 *)
  852.                     echo "$badsub"
  853.                     badsub=no
  854.                     continue
  855.                     ;;
  856.             esac
  857.             cmd="$tmp"
  858.             echoit=yes
  859.             getcmd=no
  860.             continue
  861.             ;;
  862.         *)
  863.             run=yes
  864.             ;;
  865.     esac
  866.     case "$cmd" in
  867.         *\^*\^*\^*)
  868.  
  869.             : see if the substitution is global
  870.  
  871.             case "$cmd" in
  872.                 g*)
  873.                     global=g
  874.                     ;;
  875.                 *)
  876.                     global=
  877.                     ;;
  878.             esac
  879.  
  880.             : put a '^' on the end if necessary
  881.  
  882.             case "$cmd" in
  883.                 *\^)
  884.                     ;;
  885.                 *)
  886.                     cmd="${cmd}^"
  887.                     ;;
  888.             esac
  889.  
  890.             : find what substitution is wanted
  891.  
  892.             first="`expr \"$cmd\" : '*\^\(.*\)\^.*\^.*'`"
  893.             second="`expr \"$cmd\" : '*\^.*\^\(.*\)\^.*'`"
  894.             rest="`expr \"$cmd\" : '*\^.*\^.*\^\(.*\)'`"
  895.             cmd="`echo \"$lastcmd\" | sed -e \"s@$first@$second@$global\"`$rest"
  896.  
  897.             : see if the substitution worked
  898.  
  899.             case "$cmd" in
  900.                 "")
  901.                     echo "Substitution failed"
  902.                     continue
  903.                     ;;
  904.             esac
  905.             echoit=yes
  906.             getcmd=no
  907.             continue
  908.             ;;
  909.         *~e)
  910.             echo "$cmd" | sed -e "s@~e@@" > /tmp/bcsh$$
  911.             $EDITOR /tmp/bcsh$$
  912.             cmd="`cat /tmp/bcsh$$`"
  913.             getcmd=no
  914.             continue
  915.             ;;
  916.         *~v)
  917.             echo "$cmd" | sed -e "s@~v@@" > /tmp/bcsh$$
  918.             echo "$lastcmd" > /tmp/bcsh$$
  919.             $VISUAL /tmp/bcsh$$
  920.             cmd="`cat /tmp/bcsh$$`"
  921.             getcmd=no
  922.             continue
  923.             ;;
  924.         exec[\ \    ]*)
  925.             tail -$savehist $histfile>/tmp/hist$$
  926.             uniq /tmp/hist$$ > $histfile
  927.             rm -f /tmp/*$$
  928.             echo $cmd > /tmp/cmd$$
  929.             . /tmp/cmd$$
  930.             ;;
  931.         login[\ \    ]*|newgrp[\ \    ]*)
  932.             tail -$savehist $histfile>/tmp/hist$$
  933.             uniq /tmp/hist$$ > $histfile
  934.             rm -f /tmp/*$$
  935.             echo $cmd > /tmp/cmd$$
  936.             . /tmp/cmd$$
  937.             ;;
  938.         logout|exit|bye)
  939.             if test -s "$logoutfile"
  940.                 then
  941.                 sh $logoutfile
  942.             fi
  943.             tail -$savehist $histfile > /tmp/hist$$
  944.             uniq /tmp/hist$$ > $histfile
  945.             rm -f /tmp/*$$
  946.             exit 0
  947.             ;;
  948.         h|history)
  949.             grep -n . $histfile | tail -$history | sed -e 's@:@    @' | $PAGER
  950.             continue
  951.             ;;
  952.         h[\ \    ]\|*|h[\ \    ]\>*|h\|*|h\>*)
  953.             cmd="`echo \"$cmd\" | sed -e \"s@h@grep -n . $histfile | tail -$history | sed -e 's@:@    @'@\"`"
  954.             getcmd=no
  955.             continue
  956.             ;;
  957.         history[\ \    ]*\|*|history[\ \    ]*\>*)
  958.             cmd="`echo \"$cmd\" | sed -e \"s@history@grep -n . $histfile | tail -$history | sed -e 's@:@ @'@\"`"
  959.             getcmd=no
  960.             continue
  961.             ;;
  962.         source[\ \    ]*)
  963.             set - $cmd
  964.             shift
  965.             echo . $*  > /tmp/cmd$$
  966.             . /tmp/cmd$$
  967.             run=no
  968.             ;;
  969.         wait)
  970.             wait
  971.             run=no
  972.             ;;
  973.         .[\ \    ]*)
  974.             echo $cmd > /tmp/cmd$$
  975.             . /tmp/cmd$$
  976.             run=no
  977.             ;;
  978.         cd|cd[\ \    ]*)
  979.             
  980.             : check if it will work first, or else this shell will terminate
  981.             : if the cd dies.  If you have a built-in test, you might want
  982.             : to replace the try-it-and-see below with a couple of tests,
  983.             : but it is probably just as fast like this.
  984.  
  985.             echo $cmd > /tmp/cmd$$
  986.             if (sh /tmp/cmd$$)
  987.                 then
  988.                 . /tmp/cmd$$
  989.             fi
  990.             run=no
  991.             ;;
  992.         awk[\ \    ]*|dd[\ \    ]*|cc[\ \    ]*|make[\ \    ]*)
  993.             : these are the only commands I can think of whose syntax
  994.             : includes an equals sign.  Add others as you find them.
  995.  
  996.             echo "$cmd" > /tmp/bcsh$$
  997.             ;;
  998.         setenv*|*=*)
  999.  
  1000.             : handle setting shell variables, turning cshell syntax to Bourne
  1001.             : syntax -- note all variables must be exported or they will not
  1002.             : be usable in other commands
  1003.  
  1004.             echo "$cmd" > /tmp/cmd$$
  1005.             ed - /tmp/cmd$$ << ++++
  1006.             g/^setenv[     ]/s/[     ]/@/
  1007.             g/^setenv@/s/[     ]/=/
  1008.             g/^setenv@/s///
  1009.             g/^set/s///
  1010.             .t.
  1011.             \$s/=.*//
  1012.             s/^/export /
  1013.             w
  1014. ++++
  1015.             . /tmp/cmd$$
  1016.             rm -f /tmp/cmd$$
  1017.             run=no
  1018.             ;;
  1019.         unset[\ \    ]*|umask[\ \    ]*|export[\ \    ]*|set[\ \    ]*)
  1020.  
  1021.             : handle commands which twiddle current environment
  1022.  
  1023.             $cmd
  1024.             run=no
  1025.             ;;
  1026.         alias|alias[\ \    ])
  1027.             $PAGER $aliasfile
  1028.             lastcmd=$cmd
  1029.             run=no
  1030.             continue
  1031.             ;;
  1032.         alias[\ \    ]*)
  1033.             case "$cmd" in
  1034.                 alias[\ \    ]\|*|alias[\ \    ]\>*)
  1035.                     cmd="`echo \"$cmd\" | sed -e \"s@alias@cat $aliasfile@\"`"
  1036.                     getcmd=no
  1037.                     continue
  1038.                     ;;
  1039.                 alias[\ \    ]*[\ \    ]*)
  1040.                     ;;
  1041.                 *)
  1042.                     echo "Syntax: alias name command"
  1043.                     cmd=
  1044.                     continue
  1045.                     ;;
  1046.             esac
  1047.             set - $cmd
  1048.             shift
  1049.             cmd="$*"
  1050.  
  1051.             : make sure there is always 1 blank line in file so
  1052.             : unaliasing will always work -- ed normally refuses
  1053.             : to write an empty file
  1054.  
  1055.             echo "" >> $aliasfile
  1056.             cat << ++++ >> $aliasfile
  1057. $cmd
  1058. ++++
  1059.             ed - $aliasfile << '++++'
  1060.             g/alias[     ]/s///
  1061.             g/^['"]\(.*\)['"]$/s//\1/
  1062.             g/^/s//alias    /
  1063.             w
  1064. ++++
  1065.             sort -u -o $aliasfile $aliasfile
  1066.             doalias=yes
  1067.             cmd="alias $cmd"
  1068.             run=no
  1069.             ;;
  1070.         unalias[\ \    ]*)
  1071.             set - $cmd
  1072.             case "$#" in
  1073.                 2)
  1074.                     cmd=$2
  1075.                     ;;
  1076.                 *)
  1077.                     echo "Syntax: unalias alias_name"
  1078.                     continue
  1079.                     ;;
  1080.             esac
  1081.             ed - $aliasfile << ++++
  1082.             /^$cmd[     ]/d
  1083.             w
  1084. ++++
  1085.             case "`set - \`wc -l $aliasfile\`;echo $1``" in
  1086.                 1)
  1087.                     : just removed last alias
  1088.  
  1089.                     doalias=no
  1090.                     ;;
  1091.             esac
  1092.             run=no
  1093.             ;;
  1094.         *)
  1095.             case "$doalias" in
  1096.                 yes)
  1097.                     set - $cmd
  1098.                     tmp="`grep \"^$1 \" $aliasfile`"
  1099.                     case "$tmp" in
  1100.                         $1[\ \    ]*)
  1101.                             shift
  1102.                             cmd=$*
  1103.                             set - $tmp
  1104.                             shift
  1105.                             tmp=$*
  1106.                             case "$tmp" in
  1107.                                 *\$*)
  1108.                                     : uses positional variables
  1109.  
  1110.                                     cmd="set - $cmd ; $tmp"
  1111.                                     getcmd=no
  1112.                                     continue
  1113.                                     ;;
  1114.                                 *)
  1115.                                     cmd="$tmp $cmd"
  1116.                                     getcmd=no
  1117.                                     continue
  1118.                                     ;;
  1119.                             esac
  1120.                             ;;
  1121.                         *)
  1122.                             echo "$cmd" > /tmp/bcsh$$
  1123.                             ;;
  1124.                     esac
  1125.                     ;;
  1126.                 no)
  1127.                     echo "$cmd" > /tmp/bcsh$$
  1128.                     ;;
  1129.             esac
  1130.             ;;
  1131.     esac
  1132.     case "$cmd" in
  1133.         *+~+p)
  1134.             cmd="`expr \"$cmd\" : '\(.*\)+~+p'`"
  1135.             echoit=yes
  1136.             run=no
  1137.             ;;
  1138.     esac
  1139.     case "$cmd" in
  1140.         "")
  1141.             continue
  1142.             ;;
  1143.         *)
  1144.             case "$exclaim" in
  1145.                 yes)
  1146.                     cmd="`echo \"$cmd\" | sed -e 's@REALEXCLAMATIONMARK@!@g'`"
  1147.                     echo "$cmd" > /tmp/bcsh$$
  1148.                     ;;
  1149.             esac
  1150.             case "$echoit" in
  1151.                 yes)
  1152.                     echo $cmd
  1153.                     ;;
  1154.             esac
  1155.             case "$run" in
  1156.                 yes)
  1157.                     case "${noclobber+yes}" in
  1158.                         yes)
  1159.                             case "$cmd" in
  1160.                                 *\>![\ \    ]*)
  1161.                                     ed - /tmp/bcsh$$ << ++++
  1162.                                     g/>!/s//>/
  1163.                                     w
  1164. ++++
  1165.                                     ;;
  1166.                                 *\>\>*)
  1167.                                     ;;
  1168.                                 *\>*)
  1169.                                     outfile="`expr \"$cmd\" : '.*>\(.*\)'`"
  1170.                                     case "$outfile" in
  1171.                                         \&*)
  1172.                                             ;;
  1173.                                         *)
  1174.                                             set - $outfile
  1175.                                             outfile="$1"
  1176.                                             if test -s "$outfile"
  1177.                                                 then
  1178.                                                 case "${iclobber+yes}" in
  1179.                                                     yes)
  1180.                                                         echo $n "Overwrite ${outfile}? $c"
  1181.                                                         read answer
  1182.                                                         case "$answer" in
  1183.                                                             y*)
  1184.                                                                 ;;
  1185.                                                             *)
  1186.                                                                 echo ':' > /tmp/bcsh$$
  1187.                                                                 ;;
  1188.                                                         esac
  1189.                                                         ;;
  1190.                                                     *)
  1191.                                                         echo "${outfile}: file exists"
  1192.                                                         echo ':' > /tmp/bcsh$$
  1193.                                                         ;;
  1194.                                                 esac
  1195.                                             fi
  1196.                                             ;;
  1197.                                     esac
  1198.                                     ;;
  1199.                             esac
  1200.                             ;;
  1201.                         *)
  1202.                             case "$cmd" in
  1203.                                 *\>![\ \    ]*)
  1204.                                     ed - /tmp/bcsh$$ << ++++
  1205.                                     g/>!/s//>/g
  1206.                                     w
  1207. ++++
  1208.                                     ;;
  1209.                             esac
  1210.                             ;;
  1211.                     esac
  1212.                     (trap 'exit 1' 2 3;sh /tmp/bcsh$$)
  1213.                     ;;
  1214.             esac
  1215.             case "$cmd" in
  1216.                 $lastcmd)
  1217.                     ;;
  1218.                 *)
  1219.                     case "$exclaim" in
  1220.                         yes)
  1221.                             cmd="`echo \"$cmd\" | sed -e 's@!@\\\\!@g'`"
  1222.                             ;;
  1223.                     esac
  1224.                     cat << ++++ >> $histfile
  1225. $cmd
  1226. ++++
  1227.                     lastcmd=$cmd
  1228.  
  1229.                     case "$inc_cmdno" in
  1230.                         yes)
  1231.                             cmdno="`expr \"$cmdno\" + 1`"
  1232.                             ;;
  1233.                     esac
  1234.                     ;;
  1235.             esac
  1236.             ;;
  1237.     esac
  1238.  
  1239.     : The next commented-out line sets the prompt to include the command
  1240.     : number -- you should only un-comment this if it is the ONLY thing
  1241.     : you ever want as your prompt, because it will override attempts
  1242.     : to set PS1 from the command level.  If you want the command number
  1243.     : in your prompt without sacrificing the ability to change the prompt
  1244.     : later, replace the default setting for PS1 before the beginning of
  1245.     : the main loop with the following:  PS1='echo -n "${cmdno}% "'
  1246.     : Doing it this way is, however, slower than the simple version below.
  1247.      
  1248.     PS1="${cmdno}% "
  1249.  
  1250.     getcmd=yes
  1251.     echoit=no
  1252.     exclaim=no
  1253. done
  1254. exit 0
  1255. --------------- cut here --------------------- cut here -------------------
  1256. -- 
  1257.  
  1258. Christine Robertson  {linus, ihnp4, decvax}!utzoo!globetek!chris
  1259.  
  1260. Money may not buy happiness, but misery in luxury has its compensations...
  1261.