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