xref: /plan9/sys/src/cmd/mk/lex.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include	"mk.h"
2 
3 static int initdone = 0;
4 
5 static int bquote(Biobuf *, Bufblock *);
6 static int assquote(Biobuf *, Bufblock *, int, int);
7 static char *squote(char*);
8 static long nextrune(Biobuf*, int);
9 static int expandvar(Biobuf *, Bufblock *);
10 static Bufblock *varname(Biobuf*);
11 static int varsub(Biobuf *, Bufblock *);
12 
13 /*
14  *	Assemble a line skipping blank lines, comments, and eliding
15  *	escaped newlines
16  */
17 int
18 assline(Biobuf *bp, Bufblock *buf)
19 {
20 	int c;
21 	int lastc;
22 
23 	initdone = 0;
24 	buf->current=buf->start;
25 	while ((c = nextrune(bp, 1)) >= 0){
26 		switch(c)
27 		{
28 		case '\n':
29 			if (buf->current != buf->start) {
30 				insert(buf, 0);
31 				return 1;
32 			}
33 			break;		/* skip empty lines */
34 		case '\'':
35 			rinsert(buf, c);
36 			if (!assquote(bp, buf, 1, c))
37 				Exit();
38 			break;
39 		case '`':
40 			if (!bquote(bp, buf))
41 				Exit();
42 			break;
43 		case '$':
44 			if (!varsub(bp, buf))
45 				Exit();
46 			break;
47 		case '#':
48 			lastc = '#';
49 			while ((c = Bgetc(bp)) != '\n') {
50 				if (c < 0)
51 					goto eof;
52 				lastc = c;
53 			}
54 			inline++;
55 			if (lastc == '\\')
56 				break;		/* propagate escaped newlines??*/
57 			if (buf->current != buf->start) {
58 				insert(buf, 0);
59 				return 1;
60 			}
61 			break;
62 		default:
63 			rinsert(buf, c);
64 			break;
65 		}
66 	}
67 eof:
68 	insert(buf, 0);
69 	return *buf->start != 0;
70 }
71 /*
72  *	Assemble a token surrounded by single quotes
73  */
74 static int
75 assquote(Biobuf *bp, Bufblock *buf, int preserve, int termchar)
76 {
77 	int c, line;
78 
79 	line = inline;
80 	while ((c = nextrune(bp, 0)) >= 0) {
81 		if (c == termchar) {
82 			if (preserve)
83 				rinsert(buf, c);
84 			c = Bgetrune(bp);
85 			if (c < 0)
86 				break;
87 			if (c != termchar) {
88 				Bungetrune(bp);
89 				return 1;
90 			}
91 		}
92 		rinsert(buf, c);
93 	}
94 	SYNERR(line); fprint(2, "missing closing '\n");
95 	return 0;
96 }
97 /*
98  *	assemble a back-quoted shell command
99  */
100 static int
101 bquote(Biobuf *bp, Bufblock *buf)
102 {
103 	int c, line;
104 	int start;
105 	char *end;
106 
107 	line = inline;
108 	while ((c = Bgetrune(bp)) == ' ' || c == '\t')
109 			;
110 	if(c != '{') {
111 		SYNERR(line);
112 		fprint(2, "missing opening { after `\n");
113 		return 0;
114 	}
115 	start = buf->current-buf->start;
116 	while ((c = nextrune(bp, 0)) > 0) {
117 		if (c == '\n')
118 			break;
119 		if (c == '}') {
120 			insert(buf, '\n');
121 			insert(buf,0);
122 			end = buf->current-1;
123 			buf->current = buf->start+start;
124 			if(initdone == 0){
125 				execinit();
126 				initdone = 1;
127 			}
128 			rcexec(buf->current, end, buf);
129 			return 1;
130 		} else if (QUOTE(c)) {
131 			insert(buf, c);
132 			if (!assquote(bp, buf, 1, c))
133 				return 0;
134 			continue;
135 		}
136 		rinsert(buf, c);
137 	}
138 	SYNERR(line);
139 	fprint(2, "missing closing } after `{\n");
140 	return 0;
141 }
142 
143 static int
144 varsub(Biobuf *bp, Bufblock *buf)
145 {
146 	int c;
147 	Bufblock *buf2;
148 
149 	c = Bgetrune(bp);
150 	if(c == '{')		/* either ${name} or ${name: A%B==C%D}*/
151 		return expandvar(bp, buf);
152 	Bungetrune(bp);
153 	buf2 = varname(bp);
154 	if (!buf2)
155 		return 0;
156 	varmatch(buf2->start, buf);
157 	freebuf(buf2);
158 	return 1;
159 }
160 
161 static int
162 expandvar(Biobuf *bp, Bufblock *buf)
163 {
164 	Bufblock *buf2;
165 	int c;
166 	int start;
167 	Symtab *sym;
168 
169 	buf2 = varname(bp);
170 	if (!buf2)
171 		return 0;
172 	c = Bgetrune(bp);
173 	if (c == '}') {				/* ${name} variant*/
174 		varmatch(buf2->start, buf);
175 		freebuf(buf2);
176 		return 1;
177 	}
178 	if (c != ':') {
179 		SYNERR(-1);
180 		fprint(2, "bad variable name <%s>\n", buf2->start);
181 		freebuf(buf2);
182 		return 0;
183 	}
184 	start = buf2->current-buf2->start;
185 	while ((c = Bgetrune(bp)) != '}') {	/* ${name:A%B=C%D} variant */
186 		switch(c)
187 		{
188 		case 0:
189 		case '\n':
190 			SYNERR(-1);
191 			fprint(2, "missing '}'\n");
192 			freebuf(buf2);
193 			return 0;
194 		case '\'':
195 			if (!assquote(bp, buf2, 0, c)) {
196 				freebuf(buf2);
197 				return 0;
198 			}
199 			break;
200 		case '`':
201 			if (!bquote(bp, buf2)) {
202 				freebuf(buf2);
203 				return 0;
204 			}
205 			break;
206 		case '$':
207 			if (!varsub(bp, buf2)) {
208 				freebuf(buf2);
209 				return 0;
210 			}
211 			break;
212 		case ' ':
213 		case '\t':
214 			break;
215 		default:
216 			rinsert(buf2, c);
217 			break;
218 		}
219 	}
220 	insert(buf2, 0);
221 	sym = symlook(buf2->start, S_VAR, 0);
222 	if (!sym || !sym->value)
223 		bufcpy(buf, buf2->start, start-2);
224 	else
225 		subsub((Word *) sym->value, buf2->start+start, buf);
226 	freebuf(buf2);
227 	return 1;
228 }
229 /*
230  *	extract a variable name
231  */
232 static Bufblock *
233 varname(Biobuf *bp)
234 {
235 	Bufblock *buf;
236 	int c;
237 
238 	buf = newbuf();
239 	c = Bgetrune(bp);
240 	while (WORDCHR(c)) {
241 		rinsert(buf, c);
242 		c = Bgetrune(bp);
243 	}
244 	if (c < 0 || buf->current == buf->start) {
245 		SYNERR(-1);
246 		fprint(2, "bad variable name <%s>\n", buf->start);
247 		freebuf(buf);
248 		return 0;
249 	}
250 	Bungetrune(bp);
251 	insert(buf, 0);
252 	return buf;
253 }
254 /*
255  *	search a string for unescaped characters in a pattern set
256  */
257 char *
258 charin(char *cp, char *pat)
259 {
260 	Rune r;
261 	int n;
262 
263 	while (*cp) {
264 		n = chartorune(&r, cp);
265 		if (r == '\'') {	/* skip quoted string */
266 			cp = squote(cp);	/* n must = 1 */
267 			if (!cp)
268 				return 0;
269 		} else
270 			if (utfrune(pat, r))
271 				return cp;
272 		cp += n;
273 	}
274 	return 0;
275 }
276 /*
277  *	skip a token in single quotes.
278  */
279 static char *
280 squote(char *cp)
281 {
282 	Rune r;
283 	int n;
284 
285 	cp++;			/* skip opening quote */
286 	while (*cp) {
287 		n = chartorune(&r, cp);
288 		if (r == '\'') {
289 			n += chartorune(&r, cp+n);
290 			if (r != '\'')
291 				return(cp);
292 		}
293 		cp += n;
294 	}
295 	SYNERR(-1);		/* should never occur */
296 	fprint(2, "missing closing '\n");
297 	return 0;
298 }
299 /*
300  *	get next character stripping escaped newlines
301  *	the flag specifies whether escaped newlines are to be elided or
302  *	replaced with a blank.
303  */
304 static long
305 nextrune(Biobuf *bp, int elide)
306 {
307 	int c;
308 
309 	for (;;) {
310 		c = Bgetrune(bp);
311 		if (c == '\\') {
312 			if (Bgetrune(bp) == '\n') {
313 				inline++;
314 				if (elide)
315 					continue;
316 				return ' ';
317 			}
318 			Bungetrune(bp);
319 		}
320 		if (c == '\n')
321 			inline++;
322 		return c;
323 	}
324 	return 0;
325 }
326