xref: /plan9/sys/src/cmd/rc/lex.c (revision 3e12c5d1bb89fc02707907988834ef147769ddaf)
1*3e12c5d1SDavid du Colombier #include "rc.h"
2*3e12c5d1SDavid du Colombier #include "exec.h"
3*3e12c5d1SDavid du Colombier #include "io.h"
4*3e12c5d1SDavid du Colombier #include "getflags.h"
5*3e12c5d1SDavid du Colombier #include "fns.h"
6*3e12c5d1SDavid du Colombier int getnext(void);
7*3e12c5d1SDavid du Colombier int wordchr(int c)
8*3e12c5d1SDavid du Colombier {
9*3e12c5d1SDavid du Colombier 	return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF;
10*3e12c5d1SDavid du Colombier }
11*3e12c5d1SDavid du Colombier int idchr(int c)
12*3e12c5d1SDavid du Colombier {
13*3e12c5d1SDavid du Colombier 	/*
14*3e12c5d1SDavid du Colombier 	 * Formerly:
15*3e12c5d1SDavid du Colombier 	 * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9'
16*3e12c5d1SDavid du Colombier 	 *	|| c=='_' || c=='*';
17*3e12c5d1SDavid du Colombier 	 */
18*3e12c5d1SDavid du Colombier 	return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
19*3e12c5d1SDavid du Colombier }
20*3e12c5d1SDavid du Colombier int future=EOF;
21*3e12c5d1SDavid du Colombier int doprompt=1;
22*3e12c5d1SDavid du Colombier int inquote;
23*3e12c5d1SDavid du Colombier /*
24*3e12c5d1SDavid du Colombier  * Look ahead in the input stream
25*3e12c5d1SDavid du Colombier  */
26*3e12c5d1SDavid du Colombier int nextc(void){
27*3e12c5d1SDavid du Colombier 	if(future==EOF) future=getnext();
28*3e12c5d1SDavid du Colombier 	return future;
29*3e12c5d1SDavid du Colombier }
30*3e12c5d1SDavid du Colombier /*
31*3e12c5d1SDavid du Colombier  * Consume the lookahead character.
32*3e12c5d1SDavid du Colombier  */
33*3e12c5d1SDavid du Colombier int advance(void){
34*3e12c5d1SDavid du Colombier 	int c=nextc();
35*3e12c5d1SDavid du Colombier 	lastc=future;
36*3e12c5d1SDavid du Colombier 	future=EOF;
37*3e12c5d1SDavid du Colombier 	return c;
38*3e12c5d1SDavid du Colombier }
39*3e12c5d1SDavid du Colombier /*
40*3e12c5d1SDavid du Colombier  * read a character from the input stream
41*3e12c5d1SDavid du Colombier  */
42*3e12c5d1SDavid du Colombier int getnext(void){
43*3e12c5d1SDavid du Colombier 	register int c;
44*3e12c5d1SDavid du Colombier 	static peekc=EOF;
45*3e12c5d1SDavid du Colombier 	if(peekc!=EOF){
46*3e12c5d1SDavid du Colombier 		c=peekc;
47*3e12c5d1SDavid du Colombier 		peekc=EOF;
48*3e12c5d1SDavid du Colombier 		return c;
49*3e12c5d1SDavid du Colombier 	}
50*3e12c5d1SDavid du Colombier 	if(runq->eof) return EOF;
51*3e12c5d1SDavid du Colombier 	if(doprompt) pprompt();
52*3e12c5d1SDavid du Colombier 	c=rchr(runq->cmdfd);
53*3e12c5d1SDavid du Colombier 	if(!inquote && c=='\\'){
54*3e12c5d1SDavid du Colombier 		c=rchr(runq->cmdfd);
55*3e12c5d1SDavid du Colombier 		if(c=='\n'){
56*3e12c5d1SDavid du Colombier 			doprompt=1;
57*3e12c5d1SDavid du Colombier 			c=' ';
58*3e12c5d1SDavid du Colombier 		}
59*3e12c5d1SDavid du Colombier 		else{
60*3e12c5d1SDavid du Colombier 			peekc=c;
61*3e12c5d1SDavid du Colombier 			c='\\';
62*3e12c5d1SDavid du Colombier 		}
63*3e12c5d1SDavid du Colombier 	}
64*3e12c5d1SDavid du Colombier 	doprompt=doprompt || c=='\n' || c==EOF;
65*3e12c5d1SDavid du Colombier 	if(c==EOF) runq->eof++;
66*3e12c5d1SDavid du Colombier 	else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c);
67*3e12c5d1SDavid du Colombier 	return c;
68*3e12c5d1SDavid du Colombier }
69*3e12c5d1SDavid du Colombier void pprompt(void){
70*3e12c5d1SDavid du Colombier 	var *prompt;
71*3e12c5d1SDavid du Colombier 	if(runq->iflag){
72*3e12c5d1SDavid du Colombier 		pstr(err, promptstr);
73*3e12c5d1SDavid du Colombier 		flush(err);
74*3e12c5d1SDavid du Colombier 		prompt=vlook("prompt");
75*3e12c5d1SDavid du Colombier 		if(prompt->val && prompt->val->next)
76*3e12c5d1SDavid du Colombier 			promptstr=prompt->val->next->word;
77*3e12c5d1SDavid du Colombier 		else
78*3e12c5d1SDavid du Colombier 			promptstr="\t";
79*3e12c5d1SDavid du Colombier 	}
80*3e12c5d1SDavid du Colombier 	runq->lineno++;
81*3e12c5d1SDavid du Colombier 	doprompt=0;
82*3e12c5d1SDavid du Colombier }
83*3e12c5d1SDavid du Colombier void skipwhite(void){
84*3e12c5d1SDavid du Colombier 	int c;
85*3e12c5d1SDavid du Colombier 	for(;;){
86*3e12c5d1SDavid du Colombier 		c=nextc();
87*3e12c5d1SDavid du Colombier 		if(c=='#'){	/* Why did this used to be  if(!inquote && c=='#') ?? */
88*3e12c5d1SDavid du Colombier 			for(;;){
89*3e12c5d1SDavid du Colombier 				c=nextc();
90*3e12c5d1SDavid du Colombier 				if(c=='\n' || c==EOF) break;
91*3e12c5d1SDavid du Colombier 				advance();
92*3e12c5d1SDavid du Colombier 			}
93*3e12c5d1SDavid du Colombier 		}
94*3e12c5d1SDavid du Colombier 		if(c==' ' || c=='\t') advance();
95*3e12c5d1SDavid du Colombier 		else return;
96*3e12c5d1SDavid du Colombier 	}
97*3e12c5d1SDavid du Colombier }
98*3e12c5d1SDavid du Colombier void skipnl(void){
99*3e12c5d1SDavid du Colombier 	register int c;
100*3e12c5d1SDavid du Colombier 	for(;;){
101*3e12c5d1SDavid du Colombier 		skipwhite();
102*3e12c5d1SDavid du Colombier 		c=nextc();
103*3e12c5d1SDavid du Colombier 		if(c!='\n') return;
104*3e12c5d1SDavid du Colombier 		advance();
105*3e12c5d1SDavid du Colombier 	}
106*3e12c5d1SDavid du Colombier }
107*3e12c5d1SDavid du Colombier int nextis(int c){
108*3e12c5d1SDavid du Colombier 	if(nextc()==c){
109*3e12c5d1SDavid du Colombier 		advance();
110*3e12c5d1SDavid du Colombier 		return 1;
111*3e12c5d1SDavid du Colombier 	}
112*3e12c5d1SDavid du Colombier 	return 0;
113*3e12c5d1SDavid du Colombier }
114*3e12c5d1SDavid du Colombier char *addtok(char *p, int val){
115*3e12c5d1SDavid du Colombier 	if(p==0) return 0;
116*3e12c5d1SDavid du Colombier 	if(p==&tok[NTOK]){
117*3e12c5d1SDavid du Colombier 		*p=0;
118*3e12c5d1SDavid du Colombier 		yyerror("token buffer too short");
119*3e12c5d1SDavid du Colombier 		return 0;
120*3e12c5d1SDavid du Colombier 	}
121*3e12c5d1SDavid du Colombier 	*p++=val;
122*3e12c5d1SDavid du Colombier 	return p;
123*3e12c5d1SDavid du Colombier }
124*3e12c5d1SDavid du Colombier char *addutf(char *p, int c){
125*3e12c5d1SDavid du Colombier 	p=addtok(p, c);
126*3e12c5d1SDavid du Colombier 	if(twobyte(c))	 /* 2-byte escape */
127*3e12c5d1SDavid du Colombier 		return addtok(p, advance());
128*3e12c5d1SDavid du Colombier 	if(threebyte(c)){	/* 3-byte escape */
129*3e12c5d1SDavid du Colombier 		p=addtok(p, advance());
130*3e12c5d1SDavid du Colombier 		return addtok(p, advance());
131*3e12c5d1SDavid du Colombier 	}
132*3e12c5d1SDavid du Colombier 	return p;
133*3e12c5d1SDavid du Colombier }
134*3e12c5d1SDavid du Colombier int lastdol;	/* was the last token read '$' or '$#' or '"'? */
135*3e12c5d1SDavid du Colombier int lastword;	/* was the last token read a word or compound word terminator? */
136*3e12c5d1SDavid du Colombier int yylex(void){
137*3e12c5d1SDavid du Colombier 	register int c, d=nextc();
138*3e12c5d1SDavid du Colombier 	register char *w=tok;
139*3e12c5d1SDavid du Colombier 	register struct tree *t;
140*3e12c5d1SDavid du Colombier 	yylval.tree=0;
141*3e12c5d1SDavid du Colombier 	/*
142*3e12c5d1SDavid du Colombier 	 * Embarassing sneakiness:  if the last token read was a quoted or unquoted
143*3e12c5d1SDavid du Colombier 	 * WORD then we alter the meaning of what follows.  If the next character
144*3e12c5d1SDavid du Colombier 	 * is `(', we return SUB (a subscript paren) and consume the `('.  Otherwise,
145*3e12c5d1SDavid du Colombier 	 * if the next character is the first character of a simple or compound word,
146*3e12c5d1SDavid du Colombier 	 * we insert a `^' before it.
147*3e12c5d1SDavid du Colombier 	 */
148*3e12c5d1SDavid du Colombier 	if(lastword){
149*3e12c5d1SDavid du Colombier 		lastword=0;
150*3e12c5d1SDavid du Colombier 		if(d=='('){
151*3e12c5d1SDavid du Colombier 			advance();
152*3e12c5d1SDavid du Colombier 			strcpy(tok, "( [SUB]");
153*3e12c5d1SDavid du Colombier 			return SUB;
154*3e12c5d1SDavid du Colombier 		}
155*3e12c5d1SDavid du Colombier 		if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){
156*3e12c5d1SDavid du Colombier 			strcpy(tok, "^");
157*3e12c5d1SDavid du Colombier 			return '^';
158*3e12c5d1SDavid du Colombier 		}
159*3e12c5d1SDavid du Colombier 	}
160*3e12c5d1SDavid du Colombier 	inquote=0;
161*3e12c5d1SDavid du Colombier 	skipwhite();
162*3e12c5d1SDavid du Colombier 	switch(c=advance()){
163*3e12c5d1SDavid du Colombier 	case EOF:
164*3e12c5d1SDavid du Colombier 		lastdol=0;
165*3e12c5d1SDavid du Colombier 		strcpy(tok, "EOF");
166*3e12c5d1SDavid du Colombier 		return EOF;
167*3e12c5d1SDavid du Colombier 	case '$':
168*3e12c5d1SDavid du Colombier 		lastdol=1;
169*3e12c5d1SDavid du Colombier 		if(nextis('#')){
170*3e12c5d1SDavid du Colombier 			strcpy(tok, "$#");
171*3e12c5d1SDavid du Colombier 			return COUNT;
172*3e12c5d1SDavid du Colombier 		}
173*3e12c5d1SDavid du Colombier 		if(nextis('"')){
174*3e12c5d1SDavid du Colombier 			strcpy(tok, "$\"");
175*3e12c5d1SDavid du Colombier 			return '"';
176*3e12c5d1SDavid du Colombier 		}
177*3e12c5d1SDavid du Colombier 		strcpy(tok, "$");
178*3e12c5d1SDavid du Colombier 		return '$';
179*3e12c5d1SDavid du Colombier 	case '&':
180*3e12c5d1SDavid du Colombier 		lastdol=0;
181*3e12c5d1SDavid du Colombier 		if(nextis('&')){
182*3e12c5d1SDavid du Colombier 			skipnl();
183*3e12c5d1SDavid du Colombier 			strcpy(tok, "&&");
184*3e12c5d1SDavid du Colombier 			return ANDAND;
185*3e12c5d1SDavid du Colombier 		}
186*3e12c5d1SDavid du Colombier 		strcpy(tok, "&");
187*3e12c5d1SDavid du Colombier 		return '&';
188*3e12c5d1SDavid du Colombier 	case '|':
189*3e12c5d1SDavid du Colombier 		lastdol=0;
190*3e12c5d1SDavid du Colombier 		if(nextis(c)){
191*3e12c5d1SDavid du Colombier 			skipnl();
192*3e12c5d1SDavid du Colombier 			strcpy(tok, "||");
193*3e12c5d1SDavid du Colombier 			return OROR;
194*3e12c5d1SDavid du Colombier 		}
195*3e12c5d1SDavid du Colombier 	case '<':
196*3e12c5d1SDavid du Colombier 	case '>':
197*3e12c5d1SDavid du Colombier 		lastdol=0;
198*3e12c5d1SDavid du Colombier 		/*
199*3e12c5d1SDavid du Colombier 		 * funny redirection tokens:
200*3e12c5d1SDavid du Colombier 		 *	redir:	arrow | arrow '[' fd ']'
201*3e12c5d1SDavid du Colombier 		 *	arrow:	'<' | '<<' | '>' | '>>' | '|'
202*3e12c5d1SDavid du Colombier 		 *	fd:	digit | digit '=' | digit '=' digit
203*3e12c5d1SDavid du Colombier 		 *	digit:	'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
204*3e12c5d1SDavid du Colombier 		 * some possibilities are nonsensical and get a message.
205*3e12c5d1SDavid du Colombier 		 */
206*3e12c5d1SDavid du Colombier 		*w++=c;
207*3e12c5d1SDavid du Colombier 		t=newtree();
208*3e12c5d1SDavid du Colombier 		switch(c){
209*3e12c5d1SDavid du Colombier 		case '|':
210*3e12c5d1SDavid du Colombier 			t->type=PIPE;
211*3e12c5d1SDavid du Colombier 			t->fd0=1;
212*3e12c5d1SDavid du Colombier 			t->fd1=0;
213*3e12c5d1SDavid du Colombier 			break;
214*3e12c5d1SDavid du Colombier 		case '>':
215*3e12c5d1SDavid du Colombier 			t->type=REDIR;
216*3e12c5d1SDavid du Colombier 			if(nextis(c)){
217*3e12c5d1SDavid du Colombier 				t->rtype=APPEND;
218*3e12c5d1SDavid du Colombier 				*w++=c;
219*3e12c5d1SDavid du Colombier 			}
220*3e12c5d1SDavid du Colombier 			else t->rtype=WRITE;
221*3e12c5d1SDavid du Colombier 			t->fd0=1;
222*3e12c5d1SDavid du Colombier 			break;
223*3e12c5d1SDavid du Colombier 		case '<':
224*3e12c5d1SDavid du Colombier 			t->type=REDIR;
225*3e12c5d1SDavid du Colombier 			if(nextis(c)){
226*3e12c5d1SDavid du Colombier 				t->rtype=HERE;
227*3e12c5d1SDavid du Colombier 				*w++=c;
228*3e12c5d1SDavid du Colombier 			}
229*3e12c5d1SDavid du Colombier 			else t->rtype=READ;
230*3e12c5d1SDavid du Colombier 			t->fd0=0;
231*3e12c5d1SDavid du Colombier 			break;
232*3e12c5d1SDavid du Colombier 		}
233*3e12c5d1SDavid du Colombier 		if(nextis('[')){
234*3e12c5d1SDavid du Colombier 			*w++='[';
235*3e12c5d1SDavid du Colombier 			c=advance();
236*3e12c5d1SDavid du Colombier 			*w++=c;
237*3e12c5d1SDavid du Colombier 			if(c<'0' || '9'<c){
238*3e12c5d1SDavid du Colombier 			RedirErr:
239*3e12c5d1SDavid du Colombier 				*w=0;
240*3e12c5d1SDavid du Colombier 				yyerror(t->type==PIPE?"pipe syntax"
241*3e12c5d1SDavid du Colombier 						:"redirection syntax");
242*3e12c5d1SDavid du Colombier 				return EOF;
243*3e12c5d1SDavid du Colombier 			}
244*3e12c5d1SDavid du Colombier 			t->fd0=0;
245*3e12c5d1SDavid du Colombier 			do{
246*3e12c5d1SDavid du Colombier 				t->fd0=t->fd0*10+c-'0';
247*3e12c5d1SDavid du Colombier 				*w++=c;
248*3e12c5d1SDavid du Colombier 				c=advance();
249*3e12c5d1SDavid du Colombier 			}while('0'<=c && c<='9');
250*3e12c5d1SDavid du Colombier 			if(c=='='){
251*3e12c5d1SDavid du Colombier 				*w++='=';
252*3e12c5d1SDavid du Colombier 				if(t->type==REDIR) t->type=DUP;
253*3e12c5d1SDavid du Colombier 				c=advance();
254*3e12c5d1SDavid du Colombier 				if('0'<=c && c<='9'){
255*3e12c5d1SDavid du Colombier 					t->rtype=DUPFD;
256*3e12c5d1SDavid du Colombier 					t->fd1=t->fd0;
257*3e12c5d1SDavid du Colombier 					t->fd0=0;
258*3e12c5d1SDavid du Colombier 					do{
259*3e12c5d1SDavid du Colombier 						t->fd0=t->fd0*10+c-'0';
260*3e12c5d1SDavid du Colombier 						*w++=c;
261*3e12c5d1SDavid du Colombier 						c=advance();
262*3e12c5d1SDavid du Colombier 					}while('0'<=c && c<='9');
263*3e12c5d1SDavid du Colombier 				}
264*3e12c5d1SDavid du Colombier 				else{
265*3e12c5d1SDavid du Colombier 					if(t->type==PIPE) goto RedirErr;
266*3e12c5d1SDavid du Colombier 					t->rtype=CLOSE;
267*3e12c5d1SDavid du Colombier 				}
268*3e12c5d1SDavid du Colombier 			}
269*3e12c5d1SDavid du Colombier 			if(c!=']'
270*3e12c5d1SDavid du Colombier 			|| t->type==DUP && (t->rtype==HERE || t->rtype==APPEND))
271*3e12c5d1SDavid du Colombier 				goto RedirErr;
272*3e12c5d1SDavid du Colombier 			*w++=']';
273*3e12c5d1SDavid du Colombier 		}
274*3e12c5d1SDavid du Colombier 		*w='\0';
275*3e12c5d1SDavid du Colombier 		yylval.tree=t;
276*3e12c5d1SDavid du Colombier 		if(t->type==PIPE) skipnl();
277*3e12c5d1SDavid du Colombier 		return t->type;
278*3e12c5d1SDavid du Colombier 	case '\'':
279*3e12c5d1SDavid du Colombier 		lastdol=0;
280*3e12c5d1SDavid du Colombier 		lastword=1;
281*3e12c5d1SDavid du Colombier 		inquote=1;
282*3e12c5d1SDavid du Colombier 		for(;;){
283*3e12c5d1SDavid du Colombier 			c=advance();
284*3e12c5d1SDavid du Colombier 			if(c==EOF) break;
285*3e12c5d1SDavid du Colombier 			if(c=='\''){
286*3e12c5d1SDavid du Colombier 				if(nextc()!='\'')
287*3e12c5d1SDavid du Colombier 					break;
288*3e12c5d1SDavid du Colombier 				advance();
289*3e12c5d1SDavid du Colombier 			}
290*3e12c5d1SDavid du Colombier 			w=addutf(w, c);
291*3e12c5d1SDavid du Colombier 		}
292*3e12c5d1SDavid du Colombier 		if(w!=0) *w='\0';
293*3e12c5d1SDavid du Colombier 		t=token(tok, WORD);
294*3e12c5d1SDavid du Colombier 		t->quoted=1;
295*3e12c5d1SDavid du Colombier 		yylval.tree=t;
296*3e12c5d1SDavid du Colombier 		return t->type;
297*3e12c5d1SDavid du Colombier 	}
298*3e12c5d1SDavid du Colombier 	if(!wordchr(c)){
299*3e12c5d1SDavid du Colombier 		lastdol=0;
300*3e12c5d1SDavid du Colombier 		tok[0]=c;
301*3e12c5d1SDavid du Colombier 		tok[1]='\0';
302*3e12c5d1SDavid du Colombier 		return c;
303*3e12c5d1SDavid du Colombier 	}
304*3e12c5d1SDavid du Colombier 	for(;;){
305*3e12c5d1SDavid du Colombier 		/* next line should have (char)c==GLOB, but ken's compiler is broken */
306*3e12c5d1SDavid du Colombier 		if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB)
307*3e12c5d1SDavid du Colombier 			w=addtok(w, GLOB);
308*3e12c5d1SDavid du Colombier 		w=addutf(w, c);
309*3e12c5d1SDavid du Colombier 		c=nextc();
310*3e12c5d1SDavid du Colombier 		if(lastdol?!idchr(c):!wordchr(c)) break;
311*3e12c5d1SDavid du Colombier 		advance();
312*3e12c5d1SDavid du Colombier 	}
313*3e12c5d1SDavid du Colombier Out:
314*3e12c5d1SDavid du Colombier 	lastword=1;
315*3e12c5d1SDavid du Colombier 	lastdol=0;
316*3e12c5d1SDavid du Colombier 	if(w!=0) *w='\0';
317*3e12c5d1SDavid du Colombier 	t=klook(tok);
318*3e12c5d1SDavid du Colombier 	if(t->type!=WORD) lastword=0;
319*3e12c5d1SDavid du Colombier 	t->quoted=0;
320*3e12c5d1SDavid du Colombier 	yylval.tree=t;
321*3e12c5d1SDavid du Colombier 	return t->type;
322*3e12c5d1SDavid du Colombier }
323