xref: /openbsd/bin/ksh/lex.c (revision 55449a4b)
1*55449a4bSflorian /*	$OpenBSD: lex.c,v 1.80 2024/04/28 16:43:15 florian 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 */
1034c503bcdSmillert uint32_t 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
yylex(int cf)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 					 */
38851c2de25Santon 					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 				 * "`..\"..`".
4307cb960a2Sdownsj 				 */
4313b015934Smillert 				statep->ls_sbquote.indquotes = 0;
4323b015934Smillert 				Lex_state *s = statep;
4333b015934Smillert 				Lex_state *base = state_info.base;
4343b015934Smillert 				while (1) {
4353b015934Smillert 					for (; s != base; s--) {
4363b015934Smillert 						if (s->ls_state == SDQUOTE) {
4373b015934Smillert 							statep->ls_sbquote.indquotes = 1;
4383b015934Smillert 							break;
4393b015934Smillert 						}
4403b015934Smillert 					}
4413b015934Smillert 					if (s != base)
4423b015934Smillert 						break;
4433b015934Smillert 					if (!(s = s->ls_info.base))
4443b015934Smillert 						break;
4453b015934Smillert 					base = s-- - STATE_BSIZE;
4463b015934Smillert 				}
4477cb960a2Sdownsj 				break;
4487cb960a2Sdownsj 			default:
4497cb960a2Sdownsj 				*wp++ = CHAR, *wp++ = c;
4507cb960a2Sdownsj 			}
4517cb960a2Sdownsj 			break;
4527cb960a2Sdownsj 
4537cb960a2Sdownsj 		case SSQUOTE:
4547cb960a2Sdownsj 			if (c == '\'') {
4553b015934Smillert 				POP_STATE();
45622a9cf94Sguenther 				if (state == SBRACEQ) {
45722a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
45822a9cf94Sguenther 					break;
45922a9cf94Sguenther 				}
4607cb960a2Sdownsj 				*wp++ = CQUOTE;
461e55c1b2cSdownsj 				ignore_backslash_newline--;
4627cb960a2Sdownsj 			} else
4637cb960a2Sdownsj 				*wp++ = QCHAR, *wp++ = c;
4647cb960a2Sdownsj 			break;
4657cb960a2Sdownsj 
4667cb960a2Sdownsj 		case SDQUOTE:
4677cb960a2Sdownsj 			if (c == '"') {
4683b015934Smillert 				POP_STATE();
4697cb960a2Sdownsj 				*wp++ = CQUOTE;
4707cb960a2Sdownsj 			} else
4717cb960a2Sdownsj 				goto Subst;
4727cb960a2Sdownsj 			break;
4737cb960a2Sdownsj 
4743b015934Smillert 		case SCSPAREN: /* $( .. ) */
4757cb960a2Sdownsj 			/* todo: deal with $(...) quoting properly
4767cb960a2Sdownsj 			 * kludge to partly fake quoting inside $(..): doesn't
4777cb960a2Sdownsj 			 * really work because nested $(..) or ${..} inside
4787cb960a2Sdownsj 			 * double quotes aren't dealt with.
4797cb960a2Sdownsj 			 */
4803b015934Smillert 			switch (statep->ls_scsparen.csstate) {
4817cb960a2Sdownsj 			case 0: /* normal */
4827cb960a2Sdownsj 				switch (c) {
4837cb960a2Sdownsj 				case '(':
4843b015934Smillert 					statep->ls_scsparen.nparen++;
4857cb960a2Sdownsj 					break;
4867cb960a2Sdownsj 				case ')':
4873b015934Smillert 					statep->ls_scsparen.nparen--;
4887cb960a2Sdownsj 					break;
4897cb960a2Sdownsj 				case '\\':
4903b015934Smillert 					statep->ls_scsparen.csstate = 1;
4917cb960a2Sdownsj 					break;
4927cb960a2Sdownsj 				case '"':
4933b015934Smillert 					statep->ls_scsparen.csstate = 2;
4947cb960a2Sdownsj 					break;
4957cb960a2Sdownsj 				case '\'':
4963b015934Smillert 					statep->ls_scsparen.csstate = 4;
497e55c1b2cSdownsj 					ignore_backslash_newline++;
4987cb960a2Sdownsj 					break;
4997cb960a2Sdownsj 				}
5007cb960a2Sdownsj 				break;
5017cb960a2Sdownsj 
5027cb960a2Sdownsj 			case 1: /* backslash in normal mode */
5037cb960a2Sdownsj 			case 3: /* backslash in double quotes */
5043b015934Smillert 				--statep->ls_scsparen.csstate;
5057cb960a2Sdownsj 				break;
5067cb960a2Sdownsj 
5077cb960a2Sdownsj 			case 2: /* double quotes */
5087cb960a2Sdownsj 				if (c == '"')
5093b015934Smillert 					statep->ls_scsparen.csstate = 0;
5107cb960a2Sdownsj 				else if (c == '\\')
5113b015934Smillert 					statep->ls_scsparen.csstate = 3;
5127cb960a2Sdownsj 				break;
5137cb960a2Sdownsj 
5147cb960a2Sdownsj 			case 4: /* single quotes */
515e55c1b2cSdownsj 				if (c == '\'') {
5163b015934Smillert 					statep->ls_scsparen.csstate = 0;
517e55c1b2cSdownsj 					ignore_backslash_newline--;
518e55c1b2cSdownsj 				}
5197cb960a2Sdownsj 				break;
5207cb960a2Sdownsj 			}
5213b015934Smillert 			if (statep->ls_scsparen.nparen == 0) {
5223b015934Smillert 				POP_STATE();
5237cb960a2Sdownsj 				*wp++ = 0; /* end of COMSUB */
5247cb960a2Sdownsj 			} else
5257cb960a2Sdownsj 				*wp++ = c;
5267cb960a2Sdownsj 			break;
5277cb960a2Sdownsj 
5283b015934Smillert 		case SASPAREN: /* $(( .. )) */
5297cb960a2Sdownsj 			/* todo: deal with $((...); (...)) properly */
5307cb960a2Sdownsj 			/* XXX should nest using existing state machine
5317cb960a2Sdownsj 			 * (embed "..", $(...), etc.) */
5327cb960a2Sdownsj 			if (c == '(')
5333b015934Smillert 				statep->ls_sasparen.nparen++;
5347cb960a2Sdownsj 			else if (c == ')') {
5353b015934Smillert 				statep->ls_sasparen.nparen--;
5363b015934Smillert 				if (statep->ls_sasparen.nparen == 1) {
5377cb960a2Sdownsj 					/*(*/
5387cb960a2Sdownsj 					if ((c2 = getsc()) == ')') {
5393b015934Smillert 						POP_STATE();
5407cb960a2Sdownsj 						*wp++ = 0; /* end of EXPRSUB */
5417cb960a2Sdownsj 						break;
5427cb960a2Sdownsj 					} else {
5433b015934Smillert 						char *s;
5443b015934Smillert 
5457cb960a2Sdownsj 						ungetsc(c2);
5467cb960a2Sdownsj 						/* mismatched parenthesis -
5477cb960a2Sdownsj 						 * assume we were really
5487cb960a2Sdownsj 						 * parsing a $(..) expression
5497cb960a2Sdownsj 						 */
5503b015934Smillert 						s = Xrestpos(ws, wp,
5513b015934Smillert 						    statep->ls_sasparen.start);
5523b015934Smillert 						memmove(s + 1, s, wp - s);
5533b015934Smillert 						*s++ = COMSUB;
5543b015934Smillert 						*s = '('; /*)*/
5557cb960a2Sdownsj 						wp++;
5563b015934Smillert 						statep->ls_scsparen.nparen = 1;
5573b015934Smillert 						statep->ls_scsparen.csstate = 0;
5587a8124d8Sderaadt 						state = statep->ls_state =
5597a8124d8Sderaadt 						    SCSPAREN;
5607cb960a2Sdownsj 					}
5617cb960a2Sdownsj 				}
5627cb960a2Sdownsj 			}
5637cb960a2Sdownsj 			*wp++ = c;
5647cb960a2Sdownsj 			break;
5657cb960a2Sdownsj 
56622a9cf94Sguenther 		case SBRACEQ:
567ca3ae29fSczarkoff 			/*{*/
568ca3ae29fSczarkoff 			if (c == '}') {
569ca3ae29fSczarkoff 				POP_STATE();
570ca3ae29fSczarkoff 				*wp++ = CSUBST;
571ca3ae29fSczarkoff 				*wp++ = /*{*/ '}';
572ca3ae29fSczarkoff 			} else
573ca3ae29fSczarkoff 				goto Sbase2;
574ca3ae29fSczarkoff 			break;
575ca3ae29fSczarkoff 
5767cb960a2Sdownsj 		case SBRACE:
5777cb960a2Sdownsj 			/*{*/
5787cb960a2Sdownsj 			if (c == '}') {
5793b015934Smillert 				POP_STATE();
5807cb960a2Sdownsj 				*wp++ = CSUBST;
5813b015934Smillert 				*wp++ = /*{*/ '}';
5827cb960a2Sdownsj 			} else
5837cb960a2Sdownsj 				goto Sbase1;
5847cb960a2Sdownsj 			break;
5857cb960a2Sdownsj 
5867cb960a2Sdownsj 		case STBRACE:
5873b015934Smillert 			/* Same as SBRACE, except (,|,) treated specially */
5887cb960a2Sdownsj 			/*{*/
5897cb960a2Sdownsj 			if (c == '}') {
5903b015934Smillert 				POP_STATE();
5917cb960a2Sdownsj 				*wp++ = CSUBST;
5923b015934Smillert 				*wp++ = /*{*/ '}';
5937cb960a2Sdownsj 			} else if (c == '|') {
5947cb960a2Sdownsj 				*wp++ = SPAT;
5953b015934Smillert 			} else if (c == '(') {
5963b015934Smillert 				*wp++ = OPAT;
5973b015934Smillert 				*wp++ = ' ';	/* simile for @ */
5983b015934Smillert 				PUSH_STATE(SPATTERN);
5997cb960a2Sdownsj 			} else
6007cb960a2Sdownsj 				goto Sbase1;
6017cb960a2Sdownsj 			break;
6027cb960a2Sdownsj 
6037cb960a2Sdownsj 		case SBQUOTE:
6047cb960a2Sdownsj 			if (c == '`') {
6057cb960a2Sdownsj 				*wp++ = 0;
6063b015934Smillert 				POP_STATE();
6077cb960a2Sdownsj 			} else if (c == '\\') {
6087cb960a2Sdownsj 				switch (c = getsc()) {
6097cb960a2Sdownsj 				case '\\':
6107cb960a2Sdownsj 				case '$': case '`':
6117cb960a2Sdownsj 					*wp++ = c;
6127cb960a2Sdownsj 					break;
6137cb960a2Sdownsj 				case '"':
6143b015934Smillert 					if (statep->ls_sbquote.indquotes) {
6157cb960a2Sdownsj 						*wp++ = c;
6167cb960a2Sdownsj 						break;
6177cb960a2Sdownsj 					}
6187eab881bSjaredy 					/* FALLTHROUGH */
6197cb960a2Sdownsj 				default:
620e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
6217cb960a2Sdownsj 						*wp++ = '\\';
6227cb960a2Sdownsj 						*wp++ = c;
623e55c1b2cSdownsj 					}
6247cb960a2Sdownsj 					break;
6257cb960a2Sdownsj 				}
6267cb960a2Sdownsj 			} else
6277cb960a2Sdownsj 				*wp++ = c;
6287cb960a2Sdownsj 			break;
6297cb960a2Sdownsj 
6307cb960a2Sdownsj 		case SWORD:	/* ONEWORD */
6317cb960a2Sdownsj 			goto Subst;
6327cb960a2Sdownsj 
6333b015934Smillert 		case SLETPAREN:	/* LETEXPR: (( ... )) */
6347cb960a2Sdownsj 			/*(*/
6357cb960a2Sdownsj 			if (c == ')') {
6363b015934Smillert 				if (statep->ls_sletparen.nparen > 0)
6373b015934Smillert 				    --statep->ls_sletparen.nparen;
6387cb960a2Sdownsj 				/*(*/
6397cb960a2Sdownsj 				else if ((c2 = getsc()) == ')') {
6407cb960a2Sdownsj 					c = 0;
6417cb960a2Sdownsj 					*wp++ = CQUOTE;
6427cb960a2Sdownsj 					goto Done;
6437cb960a2Sdownsj 				} else
6447cb960a2Sdownsj 					ungetsc(c2);
6457cb960a2Sdownsj 			} else if (c == '(')
6467cb960a2Sdownsj 				/* parenthesis inside quotes and backslashes
6477cb960a2Sdownsj 				 * are lost, but at&t ksh doesn't count them
6487cb960a2Sdownsj 				 * either
6497cb960a2Sdownsj 				 */
6503b015934Smillert 				++statep->ls_sletparen.nparen;
6517cb960a2Sdownsj 			goto Sbase2;
6527cb960a2Sdownsj 
6537cb960a2Sdownsj 		case SHEREDELIM:	/* <<,<<- delimiter */
6547cb960a2Sdownsj 			/* XXX chuck this state (and the next) - use
6557cb960a2Sdownsj 			 * the existing states ($ and \`..` should be
6567cb960a2Sdownsj 			 * stripped of their specialness after the
6577cb960a2Sdownsj 			 * fact).
6587cb960a2Sdownsj 			 */
6597cb960a2Sdownsj 			/* here delimiters need a special case since
6607cb960a2Sdownsj 			 * $ and `..` are not to be treated specially
6617cb960a2Sdownsj 			 */
6627cb960a2Sdownsj 			if (c == '\\') {
6637cb960a2Sdownsj 				c = getsc();
664e55c1b2cSdownsj 				if (c) { /* trailing \ is lost */
6657cb960a2Sdownsj 					*wp++ = QCHAR;
6667cb960a2Sdownsj 					*wp++ = c;
6677cb960a2Sdownsj 				}
6687cb960a2Sdownsj 			} else if (c == '\'') {
6693b015934Smillert 				PUSH_STATE(SSQUOTE);
6707cb960a2Sdownsj 				*wp++ = OQUOTE;
671e55c1b2cSdownsj 				ignore_backslash_newline++;
6727cb960a2Sdownsj 			} else if (c == '"') {
6733b015934Smillert 				state = statep->ls_state = SHEREDQUOTE;
6747cb960a2Sdownsj 				*wp++ = OQUOTE;
6757cb960a2Sdownsj 			} else {
6767cb960a2Sdownsj 				*wp++ = CHAR;
6777cb960a2Sdownsj 				*wp++ = c;
6787cb960a2Sdownsj 			}
6797cb960a2Sdownsj 			break;
6807cb960a2Sdownsj 
6817cb960a2Sdownsj 		case SHEREDQUOTE:	/* " in <<,<<- delimiter */
6827cb960a2Sdownsj 			if (c == '"') {
6837cb960a2Sdownsj 				*wp++ = CQUOTE;
6843b015934Smillert 				state = statep->ls_state = SHEREDELIM;
6857cb960a2Sdownsj 			} else {
686e55c1b2cSdownsj 				if (c == '\\') {
687e55c1b2cSdownsj 					switch (c = getsc()) {
688e55c1b2cSdownsj 					case '\\': case '"':
689e55c1b2cSdownsj 					case '$': case '`':
6907cb960a2Sdownsj 						break;
691e55c1b2cSdownsj 					default:
692e55c1b2cSdownsj 						if (c) { /* trailing \ lost */
693e55c1b2cSdownsj 							*wp++ = CHAR;
694e55c1b2cSdownsj 							*wp++ = '\\';
695e55c1b2cSdownsj 						}
696e55c1b2cSdownsj 						break;
697e55c1b2cSdownsj 					}
698e55c1b2cSdownsj 				}
6997cb960a2Sdownsj 				*wp++ = CHAR;
7007cb960a2Sdownsj 				*wp++ = c;
7017cb960a2Sdownsj 			}
7027cb960a2Sdownsj 			break;
7037cb960a2Sdownsj 
7047cb960a2Sdownsj 		case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
7057cb960a2Sdownsj 			if ( /*(*/ c == ')') {
7067cb960a2Sdownsj 				*wp++ = CPAT;
7073b015934Smillert 				POP_STATE();
7083b015934Smillert 			} else if (c == '|') {
7097cb960a2Sdownsj 				*wp++ = SPAT;
7103b015934Smillert 			} else if (c == '(') {
7113b015934Smillert 				*wp++ = OPAT;
7123b015934Smillert 				*wp++ = ' ';	/* simile for @ */
7133b015934Smillert 				PUSH_STATE(SPATTERN);
7143b015934Smillert 			} else
7157cb960a2Sdownsj 				goto Sbase1;
7167cb960a2Sdownsj 			break;
7177cb960a2Sdownsj 		}
7187cb960a2Sdownsj 	}
7197cb960a2Sdownsj Done:
7207cb960a2Sdownsj 	Xcheck(ws, wp);
7213b015934Smillert 	if (statep != &states[1])
7223b015934Smillert 		/* XXX figure out what is missing */
7237cb960a2Sdownsj 		yyerror("no closing quote\n");
7247cb960a2Sdownsj 
7257cb960a2Sdownsj 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
7267cb960a2Sdownsj 	if (state == SHEREDELIM)
7277cb960a2Sdownsj 		state = SBASE;
7287cb960a2Sdownsj 
7293b015934Smillert 	dp = Xstring(ws, wp);
7307a8124d8Sderaadt 	if ((c == '<' || c == '>') && state == SBASE &&
7317a8124d8Sderaadt 	    ((c2 = Xlength(ws, wp)) == 0 ||
7327a8124d8Sderaadt 	    (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
7338c046d24Snicm 		struct ioword *iop = alloc(sizeof(*iop), ATEMP);
7347cb960a2Sdownsj 
7353b015934Smillert 		if (c2 == 2)
7363b015934Smillert 			iop->unit = dp[1] - '0';
7377cb960a2Sdownsj 		else
7383b015934Smillert 			iop->unit = c == '>'; /* 0 for <, 1 for > */
7397cb960a2Sdownsj 
7407cb960a2Sdownsj 		c2 = getsc();
7417cb960a2Sdownsj 		/* <<, >>, <> are ok, >< is not */
7427cb960a2Sdownsj 		if (c == c2 || (c == '<' && c2 == '>')) {
7437cb960a2Sdownsj 			iop->flag = c == c2 ?
7447cb960a2Sdownsj 			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
74518bbba6bSmillert 			if (iop->flag == IOHERE) {
746e55c1b2cSdownsj 				if ((c2 = getsc()) == '-')
7477cb960a2Sdownsj 					iop->flag |= IOSKIP;
7487cb960a2Sdownsj 				else
7497cb960a2Sdownsj 					ungetsc(c2);
75018bbba6bSmillert 			}
7517cb960a2Sdownsj 		} else if (c2 == '&')
7527cb960a2Sdownsj 			iop->flag = IODUP | (c == '<' ? IORDUP : 0);
7537cb960a2Sdownsj 		else {
7547cb960a2Sdownsj 			iop->flag = c == '>' ? IOWRITE : IOREAD;
7557cb960a2Sdownsj 			if (c == '>' && c2 == '|')
7567cb960a2Sdownsj 				iop->flag |= IOCLOB;
7577cb960a2Sdownsj 			else
7587cb960a2Sdownsj 				ungetsc(c2);
7597cb960a2Sdownsj 		}
7607cb960a2Sdownsj 
761355ffa75Stedu 		iop->name = NULL;
762355ffa75Stedu 		iop->delim = NULL;
763355ffa75Stedu 		iop->heredoc = NULL;
7643b015934Smillert 		Xfree(ws, wp);	/* free word */
7657cb960a2Sdownsj 		yylval.iop = iop;
7667cb960a2Sdownsj 		return REDIR;
7677cb960a2Sdownsj 	}
7683b015934Smillert 
7693b015934Smillert 	if (wp == dp && state == SBASE) {
7703b015934Smillert 		Xfree(ws, wp);	/* free word */
7713b015934Smillert 		/* no word, process LEX1 character */
7723b015934Smillert 		switch (c) {
7733b015934Smillert 		default:
7743b015934Smillert 			return c;
7753b015934Smillert 
7763b015934Smillert 		case '|':
7773b015934Smillert 		case '&':
7783b015934Smillert 		case ';':
7793b015934Smillert 			if ((c2 = getsc()) == c)
7803b015934Smillert 				c = (c == ';') ? BREAK :
7813b015934Smillert 				    (c == '|') ? LOGOR :
7823b015934Smillert 				    (c == '&') ? LOGAND :
7833b015934Smillert 				    YYERRCODE;
7843b015934Smillert 			else if (c == '|' && c2 == '&')
7853b015934Smillert 				c = COPROC;
7863b015934Smillert 			else
7873b015934Smillert 				ungetsc(c2);
7883b015934Smillert 			return c;
7893b015934Smillert 
7907cb960a2Sdownsj 		case '\n':
7917cb960a2Sdownsj 			gethere();
7927cb960a2Sdownsj 			if (cf & CONTIN)
7937cb960a2Sdownsj 				goto Again;
7947cb960a2Sdownsj 			return c;
7957cb960a2Sdownsj 
7967cb960a2Sdownsj 		case '(':  /*)*/
797b69beb66Sgrr 			if (!Flag(FSH)) {
7987cb960a2Sdownsj 				if ((c2 = getsc()) == '(') /*)*/
7993b015934Smillert 					/* XXX need to handle ((...); (...)) */
8007cb960a2Sdownsj 					c = MDPAREN;
8017cb960a2Sdownsj 				else
8027cb960a2Sdownsj 					ungetsc(c2);
803b69beb66Sgrr 			}
8047cb960a2Sdownsj 			return c;
8057cb960a2Sdownsj 		  /*(*/
8067cb960a2Sdownsj 		case ')':
8077cb960a2Sdownsj 			return c;
8087cb960a2Sdownsj 		}
8097cb960a2Sdownsj 	}
8107cb960a2Sdownsj 
8117cb960a2Sdownsj 	*wp++ = EOS;		/* terminate word */
8127cb960a2Sdownsj 	yylval.cp = Xclose(ws, wp);
81394e42df6Smillert 	if (state == SWORD || state == SLETPAREN)	/* ONEWORD? */
8147cb960a2Sdownsj 		return LWORD;
8157cb960a2Sdownsj 	ungetsc(c);		/* unget terminator */
8167cb960a2Sdownsj 
8177cb960a2Sdownsj 	/* copy word to unprefixed string ident */
8187cb960a2Sdownsj 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
8197cb960a2Sdownsj 		*dp++ = *sp++;
82012e7fb2dSjmc 	/* Make sure the ident array stays '\0' padded */
8217cb960a2Sdownsj 	memset(dp, 0, (ident+IDENT) - dp + 1);
8227cb960a2Sdownsj 	if (c != EOS)
8237cb960a2Sdownsj 		*ident = '\0';	/* word is not unquoted */
8247cb960a2Sdownsj 
8257cb960a2Sdownsj 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
8267cb960a2Sdownsj 		struct tbl *p;
8277cb960a2Sdownsj 		int h = hash(ident);
8287cb960a2Sdownsj 
8297cb960a2Sdownsj 		/* { */
8306df3ee40Sotto 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
8317a8124d8Sderaadt 		    (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
8327cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8337cb960a2Sdownsj 			return p->val.i;
8347cb960a2Sdownsj 		}
8356df3ee40Sotto 		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
8367a8124d8Sderaadt 		    (p->flag & ISSET)) {
8377894b443Smillert 			Source *s;
8387cb960a2Sdownsj 
8397cb960a2Sdownsj 			for (s = source; s->type == SALIAS; s = s->next)
8407cb960a2Sdownsj 				if (s->u.tblp == p)
8417cb960a2Sdownsj 					return LWORD;
8427cb960a2Sdownsj 			/* push alias expansion */
8437cb960a2Sdownsj 			s = pushs(SALIAS, source->areap);
8447cb960a2Sdownsj 			s->start = s->str = p->val.s;
8457cb960a2Sdownsj 			s->u.tblp = p;
8467cb960a2Sdownsj 			s->next = source;
8477cb960a2Sdownsj 			source = s;
8487cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8497cb960a2Sdownsj 			goto Again;
8507cb960a2Sdownsj 		}
8517cb960a2Sdownsj 	}
8527cb960a2Sdownsj 
8537cb960a2Sdownsj 	return LWORD;
8547cb960a2Sdownsj }
8557cb960a2Sdownsj 
8567cb960a2Sdownsj static void
gethere(void)857c5d5393cSotto gethere(void)
8587cb960a2Sdownsj {
8597894b443Smillert 	struct ioword **p;
8607cb960a2Sdownsj 
8617cb960a2Sdownsj 	for (p = heres; p < herep; p++)
8627cb960a2Sdownsj 		readhere(*p);
8637cb960a2Sdownsj 	herep = heres;
8647cb960a2Sdownsj }
8657cb960a2Sdownsj 
8667cb960a2Sdownsj /*
8677cb960a2Sdownsj  * read "<<word" text into temp file
8687cb960a2Sdownsj  */
8697cb960a2Sdownsj 
8707cb960a2Sdownsj static void
readhere(struct ioword * iop)871c5d5393cSotto readhere(struct ioword *iop)
8727cb960a2Sdownsj {
8737894b443Smillert 	int c;
8747cb960a2Sdownsj 	char *volatile eof;
8757cb960a2Sdownsj 	char *eofp;
876e55c1b2cSdownsj 	int skiptabs;
877f00c5086Smillert 	XString xs;
878f00c5086Smillert 	char *xp;
879f00c5086Smillert 	int xpos;
8807cb960a2Sdownsj 
8817cb960a2Sdownsj 	eof = evalstr(iop->delim, 0);
8827cb960a2Sdownsj 
883e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
884e55c1b2cSdownsj 		ignore_backslash_newline++;
885e55c1b2cSdownsj 
886f00c5086Smillert 	Xinit(xs, xp, 256, ATEMP);
887f00c5086Smillert 
8887cb960a2Sdownsj 	for (;;) {
8897cb960a2Sdownsj 		eofp = eof;
8907cb960a2Sdownsj 		skiptabs = iop->flag & IOSKIP;
891f00c5086Smillert 		xpos = Xsavepos(xs, xp);
892e55c1b2cSdownsj 		while ((c = getsc()) != 0) {
8937cb960a2Sdownsj 			if (skiptabs) {
8947cb960a2Sdownsj 				if (c == '\t')
8957cb960a2Sdownsj 					continue;
8967cb960a2Sdownsj 				skiptabs = 0;
8977cb960a2Sdownsj 			}
8987cb960a2Sdownsj 			if (c != *eofp)
8997cb960a2Sdownsj 				break;
900f00c5086Smillert 			Xcheck(xs, xp);
901f00c5086Smillert 			Xput(xs, xp, c);
9027cb960a2Sdownsj 			eofp++;
9037cb960a2Sdownsj 		}
9047cb960a2Sdownsj 		/* Allow EOF here so commands with out trailing newlines
9057cb960a2Sdownsj 		 * will work (eg, ksh -c '...', $(...), etc).
9067cb960a2Sdownsj 		 */
907f00c5086Smillert 		if (*eofp == '\0' && (c == 0 || c == '\n')) {
908f00c5086Smillert 			xp = Xrestpos(xs, xp, xpos);
9097cb960a2Sdownsj 			break;
910f00c5086Smillert 		}
9117cb960a2Sdownsj 		ungetsc(c);
912e55c1b2cSdownsj 		while ((c = getsc()) != '\n') {
9137cb960a2Sdownsj 			if (c == 0)
9147cb960a2Sdownsj 				yyerror("here document `%s' unclosed\n", eof);
915f00c5086Smillert 			Xcheck(xs, xp);
916f00c5086Smillert 			Xput(xs, xp, c);
9177cb960a2Sdownsj 		}
918f00c5086Smillert 		Xcheck(xs, xp);
919f00c5086Smillert 		Xput(xs, xp, c);
9207cb960a2Sdownsj 	}
921f00c5086Smillert 	Xput(xs, xp, '\0');
922f00c5086Smillert 	iop->heredoc = Xclose(xs, xp);
923f00c5086Smillert 
924e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
925e55c1b2cSdownsj 		ignore_backslash_newline--;
9267cb960a2Sdownsj }
9277cb960a2Sdownsj 
9287cb960a2Sdownsj void
yyerror(const char * fmt,...)9297cb960a2Sdownsj yyerror(const char *fmt, ...)
9307cb960a2Sdownsj {
9317cb960a2Sdownsj 	va_list va;
9327cb960a2Sdownsj 
9337cb960a2Sdownsj 	/* pop aliases and re-reads */
9347cb960a2Sdownsj 	while (source->type == SALIAS || source->type == SREREAD)
9357cb960a2Sdownsj 		source = source->next;
9367cb960a2Sdownsj 	source->str = null;	/* zap pending input */
9377cb960a2Sdownsj 
9380e7d3a01Smillert 	error_prefix(true);
93969b9f96bSmillert 	va_start(va, fmt);
9407cb960a2Sdownsj 	shf_vfprintf(shl_out, fmt, va);
9417cb960a2Sdownsj 	va_end(va);
94263ca93eaSmillert 	errorf(NULL);
9437cb960a2Sdownsj }
9447cb960a2Sdownsj 
9457cb960a2Sdownsj /*
9467cb960a2Sdownsj  * input for yylex with alias expansion
9477cb960a2Sdownsj  */
9487cb960a2Sdownsj 
9497cb960a2Sdownsj Source *
pushs(int type,Area * areap)950c5d5393cSotto pushs(int type, Area *areap)
9517cb960a2Sdownsj {
9527894b443Smillert 	Source *s;
9537cb960a2Sdownsj 
9548c046d24Snicm 	s = alloc(sizeof(Source), areap);
9557cb960a2Sdownsj 	s->type = type;
9567cb960a2Sdownsj 	s->str = null;
9577cb960a2Sdownsj 	s->start = NULL;
9587cb960a2Sdownsj 	s->line = 0;
959e7b1290aSotto 	s->cmd_offset = 0;
9607cb960a2Sdownsj 	s->errline = 0;
9617cb960a2Sdownsj 	s->file = NULL;
9627cb960a2Sdownsj 	s->flags = 0;
9637cb960a2Sdownsj 	s->next = NULL;
9647cb960a2Sdownsj 	s->areap = areap;
9657cb960a2Sdownsj 	if (type == SFILE || type == SSTDIN) {
9667cb960a2Sdownsj 		char *dummy;
9677cb960a2Sdownsj 		Xinit(s->xs, dummy, 256, s->areap);
9687cb960a2Sdownsj 	} else
9697cb960a2Sdownsj 		memset(&s->xs, 0, sizeof(s->xs));
9707cb960a2Sdownsj 	return s;
9717cb960a2Sdownsj }
9727cb960a2Sdownsj 
9737cb960a2Sdownsj static int
getsc__(void)974c5d5393cSotto getsc__(void)
9757cb960a2Sdownsj {
9767894b443Smillert 	Source *s = source;
9777894b443Smillert 	int c;
9787cb960a2Sdownsj 
9797cb960a2Sdownsj 	while ((c = *s->str++) == 0) {
9807cb960a2Sdownsj 		s->str = NULL;		/* return 0 for EOF by default */
9817cb960a2Sdownsj 		switch (s->type) {
9827cb960a2Sdownsj 		case SEOF:
9837cb960a2Sdownsj 			s->str = null;
9847cb960a2Sdownsj 			return 0;
9857cb960a2Sdownsj 
9867cb960a2Sdownsj 		case SSTDIN:
9877cb960a2Sdownsj 		case SFILE:
9887cb960a2Sdownsj 			getsc_line(s);
9897cb960a2Sdownsj 			break;
9907cb960a2Sdownsj 
9917cb960a2Sdownsj 		case SWSTR:
9927cb960a2Sdownsj 			break;
9937cb960a2Sdownsj 
9947cb960a2Sdownsj 		case SSTRING:
9957cb960a2Sdownsj 			break;
9967cb960a2Sdownsj 
9977cb960a2Sdownsj 		case SWORDS:
9987cb960a2Sdownsj 			s->start = s->str = *s->u.strv++;
9997cb960a2Sdownsj 			s->type = SWORDSEP;
10007cb960a2Sdownsj 			break;
10017cb960a2Sdownsj 
10027cb960a2Sdownsj 		case SWORDSEP:
10037cb960a2Sdownsj 			if (*s->u.strv == NULL) {
100496085982Snicm 				s->start = s->str = "\n";
10057cb960a2Sdownsj 				s->type = SEOF;
10067cb960a2Sdownsj 			} else {
100796085982Snicm 				s->start = s->str = " ";
10087cb960a2Sdownsj 				s->type = SWORDS;
10097cb960a2Sdownsj 			}
10107cb960a2Sdownsj 			break;
10117cb960a2Sdownsj 
10127cb960a2Sdownsj 		case SALIAS:
10137cb960a2Sdownsj 			if (s->flags & SF_ALIASEND) {
10147cb960a2Sdownsj 				/* pass on an unused SF_ALIAS flag */
10157cb960a2Sdownsj 				source = s->next;
10167cb960a2Sdownsj 				source->flags |= s->flags & SF_ALIAS;
10177cb960a2Sdownsj 				s = source;
10187a8124d8Sderaadt 			} else if (*s->u.tblp->val.s &&
1019e569fc7cSderaadt 			    isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) {
10207cb960a2Sdownsj 				source = s = s->next;	/* pop source stack */
1021e55c1b2cSdownsj 				/* Note that this alias ended with a space,
1022e55c1b2cSdownsj 				 * enabling alias expansion on the following
1023e55c1b2cSdownsj 				 * word.
1024e55c1b2cSdownsj 				 */
10257cb960a2Sdownsj 				s->flags |= SF_ALIAS;
10267cb960a2Sdownsj 			} else {
1027e55c1b2cSdownsj 				/* At this point, we need to keep the current
1028e55c1b2cSdownsj 				 * alias in the source list so recursive
1029e55c1b2cSdownsj 				 * aliases can be detected and we also need
1030e55c1b2cSdownsj 				 * to return the next character.  Do this
1031e55c1b2cSdownsj 				 * by temporarily popping the alias to get
1032e55c1b2cSdownsj 				 * the next character and then put it back
1033e55c1b2cSdownsj 				 * in the source list with the SF_ALIASEND
1034e55c1b2cSdownsj 				 * flag set.
10357cb960a2Sdownsj 				 */
1036e55c1b2cSdownsj 				source = s->next;	/* pop source stack */
1037e55c1b2cSdownsj 				source->flags |= s->flags & SF_ALIAS;
1038e55c1b2cSdownsj 				c = getsc__();
1039e55c1b2cSdownsj 				if (c) {
10407cb960a2Sdownsj 					s->flags |= SF_ALIASEND;
1041e55c1b2cSdownsj 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1042e55c1b2cSdownsj 					s->start = s->str = s->ugbuf;
1043e55c1b2cSdownsj 					s->next = source;
1044e55c1b2cSdownsj 					source = s;
1045e55c1b2cSdownsj 				} else {
1046e55c1b2cSdownsj 					s = source;
1047e55c1b2cSdownsj 					/* avoid reading eof twice */
1048e55c1b2cSdownsj 					s->str = NULL;
1049e7bc3c65Sdownsj 					break;
1050e55c1b2cSdownsj 				}
10517cb960a2Sdownsj 			}
10527cb960a2Sdownsj 			continue;
10537cb960a2Sdownsj 
10547cb960a2Sdownsj 		case SREREAD:
1055e55c1b2cSdownsj 			if (s->start != s->ugbuf) /* yuck */
10567cb960a2Sdownsj 				afree(s->u.freeme, ATEMP);
10577cb960a2Sdownsj 			source = s = s->next;
10587cb960a2Sdownsj 			continue;
10597cb960a2Sdownsj 		}
10607cb960a2Sdownsj 		if (s->str == NULL) {
10617cb960a2Sdownsj 			s->type = SEOF;
10627cb960a2Sdownsj 			s->start = s->str = null;
10637cb960a2Sdownsj 			return '\0';
10647cb960a2Sdownsj 		}
10657cb960a2Sdownsj 		if (s->flags & SF_ECHO) {
10667cb960a2Sdownsj 			shf_puts(s->str, shl_out);
10677cb960a2Sdownsj 			shf_flush(shl_out);
10687cb960a2Sdownsj 		}
10697cb960a2Sdownsj 	}
10707cb960a2Sdownsj 	return c;
10717cb960a2Sdownsj }
10727cb960a2Sdownsj 
10737cb960a2Sdownsj static void
getsc_line(Source * s)1074c5d5393cSotto getsc_line(Source *s)
10757cb960a2Sdownsj {
10767cb960a2Sdownsj 	char *xp = Xstring(s->xs, xp);
10777cb960a2Sdownsj 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
10787cb960a2Sdownsj 	int have_tty = interactive && (s->flags & SF_TTY);
10797cb960a2Sdownsj 
10807cb960a2Sdownsj 	/* Done here to ensure nothing odd happens when a timeout occurs */
10817cb960a2Sdownsj 	XcheckN(s->xs, xp, LINE);
10827cb960a2Sdownsj 	*xp = '\0';
10837cb960a2Sdownsj 	s->start = s->str = xp;
10847cb960a2Sdownsj 
10857cb960a2Sdownsj 	if (have_tty && ksh_tmout) {
10867cb960a2Sdownsj 		ksh_tmout_state = TMOUT_READING;
10877cb960a2Sdownsj 		alarm(ksh_tmout);
10887cb960a2Sdownsj 	}
10897cb960a2Sdownsj 	if (have_tty && (0
10907cb960a2Sdownsj #ifdef VI
10917cb960a2Sdownsj 	    || Flag(FVI)
10927cb960a2Sdownsj #endif /* VI */
10937cb960a2Sdownsj #ifdef EMACS
10947cb960a2Sdownsj 	    || Flag(FEMACS) || Flag(FGMACS)
10957cb960a2Sdownsj #endif /* EMACS */
10967a8124d8Sderaadt 	    )) {
10977cb960a2Sdownsj 		int nread;
10987cb960a2Sdownsj 
10997cb960a2Sdownsj 		nread = x_read(xp, LINE);
11007cb960a2Sdownsj 		if (nread < 0)	/* read error */
11017cb960a2Sdownsj 			nread = 0;
11027cb960a2Sdownsj 		xp[nread] = '\0';
11037cb960a2Sdownsj 		xp += nread;
11043dc0c5b1Sjca 	} else {
11057cb960a2Sdownsj 		if (interactive) {
11067cb960a2Sdownsj 			pprompt(prompt, 0);
11077cb960a2Sdownsj 		} else
11087cb960a2Sdownsj 			s->line++;
11097cb960a2Sdownsj 
11107cb960a2Sdownsj 		while (1) {
11117cb960a2Sdownsj 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
11127cb960a2Sdownsj 
11137a8124d8Sderaadt 			if (!p && shf_error(s->u.shf) &&
11145ef49dcfSmmcc 			    s->u.shf->errno_ == EINTR) {
11157cb960a2Sdownsj 				shf_clearerr(s->u.shf);
11167cb960a2Sdownsj 				if (trap)
11177cb960a2Sdownsj 					runtraps(0);
11187cb960a2Sdownsj 				continue;
11197cb960a2Sdownsj 			}
11207cb960a2Sdownsj 			if (!p || (xp = p, xp[-1] == '\n'))
11217cb960a2Sdownsj 				break;
11227cb960a2Sdownsj 			/* double buffer size */
11237cb960a2Sdownsj 			xp++; /* move past null so doubling works... */
11247cb960a2Sdownsj 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
11257cb960a2Sdownsj 			xp--; /* ...and move back again */
11267cb960a2Sdownsj 		}
11277cb960a2Sdownsj 		/* flush any unwanted input so other programs/builtins
11287cb960a2Sdownsj 		 * can read it.  Not very optimal, but less error prone
11297cb960a2Sdownsj 		 * than flushing else where, dealing with redirections,
11307cb960a2Sdownsj 		 * etc..
11317cb960a2Sdownsj 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
11327cb960a2Sdownsj 		 */
11337cb960a2Sdownsj 		if (s->type == SSTDIN)
11347cb960a2Sdownsj 			shf_flush(s->u.shf);
11357cb960a2Sdownsj 	}
11367cb960a2Sdownsj 	/* XXX: temporary kludge to restore source after a
11377cb960a2Sdownsj 	 * trap may have been executed.
11387cb960a2Sdownsj 	 */
11397cb960a2Sdownsj 	source = s;
114094e42df6Smillert 	if (have_tty && ksh_tmout) {
11417cb960a2Sdownsj 		ksh_tmout_state = TMOUT_EXECUTING;
11427cb960a2Sdownsj 		alarm(0);
11437cb960a2Sdownsj 	}
11447cb960a2Sdownsj 	s->start = s->str = Xstring(s->xs, xp);
11457cb960a2Sdownsj 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
11467cb960a2Sdownsj 	/* Note: if input is all nulls, this is not eof */
11477cb960a2Sdownsj 	if (Xlength(s->xs, xp) == 0) { /* EOF */
11487cb960a2Sdownsj 		if (s->type == SFILE)
11497cb960a2Sdownsj 			shf_fdclose(s->u.shf);
11507cb960a2Sdownsj 		s->str = NULL;
11517cb960a2Sdownsj 	} else if (interactive) {
11527cb960a2Sdownsj 		char *p = Xstring(s->xs, xp);
11537cb960a2Sdownsj 		if (cur_prompt == PS1)
11547cb960a2Sdownsj 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
11557cb960a2Sdownsj 				p++;
11567cb960a2Sdownsj 		if (*p) {
11577cb960a2Sdownsj 			s->line++;
11587cb960a2Sdownsj 			histsave(s->line, s->str, 1);
11597cb960a2Sdownsj 		}
11607cb960a2Sdownsj 	}
11617cb960a2Sdownsj 	if (interactive)
1162311eb4a5Stb 		set_prompt(PS2);
11637cb960a2Sdownsj }
11647cb960a2Sdownsj 
1165e7b1290aSotto static char *
special_prompt_expand(char * str)1166e7b1290aSotto special_prompt_expand(char *str)
1167e7b1290aSotto {
1168e7b1290aSotto 	char *p = str;
1169e7b1290aSotto 
1170e7b1290aSotto 	while ((p = strstr(p, "\\$")) != NULL) {
11716aa3ef4bSdrahn 		*(p+1) = 'p';
1172e7b1290aSotto 	}
1173e7b1290aSotto 	return str;
1174e7b1290aSotto }
1175e7b1290aSotto 
11767cb960a2Sdownsj void
set_prompt(int to)1177311eb4a5Stb set_prompt(int to)
11787cb960a2Sdownsj {
11794300ac9bSmillert 	char *ps1;
11807cb960a2Sdownsj 	Area *saved_atemp;
1181fc804817Smillert 
118294e42df6Smillert 	cur_prompt = to;
118394e42df6Smillert 
118494e42df6Smillert 	switch (to) {
118594e42df6Smillert 	case PS1: /* command */
11864300ac9bSmillert 		ps1 = str_save(str_val(global("PS1")), ATEMP);
11874300ac9bSmillert 		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
11887cb960a2Sdownsj 		newenv(E_ERRH);
11895ae5b57eStedu 		if (sigsetjmp(genv->jbuf, 0)) {
11907cb960a2Sdownsj 			prompt = safe_prompt;
1191dcacb757Sdownsj 			/* Don't print an error - assume it has already
1192dcacb757Sdownsj 			 * been printed.  Reason is we may have forked
1193dcacb757Sdownsj 			 * to run a command and the child may be
1194dcacb757Sdownsj 			 * unwinding its stack through this code as it
1195dcacb757Sdownsj 			 * exits.
1196dcacb757Sdownsj 			 */
1197e7b1290aSotto 		} else {
1198e7b1290aSotto 			/* expand \$ before other substitutions are done */
1199e7b1290aSotto 			char *tmp = special_prompt_expand(ps1);
1200e7b1290aSotto 			prompt = str_save(substitute(tmp, 0), saved_atemp);
1201e7b1290aSotto 		}
12020ee3f80eSotto 		quitenv(NULL);
12037cb960a2Sdownsj 		break;
12047cb960a2Sdownsj 	case PS2: /* command continuation */
12057cb960a2Sdownsj 		prompt = str_val(global("PS2"));
12067cb960a2Sdownsj 		break;
12077cb960a2Sdownsj 	}
12087cb960a2Sdownsj }
12097cb960a2Sdownsj 
1210eb6bc482Sderaadt static int
dopprompt(const char * sp,int ntruncate,const char ** spp,int doprint)1211c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
12127cb960a2Sdownsj {
1213eb6bc482Sderaadt 	char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1214a1344090Sderaadt 	int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
121541c26999Sotto 	const char *cp = sp;
1216eb6bc482Sderaadt 	struct tm *tm;
1217eb6bc482Sderaadt 	time_t t;
1218eb6bc482Sderaadt 
1219eb6bc482Sderaadt 	if (*cp && cp[1] == '\r') {
1220eb6bc482Sderaadt 		delimiter = *cp;
1221eb6bc482Sderaadt 		cp += 2;
1222eb6bc482Sderaadt 	}
12237cb960a2Sdownsj 
12247cb960a2Sdownsj 	while (*cp != 0) {
1225a1344090Sderaadt 		delimitthis = 0;
1226eb6bc482Sderaadt 		if (indelimit && *cp != delimiter)
1227eb6bc482Sderaadt 			;
1228eb6bc482Sderaadt 		else if (*cp == '\n' || *cp == '\r') {
1229eb6bc482Sderaadt 			totlen = 0;
1230eb6bc482Sderaadt 			sp = cp + 1;
1231a1344090Sderaadt 		} else if (*cp == '\t') {
1232a1344090Sderaadt 			if (counting)
1233eb6bc482Sderaadt 				totlen = (totlen | 7) + 1;
1234a1344090Sderaadt 		} else if (*cp == delimiter) {
1235eb6bc482Sderaadt 			indelimit = !indelimit;
1236a1344090Sderaadt 			delimitthis = 1;
1237a1344090Sderaadt 		}
1238eb6bc482Sderaadt 
1239eb6bc482Sderaadt 		if (*cp == '\\') {
1240eb6bc482Sderaadt 			cp++;
1241eb6bc482Sderaadt 			if (!*cp)
1242eb6bc482Sderaadt 				break;
12430a9a54a3Stb 			/* Expand \h and \$ for both, sh(1) and ksh(1) */
12440a9a54a3Stb 			if (Flag(FSH) && !(*cp == 'h' || *cp == 'p'))
1245eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1246eb6bc482Sderaadt 			else switch (*cp) {
1247eb6bc482Sderaadt 			case 'a':	/* '\' 'a' bell */
1248eb6bc482Sderaadt 				strbuf[0] = '\007';
1249eb6bc482Sderaadt 				strbuf[1] = '\0';
1250eb6bc482Sderaadt 				break;
1251eb6bc482Sderaadt 			case 'd':	/* '\' 'd' Dow Mon DD */
1252eb6bc482Sderaadt 				time(&t);
1253eb6bc482Sderaadt 				tm = localtime(&t);
1254*55449a4bSflorian 				if (tm)
1255*55449a4bSflorian 					strftime(strbuf, sizeof strbuf,
1256*55449a4bSflorian 					    "%a %b %d", tm);
1257*55449a4bSflorian 				else
1258*55449a4bSflorian 					strbuf[0] = '\0';
1259eb6bc482Sderaadt 				break;
1260eb6bc482Sderaadt 			case 'D': /* '\' 'D' '{' strftime format '}' */
1261eb6bc482Sderaadt 				p = strchr(cp + 2, '}');
1262eb6bc482Sderaadt 				if (cp[1] != '{' || p == NULL) {
1263eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1264eb6bc482Sderaadt 					    "\\%c", *cp);
1265eb6bc482Sderaadt 					break;
1266eb6bc482Sderaadt 				}
1267eb6bc482Sderaadt 				strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1268eb6bc482Sderaadt 				p = strchr(tmpbuf, '}');
1269eb6bc482Sderaadt 				if (p)
1270eb6bc482Sderaadt 					*p = '\0';
1271eb6bc482Sderaadt 				time(&t);
1272eb6bc482Sderaadt 				tm = localtime(&t);
1273*55449a4bSflorian 				if (tm)
1274*55449a4bSflorian 					strftime(strbuf, sizeof strbuf, tmpbuf,
1275*55449a4bSflorian 					    tm);
1276*55449a4bSflorian 				else
1277*55449a4bSflorian 					strbuf[0] = '\0';
1278eb6bc482Sderaadt 				cp = strchr(cp + 2, '}');
1279eb6bc482Sderaadt 				break;
1280eb6bc482Sderaadt 			case 'e':	/* '\' 'e' escape */
1281eb6bc482Sderaadt 				strbuf[0] = '\033';
1282eb6bc482Sderaadt 				strbuf[1] = '\0';
1283eb6bc482Sderaadt 				break;
1284eb6bc482Sderaadt 			case 'h':	/* '\' 'h' shortened hostname */
1285eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1286eb6bc482Sderaadt 				p = strchr(strbuf, '.');
1287eb6bc482Sderaadt 				if (p)
1288eb6bc482Sderaadt 					*p = '\0';
1289eb6bc482Sderaadt 				break;
1290eb6bc482Sderaadt 			case 'H':	/* '\' 'H' full hostname */
1291eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1292eb6bc482Sderaadt 				break;
1293eb6bc482Sderaadt 			case 'j':	/* '\' 'j' number of jobs */
1294eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1295eb6bc482Sderaadt 				    j_njobs());
1296eb6bc482Sderaadt 				break;
1297eb6bc482Sderaadt 			case 'l':	/* '\' 'l' basename of tty */
1298eb6bc482Sderaadt 				p = ttyname(0);
1299eb6bc482Sderaadt 				if (p)
1300eb6bc482Sderaadt 					p = basename(p);
1301eb6bc482Sderaadt 				if (p)
1302eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1303eb6bc482Sderaadt 				break;
1304eb6bc482Sderaadt 			case 'n':	/* '\' 'n' newline */
1305eb6bc482Sderaadt 				strbuf[0] = '\n';
1306eb6bc482Sderaadt 				strbuf[1] = '\0';
13075c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
13085c4a5744Sderaadt 				sp = cp + 1;
1309eb6bc482Sderaadt 				break;
13106aa3ef4bSdrahn 			case 'p':	/* '\' '$' $ or # */
13116aa3ef4bSdrahn 				strbuf[0] = ksheuid ? '$' : '#';
13126aa3ef4bSdrahn 				strbuf[1] = '\0';
13136aa3ef4bSdrahn 				break;
1314eb6bc482Sderaadt 			case 'r':	/* '\' 'r' return */
1315eb6bc482Sderaadt 				strbuf[0] = '\r';
1316eb6bc482Sderaadt 				strbuf[1] = '\0';
13175c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
13185c4a5744Sderaadt 				sp = cp + 1;
1319eb6bc482Sderaadt 				break;
1320eb6bc482Sderaadt 			case 's':	/* '\' 's' basename $0 */
1321eb6bc482Sderaadt 				strlcpy(strbuf, kshname, sizeof strbuf);
1322eb6bc482Sderaadt 				break;
1323eb6bc482Sderaadt 			case 't':	/* '\' 't' 24 hour HH:MM:SS */
1324eb6bc482Sderaadt 				time(&t);
1325eb6bc482Sderaadt 				tm = localtime(&t);
1326*55449a4bSflorian 				if (tm)
1327*55449a4bSflorian 					strftime(strbuf, sizeof strbuf, "%T",
1328*55449a4bSflorian 					    tm);
1329*55449a4bSflorian 				else
1330*55449a4bSflorian 					strbuf[0] = '\0';
1331eb6bc482Sderaadt 				break;
1332eb6bc482Sderaadt 			case 'T':	/* '\' 'T' 12 hour HH:MM:SS */
1333eb6bc482Sderaadt 				time(&t);
1334eb6bc482Sderaadt 				tm = localtime(&t);
1335*55449a4bSflorian 				if (tm)
1336*55449a4bSflorian 					strftime(strbuf, sizeof strbuf,
1337*55449a4bSflorian 					    "%l:%M:%S", tm);
1338*55449a4bSflorian 				else
1339*55449a4bSflorian 					strbuf[0] = '\0';
1340eb6bc482Sderaadt 				break;
1341eb6bc482Sderaadt 			case '@':	/* '\' '@' 12 hour am/pm format */
1342eb6bc482Sderaadt 				time(&t);
1343eb6bc482Sderaadt 				tm = localtime(&t);
1344*55449a4bSflorian 				if (tm)
1345*55449a4bSflorian 					strftime(strbuf, sizeof strbuf, "%r",
1346*55449a4bSflorian 					    tm);
1347*55449a4bSflorian 				else
1348*55449a4bSflorian 					strbuf[0] = '\0';
1349eb6bc482Sderaadt 				break;
1350eb6bc482Sderaadt 			case 'A':	/* '\' 'A' 24 hour HH:MM */
1351eb6bc482Sderaadt 				time(&t);
1352eb6bc482Sderaadt 				tm = localtime(&t);
1353*55449a4bSflorian 				if (tm)
1354*55449a4bSflorian 					strftime(strbuf, sizeof strbuf, "%R",
1355*55449a4bSflorian 					    tm);
1356*55449a4bSflorian 				else
1357*55449a4bSflorian 					strbuf[0] = '\0';
1358eb6bc482Sderaadt 				break;
1359eb6bc482Sderaadt 			case 'u':	/* '\' 'u' username */
1360f351fb1cSotto 				strlcpy(strbuf, username, sizeof strbuf);
1361eb6bc482Sderaadt 				break;
13627fada9fdSkn #ifndef SMALL
1363eb6bc482Sderaadt 			case 'v':	/* '\' 'v' version (short) */
1364eb6bc482Sderaadt 				p = strchr(ksh_version, ' ');
1365eb6bc482Sderaadt 				if (p)
1366eb6bc482Sderaadt 					p = strchr(p + 1, ' ');
1367eb6bc482Sderaadt 				if (p) {
1368eb6bc482Sderaadt 					p++;
1369eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1370eb6bc482Sderaadt 					p = strchr(strbuf, ' ');
1371eb6bc482Sderaadt 					if (p)
1372eb6bc482Sderaadt 						*p = '\0';
1373eb6bc482Sderaadt 				}
1374eb6bc482Sderaadt 				break;
1375eb6bc482Sderaadt 			case 'V':	/* '\' 'V' version (long) */
1376eb6bc482Sderaadt 				strlcpy(strbuf, ksh_version, sizeof strbuf);
1377eb6bc482Sderaadt 				break;
13787fada9fdSkn #endif /* SMALL */
1379eb6bc482Sderaadt 			case 'w':	/* '\' 'w' cwd */
1380eb6bc482Sderaadt 				p = str_val(global("PWD"));
1381ad55f791Sderaadt 				n = strlen(str_val(global("HOME")));
1382ad55f791Sderaadt 				if (strcmp(p, "/") == 0) {
1383ad55f791Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1384ad55f791Sderaadt 				} else if (strcmp(p, str_val(global("HOME"))) == 0) {
1385eb6bc482Sderaadt 					strbuf[0] = '~';
1386eb6bc482Sderaadt 					strbuf[1] = '\0';
1387ad55f791Sderaadt 				} else if (strncmp(p, str_val(global("HOME")), n)
1388ad55f791Sderaadt 				    == 0 && p[n] == '/') {
1389ad55f791Sderaadt 					snprintf(strbuf, sizeof strbuf, "~/%s",
1390ad55f791Sderaadt 					    str_val(global("PWD")) + n + 1);
1391eb6bc482Sderaadt 				} else
1392eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1393eb6bc482Sderaadt 				break;
1394eb6bc482Sderaadt 			case 'W':	/* '\' 'W' basename(cwd) */
1395eb6bc482Sderaadt 				p = str_val(global("PWD"));
139630e9b9efSokan 				if (strcmp(p, str_val(global("HOME"))) == 0) {
139730e9b9efSokan 					strbuf[0] = '~';
139830e9b9efSokan 					strbuf[1] = '\0';
139930e9b9efSokan 				} else
1400eb6bc482Sderaadt 					strlcpy(strbuf, basename(p), sizeof strbuf);
1401eb6bc482Sderaadt 				break;
1402e7b1290aSotto 			case '!':	/* '\' '!' history line number */
1403eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1404eb6bc482Sderaadt 				    source->line + 1);
1405eb6bc482Sderaadt 				break;
1406e7b1290aSotto 			case '#':	/* '\' '#' command line number */
1407eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1408e7b1290aSotto 				    source->line - source->cmd_offset + 1);
1409eb6bc482Sderaadt 				break;
1410eb6bc482Sderaadt 			case '0':	/* '\' '#' '#' ' #' octal numeric handling */
1411eb6bc482Sderaadt 			case '1':
1412eb6bc482Sderaadt 			case '2':
1413eb6bc482Sderaadt 			case '3':
1414eb6bc482Sderaadt 			case '4':
1415eb6bc482Sderaadt 			case '5':
1416eb6bc482Sderaadt 			case '6':
1417eb6bc482Sderaadt 			case '7':
1418eb6bc482Sderaadt 				if ((cp[1] > '7' || cp[1] < '0') ||
1419eb6bc482Sderaadt 				    (cp[2] > '7' || cp[2] < '0')) {
1420eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1421eb6bc482Sderaadt 					    "\\%c", *cp);
1422eb6bc482Sderaadt 					break;
1423eb6bc482Sderaadt 				}
1424064356ecSzhuk 				n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 +
1425064356ecSzhuk 				    (cp[2] - '0');
1426eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%c", n);
1427eb6bc482Sderaadt 				cp += 2;
1428eb6bc482Sderaadt 				break;
1429eb6bc482Sderaadt 			case '\\':	/* '\' '\' */
1430eb6bc482Sderaadt 				strbuf[0] = '\\';
1431eb6bc482Sderaadt 				strbuf[1] = '\0';
1432eb6bc482Sderaadt 				break;
1433a1344090Sderaadt 			case '[': /* '\' '[' .... stop counting */
1434a1344090Sderaadt 				strbuf[0] = '\0';
1435a1344090Sderaadt 				counting = 0;
1436eb6bc482Sderaadt 				break;
1437a1344090Sderaadt 			case ']': /* '\' ']' restart counting */
1438a1344090Sderaadt 				strbuf[0] = '\0';
1439a1344090Sderaadt 				counting = 1;
1440eb6bc482Sderaadt 				break;
1441eb6bc482Sderaadt 
1442eb6bc482Sderaadt 			default:
1443eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1444eb6bc482Sderaadt 				break;
1445eb6bc482Sderaadt 			}
1446eb6bc482Sderaadt 			cp++;
1447eb6bc482Sderaadt 
1448eb6bc482Sderaadt 			str = strbuf;
1449eb6bc482Sderaadt 			len = strlen(str);
1450eb6bc482Sderaadt 			if (ntruncate) {
1451eb6bc482Sderaadt 				if (ntruncate >= len) {
1452eb6bc482Sderaadt 					ntruncate -= len;
1453eb6bc482Sderaadt 					continue;
1454eb6bc482Sderaadt 				}
1455eb6bc482Sderaadt 				str += ntruncate;
1456eb6bc482Sderaadt 				len -= ntruncate;
1457eb6bc482Sderaadt 				ntruncate = 0;
1458eb6bc482Sderaadt 			}
1459eb6bc482Sderaadt 			if (doprint)
1460eb6bc482Sderaadt 				shf_write(str, len, shl_out);
1461a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1462eb6bc482Sderaadt 				totlen += len;
1463eb6bc482Sderaadt 			continue;
1464eb6bc482Sderaadt 		} else if (*cp != '!')
14657cb960a2Sdownsj 			c = *cp++;
14667cb960a2Sdownsj 		else if (*++cp == '!')
14677cb960a2Sdownsj 			c = *cp++;
14687cb960a2Sdownsj 		else {
14697cb960a2Sdownsj 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
14707cb960a2Sdownsj 			    source->line + 1);
14717cb960a2Sdownsj 			len = strlen(nbuf);
14727cb960a2Sdownsj 			if (ntruncate) {
14737cb960a2Sdownsj 				if (ntruncate >= len) {
14747cb960a2Sdownsj 					ntruncate -= len;
14757cb960a2Sdownsj 					continue;
14767cb960a2Sdownsj 				}
14777cb960a2Sdownsj 				p += ntruncate;
14787cb960a2Sdownsj 				len -= ntruncate;
14797cb960a2Sdownsj 				ntruncate = 0;
14807cb960a2Sdownsj 			}
1481eb6bc482Sderaadt 			if (doprint)
14827cb960a2Sdownsj 				shf_write(p, len, shl_out);
1483a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1484eb6bc482Sderaadt 				totlen += len;
14857cb960a2Sdownsj 			continue;
14867cb960a2Sdownsj 		}
1487638b9e76Sbeck 		if (counting && ntruncate)
14887cb960a2Sdownsj 			--ntruncate;
1489eb6bc482Sderaadt 		else if (doprint) {
14907cb960a2Sdownsj 			shf_putc(c, shl_out);
14917cb960a2Sdownsj 		}
1492a1344090Sderaadt 		if (counting && !indelimit && !delimitthis)
1493eb6bc482Sderaadt 			totlen++;
1494eb6bc482Sderaadt 	}
1495eb6bc482Sderaadt 	if (doprint)
14967cb960a2Sdownsj 		shf_flush(shl_out);
1497eb6bc482Sderaadt 	if (spp)
1498eb6bc482Sderaadt 		*spp = sp;
1499eb6bc482Sderaadt 	return (totlen);
1500eb6bc482Sderaadt }
1501eb6bc482Sderaadt 
1502eb6bc482Sderaadt void
pprompt(const char * cp,int ntruncate)1503c5d5393cSotto pprompt(const char *cp, int ntruncate)
1504eb6bc482Sderaadt {
1505eb6bc482Sderaadt 	dopprompt(cp, ntruncate, NULL, 1);
1506eb6bc482Sderaadt }
1507eb6bc482Sderaadt 
1508eb6bc482Sderaadt int
promptlen(const char * cp,const char ** spp)1509c5d5393cSotto promptlen(const char *cp, const char **spp)
1510eb6bc482Sderaadt {
1511eb6bc482Sderaadt 	return dopprompt(cp, 0, spp, 0);
15127cb960a2Sdownsj }
15137cb960a2Sdownsj 
15147cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including
15157cb960a2Sdownsj  * the :[-+?=#%] or close-brace.
15167cb960a2Sdownsj  */
15177cb960a2Sdownsj static char *
get_brace_var(XString * wsp,char * wp)1518c5d5393cSotto get_brace_var(XString *wsp, char *wp)
15197cb960a2Sdownsj {
15207cb960a2Sdownsj 	enum parse_state {
15217cb960a2Sdownsj 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
15227cb960a2Sdownsj 			   PS_NUMBER, PS_VAR1, PS_END
15237cb960a2Sdownsj 			 }
15247cb960a2Sdownsj 		state;
15257cb960a2Sdownsj 	char c;
15267cb960a2Sdownsj 
15277cb960a2Sdownsj 	state = PS_INITIAL;
15287cb960a2Sdownsj 	while (1) {
1529e55c1b2cSdownsj 		c = getsc();
15307cb960a2Sdownsj 		/* State machine to figure out where the variable part ends. */
15317cb960a2Sdownsj 		switch (state) {
15327cb960a2Sdownsj 		case PS_INITIAL:
15337cb960a2Sdownsj 			if (c == '#') {
15347cb960a2Sdownsj 				state = PS_SAW_HASH;
15357cb960a2Sdownsj 				break;
15367cb960a2Sdownsj 			}
15377eab881bSjaredy 			/* FALLTHROUGH */
15387cb960a2Sdownsj 		case PS_SAW_HASH:
15397cb960a2Sdownsj 			if (letter(c))
15407cb960a2Sdownsj 				state = PS_IDENT;
15417cb960a2Sdownsj 			else if (digit(c))
15427cb960a2Sdownsj 				state = PS_NUMBER;
15437cb960a2Sdownsj 			else if (ctype(c, C_VAR1))
15447cb960a2Sdownsj 				state = PS_VAR1;
15457cb960a2Sdownsj 			else
15467cb960a2Sdownsj 				state = PS_END;
15477cb960a2Sdownsj 			break;
15487cb960a2Sdownsj 		case PS_IDENT:
15497cb960a2Sdownsj 			if (!letnum(c)) {
15507cb960a2Sdownsj 				state = PS_END;
15517cb960a2Sdownsj 				if (c == '[') {
15527cb960a2Sdownsj 					char *tmp, *p;
15537cb960a2Sdownsj 
15547cb960a2Sdownsj 					if (!arraysub(&tmp))
15557cb960a2Sdownsj 						yyerror("missing ]\n");
15567cb960a2Sdownsj 					*wp++ = c;
15577cb960a2Sdownsj 					for (p = tmp; *p; ) {
15587cb960a2Sdownsj 						Xcheck(*wsp, wp);
15597cb960a2Sdownsj 						*wp++ = *p++;
15607cb960a2Sdownsj 					}
15617cb960a2Sdownsj 					afree(tmp, ATEMP);
1562e55c1b2cSdownsj 					c = getsc(); /* the ] */
15637cb960a2Sdownsj 				}
15647cb960a2Sdownsj 			}
15657cb960a2Sdownsj 			break;
15667cb960a2Sdownsj 		case PS_NUMBER:
15677cb960a2Sdownsj 			if (!digit(c))
15687cb960a2Sdownsj 				state = PS_END;
15697cb960a2Sdownsj 			break;
15707cb960a2Sdownsj 		case PS_VAR1:
15717cb960a2Sdownsj 			state = PS_END;
15727cb960a2Sdownsj 			break;
15737cb960a2Sdownsj 		case PS_END: /* keep gcc happy */
15747cb960a2Sdownsj 			break;
15757cb960a2Sdownsj 		}
15767cb960a2Sdownsj 		if (state == PS_END) {
15777cb960a2Sdownsj 			*wp++ = '\0';	/* end of variable part */
15787cb960a2Sdownsj 			ungetsc(c);
15797cb960a2Sdownsj 			break;
15807cb960a2Sdownsj 		}
15817cb960a2Sdownsj 		Xcheck(*wsp, wp);
15827cb960a2Sdownsj 		*wp++ = c;
15837cb960a2Sdownsj 	}
15847cb960a2Sdownsj 	return wp;
15857cb960a2Sdownsj }
15867cb960a2Sdownsj 
15877cb960a2Sdownsj /*
15887cb960a2Sdownsj  * Save an array subscript - returns true if matching bracket found, false
15897cb960a2Sdownsj  * if eof or newline was found.
15907cb960a2Sdownsj  * (Returned string double null terminated)
15917cb960a2Sdownsj  */
15927cb960a2Sdownsj static int
arraysub(char ** strp)1593c5d5393cSotto arraysub(char **strp)
15947cb960a2Sdownsj {
15957cb960a2Sdownsj 	XString ws;
15967cb960a2Sdownsj 	char	*wp;
15977cb960a2Sdownsj 	char	c;
15987cb960a2Sdownsj 	int	depth = 1;	/* we are just past the initial [ */
15997cb960a2Sdownsj 
16007cb960a2Sdownsj 	Xinit(ws, wp, 32, ATEMP);
16017cb960a2Sdownsj 
16027cb960a2Sdownsj 	do {
1603e55c1b2cSdownsj 		c = getsc();
16047cb960a2Sdownsj 		Xcheck(ws, wp);
16057cb960a2Sdownsj 		*wp++ = c;
16067cb960a2Sdownsj 		if (c == '[')
16077cb960a2Sdownsj 			depth++;
16087cb960a2Sdownsj 		else if (c == ']')
16097cb960a2Sdownsj 			depth--;
16107cb960a2Sdownsj 	} while (depth > 0 && c && c != '\n');
16117cb960a2Sdownsj 
16127cb960a2Sdownsj 	*wp++ = '\0';
16137cb960a2Sdownsj 	*strp = Xclose(ws, wp);
16147cb960a2Sdownsj 
16157cb960a2Sdownsj 	return depth == 0 ? 1 : 0;
16167cb960a2Sdownsj }
16177cb960a2Sdownsj 
16187cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */
16197cb960a2Sdownsj static const char *
ungetsc(int c)1620c5d5393cSotto ungetsc(int c)
16217cb960a2Sdownsj {
1622e55c1b2cSdownsj 	if (backslash_skip)
1623e55c1b2cSdownsj 		backslash_skip--;
16247cb960a2Sdownsj 	/* Don't unget eof... */
16257cb960a2Sdownsj 	if (source->str == null && c == '\0')
16267cb960a2Sdownsj 		return source->str;
16277cb960a2Sdownsj 	if (source->str > source->start)
16287cb960a2Sdownsj 		source->str--;
16297cb960a2Sdownsj 	else {
16307cb960a2Sdownsj 		Source *s;
16317cb960a2Sdownsj 
16327cb960a2Sdownsj 		s = pushs(SREREAD, source->areap);
1633e55c1b2cSdownsj 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1634e55c1b2cSdownsj 		s->start = s->str = s->ugbuf;
16357cb960a2Sdownsj 		s->next = source;
16367cb960a2Sdownsj 		source = s;
16377cb960a2Sdownsj 	}
16387cb960a2Sdownsj 	return source->str;
16397cb960a2Sdownsj }
16407cb960a2Sdownsj 
1641e55c1b2cSdownsj 
16427cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */
16437cb960a2Sdownsj static int
getsc_bn(void)164469b9f96bSmillert getsc_bn(void)
16457cb960a2Sdownsj {
1646e55c1b2cSdownsj 	int c, c2;
1647e55c1b2cSdownsj 
1648e55c1b2cSdownsj 	if (ignore_backslash_newline)
1649e55c1b2cSdownsj 		return getsc_();
1650e55c1b2cSdownsj 
1651e55c1b2cSdownsj 	if (backslash_skip == 1) {
1652e55c1b2cSdownsj 		backslash_skip = 2;
1653e55c1b2cSdownsj 		return getsc_();
1654e55c1b2cSdownsj 	}
1655e55c1b2cSdownsj 
1656e55c1b2cSdownsj 	backslash_skip = 0;
16577cb960a2Sdownsj 
16587cb960a2Sdownsj 	while (1) {
16597cb960a2Sdownsj 		c = getsc_();
1660e55c1b2cSdownsj 		if (c == '\\') {
1661e55c1b2cSdownsj 			if ((c2 = getsc_()) == '\n')
16627cb960a2Sdownsj 				/* ignore the \newline; get the next char... */
1663e55c1b2cSdownsj 				continue;
1664e55c1b2cSdownsj 			ungetsc(c2);
1665e55c1b2cSdownsj 			backslash_skip = 1;
1666e55c1b2cSdownsj 		}
1667e55c1b2cSdownsj 		return c;
16687cb960a2Sdownsj 	}
16697cb960a2Sdownsj }
16703b015934Smillert 
16713b015934Smillert static Lex_state *
push_state_(State_info * si,Lex_state * old_end)1672c5d5393cSotto push_state_(State_info *si, Lex_state *old_end)
16733b015934Smillert {
1674d67c3782Smmcc 	Lex_state *new = areallocarray(NULL, STATE_BSIZE,
1675d67c3782Smmcc 	    sizeof(Lex_state), ATEMP);
16763b015934Smillert 
16773b015934Smillert 	new[0].ls_info.base = old_end;
16783b015934Smillert 	si->base = &new[0];
16793b015934Smillert 	si->end = &new[STATE_BSIZE];
16803b015934Smillert 	return &new[1];
16813b015934Smillert }
16823b015934Smillert 
16833b015934Smillert static Lex_state *
pop_state_(State_info * si,Lex_state * old_end)1684c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end)
16853b015934Smillert {
16863b015934Smillert 	Lex_state *old_base = si->base;
16873b015934Smillert 
16883b015934Smillert 	si->base = old_end->ls_info.base - STATE_BSIZE;
16893b015934Smillert 	si->end = old_end->ls_info.base;
16903b015934Smillert 
16913b015934Smillert 	afree(old_base, ATEMP);
16923b015934Smillert 
16937b425235Smillert 	return si->base + STATE_BSIZE - 1;
16943b015934Smillert }
1695