lex.c revision 1.4 1 1.4 thorpej /* $NetBSD: lex.c,v 1.4 1998/08/19 01:43:22 thorpej Exp $ */
2 1.2 tls
3 1.1 jtc /*
4 1.1 jtc * lexical analysis and source input
5 1.1 jtc */
6 1.1 jtc
7 1.1 jtc #include "sh.h"
8 1.1 jtc #include <ctype.h>
9 1.1 jtc
10 1.1 jtc static void readhere ARGS((struct ioword *iop));
11 1.2 tls static int getsc__ ARGS((void));
12 1.1 jtc static void getsc_line ARGS((Source *s));
13 1.1 jtc static char *get_brace_var ARGS((XString *wsp, char *wp));
14 1.1 jtc static int arraysub ARGS((char **strp));
15 1.2 tls static const char *ungetsc ARGS((int c));
16 1.2 tls static int getsc_bn ARGS((void));
17 1.2 tls static void gethere ARGS((void));
18 1.2 tls
19 1.2 tls static int backslash_skip;
20 1.2 tls static int ignore_backslash_newline;
21 1.2 tls
22 1.2 tls /* optimized getsc_bn() */
23 1.2 tls #define getsc() (*source->str != '\0' && *source->str != '\\' \
24 1.2 tls && !backslash_skip ? *source->str++ : getsc_bn())
25 1.2 tls /* optimized getsc__() */
26 1.2 tls #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__())
27 1.1 jtc
28 1.1 jtc
29 1.1 jtc /*
30 1.1 jtc * Lexical analyzer
31 1.1 jtc *
32 1.1 jtc * tokens are not regular expressions, they are LL(1).
33 1.1 jtc * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
34 1.1 jtc * hence the state stack.
35 1.1 jtc */
36 1.1 jtc
37 1.1 jtc int
38 1.1 jtc yylex(cf)
39 1.1 jtc int cf;
40 1.1 jtc {
41 1.1 jtc register int c, state;
42 1.1 jtc char states [64], *statep = states; /* XXX overflow check */
43 1.1 jtc XString ws; /* expandable output word */
44 1.1 jtc register char *wp; /* output word pointer */
45 1.1 jtc register char *sp, *dp;
46 1.1 jtc char UNINITIALIZED(*ddparen_start);
47 1.1 jtc int istate;
48 1.1 jtc int UNINITIALIZED(c2);
49 1.1 jtc int UNINITIALIZED(nparen), UNINITIALIZED(csstate);
50 1.1 jtc int UNINITIALIZED(ndparen);
51 1.1 jtc int UNINITIALIZED(indquotes);
52 1.1 jtc
53 1.1 jtc
54 1.1 jtc Again:
55 1.1 jtc Xinit(ws, wp, 64, ATEMP);
56 1.1 jtc
57 1.2 tls backslash_skip = 0;
58 1.2 tls ignore_backslash_newline = 0;
59 1.2 tls
60 1.1 jtc if (cf&ONEWORD)
61 1.1 jtc istate = SWORD;
62 1.2 tls #ifdef KSH
63 1.1 jtc else if (cf&LETEXPR) {
64 1.1 jtc *wp++ = OQUOTE; /* enclose arguments in (double) quotes */
65 1.1 jtc istate = SDPAREN;
66 1.1 jtc ndparen = 0;
67 1.2 tls }
68 1.2 tls #endif /* KSH */
69 1.2 tls else { /* normal lexing */
70 1.1 jtc istate = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
71 1.1 jtc while ((c = getsc()) == ' ' || c == '\t')
72 1.1 jtc ;
73 1.2 tls if (c == '#') {
74 1.2 tls ignore_backslash_newline++;
75 1.1 jtc while ((c = getsc()) != '\0' && c != '\n')
76 1.1 jtc ;
77 1.2 tls ignore_backslash_newline--;
78 1.2 tls }
79 1.1 jtc ungetsc(c);
80 1.1 jtc }
81 1.1 jtc if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */
82 1.1 jtc source->flags &= ~SF_ALIAS;
83 1.1 jtc /* In POSIX mode, a trailing space only counts if we are
84 1.1 jtc * parsing a simple command
85 1.1 jtc */
86 1.1 jtc if (!Flag(FPOSIX) || (cf & CMDWORD))
87 1.1 jtc cf |= ALIAS;
88 1.1 jtc }
89 1.1 jtc
90 1.1 jtc /* collect non-special or quoted characters to form word */
91 1.1 jtc for (*statep = state = istate;
92 1.1 jtc !((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM)
93 1.1 jtc && ctype(c, C_LEX1))); )
94 1.1 jtc {
95 1.1 jtc Xcheck(ws, wp);
96 1.1 jtc switch (state) {
97 1.1 jtc case SBASE:
98 1.1 jtc if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
99 1.1 jtc *wp = EOS; /* temporary */
100 1.1 jtc if (is_wdvarname(Xstring(ws, wp), FALSE))
101 1.1 jtc {
102 1.1 jtc char *p, *tmp;
103 1.1 jtc
104 1.1 jtc if (arraysub(&tmp)) {
105 1.1 jtc *wp++ = CHAR;
106 1.1 jtc *wp++ = c;
107 1.1 jtc for (p = tmp; *p; ) {
108 1.1 jtc Xcheck(ws, wp);
109 1.1 jtc *wp++ = CHAR;
110 1.1 jtc *wp++ = *p++;
111 1.1 jtc }
112 1.1 jtc afree(tmp, ATEMP);
113 1.1 jtc break;
114 1.1 jtc } else {
115 1.1 jtc Source *s;
116 1.1 jtc
117 1.1 jtc s = pushs(SREREAD,
118 1.1 jtc source->areap);
119 1.1 jtc s->start = s->str
120 1.1 jtc = s->u.freeme = tmp;
121 1.1 jtc s->next = source;
122 1.1 jtc source = s;
123 1.1 jtc }
124 1.1 jtc }
125 1.1 jtc *wp++ = CHAR;
126 1.1 jtc *wp++ = c;
127 1.1 jtc break;
128 1.1 jtc }
129 1.1 jtc /* fall through.. */
130 1.1 jtc Sbase1: /* includes *(...|...) pattern (*+?@!) */
131 1.1 jtc #ifdef KSH
132 1.1 jtc if (c == '*' || c == '@' || c == '+' || c == '?'
133 1.1 jtc || c == '!')
134 1.1 jtc {
135 1.1 jtc c2 = getsc();
136 1.1 jtc if (c2 == '(' /*)*/ ) {
137 1.1 jtc *wp++ = OPAT;
138 1.1 jtc *wp++ = c;
139 1.1 jtc *++statep = state = SPATTERN;
140 1.1 jtc break;
141 1.1 jtc }
142 1.1 jtc ungetsc(c2);
143 1.1 jtc }
144 1.1 jtc #endif /* KSH */
145 1.1 jtc /* fall through.. */
146 1.1 jtc Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */
147 1.1 jtc switch (c) {
148 1.1 jtc case '\\':
149 1.1 jtc c = getsc();
150 1.1 jtc #ifdef OS2
151 1.2 tls if (isalnum(c)) {
152 1.2 tls *wp++ = CHAR, *wp++ = '\\';
153 1.2 tls *wp++ = CHAR, *wp++ = c;
154 1.2 tls } else
155 1.1 jtc #endif
156 1.2 tls if (c) /* trailing \ is lost */
157 1.2 tls *wp++ = QCHAR, *wp++ = c;
158 1.1 jtc break;
159 1.1 jtc case '\'':
160 1.1 jtc *++statep = state = SSQUOTE;
161 1.1 jtc *wp++ = OQUOTE;
162 1.2 tls ignore_backslash_newline++;
163 1.1 jtc break;
164 1.1 jtc case '"':
165 1.1 jtc *++statep = state = SDQUOTE;
166 1.1 jtc *wp++ = OQUOTE;
167 1.1 jtc break;
168 1.1 jtc default:
169 1.1 jtc goto Subst;
170 1.1 jtc }
171 1.1 jtc break;
172 1.1 jtc
173 1.1 jtc Subst:
174 1.1 jtc switch (c) {
175 1.1 jtc case '\\':
176 1.1 jtc c = getsc();
177 1.1 jtc switch (c) {
178 1.1 jtc case '"': case '\\':
179 1.1 jtc case '$': case '`':
180 1.1 jtc *wp++ = QCHAR, *wp++ = c;
181 1.1 jtc break;
182 1.1 jtc default:
183 1.1 jtc Xcheck(ws, wp);
184 1.2 tls if (c) { /* trailing \ is lost */
185 1.2 tls *wp++ = CHAR, *wp++ = '\\';
186 1.2 tls *wp++ = CHAR, *wp++ = c;
187 1.2 tls }
188 1.1 jtc break;
189 1.1 jtc }
190 1.1 jtc break;
191 1.1 jtc case '$':
192 1.1 jtc c = getsc();
193 1.1 jtc if (c == '(') /*)*/ {
194 1.1 jtc c = getsc();
195 1.1 jtc if (c == '(') /*)*/ {
196 1.1 jtc *++statep = state = SDDPAREN;
197 1.1 jtc nparen = 2;
198 1.1 jtc ddparen_start = wp;
199 1.1 jtc *wp++ = EXPRSUB;
200 1.1 jtc } else {
201 1.1 jtc ungetsc(c);
202 1.1 jtc *++statep = state = SPAREN;
203 1.1 jtc nparen = 1;
204 1.1 jtc csstate = 0;
205 1.1 jtc *wp++ = COMSUB;
206 1.1 jtc }
207 1.1 jtc } else if (c == '{') /*}*/ {
208 1.1 jtc *wp++ = OSUBST;
209 1.1 jtc wp = get_brace_var(&ws, wp);
210 1.1 jtc /* If this is a trim operation,
211 1.1 jtc * wrap @(...) around the pattern
212 1.1 jtc * (allows easy handling of ${a#b|c})
213 1.1 jtc */
214 1.2 tls c = getsc();
215 1.1 jtc if (c == '#' || c == '%') {
216 1.1 jtc *wp++ = CHAR, *wp++ = c;
217 1.2 tls if ((c2 = getsc()) == c)
218 1.1 jtc *wp++ = CHAR, *wp++ = c;
219 1.1 jtc else
220 1.1 jtc ungetsc(c2);
221 1.1 jtc *wp++ = OPAT, *wp++ = '@';
222 1.1 jtc *++statep = state = STBRACE;
223 1.1 jtc } else {
224 1.1 jtc ungetsc(c);
225 1.1 jtc *++statep = state = SBRACE;
226 1.1 jtc }
227 1.1 jtc } else if (ctype(c, C_ALPHA)) {
228 1.1 jtc *wp++ = OSUBST;
229 1.1 jtc do {
230 1.1 jtc Xcheck(ws, wp);
231 1.1 jtc *wp++ = c;
232 1.1 jtc c = getsc();
233 1.1 jtc } while (ctype(c, C_ALPHA|C_DIGIT));
234 1.1 jtc *wp++ = '\0';
235 1.1 jtc *wp++ = CSUBST;
236 1.1 jtc ungetsc(c);
237 1.1 jtc } else if (ctype(c, C_DIGIT|C_VAR1)) {
238 1.1 jtc Xcheck(ws, wp);
239 1.1 jtc *wp++ = OSUBST;
240 1.1 jtc *wp++ = c;
241 1.1 jtc *wp++ = '\0';
242 1.1 jtc *wp++ = CSUBST;
243 1.1 jtc } else {
244 1.1 jtc *wp++ = CHAR, *wp++ = '$';
245 1.1 jtc ungetsc(c);
246 1.1 jtc }
247 1.1 jtc break;
248 1.1 jtc case '`':
249 1.1 jtc *++statep = state = SBQUOTE;
250 1.1 jtc *wp++ = COMSUB;
251 1.1 jtc /* Need to know if we are inside double quotes
252 1.1 jtc * since sh/at&t-ksh translate the \" to " in
253 1.1 jtc * "`..\"..`".
254 1.1 jtc */
255 1.1 jtc indquotes = 0;
256 1.1 jtc if (!Flag(FPOSIX))
257 1.1 jtc for (sp = statep; sp > states; --sp)
258 1.1 jtc if (*sp == SDQUOTE)
259 1.1 jtc indquotes = 1;
260 1.1 jtc break;
261 1.1 jtc default:
262 1.1 jtc *wp++ = CHAR, *wp++ = c;
263 1.1 jtc }
264 1.1 jtc break;
265 1.1 jtc
266 1.1 jtc case SSQUOTE:
267 1.1 jtc if (c == '\'') {
268 1.1 jtc state = *--statep;
269 1.1 jtc *wp++ = CQUOTE;
270 1.2 tls ignore_backslash_newline--;
271 1.1 jtc } else
272 1.1 jtc *wp++ = QCHAR, *wp++ = c;
273 1.1 jtc break;
274 1.1 jtc
275 1.1 jtc case SDQUOTE:
276 1.1 jtc if (c == '"') {
277 1.1 jtc state = *--statep;
278 1.1 jtc *wp++ = CQUOTE;
279 1.1 jtc } else
280 1.1 jtc goto Subst;
281 1.1 jtc break;
282 1.1 jtc
283 1.1 jtc case SPAREN: /* $( .. ) */
284 1.1 jtc /* todo: deal with $(...) quoting properly
285 1.1 jtc * kludge to partly fake quoting inside $(..): doesn't
286 1.1 jtc * really work because nested $(..) or ${..} inside
287 1.1 jtc * double quotes aren't dealt with.
288 1.1 jtc */
289 1.1 jtc switch (csstate) {
290 1.1 jtc case 0: /* normal */
291 1.1 jtc switch (c) {
292 1.1 jtc case '(':
293 1.1 jtc nparen++;
294 1.1 jtc break;
295 1.1 jtc case ')':
296 1.1 jtc nparen--;
297 1.1 jtc break;
298 1.1 jtc case '\\':
299 1.1 jtc csstate = 1;
300 1.1 jtc break;
301 1.1 jtc case '"':
302 1.1 jtc csstate = 2;
303 1.1 jtc break;
304 1.1 jtc case '\'':
305 1.1 jtc csstate = 4;
306 1.2 tls ignore_backslash_newline++;
307 1.1 jtc break;
308 1.1 jtc }
309 1.1 jtc break;
310 1.1 jtc
311 1.1 jtc case 1: /* backslash in normal mode */
312 1.1 jtc case 3: /* backslash in double quotes */
313 1.1 jtc --csstate;
314 1.1 jtc break;
315 1.1 jtc
316 1.1 jtc case 2: /* double quotes */
317 1.1 jtc if (c == '"')
318 1.1 jtc csstate = 0;
319 1.1 jtc else if (c == '\\')
320 1.1 jtc csstate = 3;
321 1.1 jtc break;
322 1.1 jtc
323 1.1 jtc case 4: /* single quotes */
324 1.2 tls if (c == '\'') {
325 1.1 jtc csstate = 0;
326 1.2 tls ignore_backslash_newline--;
327 1.2 tls }
328 1.1 jtc break;
329 1.1 jtc }
330 1.1 jtc if (nparen == 0) {
331 1.1 jtc state = *--statep;
332 1.1 jtc *wp++ = 0; /* end of COMSUB */
333 1.1 jtc } else
334 1.1 jtc *wp++ = c;
335 1.1 jtc break;
336 1.1 jtc
337 1.1 jtc case SDDPAREN: /* $(( .. )) */
338 1.1 jtc /* todo: deal with $((...); (...)) properly */
339 1.1 jtc /* XXX should nest using existing state machine
340 1.1 jtc * (embed "..", $(...), etc.) */
341 1.1 jtc if (c == '(')
342 1.1 jtc nparen++;
343 1.1 jtc else if (c == ')') {
344 1.1 jtc nparen--;
345 1.1 jtc if (nparen == 1) {
346 1.1 jtc /*(*/
347 1.1 jtc if ((c2 = getsc()) == ')') {
348 1.1 jtc state = *--statep;
349 1.1 jtc *wp++ = 0; /* end of EXPRSUB */
350 1.1 jtc break;
351 1.1 jtc } else {
352 1.1 jtc ungetsc(c2);
353 1.1 jtc /* mismatched parenthesis -
354 1.1 jtc * assume we were really
355 1.1 jtc * parsing a $(..) expression
356 1.1 jtc */
357 1.1 jtc memmove(ddparen_start + 1,
358 1.1 jtc ddparen_start,
359 1.1 jtc wp - ddparen_start);
360 1.1 jtc *ddparen_start++ = COMSUB;
361 1.1 jtc *ddparen_start = '('; /*)*/
362 1.1 jtc wp++;
363 1.1 jtc csstate = 0;
364 1.1 jtc *statep = state = SPAREN;
365 1.1 jtc }
366 1.1 jtc }
367 1.1 jtc }
368 1.1 jtc *wp++ = c;
369 1.1 jtc break;
370 1.1 jtc
371 1.1 jtc case SBRACE:
372 1.1 jtc /*{*/
373 1.1 jtc if (c == '}') {
374 1.1 jtc state = *--statep;
375 1.1 jtc *wp++ = CSUBST;
376 1.1 jtc } else
377 1.1 jtc goto Sbase1;
378 1.1 jtc break;
379 1.1 jtc
380 1.1 jtc case STBRACE:
381 1.1 jtc /* same as SBRACE, except | is saved as SPAT and
382 1.1 jtc * CPAT is added at the end.
383 1.1 jtc */
384 1.1 jtc /*{*/
385 1.1 jtc if (c == '}') {
386 1.1 jtc state = *--statep;
387 1.1 jtc *wp++ = CPAT;
388 1.1 jtc *wp++ = CSUBST;
389 1.1 jtc } else if (c == '|') {
390 1.1 jtc *wp++ = SPAT;
391 1.1 jtc } else
392 1.1 jtc goto Sbase1;
393 1.1 jtc break;
394 1.1 jtc
395 1.1 jtc case SBQUOTE:
396 1.1 jtc if (c == '`') {
397 1.1 jtc *wp++ = 0;
398 1.1 jtc state = *--statep;
399 1.1 jtc } else if (c == '\\') {
400 1.1 jtc switch (c = getsc()) {
401 1.1 jtc case '\\':
402 1.1 jtc case '$': case '`':
403 1.1 jtc *wp++ = c;
404 1.1 jtc break;
405 1.1 jtc case '"':
406 1.1 jtc if (indquotes) {
407 1.1 jtc *wp++ = c;
408 1.1 jtc break;
409 1.1 jtc }
410 1.1 jtc /* fall through.. */
411 1.1 jtc default:
412 1.2 tls if (c) { /* trailing \ is lost */
413 1.2 tls *wp++ = '\\';
414 1.2 tls *wp++ = c;
415 1.2 tls }
416 1.1 jtc break;
417 1.1 jtc }
418 1.1 jtc } else
419 1.1 jtc *wp++ = c;
420 1.1 jtc break;
421 1.1 jtc
422 1.1 jtc case SWORD: /* ONEWORD */
423 1.1 jtc goto Subst;
424 1.1 jtc
425 1.2 tls #ifdef KSH
426 1.1 jtc case SDPAREN: /* LETEXPR: (( ... )) */
427 1.1 jtc /*(*/
428 1.1 jtc if (c == ')') {
429 1.1 jtc if (ndparen > 0)
430 1.1 jtc --ndparen;
431 1.1 jtc /*(*/
432 1.1 jtc else if ((c2 = getsc()) == ')') {
433 1.1 jtc c = 0;
434 1.1 jtc *wp++ = CQUOTE;
435 1.1 jtc goto Done;
436 1.1 jtc } else
437 1.1 jtc ungetsc(c2);
438 1.1 jtc } else if (c == '(')
439 1.1 jtc /* parenthesis inside quotes and backslashes
440 1.1 jtc * are lost, but at&t ksh doesn't count them
441 1.1 jtc * either
442 1.1 jtc */
443 1.1 jtc ++ndparen;
444 1.1 jtc goto Sbase2;
445 1.2 tls #endif /* KSH */
446 1.1 jtc
447 1.1 jtc case SHEREDELIM: /* <<,<<- delimiter */
448 1.1 jtc /* XXX chuck this state (and the next) - use
449 1.1 jtc * the existing states ($ and \`..` should be
450 1.1 jtc * stripped of their specialness after the
451 1.1 jtc * fact).
452 1.1 jtc */
453 1.1 jtc /* here delimiters need a special case since
454 1.1 jtc * $ and `..` are not to be treated specially
455 1.1 jtc */
456 1.1 jtc if (c == '\\') {
457 1.1 jtc c = getsc();
458 1.2 tls if (c) { /* trailing \ is lost */
459 1.1 jtc *wp++ = QCHAR;
460 1.1 jtc *wp++ = c;
461 1.1 jtc }
462 1.1 jtc } else if (c == '\'') {
463 1.1 jtc *++statep = state = SSQUOTE;
464 1.1 jtc *wp++ = OQUOTE;
465 1.2 tls ignore_backslash_newline++;
466 1.1 jtc } else if (c == '"') {
467 1.1 jtc state = SHEREDQUOTE;
468 1.1 jtc *wp++ = OQUOTE;
469 1.1 jtc } else {
470 1.1 jtc *wp++ = CHAR;
471 1.1 jtc *wp++ = c;
472 1.1 jtc }
473 1.1 jtc break;
474 1.1 jtc
475 1.1 jtc case SHEREDQUOTE: /* " in <<,<<- delimiter */
476 1.1 jtc if (c == '"') {
477 1.1 jtc *wp++ = CQUOTE;
478 1.1 jtc state = SHEREDELIM;
479 1.1 jtc } else {
480 1.2 tls if (c == '\\') {
481 1.2 tls switch (c = getsc()) {
482 1.2 tls case '\\': case '"':
483 1.2 tls case '$': case '`':
484 1.2 tls break;
485 1.2 tls default:
486 1.2 tls if (c) { /* trailing \ lost */
487 1.2 tls *wp++ = CHAR;
488 1.2 tls *wp++ = '\\';
489 1.2 tls }
490 1.2 tls break;
491 1.2 tls }
492 1.2 tls }
493 1.1 jtc *wp++ = CHAR;
494 1.1 jtc *wp++ = c;
495 1.1 jtc }
496 1.1 jtc break;
497 1.1 jtc
498 1.1 jtc case SPATTERN: /* in *(...|...) pattern (*+?@!) */
499 1.1 jtc if ( /*(*/ c == ')') {
500 1.1 jtc *wp++ = CPAT;
501 1.1 jtc state = *--statep;
502 1.1 jtc } else if (c == '|')
503 1.1 jtc *wp++ = SPAT;
504 1.1 jtc else
505 1.1 jtc goto Sbase1;
506 1.1 jtc break;
507 1.1 jtc }
508 1.1 jtc }
509 1.1 jtc Done:
510 1.1 jtc Xcheck(ws, wp);
511 1.1 jtc if (state != istate)
512 1.1 jtc yyerror("no closing quote\n");
513 1.1 jtc
514 1.1 jtc /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
515 1.1 jtc if (state == SHEREDELIM)
516 1.1 jtc state = SBASE;
517 1.1 jtc
518 1.1 jtc if ((c == '<' || c == '>') && state == SBASE) {
519 1.1 jtc char *cp = Xstring(ws, wp);
520 1.1 jtc if (Xlength(ws, wp) == 2 && cp[0] == CHAR && digit(cp[1])) {
521 1.1 jtc wp = cp; /* throw away word */
522 1.1 jtc c2/*unit*/ = cp[1] - '0';
523 1.1 jtc } else
524 1.1 jtc c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
525 1.1 jtc }
526 1.1 jtc
527 1.1 jtc if (wp == Xstring(ws, wp) && state == SBASE) {
528 1.1 jtc Xfree(ws, wp); /* free word */
529 1.1 jtc /* no word, process LEX1 character */
530 1.1 jtc switch (c) {
531 1.1 jtc default:
532 1.1 jtc return c;
533 1.1 jtc
534 1.1 jtc case '|':
535 1.1 jtc case '&':
536 1.1 jtc case ';':
537 1.1 jtc if ((c2 = getsc()) == c)
538 1.1 jtc c = (c == ';') ? BREAK :
539 1.1 jtc (c == '|') ? LOGOR :
540 1.1 jtc (c == '&') ? LOGAND :
541 1.1 jtc YYERRCODE;
542 1.1 jtc #ifdef KSH
543 1.1 jtc else if (c == '|' && c2 == '&')
544 1.1 jtc c = COPROC;
545 1.1 jtc #endif /* KSH */
546 1.1 jtc else
547 1.1 jtc ungetsc(c2);
548 1.1 jtc return c;
549 1.1 jtc
550 1.1 jtc case '>':
551 1.1 jtc case '<': {
552 1.1 jtc register struct ioword *iop;
553 1.1 jtc
554 1.1 jtc iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
555 1.1 jtc iop->unit = c2/*unit*/;
556 1.1 jtc
557 1.1 jtc c2 = getsc();
558 1.1 jtc /* <<, >>, <> are ok, >< is not */
559 1.1 jtc if (c == c2 || (c == '<' && c2 == '>')) {
560 1.1 jtc iop->flag = c == c2 ?
561 1.1 jtc (c == '>' ? IOCAT : IOHERE) : IORDWR;
562 1.4 thorpej if (iop->flag == IOHERE) {
563 1.2 tls if ((c2 = getsc()) == '-')
564 1.1 jtc iop->flag |= IOSKIP;
565 1.1 jtc else
566 1.1 jtc ungetsc(c2);
567 1.4 thorpej }
568 1.1 jtc } else if (c2 == '&')
569 1.1 jtc iop->flag = IODUP | (c == '<' ? IORDUP : 0);
570 1.1 jtc else {
571 1.1 jtc iop->flag = c == '>' ? IOWRITE : IOREAD;
572 1.1 jtc if (c == '>' && c2 == '|')
573 1.1 jtc iop->flag |= IOCLOB;
574 1.1 jtc else
575 1.1 jtc ungetsc(c2);
576 1.1 jtc }
577 1.1 jtc
578 1.1 jtc iop->name = (char *) 0;
579 1.1 jtc iop->delim = (char *) 0;
580 1.1 jtc yylval.iop = iop;
581 1.1 jtc return REDIR;
582 1.1 jtc }
583 1.1 jtc case '\n':
584 1.1 jtc gethere();
585 1.1 jtc if (cf & CONTIN)
586 1.1 jtc goto Again;
587 1.1 jtc return c;
588 1.1 jtc
589 1.1 jtc case '(': /*)*/
590 1.2 tls #ifdef KSH
591 1.1 jtc if ((c2 = getsc()) == '(') /*)*/
592 1.1 jtc c = MDPAREN;
593 1.1 jtc else
594 1.1 jtc ungetsc(c2);
595 1.2 tls #endif /* KSH */
596 1.1 jtc return c;
597 1.1 jtc /*(*/
598 1.1 jtc case ')':
599 1.1 jtc return c;
600 1.1 jtc }
601 1.1 jtc }
602 1.1 jtc
603 1.1 jtc *wp++ = EOS; /* terminate word */
604 1.1 jtc yylval.cp = Xclose(ws, wp);
605 1.2 tls if (state == SWORD
606 1.2 tls #ifdef KSH
607 1.2 tls || state == SDPAREN
608 1.2 tls #endif /* KSH */
609 1.2 tls ) /* ONEWORD? */
610 1.1 jtc return LWORD;
611 1.1 jtc ungetsc(c); /* unget terminator */
612 1.1 jtc
613 1.1 jtc /* copy word to unprefixed string ident */
614 1.1 jtc for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
615 1.1 jtc *dp++ = *sp++;
616 1.1 jtc /* Make sure the ident array stays '\0' paded */
617 1.1 jtc memset(dp, 0, (ident+IDENT) - dp + 1);
618 1.1 jtc if (c != EOS)
619 1.1 jtc *ident = '\0'; /* word is not unquoted */
620 1.1 jtc
621 1.1 jtc if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
622 1.1 jtc struct tbl *p;
623 1.1 jtc int h = hash(ident);
624 1.1 jtc
625 1.1 jtc /* { */
626 1.1 jtc if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h))
627 1.1 jtc && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}'))
628 1.1 jtc {
629 1.1 jtc afree(yylval.cp, ATEMP);
630 1.1 jtc return p->val.i;
631 1.1 jtc }
632 1.1 jtc if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h))
633 1.1 jtc && (p->flag & ISSET))
634 1.1 jtc {
635 1.1 jtc register Source *s;
636 1.1 jtc
637 1.1 jtc for (s = source; s->type == SALIAS; s = s->next)
638 1.1 jtc if (s->u.tblp == p)
639 1.1 jtc return LWORD;
640 1.1 jtc /* push alias expansion */
641 1.1 jtc s = pushs(SALIAS, source->areap);
642 1.1 jtc s->start = s->str = p->val.s;
643 1.1 jtc s->u.tblp = p;
644 1.1 jtc s->next = source;
645 1.1 jtc source = s;
646 1.1 jtc afree(yylval.cp, ATEMP);
647 1.1 jtc goto Again;
648 1.1 jtc }
649 1.1 jtc }
650 1.1 jtc
651 1.1 jtc return LWORD;
652 1.1 jtc }
653 1.1 jtc
654 1.1 jtc static void
655 1.1 jtc gethere()
656 1.1 jtc {
657 1.1 jtc register struct ioword **p;
658 1.1 jtc
659 1.1 jtc for (p = heres; p < herep; p++)
660 1.1 jtc readhere(*p);
661 1.1 jtc herep = heres;
662 1.1 jtc }
663 1.1 jtc
664 1.1 jtc /*
665 1.1 jtc * read "<<word" text into temp file
666 1.1 jtc */
667 1.1 jtc
668 1.1 jtc static void
669 1.1 jtc readhere(iop)
670 1.1 jtc register struct ioword *iop;
671 1.1 jtc {
672 1.1 jtc struct shf *volatile shf;
673 1.1 jtc struct temp *h;
674 1.1 jtc register int c;
675 1.1 jtc char *volatile eof;
676 1.1 jtc char *eofp;
677 1.2 tls int skiptabs;
678 1.1 jtc int i;
679 1.1 jtc
680 1.1 jtc eof = evalstr(iop->delim, 0);
681 1.1 jtc
682 1.1 jtc if (e->flags & EF_FUNC_PARSE) {
683 1.1 jtc h = maketemp(APERM);
684 1.1 jtc h->next = func_heredocs;
685 1.1 jtc func_heredocs = h;
686 1.1 jtc } else {
687 1.1 jtc h = maketemp(ATEMP);
688 1.1 jtc h->next = e->temps;
689 1.1 jtc e->temps = h;
690 1.1 jtc }
691 1.1 jtc iop->name = h->name;
692 1.1 jtc if (!(shf = h->shf))
693 1.1 jtc yyerror("cannot create temporary file %s - %s\n",
694 1.1 jtc h->name, strerror(errno));
695 1.1 jtc
696 1.1 jtc newenv(E_ERRH);
697 1.1 jtc i = ksh_sigsetjmp(e->jbuf, 0);
698 1.1 jtc if (i) {
699 1.1 jtc quitenv();
700 1.1 jtc shf_close(shf);
701 1.1 jtc unwind(i);
702 1.1 jtc }
703 1.1 jtc
704 1.2 tls if (!(iop->flag & IOEVAL))
705 1.2 tls ignore_backslash_newline++;
706 1.2 tls
707 1.1 jtc for (;;) {
708 1.1 jtc eofp = eof;
709 1.1 jtc skiptabs = iop->flag & IOSKIP;
710 1.2 tls while ((c = getsc()) != 0) {
711 1.1 jtc if (skiptabs) {
712 1.1 jtc if (c == '\t')
713 1.1 jtc continue;
714 1.1 jtc skiptabs = 0;
715 1.1 jtc }
716 1.1 jtc if (c != *eofp)
717 1.1 jtc break;
718 1.1 jtc eofp++;
719 1.1 jtc }
720 1.1 jtc /* Allow EOF here so commands with out trailing newlines
721 1.1 jtc * will work (eg, ksh -c '...', $(...), etc).
722 1.1 jtc */
723 1.1 jtc if (*eofp == '\0' && (c == 0 || c == '\n'))
724 1.1 jtc break;
725 1.1 jtc ungetsc(c);
726 1.1 jtc shf_write(eof, eofp - eof, shf);
727 1.2 tls while ((c = getsc()) != '\n') {
728 1.1 jtc if (c == 0)
729 1.1 jtc yyerror("here document `%s' unclosed\n", eof);
730 1.1 jtc shf_putc(c, shf);
731 1.1 jtc }
732 1.1 jtc shf_putc(c, shf);
733 1.1 jtc }
734 1.1 jtc shf_flush(shf);
735 1.1 jtc if (shf_error(shf))
736 1.1 jtc yyerror("error saving here document `%s': %s\n",
737 1.1 jtc eof, strerror(shf_errno(shf)));
738 1.1 jtc /*XXX add similar checks for write errors everywhere */
739 1.1 jtc quitenv();
740 1.1 jtc shf_close(shf);
741 1.2 tls if (!(iop->flag & IOEVAL))
742 1.2 tls ignore_backslash_newline--;
743 1.1 jtc }
744 1.1 jtc
745 1.1 jtc void
746 1.1 jtc #ifdef HAVE_PROTOTYPES
747 1.1 jtc yyerror(const char *fmt, ...)
748 1.1 jtc #else
749 1.1 jtc yyerror(fmt, va_alist)
750 1.1 jtc const char *fmt;
751 1.1 jtc va_dcl
752 1.1 jtc #endif
753 1.1 jtc {
754 1.1 jtc va_list va;
755 1.1 jtc
756 1.1 jtc yynerrs++;
757 1.1 jtc /* pop aliases and re-reads */
758 1.1 jtc while (source->type == SALIAS || source->type == SREREAD)
759 1.1 jtc source = source->next;
760 1.1 jtc source->str = null; /* zap pending input */
761 1.1 jtc
762 1.1 jtc error_prefix(TRUE);
763 1.1 jtc SH_VA_START(va, fmt);
764 1.1 jtc shf_vfprintf(shl_out, fmt, va);
765 1.1 jtc va_end(va);
766 1.1 jtc errorf(null);
767 1.1 jtc }
768 1.1 jtc
769 1.1 jtc /*
770 1.1 jtc * input for yylex with alias expansion
771 1.1 jtc */
772 1.1 jtc
773 1.1 jtc Source *
774 1.1 jtc pushs(type, areap)
775 1.1 jtc int type;
776 1.1 jtc Area *areap;
777 1.1 jtc {
778 1.1 jtc register Source *s;
779 1.1 jtc
780 1.1 jtc s = (Source *) alloc(sizeof(Source), areap);
781 1.1 jtc s->type = type;
782 1.1 jtc s->str = null;
783 1.1 jtc s->start = NULL;
784 1.1 jtc s->line = 0;
785 1.1 jtc s->errline = 0;
786 1.1 jtc s->file = NULL;
787 1.1 jtc s->flags = 0;
788 1.1 jtc s->next = NULL;
789 1.1 jtc s->areap = areap;
790 1.1 jtc if (type == SFILE || type == SSTDIN) {
791 1.1 jtc char *dummy;
792 1.1 jtc Xinit(s->xs, dummy, 256, s->areap);
793 1.1 jtc } else
794 1.1 jtc memset(&s->xs, 0, sizeof(s->xs));
795 1.1 jtc return s;
796 1.1 jtc }
797 1.1 jtc
798 1.1 jtc static int
799 1.2 tls getsc__()
800 1.1 jtc {
801 1.1 jtc register Source *s = source;
802 1.1 jtc register int c;
803 1.1 jtc
804 1.1 jtc while ((c = *s->str++) == 0) {
805 1.1 jtc s->str = NULL; /* return 0 for EOF by default */
806 1.1 jtc switch (s->type) {
807 1.1 jtc case SEOF:
808 1.1 jtc s->str = null;
809 1.1 jtc return 0;
810 1.1 jtc
811 1.1 jtc case SSTDIN:
812 1.1 jtc case SFILE:
813 1.1 jtc getsc_line(s);
814 1.1 jtc break;
815 1.1 jtc
816 1.1 jtc case SWSTR:
817 1.1 jtc break;
818 1.1 jtc
819 1.1 jtc case SSTRING:
820 1.1 jtc break;
821 1.1 jtc
822 1.1 jtc case SWORDS:
823 1.1 jtc s->start = s->str = *s->u.strv++;
824 1.1 jtc s->type = SWORDSEP;
825 1.1 jtc break;
826 1.1 jtc
827 1.1 jtc case SWORDSEP:
828 1.1 jtc if (*s->u.strv == NULL) {
829 1.1 jtc s->start = s->str = newline;
830 1.1 jtc s->type = SEOF;
831 1.1 jtc } else {
832 1.1 jtc s->start = s->str = space;
833 1.1 jtc s->type = SWORDS;
834 1.1 jtc }
835 1.1 jtc break;
836 1.1 jtc
837 1.1 jtc case SALIAS:
838 1.1 jtc if (s->flags & SF_ALIASEND) {
839 1.1 jtc /* pass on an unused SF_ALIAS flag */
840 1.1 jtc source = s->next;
841 1.1 jtc source->flags |= s->flags & SF_ALIAS;
842 1.1 jtc s = source;
843 1.1 jtc } else if (*s->u.tblp->val.s
844 1.1 jtc && isspace(strchr(s->u.tblp->val.s, 0)[-1]))
845 1.1 jtc {
846 1.1 jtc source = s = s->next; /* pop source stack */
847 1.2 tls /* Note that this alias ended with a space,
848 1.2 tls * enabling alias expansion on the following
849 1.2 tls * word.
850 1.2 tls */
851 1.1 jtc s->flags |= SF_ALIAS;
852 1.1 jtc } else {
853 1.2 tls /* At this point, we need to keep the current
854 1.2 tls * alias in the source list so recursive
855 1.2 tls * aliases can be detected and we also need
856 1.2 tls * to return the next character. Do this
857 1.2 tls * by temporarily popping the alias to get
858 1.2 tls * the next character and then put it back
859 1.2 tls * in the source list with the SF_ALIASEND
860 1.2 tls * flag set.
861 1.1 jtc */
862 1.2 tls source = s->next; /* pop source stack */
863 1.2 tls source->flags |= s->flags & SF_ALIAS;
864 1.2 tls c = getsc__();
865 1.2 tls if (c) {
866 1.2 tls s->flags |= SF_ALIASEND;
867 1.2 tls s->ugbuf[0] = c; s->ugbuf[1] = '\0';
868 1.2 tls s->start = s->str = s->ugbuf;
869 1.2 tls s->next = source;
870 1.2 tls source = s;
871 1.2 tls } else {
872 1.2 tls s = source;
873 1.2 tls /* avoid reading eof twice */
874 1.2 tls s->str = NULL;
875 1.2 tls break;
876 1.2 tls }
877 1.1 jtc }
878 1.1 jtc continue;
879 1.1 jtc
880 1.1 jtc case SREREAD:
881 1.2 tls if (s->start != s->ugbuf) /* yuck */
882 1.1 jtc afree(s->u.freeme, ATEMP);
883 1.1 jtc source = s = s->next;
884 1.1 jtc continue;
885 1.1 jtc }
886 1.1 jtc if (s->str == NULL) {
887 1.1 jtc s->type = SEOF;
888 1.1 jtc s->start = s->str = null;
889 1.1 jtc return '\0';
890 1.1 jtc }
891 1.1 jtc if (s->flags & SF_ECHO) {
892 1.1 jtc shf_puts(s->str, shl_out);
893 1.1 jtc shf_flush(shl_out);
894 1.1 jtc }
895 1.1 jtc }
896 1.1 jtc return c;
897 1.1 jtc }
898 1.1 jtc
899 1.1 jtc static void
900 1.1 jtc getsc_line(s)
901 1.1 jtc Source *s;
902 1.1 jtc {
903 1.1 jtc char *xp = Xstring(s->xs, xp);
904 1.1 jtc int interactive = Flag(FTALKING) && s->type == SSTDIN;
905 1.1 jtc int have_tty = interactive && (s->flags & SF_TTY);
906 1.1 jtc
907 1.1 jtc /* Done here to ensure nothing odd happens when a timeout occurs */
908 1.1 jtc XcheckN(s->xs, xp, LINE);
909 1.1 jtc *xp = '\0';
910 1.1 jtc s->start = s->str = xp;
911 1.1 jtc
912 1.1 jtc #ifdef KSH
913 1.1 jtc if (have_tty && ksh_tmout) {
914 1.1 jtc ksh_tmout_state = TMOUT_READING;
915 1.1 jtc alarm(ksh_tmout);
916 1.1 jtc }
917 1.1 jtc #endif /* KSH */
918 1.1 jtc #ifdef EDIT
919 1.1 jtc if (have_tty && (0
920 1.1 jtc # ifdef VI
921 1.1 jtc || Flag(FVI)
922 1.1 jtc # endif /* VI */
923 1.1 jtc # ifdef EMACS
924 1.1 jtc || Flag(FEMACS) || Flag(FGMACS)
925 1.1 jtc # endif /* EMACS */
926 1.1 jtc ))
927 1.1 jtc {
928 1.1 jtc int nread;
929 1.1 jtc
930 1.1 jtc nread = x_read(xp, LINE);
931 1.1 jtc if (nread < 0) /* read error */
932 1.1 jtc nread = 0;
933 1.1 jtc xp[nread] = '\0';
934 1.1 jtc xp += nread;
935 1.1 jtc }
936 1.1 jtc else
937 1.1 jtc #endif /* EDIT */
938 1.1 jtc {
939 1.1 jtc if (interactive) {
940 1.1 jtc pprompt(prompt, 0);
941 1.1 jtc #ifdef OS2
942 1.1 jtc setmode (0, O_TEXT);
943 1.1 jtc #endif /* OS2 */
944 1.1 jtc } else
945 1.1 jtc s->line++;
946 1.1 jtc
947 1.1 jtc while (1) {
948 1.1 jtc char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
949 1.1 jtc
950 1.1 jtc if (!p && shf_error(s->u.shf)
951 1.1 jtc && shf_errno(s->u.shf) == EINTR)
952 1.1 jtc {
953 1.1 jtc shf_clearerr(s->u.shf);
954 1.1 jtc if (trap)
955 1.1 jtc runtraps(0);
956 1.1 jtc continue;
957 1.1 jtc }
958 1.1 jtc if (!p || (xp = p, xp[-1] == '\n'))
959 1.1 jtc break;
960 1.1 jtc /* double buffer size */
961 1.1 jtc xp++; /* move past null so doubling works... */
962 1.1 jtc XcheckN(s->xs, xp, Xlength(s->xs, xp));
963 1.1 jtc xp--; /* ...and move back again */
964 1.1 jtc }
965 1.1 jtc #ifdef OS2
966 1.1 jtc setmode(0, O_BINARY);
967 1.1 jtc #endif /* OS2 */
968 1.1 jtc /* flush any unwanted input so other programs/builtins
969 1.1 jtc * can read it. Not very optimal, but less error prone
970 1.1 jtc * than flushing else where, dealing with redirections,
971 1.1 jtc * etc..
972 1.1 jtc * todo: reduce size of shf buffer (~128?) if SSTDIN
973 1.1 jtc */
974 1.1 jtc if (s->type == SSTDIN)
975 1.1 jtc shf_flush(s->u.shf);
976 1.1 jtc }
977 1.1 jtc /* XXX: temporary kludge to restore source after a
978 1.1 jtc * trap may have been executed.
979 1.1 jtc */
980 1.1 jtc source = s;
981 1.1 jtc #ifdef KSH
982 1.1 jtc if (have_tty && ksh_tmout)
983 1.1 jtc {
984 1.1 jtc ksh_tmout_state = TMOUT_EXECUTING;
985 1.1 jtc alarm(0);
986 1.1 jtc }
987 1.1 jtc #endif /* KSH */
988 1.1 jtc s->start = s->str = Xstring(s->xs, xp);
989 1.1 jtc strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
990 1.1 jtc /* Note: if input is all nulls, this is not eof */
991 1.1 jtc if (Xlength(s->xs, xp) == 0) { /* EOF */
992 1.1 jtc if (s->type == SFILE)
993 1.1 jtc shf_fdclose(s->u.shf);
994 1.1 jtc s->str = NULL;
995 1.1 jtc } else if (interactive) {
996 1.1 jtc #ifdef HISTORY
997 1.1 jtc char *p = Xstring(s->xs, xp);
998 1.1 jtc if (cur_prompt == PS1)
999 1.1 jtc while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
1000 1.1 jtc p++;
1001 1.1 jtc if (*p) {
1002 1.1 jtc # ifdef EASY_HISTORY
1003 1.1 jtc if (cur_prompt == PS2)
1004 1.1 jtc histappend(Xstring(s->xs, xp), 1);
1005 1.1 jtc else
1006 1.1 jtc # endif /* EASY_HISTORY */
1007 1.1 jtc {
1008 1.1 jtc s->line++;
1009 1.1 jtc histsave(s->line, s->str, 1);
1010 1.1 jtc }
1011 1.1 jtc }
1012 1.1 jtc #endif /* HISTORY */
1013 1.1 jtc }
1014 1.1 jtc if (interactive)
1015 1.1 jtc set_prompt(PS2, (Source *) 0);
1016 1.1 jtc }
1017 1.1 jtc
1018 1.1 jtc void
1019 1.1 jtc set_prompt(to, s)
1020 1.1 jtc int to;
1021 1.1 jtc Source *s;
1022 1.1 jtc {
1023 1.1 jtc cur_prompt = to;
1024 1.1 jtc
1025 1.1 jtc switch (to) {
1026 1.1 jtc case PS1: /* command */
1027 1.1 jtc #ifdef KSH
1028 1.1 jtc /* Substitute ! and !! here, before substitutions are done
1029 1.1 jtc * so ! in expanded variables are not expanded.
1030 1.1 jtc * NOTE: this is not what at&t ksh does (it does it after
1031 1.1 jtc * substitutions, POSIX doesn't say which is to be done.
1032 1.1 jtc */
1033 1.1 jtc {
1034 1.1 jtc struct shf *shf;
1035 1.1 jtc char *ps1;
1036 1.1 jtc Area *saved_atemp;
1037 1.3 christos #ifdef __GNUC__
1038 1.3 christos (void) &ps1;
1039 1.3 christos #endif
1040 1.1 jtc
1041 1.1 jtc ps1 = str_val(global("PS1"));
1042 1.1 jtc shf = shf_sopen((char *) 0, strlen(ps1) * 2,
1043 1.1 jtc SHF_WR | SHF_DYNAMIC, (struct shf *) 0);
1044 1.1 jtc while (*ps1) {
1045 1.1 jtc if (*ps1 != '!' || *++ps1 == '!')
1046 1.1 jtc shf_putchar(*ps1++, shf);
1047 1.1 jtc else
1048 1.1 jtc shf_fprintf(shf, "%d",
1049 1.1 jtc s ? s->line + 1 : 0);
1050 1.1 jtc }
1051 1.1 jtc ps1 = shf_sclose(shf);
1052 1.1 jtc saved_atemp = ATEMP;
1053 1.1 jtc newenv(E_ERRH);
1054 1.1 jtc if (ksh_sigsetjmp(e->jbuf, 0)) {
1055 1.1 jtc prompt = safe_prompt;
1056 1.1 jtc /* Don't print an error - assume it has already
1057 1.1 jtc * been printed. Reason is we may have forked
1058 1.1 jtc * to run a command and the child may be
1059 1.1 jtc * unwinding its stack through this code as it
1060 1.1 jtc * exits.
1061 1.1 jtc */
1062 1.1 jtc } else
1063 1.1 jtc prompt = str_save(substitute(ps1, 0),
1064 1.1 jtc saved_atemp);
1065 1.1 jtc quitenv();
1066 1.1 jtc }
1067 1.1 jtc #else /* KSH */
1068 1.1 jtc prompt = str_val(global("PS1"));
1069 1.1 jtc #endif /* KSH */
1070 1.1 jtc break;
1071 1.1 jtc
1072 1.1 jtc case PS2: /* command continuation */
1073 1.1 jtc prompt = str_val(global("PS2"));
1074 1.1 jtc break;
1075 1.1 jtc }
1076 1.1 jtc }
1077 1.1 jtc
1078 1.1 jtc /* See also related routine, promptlen() in edit.c */
1079 1.1 jtc void
1080 1.1 jtc pprompt(cp, ntruncate)
1081 1.1 jtc const char *cp;
1082 1.1 jtc int ntruncate;
1083 1.1 jtc {
1084 1.1 jtc #if 0
1085 1.1 jtc char nbuf[32];
1086 1.1 jtc int c;
1087 1.1 jtc
1088 1.1 jtc while (*cp != 0) {
1089 1.1 jtc if (*cp != '!')
1090 1.1 jtc c = *cp++;
1091 1.1 jtc else if (*++cp == '!')
1092 1.1 jtc c = *cp++;
1093 1.1 jtc else {
1094 1.1 jtc int len;
1095 1.1 jtc char *p;
1096 1.1 jtc
1097 1.1 jtc shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
1098 1.1 jtc source->line + 1);
1099 1.1 jtc len = strlen(nbuf);
1100 1.1 jtc if (ntruncate) {
1101 1.1 jtc if (ntruncate >= len) {
1102 1.1 jtc ntruncate -= len;
1103 1.1 jtc continue;
1104 1.1 jtc }
1105 1.1 jtc p += ntruncate;
1106 1.1 jtc len -= ntruncate;
1107 1.1 jtc ntruncate = 0;
1108 1.1 jtc }
1109 1.1 jtc shf_write(p, len, shl_out);
1110 1.1 jtc continue;
1111 1.1 jtc }
1112 1.1 jtc if (ntruncate)
1113 1.1 jtc --ntruncate;
1114 1.1 jtc else
1115 1.1 jtc shf_putc(c, shl_out);
1116 1.1 jtc }
1117 1.1 jtc #endif /* 0 */
1118 1.2 tls shf_puts(cp + ntruncate, shl_out);
1119 1.2 tls shf_flush(shl_out);
1120 1.1 jtc }
1121 1.1 jtc
1122 1.1 jtc /* Read the variable part of a ${...} expression (ie, up to but not including
1123 1.1 jtc * the :[-+?=#%] or close-brace.
1124 1.1 jtc */
1125 1.1 jtc static char *
1126 1.1 jtc get_brace_var(wsp, wp)
1127 1.1 jtc XString *wsp;
1128 1.1 jtc char *wp;
1129 1.1 jtc {
1130 1.1 jtc enum parse_state {
1131 1.1 jtc PS_INITIAL, PS_SAW_HASH, PS_IDENT,
1132 1.1 jtc PS_NUMBER, PS_VAR1, PS_END
1133 1.1 jtc }
1134 1.1 jtc state;
1135 1.1 jtc char c;
1136 1.1 jtc
1137 1.1 jtc state = PS_INITIAL;
1138 1.1 jtc while (1) {
1139 1.2 tls c = getsc();
1140 1.1 jtc /* State machine to figure out where the variable part ends. */
1141 1.1 jtc switch (state) {
1142 1.1 jtc case PS_INITIAL:
1143 1.1 jtc if (c == '#') {
1144 1.1 jtc state = PS_SAW_HASH;
1145 1.1 jtc break;
1146 1.1 jtc }
1147 1.1 jtc /* fall through.. */
1148 1.1 jtc case PS_SAW_HASH:
1149 1.1 jtc if (letter(c))
1150 1.1 jtc state = PS_IDENT;
1151 1.1 jtc else if (digit(c))
1152 1.1 jtc state = PS_NUMBER;
1153 1.1 jtc else if (ctype(c, C_VAR1))
1154 1.1 jtc state = PS_VAR1;
1155 1.1 jtc else
1156 1.1 jtc state = PS_END;
1157 1.1 jtc break;
1158 1.1 jtc case PS_IDENT:
1159 1.1 jtc if (!letnum(c)) {
1160 1.1 jtc state = PS_END;
1161 1.1 jtc if (c == '[') {
1162 1.1 jtc char *tmp, *p;
1163 1.1 jtc
1164 1.1 jtc if (!arraysub(&tmp))
1165 1.1 jtc yyerror("missing ]\n");
1166 1.1 jtc *wp++ = c;
1167 1.1 jtc for (p = tmp; *p; ) {
1168 1.1 jtc Xcheck(*wsp, wp);
1169 1.1 jtc *wp++ = *p++;
1170 1.1 jtc }
1171 1.1 jtc afree(tmp, ATEMP);
1172 1.2 tls c = getsc(); /* the ] */
1173 1.1 jtc }
1174 1.1 jtc }
1175 1.1 jtc break;
1176 1.1 jtc case PS_NUMBER:
1177 1.1 jtc if (!digit(c))
1178 1.1 jtc state = PS_END;
1179 1.1 jtc break;
1180 1.1 jtc case PS_VAR1:
1181 1.1 jtc state = PS_END;
1182 1.1 jtc break;
1183 1.1 jtc case PS_END: /* keep gcc happy */
1184 1.1 jtc break;
1185 1.1 jtc }
1186 1.1 jtc if (state == PS_END) {
1187 1.1 jtc *wp++ = '\0'; /* end of variable part */
1188 1.1 jtc ungetsc(c);
1189 1.1 jtc break;
1190 1.1 jtc }
1191 1.1 jtc Xcheck(*wsp, wp);
1192 1.1 jtc *wp++ = c;
1193 1.1 jtc }
1194 1.1 jtc return wp;
1195 1.1 jtc }
1196 1.1 jtc
1197 1.1 jtc /*
1198 1.1 jtc * Save an array subscript - returns true if matching bracket found, false
1199 1.1 jtc * if eof or newline was found.
1200 1.1 jtc * (Returned string double null terminated)
1201 1.1 jtc */
1202 1.1 jtc static int
1203 1.1 jtc arraysub(strp)
1204 1.1 jtc char **strp;
1205 1.1 jtc {
1206 1.1 jtc XString ws;
1207 1.1 jtc char *wp;
1208 1.1 jtc char c;
1209 1.1 jtc int depth = 1; /* we are just past the initial [ */
1210 1.1 jtc
1211 1.1 jtc Xinit(ws, wp, 32, ATEMP);
1212 1.1 jtc
1213 1.1 jtc do {
1214 1.2 tls c = getsc();
1215 1.1 jtc Xcheck(ws, wp);
1216 1.1 jtc *wp++ = c;
1217 1.1 jtc if (c == '[')
1218 1.1 jtc depth++;
1219 1.1 jtc else if (c == ']')
1220 1.1 jtc depth--;
1221 1.1 jtc } while (depth > 0 && c && c != '\n');
1222 1.1 jtc
1223 1.1 jtc *wp++ = '\0';
1224 1.1 jtc *strp = Xclose(ws, wp);
1225 1.1 jtc
1226 1.1 jtc return depth == 0 ? 1 : 0;
1227 1.1 jtc }
1228 1.1 jtc
1229 1.1 jtc /* Unget a char: handles case when we are already at the start of the buffer */
1230 1.1 jtc static const char *
1231 1.2 tls ungetsc(c)
1232 1.1 jtc int c;
1233 1.1 jtc {
1234 1.2 tls if (backslash_skip)
1235 1.2 tls backslash_skip--;
1236 1.1 jtc /* Don't unget eof... */
1237 1.1 jtc if (source->str == null && c == '\0')
1238 1.1 jtc return source->str;
1239 1.1 jtc if (source->str > source->start)
1240 1.1 jtc source->str--;
1241 1.1 jtc else {
1242 1.1 jtc Source *s;
1243 1.1 jtc
1244 1.1 jtc s = pushs(SREREAD, source->areap);
1245 1.2 tls s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1246 1.2 tls s->start = s->str = s->ugbuf;
1247 1.1 jtc s->next = source;
1248 1.1 jtc source = s;
1249 1.1 jtc }
1250 1.1 jtc return source->str;
1251 1.1 jtc }
1252 1.1 jtc
1253 1.2 tls
1254 1.1 jtc /* Called to get a char that isn't a \newline sequence. */
1255 1.1 jtc static int
1256 1.2 tls getsc_bn ARGS((void))
1257 1.1 jtc {
1258 1.2 tls int c, c2;
1259 1.2 tls
1260 1.2 tls if (ignore_backslash_newline)
1261 1.2 tls return getsc_();
1262 1.2 tls
1263 1.2 tls if (backslash_skip == 1) {
1264 1.2 tls backslash_skip = 2;
1265 1.2 tls return getsc_();
1266 1.2 tls }
1267 1.2 tls
1268 1.2 tls backslash_skip = 0;
1269 1.1 jtc
1270 1.1 jtc while (1) {
1271 1.1 jtc c = getsc_();
1272 1.2 tls if (c == '\\') {
1273 1.2 tls if ((c2 = getsc_()) == '\n')
1274 1.2 tls /* ignore the \newline; get the next char... */
1275 1.2 tls continue;
1276 1.2 tls ungetsc(c2);
1277 1.2 tls backslash_skip = 1;
1278 1.1 jtc }
1279 1.2 tls return c;
1280 1.1 jtc }
1281 1.1 jtc }
1282