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