Blob
- Date:
- Message:
- rc: add recursive descent parser The old yacc-based parser is available with the -Y flag, which will probably be removed at some point. The new -D flag dumps a parse tree of the input, without executing it. This allows comparing the output of rc -D and rc -DY on different scripts to see that the two parsers behave the same. The rc paper ends by saying: It is remarkable that in the four most recent editions of the UNIX system programmer’s manual the Bourne shell grammar described in the manual page does not admit the command who|wc. This is surely an oversight, but it suggests something darker: nobody really knows what the Bourne shell’s grammar is. Even examination of the source code is little help. The parser is implemented by recursive descent, but the routines corresponding to the syntactic categories all have a flag argument that subtly changes their operation depending on the context. Rc’s parser is implemented using yacc, so I can say precisely what the grammar is. The new recursive descent parser here has no such flags. It is a straightforward translation of the yacc. The new parser will make it easier to handle free carats in more generality as well as potentially allow the use of unquoted = as a word character. Going through this exercise has highlighted a few dark corners here as well. For example, I was surprised to find that x >f | y >f x | y are different commands (the latter redirects y's output). It is similarly surprising that a=b x | y sets a during the execution of y. It is also a bit counter-intuitive x | y | z x | if(c) y | z are not both 3-phase pipelines. These are certainly not things we should change, but they are not entirely obvious from the man page description, undercutting the quoted claim a bit. On the other hand, who | wc is clearly accepted by the grammar in the manual page, and the new parser still handles that test case.
- Actions:
- History | Blame | Raw File
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 int13 exitnext(void){14 union code *c=&runq->code[runq->pc];15 while(c->f==Xpopredir) c++;16 return c->f==Xexit;17 }19 void20 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 }68 /* interrupts don't get us out */69 poplist();70 while(Waitfor(pid, 1) < 0)71 ;72 }73 }74 }75 struct word nullpath = { "", 0};77 void78 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 }99 word*100 searchpath(char *w)101 {102 word *path;103 if(strncmp(w, "/", 1)==0104 /* || strncmp(w, "#", 1)==0 */105 || strncmp(w, "./", 2)==0106 || strncmp(w, "../", 3)==0107 || (path = vlook("path")->val)==0)108 path=&nullpath;109 return path;110 }112 void113 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 }125 void126 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 }139 int140 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 }154 void155 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 else191 pfmt(err, "Can't cd %s: %r\n", a->word);192 }193 else194 pfmt(err, "Can't cd -- $home empty\n");195 break;196 }197 poplist();198 }200 void201 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 }212 void213 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 }243 int244 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 }252 int253 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];273 void274 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 }288 void289 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];314 void315 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;326 if(first){327 dotcmds[0].i = 1;328 dotcmds[1].f = Xmark;329 dotcmds[2].f = Xword;330 dotcmds[3].s="0";331 dotcmds[4].f = Xlocal;332 dotcmds[5].f = Xmark;333 dotcmds[6].f = Xword;334 dotcmds[7].s="*";335 dotcmds[8].f = Xlocal;336 dotcmds[9].f = Xrdcmds;337 dotcmds[10].f = Xunlocal;338 dotcmds[11].f = Xunlocal;339 dotcmds[12].f = Xreturn;340 first = 0;341 }342 else343 eflagok = 1;344 popword();345 if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){346 iflag = 1;347 popword();348 }349 /* get input file */350 if(p->argv->words==0){351 Xerror1("Usage: . [-i] file [arg ...]");352 return;353 }354 zero = strdup(p->argv->words->word);355 popword();356 fd=-1;357 for(path = searchpath(zero);path;path = path->next){358 strcpy(file, path->word);359 if(file[0])360 strcat(file, "/");361 strcat(file, zero);362 if((fd = open(file, 0))>=0) break;363 if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */364 fd = Dup1(0);365 if(fd>=0)366 break;367 }368 }369 if(fd<0){370 pfmt(err, "%s: ", zero);371 setstatus("can't open");372 Xerror(".: can't open");373 return;374 }375 /* set up for a new command loop */376 start(dotcmds, 1, (struct var *)0);377 pushredir(RCLOSE, fd, 0);378 runq->cmdfile = zero;379 runq->cmdfd = openfd(fd);380 runq->iflag = iflag;381 runq->iflast = 0;382 /* push $* value */383 pushlist();384 runq->argv->words = p->argv->words;385 /* free caller's copy of $* */386 av = p->argv;387 p->argv = av->next;388 efree((char *)av);389 /* push $0 value */390 pushlist();391 pushword(zero);392 ndot++;393 }395 void396 execflag(void)397 {398 char *letter, *val;399 switch(count(runq->argv->words)){400 case 2:401 setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set");402 break;403 case 3:404 letter = runq->argv->words->next->word;405 val = runq->argv->words->next->next->word;406 if(strlen(letter)==1){407 if(strcmp(val, "+")==0){408 flag[(uchar)letter[0]] = flagset;409 break;410 }411 if(strcmp(val, "-")==0){412 flag[(uchar)letter[0]] = 0;413 break;414 }415 }416 default:417 Xerror1("Usage: flag [letter] [+-]");418 return;419 }420 poplist();421 }423 void424 execwhatis(void){ /* mildly wrong -- should fork before writing */425 word *a, *b, *path;426 var *v;427 struct builtin *bp;428 char file[512];429 struct io out[1];430 int found, sep;431 a = runq->argv->words->next;432 if(a==0){433 Xerror1("Usage: whatis name ...");434 return;435 }436 setstatus("");437 out->fd = mapfd(1);438 out->bufp = out->buf;439 out->ebuf = &out->buf[NBUF];440 out->strp = 0;441 for(;a;a = a->next){442 v = vlook(a->word);443 if(v->val){444 pfmt(out, "%s=", a->word);445 if(v->val->next==0)446 pfmt(out, "%q\n", v->val->word);447 else{448 sep='(';449 for(b = v->val;b && b->word;b = b->next){450 pfmt(out, "%c%q", sep, b->word);451 sep=' ';452 }453 pfmt(out, ")\n");454 }455 found = 1;456 }457 else458 found = 0;459 v = gvlook(a->word);460 if(v->fn)461 pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s);462 else{463 for(bp = Builtin;bp->name;bp++)464 if(strcmp(a->word, bp->name)==0){465 pfmt(out, "builtin %s\n", a->word);466 break;467 }468 if(!bp->name){469 for(path = searchpath(a->word);path;path = path->next){470 strcpy(file, path->word);471 if(file[0])472 strcat(file, "/");473 strcat(file, a->word);474 if(Executable(file)){475 pfmt(out, "%s\n", file);476 break;477 }478 }479 if(!path && !found){480 pfmt(err, "%s: not found\n", a->word);481 setstatus("not found");482 }483 }484 }485 }486 poplist();487 flush(err);488 }490 void491 execwait(void)492 {493 switch(count(runq->argv->words)){494 default:495 Xerror1("Usage: wait [pid]");496 return;497 case 2:498 Waitfor(atoi(runq->argv->words->next->word), 0);499 break;500 case 1:501 Waitfor(-1, 0);502 break;503 }504 poplist();505 }