xref: /plan9/sys/src/cmd/rc/lex.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
13e12c5d1SDavid du Colombier #include "rc.h"
23e12c5d1SDavid du Colombier #include "exec.h"
33e12c5d1SDavid du Colombier #include "io.h"
43e12c5d1SDavid du Colombier #include "getflags.h"
53e12c5d1SDavid du Colombier #include "fns.h"
63e12c5d1SDavid du Colombier int getnext(void);
73e12c5d1SDavid du Colombier int wordchr(int c)
83e12c5d1SDavid du Colombier {
93e12c5d1SDavid du Colombier 	return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF;
103e12c5d1SDavid du Colombier }
113e12c5d1SDavid du Colombier int idchr(int c)
123e12c5d1SDavid du Colombier {
133e12c5d1SDavid du Colombier 	/*
143e12c5d1SDavid du Colombier 	 * Formerly:
153e12c5d1SDavid du Colombier 	 * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
163e12c5d1SDavid du Colombier 	 *	|| c=='_' || c=='*';
173e12c5d1SDavid du Colombier 	 */
183e12c5d1SDavid du Colombier 	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
193e12c5d1SDavid du Colombier }
203e12c5d1SDavid du Colombier int future=EOF;
213e12c5d1SDavid du Colombier int doprompt=1;
223e12c5d1SDavid du Colombier int inquote;
233e12c5d1SDavid du Colombier /*
243e12c5d1SDavid du Colombier  * Look ahead in the input stream
253e12c5d1SDavid du Colombier  */
263e12c5d1SDavid du Colombier int nextc(void){
273e12c5d1SDavid du Colombier 	if(future==EOF) future=getnext();
283e12c5d1SDavid du Colombier 	return future;
293e12c5d1SDavid du Colombier }
303e12c5d1SDavid du Colombier /*
313e12c5d1SDavid du Colombier  * Consume the lookahead character.
323e12c5d1SDavid du Colombier  */
333e12c5d1SDavid du Colombier int advance(void){
343e12c5d1SDavid du Colombier 	int c=nextc();
353e12c5d1SDavid du Colombier 	lastc=future;
363e12c5d1SDavid du Colombier 	future=EOF;
373e12c5d1SDavid du Colombier 	return c;
383e12c5d1SDavid du Colombier }
393e12c5d1SDavid du Colombier /*
403e12c5d1SDavid du Colombier  * read a character from the input stream
413e12c5d1SDavid du Colombier  */
423e12c5d1SDavid du Colombier int getnext(void){
433e12c5d1SDavid du Colombier 	register int c;
443e12c5d1SDavid du Colombier 	static peekc=EOF;
453e12c5d1SDavid du Colombier 	if(peekc!=EOF){
463e12c5d1SDavid du Colombier 		c=peekc;
473e12c5d1SDavid du Colombier 		peekc=EOF;
483e12c5d1SDavid du Colombier 		return c;
493e12c5d1SDavid du Colombier 	}
503e12c5d1SDavid du Colombier 	if(runq->eof) return EOF;
513e12c5d1SDavid du Colombier 	if(doprompt) pprompt();
523e12c5d1SDavid du Colombier 	c=rchr(runq->cmdfd);
533e12c5d1SDavid du Colombier 	if(!inquote && c=='\\'){
543e12c5d1SDavid du Colombier 		c=rchr(runq->cmdfd);
553e12c5d1SDavid du Colombier 		if(c=='\n'){
563e12c5d1SDavid du Colombier 			doprompt=1;
573e12c5d1SDavid du Colombier 			c=' ';
583e12c5d1SDavid du Colombier 		}
593e12c5d1SDavid du Colombier 		else{
603e12c5d1SDavid du Colombier 			peekc=c;
613e12c5d1SDavid du Colombier 			c='\\';
623e12c5d1SDavid du Colombier 		}
633e12c5d1SDavid du Colombier 	}
643e12c5d1SDavid du Colombier 	doprompt=doprompt || c=='\n' || c==EOF;
653e12c5d1SDavid du Colombier 	if(c==EOF) runq->eof++;
663e12c5d1SDavid du Colombier 	else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c);
673e12c5d1SDavid du Colombier 	return c;
683e12c5d1SDavid du Colombier }
693e12c5d1SDavid du Colombier void pprompt(void){
703e12c5d1SDavid du Colombier 	var *prompt;
713e12c5d1SDavid du Colombier 	if(runq->iflag){
723e12c5d1SDavid du Colombier 		pstr(err, promptstr);
733e12c5d1SDavid du Colombier 		flush(err);
743e12c5d1SDavid du Colombier 		prompt=vlook("prompt");
753e12c5d1SDavid du Colombier 		if(prompt->val && prompt->val->next)
763e12c5d1SDavid du Colombier 			promptstr=prompt->val->next->word;
773e12c5d1SDavid du Colombier 		else
783e12c5d1SDavid du Colombier 			promptstr="\t";
793e12c5d1SDavid du Colombier 	}
803e12c5d1SDavid du Colombier 	runq->lineno++;
813e12c5d1SDavid du Colombier 	doprompt=0;
823e12c5d1SDavid du Colombier }
833e12c5d1SDavid du Colombier void skipwhite(void){
843e12c5d1SDavid du Colombier 	int c;
853e12c5d1SDavid du Colombier 	for(;;){
863e12c5d1SDavid du Colombier 		c=nextc();
873e12c5d1SDavid du Colombier 		if(c=='#'){	/* Why did this used to be  if(!inquote && c=='#') ?? */
883e12c5d1SDavid du Colombier 			for(;;){
893e12c5d1SDavid du Colombier 				c=nextc();
903e12c5d1SDavid du Colombier 				if(c=='\n' || c==EOF) break;
913e12c5d1SDavid du Colombier 				advance();
923e12c5d1SDavid du Colombier 			}
933e12c5d1SDavid du Colombier 		}
943e12c5d1SDavid du Colombier 		if(c==' ' || c=='\t') advance();
953e12c5d1SDavid du Colombier 		else return;
963e12c5d1SDavid du Colombier 	}
973e12c5d1SDavid du Colombier }
983e12c5d1SDavid du Colombier void skipnl(void){
993e12c5d1SDavid du Colombier 	register int c;
1003e12c5d1SDavid du Colombier 	for(;;){
1013e12c5d1SDavid du Colombier 		skipwhite();
1023e12c5d1SDavid du Colombier 		c=nextc();
1033e12c5d1SDavid du Colombier 		if(c!='\n') return;
1043e12c5d1SDavid du Colombier 		advance();
1053e12c5d1SDavid du Colombier 	}
1063e12c5d1SDavid du Colombier }
1073e12c5d1SDavid du Colombier int nextis(int c){
1083e12c5d1SDavid du Colombier 	if(nextc()==c){
1093e12c5d1SDavid du Colombier 		advance();
1103e12c5d1SDavid du Colombier 		return 1;
1113e12c5d1SDavid du Colombier 	}
1123e12c5d1SDavid du Colombier 	return 0;
1133e12c5d1SDavid du Colombier }
1143e12c5d1SDavid du Colombier char *addtok(char *p, int val){
1153e12c5d1SDavid du Colombier 	if(p==0) return 0;
1163e12c5d1SDavid du Colombier 	if(p==&tok[NTOK]){
1173e12c5d1SDavid du Colombier 		*p=0;
1183e12c5d1SDavid du Colombier 		yyerror("token buffer too short");
1193e12c5d1SDavid du Colombier 		return 0;
1203e12c5d1SDavid du Colombier 	}
1213e12c5d1SDavid du Colombier 	*p++=val;
1223e12c5d1SDavid du Colombier 	return p;
1233e12c5d1SDavid du Colombier }
1243e12c5d1SDavid du Colombier char *addutf(char *p, int c){
1253e12c5d1SDavid du Colombier 	p=addtok(p, c);
1263e12c5d1SDavid du Colombier 	if(twobyte(c))	 /* 2-byte escape */
1273e12c5d1SDavid du Colombier 		return addtok(p, advance());
1283e12c5d1SDavid du Colombier 	if(threebyte(c)){	/* 3-byte escape */
1293e12c5d1SDavid du Colombier 		p=addtok(p, advance());
1303e12c5d1SDavid du Colombier 		return addtok(p, advance());
1313e12c5d1SDavid du Colombier 	}
1323e12c5d1SDavid du Colombier 	return p;
1333e12c5d1SDavid du Colombier }
1343e12c5d1SDavid du Colombier int lastdol;	/* was the last token read '$' or '$#' or '"'? */
1353e12c5d1SDavid du Colombier int lastword;	/* was the last token read a word or compound word terminator? */
1363e12c5d1SDavid du Colombier int yylex(void){
1373e12c5d1SDavid du Colombier 	register int c, d=nextc();
1383e12c5d1SDavid du Colombier 	register char *w=tok;
1393e12c5d1SDavid du Colombier 	register struct tree *t;
1403e12c5d1SDavid du Colombier 	yylval.tree=0;
1413e12c5d1SDavid du Colombier 	/*
1423e12c5d1SDavid du Colombier 	 * Embarassing sneakiness:  if the last token read was a quoted or unquoted
1433e12c5d1SDavid du Colombier 	 * WORD then we alter the meaning of what follows.  If the next character
1443e12c5d1SDavid du Colombier 	 * is `(', we return SUB (a subscript paren) and consume the `('.  Otherwise,
1453e12c5d1SDavid du Colombier 	 * if the next character is the first character of a simple or compound word,
1463e12c5d1SDavid du Colombier 	 * we insert a `^' before it.
1473e12c5d1SDavid du Colombier 	 */
1483e12c5d1SDavid du Colombier 	if(lastword){
1493e12c5d1SDavid du Colombier 		lastword=0;
1503e12c5d1SDavid du Colombier 		if(d=='('){
1513e12c5d1SDavid du Colombier 			advance();
1523e12c5d1SDavid du Colombier 			strcpy(tok, "( [SUB]");
1533e12c5d1SDavid du Colombier 			return SUB;
1543e12c5d1SDavid du Colombier 		}
1553e12c5d1SDavid du Colombier 		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
1563e12c5d1SDavid du Colombier 			strcpy(tok, "^");
1573e12c5d1SDavid du Colombier 			return '^';
1583e12c5d1SDavid du Colombier 		}
1593e12c5d1SDavid du Colombier 	}
1603e12c5d1SDavid du Colombier 	inquote=0;
1613e12c5d1SDavid du Colombier 	skipwhite();
1623e12c5d1SDavid du Colombier 	switch(c=advance()){
1633e12c5d1SDavid du Colombier 	case EOF:
1643e12c5d1SDavid du Colombier 		lastdol=0;
1653e12c5d1SDavid du Colombier 		strcpy(tok, "EOF");
1663e12c5d1SDavid du Colombier 		return EOF;
1673e12c5d1SDavid du Colombier 	case '$':
1683e12c5d1SDavid du Colombier 		lastdol=1;
1693e12c5d1SDavid du Colombier 		if(nextis('#')){
1703e12c5d1SDavid du Colombier 			strcpy(tok, "$#");
1713e12c5d1SDavid du Colombier 			return COUNT;
1723e12c5d1SDavid du Colombier 		}
1733e12c5d1SDavid du Colombier 		if(nextis('"')){
1743e12c5d1SDavid du Colombier 			strcpy(tok, "$\"");
1753e12c5d1SDavid du Colombier 			return '"';
1763e12c5d1SDavid du Colombier 		}
1773e12c5d1SDavid du Colombier 		strcpy(tok, "$");
1783e12c5d1SDavid du Colombier 		return '$';
1793e12c5d1SDavid du Colombier 	case '&':
1803e12c5d1SDavid du Colombier 		lastdol=0;
1813e12c5d1SDavid du Colombier 		if(nextis('&')){
1823e12c5d1SDavid du Colombier 			skipnl();
1833e12c5d1SDavid du Colombier 			strcpy(tok, "&&");
1843e12c5d1SDavid du Colombier 			return ANDAND;
1853e12c5d1SDavid du Colombier 		}
1863e12c5d1SDavid du Colombier 		strcpy(tok, "&");
1873e12c5d1SDavid du Colombier 		return '&';
1883e12c5d1SDavid du Colombier 	case '|':
1893e12c5d1SDavid du Colombier 		lastdol=0;
1903e12c5d1SDavid du Colombier 		if(nextis(c)){
1913e12c5d1SDavid du Colombier 			skipnl();
1923e12c5d1SDavid du Colombier 			strcpy(tok, "||");
1933e12c5d1SDavid du Colombier 			return OROR;
1943e12c5d1SDavid du Colombier 		}
1953e12c5d1SDavid du Colombier 	case '<':
1963e12c5d1SDavid du Colombier 	case '>':
1973e12c5d1SDavid du Colombier 		lastdol=0;
1983e12c5d1SDavid du Colombier 		/*
1993e12c5d1SDavid du Colombier 		 * funny redirection tokens:
2003e12c5d1SDavid du Colombier 		 *	redir:	arrow | arrow '[' fd ']'
2013e12c5d1SDavid du Colombier 		 *	arrow:	'<' | '<<' | '>' | '>>' | '|'
2023e12c5d1SDavid du Colombier 		 *	fd:	digit | digit '=' | digit '=' digit
2033e12c5d1SDavid du Colombier 		 *	digit:	'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
2043e12c5d1SDavid du Colombier 		 * some possibilities are nonsensical and get a message.
2053e12c5d1SDavid du Colombier 		 */
2063e12c5d1SDavid du Colombier 		*w++=c;
2073e12c5d1SDavid du Colombier 		t=newtree();
2083e12c5d1SDavid du Colombier 		switch(c){
2093e12c5d1SDavid du Colombier 		case '|':
2103e12c5d1SDavid du Colombier 			t->type=PIPE;
2113e12c5d1SDavid du Colombier 			t->fd0=1;
2123e12c5d1SDavid du Colombier 			t->fd1=0;
2133e12c5d1SDavid du Colombier 			break;
2143e12c5d1SDavid du Colombier 		case '>':
2153e12c5d1SDavid du Colombier 			t->type=REDIR;
2163e12c5d1SDavid du Colombier 			if(nextis(c)){
2173e12c5d1SDavid du Colombier 				t->rtype=APPEND;
2183e12c5d1SDavid du Colombier 				*w++=c;
2193e12c5d1SDavid du Colombier 			}
2203e12c5d1SDavid du Colombier 			else t->rtype=WRITE;
2213e12c5d1SDavid du Colombier 			t->fd0=1;
2223e12c5d1SDavid du Colombier 			break;
2233e12c5d1SDavid du Colombier 		case '<':
2243e12c5d1SDavid du Colombier 			t->type=REDIR;
2253e12c5d1SDavid du Colombier 			if(nextis(c)){
2263e12c5d1SDavid du Colombier 				t->rtype=HERE;
2273e12c5d1SDavid du Colombier 				*w++=c;
2283e12c5d1SDavid du Colombier 			}
2293e12c5d1SDavid du Colombier 			else t->rtype=READ;
2303e12c5d1SDavid du Colombier 			t->fd0=0;
2313e12c5d1SDavid du Colombier 			break;
2323e12c5d1SDavid du Colombier 		}
2333e12c5d1SDavid du Colombier 		if(nextis('[')){
2343e12c5d1SDavid du Colombier 			*w++='[';
2353e12c5d1SDavid du Colombier 			c=advance();
2363e12c5d1SDavid du Colombier 			*w++=c;
2373e12c5d1SDavid du Colombier 			if(c<'0' || '9'<c){
2383e12c5d1SDavid du Colombier 			RedirErr:
2393e12c5d1SDavid du Colombier 				*w=0;
2403e12c5d1SDavid du Colombier 				yyerror(t->type==PIPE?"pipe syntax"
2413e12c5d1SDavid du Colombier 						:"redirection syntax");
2423e12c5d1SDavid du Colombier 				return EOF;
2433e12c5d1SDavid du Colombier 			}
2443e12c5d1SDavid du Colombier 			t->fd0=0;
2453e12c5d1SDavid du Colombier 			do{
2463e12c5d1SDavid du Colombier 				t->fd0=t->fd0*10+c-'0';
2473e12c5d1SDavid du Colombier 				*w++=c;
2483e12c5d1SDavid du Colombier 				c=advance();
2493e12c5d1SDavid du Colombier 			}while('0'<=c && c<='9');
2503e12c5d1SDavid du Colombier 			if(c=='='){
2513e12c5d1SDavid du Colombier 				*w++='=';
2523e12c5d1SDavid du Colombier 				if(t->type==REDIR) t->type=DUP;
2533e12c5d1SDavid du Colombier 				c=advance();
2543e12c5d1SDavid du Colombier 				if('0'<=c && c<='9'){
2553e12c5d1SDavid du Colombier 					t->rtype=DUPFD;
2563e12c5d1SDavid du Colombier 					t->fd1=t->fd0;
2573e12c5d1SDavid du Colombier 					t->fd0=0;
2583e12c5d1SDavid du Colombier 					do{
2593e12c5d1SDavid du Colombier 						t->fd0=t->fd0*10+c-'0';
2603e12c5d1SDavid du Colombier 						*w++=c;
2613e12c5d1SDavid du Colombier 						c=advance();
2623e12c5d1SDavid du Colombier 					}while('0'<=c && c<='9');
2633e12c5d1SDavid du Colombier 				}
2643e12c5d1SDavid du Colombier 				else{
2653e12c5d1SDavid du Colombier 					if(t->type==PIPE) goto RedirErr;
2663e12c5d1SDavid du Colombier 					t->rtype=CLOSE;
2673e12c5d1SDavid du Colombier 				}
2683e12c5d1SDavid du Colombier 			}
2693e12c5d1SDavid du Colombier 			if(c!=']'
2703e12c5d1SDavid du Colombier 			|| t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
2713e12c5d1SDavid du Colombier 				goto RedirErr;
2723e12c5d1SDavid du Colombier 			*w++=']';
2733e12c5d1SDavid du Colombier 		}
2743e12c5d1SDavid du Colombier 		*w='\0';
2753e12c5d1SDavid du Colombier 		yylval.tree=t;
2763e12c5d1SDavid du Colombier 		if(t->type==PIPE) skipnl();
2773e12c5d1SDavid du Colombier 		return t->type;
2783e12c5d1SDavid du Colombier 	case '\'':
2793e12c5d1SDavid du Colombier 		lastdol=0;
2803e12c5d1SDavid du Colombier 		lastword=1;
2813e12c5d1SDavid du Colombier 		inquote=1;
2823e12c5d1SDavid du Colombier 		for(;;){
2833e12c5d1SDavid du Colombier 			c=advance();
2843e12c5d1SDavid du Colombier 			if(c==EOF) break;
2853e12c5d1SDavid du Colombier 			if(c=='\''){
2863e12c5d1SDavid du Colombier 				if(nextc()!='\'')
2873e12c5d1SDavid du Colombier 					break;
2883e12c5d1SDavid du Colombier 				advance();
2893e12c5d1SDavid du Colombier 			}
2903e12c5d1SDavid du Colombier 			w=addutf(w, c);
2913e12c5d1SDavid du Colombier 		}
2923e12c5d1SDavid du Colombier 		if(w!=0) *w='\0';
2933e12c5d1SDavid du Colombier 		t=token(tok, WORD);
2943e12c5d1SDavid du Colombier 		t->quoted=1;
2953e12c5d1SDavid du Colombier 		yylval.tree=t;
2963e12c5d1SDavid du Colombier 		return t->type;
2973e12c5d1SDavid du Colombier 	}
2983e12c5d1SDavid du Colombier 	if(!wordchr(c)){
2993e12c5d1SDavid du Colombier 		lastdol=0;
3003e12c5d1SDavid du Colombier 		tok[0]=c;
3013e12c5d1SDavid du Colombier 		tok[1]='\0';
3023e12c5d1SDavid du Colombier 		return c;
3033e12c5d1SDavid du Colombier 	}
3043e12c5d1SDavid du Colombier 	for(;;){
3053e12c5d1SDavid du Colombier 		/* next line should have (char)c==GLOB, but ken's compiler is broken */
3063e12c5d1SDavid du Colombier 		if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
3073e12c5d1SDavid du Colombier 			w=addtok(w, GLOB);
3083e12c5d1SDavid du Colombier 		w=addutf(w, c);
3093e12c5d1SDavid du Colombier 		c=nextc();
3103e12c5d1SDavid du Colombier 		if(lastdol?!idchr(c):!wordchr(c)) break;
3113e12c5d1SDavid du Colombier 		advance();
3123e12c5d1SDavid du Colombier 	}
313*7dd7cddfSDavid du Colombier 
3143e12c5d1SDavid du Colombier 	lastword=1;
3153e12c5d1SDavid du Colombier 	lastdol=0;
3163e12c5d1SDavid du Colombier 	if(w!=0) *w='\0';
3173e12c5d1SDavid du Colombier 	t=klook(tok);
3183e12c5d1SDavid du Colombier 	if(t->type!=WORD) lastword=0;
3193e12c5d1SDavid du Colombier 	t->quoted=0;
3203e12c5d1SDavid du Colombier 	yylval.tree=t;
3213e12c5d1SDavid du Colombier 	return t->type;
3223e12c5d1SDavid du Colombier }
323