rsc | 4ee543e | 2005-03-18 19:03:25 +0000 | [diff] [blame] | 1 | #include <u.h> |
| 2 | #include <signal.h> |
| 3 | #include <sys/ioctl.h> |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 4 | #include "rc.h" |
| 5 | #include "getflags.h" |
| 6 | #include "exec.h" |
| 7 | #include "io.h" |
| 8 | #include "fns.h" |
| 9 | /* |
| 10 | * Start executing the given code at the given pc with the given redirection |
| 11 | */ |
| 12 | char *argv0="rc"; |
| 13 | void start(code *c, int pc, var *local) |
| 14 | { |
| 15 | struct thread *p=new(struct thread); |
| 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; |
rsc | 26a5fd5 | 2005-02-11 16:58:06 +0000 | [diff] [blame] | 26 | p->pid=-1; |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 27 | p->ret=runq; |
| 28 | runq=p; |
| 29 | } |
| 30 | word *newword(char *wd, word *next) |
| 31 | { |
| 32 | word *p=new(word); |
| 33 | p->word=strdup(wd); |
| 34 | p->next=next; |
| 35 | return p; |
| 36 | } |
| 37 | void pushword(char *wd) |
| 38 | { |
| 39 | if(runq->argv==0) panic("pushword but no argv!", 0); |
| 40 | runq->argv->words=newword(wd, runq->argv->words); |
| 41 | } |
| 42 | void popword(void){ |
| 43 | word *p; |
| 44 | if(runq->argv==0) panic("popword but no argv!", 0); |
| 45 | p=runq->argv->words; |
| 46 | if(p==0) panic("popword but no word!", 0); |
| 47 | runq->argv->words=p->next; |
| 48 | efree(p->word); |
| 49 | efree((char *)p); |
| 50 | } |
| 51 | void freelist(word *w) |
| 52 | { |
| 53 | word *nw; |
| 54 | while(w){ |
| 55 | nw=w->next; |
| 56 | efree(w->word); |
| 57 | efree((char *)w); |
| 58 | w=nw; |
| 59 | } |
| 60 | } |
| 61 | void pushlist(void){ |
| 62 | list *p=new(list); |
| 63 | p->next=runq->argv; |
| 64 | p->words=0; |
| 65 | runq->argv=p; |
| 66 | } |
| 67 | void poplist(void){ |
| 68 | list *p=runq->argv; |
| 69 | if(p==0) panic("poplist but no argv", 0); |
| 70 | freelist(p->words); |
| 71 | runq->argv=p->next; |
| 72 | efree((char *)p); |
| 73 | } |
| 74 | int count(word *w) |
| 75 | { |
| 76 | int n; |
| 77 | for(n=0;w;n++) w=w->next; |
| 78 | return n; |
| 79 | } |
| 80 | void pushredir(int type, int from, int to){ |
| 81 | redir * rp=new(redir); |
| 82 | rp->type=type; |
| 83 | rp->from=from; |
| 84 | rp->to=to; |
| 85 | rp->next=runq->redir; |
| 86 | runq->redir=rp; |
| 87 | } |
| 88 | var *newvar(char *name, var *next) |
| 89 | { |
| 90 | var *v=new(var); |
| 91 | v->name=name; |
| 92 | v->val=0; |
| 93 | v->fn=0; |
| 94 | v->changed=0; |
| 95 | v->fnchanged=0; |
| 96 | v->next=next; |
rsc | c8b6342 | 2005-01-13 04:49:19 +0000 | [diff] [blame] | 97 | v->changefn = 0; |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 98 | return v; |
| 99 | } |
| 100 | /* |
| 101 | * get command line flags, initialize keywords & traps. |
| 102 | * get values from environment. |
| 103 | * set $pid, $cflag, $* |
| 104 | * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*) |
| 105 | * start interpreting code |
| 106 | */ |
| 107 | int |
| 108 | main(int argc, char *argv[]) |
| 109 | { |
| 110 | code bootstrap[32]; |
| 111 | char num[12], *rcmain; |
| 112 | int i; |
| 113 | |
| 114 | argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1); |
| 115 | if(argc==-1) usage("[file [arg ...]]"); |
| 116 | if(argv[0][0]=='-') flag['l']=flagset; |
| 117 | if(flag['I']) flag['i'] = 0; |
| 118 | else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; |
| 119 | rcmain=flag['m']?flag['m'][0]:Rcmain(); |
| 120 | err=openfd(2); |
| 121 | kinit(); |
| 122 | Trapinit(); |
| 123 | Vinit(); |
| 124 | itoa(num, mypid=getpid()); |
rsc | a9eaaa0 | 2005-01-12 16:59:50 +0000 | [diff] [blame] | 125 | pathinit(); |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 126 | setvar("pid", newword(num, (word *)0)); |
| 127 | setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) |
| 128 | :(word *)0); |
| 129 | setvar("rcname", newword(argv[0], (word *)0)); |
| 130 | i=0; |
| 131 | bootstrap[i++].i=1; |
| 132 | bootstrap[i++].f=Xmark; |
| 133 | bootstrap[i++].f=Xword; |
| 134 | bootstrap[i++].s="*"; |
| 135 | bootstrap[i++].f=Xassign; |
| 136 | bootstrap[i++].f=Xmark; |
| 137 | bootstrap[i++].f=Xmark; |
| 138 | bootstrap[i++].f=Xword; |
| 139 | bootstrap[i++].s="*"; |
| 140 | bootstrap[i++].f=Xdol; |
| 141 | bootstrap[i++].f=Xword; |
| 142 | bootstrap[i++].s=rcmain; |
| 143 | bootstrap[i++].f=Xword; |
| 144 | bootstrap[i++].s="."; |
| 145 | bootstrap[i++].f=Xsimple; |
| 146 | bootstrap[i++].f=Xexit; |
| 147 | bootstrap[i].i=0; |
| 148 | start(bootstrap, 1, (var *)0); |
| 149 | /* prime bootstrap argv */ |
| 150 | pushlist(); |
| 151 | argv0 = strdup(argv[0]); |
| 152 | for(i=argc-1;i!=0;--i) pushword(argv[i]); |
| 153 | for(;;){ |
| 154 | if(flag['r']) pfnc(err, runq); |
| 155 | runq->pc++; |
| 156 | (*runq->code[runq->pc-1].f)(); |
| 157 | if(ntrap) dotrap(); |
| 158 | } |
| 159 | } |
| 160 | /* |
| 161 | * Opcode routines |
| 162 | * Arguments on stack (...) |
| 163 | * Arguments in line [...] |
| 164 | * Code in line with jump around {...} |
| 165 | * |
| 166 | * Xappend(file)[fd] open file to append |
| 167 | * Xassign(name, val) assign val to name |
| 168 | * Xasync{... Xexit} make thread for {}, no wait |
| 169 | * Xbackq{... Xreturn} make thread for {}, push stdout |
| 170 | * Xbang complement condition |
| 171 | * Xcase(pat, value){...} exec code on match, leave (value) on |
| 172 | * stack |
| 173 | * Xclose[i] close file descriptor |
| 174 | * Xconc(left, right) concatenate, push results |
| 175 | * Xcount(name) push var count |
| 176 | * Xdelfn(name) delete function definition |
| 177 | * Xdeltraps(names) delete named traps |
| 178 | * Xdol(name) get variable value |
| 179 | * Xqdol(name) concatenate variable components |
| 180 | * Xdup[i j] dup file descriptor |
| 181 | * Xexit rc exits with status |
| 182 | * Xfalse{...} execute {} if false |
| 183 | * Xfn(name){... Xreturn} define function |
| 184 | * Xfor(var, list){... Xreturn} for loop |
| 185 | * Xjump[addr] goto |
| 186 | * Xlocal(name, val) create local variable, assign value |
| 187 | * Xmark mark stack |
| 188 | * Xmatch(pat, str) match pattern, set status |
| 189 | * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, |
| 190 | * wait for both |
| 191 | * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, |
| 192 | * depending on type), push /dev/fd/?? |
| 193 | * Xpopm(value) pop value from stack |
| 194 | * Xread(file)[fd] open file to read |
| 195 | * Xsettraps(names){... Xreturn} define trap functions |
| 196 | * Xshowtraps print trap list |
| 197 | * Xsimple(args) run command and wait |
| 198 | * Xreturn kill thread |
| 199 | * Xsubshell{... Xexit} execute {} in a subshell and wait |
| 200 | * Xtrue{...} execute {} if true |
| 201 | * Xunlocal delete local variable |
| 202 | * Xword[string] push string |
| 203 | * Xwrite(file)[fd] open file to write |
| 204 | */ |
| 205 | void Xappend(void){ |
| 206 | char *file; |
| 207 | int f; |
| 208 | switch(count(runq->argv->words)){ |
| 209 | default: Xerror1(">> requires singleton"); return; |
| 210 | case 0: Xerror1(">> requires file"); return; |
| 211 | case 1: break; |
| 212 | } |
| 213 | file=runq->argv->words->word; |
| 214 | if((f=open(file, 1))<0 && (f=Creat(file))<0){ |
| 215 | pfmt(err, "%s: ", file); |
| 216 | Xerror("can't open"); |
| 217 | return; |
| 218 | } |
| 219 | Seek(f, 0L, 2); |
| 220 | pushredir(ROPEN, f, runq->code[runq->pc].i); |
| 221 | runq->pc++; |
| 222 | poplist(); |
| 223 | } |
| 224 | void Xasync(void){ |
| 225 | int null=open("/dev/null", 0); |
rsc | 4ee543e | 2005-03-18 19:03:25 +0000 | [diff] [blame] | 226 | int tty; |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 227 | int pid; |
| 228 | char npid[10]; |
| 229 | if(null<0){ |
| 230 | Xerror("Can't open /dev/null\n"); |
| 231 | return; |
| 232 | } |
| 233 | switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){ |
| 234 | case -1: |
| 235 | close(null); |
| 236 | Xerror("try again"); |
| 237 | break; |
| 238 | case 0: |
rsc | 4ee543e | 2005-03-18 19:03:25 +0000 | [diff] [blame] | 239 | /* |
| 240 | * I don't know what the right thing to do here is, |
| 241 | * so this is all experimentally determined. |
| 242 | * If we just dup /dev/null onto 0, then running |
| 243 | * ssh foo & will reopen /dev/tty, try to read a password, |
| 244 | * get a signal, and repeat, in a tight loop, forever. |
| 245 | * Arguably this is a bug in ssh (it behaves the same |
| 246 | * way under bash as under rc) but I'm fixing it here |
| 247 | * anyway. If we dissociate the process from the tty, |
| 248 | * then it won't be able to open /dev/tty ever again. |
| 249 | * The SIG_IGN on SIGTTOU makes writing the tty |
| 250 | * (via fd 1 or 2, for example) succeed even though |
| 251 | * our pgrp is not the terminal's controlling pgrp. |
| 252 | */ |
| 253 | if((tty=open("/dev/tty", OREAD)) >= 0){ |
| 254 | /* |
| 255 | * Should make reads of tty fail, writes succeed. |
| 256 | */ |
| 257 | signal(SIGTTIN, SIG_IGN); |
| 258 | signal(SIGTTOU, SIG_IGN); |
| 259 | ioctl(tty, TIOCNOTTY); |
| 260 | close(tty); |
| 261 | } |
| 262 | if(isatty(0)) |
| 263 | pushredir(ROPEN, null, 0); |
| 264 | else |
| 265 | close(null); |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 266 | start(runq->code, runq->pc+1, runq->local); |
| 267 | runq->ret=0; |
| 268 | break; |
| 269 | default: |
| 270 | close(null); |
| 271 | runq->pc=runq->code[runq->pc].i; |
| 272 | itoa(npid, pid); |
| 273 | setvar("apid", newword(npid, (word *)0)); |
| 274 | break; |
| 275 | } |
| 276 | } |
| 277 | void Xsettrue(void){ |
| 278 | setstatus(""); |
| 279 | } |
| 280 | void Xbang(void){ |
| 281 | setstatus(truestatus()?"false":""); |
| 282 | } |
| 283 | void Xclose(void){ |
| 284 | pushredir(RCLOSE, runq->code[runq->pc].i, 0); |
| 285 | runq->pc++; |
| 286 | } |
| 287 | void Xdup(void){ |
| 288 | pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); |
| 289 | runq->pc+=2; |
| 290 | } |
| 291 | void Xeflag(void){ |
| 292 | if(eflagok && !truestatus()) Xexit(); |
| 293 | } |
| 294 | void Xexit(void){ |
| 295 | struct var *trapreq; |
| 296 | struct word *starval; |
| 297 | static int beenhere=0; |
| 298 | if(getpid()==mypid && !beenhere){ |
| 299 | trapreq=vlook("sigexit"); |
| 300 | if(trapreq->fn){ |
| 301 | beenhere=1; |
| 302 | --runq->pc; |
| 303 | starval=vlook("*")->val; |
| 304 | start(trapreq->fn, trapreq->pc, (struct var *)0); |
| 305 | runq->local=newvar(strdup("*"), runq->local); |
| 306 | runq->local->val=copywords(starval, (struct word *)0); |
| 307 | runq->local->changed=1; |
| 308 | runq->redir=runq->startredir=0; |
| 309 | return; |
| 310 | } |
| 311 | } |
| 312 | Exit(getstatus()); |
| 313 | } |
| 314 | void Xfalse(void){ |
| 315 | if(truestatus()) runq->pc=runq->code[runq->pc].i; |
| 316 | else runq->pc++; |
| 317 | } |
| 318 | int ifnot; /* dynamic if not flag */ |
| 319 | void Xifnot(void){ |
| 320 | if(ifnot) |
| 321 | runq->pc++; |
| 322 | else |
| 323 | runq->pc=runq->code[runq->pc].i; |
| 324 | } |
| 325 | void Xjump(void){ |
| 326 | runq->pc=runq->code[runq->pc].i; |
| 327 | } |
| 328 | void Xmark(void){ |
| 329 | pushlist(); |
| 330 | } |
| 331 | void Xpopm(void){ |
| 332 | poplist(); |
| 333 | } |
| 334 | void Xread(void){ |
| 335 | char *file; |
| 336 | int f; |
| 337 | switch(count(runq->argv->words)){ |
| 338 | default: Xerror1("< requires singleton\n"); return; |
| 339 | case 0: Xerror1("< requires file\n"); return; |
| 340 | case 1: break; |
| 341 | } |
| 342 | file=runq->argv->words->word; |
| 343 | if((f=open(file, 0))<0){ |
| 344 | pfmt(err, "%s: ", file); |
| 345 | Xerror("can't open"); |
| 346 | return; |
| 347 | } |
| 348 | pushredir(ROPEN, f, runq->code[runq->pc].i); |
| 349 | runq->pc++; |
| 350 | poplist(); |
| 351 | } |
| 352 | void turfredir(void){ |
| 353 | while(runq->redir!=runq->startredir) |
| 354 | Xpopredir(); |
| 355 | } |
| 356 | void Xpopredir(void){ |
| 357 | struct redir *rp=runq->redir; |
| 358 | if(rp==0) panic("turfredir null!", 0); |
| 359 | runq->redir=rp->next; |
| 360 | if(rp->type==ROPEN) close(rp->from); |
| 361 | efree((char *)rp); |
| 362 | } |
| 363 | void Xreturn(void){ |
| 364 | struct thread *p=runq; |
| 365 | turfredir(); |
| 366 | while(p->argv) poplist(); |
| 367 | codefree(p->code); |
| 368 | runq=p->ret; |
| 369 | efree((char *)p); |
| 370 | if(runq==0) Exit(getstatus()); |
| 371 | } |
| 372 | void Xtrue(void){ |
| 373 | if(truestatus()) runq->pc++; |
| 374 | else runq->pc=runq->code[runq->pc].i; |
| 375 | } |
| 376 | void Xif(void){ |
| 377 | ifnot=1; |
| 378 | if(truestatus()) runq->pc++; |
| 379 | else runq->pc=runq->code[runq->pc].i; |
| 380 | } |
| 381 | void Xwastrue(void){ |
| 382 | ifnot=0; |
| 383 | } |
| 384 | void Xword(void){ |
| 385 | pushword(runq->code[runq->pc++].s); |
| 386 | } |
| 387 | void Xwrite(void){ |
| 388 | char *file; |
| 389 | int f; |
| 390 | switch(count(runq->argv->words)){ |
| 391 | default: Xerror1("> requires singleton\n"); return; |
| 392 | case 0: Xerror1("> requires file\n"); return; |
| 393 | case 1: break; |
| 394 | } |
| 395 | file=runq->argv->words->word; |
| 396 | if((f=Creat(file))<0){ |
| 397 | pfmt(err, "%s: ", file); |
| 398 | Xerror("can't open"); |
| 399 | return; |
| 400 | } |
| 401 | pushredir(ROPEN, f, runq->code[runq->pc].i); |
| 402 | runq->pc++; |
| 403 | poplist(); |
| 404 | } |
rsc | a9eaaa0 | 2005-01-12 16:59:50 +0000 | [diff] [blame] | 405 | char *_list2str(word *words, int c){ |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 406 | char *value, *s, *t; |
| 407 | int len=0; |
| 408 | word *ap; |
| 409 | for(ap=words;ap;ap=ap->next) |
| 410 | len+=1+strlen(ap->word); |
| 411 | value=emalloc(len+1); |
| 412 | s=value; |
| 413 | for(ap=words;ap;ap=ap->next){ |
| 414 | for(t=ap->word;*t;) *s++=*t++; |
rsc | a9eaaa0 | 2005-01-12 16:59:50 +0000 | [diff] [blame] | 415 | *s++=c; |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 416 | } |
| 417 | if(s==value) *s='\0'; |
| 418 | else s[-1]='\0'; |
| 419 | return value; |
| 420 | } |
rsc | a9eaaa0 | 2005-01-12 16:59:50 +0000 | [diff] [blame] | 421 | char *list2str(word *words){ |
| 422 | return _list2str(words, ' '); |
| 423 | } |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 424 | void Xmatch(void){ |
| 425 | word *p; |
| 426 | char *subject; |
| 427 | subject=list2str(runq->argv->words); |
| 428 | setstatus("no match"); |
| 429 | for(p=runq->argv->next->words;p;p=p->next) |
| 430 | if(match(subject, p->word, '\0')){ |
| 431 | setstatus(""); |
| 432 | break; |
| 433 | } |
| 434 | efree(subject); |
| 435 | poplist(); |
| 436 | poplist(); |
| 437 | } |
| 438 | void Xcase(void){ |
| 439 | word *p; |
| 440 | char *s; |
| 441 | int ok=0; |
| 442 | s=list2str(runq->argv->next->words); |
| 443 | for(p=runq->argv->words;p;p=p->next){ |
| 444 | if(match(s, p->word, '\0')){ |
| 445 | ok=1; |
| 446 | break; |
| 447 | } |
| 448 | } |
| 449 | efree(s); |
| 450 | if(ok) |
| 451 | runq->pc++; |
| 452 | else |
| 453 | runq->pc=runq->code[runq->pc].i; |
| 454 | poplist(); |
| 455 | } |
| 456 | word *conclist(word *lp, word *rp, word *tail) |
| 457 | { |
| 458 | char *buf; |
| 459 | word *v; |
| 460 | if(lp->next || rp->next) |
| 461 | tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, |
| 462 | tail); |
| 463 | buf=emalloc(strlen(lp->word)+strlen(rp->word)+1); |
| 464 | strcpy(buf, lp->word); |
| 465 | strcat(buf, rp->word); |
| 466 | v=newword(buf, tail); |
| 467 | efree(buf); |
| 468 | return v; |
| 469 | } |
| 470 | void Xconc(void){ |
| 471 | word *lp=runq->argv->words; |
| 472 | word *rp=runq->argv->next->words; |
| 473 | word *vp=runq->argv->next->next->words; |
| 474 | int lc=count(lp), rc=count(rp); |
| 475 | if(lc!=0 || rc!=0){ |
| 476 | if(lc==0 || rc==0){ |
| 477 | Xerror1("null list in concatenation"); |
| 478 | return; |
| 479 | } |
| 480 | if(lc!=1 && rc!=1 && lc!=rc){ |
| 481 | Xerror1("mismatched list lengths in concatenation"); |
| 482 | return; |
| 483 | } |
| 484 | vp=conclist(lp, rp, vp); |
| 485 | } |
| 486 | poplist(); |
| 487 | poplist(); |
| 488 | runq->argv->words=vp; |
| 489 | } |
| 490 | void Xassign(void){ |
| 491 | var *v; |
| 492 | if(count(runq->argv->words)!=1){ |
| 493 | Xerror1("variable name not singleton!"); |
| 494 | return; |
| 495 | } |
| 496 | deglob(runq->argv->words->word); |
| 497 | v=vlook(runq->argv->words->word); |
| 498 | poplist(); |
| 499 | globlist(); |
| 500 | freewords(v->val); |
| 501 | v->val=runq->argv->words; |
| 502 | v->changed=1; |
rsc | a9eaaa0 | 2005-01-12 16:59:50 +0000 | [diff] [blame] | 503 | if(v->changefn) |
| 504 | v->changefn(v); |
rsc | f08fded | 2003-11-23 18:04:08 +0000 | [diff] [blame] | 505 | runq->argv->words=0; |
| 506 | poplist(); |
| 507 | } |
| 508 | /* |
| 509 | * copy arglist a, adding the copy to the front of tail |
| 510 | */ |
| 511 | word *copywords(word *a, word *tail) |
| 512 | { |
| 513 | word *v=0, **end; |
| 514 | for(end=&v;a;a=a->next,end=&(*end)->next) |
| 515 | *end=newword(a->word, 0); |
| 516 | *end=tail; |
| 517 | return v; |
| 518 | } |
| 519 | void Xdol(void){ |
| 520 | word *a, *star; |
| 521 | char *s, *t; |
| 522 | int n; |
| 523 | if(count(runq->argv->words)!=1){ |
| 524 | Xerror1("variable name not singleton!"); |
| 525 | return; |
| 526 | } |
| 527 | s=runq->argv->words->word; |
| 528 | deglob(s); |
| 529 | n=0; |
| 530 | for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; |
| 531 | a=runq->argv->next->words; |
| 532 | if(n==0 || *t) |
| 533 | a=copywords(vlook(s)->val, a); |
| 534 | else{ |
| 535 | star=vlook("*")->val; |
| 536 | if(star && 1<=n && n<=count(star)){ |
| 537 | while(--n) star=star->next; |
| 538 | a=newword(star->word, a); |
| 539 | } |
| 540 | } |
| 541 | poplist(); |
| 542 | runq->argv->words=a; |
| 543 | } |
| 544 | void Xqdol(void){ |
| 545 | word *a, *p; |
| 546 | char *s; |
| 547 | int n; |
| 548 | if(count(runq->argv->words)!=1){ |
| 549 | Xerror1("variable name not singleton!"); |
| 550 | return; |
| 551 | } |
| 552 | s=runq->argv->words->word; |
| 553 | deglob(s); |
| 554 | a=vlook(s)->val; |
| 555 | poplist(); |
| 556 | n=count(a); |
| 557 | if(n==0){ |
| 558 | pushword(""); |
| 559 | return; |
| 560 | } |
| 561 | for(p=a;p;p=p->next) n+=strlen(p->word); |
| 562 | s=emalloc(n); |
| 563 | if(a){ |
| 564 | strcpy(s, a->word); |
| 565 | for(p=a->next;p;p=p->next){ |
| 566 | strcat(s, " "); |
| 567 | strcat(s, p->word); |
| 568 | } |
| 569 | } |
| 570 | else |
| 571 | s[0]='\0'; |
| 572 | pushword(s); |
| 573 | efree(s); |
| 574 | } |
| 575 | word *subwords(word *val, int len, word *sub, word *a) |
| 576 | { |
| 577 | int n; |
| 578 | char *s; |
| 579 | if(!sub) return a; |
| 580 | a=subwords(val, len, sub->next, a); |
| 581 | s=sub->word; |
| 582 | deglob(s); |
| 583 | n=0; |
| 584 | while('0'<=*s && *s<='9') n=n*10+ *s++ -'0'; |
| 585 | if(n<1 || len<n) return a; |
| 586 | for(;n!=1;--n) val=val->next; |
| 587 | return newword(val->word, a); |
| 588 | } |
| 589 | void Xsub(void){ |
| 590 | word *a, *v; |
| 591 | char *s; |
| 592 | if(count(runq->argv->next->words)!=1){ |
| 593 | Xerror1("variable name not singleton!"); |
| 594 | return; |
| 595 | } |
| 596 | s=runq->argv->next->words->word; |
| 597 | deglob(s); |
| 598 | a=runq->argv->next->next->words; |
| 599 | v=vlook(s)->val; |
| 600 | a=subwords(v, count(v), runq->argv->words, a); |
| 601 | poplist(); |
| 602 | poplist(); |
| 603 | runq->argv->words=a; |
| 604 | } |
| 605 | void Xcount(void){ |
| 606 | word *a; |
| 607 | char *s, *t; |
| 608 | int n; |
| 609 | char num[12]; |
| 610 | if(count(runq->argv->words)!=1){ |
| 611 | Xerror1("variable name not singleton!"); |
| 612 | return; |
| 613 | } |
| 614 | s=runq->argv->words->word; |
| 615 | deglob(s); |
| 616 | n=0; |
| 617 | for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; |
| 618 | if(n==0 || *t){ |
| 619 | a=vlook(s)->val; |
| 620 | itoa(num, count(a)); |
| 621 | } |
| 622 | else{ |
| 623 | a=vlook("*")->val; |
| 624 | itoa(num, a && 1<=n && n<=count(a)?1:0); |
| 625 | } |
| 626 | poplist(); |
| 627 | pushword(num); |
| 628 | } |
| 629 | void Xlocal(void){ |
| 630 | if(count(runq->argv->words)!=1){ |
| 631 | Xerror1("variable name must be singleton\n"); |
| 632 | return; |
| 633 | } |
| 634 | deglob(runq->argv->words->word); |
| 635 | runq->local=newvar(strdup(runq->argv->words->word), runq->local); |
| 636 | runq->local->val=copywords(runq->argv->next->words, (word *)0); |
| 637 | runq->local->changed=1; |
| 638 | poplist(); |
| 639 | poplist(); |
| 640 | } |
| 641 | void Xunlocal(void){ |
| 642 | var *v=runq->local, *hid; |
| 643 | if(v==0) panic("Xunlocal: no locals!", 0); |
| 644 | runq->local=v->next; |
| 645 | hid=vlook(v->name); |
| 646 | hid->changed=1; |
| 647 | efree(v->name); |
| 648 | freewords(v->val); |
| 649 | efree((char *)v); |
| 650 | } |
| 651 | void freewords(word *w) |
| 652 | { |
| 653 | word *nw; |
| 654 | while(w){ |
| 655 | efree(w->word); |
| 656 | nw=w->next; |
| 657 | efree((char *)w); |
| 658 | w=nw; |
| 659 | } |
| 660 | } |
| 661 | void Xfn(void){ |
| 662 | var *v; |
| 663 | word *a; |
| 664 | int end; |
| 665 | end=runq->code[runq->pc].i; |
| 666 | for(a=runq->argv->words;a;a=a->next){ |
| 667 | v=gvlook(a->word); |
| 668 | if(v->fn) codefree(v->fn); |
| 669 | v->fn=codecopy(runq->code); |
| 670 | v->pc=runq->pc+2; |
| 671 | v->fnchanged=1; |
| 672 | } |
| 673 | runq->pc=end; |
| 674 | poplist(); |
| 675 | } |
| 676 | void Xdelfn(void){ |
| 677 | var *v; |
| 678 | word *a; |
| 679 | for(a=runq->argv->words;a;a=a->next){ |
| 680 | v=gvlook(a->word); |
| 681 | if(v->fn) codefree(v->fn); |
| 682 | v->fn=0; |
| 683 | v->fnchanged=1; |
| 684 | } |
| 685 | poplist(); |
| 686 | } |
| 687 | void Xpipe(void){ |
| 688 | struct thread *p=runq; |
| 689 | int pc=p->pc, forkid; |
| 690 | int lfd=p->code[pc++].i; |
| 691 | int rfd=p->code[pc++].i; |
| 692 | int pfd[2]; |
| 693 | if(pipe(pfd)<0){ |
| 694 | Xerror("can't get pipe"); |
| 695 | return; |
| 696 | } |
| 697 | switch(forkid=fork()){ |
| 698 | case -1: |
| 699 | Xerror("try again"); |
| 700 | break; |
| 701 | case 0: |
| 702 | start(p->code, pc+2, runq->local); |
| 703 | runq->ret=0; |
| 704 | close(pfd[PRD]); |
| 705 | pushredir(ROPEN, pfd[PWR], lfd); |
| 706 | break; |
| 707 | default: |
| 708 | start(p->code, p->code[pc].i, runq->local); |
| 709 | close(pfd[PWR]); |
| 710 | pushredir(ROPEN, pfd[PRD], rfd); |
| 711 | p->pc=p->code[pc+1].i; |
| 712 | p->pid=forkid; |
| 713 | break; |
| 714 | } |
| 715 | } |
| 716 | char *concstatus(char *s, char *t) |
| 717 | { |
| 718 | static char v[NSTATUS+1]; |
| 719 | int n=strlen(s); |
| 720 | strncpy(v, s, NSTATUS); |
| 721 | if(n<NSTATUS){ |
| 722 | v[n]='|'; |
| 723 | strncpy(v+n+1, t, NSTATUS-n-1); |
| 724 | } |
| 725 | v[NSTATUS]='\0'; |
| 726 | return v; |
| 727 | } |
| 728 | void Xpipewait(void){ |
| 729 | char status[NSTATUS+1]; |
| 730 | if(runq->pid==-1) |
| 731 | setstatus(concstatus(runq->status, getstatus())); |
| 732 | else{ |
| 733 | strncpy(status, getstatus(), NSTATUS); |
| 734 | status[NSTATUS]='\0'; |
| 735 | Waitfor(runq->pid, 1); |
| 736 | runq->pid=-1; |
| 737 | setstatus(concstatus(getstatus(), status)); |
| 738 | } |
| 739 | } |
| 740 | void Xrdcmds(void){ |
| 741 | struct thread *p=runq; |
| 742 | word *prompt; |
| 743 | flush(err); |
| 744 | nerror=0; |
| 745 | if(flag['s'] && !truestatus()) |
| 746 | pfmt(err, "status=%v\n", vlook("status")->val); |
| 747 | if(runq->iflag){ |
| 748 | prompt=vlook("prompt")->val; |
| 749 | if(prompt) |
| 750 | promptstr=prompt->word; |
| 751 | else |
| 752 | promptstr="% "; |
| 753 | } |
| 754 | Noerror(); |
| 755 | if(yyparse()){ |
| 756 | if(!p->iflag || p->eof && !Eintr()){ |
| 757 | if(p->cmdfile) efree(p->cmdfile); |
| 758 | closeio(p->cmdfd); |
| 759 | Xreturn(); /* should this be omitted? */ |
| 760 | } |
| 761 | else{ |
| 762 | if(Eintr()){ |
| 763 | pchr(err, '\n'); |
| 764 | p->eof=0; |
| 765 | } |
| 766 | --p->pc; /* go back for next command */ |
| 767 | } |
| 768 | } |
| 769 | else{ |
| 770 | ntrap = 0; /* avoid double-interrupts during blocked writes */ |
| 771 | --p->pc; /* re-execute Xrdcmds after codebuf runs */ |
| 772 | start(codebuf, 1, runq->local); |
| 773 | } |
| 774 | freenodes(); |
| 775 | } |
| 776 | void Xerror(char *s) |
| 777 | { |
| 778 | if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) |
| 779 | pfmt(err, "rc: %s: %r\n", s); |
| 780 | else |
| 781 | pfmt(err, "rc (%s): %s: %r\n", argv0, s); |
| 782 | flush(err); |
| 783 | while(!runq->iflag) Xreturn(); |
| 784 | } |
| 785 | void Xerror1(char *s) |
| 786 | { |
| 787 | if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) |
| 788 | pfmt(err, "rc: %s\n", s); |
| 789 | else |
| 790 | pfmt(err, "rc (%s): %s\n", argv0, s); |
| 791 | flush(err); |
| 792 | while(!runq->iflag) Xreturn(); |
| 793 | } |
| 794 | void Xbackq(void){ |
| 795 | char wd[8193]; |
| 796 | int c; |
| 797 | char *s, *ewd=&wd[8192], *stop; |
| 798 | struct io *f; |
| 799 | var *ifs=vlook("ifs"); |
| 800 | word *v, *nextv; |
| 801 | int pfd[2]; |
| 802 | int pid; |
| 803 | stop=ifs->val?ifs->val->word:""; |
| 804 | if(pipe(pfd)<0){ |
| 805 | Xerror("can't make pipe"); |
| 806 | return; |
| 807 | } |
| 808 | switch(pid=fork()){ |
| 809 | case -1: Xerror("try again"); |
| 810 | close(pfd[PRD]); |
| 811 | close(pfd[PWR]); |
| 812 | return; |
| 813 | case 0: |
| 814 | close(pfd[PRD]); |
| 815 | start(runq->code, runq->pc+1, runq->local); |
| 816 | pushredir(ROPEN, pfd[PWR], 1); |
| 817 | return; |
| 818 | default: |
| 819 | close(pfd[PWR]); |
| 820 | f=openfd(pfd[PRD]); |
| 821 | s=wd; |
| 822 | v=0; |
| 823 | while((c=rchr(f))!=EOF){ |
| 824 | if(strchr(stop, c) || s==ewd){ |
| 825 | if(s!=wd){ |
| 826 | *s='\0'; |
| 827 | v=newword(wd, v); |
| 828 | s=wd; |
| 829 | } |
| 830 | } |
| 831 | else *s++=c; |
| 832 | } |
| 833 | if(s!=wd){ |
| 834 | *s='\0'; |
| 835 | v=newword(wd, v); |
| 836 | } |
| 837 | closeio(f); |
| 838 | Waitfor(pid, 0); |
| 839 | /* v points to reversed arglist -- reverse it onto argv */ |
| 840 | while(v){ |
| 841 | nextv=v->next; |
| 842 | v->next=runq->argv->words; |
| 843 | runq->argv->words=v; |
| 844 | v=nextv; |
| 845 | } |
| 846 | runq->pc=runq->code[runq->pc].i; |
| 847 | return; |
| 848 | } |
| 849 | } |
| 850 | /* |
| 851 | * Who should wait for the exit from the fork? |
| 852 | */ |
| 853 | void Xpipefd(void){ |
| 854 | struct thread *p=runq; |
| 855 | int pc=p->pc; |
| 856 | char name[40]; |
| 857 | int pfd[2]; |
| 858 | int sidefd, mainfd; |
| 859 | if(pipe(pfd)<0){ |
| 860 | Xerror("can't get pipe"); |
| 861 | return; |
| 862 | } |
| 863 | if(p->code[pc].i==READ){ |
| 864 | sidefd=pfd[PWR]; |
| 865 | mainfd=pfd[PRD]; |
| 866 | } |
| 867 | else{ |
| 868 | sidefd=pfd[PRD]; |
| 869 | mainfd=pfd[PWR]; |
| 870 | } |
| 871 | switch(fork()){ |
| 872 | case -1: |
| 873 | Xerror("try again"); |
| 874 | break; |
| 875 | case 0: |
| 876 | start(p->code, pc+2, runq->local); |
| 877 | close(mainfd); |
| 878 | pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); |
| 879 | runq->ret=0; |
| 880 | break; |
| 881 | default: |
| 882 | close(sidefd); |
| 883 | pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ |
| 884 | strcpy(name, Fdprefix); |
| 885 | itoa(name+strlen(name), mainfd); |
| 886 | pushword(name); |
| 887 | p->pc=p->code[pc+1].i; |
| 888 | break; |
| 889 | } |
| 890 | } |
| 891 | void Xsubshell(void){ |
| 892 | int pid; |
| 893 | switch(pid=fork()){ |
| 894 | case -1: |
| 895 | Xerror("try again"); |
| 896 | break; |
| 897 | case 0: |
| 898 | start(runq->code, runq->pc+1, runq->local); |
| 899 | runq->ret=0; |
| 900 | break; |
| 901 | default: |
| 902 | Waitfor(pid, 1); |
| 903 | runq->pc=runq->code[runq->pc].i; |
| 904 | break; |
| 905 | } |
| 906 | } |
| 907 | void setstatus(char *s) |
| 908 | { |
| 909 | setvar("status", newword(s, (word *)0)); |
| 910 | } |
| 911 | char *getstatus(void){ |
| 912 | var *status=vlook("status"); |
| 913 | return status->val?status->val->word:""; |
| 914 | } |
| 915 | int truestatus(void){ |
| 916 | char *s; |
| 917 | for(s=getstatus();*s;s++) |
| 918 | if(*s!='|' && *s!='0') return 0; |
| 919 | return 1; |
| 920 | } |
| 921 | void Xdelhere(void){ |
| 922 | Unlink(runq->code[runq->pc++].s); |
| 923 | } |
| 924 | void Xfor(void){ |
| 925 | if(runq->argv->words==0){ |
| 926 | poplist(); |
| 927 | runq->pc=runq->code[runq->pc].i; |
| 928 | } |
| 929 | else{ |
| 930 | freelist(runq->local->val); |
| 931 | runq->local->val=runq->argv->words; |
| 932 | runq->local->changed=1; |
| 933 | runq->argv->words=runq->argv->words->next; |
| 934 | runq->local->val->next=0; |
| 935 | runq->pc++; |
| 936 | } |
| 937 | } |
| 938 | void Xglob(void){ |
| 939 | globlist(); |
| 940 | } |