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