9base

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

cmd.c (10833B)


      1 #include "sam.h"
      2 #include "parse.h"
      3 
      4 static char	linex[]="\n";
      5 static char	wordx[]=" \t\n";
      6 struct cmdtab cmdtab[]={
      7 /*	cmdc	text	regexp	addr	defcmd	defaddr	count	token	 fn	*/
      8 	'\n',	0,	0,	0,	0,	aDot,	0,	0,	nl_cmd,
      9 	'a',	1,	0,	0,	0,	aDot,	0,	0,	a_cmd,
     10 	'b',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
     11 	'B',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
     12 	'c',	1,	0,	0,	0,	aDot,	0,	0,	c_cmd,
     13 	'd',	0,	0,	0,	0,	aDot,	0,	0,	d_cmd,
     14 	'D',	0,	0,	0,	0,	aNo,	0,	linex,	D_cmd,
     15 	'e',	0,	0,	0,	0,	aNo,	0,	wordx,	e_cmd,
     16 	'f',	0,	0,	0,	0,	aNo,	0,	wordx,	f_cmd,
     17 	'g',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
     18 	'i',	1,	0,	0,	0,	aDot,	0,	0,	i_cmd,
     19 	'k',	0,	0,	0,	0,	aDot,	0,	0,	k_cmd,
     20 	'm',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
     21 	'n',	0,	0,	0,	0,	aNo,	0,	0,	n_cmd,
     22 	'p',	0,	0,	0,	0,	aDot,	0,	0,	p_cmd,
     23 	'q',	0,	0,	0,	0,	aNo,	0,	0,	q_cmd,
     24 	'r',	0,	0,	0,	0,	aDot,	0,	wordx,	e_cmd,
     25 	's',	0,	1,	0,	0,	aDot,	1,	0,	s_cmd,
     26 	't',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
     27 	'u',	0,	0,	0,	0,	aNo,	2,	0,	u_cmd,
     28 	'v',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
     29 	'w',	0,	0,	0,	0,	aAll,	0,	wordx,	w_cmd,
     30 	'x',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
     31 	'y',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
     32 	'X',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
     33 	'Y',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
     34 	'!',	0,	0,	0,	0,	aNo,	0,	linex,	plan9_cmd,
     35 	'>',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
     36 	'<',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
     37 	'|',	0,	0,	0,	0,	aDot,	0,	linex,	plan9_cmd,
     38 	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
     39 	'c'|0x100,0,	0,	0,	0,	aNo,	0,	wordx,	cd_cmd,
     40 	0,	0,	0,	0,	0,	0,	0,	0
     41 };
     42 Cmd	*parsecmd(int);
     43 Addr	*compoundaddr(void);
     44 Addr	*simpleaddr(void);
     45 void	freecmd(void);
     46 void	okdelim(int);
     47 
     48 Rune	line[BLOCKSIZE];
     49 Rune	termline[BLOCKSIZE];
     50 Rune	*linep = line;
     51 Rune	*terminp = termline;
     52 Rune	*termoutp = termline;
     53 
     54 List	cmdlist = { 'p' };
     55 List	addrlist = { 'p' };
     56 List	relist = { 'p' };
     57 List	stringlist = { 'p' };
     58 
     59 int	eof;
     60 
     61 void
     62 resetcmd(void)
     63 {
     64 	linep = line;
     65 	*linep = 0;
     66 	terminp = termoutp = termline;
     67 	freecmd();
     68 }
     69 
     70 int
     71 inputc(void)
     72 {
     73 	int n, nbuf;
     74 	char buf[UTFmax];
     75 	Rune r;
     76 
     77     Again:
     78 	nbuf = 0;
     79 	if(downloaded){
     80 		while(termoutp == terminp){
     81 			cmdupdate();
     82 			if(patset)
     83 				tellpat();
     84 			while(termlocked > 0){
     85 				outT0(Hunlock);
     86 				termlocked--;
     87 			}
     88 			if(rcv() == 0)
     89 				return -1;
     90 		}
     91 		r = *termoutp++;
     92 		if(termoutp == terminp)
     93 			terminp = termoutp = termline;
     94 	}else{
     95    		do{
     96 			n = read(0, buf+nbuf, 1);
     97 			if(n <= 0)
     98 				return -1;
     99 			nbuf += n;
    100 		}while(!fullrune(buf, nbuf));
    101 		chartorune(&r, buf);
    102 	}
    103 	if(r == 0){
    104 		warn(Wnulls);
    105 		goto Again;
    106 	}
    107 	return r;
    108 }
    109 
    110 int
    111 inputline(void)
    112 {
    113 	int i, c, start;
    114 
    115 	/*
    116 	 * Could set linep = line and i = 0 here and just
    117 	 * error(Etoolong) below, but this way we keep
    118 	 * old input buffer history around for a while.
    119 	 * This is useful only for debugging.
    120 	 */
    121 	i = linep - line;
    122 	do{
    123 		if((c = inputc())<=0)
    124 			return -1;
    125 		if(i == nelem(line)-1){
    126 			if(linep == line)
    127 				error(Etoolong);
    128 			start = linep - line;
    129 			runemove(line, linep, i-start);
    130 			i -= start;
    131 			linep = line;
    132 		}
    133 	}while((line[i++]=c) != '\n');
    134 	line[i] = 0;
    135 	return 1;
    136 }
    137 
    138 int
    139 getch(void)
    140 {
    141 	if(eof)
    142 		return -1;
    143 	if(*linep==0 && inputline()<0){
    144 		eof = TRUE;
    145 		return -1;
    146 	}
    147 	return *linep++;
    148 }
    149 
    150 int
    151 nextc(void)
    152 {
    153 	if(*linep == 0)
    154 		return -1;
    155 	return *linep;
    156 }
    157 
    158 void
    159 ungetch(void)
    160 {
    161 	if(--linep < line)
    162 		panic("ungetch");
    163 }
    164 
    165 Posn
    166 getnum(int signok)
    167 {
    168 	Posn n=0;
    169 	int c, sign;
    170 
    171 	sign = 1;
    172 	if(signok>1 && nextc()=='-'){
    173 		sign = -1;
    174 		getch();
    175 	}
    176 	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
    177 		return sign;
    178 	while('0'<=(c=getch()) && c<='9')
    179 		n = n*10 + (c-'0');
    180 	ungetch();
    181 	return sign*n;
    182 }
    183 
    184 int
    185 skipbl(void)
    186 {
    187 	int c;
    188 	do
    189 		c = getch();
    190 	while(c==' ' || c=='\t');
    191 	if(c >= 0)
    192 		ungetch();
    193 	return c;
    194 }
    195 
    196 void
    197 termcommand(void)
    198 {
    199 	Posn p;
    200 
    201 	for(p=cmdpt; p<cmd->b.nc; p++){
    202 		if(terminp >= &termline[BLOCKSIZE]){
    203 			cmdpt = cmd->b.nc;
    204 			error(Etoolong);
    205 		}
    206 		*terminp++ = filereadc(cmd, p);
    207 	}
    208 	cmdpt = cmd->b.nc;
    209 }
    210 
    211 void
    212 cmdloop(void)
    213 {
    214 	Cmd *cmdp;
    215 	File *ocurfile;
    216 	int loaded;
    217 
    218 	for(;;){
    219 		if(!downloaded && curfile && curfile->unread)
    220 			load(curfile);
    221 		if((cmdp = parsecmd(0))==0){
    222 			if(downloaded){
    223 				rescue();
    224 				exits("eof");
    225 			}
    226 			break;
    227 		}
    228 		ocurfile = curfile;
    229 		loaded = curfile && !curfile->unread;
    230 		if(cmdexec(curfile, cmdp) == 0)
    231 			break;
    232 		freecmd();
    233 		cmdupdate();
    234 		update();
    235 		if(downloaded && curfile &&
    236 		    (ocurfile!=curfile || (!loaded && !curfile->unread)))
    237 			outTs(Hcurrent, curfile->tag);
    238 			/* don't allow type ahead on files that aren't bound */
    239 		if(downloaded && curfile && curfile->rasp == 0)
    240 			terminp = termoutp;
    241 	}
    242 }
    243 
    244 Cmd *
    245 newcmd(void){
    246 	Cmd *p;
    247 
    248 	p = emalloc(sizeof(Cmd));
    249 	inslist(&cmdlist, cmdlist.nused, (long)p);
    250 	return p;
    251 }
    252 
    253 Addr*
    254 newaddr(void)
    255 {
    256 	Addr *p;
    257 
    258 	p = emalloc(sizeof(Addr));
    259 	inslist(&addrlist, addrlist.nused, (long)p);
    260 	return p;
    261 }
    262 
    263 String*
    264 newre(void)
    265 {
    266 	String *p;
    267 
    268 	p = emalloc(sizeof(String));
    269 	inslist(&relist, relist.nused, (long)p);
    270 	Strinit(p);
    271 	return p;
    272 }
    273 
    274 String*
    275 newstring(void)
    276 {
    277 	String *p;
    278 
    279 	p = emalloc(sizeof(String));
    280 	inslist(&stringlist, stringlist.nused, (long)p);
    281 	Strinit(p);
    282 	return p;
    283 }
    284 
    285 void
    286 freecmd(void)
    287 {
    288 	int i;
    289 
    290 	while(cmdlist.nused > 0)
    291 		free(cmdlist.voidpptr[--cmdlist.nused]);
    292 	while(addrlist.nused > 0)
    293 		free(addrlist.voidpptr[--addrlist.nused]);
    294 	while(relist.nused > 0){
    295 		i = --relist.nused;
    296 		Strclose(relist.stringpptr[i]);
    297 		free(relist.stringpptr[i]);
    298 	}
    299 	while(stringlist.nused>0){
    300 		i = --stringlist.nused;
    301 		Strclose(stringlist.stringpptr[i]);
    302 		free(stringlist.stringpptr[i]);
    303 	}
    304 }
    305 
    306 int
    307 lookup(int c)
    308 {
    309 	int i;
    310 
    311 	for(i=0; cmdtab[i].cmdc; i++)
    312 		if(cmdtab[i].cmdc == c)
    313 			return i;
    314 	return -1;
    315 }
    316 
    317 void
    318 okdelim(int c)
    319 {
    320 	if(c=='\\' || ('a'<=c && c<='z')
    321 	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
    322 		error_c(Edelim, c);
    323 }
    324 
    325 void
    326 atnl(void)
    327 {
    328 	skipbl();
    329 	if(getch() != '\n')
    330 		error(Enewline);
    331 }
    332 
    333 void
    334 getrhs(String *s, int delim, int cmd)
    335 {
    336 	int c;
    337 
    338 	while((c = getch())>0 && c!=delim && c!='\n'){
    339 		if(c == '\\'){
    340 			if((c=getch()) <= 0)
    341 				error(Ebadrhs);
    342 			if(c == '\n'){
    343 				ungetch();
    344 				c='\\';
    345 			}else if(c == 'n')
    346 				c='\n';
    347 			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
    348 				Straddc(s, '\\');
    349 		}
    350 		Straddc(s, c);
    351 	}
    352 	ungetch();	/* let client read whether delimeter, '\n' or whatever */
    353 }
    354 
    355 String *
    356 collecttoken(char *end)
    357 {
    358 	String *s = newstring();
    359 	int c;
    360 
    361 	while((c=nextc())==' ' || c=='\t')
    362 		Straddc(s, getch()); /* blanks significant for getname() */
    363 	while((c=getch())>0 && utfrune(end, c)==0)
    364 		Straddc(s, c);
    365 	Straddc(s, 0);
    366 	if(c != '\n')
    367 		atnl();
    368 	return s;
    369 }
    370 
    371 String *
    372 collecttext(void)
    373 {
    374 	String *s = newstring();
    375 	int begline, i, c, delim;
    376 
    377 	if(skipbl()=='\n'){
    378 		getch();
    379 		i = 0;
    380 		do{
    381 			begline = i;
    382 			while((c = getch())>0 && c!='\n')
    383 				i++, Straddc(s, c);
    384 			i++, Straddc(s, '\n');
    385 			if(c < 0)
    386 				goto Return;
    387 		}while(s->s[begline]!='.' || s->s[begline+1]!='\n');
    388 		Strdelete(s, s->n-2, s->n);
    389 	}else{
    390 		okdelim(delim = getch());
    391 		getrhs(s, delim, 'a');
    392 		if(nextc()==delim)
    393 			getch();
    394 		atnl();
    395 	}
    396     Return:
    397 	Straddc(s, 0);		/* JUST FOR CMDPRINT() */
    398 	return s;
    399 }
    400 
    401 Cmd *
    402 parsecmd(int nest)
    403 {
    404 	int i, c;
    405 	struct cmdtab *ct;
    406 	Cmd *cp, *ncp;
    407 	Cmd cmd;
    408 
    409 	cmd.next = cmd.ccmd = 0;
    410 	cmd.re = 0;
    411 	cmd.flag = cmd.num = 0;
    412 	cmd.addr = compoundaddr();
    413 	if(skipbl() == -1)
    414 		return 0;
    415 	if((c=getch())==-1)
    416 		return 0;
    417 	cmd.cmdc = c;
    418 	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
    419 		getch();		/* the 'd' */
    420 		cmd.cmdc='c'|0x100;
    421 	}
    422 	i = lookup(cmd.cmdc);
    423 	if(i >= 0){
    424 		if(cmd.cmdc == '\n')
    425 			goto Return;	/* let nl_cmd work it all out */
    426 		ct = &cmdtab[i];
    427 		if(ct->defaddr==aNo && cmd.addr)
    428 			error(Enoaddr);
    429 		if(ct->count)
    430 			cmd.num = getnum(ct->count);
    431 		if(ct->regexp){
    432 			/* x without pattern -> .*\n, indicated by cmd.re==0 */
    433 			/* X without pattern is all files */
    434 			if((ct->cmdc!='x' && ct->cmdc!='X') ||
    435 			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
    436 				skipbl();
    437 				if((c = getch())=='\n' || c<0)
    438 					error(Enopattern);
    439 				okdelim(c);
    440 				cmd.re = getregexp(c);
    441 				if(ct->cmdc == 's'){
    442 					cmd.ctext = newstring();
    443 					getrhs(cmd.ctext, c, 's');
    444 					if(nextc() == c){
    445 						getch();
    446 						if(nextc() == 'g')
    447 							cmd.flag = getch();
    448 					}
    449 			
    450 				}
    451 			}
    452 		}
    453 		if(ct->addr && (cmd.caddr=simpleaddr())==0)
    454 			error(Eaddress);
    455 		if(ct->defcmd){
    456 			if(skipbl() == '\n'){
    457 				getch();
    458 				cmd.ccmd = newcmd();
    459 				cmd.ccmd->cmdc = ct->defcmd;
    460 			}else if((cmd.ccmd = parsecmd(nest))==0)
    461 				panic("defcmd");
    462 		}else if(ct->text)
    463 			cmd.ctext = collecttext();
    464 		else if(ct->token)
    465 			cmd.ctext = collecttoken(ct->token);
    466 		else
    467 			atnl();
    468 	}else
    469 		switch(cmd.cmdc){
    470 		case '{':
    471 			cp = 0;
    472 			do{
    473 				if(skipbl()=='\n')
    474 					getch();
    475 				ncp = parsecmd(nest+1);
    476 				if(cp)
    477 					cp->next = ncp;
    478 				else
    479 					cmd.ccmd = ncp;
    480 			}while(cp = ncp);
    481 			break;
    482 		case '}':
    483 			atnl();
    484 			if(nest==0)
    485 				error(Enolbrace);
    486 			return 0;
    487 		default:
    488 			error_c(Eunk, cmd.cmdc);
    489 		}
    490     Return:
    491 	cp = newcmd();
    492 	*cp = cmd;
    493 	return cp;
    494 }
    495 
    496 String*				/* BUGGERED */
    497 getregexp(int delim)
    498 {
    499 	String *r = newre();
    500 	int c;
    501 
    502 	for(Strzero(&genstr); ; Straddc(&genstr, c))
    503 		if((c = getch())=='\\'){
    504 			if(nextc()==delim)
    505 				c = getch();
    506 			else if(nextc()=='\\'){
    507 				Straddc(&genstr, c);
    508 				c = getch();
    509 			}
    510 		}else if(c==delim || c=='\n')
    511 			break;
    512 	if(c!=delim && c)
    513 		ungetch();
    514 	if(genstr.n > 0){
    515 		patset = TRUE;
    516 		Strduplstr(&lastpat, &genstr);
    517 		Straddc(&lastpat, '\0');
    518 	}
    519 	if(lastpat.n <= 1)
    520 		error(Epattern);
    521 	Strduplstr(r, &lastpat);
    522 	return r;
    523 }
    524 
    525 Addr *
    526 simpleaddr(void)
    527 {
    528 	Addr addr;
    529 	Addr *ap, *nap;
    530 
    531 	addr.next = 0;
    532 	addr.left = 0;
    533 	addr.num = 0;
    534 	switch(skipbl()){
    535 	case '#':
    536 		addr.type = getch();
    537 		addr.num = getnum(1);
    538 		break;
    539 	case '0': case '1': case '2': case '3': case '4':
    540 	case '5': case '6': case '7': case '8': case '9': 
    541 		addr.num = getnum(1);
    542 		addr.type='l';
    543 		break;
    544 	case '/': case '?': case '"':
    545 		addr.are = getregexp(addr.type = getch());
    546 		break;
    547 	case '.':
    548 	case '$':
    549 	case '+':
    550 	case '-':
    551 	case '\'':
    552 		addr.type = getch();
    553 		break;
    554 	default:
    555 		return 0;
    556 	}
    557 	if(addr.next = simpleaddr())
    558 		switch(addr.next->type){
    559 		case '.':
    560 		case '$':
    561 		case '\'':
    562 			if(addr.type!='"')
    563 		case '"':
    564 				error(Eaddress);
    565 			break;
    566 		case 'l':
    567 		case '#':
    568 			if(addr.type=='"')
    569 				break;
    570 			/* fall through */
    571 		case '/':
    572 		case '?':
    573 			if(addr.type!='+' && addr.type!='-'){
    574 				/* insert the missing '+' */
    575 				nap = newaddr();
    576 				nap->type='+';
    577 				nap->next = addr.next;
    578 				addr.next = nap;
    579 			}
    580 			break;
    581 		case '+':
    582 		case '-':
    583 			break;
    584 		default:
    585 			panic("simpleaddr");
    586 		}
    587 	ap = newaddr();
    588 	*ap = addr;
    589 	return ap;
    590 }
    591 
    592 Addr *
    593 compoundaddr(void)
    594 {
    595 	Addr addr;
    596 	Addr *ap, *next;
    597 
    598 	addr.left = simpleaddr();
    599 	if((addr.type = skipbl())!=',' && addr.type!=';')
    600 		return addr.left;
    601 	getch();
    602 	next = addr.next = compoundaddr();
    603 	if(next && (next->type==',' || next->type==';') && next->left==0)
    604 		error(Eaddress);
    605 	ap = newaddr();
    606 	*ap = addr;
    607 	return ap;
    608 }