xref: /openbsd/bin/ksh/lex.c (revision 8c046d24)
1*8c046d24Snicm /*	$OpenBSD: lex.c,v 1.53 2015/09/17 14:21:33 nicm Exp $	*/
27cb960a2Sdownsj 
37cb960a2Sdownsj /*
47cb960a2Sdownsj  * lexical analysis and source input
57cb960a2Sdownsj  */
67cb960a2Sdownsj 
77cb960a2Sdownsj #include "sh.h"
8eb6bc482Sderaadt #include <libgen.h>
97cb960a2Sdownsj #include <ctype.h>
107cb960a2Sdownsj 
113b015934Smillert 
123b015934Smillert /* Structure to keep track of the lexing state and the various pieces of info
133b015934Smillert  * needed for each particular state.
143b015934Smillert  */
153b015934Smillert typedef struct lex_state Lex_state;
163b015934Smillert struct lex_state {
173b015934Smillert 	int ls_state;
183b015934Smillert 	union {
193b015934Smillert 		/* $(...) */
203b015934Smillert 		struct scsparen_info {
213b015934Smillert 			int nparen;	/* count open parenthesis */
223b015934Smillert 			int csstate;	/* XXX remove */
233b015934Smillert #define ls_scsparen ls_info.u_scsparen
243b015934Smillert 		} u_scsparen;
253b015934Smillert 
263b015934Smillert 		/* $((...)) */
273b015934Smillert 		struct sasparen_info {
283b015934Smillert 			int nparen;	/* count open parenthesis */
293b015934Smillert 			int start;	/* marks start of $(( in output str */
303b015934Smillert #define ls_sasparen ls_info.u_sasparen
313b015934Smillert 		} u_sasparen;
323b015934Smillert 
333b015934Smillert 		/* ((...)) */
343b015934Smillert 		struct sletparen_info {
353b015934Smillert 			int nparen;	/* count open parenthesis */
363b015934Smillert #define ls_sletparen ls_info.u_sletparen
373b015934Smillert 		} u_sletparen;
383b015934Smillert 
393b015934Smillert 		/* `...` */
403b015934Smillert 		struct sbquote_info {
413b015934Smillert 			int indquotes;	/* true if in double quotes: "`...`" */
423b015934Smillert #define ls_sbquote ls_info.u_sbquote
433b015934Smillert 		} u_sbquote;
443b015934Smillert 
453b015934Smillert 		Lex_state *base;	/* used to point to next state block */
463b015934Smillert 	} ls_info;
473b015934Smillert };
483b015934Smillert 
493b015934Smillert typedef struct State_info State_info;
503b015934Smillert struct State_info {
513b015934Smillert 	Lex_state	*base;
523b015934Smillert 	Lex_state	*end;
533b015934Smillert };
543b015934Smillert 
553b015934Smillert 
56c5d5393cSotto static void	readhere(struct ioword *);
5769b9f96bSmillert static int	getsc__(void);
58c5d5393cSotto static void	getsc_line(Source *);
5969b9f96bSmillert static int	getsc_bn(void);
60c5d5393cSotto static char	*get_brace_var(XString *, char *);
61c5d5393cSotto static int	arraysub(char **);
62c5d5393cSotto static const char *ungetsc(int);
6369b9f96bSmillert static void	gethere(void);
64c5d5393cSotto static Lex_state *push_state_(State_info *, Lex_state *);
65c5d5393cSotto static Lex_state *pop_state_(State_info *, Lex_state *);
66e7b1290aSotto static char	*special_prompt_expand(char *);
67c5d5393cSotto static int	dopprompt(const char *, int, const char **, int);
68a3427febSderaadt int		promptlen(const char *cp, const char **spp);
697cb960a2Sdownsj 
70e55c1b2cSdownsj static int backslash_skip;
71e55c1b2cSdownsj static int ignore_backslash_newline;
72e55c1b2cSdownsj 
73e55c1b2cSdownsj /* optimized getsc_bn() */
74e55c1b2cSdownsj #define getsc()		(*source->str != '\0' && *source->str != '\\' \
75e55c1b2cSdownsj 			 && !backslash_skip ? *source->str++ : getsc_bn())
76e55c1b2cSdownsj /* optimized getsc__() */
77e55c1b2cSdownsj #define	getsc_()	((*source->str != '\0') ? *source->str++ : getsc__())
787cb960a2Sdownsj 
793b015934Smillert #define STATE_BSIZE	32
803b015934Smillert 
813b015934Smillert #define PUSH_STATE(s)	do { \
823b015934Smillert 			    if (++statep == state_info.end) \
833b015934Smillert 				statep = push_state_(&state_info, statep); \
843b015934Smillert 			    state = statep->ls_state = (s); \
853b015934Smillert 			} while (0)
863b015934Smillert 
873b015934Smillert #define POP_STATE()	do { \
883b015934Smillert 			    if (--statep == state_info.base) \
893b015934Smillert 				statep = pop_state_(&state_info, statep); \
903b015934Smillert 			    state = statep->ls_state; \
913b015934Smillert 			} while (0)
923b015934Smillert 
933b015934Smillert 
947cb960a2Sdownsj 
957cb960a2Sdownsj /*
967cb960a2Sdownsj  * Lexical analyzer
977cb960a2Sdownsj  *
987cb960a2Sdownsj  * tokens are not regular expressions, they are LL(1).
997cb960a2Sdownsj  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
1007cb960a2Sdownsj  * hence the state stack.
1017cb960a2Sdownsj  */
1027cb960a2Sdownsj 
1037cb960a2Sdownsj int
104c5d5393cSotto yylex(int cf)
1057cb960a2Sdownsj {
1063b015934Smillert 	Lex_state states[STATE_BSIZE], *statep;
1073b015934Smillert 	State_info state_info;
1087894b443Smillert 	int c, state;
1097cb960a2Sdownsj 	XString ws;		/* expandable output word */
1107894b443Smillert 	char *wp;		/* output word pointer */
1113b015934Smillert 	char *sp, *dp;
1123b015934Smillert 	int c2;
1137cb960a2Sdownsj 
1147cb960a2Sdownsj 
1157cb960a2Sdownsj   Again:
1163b015934Smillert 	states[0].ls_state = -1;
11722a9cf94Sguenther 	states[0].ls_info.base = NULL;
1183b015934Smillert 	statep = &states[1];
1193b015934Smillert 	state_info.base = states;
1203b015934Smillert 	state_info.end = &states[STATE_BSIZE];
1213b015934Smillert 
1227cb960a2Sdownsj 	Xinit(ws, wp, 64, ATEMP);
1237cb960a2Sdownsj 
124e55c1b2cSdownsj 	backslash_skip = 0;
125e55c1b2cSdownsj 	ignore_backslash_newline = 0;
126e55c1b2cSdownsj 
1277cb960a2Sdownsj 	if (cf&ONEWORD)
1283b015934Smillert 		state = SWORD;
1297cb960a2Sdownsj 	else if (cf&LETEXPR) {
1307cb960a2Sdownsj 		*wp++ = OQUOTE;	 /* enclose arguments in (double) quotes */
1313b015934Smillert 		state = SLETPAREN;
1323b015934Smillert 		statep->ls_sletparen.nparen = 0;
13394e42df6Smillert 	} else {		/* normal lexing */
1343b015934Smillert 		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
1357cb960a2Sdownsj 		while ((c = getsc()) == ' ' || c == '\t')
1367cb960a2Sdownsj 			;
137e55c1b2cSdownsj 		if (c == '#') {
138e55c1b2cSdownsj 			ignore_backslash_newline++;
1397cb960a2Sdownsj 			while ((c = getsc()) != '\0' && c != '\n')
1407cb960a2Sdownsj 				;
141e55c1b2cSdownsj 			ignore_backslash_newline--;
142e55c1b2cSdownsj 		}
1437cb960a2Sdownsj 		ungetsc(c);
1447cb960a2Sdownsj 	}
1457cb960a2Sdownsj 	if (source->flags & SF_ALIAS) {	/* trailing ' ' in alias definition */
1467cb960a2Sdownsj 		source->flags &= ~SF_ALIAS;
1477cb960a2Sdownsj 		/* In POSIX mode, a trailing space only counts if we are
1487cb960a2Sdownsj 		 * parsing a simple command
1497cb960a2Sdownsj 		 */
1507cb960a2Sdownsj 		if (!Flag(FPOSIX) || (cf & CMDWORD))
1517cb960a2Sdownsj 			cf |= ALIAS;
1527cb960a2Sdownsj 	}
1537cb960a2Sdownsj 
1543b015934Smillert 	/* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
1553b015934Smillert 	statep->ls_state = state;
1563b015934Smillert 
1577cb960a2Sdownsj 	/* collect non-special or quoted characters to form word */
1587a8124d8Sderaadt 	while (!((c = getsc()) == 0 ||
1597a8124d8Sderaadt 	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
1607cb960a2Sdownsj 		Xcheck(ws, wp);
1617cb960a2Sdownsj 		switch (state) {
1627cb960a2Sdownsj 		case SBASE:
1637a8124d8Sderaadt 			if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) &&
1647a8124d8Sderaadt 			    c == '!') {
165f740f764Sderaadt 				char **replace = NULL;
166f740f764Sderaadt 
167f740f764Sderaadt 				c2 = getsc();
1680b1768a7Sotto 				if (c2 == '\0' || c2 == ' ' || c2 == '\t')
169f740f764Sderaadt 					;
170f740f764Sderaadt 				else if (c2 == '!')
171f740f764Sderaadt 					replace = hist_get_newest(0);
172f740f764Sderaadt 				else if (isdigit(c2) || c2 == '-' ||
173f740f764Sderaadt 				    isalpha(c2)) {
174f740f764Sderaadt 					int get = !isalpha(c2);
175f740f764Sderaadt 					char match[200], *str = match;
176f740f764Sderaadt 
177f740f764Sderaadt 					*str++ = c2;
178f740f764Sderaadt 					do {
179f740f764Sderaadt 						if ((c2 = getsc()) == '\0')
180f740f764Sderaadt 							break;
181f740f764Sderaadt 						if (c2 == '\t' || c2 == ' ' ||
182f740f764Sderaadt 						    c2 == '\n') {
183f740f764Sderaadt 							ungetsc(c2);
184f740f764Sderaadt 							break;
185f740f764Sderaadt 						}
186f740f764Sderaadt 						*str++ = c2;
187f740f764Sderaadt 					} while (str < &match[sizeof(match)-1]);
188f740f764Sderaadt 					*str = '\0';
189f740f764Sderaadt 
190f740f764Sderaadt 					if (get) {
191f740f764Sderaadt 						int h = findhistrel(match);
192f740f764Sderaadt 						if (h >= 0)
193f740f764Sderaadt 							replace = &history[h];
194f740f764Sderaadt 					} else {
1950e7d3a01Smillert 						int h = findhist(-1, 0, match, true);
196f740f764Sderaadt 						if (h >= 0)
197f740f764Sderaadt 							replace = &history[h];
198f740f764Sderaadt 					}
199f740f764Sderaadt 				}
200f740f764Sderaadt 
201f740f764Sderaadt 				/*
202f740f764Sderaadt 				 * XXX ksh history buffer saves un-expanded
203f740f764Sderaadt 				 * commands. Until the history buffer code is
204f740f764Sderaadt 				 * changed to contain expanded commands, we
205f740f764Sderaadt 				 * ignore the bad commands (spinning sucks)
206f740f764Sderaadt 				 */
207f740f764Sderaadt 				if (replace && **replace == '!')
208f740f764Sderaadt 					ungetsc(c2);
209f740f764Sderaadt 				else if (replace) {
210f740f764Sderaadt 					Source *s;
211f740f764Sderaadt 
212f740f764Sderaadt 					/* do not strdup replacement via alloc */
2137a8124d8Sderaadt 					s = pushs(SREREAD, source->areap);
214f740f764Sderaadt 					s->start = s->str = *replace;
215f740f764Sderaadt 					s->next = source;
2167d849734Smillert 					s->u.freeme = NULL;
217f740f764Sderaadt 					source = s;
218f740f764Sderaadt 					continue;
219f740f764Sderaadt 				} else
220f740f764Sderaadt 					ungetsc(c2);
221f740f764Sderaadt 			}
2227cb960a2Sdownsj 			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
2237cb960a2Sdownsj 				*wp = EOS; /* temporary */
2247a8124d8Sderaadt 				if (is_wdvarname(Xstring(ws, wp), false)) {
2257cb960a2Sdownsj 					char *p, *tmp;
2267cb960a2Sdownsj 
2277cb960a2Sdownsj 					if (arraysub(&tmp)) {
2287cb960a2Sdownsj 						*wp++ = CHAR;
2297cb960a2Sdownsj 						*wp++ = c;
2307cb960a2Sdownsj 						for (p = tmp; *p; ) {
2317cb960a2Sdownsj 							Xcheck(ws, wp);
2327cb960a2Sdownsj 							*wp++ = CHAR;
2337cb960a2Sdownsj 							*wp++ = *p++;
2347cb960a2Sdownsj 						}
2357cb960a2Sdownsj 						afree(tmp, ATEMP);
2367cb960a2Sdownsj 						break;
2377cb960a2Sdownsj 					} else {
2387cb960a2Sdownsj 						Source *s;
2397cb960a2Sdownsj 
2407cb960a2Sdownsj 						s = pushs(SREREAD,
2417cb960a2Sdownsj 							  source->areap);
2427cb960a2Sdownsj 						s->start = s->str
2437cb960a2Sdownsj 							= s->u.freeme = tmp;
2447cb960a2Sdownsj 						s->next = source;
2457cb960a2Sdownsj 						source = s;
2467cb960a2Sdownsj 					}
2477cb960a2Sdownsj 				}
2487cb960a2Sdownsj 				*wp++ = CHAR;
2497cb960a2Sdownsj 				*wp++ = c;
2507cb960a2Sdownsj 				break;
2517cb960a2Sdownsj 			}
2527eab881bSjaredy 			/* FALLTHROUGH */
2537cb960a2Sdownsj 		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */
2547a8124d8Sderaadt 			if (c == '*' || c == '@' || c == '+' || c == '?' ||
2557a8124d8Sderaadt 			    c == '!') {
2567cb960a2Sdownsj 				c2 = getsc();
2577cb960a2Sdownsj 				if (c2 == '(' /*)*/ ) {
2587cb960a2Sdownsj 					*wp++ = OPAT;
2597cb960a2Sdownsj 					*wp++ = c;
2603b015934Smillert 					PUSH_STATE(SPATTERN);
2617cb960a2Sdownsj 					break;
2627cb960a2Sdownsj 				}
2637cb960a2Sdownsj 				ungetsc(c2);
2647cb960a2Sdownsj 			}
2657eab881bSjaredy 			/* FALLTHROUGH */
2667cb960a2Sdownsj 		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */
2677cb960a2Sdownsj 			switch (c) {
2687cb960a2Sdownsj 			case '\\':
2697cb960a2Sdownsj 				c = getsc();
270e55c1b2cSdownsj 				if (c) /* trailing \ is lost */
2717cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
2727cb960a2Sdownsj 				break;
2737cb960a2Sdownsj 			case '\'':
27422a9cf94Sguenther 				if ((cf & HEREDOC) || state == SBRACEQ) {
27522a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
27622a9cf94Sguenther 					break;
27722a9cf94Sguenther 				}
2787cb960a2Sdownsj 				*wp++ = OQUOTE;
279e55c1b2cSdownsj 				ignore_backslash_newline++;
2803b015934Smillert 				PUSH_STATE(SSQUOTE);
2817cb960a2Sdownsj 				break;
2827cb960a2Sdownsj 			case '"':
2837cb960a2Sdownsj 				*wp++ = OQUOTE;
2843b015934Smillert 				PUSH_STATE(SDQUOTE);
2857cb960a2Sdownsj 				break;
2867cb960a2Sdownsj 			default:
2877cb960a2Sdownsj 				goto Subst;
2887cb960a2Sdownsj 			}
2897cb960a2Sdownsj 			break;
2907cb960a2Sdownsj 
2917cb960a2Sdownsj 		  Subst:
2927cb960a2Sdownsj 			switch (c) {
2937cb960a2Sdownsj 			case '\\':
2947cb960a2Sdownsj 				c = getsc();
2957cb960a2Sdownsj 				switch (c) {
29657f623feSotto 				case '\\':
2977cb960a2Sdownsj 				case '$': case '`':
2987cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
2997cb960a2Sdownsj 					break;
30057f623feSotto 				case '"':
30157f623feSotto 					if ((cf & HEREDOC) == 0) {
30257f623feSotto 						*wp++ = QCHAR, *wp++ = c;
30357f623feSotto 						break;
30457f623feSotto 					}
30525d8d262Smoritz 					/* FALLTHROUGH */
3067cb960a2Sdownsj 				default:
307d3ecfddbSstsp 					if (cf & UNESCAPE) {
308d3ecfddbSstsp 						*wp++ = QCHAR, *wp++ = c;
309d3ecfddbSstsp 						break;
310d3ecfddbSstsp 					}
3117cb960a2Sdownsj 					Xcheck(ws, wp);
312e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
3137cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = '\\';
3147cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = c;
315e55c1b2cSdownsj 					}
3167cb960a2Sdownsj 					break;
3177cb960a2Sdownsj 				}
3187cb960a2Sdownsj 				break;
3197cb960a2Sdownsj 			case '$':
3207cb960a2Sdownsj 				c = getsc();
3217cb960a2Sdownsj 				if (c == '(') /*)*/ {
3227cb960a2Sdownsj 					c = getsc();
3237cb960a2Sdownsj 					if (c == '(') /*)*/ {
3243b015934Smillert 						PUSH_STATE(SASPAREN);
3253b015934Smillert 						statep->ls_sasparen.nparen = 2;
3263b015934Smillert 						statep->ls_sasparen.start =
3273b015934Smillert 						    Xsavepos(ws, wp);
3287cb960a2Sdownsj 						*wp++ = EXPRSUB;
3297cb960a2Sdownsj 					} else {
3307cb960a2Sdownsj 						ungetsc(c);
3313b015934Smillert 						PUSH_STATE(SCSPAREN);
3323b015934Smillert 						statep->ls_scsparen.nparen = 1;
3333b015934Smillert 						statep->ls_scsparen.csstate = 0;
3347cb960a2Sdownsj 						*wp++ = COMSUB;
3357cb960a2Sdownsj 					}
3367cb960a2Sdownsj 				} else if (c == '{') /*}*/ {
3377cb960a2Sdownsj 					*wp++ = OSUBST;
3383b015934Smillert 					*wp++ = '{'; /*}*/
3397cb960a2Sdownsj 					wp = get_brace_var(&ws, wp);
340e55c1b2cSdownsj 					c = getsc();
3413b015934Smillert 					/* allow :# and :% (ksh88 compat) */
3423b015934Smillert 					if (c == ':') {
3433b015934Smillert 						*wp++ = CHAR, *wp++ = c;
3443b015934Smillert 						c = getsc();
3453b015934Smillert 					}
3463b015934Smillert 					/* If this is a trim operation,
3473b015934Smillert 					 * treat (,|,) specially in STBRACE.
3483b015934Smillert 					 */
3497cb960a2Sdownsj 					if (c == '#' || c == '%') {
3503b015934Smillert 						ungetsc(c);
3513b015934Smillert 						PUSH_STATE(STBRACE);
3527cb960a2Sdownsj 					} else {
3537cb960a2Sdownsj 						ungetsc(c);
35422a9cf94Sguenther 						if (state == SDQUOTE ||
35522a9cf94Sguenther 						    state == SBRACEQ)
35622a9cf94Sguenther 							PUSH_STATE(SBRACEQ);
35722a9cf94Sguenther 						else
3583b015934Smillert 							PUSH_STATE(SBRACE);
3597cb960a2Sdownsj 					}
3607cb960a2Sdownsj 				} else if (ctype(c, C_ALPHA)) {
3617cb960a2Sdownsj 					*wp++ = OSUBST;
3623b015934Smillert 					*wp++ = 'X';
3637cb960a2Sdownsj 					do {
3647cb960a2Sdownsj 						Xcheck(ws, wp);
3657cb960a2Sdownsj 						*wp++ = c;
3667cb960a2Sdownsj 						c = getsc();
3677cb960a2Sdownsj 					} while (ctype(c, C_ALPHA|C_DIGIT));
3687cb960a2Sdownsj 					*wp++ = '\0';
3697cb960a2Sdownsj 					*wp++ = CSUBST;
3703b015934Smillert 					*wp++ = 'X';
3717cb960a2Sdownsj 					ungetsc(c);
3727cb960a2Sdownsj 				} else if (ctype(c, C_DIGIT|C_VAR1)) {
3737cb960a2Sdownsj 					Xcheck(ws, wp);
3747cb960a2Sdownsj 					*wp++ = OSUBST;
3753b015934Smillert 					*wp++ = 'X';
3767cb960a2Sdownsj 					*wp++ = c;
3777cb960a2Sdownsj 					*wp++ = '\0';
3787cb960a2Sdownsj 					*wp++ = CSUBST;
3793b015934Smillert 					*wp++ = 'X';
3807cb960a2Sdownsj 				} else {
3817cb960a2Sdownsj 					*wp++ = CHAR, *wp++ = '$';
3827cb960a2Sdownsj 					ungetsc(c);
3837cb960a2Sdownsj 				}
3847cb960a2Sdownsj 				break;
3857cb960a2Sdownsj 			case '`':
3863b015934Smillert 				PUSH_STATE(SBQUOTE);
3877cb960a2Sdownsj 				*wp++ = COMSUB;
3887cb960a2Sdownsj 				/* Need to know if we are inside double quotes
3897cb960a2Sdownsj 				 * since sh/at&t-ksh translate the \" to " in
3907cb960a2Sdownsj 				 * "`..\"..`".
3913b015934Smillert 				 * This is not done in posix mode (section
3923b015934Smillert 				 * 3.2.3, Double Quotes: "The backquote shall
3933b015934Smillert 				 * retain its special meaning introducing the
3943b015934Smillert 				 * other form of command substitution (see
3953b015934Smillert 				 * 3.6.3). The portion of the quoted string
3963b015934Smillert 				 * from the initial backquote and the
3973b015934Smillert 				 * characters up to the next backquote that
3983b015934Smillert 				 * is not preceded by a backslash (having
3993b015934Smillert 				 * escape characters removed) defines that
4003b015934Smillert 				 * command whose output replaces `...` when
4013b015934Smillert 				 * the word is expanded."
4023b015934Smillert 				 * Section 3.6.3, Command Substitution:
4033b015934Smillert 				 * "Within the backquoted style of command
4043b015934Smillert 				 * substitution, backslash shall retain its
4053b015934Smillert 				 * literal meaning, except when followed by
4063b015934Smillert 				 * $ ` \.").
4077cb960a2Sdownsj 				 */
4083b015934Smillert 				statep->ls_sbquote.indquotes = 0;
4093b015934Smillert 				if (!Flag(FPOSIX)) {
4103b015934Smillert 					Lex_state *s = statep;
4113b015934Smillert 					Lex_state *base = state_info.base;
4123b015934Smillert 					while (1) {
4133b015934Smillert 						for (; s != base; s--) {
4143b015934Smillert 							if (s->ls_state == SDQUOTE) {
4153b015934Smillert 								statep->ls_sbquote.indquotes = 1;
4163b015934Smillert 								break;
4173b015934Smillert 							}
4183b015934Smillert 						}
4193b015934Smillert 						if (s != base)
4203b015934Smillert 							break;
4213b015934Smillert 						if (!(s = s->ls_info.base))
4223b015934Smillert 							break;
4233b015934Smillert 						base = s-- - STATE_BSIZE;
4243b015934Smillert 					}
4253b015934Smillert 				}
4267cb960a2Sdownsj 				break;
4277cb960a2Sdownsj 			default:
4287cb960a2Sdownsj 				*wp++ = CHAR, *wp++ = c;
4297cb960a2Sdownsj 			}
4307cb960a2Sdownsj 			break;
4317cb960a2Sdownsj 
4327cb960a2Sdownsj 		case SSQUOTE:
4337cb960a2Sdownsj 			if (c == '\'') {
4343b015934Smillert 				POP_STATE();
43522a9cf94Sguenther 				if (state == SBRACEQ) {
43622a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
43722a9cf94Sguenther 					break;
43822a9cf94Sguenther 				}
4397cb960a2Sdownsj 				*wp++ = CQUOTE;
440e55c1b2cSdownsj 				ignore_backslash_newline--;
4417cb960a2Sdownsj 			} else
4427cb960a2Sdownsj 				*wp++ = QCHAR, *wp++ = c;
4437cb960a2Sdownsj 			break;
4447cb960a2Sdownsj 
4457cb960a2Sdownsj 		case SDQUOTE:
4467cb960a2Sdownsj 			if (c == '"') {
4473b015934Smillert 				POP_STATE();
4487cb960a2Sdownsj 				*wp++ = CQUOTE;
4497cb960a2Sdownsj 			} else
4507cb960a2Sdownsj 				goto Subst;
4517cb960a2Sdownsj 			break;
4527cb960a2Sdownsj 
4533b015934Smillert 		case SCSPAREN: /* $( .. ) */
4547cb960a2Sdownsj 			/* todo: deal with $(...) quoting properly
4557cb960a2Sdownsj 			 * kludge to partly fake quoting inside $(..): doesn't
4567cb960a2Sdownsj 			 * really work because nested $(..) or ${..} inside
4577cb960a2Sdownsj 			 * double quotes aren't dealt with.
4587cb960a2Sdownsj 			 */
4593b015934Smillert 			switch (statep->ls_scsparen.csstate) {
4607cb960a2Sdownsj 			case 0: /* normal */
4617cb960a2Sdownsj 				switch (c) {
4627cb960a2Sdownsj 				case '(':
4633b015934Smillert 					statep->ls_scsparen.nparen++;
4647cb960a2Sdownsj 					break;
4657cb960a2Sdownsj 				case ')':
4663b015934Smillert 					statep->ls_scsparen.nparen--;
4677cb960a2Sdownsj 					break;
4687cb960a2Sdownsj 				case '\\':
4693b015934Smillert 					statep->ls_scsparen.csstate = 1;
4707cb960a2Sdownsj 					break;
4717cb960a2Sdownsj 				case '"':
4723b015934Smillert 					statep->ls_scsparen.csstate = 2;
4737cb960a2Sdownsj 					break;
4747cb960a2Sdownsj 				case '\'':
4753b015934Smillert 					statep->ls_scsparen.csstate = 4;
476e55c1b2cSdownsj 					ignore_backslash_newline++;
4777cb960a2Sdownsj 					break;
4787cb960a2Sdownsj 				}
4797cb960a2Sdownsj 				break;
4807cb960a2Sdownsj 
4817cb960a2Sdownsj 			case 1: /* backslash in normal mode */
4827cb960a2Sdownsj 			case 3: /* backslash in double quotes */
4833b015934Smillert 				--statep->ls_scsparen.csstate;
4847cb960a2Sdownsj 				break;
4857cb960a2Sdownsj 
4867cb960a2Sdownsj 			case 2: /* double quotes */
4877cb960a2Sdownsj 				if (c == '"')
4883b015934Smillert 					statep->ls_scsparen.csstate = 0;
4897cb960a2Sdownsj 				else if (c == '\\')
4903b015934Smillert 					statep->ls_scsparen.csstate = 3;
4917cb960a2Sdownsj 				break;
4927cb960a2Sdownsj 
4937cb960a2Sdownsj 			case 4: /* single quotes */
494e55c1b2cSdownsj 				if (c == '\'') {
4953b015934Smillert 					statep->ls_scsparen.csstate = 0;
496e55c1b2cSdownsj 					ignore_backslash_newline--;
497e55c1b2cSdownsj 				}
4987cb960a2Sdownsj 				break;
4997cb960a2Sdownsj 			}
5003b015934Smillert 			if (statep->ls_scsparen.nparen == 0) {
5013b015934Smillert 				POP_STATE();
5027cb960a2Sdownsj 				*wp++ = 0; /* end of COMSUB */
5037cb960a2Sdownsj 			} else
5047cb960a2Sdownsj 				*wp++ = c;
5057cb960a2Sdownsj 			break;
5067cb960a2Sdownsj 
5073b015934Smillert 		case SASPAREN: /* $(( .. )) */
5087cb960a2Sdownsj 			/* todo: deal with $((...); (...)) properly */
5097cb960a2Sdownsj 			/* XXX should nest using existing state machine
5107cb960a2Sdownsj 			 * (embed "..", $(...), etc.) */
5117cb960a2Sdownsj 			if (c == '(')
5123b015934Smillert 				statep->ls_sasparen.nparen++;
5137cb960a2Sdownsj 			else if (c == ')') {
5143b015934Smillert 				statep->ls_sasparen.nparen--;
5153b015934Smillert 				if (statep->ls_sasparen.nparen == 1) {
5167cb960a2Sdownsj 					/*(*/
5177cb960a2Sdownsj 					if ((c2 = getsc()) == ')') {
5183b015934Smillert 						POP_STATE();
5197cb960a2Sdownsj 						*wp++ = 0; /* end of EXPRSUB */
5207cb960a2Sdownsj 						break;
5217cb960a2Sdownsj 					} else {
5223b015934Smillert 						char *s;
5233b015934Smillert 
5247cb960a2Sdownsj 						ungetsc(c2);
5257cb960a2Sdownsj 						/* mismatched parenthesis -
5267cb960a2Sdownsj 						 * assume we were really
5277cb960a2Sdownsj 						 * parsing a $(..) expression
5287cb960a2Sdownsj 						 */
5293b015934Smillert 						s = Xrestpos(ws, wp,
5303b015934Smillert 						    statep->ls_sasparen.start);
5313b015934Smillert 						memmove(s + 1, s, wp - s);
5323b015934Smillert 						*s++ = COMSUB;
5333b015934Smillert 						*s = '('; /*)*/
5347cb960a2Sdownsj 						wp++;
5353b015934Smillert 						statep->ls_scsparen.nparen = 1;
5363b015934Smillert 						statep->ls_scsparen.csstate = 0;
5377a8124d8Sderaadt 						state = statep->ls_state =
5387a8124d8Sderaadt 						    SCSPAREN;
5397cb960a2Sdownsj 					}
5407cb960a2Sdownsj 				}
5417cb960a2Sdownsj 			}
5427cb960a2Sdownsj 			*wp++ = c;
5437cb960a2Sdownsj 			break;
5447cb960a2Sdownsj 
54522a9cf94Sguenther 		case SBRACEQ:
5467cb960a2Sdownsj 		case SBRACE:
5477cb960a2Sdownsj 			/*{*/
5487cb960a2Sdownsj 			if (c == '}') {
5493b015934Smillert 				POP_STATE();
5507cb960a2Sdownsj 				*wp++ = CSUBST;
5513b015934Smillert 				*wp++ = /*{*/ '}';
5527cb960a2Sdownsj 			} else
5537cb960a2Sdownsj 				goto Sbase1;
5547cb960a2Sdownsj 			break;
5557cb960a2Sdownsj 
5567cb960a2Sdownsj 		case STBRACE:
5573b015934Smillert 			/* Same as SBRACE, except (,|,) treated specially */
5587cb960a2Sdownsj 			/*{*/
5597cb960a2Sdownsj 			if (c == '}') {
5603b015934Smillert 				POP_STATE();
5617cb960a2Sdownsj 				*wp++ = CSUBST;
5623b015934Smillert 				*wp++ = /*{*/ '}';
5637cb960a2Sdownsj 			} else if (c == '|') {
5647cb960a2Sdownsj 				*wp++ = SPAT;
5653b015934Smillert 			} else if (c == '(') {
5663b015934Smillert 				*wp++ = OPAT;
5673b015934Smillert 				*wp++ = ' ';	/* simile for @ */
5683b015934Smillert 				PUSH_STATE(SPATTERN);
5697cb960a2Sdownsj 			} else
5707cb960a2Sdownsj 				goto Sbase1;
5717cb960a2Sdownsj 			break;
5727cb960a2Sdownsj 
5737cb960a2Sdownsj 		case SBQUOTE:
5747cb960a2Sdownsj 			if (c == '`') {
5757cb960a2Sdownsj 				*wp++ = 0;
5763b015934Smillert 				POP_STATE();
5777cb960a2Sdownsj 			} else if (c == '\\') {
5787cb960a2Sdownsj 				switch (c = getsc()) {
5797cb960a2Sdownsj 				case '\\':
5807cb960a2Sdownsj 				case '$': case '`':
5817cb960a2Sdownsj 					*wp++ = c;
5827cb960a2Sdownsj 					break;
5837cb960a2Sdownsj 				case '"':
5843b015934Smillert 					if (statep->ls_sbquote.indquotes) {
5857cb960a2Sdownsj 						*wp++ = c;
5867cb960a2Sdownsj 						break;
5877cb960a2Sdownsj 					}
5887eab881bSjaredy 					/* FALLTHROUGH */
5897cb960a2Sdownsj 				default:
590e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
5917cb960a2Sdownsj 						*wp++ = '\\';
5927cb960a2Sdownsj 						*wp++ = c;
593e55c1b2cSdownsj 					}
5947cb960a2Sdownsj 					break;
5957cb960a2Sdownsj 				}
5967cb960a2Sdownsj 			} else
5977cb960a2Sdownsj 				*wp++ = c;
5987cb960a2Sdownsj 			break;
5997cb960a2Sdownsj 
6007cb960a2Sdownsj 		case SWORD:	/* ONEWORD */
6017cb960a2Sdownsj 			goto Subst;
6027cb960a2Sdownsj 
6033b015934Smillert 		case SLETPAREN:	/* LETEXPR: (( ... )) */
6047cb960a2Sdownsj 			/*(*/
6057cb960a2Sdownsj 			if (c == ')') {
6063b015934Smillert 				if (statep->ls_sletparen.nparen > 0)
6073b015934Smillert 				    --statep->ls_sletparen.nparen;
6087cb960a2Sdownsj 				/*(*/
6097cb960a2Sdownsj 				else if ((c2 = getsc()) == ')') {
6107cb960a2Sdownsj 					c = 0;
6117cb960a2Sdownsj 					*wp++ = CQUOTE;
6127cb960a2Sdownsj 					goto Done;
6137cb960a2Sdownsj 				} else
6147cb960a2Sdownsj 					ungetsc(c2);
6157cb960a2Sdownsj 			} else if (c == '(')
6167cb960a2Sdownsj 				/* parenthesis inside quotes and backslashes
6177cb960a2Sdownsj 				 * are lost, but at&t ksh doesn't count them
6187cb960a2Sdownsj 				 * either
6197cb960a2Sdownsj 				 */
6203b015934Smillert 				++statep->ls_sletparen.nparen;
6217cb960a2Sdownsj 			goto Sbase2;
6227cb960a2Sdownsj 
6237cb960a2Sdownsj 		case SHEREDELIM:	/* <<,<<- delimiter */
6247cb960a2Sdownsj 			/* XXX chuck this state (and the next) - use
6257cb960a2Sdownsj 			 * the existing states ($ and \`..` should be
6267cb960a2Sdownsj 			 * stripped of their specialness after the
6277cb960a2Sdownsj 			 * fact).
6287cb960a2Sdownsj 			 */
6297cb960a2Sdownsj 			/* here delimiters need a special case since
6307cb960a2Sdownsj 			 * $ and `..` are not to be treated specially
6317cb960a2Sdownsj 			 */
6327cb960a2Sdownsj 			if (c == '\\') {
6337cb960a2Sdownsj 				c = getsc();
634e55c1b2cSdownsj 				if (c) { /* trailing \ is lost */
6357cb960a2Sdownsj 					*wp++ = QCHAR;
6367cb960a2Sdownsj 					*wp++ = c;
6377cb960a2Sdownsj 				}
6387cb960a2Sdownsj 			} else if (c == '\'') {
6393b015934Smillert 				PUSH_STATE(SSQUOTE);
6407cb960a2Sdownsj 				*wp++ = OQUOTE;
641e55c1b2cSdownsj 				ignore_backslash_newline++;
6427cb960a2Sdownsj 			} else if (c == '"') {
6433b015934Smillert 				state = statep->ls_state = SHEREDQUOTE;
6447cb960a2Sdownsj 				*wp++ = OQUOTE;
6457cb960a2Sdownsj 			} else {
6467cb960a2Sdownsj 				*wp++ = CHAR;
6477cb960a2Sdownsj 				*wp++ = c;
6487cb960a2Sdownsj 			}
6497cb960a2Sdownsj 			break;
6507cb960a2Sdownsj 
6517cb960a2Sdownsj 		case SHEREDQUOTE:	/* " in <<,<<- delimiter */
6527cb960a2Sdownsj 			if (c == '"') {
6537cb960a2Sdownsj 				*wp++ = CQUOTE;
6543b015934Smillert 				state = statep->ls_state = SHEREDELIM;
6557cb960a2Sdownsj 			} else {
656e55c1b2cSdownsj 				if (c == '\\') {
657e55c1b2cSdownsj 					switch (c = getsc()) {
658e55c1b2cSdownsj 					case '\\': case '"':
659e55c1b2cSdownsj 					case '$': case '`':
6607cb960a2Sdownsj 						break;
661e55c1b2cSdownsj 					default:
662e55c1b2cSdownsj 						if (c) { /* trailing \ lost */
663e55c1b2cSdownsj 							*wp++ = CHAR;
664e55c1b2cSdownsj 							*wp++ = '\\';
665e55c1b2cSdownsj 						}
666e55c1b2cSdownsj 						break;
667e55c1b2cSdownsj 					}
668e55c1b2cSdownsj 				}
6697cb960a2Sdownsj 				*wp++ = CHAR;
6707cb960a2Sdownsj 				*wp++ = c;
6717cb960a2Sdownsj 			}
6727cb960a2Sdownsj 			break;
6737cb960a2Sdownsj 
6747cb960a2Sdownsj 		case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
6757cb960a2Sdownsj 			if ( /*(*/ c == ')') {
6767cb960a2Sdownsj 				*wp++ = CPAT;
6773b015934Smillert 				POP_STATE();
6783b015934Smillert 			} else if (c == '|') {
6797cb960a2Sdownsj 				*wp++ = SPAT;
6803b015934Smillert 			} else if (c == '(') {
6813b015934Smillert 				*wp++ = OPAT;
6823b015934Smillert 				*wp++ = ' ';	/* simile for @ */
6833b015934Smillert 				PUSH_STATE(SPATTERN);
6843b015934Smillert 			} else
6857cb960a2Sdownsj 				goto Sbase1;
6867cb960a2Sdownsj 			break;
6877cb960a2Sdownsj 		}
6887cb960a2Sdownsj 	}
6897cb960a2Sdownsj Done:
6907cb960a2Sdownsj 	Xcheck(ws, wp);
6913b015934Smillert 	if (statep != &states[1])
6923b015934Smillert 		/* XXX figure out what is missing */
6937cb960a2Sdownsj 		yyerror("no closing quote\n");
6947cb960a2Sdownsj 
6957cb960a2Sdownsj 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
6967cb960a2Sdownsj 	if (state == SHEREDELIM)
6977cb960a2Sdownsj 		state = SBASE;
6987cb960a2Sdownsj 
6993b015934Smillert 	dp = Xstring(ws, wp);
7007a8124d8Sderaadt 	if ((c == '<' || c == '>') && state == SBASE &&
7017a8124d8Sderaadt 	    ((c2 = Xlength(ws, wp)) == 0 ||
7027a8124d8Sderaadt 	    (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
703*8c046d24Snicm 		struct ioword *iop = alloc(sizeof(*iop), ATEMP);
7047cb960a2Sdownsj 
7053b015934Smillert 		if (c2 == 2)
7063b015934Smillert 			iop->unit = dp[1] - '0';
7077cb960a2Sdownsj 		else
7083b015934Smillert 			iop->unit = c == '>'; /* 0 for <, 1 for > */
7097cb960a2Sdownsj 
7107cb960a2Sdownsj 		c2 = getsc();
7117cb960a2Sdownsj 		/* <<, >>, <> are ok, >< is not */
7127cb960a2Sdownsj 		if (c == c2 || (c == '<' && c2 == '>')) {
7137cb960a2Sdownsj 			iop->flag = c == c2 ?
7147cb960a2Sdownsj 			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
71518bbba6bSmillert 			if (iop->flag == IOHERE) {
716e55c1b2cSdownsj 				if ((c2 = getsc()) == '-')
7177cb960a2Sdownsj 					iop->flag |= IOSKIP;
7187cb960a2Sdownsj 				else
7197cb960a2Sdownsj 					ungetsc(c2);
72018bbba6bSmillert 			}
7217cb960a2Sdownsj 		} else if (c2 == '&')
7227cb960a2Sdownsj 			iop->flag = IODUP | (c == '<' ? IORDUP : 0);
7237cb960a2Sdownsj 		else {
7247cb960a2Sdownsj 			iop->flag = c == '>' ? IOWRITE : IOREAD;
7257cb960a2Sdownsj 			if (c == '>' && c2 == '|')
7267cb960a2Sdownsj 				iop->flag |= IOCLOB;
7277cb960a2Sdownsj 			else
7287cb960a2Sdownsj 				ungetsc(c2);
7297cb960a2Sdownsj 		}
7307cb960a2Sdownsj 
731355ffa75Stedu 		iop->name = NULL;
732355ffa75Stedu 		iop->delim = NULL;
733355ffa75Stedu 		iop->heredoc = NULL;
7343b015934Smillert 		Xfree(ws, wp);	/* free word */
7357cb960a2Sdownsj 		yylval.iop = iop;
7367cb960a2Sdownsj 		return REDIR;
7377cb960a2Sdownsj 	}
7383b015934Smillert 
7393b015934Smillert 	if (wp == dp && state == SBASE) {
7403b015934Smillert 		Xfree(ws, wp);	/* free word */
7413b015934Smillert 		/* no word, process LEX1 character */
7423b015934Smillert 		switch (c) {
7433b015934Smillert 		default:
7443b015934Smillert 			return c;
7453b015934Smillert 
7463b015934Smillert 		case '|':
7473b015934Smillert 		case '&':
7483b015934Smillert 		case ';':
7493b015934Smillert 			if ((c2 = getsc()) == c)
7503b015934Smillert 				c = (c == ';') ? BREAK :
7513b015934Smillert 				    (c == '|') ? LOGOR :
7523b015934Smillert 				    (c == '&') ? LOGAND :
7533b015934Smillert 				    YYERRCODE;
7543b015934Smillert 			else if (c == '|' && c2 == '&')
7553b015934Smillert 				c = COPROC;
7563b015934Smillert 			else
7573b015934Smillert 				ungetsc(c2);
7583b015934Smillert 			return c;
7593b015934Smillert 
7607cb960a2Sdownsj 		case '\n':
7617cb960a2Sdownsj 			gethere();
7627cb960a2Sdownsj 			if (cf & CONTIN)
7637cb960a2Sdownsj 				goto Again;
7647cb960a2Sdownsj 			return c;
7657cb960a2Sdownsj 
7667cb960a2Sdownsj 		case '(':  /*)*/
767b69beb66Sgrr 			if (!Flag(FSH)) {
7687cb960a2Sdownsj 				if ((c2 = getsc()) == '(') /*)*/
7693b015934Smillert 					/* XXX need to handle ((...); (...)) */
7707cb960a2Sdownsj 					c = MDPAREN;
7717cb960a2Sdownsj 				else
7727cb960a2Sdownsj 					ungetsc(c2);
773b69beb66Sgrr 			}
7747cb960a2Sdownsj 			return c;
7757cb960a2Sdownsj 		  /*(*/
7767cb960a2Sdownsj 		case ')':
7777cb960a2Sdownsj 			return c;
7787cb960a2Sdownsj 		}
7797cb960a2Sdownsj 	}
7807cb960a2Sdownsj 
7817cb960a2Sdownsj 	*wp++ = EOS;		/* terminate word */
7827cb960a2Sdownsj 	yylval.cp = Xclose(ws, wp);
78394e42df6Smillert 	if (state == SWORD || state == SLETPAREN)	/* ONEWORD? */
7847cb960a2Sdownsj 		return LWORD;
7857cb960a2Sdownsj 	ungetsc(c);		/* unget terminator */
7867cb960a2Sdownsj 
7877cb960a2Sdownsj 	/* copy word to unprefixed string ident */
7887cb960a2Sdownsj 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
7897cb960a2Sdownsj 		*dp++ = *sp++;
79012e7fb2dSjmc 	/* Make sure the ident array stays '\0' padded */
7917cb960a2Sdownsj 	memset(dp, 0, (ident+IDENT) - dp + 1);
7927cb960a2Sdownsj 	if (c != EOS)
7937cb960a2Sdownsj 		*ident = '\0';	/* word is not unquoted */
7947cb960a2Sdownsj 
7957cb960a2Sdownsj 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
7967cb960a2Sdownsj 		struct tbl *p;
7977cb960a2Sdownsj 		int h = hash(ident);
7987cb960a2Sdownsj 
7997cb960a2Sdownsj 		/* { */
8006df3ee40Sotto 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
8017a8124d8Sderaadt 		    (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
8027cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8037cb960a2Sdownsj 			return p->val.i;
8047cb960a2Sdownsj 		}
8056df3ee40Sotto 		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
8067a8124d8Sderaadt 		    (p->flag & ISSET)) {
8077894b443Smillert 			Source *s;
8087cb960a2Sdownsj 
8097cb960a2Sdownsj 			for (s = source; s->type == SALIAS; s = s->next)
8107cb960a2Sdownsj 				if (s->u.tblp == p)
8117cb960a2Sdownsj 					return LWORD;
8127cb960a2Sdownsj 			/* push alias expansion */
8137cb960a2Sdownsj 			s = pushs(SALIAS, source->areap);
8147cb960a2Sdownsj 			s->start = s->str = p->val.s;
8157cb960a2Sdownsj 			s->u.tblp = p;
8167cb960a2Sdownsj 			s->next = source;
8177cb960a2Sdownsj 			source = s;
8187cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8197cb960a2Sdownsj 			goto Again;
8207cb960a2Sdownsj 		}
8217cb960a2Sdownsj 	}
8227cb960a2Sdownsj 
8237cb960a2Sdownsj 	return LWORD;
8247cb960a2Sdownsj }
8257cb960a2Sdownsj 
8267cb960a2Sdownsj static void
827c5d5393cSotto gethere(void)
8287cb960a2Sdownsj {
8297894b443Smillert 	struct ioword **p;
8307cb960a2Sdownsj 
8317cb960a2Sdownsj 	for (p = heres; p < herep; p++)
8327cb960a2Sdownsj 		readhere(*p);
8337cb960a2Sdownsj 	herep = heres;
8347cb960a2Sdownsj }
8357cb960a2Sdownsj 
8367cb960a2Sdownsj /*
8377cb960a2Sdownsj  * read "<<word" text into temp file
8387cb960a2Sdownsj  */
8397cb960a2Sdownsj 
8407cb960a2Sdownsj static void
841c5d5393cSotto readhere(struct ioword *iop)
8427cb960a2Sdownsj {
8437894b443Smillert 	int c;
8447cb960a2Sdownsj 	char *volatile eof;
8457cb960a2Sdownsj 	char *eofp;
846e55c1b2cSdownsj 	int skiptabs;
847f00c5086Smillert 	XString xs;
848f00c5086Smillert 	char *xp;
849f00c5086Smillert 	int xpos;
8507cb960a2Sdownsj 
8517cb960a2Sdownsj 	eof = evalstr(iop->delim, 0);
8527cb960a2Sdownsj 
853e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
854e55c1b2cSdownsj 		ignore_backslash_newline++;
855e55c1b2cSdownsj 
856f00c5086Smillert 	Xinit(xs, xp, 256, ATEMP);
857f00c5086Smillert 
8587cb960a2Sdownsj 	for (;;) {
8597cb960a2Sdownsj 		eofp = eof;
8607cb960a2Sdownsj 		skiptabs = iop->flag & IOSKIP;
861f00c5086Smillert 		xpos = Xsavepos(xs, xp);
862e55c1b2cSdownsj 		while ((c = getsc()) != 0) {
8637cb960a2Sdownsj 			if (skiptabs) {
8647cb960a2Sdownsj 				if (c == '\t')
8657cb960a2Sdownsj 					continue;
8667cb960a2Sdownsj 				skiptabs = 0;
8677cb960a2Sdownsj 			}
8687cb960a2Sdownsj 			if (c != *eofp)
8697cb960a2Sdownsj 				break;
870f00c5086Smillert 			Xcheck(xs, xp);
871f00c5086Smillert 			Xput(xs, xp, c);
8727cb960a2Sdownsj 			eofp++;
8737cb960a2Sdownsj 		}
8747cb960a2Sdownsj 		/* Allow EOF here so commands with out trailing newlines
8757cb960a2Sdownsj 		 * will work (eg, ksh -c '...', $(...), etc).
8767cb960a2Sdownsj 		 */
877f00c5086Smillert 		if (*eofp == '\0' && (c == 0 || c == '\n')) {
878f00c5086Smillert 			xp = Xrestpos(xs, xp, xpos);
8797cb960a2Sdownsj 			break;
880f00c5086Smillert 		}
8817cb960a2Sdownsj 		ungetsc(c);
882e55c1b2cSdownsj 		while ((c = getsc()) != '\n') {
8837cb960a2Sdownsj 			if (c == 0)
8847cb960a2Sdownsj 				yyerror("here document `%s' unclosed\n", eof);
885f00c5086Smillert 			Xcheck(xs, xp);
886f00c5086Smillert 			Xput(xs, xp, c);
8877cb960a2Sdownsj 		}
888f00c5086Smillert 		Xcheck(xs, xp);
889f00c5086Smillert 		Xput(xs, xp, c);
8907cb960a2Sdownsj 	}
891f00c5086Smillert 	Xput(xs, xp, '\0');
892f00c5086Smillert 	iop->heredoc = Xclose(xs, xp);
893f00c5086Smillert 
894e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
895e55c1b2cSdownsj 		ignore_backslash_newline--;
8967cb960a2Sdownsj }
8977cb960a2Sdownsj 
8987cb960a2Sdownsj void
8997cb960a2Sdownsj yyerror(const char *fmt, ...)
9007cb960a2Sdownsj {
9017cb960a2Sdownsj 	va_list va;
9027cb960a2Sdownsj 
9037cb960a2Sdownsj 	/* pop aliases and re-reads */
9047cb960a2Sdownsj 	while (source->type == SALIAS || source->type == SREREAD)
9057cb960a2Sdownsj 		source = source->next;
9067cb960a2Sdownsj 	source->str = null;	/* zap pending input */
9077cb960a2Sdownsj 
9080e7d3a01Smillert 	error_prefix(true);
90969b9f96bSmillert 	va_start(va, fmt);
9107cb960a2Sdownsj 	shf_vfprintf(shl_out, fmt, va);
9117cb960a2Sdownsj 	va_end(va);
9127cb960a2Sdownsj 	errorf(null);
9137cb960a2Sdownsj }
9147cb960a2Sdownsj 
9157cb960a2Sdownsj /*
9167cb960a2Sdownsj  * input for yylex with alias expansion
9177cb960a2Sdownsj  */
9187cb960a2Sdownsj 
9197cb960a2Sdownsj Source *
920c5d5393cSotto pushs(int type, Area *areap)
9217cb960a2Sdownsj {
9227894b443Smillert 	Source *s;
9237cb960a2Sdownsj 
924*8c046d24Snicm 	s = alloc(sizeof(Source), areap);
9257cb960a2Sdownsj 	s->type = type;
9267cb960a2Sdownsj 	s->str = null;
9277cb960a2Sdownsj 	s->start = NULL;
9287cb960a2Sdownsj 	s->line = 0;
929e7b1290aSotto 	s->cmd_offset = 0;
9307cb960a2Sdownsj 	s->errline = 0;
9317cb960a2Sdownsj 	s->file = NULL;
9327cb960a2Sdownsj 	s->flags = 0;
9337cb960a2Sdownsj 	s->next = NULL;
9347cb960a2Sdownsj 	s->areap = areap;
9357cb960a2Sdownsj 	if (type == SFILE || type == SSTDIN) {
9367cb960a2Sdownsj 		char *dummy;
9377cb960a2Sdownsj 		Xinit(s->xs, dummy, 256, s->areap);
9387cb960a2Sdownsj 	} else
9397cb960a2Sdownsj 		memset(&s->xs, 0, sizeof(s->xs));
9407cb960a2Sdownsj 	return s;
9417cb960a2Sdownsj }
9427cb960a2Sdownsj 
9437cb960a2Sdownsj static int
944c5d5393cSotto getsc__(void)
9457cb960a2Sdownsj {
9467894b443Smillert 	Source *s = source;
9477894b443Smillert 	int c;
9487cb960a2Sdownsj 
9497cb960a2Sdownsj 	while ((c = *s->str++) == 0) {
9507cb960a2Sdownsj 		s->str = NULL;		/* return 0 for EOF by default */
9517cb960a2Sdownsj 		switch (s->type) {
9527cb960a2Sdownsj 		case SEOF:
9537cb960a2Sdownsj 			s->str = null;
9547cb960a2Sdownsj 			return 0;
9557cb960a2Sdownsj 
9567cb960a2Sdownsj 		case SSTDIN:
9577cb960a2Sdownsj 		case SFILE:
9587cb960a2Sdownsj 			getsc_line(s);
9597cb960a2Sdownsj 			break;
9607cb960a2Sdownsj 
9617cb960a2Sdownsj 		case SWSTR:
9627cb960a2Sdownsj 			break;
9637cb960a2Sdownsj 
9647cb960a2Sdownsj 		case SSTRING:
9657cb960a2Sdownsj 			break;
9667cb960a2Sdownsj 
9677cb960a2Sdownsj 		case SWORDS:
9687cb960a2Sdownsj 			s->start = s->str = *s->u.strv++;
9697cb960a2Sdownsj 			s->type = SWORDSEP;
9707cb960a2Sdownsj 			break;
9717cb960a2Sdownsj 
9727cb960a2Sdownsj 		case SWORDSEP:
9737cb960a2Sdownsj 			if (*s->u.strv == NULL) {
97496085982Snicm 				s->start = s->str = "\n";
9757cb960a2Sdownsj 				s->type = SEOF;
9767cb960a2Sdownsj 			} else {
97796085982Snicm 				s->start = s->str = " ";
9787cb960a2Sdownsj 				s->type = SWORDS;
9797cb960a2Sdownsj 			}
9807cb960a2Sdownsj 			break;
9817cb960a2Sdownsj 
9827cb960a2Sdownsj 		case SALIAS:
9837cb960a2Sdownsj 			if (s->flags & SF_ALIASEND) {
9847cb960a2Sdownsj 				/* pass on an unused SF_ALIAS flag */
9857cb960a2Sdownsj 				source = s->next;
9867cb960a2Sdownsj 				source->flags |= s->flags & SF_ALIAS;
9877cb960a2Sdownsj 				s = source;
9887a8124d8Sderaadt 			} else if (*s->u.tblp->val.s &&
989e569fc7cSderaadt 			    isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) {
9907cb960a2Sdownsj 				source = s = s->next;	/* pop source stack */
991e55c1b2cSdownsj 				/* Note that this alias ended with a space,
992e55c1b2cSdownsj 				 * enabling alias expansion on the following
993e55c1b2cSdownsj 				 * word.
994e55c1b2cSdownsj 				 */
9957cb960a2Sdownsj 				s->flags |= SF_ALIAS;
9967cb960a2Sdownsj 			} else {
997e55c1b2cSdownsj 				/* At this point, we need to keep the current
998e55c1b2cSdownsj 				 * alias in the source list so recursive
999e55c1b2cSdownsj 				 * aliases can be detected and we also need
1000e55c1b2cSdownsj 				 * to return the next character.  Do this
1001e55c1b2cSdownsj 				 * by temporarily popping the alias to get
1002e55c1b2cSdownsj 				 * the next character and then put it back
1003e55c1b2cSdownsj 				 * in the source list with the SF_ALIASEND
1004e55c1b2cSdownsj 				 * flag set.
10057cb960a2Sdownsj 				 */
1006e55c1b2cSdownsj 				source = s->next;	/* pop source stack */
1007e55c1b2cSdownsj 				source->flags |= s->flags & SF_ALIAS;
1008e55c1b2cSdownsj 				c = getsc__();
1009e55c1b2cSdownsj 				if (c) {
10107cb960a2Sdownsj 					s->flags |= SF_ALIASEND;
1011e55c1b2cSdownsj 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1012e55c1b2cSdownsj 					s->start = s->str = s->ugbuf;
1013e55c1b2cSdownsj 					s->next = source;
1014e55c1b2cSdownsj 					source = s;
1015e55c1b2cSdownsj 				} else {
1016e55c1b2cSdownsj 					s = source;
1017e55c1b2cSdownsj 					/* avoid reading eof twice */
1018e55c1b2cSdownsj 					s->str = NULL;
1019e7bc3c65Sdownsj 					break;
1020e55c1b2cSdownsj 				}
10217cb960a2Sdownsj 			}
10227cb960a2Sdownsj 			continue;
10237cb960a2Sdownsj 
10247cb960a2Sdownsj 		case SREREAD:
1025e55c1b2cSdownsj 			if (s->start != s->ugbuf) /* yuck */
10267cb960a2Sdownsj 				afree(s->u.freeme, ATEMP);
10277cb960a2Sdownsj 			source = s = s->next;
10287cb960a2Sdownsj 			continue;
10297cb960a2Sdownsj 		}
10307cb960a2Sdownsj 		if (s->str == NULL) {
10317cb960a2Sdownsj 			s->type = SEOF;
10327cb960a2Sdownsj 			s->start = s->str = null;
10337cb960a2Sdownsj 			return '\0';
10347cb960a2Sdownsj 		}
10357cb960a2Sdownsj 		if (s->flags & SF_ECHO) {
10367cb960a2Sdownsj 			shf_puts(s->str, shl_out);
10377cb960a2Sdownsj 			shf_flush(shl_out);
10387cb960a2Sdownsj 		}
10397cb960a2Sdownsj 	}
10407cb960a2Sdownsj 	return c;
10417cb960a2Sdownsj }
10427cb960a2Sdownsj 
10437cb960a2Sdownsj static void
1044c5d5393cSotto getsc_line(Source *s)
10457cb960a2Sdownsj {
10467cb960a2Sdownsj 	char *xp = Xstring(s->xs, xp);
10477cb960a2Sdownsj 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
10487cb960a2Sdownsj 	int have_tty = interactive && (s->flags & SF_TTY);
10497cb960a2Sdownsj 
10507cb960a2Sdownsj 	/* Done here to ensure nothing odd happens when a timeout occurs */
10517cb960a2Sdownsj 	XcheckN(s->xs, xp, LINE);
10527cb960a2Sdownsj 	*xp = '\0';
10537cb960a2Sdownsj 	s->start = s->str = xp;
10547cb960a2Sdownsj 
10557cb960a2Sdownsj 	if (have_tty && ksh_tmout) {
10567cb960a2Sdownsj 		ksh_tmout_state = TMOUT_READING;
10577cb960a2Sdownsj 		alarm(ksh_tmout);
10587cb960a2Sdownsj 	}
10597cb960a2Sdownsj #ifdef EDIT
10607cb960a2Sdownsj 	if (have_tty && (0
10617cb960a2Sdownsj # ifdef VI
10627cb960a2Sdownsj 	    || Flag(FVI)
10637cb960a2Sdownsj # endif /* VI */
10647cb960a2Sdownsj # ifdef EMACS
10657cb960a2Sdownsj 	    || Flag(FEMACS) || Flag(FGMACS)
10667cb960a2Sdownsj # endif /* EMACS */
10677a8124d8Sderaadt 	    )) {
10687cb960a2Sdownsj 		int nread;
10697cb960a2Sdownsj 
10707cb960a2Sdownsj 		nread = x_read(xp, LINE);
10717cb960a2Sdownsj 		if (nread < 0)	/* read error */
10727cb960a2Sdownsj 			nread = 0;
10737cb960a2Sdownsj 		xp[nread] = '\0';
10747cb960a2Sdownsj 		xp += nread;
10757cb960a2Sdownsj 	}
10767cb960a2Sdownsj 	else
10777cb960a2Sdownsj #endif /* EDIT */
10787cb960a2Sdownsj 	{
10797cb960a2Sdownsj 		if (interactive) {
10807cb960a2Sdownsj 			pprompt(prompt, 0);
10817cb960a2Sdownsj 		} else
10827cb960a2Sdownsj 			s->line++;
10837cb960a2Sdownsj 
10847cb960a2Sdownsj 		while (1) {
10857cb960a2Sdownsj 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
10867cb960a2Sdownsj 
10877a8124d8Sderaadt 			if (!p && shf_error(s->u.shf) &&
10887a8124d8Sderaadt 			    shf_errno(s->u.shf) == EINTR) {
10897cb960a2Sdownsj 				shf_clearerr(s->u.shf);
10907cb960a2Sdownsj 				if (trap)
10917cb960a2Sdownsj 					runtraps(0);
10927cb960a2Sdownsj 				continue;
10937cb960a2Sdownsj 			}
10947cb960a2Sdownsj 			if (!p || (xp = p, xp[-1] == '\n'))
10957cb960a2Sdownsj 				break;
10967cb960a2Sdownsj 			/* double buffer size */
10977cb960a2Sdownsj 			xp++; /* move past null so doubling works... */
10987cb960a2Sdownsj 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
10997cb960a2Sdownsj 			xp--; /* ...and move back again */
11007cb960a2Sdownsj 		}
11017cb960a2Sdownsj 		/* flush any unwanted input so other programs/builtins
11027cb960a2Sdownsj 		 * can read it.  Not very optimal, but less error prone
11037cb960a2Sdownsj 		 * than flushing else where, dealing with redirections,
11047cb960a2Sdownsj 		 * etc..
11057cb960a2Sdownsj 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
11067cb960a2Sdownsj 		 */
11077cb960a2Sdownsj 		if (s->type == SSTDIN)
11087cb960a2Sdownsj 			shf_flush(s->u.shf);
11097cb960a2Sdownsj 	}
11107cb960a2Sdownsj 	/* XXX: temporary kludge to restore source after a
11117cb960a2Sdownsj 	 * trap may have been executed.
11127cb960a2Sdownsj 	 */
11137cb960a2Sdownsj 	source = s;
111494e42df6Smillert 	if (have_tty && ksh_tmout) {
11157cb960a2Sdownsj 		ksh_tmout_state = TMOUT_EXECUTING;
11167cb960a2Sdownsj 		alarm(0);
11177cb960a2Sdownsj 	}
11187cb960a2Sdownsj 	s->start = s->str = Xstring(s->xs, xp);
11197cb960a2Sdownsj 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
11207cb960a2Sdownsj 	/* Note: if input is all nulls, this is not eof */
11217cb960a2Sdownsj 	if (Xlength(s->xs, xp) == 0) { /* EOF */
11227cb960a2Sdownsj 		if (s->type == SFILE)
11237cb960a2Sdownsj 			shf_fdclose(s->u.shf);
11247cb960a2Sdownsj 		s->str = NULL;
11257cb960a2Sdownsj 	} else if (interactive) {
11267cb960a2Sdownsj #ifdef HISTORY
11277cb960a2Sdownsj 		char *p = Xstring(s->xs, xp);
11287cb960a2Sdownsj 		if (cur_prompt == PS1)
11297cb960a2Sdownsj 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
11307cb960a2Sdownsj 				p++;
11317cb960a2Sdownsj 		if (*p) {
11327cb960a2Sdownsj 			s->line++;
11337cb960a2Sdownsj 			histsave(s->line, s->str, 1);
11347cb960a2Sdownsj 		}
11357cb960a2Sdownsj #endif /* HISTORY */
11367cb960a2Sdownsj 	}
11377cb960a2Sdownsj 	if (interactive)
11384c69a2c1Skstailey 		set_prompt(PS2, (Source *) 0);
11397cb960a2Sdownsj }
11407cb960a2Sdownsj 
1141e7b1290aSotto static char *
1142e7b1290aSotto special_prompt_expand(char *str)
1143e7b1290aSotto {
1144e7b1290aSotto 	char *p = str;
1145e7b1290aSotto 
1146e7b1290aSotto 	while ((p = strstr(p, "\\$")) != NULL) {
11476aa3ef4bSdrahn 		*(p+1) = 'p';
1148e7b1290aSotto 	}
1149e7b1290aSotto 	return str;
1150e7b1290aSotto }
1151e7b1290aSotto 
11527cb960a2Sdownsj void
1153c5d5393cSotto set_prompt(int to, Source *s)
11547cb960a2Sdownsj {
11554300ac9bSmillert 	char *ps1;
11567cb960a2Sdownsj 	Area *saved_atemp;
1157fc804817Smillert 
115894e42df6Smillert 	cur_prompt = to;
115994e42df6Smillert 
116094e42df6Smillert 	switch (to) {
116194e42df6Smillert 	case PS1: /* command */
11624300ac9bSmillert 		ps1 = str_save(str_val(global("PS1")), ATEMP);
11634300ac9bSmillert 		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
11647cb960a2Sdownsj 		newenv(E_ERRH);
116569b9f96bSmillert 		if (sigsetjmp(e->jbuf, 0)) {
11667cb960a2Sdownsj 			prompt = safe_prompt;
1167dcacb757Sdownsj 			/* Don't print an error - assume it has already
1168dcacb757Sdownsj 			 * been printed.  Reason is we may have forked
1169dcacb757Sdownsj 			 * to run a command and the child may be
1170dcacb757Sdownsj 			 * unwinding its stack through this code as it
1171dcacb757Sdownsj 			 * exits.
1172dcacb757Sdownsj 			 */
1173e7b1290aSotto 		} else {
1174e7b1290aSotto 			/* expand \$ before other substitutions are done */
1175e7b1290aSotto 			char *tmp = special_prompt_expand(ps1);
1176e7b1290aSotto 			prompt = str_save(substitute(tmp, 0), saved_atemp);
1177e7b1290aSotto 		}
11780ee3f80eSotto 		quitenv(NULL);
11797cb960a2Sdownsj 		break;
11807cb960a2Sdownsj 	case PS2: /* command continuation */
11817cb960a2Sdownsj 		prompt = str_val(global("PS2"));
11827cb960a2Sdownsj 		break;
11837cb960a2Sdownsj 	}
11847cb960a2Sdownsj }
11857cb960a2Sdownsj 
1186eb6bc482Sderaadt static int
1187c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
11887cb960a2Sdownsj {
1189eb6bc482Sderaadt 	char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1190a1344090Sderaadt 	int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
119141c26999Sotto 	const char *cp = sp;
1192eb6bc482Sderaadt 	struct tm *tm;
1193eb6bc482Sderaadt 	time_t t;
1194eb6bc482Sderaadt 
1195eb6bc482Sderaadt 	if (*cp && cp[1] == '\r') {
1196eb6bc482Sderaadt 		delimiter = *cp;
1197eb6bc482Sderaadt 		cp += 2;
1198eb6bc482Sderaadt 	}
11997cb960a2Sdownsj 
12007cb960a2Sdownsj 	while (*cp != 0) {
1201a1344090Sderaadt 		delimitthis = 0;
1202eb6bc482Sderaadt 		if (indelimit && *cp != delimiter)
1203eb6bc482Sderaadt 			;
1204eb6bc482Sderaadt 		else if (*cp == '\n' || *cp == '\r') {
1205eb6bc482Sderaadt 			totlen = 0;
1206eb6bc482Sderaadt 			sp = cp + 1;
1207a1344090Sderaadt 		} else if (*cp == '\t') {
1208a1344090Sderaadt 			if (counting)
1209eb6bc482Sderaadt 				totlen = (totlen | 7) + 1;
1210a1344090Sderaadt 		} else if (*cp == delimiter) {
1211eb6bc482Sderaadt 			indelimit = !indelimit;
1212a1344090Sderaadt 			delimitthis = 1;
1213a1344090Sderaadt 		}
1214eb6bc482Sderaadt 
1215eb6bc482Sderaadt 		if (*cp == '\\') {
1216eb6bc482Sderaadt 			cp++;
1217eb6bc482Sderaadt 			if (!*cp)
1218eb6bc482Sderaadt 				break;
1219eb6bc482Sderaadt 			if (Flag(FSH))
1220eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1221eb6bc482Sderaadt 			else switch (*cp) {
1222eb6bc482Sderaadt 			case 'a':	/* '\' 'a' bell */
1223eb6bc482Sderaadt 				strbuf[0] = '\007';
1224eb6bc482Sderaadt 				strbuf[1] = '\0';
1225eb6bc482Sderaadt 				break;
1226eb6bc482Sderaadt 			case 'd':	/* '\' 'd' Dow Mon DD */
1227eb6bc482Sderaadt 				time(&t);
1228eb6bc482Sderaadt 				tm = localtime(&t);
1229eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%a %b %d", tm);
1230eb6bc482Sderaadt 				break;
1231eb6bc482Sderaadt 			case 'D': /* '\' 'D' '{' strftime format '}' */
1232eb6bc482Sderaadt 				p = strchr(cp + 2, '}');
1233eb6bc482Sderaadt 				if (cp[1] != '{' || p == NULL) {
1234eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1235eb6bc482Sderaadt 					    "\\%c", *cp);
1236eb6bc482Sderaadt 					break;
1237eb6bc482Sderaadt 				}
1238eb6bc482Sderaadt 				strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1239eb6bc482Sderaadt 				p = strchr(tmpbuf, '}');
1240eb6bc482Sderaadt 				if (p)
1241eb6bc482Sderaadt 					*p = '\0';
1242eb6bc482Sderaadt 				time(&t);
1243eb6bc482Sderaadt 				tm = localtime(&t);
1244eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, tmpbuf, tm);
1245eb6bc482Sderaadt 				cp = strchr(cp + 2, '}');
1246eb6bc482Sderaadt 				break;
1247eb6bc482Sderaadt 			case 'e':	/* '\' 'e' escape */
1248eb6bc482Sderaadt 				strbuf[0] = '\033';
1249eb6bc482Sderaadt 				strbuf[1] = '\0';
1250eb6bc482Sderaadt 				break;
1251eb6bc482Sderaadt 			case 'h':	/* '\' 'h' shortened hostname */
1252eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1253eb6bc482Sderaadt 				p = strchr(strbuf, '.');
1254eb6bc482Sderaadt 				if (p)
1255eb6bc482Sderaadt 					*p = '\0';
1256eb6bc482Sderaadt 				break;
1257eb6bc482Sderaadt 			case 'H':	/* '\' 'H' full hostname */
1258eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1259eb6bc482Sderaadt 				break;
1260eb6bc482Sderaadt 			case 'j':	/* '\' 'j' number of jobs */
1261eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1262eb6bc482Sderaadt 				    j_njobs());
1263eb6bc482Sderaadt 				break;
1264eb6bc482Sderaadt 			case 'l':	/* '\' 'l' basename of tty */
1265eb6bc482Sderaadt 				p = ttyname(0);
1266eb6bc482Sderaadt 				if (p)
1267eb6bc482Sderaadt 					p = basename(p);
1268eb6bc482Sderaadt 				if (p)
1269eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1270eb6bc482Sderaadt 				break;
1271eb6bc482Sderaadt 			case 'n':	/* '\' 'n' newline */
1272eb6bc482Sderaadt 				strbuf[0] = '\n';
1273eb6bc482Sderaadt 				strbuf[1] = '\0';
12745c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
12755c4a5744Sderaadt 				sp = cp + 1;
1276eb6bc482Sderaadt 				break;
12776aa3ef4bSdrahn 			case 'p':	/* '\' '$' $ or # */
12786aa3ef4bSdrahn 				strbuf[0] = ksheuid ? '$' : '#';
12796aa3ef4bSdrahn 				strbuf[1] = '\0';
12806aa3ef4bSdrahn 				break;
1281eb6bc482Sderaadt 			case 'r':	/* '\' 'r' return */
1282eb6bc482Sderaadt 				strbuf[0] = '\r';
1283eb6bc482Sderaadt 				strbuf[1] = '\0';
12845c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
12855c4a5744Sderaadt 				sp = cp + 1;
1286eb6bc482Sderaadt 				break;
1287eb6bc482Sderaadt 			case 's':	/* '\' 's' basename $0 */
1288eb6bc482Sderaadt 				strlcpy(strbuf, kshname, sizeof strbuf);
1289eb6bc482Sderaadt 				break;
1290eb6bc482Sderaadt 			case 't':	/* '\' 't' 24 hour HH:MM:SS */
1291eb6bc482Sderaadt 				time(&t);
1292eb6bc482Sderaadt 				tm = localtime(&t);
1293eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%T", tm);
1294eb6bc482Sderaadt 				break;
1295eb6bc482Sderaadt 			case 'T':	/* '\' 'T' 12 hour HH:MM:SS */
1296eb6bc482Sderaadt 				time(&t);
1297eb6bc482Sderaadt 				tm = localtime(&t);
1298eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm);
1299eb6bc482Sderaadt 				break;
1300eb6bc482Sderaadt 			case '@':	/* '\' '@' 12 hour am/pm format */
1301eb6bc482Sderaadt 				time(&t);
1302eb6bc482Sderaadt 				tm = localtime(&t);
1303eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%r", tm);
1304eb6bc482Sderaadt 				break;
1305eb6bc482Sderaadt 			case 'A':	/* '\' 'A' 24 hour HH:MM */
1306eb6bc482Sderaadt 				time(&t);
1307eb6bc482Sderaadt 				tm = localtime(&t);
1308eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%R", tm);
1309eb6bc482Sderaadt 				break;
1310eb6bc482Sderaadt 			case 'u':	/* '\' 'u' username */
1311f351fb1cSotto 				strlcpy(strbuf, username, sizeof strbuf);
1312eb6bc482Sderaadt 				break;
1313eb6bc482Sderaadt 			case 'v':	/* '\' 'v' version (short) */
1314eb6bc482Sderaadt 				p = strchr(ksh_version, ' ');
1315eb6bc482Sderaadt 				if (p)
1316eb6bc482Sderaadt 					p = strchr(p + 1, ' ');
1317eb6bc482Sderaadt 				if (p) {
1318eb6bc482Sderaadt 					p++;
1319eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1320eb6bc482Sderaadt 					p = strchr(strbuf, ' ');
1321eb6bc482Sderaadt 					if (p)
1322eb6bc482Sderaadt 						*p = '\0';
1323eb6bc482Sderaadt 				}
1324eb6bc482Sderaadt 				break;
1325eb6bc482Sderaadt 			case 'V':	/* '\' 'V' version (long) */
1326eb6bc482Sderaadt 				strlcpy(strbuf, ksh_version, sizeof strbuf);
1327eb6bc482Sderaadt 				break;
1328eb6bc482Sderaadt 			case 'w':	/* '\' 'w' cwd */
1329eb6bc482Sderaadt 				p = str_val(global("PWD"));
1330ad55f791Sderaadt 				n = strlen(str_val(global("HOME")));
1331ad55f791Sderaadt 				if (strcmp(p, "/") == 0) {
1332ad55f791Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1333ad55f791Sderaadt 				} else if (strcmp(p, str_val(global("HOME"))) == 0) {
1334eb6bc482Sderaadt 					strbuf[0] = '~';
1335eb6bc482Sderaadt 					strbuf[1] = '\0';
1336ad55f791Sderaadt 				} else if (strncmp(p, str_val(global("HOME")), n)
1337ad55f791Sderaadt 				    == 0 && p[n] == '/') {
1338ad55f791Sderaadt 					snprintf(strbuf, sizeof strbuf, "~/%s",
1339ad55f791Sderaadt 					    str_val(global("PWD")) + n + 1);
1340eb6bc482Sderaadt 				} else
1341eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1342eb6bc482Sderaadt 				break;
1343eb6bc482Sderaadt 			case 'W':	/* '\' 'W' basename(cwd) */
1344eb6bc482Sderaadt 				p = str_val(global("PWD"));
134530e9b9efSokan 				if (strcmp(p, str_val(global("HOME"))) == 0) {
134630e9b9efSokan 					strbuf[0] = '~';
134730e9b9efSokan 					strbuf[1] = '\0';
134830e9b9efSokan 				} else
1349eb6bc482Sderaadt 					strlcpy(strbuf, basename(p), sizeof strbuf);
1350eb6bc482Sderaadt 				break;
1351e7b1290aSotto 			case '!':	/* '\' '!' history line number */
1352eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1353eb6bc482Sderaadt 				    source->line + 1);
1354eb6bc482Sderaadt 				break;
1355e7b1290aSotto 			case '#':	/* '\' '#' command line number */
1356eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1357e7b1290aSotto 				    source->line - source->cmd_offset + 1);
1358eb6bc482Sderaadt 				break;
1359eb6bc482Sderaadt 			case '0':	/* '\' '#' '#' ' #' octal numeric handling */
1360eb6bc482Sderaadt 			case '1':
1361eb6bc482Sderaadt 			case '2':
1362eb6bc482Sderaadt 			case '3':
1363eb6bc482Sderaadt 			case '4':
1364eb6bc482Sderaadt 			case '5':
1365eb6bc482Sderaadt 			case '6':
1366eb6bc482Sderaadt 			case '7':
1367eb6bc482Sderaadt 				if ((cp[1] > '7' || cp[1] < '0') ||
1368eb6bc482Sderaadt 				    (cp[2] > '7' || cp[2] < '0')) {
1369eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1370eb6bc482Sderaadt 					    "\\%c", *cp);
1371eb6bc482Sderaadt 					break;
1372eb6bc482Sderaadt 				}
1373064356ecSzhuk 				n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 +
1374064356ecSzhuk 				    (cp[2] - '0');
1375eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%c", n);
1376eb6bc482Sderaadt 				cp += 2;
1377eb6bc482Sderaadt 				break;
1378eb6bc482Sderaadt 			case '\\':	/* '\' '\' */
1379eb6bc482Sderaadt 				strbuf[0] = '\\';
1380eb6bc482Sderaadt 				strbuf[1] = '\0';
1381eb6bc482Sderaadt 				break;
1382a1344090Sderaadt 			case '[': /* '\' '[' .... stop counting */
1383a1344090Sderaadt 				strbuf[0] = '\0';
1384a1344090Sderaadt 				counting = 0;
1385eb6bc482Sderaadt 				break;
1386a1344090Sderaadt 			case ']': /* '\' ']' restart counting */
1387a1344090Sderaadt 				strbuf[0] = '\0';
1388a1344090Sderaadt 				counting = 1;
1389eb6bc482Sderaadt 				break;
1390eb6bc482Sderaadt 
1391eb6bc482Sderaadt 			default:
1392eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1393eb6bc482Sderaadt 				break;
1394eb6bc482Sderaadt 			}
1395eb6bc482Sderaadt 			cp++;
1396eb6bc482Sderaadt 
1397eb6bc482Sderaadt 			str = strbuf;
1398eb6bc482Sderaadt 			len = strlen(str);
1399eb6bc482Sderaadt 			if (ntruncate) {
1400eb6bc482Sderaadt 				if (ntruncate >= len) {
1401eb6bc482Sderaadt 					ntruncate -= len;
1402eb6bc482Sderaadt 					continue;
1403eb6bc482Sderaadt 				}
1404eb6bc482Sderaadt 				str += ntruncate;
1405eb6bc482Sderaadt 				len -= ntruncate;
1406eb6bc482Sderaadt 				ntruncate = 0;
1407eb6bc482Sderaadt 			}
1408eb6bc482Sderaadt 			if (doprint)
1409eb6bc482Sderaadt 				shf_write(str, len, shl_out);
1410a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1411eb6bc482Sderaadt 				totlen += len;
1412eb6bc482Sderaadt 			continue;
1413eb6bc482Sderaadt 		} else if (*cp != '!')
14147cb960a2Sdownsj 			c = *cp++;
14157cb960a2Sdownsj 		else if (*++cp == '!')
14167cb960a2Sdownsj 			c = *cp++;
14177cb960a2Sdownsj 		else {
14187cb960a2Sdownsj 			char *p;
14197cb960a2Sdownsj 
14207cb960a2Sdownsj 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
14217cb960a2Sdownsj 			    source->line + 1);
14227cb960a2Sdownsj 			len = strlen(nbuf);
14237cb960a2Sdownsj 			if (ntruncate) {
14247cb960a2Sdownsj 				if (ntruncate >= len) {
14257cb960a2Sdownsj 					ntruncate -= len;
14267cb960a2Sdownsj 					continue;
14277cb960a2Sdownsj 				}
14287cb960a2Sdownsj 				p += ntruncate;
14297cb960a2Sdownsj 				len -= ntruncate;
14307cb960a2Sdownsj 				ntruncate = 0;
14317cb960a2Sdownsj 			}
1432eb6bc482Sderaadt 			if (doprint)
14337cb960a2Sdownsj 				shf_write(p, len, shl_out);
1434a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1435eb6bc482Sderaadt 				totlen += len;
14367cb960a2Sdownsj 			continue;
14377cb960a2Sdownsj 		}
1438638b9e76Sbeck 		if (counting && ntruncate)
14397cb960a2Sdownsj 			--ntruncate;
1440eb6bc482Sderaadt 		else if (doprint) {
14417cb960a2Sdownsj 			shf_putc(c, shl_out);
14427cb960a2Sdownsj 		}
1443a1344090Sderaadt 		if (counting && !indelimit && !delimitthis)
1444eb6bc482Sderaadt 			totlen++;
1445eb6bc482Sderaadt 	}
1446eb6bc482Sderaadt 	if (doprint)
14477cb960a2Sdownsj 		shf_flush(shl_out);
1448eb6bc482Sderaadt 	if (spp)
1449eb6bc482Sderaadt 		*spp = sp;
1450eb6bc482Sderaadt 	return (totlen);
1451eb6bc482Sderaadt }
1452eb6bc482Sderaadt 
1453eb6bc482Sderaadt void
1454c5d5393cSotto pprompt(const char *cp, int ntruncate)
1455eb6bc482Sderaadt {
1456eb6bc482Sderaadt 	dopprompt(cp, ntruncate, NULL, 1);
1457eb6bc482Sderaadt }
1458eb6bc482Sderaadt 
1459eb6bc482Sderaadt int
1460c5d5393cSotto promptlen(const char *cp, const char **spp)
1461eb6bc482Sderaadt {
1462eb6bc482Sderaadt 	return dopprompt(cp, 0, spp, 0);
14637cb960a2Sdownsj }
14647cb960a2Sdownsj 
14657cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including
14667cb960a2Sdownsj  * the :[-+?=#%] or close-brace.
14677cb960a2Sdownsj  */
14687cb960a2Sdownsj static char *
1469c5d5393cSotto get_brace_var(XString *wsp, char *wp)
14707cb960a2Sdownsj {
14717cb960a2Sdownsj 	enum parse_state {
14727cb960a2Sdownsj 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
14737cb960a2Sdownsj 			   PS_NUMBER, PS_VAR1, PS_END
14747cb960a2Sdownsj 			 }
14757cb960a2Sdownsj 		state;
14767cb960a2Sdownsj 	char c;
14777cb960a2Sdownsj 
14787cb960a2Sdownsj 	state = PS_INITIAL;
14797cb960a2Sdownsj 	while (1) {
1480e55c1b2cSdownsj 		c = getsc();
14817cb960a2Sdownsj 		/* State machine to figure out where the variable part ends. */
14827cb960a2Sdownsj 		switch (state) {
14837cb960a2Sdownsj 		case PS_INITIAL:
14847cb960a2Sdownsj 			if (c == '#') {
14857cb960a2Sdownsj 				state = PS_SAW_HASH;
14867cb960a2Sdownsj 				break;
14877cb960a2Sdownsj 			}
14887eab881bSjaredy 			/* FALLTHROUGH */
14897cb960a2Sdownsj 		case PS_SAW_HASH:
14907cb960a2Sdownsj 			if (letter(c))
14917cb960a2Sdownsj 				state = PS_IDENT;
14927cb960a2Sdownsj 			else if (digit(c))
14937cb960a2Sdownsj 				state = PS_NUMBER;
14947cb960a2Sdownsj 			else if (ctype(c, C_VAR1))
14957cb960a2Sdownsj 				state = PS_VAR1;
14967cb960a2Sdownsj 			else
14977cb960a2Sdownsj 				state = PS_END;
14987cb960a2Sdownsj 			break;
14997cb960a2Sdownsj 		case PS_IDENT:
15007cb960a2Sdownsj 			if (!letnum(c)) {
15017cb960a2Sdownsj 				state = PS_END;
15027cb960a2Sdownsj 				if (c == '[') {
15037cb960a2Sdownsj 					char *tmp, *p;
15047cb960a2Sdownsj 
15057cb960a2Sdownsj 					if (!arraysub(&tmp))
15067cb960a2Sdownsj 						yyerror("missing ]\n");
15077cb960a2Sdownsj 					*wp++ = c;
15087cb960a2Sdownsj 					for (p = tmp; *p; ) {
15097cb960a2Sdownsj 						Xcheck(*wsp, wp);
15107cb960a2Sdownsj 						*wp++ = *p++;
15117cb960a2Sdownsj 					}
15127cb960a2Sdownsj 					afree(tmp, ATEMP);
1513e55c1b2cSdownsj 					c = getsc(); /* the ] */
15147cb960a2Sdownsj 				}
15157cb960a2Sdownsj 			}
15167cb960a2Sdownsj 			break;
15177cb960a2Sdownsj 		case PS_NUMBER:
15187cb960a2Sdownsj 			if (!digit(c))
15197cb960a2Sdownsj 				state = PS_END;
15207cb960a2Sdownsj 			break;
15217cb960a2Sdownsj 		case PS_VAR1:
15227cb960a2Sdownsj 			state = PS_END;
15237cb960a2Sdownsj 			break;
15247cb960a2Sdownsj 		case PS_END: /* keep gcc happy */
15257cb960a2Sdownsj 			break;
15267cb960a2Sdownsj 		}
15277cb960a2Sdownsj 		if (state == PS_END) {
15287cb960a2Sdownsj 			*wp++ = '\0';	/* end of variable part */
15297cb960a2Sdownsj 			ungetsc(c);
15307cb960a2Sdownsj 			break;
15317cb960a2Sdownsj 		}
15327cb960a2Sdownsj 		Xcheck(*wsp, wp);
15337cb960a2Sdownsj 		*wp++ = c;
15347cb960a2Sdownsj 	}
15357cb960a2Sdownsj 	return wp;
15367cb960a2Sdownsj }
15377cb960a2Sdownsj 
15387cb960a2Sdownsj /*
15397cb960a2Sdownsj  * Save an array subscript - returns true if matching bracket found, false
15407cb960a2Sdownsj  * if eof or newline was found.
15417cb960a2Sdownsj  * (Returned string double null terminated)
15427cb960a2Sdownsj  */
15437cb960a2Sdownsj static int
1544c5d5393cSotto arraysub(char **strp)
15457cb960a2Sdownsj {
15467cb960a2Sdownsj 	XString ws;
15477cb960a2Sdownsj 	char	*wp;
15487cb960a2Sdownsj 	char	c;
15497cb960a2Sdownsj 	int	depth = 1;	/* we are just past the initial [ */
15507cb960a2Sdownsj 
15517cb960a2Sdownsj 	Xinit(ws, wp, 32, ATEMP);
15527cb960a2Sdownsj 
15537cb960a2Sdownsj 	do {
1554e55c1b2cSdownsj 		c = getsc();
15557cb960a2Sdownsj 		Xcheck(ws, wp);
15567cb960a2Sdownsj 		*wp++ = c;
15577cb960a2Sdownsj 		if (c == '[')
15587cb960a2Sdownsj 			depth++;
15597cb960a2Sdownsj 		else if (c == ']')
15607cb960a2Sdownsj 			depth--;
15617cb960a2Sdownsj 	} while (depth > 0 && c && c != '\n');
15627cb960a2Sdownsj 
15637cb960a2Sdownsj 	*wp++ = '\0';
15647cb960a2Sdownsj 	*strp = Xclose(ws, wp);
15657cb960a2Sdownsj 
15667cb960a2Sdownsj 	return depth == 0 ? 1 : 0;
15677cb960a2Sdownsj }
15687cb960a2Sdownsj 
15697cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */
15707cb960a2Sdownsj static const char *
1571c5d5393cSotto ungetsc(int c)
15727cb960a2Sdownsj {
1573e55c1b2cSdownsj 	if (backslash_skip)
1574e55c1b2cSdownsj 		backslash_skip--;
15757cb960a2Sdownsj 	/* Don't unget eof... */
15767cb960a2Sdownsj 	if (source->str == null && c == '\0')
15777cb960a2Sdownsj 		return source->str;
15787cb960a2Sdownsj 	if (source->str > source->start)
15797cb960a2Sdownsj 		source->str--;
15807cb960a2Sdownsj 	else {
15817cb960a2Sdownsj 		Source *s;
15827cb960a2Sdownsj 
15837cb960a2Sdownsj 		s = pushs(SREREAD, source->areap);
1584e55c1b2cSdownsj 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1585e55c1b2cSdownsj 		s->start = s->str = s->ugbuf;
15867cb960a2Sdownsj 		s->next = source;
15877cb960a2Sdownsj 		source = s;
15887cb960a2Sdownsj 	}
15897cb960a2Sdownsj 	return source->str;
15907cb960a2Sdownsj }
15917cb960a2Sdownsj 
1592e55c1b2cSdownsj 
15937cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */
15947cb960a2Sdownsj static int
159569b9f96bSmillert getsc_bn(void)
15967cb960a2Sdownsj {
1597e55c1b2cSdownsj 	int c, c2;
1598e55c1b2cSdownsj 
1599e55c1b2cSdownsj 	if (ignore_backslash_newline)
1600e55c1b2cSdownsj 		return getsc_();
1601e55c1b2cSdownsj 
1602e55c1b2cSdownsj 	if (backslash_skip == 1) {
1603e55c1b2cSdownsj 		backslash_skip = 2;
1604e55c1b2cSdownsj 		return getsc_();
1605e55c1b2cSdownsj 	}
1606e55c1b2cSdownsj 
1607e55c1b2cSdownsj 	backslash_skip = 0;
16087cb960a2Sdownsj 
16097cb960a2Sdownsj 	while (1) {
16107cb960a2Sdownsj 		c = getsc_();
1611e55c1b2cSdownsj 		if (c == '\\') {
1612e55c1b2cSdownsj 			if ((c2 = getsc_()) == '\n')
16137cb960a2Sdownsj 				/* ignore the \newline; get the next char... */
1614e55c1b2cSdownsj 				continue;
1615e55c1b2cSdownsj 			ungetsc(c2);
1616e55c1b2cSdownsj 			backslash_skip = 1;
1617e55c1b2cSdownsj 		}
1618e55c1b2cSdownsj 		return c;
16197cb960a2Sdownsj 	}
16207cb960a2Sdownsj }
16213b015934Smillert 
16223b015934Smillert static Lex_state *
1623c5d5393cSotto push_state_(State_info *si, Lex_state *old_end)
16243b015934Smillert {
16253b015934Smillert 	Lex_state	*new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP);
16263b015934Smillert 
16273b015934Smillert 	new[0].ls_info.base = old_end;
16283b015934Smillert 	si->base = &new[0];
16293b015934Smillert 	si->end = &new[STATE_BSIZE];
16303b015934Smillert 	return &new[1];
16313b015934Smillert }
16323b015934Smillert 
16333b015934Smillert static Lex_state *
1634c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end)
16353b015934Smillert {
16363b015934Smillert 	Lex_state *old_base = si->base;
16373b015934Smillert 
16383b015934Smillert 	si->base = old_end->ls_info.base - STATE_BSIZE;
16393b015934Smillert 	si->end = old_end->ls_info.base;
16403b015934Smillert 
16413b015934Smillert 	afree(old_base, ATEMP);
16423b015934Smillert 
16437b425235Smillert 	return si->base + STATE_BSIZE - 1;
16443b015934Smillert }
1645