Newsgroups: comp.sources.misc From: byron@archone.tamu.edu (Byron Rakitzis) Subject: v23i065: rc - A Plan 9 shell reimplementation, v1.2, Part05/06 Message-ID: <1991Oct18.034451.2490@sparky.imd.sterling.com> X-Md4-Signature: 52bd4c6ef9a95805c71f329459ca9c28 Date: Fri, 18 Oct 1991 03:44:51 GMT Approved: kent@sparky.imd.sterling.com Submitted-by: byron@archone.tamu.edu (Byron Rakitzis) Posting-number: Volume 23, Issue 65 Archive-name: rc/part05 Environment: UNIX Supersedes: rc: Volume 20, Issue 10-13 #!/bin/sh # do not concatenate these parts, unpack them in order with /bin/sh # file plan9.ps continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 5; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping plan9.ps' else echo 'x - continuing file plan9.ps' sed 's/^X//' << 'SHAR_EOF' >> 'plan9.ps' && (program to pick a random line from)6 1479 1 3561 6182 t ( prints the location, and if it guessed Holmdel, prints a message and exits.)13 2942( It)1 111(the location list.)2 642 3 720 6302 t ( the)1 149(Then it uses)2 485 2 720 6458 t 10 CW f (read)1381 6458 w 10 R f (function to get lines from standard input and validity-check them until it gets a legal)14 3392 1 1648 6458 t ( that the condition part of a)6 1118(name. Note)1 491 2 720 6578 t 10 CW f (while)2360 6578 w 10 R f ( the exit status of the)5 855( Only)1 255( compound command.)2 901(can be a)2 338 4 2691 6578 t (last command in the sequence is checked.)6 1666 1 720 6698 t ( it goes back to the top of the loop.)9 1388( Otherwise)1 460(Again, if the result is Holmdel, it prints a message and exits.)11 2415 3 720 6854 t cleartomark showpage saveobj restore %%EndPage: 11 11 %%Page: 12 12 /saveobj save def mark 12 pagesetup 10 R f (- 12 -)2 216 1 2772 480 t 9 CW f (t=/tmp/holmdel$pid)1008 890 w (fn read{)1 432 1 1008 1000 t ($1=`{awk '{print;exit}'})1 1296 1 1440 1110 t (})1008 1220 w (ifs=')1008 1330 w ( just a newline)3 810(' #)1 486 2 1008 1440 t (fn sigexit sigint sigquit sighup{)4 1782 1 1008 1550 t (rm -f $t)2 432 1 1440 1660 t (exit)1440 1770 w (})1008 1880 w (cat <<'!' >$t)2 702 1 1008 1990 t (Allentown)1008 2100 w (Atlanta)1008 2210 w (Cedar Crest)1 594 1 1008 2320 t (Chester)1008 2430 w (Columbus)1008 2540 w (Elmhurst)1008 2650 w (Fullerton)1008 2760 w (Holmdel)1008 2870 w (Indian Hill)1 594 1 1008 2980 t (Merrimack Valley)1 864 1 1008 3090 t (Morristown)1008 3200 w (Piscataway)1008 3310 w (Reading)1008 3420 w (Short Hills)1 594 1 1008 3530 t (South Plainfield)1 864 1 1008 3640 t (Summit)1008 3750 w (Whippany)1008 3860 w (West Long Branch)2 864 1 1008 3970 t (!)1008 4080 w (while\(true\){)1008 4190 w (lab=`{/usr/games/fortune $t})1 1512 1 1170 4300 t (echo $lab)1 486 1 1170 4410 t (if\(\304 $lab Holmdel\){)2 1026 1 1170 4520 t (echo You lose.)2 756 1 1332 4630 t (exit)1332 4740 w (})1170 4850 w (while\(read lab; ! grep -i -s $lab $t\) echo No such location.)11 3240 1 1170 4960 t (if\(\304 $lab [hH]olmdel\){)2 1188 1 1170 5070 t (echo You win.)2 702 1 1332 5180 t (exit)1332 5290 w (})1170 5400 w (})1008 5510 w 10 B f (27. Discussion)1 626 1 720 5690 t 10 R f (Steve Bourne's)1 622 1 720 5846 t 10 CW f (/bin/sh)1376 5846 w 10 R f ( I)1 93( comparison.)1 526(is extremely well-designed; any successor is bound to suffer in)9 2591 3 1830 5846 t ( things wherever possible, usually by)5 1512(have tried to fix its best-acknowledged shortcomings and to simplify)9 2808 2 720 5966 t ( I)1 67( Obviously)1 475( when irresistibly tempted have I introduced novel ideas.)8 2330( Only)1 258(omitting unessential features.)2 1190 5 720 6086 t (have tinkered extensively with Bourne's syntax, that being where his work was most open to criticism.)15 4109 1 720 6206 t (The most important principle in)4 1284 1 720 6362 t 10 I f (rc)2032 6362 w 10 R f ( is never scanned more)4 930( Input)1 265('s design is that it's not a macro processor.)8 1730 3 2115 6362 t ( by the lexical and syntactic analysis code \(except, of course, by the)12 2885(than once)1 401 2 720 6482 t 10 CW f (eval)4046 6482 w 10 R f (command, whose)1 714 1 4326 6482 t 10 I f (raison d'etre)1 519 1 720 6602 t 10 R f (is to break the rule\).)4 801 1 1264 6602 t ( These)1 298( arguments containing spaces.)3 1228(Bourne shell scripts can often be made to run wild by passing them)12 2794 3 720 6758 t (will be split into multiple arguments using)6 1780 1 720 6878 t 10 CW f (IFS)2539 6878 w 10 R f ( In)1 146( inopportune times.)2 801(, often as)2 391 3 2719 6878 t 10 I f (rc)4095 6878 w 10 R f (, values of variables,)3 862 1 4178 6878 t ( have)1 227( Arguments)1 508( when substituted into a command.)5 1464(including command line arguments, are not re-read)6 2121 4 720 6998 t (presumably been scanned in the parent process, and ought not to be re-read.)12 3026 1 720 7118 t ( store lists of)3 571( needs to be able to)5 858( He)1 184(Why does Bourne re-scan commands after variable substitution?)7 2707 4 720 7274 t cleartomark showpage saveobj restore %%EndPage: 12 12 %%Page: 13 13 /saveobj save def mark 13 pagesetup 10 R f (- 13 -)2 216 1 2772 480 t ( we eliminate re-scanning, we must change the)7 1875( If)1 117( character strings.)2 708(arguments in variables whose values are)5 1620 4 720 840 t (type of variables, so that they can explicitly carry lists of strings.)11 2583 1 720 960 t ( are two dif-)3 505( There)1 287( for lists of words.)4 750( need a notation)3 647( We)1 192(This introduces some conceptual complications.)4 1939 6 720 1116 t (ferent kinds of concatenation, for strings \320)6 1798 1 720 1236 t 10 CW f ($a\303$b)2552 1236 w 10 R f (, and lists \320)3 533 1 2852 1236 t 10 CW f (\($a $b\))1 429 1 3419 1236 t 10 R f ( difference between)2 801(. The)1 238 2 3848 1236 t 10 CW f (\(\))4920 1236 w 10 R f (and)720 1356 w 10 CW f ('')895 1356 w 10 R f ( not the)2 314(is confusing to novices, although the distinction is arguably sensible \320 a null argument is)14 3680 2 1046 1356 t (same as no argument.)3 865 1 720 1476 t ( the text enclosed in back-)5 1100( is because)2 452( This)1 238(Bourne also rescans input when doing command substitution.)7 2530 4 720 1632 t ( it ought to be parsed when the enclosing com-)9 1939( Properly,)1 427(quotes is not properly a string, but a command.)8 1954 3 720 1752 t (mand is, but this makes it difficult to handle nested command substitutions, like this:)13 3393 1 720 1872 t 9 CW f (size=`wc -l \\`ls -t|sed 1q\\``)4 1566 1 1008 2042 t 10 R f ( can get much worse)4 832( This)1 231( escaped to avoid terminating the outer command.)7 2018(The inner back-quotes must be)4 1239 4 720 2222 t (than the above example; the number of)6 1612 1 720 2342 t 10 CW f (\\)2366 2342 w 10 R f ('s required is exponential in the nesting depth.)7 1911 1 2426 2342 t 10 I f (Rc)4396 2342 w 10 R f (fixes this by)2 505 1 4535 2342 t (making the backquote a unary operator whose argument is a command, like this:)12 3220 1 720 2462 t 9 CW f (size=`{wc -l `{ls -t|sed 1q}})4 1566 1 1008 2632 t 10 R f (No escapes are ever required, and the whole thing is parsed in one pass.)13 2862 1 720 2812 t (For similar reasons)2 772 1 720 2968 t 10 I f (rc)1520 2968 w 10 R f ( associating a string)3 803(defines signal handlers as though they were functions, instead of)9 2606 2 1631 2968 t (with each signal, as Bourne does, with the attendant possibility of getting a syntax error message in)16 4320 1 720 3088 t ( Since)1 285(response to typing the interrupt character.)5 1723 2 720 3208 t 10 I f (rc)2766 3208 w 10 R f (parses input when typed, it reports errors when you)8 2153 1 2887 3208 t (make them.)1 466 1 720 3328 t ( need for the distinction)4 1029( is no)2 255( There)1 301(For all this trouble, we gain substantial semantic simplifications.)8 2735 4 720 3484 t (between)720 3604 w 10 CW f ($*)1080 3604 w 10 R f (and)1228 3604 w 10 CW f ($@)1400 3604 w 10 R f ( rules that)2 398( is no need for four types of quotation, nor the extremely complicated)12 2812(. There)1 310 3 1520 3604 t ( In)1 133(govern them.)1 527 2 720 3724 t 10 I f (rc)1405 3724 w 10 R f ( to appear in an argu-)5 855(you use quotation marks exactly when you want a syntax character)10 2672 2 1513 3724 t (ment.)720 3844 w 10 CW f (IFS)999 3844 w 10 R f (is no longer used, except in the one case where it was indispensable: converting command out-)15 3833 1 1207 3844 t (put into argument lists during command substitution.)6 2121 1 720 3964 t ( security hole [Ree88].)3 933(This also avoids an important)4 1211 2 720 4120 t 10 I f (System)2922 4120 w 10 R f (\(3\) and)1 293 1 3199 4120 t 10 I f (popen)3525 4120 w 10 R f (\(3\) call)1 293 1 3769 4120 t 10 CW f (/bin/sh)4095 4120 w 10 R f (to execute a)2 492 1 4548 4120 t ( to use either of these routines with any assurance that the specified command)13 3212( is impossible)2 569(command. It)1 539 3 720 4240 t (will be executed, even if the caller of)7 1479 1 720 4360 t 10 I f (system)2224 4360 w 10 R f (or)2515 4360 w 10 I f (popen)2623 4360 w 10 R f ( can)1 164( This)1 229( for the command.)3 735(specifies a full path name)4 1020 4 2892 4360 t ( problem is that)3 643( The)1 211( a set-userid program.)3 888(be devastating if it occurs in)5 1164 4 720 4480 t 10 CW f (IFS)3657 4480 w 10 R f (is used to split the command)5 1172 1 3868 4480 t (into words, so an attacker can just set)7 1534 1 720 4600 t 10 CW f (IFS=/)2285 4600 w 10 R f ( Trojan horse named)3 844(in his environment and leave a)5 1253 2 2616 4600 t 10 CW f (usr)4745 4600 w 10 R f (or)4957 4600 w 10 CW f (bin)720 4720 w 10 R f (in the current working directory before running the privileged program.)9 2891 1 929 4720 t 10 I f (Rc)3874 4720 w 10 R f (fixes this by not ever res-)5 1032 1 4008 4720 t (canning input for any reason.)4 1167 1 720 4840 t (Most of the other differences between)5 1565 1 720 4996 t 10 I f (rc)2320 4996 w 10 R f ( eliminated Bourne's)2 860( I)1 94(and the Bourne shell are not so serious.)7 1647 3 2439 4996 t (peculiar forms of variable substitution, like)5 1726 1 720 5116 t 9 CW f (echo ${a=b} ${c-d} ${e?error})3 1566 1 1008 5286 t 10 R f ( deleted the builtins)3 822( I)1 95(because they are little used, redundant and easily expressed in less abstruse terms.)12 3403 3 720 5466 t 10 CW f (export)720 5586 w 10 R f (,)1080 5586 w 10 CW f (readonly)1131 5586 w 10 R f (,)1611 5586 w 10 CW f (break)1662 5586 w 10 R f (,)1962 5586 w 10 CW f (continue)2013 5586 w 10 R f (,)2493 5586 w 10 CW f (read)2544 5586 w 10 R f (,)2784 5586 w 10 CW f (return)2835 5586 w 10 R f (,)3195 5586 w 10 CW f (set)3246 5586 w 10 R f (,)3426 5586 w 10 CW f (times)3477 5586 w 10 R f (and)3803 5586 w 10 CW f (unset)3973 5586 w 10 R f (because they seem)2 742 1 4298 5586 t (redundant or only marginally useful.)4 1462 1 720 5706 t (Where Bourne's syntax draws from Algol 68,)6 1917 1 720 5862 t 10 I f (rc)2677 5862 w 10 R f ( I)1 99( is harder to defend.)4 859( This)1 244( C or Awk.)3 492('s is based on)3 586 5 2760 5862 t (believe that, for example)3 992 1 720 5982 t 9 CW f (if\(test -f junk\) rm junk)4 1296 1 1008 6152 t 10 R f (is better syntax than)3 802 1 720 6332 t 9 CW f (if test -f junk; then rm junk; fi)7 1782 1 1008 6502 t 10 R f ( is less cluttered with keywords, it avoids the semicolons that Bourne requires in odd places, and)16 3921(because it)1 399 2 720 6682 t (the syntax characters better set off the active parts of the command.)11 2693 1 720 6802 t (The one bit of large-scale syntax that Bourne unquestionably does better than)11 3169 1 720 6958 t 10 I f (rc)3922 6958 w 10 R f (is the)1 222 1 4038 6958 t 10 CW f (if)4293 6958 w 10 R f (statement with)1 594 1 4446 6958 t 10 CW f (else)720 7078 w 10 R f (clause.)986 7078 w 10 I f (Rc)1310 7078 w 10 R f ('s)1415 7078 w 10 CW f (if)1512 7078 w 10 R f (has no terminating)2 744 1 1657 7078 t 10 CW f (fi)2426 7078 w 10 R f ( a result, the parser cannot tell whether or not)9 1807( As)1 161(-like bracket.)1 526 3 2546 7078 t (to expect an)2 482 1 720 7198 t 10 CW f (else)1227 7198 w 10 R f ( problem is that after reading, for example)7 1685( The)1 205(clause without looking ahead in its input.)6 1647 3 1492 7198 t cleartomark showpage saveobj restore %%EndPage: 13 13 %%Page: 14 14 /saveobj save def mark 14 pagesetup 10 R f (- 14 -)2 216 1 2772 480 t 9 CW f (if\(test -f junk\) echo junk found)5 1728 1 1008 830 t 10 R f (in interactive mode,)2 824 1 720 1010 t 10 I f (rc)1583 1010 w 10 R f (cannot decide whether to execute it immediately and print)8 2430 1 1705 1010 t 10 CW f ($prompt\(1\))4174 1010 w 10 R f (, or to)2 266 1 4774 1010 t (print)720 1130 w 10 CW f ($prompt\(2\))937 1130 w 10 R f (and wait for the)3 638 1 1565 1130 t 10 CW f (else)2231 1130 w 10 R f ( the Bourne shell, this is not a problem, because)9 1930( In)1 136(to be typed.)2 475 3 2499 1130 t (the)720 1250 w 10 CW f (if)867 1250 w 10 R f (command must end with)3 986 1 1012 1250 t 10 CW f (fi)2023 1250 w 10 R f (, regardless of whether it contains an)6 1466 1 2143 1250 t 10 CW f (else)3634 1250 w 10 R f (or not.)1 261 1 3899 1250 t 10 I f (Rc)720 1406 w 10 R f ( is to declare that the)5 839('s admittedly feeble solution)3 1144 2 825 1406 t 10 CW f (else)2835 1406 w 10 R f (clause is a separate statement, with the semantic)7 1938 1 3102 1406 t (proviso that it must immediately follow an)6 1724 1 720 1526 t 10 CW f (if)2472 1526 w 10 R f (, and to call it)4 559 1 2592 1526 t 10 CW f (if not)1 363 1 3179 1526 t 10 R f (rather than)1 432 1 3570 1526 t 10 CW f (else)4030 1526 w 10 R f (, as a reminder that)4 770 1 4270 1526 t ( the braces are required in the)6 1227( only noticeable consequence of this is that)7 1766( The)1 212(something odd is going on.)4 1115 4 720 1646 t (construction)720 1766 w 9 CW f (for\(i\){)1008 1936 w (if\(test -f $i\) echo $i found)5 1512 1 1224 2046 t (if not echo $i not found)5 1296 1 1224 2156 t (})1008 2266 w 10 R f (and that)1 319 1 720 2446 t 10 I f (rc)1064 2446 w 10 R f (resolves the ``dangling else'' ambiguity in opposition to most people's expectations.)10 3378 1 1172 2446 t ( the UNIX system programmer's manual the Bourne)7 2120(It is remarkable that in the four most recent editions of)10 2200 2 720 2602 t ( not admit the command)4 976(shell grammar described in the manual page does)7 1985 2 720 2722 t 10 CW f (who|wc)3707 2722 w 10 R f ( is surely an over-)4 719(. This)1 254 2 4067 2722 t ( Even)1 266( something darker: nobody really knows what the Bourne shell's grammar is.)11 3206(sight, but it suggests)3 848 3 720 2842 t ( the rou-)2 346( parser is implemented by recursive descent, but)7 1959( The)1 210(examination of the source code is little help.)7 1805 4 720 2962 t ( categories all have a flag argument that subtly changes their operation)11 2885(tines corresponding to the syntactic)4 1435 2 720 3082 t (depending on the context.)3 1041 1 720 3202 t 10 I f (Rc)1814 3202 w 10 R f ('s parser is implemented using)4 1233 1 1919 3202 t 10 I f (yacc)3180 3202 w 10 R f (, so I can say precisely what the grammar)8 1678 1 3362 3202 t (is.)720 3322 w ( is a)2 165( There)1 284( it considerably except for two things.)6 1524( would simplify)2 636( I)1 84(Its lexical structure is harder to describe.)6 1627 6 720 3478 t ( distinguish between parentheses that immediately follow a word with no intervening)11 3614(lexical kludge to)2 706 2 720 3598 t ( use for)2 319(spaces and those that don't that I would eliminate if there were a reasonable pair of characters to)17 4001 2 720 3718 t ( could also eliminate the insertion of free carets if users were not adamant about it.)15 3299( I)1 83(subscript brackets.)1 743 3 720 3838 t 10 B f (28. Acknowledgements)1 1002 1 720 4078 t 10 R f ( Plan 9 users have been insistent, incessant sources of good ideas and)12 2825(Rob Pike, Howard Trickey and other)5 1495 2 720 4234 t ( examples in this document are plagiarized from [Bou78], as are most of)12 2892(criticism. Some)1 653 2 720 4354 t 10 I f (rc)4290 4354 w 10 R f ('s good features.)2 662 1 4373 4354 t 10 B f (29. References)1 639 1 720 4594 t 10 R f ( R. Bourne, ``U)3 678(Bou78. S.)1 423 2 720 4822 t 8 R f (NIX)1821 4822 w 10 R f (Time-Sharing System: The U)3 1234 1 2006 4822 t 8 R f (NIX)3240 4822 w 10 R f (Shell,'')3425 4822 w 10 I f (Bell System Technical Journal)3 1275 1 3765 4822 t 10 B f (57)970 4942 w 10 R f (\(6\), pp. 1971-1990 \(July-August 1978\).)4 1579 1 1070 4942 t ( Reeds, ``)2 409(Ree88. J.)1 394 2 720 5098 t 10 CW f (/bin/sh)1523 5098 w 10 R f ( Bell)1 205( AT&T)1 334(: the biggest UNIX security loophole,'' 11217-840302-04TM,)6 2558 3 1943 5098 t (Laboratories \(1988\).)1 820 1 970 5218 t cleartomark showpage saveobj restore %%EndPage: 14 14 %%Trailer done %%Pages: 14 %%DocumentFonts: Courier Times-Bold Times-Italic Times-Roman Symbol SHAR_EOF echo 'File plan9.ps is complete' && chmod 0644 plan9.ps || echo 'restore of plan9.ps failed' Wc_c="`wc -c < 'plan9.ps'`" test 67056 -eq "$Wc_c" || echo 'plan9.ps: original size 67056, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= rc.1 ============== if test -f 'rc.1' -a X"$1" != X"-c"; then echo 'x - skipping rc.1 (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting rc.1 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'rc.1' && .\" rc.1 .\"------- .\" Dd distance to space vertically before a "display" .\" These are what n/troff use for interparagraph distance .\"------- .if t .nr Dd .4v .if n .nr Dd 1v .\"------- .\" Ds begin a display .\"------- .de Ds .RS \\$1 .sp \\n(Ddu .nf .. .\"------- .\" De end a display (no trailing vertical spacing) .\"------- .de De .fi .RE .. .\"------- .\" I stole the Xf macro from the -man macros on my machine (originally .\" "}S", I renamed it so that it won't conflict). .\"------- .\" Set Cf to the name of the constant width font. .\" It will be "C" or "(CW", typically. .\" NOTEZ BIEN the lines defining Cf must have no trailing white space: .\"------- .if t .ds Cf C .if n .ds Cf R .\"------- .\" Rc - Alternate Roman and Courier .\"------- .de Rc .Xf 1 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Ic - Alternate Italic and Courier .\"------- .de Ic .Xf 2 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Bc - Alternate Bold and Courier .\"------- .de Bc .Xf 3 \\*(Cf \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Cr - Alternate Courier and Roman .\"------- .de Cr .Xf \\*(Cf 1 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Ci - Alternate Courier and Italic .\"------- .de Ci .Xf \\*(Cf 2 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Cb - Alternate Courier and Bold .\"------- .de Cb .Xf \\*(Cf 3 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" .. .\"------- .\" Xf - Alternate fonts .\" .\" \$1 - first font .\" \$2 - second font .\" \$3 - desired word with embedded font changes, built up by recursion .\" \$4 - text for first font .\" \$5 - \$9 - remaining args .\" .\" Every time we are called: .\" .\" If there is something in \$4 .\" then Call ourself with the fonts switched, .\" with a new word made of the current word (\$3) and \$4 .\" rendered in the first font, .\" and with the remaining args following \$4. .\" else We are done recursing. \$3 holds the desired output .\" word. We emit \$3, change to Roman font, and restore .\" the point size to the default. .\" fi .\" .\" Use Xi to add a little bit of space after italic text. .\"------- .de Xf .ds Xi .if "\\$1"2" .if !"\\$5"" .ds Xi \^ .if "\\$1"I" .if !"\\$5"" .ds Xi \^ .ie !"\\$4"" \{\ . Xf \\$2 \\$1 "\\$3\\f\\$1\\$4\\*(Xi" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9" .\} .el \{\\$3 . ft R \" Restore the default font, since we don't know . \" what the last font change was. . ps 10 \" Restore the default point size, since it might . \" have been changed by an argument to this macro. .\} .. .TH RC 1 "28 April 1991" .SH NAME rc \- shell .SH SYNOPSIS .B rc .RB [ \-eixvldn ] .RB [ \-c .IR command ] .RI [ arguments ] .SH DESCRIPTION .I rc is a command interpreter and programming language similar to .IR sh (1). It is based on the AT&T Plan 9 shell of the same name. The shell offers a C-like syntax (much more so than the C shell), and a powerful mechanism for manipulating variables. It is reasonably small and reasonably fast, especially when compared to contemporary shells. Its use is intended to be interactive, but the language lends itself well to scripts. .SH OPTIONS .TP .Cr \-e If the .Cr \-e option is present, then .I rc will exit if the exit status of a command is false (nonzero). .I rc will not exit, however, if a conditional fails, e.g., an .Cr if() command. .TP .Cr \-i If the .Cr \-i option is present or if the input to .I rc is from a terminal (as determined by .IR isatty (3)) then .I rc will be in .I interactive mode. That is, a prompt (from .Cr $prompt(1)\^ ) will be printed before an input line is taken, and .I rc will ignore the signals .Cr SIGINT and .Cr SIGQUIT . .TP .Cr \-x This option will make .I rc print every command on standard error before it is executed. It can be useful for debugging .I rc scripts. .TP .Cr \-v This option will echo input to .I rc on standard error as it is read. .TP .Cr \-l If the .Cr \-l option is present, or if .IR rc 's .Cr argv[0][0] is a dash .Rc ( \- ), then .I rc will behave as a login shell. That is, it will try to run commands present in .Cr $home/.rcrc , if this file exists, before reading any other input. .TP .Cr \-d This flag causes .I rc not to trap .Cr SIGQUIT , and thus .I rc will dump core when it receives this signal. This option is only useful for debugging .IR rc . .TP .Cr \-n This flag causes .I rc to read its input and parse it, but not to execute any commands. This is useful for syntax checking on scripts. If used in combination with the .Cr \-x option, .I rc will print each command as it is parsed in a form similar to the one used for exporting functions into the environment. .TP .Cr \-c If .Cr \-c is present, commands are executed from the immediately following argument. Any further arguments to .I rc are placed in .Cr $* . .PP .SH COMMANDS A simple command is a sequence of words, separated by white space (space and tab) characters that ends with a newline, semicolon .Rc ( ; ), or ampersand .Rc ( & ). The first word of a command is the name of that command. If the name begins with .Cr / , .Cr ./ , or .Cr ../ , then the name is used as an absolute path name referring to an executable file. Otherwise, the name of the command is looked up in a table of shell functions, builtin commands, or as a file in the directories named by .Cr $path . .SS "Background Tasks" A command ending with a .Cr & is run in the background; that is, the shell returns immediately rather than waiting for the command to complete. Background commands have .Cr /dev/null connected to their standard input unless an explicit redirection for standard input is used. .SS "Subshells" A command prefixed with an at-sign .Rc ( @ ) is executed in a subshell. This insulates the parent shell from the effects of state changing operations such as a .B cd or a variable assignment. For example: .Ds .Cr "@ {cd ..; make} .De .PP will run .IR make (1) in the parent directory .Rc ( .. ), but leaves the shell running in the current directory. .SS "Line continuation" A long logical line may be continued over several physical lines by terminating each line (except the last) with a backslash .Rc ( \e ). The backslash-newline sequence is treated as a space. A backslash is not otherwise special to .IR rc . .SS Quoting .IR rc interprets several characters specially; special characters automatically terminate words. The following characters are special: .Ds .Cr "# ; & | ^ $ = \` ' { } ( ) < > .De .PP The single quote .Rc ( ' ) prevents special treatment of any character other than itself. All characters, including control characters, newlines, and backslashes between two quote characters are treated as an uninterpreted string. A quote character itself may be quoted by placing two quotes in a row. The minimal sequence needed to enter the quote character is .Cr '''' . The empty string is represented by .Cr '' . Thus: .Ds .Cr "echo 'What''s the plan, Stan?' .De .PP prints out .Ds .Cr "What's the plan, Stan? .De .SS Grouping Zero or more commands may be grouped within braces .Rc (`` { '' and .Rc `` } ''), and are then treated as one command. Braces do not otherwise define scope; they are used only for command grouping. In particular, be wary of the command: .Ds .Cr "for (i) { .Cr " command" .Cr "} | command .De .PP Since pipe binds tighter than .Cr for , this command does not perform what the user expects it to. Instead, enclose the whole .Cr for statement in braces: .Ds .Cr "{for (i) command} | command .De .PP Fortunately, .IR rc 's grammar is simple enough that a (confident) user can understand it by examining the skeletal .IR yacc (1) grammar at the end of this man page (see the section entitled .BR GRAMMAR ). .SS "Input and output" .PP The standard output may be redirected to a file with .Ds .Cr "command > file" .De .PP and the standard input may be taken from a file with .Ds .Cr "command < file .De .PP File descriptors other than 0 and 1 may be specified also. For example, to redirect standard error to a file, use: .Ds .Cr "command >[2] file .De .PP In order to duplicate a file descriptor, use .Ci >[ n = m ]\fR. Thus to redirect both standard output and standard error to the same file, use .Ds .Cr "command > file >[2=1] .De .PP To close a file descriptor that may be open, use .Ci >[ n =]\fR. For example, to close file descriptor 7: .Ds .Cr "command >[7=] .De .PP In order to place the output of a command at the end of an already existing file, use: .Ds .Cr "command >> file .De .PP If the file does not exist, then it is created. .PP ``Here documents'' are supported as in .I sh with the use of .Ds .Cr "command << 'eof-marker' .De .PP If the end-of-file marker is enclosed in quotes, then no variable substitution occurs inside the here document. Otherwise, every variable is substituted by its space-separated-list value (see .BR "Flat Lists" , below), and if a .Cr ^ character follows a variable name, it is deleted. This allows the unambiguous use of variables adjacent to text, as in .Ds .Cr $variable^follow .De .PP Additionally, .I rc supports ``here strings'', which are like here documents, except that input is taken directly from a string on the command line. Its use is illustrated here: .Ds .Cr "cat <<< 'this is a here string' | wc .De .PP (This feature enables .I rc to export functions using here documents into the environment; the author does not expect users to find this feature useful.) .SS Pipes Two or more commands may be combined in a pipeline by placing the vertical bar .Rc ( \||\| ) between them. The standard output (file descriptor 1) of the command on the left is tied to the standard input (file descriptor 0) of the command on the right. The notation .Ci |[ n = m ] indicates that file descriptor .I n of the left process is connected to file descriptor .I m of the right process. .Ci |[ n ] is a shorthand for .Ci |[ n =0]\fR. As an example, to pipe the standard error of a command to .IR wc (1), use: .Ds .Cr "command |[2] wc .De .PP The exit status of a pipeline is considered true if and only if every command in the pipeline exits true. .SS "Commands as Arguments" Some commands, like .IR cmp (1) or .IR diff (1), take their arguments on the command line, and do not read input from standard input. It is convenient sometimes to build nonlinear pipelines so that a command like .I cmp can read the output of two other commands at once. .I rc does it like this: .Ds .Cr "cmp <{command} <{command} .De .PP compares the output of the two commands in braces. A note: since this form of redirection is implemented with some kind of pipe, and since one cannot .IR lseek (2) on a pipe, commands that use .IR lseek (2) will hang. For example, most versions of .IR diff (1) use .IR lseek (2) on their inputs. .PP Data can be sent down a pipe to several commands using .IR tee (1) and the output version of this notation: .Ds .Cr "echo hi there | tee >{sed 's/^/p1 /'} >{sed 's/^/p2 /'} .De .SH "CONTROL STRUCTURES" The following may be used for control flow in .IR rc : .SS "If-else Statements" .PD 0 .sp .Ci "if (" test ") {" .br .I " cmd .br .TP .Ci "} else " cmd The .I test is executed, and if its return status is zero, the first command is executed, otherwise the second is. Braces are not mandatory around the commands. However, an .Cr else statement is valid only if it follows a close-brace on the same line. Otherwise, the .Cr if is taken to be a simple-if: .Ds +1.0i .Cr "if (test) .Cr " command .De .PD .SS "While and For Loops" .TP .Ci "while (" test ) " cmd .I rc executes the .I test and performs the command as long as the .I test is true. .TP .Ci "for (" var " in" " list" ) " cmd .I rc sets .I var to each element of .I list (which may contain variables and backquote substitutions) and runs .IR cmd . If .Rc `` in .IR list '' is omitted, then .I rc will set .I var to each element of .Cr $* (excluding .Cr $0 ). For example: .Ds +1.0i .Cr "for (i in \`{ls -F | grep '\e*$' | sed 's/\e*$//'}) { commands } .De .TP \& will set .Cr $i to the name of each file in the current directory that is executable. .SS "Switch" .TP .Ci "switch (" list ") { case" " ..." " } .I rc looks inside the braces after a .Cr switch statement for single lines beginning with the word .Cr case . If any of the patterns following .Cr case match the list supplied to .Cr switch , then the commands up until the next .Cr case statement are executed. Metacharacters should not be quoted; matching is performed only against the strings in .IR list , not against file names. (Matching for case statements is the same as for the .Cr ~ command.) .SS "Logical Operators" There are a number of operators in .I rc which depend on the exit status of a command. .Ds .Cr "command && command .De .PP executes the first command and then executes the second command if and only if the first command exits with a zero exit status (``true'' in Unix). .Ds .Cr "command || command .De .PP executes the first command executing the second command if and only if the second command exits with a nonzero exit status (``false'' in Unix). .Ds .Cr "! command .De .PP negates the exit status of a command. .SH "PATTERN MATCHING" There are two forms of pattern matching in .IR rc . One is traditional shell globbing. This occurs in matching for file names in argument lists: .Ds .Cr "command argument argument ... .De .PP When the characters .Cr "*" , .Cr [ or .Cr ? occur in an argument, .I rc looks at the argument as a pattern for matching against files. (Contrary to the behavior other shells exhibit, .I rc will only perform pattern matching if a metacharacter occurs unquoted and literally in the input. Thus, .Ds .Cr "foo='*' .Cr "echo $foo .De .PP will always echo just a star. In order for non-literal metacharacters to be expanded, an .Cr eval statement must be used in order to rescan the input.) Pattern matching occurs according to the following rules: a .Cr * matches any number (including zero) of characters. A .Cr ? matches any single character, and a .Cr [ followed by a number of characters followed by a .Cr ] matches a single character in that class. The rules for character class matching are the same as those for .IR ed (1), with the exception that character class negation is achieved with the tilde .Rc ( ~ ), not the caret .Rc ( ^ ), since the caret already means something else in .IR rc . .PP .I rc also matches patterns against strings with the .Cr ~ command: .Ds .Cr "~ subject pattern pattern ... .De .PP .Cr ~ sets .Cr $status to zero if and only if a supplied pattern matches any single element of the subject list. Thus .Ds .Cr "~ foo f* .De .PP sets status to zero, while .Ds .Cr "~ (bar baz) f* .De .PP sets status to one. The null list is matched by the null list, so .Ds .Cr "~ $foo () .De .PP checks to see whether .Cr $foo is empty or not. This may also be achieved by the test .Ds .Cr "~ $#foo 0 .De .PP Note that inside a .Cr ~ command .I rc does not match patterns against file names, so it is not necessary to quote the characters .Cr "*" , .Cr [ and .Cr "?" . However, .I rc does expand the glob the subject against filenames if it contains metacharacters. Thus, the command .Ds .Cr "~ * ? .De .PP returns true if any of the files in the current directory have a single-character name. (Note that if the .Cr ~ command is given a list as its first argument, then a successful match against any of the elements of that list will cause .Cr ~ to return true. For example: .Ds .Cr "~ (foo goo zoo) z* .De .PP is true.) .SH "LISTS AND VARIABLES" The primary data structure in .IR rc is the list, which is a sequence of words. Parentheses are used to group lists. The empty list is represented by .Cr "()" . Lists have no hierarchical structure; a list inside another list is expanded so the outer list contains all the elements of the inner list. Thus, the following are all equivalent .Ds .Cr "one two three X .Cr "(one two three) X .Cr "((one) () ((two three))) .De .PP Note that the null string, .Cr "''" , and the null list, .Cr "()" , are two very different things. Assigning the null string to variable is a valid operation, but it does not remove its definition. For example, if .Cr $a is set to .Cr "''" , then .Cr "$#a" , returns a 1. .SS "List Concatenation" Two lists may be joined by the concatenation operator .Rc ( ^ ). A single word is treated as a list of length one, so .Ds .Cr "echo foo^bar .De .PP produces the output .Ds .Cr foobar .De .PP For lists of more than one element, concatenation works according to the following rules: if the two lists have the same number of elements, then concatenation is pairwise: .Ds .Cr "echo (a\- b\- c\-)^(1 2 3) .De .PP produces the output .Ds .Cr "a\-1 b\-2 c\-3 .De .PP Otherwise, one of the lists must have a single element, and then the concatenation is distributive: .Ds .Cr "cc \-^(O g c) (malloc alloca)^.c .De .PP has the effect of performing the command .Ds .Cr "cc \-O \-g \-c malloc.c alloca.c .De .SS "Free Carets" .I rc inserts carets (concatenation operators) for free in certain situations, in order to save some typing on the user's behalf. For example, the above example could also be typed in as: .Ds .Cr "opts=(O g c) files=(malloc alloca) cc \-$opts $files.c .De .PP .I rc takes care to insert a free-caret between the .Rc `` \- '' and .Cr "$opts" , as well as between .Cr $files and .Cr ".c" . The rule for free carets is as follows: if a word or keyword is immediately followed by another word, keyword, dollar-sign or backquote, then .I rc inserts a caret between them. .SS "Variables" A list may be assigned to a variable, using the notation: .Ds .Ic var " = " list .De .PP Any sequence of non-special characters, except a sequence including only digits, may be used as a variable name. All user-defined variables are exported into the environment. .PP The value of a variable is referenced with the notation: .Ds .Ci $ var .De .PP Any variable which has not been assigned a value returns the null list, .Cr "()" , when referenced. In addition, multiple references are allowed: .Ds .Cr a=foo .Cr b=a .Cr "echo $$b .De .PP prints .Ds .Cr foo .De .PP A variable's definition may also be removed by assigning the null list to a variable: .Ds .Ic var =() .De .PP For ``free careting'' to work correctly, .I rc must make certain assumptions about what characters may appear in a variable name. .I rc assumes that a variable name consists only of alphanumeric characters, underscore .Rc ( \|_\| ) and star .Rc ( * ). To reference a variable with other characters in its name, quote the variable name. Thus: .Ds .Cr "echo $'we$Ird\Variab!le' .De .SS "Local Variables" Any number of variable assignments may be made local to a single command by typing: .Ds .Cr "a=foo b=bar ... command .De .PP The command may be a compound command, so for example: .Ds .Cr "path=. ifs=() { .Cr " " ... .Cr } .De .PP sets .Cr path to .Cr . and removes .Cr ifs for the duration of one long compound command. .SS "Variable Subscripts" Variables may be subscripted with the notation .Ds .Ci $var( n ) .De .PP where .I n is a list of integers (origin 1). The list of subscripts need not be in order or even unique. Thus, if .Ds .Cr "a=(one two three) .De .PP then .Ds .Cr "echo $a(3 3 3) .De .PP prints .Ds .Cr "three three three .De .PP If .I n references a nonexistent element, then .Ci $var( n ) returns the null list. The notation .Ci "$" n\fR, where .I n is an integer, is a shorthand for .Ci $*( n )\fR. Thus, .IR rc 's arguments may be referred to as .Cr "$1" , .Cr "$2" , and so on. .PP Note also that the list of subscripts may be given by any of .IR rc 's list operations: .Ds .Cr "$var(\`{awk 'BEGIN{for(i=1;i<=10;i++)print i;exit; }'}) .De .PP returns the first 10 elements of .Cr $var . .PP To count the number of elements in a variable, use .Ds .Cr $#var .De .PP This returns a single-element list, with the number of elements in .Cr $var . .SS "Flat Lists" In order to create a single-element list from a multi-element list, with the components space-separated, use .Ds .Cr $^var .De .PP This is useful when the normal list concatenation rules need to be bypassed. For example, to append a single period at the end of .Cr $path , use: .Ds .Cr "echo $^path. .De .SS "Backquote Substitution" A list may be formed from the output of a command by using backquote substitution: .Ds .Cr "\`{ command } .De .PP returns a list formed from the standard output of the command in braces. .Cr $ifs is used to split the output into list elements. By default, .Cr $ifs has the value space-tab-newline. The braces may be omitted if the command is a single word. Thus .Cr \`ls may be used instead of .Cr "\`{ls}" . This last feature is useful when defining functions that expand to useful argument lists. A frequent use is: .Ds .Cr "fn src { echo *.[chy] } .De .PP followed by .Ds .Cr "wc \`src .De .PP (This will print out a word-count of all C source files in the current directory.) .PP In order to override the value of .Cr $ifs for a single backquote substitution, use: .Ds .Cr "\`\` (ifs-list) { command } .De .PP .Cr $ifs will be temporarily ignored and the command's output will be split as specified by the list following the double backquote. For example: .Ds .Cr "\`\` ($nl :) {cat /etc/passwd} .De .PP splits up .Cr /etc/passwd into fields, assuming that .Cr $nl contains a newline as its value. .SH "SPECIAL VARIABLES" Several variables are known to .I rc and are treated specially. .TP .Cr * The argument list of .IR rc . .Cr "$1, $2, etc. are the same as .Cr $*(1) , .Cr $*(2) , etc. The variable .Cr $0 holds the value of .Cr argv[0] with which .I rc was invoked. Additionally, .Cr $0 is set to the name of a function for the duration of the execution of that function, and .Cr $0 is also set to the name of the file being interpreted for the duration of a .Cr . command. .TP .Cr apid The process ID of the last process started in the background. .TP .Cr cdpath A list of directories to search for the target of a .B cd command. The empty string stands for the current directory. Note that if the .Cr $cdpath variable does not contain the current directory, then the current directory will not be searched; this allows directory searching to begin in a directory other than the current directory. Note also that an assignment to .Cr $cdpath causes an automatic assignment to .Cr $CDPATH , and vice-versa. .TP .Cr history .Cr $history contains the name of a file to which commands are appended as .I rc reads them. This facilitates the use of a stand-alone history program (such as .IR history (1)) which parses the contents of the history file and presents them to .I rc for reinterpretation. If .Cr $history is not set, then .I rc does not append commands to any file. .TP .Cr home The default directory for the builtin .B cd command and is the directory in which .I rc looks to find its initialization file, .Cr .rcrc , if .I rc has been started up as a login shell. Like .Cr $cdpath and .Cr $CDPATH , .Cr $home and .Cr $HOME are aliased to each other. .TP .Cr ifs The internal field separator, used for splitting up the output of backquote commands for digestion as a list. .TP .Cr path This is a list of directories to search in for commands. The empty string stands for the current directory. Note that like .Cr $cdpath and .Cr $CDPATH , .Cr $path and .Cr $PATH are aliased to each other. .TP .Cr pid The process ID of the currently running .IR rc . .TP .Cr prompt This variable holds the two prompts (in list form, of course) that .I rc prints. .Cr $prompt(1) is printed before each command is read, and .Cr $prompt(2) is printed when input is expected to continue on the next line. .I rc sets .Cr $prompt to .Cr "('; ' '') by default. The reason for this is that it enables an .I rc user to grab commands from previous lines using a mouse, and to present them to .I rc for re-interpretation; the semicolon prompt is simply ignored by .IR rc . The null .Cr $prompt(2) also has its justification: an .I rc script, when typed interactively, will not leave .Cr $prompt(2) 's on the screen, and can therefore be grabbed by a mouse and placed directly into a file for use as a shell script, without further editing being necessary. .TP .Cr prompt " (function) If this function is set, then it gets executed every time .I rc is about to print .Cr "$prompt(1)" . .TP .Cr status The exit status of the last command. If the command exited with a numeric value, that number is the status. If the died with a signal, the status is the name of that signal; if a core file was created, the string .Rc `` +core '' is appended. The value of .Cr $status for a pipeline is a list, with one entry, as above, for each process in the pipeline. For example, the command .Ds 1i .Cr "ls | wc .De .TP \& usually sets .Cr $status to .Cr "(0 0)" . .PP The values of .Cr "$path" , .Cr "$cdpath" , and .Cr $home are derived from the environment values of .Cr "$PATH" , .Cr "$CDPATH" , and .Cr "$HOME" . Otherwise, they are derived from the environment values of .Cr $path , .Cr $cdpath and .Cr $home . This is for compatibility with other Unix programs, like .IR sh (1). .Cr $PATH and .Cr $CDPATH are assumed to be colon-separated lists. .SH FUNCTIONS .I rc functions are identical to .I rc scripts, except that they are stored in memory and are automatically exported into the environment. A shell function is declared as: .Ds .Cr "fn name { commands } .De .PP .I rc scans the definition until the close-brace, so the function can span more than one line. The function definition may be removed by typing .Ds .Cr "fn name .De .PP (One or more names may be specified. With an accompanying definition, all names receive the same definition. This is sometimes useful for assigning the same signal handler to many signals. Without a definition, all named functions are deleted.) When a function is executed, .Cr $* is set to the arguments to that function for the duration of the command. Thus a reasonable definition for .Cr "l" , a shorthand for .IR ls (1), could be: .Ds .Cr "fn l { ls -FC $* } .De .PP but not .Ds .Cr "fn l { ls -FC } .De .SH "INTERRUPTS AND SIGNALS" .I rc recognizes a number of signals, and allows the user to define shell functions which act as signal handlers. .I rc by default traps .Cr SIGINT and .Cr SIGQUIT when it is in interactive mode. .Cr SIGQUIT is ignored, unless .I rc has been invoked with the .Cr \-d flag. However, user-defined signal handlers may be written for these and all other signals. The way to define a signal handler is to write a function by the name of the signal in lower case. Thus: .Ds .Cr "fn sighup { echo hangup; rm /tmp/rc$pid.*; exit } .De .PP In addition to Unix signals, .I rc recognizes the artificial signal .Cr SIGEXIT which occurs as .I rc is about to exit. .PP In order to remove a signal handler's definition, remove it as though it were a regular function. For example: .Ds .Cr "fn sigint .De .PP returns the handler of .Cr SIGINT to the default value. In order to ignore a signal, set the signal handler's value to .Cr "{}" . Thus: .Ds .Cr "fn sigint {} .De .PP causes SIGINT to be ignored by the shell. .PP On System V-based Unix systems, .I rc will not allow you to trap .Cr SIGCLD . .SH "BUILTIN COMMANDS" Builtin commands execute in the context of the shell, but otherwise behave exactly like other commands. .TP \&\fB.\fR [\fB\-i\fR] \fIfile \fR[\fIarg ...\fR] Reads .I file as input to .IR rc and executes its contents. With a .Cr \-i flag, input is interactive. Thus from within a shell script, .Ds 1i .Cr ". \-i /dev/tty .De .TP \& does the ``right'' thing. .TP .B break Breaks from the innermost .Cr for or .Cr while , as in C. It is an error to invoke .B break outside of a loop. (Note that there is no .B break keyword between commands in .Cr switch statements, unlike C.) .TP \fBbuiltin \fIcommand \fR[\fIarg ...\fR] Executes the command ignoring any function definition of the same name. This command is present to allow functions with the same names as builtins to use the underlying builtin or binary. .TP \fBcd \fR[\fIdirectory\fR] Changes the current directory to .IR directory . The variable .Cr $cdpath is searched for possible locations of .IR directory , analogous to the searching of .Cr $path for executable files. With no argument, .B cd changes the current directory to .Cr "$home" . .TP \fBecho \fR[\fIarg ...\fR] Prints its arguments to standard output, terminated by a newline. Arguments are separated by spaces. If the first argument is .Cr "\-n" , no final newline is printed. If the first argument is .\" The \| puts in a little space to separate the dashes in troff. .Cr "\-\|\-" , then any subsequent arguments will not be interpreted as options to .IR echo . .TP \fBeval \fR[\fIlist\fR] Concatenates the elements of .I list with spaces and feeds the resulting string to .I rc for re-scanning. This is the only time input is rescanned in .IR rc . .TP \fBexec \fR[\fIarg ...\fR] Replaces .I rc with the given command. If the exec contains only redirections, then these redirections apply to the current shell and the shell does not exit. For example, .Ds 1i .Cr "exec >[2] err.out .De .TP \& places further output to standard error in the file .IR err.out . .TP \fBexit \fR[\fIstatus\fR] Cause the current shell to exit with the given exit .IR status . If no argument is given, the current value of .Cr $status is used. .TP \fBlimit \fR[\fB\-h\fR] [\fIresource \fR[\fIvalue\fR]] Similar to the .IR csh (1) .B limit builtin, this command operates upon the BSD-style limits of a process. The .Cr \-h flag displays/alters the hard limits. The resources which can be shown or altered are .BR cputime , .BR filesize , .BR datasize , .BR stacksize , .B coredumpsize and .BR memoryuse . For example: .Ds 1i .Cr "limit coredumpsize 0 .De .TP \& disables core dumps. .TP \fBreturn \fR[\fIn\fR] Returns from the current function, with status .IR n , where .IR n is a single value or a list of possible exit statuses. Thus it is legal to have .Ds .Cr "return (sigpipe 1 2 3) .De .PP (This is commonly used to allow a function to return with the exit status of a previously executed pipeline of commands.) If .IR n is omitted, then .Cr $status is left unchanged. It is an error to invoke .B return when not inside a function. .TP \fBshift \fR[\fIn\fR] Deletes .I n elements from the beginning of .Cr $* and shifts the other elements down by .IR n . .I n defaults to 1. (Note that .Cr $0 is not affected by .BR shift .) .TP \fBumask \fR[\fImask\fR] Sets the current umask (see .IR umask (2)) to the octal .IR mask . If no argument is present, the current mask value is printed. .TP \fBwait \fR[\fIpid\fR] Waits for the specified .IR pid , which must have been started by .IR rc . If no .I pid is specified, .I rc waits for any child process to exit. .TP \fBwhatis \fR[\fB\-s\fR] \fR[\fIname ...\fR] Prints a definition of the named objects. For variables, their values are printed; for functions, their definitions are; and for executable files, path names are printed. Without arguments, .B whatis prints the values of all shell variables and functions. With a .Cr \-s argument, .B whatis also prints out a list of available signals and their handlers (if any). Note that .B whatis output is suitable for input to .IR rc ; by saving the output of .B whatis in a file, it should be possible to recreate the state of .I rc by sourcing this file with a .Cr . command. Another note: .Cr "whatis -s > file cannot be used to store the state of .IR rc 's signal handlers in a file, because builtins with redirections are run in a subshell, and .I rc always restores signal handlers to their default value after a .Cr fork() . .SH GRAMMAR Here is .IR rc 's grammar, edited to remove semantic actions. .Ds .ft \*(Cf %term ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN %term OROR PIPE REDIR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD HUH X %left WHILE ')' ELSE %left ANDAND OROR '\n' %left BANG SUBSHELL %left PIPE %right '$' %left SUB X %start rc X %% X rc: line end X | error end X end: END /* EOF */ | '\n' X cmdsa: cmd ';' | cmd '&' X line: cmd | cmdsa line X body: cmd | cmdsan body X cmdsan: cmdsa | cmd '\n' X brace: '{' body '}' X paren: '(' body ')' X assign: first '=' word X epilog: /* empty */ | redir epilog X redir: DUP | REDIR word X case: CASE words ';' | CASE words '\n' X cbody: cmd | case cbody | cmdsan cbody X iftail: cmd %prec ELSE X | brace ELSE optnl cmd X cmd : /* empty */ %prec WHILE X | simple X | brace epilog X | IF paren optnl iftail X | FOR '(' word IN words ')' optnl cmd X | FOR '(' word ')' optnl cmd X | WHILE paren optnl cmd X | SWITCH '(' word ')' optnl '{' cbody '}' X | TWIDDLE optcaret word words X | cmd ANDAND optnl cmd X | cmd OROR optnl cmd X | cmd PIPE optnl cmd X | redir cmd %prec BANG X | assign cmd %prec BANG X | BANG optcaret cmd X | SUBSHELL optcaret cmd X | FN words brace X | FN words X optcaret: /* empty */ | '^' X simple: first | simple word | simple redir X first: comword | first '^' sword X sword: comword | keyword X word: sword | word '^' sword X comword: '$' sword X | '$' sword SUB words ')' X | COUNT sword X | FLAT sword X | '`' sword X | '`' brace X | BACKBACK word brace | BACKBACK word sword X | '(' words ')' X | REDIR brace X | WORD X keyword: FOR | IN | WHILE | IF | SWITCH X | FN | ELSE | CASE | TWIDDLE | BANG | SUBSHELL X words: /* empty */ | words word X optnl: /* empty */ | optnl '\n' .ft R .De .SH FILES .Cr $HOME/.rcrc , .Cr /tmp/rc* , .Cr /dev/null .SH CREDITS .I rc was written by Byron Rakitzis, with valuable help from Paul Haahr, Hugh Redelmeier and David Sanderson. The design of this shell has been copied from the .I rc that Tom Duff wrote at Bell Labs. .SH BUGS On systems that support .Cr /dev/fd , .Cr <{foo} style redirection is implemented that way. However, on other systems it is implemented with named pipes, and it is sometimes possible to foil .I rc into removing the FIFO it places in .Cr /tmp prematurely, or it is even possible to cause .I rc to hang. .PP The functionality of .B shift should be available for variables other than .Cr "$*" . .PP .B echo is built in only for performance reasons, which is a bad idea. .PP There should be a way to avoid exporting a variable. .PP The .Cr $^var notation for flattening should allow for using an arbitrary separating character, not just space. .PP Bug reports should be mailed to .Cr "byron@archone.tamu.edu" . .SH INCOMPATIBILITIES Here is a list of features which distinguish this incarnation of .I rc from the one described in the Bell Labs manual pages: .PP The treatment of .Cr if - else is different in the v10 .IR rc : that version uses an .Cr "if not clause which gets executed if the preceding .Cr if test does not succeed. .PP Backquotes are slightly different in v10 .IR rc : a backquote must always be followed by a left-brace. This restriction is not present for single-word commands in this .IR rc . .PP The following are all new with this version of .IR rc : The .Cr \-n option, the list flattening operator, here strings (they facilitate exporting of functions with here documents into the environment), the .B return and .B break keywords, the .B echo builtin, the support for the GNU .IR readline (3) library and the support for the .Cr prompt function. This .I rc also sets .Cr $0 to the name of a function being executed/file being sourced. .SH "SEE ALSO" ``rc \(em A Shell for Plan 9 and UNIX'', Unix Research System, 10th Edition, vol. 2. (Saunders College Publishing) (This paper is also distributed with this rc in PostScript form.) .PP .IR history (1) SHAR_EOF chmod 0644 rc.1 || echo 'restore of rc.1 failed' Wc_c="`wc -c < 'rc.1'`" test 35457 -eq "$Wc_c" || echo 'rc.1: original size 35457, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= rc.h ============== if test -f 'rc.h' -a X"$1" != X"-c"; then echo 'x - skipping rc.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting rc.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'rc.h' && #include "config.h" #include "stddef.h" #include "stdlib.h" #include "string.h" #include "unistd.h" X X /* braindamaged IBM header files #define true and false */ #undef FALSE #undef TRUE X enum bool { FALSE, TRUE }; X typedef enum bool boolean; typedef struct Rq Rq; typedef struct Block Block; typedef struct List List; typedef struct Fq Fq; X struct List { X char *w; X char *m; X List *n; }; X #include "node.h" X extern char *prompt, *prompt2; extern Rq *redirq; extern boolean dashdee, dashee, dashvee, dashex, dashell, dasheye, dashen, interactive; extern int rc_pid; extern int lineno; extern Fq *fifoq; X #define arraysize(a) ((int)(sizeof(a)/sizeof(*a))) SHAR_EOF chmod 0644 rc.h || echo 'restore of rc.h failed' Wc_c="`wc -c < 'rc.h'`" test 654 -eq "$Wc_c" || echo 'rc.h: original size 654, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= redir.c ============== if test -f 'redir.c' -a X"$1" != X"-c"; then echo 'x - skipping redir.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting redir.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'redir.c' && /* redir.c: code for opening files and piping heredocs after fork but before exec. */ X #include "rc.h" #include "lex.h" #include "glom.h" #include "glob.h" #include "open.h" #include "exec.h" #include "utils.h" #include "redir.h" #include "hash.h" X /* X Walk the redirection queue, and open files and dup2 to them. Also, here-documents are treated X here by dumping them down a pipe. (this should make here-documents fast on systems with lots X of memory which do pipes right. Under sh, a file is copied to /tmp, and then read out of /tmp X again. I'm interested in knowing how much faster, say, shar runs when unpacking when invoked X with rc instead of sh. On my sun4/280, it runs in about 60-75% of the time of sh for unpacking X the rc source distribution.) */ X void doredirs() { X List *fname; X int fd, p[2]; X Rq *r; X X for (r = redirq; r != NULL; r = r->n) { X switch(r->r->type) { X default: X fprint(2,"%d: bad node in doredirs\n", r->r->type); X exit(1); X /* NOTREACHED */ X case rREDIR: X if (r->r->u[0].i == HERESTRING) { X fname = flatten(glom(r->r->u[2].p)); /* fname is really a string */ X if (fname == NULL) { X close(r->r->u[1].i); /* feature? */ X break; X } X if (pipe(p) < 0) { X uerror("pipe"); X exit(1); X } X switch (fork()) { X case -1: X uerror("fork"); X exit(1); X /* NOTREACHED */ X case 0: /* child writes to pipe */ X setsigdefaults(); X close(p[0]); X writeall(p[1], fname->w, strlen(fname->w)); X exit(0); X /* NOTREACHED */ X default: X close(p[1]); X if (dup2(p[0], r->r->u[1].i) < 0) { X uerror("dup"); SHAR_EOF true || echo 'restore of redir.c failed' fi echo 'End of part 5' echo 'File redir.c is continued in part 6' echo 6 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.