xref: /openbsd/bin/ksh/lex.c (revision ca3ae29f)
1*ca3ae29fSczarkoff /*	$OpenBSD: lex.c,v 1.68 2016/03/04 09:37:23 czarkoff Exp $	*/
27cb960a2Sdownsj 
37cb960a2Sdownsj /*
47cb960a2Sdownsj  * lexical analysis and source input
57cb960a2Sdownsj  */
67cb960a2Sdownsj 
77cb960a2Sdownsj #include <ctype.h>
84a010e0cStb #include <errno.h>
9b608f594Smmcc #include <libgen.h>
104a010e0cStb #include <stdio.h>
1156018212Smmcc #include <string.h>
124a010e0cStb #include <unistd.h>
137cb960a2Sdownsj 
14b608f594Smmcc #include "sh.h"
153b015934Smillert 
161bbe2e09Smillert /*
171bbe2e09Smillert  * states while lexing word
181bbe2e09Smillert  */
19091f90deSnicm #define	SINVALID	-1	/* invalid state */
201bbe2e09Smillert #define	SBASE	0		/* outside any lexical constructs */
211bbe2e09Smillert #define	SWORD	1		/* implicit quoting for substitute() */
221bbe2e09Smillert #define	SLETPAREN 2		/* inside (( )), implicit quoting */
231bbe2e09Smillert #define	SSQUOTE	3		/* inside '' */
241bbe2e09Smillert #define	SDQUOTE	4		/* inside "" */
251bbe2e09Smillert #define	SBRACE	5		/* inside ${} */
261bbe2e09Smillert #define	SCSPAREN 6		/* inside $() */
271bbe2e09Smillert #define	SBQUOTE	7		/* inside `` */
281bbe2e09Smillert #define	SASPAREN 8		/* inside $(( )) */
291bbe2e09Smillert #define SHEREDELIM 9		/* parsing <<,<<- delimiter */
301bbe2e09Smillert #define SHEREDQUOTE 10		/* parsing " in <<,<<- delimiter */
311bbe2e09Smillert #define SPATTERN 11		/* parsing *(...|...) pattern (*+?@!) */
321bbe2e09Smillert #define STBRACE 12		/* parsing ${..[#%]..} */
331bbe2e09Smillert #define	SBRACEQ	13		/* inside "${}" */
341bbe2e09Smillert 
353b015934Smillert /* Structure to keep track of the lexing state and the various pieces of info
363b015934Smillert  * needed for each particular state.
373b015934Smillert  */
383b015934Smillert typedef struct lex_state Lex_state;
393b015934Smillert struct lex_state {
403b015934Smillert 	int ls_state;
413b015934Smillert 	union {
423b015934Smillert 		/* $(...) */
433b015934Smillert 		struct scsparen_info {
443b015934Smillert 			int nparen;	/* count open parenthesis */
453b015934Smillert 			int csstate;	/* XXX remove */
463b015934Smillert #define ls_scsparen ls_info.u_scsparen
473b015934Smillert 		} u_scsparen;
483b015934Smillert 
493b015934Smillert 		/* $((...)) */
503b015934Smillert 		struct sasparen_info {
513b015934Smillert 			int nparen;	/* count open parenthesis */
523b015934Smillert 			int start;	/* marks start of $(( in output str */
533b015934Smillert #define ls_sasparen ls_info.u_sasparen
543b015934Smillert 		} u_sasparen;
553b015934Smillert 
563b015934Smillert 		/* ((...)) */
573b015934Smillert 		struct sletparen_info {
583b015934Smillert 			int nparen;	/* count open parenthesis */
593b015934Smillert #define ls_sletparen ls_info.u_sletparen
603b015934Smillert 		} u_sletparen;
613b015934Smillert 
623b015934Smillert 		/* `...` */
633b015934Smillert 		struct sbquote_info {
643b015934Smillert 			int indquotes;	/* true if in double quotes: "`...`" */
653b015934Smillert #define ls_sbquote ls_info.u_sbquote
663b015934Smillert 		} u_sbquote;
673b015934Smillert 
683b015934Smillert 		Lex_state *base;	/* used to point to next state block */
693b015934Smillert 	} ls_info;
703b015934Smillert };
713b015934Smillert 
723b015934Smillert typedef struct State_info State_info;
733b015934Smillert struct State_info {
743b015934Smillert 	Lex_state	*base;
753b015934Smillert 	Lex_state	*end;
763b015934Smillert };
773b015934Smillert 
783b015934Smillert 
79c5d5393cSotto static void	readhere(struct ioword *);
8069b9f96bSmillert static int	getsc__(void);
81c5d5393cSotto static void	getsc_line(Source *);
8269b9f96bSmillert static int	getsc_bn(void);
83c5d5393cSotto static char	*get_brace_var(XString *, char *);
84c5d5393cSotto static int	arraysub(char **);
85c5d5393cSotto static const char *ungetsc(int);
8669b9f96bSmillert static void	gethere(void);
87c5d5393cSotto static Lex_state *push_state_(State_info *, Lex_state *);
88c5d5393cSotto static Lex_state *pop_state_(State_info *, Lex_state *);
89e7b1290aSotto static char	*special_prompt_expand(char *);
90c5d5393cSotto static int	dopprompt(const char *, int, const char **, int);
91a3427febSderaadt int		promptlen(const char *cp, const char **spp);
927cb960a2Sdownsj 
93e55c1b2cSdownsj static int backslash_skip;
94e55c1b2cSdownsj static int ignore_backslash_newline;
95e55c1b2cSdownsj 
9618ad1d01Snicm Source *source;		/* yyparse/yylex source */
9718ad1d01Snicm YYSTYPE	yylval;		/* result from yylex */
9818ad1d01Snicm struct ioword *heres[HERES], **herep;
9918ad1d01Snicm char	ident[IDENT+1];
10018ad1d01Snicm 
10118ad1d01Snicm char  **history;	/* saved commands */
10218ad1d01Snicm char  **histptr;	/* last history item */
10318ad1d01Snicm int	histsize;	/* history size */
10418ad1d01Snicm 
105e55c1b2cSdownsj /* optimized getsc_bn() */
106e55c1b2cSdownsj #define getsc()		(*source->str != '\0' && *source->str != '\\' \
107e55c1b2cSdownsj 			 && !backslash_skip ? *source->str++ : getsc_bn())
108e55c1b2cSdownsj /* optimized getsc__() */
109e55c1b2cSdownsj #define	getsc_()	((*source->str != '\0') ? *source->str++ : getsc__())
1107cb960a2Sdownsj 
1113b015934Smillert #define STATE_BSIZE	32
1123b015934Smillert 
1133b015934Smillert #define PUSH_STATE(s)	do { \
1143b015934Smillert 			    if (++statep == state_info.end) \
1153b015934Smillert 				statep = push_state_(&state_info, statep); \
1163b015934Smillert 			    state = statep->ls_state = (s); \
1173b015934Smillert 			} while (0)
1183b015934Smillert 
1193b015934Smillert #define POP_STATE()	do { \
1203b015934Smillert 			    if (--statep == state_info.base) \
1213b015934Smillert 				statep = pop_state_(&state_info, statep); \
1223b015934Smillert 			    state = statep->ls_state; \
1233b015934Smillert 			} while (0)
1243b015934Smillert 
1253b015934Smillert 
1267cb960a2Sdownsj 
1277cb960a2Sdownsj /*
1287cb960a2Sdownsj  * Lexical analyzer
1297cb960a2Sdownsj  *
1307cb960a2Sdownsj  * tokens are not regular expressions, they are LL(1).
1317cb960a2Sdownsj  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
1327cb960a2Sdownsj  * hence the state stack.
1337cb960a2Sdownsj  */
1347cb960a2Sdownsj 
1357cb960a2Sdownsj int
136c5d5393cSotto yylex(int cf)
1377cb960a2Sdownsj {
1383b015934Smillert 	Lex_state states[STATE_BSIZE], *statep;
1393b015934Smillert 	State_info state_info;
1407894b443Smillert 	int c, state;
1417cb960a2Sdownsj 	XString ws;		/* expandable output word */
1427894b443Smillert 	char *wp;		/* output word pointer */
1433b015934Smillert 	char *sp, *dp;
1443b015934Smillert 	int c2;
1457cb960a2Sdownsj 
1467cb960a2Sdownsj 
1477cb960a2Sdownsj   Again:
148091f90deSnicm 	states[0].ls_state = SINVALID;
14922a9cf94Sguenther 	states[0].ls_info.base = NULL;
1503b015934Smillert 	statep = &states[1];
1513b015934Smillert 	state_info.base = states;
1523b015934Smillert 	state_info.end = &states[STATE_BSIZE];
1533b015934Smillert 
1547cb960a2Sdownsj 	Xinit(ws, wp, 64, ATEMP);
1557cb960a2Sdownsj 
156e55c1b2cSdownsj 	backslash_skip = 0;
157e55c1b2cSdownsj 	ignore_backslash_newline = 0;
158e55c1b2cSdownsj 
1597cb960a2Sdownsj 	if (cf&ONEWORD)
1603b015934Smillert 		state = SWORD;
1617cb960a2Sdownsj 	else if (cf&LETEXPR) {
1627cb960a2Sdownsj 		*wp++ = OQUOTE;	 /* enclose arguments in (double) quotes */
1633b015934Smillert 		state = SLETPAREN;
1643b015934Smillert 		statep->ls_sletparen.nparen = 0;
16594e42df6Smillert 	} else {		/* normal lexing */
1663b015934Smillert 		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
1677cb960a2Sdownsj 		while ((c = getsc()) == ' ' || c == '\t')
1687cb960a2Sdownsj 			;
169e55c1b2cSdownsj 		if (c == '#') {
170e55c1b2cSdownsj 			ignore_backslash_newline++;
1717cb960a2Sdownsj 			while ((c = getsc()) != '\0' && c != '\n')
1727cb960a2Sdownsj 				;
173e55c1b2cSdownsj 			ignore_backslash_newline--;
174e55c1b2cSdownsj 		}
1757cb960a2Sdownsj 		ungetsc(c);
1767cb960a2Sdownsj 	}
1777cb960a2Sdownsj 	if (source->flags & SF_ALIAS) {	/* trailing ' ' in alias definition */
1787cb960a2Sdownsj 		source->flags &= ~SF_ALIAS;
1797cb960a2Sdownsj 		/* In POSIX mode, a trailing space only counts if we are
1807cb960a2Sdownsj 		 * parsing a simple command
1817cb960a2Sdownsj 		 */
1827cb960a2Sdownsj 		if (!Flag(FPOSIX) || (cf & CMDWORD))
1837cb960a2Sdownsj 			cf |= ALIAS;
1847cb960a2Sdownsj 	}
1857cb960a2Sdownsj 
1863b015934Smillert 	/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
1873b015934Smillert 	statep->ls_state = state;
1883b015934Smillert 
1897cb960a2Sdownsj 	/* collect non-special or quoted characters to form word */
1907a8124d8Sderaadt 	while (!((c = getsc()) == 0 ||
1917a8124d8Sderaadt 	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
1927cb960a2Sdownsj 		Xcheck(ws, wp);
1937cb960a2Sdownsj 		switch (state) {
1947cb960a2Sdownsj 		case SBASE:
1957a8124d8Sderaadt 			if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) &&
1967a8124d8Sderaadt 			    c == '!') {
197f740f764Sderaadt 				char **replace = NULL;
1981c2ba45aStedu 				int get, i;
199bc7ffb06Snicm 				char match[200] = { 0 }, *str = match;
2001c2ba45aStedu 				size_t mlen;
201f740f764Sderaadt 
202f740f764Sderaadt 				c2 = getsc();
2030b1768a7Sotto 				if (c2 == '\0' || c2 == ' ' || c2 == '\t')
204f740f764Sderaadt 					;
205f740f764Sderaadt 				else if (c2 == '!')
206f740f764Sderaadt 					replace = hist_get_newest(0);
207f740f764Sderaadt 				else if (isdigit(c2) || c2 == '-' ||
208f740f764Sderaadt 				    isalpha(c2)) {
2091c2ba45aStedu 					get = !isalpha(c2);
210f740f764Sderaadt 
211f740f764Sderaadt 					*str++ = c2;
212f740f764Sderaadt 					do {
213f740f764Sderaadt 						if ((c2 = getsc()) == '\0')
214f740f764Sderaadt 							break;
215f740f764Sderaadt 						if (c2 == '\t' || c2 == ' ' ||
216f740f764Sderaadt 						    c2 == '\n') {
217f740f764Sderaadt 							ungetsc(c2);
218f740f764Sderaadt 							break;
219f740f764Sderaadt 						}
220f740f764Sderaadt 						*str++ = c2;
221f740f764Sderaadt 					} while (str < &match[sizeof(match)-1]);
222f740f764Sderaadt 					*str = '\0';
223f740f764Sderaadt 
224f740f764Sderaadt 					if (get) {
225f740f764Sderaadt 						int h = findhistrel(match);
226f740f764Sderaadt 						if (h >= 0)
227f740f764Sderaadt 							replace = &history[h];
228f740f764Sderaadt 					} else {
2290e7d3a01Smillert 						int h = findhist(-1, 0, match, true);
230f740f764Sderaadt 						if (h >= 0)
231f740f764Sderaadt 							replace = &history[h];
232f740f764Sderaadt 					}
233f740f764Sderaadt 				}
234f740f764Sderaadt 
235f740f764Sderaadt 				/*
236f740f764Sderaadt 				 * XXX ksh history buffer saves un-expanded
237f740f764Sderaadt 				 * commands. Until the history buffer code is
238f740f764Sderaadt 				 * changed to contain expanded commands, we
239f740f764Sderaadt 				 * ignore the bad commands (spinning sucks)
240f740f764Sderaadt 				 */
241f740f764Sderaadt 				if (replace && **replace == '!')
242f740f764Sderaadt 					ungetsc(c2);
243f740f764Sderaadt 				else if (replace) {
244f740f764Sderaadt 					Source *s;
245f740f764Sderaadt 
246f740f764Sderaadt 					/* do not strdup replacement via alloc */
2477a8124d8Sderaadt 					s = pushs(SREREAD, source->areap);
248f740f764Sderaadt 					s->start = s->str = *replace;
249f740f764Sderaadt 					s->next = source;
2507d849734Smillert 					s->u.freeme = NULL;
251f740f764Sderaadt 					source = s;
252f740f764Sderaadt 					continue;
253bc7ffb06Snicm 				} else if (*match != '\0') {
2541c2ba45aStedu 					/* restore what followed the '!' */
2551c2ba45aStedu 					mlen = strlen(match);
2561c2ba45aStedu 					for (i = mlen-1; i >= 0; i--)
2571c2ba45aStedu 						ungetsc(match[i]);
258bc7ffb06Snicm 				} else
259bc7ffb06Snicm 					ungetsc(c2);
260f740f764Sderaadt 			}
2617cb960a2Sdownsj 			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
2627cb960a2Sdownsj 				*wp = EOS; /* temporary */
2637a8124d8Sderaadt 				if (is_wdvarname(Xstring(ws, wp), false)) {
2647cb960a2Sdownsj 					char *p, *tmp;
2657cb960a2Sdownsj 
2667cb960a2Sdownsj 					if (arraysub(&tmp)) {
2677cb960a2Sdownsj 						*wp++ = CHAR;
2687cb960a2Sdownsj 						*wp++ = c;
2697cb960a2Sdownsj 						for (p = tmp; *p; ) {
2707cb960a2Sdownsj 							Xcheck(ws, wp);
2717cb960a2Sdownsj 							*wp++ = CHAR;
2727cb960a2Sdownsj 							*wp++ = *p++;
2737cb960a2Sdownsj 						}
2747cb960a2Sdownsj 						afree(tmp, ATEMP);
2757cb960a2Sdownsj 						break;
2767cb960a2Sdownsj 					} else {
2777cb960a2Sdownsj 						Source *s;
2787cb960a2Sdownsj 
2797cb960a2Sdownsj 						s = pushs(SREREAD,
2807cb960a2Sdownsj 							  source->areap);
2817cb960a2Sdownsj 						s->start = s->str
2827cb960a2Sdownsj 							= s->u.freeme = tmp;
2837cb960a2Sdownsj 						s->next = source;
2847cb960a2Sdownsj 						source = s;
2857cb960a2Sdownsj 					}
2867cb960a2Sdownsj 				}
2877cb960a2Sdownsj 				*wp++ = CHAR;
2887cb960a2Sdownsj 				*wp++ = c;
2897cb960a2Sdownsj 				break;
2907cb960a2Sdownsj 			}
2917eab881bSjaredy 			/* FALLTHROUGH */
2927cb960a2Sdownsj 		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */
2937a8124d8Sderaadt 			if (c == '*' || c == '@' || c == '+' || c == '?' ||
2947a8124d8Sderaadt 			    c == '!') {
2957cb960a2Sdownsj 				c2 = getsc();
2967cb960a2Sdownsj 				if (c2 == '(' /*)*/ ) {
2977cb960a2Sdownsj 					*wp++ = OPAT;
2987cb960a2Sdownsj 					*wp++ = c;
2993b015934Smillert 					PUSH_STATE(SPATTERN);
3007cb960a2Sdownsj 					break;
3017cb960a2Sdownsj 				}
3027cb960a2Sdownsj 				ungetsc(c2);
3037cb960a2Sdownsj 			}
3047eab881bSjaredy 			/* FALLTHROUGH */
3057cb960a2Sdownsj 		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */
3067cb960a2Sdownsj 			switch (c) {
3077cb960a2Sdownsj 			case '\\':
3087cb960a2Sdownsj 				c = getsc();
309e55c1b2cSdownsj 				if (c) /* trailing \ is lost */
3107cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
3117cb960a2Sdownsj 				break;
3127cb960a2Sdownsj 			case '\'':
31322a9cf94Sguenther 				if ((cf & HEREDOC) || state == SBRACEQ) {
31422a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
31522a9cf94Sguenther 					break;
31622a9cf94Sguenther 				}
3177cb960a2Sdownsj 				*wp++ = OQUOTE;
318e55c1b2cSdownsj 				ignore_backslash_newline++;
3193b015934Smillert 				PUSH_STATE(SSQUOTE);
3207cb960a2Sdownsj 				break;
3217cb960a2Sdownsj 			case '"':
3227cb960a2Sdownsj 				*wp++ = OQUOTE;
3233b015934Smillert 				PUSH_STATE(SDQUOTE);
3247cb960a2Sdownsj 				break;
3257cb960a2Sdownsj 			default:
3267cb960a2Sdownsj 				goto Subst;
3277cb960a2Sdownsj 			}
3287cb960a2Sdownsj 			break;
3297cb960a2Sdownsj 
3307cb960a2Sdownsj 		  Subst:
3317cb960a2Sdownsj 			switch (c) {
3327cb960a2Sdownsj 			case '\\':
3337cb960a2Sdownsj 				c = getsc();
3347cb960a2Sdownsj 				switch (c) {
33557f623feSotto 				case '\\':
3367cb960a2Sdownsj 				case '$': case '`':
3377cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
3387cb960a2Sdownsj 					break;
33957f623feSotto 				case '"':
34057f623feSotto 					if ((cf & HEREDOC) == 0) {
34157f623feSotto 						*wp++ = QCHAR, *wp++ = c;
34257f623feSotto 						break;
34357f623feSotto 					}
34425d8d262Smoritz 					/* FALLTHROUGH */
3457cb960a2Sdownsj 				default:
346d3ecfddbSstsp 					if (cf & UNESCAPE) {
347d3ecfddbSstsp 						*wp++ = QCHAR, *wp++ = c;
348d3ecfddbSstsp 						break;
349d3ecfddbSstsp 					}
3507cb960a2Sdownsj 					Xcheck(ws, wp);
351e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
3527cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = '\\';
3537cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = c;
354e55c1b2cSdownsj 					}
3557cb960a2Sdownsj 					break;
3567cb960a2Sdownsj 				}
3577cb960a2Sdownsj 				break;
3587cb960a2Sdownsj 			case '$':
3597cb960a2Sdownsj 				c = getsc();
3607cb960a2Sdownsj 				if (c == '(') /*)*/ {
3617cb960a2Sdownsj 					c = getsc();
3627cb960a2Sdownsj 					if (c == '(') /*)*/ {
3633b015934Smillert 						PUSH_STATE(SASPAREN);
3643b015934Smillert 						statep->ls_sasparen.nparen = 2;
3653b015934Smillert 						statep->ls_sasparen.start =
3663b015934Smillert 						    Xsavepos(ws, wp);
3677cb960a2Sdownsj 						*wp++ = EXPRSUB;
3687cb960a2Sdownsj 					} else {
3697cb960a2Sdownsj 						ungetsc(c);
3703b015934Smillert 						PUSH_STATE(SCSPAREN);
3713b015934Smillert 						statep->ls_scsparen.nparen = 1;
3723b015934Smillert 						statep->ls_scsparen.csstate = 0;
3737cb960a2Sdownsj 						*wp++ = COMSUB;
3747cb960a2Sdownsj 					}
3757cb960a2Sdownsj 				} else if (c == '{') /*}*/ {
3767cb960a2Sdownsj 					*wp++ = OSUBST;
3773b015934Smillert 					*wp++ = '{'; /*}*/
3787cb960a2Sdownsj 					wp = get_brace_var(&ws, wp);
379e55c1b2cSdownsj 					c = getsc();
3803b015934Smillert 					/* allow :# and :% (ksh88 compat) */
3813b015934Smillert 					if (c == ':') {
3823b015934Smillert 						*wp++ = CHAR, *wp++ = c;
3833b015934Smillert 						c = getsc();
3843b015934Smillert 					}
3853b015934Smillert 					/* If this is a trim operation,
3863b015934Smillert 					 * treat (,|,) specially in STBRACE.
3873b015934Smillert 					 */
3887cb960a2Sdownsj 					if (c == '#' || c == '%') {
3893b015934Smillert 						ungetsc(c);
3903b015934Smillert 						PUSH_STATE(STBRACE);
3917cb960a2Sdownsj 					} else {
3927cb960a2Sdownsj 						ungetsc(c);
39322a9cf94Sguenther 						if (state == SDQUOTE ||
39422a9cf94Sguenther 						    state == SBRACEQ)
39522a9cf94Sguenther 							PUSH_STATE(SBRACEQ);
39622a9cf94Sguenther 						else
3973b015934Smillert 							PUSH_STATE(SBRACE);
3987cb960a2Sdownsj 					}
3997cb960a2Sdownsj 				} else if (ctype(c, C_ALPHA)) {
4007cb960a2Sdownsj 					*wp++ = OSUBST;
4013b015934Smillert 					*wp++ = 'X';
4027cb960a2Sdownsj 					do {
4037cb960a2Sdownsj 						Xcheck(ws, wp);
4047cb960a2Sdownsj 						*wp++ = c;
4057cb960a2Sdownsj 						c = getsc();
406bbf05626Smmcc 					} while (ctype(c, C_ALPHA) || digit(c));
4077cb960a2Sdownsj 					*wp++ = '\0';
4087cb960a2Sdownsj 					*wp++ = CSUBST;
4093b015934Smillert 					*wp++ = 'X';
4107cb960a2Sdownsj 					ungetsc(c);
411bbf05626Smmcc 				} else if (ctype(c, C_VAR1) || digit(c)) {
4127cb960a2Sdownsj 					Xcheck(ws, wp);
4137cb960a2Sdownsj 					*wp++ = OSUBST;
4143b015934Smillert 					*wp++ = 'X';
4157cb960a2Sdownsj 					*wp++ = c;
4167cb960a2Sdownsj 					*wp++ = '\0';
4177cb960a2Sdownsj 					*wp++ = CSUBST;
4183b015934Smillert 					*wp++ = 'X';
4197cb960a2Sdownsj 				} else {
4207cb960a2Sdownsj 					*wp++ = CHAR, *wp++ = '$';
4217cb960a2Sdownsj 					ungetsc(c);
4227cb960a2Sdownsj 				}
4237cb960a2Sdownsj 				break;
4247cb960a2Sdownsj 			case '`':
4253b015934Smillert 				PUSH_STATE(SBQUOTE);
4267cb960a2Sdownsj 				*wp++ = COMSUB;
4277cb960a2Sdownsj 				/* Need to know if we are inside double quotes
4287cb960a2Sdownsj 				 * since sh/at&t-ksh translate the \" to " in
4297cb960a2Sdownsj 				 * "`..\"..`".
4303b015934Smillert 				 * This is not done in posix mode (section
4313b015934Smillert 				 * 3.2.3, Double Quotes: "The backquote shall
4323b015934Smillert 				 * retain its special meaning introducing the
4333b015934Smillert 				 * other form of command substitution (see
4343b015934Smillert 				 * 3.6.3). The portion of the quoted string
4353b015934Smillert 				 * from the initial backquote and the
4363b015934Smillert 				 * characters up to the next backquote that
4373b015934Smillert 				 * is not preceded by a backslash (having
4383b015934Smillert 				 * escape characters removed) defines that
4393b015934Smillert 				 * command whose output replaces `...` when
4403b015934Smillert 				 * the word is expanded."
4413b015934Smillert 				 * Section 3.6.3, Command Substitution:
4423b015934Smillert 				 * "Within the backquoted style of command
4433b015934Smillert 				 * substitution, backslash shall retain its
4443b015934Smillert 				 * literal meaning, except when followed by
4453b015934Smillert 				 * $ ` \.").
4467cb960a2Sdownsj 				 */
4473b015934Smillert 				statep->ls_sbquote.indquotes = 0;
4483b015934Smillert 				if (!Flag(FPOSIX)) {
4493b015934Smillert 					Lex_state *s = statep;
4503b015934Smillert 					Lex_state *base = state_info.base;
4513b015934Smillert 					while (1) {
4523b015934Smillert 						for (; s != base; s--) {
4533b015934Smillert 							if (s->ls_state == SDQUOTE) {
4543b015934Smillert 								statep->ls_sbquote.indquotes = 1;
4553b015934Smillert 								break;
4563b015934Smillert 							}
4573b015934Smillert 						}
4583b015934Smillert 						if (s != base)
4593b015934Smillert 							break;
4603b015934Smillert 						if (!(s = s->ls_info.base))
4613b015934Smillert 							break;
4623b015934Smillert 						base = s-- - STATE_BSIZE;
4633b015934Smillert 					}
4643b015934Smillert 				}
4657cb960a2Sdownsj 				break;
4667cb960a2Sdownsj 			default:
4677cb960a2Sdownsj 				*wp++ = CHAR, *wp++ = c;
4687cb960a2Sdownsj 			}
4697cb960a2Sdownsj 			break;
4707cb960a2Sdownsj 
4717cb960a2Sdownsj 		case SSQUOTE:
4727cb960a2Sdownsj 			if (c == '\'') {
4733b015934Smillert 				POP_STATE();
47422a9cf94Sguenther 				if (state == SBRACEQ) {
47522a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
47622a9cf94Sguenther 					break;
47722a9cf94Sguenther 				}
4787cb960a2Sdownsj 				*wp++ = CQUOTE;
479e55c1b2cSdownsj 				ignore_backslash_newline--;
4807cb960a2Sdownsj 			} else
4817cb960a2Sdownsj 				*wp++ = QCHAR, *wp++ = c;
4827cb960a2Sdownsj 			break;
4837cb960a2Sdownsj 
4847cb960a2Sdownsj 		case SDQUOTE:
4857cb960a2Sdownsj 			if (c == '"') {
4863b015934Smillert 				POP_STATE();
4877cb960a2Sdownsj 				*wp++ = CQUOTE;
4887cb960a2Sdownsj 			} else
4897cb960a2Sdownsj 				goto Subst;
4907cb960a2Sdownsj 			break;
4917cb960a2Sdownsj 
4923b015934Smillert 		case SCSPAREN: /* $( .. ) */
4937cb960a2Sdownsj 			/* todo: deal with $(...) quoting properly
4947cb960a2Sdownsj 			 * kludge to partly fake quoting inside $(..): doesn't
4957cb960a2Sdownsj 			 * really work because nested $(..) or ${..} inside
4967cb960a2Sdownsj 			 * double quotes aren't dealt with.
4977cb960a2Sdownsj 			 */
4983b015934Smillert 			switch (statep->ls_scsparen.csstate) {
4997cb960a2Sdownsj 			case 0: /* normal */
5007cb960a2Sdownsj 				switch (c) {
5017cb960a2Sdownsj 				case '(':
5023b015934Smillert 					statep->ls_scsparen.nparen++;
5037cb960a2Sdownsj 					break;
5047cb960a2Sdownsj 				case ')':
5053b015934Smillert 					statep->ls_scsparen.nparen--;
5067cb960a2Sdownsj 					break;
5077cb960a2Sdownsj 				case '\\':
5083b015934Smillert 					statep->ls_scsparen.csstate = 1;
5097cb960a2Sdownsj 					break;
5107cb960a2Sdownsj 				case '"':
5113b015934Smillert 					statep->ls_scsparen.csstate = 2;
5127cb960a2Sdownsj 					break;
5137cb960a2Sdownsj 				case '\'':
5143b015934Smillert 					statep->ls_scsparen.csstate = 4;
515e55c1b2cSdownsj 					ignore_backslash_newline++;
5167cb960a2Sdownsj 					break;
5177cb960a2Sdownsj 				}
5187cb960a2Sdownsj 				break;
5197cb960a2Sdownsj 
5207cb960a2Sdownsj 			case 1: /* backslash in normal mode */
5217cb960a2Sdownsj 			case 3: /* backslash in double quotes */
5223b015934Smillert 				--statep->ls_scsparen.csstate;
5237cb960a2Sdownsj 				break;
5247cb960a2Sdownsj 
5257cb960a2Sdownsj 			case 2: /* double quotes */
5267cb960a2Sdownsj 				if (c == '"')
5273b015934Smillert 					statep->ls_scsparen.csstate = 0;
5287cb960a2Sdownsj 				else if (c == '\\')
5293b015934Smillert 					statep->ls_scsparen.csstate = 3;
5307cb960a2Sdownsj 				break;
5317cb960a2Sdownsj 
5327cb960a2Sdownsj 			case 4: /* single quotes */
533e55c1b2cSdownsj 				if (c == '\'') {
5343b015934Smillert 					statep->ls_scsparen.csstate = 0;
535e55c1b2cSdownsj 					ignore_backslash_newline--;
536e55c1b2cSdownsj 				}
5377cb960a2Sdownsj 				break;
5387cb960a2Sdownsj 			}
5393b015934Smillert 			if (statep->ls_scsparen.nparen == 0) {
5403b015934Smillert 				POP_STATE();
5417cb960a2Sdownsj 				*wp++ = 0; /* end of COMSUB */
5427cb960a2Sdownsj 			} else
5437cb960a2Sdownsj 				*wp++ = c;
5447cb960a2Sdownsj 			break;
5457cb960a2Sdownsj 
5463b015934Smillert 		case SASPAREN: /* $(( .. )) */
5477cb960a2Sdownsj 			/* todo: deal with $((...); (...)) properly */
5487cb960a2Sdownsj 			/* XXX should nest using existing state machine
5497cb960a2Sdownsj 			 * (embed "..", $(...), etc.) */
5507cb960a2Sdownsj 			if (c == '(')
5513b015934Smillert 				statep->ls_sasparen.nparen++;
5527cb960a2Sdownsj 			else if (c == ')') {
5533b015934Smillert 				statep->ls_sasparen.nparen--;
5543b015934Smillert 				if (statep->ls_sasparen.nparen == 1) {
5557cb960a2Sdownsj 					/*(*/
5567cb960a2Sdownsj 					if ((c2 = getsc()) == ')') {
5573b015934Smillert 						POP_STATE();
5587cb960a2Sdownsj 						*wp++ = 0; /* end of EXPRSUB */
5597cb960a2Sdownsj 						break;
5607cb960a2Sdownsj 					} else {
5613b015934Smillert 						char *s;
5623b015934Smillert 
5637cb960a2Sdownsj 						ungetsc(c2);
5647cb960a2Sdownsj 						/* mismatched parenthesis -
5657cb960a2Sdownsj 						 * assume we were really
5667cb960a2Sdownsj 						 * parsing a $(..) expression
5677cb960a2Sdownsj 						 */
5683b015934Smillert 						s = Xrestpos(ws, wp,
5693b015934Smillert 						    statep->ls_sasparen.start);
5703b015934Smillert 						memmove(s + 1, s, wp - s);
5713b015934Smillert 						*s++ = COMSUB;
5723b015934Smillert 						*s = '('; /*)*/
5737cb960a2Sdownsj 						wp++;
5743b015934Smillert 						statep->ls_scsparen.nparen = 1;
5753b015934Smillert 						statep->ls_scsparen.csstate = 0;
5767a8124d8Sderaadt 						state = statep->ls_state =
5777a8124d8Sderaadt 						    SCSPAREN;
5787cb960a2Sdownsj 					}
5797cb960a2Sdownsj 				}
5807cb960a2Sdownsj 			}
5817cb960a2Sdownsj 			*wp++ = c;
5827cb960a2Sdownsj 			break;
5837cb960a2Sdownsj 
58422a9cf94Sguenther 		case SBRACEQ:
585*ca3ae29fSczarkoff 			/*{*/
586*ca3ae29fSczarkoff 			if (c == '}') {
587*ca3ae29fSczarkoff 				POP_STATE();
588*ca3ae29fSczarkoff 				*wp++ = CSUBST;
589*ca3ae29fSczarkoff 				*wp++ = /*{*/ '}';
590*ca3ae29fSczarkoff 			} else
591*ca3ae29fSczarkoff 				goto Sbase2;
592*ca3ae29fSczarkoff 			break;
593*ca3ae29fSczarkoff 
5947cb960a2Sdownsj 		case SBRACE:
5957cb960a2Sdownsj 			/*{*/
5967cb960a2Sdownsj 			if (c == '}') {
5973b015934Smillert 				POP_STATE();
5987cb960a2Sdownsj 				*wp++ = CSUBST;
5993b015934Smillert 				*wp++ = /*{*/ '}';
6007cb960a2Sdownsj 			} else
6017cb960a2Sdownsj 				goto Sbase1;
6027cb960a2Sdownsj 			break;
6037cb960a2Sdownsj 
6047cb960a2Sdownsj 		case STBRACE:
6053b015934Smillert 			/* Same as SBRACE, except (,|,) treated specially */
6067cb960a2Sdownsj 			/*{*/
6077cb960a2Sdownsj 			if (c == '}') {
6083b015934Smillert 				POP_STATE();
6097cb960a2Sdownsj 				*wp++ = CSUBST;
6103b015934Smillert 				*wp++ = /*{*/ '}';
6117cb960a2Sdownsj 			} else if (c == '|') {
6127cb960a2Sdownsj 				*wp++ = SPAT;
6133b015934Smillert 			} else if (c == '(') {
6143b015934Smillert 				*wp++ = OPAT;
6153b015934Smillert 				*wp++ = ' ';	/* simile for @ */
6163b015934Smillert 				PUSH_STATE(SPATTERN);
6177cb960a2Sdownsj 			} else
6187cb960a2Sdownsj 				goto Sbase1;
6197cb960a2Sdownsj 			break;
6207cb960a2Sdownsj 
6217cb960a2Sdownsj 		case SBQUOTE:
6227cb960a2Sdownsj 			if (c == '`') {
6237cb960a2Sdownsj 				*wp++ = 0;
6243b015934Smillert 				POP_STATE();
6257cb960a2Sdownsj 			} else if (c == '\\') {
6267cb960a2Sdownsj 				switch (c = getsc()) {
6277cb960a2Sdownsj 				case '\\':
6287cb960a2Sdownsj 				case '$': case '`':
6297cb960a2Sdownsj 					*wp++ = c;
6307cb960a2Sdownsj 					break;
6317cb960a2Sdownsj 				case '"':
6323b015934Smillert 					if (statep->ls_sbquote.indquotes) {
6337cb960a2Sdownsj 						*wp++ = c;
6347cb960a2Sdownsj 						break;
6357cb960a2Sdownsj 					}
6367eab881bSjaredy 					/* FALLTHROUGH */
6377cb960a2Sdownsj 				default:
638e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
6397cb960a2Sdownsj 						*wp++ = '\\';
6407cb960a2Sdownsj 						*wp++ = c;
641e55c1b2cSdownsj 					}
6427cb960a2Sdownsj 					break;
6437cb960a2Sdownsj 				}
6447cb960a2Sdownsj 			} else
6457cb960a2Sdownsj 				*wp++ = c;
6467cb960a2Sdownsj 			break;
6477cb960a2Sdownsj 
6487cb960a2Sdownsj 		case SWORD:	/* ONEWORD */
6497cb960a2Sdownsj 			goto Subst;
6507cb960a2Sdownsj 
6513b015934Smillert 		case SLETPAREN:	/* LETEXPR: (( ... )) */
6527cb960a2Sdownsj 			/*(*/
6537cb960a2Sdownsj 			if (c == ')') {
6543b015934Smillert 				if (statep->ls_sletparen.nparen > 0)
6553b015934Smillert 				    --statep->ls_sletparen.nparen;
6567cb960a2Sdownsj 				/*(*/
6577cb960a2Sdownsj 				else if ((c2 = getsc()) == ')') {
6587cb960a2Sdownsj 					c = 0;
6597cb960a2Sdownsj 					*wp++ = CQUOTE;
6607cb960a2Sdownsj 					goto Done;
6617cb960a2Sdownsj 				} else
6627cb960a2Sdownsj 					ungetsc(c2);
6637cb960a2Sdownsj 			} else if (c == '(')
6647cb960a2Sdownsj 				/* parenthesis inside quotes and backslashes
6657cb960a2Sdownsj 				 * are lost, but at&t ksh doesn't count them
6667cb960a2Sdownsj 				 * either
6677cb960a2Sdownsj 				 */
6683b015934Smillert 				++statep->ls_sletparen.nparen;
6697cb960a2Sdownsj 			goto Sbase2;
6707cb960a2Sdownsj 
6717cb960a2Sdownsj 		case SHEREDELIM:	/* <<,<<- delimiter */
6727cb960a2Sdownsj 			/* XXX chuck this state (and the next) - use
6737cb960a2Sdownsj 			 * the existing states ($ and \`..` should be
6747cb960a2Sdownsj 			 * stripped of their specialness after the
6757cb960a2Sdownsj 			 * fact).
6767cb960a2Sdownsj 			 */
6777cb960a2Sdownsj 			/* here delimiters need a special case since
6787cb960a2Sdownsj 			 * $ and `..` are not to be treated specially
6797cb960a2Sdownsj 			 */
6807cb960a2Sdownsj 			if (c == '\\') {
6817cb960a2Sdownsj 				c = getsc();
682e55c1b2cSdownsj 				if (c) { /* trailing \ is lost */
6837cb960a2Sdownsj 					*wp++ = QCHAR;
6847cb960a2Sdownsj 					*wp++ = c;
6857cb960a2Sdownsj 				}
6867cb960a2Sdownsj 			} else if (c == '\'') {
6873b015934Smillert 				PUSH_STATE(SSQUOTE);
6887cb960a2Sdownsj 				*wp++ = OQUOTE;
689e55c1b2cSdownsj 				ignore_backslash_newline++;
6907cb960a2Sdownsj 			} else if (c == '"') {
6913b015934Smillert 				state = statep->ls_state = SHEREDQUOTE;
6927cb960a2Sdownsj 				*wp++ = OQUOTE;
6937cb960a2Sdownsj 			} else {
6947cb960a2Sdownsj 				*wp++ = CHAR;
6957cb960a2Sdownsj 				*wp++ = c;
6967cb960a2Sdownsj 			}
6977cb960a2Sdownsj 			break;
6987cb960a2Sdownsj 
6997cb960a2Sdownsj 		case SHEREDQUOTE:	/* " in <<,<<- delimiter */
7007cb960a2Sdownsj 			if (c == '"') {
7017cb960a2Sdownsj 				*wp++ = CQUOTE;
7023b015934Smillert 				state = statep->ls_state = SHEREDELIM;
7037cb960a2Sdownsj 			} else {
704e55c1b2cSdownsj 				if (c == '\\') {
705e55c1b2cSdownsj 					switch (c = getsc()) {
706e55c1b2cSdownsj 					case '\\': case '"':
707e55c1b2cSdownsj 					case '$': case '`':
7087cb960a2Sdownsj 						break;
709e55c1b2cSdownsj 					default:
710e55c1b2cSdownsj 						if (c) { /* trailing \ lost */
711e55c1b2cSdownsj 							*wp++ = CHAR;
712e55c1b2cSdownsj 							*wp++ = '\\';
713e55c1b2cSdownsj 						}
714e55c1b2cSdownsj 						break;
715e55c1b2cSdownsj 					}
716e55c1b2cSdownsj 				}
7177cb960a2Sdownsj 				*wp++ = CHAR;
7187cb960a2Sdownsj 				*wp++ = c;
7197cb960a2Sdownsj 			}
7207cb960a2Sdownsj 			break;
7217cb960a2Sdownsj 
7227cb960a2Sdownsj 		case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
7237cb960a2Sdownsj 			if ( /*(*/ c == ')') {
7247cb960a2Sdownsj 				*wp++ = CPAT;
7253b015934Smillert 				POP_STATE();
7263b015934Smillert 			} else if (c == '|') {
7277cb960a2Sdownsj 				*wp++ = SPAT;
7283b015934Smillert 			} else if (c == '(') {
7293b015934Smillert 				*wp++ = OPAT;
7303b015934Smillert 				*wp++ = ' ';	/* simile for @ */
7313b015934Smillert 				PUSH_STATE(SPATTERN);
7323b015934Smillert 			} else
7337cb960a2Sdownsj 				goto Sbase1;
7347cb960a2Sdownsj 			break;
7357cb960a2Sdownsj 		}
7367cb960a2Sdownsj 	}
7377cb960a2Sdownsj Done:
7387cb960a2Sdownsj 	Xcheck(ws, wp);
7393b015934Smillert 	if (statep != &states[1])
7403b015934Smillert 		/* XXX figure out what is missing */
7417cb960a2Sdownsj 		yyerror("no closing quote\n");
7427cb960a2Sdownsj 
7437cb960a2Sdownsj 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
7447cb960a2Sdownsj 	if (state == SHEREDELIM)
7457cb960a2Sdownsj 		state = SBASE;
7467cb960a2Sdownsj 
7473b015934Smillert 	dp = Xstring(ws, wp);
7487a8124d8Sderaadt 	if ((c == '<' || c == '>') && state == SBASE &&
7497a8124d8Sderaadt 	    ((c2 = Xlength(ws, wp)) == 0 ||
7507a8124d8Sderaadt 	    (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
7518c046d24Snicm 		struct ioword *iop = alloc(sizeof(*iop), ATEMP);
7527cb960a2Sdownsj 
7533b015934Smillert 		if (c2 == 2)
7543b015934Smillert 			iop->unit = dp[1] - '0';
7557cb960a2Sdownsj 		else
7563b015934Smillert 			iop->unit = c == '>'; /* 0 for <, 1 for > */
7577cb960a2Sdownsj 
7587cb960a2Sdownsj 		c2 = getsc();
7597cb960a2Sdownsj 		/* <<, >>, <> are ok, >< is not */
7607cb960a2Sdownsj 		if (c == c2 || (c == '<' && c2 == '>')) {
7617cb960a2Sdownsj 			iop->flag = c == c2 ?
7627cb960a2Sdownsj 			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
76318bbba6bSmillert 			if (iop->flag == IOHERE) {
764e55c1b2cSdownsj 				if ((c2 = getsc()) == '-')
7657cb960a2Sdownsj 					iop->flag |= IOSKIP;
7667cb960a2Sdownsj 				else
7677cb960a2Sdownsj 					ungetsc(c2);
76818bbba6bSmillert 			}
7697cb960a2Sdownsj 		} else if (c2 == '&')
7707cb960a2Sdownsj 			iop->flag = IODUP | (c == '<' ? IORDUP : 0);
7717cb960a2Sdownsj 		else {
7727cb960a2Sdownsj 			iop->flag = c == '>' ? IOWRITE : IOREAD;
7737cb960a2Sdownsj 			if (c == '>' && c2 == '|')
7747cb960a2Sdownsj 				iop->flag |= IOCLOB;
7757cb960a2Sdownsj 			else
7767cb960a2Sdownsj 				ungetsc(c2);
7777cb960a2Sdownsj 		}
7787cb960a2Sdownsj 
779355ffa75Stedu 		iop->name = NULL;
780355ffa75Stedu 		iop->delim = NULL;
781355ffa75Stedu 		iop->heredoc = NULL;
7823b015934Smillert 		Xfree(ws, wp);	/* free word */
7837cb960a2Sdownsj 		yylval.iop = iop;
7847cb960a2Sdownsj 		return REDIR;
7857cb960a2Sdownsj 	}
7863b015934Smillert 
7873b015934Smillert 	if (wp == dp && state == SBASE) {
7883b015934Smillert 		Xfree(ws, wp);	/* free word */
7893b015934Smillert 		/* no word, process LEX1 character */
7903b015934Smillert 		switch (c) {
7913b015934Smillert 		default:
7923b015934Smillert 			return c;
7933b015934Smillert 
7943b015934Smillert 		case '|':
7953b015934Smillert 		case '&':
7963b015934Smillert 		case ';':
7973b015934Smillert 			if ((c2 = getsc()) == c)
7983b015934Smillert 				c = (c == ';') ? BREAK :
7993b015934Smillert 				    (c == '|') ? LOGOR :
8003b015934Smillert 				    (c == '&') ? LOGAND :
8013b015934Smillert 				    YYERRCODE;
8023b015934Smillert 			else if (c == '|' && c2 == '&')
8033b015934Smillert 				c = COPROC;
8043b015934Smillert 			else
8053b015934Smillert 				ungetsc(c2);
8063b015934Smillert 			return c;
8073b015934Smillert 
8087cb960a2Sdownsj 		case '\n':
8097cb960a2Sdownsj 			gethere();
8107cb960a2Sdownsj 			if (cf & CONTIN)
8117cb960a2Sdownsj 				goto Again;
8127cb960a2Sdownsj 			return c;
8137cb960a2Sdownsj 
8147cb960a2Sdownsj 		case '(':  /*)*/
815b69beb66Sgrr 			if (!Flag(FSH)) {
8167cb960a2Sdownsj 				if ((c2 = getsc()) == '(') /*)*/
8173b015934Smillert 					/* XXX need to handle ((...); (...)) */
8187cb960a2Sdownsj 					c = MDPAREN;
8197cb960a2Sdownsj 				else
8207cb960a2Sdownsj 					ungetsc(c2);
821b69beb66Sgrr 			}
8227cb960a2Sdownsj 			return c;
8237cb960a2Sdownsj 		  /*(*/
8247cb960a2Sdownsj 		case ')':
8257cb960a2Sdownsj 			return c;
8267cb960a2Sdownsj 		}
8277cb960a2Sdownsj 	}
8287cb960a2Sdownsj 
8297cb960a2Sdownsj 	*wp++ = EOS;		/* terminate word */
8307cb960a2Sdownsj 	yylval.cp = Xclose(ws, wp);
83194e42df6Smillert 	if (state == SWORD || state == SLETPAREN)	/* ONEWORD? */
8327cb960a2Sdownsj 		return LWORD;
8337cb960a2Sdownsj 	ungetsc(c);		/* unget terminator */
8347cb960a2Sdownsj 
8357cb960a2Sdownsj 	/* copy word to unprefixed string ident */
8367cb960a2Sdownsj 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
8377cb960a2Sdownsj 		*dp++ = *sp++;
83812e7fb2dSjmc 	/* Make sure the ident array stays '\0' padded */
8397cb960a2Sdownsj 	memset(dp, 0, (ident+IDENT) - dp + 1);
8407cb960a2Sdownsj 	if (c != EOS)
8417cb960a2Sdownsj 		*ident = '\0';	/* word is not unquoted */
8427cb960a2Sdownsj 
8437cb960a2Sdownsj 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
8447cb960a2Sdownsj 		struct tbl *p;
8457cb960a2Sdownsj 		int h = hash(ident);
8467cb960a2Sdownsj 
8477cb960a2Sdownsj 		/* { */
8486df3ee40Sotto 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
8497a8124d8Sderaadt 		    (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
8507cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8517cb960a2Sdownsj 			return p->val.i;
8527cb960a2Sdownsj 		}
8536df3ee40Sotto 		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
8547a8124d8Sderaadt 		    (p->flag & ISSET)) {
8557894b443Smillert 			Source *s;
8567cb960a2Sdownsj 
8577cb960a2Sdownsj 			for (s = source; s->type == SALIAS; s = s->next)
8587cb960a2Sdownsj 				if (s->u.tblp == p)
8597cb960a2Sdownsj 					return LWORD;
8607cb960a2Sdownsj 			/* push alias expansion */
8617cb960a2Sdownsj 			s = pushs(SALIAS, source->areap);
8627cb960a2Sdownsj 			s->start = s->str = p->val.s;
8637cb960a2Sdownsj 			s->u.tblp = p;
8647cb960a2Sdownsj 			s->next = source;
8657cb960a2Sdownsj 			source = s;
8667cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8677cb960a2Sdownsj 			goto Again;
8687cb960a2Sdownsj 		}
8697cb960a2Sdownsj 	}
8707cb960a2Sdownsj 
8717cb960a2Sdownsj 	return LWORD;
8727cb960a2Sdownsj }
8737cb960a2Sdownsj 
8747cb960a2Sdownsj static void
875c5d5393cSotto gethere(void)
8767cb960a2Sdownsj {
8777894b443Smillert 	struct ioword **p;
8787cb960a2Sdownsj 
8797cb960a2Sdownsj 	for (p = heres; p < herep; p++)
8807cb960a2Sdownsj 		readhere(*p);
8817cb960a2Sdownsj 	herep = heres;
8827cb960a2Sdownsj }
8837cb960a2Sdownsj 
8847cb960a2Sdownsj /*
8857cb960a2Sdownsj  * read "<<word" text into temp file
8867cb960a2Sdownsj  */
8877cb960a2Sdownsj 
8887cb960a2Sdownsj static void
889c5d5393cSotto readhere(struct ioword *iop)
8907cb960a2Sdownsj {
8917894b443Smillert 	int c;
8927cb960a2Sdownsj 	char *volatile eof;
8937cb960a2Sdownsj 	char *eofp;
894e55c1b2cSdownsj 	int skiptabs;
895f00c5086Smillert 	XString xs;
896f00c5086Smillert 	char *xp;
897f00c5086Smillert 	int xpos;
8987cb960a2Sdownsj 
8997cb960a2Sdownsj 	eof = evalstr(iop->delim, 0);
9007cb960a2Sdownsj 
901e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
902e55c1b2cSdownsj 		ignore_backslash_newline++;
903e55c1b2cSdownsj 
904f00c5086Smillert 	Xinit(xs, xp, 256, ATEMP);
905f00c5086Smillert 
9067cb960a2Sdownsj 	for (;;) {
9077cb960a2Sdownsj 		eofp = eof;
9087cb960a2Sdownsj 		skiptabs = iop->flag & IOSKIP;
909f00c5086Smillert 		xpos = Xsavepos(xs, xp);
910e55c1b2cSdownsj 		while ((c = getsc()) != 0) {
9117cb960a2Sdownsj 			if (skiptabs) {
9127cb960a2Sdownsj 				if (c == '\t')
9137cb960a2Sdownsj 					continue;
9147cb960a2Sdownsj 				skiptabs = 0;
9157cb960a2Sdownsj 			}
9167cb960a2Sdownsj 			if (c != *eofp)
9177cb960a2Sdownsj 				break;
918f00c5086Smillert 			Xcheck(xs, xp);
919f00c5086Smillert 			Xput(xs, xp, c);
9207cb960a2Sdownsj 			eofp++;
9217cb960a2Sdownsj 		}
9227cb960a2Sdownsj 		/* Allow EOF here so commands with out trailing newlines
9237cb960a2Sdownsj 		 * will work (eg, ksh -c '...', $(...), etc).
9247cb960a2Sdownsj 		 */
925f00c5086Smillert 		if (*eofp == '\0' && (c == 0 || c == '\n')) {
926f00c5086Smillert 			xp = Xrestpos(xs, xp, xpos);
9277cb960a2Sdownsj 			break;
928f00c5086Smillert 		}
9297cb960a2Sdownsj 		ungetsc(c);
930e55c1b2cSdownsj 		while ((c = getsc()) != '\n') {
9317cb960a2Sdownsj 			if (c == 0)
9327cb960a2Sdownsj 				yyerror("here document `%s' unclosed\n", eof);
933f00c5086Smillert 			Xcheck(xs, xp);
934f00c5086Smillert 			Xput(xs, xp, c);
9357cb960a2Sdownsj 		}
936f00c5086Smillert 		Xcheck(xs, xp);
937f00c5086Smillert 		Xput(xs, xp, c);
9387cb960a2Sdownsj 	}
939f00c5086Smillert 	Xput(xs, xp, '\0');
940f00c5086Smillert 	iop->heredoc = Xclose(xs, xp);
941f00c5086Smillert 
942e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
943e55c1b2cSdownsj 		ignore_backslash_newline--;
9447cb960a2Sdownsj }
9457cb960a2Sdownsj 
9467cb960a2Sdownsj void
9477cb960a2Sdownsj yyerror(const char *fmt, ...)
9487cb960a2Sdownsj {
9497cb960a2Sdownsj 	va_list va;
9507cb960a2Sdownsj 
9517cb960a2Sdownsj 	/* pop aliases and re-reads */
9527cb960a2Sdownsj 	while (source->type == SALIAS || source->type == SREREAD)
9537cb960a2Sdownsj 		source = source->next;
9547cb960a2Sdownsj 	source->str = null;	/* zap pending input */
9557cb960a2Sdownsj 
9560e7d3a01Smillert 	error_prefix(true);
95769b9f96bSmillert 	va_start(va, fmt);
9587cb960a2Sdownsj 	shf_vfprintf(shl_out, fmt, va);
9597cb960a2Sdownsj 	va_end(va);
96063ca93eaSmillert 	errorf(NULL);
9617cb960a2Sdownsj }
9627cb960a2Sdownsj 
9637cb960a2Sdownsj /*
9647cb960a2Sdownsj  * input for yylex with alias expansion
9657cb960a2Sdownsj  */
9667cb960a2Sdownsj 
9677cb960a2Sdownsj Source *
968c5d5393cSotto pushs(int type, Area *areap)
9697cb960a2Sdownsj {
9707894b443Smillert 	Source *s;
9717cb960a2Sdownsj 
9728c046d24Snicm 	s = alloc(sizeof(Source), areap);
9737cb960a2Sdownsj 	s->type = type;
9747cb960a2Sdownsj 	s->str = null;
9757cb960a2Sdownsj 	s->start = NULL;
9767cb960a2Sdownsj 	s->line = 0;
977e7b1290aSotto 	s->cmd_offset = 0;
9787cb960a2Sdownsj 	s->errline = 0;
9797cb960a2Sdownsj 	s->file = NULL;
9807cb960a2Sdownsj 	s->flags = 0;
9817cb960a2Sdownsj 	s->next = NULL;
9827cb960a2Sdownsj 	s->areap = areap;
9837cb960a2Sdownsj 	if (type == SFILE || type == SSTDIN) {
9847cb960a2Sdownsj 		char *dummy;
9857cb960a2Sdownsj 		Xinit(s->xs, dummy, 256, s->areap);
9867cb960a2Sdownsj 	} else
9877cb960a2Sdownsj 		memset(&s->xs, 0, sizeof(s->xs));
9887cb960a2Sdownsj 	return s;
9897cb960a2Sdownsj }
9907cb960a2Sdownsj 
9917cb960a2Sdownsj static int
992c5d5393cSotto getsc__(void)
9937cb960a2Sdownsj {
9947894b443Smillert 	Source *s = source;
9957894b443Smillert 	int c;
9967cb960a2Sdownsj 
9977cb960a2Sdownsj 	while ((c = *s->str++) == 0) {
9987cb960a2Sdownsj 		s->str = NULL;		/* return 0 for EOF by default */
9997cb960a2Sdownsj 		switch (s->type) {
10007cb960a2Sdownsj 		case SEOF:
10017cb960a2Sdownsj 			s->str = null;
10027cb960a2Sdownsj 			return 0;
10037cb960a2Sdownsj 
10047cb960a2Sdownsj 		case SSTDIN:
10057cb960a2Sdownsj 		case SFILE:
10067cb960a2Sdownsj 			getsc_line(s);
10077cb960a2Sdownsj 			break;
10087cb960a2Sdownsj 
10097cb960a2Sdownsj 		case SWSTR:
10107cb960a2Sdownsj 			break;
10117cb960a2Sdownsj 
10127cb960a2Sdownsj 		case SSTRING:
10137cb960a2Sdownsj 			break;
10147cb960a2Sdownsj 
10157cb960a2Sdownsj 		case SWORDS:
10167cb960a2Sdownsj 			s->start = s->str = *s->u.strv++;
10177cb960a2Sdownsj 			s->type = SWORDSEP;
10187cb960a2Sdownsj 			break;
10197cb960a2Sdownsj 
10207cb960a2Sdownsj 		case SWORDSEP:
10217cb960a2Sdownsj 			if (*s->u.strv == NULL) {
102296085982Snicm 				s->start = s->str = "\n";
10237cb960a2Sdownsj 				s->type = SEOF;
10247cb960a2Sdownsj 			} else {
102596085982Snicm 				s->start = s->str = " ";
10267cb960a2Sdownsj 				s->type = SWORDS;
10277cb960a2Sdownsj 			}
10287cb960a2Sdownsj 			break;
10297cb960a2Sdownsj 
10307cb960a2Sdownsj 		case SALIAS:
10317cb960a2Sdownsj 			if (s->flags & SF_ALIASEND) {
10327cb960a2Sdownsj 				/* pass on an unused SF_ALIAS flag */
10337cb960a2Sdownsj 				source = s->next;
10347cb960a2Sdownsj 				source->flags |= s->flags & SF_ALIAS;
10357cb960a2Sdownsj 				s = source;
10367a8124d8Sderaadt 			} else if (*s->u.tblp->val.s &&
1037e569fc7cSderaadt 			    isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) {
10387cb960a2Sdownsj 				source = s = s->next;	/* pop source stack */
1039e55c1b2cSdownsj 				/* Note that this alias ended with a space,
1040e55c1b2cSdownsj 				 * enabling alias expansion on the following
1041e55c1b2cSdownsj 				 * word.
1042e55c1b2cSdownsj 				 */
10437cb960a2Sdownsj 				s->flags |= SF_ALIAS;
10447cb960a2Sdownsj 			} else {
1045e55c1b2cSdownsj 				/* At this point, we need to keep the current
1046e55c1b2cSdownsj 				 * alias in the source list so recursive
1047e55c1b2cSdownsj 				 * aliases can be detected and we also need
1048e55c1b2cSdownsj 				 * to return the next character.  Do this
1049e55c1b2cSdownsj 				 * by temporarily popping the alias to get
1050e55c1b2cSdownsj 				 * the next character and then put it back
1051e55c1b2cSdownsj 				 * in the source list with the SF_ALIASEND
1052e55c1b2cSdownsj 				 * flag set.
10537cb960a2Sdownsj 				 */
1054e55c1b2cSdownsj 				source = s->next;	/* pop source stack */
1055e55c1b2cSdownsj 				source->flags |= s->flags & SF_ALIAS;
1056e55c1b2cSdownsj 				c = getsc__();
1057e55c1b2cSdownsj 				if (c) {
10587cb960a2Sdownsj 					s->flags |= SF_ALIASEND;
1059e55c1b2cSdownsj 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1060e55c1b2cSdownsj 					s->start = s->str = s->ugbuf;
1061e55c1b2cSdownsj 					s->next = source;
1062e55c1b2cSdownsj 					source = s;
1063e55c1b2cSdownsj 				} else {
1064e55c1b2cSdownsj 					s = source;
1065e55c1b2cSdownsj 					/* avoid reading eof twice */
1066e55c1b2cSdownsj 					s->str = NULL;
1067e7bc3c65Sdownsj 					break;
1068e55c1b2cSdownsj 				}
10697cb960a2Sdownsj 			}
10707cb960a2Sdownsj 			continue;
10717cb960a2Sdownsj 
10727cb960a2Sdownsj 		case SREREAD:
1073e55c1b2cSdownsj 			if (s->start != s->ugbuf) /* yuck */
10747cb960a2Sdownsj 				afree(s->u.freeme, ATEMP);
10757cb960a2Sdownsj 			source = s = s->next;
10767cb960a2Sdownsj 			continue;
10777cb960a2Sdownsj 		}
10787cb960a2Sdownsj 		if (s->str == NULL) {
10797cb960a2Sdownsj 			s->type = SEOF;
10807cb960a2Sdownsj 			s->start = s->str = null;
10817cb960a2Sdownsj 			return '\0';
10827cb960a2Sdownsj 		}
10837cb960a2Sdownsj 		if (s->flags & SF_ECHO) {
10847cb960a2Sdownsj 			shf_puts(s->str, shl_out);
10857cb960a2Sdownsj 			shf_flush(shl_out);
10867cb960a2Sdownsj 		}
10877cb960a2Sdownsj 	}
10887cb960a2Sdownsj 	return c;
10897cb960a2Sdownsj }
10907cb960a2Sdownsj 
10917cb960a2Sdownsj static void
1092c5d5393cSotto getsc_line(Source *s)
10937cb960a2Sdownsj {
10947cb960a2Sdownsj 	char *xp = Xstring(s->xs, xp);
10957cb960a2Sdownsj 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
10967cb960a2Sdownsj 	int have_tty = interactive && (s->flags & SF_TTY);
10977cb960a2Sdownsj 
10987cb960a2Sdownsj 	/* Done here to ensure nothing odd happens when a timeout occurs */
10997cb960a2Sdownsj 	XcheckN(s->xs, xp, LINE);
11007cb960a2Sdownsj 	*xp = '\0';
11017cb960a2Sdownsj 	s->start = s->str = xp;
11027cb960a2Sdownsj 
11037cb960a2Sdownsj 	if (have_tty && ksh_tmout) {
11047cb960a2Sdownsj 		ksh_tmout_state = TMOUT_READING;
11057cb960a2Sdownsj 		alarm(ksh_tmout);
11067cb960a2Sdownsj 	}
11077cb960a2Sdownsj #ifdef EDIT
11087cb960a2Sdownsj 	if (have_tty && (0
11097cb960a2Sdownsj # ifdef VI
11107cb960a2Sdownsj 	    || Flag(FVI)
11117cb960a2Sdownsj # endif /* VI */
11127cb960a2Sdownsj # ifdef EMACS
11137cb960a2Sdownsj 	    || Flag(FEMACS) || Flag(FGMACS)
11147cb960a2Sdownsj # endif /* EMACS */
11157a8124d8Sderaadt 	    )) {
11167cb960a2Sdownsj 		int nread;
11177cb960a2Sdownsj 
11187cb960a2Sdownsj 		nread = x_read(xp, LINE);
11197cb960a2Sdownsj 		if (nread < 0)	/* read error */
11207cb960a2Sdownsj 			nread = 0;
11217cb960a2Sdownsj 		xp[nread] = '\0';
11227cb960a2Sdownsj 		xp += nread;
11237cb960a2Sdownsj 	}
11247cb960a2Sdownsj 	else
11257cb960a2Sdownsj #endif /* EDIT */
11267cb960a2Sdownsj 	{
11277cb960a2Sdownsj 		if (interactive) {
11287cb960a2Sdownsj 			pprompt(prompt, 0);
11297cb960a2Sdownsj 		} else
11307cb960a2Sdownsj 			s->line++;
11317cb960a2Sdownsj 
11327cb960a2Sdownsj 		while (1) {
11337cb960a2Sdownsj 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
11347cb960a2Sdownsj 
11357a8124d8Sderaadt 			if (!p && shf_error(s->u.shf) &&
11365ef49dcfSmmcc 			    s->u.shf->errno_ == EINTR) {
11377cb960a2Sdownsj 				shf_clearerr(s->u.shf);
11387cb960a2Sdownsj 				if (trap)
11397cb960a2Sdownsj 					runtraps(0);
11407cb960a2Sdownsj 				continue;
11417cb960a2Sdownsj 			}
11427cb960a2Sdownsj 			if (!p || (xp = p, xp[-1] == '\n'))
11437cb960a2Sdownsj 				break;
11447cb960a2Sdownsj 			/* double buffer size */
11457cb960a2Sdownsj 			xp++; /* move past null so doubling works... */
11467cb960a2Sdownsj 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
11477cb960a2Sdownsj 			xp--; /* ...and move back again */
11487cb960a2Sdownsj 		}
11497cb960a2Sdownsj 		/* flush any unwanted input so other programs/builtins
11507cb960a2Sdownsj 		 * can read it.  Not very optimal, but less error prone
11517cb960a2Sdownsj 		 * than flushing else where, dealing with redirections,
11527cb960a2Sdownsj 		 * etc..
11537cb960a2Sdownsj 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
11547cb960a2Sdownsj 		 */
11557cb960a2Sdownsj 		if (s->type == SSTDIN)
11567cb960a2Sdownsj 			shf_flush(s->u.shf);
11577cb960a2Sdownsj 	}
11587cb960a2Sdownsj 	/* XXX: temporary kludge to restore source after a
11597cb960a2Sdownsj 	 * trap may have been executed.
11607cb960a2Sdownsj 	 */
11617cb960a2Sdownsj 	source = s;
116294e42df6Smillert 	if (have_tty && ksh_tmout) {
11637cb960a2Sdownsj 		ksh_tmout_state = TMOUT_EXECUTING;
11647cb960a2Sdownsj 		alarm(0);
11657cb960a2Sdownsj 	}
11667cb960a2Sdownsj 	s->start = s->str = Xstring(s->xs, xp);
11677cb960a2Sdownsj 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
11687cb960a2Sdownsj 	/* Note: if input is all nulls, this is not eof */
11697cb960a2Sdownsj 	if (Xlength(s->xs, xp) == 0) { /* EOF */
11707cb960a2Sdownsj 		if (s->type == SFILE)
11717cb960a2Sdownsj 			shf_fdclose(s->u.shf);
11727cb960a2Sdownsj 		s->str = NULL;
11737cb960a2Sdownsj 	} else if (interactive) {
11747cb960a2Sdownsj #ifdef HISTORY
11757cb960a2Sdownsj 		char *p = Xstring(s->xs, xp);
11767cb960a2Sdownsj 		if (cur_prompt == PS1)
11777cb960a2Sdownsj 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
11787cb960a2Sdownsj 				p++;
11797cb960a2Sdownsj 		if (*p) {
11807cb960a2Sdownsj 			s->line++;
11817cb960a2Sdownsj 			histsave(s->line, s->str, 1);
11827cb960a2Sdownsj 		}
11837cb960a2Sdownsj #endif /* HISTORY */
11847cb960a2Sdownsj 	}
11857cb960a2Sdownsj 	if (interactive)
1186f7654a50Snicm 		set_prompt(PS2, NULL);
11877cb960a2Sdownsj }
11887cb960a2Sdownsj 
1189e7b1290aSotto static char *
1190e7b1290aSotto special_prompt_expand(char *str)
1191e7b1290aSotto {
1192e7b1290aSotto 	char *p = str;
1193e7b1290aSotto 
1194e7b1290aSotto 	while ((p = strstr(p, "\\$")) != NULL) {
11956aa3ef4bSdrahn 		*(p+1) = 'p';
1196e7b1290aSotto 	}
1197e7b1290aSotto 	return str;
1198e7b1290aSotto }
1199e7b1290aSotto 
12007cb960a2Sdownsj void
1201c5d5393cSotto set_prompt(int to, Source *s)
12027cb960a2Sdownsj {
12034300ac9bSmillert 	char *ps1;
12047cb960a2Sdownsj 	Area *saved_atemp;
1205fc804817Smillert 
120694e42df6Smillert 	cur_prompt = to;
120794e42df6Smillert 
120894e42df6Smillert 	switch (to) {
120994e42df6Smillert 	case PS1: /* command */
12104300ac9bSmillert 		ps1 = str_save(str_val(global("PS1")), ATEMP);
12114300ac9bSmillert 		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
12127cb960a2Sdownsj 		newenv(E_ERRH);
12135ae5b57eStedu 		if (sigsetjmp(genv->jbuf, 0)) {
12147cb960a2Sdownsj 			prompt = safe_prompt;
1215dcacb757Sdownsj 			/* Don't print an error - assume it has already
1216dcacb757Sdownsj 			 * been printed.  Reason is we may have forked
1217dcacb757Sdownsj 			 * to run a command and the child may be
1218dcacb757Sdownsj 			 * unwinding its stack through this code as it
1219dcacb757Sdownsj 			 * exits.
1220dcacb757Sdownsj 			 */
1221e7b1290aSotto 		} else {
1222e7b1290aSotto 			/* expand \$ before other substitutions are done */
1223e7b1290aSotto 			char *tmp = special_prompt_expand(ps1);
1224e7b1290aSotto 			prompt = str_save(substitute(tmp, 0), saved_atemp);
1225e7b1290aSotto 		}
12260ee3f80eSotto 		quitenv(NULL);
12277cb960a2Sdownsj 		break;
12287cb960a2Sdownsj 	case PS2: /* command continuation */
12297cb960a2Sdownsj 		prompt = str_val(global("PS2"));
12307cb960a2Sdownsj 		break;
12317cb960a2Sdownsj 	}
12327cb960a2Sdownsj }
12337cb960a2Sdownsj 
1234eb6bc482Sderaadt static int
1235c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
12367cb960a2Sdownsj {
1237eb6bc482Sderaadt 	char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1238a1344090Sderaadt 	int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
123941c26999Sotto 	const char *cp = sp;
1240eb6bc482Sderaadt 	struct tm *tm;
1241eb6bc482Sderaadt 	time_t t;
1242eb6bc482Sderaadt 
1243eb6bc482Sderaadt 	if (*cp && cp[1] == '\r') {
1244eb6bc482Sderaadt 		delimiter = *cp;
1245eb6bc482Sderaadt 		cp += 2;
1246eb6bc482Sderaadt 	}
12477cb960a2Sdownsj 
12487cb960a2Sdownsj 	while (*cp != 0) {
1249a1344090Sderaadt 		delimitthis = 0;
1250eb6bc482Sderaadt 		if (indelimit && *cp != delimiter)
1251eb6bc482Sderaadt 			;
1252eb6bc482Sderaadt 		else if (*cp == '\n' || *cp == '\r') {
1253eb6bc482Sderaadt 			totlen = 0;
1254eb6bc482Sderaadt 			sp = cp + 1;
1255a1344090Sderaadt 		} else if (*cp == '\t') {
1256a1344090Sderaadt 			if (counting)
1257eb6bc482Sderaadt 				totlen = (totlen | 7) + 1;
1258a1344090Sderaadt 		} else if (*cp == delimiter) {
1259eb6bc482Sderaadt 			indelimit = !indelimit;
1260a1344090Sderaadt 			delimitthis = 1;
1261a1344090Sderaadt 		}
1262eb6bc482Sderaadt 
1263eb6bc482Sderaadt 		if (*cp == '\\') {
1264eb6bc482Sderaadt 			cp++;
1265eb6bc482Sderaadt 			if (!*cp)
1266eb6bc482Sderaadt 				break;
1267eb6bc482Sderaadt 			if (Flag(FSH))
1268eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1269eb6bc482Sderaadt 			else switch (*cp) {
1270eb6bc482Sderaadt 			case 'a':	/* '\' 'a' bell */
1271eb6bc482Sderaadt 				strbuf[0] = '\007';
1272eb6bc482Sderaadt 				strbuf[1] = '\0';
1273eb6bc482Sderaadt 				break;
1274eb6bc482Sderaadt 			case 'd':	/* '\' 'd' Dow Mon DD */
1275eb6bc482Sderaadt 				time(&t);
1276eb6bc482Sderaadt 				tm = localtime(&t);
1277eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%a %b %d", tm);
1278eb6bc482Sderaadt 				break;
1279eb6bc482Sderaadt 			case 'D': /* '\' 'D' '{' strftime format '}' */
1280eb6bc482Sderaadt 				p = strchr(cp + 2, '}');
1281eb6bc482Sderaadt 				if (cp[1] != '{' || p == NULL) {
1282eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1283eb6bc482Sderaadt 					    "\\%c", *cp);
1284eb6bc482Sderaadt 					break;
1285eb6bc482Sderaadt 				}
1286eb6bc482Sderaadt 				strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1287eb6bc482Sderaadt 				p = strchr(tmpbuf, '}');
1288eb6bc482Sderaadt 				if (p)
1289eb6bc482Sderaadt 					*p = '\0';
1290eb6bc482Sderaadt 				time(&t);
1291eb6bc482Sderaadt 				tm = localtime(&t);
1292eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, tmpbuf, tm);
1293eb6bc482Sderaadt 				cp = strchr(cp + 2, '}');
1294eb6bc482Sderaadt 				break;
1295eb6bc482Sderaadt 			case 'e':	/* '\' 'e' escape */
1296eb6bc482Sderaadt 				strbuf[0] = '\033';
1297eb6bc482Sderaadt 				strbuf[1] = '\0';
1298eb6bc482Sderaadt 				break;
1299eb6bc482Sderaadt 			case 'h':	/* '\' 'h' shortened hostname */
1300eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1301eb6bc482Sderaadt 				p = strchr(strbuf, '.');
1302eb6bc482Sderaadt 				if (p)
1303eb6bc482Sderaadt 					*p = '\0';
1304eb6bc482Sderaadt 				break;
1305eb6bc482Sderaadt 			case 'H':	/* '\' 'H' full hostname */
1306eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1307eb6bc482Sderaadt 				break;
1308eb6bc482Sderaadt 			case 'j':	/* '\' 'j' number of jobs */
1309eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1310eb6bc482Sderaadt 				    j_njobs());
1311eb6bc482Sderaadt 				break;
1312eb6bc482Sderaadt 			case 'l':	/* '\' 'l' basename of tty */
1313eb6bc482Sderaadt 				p = ttyname(0);
1314eb6bc482Sderaadt 				if (p)
1315eb6bc482Sderaadt 					p = basename(p);
1316eb6bc482Sderaadt 				if (p)
1317eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1318eb6bc482Sderaadt 				break;
1319eb6bc482Sderaadt 			case 'n':	/* '\' 'n' newline */
1320eb6bc482Sderaadt 				strbuf[0] = '\n';
1321eb6bc482Sderaadt 				strbuf[1] = '\0';
13225c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
13235c4a5744Sderaadt 				sp = cp + 1;
1324eb6bc482Sderaadt 				break;
13256aa3ef4bSdrahn 			case 'p':	/* '\' '$' $ or # */
13266aa3ef4bSdrahn 				strbuf[0] = ksheuid ? '$' : '#';
13276aa3ef4bSdrahn 				strbuf[1] = '\0';
13286aa3ef4bSdrahn 				break;
1329eb6bc482Sderaadt 			case 'r':	/* '\' 'r' return */
1330eb6bc482Sderaadt 				strbuf[0] = '\r';
1331eb6bc482Sderaadt 				strbuf[1] = '\0';
13325c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
13335c4a5744Sderaadt 				sp = cp + 1;
1334eb6bc482Sderaadt 				break;
1335eb6bc482Sderaadt 			case 's':	/* '\' 's' basename $0 */
1336eb6bc482Sderaadt 				strlcpy(strbuf, kshname, sizeof strbuf);
1337eb6bc482Sderaadt 				break;
1338eb6bc482Sderaadt 			case 't':	/* '\' 't' 24 hour HH:MM:SS */
1339eb6bc482Sderaadt 				time(&t);
1340eb6bc482Sderaadt 				tm = localtime(&t);
1341eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%T", tm);
1342eb6bc482Sderaadt 				break;
1343eb6bc482Sderaadt 			case 'T':	/* '\' 'T' 12 hour HH:MM:SS */
1344eb6bc482Sderaadt 				time(&t);
1345eb6bc482Sderaadt 				tm = localtime(&t);
1346eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm);
1347eb6bc482Sderaadt 				break;
1348eb6bc482Sderaadt 			case '@':	/* '\' '@' 12 hour am/pm format */
1349eb6bc482Sderaadt 				time(&t);
1350eb6bc482Sderaadt 				tm = localtime(&t);
1351eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%r", tm);
1352eb6bc482Sderaadt 				break;
1353eb6bc482Sderaadt 			case 'A':	/* '\' 'A' 24 hour HH:MM */
1354eb6bc482Sderaadt 				time(&t);
1355eb6bc482Sderaadt 				tm = localtime(&t);
1356eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%R", tm);
1357eb6bc482Sderaadt 				break;
1358eb6bc482Sderaadt 			case 'u':	/* '\' 'u' username */
1359f351fb1cSotto 				strlcpy(strbuf, username, sizeof strbuf);
1360eb6bc482Sderaadt 				break;
1361eb6bc482Sderaadt 			case 'v':	/* '\' 'v' version (short) */
1362eb6bc482Sderaadt 				p = strchr(ksh_version, ' ');
1363eb6bc482Sderaadt 				if (p)
1364eb6bc482Sderaadt 					p = strchr(p + 1, ' ');
1365eb6bc482Sderaadt 				if (p) {
1366eb6bc482Sderaadt 					p++;
1367eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1368eb6bc482Sderaadt 					p = strchr(strbuf, ' ');
1369eb6bc482Sderaadt 					if (p)
1370eb6bc482Sderaadt 						*p = '\0';
1371eb6bc482Sderaadt 				}
1372eb6bc482Sderaadt 				break;
1373eb6bc482Sderaadt 			case 'V':	/* '\' 'V' version (long) */
1374eb6bc482Sderaadt 				strlcpy(strbuf, ksh_version, sizeof strbuf);
1375eb6bc482Sderaadt 				break;
1376eb6bc482Sderaadt 			case 'w':	/* '\' 'w' cwd */
1377eb6bc482Sderaadt 				p = str_val(global("PWD"));
1378ad55f791Sderaadt 				n = strlen(str_val(global("HOME")));
1379ad55f791Sderaadt 				if (strcmp(p, "/") == 0) {
1380ad55f791Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1381ad55f791Sderaadt 				} else if (strcmp(p, str_val(global("HOME"))) == 0) {
1382eb6bc482Sderaadt 					strbuf[0] = '~';
1383eb6bc482Sderaadt 					strbuf[1] = '\0';
1384ad55f791Sderaadt 				} else if (strncmp(p, str_val(global("HOME")), n)
1385ad55f791Sderaadt 				    == 0 && p[n] == '/') {
1386ad55f791Sderaadt 					snprintf(strbuf, sizeof strbuf, "~/%s",
1387ad55f791Sderaadt 					    str_val(global("PWD")) + n + 1);
1388eb6bc482Sderaadt 				} else
1389eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1390eb6bc482Sderaadt 				break;
1391eb6bc482Sderaadt 			case 'W':	/* '\' 'W' basename(cwd) */
1392eb6bc482Sderaadt 				p = str_val(global("PWD"));
139330e9b9efSokan 				if (strcmp(p, str_val(global("HOME"))) == 0) {
139430e9b9efSokan 					strbuf[0] = '~';
139530e9b9efSokan 					strbuf[1] = '\0';
139630e9b9efSokan 				} else
1397eb6bc482Sderaadt 					strlcpy(strbuf, basename(p), sizeof strbuf);
1398eb6bc482Sderaadt 				break;
1399e7b1290aSotto 			case '!':	/* '\' '!' history line number */
1400eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1401eb6bc482Sderaadt 				    source->line + 1);
1402eb6bc482Sderaadt 				break;
1403e7b1290aSotto 			case '#':	/* '\' '#' command line number */
1404eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1405e7b1290aSotto 				    source->line - source->cmd_offset + 1);
1406eb6bc482Sderaadt 				break;
1407eb6bc482Sderaadt 			case '0':	/* '\' '#' '#' ' #' octal numeric handling */
1408eb6bc482Sderaadt 			case '1':
1409eb6bc482Sderaadt 			case '2':
1410eb6bc482Sderaadt 			case '3':
1411eb6bc482Sderaadt 			case '4':
1412eb6bc482Sderaadt 			case '5':
1413eb6bc482Sderaadt 			case '6':
1414eb6bc482Sderaadt 			case '7':
1415eb6bc482Sderaadt 				if ((cp[1] > '7' || cp[1] < '0') ||
1416eb6bc482Sderaadt 				    (cp[2] > '7' || cp[2] < '0')) {
1417eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1418eb6bc482Sderaadt 					    "\\%c", *cp);
1419eb6bc482Sderaadt 					break;
1420eb6bc482Sderaadt 				}
1421064356ecSzhuk 				n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 +
1422064356ecSzhuk 				    (cp[2] - '0');
1423eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%c", n);
1424eb6bc482Sderaadt 				cp += 2;
1425eb6bc482Sderaadt 				break;
1426eb6bc482Sderaadt 			case '\\':	/* '\' '\' */
1427eb6bc482Sderaadt 				strbuf[0] = '\\';
1428eb6bc482Sderaadt 				strbuf[1] = '\0';
1429eb6bc482Sderaadt 				break;
1430a1344090Sderaadt 			case '[': /* '\' '[' .... stop counting */
1431a1344090Sderaadt 				strbuf[0] = '\0';
1432a1344090Sderaadt 				counting = 0;
1433eb6bc482Sderaadt 				break;
1434a1344090Sderaadt 			case ']': /* '\' ']' restart counting */
1435a1344090Sderaadt 				strbuf[0] = '\0';
1436a1344090Sderaadt 				counting = 1;
1437eb6bc482Sderaadt 				break;
1438eb6bc482Sderaadt 
1439eb6bc482Sderaadt 			default:
1440eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1441eb6bc482Sderaadt 				break;
1442eb6bc482Sderaadt 			}
1443eb6bc482Sderaadt 			cp++;
1444eb6bc482Sderaadt 
1445eb6bc482Sderaadt 			str = strbuf;
1446eb6bc482Sderaadt 			len = strlen(str);
1447eb6bc482Sderaadt 			if (ntruncate) {
1448eb6bc482Sderaadt 				if (ntruncate >= len) {
1449eb6bc482Sderaadt 					ntruncate -= len;
1450eb6bc482Sderaadt 					continue;
1451eb6bc482Sderaadt 				}
1452eb6bc482Sderaadt 				str += ntruncate;
1453eb6bc482Sderaadt 				len -= ntruncate;
1454eb6bc482Sderaadt 				ntruncate = 0;
1455eb6bc482Sderaadt 			}
1456eb6bc482Sderaadt 			if (doprint)
1457eb6bc482Sderaadt 				shf_write(str, len, shl_out);
1458a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1459eb6bc482Sderaadt 				totlen += len;
1460eb6bc482Sderaadt 			continue;
1461eb6bc482Sderaadt 		} else if (*cp != '!')
14627cb960a2Sdownsj 			c = *cp++;
14637cb960a2Sdownsj 		else if (*++cp == '!')
14647cb960a2Sdownsj 			c = *cp++;
14657cb960a2Sdownsj 		else {
14667cb960a2Sdownsj 			char *p;
14677cb960a2Sdownsj 
14687cb960a2Sdownsj 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
14697cb960a2Sdownsj 			    source->line + 1);
14707cb960a2Sdownsj 			len = strlen(nbuf);
14717cb960a2Sdownsj 			if (ntruncate) {
14727cb960a2Sdownsj 				if (ntruncate >= len) {
14737cb960a2Sdownsj 					ntruncate -= len;
14747cb960a2Sdownsj 					continue;
14757cb960a2Sdownsj 				}
14767cb960a2Sdownsj 				p += ntruncate;
14777cb960a2Sdownsj 				len -= ntruncate;
14787cb960a2Sdownsj 				ntruncate = 0;
14797cb960a2Sdownsj 			}
1480eb6bc482Sderaadt 			if (doprint)
14817cb960a2Sdownsj 				shf_write(p, len, shl_out);
1482a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1483eb6bc482Sderaadt 				totlen += len;
14847cb960a2Sdownsj 			continue;
14857cb960a2Sdownsj 		}
1486638b9e76Sbeck 		if (counting && ntruncate)
14877cb960a2Sdownsj 			--ntruncate;
1488eb6bc482Sderaadt 		else if (doprint) {
14897cb960a2Sdownsj 			shf_putc(c, shl_out);
14907cb960a2Sdownsj 		}
1491a1344090Sderaadt 		if (counting && !indelimit && !delimitthis)
1492eb6bc482Sderaadt 			totlen++;
1493eb6bc482Sderaadt 	}
1494eb6bc482Sderaadt 	if (doprint)
14957cb960a2Sdownsj 		shf_flush(shl_out);
1496eb6bc482Sderaadt 	if (spp)
1497eb6bc482Sderaadt 		*spp = sp;
1498eb6bc482Sderaadt 	return (totlen);
1499eb6bc482Sderaadt }
1500eb6bc482Sderaadt 
1501eb6bc482Sderaadt void
1502c5d5393cSotto pprompt(const char *cp, int ntruncate)
1503eb6bc482Sderaadt {
1504eb6bc482Sderaadt 	dopprompt(cp, ntruncate, NULL, 1);
1505eb6bc482Sderaadt }
1506eb6bc482Sderaadt 
1507eb6bc482Sderaadt int
1508c5d5393cSotto promptlen(const char *cp, const char **spp)
1509eb6bc482Sderaadt {
1510eb6bc482Sderaadt 	return dopprompt(cp, 0, spp, 0);
15117cb960a2Sdownsj }
15127cb960a2Sdownsj 
15137cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including
15147cb960a2Sdownsj  * the :[-+?=#%] or close-brace.
15157cb960a2Sdownsj  */
15167cb960a2Sdownsj static char *
1517c5d5393cSotto get_brace_var(XString *wsp, char *wp)
15187cb960a2Sdownsj {
15197cb960a2Sdownsj 	enum parse_state {
15207cb960a2Sdownsj 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
15217cb960a2Sdownsj 			   PS_NUMBER, PS_VAR1, PS_END
15227cb960a2Sdownsj 			 }
15237cb960a2Sdownsj 		state;
15247cb960a2Sdownsj 	char c;
15257cb960a2Sdownsj 
15267cb960a2Sdownsj 	state = PS_INITIAL;
15277cb960a2Sdownsj 	while (1) {
1528e55c1b2cSdownsj 		c = getsc();
15297cb960a2Sdownsj 		/* State machine to figure out where the variable part ends. */
15307cb960a2Sdownsj 		switch (state) {
15317cb960a2Sdownsj 		case PS_INITIAL:
15327cb960a2Sdownsj 			if (c == '#') {
15337cb960a2Sdownsj 				state = PS_SAW_HASH;
15347cb960a2Sdownsj 				break;
15357cb960a2Sdownsj 			}
15367eab881bSjaredy 			/* FALLTHROUGH */
15377cb960a2Sdownsj 		case PS_SAW_HASH:
15387cb960a2Sdownsj 			if (letter(c))
15397cb960a2Sdownsj 				state = PS_IDENT;
15407cb960a2Sdownsj 			else if (digit(c))
15417cb960a2Sdownsj 				state = PS_NUMBER;
15427cb960a2Sdownsj 			else if (ctype(c, C_VAR1))
15437cb960a2Sdownsj 				state = PS_VAR1;
15447cb960a2Sdownsj 			else
15457cb960a2Sdownsj 				state = PS_END;
15467cb960a2Sdownsj 			break;
15477cb960a2Sdownsj 		case PS_IDENT:
15487cb960a2Sdownsj 			if (!letnum(c)) {
15497cb960a2Sdownsj 				state = PS_END;
15507cb960a2Sdownsj 				if (c == '[') {
15517cb960a2Sdownsj 					char *tmp, *p;
15527cb960a2Sdownsj 
15537cb960a2Sdownsj 					if (!arraysub(&tmp))
15547cb960a2Sdownsj 						yyerror("missing ]\n");
15557cb960a2Sdownsj 					*wp++ = c;
15567cb960a2Sdownsj 					for (p = tmp; *p; ) {
15577cb960a2Sdownsj 						Xcheck(*wsp, wp);
15587cb960a2Sdownsj 						*wp++ = *p++;
15597cb960a2Sdownsj 					}
15607cb960a2Sdownsj 					afree(tmp, ATEMP);
1561e55c1b2cSdownsj 					c = getsc(); /* the ] */
15627cb960a2Sdownsj 				}
15637cb960a2Sdownsj 			}
15647cb960a2Sdownsj 			break;
15657cb960a2Sdownsj 		case PS_NUMBER:
15667cb960a2Sdownsj 			if (!digit(c))
15677cb960a2Sdownsj 				state = PS_END;
15687cb960a2Sdownsj 			break;
15697cb960a2Sdownsj 		case PS_VAR1:
15707cb960a2Sdownsj 			state = PS_END;
15717cb960a2Sdownsj 			break;
15727cb960a2Sdownsj 		case PS_END: /* keep gcc happy */
15737cb960a2Sdownsj 			break;
15747cb960a2Sdownsj 		}
15757cb960a2Sdownsj 		if (state == PS_END) {
15767cb960a2Sdownsj 			*wp++ = '\0';	/* end of variable part */
15777cb960a2Sdownsj 			ungetsc(c);
15787cb960a2Sdownsj 			break;
15797cb960a2Sdownsj 		}
15807cb960a2Sdownsj 		Xcheck(*wsp, wp);
15817cb960a2Sdownsj 		*wp++ = c;
15827cb960a2Sdownsj 	}
15837cb960a2Sdownsj 	return wp;
15847cb960a2Sdownsj }
15857cb960a2Sdownsj 
15867cb960a2Sdownsj /*
15877cb960a2Sdownsj  * Save an array subscript - returns true if matching bracket found, false
15887cb960a2Sdownsj  * if eof or newline was found.
15897cb960a2Sdownsj  * (Returned string double null terminated)
15907cb960a2Sdownsj  */
15917cb960a2Sdownsj static int
1592c5d5393cSotto arraysub(char **strp)
15937cb960a2Sdownsj {
15947cb960a2Sdownsj 	XString ws;
15957cb960a2Sdownsj 	char	*wp;
15967cb960a2Sdownsj 	char	c;
15977cb960a2Sdownsj 	int	depth = 1;	/* we are just past the initial [ */
15987cb960a2Sdownsj 
15997cb960a2Sdownsj 	Xinit(ws, wp, 32, ATEMP);
16007cb960a2Sdownsj 
16017cb960a2Sdownsj 	do {
1602e55c1b2cSdownsj 		c = getsc();
16037cb960a2Sdownsj 		Xcheck(ws, wp);
16047cb960a2Sdownsj 		*wp++ = c;
16057cb960a2Sdownsj 		if (c == '[')
16067cb960a2Sdownsj 			depth++;
16077cb960a2Sdownsj 		else if (c == ']')
16087cb960a2Sdownsj 			depth--;
16097cb960a2Sdownsj 	} while (depth > 0 && c && c != '\n');
16107cb960a2Sdownsj 
16117cb960a2Sdownsj 	*wp++ = '\0';
16127cb960a2Sdownsj 	*strp = Xclose(ws, wp);
16137cb960a2Sdownsj 
16147cb960a2Sdownsj 	return depth == 0 ? 1 : 0;
16157cb960a2Sdownsj }
16167cb960a2Sdownsj 
16177cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */
16187cb960a2Sdownsj static const char *
1619c5d5393cSotto ungetsc(int c)
16207cb960a2Sdownsj {
1621e55c1b2cSdownsj 	if (backslash_skip)
1622e55c1b2cSdownsj 		backslash_skip--;
16237cb960a2Sdownsj 	/* Don't unget eof... */
16247cb960a2Sdownsj 	if (source->str == null && c == '\0')
16257cb960a2Sdownsj 		return source->str;
16267cb960a2Sdownsj 	if (source->str > source->start)
16277cb960a2Sdownsj 		source->str--;
16287cb960a2Sdownsj 	else {
16297cb960a2Sdownsj 		Source *s;
16307cb960a2Sdownsj 
16317cb960a2Sdownsj 		s = pushs(SREREAD, source->areap);
1632e55c1b2cSdownsj 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1633e55c1b2cSdownsj 		s->start = s->str = s->ugbuf;
16347cb960a2Sdownsj 		s->next = source;
16357cb960a2Sdownsj 		source = s;
16367cb960a2Sdownsj 	}
16377cb960a2Sdownsj 	return source->str;
16387cb960a2Sdownsj }
16397cb960a2Sdownsj 
1640e55c1b2cSdownsj 
16417cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */
16427cb960a2Sdownsj static int
164369b9f96bSmillert getsc_bn(void)
16447cb960a2Sdownsj {
1645e55c1b2cSdownsj 	int c, c2;
1646e55c1b2cSdownsj 
1647e55c1b2cSdownsj 	if (ignore_backslash_newline)
1648e55c1b2cSdownsj 		return getsc_();
1649e55c1b2cSdownsj 
1650e55c1b2cSdownsj 	if (backslash_skip == 1) {
1651e55c1b2cSdownsj 		backslash_skip = 2;
1652e55c1b2cSdownsj 		return getsc_();
1653e55c1b2cSdownsj 	}
1654e55c1b2cSdownsj 
1655e55c1b2cSdownsj 	backslash_skip = 0;
16567cb960a2Sdownsj 
16577cb960a2Sdownsj 	while (1) {
16587cb960a2Sdownsj 		c = getsc_();
1659e55c1b2cSdownsj 		if (c == '\\') {
1660e55c1b2cSdownsj 			if ((c2 = getsc_()) == '\n')
16617cb960a2Sdownsj 				/* ignore the \newline; get the next char... */
1662e55c1b2cSdownsj 				continue;
1663e55c1b2cSdownsj 			ungetsc(c2);
1664e55c1b2cSdownsj 			backslash_skip = 1;
1665e55c1b2cSdownsj 		}
1666e55c1b2cSdownsj 		return c;
16677cb960a2Sdownsj 	}
16687cb960a2Sdownsj }
16693b015934Smillert 
16703b015934Smillert static Lex_state *
1671c5d5393cSotto push_state_(State_info *si, Lex_state *old_end)
16723b015934Smillert {
1673d67c3782Smmcc 	Lex_state *new = areallocarray(NULL, STATE_BSIZE,
1674d67c3782Smmcc 	    sizeof(Lex_state), ATEMP);
16753b015934Smillert 
16763b015934Smillert 	new[0].ls_info.base = old_end;
16773b015934Smillert 	si->base = &new[0];
16783b015934Smillert 	si->end = &new[STATE_BSIZE];
16793b015934Smillert 	return &new[1];
16803b015934Smillert }
16813b015934Smillert 
16823b015934Smillert static Lex_state *
1683c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end)
16843b015934Smillert {
16853b015934Smillert 	Lex_state *old_base = si->base;
16863b015934Smillert 
16873b015934Smillert 	si->base = old_end->ls_info.base - STATE_BSIZE;
16883b015934Smillert 	si->end = old_end->ls_info.base;
16893b015934Smillert 
16903b015934Smillert 	afree(old_base, ATEMP);
16913b015934Smillert 
16927b425235Smillert 	return si->base + STATE_BSIZE - 1;
16933b015934Smillert }
1694