9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

simple.c (9019B)


      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 }