├── LICENSE ├── Make.Darwin ├── Make.Linux ├── Makefile ├── README.md ├── code.c ├── exec.c ├── exec.h ├── fns.h ├── getflags.c ├── getflags.h ├── glob.c ├── havefork.c ├── haventfork.c ├── here.c ├── history.c ├── io.c ├── io.h ├── lex.c ├── mkfile ├── pcmd.c ├── pfnc.c ├── plan9.c ├── prompt-null.c ├── prompt-readline.c ├── rc.1 ├── rc.h ├── rc.md ├── rcmain.unix ├── simple.c ├── subr.c ├── syn.y ├── trap.c ├── tree.c ├── unix.c ├── unix.h ├── var.c ├── win32.c ├── x.tab.h ├── y.tab.c └── y.tab.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2000-2009 Lucent Technologies. All Rights Reserved. 2 | Portions Copyright © 2001-2008 Russ Cox 3 | Portions Copyright © 2008-2009 Google Inc. 4 | Portions Copyright © 2008-2009 Federico G. Benavento 5 | 6 | =================================================================== 7 | 8 | The bulk of this software is derived from Plan 9 and is thus distributed 9 | under the Lucent Public License, Version 1.02, reproduced below. 10 | 11 | There are a few exceptions: libutf, libfmt, and libregexp are distributed 12 | under simpler BSD-like boilerplates. See the LICENSE files in those 13 | directories. There are other exceptions, also marked with LICENSE files 14 | in their directories or marked at the top of the file. 15 | 16 | The bitmap fonts in the font/luc, font/lucm, font/lucsans, and font/pelm 17 | directory are copyright B&H Inc. and distributed under more restricted 18 | terms under agreement with B&H. See the NOTICE file in those directories. 19 | 20 | =================================================================== 21 | 22 | Lucent Public License Version 1.02 23 | 24 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC 25 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE 26 | PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 27 | 28 | 1. DEFINITIONS 29 | 30 | "Contribution" means: 31 | 32 | a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original 33 | Program, and 34 | b. in the case of each Contributor, 35 | 36 | i. changes to the Program, and 37 | ii. additions to the Program; 38 | 39 | where such changes and/or additions to the Program were added to the 40 | Program by such Contributor itself or anyone acting on such 41 | Contributor's behalf, and the Contributor explicitly consents, in 42 | accordance with Section 3C, to characterization of the changes and/or 43 | additions as Contributions. 44 | 45 | "Contributor" means LUCENT and any other entity that has Contributed a 46 | Contribution to the Program. 47 | 48 | "Distributor" means a Recipient that distributes the Program, 49 | modifications to the Program, or any part thereof. 50 | 51 | "Licensed Patents" mean patent claims licensable by a Contributor 52 | which are necessarily infringed by the use or sale of its Contribution 53 | alone or when combined with the Program. 54 | 55 | "Original Program" means the original version of the software 56 | accompanying this Agreement as released by LUCENT, including source 57 | code, object code and documentation, if any. 58 | 59 | "Program" means the Original Program and Contributions or any part 60 | thereof 61 | 62 | "Recipient" means anyone who receives the Program under this 63 | Agreement, including all Contributors. 64 | 65 | 2. GRANT OF RIGHTS 66 | 67 | a. Subject to the terms of this Agreement, each Contributor hereby 68 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 69 | license to reproduce, prepare derivative works of, publicly display, 70 | publicly perform, distribute and sublicense the Contribution of such 71 | Contributor, if any, and such derivative works, in source code and 72 | object code form. 73 | 74 | b. Subject to the terms of this Agreement, each Contributor hereby 75 | grants Recipient a non-exclusive, worldwide, royalty-free patent 76 | license under Licensed Patents to make, use, sell, offer to sell, 77 | import and otherwise transfer the Contribution of such Contributor, if 78 | any, in source code and object code form. The patent license granted 79 | by a Contributor shall also apply to the combination of the 80 | Contribution of that Contributor and the Program if, at the time the 81 | Contribution is added by the Contributor, such addition of the 82 | Contribution causes such combination to be covered by the Licensed 83 | Patents. The patent license granted by a Contributor shall not apply 84 | to (i) any other combinations which include the Contribution, nor to 85 | (ii) Contributions of other Contributors. No hardware per se is 86 | licensed hereunder. 87 | 88 | c. Recipient understands that although each Contributor grants the 89 | licenses to its Contributions set forth herein, no assurances are 90 | provided by any Contributor that the Program does not infringe the 91 | patent or other intellectual property rights of any other entity. Each 92 | Contributor disclaims any liability to Recipient for claims brought by 93 | any other entity based on infringement of intellectual property rights 94 | or otherwise. As a condition to exercising the rights and licenses 95 | granted hereunder, each Recipient hereby assumes sole responsibility 96 | to secure any other intellectual property rights needed, if any. For 97 | example, if a third party patent license is required to allow 98 | Recipient to distribute the Program, it is Recipient's responsibility 99 | to acquire that license before distributing the Program. 100 | 101 | d. Each Contributor represents that to its knowledge it has sufficient 102 | copyright rights in its Contribution, if any, to grant the copyright 103 | license set forth in this Agreement. 104 | 105 | 3. REQUIREMENTS 106 | 107 | A. Distributor may choose to distribute the Program in any form under 108 | this Agreement or under its own license agreement, provided that: 109 | 110 | a. it complies with the terms and conditions of this Agreement; 111 | 112 | b. if the Program is distributed in source code or other tangible 113 | form, a copy of this Agreement or Distributor's own license agreement 114 | is included with each copy of the Program; and 115 | 116 | c. if distributed under Distributor's own license agreement, such 117 | license agreement: 118 | 119 | i. effectively disclaims on behalf of all Contributors all warranties 120 | and conditions, express and implied, including warranties or 121 | conditions of title and non-infringement, and implied warranties or 122 | conditions of merchantability and fitness for a particular purpose; 123 | ii. effectively excludes on behalf of all Contributors all liability 124 | for damages, including direct, indirect, special, incidental and 125 | consequential damages, such as lost profits; and 126 | iii. states that any provisions which differ from this Agreement are 127 | offered by that Contributor alone and not by any other party. 128 | 129 | B. Each Distributor must include the following in a conspicuous 130 | location in the Program: 131 | 132 | Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights 133 | Reserved. 134 | 135 | C. In addition, each Contributor must identify itself as the 136 | originator of its Contribution in a manner that reasonably allows 137 | subsequent Recipients to identify the originator of the Contribution. 138 | Also, each Contributor must agree that the additions and/or changes 139 | are intended to be a Contribution. Once a Contribution is contributed, 140 | it may not thereafter be revoked. 141 | 142 | 4. COMMERCIAL DISTRIBUTION 143 | 144 | Commercial distributors of software may accept certain 145 | responsibilities with respect to end users, business partners and the 146 | like. While this license is intended to facilitate the commercial use 147 | of the Program, the Distributor who includes the Program in a 148 | commercial product offering should do so in a manner which does not 149 | create potential liability for Contributors. Therefore, if a 150 | Distributor includes the Program in a commercial product offering, 151 | such Distributor ("Commercial Distributor") hereby agrees to defend 152 | and indemnify every Contributor ("Indemnified Contributor") against 153 | any losses, damages and costs (collectively"Losses") arising from 154 | claims, lawsuits and other legal actions brought by a third party 155 | against the Indemnified Contributor to the extent caused by the acts 156 | or omissions of such Commercial Distributor in connection with its 157 | distribution of the Program in a commercial product offering. The 158 | obligations in this section do not apply to any claims or Losses 159 | relating to any actual or alleged intellectual property infringement. 160 | In order to qualify, an Indemnified Contributor must: a) promptly 161 | notify the Commercial Distributor in writing of such claim, and b) 162 | allow the Commercial Distributor to control, and cooperate with the 163 | Commercial Distributor in, the defense and any related settlement 164 | negotiations. The Indemnified Contributor may participate in any such 165 | claim at its own expense. 166 | 167 | For example, a Distributor might include the Program in a commercial 168 | product offering, Product X. That Distributor is then a Commercial 169 | Distributor. If that Commercial Distributor then makes performance 170 | claims, or offers warranties related to Product X, those performance 171 | claims and warranties are such Commercial Distributor's responsibility 172 | alone. Under this section, the Commercial Distributor would have to 173 | defend claims against the Contributors related to those performance 174 | claims and warranties, and if a court requires any Contributor to pay 175 | any damages as a result, the Commercial Distributor must pay those 176 | damages. 177 | 178 | 5. NO WARRANTY 179 | 180 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 181 | PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 182 | KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY 183 | WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 184 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 185 | responsible for determining the appropriateness of using and 186 | distributing the Program and assumes all risks associated with its 187 | exercise of rights under this Agreement, including but not limited to 188 | the risks and costs of program errors, compliance with applicable 189 | laws, damage to or loss of data, programs or equipment, and 190 | unavailability or interruption of operations. 191 | 192 | 6. DISCLAIMER OF LIABILITY 193 | 194 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR 195 | ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 196 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 197 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 198 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 199 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 200 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 201 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 202 | 203 | 7. EXPORT CONTROL 204 | 205 | Recipient agrees that Recipient alone is responsible for compliance 206 | with the United States export administration regulations (and the 207 | export control laws and regulation of any other countries). 208 | 209 | 8. GENERAL 210 | 211 | If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further 214 | action by the parties hereto, such provision shall be reformed to the 215 | minimum extent necessary to make such provision valid and enforceable. 216 | 217 | If Recipient institutes patent litigation against a Contributor with 218 | respect to a patent applicable to software (including a cross-claim or 219 | counterclaim in a lawsuit), then any patent licenses granted by that 220 | Contributor to such Recipient under this Agreement shall terminate as 221 | of the date such litigation is filed. In addition, if Recipient 222 | institutes patent litigation against any entity (including a 223 | cross-claim or counterclaim in a lawsuit) alleging that the Program 224 | itself (excluding combinations of the Program with other software or 225 | hardware) infringes such Recipient's patent(s), then such Recipient's 226 | rights granted under Section 2(b) shall terminate as of the date such 227 | litigation is filed. 228 | 229 | All Recipient's rights under this Agreement shall terminate if it 230 | fails to comply with any of the material terms or conditions of this 231 | Agreement and does not cure such failure in a reasonable period of 232 | time after becoming aware of such noncompliance. If all Recipient's 233 | rights under this Agreement terminate, Recipient agrees to cease use 234 | and distribution of the Program as soon as reasonably practicable. 235 | However, Recipient's obligations under this Agreement and any licenses 236 | granted by Recipient relating to the Program shall continue and 237 | survive. 238 | 239 | LUCENT may publish new versions (including revisions) of this 240 | Agreement from time to time. Each new version of the Agreement will be 241 | given a distinguishing version number. The Program (including 242 | Contributions) may always be distributed subject to the version of the 243 | Agreement under which it was received. In addition, after a new 244 | version of the Agreement is published, Contributor may elect to 245 | distribute the Program (including its Contributions) under the new 246 | version. No one other than LUCENT has the right to modify this 247 | Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, 248 | Recipient receives no rights or licenses to the intellectual property 249 | of any Contributor under this Agreement, whether expressly, by 250 | implication, estoppel or otherwise. All rights in the Program not 251 | expressly granted under this Agreement are reserved. 252 | 253 | This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No 255 | party to this Agreement will bring a legal action under this Agreement 256 | more than one year after the cause of action arose. Each party waives 257 | its rights to a jury trial in any resulting litigation. 258 | 259 | -------------------------------------------------------------------------------- /Make.Darwin: -------------------------------------------------------------------------------- 1 | O=o 2 | YACC=yacc -d 3 | PREFIX=/usr/local 4 | WARNINGS=-Wall -Wmost -Wextra -Wno-missing-braces -Wno-logical-op-parentheses -Wno-missing-field-initializers -Wno-comment -Wno-sign-compare -Wno-unused-parameter -Werror 5 | CFLAGS=-g $(WARNINGS) -DPREFIX=\"$(PREFIX)\" 6 | LFLAGS=-lreadline 7 | PROMPT=readline 8 | -------------------------------------------------------------------------------- /Make.Linux: -------------------------------------------------------------------------------- 1 | O=o 2 | YACC=yacc -d 3 | PREFIX=/usr/local 4 | WARNINGS=-Wall -Wextra -Wno-parentheses -Wno-missing-braces -Wno-missing-field-initializers -Wno-comment -Wno-sign-compare -Wno-unused-parameter -Wno-implicit-fallthrough #-Werror 5 | CFLAGS=-g $(WARNINGS) -DPREFIX=\"$(PREFIX)\" -D_XOPEN_SOURCE=500 6 | LFLAGS=-lreadline 7 | PROMPT=readline 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include Make.$(shell uname) 2 | 3 | TARG=rc 4 | 5 | OFILES=\ 6 | code.$O\ 7 | exec.$O\ 8 | getflags.$O\ 9 | glob.$O\ 10 | here.$O\ 11 | io.$O\ 12 | lex.$O\ 13 | pcmd.$O\ 14 | pfnc.$O\ 15 | simple.$O\ 16 | subr.$O\ 17 | trap.$O\ 18 | tree.$O\ 19 | var.$O\ 20 | y.tab.$O\ 21 | unix.$O\ 22 | havefork.$O\ 23 | prompt-$(PROMPT).$O\ 24 | 25 | HFILES=\ 26 | rc.h\ 27 | x.tab.h\ 28 | io.h\ 29 | exec.h\ 30 | fns.h\ 31 | 32 | YFILES=syn.y 33 | 34 | all: $(TARG) 35 | 36 | $(TARG): $(OFILES) 37 | $(CC) $(ARCHS) -o $(TARG) $(OFILES) $(LFLAGS) 38 | 39 | %.$O: %.c $(HFILES) 40 | $(CC) $(ARCHS) $(CFLAGS) -c $*.c 41 | 42 | y.tab.h y.tab.c: $(YFILES) 43 | $(YACC) $(YFLAGS) $(YFILES) 44 | 45 | x.tab.h: y.tab.h 46 | cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h 47 | 48 | install: all rcmain.unix rc.1 49 | install -m 755 $(TARG) $(PREFIX)/bin 50 | install -m 644 rcmain.unix $(PREFIX)/lib/rcmain 51 | install -m 644 rc.1 $(PREFIX)/share/man/man1 52 | 53 | %: %.c $(HFILES) 54 | $(CC) $(ARCHS) -o $@ $< $(LFLAGS) 55 | 56 | clean: 57 | rm -f $(TARG) *.$O $(CLEANFILES) 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rc — The Plan 9 Shell # 2 | 3 | Standalone port of Rc shell written by Tom Duff with edit, history and completion support. 4 | 5 | ## Building ## 6 | 7 | In a terminal run: 8 | ``` 9 | cd rc 10 | make 11 | make install 12 | 13 | ``` 14 | 15 | ## Config ## 16 | 17 | ### Profile ### 18 | 19 | ``` 20 | /Users/$USER/.rcrc 21 | ``` 22 | 23 | ### Completion ### 24 | 25 | In _.rcrc__ define a _fn_complete_ funciton. 26 | 27 | ``` 28 | fn err { echo $* >[1=2] } 29 | 30 | fn complete_history { 31 | history | sort -u | fzf -0 --header 'History' 32 | } 33 | 34 | fn complete_arg { 35 | } 36 | 37 | fn complete_command { 38 | find $path -maxdepth 1 >[2]/dev/null | sed 's/\/.*\///' | sort -u | 39 | fzf -q '^'^$1 -0 -1 --header 'Commands' --prompt $prompt(1) 40 | } 41 | 42 | fn complete { 43 | #err complete: $#* "$"*" 44 | if(~ $#* 0) 45 | exit 46 | 47 | latsp=no 48 | if(~ $"* *^' ') 49 | lastsp=yes 50 | 51 | *=`{echo $*} 52 | switch($#*){ 53 | case 0 54 | complete_history 55 | case 1 56 | if(~ $lastsp yes) 57 | complete_arg $1 58 | if not 59 | complete_command $1 60 | case * 61 | exit '' 62 | } 63 | } 64 | ``` 65 | 66 | ## Documentation ## 67 | * [Rc — The Plan 9 Shell](http://9p.io/sys/doc/rc.html) 68 | * [*rc(1)*](./rc.md) 69 | -------------------------------------------------------------------------------- /code.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "io.h" 3 | #include "exec.h" 4 | #include "fns.h" 5 | #include "getflags.h" 6 | #define c0 t->child[0] 7 | #define c1 t->child[1] 8 | #define c2 t->child[2] 9 | int codep, ncode; 10 | #define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f = (x), codep++) 11 | #define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i = (x), codep++) 12 | #define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s = (x), codep++) 13 | void stuffdot(int); 14 | char *fnstr(tree*); 15 | void outcode(tree*, int); 16 | void codeswitch(tree*, int); 17 | int iscase(tree*); 18 | code *codecopy(code*); 19 | void codefree(code*); 20 | 21 | int 22 | morecode(void) 23 | { 24 | ncode+=100; 25 | codebuf = (code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]); 26 | if(codebuf==0) 27 | panic("Can't realloc %d bytes in morecode!", 28 | ncode*sizeof codebuf[0]); 29 | memset(codebuf+ncode-100, 0, 100*sizeof codebuf[0]); 30 | return 0; 31 | } 32 | 33 | void 34 | stuffdot(int a) 35 | { 36 | if(a<0 || codep<=a) 37 | panic("Bad address %d in stuffdot", a); 38 | codebuf[a].i = codep; 39 | } 40 | 41 | int 42 | compile(tree *t) 43 | { 44 | ncode = 100; 45 | codebuf = (code *)emalloc(ncode*sizeof codebuf[0]); 46 | codep = 0; 47 | emiti(0); /* reference count */ 48 | outcode(t, flag['e']?1:0); 49 | if(nerror){ 50 | efree((char *)codebuf); 51 | return 0; 52 | } 53 | readhere(); 54 | emitf(Xreturn); 55 | emitf(0); 56 | return 1; 57 | } 58 | 59 | void 60 | cleanhere(char *f) 61 | { 62 | emitf(Xdelhere); 63 | emits(strdup(f)); 64 | } 65 | 66 | char* 67 | fnstr(tree *t) 68 | { 69 | io *f = openstr(); 70 | char *v; 71 | extern char nl; 72 | char svnl = nl; 73 | nl=';'; 74 | pfmt(f, "%t", t); 75 | nl = svnl; 76 | v = f->strp; 77 | f->strp = 0; 78 | closeio(f); 79 | return v; 80 | } 81 | 82 | void 83 | outcode(tree *t, int eflag) 84 | { 85 | int p, q; 86 | tree *tt; 87 | if(t==0) 88 | return; 89 | if(t->type!=NOT && t->type!=';') 90 | runq->iflast = 0; 91 | switch(t->type){ 92 | default: 93 | pfmt(err, "bad type %d in outcode\n", t->type); 94 | break; 95 | case '$': 96 | emitf(Xmark); 97 | outcode(c0, eflag); 98 | emitf(Xdol); 99 | break; 100 | case '"': 101 | emitf(Xmark); 102 | outcode(c0, eflag); 103 | emitf(Xqdol); 104 | break; 105 | case SUB: 106 | emitf(Xmark); 107 | outcode(c0, eflag); 108 | emitf(Xmark); 109 | outcode(c1, eflag); 110 | emitf(Xsub); 111 | break; 112 | case '&': 113 | emitf(Xasync); 114 | if(havefork){ 115 | p = emiti(0); 116 | outcode(c0, eflag); 117 | emitf(Xexit); 118 | stuffdot(p); 119 | } else 120 | emits(fnstr(c0)); 121 | break; 122 | case ';': 123 | outcode(c0, eflag); 124 | outcode(c1, eflag); 125 | break; 126 | case '^': 127 | emitf(Xmark); 128 | outcode(c1, eflag); 129 | emitf(Xmark); 130 | outcode(c0, eflag); 131 | emitf(Xconc); 132 | break; 133 | case '`': 134 | emitf(Xbackq); 135 | if(havefork){ 136 | p = emiti(0); 137 | outcode(c0, 0); 138 | emitf(Xexit); 139 | stuffdot(p); 140 | } else 141 | emits(fnstr(c0)); 142 | break; 143 | case ANDAND: 144 | outcode(c0, 0); 145 | emitf(Xtrue); 146 | p = emiti(0); 147 | outcode(c1, eflag); 148 | stuffdot(p); 149 | break; 150 | case ARGLIST: 151 | outcode(c1, eflag); 152 | outcode(c0, eflag); 153 | break; 154 | case BANG: 155 | outcode(c0, eflag); 156 | emitf(Xbang); 157 | break; 158 | case PCMD: 159 | case BRACE: 160 | outcode(c0, eflag); 161 | break; 162 | case COUNT: 163 | emitf(Xmark); 164 | outcode(c0, eflag); 165 | emitf(Xcount); 166 | break; 167 | case FN: 168 | emitf(Xmark); 169 | outcode(c0, eflag); 170 | if(c1){ 171 | emitf(Xfn); 172 | p = emiti(0); 173 | emits(fnstr(c1)); 174 | outcode(c1, eflag); 175 | emitf(Xunlocal); /* get rid of $* */ 176 | emitf(Xreturn); 177 | stuffdot(p); 178 | } 179 | else 180 | emitf(Xdelfn); 181 | break; 182 | case IF: 183 | outcode(c0, 0); 184 | emitf(Xif); 185 | p = emiti(0); 186 | outcode(c1, eflag); 187 | emitf(Xwastrue); 188 | stuffdot(p); 189 | break; 190 | case NOT: 191 | if(!runq->iflast) 192 | yyerror("`if not' does not follow `if(...)'"); 193 | emitf(Xifnot); 194 | p = emiti(0); 195 | outcode(c0, eflag); 196 | stuffdot(p); 197 | break; 198 | case OROR: 199 | outcode(c0, 0); 200 | emitf(Xfalse); 201 | p = emiti(0); 202 | outcode(c1, eflag); 203 | stuffdot(p); 204 | break; 205 | case PAREN: 206 | outcode(c0, eflag); 207 | break; 208 | case SIMPLE: 209 | emitf(Xmark); 210 | outcode(c0, eflag); 211 | emitf(Xsimple); 212 | if(eflag) 213 | emitf(Xeflag); 214 | break; 215 | case SUBSHELL: 216 | emitf(Xsubshell); 217 | if(havefork){ 218 | p = emiti(0); 219 | outcode(c0, eflag); 220 | emitf(Xexit); 221 | stuffdot(p); 222 | } else 223 | emits(fnstr(c0)); 224 | if(eflag) 225 | emitf(Xeflag); 226 | break; 227 | case SWITCH: 228 | codeswitch(t, eflag); 229 | break; 230 | case TWIDDLE: 231 | emitf(Xmark); 232 | outcode(c1, eflag); 233 | emitf(Xmark); 234 | outcode(c0, eflag); 235 | emitf(Xmatch); 236 | if(eflag) 237 | emitf(Xeflag); 238 | break; 239 | case WHILE: 240 | q = codep; 241 | outcode(c0, 0); 242 | if(q==codep) 243 | emitf(Xsettrue); /* empty condition == while(true) */ 244 | emitf(Xtrue); 245 | p = emiti(0); 246 | outcode(c1, eflag); 247 | emitf(Xjump); 248 | emiti(q); 249 | stuffdot(p); 250 | break; 251 | case WORDS: 252 | outcode(c1, eflag); 253 | outcode(c0, eflag); 254 | break; 255 | case FOR: 256 | emitf(Xmark); 257 | if(c1){ 258 | outcode(c1, eflag); 259 | emitf(Xglob); 260 | } 261 | else{ 262 | emitf(Xmark); 263 | emitf(Xword); 264 | emits(strdup("*")); 265 | emitf(Xdol); 266 | } 267 | emitf(Xmark); /* dummy value for Xlocal */ 268 | emitf(Xmark); 269 | outcode(c0, eflag); 270 | emitf(Xlocal); 271 | p = emitf(Xfor); 272 | q = emiti(0); 273 | outcode(c2, eflag); 274 | emitf(Xjump); 275 | emiti(p); 276 | stuffdot(q); 277 | emitf(Xunlocal); 278 | break; 279 | case WORD: 280 | emitf(Xword); 281 | emits(strdup(t->str)); 282 | break; 283 | case DUP: 284 | if(t->rtype==DUPFD){ 285 | emitf(Xdup); 286 | emiti(t->fd0); 287 | emiti(t->fd1); 288 | } 289 | else{ 290 | emitf(Xclose); 291 | emiti(t->fd0); 292 | } 293 | outcode(c1, eflag); 294 | emitf(Xpopredir); 295 | break; 296 | case PIPEFD: 297 | emitf(Xpipefd); 298 | emiti(t->rtype); 299 | if(havefork){ 300 | p = emiti(0); 301 | outcode(c0, eflag); 302 | emitf(Xexit); 303 | stuffdot(p); 304 | } else { 305 | emits(fnstr(c0)); 306 | } 307 | break; 308 | case REDIR: 309 | emitf(Xmark); 310 | outcode(c0, eflag); 311 | emitf(Xglob); 312 | switch(t->rtype){ 313 | case APPEND: 314 | emitf(Xappend); 315 | break; 316 | case WRITE: 317 | emitf(Xwrite); 318 | break; 319 | case READ: 320 | case HERE: 321 | emitf(Xread); 322 | break; 323 | case RDWR: 324 | emitf(Xrdwr); 325 | break; 326 | } 327 | emiti(t->fd0); 328 | outcode(c1, eflag); 329 | emitf(Xpopredir); 330 | break; 331 | case '=': 332 | tt = t; 333 | for(;t && t->type=='=';t = c2); 334 | if(t){ 335 | for(t = tt;t->type=='=';t = c2){ 336 | emitf(Xmark); 337 | outcode(c1, eflag); 338 | emitf(Xmark); 339 | outcode(c0, eflag); 340 | emitf(Xlocal); 341 | } 342 | outcode(t, eflag); 343 | for(t = tt; t->type=='='; t = c2) 344 | emitf(Xunlocal); 345 | } 346 | else{ 347 | for(t = tt;t;t = c2){ 348 | emitf(Xmark); 349 | outcode(c1, eflag); 350 | emitf(Xmark); 351 | outcode(c0, eflag); 352 | emitf(Xassign); 353 | } 354 | } 355 | t = tt; /* so tests below will work */ 356 | break; 357 | case PIPE: 358 | emitf(Xpipe); 359 | emiti(t->fd0); 360 | emiti(t->fd1); 361 | if(havefork){ 362 | p = emiti(0); 363 | q = emiti(0); 364 | outcode(c0, eflag); 365 | emitf(Xexit); 366 | stuffdot(p); 367 | } else { 368 | emits(fnstr(c0)); 369 | q = emiti(0); 370 | } 371 | outcode(c1, eflag); 372 | emitf(Xreturn); 373 | stuffdot(q); 374 | emitf(Xpipewait); 375 | break; 376 | } 377 | if(t->type!=NOT && t->type!=';') 378 | runq->iflast = t->type==IF; 379 | else if(c0) runq->iflast = c0->type==IF; 380 | } 381 | /* 382 | * switch code looks like this: 383 | * Xmark 384 | * (get switch value) 385 | * Xjump 1f 386 | * out: Xjump leave 387 | * 1: Xmark 388 | * (get case values) 389 | * Xcase 1f 390 | * (commands) 391 | * Xjump out 392 | * 1: Xmark 393 | * (get case values) 394 | * Xcase 1f 395 | * (commands) 396 | * Xjump out 397 | * 1: 398 | * leave: 399 | * Xpopm 400 | */ 401 | 402 | void 403 | codeswitch(tree *t, int eflag) 404 | { 405 | int leave; /* patch jump address to leave switch */ 406 | int out; /* jump here to leave switch */ 407 | int nextcase; /* patch jump address to next case */ 408 | tree *tt; 409 | if(c1->child[0]==nil 410 | || c1->child[0]->type!=';' 411 | || !iscase(c1->child[0]->child[0])){ 412 | yyerror("case missing in switch"); 413 | return; 414 | } 415 | emitf(Xmark); 416 | outcode(c0, eflag); 417 | emitf(Xjump); 418 | nextcase = emiti(0); 419 | out = emitf(Xjump); 420 | leave = emiti(0); 421 | stuffdot(nextcase); 422 | t = c1->child[0]; 423 | while(t->type==';'){ 424 | tt = c1; 425 | emitf(Xmark); 426 | for(t = c0->child[0];t->type==ARGLIST;t = c0) outcode(c1, eflag); 427 | emitf(Xcase); 428 | nextcase = emiti(0); 429 | t = tt; 430 | for(;;){ 431 | if(t->type==';'){ 432 | if(iscase(c0)) break; 433 | outcode(c0, eflag); 434 | t = c1; 435 | } 436 | else{ 437 | if(!iscase(t)) outcode(t, eflag); 438 | break; 439 | } 440 | } 441 | emitf(Xjump); 442 | emiti(out); 443 | stuffdot(nextcase); 444 | } 445 | stuffdot(leave); 446 | emitf(Xpopm); 447 | } 448 | 449 | int 450 | iscase(tree *t) 451 | { 452 | if(t->type!=SIMPLE) 453 | return 0; 454 | do t = c0; while(t->type==ARGLIST); 455 | return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; 456 | } 457 | 458 | code* 459 | codecopy(code *cp) 460 | { 461 | cp[0].i++; 462 | return cp; 463 | } 464 | 465 | void 466 | codefree(code *cp) 467 | { 468 | code *p; 469 | if(--cp[0].i!=0) 470 | return; 471 | for(p = cp+1;p->f;p++){ 472 | if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite 473 | || p->f==Xrdwr 474 | || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse 475 | || p->f==Xfor || p->f==Xjump 476 | || p->f==Xsubshell || p->f==Xtrue) p++; 477 | else if(p->f==Xdup || p->f==Xpipefd) p+=2; 478 | else if(p->f==Xpipe) p+=4; 479 | else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); 480 | else if(p->f==Xfn){ 481 | efree(p[2].s); 482 | p+=2; 483 | } 484 | } 485 | efree((char *)cp); 486 | } 487 | -------------------------------------------------------------------------------- /exec.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "getflags.h" 3 | #include "exec.h" 4 | #include "io.h" 5 | #include "fns.h" 6 | /* 7 | * Start executing the given code at the given pc with the given redirection 8 | */ 9 | char *argv0="rc"; 10 | 11 | void 12 | start(code *c, int pc, var *local) 13 | { 14 | struct thread *p = new(struct thread); 15 | 16 | p->code = codecopy(c); 17 | p->pc = pc; 18 | p->argv = 0; 19 | p->redir = p->startredir = runq?runq->redir:0; 20 | p->local = local; 21 | p->cmdfile = 0; 22 | p->cmdfd = 0; 23 | p->eof = 0; 24 | p->iflag = 0; 25 | p->lineno = 1; 26 | p->ret = runq; 27 | runq = p; 28 | } 29 | 30 | word* 31 | newword(char *wd, word *next) 32 | { 33 | word *p = new(word); 34 | p->word = strdup(wd); 35 | p->next = next; 36 | return p; 37 | } 38 | 39 | void 40 | pushword(char *wd) 41 | { 42 | if(runq->argv==0) 43 | panic("pushword but no argv!", 0); 44 | runq->argv->words = newword(wd, runq->argv->words); 45 | } 46 | 47 | void 48 | popword(void) 49 | { 50 | word *p; 51 | if(runq->argv==0) 52 | panic("popword but no argv!", 0); 53 | p = runq->argv->words; 54 | if(p==0) 55 | panic("popword but no word!", 0); 56 | runq->argv->words = p->next; 57 | efree(p->word); 58 | efree((char *)p); 59 | } 60 | 61 | void 62 | freelist(word *w) 63 | { 64 | word *nw; 65 | while(w){ 66 | nw = w->next; 67 | efree(w->word); 68 | efree((char *)w); 69 | w = nw; 70 | } 71 | } 72 | 73 | void 74 | pushlist(void) 75 | { 76 | list *p = new(list); 77 | p->next = runq->argv; 78 | p->words = 0; 79 | runq->argv = p; 80 | } 81 | 82 | void 83 | poplist(void) 84 | { 85 | list *p = runq->argv; 86 | if(p==0) 87 | panic("poplist but no argv", 0); 88 | freelist(p->words); 89 | runq->argv = p->next; 90 | efree((char *)p); 91 | } 92 | 93 | int 94 | count(word *w) 95 | { 96 | int n; 97 | for(n = 0;w;n++) w = w->next; 98 | return n; 99 | } 100 | 101 | void 102 | pushredir(int type, int from, int to) 103 | { 104 | redir * rp = new(redir); 105 | rp->type = type; 106 | rp->from = from; 107 | rp->to = to; 108 | rp->next = runq->redir; 109 | runq->redir = rp; 110 | } 111 | 112 | var* 113 | newvar(char *name, var *next) 114 | { 115 | var *v = new(var); 116 | v->name = name; 117 | v->val = 0; 118 | v->fn = 0; 119 | v->changed = 0; 120 | v->fnchanged = 0; 121 | v->next = next; 122 | v->changefn = 0; 123 | return v; 124 | } 125 | /* 126 | * get command line flags, initialize keywords & traps. 127 | * get values from environment. 128 | * set $pid, $cflag, $* 129 | * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*) 130 | * start interpreting code 131 | */ 132 | int 133 | main(int argc, char *argv[]) 134 | { 135 | code bootstrap[32]; 136 | char num[12], *rcmain; 137 | int i; 138 | 139 | argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1); 140 | if(argc==-1) 141 | usage("[file [arg ...]]"); 142 | if(argv[0][0]=='-') 143 | flag['l'] = flagset; 144 | if(flag['I']) 145 | flag['i'] = 0; 146 | else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; 147 | rcmain = flag['m'] ? flag['m'][0] : Rcmain; 148 | err = openfd(2); 149 | kinit(); 150 | Trapinit(); 151 | Vinit(); 152 | inttoascii(num, mypid = getpid()); 153 | pathinit(); 154 | setvar("pid", newword(num, (word *)0)); 155 | setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) 156 | :(word *)0); 157 | setvar("rcname", newword(argv[0], (word *)0)); 158 | i = 0; 159 | memset(bootstrap, 0, sizeof bootstrap); 160 | bootstrap[i++].i = 1; 161 | bootstrap[i++].f = Xmark; 162 | bootstrap[i++].f = Xword; 163 | bootstrap[i++].s="*"; 164 | bootstrap[i++].f = Xassign; 165 | bootstrap[i++].f = Xmark; 166 | bootstrap[i++].f = Xmark; 167 | bootstrap[i++].f = Xword; 168 | bootstrap[i++].s="*"; 169 | bootstrap[i++].f = Xdol; 170 | bootstrap[i++].f = Xword; 171 | bootstrap[i++].s = rcmain; 172 | bootstrap[i++].f = Xword; 173 | bootstrap[i++].s="."; 174 | bootstrap[i++].f = Xsimple; 175 | bootstrap[i++].f = Xexit; 176 | bootstrap[i].i = 0; 177 | start(bootstrap, 1, (var *)0); 178 | /* prime bootstrap argv */ 179 | pushlist(); 180 | argv0 = strdup(argv[0]); 181 | for(i = argc-1;i!=0;--i) pushword(argv[i]); 182 | for(;;){ 183 | if(flag['r']) 184 | pfnc(err, runq); 185 | runq->pc++; 186 | (*runq->code[runq->pc-1].f)(); 187 | if(ntrap) 188 | dotrap(); 189 | } 190 | return 0; /* not reached; silence OS X Lion gcc */ 191 | } 192 | /* 193 | * Opcode routines 194 | * Arguments on stack (...) 195 | * Arguments in line [...] 196 | * Code in line with jump around {...} 197 | * 198 | * Xappend(file)[fd] open file to append 199 | * Xassign(name, val) assign val to name 200 | * Xasync{... Xexit} make thread for {}, no wait 201 | * Xbackq{... Xreturn} make thread for {}, push stdout 202 | * Xbang complement condition 203 | * Xcase(pat, value){...} exec code on match, leave (value) on 204 | * stack 205 | * Xclose[i] close file descriptor 206 | * Xconc(left, right) concatenate, push results 207 | * Xcount(name) push var count 208 | * Xdelfn(name) delete function definition 209 | * Xdeltraps(names) delete named traps 210 | * Xdol(name) get variable value 211 | * Xqdol(name) concatenate variable components 212 | * Xdup[i j] dup file descriptor 213 | * Xexit rc exits with status 214 | * Xfalse{...} execute {} if false 215 | * Xfn(name){... Xreturn} define function 216 | * Xfor(var, list){... Xreturn} for loop 217 | * Xjump[addr] goto 218 | * Xlocal(name, val) create local variable, assign value 219 | * Xmark mark stack 220 | * Xmatch(pat, str) match pattern, set status 221 | * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, 222 | * wait for both 223 | * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, 224 | * depending on type), push /dev/fd/?? 225 | * Xpopm(value) pop value from stack 226 | * Xrdwr(file)[fd] open file for reading and writing 227 | * Xread(file)[fd] open file to read 228 | * Xsettraps(names){... Xreturn} define trap functions 229 | * Xshowtraps print trap list 230 | * Xsimple(args) run command and wait 231 | * Xreturn kill thread 232 | * Xsubshell{... Xexit} execute {} in a subshell and wait 233 | * Xtrue{...} execute {} if true 234 | * Xunlocal delete local variable 235 | * Xword[string] push string 236 | * Xwrite(file)[fd] open file to write 237 | */ 238 | 239 | void 240 | Xappend(void) 241 | { 242 | char *file; 243 | int f; 244 | switch(count(runq->argv->words)){ 245 | default: 246 | Xerror1(">> requires singleton"); 247 | return; 248 | case 0: 249 | Xerror1(">> requires file"); 250 | return; 251 | case 1: 252 | break; 253 | } 254 | file = runq->argv->words->word; 255 | if((f = open(file, 1))<0 && (f = Creat(file))<0){ 256 | pfmt(err, "%s: ", file); 257 | Xerror("can't open"); 258 | return; 259 | } 260 | Seek(f, 0L, 2); 261 | pushredir(ROPEN, f, runq->code[runq->pc].i); 262 | runq->pc++; 263 | poplist(); 264 | } 265 | 266 | void 267 | Xsettrue(void) 268 | { 269 | setstatus(""); 270 | } 271 | 272 | void 273 | Xbang(void) 274 | { 275 | setstatus(truestatus()?"false":""); 276 | } 277 | 278 | void 279 | Xclose(void) 280 | { 281 | pushredir(RCLOSE, runq->code[runq->pc].i, 0); 282 | runq->pc++; 283 | } 284 | 285 | void 286 | Xdup(void) 287 | { 288 | pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); 289 | runq->pc+=2; 290 | } 291 | 292 | void 293 | Xeflag(void) 294 | { 295 | if(eflagok && !truestatus()) Xexit(); 296 | } 297 | 298 | void 299 | Xexit(void) 300 | { 301 | struct var *trapreq; 302 | struct word *starval; 303 | static int beenhere = 0; 304 | if(getpid()==mypid && !beenhere){ 305 | trapreq = vlook("sigexit"); 306 | if(trapreq->fn){ 307 | beenhere = 1; 308 | --runq->pc; 309 | starval = vlook("*")->val; 310 | start(trapreq->fn, trapreq->pc, (struct var *)0); 311 | runq->local = newvar(strdup("*"), runq->local); 312 | runq->local->val = copywords(starval, (struct word *)0); 313 | runq->local->changed = 1; 314 | runq->redir = runq->startredir = 0; 315 | return; 316 | } 317 | } 318 | Exit(getstatus()); 319 | } 320 | 321 | void 322 | Xfalse(void) 323 | { 324 | if(truestatus()) runq->pc = runq->code[runq->pc].i; 325 | else runq->pc++; 326 | } 327 | int ifnot; /* dynamic if not flag */ 328 | 329 | void 330 | Xifnot(void) 331 | { 332 | if(ifnot) 333 | runq->pc++; 334 | else 335 | runq->pc = runq->code[runq->pc].i; 336 | } 337 | 338 | void 339 | Xjump(void) 340 | { 341 | runq->pc = runq->code[runq->pc].i; 342 | } 343 | 344 | void 345 | Xmark(void) 346 | { 347 | pushlist(); 348 | } 349 | 350 | void 351 | Xpopm(void) 352 | { 353 | poplist(); 354 | } 355 | 356 | void 357 | Xread(void) 358 | { 359 | char *file; 360 | int f; 361 | switch(count(runq->argv->words)){ 362 | default: 363 | Xerror1("< requires singleton\n"); 364 | return; 365 | case 0: 366 | Xerror1("< requires file\n"); 367 | return; 368 | case 1: 369 | break; 370 | } 371 | file = runq->argv->words->word; 372 | if((f = open(file, 0))<0){ 373 | pfmt(err, "%s: ", file); 374 | Xerror("can't open"); 375 | return; 376 | } 377 | pushredir(ROPEN, f, runq->code[runq->pc].i); 378 | runq->pc++; 379 | poplist(); 380 | } 381 | 382 | void 383 | Xrdwr(void) 384 | { 385 | char *file; 386 | int f; 387 | 388 | switch(count(runq->argv->words)){ 389 | default: 390 | Xerror1("<> requires singleton\n"); 391 | return; 392 | case 0: 393 | Xerror1("<> requires file\n"); 394 | return; 395 | case 1: 396 | break; 397 | } 398 | file = runq->argv->words->word; 399 | if((f = open(file, ORDWR))<0){ 400 | pfmt(err, "%s: ", file); 401 | Xerror("can't open"); 402 | return; 403 | } 404 | pushredir(ROPEN, f, runq->code[runq->pc].i); 405 | runq->pc++; 406 | poplist(); 407 | } 408 | 409 | void 410 | turfredir(void) 411 | { 412 | while(runq->redir!=runq->startredir) 413 | Xpopredir(); 414 | } 415 | 416 | void 417 | Xpopredir(void) 418 | { 419 | struct redir *rp = runq->redir; 420 | if(rp==0) 421 | panic("turfredir null!", 0); 422 | runq->redir = rp->next; 423 | if(rp->type==ROPEN) 424 | close(rp->from); 425 | efree((char *)rp); 426 | } 427 | 428 | void 429 | Xreturn(void) 430 | { 431 | struct thread *p = runq; 432 | turfredir(); 433 | while(p->argv) poplist(); 434 | codefree(p->code); 435 | runq = p->ret; 436 | efree((char *)p); 437 | if(runq==0) 438 | Exit(getstatus()); 439 | } 440 | 441 | void 442 | Xtrue(void) 443 | { 444 | if(truestatus()) runq->pc++; 445 | else runq->pc = runq->code[runq->pc].i; 446 | } 447 | 448 | void 449 | Xif(void) 450 | { 451 | ifnot = 1; 452 | if(truestatus()) runq->pc++; 453 | else runq->pc = runq->code[runq->pc].i; 454 | } 455 | 456 | void 457 | Xwastrue(void) 458 | { 459 | ifnot = 0; 460 | } 461 | 462 | void 463 | Xword(void) 464 | { 465 | pushword(runq->code[runq->pc++].s); 466 | } 467 | 468 | void 469 | Xwrite(void) 470 | { 471 | char *file; 472 | int f; 473 | switch(count(runq->argv->words)){ 474 | default: 475 | Xerror1("> requires singleton\n"); 476 | return; 477 | case 0: 478 | Xerror1("> requires file\n"); 479 | return; 480 | case 1: 481 | break; 482 | } 483 | file = runq->argv->words->word; 484 | if((f = Creat(file))<0){ 485 | pfmt(err, "%s: ", file); 486 | Xerror("can't open"); 487 | return; 488 | } 489 | pushredir(ROPEN, f, runq->code[runq->pc].i); 490 | runq->pc++; 491 | poplist(); 492 | } 493 | 494 | char* 495 | list2str(word *words) 496 | { 497 | char *value, *s, *t; 498 | int len = 0; 499 | word *ap; 500 | for(ap = words;ap;ap = ap->next) 501 | len+=1+strlen(ap->word); 502 | value = emalloc(len+1); 503 | s = value; 504 | for(ap = words;ap;ap = ap->next){ 505 | for(t = ap->word;*t;) *s++=*t++; 506 | *s++=' '; 507 | } 508 | if(s==value) 509 | *s='\0'; 510 | else s[-1]='\0'; 511 | return value; 512 | } 513 | 514 | void 515 | Xmatch(void) 516 | { 517 | word *p; 518 | char *subject; 519 | subject = list2str(runq->argv->words); 520 | setstatus("no match"); 521 | for(p = runq->argv->next->words;p;p = p->next) 522 | if(match(subject, p->word, '\0')){ 523 | setstatus(""); 524 | break; 525 | } 526 | efree(subject); 527 | poplist(); 528 | poplist(); 529 | } 530 | 531 | void 532 | Xcase(void) 533 | { 534 | word *p; 535 | char *s; 536 | int ok = 0; 537 | s = list2str(runq->argv->next->words); 538 | for(p = runq->argv->words;p;p = p->next){ 539 | if(match(s, p->word, '\0')){ 540 | ok = 1; 541 | break; 542 | } 543 | } 544 | efree(s); 545 | if(ok) 546 | runq->pc++; 547 | else 548 | runq->pc = runq->code[runq->pc].i; 549 | poplist(); 550 | } 551 | 552 | word* 553 | conclist(word *lp, word *rp, word *tail) 554 | { 555 | char *buf; 556 | word *v; 557 | if(lp->next || rp->next) 558 | tail = conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, 559 | tail); 560 | buf = emalloc(strlen(lp->word)+strlen(rp->word)+1); 561 | strcpy(buf, lp->word); 562 | strcat(buf, rp->word); 563 | v = newword(buf, tail); 564 | efree(buf); 565 | return v; 566 | } 567 | 568 | void 569 | Xconc(void) 570 | { 571 | word *lp = runq->argv->words; 572 | word *rp = runq->argv->next->words; 573 | word *vp = runq->argv->next->next->words; 574 | int lc = count(lp), rc = count(rp); 575 | if(lc!=0 || rc!=0){ 576 | if(lc==0 || rc==0){ 577 | Xerror1("null list in concatenation"); 578 | return; 579 | } 580 | if(lc!=1 && rc!=1 && lc!=rc){ 581 | Xerror1("mismatched list lengths in concatenation"); 582 | return; 583 | } 584 | vp = conclist(lp, rp, vp); 585 | } 586 | poplist(); 587 | poplist(); 588 | runq->argv->words = vp; 589 | } 590 | 591 | void 592 | Xassign(void) 593 | { 594 | var *v; 595 | if(count(runq->argv->words)!=1){ 596 | Xerror1("variable name not singleton!"); 597 | return; 598 | } 599 | deglob(runq->argv->words->word); 600 | v = vlook(runq->argv->words->word); 601 | poplist(); 602 | globlist(); 603 | freewords(v->val); 604 | v->val = runq->argv->words; 605 | v->changed = 1; 606 | if(v->changefn) 607 | v->changefn(v); 608 | runq->argv->words = 0; 609 | poplist(); 610 | } 611 | /* 612 | * copy arglist a, adding the copy to the front of tail 613 | */ 614 | 615 | word* 616 | copywords(word *a, word *tail) 617 | { 618 | word *v = 0, **end; 619 | for(end=&v;a;a = a->next,end=&(*end)->next) 620 | *end = newword(a->word, 0); 621 | *end = tail; 622 | return v; 623 | } 624 | 625 | void 626 | Xdol(void) 627 | { 628 | word *a, *star; 629 | char *s, *t; 630 | int n; 631 | if(count(runq->argv->words)!=1){ 632 | Xerror1("variable name not singleton!"); 633 | return; 634 | } 635 | s = runq->argv->words->word; 636 | deglob(s); 637 | n = 0; 638 | for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; 639 | a = runq->argv->next->words; 640 | if(n==0 || *t) 641 | a = copywords(vlook(s)->val, a); 642 | else{ 643 | star = vlook("*")->val; 644 | if(star && 1<=n && n<=count(star)){ 645 | while(--n) star = star->next; 646 | a = newword(star->word, a); 647 | } 648 | } 649 | poplist(); 650 | runq->argv->words = a; 651 | } 652 | 653 | void 654 | Xqdol(void) 655 | { 656 | word *a, *p; 657 | char *s; 658 | int n; 659 | if(count(runq->argv->words)!=1){ 660 | Xerror1("variable name not singleton!"); 661 | return; 662 | } 663 | s = runq->argv->words->word; 664 | deglob(s); 665 | a = vlook(s)->val; 666 | poplist(); 667 | n = count(a); 668 | if(n==0){ 669 | pushword(""); 670 | return; 671 | } 672 | for(p = a;p;p = p->next) n+=strlen(p->word); 673 | s = emalloc(n); 674 | if(a){ 675 | strcpy(s, a->word); 676 | for(p = a->next;p;p = p->next){ 677 | strcat(s, " "); 678 | strcat(s, p->word); 679 | } 680 | } 681 | else 682 | s[0]='\0'; 683 | pushword(s); 684 | efree(s); 685 | } 686 | 687 | word* 688 | copynwords(word *a, word *tail, int n) 689 | { 690 | word *v, **end; 691 | 692 | v = 0; 693 | end = &v; 694 | while(n-- > 0){ 695 | *end = newword(a->word, 0); 696 | end = &(*end)->next; 697 | a = a->next; 698 | } 699 | *end = tail; 700 | return v; 701 | } 702 | 703 | word* 704 | subwords(word *val, int len, word *sub, word *a) 705 | { 706 | int n, m; 707 | char *s; 708 | if(!sub) 709 | return a; 710 | a = subwords(val, len, sub->next, a); 711 | s = sub->word; 712 | deglob(s); 713 | m = 0; 714 | n = 0; 715 | while('0'<=*s && *s<='9') 716 | n = n*10+ *s++ -'0'; 717 | if(*s == '-'){ 718 | if(*++s == 0) 719 | m = len - n; 720 | else{ 721 | while('0'<=*s && *s<='9') 722 | m = m*10+ *s++ -'0'; 723 | m -= n; 724 | } 725 | } 726 | if(n<1 || n>len || m<0) 727 | return a; 728 | if(n+m>len) 729 | m = len-n; 730 | while(--n > 0) 731 | val = val->next; 732 | return copynwords(val, a, m+1); 733 | } 734 | 735 | void 736 | Xsub(void) 737 | { 738 | word *a, *v; 739 | char *s; 740 | if(count(runq->argv->next->words)!=1){ 741 | Xerror1("variable name not singleton!"); 742 | return; 743 | } 744 | s = runq->argv->next->words->word; 745 | deglob(s); 746 | a = runq->argv->next->next->words; 747 | v = vlook(s)->val; 748 | a = subwords(v, count(v), runq->argv->words, a); 749 | poplist(); 750 | poplist(); 751 | runq->argv->words = a; 752 | } 753 | 754 | void 755 | Xcount(void) 756 | { 757 | word *a; 758 | char *s, *t; 759 | int n; 760 | char num[12]; 761 | if(count(runq->argv->words)!=1){ 762 | Xerror1("variable name not singleton!"); 763 | return; 764 | } 765 | s = runq->argv->words->word; 766 | deglob(s); 767 | n = 0; 768 | for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; 769 | if(n==0 || *t){ 770 | a = vlook(s)->val; 771 | inttoascii(num, count(a)); 772 | } 773 | else{ 774 | a = vlook("*")->val; 775 | inttoascii(num, a && 1<=n && n<=count(a)?1:0); 776 | } 777 | poplist(); 778 | pushword(num); 779 | } 780 | 781 | void 782 | Xlocal(void) 783 | { 784 | if(count(runq->argv->words)!=1){ 785 | Xerror1("variable name must be singleton\n"); 786 | return; 787 | } 788 | deglob(runq->argv->words->word); 789 | runq->local = newvar(strdup(runq->argv->words->word), runq->local); 790 | runq->local->val = copywords(runq->argv->next->words, (word *)0); 791 | runq->local->changed = 1; 792 | poplist(); 793 | poplist(); 794 | } 795 | 796 | void 797 | Xunlocal(void) 798 | { 799 | var *v = runq->local, *hid; 800 | if(v==0) 801 | panic("Xunlocal: no locals!", 0); 802 | runq->local = v->next; 803 | hid = vlook(v->name); 804 | hid->changed = 1; 805 | efree(v->name); 806 | freewords(v->val); 807 | efree((char *)v); 808 | } 809 | 810 | void 811 | freewords(word *w) 812 | { 813 | word *nw; 814 | while(w){ 815 | efree(w->word); 816 | nw = w->next; 817 | efree((char *)w); 818 | w = nw; 819 | } 820 | } 821 | 822 | void 823 | Xfn(void) 824 | { 825 | var *v; 826 | word *a; 827 | int end; 828 | end = runq->code[runq->pc].i; 829 | for(a = runq->argv->words;a;a = a->next){ 830 | v = gvlook(a->word); 831 | if(v->fn) 832 | codefree(v->fn); 833 | v->fn = codecopy(runq->code); 834 | v->pc = runq->pc+2; 835 | v->fnchanged = 1; 836 | } 837 | runq->pc = end; 838 | poplist(); 839 | } 840 | 841 | void 842 | Xdelfn(void) 843 | { 844 | var *v; 845 | word *a; 846 | for(a = runq->argv->words;a;a = a->next){ 847 | v = gvlook(a->word); 848 | if(v->fn) 849 | codefree(v->fn); 850 | v->fn = 0; 851 | v->fnchanged = 1; 852 | } 853 | poplist(); 854 | } 855 | 856 | char* 857 | concstatus(char *s, char *t) 858 | { 859 | static char v[NSTATUS+1]; 860 | int n = strlen(s); 861 | strncpy(v, s, NSTATUS); 862 | if(npid==-1) 875 | setstatus(concstatus(runq->status, getstatus())); 876 | else{ 877 | strncpy(status, getstatus(), NSTATUS); 878 | status[NSTATUS]='\0'; 879 | Waitfor(runq->pid, 1); 880 | runq->pid=-1; 881 | setstatus(concstatus(getstatus(), status)); 882 | } 883 | } 884 | 885 | void 886 | Xrdcmds(void) 887 | { 888 | struct thread *p = runq; 889 | word *prompt; 890 | flush(err); 891 | nerror = 0; 892 | if(flag['s'] && !truestatus()) 893 | pfmt(err, "status=%v\n", vlook("status")->val); 894 | if(runq->iflag){ 895 | prompt = vlook("prompt")->val; 896 | if(prompt) 897 | promptstr = prompt->word; 898 | else 899 | promptstr="% "; 900 | } 901 | Noerror(); 902 | if(yyparse()){ 903 | if(!p->iflag || p->eof && !Eintr()){ 904 | if(p->cmdfile) 905 | efree(p->cmdfile); 906 | closeio(p->cmdfd); 907 | Xreturn(); /* should this be omitted? */ 908 | } 909 | else{ 910 | if(Eintr()){ 911 | pchr(err, '\n'); 912 | p->eof = 0; 913 | } 914 | --p->pc; /* go back for next command */ 915 | } 916 | } 917 | else{ 918 | ntrap = 0; /* avoid double-interrupts during blocked writes */ 919 | --p->pc; /* re-execute Xrdcmds after codebuf runs */ 920 | start(codebuf, 1, runq->local); 921 | } 922 | freenodes(); 923 | } 924 | 925 | void 926 | Xerror(char *s) 927 | { 928 | if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) 929 | pfmt(err, "rc: %s: %r\n", s); 930 | else 931 | pfmt(err, "rc (%s): %s: %r\n", argv0, s); 932 | flush(err); 933 | setstatus("error"); 934 | while(!runq->iflag) Xreturn(); 935 | } 936 | 937 | void 938 | Xerror1(char *s) 939 | { 940 | if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) 941 | pfmt(err, "rc: %s\n", s); 942 | else 943 | pfmt(err, "rc (%s): %s\n", argv0, s); 944 | flush(err); 945 | setstatus("error"); 946 | while(!runq->iflag) Xreturn(); 947 | } 948 | 949 | void 950 | setstatus(char *s) 951 | { 952 | setvar("status", newword(s, (word *)0)); 953 | } 954 | 955 | char* 956 | getstatus(void) 957 | { 958 | var *status = vlook("status"); 959 | return status->val?status->val->word:""; 960 | } 961 | 962 | int 963 | truestatus(void) 964 | { 965 | char *s; 966 | for(s = getstatus();*s;s++) 967 | if(*s!='|' && *s!='0') 968 | return 0; 969 | return 1; 970 | } 971 | 972 | void 973 | Xdelhere(void) 974 | { 975 | Unlink(runq->code[runq->pc++].s); 976 | } 977 | 978 | void 979 | Xfor(void) 980 | { 981 | if(runq->argv->words==0){ 982 | poplist(); 983 | runq->pc = runq->code[runq->pc].i; 984 | } 985 | else{ 986 | freelist(runq->local->val); 987 | runq->local->val = runq->argv->words; 988 | runq->local->changed = 1; 989 | runq->argv->words = runq->argv->words->next; 990 | runq->local->val->next = 0; 991 | runq->pc++; 992 | } 993 | } 994 | 995 | void 996 | Xglob(void) 997 | { 998 | globlist(); 999 | } 1000 | -------------------------------------------------------------------------------- /exec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Definitions used in the interpreter 3 | */ 4 | extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void); 5 | extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void); 6 | extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void); 7 | extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void); 8 | extern void Xrdwr(void); 9 | extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void); 10 | extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void); 11 | extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void); 12 | extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void); 13 | extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void); 14 | extern void Xerror(char*); 15 | extern void Xerror1(char*); 16 | /* 17 | * word lists are in correct order, 18 | * i.e. word0->word1->word2->word3->0 19 | */ 20 | struct word{ 21 | char *word; 22 | word *next; 23 | }; 24 | struct list{ 25 | word *words; 26 | list *next; 27 | }; 28 | word *newword(char *, word *), *copywords(word *, word *); 29 | struct redir{ 30 | char type; /* what to do */ 31 | short from, to; /* what to do it to */ 32 | struct redir *next; /* what else to do (reverse order) */ 33 | }; 34 | #define NSTATUS ERRMAX /* length of status (from plan 9) */ 35 | /* 36 | * redir types 37 | */ 38 | #define ROPEN 1 /* dup2(from, to); close(from); */ 39 | #define RDUP 2 /* dup2(from, to); */ 40 | #define RCLOSE 3 /* close(from); */ 41 | struct thread{ 42 | union code *code; /* code for this thread */ 43 | int pc; /* code[pc] is the next instruction */ 44 | struct list *argv; /* argument stack */ 45 | struct redir *redir; /* redirection stack */ 46 | struct redir *startredir; /* redir inheritance point */ 47 | struct var *local; /* list of local variables */ 48 | char *cmdfile; /* file name in Xrdcmd */ 49 | struct io *cmdfd; /* file descriptor for Xrdcmd */ 50 | int iflast; /* static `if not' checking */ 51 | int eof; /* is cmdfd at eof? */ 52 | int iflag; /* interactive? */ 53 | int lineno; /* linenumber */ 54 | int pid; /* process for Xpipewait to wait for */ 55 | char status[NSTATUS]; /* status for Xpipewait */ 56 | tree *treenodes; /* tree nodes created by this process */ 57 | thread *ret; /* who continues when this finishes */ 58 | }; 59 | thread *runq; 60 | code *codecopy(code*); 61 | code *codebuf; /* compiler output */ 62 | int ntrap; /* number of outstanding traps */ 63 | int trap[NSIG]; /* number of outstanding traps per type */ 64 | struct builtin{ 65 | char *name; 66 | void (*fnc)(void); 67 | }; 68 | extern struct builtin Builtin[]; 69 | int eflagok; /* kludge flag so that -e doesn't exit in startup */ 70 | int havefork; 71 | 72 | void execcd(void), execwhatis(void), execeval(void), execexec(void); 73 | int execforkexec(void); 74 | void execexit(void), execshift(void); 75 | void execwait(void), execumask(void), execdot(void), execflag(void); 76 | void execfunc(var*), execcmds(io *); 77 | -------------------------------------------------------------------------------- /fns.h: -------------------------------------------------------------------------------- 1 | void Abort(void); 2 | void Closedir(int); 3 | int Creat(char*); 4 | int Dup(int, int); 5 | int Dup1(int); 6 | int Eintr(void); 7 | int Executable(char*); 8 | void Execute(word*, word*); 9 | void Exit(char*); 10 | int ForkExecute(char*, char**, int, int, int); 11 | int Globsize(char*); 12 | int Isatty(int); 13 | void Memcpy(char*, char*, long); 14 | void Noerror(void); 15 | int Opendir(char*); 16 | long Read(int, char*, long); 17 | int Readdir(int, char*, int); 18 | long Seek(int, long, long); 19 | void Trapinit(void); 20 | void Unlink(char*); 21 | void Updenv(void); 22 | void Vinit(void); 23 | int Waitfor(int, int); 24 | long Write(int, char*, long); 25 | void addwaitpid(int); 26 | int advance(void); 27 | int back(int); 28 | void cleanhere(char*); 29 | void codefree(code*); 30 | int compile(tree*); 31 | char * list2str(word*); 32 | int count(word*); 33 | void deglob(char*); 34 | void delwaitpid(int); 35 | void dotrap(void); 36 | void freenodes(void); 37 | void freewords(word*); 38 | void globlist(void); 39 | int havewaitpid(int); 40 | int idchr(int); 41 | void inttoascii(char*, long); 42 | void kinit(void); 43 | int mapfd(int); 44 | int match(char*, char*, int); 45 | int matchfn(char*, char*); 46 | char** mkargv(word*); 47 | void clearwaitpids(void); 48 | void panic(char*, int); 49 | void pathinit(void); 50 | void poplist(void); 51 | void popword(void); 52 | void pprompt(void); 53 | void pushlist(void); 54 | void pushredir(int, int, int); 55 | void pushword(char*); 56 | void readhere(void); 57 | word* searchpath(char*); 58 | void setstatus(char*); 59 | void setvar(char*, word*); 60 | void skipnl(void); 61 | void start(code*, int, var*); 62 | int truestatus(void); 63 | void usage(char*); 64 | int wordchr(int); 65 | void yyerror(char*); 66 | int yylex(void); 67 | int yyparse(void); 68 | int octal(char*); 69 | -------------------------------------------------------------------------------- /getflags.c: -------------------------------------------------------------------------------- 1 | /*% cyntax -DTEST % && cc -DTEST -go # % 2 | */ 3 | #include "rc.h" 4 | #include "getflags.h" 5 | #include "fns.h" 6 | char *flagset[] = {""}; 7 | char **flag[NFLAG]; 8 | char cmdline[NCMDLINE+1]; 9 | char *cmdname; 10 | static char *flagarg=""; 11 | static void reverse(char**, char**); 12 | static int scanflag(int, char*); 13 | static void errn(char*, int); 14 | static void errs(char*); 15 | static void errc(int); 16 | static int reason; 17 | #define RESET 1 18 | #define FEWARGS 2 19 | #define FLAGSYN 3 20 | #define BADFLAG 4 21 | static int badflag; 22 | 23 | int 24 | getflags(int argc, char *argv[], char *flags, int stop) 25 | { 26 | char *s, *t; 27 | int i, j, c, count; 28 | flagarg = flags; 29 | if(cmdname==0) 30 | cmdname = argv[0]; 31 | s = cmdline; 32 | for(i = 0;i!=argc;i++){ 33 | for(t = argv[i];*t;t++) 34 | if(s!=&cmdline[NCMDLINE]) 35 | *s++=*t; 36 | if(i!=argc-1 && s!=&cmdline[NCMDLINE]) 37 | *s++=' '; 38 | } 39 | *s='\0'; 40 | i = 1; 41 | while(i!=argc){ 42 | if(argv[i][0]!='-' || argv[i][1]=='\0'){ 43 | if(stop) 44 | return argc; 45 | i++; 46 | continue; 47 | } 48 | s = argv[i]+1; 49 | while(*s){ 50 | c=*s++; 51 | count = scanflag(c, flags); 52 | if(count==-1) 53 | return -1; 54 | if(flag[c]){ reason = RESET; badflag = c; return -1; } 55 | if(count==0){ 56 | flag[c] = flagset; 57 | if(*s=='\0'){ 58 | for(j = i+1;j<=argc;j++) 59 | argv[j-1] = argv[j]; 60 | --argc; 61 | } 62 | } 63 | else{ 64 | if(*s=='\0'){ 65 | for(j = i+1;j<=argc;j++) 66 | argv[j-1] = argv[j]; 67 | --argc; 68 | s = argv[i]; 69 | } 70 | if(argc-inext) n++; 34 | list = (char **)emalloc(n*sizeof(char *)); 35 | for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word; 36 | qsort((void *)list, n, sizeof(void *), globcmp); 37 | for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n]; 38 | efree((char *)list); 39 | } 40 | /* 41 | * Push names prefixed by globname and suffixed by a match of p onto the astack. 42 | * namep points to the end of the prefix in globname. 43 | */ 44 | 45 | void 46 | globdir(char *p, char *namep) 47 | { 48 | char *t, *newp; 49 | int f; 50 | /* scan the pattern looking for a component with a metacharacter in it */ 51 | if(*p=='\0'){ 52 | globv = newword(globname, globv); 53 | return; 54 | } 55 | t = namep; 56 | newp = p; 57 | while(*newp){ 58 | if(*newp==GLOB) 59 | break; 60 | *t=*newp++; 61 | if(*t++=='/'){ 62 | namep = t; 63 | p = newp; 64 | } 65 | } 66 | /* If we ran out of pattern, append the name if accessible */ 67 | if(*newp=='\0'){ 68 | *t='\0'; 69 | if(access(globname, 0)==0) 70 | globv = newword(globname, globv); 71 | return; 72 | } 73 | /* read the directory and recur for any entry that matches */ 74 | *namep='\0'; 75 | if((f = Opendir(globname[0]?globname:"."))<0) return; 76 | while(*newp!='/' && *newp!='\0') newp++; 77 | while(Readdir(f, namep, *newp=='/')){ 78 | if(matchfn(namep, p)){ 79 | for(t = namep;*t;t++); 80 | globdir(newp, t); 81 | } 82 | } 83 | Closedir(f); 84 | } 85 | /* 86 | * Push all file names matched by p on the current thread's stack. 87 | * If there are no matches, the list consists of p. 88 | */ 89 | 90 | void 91 | glob(char *p) 92 | { 93 | word *svglobv = globv; 94 | int globlen = Globsize(p); 95 | if(!globlen){ 96 | deglob(p); 97 | globv = newword(p, globv); 98 | return; 99 | } 100 | globname = emalloc(globlen); 101 | globname[0]='\0'; 102 | globdir(p, globname); 103 | efree(globname); 104 | if(svglobv==globv){ 105 | deglob(p); 106 | globv = newword(p, globv); 107 | } 108 | else 109 | globsort(globv, svglobv); 110 | } 111 | /* 112 | * Do p and q point at equal utf codes 113 | */ 114 | 115 | int 116 | equtf(char *p, char *q) 117 | { 118 | if(*p!=*q) 119 | return 0; 120 | if(twobyte(*p)) return p[1]==q[1]; 121 | if(threebyte(*p)){ 122 | if(p[1]!=q[1]) 123 | return 0; 124 | if(p[1]=='\0') 125 | return 1; /* broken code at end of string! */ 126 | return p[2]==q[2]; 127 | } 128 | if(fourbyte(*p)){ 129 | if(p[1]!=q[1]) 130 | return 0; 131 | if(p[1]=='\0') 132 | return 1; 133 | if(p[2]!=q[2]) 134 | return 0; 135 | if(p[2]=='\0') 136 | return 1; 137 | return p[3]==q[3]; 138 | } 139 | return 1; 140 | } 141 | /* 142 | * Return a pointer to the next utf code in the string, 143 | * not jumping past nuls in broken utf codes! 144 | */ 145 | 146 | char* 147 | nextutf(char *p) 148 | { 149 | if(twobyte(*p)) return p[1]=='\0'?p+1:p+2; 150 | if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; 151 | if(fourbyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p[3]=='\0'?p+3:p+4; 152 | return p+1; 153 | } 154 | /* 155 | * Convert the utf code at *p to a unicode value 156 | */ 157 | 158 | int 159 | unicode(char *p) 160 | { 161 | int u=*p&0xff; 162 | if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f); 163 | if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); 164 | if(fourbyte(u)) return (u<<18)|((p[1]&0x3f)<<12)|((p[2]&0x3f)<<6)|(p[3]&0x3f); 165 | return u; 166 | } 167 | /* 168 | * Does the string s match the pattern p 169 | * . and .. are only matched by patterns starting with . 170 | * * matches any sequence of characters 171 | * ? matches any single character 172 | * [...] matches the enclosed list of characters 173 | */ 174 | 175 | int 176 | matchfn(char *s, char *p) 177 | { 178 | if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') 179 | return 0; 180 | return match(s, p, '/'); 181 | } 182 | 183 | int 184 | match(char *s, char *p, int stop) 185 | { 186 | int compl, hit, lo, hi, t, c; 187 | for(;*p!=stop && *p!='\0';s = nextutf(s),p = nextutf(p)){ 188 | if(*p!=GLOB){ 189 | if(!equtf(p, s)) return 0; 190 | } 191 | else switch(*++p){ 192 | case GLOB: 193 | if(*s!=GLOB) 194 | return 0; 195 | break; 196 | case '*': 197 | for(;;){ 198 | if(match(s, nextutf(p), stop)) return 1; 199 | if(!*s) 200 | break; 201 | s = nextutf(s); 202 | } 203 | return 0; 204 | case '?': 205 | if(*s=='\0') 206 | return 0; 207 | break; 208 | case '[': 209 | if(*s=='\0') 210 | return 0; 211 | c = unicode(s); 212 | p++; 213 | compl=*p=='~'; 214 | if(compl) 215 | p++; 216 | hit = 0; 217 | while(*p!=']'){ 218 | if(*p=='\0') 219 | return 0; /* syntax error */ 220 | lo = unicode(p); 221 | p = nextutf(p); 222 | if(*p!='-') 223 | hi = lo; 224 | else{ 225 | p++; 226 | if(*p=='\0') 227 | return 0; /* syntax error */ 228 | hi = unicode(p); 229 | p = nextutf(p); 230 | if(hinext); 250 | glob(gl->word); 251 | } 252 | } 253 | 254 | void 255 | globlist(void) 256 | { 257 | word *a; 258 | globv = 0; 259 | globlist1(runq->argv->words); 260 | poplist(); 261 | pushlist(); 262 | if(globv){ 263 | for(a = globv;a->next;a = a->next); 264 | a->next = runq->argv->words; 265 | runq->argv->words = globv; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /havefork.c: -------------------------------------------------------------------------------- 1 | #ifdef Plan9 2 | #include 3 | #endif 4 | #include 5 | #if defined(PLAN9PORT) && defined(__sun__) 6 | # define BSD_COMP /* sigh. for TIOCNOTTY */ 7 | #endif 8 | #include 9 | #include "rc.h" 10 | #include "getflags.h" 11 | #include "exec.h" 12 | #include "io.h" 13 | #include "fns.h" 14 | 15 | int havefork = 1; 16 | 17 | void 18 | Xasync(void) 19 | { 20 | int null = open("/dev/null", 0); 21 | int tty; 22 | int pid; 23 | char npid[10]; 24 | if(null<0){ 25 | Xerror("Can't open /dev/null\n"); 26 | return; 27 | } 28 | switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){ 29 | case -1: 30 | close(null); 31 | Xerror("try again"); 32 | break; 33 | case 0: 34 | clearwaitpids(); 35 | /* 36 | * I don't know what the right thing to do here is, 37 | * so this is all experimentally determined. 38 | * If we just dup /dev/null onto 0, then running 39 | * ssh foo & will reopen /dev/tty, try to read a password, 40 | * get a signal, and repeat, in a tight loop, forever. 41 | * Arguably this is a bug in ssh (it behaves the same 42 | * way under bash as under rc) but I'm fixing it here 43 | * anyway. If we dissociate the process from the tty, 44 | * then it won't be able to open /dev/tty ever again. 45 | * The SIG_IGN on SIGTTOU makes writing the tty 46 | * (via fd 1 or 2, for example) succeed even though 47 | * our pgrp is not the terminal's controlling pgrp. 48 | */ 49 | if((tty = open("/dev/tty", OREAD)) >= 0){ 50 | /* 51 | * Should make reads of tty fail, writes succeed. 52 | */ 53 | signal(SIGTTIN, SIG_IGN); 54 | signal(SIGTTOU, SIG_IGN); 55 | ioctl(tty, TIOCNOTTY); 56 | close(tty); 57 | } 58 | if(isatty(0)) 59 | pushredir(ROPEN, null, 0); 60 | else 61 | close(null); 62 | start(runq->code, runq->pc+1, runq->local); 63 | runq->ret = 0; 64 | break; 65 | default: 66 | addwaitpid(pid); 67 | close(null); 68 | runq->pc = runq->code[runq->pc].i; 69 | inttoascii(npid, pid); 70 | setvar("apid", newword(npid, (word *)0)); 71 | break; 72 | } 73 | } 74 | 75 | void 76 | Xpipe(void) 77 | { 78 | struct thread *p = runq; 79 | int pc = p->pc, forkid; 80 | int lfd = p->code[pc++].i; 81 | int rfd = p->code[pc++].i; 82 | int pfd[2]; 83 | if(pipe(pfd)<0){ 84 | Xerror("can't get pipe"); 85 | return; 86 | } 87 | switch(forkid = fork()){ 88 | case -1: 89 | Xerror("try again"); 90 | break; 91 | case 0: 92 | clearwaitpids(); 93 | start(p->code, pc+2, runq->local); 94 | runq->ret = 0; 95 | close(pfd[PRD]); 96 | pushredir(ROPEN, pfd[PWR], lfd); 97 | break; 98 | default: 99 | addwaitpid(forkid); 100 | start(p->code, p->code[pc].i, runq->local); 101 | close(pfd[PWR]); 102 | pushredir(ROPEN, pfd[PRD], rfd); 103 | p->pc = p->code[pc+1].i; 104 | p->pid = forkid; 105 | break; 106 | } 107 | } 108 | 109 | /* 110 | * Who should wait for the exit from the fork? 111 | */ 112 | void 113 | Xbackq(void) 114 | { 115 | struct thread *p = runq; 116 | char wd[8193]; 117 | int c, n; 118 | char *s, *ewd=&wd[8192], *stop, *q; 119 | struct io *f; 120 | var *ifs = vlook("ifs"); 121 | word *v, *nextv; 122 | int pfd[2]; 123 | int pid; 124 | Rune r; 125 | stop = ifs->val?ifs->val->word:""; 126 | if(pipe(pfd)<0){ 127 | Xerror("can't make pipe"); 128 | return; 129 | } 130 | switch(pid = fork()){ 131 | case -1: 132 | Xerror("try again"); 133 | close(pfd[PRD]); 134 | close(pfd[PWR]); 135 | return; 136 | case 0: 137 | clearwaitpids(); 138 | close(pfd[PRD]); 139 | start(runq->code, runq->pc+1, runq->local); 140 | pushredir(ROPEN, pfd[PWR], 1); 141 | return; 142 | default: 143 | addwaitpid(pid); 144 | close(pfd[PWR]); 145 | f = openfd(pfd[PRD]); 146 | s = wd; 147 | v = 0; 148 | while((c = rchr(f))!=EOF){ 149 | if(s != ewd) { 150 | *s++ = c; 151 | for(q=stop; *q; q+=n) { 152 | n = chartorune(&r, q); 153 | if(s-wd >= n && memcmp(s-n, q, n) == 0) { 154 | s -= n; 155 | goto stop; 156 | } 157 | } 158 | continue; 159 | } 160 | stop: 161 | if(s != wd) { 162 | *s = '\0'; 163 | v = newword(wd, v); 164 | } 165 | s = wd; 166 | } 167 | if(s!=wd){ 168 | *s='\0'; 169 | v = newword(wd, v); 170 | } 171 | closeio(f); 172 | Waitfor(pid, 0); 173 | /* v points to reversed arglist -- reverse it onto argv */ 174 | while(v){ 175 | nextv = v->next; 176 | v->next = runq->argv->words; 177 | runq->argv->words = v; 178 | v = nextv; 179 | } 180 | p->pc = p->code[p->pc].i; 181 | return; 182 | } 183 | } 184 | 185 | void 186 | Xpipefd(void) 187 | { 188 | struct thread *p = runq; 189 | int pc = p->pc, pid; 190 | char name[40]; 191 | int pfd[2]; 192 | struct { int sidefd, mainfd; } fd[2], *r, *w; 193 | 194 | r = &fd[0]; 195 | w = &fd[1]; 196 | switch(p->code[pc].i){ 197 | case READ: 198 | w = nil; 199 | break; 200 | case WRITE: 201 | r = nil; 202 | } 203 | 204 | if(r){ 205 | if(pipe(pfd)<0){ 206 | Xerror("can't get pipe"); 207 | return; 208 | } 209 | r->sidefd = pfd[PWR]; 210 | r->mainfd = pfd[PRD]; 211 | } 212 | if(w){ 213 | if(pipe(pfd)<0){ 214 | Xerror("can't get pipe"); 215 | return; 216 | } 217 | w->sidefd = pfd[PRD]; 218 | w->mainfd = pfd[PWR]; 219 | } 220 | switch(pid = fork()){ 221 | case -1: 222 | Xerror("try again"); 223 | break; 224 | case 0: 225 | clearwaitpids(); 226 | start(p->code, pc+2, runq->local); 227 | if(r){ 228 | close(r->mainfd); 229 | pushredir(ROPEN, r->sidefd, 1); 230 | } 231 | if(w){ 232 | close(w->mainfd); 233 | pushredir(ROPEN, w->sidefd, 0); 234 | } 235 | runq->ret = 0; 236 | break; 237 | default: 238 | addwaitpid(pid); 239 | if(w){ 240 | close(w->sidefd); 241 | pushredir(ROPEN, w->mainfd, w->mainfd); /* so that Xpopredir can close it later */ 242 | strcpy(name, Fdprefix); 243 | inttoascii(name+strlen(name), w->mainfd); 244 | pushword(name); 245 | } 246 | if(r){ 247 | close(r->sidefd); 248 | pushredir(ROPEN, r->mainfd, r->mainfd); 249 | strcpy(name, Fdprefix); 250 | inttoascii(name+strlen(name), r->mainfd); 251 | pushword(name); 252 | } 253 | p->pc = p->code[pc+1].i; 254 | break; 255 | } 256 | } 257 | 258 | void 259 | Xsubshell(void) 260 | { 261 | int pid; 262 | switch(pid = fork()){ 263 | case -1: 264 | Xerror("try again"); 265 | break; 266 | case 0: 267 | clearwaitpids(); 268 | start(runq->code, runq->pc+1, runq->local); 269 | runq->ret = 0; 270 | break; 271 | default: 272 | addwaitpid(pid); 273 | Waitfor(pid, 1); 274 | runq->pc = runq->code[runq->pc].i; 275 | break; 276 | } 277 | } 278 | 279 | int 280 | execforkexec(void) 281 | { 282 | int pid; 283 | int n; 284 | char buf[ERRMAX]; 285 | 286 | switch(pid = fork()){ 287 | case -1: 288 | return -1; 289 | case 0: 290 | clearwaitpids(); 291 | pushword("exec"); 292 | execexec(); 293 | strcpy(buf, "can't exec: "); 294 | n = strlen(buf); 295 | errstr(buf+n, ERRMAX-n); 296 | Exit(buf); 297 | } 298 | addwaitpid(pid); 299 | return pid; 300 | } 301 | -------------------------------------------------------------------------------- /haventfork.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "getflags.h" 3 | #include "exec.h" 4 | #include "io.h" 5 | #include "fns.h" 6 | 7 | int havefork = 0; 8 | 9 | static char ** 10 | rcargv(char *s) 11 | { 12 | int argc; 13 | char **argv; 14 | word *p; 15 | 16 | p = vlook("*")->val; 17 | argv = emalloc((count(p)+6)*sizeof(char*)); 18 | argc = 0; 19 | argv[argc++] = argv0; 20 | if(flag['e']) 21 | argv[argc++] = "-Se"; 22 | else 23 | argv[argc++] = "-S"; 24 | argv[argc++] = "-c"; 25 | argv[argc++] = s; 26 | for(p = vlook("*")->val; p; p = p->next) 27 | argv[argc++] = p->word; 28 | argv[argc] = 0; 29 | return argv; 30 | } 31 | 32 | void 33 | Xasync(void) 34 | { 35 | uint pid; 36 | char buf[20], **argv; 37 | 38 | Updenv(); 39 | 40 | argv = rcargv(runq->code[runq->pc].s); 41 | pid = ForkExecute(argv0, argv, -1, 1, 2); 42 | free(argv); 43 | 44 | if(pid == 0) { 45 | Xerror("proc failed"); 46 | return; 47 | } 48 | 49 | runq->pc++; 50 | sprint(buf, "%d", pid); 51 | setvar("apid", newword(buf, (word *)0)); 52 | } 53 | 54 | void 55 | Xbackq(void) 56 | { 57 | char wd[8193], **argv; 58 | int c; 59 | char *s, *ewd=&wd[8192], *stop; 60 | struct io *f; 61 | var *ifs = vlook("ifs"); 62 | word *v, *nextv; 63 | int pfd[2]; 64 | int pid; 65 | 66 | stop = ifs->val?ifs->val->word:""; 67 | if(pipe(pfd)<0){ 68 | Xerror("can't make pipe"); 69 | return; 70 | } 71 | 72 | Updenv(); 73 | 74 | argv = rcargv(runq->code[runq->pc].s); 75 | pid = ForkExecute(argv0, argv, -1, pfd[1], 2); 76 | free(argv); 77 | 78 | close(pfd[1]); 79 | 80 | if(pid == 0) { 81 | Xerror("proc failed"); 82 | close(pfd[0]); 83 | return; 84 | } 85 | 86 | f = openfd(pfd[0]); 87 | s = wd; 88 | v = 0; 89 | while((c=rchr(f))!=EOF){ 90 | if(strchr(stop, c) || s==ewd){ 91 | if(s!=wd){ 92 | *s='\0'; 93 | v=newword(wd, v); 94 | s=wd; 95 | } 96 | } 97 | else *s++=c; 98 | } 99 | if(s!=wd){ 100 | *s='\0'; 101 | v=newword(wd, v); 102 | } 103 | closeio(f); 104 | Waitfor(pid, 1); 105 | /* v points to reversed arglist -- reverse it onto argv */ 106 | while(v){ 107 | nextv=v->next; 108 | v->next=runq->argv->words; 109 | runq->argv->words=v; 110 | v=nextv; 111 | } 112 | runq->pc++; 113 | } 114 | 115 | void 116 | Xpipe(void) 117 | { 118 | thread *p=runq; 119 | int pc=p->pc, pid; 120 | int rfd=p->code[pc+1].i; 121 | int pfd[2]; 122 | char **argv; 123 | 124 | if(pipe(pfd)<0){ 125 | Xerror1("can't get pipe"); 126 | return; 127 | } 128 | 129 | Updenv(); 130 | 131 | argv = rcargv(runq->code[pc+2].s); 132 | pid = ForkExecute(argv0, argv, 0, pfd[1], 2); 133 | free(argv); 134 | close(pfd[1]); 135 | 136 | if(pid == 0) { 137 | Xerror("proc failed"); 138 | close(pfd[0]); 139 | return; 140 | } 141 | 142 | start(p->code, pc+4, runq->local); 143 | pushredir(ROPEN, pfd[0], rfd); 144 | p->pc=p->code[pc+3].i; 145 | p->pid=pid; 146 | } 147 | 148 | void 149 | Xpipefd(void) 150 | { 151 | Abort(); 152 | } 153 | 154 | void 155 | Xsubshell(void) 156 | { 157 | char **argv; 158 | int pid; 159 | 160 | Updenv(); 161 | 162 | argv = rcargv(runq->code[runq->pc].s); 163 | pid = ForkExecute(argv0, argv, -1, 1, 2); 164 | free(argv); 165 | 166 | if(pid < 0) { 167 | Xerror("proc failed"); 168 | return; 169 | } 170 | 171 | Waitfor(pid, 1); 172 | runq->pc++; 173 | } 174 | 175 | /* 176 | * start a process running the cmd on the stack and return its pid. 177 | */ 178 | int 179 | execforkexec(void) 180 | { 181 | char **argv; 182 | char file[1024]; 183 | int nc; 184 | word *path; 185 | int pid; 186 | 187 | if(runq->argv->words==0) 188 | return -1; 189 | argv = mkargv(runq->argv->words); 190 | 191 | for(path = searchpath(runq->argv->words->word);path;path = path->next){ 192 | nc = strlen(path->word); 193 | if(nc < sizeof file - 1){ /* 1 for / */ 194 | strcpy(file, path->word); 195 | if(file[0]){ 196 | strcat(file, "/"); 197 | nc++; 198 | } 199 | if(nc+strlen(argv[1])= 0){ 203 | free(argv); 204 | return pid; 205 | } 206 | } 207 | } 208 | } 209 | free(argv); 210 | return -1; 211 | } 212 | -------------------------------------------------------------------------------- /here.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "io.h" 4 | #include "fns.h" 5 | struct here *here, **ehere; 6 | int ser = 0; 7 | char tmp[]="/tmp/here0000.0000"; 8 | char hex[]="0123456789abcdef"; 9 | void psubst(io*, char*); 10 | void pstrs(io*, word*); 11 | 12 | void 13 | hexnum(char *p, int n) 14 | { 15 | *p++=hex[(n>>12)&0xF]; 16 | *p++=hex[(n>>8)&0xF]; 17 | *p++=hex[(n>>4)&0xF]; 18 | *p = hex[n&0xF]; 19 | } 20 | 21 | tree* 22 | heredoc(tree *tag) 23 | { 24 | struct here *h = new(struct here); 25 | if(tag->type!=WORD) 26 | yyerror("Bad here tag"); 27 | h->next = 0; 28 | if(here) 29 | *ehere = h; 30 | else 31 | here = h; 32 | ehere=&h->next; 33 | h->tag = tag; 34 | hexnum(&tmp[9], getpid()); 35 | hexnum(&tmp[14], ser++); 36 | h->name = strdup(tmp); 37 | return token(tmp, WORD); 38 | } 39 | /* 40 | * bug: lines longer than NLINE get split -- this can cause spurious 41 | * missubstitution, or a misrecognized EOF marker. 42 | */ 43 | #define NLINE 4096 44 | 45 | void 46 | readhere(void) 47 | { 48 | struct here *h, *nexth; 49 | io *f; 50 | char *s, *tag; 51 | int c, subst; 52 | char line[NLINE+1]; 53 | for(h = here;h;h = nexth){ 54 | subst=!h->tag->quoted; 55 | tag = h->tag->str; 56 | c = Creat(h->name); 57 | if(c<0) 58 | yyerror("can't create here document"); 59 | f = openfd(c); 60 | s = line; 61 | pprompt(); 62 | while((c = rchr(runq->cmdfd))!=EOF){ 63 | if(c=='\n' || s==&line[NLINE]){ 64 | *s='\0'; 65 | if(tag && strcmp(line, tag)==0) break; 66 | if(subst) 67 | psubst(f, line); 68 | else pstr(f, line); 69 | s = line; 70 | if(c=='\n'){ 71 | pprompt(); 72 | pchr(f, c); 73 | } 74 | else *s++=c; 75 | } 76 | else *s++=c; 77 | } 78 | flush(f); 79 | closeio(f); 80 | cleanhere(h->name); 81 | nexth = h->next; 82 | efree((char *)h); 83 | } 84 | here = 0; 85 | doprompt = 1; 86 | } 87 | 88 | void 89 | psubst(io *f, char *s) 90 | { 91 | char *t, *u; 92 | int savec, n; 93 | word *star; 94 | while(*s){ 95 | if(*s!='$'){ 96 | if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){ 97 | pchr(f, *s++); 98 | if(*s=='\0') 99 | break; 100 | } 101 | else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){ 102 | pchr(f, *s++); 103 | if(*s=='\0') 104 | break; 105 | pchr(f, *s++); 106 | if(*s=='\0') 107 | break; 108 | } 109 | pchr(f, *s++); 110 | } 111 | else{ 112 | t=++s; 113 | if(*t=='$') 114 | pchr(f, *t++); 115 | else{ 116 | while(*t && idchr(*t)) t++; 117 | savec=*t; 118 | *t='\0'; 119 | n = 0; 120 | for(u = s;*u && '0'<=*u && *u<='9';u++) n = n*10+*u-'0'; 121 | if(n && *u=='\0'){ 122 | star = vlook("*")->val; 123 | if(star && 1<=n && n<=count(star)){ 124 | while(--n) star = star->next; 125 | pstr(f, star->word); 126 | } 127 | } 128 | else 129 | pstrs(f, vlook(s)->val); 130 | *t = savec; 131 | if(savec=='^') 132 | t++; 133 | } 134 | s = t; 135 | } 136 | } 137 | } 138 | 139 | void 140 | pstrs(io *f, word *a) 141 | { 142 | if(a){ 143 | while(a->next && a->next->word){ 144 | pstr(f, a->word); 145 | pchr(f, ' '); 146 | a = a->next; 147 | } 148 | pstr(f, a->word); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /history.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rc.h" 3 | #include "exec.h" 4 | #include "io.h" 5 | #include "fns.h" 6 | int pfmtnest = 0; 7 | 8 | void 9 | pfmt(io *f, char *fmt, ...) 10 | { 11 | va_list ap; 12 | char err[ERRMAX]; 13 | va_start(ap, fmt); 14 | pfmtnest++; 15 | for(;*fmt;fmt++) 16 | if(*fmt!='%') 17 | pchr(f, *fmt); 18 | else switch(*++fmt){ 19 | case '\0': 20 | va_end(ap); 21 | return; 22 | case 'c': 23 | pchr(f, va_arg(ap, int)); 24 | break; 25 | case 'd': 26 | pdec(f, va_arg(ap, int)); 27 | break; 28 | case 'o': 29 | poct(f, va_arg(ap, unsigned)); 30 | break; 31 | case 'p': 32 | pptr(f, va_arg(ap, void*)); 33 | break; 34 | case 'Q': 35 | pquo(f, va_arg(ap, char *)); 36 | break; 37 | case 'q': 38 | pwrd(f, va_arg(ap, char *)); 39 | break; 40 | case 'r': 41 | rerrstr(err, sizeof err); pstr(f, err); 42 | break; 43 | case 's': 44 | pstr(f, va_arg(ap, char *)); 45 | break; 46 | case 't': 47 | pcmd(f, va_arg(ap, struct tree *)); 48 | break; 49 | case 'v': 50 | pval(f, va_arg(ap, struct word *)); 51 | break; 52 | default: 53 | pchr(f, *fmt); 54 | break; 55 | } 56 | va_end(ap); 57 | if(--pfmtnest==0) 58 | flush(f); 59 | } 60 | 61 | void 62 | pchr(io *b, int c) 63 | { 64 | if(b->bufp==b->ebuf) 65 | fullbuf(b, c); 66 | else *b->bufp++=c; 67 | } 68 | 69 | int 70 | rchr(io *b) 71 | { 72 | if(b->bufp==b->ebuf) 73 | return emptybuf(b); 74 | return *b->bufp++ & 0xFF; 75 | } 76 | 77 | void 78 | pquo(io *f, char *s) 79 | { 80 | pchr(f, '\''); 81 | for(;*s;s++) 82 | if(*s=='\'') 83 | pfmt(f, "''"); 84 | else pchr(f, *s); 85 | pchr(f, '\''); 86 | } 87 | 88 | void 89 | pwrd(io *f, char *s) 90 | { 91 | char *t; 92 | for(t = s;*t;t++) if(!wordchr(*t)) break; 93 | if(t==s || *t) 94 | pquo(f, s); 95 | else pstr(f, s); 96 | } 97 | 98 | void 99 | pptr(io *f, void *v) 100 | { 101 | int n; 102 | uintptr p; 103 | 104 | p = (uintptr)v; 105 | if(sizeof(uintptr) == sizeof(uvlong) && p>>32) 106 | for(n = 60;n>=32;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); 107 | 108 | for(n = 28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); 109 | } 110 | 111 | void 112 | pstr(io *f, char *s) 113 | { 114 | if(s==0) 115 | s="(null)"; 116 | while(*s) pchr(f, *s++); 117 | } 118 | 119 | void 120 | pdec(io *f, int n) 121 | { 122 | if(n<0){ 123 | if(n!=INT_MIN){ 124 | pchr(f, '-'); 125 | pdec(f, -n); 126 | return; 127 | } 128 | /* n is two's complement minimum integer */ 129 | n = -(INT_MIN+1); 130 | pchr(f, '-'); 131 | pdec(f, n/10); 132 | pchr(f, n%10+'1'); 133 | return; 134 | } 135 | if(n>9) 136 | pdec(f, n/10); 137 | pchr(f, n%10+'0'); 138 | } 139 | 140 | void 141 | poct(io *f, unsigned n) 142 | { 143 | if(n>7) 144 | poct(f, n>>3); 145 | pchr(f, (n&7)+'0'); 146 | } 147 | 148 | void 149 | pval(io *f, word *a) 150 | { 151 | if(a){ 152 | while(a->next && a->next->word){ 153 | pwrd(f, a->word); 154 | pchr(f, ' '); 155 | a = a->next; 156 | } 157 | pwrd(f, a->word); 158 | } 159 | } 160 | 161 | int 162 | fullbuf(io *f, int c) 163 | { 164 | flush(f); 165 | return *f->bufp++=c; 166 | } 167 | 168 | void 169 | flush(io *f) 170 | { 171 | int n; 172 | char *s; 173 | if(f->strp){ 174 | n = f->ebuf-f->strp; 175 | f->strp = realloc(f->strp, n+101); 176 | if(f->strp==0) 177 | panic("Can't realloc %d bytes in flush!", n+101); 178 | f->bufp = f->strp+n; 179 | f->ebuf = f->bufp+100; 180 | for(s = f->bufp;s<=f->ebuf;s++) *s='\0'; 181 | } 182 | else{ 183 | n = f->bufp-f->buf; 184 | if(n && Write(f->fd, f->buf, n) < 0){ 185 | Write(3, "Write error\n", 12); 186 | if(ntrap) 187 | dotrap(); 188 | } 189 | f->bufp = f->buf; 190 | f->ebuf = f->buf+NBUF; 191 | } 192 | } 193 | 194 | io* 195 | openfd(int fd) 196 | { 197 | io *f = new(struct io); 198 | f->fd = fd; 199 | f->bufp = f->ebuf = f->buf; 200 | f->strp = 0; 201 | return f; 202 | } 203 | 204 | io* 205 | openstr(void) 206 | { 207 | io *f = new(struct io); 208 | char *s; 209 | f->fd=-1; 210 | f->bufp = f->strp = emalloc(101); 211 | f->ebuf = f->bufp+100; 212 | for(s = f->bufp;s<=f->ebuf;s++) *s='\0'; 213 | return f; 214 | } 215 | /* 216 | * Open a corebuffer to read. EOF occurs after reading len 217 | * characters from buf. 218 | */ 219 | 220 | io* 221 | opencore(char *s, int len) 222 | { 223 | io *f = new(struct io); 224 | char *buf = emalloc(len); 225 | f->fd= -1 /*open("/dev/null", 0)*/; 226 | f->bufp = f->strp = buf; 227 | f->ebuf = buf+len; 228 | Memcpy(buf, s, len); 229 | return f; 230 | } 231 | 232 | void 233 | iorewind(io *io) 234 | { 235 | if(io->fd==-1) 236 | io->bufp = io->strp; 237 | else{ 238 | io->bufp = io->ebuf = io->buf; 239 | Seek(io->fd, 0L, 0); 240 | } 241 | } 242 | 243 | void 244 | closeio(io *io) 245 | { 246 | if(io->fd>=0) 247 | close(io->fd); 248 | if(io->strp) 249 | efree(io->strp); 250 | efree((char *)io); 251 | } 252 | 253 | int 254 | emptybuf(io *f) 255 | { 256 | int n; 257 | if(f->fd==-1 || (n = Read(f->fd, f->buf, NBUF))<=0) return EOF; 258 | f->bufp = f->buf; 259 | f->ebuf = f->buf+n; 260 | return *f->bufp++&0xff; 261 | } 262 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * on Mac OS X, err is something else, 3 | * and assigning to it causes a bus error. 4 | * what a crappy linker. 5 | */ 6 | #define err rc_err 7 | #define EOF (-1) 8 | #define NBUF 512 9 | struct io{ 10 | int fd; 11 | char *bufp, *ebuf, *strp, buf[NBUF]; 12 | }; 13 | io *err; 14 | io *openfd(int), *openstr(void), *opencore(char *, int); 15 | int emptybuf(io*); 16 | void pchr(io*, int); 17 | int rchr(io*); 18 | void closeio(io*); 19 | void flush(io*); 20 | int fullbuf(io*, int); 21 | void pdec(io*, int); 22 | void poct(io*, unsigned); 23 | void pptr(io*, void*); 24 | void pquo(io*, char*); 25 | void pwrd(io*, char*); 26 | void pstr(io*, char*); 27 | void pcmd(io*, tree*); 28 | void pval(io*, word*); 29 | void pfnc(io*, thread*); 30 | void pfmt(io*, char*, ...); 31 | -------------------------------------------------------------------------------- /lex.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "io.h" 4 | #include "getflags.h" 5 | #include "fns.h" 6 | int getnext(void); 7 | 8 | int 9 | wordchr(int c) 10 | { 11 | return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; 12 | } 13 | 14 | int 15 | idchr(int c) 16 | { 17 | /* 18 | * Formerly: 19 | * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' 20 | * || c=='_' || c=='*'; 21 | */ 22 | return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); 23 | } 24 | int future = EOF; 25 | int doprompt = 1; 26 | int inquote; 27 | int incomm; 28 | /* 29 | * Look ahead in the input stream 30 | */ 31 | 32 | int 33 | nextc(void) 34 | { 35 | if(future==EOF) 36 | future = getnext(); 37 | return future; 38 | } 39 | /* 40 | * Consume the lookahead character. 41 | */ 42 | 43 | int 44 | advance(void) 45 | { 46 | int c = nextc(); 47 | lastc = future; 48 | future = EOF; 49 | return c; 50 | } 51 | /* 52 | * read a character from the input stream 53 | */ 54 | 55 | int 56 | getnext(void) 57 | { 58 | int c; 59 | static int peekc = EOF; 60 | if(peekc!=EOF){ 61 | c = peekc; 62 | peekc = EOF; 63 | return c; 64 | } 65 | if(runq->eof) 66 | return EOF; 67 | if(doprompt) 68 | pprompt(); 69 | c = rchr(runq->cmdfd); 70 | if(!inquote && c=='\\'){ 71 | c = rchr(runq->cmdfd); 72 | if(c=='\n' && !incomm){ /* don't continue a comment */ 73 | doprompt = 1; 74 | c=' '; 75 | } 76 | else{ 77 | peekc = c; 78 | c='\\'; 79 | } 80 | } 81 | doprompt = doprompt || c=='\n' || c==EOF; 82 | if(c==EOF) 83 | runq->eof++; 84 | else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c); 85 | return c; 86 | } 87 | 88 | void 89 | skipwhite(void) 90 | { 91 | int c; 92 | for(;;){ 93 | c = nextc(); 94 | /* Why did this used to be if(!inquote && c=='#') ?? */ 95 | if(c=='#'){ 96 | incomm = 1; 97 | for(;;){ 98 | c = nextc(); 99 | if(c=='\n' || c==EOF) { 100 | incomm = 0; 101 | break; 102 | } 103 | advance(); 104 | } 105 | } 106 | if(c==' ' || c=='\t') 107 | advance(); 108 | else return; 109 | } 110 | } 111 | 112 | void 113 | skipnl(void) 114 | { 115 | int c; 116 | for(;;){ 117 | skipwhite(); 118 | c = nextc(); 119 | if(c!='\n') 120 | return; 121 | advance(); 122 | } 123 | } 124 | 125 | int 126 | nextis(int c) 127 | { 128 | if(nextc()==c){ 129 | advance(); 130 | return 1; 131 | } 132 | return 0; 133 | } 134 | 135 | char* 136 | addtok(char *p, int val) 137 | { 138 | if(p==0) 139 | return 0; 140 | if(p==&tok[NTOK-1]){ 141 | *p = 0; 142 | yyerror("token buffer too short"); 143 | return 0; 144 | } 145 | *p++=val; 146 | return p; 147 | } 148 | 149 | char* 150 | addutf(char *p, int c) 151 | { 152 | p = addtok(p, c); 153 | if(twobyte(c)) /* 2-byte escape */ 154 | return addtok(p, advance()); 155 | if(threebyte(c)){ /* 3-byte escape */ 156 | p = addtok(p, advance()); 157 | return addtok(p, advance()); 158 | } 159 | if(fourbyte(c)){ /* 4-byte escape */ 160 | p = addtok(p, advance()); 161 | p = addtok(p, advance()); 162 | return addtok(p, advance()); 163 | } 164 | return p; 165 | } 166 | int lastdol; /* was the last token read '$' or '$#' or '"'? */ 167 | int lastword; /* was the last token read a word or compound word terminator? */ 168 | 169 | int 170 | yylex(void) 171 | { 172 | int c, d = nextc(); 173 | char *w = tok; 174 | struct tree *t; 175 | yylval.tree = 0; 176 | /* 177 | * Embarassing sneakiness: if the last token read was a quoted or unquoted 178 | * WORD then we alter the meaning of what follows. If the next character 179 | * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, 180 | * if the next character is the first character of a simple or compound word, 181 | * we insert a `^' before it. 182 | */ 183 | if(lastword){ 184 | lastword = 0; 185 | if(d=='('){ 186 | advance(); 187 | strcpy(tok, "( [SUB]"); 188 | return SUB; 189 | } 190 | if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ 191 | strcpy(tok, "^"); 192 | return '^'; 193 | } 194 | } 195 | inquote = 0; 196 | skipwhite(); 197 | switch(c = advance()){ 198 | case EOF: 199 | lastdol = 0; 200 | strcpy(tok, "EOF"); 201 | return EOF; 202 | case '$': 203 | lastdol = 1; 204 | if(nextis('#')){ 205 | strcpy(tok, "$#"); 206 | return COUNT; 207 | } 208 | if(nextis('"')){ 209 | strcpy(tok, "$\""); 210 | return '"'; 211 | } 212 | strcpy(tok, "$"); 213 | return '$'; 214 | case '&': 215 | lastdol = 0; 216 | if(nextis('&')){ 217 | skipnl(); 218 | strcpy(tok, "&&"); 219 | return ANDAND; 220 | } 221 | strcpy(tok, "&"); 222 | return '&'; 223 | case '|': 224 | lastdol = 0; 225 | if(nextis(c)){ 226 | skipnl(); 227 | strcpy(tok, "||"); 228 | return OROR; 229 | } 230 | case '<': 231 | case '>': 232 | lastdol = 0; 233 | /* 234 | * funny redirection tokens: 235 | * redir: arrow | arrow '[' fd ']' 236 | * arrow: '<' | '<<' | '>' | '>>' | '|' 237 | * fd: digit | digit '=' | digit '=' digit 238 | * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' 239 | * some possibilities are nonsensical and get a message. 240 | */ 241 | *w++=c; 242 | t = newtree(); 243 | switch(c){ 244 | case '|': 245 | t->type = PIPE; 246 | t->fd0 = 1; 247 | t->fd1 = 0; 248 | break; 249 | case '>': 250 | t->type = REDIR; 251 | if(nextis(c)){ 252 | t->rtype = APPEND; 253 | *w++=c; 254 | } 255 | else t->rtype = WRITE; 256 | t->fd0 = 1; 257 | break; 258 | case '<': 259 | t->type = REDIR; 260 | if(nextis(c)){ 261 | t->rtype = HERE; 262 | *w++=c; 263 | } else if (nextis('>')){ 264 | t->rtype = RDWR; 265 | *w++=c; 266 | } else t->rtype = READ; 267 | t->fd0 = 0; 268 | break; 269 | } 270 | if(nextis('[')){ 271 | *w++='['; 272 | c = advance(); 273 | *w++=c; 274 | if(c<'0' || '9'type==PIPE?"pipe syntax" 278 | :"redirection syntax"); 279 | return EOF; 280 | } 281 | t->fd0 = 0; 282 | do{ 283 | t->fd0 = t->fd0*10+c-'0'; 284 | *w++=c; 285 | c = advance(); 286 | }while('0'<=c && c<='9'); 287 | if(c=='='){ 288 | *w++='='; 289 | if(t->type==REDIR) 290 | t->type = DUP; 291 | c = advance(); 292 | if('0'<=c && c<='9'){ 293 | t->rtype = DUPFD; 294 | t->fd1 = t->fd0; 295 | t->fd0 = 0; 296 | do{ 297 | t->fd0 = t->fd0*10+c-'0'; 298 | *w++=c; 299 | c = advance(); 300 | }while('0'<=c && c<='9'); 301 | } 302 | else{ 303 | if(t->type==PIPE) 304 | goto RedirErr; 305 | t->rtype = CLOSE; 306 | } 307 | } 308 | if(c!=']' 309 | || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) 310 | goto RedirErr; 311 | *w++=']'; 312 | } 313 | *w='\0'; 314 | yylval.tree = t; 315 | if(t->type==PIPE) 316 | skipnl(); 317 | return t->type; 318 | case '\'': 319 | lastdol = 0; 320 | lastword = 1; 321 | inquote = 1; 322 | for(;;){ 323 | c = advance(); 324 | if(c==EOF) 325 | break; 326 | if(c=='\''){ 327 | if(nextc()!='\'') 328 | break; 329 | advance(); 330 | } 331 | w = addutf(w, c); 332 | } 333 | if(w!=0) 334 | *w='\0'; 335 | t = token(tok, WORD); 336 | t->quoted = 1; 337 | yylval.tree = t; 338 | return t->type; 339 | } 340 | if(!wordchr(c)){ 341 | lastdol = 0; 342 | tok[0] = c; 343 | tok[1]='\0'; 344 | return c; 345 | } 346 | for(;;){ 347 | /* next line should have (char)c==GLOB, but ken's compiler is broken */ 348 | if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) 349 | w = addtok(w, GLOB); 350 | w = addutf(w, c); 351 | c = nextc(); 352 | if(lastdol?!idchr(c):!wordchr(c)) break; 353 | advance(); 354 | } 355 | 356 | lastword = 1; 357 | lastdol = 0; 358 | if(w!=0) 359 | *w='\0'; 360 | t = klook(tok); 361 | if(t->type!=WORD) 362 | lastword = 0; 363 | t->quoted = 0; 364 | yylval.tree = t; 365 | return t->type; 366 | } 367 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | <$PLAN9/src/mkhdr 2 | 3 | TARG=rc 4 | 5 | OFILES=\ 6 | code.$O\ 7 | exec.$O\ 8 | getflags.$O\ 9 | glob.$O\ 10 | here.$O\ 11 | io.$O\ 12 | lex.$O\ 13 | pcmd.$O\ 14 | pfnc.$O\ 15 | simple.$O\ 16 | subr.$O\ 17 | trap.$O\ 18 | tree.$O\ 19 | unixcrap.$O\ 20 | var.$O\ 21 | y.tab.$O\ 22 | plan9ish.$O\ 23 | havefork.$O\ 24 | 25 | HFILES=\ 26 | rc.h\ 27 | x.tab.h\ 28 | io.h\ 29 | exec.h\ 30 | fns.h\ 31 | 32 | YFILES=syn.y 33 | 34 | <$PLAN9/src/mkone 35 | 36 | x.tab.h: y.tab.h 37 | cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h 38 | -------------------------------------------------------------------------------- /pcmd.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "io.h" 3 | #include "fns.h" 4 | char nl='\n'; /* change to semicolon for bourne-proofing */ 5 | #define c0 t->child[0] 6 | #define c1 t->child[1] 7 | #define c2 t->child[2] 8 | 9 | void 10 | pdeglob(io *f, char *s) 11 | { 12 | while(*s){ 13 | if(*s==GLOB) 14 | s++; 15 | pchr(f, *s++); 16 | } 17 | } 18 | 19 | void 20 | pcmd(io *f, tree *t) 21 | { 22 | if(t==0) 23 | return; 24 | switch(t->type){ 25 | default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); 26 | break; 27 | case '$': pfmt(f, "$%t", c0); 28 | break; 29 | case '"': pfmt(f, "$\"%t", c0); 30 | break; 31 | case '&': pfmt(f, "%t&", c0); 32 | break; 33 | case '^': pfmt(f, "%t^%t", c0, c1); 34 | break; 35 | case '`': pfmt(f, "`%t", c0); 36 | break; 37 | case ANDAND: pfmt(f, "%t && %t", c0, c1); 38 | break; 39 | case BANG: pfmt(f, "! %t", c0); 40 | break; 41 | case BRACE: pfmt(f, "{%t}", c0); 42 | break; 43 | case COUNT: pfmt(f, "$#%t", c0); 44 | break; 45 | case FN: pfmt(f, "fn %t %t", c0, c1); 46 | break; 47 | case IF: pfmt(f, "if%t%t", c0, c1); 48 | break; 49 | case NOT: pfmt(f, "if not %t", c0); 50 | break; 51 | case OROR: pfmt(f, "%t || %t", c0, c1); 52 | break; 53 | case PCMD: 54 | case PAREN: pfmt(f, "(%t)", c0); 55 | break; 56 | case SUB: pfmt(f, "$%t(%t)", c0, c1); 57 | break; 58 | case SIMPLE: pfmt(f, "%t", c0); 59 | break; 60 | case SUBSHELL: pfmt(f, "@ %t", c0); 61 | break; 62 | case SWITCH: pfmt(f, "switch %t %t", c0, c1); 63 | break; 64 | case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); 65 | break; 66 | case WHILE: pfmt(f, "while %t%t", c0, c1); 67 | break; 68 | case ARGLIST: 69 | if(c0==0) 70 | pfmt(f, "%t", c1); 71 | else if(c1==0) 72 | pfmt(f, "%t", c0); 73 | else 74 | pfmt(f, "%t %t", c0, c1); 75 | break; 76 | case ';': 77 | if(c0){ 78 | if(c1) 79 | pfmt(f, "%t%c%t", c0, nl, c1); 80 | else pfmt(f, "%t", c0); 81 | } 82 | else pfmt(f, "%t", c1); 83 | break; 84 | case WORDS: 85 | if(c0) 86 | pfmt(f, "%t ", c0); 87 | pfmt(f, "%t", c1); 88 | break; 89 | case FOR: 90 | pfmt(f, "for(%t", c0); 91 | if(c1) 92 | pfmt(f, " in %t", c1); 93 | pfmt(f, ")%t", c2); 94 | break; 95 | case WORD: 96 | if(t->quoted) 97 | pfmt(f, "%Q", t->str); 98 | else pdeglob(f, t->str); 99 | break; 100 | case DUP: 101 | if(t->rtype==DUPFD) 102 | pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ 103 | else 104 | pfmt(f, ">[%d=]", t->fd0); 105 | pfmt(f, "%t", c1); 106 | break; 107 | case PIPEFD: 108 | case REDIR: 109 | switch(t->rtype){ 110 | case HERE: 111 | pchr(f, '<'); 112 | case READ: 113 | case RDWR: 114 | pchr(f, '<'); 115 | if(t->rtype==RDWR) 116 | pchr(f, '>'); 117 | if(t->fd0!=0) 118 | pfmt(f, "[%d]", t->fd0); 119 | break; 120 | case APPEND: 121 | pchr(f, '>'); 122 | case WRITE: 123 | pchr(f, '>'); 124 | if(t->fd0!=1) 125 | pfmt(f, "[%d]", t->fd0); 126 | break; 127 | } 128 | pfmt(f, "%t", c0); 129 | if(c1) 130 | pfmt(f, " %t", c1); 131 | break; 132 | case '=': 133 | pfmt(f, "%t=%t", c0, c1); 134 | if(c2) 135 | pfmt(f, " %t", c2); 136 | break; 137 | case PIPE: 138 | pfmt(f, "%t|", c0); 139 | if(t->fd1==0){ 140 | if(t->fd0!=1) 141 | pfmt(f, "[%d]", t->fd0); 142 | } 143 | else pfmt(f, "[%d=%d]", t->fd0, t->fd1); 144 | pfmt(f, "%t", c1); 145 | break; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /pfnc.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "io.h" 4 | #include "fns.h" 5 | struct{ 6 | void (*f)(void); 7 | char *name; 8 | }fname[] = { 9 | Xappend, "Xappend", 10 | Xasync, "Xasync", 11 | Xbang, "Xbang", 12 | Xclose, "Xclose", 13 | Xdup, "Xdup", 14 | Xeflag, "Xeflag", 15 | Xexit, "Xexit", 16 | Xfalse, "Xfalse", 17 | Xifnot, "Xifnot", 18 | Xjump, "Xjump", 19 | Xmark, "Xmark", 20 | Xpopm, "Xpopm", 21 | Xrdwr, "Xrdwr", 22 | Xread, "Xread", 23 | Xreturn, "Xreturn", 24 | Xtrue, "Xtrue", 25 | Xif, "Xif", 26 | Xwastrue, "Xwastrue", 27 | Xword, "Xword", 28 | Xwrite, "Xwrite", 29 | Xmatch, "Xmatch", 30 | Xcase, "Xcase", 31 | Xconc, "Xconc", 32 | Xassign, "Xassign", 33 | Xdol, "Xdol", 34 | Xcount, "Xcount", 35 | Xlocal, "Xlocal", 36 | Xunlocal, "Xunlocal", 37 | Xfn, "Xfn", 38 | Xdelfn, "Xdelfn", 39 | Xpipe, "Xpipe", 40 | Xpipewait, "Xpipewait", 41 | Xrdcmds, "Xrdcmds", 42 | (void (*)(void))Xerror, "Xerror", 43 | Xbackq, "Xbackq", 44 | Xpipefd, "Xpipefd", 45 | Xsubshell, "Xsubshell", 46 | Xdelhere, "Xdelhere", 47 | Xfor, "Xfor", 48 | Xglob, "Xglob", 49 | Xrdfn, "Xrdfn", 50 | Xsimple, "Xsimple", 51 | Xrdfn, "Xrdfn", 52 | Xqdol, "Xqdol", 53 | 0}; 54 | 55 | void 56 | pfnc(io *fd, thread *t) 57 | { 58 | int i; 59 | void (*fn)(void) = t->code[t->pc].f; 60 | list *a; 61 | pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc); 62 | for(i = 0;fname[i].f;i++) if(fname[i].f==fn){ 63 | pstr(fd, fname[i].name); 64 | break; 65 | } 66 | if(!fname[i].f) 67 | pfmt(fd, "%p", fn); 68 | for(a = t->argv;a;a = a->next) pfmt(fd, " (%v)", a->words); 69 | pchr(fd, '\n'); 70 | flush(fd); 71 | } 72 | -------------------------------------------------------------------------------- /plan9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Plan 9 versions of system-specific functions 3 | * By convention, exported routines herein have names beginning with an 4 | * upper case letter. 5 | */ 6 | #include "rc.h" 7 | #include "exec.h" 8 | #include "io.h" 9 | #include "fns.h" 10 | #include "getflags.h" 11 | 12 | enum { 13 | Maxenvname = 256, /* undocumented limit */ 14 | }; 15 | 16 | char *Signame[] = { 17 | "sigexit", "sighup", "sigint", "sigquit", 18 | "sigalrm", "sigkill", "sigfpe", "sigterm", 19 | 0 20 | }; 21 | char *syssigname[] = { 22 | "exit", /* can't happen */ 23 | "hangup", 24 | "interrupt", 25 | "quit", /* can't happen */ 26 | "alarm", 27 | "kill", 28 | "sys: fp: ", 29 | "term", 30 | 0 31 | }; 32 | char *Rcmain = "/rc/lib/rcmain"; 33 | char *Fdprefix = "/fd/"; 34 | 35 | void execfinit(void); 36 | void execbind(void); 37 | void execmount(void); 38 | void execnewpgrp(void); 39 | 40 | builtin Builtin[] = { 41 | "cd", execcd, 42 | "whatis", execwhatis, 43 | "eval", execeval, 44 | "exec", execexec, /* but with popword first */ 45 | "exit", execexit, 46 | "shift", execshift, 47 | "wait", execwait, 48 | ".", execdot, 49 | "finit", execfinit, 50 | "flag", execflag, 51 | "rfork", execnewpgrp, 52 | 0 53 | }; 54 | 55 | void 56 | execnewpgrp(void) 57 | { 58 | int arg; 59 | char *s; 60 | switch(count(runq->argv->words)){ 61 | case 1: 62 | arg = RFENVG|RFNAMEG|RFNOTEG; 63 | break; 64 | case 2: 65 | arg = 0; 66 | for(s = runq->argv->words->next->word;*s;s++) switch(*s){ 67 | default: 68 | goto Usage; 69 | case 'n': 70 | arg|=RFNAMEG; break; 71 | case 'N': 72 | arg|=RFCNAMEG; 73 | break; 74 | case 'm': 75 | arg|=RFNOMNT; break; 76 | case 'e': 77 | arg|=RFENVG; break; 78 | case 'E': 79 | arg|=RFCENVG; break; 80 | case 's': 81 | arg|=RFNOTEG; break; 82 | case 'f': 83 | arg|=RFFDG; break; 84 | case 'F': 85 | arg|=RFCFDG; break; 86 | } 87 | break; 88 | default: 89 | Usage: 90 | pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word); 91 | setstatus("rfork usage"); 92 | poplist(); 93 | return; 94 | } 95 | if(rfork(arg)==-1){ 96 | pfmt(err, "rc: %s failed\n", runq->argv->words->word); 97 | setstatus("rfork failed"); 98 | } 99 | else 100 | setstatus(""); 101 | poplist(); 102 | } 103 | 104 | void 105 | Vinit(void) 106 | { 107 | int dir, f, len, i, n, nent; 108 | char *buf, *s; 109 | char envname[Maxenvname]; 110 | word *val; 111 | Dir *ent; 112 | 113 | dir = open("/env", OREAD); 114 | if(dir<0){ 115 | pfmt(err, "rc: can't open /env: %r\n"); 116 | return; 117 | } 118 | ent = nil; 119 | for(;;){ 120 | nent = dirread(dir, &ent); 121 | if(nent <= 0) 122 | break; 123 | for(i = 0; i=0){ 128 | buf = emalloc(len+1); 129 | n = readn(f, buf, len); 130 | if (n <= 0) 131 | buf[0] = '\0'; 132 | else 133 | buf[n] = '\0'; 134 | val = 0; 135 | /* Charitably add a 0 at the end if need be */ 136 | if(buf[len-1]) 137 | buf[len++]='\0'; 138 | s = buf+len-1; 139 | for(;;){ 140 | while(s!=buf && s[-1]!='\0') --s; 141 | val = newword(s, val); 142 | if(s==buf) 143 | break; 144 | --s; 145 | } 146 | setvar(ent[i].name, val); 147 | vlook(ent[i].name)->changed = 0; 148 | close(f); 149 | efree(buf); 150 | } 151 | } 152 | } 153 | free(ent); 154 | } 155 | close(dir); 156 | } 157 | int envdir; 158 | 159 | void 160 | Xrdfn(void) 161 | { 162 | int f, len; 163 | Dir *e; 164 | char envname[Maxenvname]; 165 | static Dir *ent, *allocent; 166 | static int nent; 167 | 168 | for(;;){ 169 | if(nent == 0){ 170 | free(allocent); 171 | nent = dirread(envdir, &allocent); 172 | ent = allocent; 173 | } 174 | if(nent <= 0) 175 | break; 176 | while(nent){ 177 | e = ent++; 178 | nent--; 179 | len = e->length; 180 | if(len && strncmp(e->name, "fn#", 3)==0){ 181 | snprint(envname, sizeof envname, "/env/%s", e->name); 182 | if((f = open(envname, 0))>=0){ 183 | execcmds(openfd(f)); 184 | return; 185 | } 186 | } 187 | } 188 | } 189 | close(envdir); 190 | Xreturn(); 191 | } 192 | union code rdfns[4]; 193 | 194 | void 195 | execfinit(void) 196 | { 197 | static int first = 1; 198 | if(first){ 199 | rdfns[0].i = 1; 200 | rdfns[1].f = Xrdfn; 201 | rdfns[2].f = Xjump; 202 | rdfns[3].i = 1; 203 | first = 0; 204 | } 205 | Xpopm(); 206 | envdir = open("/env", 0); 207 | if(envdir<0){ 208 | pfmt(err, "rc: can't open /env: %r\n"); 209 | return; 210 | } 211 | start(rdfns, 1, runq->local); 212 | } 213 | 214 | int 215 | Waitfor(int pid, int) 216 | { 217 | thread *p; 218 | Waitmsg *w; 219 | char errbuf[ERRMAX]; 220 | 221 | if(pid >= 0 && !havewaitpid(pid)) 222 | return 0; 223 | 224 | while((w = wait()) != nil){ 225 | delwaitpid(w->pid); 226 | if(w->pid==pid){ 227 | setstatus(w->msg); 228 | free(w); 229 | return 0; 230 | } 231 | for(p = runq->ret;p;p = p->ret) 232 | if(p->pid==w->pid){ 233 | p->pid=-1; 234 | strcpy(p->status, w->msg); 235 | } 236 | free(w); 237 | } 238 | 239 | errstr(errbuf, sizeof errbuf); 240 | if(strcmp(errbuf, "interrupted")==0) return -1; 241 | return 0; 242 | } 243 | 244 | char ** 245 | mkargv(word *a) 246 | { 247 | char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); 248 | char **argp = argv+1; /* leave one at front for runcoms */ 249 | for(;a;a = a->next) *argp++=a->word; 250 | *argp = 0; 251 | return argv; 252 | } 253 | 254 | void 255 | addenv(var *v) 256 | { 257 | char envname[Maxenvname]; 258 | word *w; 259 | int f; 260 | io *fd; 261 | if(v->changed){ 262 | v->changed = 0; 263 | snprint(envname, sizeof envname, "/env/%s", v->name); 264 | if((f = Creat(envname))<0) 265 | pfmt(err, "rc: can't open %s: %r\n", envname); 266 | else{ 267 | for(w = v->val;w;w = w->next) 268 | write(f, w->word, strlen(w->word)+1L); 269 | close(f); 270 | } 271 | } 272 | if(v->fnchanged){ 273 | v->fnchanged = 0; 274 | snprint(envname, sizeof envname, "/env/fn#%s", v->name); 275 | if((f = Creat(envname))<0) 276 | pfmt(err, "rc: can't open %s: %r\n", envname); 277 | else{ 278 | if(v->fn){ 279 | fd = openfd(f); 280 | pfmt(fd, "fn %q %s\n", v->name, v->fn[v->pc-1].s); 281 | closeio(fd); 282 | } 283 | close(f); 284 | } 285 | } 286 | } 287 | 288 | void 289 | updenvlocal(var *v) 290 | { 291 | if(v){ 292 | updenvlocal(v->next); 293 | addenv(v); 294 | } 295 | } 296 | 297 | void 298 | Updenv(void) 299 | { 300 | var *v, **h; 301 | for(h = gvar;h!=&gvar[NVAR];h++) 302 | for(v=*h;v;v = v->next) 303 | addenv(v); 304 | if(runq) 305 | updenvlocal(runq->local); 306 | } 307 | 308 | /* not used on plan 9 */ 309 | int 310 | ForkExecute(char *file, char **argv, int sin, int sout, int serr) 311 | { 312 | int pid; 313 | 314 | if(access(file, 1) != 0) 315 | return -1; 316 | switch(pid = fork()){ 317 | case -1: 318 | return -1; 319 | case 0: 320 | if(sin >= 0) 321 | dup(sin, 0); 322 | else 323 | close(0); 324 | if(sout >= 0) 325 | dup(sout, 1); 326 | else 327 | close(1); 328 | if(serr >= 0) 329 | dup(serr, 2); 330 | else 331 | close(2); 332 | exec(file, argv); 333 | exits(file); 334 | } 335 | return pid; 336 | } 337 | 338 | void 339 | Execute(word *args, word *path) 340 | { 341 | char **argv = mkargv(args); 342 | char file[1024], errstr[1024]; 343 | int nc; 344 | 345 | Updenv(); 346 | errstr[0] = '\0'; 347 | for(;path;path = path->next){ 348 | nc = strlen(path->word); 349 | if(nc < sizeof file - 1){ /* 1 for / */ 350 | strcpy(file, path->word); 351 | if(file[0]){ 352 | strcat(file, "/"); 353 | nc++; 354 | } 355 | if(nc + strlen(argv[1]) < sizeof file){ 356 | strcat(file, argv[1]); 357 | exec(file, argv+1); 358 | rerrstr(errstr, sizeof errstr); 359 | /* 360 | * if file exists and is executable, exec should 361 | * have worked, unless it's a directory or an 362 | * executable for another architecture. in 363 | * particular, if it failed due to lack of 364 | * swap/vm (e.g., arg. list too long) or other 365 | * allocation failure, stop searching and print 366 | * the reason for failure. 367 | */ 368 | if (strstr(errstr, " allocat") != nil || 369 | strstr(errstr, " full") != nil) 370 | break; 371 | } 372 | else werrstr("command name too long"); 373 | } 374 | } 375 | pfmt(err, "%s: %s\n", argv[1], errstr); 376 | efree((char *)argv); 377 | } 378 | #define NDIR 256 /* shoud be a better way */ 379 | 380 | int 381 | Globsize(char *p) 382 | { 383 | int isglob = 0, globlen = NDIR+1; 384 | for(;*p;p++){ 385 | if(*p==GLOB){ 386 | p++; 387 | if(*p!=GLOB) 388 | isglob++; 389 | globlen+=*p=='*'?NDIR:1; 390 | } 391 | else 392 | globlen++; 393 | } 394 | return isglob?globlen:0; 395 | } 396 | #define NFD 50 397 | 398 | struct{ 399 | Dir *dbuf; 400 | int i; 401 | int n; 402 | }dir[NFD]; 403 | 404 | int 405 | Opendir(char *name) 406 | { 407 | Dir *db; 408 | int f; 409 | f = open(name, 0); 410 | if(f==-1) 411 | return f; 412 | db = dirfstat(f); 413 | if(db!=nil && (db->mode&DMDIR)){ 414 | if(f=NFD) 450 | return 0; 451 | Again: 452 | if(dir[f].i==dir[f].n){ /* read */ 453 | free(dir[f].dbuf); 454 | dir[f].dbuf = 0; 455 | n = dirread(f, &dir[f].dbuf); 456 | if(n>0){ 457 | if(onlydirs){ 458 | n = trimdirs(dir[f].dbuf, n); 459 | if(n == 0) 460 | goto Again; 461 | } 462 | dir[f].n = n; 463 | }else 464 | dir[f].n = 0; 465 | dir[f].i = 0; 466 | } 467 | if(dir[f].i == dir[f].n) 468 | return 0; 469 | strcpy(p, dir[f].dbuf[dir[f].i].name); 470 | dir[f].i++; 471 | return 1; 472 | } 473 | 474 | void 475 | Closedir(int f) 476 | { 477 | if(f>=0 && f=32){ /* rc is probably in a trap loop */ 503 | pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); 504 | abort(); 505 | } 506 | noted(NCONT); 507 | } 508 | 509 | void 510 | Trapinit(void) 511 | { 512 | notify(notifyf); 513 | } 514 | 515 | void 516 | Unlink(char *name) 517 | { 518 | remove(name); 519 | } 520 | 521 | long 522 | Write(int fd, void *buf, long cnt) 523 | { 524 | return write(fd, buf, cnt); 525 | } 526 | 527 | long 528 | Read(int fd, void *buf, long cnt) 529 | { 530 | return read(fd, buf, cnt); 531 | } 532 | 533 | long 534 | Seek(int fd, long cnt, long whence) 535 | { 536 | return seek(fd, cnt, whence); 537 | } 538 | 539 | int 540 | Executable(char *file) 541 | { 542 | Dir *statbuf; 543 | int ret; 544 | 545 | statbuf = dirstat(file); 546 | if(statbuf == nil) 547 | return 0; 548 | ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); 549 | free(statbuf); 550 | return ret; 551 | } 552 | 553 | int 554 | Creat(char *file) 555 | { 556 | return create(file, 1, 0666L); 557 | } 558 | 559 | int 560 | Dup(int a, int b) 561 | { 562 | return dup(a, b); 563 | } 564 | 565 | int 566 | Dup1(int) 567 | { 568 | return -1; 569 | } 570 | 571 | void 572 | Exit(char *stat) 573 | { 574 | Updenv(); 575 | setstatus(stat); 576 | exits(truestatus()?"":getstatus()); 577 | } 578 | 579 | int 580 | Eintr(void) 581 | { 582 | return interrupted; 583 | } 584 | 585 | void 586 | Noerror(void) 587 | { 588 | interrupted = 0; 589 | } 590 | 591 | int 592 | Isatty(int fd) 593 | { 594 | char buf[64]; 595 | 596 | if(fd2path(fd, buf, sizeof buf) != 0) 597 | return 0; 598 | 599 | /* might be #c/cons during boot - fixed 22 april 2005, remove this later */ 600 | if(strcmp(buf, "#c/cons") == 0) 601 | return 1; 602 | 603 | /* might be /mnt/term/dev/cons */ 604 | return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; 605 | } 606 | 607 | void 608 | Abort(void) 609 | { 610 | pfmt(err, "aborting\n"); 611 | flush(err); 612 | Exit("aborting"); 613 | } 614 | 615 | void 616 | Memcpy(void *a, void *b, long n) 617 | { 618 | memmove(a, b, n); 619 | } 620 | 621 | void* 622 | Malloc(ulong n) 623 | { 624 | return mallocz(n, 1); 625 | } 626 | 627 | int *waitpids; 628 | int nwaitpids; 629 | 630 | void 631 | addwaitpid(int pid) 632 | { 633 | waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); 634 | if(waitpids == 0) 635 | panic("Can't realloc %d waitpids", nwaitpids+1); 636 | waitpids[nwaitpids++] = pid; 637 | } 638 | 639 | void 640 | delwaitpid(int pid) 641 | { 642 | int r, w; 643 | 644 | for(r=w=0; riflag){ 11 | pstr(err, promptstr); 12 | flush(err); 13 | prompt = vlook("prompt"); 14 | if(prompt->val && prompt->val->next) 15 | promptstr = prompt->val->next->word; 16 | else 17 | promptstr="\t"; 18 | } 19 | runq->lineno++; 20 | doprompt = 0; 21 | } 22 | 23 | void 24 | exechistory(void) 25 | { 26 | setstatus("not implemented"); 27 | } 28 | -------------------------------------------------------------------------------- /prompt-readline.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "fns.h" 3 | #include "io.h" 4 | #include "exec.h" 5 | #include "getflags.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define DEBUG(f, a...) \ 14 | if(0){}else{pfmt(err, "\n%s: "f, __FUNCTION__, ## a);flush(err);} 15 | 16 | #define COMPLETE_FN "complete" 17 | #define COMPLETE_RESULTS "rc_complete_results" 18 | 19 | static char* 20 | matches_rl(const char *s, int i) 21 | { 22 | word *w; 23 | 24 | for(w=vlook(COMPLETE_RESULTS)->val; w; w=w->next, i--) 25 | if(i == 0) 26 | return strdup(w->word); 27 | return NULL; 28 | } 29 | 30 | static char** 31 | complete_rl(const char *text, int p0, int p1) 32 | { 33 | code complete[32]; 34 | thread *oldrunq; 35 | char **l; 36 | int i, dot; 37 | struct stat st; 38 | 39 | /* prevent completion fallback */ 40 | //rl_attempted_completion_over = 1; 41 | 42 | if(vlook(COMPLETE_FN)->fn == NULL) 43 | return NULL; 44 | 45 | rl_line_buffer[p1] = '\0'; 46 | oldrunq = runq; 47 | memset(complete, 0, sizeof complete); 48 | i = 0; 49 | complete[i++].i = 1; 50 | complete[i++].f = Xmark; 51 | complete[i++].f = Xbackq; 52 | dot = i; 53 | complete[i++].i = 0; 54 | complete[i++].f = Xmark; 55 | complete[i++].f = Xword; 56 | complete[i++].s = (char*)rl_line_buffer; 57 | complete[i++].f = Xword; 58 | complete[i++].s = COMPLETE_FN; 59 | complete[i++].f = Xsimple; 60 | complete[i++].f = Xexit; 61 | complete[dot].i = i; 62 | complete[i++].f = Xmark; 63 | complete[i++].f = Xword; 64 | complete[i++].s = COMPLETE_RESULTS; 65 | complete[i++].f = Xassign; 66 | complete[i++].f = Xreturn; 67 | start(complete, 1, NULL); 68 | 69 | while(runq != oldrunq){ 70 | if(flag['r']) pfnc(err, runq); 71 | (*runq->code[runq->pc++].f)(); 72 | if(ntrap) dotrap(); 73 | } 74 | 75 | l = rl_completion_matches(text, matches_rl); 76 | 77 | /* if dir append '/' if not already there */ 78 | if(l==NULL && stat(text, &st)==0 && S_ISDIR(st.st_mode) && text[(p1-p0)-1]!='/'){ 79 | l = emalloc(sizeof(char*)*2); 80 | l[0] = emalloc(strlen(text)+2); 81 | strcpy(l[0], text); 82 | strcat(l[0], "/"); 83 | } 84 | return l; 85 | } 86 | 87 | static void 88 | savehist(void) 89 | { 90 | write_history(NULL); 91 | } 92 | 93 | static void 94 | read_rl(void) 95 | { 96 | static int first = 1; 97 | io *f = runq->cmdfd; 98 | char *s; 99 | long n; 100 | 101 | if(first){ 102 | /* I/O */ 103 | rl_readline_name = "rc"; /* for .editrc */ 104 | rl_outstream = fdopen(err->fd, "w"); // stderr 105 | 106 | /* signals */ 107 | // rl_catch_signals = 0; 108 | 109 | /* complete */ 110 | rl_completion_append_character = '\0'; 111 | rl_completer_quote_characters = "'"; 112 | rl_attempted_completion_function = complete_rl; 113 | 114 | /* history */ 115 | if (read_history(NULL)!=0 && errno!=ENOENT) 116 | pfmt(err, "rc: read_history: %s\n", strerror(errno)); 117 | atexit(savehist); 118 | first = 0; 119 | } 120 | 121 | s = readline(promptstr); 122 | if(s == NULL) 123 | return; 124 | 125 | n = strlen(s); 126 | assert(n < NBUF-1); 127 | strcpy(f->buf, s); 128 | f->buf[n++] = '\n'; 129 | f->bufp = f->buf; 130 | f->ebuf = f->buf+n; 131 | 132 | add_history(s); 133 | free(s); 134 | } 135 | 136 | void 137 | pprompt(void) 138 | { 139 | var *prompt; 140 | 141 | if(runq->iflag){ 142 | flush(err); 143 | read_rl(); 144 | prompt = vlook("prompt"); 145 | if(prompt->val && prompt->val->next) 146 | promptstr = prompt->val->next->word; 147 | else 148 | promptstr="\t"; 149 | } 150 | runq->lineno++; 151 | doprompt = 0; 152 | } 153 | 154 | void 155 | exechistory(void) 156 | { 157 | HIST_ENTRY *e; 158 | struct io out[1]; 159 | int i; 160 | 161 | setstatus(""); 162 | out->fd = mapfd(1); 163 | out->bufp = out->buf; 164 | out->ebuf = &out->buf[NBUF]; 165 | out->strp = 0; 166 | for(i=history_base; iline); 171 | } 172 | poplist(); 173 | } 174 | -------------------------------------------------------------------------------- /rc.1: -------------------------------------------------------------------------------- 1 | .TH RC 1 2 | .SH NAME 3 | rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., ~ \- command language 4 | .SH SYNOPSIS 5 | .B rc 6 | [ 7 | .B -srdiIlxepvV 8 | ] 9 | [ 10 | .B -c 11 | .I command 12 | ] 13 | [ 14 | .B -m 15 | .I initial 16 | ] 17 | [ 18 | .I file 19 | [ 20 | .I arg ... 21 | ]] 22 | .SH DESCRIPTION 23 | .I Rc 24 | is the Plan 9 shell. 25 | It executes command lines read from a terminal or a file or, with the 26 | .B -c 27 | flag, from 28 | .I rc's 29 | argument list. 30 | .SS Command Lines 31 | A command line is a sequence of commands, separated by ampersands or semicolons 32 | .RB ( & 33 | or 34 | .BR ; ), 35 | terminated by a newline. 36 | The commands are executed in sequence 37 | from left to right. 38 | .I Rc 39 | does not wait for a command followed by 40 | .B & 41 | to finish executing before starting 42 | the following command. 43 | Whenever a command followed by 44 | .B & 45 | is executed, its process id is assigned to the 46 | .I rc 47 | variable 48 | .BR $apid . 49 | Whenever a command 50 | .I not 51 | followed by 52 | .B & 53 | exits or is terminated, the 54 | .I rc 55 | variable 56 | .B $status 57 | gets the process's wait message (see 58 | .IR wait (2)); 59 | it will be the null string if the command was successful. 60 | .PP 61 | A long command line may be continued on subsequent lines by typing 62 | a backslash 63 | .RB ( \e ) 64 | followed by a newline. 65 | This sequence is treated as though it were a blank. 66 | Backslash is not otherwise a special character. 67 | .PP 68 | A number-sign 69 | .RB ( # ) 70 | and any following characters up to (but not including) the next newline 71 | are ignored, except in quotation marks. 72 | .SS Simple Commands 73 | A simple command is a sequence of arguments interspersed with I/O redirections. 74 | If the first argument is the name of an 75 | .I rc 76 | function or of one of 77 | .I rc's 78 | built-in commands, it is executed by 79 | .IR rc . 80 | Otherwise if the name starts with a slash 81 | .RB ( / ), 82 | it must be the path name of the program to be executed. 83 | Names containing no initial slash are searched for in 84 | a list of directory names stored in 85 | .BR $path . 86 | The first executable file of the given name found 87 | in a directory in 88 | .B $path 89 | is the program to be executed. 90 | To be executable, the user must have execute permission (see 91 | .IR stat (2)) 92 | and the file must be either an executable binary 93 | for the current machine's CPU type, or a shell script. 94 | Shell scripts begin with a line containing the full path name of a shell 95 | (usually 96 | .BR /bin/rc ), 97 | prefixed by 98 | .LR #! . 99 | .PP 100 | The first word of a simple command cannot be a keyword unless it is 101 | quoted or otherwise disguised. 102 | The keywords are 103 | .EX 104 | for in while if not switch fn ~ ! @ 105 | .EE 106 | .SS Arguments and Variables 107 | A number of constructions may be used where 108 | .I rc's 109 | syntax requires an argument to appear. 110 | In many cases a construction's 111 | value will be a list of arguments rather than a single string. 112 | .PP 113 | The simplest kind of argument is the unquoted word: 114 | a sequence of one or more characters none of which is a blank, tab, 115 | newline, or any of the following: 116 | .EX 117 | # ; & | ^ $ = ` ' { } ( ) < > 118 | .EE 119 | An unquoted word that contains any of the characters 120 | .B * 121 | .B ? 122 | .B [ 123 | is a pattern for matching against file names. 124 | The character 125 | .B * 126 | matches any sequence of characters, 127 | .B ? 128 | matches any single character, and 129 | .BI [ class ] 130 | matches any character in the 131 | .IR class . 132 | If the first character of 133 | .I class 134 | is 135 | .BR ~ , 136 | the class is complemented. 137 | The 138 | .I class 139 | may also contain pairs of characters separated by 140 | .BR - , 141 | standing for all characters lexically between the two. 142 | The character 143 | .B / 144 | must appear explicitly in a pattern, as must the 145 | first character of the path name components 146 | .B . 147 | and 148 | .BR .. . 149 | A pattern is replaced by a list of arguments, one for each path name matched, 150 | except that a pattern matching no names is not replaced by the empty list, 151 | but rather stands for itself. 152 | Pattern matching is done after all other 153 | operations. 154 | Thus, 155 | .EX 156 | x=/tmp echo $x^/*.c 157 | .EE 158 | matches 159 | .BR /tmp/*.c , 160 | rather than matching 161 | .B "/*.c 162 | and then prefixing 163 | .BR /tmp . 164 | .PP 165 | A quoted word is a sequence of characters surrounded by single quotes 166 | .RB ( ' ). 167 | A single quote is represented in a quoted word by a pair of quotes 168 | .RB ( '' ). 169 | .PP 170 | Each of the following is an argument. 171 | .PD 0 172 | .HP 173 | .BI ( arguments ) 174 | .br 175 | The value of a sequence of arguments enclosed in parentheses is 176 | a list comprising the members of each element of the sequence. 177 | Argument lists have no recursive structure, although their syntax may 178 | suggest it. 179 | The following are entirely equivalent: 180 | .EX 181 | echo hi there everybody 182 | ((echo) (hi there) everybody) 183 | .EE 184 | .HP 185 | .BI $ argument 186 | .HP 187 | .BI $ argument ( subscript ) 188 | .br 189 | The 190 | .I argument 191 | after the 192 | .B $ 193 | is the name of a variable whose value is substituted. 194 | Multiple levels 195 | of indirection are possible, but of questionable utility. 196 | Variable values 197 | are lists of strings. 198 | If 199 | .I argument 200 | is a number 201 | .IR n , 202 | the value is the 203 | .IR n th 204 | element of 205 | .BR $* , 206 | unless 207 | .B $* 208 | doesn't have 209 | .I n 210 | elements, in which case the value is empty. 211 | If 212 | .I argument 213 | is followed by a parenthesized list of subscripts, the 214 | value substituted is a list composed of the requested elements (origin 1). 215 | The parenthesis must follow the variable name with no spaces. 216 | Subscripts can also take the form 217 | .IB m - n 218 | or 219 | .IB m - 220 | to indicate a sequence of elements. 221 | Assignments to variables are described below. 222 | .HP 223 | .BI $# argument 224 | .br 225 | The value is the number of elements in the named variable. 226 | A variable 227 | never assigned a value has zero elements. 228 | .HP 229 | $"\c 230 | .I argument 231 | .br 232 | The value is a single string containing the components of the named variable 233 | separated by spaces. A variable with zero elements yields the empty string. 234 | .HP 235 | .BI `{ command } 236 | .br 237 | .I rc 238 | executes the 239 | .I command 240 | and reads its standard output, splitting it into a list of arguments, 241 | using characters in 242 | .B $ifs 243 | as separators. 244 | If 245 | .B $ifs 246 | is not otherwise set, its value is 247 | .BR "'\ \et\en'" . 248 | .HP 249 | .BI <{ command } 250 | .HP 251 | .BI >{ command } 252 | .br 253 | The 254 | .I command 255 | is executed asynchronously with its standard output or standard input 256 | connected to a pipe. 257 | The value of the argument is the name of a file 258 | referring to the other end of the pipe. 259 | This allows the construction of 260 | non-linear pipelines. 261 | For example, the following runs two commands 262 | .B old 263 | and 264 | .B new 265 | and uses 266 | .B cmp 267 | to compare their outputs 268 | .EX 269 | cmp <{old} <{new} 270 | .EE 271 | .HP 272 | .IB argument ^ argument 273 | .br 274 | The 275 | .B ^ 276 | operator concatenates its two operands. 277 | If the two operands 278 | have the same number of components, they are concatenated pairwise. 279 | If not, 280 | then one operand must have one component, and the other must be non-empty, 281 | and concatenation is distributive. 282 | .PD 283 | .SS Free Carets 284 | In most circumstances, 285 | .I rc 286 | will insert the 287 | .B ^ 288 | operator automatically between words that are not separated by white space. 289 | Whenever one of 290 | .B $ 291 | .B ' 292 | .B ` 293 | follows a quoted or unquoted word or an unquoted word follows a quoted word 294 | with no intervening blanks or tabs, 295 | a 296 | .B ^ 297 | is inserted between the two. 298 | If an unquoted word immediately follows a 299 | .BR $ 300 | and contains a character other than an alphanumeric, underscore, 301 | or 302 | .BR * , 303 | a 304 | .B ^ 305 | is inserted before the first such character. 306 | Thus 307 | .IP 308 | .B cc -$flags $stem.c 309 | .LP 310 | is equivalent to 311 | .IP 312 | .B cc -^$flags $stem^.c 313 | .SS I/O Redirections 314 | The sequence 315 | .BI > file 316 | redirects the standard output file (file descriptor 1, normally the 317 | terminal) to the named 318 | .IR file ; 319 | .BI >> file 320 | appends standard output to the file. 321 | The standard input file (file descriptor 0, also normally the terminal) 322 | may be redirected from a file by the sequence 323 | .BI < file \f1, 324 | or from an inline `here document' 325 | by the sequence 326 | .BI << eof-marker\f1. 327 | The contents of a here document are lines of text taken from the command 328 | input stream up to a line containing nothing but the 329 | .IR eof-marker , 330 | which may be either a quoted or unquoted word. 331 | If 332 | .I eof-marker 333 | is unquoted, variable names of the form 334 | .BI $ word 335 | have their values substituted from 336 | .I rc's 337 | environment. 338 | If 339 | .BI $ word 340 | is followed by a caret 341 | .RB ( ^ ), 342 | the caret is deleted. 343 | If 344 | .I eof-marker 345 | is quoted, no substitution occurs. 346 | The standard input file 347 | may also be redirected from a file by the sequence 348 | .BI <> file \f1, 349 | which opens 350 | .I file 351 | exactly once, for reading and writing. 352 | .PP 353 | Redirections may be applied to a file-descriptor other than standard input 354 | or output by qualifying the redirection operator 355 | with a number in square brackets. 356 | For example, the diagnostic output (file descriptor 2) 357 | may be redirected by writing 358 | .BR "cc junk.c >[2]junk" . 359 | .PP 360 | A file descriptor may be redirected to an already open descriptor by writing 361 | .BI >[ fd0 = fd1 ], 362 | .BI <>[ fd0 = fd1 ], 363 | or 364 | .BI <[ fd0 = fd1 ]\f1. 365 | .I Fd1 366 | is a previously opened file descriptor and 367 | .I fd0 368 | becomes a new copy (in the sense of 369 | .IR dup (2)) 370 | of it. 371 | A file descriptor may be closed by writing 372 | .BI >[ fd0 =] 373 | or 374 | .BI <[ fd0 =]\f1. 375 | .PP 376 | Redirections are executed from left to right. 377 | Therefore, 378 | .B cc junk.c >/dev/null >[2=1] 379 | and 380 | .B cc junk.c >[2=1] >/dev/null 381 | have different effects: the first puts standard output in 382 | .BR /dev/null 383 | and then puts diagnostic output in the same place, where the second 384 | directs diagnostic output to the terminal and sends standard output to 385 | .BR /dev/null . 386 | .PP 387 | .B newconn <>/net/tcp/clone >[1=0] 388 | opens 389 | .B /net/tcp/clone 390 | exactly once for reading and writing and puts it on standard input and output. 391 | .B lpd <>[3]/net/tcp/42/data 392 | opens 393 | .B /net/tcp/42/data 394 | exactly once for reading and writing and puts it on file descriptor 3. 395 | .SS Compound Commands 396 | A pair of commands separated by a pipe operator 397 | .RB ( | ) 398 | is a command. 399 | The standard output of the left command is sent through a pipe 400 | to the standard input of the right command. 401 | The pipe operator may be decorated 402 | to use different file descriptors. 403 | .BI |[ fd ] 404 | connects the output end of the pipe to file descriptor 405 | .I fd 406 | rather than 1. 407 | .BI |[ fd0 = fd1 ] 408 | connects output to 409 | .I fd1 410 | of the left command and input to 411 | .I fd0 412 | of the right command. 413 | .PP 414 | A pair of commands separated by 415 | .B && 416 | or 417 | .B || 418 | is a command. 419 | In either case, the left command is executed and its exit status examined. 420 | If the operator is 421 | .B && 422 | the right command is executed if the left command's status is null. 423 | .B || 424 | causes the right command to be executed if the left command's status is non-null. 425 | .PP 426 | The exit status of a command may be inverted (non-null is changed to null, null 427 | is changed to non-null) by preceding it with a 428 | .BR ! . 429 | .PP 430 | The 431 | .B | 432 | operator has highest precedence, and is left-associative (i.e. binds tighter 433 | to the left than the right). 434 | .B ! 435 | has intermediate precedence, and 436 | .B && 437 | and 438 | .B || 439 | have the lowest precedence. 440 | .PP 441 | The unary 442 | .B @ 443 | operator, with precedence equal to 444 | .BR ! , 445 | causes its operand to be executed in a subshell. 446 | .PP 447 | Each of the following is a command. 448 | .PD 0 449 | .HP 450 | .B if ( 451 | .I list 452 | .B ) 453 | .I command 454 | .br 455 | A 456 | .I list 457 | is a sequence of commands, separated by 458 | .BR & , 459 | .BR ; , 460 | or newline. 461 | It is executed and 462 | if its exit status is null, the 463 | .I command 464 | is executed. 465 | .HP 466 | .B if not 467 | .I command 468 | .br 469 | The immediately preceding command must have been 470 | .BI if( list ) 471 | .IR command . 472 | If its condition was non-zero, the 473 | .I command 474 | is executed. 475 | .HP 476 | .BI for( name 477 | .B in 478 | .IB arguments ) 479 | .I command 480 | .HP 481 | .BI for( name ) 482 | .I command 483 | .br 484 | The 485 | .I command 486 | is executed once for each 487 | .IR argument 488 | with that argument assigned to 489 | .IR name . 490 | If the argument list is omitted, 491 | .B $* 492 | is used. 493 | .HP 494 | .BI while( list ) 495 | .I command 496 | .br 497 | The 498 | .I list 499 | is executed repeatedly until its exit status is non-null. 500 | Each time it returns null status, the 501 | .I command 502 | is executed. 503 | An empty 504 | .I list 505 | is taken to give null status. 506 | .HP 507 | .BI "switch(" argument "){" list } 508 | .br 509 | The 510 | .IR list 511 | is searched for simple commands beginning with the word 512 | .BR case . 513 | (The search is only at the `top level' of the 514 | .IR list . 515 | That is, 516 | .B cases 517 | in nested constructs are not found.) 518 | .I Argument 519 | is matched against each word following 520 | .B case 521 | using the pattern-matching algorithm described above, except that 522 | .B / 523 | and the first characters of 524 | .B . 525 | and 526 | .B .. 527 | need not be matched explicitly. 528 | When a match is found, commands in the list are executed up to the next 529 | following 530 | .B case 531 | command (at the top level) or the closing brace. 532 | .HP 533 | .BI { list } 534 | .br 535 | Braces serve to alter the grouping of commands implied by operator 536 | priorities. 537 | The 538 | .I body 539 | is a sequence of commands separated by 540 | .BR & , 541 | .BR ; , 542 | or newline. 543 | .HP 544 | .BI "fn " name { list } 545 | .HP 546 | .BI "fn " name 547 | .br 548 | The first form defines a function with the given 549 | .IR name . 550 | Subsequently, whenever a command whose first argument is 551 | .I name 552 | is encountered, the current value of 553 | the remainder of the command's argument list will be assigned to 554 | .BR $* , 555 | after saving its current value, and 556 | .I rc 557 | will execute the 558 | .IR list . 559 | The second form removes 560 | .IR name 's 561 | function definition. 562 | .HP 563 | .BI "fn " note { list } 564 | .br 565 | .HP 566 | .BI "fn " note 567 | .br 568 | A function with a special name will be called when 569 | .I rc 570 | receives a corresponding note; see 571 | .IR notify (2). 572 | The valid note names (and corresponding notes) are 573 | .B sighup 574 | .RB ( hangup ), 575 | .B sigint 576 | .RB ( interrupt ), 577 | .BR sigalrm 578 | .RB ( alarm ), 579 | and 580 | .B sigfpe 581 | (floating point trap). 582 | By default 583 | .I rc 584 | exits on receiving any signal, except when run interactively, 585 | in which case interrupts and quits normally cause 586 | .I rc 587 | to stop whatever it's doing and start reading a new command. 588 | The second form causes 589 | .I rc 590 | to handle a signal in the default manner. 591 | .I Rc 592 | recognizes an artificial note, 593 | .BR sigexit , 594 | which occurs when 595 | .I rc 596 | is about to finish executing. 597 | .HP 598 | .IB name = "argument command" 599 | .br 600 | Any command may be preceded by a sequence of assignments 601 | interspersed with redirections. 602 | The assignments remain in effect until the end of the command, unless 603 | the command is empty (i.e. the assignments stand alone), in which case 604 | they are effective until rescinded by later assignments. 605 | .PD 606 | .SS Built-in Commands 607 | These commands are executed internally by 608 | .IR rc , 609 | usually because their execution changes or depends on 610 | .IR rc 's 611 | internal state. 612 | .PD 0 613 | .HP 614 | .BI . " file ..." 615 | .br 616 | Execute commands from 617 | .IR file . 618 | .B $* 619 | is set for the duration to the remainder of the argument list following 620 | .IR file . 621 | .I File 622 | is searched for using 623 | .BR $path . 624 | .HP 625 | .BI builtin " command ..." 626 | .br 627 | Execute 628 | .I command 629 | as usual except that any function named 630 | .I command 631 | is ignored in favor of the built-in meaning. 632 | .HP 633 | .BI "cd [" dir "]" 634 | .br 635 | Change the current directory to 636 | .IR dir . 637 | The default argument is 638 | .BR $home . 639 | .I dir 640 | is searched for in each of the directories mentioned in 641 | .BR $cdpath . 642 | .HP 643 | .BI "eval [" "arg ..." "]" 644 | .br 645 | The arguments are concatenated separated by spaces into a single string, 646 | read as input to 647 | .IR rc , 648 | and executed. 649 | .HP 650 | .BI "exec [" "command ..." "]" 651 | .br 652 | This instance of 653 | .I rc 654 | replaces itself with the given (non-built-in) 655 | .IR command . 656 | .HP 657 | .BI "flag " f " [+-]" 658 | .br 659 | Either set 660 | .RB ( + ), 661 | clear 662 | .RB ( - ), 663 | or test (neither 664 | .B + 665 | nor 666 | .BR - ) 667 | the flag 668 | .IR f , 669 | where 670 | .I f 671 | is a single character, one of the command line flags (see Invocation, below). 672 | .HP 673 | .BI "exit [" status "]" 674 | .br 675 | Exit with the given exit status. 676 | If none is given, the current value of 677 | .B $status 678 | is used. 679 | .HP 680 | .BR "rfork " [ nNeEsfFm ] 681 | .br 682 | Become a new process group using 683 | .BI rfork( flags ) 684 | where 685 | .I flags 686 | is composed of the bitwise OR of the 687 | .B rfork 688 | flags specified by the option letters 689 | (see 690 | .IR fork (2)). 691 | If no 692 | .I flags 693 | are given, they default to 694 | .BR ens . 695 | The 696 | .I flags 697 | and their meanings are: 698 | .B n 699 | is 700 | .BR RFNAMEG ; 701 | .B N 702 | is 703 | .BR RFCNAMEG ; 704 | .B e 705 | is 706 | .BR RFENVG ; 707 | .B E 708 | is 709 | .BR RFCENVG ; 710 | .B s 711 | is 712 | .BR RFNOTEG ; 713 | .B f 714 | is 715 | .BR RFFDG ; 716 | .B F 717 | is 718 | .BR RFCFDG ; 719 | and 720 | .B m 721 | is 722 | .BR RFNOMNT . 723 | .HP 724 | .BI "shift [" n "]" 725 | .br 726 | Delete the first 727 | .IR n 728 | (default 1) 729 | elements of 730 | .BR $* . 731 | .HP 732 | .BI "wait [" pid "]" 733 | .br 734 | Wait for the process with the given 735 | .I pid 736 | to exit. 737 | If no 738 | .I pid 739 | is given, all outstanding processes are waited for. 740 | .HP 741 | .BI whatis " name ..." 742 | .br 743 | Print the value of each 744 | .I name 745 | in a form suitable for input to 746 | .IR rc . 747 | The output is 748 | an assignment to any variable, 749 | the definition of any function, 750 | a call to 751 | .B builtin 752 | for any built-in command, or 753 | the completed pathname of any executable file. 754 | .HP 755 | .BI ~ " subject pattern ..." 756 | .br 757 | The 758 | .I subject 759 | is matched against each 760 | .I pattern 761 | in sequence. 762 | If it matches any pattern, 763 | .B $status 764 | is set to zero. 765 | Otherwise, 766 | .B $status 767 | is set to one. 768 | Patterns are the same as for file name matching, except that 769 | .B / 770 | and the first character of 771 | .B . 772 | and 773 | .B .. 774 | need not be matched explicitly. 775 | The 776 | .I patterns 777 | are not subjected to 778 | file name matching before the 779 | .B ~ 780 | command is executed, so they need not be enclosed in quotation marks. 781 | .PD 782 | .SS Environment 783 | The 784 | .I environment 785 | is a list of strings made available to executing binaries by the 786 | .B env 787 | device 788 | (see 789 | .IR env (3)). 790 | .I Rc 791 | creates an environment entry for each variable whose value is non-empty, 792 | and for each function. 793 | The string for a variable entry has the variable's name followed by 794 | .B = 795 | and its value. 796 | If the value has more than one component, these 797 | are separated by ctrl-a 798 | .RB ( '\e001' ) 799 | characters. 800 | The string for a function is just the 801 | .I rc 802 | input that defines the function. 803 | The name of a function in the environment is the function name 804 | preceded by 805 | .LR fn# . 806 | .PP 807 | When 808 | .I rc 809 | starts executing it reads variable and function definitions from its 810 | environment. 811 | .SS Special Variables 812 | The following variables are set or used by 813 | .IR rc . 814 | .PD 0 815 | .TP \w'\fL$promptXX'u 816 | .B $* 817 | Set to 818 | .IR rc 's 819 | argument list during initialization. 820 | Whenever a 821 | .B . 822 | command or a function is executed, the current value is saved and 823 | .B $* 824 | receives the new argument list. 825 | The saved value is restored on completion of the 826 | .B . 827 | or function. 828 | .TP 829 | .B $apid 830 | Whenever a process is started asynchronously with 831 | .BR & , 832 | .B $apid 833 | is set to its process id. 834 | .TP 835 | .B $home 836 | The default directory for 837 | .BR cd . 838 | .TP 839 | .B $ifs 840 | The input field separators used in backquote substitutions. 841 | If 842 | .B $ifs 843 | is not set in 844 | .IR rc 's 845 | environment, it is initialized to blank, tab and newline. 846 | .TP 847 | .B $path 848 | The search path used to find commands and input files 849 | for the 850 | .B . 851 | command. 852 | If not set in the environment, it is initialized by 853 | .BR "path=(.\ /bin)" . 854 | Its use is discouraged; instead use 855 | .IR bind (1) 856 | to build a 857 | .B /bin 858 | containing what's needed. 859 | .TP 860 | .B $pid 861 | Set during initialization to 862 | .IR rc 's 863 | process id. 864 | .TP 865 | .B $prompt 866 | When 867 | .I rc 868 | is run interactively, the first component of 869 | .B $prompt 870 | is printed before reading each command. 871 | The second component is printed whenever a newline is typed and more lines 872 | are required to complete the command. 873 | If not set in the environment, it is initialized by 874 | .BR "prompt=('%\ '\ '\ ')" . 875 | .TP 876 | .B $status 877 | Set to the wait message of the last-executed program. 878 | (unless started with 879 | .BR &). 880 | .B ! 881 | and 882 | .B ~ 883 | also change 884 | .BR $status . 885 | Its value is used to control execution in 886 | .BR && , 887 | .BR || , 888 | .B if 889 | and 890 | .B while 891 | commands. 892 | When 893 | .I rc 894 | exits at end-of-file of its input or on executing an 895 | .B exit 896 | command with no argument, 897 | .B $status 898 | is its exit status. 899 | .PD 900 | .SS Invocation 901 | If 902 | .I rc 903 | is started with no arguments it reads commands from standard input. 904 | Otherwise its first non-flag argument is the name of a file from which 905 | to read commands (but see 906 | .B -c 907 | below). 908 | Subsequent arguments become the initial value of 909 | .BR $* . 910 | .I Rc 911 | accepts the following command-line flags. 912 | .PD 0 913 | .TP \w'\fL-c\ \fIstring\fLXX'u 914 | .BI -c " string" 915 | Commands are read from 916 | .IR string . 917 | .TP 918 | .B -s 919 | Print out exit status after any command where the status is non-null. 920 | .TP 921 | .B -e 922 | Exit if 923 | .B $status 924 | is non-null after executing a simple command. 925 | .TP 926 | .B -i 927 | If 928 | .B -i 929 | is present, or 930 | .I rc 931 | is given no arguments and its standard input is a terminal, 932 | it runs interactively. 933 | Commands are prompted for using 934 | .BR $prompt . 935 | .TP 936 | .B -I 937 | Makes sure 938 | .I rc 939 | is not run interactively. 940 | .TP 941 | .B -l 942 | If 943 | .B -l 944 | is given or the first character of argument zero is 945 | .BR - , 946 | .I rc 947 | reads commands from 948 | .BR $home/lib/profile , 949 | if it exists, before reading its normal input. 950 | .TP 951 | .B -m 952 | Read commands to initialize 953 | .I rc 954 | from 955 | .I initial 956 | instead of from 957 | .BR /rc/lib/rcmain . 958 | .TP 959 | .B -p 960 | A no-op. 961 | .TP 962 | .B -d 963 | A no-op. 964 | .TP 965 | .B -v 966 | Echo input on file descriptor 2 as it is read. 967 | .TP 968 | .B -x 969 | Print each simple command before executing it. 970 | .TP 971 | .B -r 972 | Print debugging information (internal form of commands 973 | as they are executed). 974 | .PD 975 | .SH SOURCE 976 | .B /sys/src/cmd/rc 977 | .SH "SEE ALSO" 978 | Tom Duff, 979 | ``Rc \- The Plan 9 Shell''. 980 | .SH BUGS 981 | There should be a way to match patterns against whole lists rather than 982 | just single strings. 983 | .PP 984 | Using 985 | .B ~ 986 | to check the value of 987 | .B $status 988 | changes 989 | .BR $status . 990 | .PP 991 | Functions containing here documents don't work. 992 | .PP 993 | Free carets don't get inserted next to keywords. 994 | -------------------------------------------------------------------------------- /rc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Plan9 is defined for plan 9 3 | * V9 is defined for 9th edition 4 | * Sun is defined for sun-os 5 | * Please don't litter the code with ifdefs. The three below (and one in 6 | * getflags) should be enough. 7 | */ 8 | #ifdef Plan9 9 | #include 10 | #include 11 | #undef NSIG 12 | #undef SIGINT 13 | #undef SIGQUIT 14 | #define NSIG 32 15 | #define SIGINT 2 16 | #define SIGQUIT 3 17 | #else 18 | #include "unix.h" 19 | #endif 20 | #define YYMAXDEPTH 500 21 | #ifndef PAREN 22 | #ifndef YYMAJOR 23 | #include "x.tab.h" 24 | #endif 25 | #endif 26 | 27 | #undef pipe /* so that /dev/fd works */ 28 | #define searchpath rcsearchpath /* avoid new libc function */ 29 | 30 | typedef struct tree tree; 31 | typedef struct word word; 32 | typedef struct io io; 33 | typedef union code code; 34 | typedef struct var var; 35 | typedef struct list list; 36 | typedef struct redir redir; 37 | typedef struct thread thread; 38 | typedef struct builtin builtin; 39 | 40 | struct tree{ 41 | int type; 42 | int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ 43 | char *str; 44 | int quoted; 45 | int iskw; 46 | tree *child[3]; 47 | tree *next; 48 | }; 49 | tree *newtree(void); 50 | tree *token(char*, int), *klook(char*), *tree1(int, tree*); 51 | tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*); 52 | tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*); 53 | tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*); 54 | tree *simplemung(tree*), *heredoc(tree*); 55 | void freetree(tree*); 56 | tree *cmdtree; 57 | /* 58 | * The first word of any code vector is a reference count. 59 | * Always create a new reference to a code vector by calling codecopy(.). 60 | * Always call codefree(.) when deleting a reference. 61 | */ 62 | union code{ 63 | void (*f)(void); 64 | int i; 65 | char *s; 66 | }; 67 | char *promptstr; 68 | int doprompt; 69 | #define NTOK 8192 70 | char tok[NTOK]; 71 | #define APPEND 1 72 | #define WRITE 2 73 | #define READ 3 74 | #define HERE 4 75 | #define DUPFD 5 76 | #define CLOSE 6 77 | #define RDWR 7 78 | struct var{ 79 | char *name; /* ascii name */ 80 | word *val; /* value */ 81 | int changed; 82 | code *fn; /* pointer to function's code vector */ 83 | int fnchanged; 84 | int pc; /* pc of start of function */ 85 | var *next; /* next on hash or local list */ 86 | void (*changefn)(var*); 87 | }; 88 | var *vlook(char*), *gvlook(char*), *newvar(char*, var*); 89 | #define NVAR 521 90 | var *gvar[NVAR]; /* hash for globals */ 91 | #define new(type) ((type *)emalloc(sizeof(type))) 92 | void *emalloc(long); 93 | void *Malloc(ulong); 94 | void efree(void*); 95 | #define NOFILE 128 /* should come from */ 96 | struct here{ 97 | tree *tag; 98 | char *name; 99 | struct here *next; 100 | }; 101 | int mypid; 102 | /* 103 | * Glob character escape in strings: 104 | * In a string, GLOB must be followed by *?[ or GLOB. 105 | * GLOB* matches any string 106 | * GLOB? matches any single character 107 | * GLOB[...] matches anything in the brackets 108 | * GLOBGLOB matches GLOB 109 | */ 110 | #define GLOB ((char)0x01) 111 | /* 112 | * onebyte(c), twobyte(c), threebyte(c) 113 | * Is c the first character of a one- two- or three-byte utf sequence? 114 | */ 115 | #define onebyte(c) ((c&0x80)==0x00) 116 | #define twobyte(c) ((c&0xe0)==0xc0) 117 | #define threebyte(c) ((c&0xf0)==0xe0) 118 | #define fourbyte(c) ((c&0xf8)==0xf0) 119 | 120 | char **argp; 121 | char **args; 122 | int nerror; /* number of errors encountered during compilation */ 123 | int doprompt; /* is it time for a prompt? */ 124 | /* 125 | * Which fds are the reading/writing end of a pipe? 126 | * Unfortunately, this can vary from system to system. 127 | * 9th edition Unix doesn't care, the following defines 128 | * work on plan 9. 129 | */ 130 | #define PRD 0 131 | #define PWR 1 132 | char *Rcmain, *Fdprefix; 133 | #define register 134 | /* 135 | * How many dot commands have we executed? 136 | * Used to ensure that -v flag doesn't print rcmain. 137 | */ 138 | int ndot; 139 | char *getstatus(void); 140 | int lastc; 141 | int lastword; 142 | int kidpid; 143 | -------------------------------------------------------------------------------- /rc.md: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | rc, cd, eval, exec, exit, flag, rfork, shift, wait, whatis, ., \~ - 4 | command language 5 | 6 | # SYNOPSIS 7 | 8 | **rc** \[ **-srdiIlxepvV** \] \[ **-c** *command* \] \[ **-m** *initial* 9 | \] \[ *file* \[ *arg ...* \]\] 10 | 11 | # DESCRIPTION 12 | 13 | *Rc* is the Plan 9 shell. It executes command lines read from a terminal 14 | or a file or, with the **-c** flag, from *rc's* argument list. 15 | 16 | ## Command Lines 17 | 18 | A command line is a sequence of commands, separated by ampersands or 19 | semicolons (**&** or **;**), terminated by a newline. The commands are 20 | executed in sequence from left to right. *Rc* does not wait for a 21 | command followed by **&** to finish executing before starting the 22 | following command. Whenever a command followed by **&** is executed, its 23 | process id is assigned to the *rc* variable **$apid**. Whenever a 24 | command *not* followed by **&** exits or is terminated, the *rc* 25 | variable **$status** gets the process's wait message (see *wait*(2)); it 26 | will be the null string if the command was successful. 27 | 28 | A long command line may be continued on subsequent lines by typing a 29 | backslash (**\\**) followed by a newline. This sequence is treated as 30 | though it were a blank. Backslash is not otherwise a special character. 31 | 32 | A number-sign (**\#**) and any following characters up to (but not 33 | including) the next newline are ignored, except in quotation marks. 34 | 35 | ## Simple Commands 36 | 37 | A simple command is a sequence of arguments interspersed with I/O 38 | redirections. If the first argument is the name of an *rc* function or 39 | of one of *rc's* built-in commands, it is executed by *rc*. Otherwise if 40 | the name starts with a slash (**/**), it must be the path name of the 41 | program to be executed. Names containing no initial slash are searched 42 | for in a list of directory names stored in **$path**. The first 43 | executable file of the given name found in a directory in **$path** is 44 | the program to be executed. To be executable, the user must have execute 45 | permission (see *stat*(2)) and the file must be either an executable 46 | binary for the current machine's CPU type, or a shell script. Shell 47 | scripts begin with a line containing the full path name of a shell 48 | (usually **/bin/rc**), prefixed by 49 | 50 | The first word of a simple command cannot be a keyword unless it is 51 | quoted or otherwise disguised. The keywords are 52 | 53 | ``` 54 | for in while if not switch fn ~ ! @ 55 | ``` 56 | 57 | ## Arguments and Variables 58 | 59 | A number of constructions may be used where *rc's* syntax requires an 60 | argument to appear. In many cases a construction's value will be a list 61 | of arguments rather than a single string. 62 | 63 | The simplest kind of argument is the unquoted word: a sequence of one or 64 | more characters none of which is a blank, tab, newline, or any of the 65 | following: 66 | 67 | ``` 68 | # ; & | ^ $ = ` ' { } ( ) < > 69 | ``` 70 | 71 | An unquoted word that contains any of the characters **\*** **?** **\[** 72 | is a pattern for matching against file names. The character **\*** 73 | matches any sequence of characters, **?** matches any single character, 74 | and **\[***class***\]** matches any character in the *class*. If the 75 | first character of *class* is **\~**, the class is complemented. The 76 | *class* may also contain pairs of characters separated by **-**, 77 | standing for all characters lexically between the two. The character 78 | **/** must appear explicitly in a pattern, as must the first character 79 | of the path name components **.** and **..**. A pattern is replaced by a 80 | list of arguments, one for each path name matched, except that a pattern 81 | matching no names is not replaced by the empty list, but rather stands 82 | for itself. Pattern matching is done after all other operations. Thus, 83 | 84 | ``` 85 | x=/tmp echo $x^/*.c 86 | ``` 87 | 88 | matches **/tmp/\*.c**, rather than matching **"/\*.c** and then 89 | prefixing **/tmp**. 90 | 91 | A quoted word is a sequence of characters surrounded by single quotes 92 | (**'**). A single quote is represented in a quoted word by a pair of 93 | quotes (**''**). 94 | 95 | Each of the following is an argument. 96 | 97 | **(***arguments***)** 98 | The value of a sequence of arguments enclosed in parentheses is a list 99 | comprising the members of each element of the sequence. Argument lists 100 | have no recursive structure, although their syntax may suggest it. The 101 | following are entirely equivalent: 102 | 103 | ``` 104 | echo hi there everybody 105 | ((echo) (hi there) everybody) 106 | ``` 107 | 108 | **$***argument* 109 | 110 | **$***argument***(***subscript***)** 111 | The *argument* after the **$** is the name of a variable whose value is 112 | substituted. Multiple levels of indirection are possible, but of 113 | questionable utility. Variable values are lists of strings. If 114 | *argument* is a number *n*, the value is the *n*th element of **$\***, 115 | unless **$\*** doesn't have *n* elements, in which case the value is 116 | empty. If *argument* is followed by a parenthesized list of subscripts, 117 | the value substituted is a list composed of the requested elements 118 | (origin 1). The parenthesis must follow the variable name with no 119 | spaces. Subscripts can also take the form *m***-***n* or *m***-** to 120 | indicate a sequence of elements. Assignments to variables are described 121 | below. 122 | 123 | **$\#***argument* 124 | The value is the number of elements in the named variable. A variable 125 | never assigned a value has zero elements. 126 | 127 | $" *argument* 128 | The value is a single string containing the components of the named 129 | variable separated by spaces. A variable with zero elements yields the 130 | empty string. 131 | 132 | **\`{***command***}** 133 | *rc* executes the *command* and reads its standard output, splitting it 134 | into a list of arguments, using characters in **$ifs** as separators. If 135 | **$ifs** is not otherwise set, its value is **' \\t\\n'**. 136 | 137 | **\<{***command***}** 138 | 139 | **\>{***command***}** 140 | The *command* is executed asynchronously with its standard output or 141 | standard input connected to a pipe. The value of the argument is the 142 | name of a file referring to the other end of the pipe. This allows the 143 | construction of non-linear pipelines. For example, the following runs 144 | two commands **old** and **new** and uses **cmp** to compare their 145 | outputs 146 | 147 | ``` 148 | cmp <{old} <{new} 149 | ``` 150 | 151 | *argument***^***argument* 152 | The **^** operator concatenates its two operands. If the two operands 153 | have the same number of components, they are concatenated pairwise. If 154 | not, then one operand must have one component, and the other must be 155 | non-empty, and concatenation is distributive. 156 | 157 | ## Free Carets 158 | 159 | In most circumstances, *rc* will insert the **^** operator automatically 160 | between words that are not separated by white space. Whenever one of 161 | **$** **'** **\`** follows a quoted or unquoted word or an unquoted word 162 | follows a quoted word with no intervening blanks or tabs, a **^** is 163 | inserted between the two. If an unquoted word immediately follows a 164 | **$** and contains a character other than an alphanumeric, underscore, 165 | or **\***, a **^** is inserted before the first such character. Thus 166 | 167 | > **cc -$flags $stem.c** 168 | 169 | is equivalent to 170 | 171 | > **cc -^$flags $stem^.c** 172 | 173 | ## I/O Redirections 174 | 175 | The sequence **\>***file* redirects the standard output file (file 176 | descriptor 1, normally the terminal) to the named *file*; **\>\>***file* 177 | appends standard output to the file. The standard input file (file 178 | descriptor 0, also normally the terminal) may be redirected from a file 179 | by the sequence **\<***file***,** or from an inline \`here document' by 180 | the sequence **\<\<***eof-marker.* The contents of a here document are 181 | lines of text taken from the command input stream up to a line 182 | containing nothing but the *eof-marker*, which may be either a quoted or 183 | unquoted word. If *eof-marker* is unquoted, variable names of the form 184 | **$***word* have their values substituted from *rc's* environment. If 185 | **$***word* is followed by a caret (**^**), the caret is deleted. If 186 | *eof-marker* is quoted, no substitution occurs. The standard input file 187 | may also be redirected from a file by the sequence **\<\>***file***,** 188 | which opens *file* exactly once, for reading and writing. 189 | 190 | Redirections may be applied to a file-descriptor other than standard 191 | input or output by qualifying the redirection operator with a number in 192 | square brackets. For example, the diagnostic output (file descriptor 2) 193 | may be redirected by writing **cc junk.c \>\[2\]junk**. 194 | 195 | A file descriptor may be redirected to an already open descriptor by 196 | writing **\>\[***fd0***=***fd1***\],** **\<\>\[***fd0***=***fd1***\],** 197 | or **\<\[***fd0***=***fd1***\].** *Fd1* is a previously opened file 198 | descriptor and *fd0* becomes a new copy (in the sense of *dup*(2)) of 199 | it. A file descriptor may be closed by writing **\>\[***fd0***=\]** or 200 | **\<\[***fd0***=\].** 201 | 202 | Redirections are executed from left to right. Therefore, **cc junk.c 203 | \>/dev/null \>\[2=1\]** and **cc junk.c \>\[2=1\] \>/dev/null** have 204 | different effects: the first puts standard output in **/dev/null** and 205 | then puts diagnostic output in the same place, where the second directs 206 | diagnostic output to the terminal and sends standard output to 207 | **/dev/null**. 208 | 209 | **newconn \<\>/net/tcp/clone \>\[1=0\]** opens **/net/tcp/clone** 210 | exactly once for reading and writing and puts it on standard input and 211 | output. **lpd \<\>\[3\]/net/tcp/42/data** opens **/net/tcp/42/data** 212 | exactly once for reading and writing and puts it on file descriptor 3. 213 | 214 | ## Compound Commands 215 | 216 | A pair of commands separated by a pipe operator (**|**) is a command. 217 | The standard output of the left command is sent through a pipe to the 218 | standard input of the right command. The pipe operator may be decorated 219 | to use different file descriptors. **|\[***fd***\]** connects the output 220 | end of the pipe to file descriptor *fd* rather than 1. 221 | **|\[***fd0***=***fd1***\]** connects output to *fd1* of the left 222 | command and input to *fd0* of the right command. 223 | 224 | A pair of commands separated by **&&** or **||** is a command. In either 225 | case, the left command is executed and its exit status examined. If the 226 | operator is **&&** the right command is executed if the left command's 227 | status is null. **||** causes the right command to be executed if the 228 | left command's status is non-null. 229 | 230 | The exit status of a command may be inverted (non-null is changed to 231 | null, null is changed to non-null) by preceding it with a **\!**. 232 | 233 | The **|** operator has highest precedence, and is left-associative (i.e. 234 | binds tighter to the left than the right). **\!** has intermediate 235 | precedence, and **&&** and **||** have the lowest precedence. 236 | 237 | The unary **@** operator, with precedence equal to **\!**, causes its 238 | operand to be executed in a subshell. 239 | 240 | Each of the following is a command. 241 | 242 | **if (** *list* **)** *command* 243 | A *list* is a sequence of commands, separated by **&**, **;**, or 244 | newline. It is executed and if its exit status is null, the *command* is 245 | executed. 246 | 247 | **if not** *command* 248 | The immediately preceding command must have been **if(***list***)** 249 | *command*. If its condition was non-zero, the *command* is executed. 250 | 251 | **for(***name* **in** *arguments***)** *command* 252 | 253 | **for(***name***)** *command* 254 | The *command* is executed once for each *argument* with that argument 255 | assigned to *name*. If the argument list is omitted, **$\*** is used. 256 | 257 | **while(***list***)** *command* 258 | The *list* is executed repeatedly until its exit status is non-null. 259 | Each time it returns null status, the *command* is executed. An empty 260 | *list* is taken to give null status. 261 | 262 | **switch(***argument***){***list***}** 263 | The *list* is searched for simple commands beginning with the word 264 | **case**. (The search is only at the \`top level' of the *list*. That 265 | is, **cases** in nested constructs are not found.) *Argument* is matched 266 | against each word following **case** using the pattern-matching 267 | algorithm described above, except that **/** and the first characters of 268 | **.** and **..** need not be matched explicitly. When a match is found, 269 | commands in the list are executed up to the next following **case** 270 | command (at the top level) or the closing brace. 271 | 272 | **{***list***}** 273 | Braces serve to alter the grouping of commands implied by operator 274 | priorities. The *body* is a sequence of commands separated by **&**, 275 | **;**, or newline. 276 | 277 | **fn ***name***{***list***}** 278 | 279 | **fn ***name* 280 | The first form defines a function with the given *name*. Subsequently, 281 | whenever a command whose first argument is *name* is encountered, the 282 | current value of the remainder of the command's argument list will be 283 | assigned to **$\***, after saving its current value, and *rc* will 284 | execute the *list*. The second form removes *name*'s function 285 | definition. 286 | 287 | **fn ***note***{***list***}** 288 | 289 | **fn ***note* 290 | A function with a special name will be called when *rc* receives a 291 | corresponding note; see *notify*(2). The valid note names (and 292 | corresponding notes) are **sighup** (**hangup**), **sigint** 293 | (**interrupt**), **sigalrm** (**alarm**), and **sigfpe** (floating point 294 | trap). By default *rc* exits on receiving any signal, except when run 295 | interactively, in which case interrupts and quits normally cause *rc* to 296 | stop whatever it's doing and start reading a new command. The second 297 | form causes *rc* to handle a signal in the default manner. *Rc* 298 | recognizes an artificial note, **sigexit**, which occurs when *rc* is 299 | about to finish executing. 300 | 301 | *name***=***argument command* 302 | Any command may be preceded by a sequence of assignments interspersed 303 | with redirections. The assignments remain in effect until the end of the 304 | command, unless the command is empty (i.e. the assignments stand alone), 305 | in which case they are effective until rescinded by later assignments. 306 | 307 | ## Built-in Commands 308 | 309 | These commands are executed internally by *rc*, usually because their 310 | execution changes or depends on *rc*'s internal state. 311 | 312 | **.*** file ...* 313 | Execute commands from *file*. **$\*** is set for the duration to the 314 | remainder of the argument list following *file*. *File* is searched for 315 | using **$path**. 316 | 317 | **builtin*** command ...* 318 | Execute *command* as usual except that any function named *command* is 319 | ignored in favor of the built-in meaning. 320 | 321 | **cd \[***dir***\]** 322 | Change the current directory to *dir*. The default argument is 323 | **$home**. *dir* is searched for in each of the directories mentioned in 324 | **$cdpath**. 325 | 326 | **eval \[***arg ...***\]** 327 | The arguments are concatenated separated by spaces into a single string, 328 | read as input to *rc*, and executed. 329 | 330 | **exec \[***command ...***\]** 331 | This instance of *rc* replaces itself with the given (non-built-in) 332 | *command*. 333 | 334 | **flag ***f*** \[+-\]** 335 | Either set (**+**), clear (**-**), or test (neither **+** nor **-**) the 336 | flag *f*, where *f* is a single character, one of the command line flags 337 | (see Invocation, below). 338 | 339 | **exit \[***status***\]** 340 | Exit with the given exit status. If none is given, the current value of 341 | **$status** is used. 342 | 343 | **rfork **\[**nNeEsfFm**\] 344 | Become a new process group using **rfork(***flags***)** where *flags* is 345 | composed of the bitwise OR of the **rfork** flags specified by the 346 | option letters (see *fork*(2)). If no *flags* are given, they default to 347 | **ens**. The *flags* and their meanings are: **n** is **RFNAMEG**; **N** 348 | is **RFCNAMEG**; **e** is **RFENVG**; **E** is **RFCENVG**; **s** is 349 | **RFNOTEG**; **f** is **RFFDG**; **F** is **RFCFDG**; and **m** is 350 | **RFNOMNT**. 351 | 352 | **shift \[***n***\]** 353 | Delete the first *n* (default 1) elements of **$\***. 354 | 355 | **wait \[***pid***\]** 356 | Wait for the process with the given *pid* to exit. If no *pid* is given, 357 | all outstanding processes are waited for. 358 | 359 | **whatis*** name ...* 360 | Print the value of each *name* in a form suitable for input to *rc*. The 361 | output is an assignment to any variable, the definition of any function, 362 | a call to **builtin** for any built-in command, or the completed 363 | pathname of any executable file. 364 | 365 | **\~*** subject pattern ...* 366 | The *subject* is matched against each *pattern* in sequence. If it 367 | matches any pattern, **$status** is set to zero. Otherwise, **$status** 368 | is set to one. Patterns are the same as for file name matching, except 369 | that **/** and the first character of **.** and **..** need not be 370 | matched explicitly. The *patterns* are not subjected to file name 371 | matching before the **\~** command is executed, so they need not be 372 | enclosed in quotation marks. 373 | 374 | ## Environment 375 | 376 | The *environment* is a list of strings made available to executing 377 | binaries by the **env** device (see *env*(3)). *Rc* creates an 378 | environment entry for each variable whose value is non-empty, and for 379 | each function. The string for a variable entry has the variable's name 380 | followed by **=** and its value. If the value has more than one 381 | component, these are separated by ctrl-a (**'\\001'**) characters. The 382 | string for a function is just the *rc* input that defines the function. 383 | The name of a function in the environment is the function name preceded 384 | by 385 | 386 | When *rc* starts executing it reads variable and function definitions 387 | from its environment. 388 | 389 | ## Special Variables 390 | 391 | The following variables are set or used by *rc*. 392 | 393 | - **$\*** 394 | Set to *rc*'s argument list during initialization. Whenever a **.** 395 | command or a function is executed, the current value is saved and 396 | **$\*** receives the new argument list. The saved value is restored 397 | on completion of the **.** or function. 398 | 399 | - **$apid** 400 | Whenever a process is started asynchronously with **&**, **$apid** 401 | is set to its process id. 402 | 403 | - **$home** 404 | The default directory for **cd**. 405 | 406 | - **$ifs** 407 | The input field separators used in backquote substitutions. If 408 | **$ifs** is not set in *rc*'s environment, it is initialized to 409 | blank, tab and newline. 410 | 411 | - **$path** 412 | The search path used to find commands and input files for the **.** 413 | command. If not set in the environment, it is initialized by 414 | **path=(. /bin)**. Its use is discouraged; instead use *bind*(1) to 415 | build a **/bin** containing what's needed. 416 | 417 | - **$pid** 418 | Set during initialization to *rc*'s process id. 419 | 420 | - **$prompt** 421 | When *rc* is run interactively, the first component of **$prompt** 422 | is printed before reading each command. The second component is 423 | printed whenever a newline is typed and more lines are required to 424 | complete the command. If not set in the environment, it is 425 | initialized by **prompt=('% ' ' ')**. 426 | 427 | - **$status** 428 | Set to the wait message of the last-executed program. (unless 429 | started with **&).** **\!** and **\~** also change **$status**. Its 430 | value is used to control execution in **&&**, **||**, **if** and 431 | **while** commands. When *rc* exits at end-of-file of its input or 432 | on executing an **exit** command with no argument, **$status** is 433 | its exit status. 434 | 435 | ## Invocation 436 | 437 | If *rc* is started with no arguments it reads commands from standard 438 | input. Otherwise its first non-flag argument is the name of a file from 439 | which to read commands (but see **-c** below). Subsequent arguments 440 | become the initial value of **$\***. *Rc* accepts the following 441 | command-line flags. 442 | 443 | - **-c*** string* 444 | Commands are read from *string*. 445 | 446 | - **-s** 447 | Print out exit status after any command where the status is 448 | non-null. 449 | 450 | - **-e** 451 | Exit if **$status** is non-null after executing a simple command. 452 | 453 | - **-i** 454 | If **-i** is present, or *rc* is given no arguments and its standard 455 | input is a terminal, it runs interactively. Commands are prompted 456 | for using **$prompt**. 457 | 458 | - **-I** 459 | Makes sure *rc* is not run interactively. 460 | 461 | - **-l** 462 | If **-l** is given or the first character of argument zero is **-**, 463 | *rc* reads commands from **$home/lib/profile**, if it exists, before 464 | reading its normal input. 465 | 466 | - **-m** 467 | Read commands to initialize *rc* from *initial* instead of from 468 | **/rc/lib/rcmain**. 469 | 470 | - **-p** 471 | A no-op. 472 | 473 | - **-d** 474 | A no-op. 475 | 476 | - **-v** 477 | Echo input on file descriptor 2 as it is read. 478 | 479 | - **-x** 480 | Print each simple command before executing it. 481 | 482 | - **-r** 483 | Print debugging information (internal form of commands as they are 484 | executed). 485 | 486 | # SOURCE 487 | 488 | **/sys/src/cmd/rc** 489 | 490 | # SEE ALSO 491 | 492 | Tom Duff, \`\`Rc - The Plan 9 Shell''. 493 | 494 | # BUGS 495 | 496 | There should be a way to match patterns against whole lists rather than 497 | just single strings. 498 | 499 | Using **\~** to check the value of **$status** changes **$status**. 500 | 501 | Functions containing here documents don't work. 502 | 503 | Free carets don't get inserted next to keywords. 504 | -------------------------------------------------------------------------------- /rcmain.unix: -------------------------------------------------------------------------------- 1 | # rcmain: unix version 2 | if(~ $#home 0) home=$HOME 3 | if(~ $#ifs 0) ifs=' 4 | ' 5 | profile=$home/.rcrc 6 | switch($#prompt){ 7 | case 0 8 | prompt=('% ' ' ') 9 | case 1 10 | prompt=($prompt ' ') 11 | } 12 | if(~ $rcname ?.out) prompt=('broken! ' ' ') 13 | if(flag p) path=/bin 14 | if not { 15 | finit 16 | if(~ $#path 0) path=(. /bin /usr/bin /usr/local/bin) 17 | } 18 | fn sigexit 19 | if(! ~ $#cflag 0){ 20 | if(flag l && test -r $profile) . $profile 21 | status='' 22 | eval $cflag 23 | } 24 | if not if(flag i){ 25 | if(flag l && test -r $profile) . $profile 26 | status='' 27 | if(! ~ $#* 0) . $* 28 | . -i /dev/fd/0 29 | } 30 | if not if(~ $#* 0) . /dev/fd/0 31 | if not{ 32 | status='' 33 | . $* 34 | } 35 | exit $status 36 | -------------------------------------------------------------------------------- /simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Maybe `simple' is a misnomer. 3 | */ 4 | #include "rc.h" 5 | #include "getflags.h" 6 | #include "exec.h" 7 | #include "io.h" 8 | #include "fns.h" 9 | /* 10 | * Search through the following code to see if we're just going to exit. 11 | */ 12 | int 13 | exitnext(void){ 14 | union code *c=&runq->code[runq->pc]; 15 | while(c->f==Xpopredir) c++; 16 | return c->f==Xexit; 17 | } 18 | 19 | void 20 | Xsimple(void) 21 | { 22 | word *a; 23 | thread *p = runq; 24 | var *v; 25 | struct builtin *bp; 26 | int pid; 27 | globlist(); 28 | a = runq->argv->words; 29 | if(a==0){ 30 | Xerror1("empty argument list"); 31 | return; 32 | } 33 | if(flag['x']) 34 | pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ 35 | v = gvlook(a->word); 36 | if(v->fn) 37 | execfunc(v); 38 | else{ 39 | if(strcmp(a->word, "builtin")==0){ 40 | if(count(a)==1){ 41 | pfmt(err, "builtin: empty argument list\n"); 42 | setstatus("empty arg list"); 43 | poplist(); 44 | return; 45 | } 46 | a = a->next; 47 | popword(); 48 | } 49 | for(bp = Builtin;bp->name;bp++) 50 | if(strcmp(a->word, bp->name)==0){ 51 | (*bp->fnc)(); 52 | return; 53 | } 54 | if(exitnext()){ 55 | /* fork and wait is redundant */ 56 | pushword("exec"); 57 | execexec(); 58 | Xexit(); 59 | } 60 | else{ 61 | flush(err); 62 | Updenv(); /* necessary so changes don't go out again */ 63 | if((pid = execforkexec()) < 0){ 64 | Xerror("try again"); 65 | return; 66 | } 67 | 68 | /* interrupts don't get us out */ 69 | poplist(); 70 | while(Waitfor(pid, 1) < 0) 71 | ; 72 | } 73 | } 74 | } 75 | struct word nullpath = { "", 0}; 76 | 77 | void 78 | doredir(redir *rp) 79 | { 80 | if(rp){ 81 | doredir(rp->next); 82 | switch(rp->type){ 83 | case ROPEN: 84 | if(rp->from!=rp->to){ 85 | Dup(rp->from, rp->to); 86 | close(rp->from); 87 | } 88 | break; 89 | case RDUP: 90 | Dup(rp->from, rp->to); 91 | break; 92 | case RCLOSE: 93 | close(rp->from); 94 | break; 95 | } 96 | } 97 | } 98 | 99 | word* 100 | searchpath(char *w) 101 | { 102 | word *path; 103 | if(strncmp(w, "/", 1)==0 104 | /* || strncmp(w, "#", 1)==0 */ 105 | || strncmp(w, "./", 2)==0 106 | || strncmp(w, "../", 3)==0 107 | || (path = vlook("path")->val)==0) 108 | path=&nullpath; 109 | return path; 110 | } 111 | 112 | void 113 | execexec(void) 114 | { 115 | popword(); /* "exec" */ 116 | if(runq->argv->words==0){ 117 | Xerror1("empty argument list"); 118 | return; 119 | } 120 | doredir(runq->redir); 121 | Execute(runq->argv->words, searchpath(runq->argv->words->word)); 122 | poplist(); 123 | } 124 | 125 | void 126 | execfunc(var *func) 127 | { 128 | word *starval; 129 | popword(); 130 | starval = runq->argv->words; 131 | runq->argv->words = 0; 132 | poplist(); 133 | start(func->fn, func->pc, runq->local); 134 | runq->local = newvar(strdup("*"), runq->local); 135 | runq->local->val = starval; 136 | runq->local->changed = 1; 137 | } 138 | 139 | int 140 | dochdir(char *word) 141 | { 142 | /* report to /dev/wdir if it exists and we're interactive */ 143 | static int wdirfd = -2; 144 | if(chdir(word)<0) return -1; 145 | if(flag['i']!=0){ 146 | if(wdirfd==-2) /* try only once */ 147 | wdirfd = open("/dev/wdir", OWRITE|OCEXEC); 148 | if(wdirfd>=0) 149 | write(wdirfd, word, strlen(word)); 150 | } 151 | return 1; 152 | } 153 | 154 | void 155 | execcd(void) 156 | { 157 | word *a = runq->argv->words; 158 | word *cdpath; 159 | char dir[512]; 160 | setstatus("can't cd"); 161 | cdpath = vlook("cdpath")->val; 162 | switch(count(a)){ 163 | default: 164 | pfmt(err, "Usage: cd [directory]\n"); 165 | break; 166 | case 2: 167 | if(a->next->word[0]=='/' || cdpath==0) 168 | cdpath=&nullpath; 169 | for(;cdpath;cdpath = cdpath->next){ 170 | strcpy(dir, cdpath->word); 171 | if(dir[0]) 172 | strcat(dir, "/"); 173 | strcat(dir, a->next->word); 174 | if(dochdir(dir)>=0){ 175 | if(strlen(cdpath->word) 176 | && strcmp(cdpath->word, ".")!=0) 177 | pfmt(err, "%s\n", dir); 178 | setstatus(""); 179 | break; 180 | } 181 | } 182 | if(cdpath==0) 183 | pfmt(err, "Can't cd %s: %r\n", a->next->word); 184 | break; 185 | case 1: 186 | a = vlook("home")->val; 187 | if(count(a)>=1){ 188 | if(dochdir(a->word)>=0) 189 | setstatus(""); 190 | else 191 | pfmt(err, "Can't cd %s: %r\n", a->word); 192 | } 193 | else 194 | pfmt(err, "Can't cd -- $home empty\n"); 195 | break; 196 | } 197 | poplist(); 198 | } 199 | 200 | void 201 | execexit(void) 202 | { 203 | switch(count(runq->argv->words)){ 204 | default: 205 | pfmt(err, "Usage: exit [status]\nExiting anyway\n"); 206 | case 2: 207 | setstatus(runq->argv->words->next->word); 208 | case 1: Xexit(); 209 | } 210 | } 211 | 212 | void 213 | execshift(void) 214 | { 215 | int n; 216 | word *a; 217 | var *star; 218 | switch(count(runq->argv->words)){ 219 | default: 220 | pfmt(err, "Usage: shift [n]\n"); 221 | setstatus("shift usage"); 222 | poplist(); 223 | return; 224 | case 2: 225 | n = atoi(runq->argv->words->next->word); 226 | break; 227 | case 1: 228 | n = 1; 229 | break; 230 | } 231 | star = vlook("*"); 232 | for(;n && star->val;--n){ 233 | a = star->val->next; 234 | efree(star->val->word); 235 | efree((char *)star->val); 236 | star->val = a; 237 | star->changed = 1; 238 | } 239 | setstatus(""); 240 | poplist(); 241 | } 242 | 243 | int 244 | octal(char *s) 245 | { 246 | int n = 0; 247 | while(*s==' ' || *s=='\t' || *s=='\n') s++; 248 | while('0'<=*s && *s<='7') n = n*8+*s++-'0'; 249 | return n; 250 | } 251 | 252 | int 253 | mapfd(int fd) 254 | { 255 | redir *rp; 256 | for(rp = runq->redir;rp;rp = rp->next){ 257 | switch(rp->type){ 258 | case RCLOSE: 259 | if(rp->from==fd) 260 | fd=-1; 261 | break; 262 | case RDUP: 263 | case ROPEN: 264 | if(rp->to==fd) 265 | fd = rp->from; 266 | break; 267 | } 268 | } 269 | return fd; 270 | } 271 | union code rdcmds[4]; 272 | 273 | void 274 | execcmds(io *f) 275 | { 276 | static int first = 1; 277 | if(first){ 278 | rdcmds[0].i = 1; 279 | rdcmds[1].f = Xrdcmds; 280 | rdcmds[2].f = Xreturn; 281 | first = 0; 282 | } 283 | start(rdcmds, 1, runq->local); 284 | runq->cmdfd = f; 285 | runq->iflast = 0; 286 | } 287 | 288 | void 289 | execeval(void) 290 | { 291 | char *cmdline, *s, *t; 292 | int len = 0; 293 | word *ap; 294 | if(count(runq->argv->words)<=1){ 295 | Xerror1("Usage: eval cmd ..."); 296 | return; 297 | } 298 | eflagok = 1; 299 | for(ap = runq->argv->words->next;ap;ap = ap->next) 300 | len+=1+strlen(ap->word); 301 | cmdline = emalloc(len); 302 | s = cmdline; 303 | for(ap = runq->argv->words->next;ap;ap = ap->next){ 304 | for(t = ap->word;*t;) *s++=*t++; 305 | *s++=' '; 306 | } 307 | s[-1]='\n'; 308 | poplist(); 309 | execcmds(opencore(cmdline, len)); 310 | efree(cmdline); 311 | } 312 | union code dotcmds[14]; 313 | 314 | void 315 | execdot(void) 316 | { 317 | int iflag = 0; 318 | int fd; 319 | list *av; 320 | thread *p = runq; 321 | char *zero; 322 | static int first = 1; 323 | char file[512]; 324 | word *path; 325 | if(first){ 326 | dotcmds[0].i = 1; 327 | dotcmds[1].f = Xmark; 328 | dotcmds[2].f = Xword; 329 | dotcmds[3].s="0"; 330 | dotcmds[4].f = Xlocal; 331 | dotcmds[5].f = Xmark; 332 | dotcmds[6].f = Xword; 333 | dotcmds[7].s="*"; 334 | dotcmds[8].f = Xlocal; 335 | dotcmds[9].f = Xrdcmds; 336 | dotcmds[10].f = Xunlocal; 337 | dotcmds[11].f = Xunlocal; 338 | dotcmds[12].f = Xreturn; 339 | first = 0; 340 | } 341 | else 342 | eflagok = 1; 343 | popword(); 344 | if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ 345 | iflag = 1; 346 | popword(); 347 | } 348 | /* get input file */ 349 | if(p->argv->words==0){ 350 | Xerror1("Usage: . [-i] file [arg ...]"); 351 | return; 352 | } 353 | zero = strdup(p->argv->words->word); 354 | popword(); 355 | fd=-1; 356 | for(path = searchpath(zero);path;path = path->next){ 357 | strcpy(file, path->word); 358 | if(file[0]) 359 | strcat(file, "/"); 360 | strcat(file, zero); 361 | if((fd = open(file, 0))>=0) break; 362 | if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ 363 | fd = Dup1(0); 364 | if(fd>=0) 365 | break; 366 | } 367 | } 368 | if(fd<0){ 369 | pfmt(err, "%s: ", zero); 370 | setstatus("can't open"); 371 | Xerror(".: can't open"); 372 | return; 373 | } 374 | /* set up for a new command loop */ 375 | start(dotcmds, 1, (struct var *)0); 376 | pushredir(RCLOSE, fd, 0); 377 | runq->cmdfile = zero; 378 | runq->cmdfd = openfd(fd); 379 | runq->iflag = iflag; 380 | runq->iflast = 0; 381 | /* push $* value */ 382 | pushlist(); 383 | runq->argv->words = p->argv->words; 384 | /* free caller's copy of $* */ 385 | av = p->argv; 386 | p->argv = av->next; 387 | efree((char *)av); 388 | /* push $0 value */ 389 | pushlist(); 390 | pushword(zero); 391 | ndot++; 392 | } 393 | 394 | void 395 | execflag(void) 396 | { 397 | char *letter, *val; 398 | switch(count(runq->argv->words)){ 399 | case 2: 400 | setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set"); 401 | break; 402 | case 3: 403 | letter = runq->argv->words->next->word; 404 | val = runq->argv->words->next->next->word; 405 | if(strlen(letter)==1){ 406 | if(strcmp(val, "+")==0){ 407 | flag[(uchar)letter[0]] = flagset; 408 | break; 409 | } 410 | if(strcmp(val, "-")==0){ 411 | flag[(uchar)letter[0]] = 0; 412 | break; 413 | } 414 | } 415 | default: 416 | Xerror1("Usage: flag [letter] [+-]"); 417 | return; 418 | } 419 | poplist(); 420 | } 421 | 422 | void 423 | execwhatis(void){ /* mildly wrong -- should fork before writing */ 424 | word *a, *b, *path; 425 | var *v; 426 | struct builtin *bp; 427 | char file[512]; 428 | struct io out[1]; 429 | int found, sep; 430 | a = runq->argv->words->next; 431 | if(a==0){ 432 | Xerror1("Usage: whatis name ..."); 433 | return; 434 | } 435 | setstatus(""); 436 | out->fd = mapfd(1); 437 | out->bufp = out->buf; 438 | out->ebuf = &out->buf[NBUF]; 439 | out->strp = 0; 440 | for(;a;a = a->next){ 441 | v = vlook(a->word); 442 | if(v->val){ 443 | pfmt(out, "%s=", a->word); 444 | if(v->val->next==0) 445 | pfmt(out, "%q\n", v->val->word); 446 | else{ 447 | sep='('; 448 | for(b = v->val;b && b->word;b = b->next){ 449 | pfmt(out, "%c%q", sep, b->word); 450 | sep=' '; 451 | } 452 | pfmt(out, ")\n"); 453 | } 454 | found = 1; 455 | } 456 | else 457 | found = 0; 458 | v = gvlook(a->word); 459 | if(v->fn) 460 | pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); 461 | else{ 462 | for(bp = Builtin;bp->name;bp++) 463 | if(strcmp(a->word, bp->name)==0){ 464 | pfmt(out, "builtin %s\n", a->word); 465 | break; 466 | } 467 | if(!bp->name){ 468 | for(path = searchpath(a->word);path;path = path->next){ 469 | strcpy(file, path->word); 470 | if(file[0]) 471 | strcat(file, "/"); 472 | strcat(file, a->word); 473 | if(Executable(file)){ 474 | pfmt(out, "%s\n", file); 475 | break; 476 | } 477 | } 478 | if(!path && !found){ 479 | pfmt(err, "%s: not found\n", a->word); 480 | setstatus("not found"); 481 | } 482 | } 483 | } 484 | } 485 | poplist(); 486 | flush(err); 487 | } 488 | 489 | void 490 | execwait(void) 491 | { 492 | switch(count(runq->argv->words)){ 493 | default: 494 | Xerror1("Usage: wait [pid]"); 495 | return; 496 | case 2: 497 | Waitfor(atoi(runq->argv->words->next->word), 0); 498 | break; 499 | case 1: 500 | Waitfor(-1, 0); 501 | break; 502 | } 503 | poplist(); 504 | } 505 | -------------------------------------------------------------------------------- /subr.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "io.h" 4 | #include "fns.h" 5 | 6 | void* 7 | emalloc(long n) 8 | { 9 | char *p = (char *)Malloc(n); 10 | if(p==0) 11 | panic("Can't malloc %d bytes", n); 12 | /* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } /**/ 13 | memset(p, 0, n); 14 | return p; 15 | } 16 | 17 | void 18 | efree(void *p) 19 | { 20 | /* pfmt(err, "free %p\n", p); flush(err); /**/ 21 | if(p) 22 | free(p); 23 | else pfmt(err, "free 0\n"); 24 | } 25 | extern int lastword, lastdol; 26 | 27 | void 28 | yyerror(char *m) 29 | { 30 | pfmt(err, "rc: "); 31 | if(runq->cmdfile && !runq->iflag) 32 | pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno); 33 | else if(runq->cmdfile) 34 | pfmt(err, "%s: ", runq->cmdfile); 35 | else if(!runq->iflag) 36 | pfmt(err, "line %d: ", runq->lineno); 37 | if(tok[0] && tok[0]!='\n') 38 | pfmt(err, "token %q: ", tok); 39 | pfmt(err, "%s\n", m); 40 | flush(err); 41 | lastword = 0; 42 | lastdol = 0; 43 | while(lastc!='\n' && lastc!=EOF) advance(); 44 | nerror++; 45 | setvar("status", newword(m, (word *)0)); 46 | } 47 | char *bp; 48 | 49 | static void 50 | iacvt(int n) 51 | { 52 | if(n<0){ 53 | *bp++='-'; 54 | n=-n; /* doesn't work for n==-inf */ 55 | } 56 | if(n/10) 57 | iacvt(n/10); 58 | *bp++=n%10+'0'; 59 | } 60 | 61 | void 62 | inttoascii(char *s, long n) 63 | { 64 | bp = s; 65 | iacvt(n); 66 | *bp='\0'; 67 | } 68 | 69 | void 70 | panic(char *s, int n) 71 | { 72 | pfmt(err, "rc: "); 73 | pfmt(err, s, n); 74 | pchr(err, '\n'); 75 | flush(err); 76 | Abort(); 77 | } 78 | -------------------------------------------------------------------------------- /syn.y: -------------------------------------------------------------------------------- 1 | %term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN 2 | %term WORD REDIR DUP PIPE SUB 3 | %term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ 4 | /* operator priorities -- lowest first */ 5 | %left IF WHILE FOR SWITCH ')' NOT 6 | %left ANDAND OROR 7 | %left BANG SUBSHELL 8 | %left PIPE 9 | %left '^' 10 | %right '$' COUNT '"' 11 | %left SUB 12 | %{ 13 | #include "rc.h" 14 | #include "fns.h" 15 | %} 16 | %union{ 17 | struct tree *tree; 18 | }; 19 | %type line paren brace body cmdsa cmdsan assign epilog redir 20 | %type cmd simple first word comword keyword words 21 | %type NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN 22 | %type WORD REDIR DUP PIPE 23 | %% 24 | rc: { return 1;} 25 | | line '\n' {return !compile($1);} 26 | line: cmd 27 | | cmdsa line {$$=tree2(';', $1, $2);} 28 | body: cmd 29 | | cmdsan body {$$=tree2(';', $1, $2);} 30 | cmdsa: cmd ';' 31 | | cmd '&' {$$=tree1('&', $1);} 32 | cmdsan: cmdsa 33 | | cmd '\n' 34 | brace: '{' body '}' {$$=tree1(BRACE, $2);} 35 | paren: '(' body ')' {$$=tree1(PCMD, $2);} 36 | assign: first '=' word {$$=tree2('=', $1, $3);} 37 | epilog: {$$=0;} 38 | | redir epilog {$$=mung2($1, $1->child[0], $2);} 39 | redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} 40 | | DUP 41 | cmd: {$$=0;} 42 | | brace epilog {$$=epimung($1, $2);} 43 | | IF paren {skipnl();} cmd 44 | {$$=mung2($1, $2, $4);} 45 | | IF NOT {skipnl();} cmd {$$=mung1($2, $4);} 46 | | FOR '(' word IN words ')' {skipnl();} cmd 47 | /* 48 | * if ``words'' is nil, we need a tree element to distinguish between 49 | * for(i in ) and for(i), the former being a loop over the empty set 50 | * and the latter being the implicit argument loop. so if $5 is nil 51 | * (the empty set), we represent it as "()". don't parenthesize non-nil 52 | * functions, to avoid growing parentheses every time we reread the 53 | * definition. 54 | */ 55 | {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} 56 | | FOR '(' word ')' {skipnl();} cmd 57 | {$$=mung3($1, $3, (struct tree *)0, $6);} 58 | | WHILE paren {skipnl();} cmd 59 | {$$=mung2($1, $2, $4);} 60 | | SWITCH word {skipnl();} brace 61 | {$$=tree2(SWITCH, $2, $4);} 62 | | simple {$$=simplemung($1);} 63 | | TWIDDLE word words {$$=mung2($1, $2, $3);} 64 | | cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} 65 | | cmd OROR cmd {$$=tree2(OROR, $1, $3);} 66 | | cmd PIPE cmd {$$=mung2($2, $1, $3);} 67 | | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} 68 | | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} 69 | | BANG cmd {$$=mung1($1, $2);} 70 | | SUBSHELL cmd {$$=mung1($1, $2);} 71 | | FN words brace {$$=tree2(FN, $2, $3);} 72 | | FN words {$$=tree1(FN, $2);} 73 | simple: first 74 | | simple word {$$=tree2(ARGLIST, $1, $2);} 75 | | simple redir {$$=tree2(ARGLIST, $1, $2);} 76 | first: comword 77 | | first '^' word {$$=tree2('^', $1, $3);} 78 | word: keyword {lastword=1; $1->type=WORD;} 79 | | comword 80 | | word '^' word {$$=tree2('^', $1, $3);} 81 | comword: '$' word {$$=tree1('$', $2);} 82 | | '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} 83 | | '"' word {$$=tree1('"', $2);} 84 | | COUNT word {$$=tree1(COUNT, $2);} 85 | | WORD 86 | | '`' brace {$$=tree1('`', $2);} 87 | | '(' words ')' {$$=tree1(PAREN, $2);} 88 | | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} 89 | keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN 90 | words: {$$=(struct tree*)0;} 91 | | words word {$$=tree2(WORDS, $1, $2);} 92 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "fns.h" 4 | #include "io.h" 5 | extern char *Signame[]; 6 | 7 | void 8 | dotrap(void) 9 | { 10 | int i; 11 | struct var *trapreq; 12 | struct word *starval; 13 | starval = vlook("*")->val; 14 | while(ntrap) for(i = 0;i!=NSIG;i++) while(trap[i]){ 15 | --trap[i]; 16 | --ntrap; 17 | if(getpid()!=mypid) Exit(getstatus()); 18 | trapreq = vlook(Signame[i]); 19 | if(trapreq->fn){ 20 | start(trapreq->fn, trapreq->pc, (struct var *)0); 21 | runq->local = newvar(strdup("*"), runq->local); 22 | runq->local->val = copywords(starval, (struct word *)0); 23 | runq->local->changed = 1; 24 | runq->redir = runq->startredir = 0; 25 | } 26 | else if(i==SIGINT || i==SIGQUIT){ 27 | /* 28 | * run the stack down until we uncover the 29 | * command reading loop. Xreturn will exit 30 | * if there is none (i.e. if this is not 31 | * an interactive rc.) 32 | */ 33 | while(!runq->iflag) Xreturn(); 34 | } 35 | else Exit(getstatus()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tree.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "io.h" 4 | #include "fns.h" 5 | tree *treenodes; 6 | /* 7 | * create and clear a new tree node, and add it 8 | * to the node list. 9 | */ 10 | 11 | tree* 12 | newtree(void) 13 | { 14 | tree *t = new(tree); 15 | t->iskw = 0; 16 | t->str = 0; 17 | t->child[0] = t->child[1] = t->child[2] = 0; 18 | t->next = treenodes; 19 | treenodes = t; 20 | return t; 21 | } 22 | 23 | void 24 | freenodes(void) 25 | { 26 | tree *t, *u; 27 | for(t = treenodes;t;t = u){ 28 | u = t->next; 29 | if(t->str) 30 | efree(t->str); 31 | efree((char *)t); 32 | } 33 | treenodes = 0; 34 | } 35 | 36 | tree* 37 | tree1(int type, tree *c0) 38 | { 39 | return tree3(type, c0, (tree *)0, (tree *)0); 40 | } 41 | 42 | tree* 43 | tree2(int type, tree *c0, tree *c1) 44 | { 45 | return tree3(type, c0, c1, (tree *)0); 46 | } 47 | 48 | tree* 49 | tree3(int type, tree *c0, tree *c1, tree *c2) 50 | { 51 | tree *t; 52 | if(type==';'){ 53 | if(c0==0) 54 | return c1; 55 | if(c1==0) 56 | return c0; 57 | } 58 | t = newtree(); 59 | t->type = type; 60 | t->child[0] = c0; 61 | t->child[1] = c1; 62 | t->child[2] = c2; 63 | return t; 64 | } 65 | 66 | tree* 67 | mung1(tree *t, tree *c0) 68 | { 69 | t->child[0] = c0; 70 | return t; 71 | } 72 | 73 | tree* 74 | mung2(tree *t, tree *c0, tree *c1) 75 | { 76 | t->child[0] = c0; 77 | t->child[1] = c1; 78 | return t; 79 | } 80 | 81 | tree* 82 | mung3(tree *t, tree *c0, tree *c1, tree *c2) 83 | { 84 | t->child[0] = c0; 85 | t->child[1] = c1; 86 | t->child[2] = c2; 87 | return t; 88 | } 89 | 90 | tree* 91 | epimung(tree *comp, tree *epi) 92 | { 93 | tree *p; 94 | if(epi==0) 95 | return comp; 96 | for(p = epi;p->child[1];p = p->child[1]); 97 | p->child[1] = comp; 98 | return epi; 99 | } 100 | /* 101 | * Add a SIMPLE node at the root of t and percolate all the redirections 102 | * up to the root. 103 | */ 104 | 105 | tree* 106 | simplemung(tree *t) 107 | { 108 | tree *u; 109 | struct io *s; 110 | t = tree1(SIMPLE, t); 111 | s = openstr(); 112 | pfmt(s, "%t", t); 113 | t->str = strdup(s->strp); 114 | closeio(s); 115 | for(u = t->child[0];u->type==ARGLIST;u = u->child[0]){ 116 | if(u->child[1]->type==DUP 117 | || u->child[1]->type==REDIR){ 118 | u->child[1]->child[1] = t; 119 | t = u->child[1]; 120 | u->child[1] = 0; 121 | } 122 | } 123 | return t; 124 | } 125 | 126 | tree* 127 | token(char *str, int type) 128 | { 129 | tree *t = newtree(); 130 | t->type = type; 131 | t->str = strdup(str); 132 | return t; 133 | } 134 | 135 | void 136 | freetree(tree *p) 137 | { 138 | if(p==0) 139 | return; 140 | freetree(p->child[0]); 141 | freetree(p->child[1]); 142 | freetree(p->child[2]); 143 | if(p->str) 144 | efree(p->str); 145 | efree((char *)p); 146 | } 147 | -------------------------------------------------------------------------------- /unix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Unix versions of system-specific functions 3 | * By convention, exported routines herein have names beginning with an 4 | * upper case letter. 5 | */ 6 | #include "rc.h" 7 | #include "fns.h" 8 | #include "io.h" 9 | #include "exec.h" 10 | #include "getflags.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | char *Rcmain = PREFIX "/lib/rcmain"; 20 | char *Fdprefix = "/dev/fd/"; 21 | 22 | void execfinit(void); 23 | 24 | struct builtin Builtin[] = { 25 | "cd", execcd, 26 | "whatis", execwhatis, 27 | "eval", execeval, 28 | "exec", execexec, /* but with popword first */ 29 | "exit", execexit, 30 | "shift", execshift, 31 | "wait", execwait, 32 | "umask", execumask, 33 | ".", execdot, 34 | "finit", execfinit, 35 | "flag", execflag, 36 | "history", exechistory, 37 | 0 38 | }; 39 | #define SEP '\1' 40 | char **environp; 41 | 42 | struct word* 43 | enval(register char *s) 44 | { 45 | char *t, c; 46 | struct word *v; 47 | for(t = s;*t && *t!=SEP;t++); 48 | c=*t; 49 | *t='\0'; 50 | v = newword(s, c=='\0'?(struct word *)0:enval(t+1)); 51 | *t = c; 52 | return v; 53 | } 54 | 55 | void 56 | Vinit(void) 57 | { 58 | extern char **environ; 59 | char *s; 60 | char **env = environ; 61 | environp = env; 62 | for(;*env;env++){ 63 | for(s=*env;*s && *s!='(' && *s!='=';s++); 64 | switch(*s){ 65 | case '\0': 66 | pfmt(err, "environment %q?\n", *env); 67 | break; 68 | case '=': 69 | *s='\0'; 70 | setvar(*env, enval(s+1)); 71 | *s='='; 72 | break; 73 | case '(': /* ignore functions for now */ 74 | break; 75 | } 76 | } 77 | } 78 | 79 | char **envp; 80 | 81 | void 82 | Xrdfn(void) 83 | { 84 | char *s; 85 | int len; 86 | for(;*envp;envp++){ 87 | for(s=*envp;*s && *s!='(' && *s!='=';s++); 88 | switch(*s){ 89 | case '\0': 90 | pfmt(err, "environment %q?\n", *envp); 91 | break; 92 | case '=': /* ignore variables */ 93 | break; 94 | case '(': /* Bourne again */ 95 | s=*envp+3; 96 | envp++; 97 | len = strlen(s); 98 | s[len]='\n'; 99 | execcmds(opencore(s, len+1)); 100 | s[len]='\0'; 101 | return; 102 | } 103 | } 104 | Xreturn(); 105 | } 106 | 107 | union code rdfns[4]; 108 | 109 | void 110 | execfinit(void) 111 | { 112 | static int first = 1; 113 | if(first){ 114 | rdfns[0].i = 1; 115 | rdfns[1].f = Xrdfn; 116 | rdfns[2].f = Xjump; 117 | rdfns[3].i = 1; 118 | first = 0; 119 | } 120 | Xpopm(); 121 | envp = environp; 122 | start(rdfns, 1, runq->local); 123 | } 124 | 125 | int 126 | cmpenv(const void *aa, const void *ab) 127 | { 128 | char **a = (char**)aa, **b = (char**)ab; 129 | 130 | return strcmp(*a, *b); 131 | } 132 | 133 | char ** 134 | mkenv(void) 135 | { 136 | char **env, **ep, *p, *q; 137 | struct var **h, *v; 138 | struct word *a; 139 | int nvar = 0, nchr = 0, sep; 140 | 141 | /* 142 | * Slightly kludgy loops look at locals then globals. 143 | * locals no longer exist - geoff 144 | */ 145 | for(h = gvar-1; h != &gvar[NVAR]; h++) 146 | for(v = h >= gvar? *h: runq->local; v ;v = v->next){ 147 | if((v==vlook(v->name)) && v->val){ 148 | nvar++; 149 | nchr+=strlen(v->name)+1; 150 | for(a = v->val;a;a = a->next) 151 | nchr+=strlen(a->word)+1; 152 | } 153 | if(v->fn){ 154 | nvar++; 155 | nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; 156 | } 157 | } 158 | env = (char **)emalloc((nvar+1)*sizeof(char *)+nchr); 159 | ep = env; 160 | p = (char *)&env[nvar+1]; 161 | for(h = gvar-1; h != &gvar[NVAR]; h++) 162 | for(v = h >= gvar? *h: runq->local;v;v = v->next){ 163 | if((v==vlook(v->name)) && v->val){ 164 | *ep++=p; 165 | q = v->name; 166 | while(*q) *p++=*q++; 167 | sep='='; 168 | for(a = v->val;a;a = a->next){ 169 | *p++=sep; 170 | sep = SEP; 171 | q = a->word; 172 | while(*q) *p++=*q++; 173 | } 174 | *p++='\0'; 175 | } 176 | if(v->fn){ 177 | *ep++=p; 178 | *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ 179 | *p++='f'; *p++='n'; *p++=' '; 180 | q = v->name; 181 | while(*q) *p++=*q++; 182 | *p++=' '; 183 | q = v->fn[v->pc-1].s; 184 | while(*q) *p++=*q++; 185 | *p++='\0'; 186 | } 187 | } 188 | *ep = 0; 189 | qsort((void *)env, nvar, sizeof ep[0], cmpenv); 190 | return env; 191 | } 192 | char *sigmsg[] = { 193 | /* 0 normal */ 0, 194 | /* 1 SIGHUP */ "Hangup", 195 | /* 2 SIGINT */ 0, 196 | /* 3 SIGQUIT */ "Quit", 197 | /* 4 SIGILL */ "Illegal instruction", 198 | /* 5 SIGTRAP */ "Trace/BPT trap", 199 | /* 6 SIGIOT */ "abort", 200 | /* 7 SIGEMT */ "EMT trap", 201 | /* 8 SIGFPE */ "Floating exception", 202 | /* 9 SIGKILL */ "Killed", 203 | /* 10 SIGBUS */ "Bus error", 204 | /* 11 SIGSEGV */ "Memory fault", 205 | /* 12 SIGSYS */ "Bad system call", 206 | /* 13 SIGPIPE */ 0, 207 | /* 14 SIGALRM */ "Alarm call", 208 | /* 15 SIGTERM */ "Terminated", 209 | /* 16 unused */ "signal 16", 210 | /* 17 SIGSTOP */ "Process stopped", 211 | /* 18 unused */ "signal 18", 212 | /* 19 SIGCONT */ "Process continued", 213 | /* 20 SIGCHLD */ "Child death", 214 | }; 215 | 216 | int 217 | Waitfor(int pid, int persist) 218 | { 219 | int wpid, sig; 220 | struct thread *p; 221 | int wstat; 222 | char wstatstr[12]; 223 | 224 | for(;;){ 225 | errno = 0; 226 | wpid = wait(&wstat); 227 | if(errno==EINTR && persist) 228 | continue; 229 | if(wpid==-1) 230 | break; 231 | sig = wstat&0177; 232 | if(sig==0177){ 233 | pfmt(err, "trace: "); 234 | sig = (wstat>>8)&0177; 235 | } 236 | if(sig>(sizeof sigmsg/sizeof sigmsg[0]) || sigmsg[sig]){ 237 | if(pid!=wpid) 238 | pfmt(err, "%d: ", wpid); 239 | if(sig<=(sizeof sigmsg/sizeof sigmsg[0])) 240 | pfmt(err, "%s", sigmsg[sig]); 241 | else if(sig==0177) pfmt(err, "stopped by ptrace"); 242 | else pfmt(err, "signal %d", sig); 243 | if(wstat&0200)pfmt(err, " -- core dumped"); 244 | pfmt(err, "\n"); 245 | } 246 | wstat = sig?sig+1000:(wstat>>8)&0xFF; 247 | if(wpid==pid){ 248 | inttoascii(wstatstr, wstat); 249 | setstatus(wstatstr); 250 | break; 251 | } 252 | else{ 253 | for(p = runq->ret;p;p = p->ret) 254 | if(p->pid==wpid){ 255 | p->pid=-1; 256 | inttoascii(p->status, wstat); 257 | break; 258 | } 259 | } 260 | } 261 | return 0; 262 | } 263 | 264 | char ** 265 | mkargv(register struct word *a) 266 | { 267 | char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); 268 | char **argp = argv+1; /* leave one at front for runcoms */ 269 | 270 | for(;a;a = a->next) 271 | *argp++=a->word; 272 | *argp = 0; 273 | return argv; 274 | } 275 | 276 | void 277 | Updenv(void) 278 | { 279 | } 280 | 281 | void 282 | Execute(struct word *args, struct word *path) 283 | { 284 | char *msg="not found"; 285 | int txtbusy = 0; 286 | char **env = mkenv(); 287 | char **argv = mkargv(args); 288 | char file[512]; 289 | 290 | for(;path;path = path->next){ 291 | strcpy(file, path->word); 292 | if(file[0]) 293 | strcat(file, "/"); 294 | strcat(file, argv[1]); 295 | ReExec: 296 | execve(file, argv+1, env); 297 | switch(errno){ 298 | case ENOEXEC: 299 | pfmt(err, "%s: Bourne again\n", argv[1]); 300 | argv[0]="sh"; 301 | argv[1] = strdup(file); 302 | execve("/bin/sh", argv, env); 303 | goto Bad; 304 | case ETXTBSY: 305 | if(++txtbusy!=5){ 306 | sleep(txtbusy); 307 | goto ReExec; 308 | } 309 | msg="text busy"; goto Bad; 310 | case EACCES: 311 | msg="no access"; 312 | break; 313 | case ENOMEM: 314 | msg="not enough memory"; goto Bad; 315 | case E2BIG: 316 | msg="too big"; goto Bad; 317 | } 318 | } 319 | Bad: 320 | pfmt(err, "%s: %s\n", argv[1], msg); 321 | efree((char *)env); 322 | efree((char *)argv); 323 | } 324 | 325 | #define NDIR NAME_MAX 326 | 327 | int 328 | Globsize(register char *p) 329 | { 330 | int isglob = 0, globlen = NDIR+1; 331 | for(;*p;p++){ 332 | if(*p==GLOB){ 333 | p++; 334 | if(*p!=GLOB) 335 | isglob++; 336 | globlen+=*p=='*'?NDIR:1; 337 | } 338 | else 339 | globlen++; 340 | } 341 | return isglob?globlen:0; 342 | } 343 | 344 | #define NDIRLIST 50 345 | 346 | DIR *dirlist[NDIRLIST]; 347 | 348 | int 349 | Opendir(char *name) 350 | { 351 | DIR **dp; 352 | for(dp = dirlist;dp!=&dirlist[NDIRLIST];dp++) 353 | if(*dp==0){ 354 | *dp = opendir(name); 355 | return *dp?dp-dirlist:-1; 356 | } 357 | return -1; 358 | } 359 | 360 | int 361 | Readdir(int f, char *p, int onlydirs) 362 | { 363 | struct dirent *dp; 364 | 365 | if(f<0 || f>=NDIRLIST) 366 | return 0; 367 | 368 | dp = readdir(dirlist[f]); 369 | if(dp==0) 370 | return 0; 371 | strcpy(p, dp->d_name); 372 | return 1; 373 | } 374 | 375 | void 376 | Closedir(int f) 377 | { 378 | if(f<0 || f>=NDIRLIST) 379 | return; 380 | 381 | closedir(dirlist[f]); 382 | dirlist[f] = 0; 383 | } 384 | 385 | char *Signame[] = { 386 | "sigexit", "sighup", "sigint", "sigquit", 387 | "sigill", "sigtrap", "sigiot", "sigemt", 388 | "sigfpe", "sigkill", "sigbus", "sigsegv", 389 | "sigsys", "sigpipe", "sigalrm", "sigterm", 390 | "sig16", "sigstop", "sigtstp", "sigcont", 391 | "sigchld", "sigttin", "sigttou", "sigtint", 392 | "sigxcpu", "sigxfsz", "sig26", "sig27", 393 | "sig28", "sig29", "sig30", "sig31", 394 | 0, 395 | }; 396 | 397 | void 398 | gettrap(int sig) 399 | { 400 | signal(sig, gettrap); 401 | trap[sig]++; 402 | ntrap++; 403 | if(ntrap>=NSIG){ 404 | pfmt(err, "rc: Too many traps (trap %d), dumping core\n", sig); 405 | signal(SIGABRT, (void (*)())0); 406 | kill(getpid(), SIGABRT); 407 | } 408 | } 409 | 410 | void 411 | Trapinit(void) 412 | { 413 | int i; 414 | void (*sig)(); 415 | 416 | if(1 || flag['d']){ /* wrong!!! */ 417 | sig = signal(SIGINT, gettrap); 418 | if(sig==SIG_IGN) 419 | signal(SIGINT, SIG_IGN); 420 | } 421 | else{ 422 | for(i = 1;i<=NSIG;i++) if(i!=SIGCHLD){ 423 | sig = signal(i, gettrap); 424 | if(sig==SIG_IGN) 425 | signal(i, SIG_IGN); 426 | } 427 | } 428 | } 429 | 430 | void 431 | Unlink(char *name) 432 | { 433 | unlink(name); 434 | } 435 | 436 | long 437 | Write(int fd, char *buf, long cnt) 438 | { 439 | return write(fd, buf, cnt); 440 | } 441 | 442 | long 443 | Read(int fd, char *buf, long cnt) 444 | { 445 | return read(fd, buf, cnt); 446 | } 447 | 448 | long 449 | Seek(int fd, long cnt, long whence) 450 | { 451 | return lseek(fd, cnt, whence); 452 | } 453 | 454 | int 455 | Executable(char *file) 456 | { 457 | return(access(file, 01)==0); 458 | } 459 | 460 | int 461 | Creat(char *file) 462 | { 463 | return creat(file, 0666); 464 | } 465 | 466 | int 467 | Dup(int a, int b){ 468 | return dup2(a, b); 469 | } 470 | 471 | int 472 | Dup1(int a){ 473 | return dup(a); 474 | } 475 | /* 476 | * Wrong: should go through components of a|b|c and return the maximum. 477 | */ 478 | void 479 | Exit(char *stat) 480 | { 481 | int n = 0; 482 | 483 | while(*stat){ 484 | if(*stat!='|'){ 485 | if(*stat<'0' || '9'<*stat) 486 | exit(1); 487 | else n = n*10+*stat-'0'; 488 | } 489 | stat++; 490 | } 491 | exit(n); 492 | } 493 | 494 | int 495 | Eintr(void){ 496 | return errno==EINTR; 497 | } 498 | 499 | void 500 | Noerror(void) 501 | { 502 | errno = 0; 503 | } 504 | 505 | int 506 | Isatty(int fd){ 507 | return isatty(fd); 508 | } 509 | 510 | void 511 | Abort(void) 512 | { 513 | abort(); 514 | } 515 | 516 | void 517 | execumask(void) /* wrong -- should fork before writing */ 518 | { 519 | int m; 520 | struct io out[1]; 521 | switch(count(runq->argv->words)){ 522 | default: 523 | pfmt(err, "Usage: umask [umask]\n"); 524 | setstatus("umask usage"); 525 | poplist(); 526 | return; 527 | case 2: 528 | umask(octal(runq->argv->words->next->word)); 529 | break; 530 | case 1: 531 | umask(m = umask(0)); 532 | out->fd = mapfd(1); 533 | out->bufp = out->buf; 534 | out->ebuf=&out->buf[NBUF]; 535 | out->strp = 0; 536 | pfmt(out, "%o\n", m); 537 | break; 538 | } 539 | setstatus(""); 540 | poplist(); 541 | } 542 | 543 | void 544 | Memcpy(char *a, char *b, long n) 545 | { 546 | memmove(a, b, n); 547 | } 548 | 549 | void* 550 | Malloc(unsigned long n) 551 | { 552 | return (void *)malloc(n); 553 | } 554 | 555 | void 556 | errstr(char *buf, int len) 557 | { 558 | strncpy(buf, strerror(errno), len); 559 | } 560 | 561 | int 562 | needsrcquote(int c) 563 | { 564 | if(c <= ' ') 565 | return 1; 566 | if(strchr("`^#*[]=|\\?${}()'<>&;", c)) 567 | return 1; 568 | return 0; 569 | } 570 | 571 | int 572 | rfork(int bits) 573 | { 574 | return fork(); 575 | } 576 | 577 | int *waitpids; 578 | int nwaitpids; 579 | 580 | void 581 | addwaitpid(int pid) 582 | { 583 | waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); 584 | if(waitpids == 0) 585 | panic("Can't realloc %d waitpids", nwaitpids+1); 586 | waitpids[nwaitpids++] = pid; 587 | } 588 | 589 | void 590 | delwaitpid(int pid) 591 | { 592 | int r, w; 593 | 594 | for(r=w=0; r 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef NSIG 13 | #define NSIG 32 14 | #endif 15 | 16 | #ifndef ERRMAX 17 | #define ERRMAX 128 18 | #endif 19 | 20 | /* plan 9 compatibility */ 21 | #define RFPROC 1 22 | #define RFFDG 1 23 | #define RFNOTEG 1 24 | 25 | #define uintptr uintptr_t 26 | #define Rune wchar_t 27 | 28 | #define nil ((void*)0) 29 | 30 | /* in case uchar, etc. are built-in types */ 31 | #define uchar _fmtuchar 32 | #define ushort _fmtushort 33 | #define uint _fmtuint 34 | #define ulong _fmtulong 35 | #define vlong _fmtvlong 36 | #define uvlong _fmtuvlong 37 | 38 | typedef unsigned char uchar; 39 | typedef unsigned short ushort; 40 | typedef unsigned int uint; 41 | typedef unsigned long ulong; 42 | typedef unsigned long long uvlong; 43 | 44 | #define OREAD O_RDONLY 45 | #define OWRITE O_WRONLY 46 | #define ORDWR O_RDWR 47 | #define OCEXEC 0 48 | 49 | extern char *argv0; 50 | extern int chartorune(wchar_t *, const char *); 51 | extern int rfork(int); 52 | extern void errstr(char *, int); 53 | #define rerrstr errstr 54 | 55 | extern void exechistory(void); 56 | 57 | -------------------------------------------------------------------------------- /var.c: -------------------------------------------------------------------------------- 1 | #include "rc.h" 2 | #include "exec.h" 3 | #include "fns.h" 4 | 5 | int 6 | hash(char *s, int n) 7 | { 8 | int h = 0, i = 1; 9 | while(*s) h+=*s++*i++; 10 | h%=n; 11 | return h<0?h+n:h; 12 | } 13 | #define NKW 30 14 | struct kw{ 15 | char *name; 16 | int type; 17 | struct kw *next; 18 | }*kw[NKW]; 19 | 20 | void 21 | kenter(int type, char *name) 22 | { 23 | int h = hash(name, NKW); 24 | struct kw *p = new(struct kw); 25 | p->type = type; 26 | p->name = name; 27 | p->next = kw[h]; 28 | kw[h] = p; 29 | } 30 | 31 | void 32 | kinit(void) 33 | { 34 | kenter(FOR, "for"); 35 | kenter(IN, "in"); 36 | kenter(WHILE, "while"); 37 | kenter(IF, "if"); 38 | kenter(NOT, "not"); 39 | kenter(TWIDDLE, "~"); 40 | kenter(BANG, "!"); 41 | kenter(SUBSHELL, "@"); 42 | kenter(SWITCH, "switch"); 43 | kenter(FN, "fn"); 44 | } 45 | 46 | tree* 47 | klook(char *name) 48 | { 49 | struct kw *p; 50 | tree *t = token(name, WORD); 51 | for(p = kw[hash(name, NKW)];p;p = p->next) 52 | if(strcmp(p->name, name)==0){ 53 | t->type = p->type; 54 | t->iskw = 1; 55 | break; 56 | } 57 | return t; 58 | } 59 | 60 | var* 61 | gvlook(char *name) 62 | { 63 | int h = hash(name, NVAR); 64 | var *v; 65 | for(v = gvar[h];v;v = v->next) if(strcmp(v->name, name)==0) return v; 66 | return gvar[h] = newvar(strdup(name), gvar[h]); 67 | } 68 | 69 | var* 70 | vlook(char *name) 71 | { 72 | var *v; 73 | if(runq) 74 | for(v = runq->local;v;v = v->next) 75 | if(strcmp(v->name, name)==0) return v; 76 | return gvlook(name); 77 | } 78 | 79 | void 80 | _setvar(char *name, word *val, int callfn) 81 | { 82 | struct var *v = vlook(name); 83 | freewords(v->val); 84 | v->val=val; 85 | v->changed=1; 86 | if(callfn && v->changefn) 87 | v->changefn(v); 88 | } 89 | 90 | void 91 | setvar(char *name, word *val) 92 | { 93 | _setvar(name, val, 1); 94 | } 95 | 96 | void 97 | bigpath(var *v) 98 | { 99 | /* convert $PATH to $path */ 100 | char *p, *q; 101 | word **l, *w; 102 | 103 | if(v->val == nil){ 104 | _setvar("path", nil, 0); 105 | return; 106 | } 107 | p = v->val->word; 108 | w = nil; 109 | l = &w; 110 | /* 111 | * Doesn't handle escaped colon nonsense. 112 | */ 113 | if(p[0] == 0) 114 | p = nil; 115 | while(p){ 116 | q = strchr(p, ':'); 117 | if(q) 118 | *q = 0; 119 | *l = newword(p[0] ? p : ".", nil); 120 | l = &(*l)->next; 121 | if(q){ 122 | *q = ':'; 123 | p = q+1; 124 | }else 125 | p = nil; 126 | } 127 | _setvar("path", w, 0); 128 | } 129 | 130 | char* 131 | list2strcolon(word *words) 132 | { 133 | char *value, *s, *t; 134 | int len = 0; 135 | word *ap; 136 | for(ap = words;ap;ap = ap->next) 137 | len+=1+strlen(ap->word); 138 | value = emalloc(len+1); 139 | s = value; 140 | for(ap = words;ap;ap = ap->next){ 141 | for(t = ap->word;*t;) *s++=*t++; 142 | *s++=':'; 143 | } 144 | if(s==value) 145 | *s='\0'; 146 | else s[-1]='\0'; 147 | return value; 148 | } 149 | void 150 | littlepath(var *v) 151 | { 152 | /* convert $path to $PATH */ 153 | char *p; 154 | word *w; 155 | 156 | p = list2strcolon(v->val); 157 | w = new(word); 158 | w->word = p; 159 | w->next = nil; 160 | _setvar("PATH", w, 1); /* 1: recompute $path to expose colon problems */ 161 | } 162 | 163 | void 164 | pathinit(void) 165 | { 166 | var *v; 167 | 168 | v = gvlook("path"); 169 | v->changefn = littlepath; 170 | v = gvlook("PATH"); 171 | v->changefn = bigpath; 172 | bigpath(v); 173 | } 174 | -------------------------------------------------------------------------------- /win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Plan 9 versions of system-specific functions 3 | * By convention, exported routines herein have names beginning with an 4 | * upper case letter. 5 | */ 6 | #include "rc.h" 7 | #include "exec.h" 8 | #include "io.h" 9 | #include "fns.h" 10 | #include "getflags.h" 11 | char *Signame[] = { 12 | "sigexit", "sighup", "sigint", "sigquit", 13 | "sigalrm", "sigkill", "sigfpe", "sigterm", 14 | 0 15 | }; 16 | char *syssigname[] = { 17 | "exit", /* can't happen */ 18 | "hangup", 19 | "interrupt", 20 | "quit", /* can't happen */ 21 | "alarm", 22 | "kill", 23 | "sys: fp: ", 24 | "term", 25 | 0 26 | }; 27 | char *Rcmain = "/rc/lib/rcmain"; 28 | char *Fdprefix = "/fd/"; 29 | 30 | void execfinit(void); 31 | void execbind(void); 32 | 33 | builtin Builtin[] = { 34 | "cd", execcd, 35 | "whatis", execwhatis, 36 | "eval", execeval, 37 | "exec", execexec, /* but with popword first */ 38 | "exit", execexit, 39 | "shift", execshift, 40 | "wait", execwait, 41 | ".", execdot, 42 | "finit", execfinit, 43 | "flag", execflag, 44 | 0 45 | }; 46 | 47 | void 48 | Vinit(void) 49 | { 50 | int dir, f, len; 51 | word *val; 52 | char *buf, *s; 53 | Dir *ent; 54 | int i, nent; 55 | char envname[256]; 56 | dir = open("/env", OREAD); 57 | if(dir<0){ 58 | pfmt(err, "rc: can't open /env: %r\n"); 59 | return; 60 | } 61 | ent = nil; 62 | for(;;){ 63 | nent = dirread(dir, &ent); 64 | if(nent <= 0) 65 | break; 66 | for(i = 0; i=0){ 71 | buf = emalloc((int)len+1); 72 | read(f, buf, (long)len); 73 | val = 0; 74 | /* Charitably add a 0 at the end if need be */ 75 | if(buf[len-1]) 76 | buf[len++]='\0'; 77 | s = buf+len-1; 78 | for(;;){ 79 | while(s!=buf && s[-1]!='\0') --s; 80 | val = newword(s, val); 81 | if(s==buf) 82 | break; 83 | --s; 84 | } 85 | setvar(ent[i].name, val); 86 | vlook(ent[i].name)->changed = 0; 87 | close(f); 88 | efree(buf); 89 | } 90 | } 91 | } 92 | free(ent); 93 | } 94 | close(dir); 95 | } 96 | int envdir; 97 | 98 | void 99 | Xrdfn(void) 100 | { 101 | int f, len; 102 | static Dir *ent, *allocent; 103 | static int nent; 104 | Dir *e; 105 | char envname[256]; 106 | 107 | for(;;){ 108 | if(nent == 0){ 109 | free(allocent); 110 | nent = dirread(envdir, &allocent); 111 | ent = allocent; 112 | } 113 | if(nent <= 0) 114 | break; 115 | while(nent){ 116 | e = ent++; 117 | nent--; 118 | len = e->length; 119 | if(len && strncmp(e->name, "fn#", 3)==0){ 120 | snprint(envname, sizeof envname, "/env/%s", e->name); 121 | if((f = open(envname, 0))>=0){ 122 | execcmds(openfd(f)); 123 | return; 124 | } 125 | } 126 | } 127 | } 128 | close(envdir); 129 | Xreturn(); 130 | } 131 | union code rdfns[4]; 132 | 133 | void 134 | execfinit(void) 135 | { 136 | static int first = 1; 137 | if(first){ 138 | rdfns[0].i = 1; 139 | rdfns[1].f = Xrdfn; 140 | rdfns[2].f = Xjump; 141 | rdfns[3].i = 1; 142 | first = 0; 143 | } 144 | Xpopm(); 145 | envdir = open("/env", 0); 146 | if(envdir<0){ 147 | pfmt(err, "rc: can't open /env: %r\n"); 148 | return; 149 | } 150 | start(rdfns, 1, runq->local); 151 | } 152 | 153 | int 154 | Waitfor(int pid, int persist) 155 | { 156 | thread *p; 157 | Waitmsg *w; 158 | char errbuf[ERRMAX]; 159 | 160 | while((w = wait()) != nil){ 161 | if(w->pid==pid){ 162 | setstatus(w->msg); 163 | free(w); 164 | return 0; 165 | } 166 | for(p = runq->ret;p;p = p->ret) 167 | if(p->pid==w->pid){ 168 | p->pid=-1; 169 | strcpy(p->status, w->msg); 170 | } 171 | free(w); 172 | } 173 | 174 | errstr(errbuf, sizeof errbuf); 175 | if(strcmp(errbuf, "interrupted")==0) return -1; 176 | return 0; 177 | } 178 | 179 | char* 180 | *mkargv(word *a) 181 | { 182 | char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); 183 | char **argp = argv+1; /* leave one at front for runcoms */ 184 | for(;a;a = a->next) *argp++=a->word; 185 | *argp = 0; 186 | return argv; 187 | } 188 | 189 | void 190 | addenv(var *v) 191 | { 192 | char envname[256]; 193 | word *w; 194 | int f; 195 | io *fd; 196 | if(v->changed){ 197 | v->changed = 0; 198 | snprint(envname, sizeof envname, "/env/%s", v->name); 199 | if((f = Creat(envname))<0) 200 | pfmt(err, "rc: can't open %s: %r\n", envname); 201 | else{ 202 | for(w = v->val;w;w = w->next) 203 | write(f, w->word, strlen(w->word)+1L); 204 | close(f); 205 | } 206 | } 207 | if(v->fnchanged){ 208 | v->fnchanged = 0; 209 | snprint(envname, sizeof envname, "/env/fn#%s", v->name); 210 | if((f = Creat(envname))<0) 211 | pfmt(err, "rc: can't open %s: %r\n", envname); 212 | else{ 213 | if(v->fn){ 214 | fd = openfd(f); 215 | pfmt(fd, "fn %q %s\n", v->name, v->fn[v->pc-1].s); 216 | closeio(fd); 217 | } 218 | close(f); 219 | } 220 | } 221 | } 222 | 223 | void 224 | updenvlocal(var *v) 225 | { 226 | if(v){ 227 | updenvlocal(v->next); 228 | addenv(v); 229 | } 230 | } 231 | 232 | void 233 | Updenv(void) 234 | { 235 | var *v, **h; 236 | for(h = gvar;h!=&gvar[NVAR];h++) 237 | for(v=*h;v;v = v->next) 238 | addenv(v); 239 | if(runq) 240 | updenvlocal(runq->local); 241 | } 242 | 243 | int 244 | ForkExecute(char *file, char **argv, int sin, int sout, int serr) 245 | { 246 | int pid; 247 | 248 | {int i; 249 | fprint(2, "forkexec %s", file); 250 | for(i = 0; argv[i]; i++)fprint(2, " %s", argv[i]); 251 | fprint(2, " %d %d %d\n", sin, sout, serr); 252 | } 253 | if(access(file, 1) != 0) 254 | return -1; 255 | fprint(2, "forking\n"); 256 | switch(pid = fork()){ 257 | case -1: 258 | return -1; 259 | case 0: 260 | if(sin >= 0) 261 | dup(sin, 0); 262 | else 263 | close(0); 264 | if(sout >= 0) 265 | dup(sout, 1); 266 | else 267 | close(1); 268 | if(serr >= 0) 269 | dup(serr, 2); 270 | else 271 | close(2); 272 | fprint(2, "execing\n"); 273 | exec(file, argv); 274 | fprint(2, "exec: %r\n"); 275 | exits(file); 276 | } 277 | return pid; 278 | } 279 | 280 | void 281 | Execute(word *args, word *path) 282 | { 283 | char **argv = mkargv(args); 284 | char file[1024]; 285 | int nc; 286 | Updenv(); 287 | for(;path;path = path->next){ 288 | nc = strlen(path->word); 289 | if(nc<1024){ 290 | strcpy(file, path->word); 291 | if(file[0]){ 292 | strcat(file, "/"); 293 | nc++; 294 | } 295 | if(nc+strlen(argv[1])<1024){ 296 | strcat(file, argv[1]); 297 | exec(file, argv+1); 298 | } 299 | else werrstr("command name too long"); 300 | } 301 | } 302 | rerrstr(file, sizeof file); 303 | pfmt(err, "%s: %s\n", argv[1], file); 304 | efree((char *)argv); 305 | } 306 | #define NDIR 256 /* shoud be a better way */ 307 | 308 | int 309 | Globsize(char *p) 310 | { 311 | int isglob = 0, globlen = NDIR+1; 312 | for(;*p;p++){ 313 | if(*p==GLOB){ 314 | p++; 315 | if(*p!=GLOB) 316 | isglob++; 317 | globlen+=*p=='*'?NDIR:1; 318 | } 319 | else 320 | globlen++; 321 | } 322 | return isglob?globlen:0; 323 | } 324 | #define NFD 50 325 | #define NDBUF 32 326 | struct{ 327 | Dir *dbuf; 328 | int i; 329 | int n; 330 | }dir[NFD]; 331 | 332 | int 333 | Opendir(char *name) 334 | { 335 | Dir *db; 336 | int f; 337 | f = open(name, 0); 338 | if(f==-1) 339 | return f; 340 | db = dirfstat(f); 341 | if(db!=nil && (db->mode&DMDIR)){ 342 | if(f=NFD) 378 | return 0; 379 | Again: 380 | if(dir[f].i==dir[f].n){ /* read */ 381 | free(dir[f].dbuf); 382 | dir[f].dbuf = 0; 383 | n = dirread(f, &dir[f].dbuf); 384 | if(n>0){ 385 | if(onlydirs){ 386 | n = trimdirs(dir[f].dbuf, n); 387 | if(n == 0) 388 | goto Again; 389 | } 390 | dir[f].n = n; 391 | }else 392 | dir[f].n = 0; 393 | dir[f].i = 0; 394 | } 395 | if(dir[f].i == dir[f].n) 396 | return 0; 397 | strcpy(p, dir[f].dbuf[dir[f].i].name); 398 | dir[f].i++; 399 | return 1; 400 | } 401 | 402 | void 403 | Closedir(int f) 404 | { 405 | if(f>=0 && f=32){ /* rc is probably in a trap loop */ 431 | pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); 432 | abort(); 433 | } 434 | noted(NCONT); 435 | } 436 | 437 | void 438 | Trapinit(void) 439 | { 440 | notify(notifyf); 441 | } 442 | 443 | void 444 | Unlink(char *name) 445 | { 446 | remove(name); 447 | } 448 | 449 | long 450 | Write(int fd, void *buf, long cnt) 451 | { 452 | return write(fd, buf, (long)cnt); 453 | } 454 | 455 | long 456 | Read(int fd, void *buf, long cnt) 457 | { 458 | return read(fd, buf, cnt); 459 | } 460 | 461 | long 462 | Seek(int fd, long cnt, long whence) 463 | { 464 | return seek(fd, cnt, whence); 465 | } 466 | 467 | int 468 | Executable(char *file) 469 | { 470 | Dir *statbuf; 471 | int ret; 472 | 473 | statbuf = dirstat(file); 474 | if(statbuf == nil) 475 | return 0; 476 | ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); 477 | free(statbuf); 478 | return ret; 479 | } 480 | 481 | int 482 | Creat(char *file) 483 | { 484 | return create(file, 1, 0666L); 485 | } 486 | 487 | int 488 | Dup(int a, int b) 489 | { 490 | return dup(a, b); 491 | } 492 | 493 | int 494 | Dup1(int) 495 | { 496 | return -1; 497 | } 498 | 499 | void 500 | Exit(char *stat) 501 | { 502 | Updenv(); 503 | setstatus(stat); 504 | exits(truestatus()?"":getstatus()); 505 | } 506 | 507 | int 508 | Eintr(void) 509 | { 510 | return interrupted; 511 | } 512 | 513 | void 514 | Noerror(void) 515 | { 516 | interrupted = 0; 517 | } 518 | 519 | int 520 | Isatty(int fd) 521 | { 522 | Dir *d1, *d2; 523 | int ret; 524 | 525 | d1 = dirfstat(fd); 526 | if(d1 == nil) 527 | return 0; 528 | if(strncmp(d1->name, "ptty", 4)==0){ /* fwd complaints to philw */ 529 | free(d1); 530 | return 1; 531 | } 532 | d2 = dirstat("/dev/cons"); 533 | if(d2 == nil){ 534 | free(d1); 535 | return 0; 536 | } 537 | ret = (d1->type==d2->type&&d1->dev==d2->dev&&d1->qid.path==d2->qid.path); 538 | free(d1); 539 | free(d2); 540 | return ret; 541 | } 542 | 543 | void 544 | Abort(void) 545 | { 546 | pfmt(err, "aborting\n"); 547 | flush(err); 548 | Exit("aborting"); 549 | } 550 | 551 | void 552 | Memcpy(void *a, void *b, long n) 553 | { 554 | memmove(a, b, n); 555 | } 556 | 557 | void* 558 | Malloc(ulong n) 559 | { 560 | return malloc(n); 561 | } 562 | -------------------------------------------------------------------------------- /x.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 2.3. */ 2 | 3 | /* Skeleton interface for Bison's Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 6 | Free Software Foundation, Inc. 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2, or (at your option) 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | /* As a special exception, you may create a larger work that contains 24 | part or all of the Bison parser skeleton and distribute that work 25 | under terms of your choice, so long as that work isn't itself a 26 | parser generator using the skeleton or a modified version thereof 27 | as a parser skeleton. Alternatively, if you modify or redistribute 28 | the parser skeleton itself, you may (at your option) remove this 29 | special exception, which will cause the skeleton and the resulting 30 | Bison output files to be licensed under the GNU General Public 31 | License without this special exception. 32 | 33 | This special exception was added by the Free Software Foundation in 34 | version 2.2 of Bison. */ 35 | 36 | /* Tokens. */ 37 | #ifndef YYTOKENTYPE 38 | # define YYTOKENTYPE 39 | /* Put the tokens into the symbol table, so that GDB and other debuggers 40 | know about them. */ 41 | enum yytokentype { 42 | FOR = 258, 43 | IN = 259, 44 | WHILE = 260, 45 | IF = 261, 46 | NOT = 262, 47 | TWIDDLE = 263, 48 | BANG = 264, 49 | SUBSHELL = 265, 50 | SWITCH = 266, 51 | FN = 267, 52 | WORD = 268, 53 | REDIR = 269, 54 | DUP = 270, 55 | PIPE = 271, 56 | SUB = 272, 57 | SIMPLE = 273, 58 | ARGLIST = 274, 59 | WORDS = 275, 60 | BRACE = 276, 61 | PAREN = 277, 62 | PCMD = 278, 63 | PIPEFD = 279, 64 | OROR = 280, 65 | ANDAND = 281, 66 | COUNT = 282 67 | }; 68 | #endif 69 | /* Tokens. */ 70 | #define FOR 258 71 | #define IN 259 72 | #define WHILE 260 73 | #define IF 261 74 | #define NOT 262 75 | #define TWIDDLE 263 76 | #define BANG 264 77 | #define SUBSHELL 265 78 | #define SWITCH 266 79 | #define FN 267 80 | #define WORD 268 81 | #define REDIR 269 82 | #define DUP 270 83 | #define PIPE 271 84 | #define SUB 272 85 | #define SIMPLE 273 86 | #define ARGLIST 274 87 | #define WORDS 275 88 | #define BRACE 276 89 | #define PAREN 277 90 | #define PCMD 278 91 | #define PIPEFD 279 92 | #define OROR 280 93 | #define ANDAND 281 94 | #define COUNT 282 95 | 96 | 97 | 98 | 99 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 100 | typedef union YYSTYPE 101 | #line 16 "syn.y" 102 | { 103 | struct tree *tree; 104 | } 105 | /* Line 1529 of yacc.c. */ 106 | #line 107 "y.tab.h" 107 | YYSTYPE; 108 | # define yystype YYSTYPE /* obsolescent; will be withdrawn */ 109 | # define YYSTYPE_IS_DECLARED 1 110 | # define YYSTYPE_IS_TRIVIAL 1 111 | #endif 112 | 113 | extern YYSTYPE yylval; 114 | 115 | -------------------------------------------------------------------------------- /y.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 2.3. */ 2 | 3 | /* Skeleton interface for Bison's Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 6 | Free Software Foundation, Inc. 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2, or (at your option) 11 | any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | /* As a special exception, you may create a larger work that contains 24 | part or all of the Bison parser skeleton and distribute that work 25 | under terms of your choice, so long as that work isn't itself a 26 | parser generator using the skeleton or a modified version thereof 27 | as a parser skeleton. Alternatively, if you modify or redistribute 28 | the parser skeleton itself, you may (at your option) remove this 29 | special exception, which will cause the skeleton and the resulting 30 | Bison output files to be licensed under the GNU General Public 31 | License without this special exception. 32 | 33 | This special exception was added by the Free Software Foundation in 34 | version 2.2 of Bison. */ 35 | 36 | /* Tokens. */ 37 | #ifndef YYTOKENTYPE 38 | # define YYTOKENTYPE 39 | /* Put the tokens into the symbol table, so that GDB and other debuggers 40 | know about them. */ 41 | enum yytokentype { 42 | FOR = 258, 43 | IN = 259, 44 | WHILE = 260, 45 | IF = 261, 46 | NOT = 262, 47 | TWIDDLE = 263, 48 | BANG = 264, 49 | SUBSHELL = 265, 50 | SWITCH = 266, 51 | FN = 267, 52 | WORD = 268, 53 | REDIR = 269, 54 | DUP = 270, 55 | PIPE = 271, 56 | SUB = 272, 57 | SIMPLE = 273, 58 | ARGLIST = 274, 59 | WORDS = 275, 60 | BRACE = 276, 61 | PAREN = 277, 62 | PCMD = 278, 63 | PIPEFD = 279, 64 | OROR = 280, 65 | ANDAND = 281, 66 | COUNT = 282 67 | }; 68 | #endif 69 | /* Tokens. */ 70 | #define FOR 258 71 | #define IN 259 72 | #define WHILE 260 73 | #define IF 261 74 | #define NOT 262 75 | #define TWIDDLE 263 76 | #define BANG 264 77 | #define SUBSHELL 265 78 | #define SWITCH 266 79 | #define FN 267 80 | #define WORD 268 81 | #define REDIR 269 82 | #define DUP 270 83 | #define PIPE 271 84 | #define SUB 272 85 | #define SIMPLE 273 86 | #define ARGLIST 274 87 | #define WORDS 275 88 | #define BRACE 276 89 | #define PAREN 277 90 | #define PCMD 278 91 | #define PIPEFD 279 92 | #define OROR 280 93 | #define ANDAND 281 94 | #define COUNT 282 95 | 96 | 97 | 98 | 99 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 100 | typedef union YYSTYPE 101 | #line 16 "syn.y" 102 | { 103 | struct tree *tree; 104 | } 105 | /* Line 1529 of yacc.c. */ 106 | #line 107 "y.tab.h" 107 | YYSTYPE; 108 | # define yystype YYSTYPE /* obsolescent; will be withdrawn */ 109 | # define YYSTYPE_IS_DECLARED 1 110 | # define YYSTYPE_IS_TRIVIAL 1 111 | #endif 112 | 113 | extern YYSTYPE yylval; 114 | 115 | --------------------------------------------------------------------------------