xref: /openbsd/bin/ksh/lex.c (revision 404b540a)
1 /*	$OpenBSD: lex.c,v 1.44 2008/07/03 17:52:08 otto Exp $	*/
2 
3 /*
4  * lexical analysis and source input
5  */
6 
7 #include "sh.h"
8 #include <libgen.h>
9 #include <ctype.h>
10 
11 
12 /* Structure to keep track of the lexing state and the various pieces of info
13  * needed for each particular state.
14  */
15 typedef struct lex_state Lex_state;
16 struct lex_state {
17 	int ls_state;
18 	union {
19 		/* $(...) */
20 		struct scsparen_info {
21 			int nparen;	/* count open parenthesis */
22 			int csstate;	/* XXX remove */
23 #define ls_scsparen ls_info.u_scsparen
24 		} u_scsparen;
25 
26 		/* $((...)) */
27 		struct sasparen_info {
28 			int nparen;	/* count open parenthesis */
29 			int start;	/* marks start of $(( in output str */
30 #define ls_sasparen ls_info.u_sasparen
31 		} u_sasparen;
32 
33 		/* ((...)) */
34 		struct sletparen_info {
35 			int nparen;	/* count open parenthesis */
36 #define ls_sletparen ls_info.u_sletparen
37 		} u_sletparen;
38 
39 		/* `...` */
40 		struct sbquote_info {
41 			int indquotes;	/* true if in double quotes: "`...`" */
42 #define ls_sbquote ls_info.u_sbquote
43 		} u_sbquote;
44 
45 		Lex_state *base;	/* used to point to next state block */
46 	} ls_info;
47 };
48 
49 typedef struct State_info State_info;
50 struct State_info {
51 	Lex_state	*base;
52 	Lex_state	*end;
53 };
54 
55 
56 static void	readhere(struct ioword *);
57 static int	getsc__(void);
58 static void	getsc_line(Source *);
59 static int	getsc_bn(void);
60 static char	*get_brace_var(XString *, char *);
61 static int	arraysub(char **);
62 static const char *ungetsc(int);
63 static void	gethere(void);
64 static Lex_state *push_state_(State_info *, Lex_state *);
65 static Lex_state *pop_state_(State_info *, Lex_state *);
66 static char	*special_prompt_expand(char *);
67 static int	dopprompt(const char *, int, const char **, int);
68 
69 static int backslash_skip;
70 static int ignore_backslash_newline;
71 
72 /* optimized getsc_bn() */
73 #define getsc()		(*source->str != '\0' && *source->str != '\\' \
74 			 && !backslash_skip ? *source->str++ : getsc_bn())
75 /* optimized getsc__() */
76 #define	getsc_()	((*source->str != '\0') ? *source->str++ : getsc__())
77 
78 #define STATE_BSIZE	32
79 
80 #define PUSH_STATE(s)	do { \
81 			    if (++statep == state_info.end) \
82 				statep = push_state_(&state_info, statep); \
83 			    state = statep->ls_state = (s); \
84 			} while (0)
85 
86 #define POP_STATE()	do { \
87 			    if (--statep == state_info.base) \
88 				statep = pop_state_(&state_info, statep); \
89 			    state = statep->ls_state; \
90 			} while (0)
91 
92 
93 
94 /*
95  * Lexical analyzer
96  *
97  * tokens are not regular expressions, they are LL(1).
98  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
99  * hence the state stack.
100  */
101 
102 int
103 yylex(int cf)
104 {
105 	Lex_state states[STATE_BSIZE], *statep;
106 	State_info state_info;
107 	int c, state;
108 	XString ws;		/* expandable output word */
109 	char *wp;		/* output word pointer */
110 	char *sp, *dp;
111 	int c2;
112 
113 
114   Again:
115 	states[0].ls_state = -1;
116 	states[0].ls_info.base = (Lex_state *) 0;
117 	statep = &states[1];
118 	state_info.base = states;
119 	state_info.end = &states[STATE_BSIZE];
120 
121 	Xinit(ws, wp, 64, ATEMP);
122 
123 	backslash_skip = 0;
124 	ignore_backslash_newline = 0;
125 
126 	if (cf&ONEWORD)
127 		state = SWORD;
128 	else if (cf&LETEXPR) {
129 		*wp++ = OQUOTE;	 /* enclose arguments in (double) quotes */
130 		state = SLETPAREN;
131 		statep->ls_sletparen.nparen = 0;
132 	} else {		/* normal lexing */
133 		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
134 		while ((c = getsc()) == ' ' || c == '\t')
135 			;
136 		if (c == '#') {
137 			ignore_backslash_newline++;
138 			while ((c = getsc()) != '\0' && c != '\n')
139 				;
140 			ignore_backslash_newline--;
141 		}
142 		ungetsc(c);
143 	}
144 	if (source->flags & SF_ALIAS) {	/* trailing ' ' in alias definition */
145 		source->flags &= ~SF_ALIAS;
146 		/* In POSIX mode, a trailing space only counts if we are
147 		 * parsing a simple command
148 		 */
149 		if (!Flag(FPOSIX) || (cf & CMDWORD))
150 			cf |= ALIAS;
151 	}
152 
153 	/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
154 	statep->ls_state = state;
155 
156 	/* collect non-special or quoted characters to form word */
157 	while (!((c = getsc()) == 0 ||
158 	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
159 		Xcheck(ws, wp);
160 		switch (state) {
161 		case SBASE:
162 			if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) &&
163 			    c == '!') {
164 				char **replace = NULL;
165 
166 				c2 = getsc();
167 				if (c2 == '\0' || c2 == ' ' || c2 == '\t')
168 					;
169 				else if (c2 == '!')
170 					replace = hist_get_newest(0);
171 				else if (isdigit(c2) || c2 == '-' ||
172 				    isalpha(c2)) {
173 					int get = !isalpha(c2);
174 					char match[200], *str = match;
175 
176 					*str++ = c2;
177 					do {
178 						if ((c2 = getsc()) == '\0')
179 							break;
180 						if (c2 == '\t' || c2 == ' ' ||
181 						    c2 == '\n') {
182 							ungetsc(c2);
183 							break;
184 						}
185 						*str++ = c2;
186 					} while (str < &match[sizeof(match)-1]);
187 					*str = '\0';
188 
189 					if (get) {
190 						int h = findhistrel(match);
191 						if (h >= 0)
192 							replace = &history[h];
193 					} else {
194 						int h = findhist(-1, 0, match, true);
195 						if (h >= 0)
196 							replace = &history[h];
197 					}
198 				}
199 
200 				/*
201 				 * XXX ksh history buffer saves un-expanded
202 				 * commands. Until the history buffer code is
203 				 * changed to contain expanded commands, we
204 				 * ignore the bad commands (spinning sucks)
205 				 */
206 				if (replace && **replace == '!')
207 					ungetsc(c2);
208 				else if (replace) {
209 					Source *s;
210 
211 					/* do not strdup replacement via alloc */
212 					s = pushs(SREREAD, source->areap);
213 					s->start = s->str = *replace;
214 					s->next = source;
215 					s->u.freeme = NULL;
216 					source = s;
217 					continue;
218 				} else
219 					ungetsc(c2);
220 			}
221 			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
222 				*wp = EOS; /* temporary */
223 				if (is_wdvarname(Xstring(ws, wp), false)) {
224 					char *p, *tmp;
225 
226 					if (arraysub(&tmp)) {
227 						*wp++ = CHAR;
228 						*wp++ = c;
229 						for (p = tmp; *p; ) {
230 							Xcheck(ws, wp);
231 							*wp++ = CHAR;
232 							*wp++ = *p++;
233 						}
234 						afree(tmp, ATEMP);
235 						break;
236 					} else {
237 						Source *s;
238 
239 						s = pushs(SREREAD,
240 							  source->areap);
241 						s->start = s->str
242 							= s->u.freeme = tmp;
243 						s->next = source;
244 						source = s;
245 					}
246 				}
247 				*wp++ = CHAR;
248 				*wp++ = c;
249 				break;
250 			}
251 			/* FALLTHROUGH */
252 		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */
253 			if (c == '*' || c == '@' || c == '+' || c == '?' ||
254 			    c == '!') {
255 				c2 = getsc();
256 				if (c2 == '(' /*)*/ ) {
257 					*wp++ = OPAT;
258 					*wp++ = c;
259 					PUSH_STATE(SPATTERN);
260 					break;
261 				}
262 				ungetsc(c2);
263 			}
264 			/* FALLTHROUGH */
265 		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */
266 			switch (c) {
267 			case '\\':
268 				c = getsc();
269 				if (c) /* trailing \ is lost */
270 					*wp++ = QCHAR, *wp++ = c;
271 				break;
272 			case '\'':
273 				*wp++ = OQUOTE;
274 				ignore_backslash_newline++;
275 				PUSH_STATE(SSQUOTE);
276 				break;
277 			case '"':
278 				*wp++ = OQUOTE;
279 				PUSH_STATE(SDQUOTE);
280 				break;
281 			default:
282 				goto Subst;
283 			}
284 			break;
285 
286 		  Subst:
287 			switch (c) {
288 			case '\\':
289 				c = getsc();
290 				switch (c) {
291 				case '\\':
292 				case '$': case '`':
293 					*wp++ = QCHAR, *wp++ = c;
294 					break;
295 				case '"':
296 					if ((cf & HEREDOC) == 0) {
297 						*wp++ = QCHAR, *wp++ = c;
298 						break;
299 					}
300 					/* FALLTHROUGH */
301 				default:
302 					Xcheck(ws, wp);
303 					if (c) { /* trailing \ is lost */
304 						*wp++ = CHAR, *wp++ = '\\';
305 						*wp++ = CHAR, *wp++ = c;
306 					}
307 					break;
308 				}
309 				break;
310 			case '$':
311 				c = getsc();
312 				if (c == '(') /*)*/ {
313 					c = getsc();
314 					if (c == '(') /*)*/ {
315 						PUSH_STATE(SASPAREN);
316 						statep->ls_sasparen.nparen = 2;
317 						statep->ls_sasparen.start =
318 						    Xsavepos(ws, wp);
319 						*wp++ = EXPRSUB;
320 					} else {
321 						ungetsc(c);
322 						PUSH_STATE(SCSPAREN);
323 						statep->ls_scsparen.nparen = 1;
324 						statep->ls_scsparen.csstate = 0;
325 						*wp++ = COMSUB;
326 					}
327 				} else if (c == '{') /*}*/ {
328 					*wp++ = OSUBST;
329 					*wp++ = '{'; /*}*/
330 					wp = get_brace_var(&ws, wp);
331 					c = getsc();
332 					/* allow :# and :% (ksh88 compat) */
333 					if (c == ':') {
334 						*wp++ = CHAR, *wp++ = c;
335 						c = getsc();
336 					}
337 					/* If this is a trim operation,
338 					 * treat (,|,) specially in STBRACE.
339 					 */
340 					if (c == '#' || c == '%') {
341 						ungetsc(c);
342 						PUSH_STATE(STBRACE);
343 					} else {
344 						ungetsc(c);
345 						PUSH_STATE(SBRACE);
346 					}
347 				} else if (ctype(c, C_ALPHA)) {
348 					*wp++ = OSUBST;
349 					*wp++ = 'X';
350 					do {
351 						Xcheck(ws, wp);
352 						*wp++ = c;
353 						c = getsc();
354 					} while (ctype(c, C_ALPHA|C_DIGIT));
355 					*wp++ = '\0';
356 					*wp++ = CSUBST;
357 					*wp++ = 'X';
358 					ungetsc(c);
359 				} else if (ctype(c, C_DIGIT|C_VAR1)) {
360 					Xcheck(ws, wp);
361 					*wp++ = OSUBST;
362 					*wp++ = 'X';
363 					*wp++ = c;
364 					*wp++ = '\0';
365 					*wp++ = CSUBST;
366 					*wp++ = 'X';
367 				} else {
368 					*wp++ = CHAR, *wp++ = '$';
369 					ungetsc(c);
370 				}
371 				break;
372 			case '`':
373 				PUSH_STATE(SBQUOTE);
374 				*wp++ = COMSUB;
375 				/* Need to know if we are inside double quotes
376 				 * since sh/at&t-ksh translate the \" to " in
377 				 * "`..\"..`".
378 				 * This is not done in posix mode (section
379 				 * 3.2.3, Double Quotes: "The backquote shall
380 				 * retain its special meaning introducing the
381 				 * other form of command substitution (see
382 				 * 3.6.3). The portion of the quoted string
383 				 * from the initial backquote and the
384 				 * characters up to the next backquote that
385 				 * is not preceded by a backslash (having
386 				 * escape characters removed) defines that
387 				 * command whose output replaces `...` when
388 				 * the word is expanded."
389 				 * Section 3.6.3, Command Substitution:
390 				 * "Within the backquoted style of command
391 				 * substitution, backslash shall retain its
392 				 * literal meaning, except when followed by
393 				 * $ ` \.").
394 				 */
395 				statep->ls_sbquote.indquotes = 0;
396 				if (!Flag(FPOSIX)) {
397 					Lex_state *s = statep;
398 					Lex_state *base = state_info.base;
399 					while (1) {
400 						for (; s != base; s--) {
401 							if (s->ls_state == SDQUOTE) {
402 								statep->ls_sbquote.indquotes = 1;
403 								break;
404 							}
405 						}
406 						if (s != base)
407 							break;
408 						if (!(s = s->ls_info.base))
409 							break;
410 						base = s-- - STATE_BSIZE;
411 					}
412 				}
413 				break;
414 			default:
415 				*wp++ = CHAR, *wp++ = c;
416 			}
417 			break;
418 
419 		case SSQUOTE:
420 			if (c == '\'') {
421 				POP_STATE();
422 				*wp++ = CQUOTE;
423 				ignore_backslash_newline--;
424 			} else
425 				*wp++ = QCHAR, *wp++ = c;
426 			break;
427 
428 		case SDQUOTE:
429 			if (c == '"') {
430 				POP_STATE();
431 				*wp++ = CQUOTE;
432 			} else
433 				goto Subst;
434 			break;
435 
436 		case SCSPAREN: /* $( .. ) */
437 			/* todo: deal with $(...) quoting properly
438 			 * kludge to partly fake quoting inside $(..): doesn't
439 			 * really work because nested $(..) or ${..} inside
440 			 * double quotes aren't dealt with.
441 			 */
442 			switch (statep->ls_scsparen.csstate) {
443 			case 0: /* normal */
444 				switch (c) {
445 				case '(':
446 					statep->ls_scsparen.nparen++;
447 					break;
448 				case ')':
449 					statep->ls_scsparen.nparen--;
450 					break;
451 				case '\\':
452 					statep->ls_scsparen.csstate = 1;
453 					break;
454 				case '"':
455 					statep->ls_scsparen.csstate = 2;
456 					break;
457 				case '\'':
458 					statep->ls_scsparen.csstate = 4;
459 					ignore_backslash_newline++;
460 					break;
461 				}
462 				break;
463 
464 			case 1: /* backslash in normal mode */
465 			case 3: /* backslash in double quotes */
466 				--statep->ls_scsparen.csstate;
467 				break;
468 
469 			case 2: /* double quotes */
470 				if (c == '"')
471 					statep->ls_scsparen.csstate = 0;
472 				else if (c == '\\')
473 					statep->ls_scsparen.csstate = 3;
474 				break;
475 
476 			case 4: /* single quotes */
477 				if (c == '\'') {
478 					statep->ls_scsparen.csstate = 0;
479 					ignore_backslash_newline--;
480 				}
481 				break;
482 			}
483 			if (statep->ls_scsparen.nparen == 0) {
484 				POP_STATE();
485 				*wp++ = 0; /* end of COMSUB */
486 			} else
487 				*wp++ = c;
488 			break;
489 
490 		case SASPAREN: /* $(( .. )) */
491 			/* todo: deal with $((...); (...)) properly */
492 			/* XXX should nest using existing state machine
493 			 * (embed "..", $(...), etc.) */
494 			if (c == '(')
495 				statep->ls_sasparen.nparen++;
496 			else if (c == ')') {
497 				statep->ls_sasparen.nparen--;
498 				if (statep->ls_sasparen.nparen == 1) {
499 					/*(*/
500 					if ((c2 = getsc()) == ')') {
501 						POP_STATE();
502 						*wp++ = 0; /* end of EXPRSUB */
503 						break;
504 					} else {
505 						char *s;
506 
507 						ungetsc(c2);
508 						/* mismatched parenthesis -
509 						 * assume we were really
510 						 * parsing a $(..) expression
511 						 */
512 						s = Xrestpos(ws, wp,
513 						    statep->ls_sasparen.start);
514 						memmove(s + 1, s, wp - s);
515 						*s++ = COMSUB;
516 						*s = '('; /*)*/
517 						wp++;
518 						statep->ls_scsparen.nparen = 1;
519 						statep->ls_scsparen.csstate = 0;
520 						state = statep->ls_state =
521 						    SCSPAREN;
522 					}
523 				}
524 			}
525 			*wp++ = c;
526 			break;
527 
528 		case SBRACE:
529 			/*{*/
530 			if (c == '}') {
531 				POP_STATE();
532 				*wp++ = CSUBST;
533 				*wp++ = /*{*/ '}';
534 			} else
535 				goto Sbase1;
536 			break;
537 
538 		case STBRACE:
539 			/* Same as SBRACE, except (,|,) treated specially */
540 			/*{*/
541 			if (c == '}') {
542 				POP_STATE();
543 				*wp++ = CSUBST;
544 				*wp++ = /*{*/ '}';
545 			} else if (c == '|') {
546 				*wp++ = SPAT;
547 			} else if (c == '(') {
548 				*wp++ = OPAT;
549 				*wp++ = ' ';	/* simile for @ */
550 				PUSH_STATE(SPATTERN);
551 			} else
552 				goto Sbase1;
553 			break;
554 
555 		case SBQUOTE:
556 			if (c == '`') {
557 				*wp++ = 0;
558 				POP_STATE();
559 			} else if (c == '\\') {
560 				switch (c = getsc()) {
561 				case '\\':
562 				case '$': case '`':
563 					*wp++ = c;
564 					break;
565 				case '"':
566 					if (statep->ls_sbquote.indquotes) {
567 						*wp++ = c;
568 						break;
569 					}
570 					/* FALLTHROUGH */
571 				default:
572 					if (c) { /* trailing \ is lost */
573 						*wp++ = '\\';
574 						*wp++ = c;
575 					}
576 					break;
577 				}
578 			} else
579 				*wp++ = c;
580 			break;
581 
582 		case SWORD:	/* ONEWORD */
583 			goto Subst;
584 
585 		case SLETPAREN:	/* LETEXPR: (( ... )) */
586 			/*(*/
587 			if (c == ')') {
588 				if (statep->ls_sletparen.nparen > 0)
589 				    --statep->ls_sletparen.nparen;
590 				/*(*/
591 				else if ((c2 = getsc()) == ')') {
592 					c = 0;
593 					*wp++ = CQUOTE;
594 					goto Done;
595 				} else
596 					ungetsc(c2);
597 			} else if (c == '(')
598 				/* parenthesis inside quotes and backslashes
599 				 * are lost, but at&t ksh doesn't count them
600 				 * either
601 				 */
602 				++statep->ls_sletparen.nparen;
603 			goto Sbase2;
604 
605 		case SHEREDELIM:	/* <<,<<- delimiter */
606 			/* XXX chuck this state (and the next) - use
607 			 * the existing states ($ and \`..` should be
608 			 * stripped of their specialness after the
609 			 * fact).
610 			 */
611 			/* here delimiters need a special case since
612 			 * $ and `..` are not to be treated specially
613 			 */
614 			if (c == '\\') {
615 				c = getsc();
616 				if (c) { /* trailing \ is lost */
617 					*wp++ = QCHAR;
618 					*wp++ = c;
619 				}
620 			} else if (c == '\'') {
621 				PUSH_STATE(SSQUOTE);
622 				*wp++ = OQUOTE;
623 				ignore_backslash_newline++;
624 			} else if (c == '"') {
625 				state = statep->ls_state = SHEREDQUOTE;
626 				*wp++ = OQUOTE;
627 			} else {
628 				*wp++ = CHAR;
629 				*wp++ = c;
630 			}
631 			break;
632 
633 		case SHEREDQUOTE:	/* " in <<,<<- delimiter */
634 			if (c == '"') {
635 				*wp++ = CQUOTE;
636 				state = statep->ls_state = SHEREDELIM;
637 			} else {
638 				if (c == '\\') {
639 					switch (c = getsc()) {
640 					case '\\': case '"':
641 					case '$': case '`':
642 						break;
643 					default:
644 						if (c) { /* trailing \ lost */
645 							*wp++ = CHAR;
646 							*wp++ = '\\';
647 						}
648 						break;
649 					}
650 				}
651 				*wp++ = CHAR;
652 				*wp++ = c;
653 			}
654 			break;
655 
656 		case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
657 			if ( /*(*/ c == ')') {
658 				*wp++ = CPAT;
659 				POP_STATE();
660 			} else if (c == '|') {
661 				*wp++ = SPAT;
662 			} else if (c == '(') {
663 				*wp++ = OPAT;
664 				*wp++ = ' ';	/* simile for @ */
665 				PUSH_STATE(SPATTERN);
666 			} else
667 				goto Sbase1;
668 			break;
669 		}
670 	}
671 Done:
672 	Xcheck(ws, wp);
673 	if (statep != &states[1])
674 		/* XXX figure out what is missing */
675 		yyerror("no closing quote\n");
676 
677 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
678 	if (state == SHEREDELIM)
679 		state = SBASE;
680 
681 	dp = Xstring(ws, wp);
682 	if ((c == '<' || c == '>') && state == SBASE &&
683 	    ((c2 = Xlength(ws, wp)) == 0 ||
684 	    (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
685 		struct ioword *iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
686 
687 		if (c2 == 2)
688 			iop->unit = dp[1] - '0';
689 		else
690 			iop->unit = c == '>'; /* 0 for <, 1 for > */
691 
692 		c2 = getsc();
693 		/* <<, >>, <> are ok, >< is not */
694 		if (c == c2 || (c == '<' && c2 == '>')) {
695 			iop->flag = c == c2 ?
696 			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
697 			if (iop->flag == IOHERE) {
698 				if ((c2 = getsc()) == '-')
699 					iop->flag |= IOSKIP;
700 				else
701 					ungetsc(c2);
702 			}
703 		} else if (c2 == '&')
704 			iop->flag = IODUP | (c == '<' ? IORDUP : 0);
705 		else {
706 			iop->flag = c == '>' ? IOWRITE : IOREAD;
707 			if (c == '>' && c2 == '|')
708 				iop->flag |= IOCLOB;
709 			else
710 				ungetsc(c2);
711 		}
712 
713 		iop->name = (char *) 0;
714 		iop->delim = (char *) 0;
715 		iop->heredoc = (char *) 0;
716 		Xfree(ws, wp);	/* free word */
717 		yylval.iop = iop;
718 		return REDIR;
719 	}
720 
721 	if (wp == dp && state == SBASE) {
722 		Xfree(ws, wp);	/* free word */
723 		/* no word, process LEX1 character */
724 		switch (c) {
725 		default:
726 			return c;
727 
728 		case '|':
729 		case '&':
730 		case ';':
731 			if ((c2 = getsc()) == c)
732 				c = (c == ';') ? BREAK :
733 				    (c == '|') ? LOGOR :
734 				    (c == '&') ? LOGAND :
735 				    YYERRCODE;
736 			else if (c == '|' && c2 == '&')
737 				c = COPROC;
738 			else
739 				ungetsc(c2);
740 			return c;
741 
742 		case '\n':
743 			gethere();
744 			if (cf & CONTIN)
745 				goto Again;
746 			return c;
747 
748 		case '(':  /*)*/
749 			if (!Flag(FSH)) {
750 				if ((c2 = getsc()) == '(') /*)*/
751 					/* XXX need to handle ((...); (...)) */
752 					c = MDPAREN;
753 				else
754 					ungetsc(c2);
755 			}
756 			return c;
757 		  /*(*/
758 		case ')':
759 			return c;
760 		}
761 	}
762 
763 	*wp++ = EOS;		/* terminate word */
764 	yylval.cp = Xclose(ws, wp);
765 	if (state == SWORD || state == SLETPAREN)	/* ONEWORD? */
766 		return LWORD;
767 	ungetsc(c);		/* unget terminator */
768 
769 	/* copy word to unprefixed string ident */
770 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
771 		*dp++ = *sp++;
772 	/* Make sure the ident array stays '\0' padded */
773 	memset(dp, 0, (ident+IDENT) - dp + 1);
774 	if (c != EOS)
775 		*ident = '\0';	/* word is not unquoted */
776 
777 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
778 		struct tbl *p;
779 		int h = hash(ident);
780 
781 		/* { */
782 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
783 		    (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
784 			afree(yylval.cp, ATEMP);
785 			return p->val.i;
786 		}
787 		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
788 		    (p->flag & ISSET)) {
789 			Source *s;
790 
791 			for (s = source; s->type == SALIAS; s = s->next)
792 				if (s->u.tblp == p)
793 					return LWORD;
794 			/* push alias expansion */
795 			s = pushs(SALIAS, source->areap);
796 			s->start = s->str = p->val.s;
797 			s->u.tblp = p;
798 			s->next = source;
799 			source = s;
800 			afree(yylval.cp, ATEMP);
801 			goto Again;
802 		}
803 	}
804 
805 	return LWORD;
806 }
807 
808 static void
809 gethere(void)
810 {
811 	struct ioword **p;
812 
813 	for (p = heres; p < herep; p++)
814 		readhere(*p);
815 	herep = heres;
816 }
817 
818 /*
819  * read "<<word" text into temp file
820  */
821 
822 static void
823 readhere(struct ioword *iop)
824 {
825 	int c;
826 	char *volatile eof;
827 	char *eofp;
828 	int skiptabs;
829 	XString xs;
830 	char *xp;
831 	int xpos;
832 
833 	eof = evalstr(iop->delim, 0);
834 
835 	if (!(iop->flag & IOEVAL))
836 		ignore_backslash_newline++;
837 
838 	Xinit(xs, xp, 256, ATEMP);
839 
840 	for (;;) {
841 		eofp = eof;
842 		skiptabs = iop->flag & IOSKIP;
843 		xpos = Xsavepos(xs, xp);
844 		while ((c = getsc()) != 0) {
845 			if (skiptabs) {
846 				if (c == '\t')
847 					continue;
848 				skiptabs = 0;
849 			}
850 			if (c != *eofp)
851 				break;
852 			Xcheck(xs, xp);
853 			Xput(xs, xp, c);
854 			eofp++;
855 		}
856 		/* Allow EOF here so commands with out trailing newlines
857 		 * will work (eg, ksh -c '...', $(...), etc).
858 		 */
859 		if (*eofp == '\0' && (c == 0 || c == '\n')) {
860 			xp = Xrestpos(xs, xp, xpos);
861 			break;
862 		}
863 		ungetsc(c);
864 		while ((c = getsc()) != '\n') {
865 			if (c == 0)
866 				yyerror("here document `%s' unclosed\n", eof);
867 			Xcheck(xs, xp);
868 			Xput(xs, xp, c);
869 		}
870 		Xcheck(xs, xp);
871 		Xput(xs, xp, c);
872 	}
873 	Xput(xs, xp, '\0');
874 	iop->heredoc = Xclose(xs, xp);
875 
876 	if (!(iop->flag & IOEVAL))
877 		ignore_backslash_newline--;
878 }
879 
880 void
881 yyerror(const char *fmt, ...)
882 {
883 	va_list va;
884 
885 	/* pop aliases and re-reads */
886 	while (source->type == SALIAS || source->type == SREREAD)
887 		source = source->next;
888 	source->str = null;	/* zap pending input */
889 
890 	error_prefix(true);
891 	va_start(va, fmt);
892 	shf_vfprintf(shl_out, fmt, va);
893 	va_end(va);
894 	errorf(null);
895 }
896 
897 /*
898  * input for yylex with alias expansion
899  */
900 
901 Source *
902 pushs(int type, Area *areap)
903 {
904 	Source *s;
905 
906 	s = (Source *) alloc(sizeof(Source), areap);
907 	s->type = type;
908 	s->str = null;
909 	s->start = NULL;
910 	s->line = 0;
911 	s->cmd_offset = 0;
912 	s->errline = 0;
913 	s->file = NULL;
914 	s->flags = 0;
915 	s->next = NULL;
916 	s->areap = areap;
917 	if (type == SFILE || type == SSTDIN) {
918 		char *dummy;
919 		Xinit(s->xs, dummy, 256, s->areap);
920 	} else
921 		memset(&s->xs, 0, sizeof(s->xs));
922 	return s;
923 }
924 
925 static int
926 getsc__(void)
927 {
928 	Source *s = source;
929 	int c;
930 
931 	while ((c = *s->str++) == 0) {
932 		s->str = NULL;		/* return 0 for EOF by default */
933 		switch (s->type) {
934 		case SEOF:
935 			s->str = null;
936 			return 0;
937 
938 		case SSTDIN:
939 		case SFILE:
940 			getsc_line(s);
941 			break;
942 
943 		case SWSTR:
944 			break;
945 
946 		case SSTRING:
947 			break;
948 
949 		case SWORDS:
950 			s->start = s->str = *s->u.strv++;
951 			s->type = SWORDSEP;
952 			break;
953 
954 		case SWORDSEP:
955 			if (*s->u.strv == NULL) {
956 				s->start = s->str = newline;
957 				s->type = SEOF;
958 			} else {
959 				s->start = s->str = space;
960 				s->type = SWORDS;
961 			}
962 			break;
963 
964 		case SALIAS:
965 			if (s->flags & SF_ALIASEND) {
966 				/* pass on an unused SF_ALIAS flag */
967 				source = s->next;
968 				source->flags |= s->flags & SF_ALIAS;
969 				s = source;
970 			} else if (*s->u.tblp->val.s &&
971 			    isspace(strchr(s->u.tblp->val.s, 0)[-1])) {
972 				source = s = s->next;	/* pop source stack */
973 				/* Note that this alias ended with a space,
974 				 * enabling alias expansion on the following
975 				 * word.
976 				 */
977 				s->flags |= SF_ALIAS;
978 			} else {
979 				/* At this point, we need to keep the current
980 				 * alias in the source list so recursive
981 				 * aliases can be detected and we also need
982 				 * to return the next character.  Do this
983 				 * by temporarily popping the alias to get
984 				 * the next character and then put it back
985 				 * in the source list with the SF_ALIASEND
986 				 * flag set.
987 				 */
988 				source = s->next;	/* pop source stack */
989 				source->flags |= s->flags & SF_ALIAS;
990 				c = getsc__();
991 				if (c) {
992 					s->flags |= SF_ALIASEND;
993 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
994 					s->start = s->str = s->ugbuf;
995 					s->next = source;
996 					source = s;
997 				} else {
998 					s = source;
999 					/* avoid reading eof twice */
1000 					s->str = NULL;
1001 					break;
1002 				}
1003 			}
1004 			continue;
1005 
1006 		case SREREAD:
1007 			if (s->start != s->ugbuf) /* yuck */
1008 				afree(s->u.freeme, ATEMP);
1009 			source = s = s->next;
1010 			continue;
1011 		}
1012 		if (s->str == NULL) {
1013 			s->type = SEOF;
1014 			s->start = s->str = null;
1015 			return '\0';
1016 		}
1017 		if (s->flags & SF_ECHO) {
1018 			shf_puts(s->str, shl_out);
1019 			shf_flush(shl_out);
1020 		}
1021 	}
1022 	return c;
1023 }
1024 
1025 static void
1026 getsc_line(Source *s)
1027 {
1028 	char *xp = Xstring(s->xs, xp);
1029 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
1030 	int have_tty = interactive && (s->flags & SF_TTY);
1031 
1032 	/* Done here to ensure nothing odd happens when a timeout occurs */
1033 	XcheckN(s->xs, xp, LINE);
1034 	*xp = '\0';
1035 	s->start = s->str = xp;
1036 
1037 	if (have_tty && ksh_tmout) {
1038 		ksh_tmout_state = TMOUT_READING;
1039 		alarm(ksh_tmout);
1040 	}
1041 #ifdef EDIT
1042 	if (have_tty && (0
1043 # ifdef VI
1044 	    || Flag(FVI)
1045 # endif /* VI */
1046 # ifdef EMACS
1047 	    || Flag(FEMACS) || Flag(FGMACS)
1048 # endif /* EMACS */
1049 	    )) {
1050 		int nread;
1051 
1052 		nread = x_read(xp, LINE);
1053 		if (nread < 0)	/* read error */
1054 			nread = 0;
1055 		xp[nread] = '\0';
1056 		xp += nread;
1057 	}
1058 	else
1059 #endif /* EDIT */
1060 	{
1061 		if (interactive) {
1062 			pprompt(prompt, 0);
1063 		} else
1064 			s->line++;
1065 
1066 		while (1) {
1067 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
1068 
1069 			if (!p && shf_error(s->u.shf) &&
1070 			    shf_errno(s->u.shf) == EINTR) {
1071 				shf_clearerr(s->u.shf);
1072 				if (trap)
1073 					runtraps(0);
1074 				continue;
1075 			}
1076 			if (!p || (xp = p, xp[-1] == '\n'))
1077 				break;
1078 			/* double buffer size */
1079 			xp++; /* move past null so doubling works... */
1080 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
1081 			xp--; /* ...and move back again */
1082 		}
1083 		/* flush any unwanted input so other programs/builtins
1084 		 * can read it.  Not very optimal, but less error prone
1085 		 * than flushing else where, dealing with redirections,
1086 		 * etc..
1087 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
1088 		 */
1089 		if (s->type == SSTDIN)
1090 			shf_flush(s->u.shf);
1091 	}
1092 	/* XXX: temporary kludge to restore source after a
1093 	 * trap may have been executed.
1094 	 */
1095 	source = s;
1096 	if (have_tty && ksh_tmout) {
1097 		ksh_tmout_state = TMOUT_EXECUTING;
1098 		alarm(0);
1099 	}
1100 	s->start = s->str = Xstring(s->xs, xp);
1101 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
1102 	/* Note: if input is all nulls, this is not eof */
1103 	if (Xlength(s->xs, xp) == 0) { /* EOF */
1104 		if (s->type == SFILE)
1105 			shf_fdclose(s->u.shf);
1106 		s->str = NULL;
1107 	} else if (interactive) {
1108 #ifdef HISTORY
1109 		char *p = Xstring(s->xs, xp);
1110 		if (cur_prompt == PS1)
1111 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
1112 				p++;
1113 		if (*p) {
1114 			s->line++;
1115 			histsave(s->line, s->str, 1);
1116 		}
1117 #endif /* HISTORY */
1118 	}
1119 	if (interactive)
1120 		set_prompt(PS2, (Source *) 0);
1121 }
1122 
1123 static char *
1124 special_prompt_expand(char *str)
1125 {
1126 	char *p = str;
1127 
1128 	while ((p = strstr(p, "\\$")) != NULL) {
1129 		*(p+1) = 'p';
1130 	}
1131 	return str;
1132 }
1133 
1134 void
1135 set_prompt(int to, Source *s)
1136 {
1137 	char *ps1;
1138 	Area *saved_atemp;
1139 
1140 	cur_prompt = to;
1141 
1142 	switch (to) {
1143 	case PS1: /* command */
1144 		ps1 = str_save(str_val(global("PS1")), ATEMP);
1145 		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
1146 		newenv(E_ERRH);
1147 		if (sigsetjmp(e->jbuf, 0)) {
1148 			prompt = safe_prompt;
1149 			/* Don't print an error - assume it has already
1150 			 * been printed.  Reason is we may have forked
1151 			 * to run a command and the child may be
1152 			 * unwinding its stack through this code as it
1153 			 * exits.
1154 			 */
1155 		} else {
1156 			/* expand \$ before other substitutions are done */
1157 			char *tmp = special_prompt_expand(ps1);
1158 			prompt = str_save(substitute(tmp, 0), saved_atemp);
1159 		}
1160 		quitenv(NULL);
1161 		break;
1162 	case PS2: /* command continuation */
1163 		prompt = str_val(global("PS2"));
1164 		break;
1165 	}
1166 }
1167 
1168 static int
1169 dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
1170 {
1171 	char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1172 	int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
1173 	const char *cp = sp;
1174 	struct tm *tm;
1175 	time_t t;
1176 
1177 	if (*cp && cp[1] == '\r') {
1178 		delimiter = *cp;
1179 		cp += 2;
1180 	}
1181 
1182 	while (*cp != 0) {
1183 		delimitthis = 0;
1184 		if (indelimit && *cp != delimiter)
1185 			;
1186 		else if (*cp == '\n' || *cp == '\r') {
1187 			totlen = 0;
1188 			sp = cp + 1;
1189 		} else if (*cp == '\t') {
1190 			if (counting)
1191 				totlen = (totlen | 7) + 1;
1192 		} else if (*cp == delimiter) {
1193 			indelimit = !indelimit;
1194 			delimitthis = 1;
1195 		}
1196 
1197 		if (*cp == '\\') {
1198 			cp++;
1199 			if (!*cp)
1200 				break;
1201 			if (Flag(FSH))
1202 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1203 			else switch (*cp) {
1204 			case 'a':	/* '\' 'a' bell */
1205 				strbuf[0] = '\007';
1206 				strbuf[1] = '\0';
1207 				break;
1208 			case 'd':	/* '\' 'd' Dow Mon DD */
1209 				time(&t);
1210 				tm = localtime(&t);
1211 				strftime(strbuf, sizeof strbuf, "%a %b %d", tm);
1212 				break;
1213 			case 'D': /* '\' 'D' '{' strftime format '}' */
1214 				p = strchr(cp + 2, '}');
1215 				if (cp[1] != '{' || p == NULL) {
1216 					snprintf(strbuf, sizeof strbuf,
1217 					    "\\%c", *cp);
1218 					break;
1219 				}
1220 				strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1221 				p = strchr(tmpbuf, '}');
1222 				if (p)
1223 					*p = '\0';
1224 				time(&t);
1225 				tm = localtime(&t);
1226 				strftime(strbuf, sizeof strbuf, tmpbuf, tm);
1227 				cp = strchr(cp + 2, '}');
1228 				break;
1229 			case 'e':	/* '\' 'e' escape */
1230 				strbuf[0] = '\033';
1231 				strbuf[1] = '\0';
1232 				break;
1233 			case 'h':	/* '\' 'h' shortened hostname */
1234 				gethostname(strbuf, sizeof strbuf);
1235 				p = strchr(strbuf, '.');
1236 				if (p)
1237 					*p = '\0';
1238 				break;
1239 			case 'H':	/* '\' 'H' full hostname */
1240 				gethostname(strbuf, sizeof strbuf);
1241 				break;
1242 			case 'j':	/* '\' 'j' number of jobs */
1243 				snprintf(strbuf, sizeof strbuf, "%d",
1244 				    j_njobs());
1245 				break;
1246 			case 'l':	/* '\' 'l' basename of tty */
1247 				p = ttyname(0);
1248 				if (p)
1249 					p = basename(p);
1250 				if (p)
1251 					strlcpy(strbuf, p, sizeof strbuf);
1252 				break;
1253 			case 'n':	/* '\' 'n' newline */
1254 				strbuf[0] = '\n';
1255 				strbuf[1] = '\0';
1256 				totlen = 0;	/* reset for prompt re-print */
1257 				sp = cp + 1;
1258 				break;
1259 			case 'p':	/* '\' '$' $ or # */
1260 				strbuf[0] = ksheuid ? '$' : '#';
1261 				strbuf[1] = '\0';
1262 				break;
1263 			case 'r':	/* '\' 'r' return */
1264 				strbuf[0] = '\r';
1265 				strbuf[1] = '\0';
1266 				totlen = 0;	/* reset for prompt re-print */
1267 				sp = cp + 1;
1268 				break;
1269 			case 's':	/* '\' 's' basename $0 */
1270 				strlcpy(strbuf, kshname, sizeof strbuf);
1271 				break;
1272 			case 't':	/* '\' 't' 24 hour HH:MM:SS */
1273 				time(&t);
1274 				tm = localtime(&t);
1275 				strftime(strbuf, sizeof strbuf, "%T", tm);
1276 				break;
1277 			case 'T':	/* '\' 'T' 12 hour HH:MM:SS */
1278 				time(&t);
1279 				tm = localtime(&t);
1280 				strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm);
1281 				break;
1282 			case '@':	/* '\' '@' 12 hour am/pm format */
1283 				time(&t);
1284 				tm = localtime(&t);
1285 				strftime(strbuf, sizeof strbuf, "%r", tm);
1286 				break;
1287 			case 'A':	/* '\' 'A' 24 hour HH:MM */
1288 				time(&t);
1289 				tm = localtime(&t);
1290 				strftime(strbuf, sizeof strbuf, "%R", tm);
1291 				break;
1292 			case 'u':	/* '\' 'u' username */
1293 				strlcpy(strbuf, username, sizeof strbuf);
1294 				break;
1295 			case 'v':	/* '\' 'v' version (short) */
1296 				p = strchr(ksh_version, ' ');
1297 				if (p)
1298 					p = strchr(p + 1, ' ');
1299 				if (p) {
1300 					p++;
1301 					strlcpy(strbuf, p, sizeof strbuf);
1302 					p = strchr(strbuf, ' ');
1303 					if (p)
1304 						*p = '\0';
1305 				}
1306 				break;
1307 			case 'V':	/* '\' 'V' version (long) */
1308 				strlcpy(strbuf, ksh_version, sizeof strbuf);
1309 				break;
1310 			case 'w':	/* '\' 'w' cwd */
1311 				p = str_val(global("PWD"));
1312 				n = strlen(str_val(global("HOME")));
1313 				if (strcmp(p, "/") == 0) {
1314 					strlcpy(strbuf, p, sizeof strbuf);
1315 				} else if (strcmp(p, str_val(global("HOME"))) == 0) {
1316 					strbuf[0] = '~';
1317 					strbuf[1] = '\0';
1318 				} else if (strncmp(p, str_val(global("HOME")), n)
1319 				    == 0 && p[n] == '/') {
1320 					snprintf(strbuf, sizeof strbuf, "~/%s",
1321 					    str_val(global("PWD")) + n + 1);
1322 				} else
1323 					strlcpy(strbuf, p, sizeof strbuf);
1324 				break;
1325 			case 'W':	/* '\' 'W' basename(cwd) */
1326 				p = str_val(global("PWD"));
1327 				strlcpy(strbuf, basename(p), sizeof strbuf);
1328 				break;
1329 			case '!':	/* '\' '!' history line number */
1330 				snprintf(strbuf, sizeof strbuf, "%d",
1331 				    source->line + 1);
1332 				break;
1333 			case '#':	/* '\' '#' command line number */
1334 				snprintf(strbuf, sizeof strbuf, "%d",
1335 				    source->line - source->cmd_offset + 1);
1336 				break;
1337 			case '0':	/* '\' '#' '#' ' #' octal numeric handling */
1338 			case '1':
1339 			case '2':
1340 			case '3':
1341 			case '4':
1342 			case '5':
1343 			case '6':
1344 			case '7':
1345 				if ((cp[1] > '7' || cp[1] < '0') ||
1346 				    (cp[2] > '7' || cp[2] < '0')) {
1347 					snprintf(strbuf, sizeof strbuf,
1348 					    "\\%c", *cp);
1349 					break;
1350 				}
1351 				n = cp[0] * 8 * 8 + cp[1] * 8 + cp[2];
1352 				snprintf(strbuf, sizeof strbuf, "%c", n);
1353 				cp += 2;
1354 				break;
1355 			case '\\':	/* '\' '\' */
1356 				strbuf[0] = '\\';
1357 				strbuf[1] = '\0';
1358 				break;
1359 			case '[': /* '\' '[' .... stop counting */
1360 				strbuf[0] = '\0';
1361 				counting = 0;
1362 				break;
1363 			case ']': /* '\' ']' restart counting */
1364 				strbuf[0] = '\0';
1365 				counting = 1;
1366 				break;
1367 
1368 			default:
1369 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1370 				break;
1371 			}
1372 			cp++;
1373 
1374 			str = strbuf;
1375 			len = strlen(str);
1376 			if (ntruncate) {
1377 				if (ntruncate >= len) {
1378 					ntruncate -= len;
1379 					continue;
1380 				}
1381 				str += ntruncate;
1382 				len -= ntruncate;
1383 				ntruncate = 0;
1384 			}
1385 			if (doprint)
1386 				shf_write(str, len, shl_out);
1387 			if (counting && !indelimit && !delimitthis)
1388 				totlen += len;
1389 			continue;
1390 		} else if (*cp != '!')
1391 			c = *cp++;
1392 		else if (*++cp == '!')
1393 			c = *cp++;
1394 		else {
1395 			char *p;
1396 
1397 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
1398 			    source->line + 1);
1399 			len = strlen(nbuf);
1400 			if (ntruncate) {
1401 				if (ntruncate >= len) {
1402 					ntruncate -= len;
1403 					continue;
1404 				}
1405 				p += ntruncate;
1406 				len -= ntruncate;
1407 				ntruncate = 0;
1408 			}
1409 			if (doprint)
1410 				shf_write(p, len, shl_out);
1411 			if (counting && !indelimit && !delimitthis)
1412 				totlen += len;
1413 			continue;
1414 		}
1415 		if (counting && ntruncate)
1416 			--ntruncate;
1417 		else if (doprint) {
1418 			shf_putc(c, shl_out);
1419 		}
1420 		if (counting && !indelimit && !delimitthis)
1421 			totlen++;
1422 	}
1423 	if (doprint)
1424 		shf_flush(shl_out);
1425 	if (spp)
1426 		*spp = sp;
1427 	return (totlen);
1428 }
1429 
1430 void
1431 pprompt(const char *cp, int ntruncate)
1432 {
1433 	dopprompt(cp, ntruncate, NULL, 1);
1434 }
1435 
1436 int
1437 promptlen(const char *cp, const char **spp)
1438 {
1439 	return dopprompt(cp, 0, spp, 0);
1440 }
1441 
1442 /* Read the variable part of a ${...} expression (ie, up to but not including
1443  * the :[-+?=#%] or close-brace.
1444  */
1445 static char *
1446 get_brace_var(XString *wsp, char *wp)
1447 {
1448 	enum parse_state {
1449 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
1450 			   PS_NUMBER, PS_VAR1, PS_END
1451 			 }
1452 		state;
1453 	char c;
1454 
1455 	state = PS_INITIAL;
1456 	while (1) {
1457 		c = getsc();
1458 		/* State machine to figure out where the variable part ends. */
1459 		switch (state) {
1460 		case PS_INITIAL:
1461 			if (c == '#') {
1462 				state = PS_SAW_HASH;
1463 				break;
1464 			}
1465 			/* FALLTHROUGH */
1466 		case PS_SAW_HASH:
1467 			if (letter(c))
1468 				state = PS_IDENT;
1469 			else if (digit(c))
1470 				state = PS_NUMBER;
1471 			else if (ctype(c, C_VAR1))
1472 				state = PS_VAR1;
1473 			else
1474 				state = PS_END;
1475 			break;
1476 		case PS_IDENT:
1477 			if (!letnum(c)) {
1478 				state = PS_END;
1479 				if (c == '[') {
1480 					char *tmp, *p;
1481 
1482 					if (!arraysub(&tmp))
1483 						yyerror("missing ]\n");
1484 					*wp++ = c;
1485 					for (p = tmp; *p; ) {
1486 						Xcheck(*wsp, wp);
1487 						*wp++ = *p++;
1488 					}
1489 					afree(tmp, ATEMP);
1490 					c = getsc(); /* the ] */
1491 				}
1492 			}
1493 			break;
1494 		case PS_NUMBER:
1495 			if (!digit(c))
1496 				state = PS_END;
1497 			break;
1498 		case PS_VAR1:
1499 			state = PS_END;
1500 			break;
1501 		case PS_END: /* keep gcc happy */
1502 			break;
1503 		}
1504 		if (state == PS_END) {
1505 			*wp++ = '\0';	/* end of variable part */
1506 			ungetsc(c);
1507 			break;
1508 		}
1509 		Xcheck(*wsp, wp);
1510 		*wp++ = c;
1511 	}
1512 	return wp;
1513 }
1514 
1515 /*
1516  * Save an array subscript - returns true if matching bracket found, false
1517  * if eof or newline was found.
1518  * (Returned string double null terminated)
1519  */
1520 static int
1521 arraysub(char **strp)
1522 {
1523 	XString ws;
1524 	char	*wp;
1525 	char	c;
1526 	int	depth = 1;	/* we are just past the initial [ */
1527 
1528 	Xinit(ws, wp, 32, ATEMP);
1529 
1530 	do {
1531 		c = getsc();
1532 		Xcheck(ws, wp);
1533 		*wp++ = c;
1534 		if (c == '[')
1535 			depth++;
1536 		else if (c == ']')
1537 			depth--;
1538 	} while (depth > 0 && c && c != '\n');
1539 
1540 	*wp++ = '\0';
1541 	*strp = Xclose(ws, wp);
1542 
1543 	return depth == 0 ? 1 : 0;
1544 }
1545 
1546 /* Unget a char: handles case when we are already at the start of the buffer */
1547 static const char *
1548 ungetsc(int c)
1549 {
1550 	if (backslash_skip)
1551 		backslash_skip--;
1552 	/* Don't unget eof... */
1553 	if (source->str == null && c == '\0')
1554 		return source->str;
1555 	if (source->str > source->start)
1556 		source->str--;
1557 	else {
1558 		Source *s;
1559 
1560 		s = pushs(SREREAD, source->areap);
1561 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1562 		s->start = s->str = s->ugbuf;
1563 		s->next = source;
1564 		source = s;
1565 	}
1566 	return source->str;
1567 }
1568 
1569 
1570 /* Called to get a char that isn't a \newline sequence. */
1571 static int
1572 getsc_bn(void)
1573 {
1574 	int c, c2;
1575 
1576 	if (ignore_backslash_newline)
1577 		return getsc_();
1578 
1579 	if (backslash_skip == 1) {
1580 		backslash_skip = 2;
1581 		return getsc_();
1582 	}
1583 
1584 	backslash_skip = 0;
1585 
1586 	while (1) {
1587 		c = getsc_();
1588 		if (c == '\\') {
1589 			if ((c2 = getsc_()) == '\n')
1590 				/* ignore the \newline; get the next char... */
1591 				continue;
1592 			ungetsc(c2);
1593 			backslash_skip = 1;
1594 		}
1595 		return c;
1596 	}
1597 }
1598 
1599 static Lex_state *
1600 push_state_(State_info *si, Lex_state *old_end)
1601 {
1602 	Lex_state	*new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP);
1603 
1604 	new[0].ls_info.base = old_end;
1605 	si->base = &new[0];
1606 	si->end = &new[STATE_BSIZE];
1607 	return &new[1];
1608 }
1609 
1610 static Lex_state *
1611 pop_state_(State_info *si, Lex_state *old_end)
1612 {
1613 	Lex_state *old_base = si->base;
1614 
1615 	si->base = old_end->ls_info.base - STATE_BSIZE;
1616 	si->end = old_end->ls_info.base;
1617 
1618 	afree(old_base, ATEMP);
1619 
1620 	return si->base + STATE_BSIZE - 1;
1621 }
1622