xref: /openbsd/bin/ksh/lex.c (revision 1c2ba45a)
1*1c2ba45aStedu /*	$OpenBSD: lex.c,v 1.56 2015/09/30 14:33:41 tedu 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;
166*1c2ba45aStedu 				int get, i;
167*1c2ba45aStedu 				char match[200], *str = match;
168*1c2ba45aStedu 				size_t mlen;
169f740f764Sderaadt 
170f740f764Sderaadt 				c2 = getsc();
1710b1768a7Sotto 				if (c2 == '\0' || c2 == ' ' || c2 == '\t')
172f740f764Sderaadt 					;
173f740f764Sderaadt 				else if (c2 == '!')
174f740f764Sderaadt 					replace = hist_get_newest(0);
175f740f764Sderaadt 				else if (isdigit(c2) || c2 == '-' ||
176f740f764Sderaadt 				    isalpha(c2)) {
177*1c2ba45aStedu 					get = !isalpha(c2);
178f740f764Sderaadt 
179f740f764Sderaadt 					*str++ = c2;
180f740f764Sderaadt 					do {
181f740f764Sderaadt 						if ((c2 = getsc()) == '\0')
182f740f764Sderaadt 							break;
183f740f764Sderaadt 						if (c2 == '\t' || c2 == ' ' ||
184f740f764Sderaadt 						    c2 == '\n') {
185f740f764Sderaadt 							ungetsc(c2);
186f740f764Sderaadt 							break;
187f740f764Sderaadt 						}
188f740f764Sderaadt 						*str++ = c2;
189f740f764Sderaadt 					} while (str < &match[sizeof(match)-1]);
190f740f764Sderaadt 					*str = '\0';
191f740f764Sderaadt 
192f740f764Sderaadt 					if (get) {
193f740f764Sderaadt 						int h = findhistrel(match);
194f740f764Sderaadt 						if (h >= 0)
195f740f764Sderaadt 							replace = &history[h];
196f740f764Sderaadt 					} else {
1970e7d3a01Smillert 						int h = findhist(-1, 0, match, true);
198f740f764Sderaadt 						if (h >= 0)
199f740f764Sderaadt 							replace = &history[h];
200f740f764Sderaadt 					}
201f740f764Sderaadt 				}
202f740f764Sderaadt 
203f740f764Sderaadt 				/*
204f740f764Sderaadt 				 * XXX ksh history buffer saves un-expanded
205f740f764Sderaadt 				 * commands. Until the history buffer code is
206f740f764Sderaadt 				 * changed to contain expanded commands, we
207f740f764Sderaadt 				 * ignore the bad commands (spinning sucks)
208f740f764Sderaadt 				 */
209f740f764Sderaadt 				if (replace && **replace == '!')
210f740f764Sderaadt 					ungetsc(c2);
211f740f764Sderaadt 				else if (replace) {
212f740f764Sderaadt 					Source *s;
213f740f764Sderaadt 
214f740f764Sderaadt 					/* do not strdup replacement via alloc */
2157a8124d8Sderaadt 					s = pushs(SREREAD, source->areap);
216f740f764Sderaadt 					s->start = s->str = *replace;
217f740f764Sderaadt 					s->next = source;
2187d849734Smillert 					s->u.freeme = NULL;
219f740f764Sderaadt 					source = s;
220f740f764Sderaadt 					continue;
221*1c2ba45aStedu 				} else {
222*1c2ba45aStedu 					/* restore what followed the '!' */
223*1c2ba45aStedu 					mlen = strlen(match);
224*1c2ba45aStedu 					for (i = mlen-1; i >= 0; i--)
225*1c2ba45aStedu 						ungetsc(match[i]);
226*1c2ba45aStedu 				}
227f740f764Sderaadt 			}
2287cb960a2Sdownsj 			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
2297cb960a2Sdownsj 				*wp = EOS; /* temporary */
2307a8124d8Sderaadt 				if (is_wdvarname(Xstring(ws, wp), false)) {
2317cb960a2Sdownsj 					char *p, *tmp;
2327cb960a2Sdownsj 
2337cb960a2Sdownsj 					if (arraysub(&tmp)) {
2347cb960a2Sdownsj 						*wp++ = CHAR;
2357cb960a2Sdownsj 						*wp++ = c;
2367cb960a2Sdownsj 						for (p = tmp; *p; ) {
2377cb960a2Sdownsj 							Xcheck(ws, wp);
2387cb960a2Sdownsj 							*wp++ = CHAR;
2397cb960a2Sdownsj 							*wp++ = *p++;
2407cb960a2Sdownsj 						}
2417cb960a2Sdownsj 						afree(tmp, ATEMP);
2427cb960a2Sdownsj 						break;
2437cb960a2Sdownsj 					} else {
2447cb960a2Sdownsj 						Source *s;
2457cb960a2Sdownsj 
2467cb960a2Sdownsj 						s = pushs(SREREAD,
2477cb960a2Sdownsj 							  source->areap);
2487cb960a2Sdownsj 						s->start = s->str
2497cb960a2Sdownsj 							= s->u.freeme = tmp;
2507cb960a2Sdownsj 						s->next = source;
2517cb960a2Sdownsj 						source = s;
2527cb960a2Sdownsj 					}
2537cb960a2Sdownsj 				}
2547cb960a2Sdownsj 				*wp++ = CHAR;
2557cb960a2Sdownsj 				*wp++ = c;
2567cb960a2Sdownsj 				break;
2577cb960a2Sdownsj 			}
2587eab881bSjaredy 			/* FALLTHROUGH */
2597cb960a2Sdownsj 		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */
2607a8124d8Sderaadt 			if (c == '*' || c == '@' || c == '+' || c == '?' ||
2617a8124d8Sderaadt 			    c == '!') {
2627cb960a2Sdownsj 				c2 = getsc();
2637cb960a2Sdownsj 				if (c2 == '(' /*)*/ ) {
2647cb960a2Sdownsj 					*wp++ = OPAT;
2657cb960a2Sdownsj 					*wp++ = c;
2663b015934Smillert 					PUSH_STATE(SPATTERN);
2677cb960a2Sdownsj 					break;
2687cb960a2Sdownsj 				}
2697cb960a2Sdownsj 				ungetsc(c2);
2707cb960a2Sdownsj 			}
2717eab881bSjaredy 			/* FALLTHROUGH */
2727cb960a2Sdownsj 		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */
2737cb960a2Sdownsj 			switch (c) {
2747cb960a2Sdownsj 			case '\\':
2757cb960a2Sdownsj 				c = getsc();
276e55c1b2cSdownsj 				if (c) /* trailing \ is lost */
2777cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
2787cb960a2Sdownsj 				break;
2797cb960a2Sdownsj 			case '\'':
28022a9cf94Sguenther 				if ((cf & HEREDOC) || state == SBRACEQ) {
28122a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
28222a9cf94Sguenther 					break;
28322a9cf94Sguenther 				}
2847cb960a2Sdownsj 				*wp++ = OQUOTE;
285e55c1b2cSdownsj 				ignore_backslash_newline++;
2863b015934Smillert 				PUSH_STATE(SSQUOTE);
2877cb960a2Sdownsj 				break;
2887cb960a2Sdownsj 			case '"':
2897cb960a2Sdownsj 				*wp++ = OQUOTE;
2903b015934Smillert 				PUSH_STATE(SDQUOTE);
2917cb960a2Sdownsj 				break;
2927cb960a2Sdownsj 			default:
2937cb960a2Sdownsj 				goto Subst;
2947cb960a2Sdownsj 			}
2957cb960a2Sdownsj 			break;
2967cb960a2Sdownsj 
2977cb960a2Sdownsj 		  Subst:
2987cb960a2Sdownsj 			switch (c) {
2997cb960a2Sdownsj 			case '\\':
3007cb960a2Sdownsj 				c = getsc();
3017cb960a2Sdownsj 				switch (c) {
30257f623feSotto 				case '\\':
3037cb960a2Sdownsj 				case '$': case '`':
3047cb960a2Sdownsj 					*wp++ = QCHAR, *wp++ = c;
3057cb960a2Sdownsj 					break;
30657f623feSotto 				case '"':
30757f623feSotto 					if ((cf & HEREDOC) == 0) {
30857f623feSotto 						*wp++ = QCHAR, *wp++ = c;
30957f623feSotto 						break;
31057f623feSotto 					}
31125d8d262Smoritz 					/* FALLTHROUGH */
3127cb960a2Sdownsj 				default:
313d3ecfddbSstsp 					if (cf & UNESCAPE) {
314d3ecfddbSstsp 						*wp++ = QCHAR, *wp++ = c;
315d3ecfddbSstsp 						break;
316d3ecfddbSstsp 					}
3177cb960a2Sdownsj 					Xcheck(ws, wp);
318e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
3197cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = '\\';
3207cb960a2Sdownsj 						*wp++ = CHAR, *wp++ = c;
321e55c1b2cSdownsj 					}
3227cb960a2Sdownsj 					break;
3237cb960a2Sdownsj 				}
3247cb960a2Sdownsj 				break;
3257cb960a2Sdownsj 			case '$':
3267cb960a2Sdownsj 				c = getsc();
3277cb960a2Sdownsj 				if (c == '(') /*)*/ {
3287cb960a2Sdownsj 					c = getsc();
3297cb960a2Sdownsj 					if (c == '(') /*)*/ {
3303b015934Smillert 						PUSH_STATE(SASPAREN);
3313b015934Smillert 						statep->ls_sasparen.nparen = 2;
3323b015934Smillert 						statep->ls_sasparen.start =
3333b015934Smillert 						    Xsavepos(ws, wp);
3347cb960a2Sdownsj 						*wp++ = EXPRSUB;
3357cb960a2Sdownsj 					} else {
3367cb960a2Sdownsj 						ungetsc(c);
3373b015934Smillert 						PUSH_STATE(SCSPAREN);
3383b015934Smillert 						statep->ls_scsparen.nparen = 1;
3393b015934Smillert 						statep->ls_scsparen.csstate = 0;
3407cb960a2Sdownsj 						*wp++ = COMSUB;
3417cb960a2Sdownsj 					}
3427cb960a2Sdownsj 				} else if (c == '{') /*}*/ {
3437cb960a2Sdownsj 					*wp++ = OSUBST;
3443b015934Smillert 					*wp++ = '{'; /*}*/
3457cb960a2Sdownsj 					wp = get_brace_var(&ws, wp);
346e55c1b2cSdownsj 					c = getsc();
3473b015934Smillert 					/* allow :# and :% (ksh88 compat) */
3483b015934Smillert 					if (c == ':') {
3493b015934Smillert 						*wp++ = CHAR, *wp++ = c;
3503b015934Smillert 						c = getsc();
3513b015934Smillert 					}
3523b015934Smillert 					/* If this is a trim operation,
3533b015934Smillert 					 * treat (,|,) specially in STBRACE.
3543b015934Smillert 					 */
3557cb960a2Sdownsj 					if (c == '#' || c == '%') {
3563b015934Smillert 						ungetsc(c);
3573b015934Smillert 						PUSH_STATE(STBRACE);
3587cb960a2Sdownsj 					} else {
3597cb960a2Sdownsj 						ungetsc(c);
36022a9cf94Sguenther 						if (state == SDQUOTE ||
36122a9cf94Sguenther 						    state == SBRACEQ)
36222a9cf94Sguenther 							PUSH_STATE(SBRACEQ);
36322a9cf94Sguenther 						else
3643b015934Smillert 							PUSH_STATE(SBRACE);
3657cb960a2Sdownsj 					}
3667cb960a2Sdownsj 				} else if (ctype(c, C_ALPHA)) {
3677cb960a2Sdownsj 					*wp++ = OSUBST;
3683b015934Smillert 					*wp++ = 'X';
3697cb960a2Sdownsj 					do {
3707cb960a2Sdownsj 						Xcheck(ws, wp);
3717cb960a2Sdownsj 						*wp++ = c;
3727cb960a2Sdownsj 						c = getsc();
3737cb960a2Sdownsj 					} while (ctype(c, C_ALPHA|C_DIGIT));
3747cb960a2Sdownsj 					*wp++ = '\0';
3757cb960a2Sdownsj 					*wp++ = CSUBST;
3763b015934Smillert 					*wp++ = 'X';
3777cb960a2Sdownsj 					ungetsc(c);
3787cb960a2Sdownsj 				} else if (ctype(c, C_DIGIT|C_VAR1)) {
3797cb960a2Sdownsj 					Xcheck(ws, wp);
3807cb960a2Sdownsj 					*wp++ = OSUBST;
3813b015934Smillert 					*wp++ = 'X';
3827cb960a2Sdownsj 					*wp++ = c;
3837cb960a2Sdownsj 					*wp++ = '\0';
3847cb960a2Sdownsj 					*wp++ = CSUBST;
3853b015934Smillert 					*wp++ = 'X';
3867cb960a2Sdownsj 				} else {
3877cb960a2Sdownsj 					*wp++ = CHAR, *wp++ = '$';
3887cb960a2Sdownsj 					ungetsc(c);
3897cb960a2Sdownsj 				}
3907cb960a2Sdownsj 				break;
3917cb960a2Sdownsj 			case '`':
3923b015934Smillert 				PUSH_STATE(SBQUOTE);
3937cb960a2Sdownsj 				*wp++ = COMSUB;
3947cb960a2Sdownsj 				/* Need to know if we are inside double quotes
3957cb960a2Sdownsj 				 * since sh/at&t-ksh translate the \" to " in
3967cb960a2Sdownsj 				 * "`..\"..`".
3973b015934Smillert 				 * This is not done in posix mode (section
3983b015934Smillert 				 * 3.2.3, Double Quotes: "The backquote shall
3993b015934Smillert 				 * retain its special meaning introducing the
4003b015934Smillert 				 * other form of command substitution (see
4013b015934Smillert 				 * 3.6.3). The portion of the quoted string
4023b015934Smillert 				 * from the initial backquote and the
4033b015934Smillert 				 * characters up to the next backquote that
4043b015934Smillert 				 * is not preceded by a backslash (having
4053b015934Smillert 				 * escape characters removed) defines that
4063b015934Smillert 				 * command whose output replaces `...` when
4073b015934Smillert 				 * the word is expanded."
4083b015934Smillert 				 * Section 3.6.3, Command Substitution:
4093b015934Smillert 				 * "Within the backquoted style of command
4103b015934Smillert 				 * substitution, backslash shall retain its
4113b015934Smillert 				 * literal meaning, except when followed by
4123b015934Smillert 				 * $ ` \.").
4137cb960a2Sdownsj 				 */
4143b015934Smillert 				statep->ls_sbquote.indquotes = 0;
4153b015934Smillert 				if (!Flag(FPOSIX)) {
4163b015934Smillert 					Lex_state *s = statep;
4173b015934Smillert 					Lex_state *base = state_info.base;
4183b015934Smillert 					while (1) {
4193b015934Smillert 						for (; s != base; s--) {
4203b015934Smillert 							if (s->ls_state == SDQUOTE) {
4213b015934Smillert 								statep->ls_sbquote.indquotes = 1;
4223b015934Smillert 								break;
4233b015934Smillert 							}
4243b015934Smillert 						}
4253b015934Smillert 						if (s != base)
4263b015934Smillert 							break;
4273b015934Smillert 						if (!(s = s->ls_info.base))
4283b015934Smillert 							break;
4293b015934Smillert 						base = s-- - STATE_BSIZE;
4303b015934Smillert 					}
4313b015934Smillert 				}
4327cb960a2Sdownsj 				break;
4337cb960a2Sdownsj 			default:
4347cb960a2Sdownsj 				*wp++ = CHAR, *wp++ = c;
4357cb960a2Sdownsj 			}
4367cb960a2Sdownsj 			break;
4377cb960a2Sdownsj 
4387cb960a2Sdownsj 		case SSQUOTE:
4397cb960a2Sdownsj 			if (c == '\'') {
4403b015934Smillert 				POP_STATE();
44122a9cf94Sguenther 				if (state == SBRACEQ) {
44222a9cf94Sguenther 					*wp++ = CHAR, *wp++ = c;
44322a9cf94Sguenther 					break;
44422a9cf94Sguenther 				}
4457cb960a2Sdownsj 				*wp++ = CQUOTE;
446e55c1b2cSdownsj 				ignore_backslash_newline--;
4477cb960a2Sdownsj 			} else
4487cb960a2Sdownsj 				*wp++ = QCHAR, *wp++ = c;
4497cb960a2Sdownsj 			break;
4507cb960a2Sdownsj 
4517cb960a2Sdownsj 		case SDQUOTE:
4527cb960a2Sdownsj 			if (c == '"') {
4533b015934Smillert 				POP_STATE();
4547cb960a2Sdownsj 				*wp++ = CQUOTE;
4557cb960a2Sdownsj 			} else
4567cb960a2Sdownsj 				goto Subst;
4577cb960a2Sdownsj 			break;
4587cb960a2Sdownsj 
4593b015934Smillert 		case SCSPAREN: /* $( .. ) */
4607cb960a2Sdownsj 			/* todo: deal with $(...) quoting properly
4617cb960a2Sdownsj 			 * kludge to partly fake quoting inside $(..): doesn't
4627cb960a2Sdownsj 			 * really work because nested $(..) or ${..} inside
4637cb960a2Sdownsj 			 * double quotes aren't dealt with.
4647cb960a2Sdownsj 			 */
4653b015934Smillert 			switch (statep->ls_scsparen.csstate) {
4667cb960a2Sdownsj 			case 0: /* normal */
4677cb960a2Sdownsj 				switch (c) {
4687cb960a2Sdownsj 				case '(':
4693b015934Smillert 					statep->ls_scsparen.nparen++;
4707cb960a2Sdownsj 					break;
4717cb960a2Sdownsj 				case ')':
4723b015934Smillert 					statep->ls_scsparen.nparen--;
4737cb960a2Sdownsj 					break;
4747cb960a2Sdownsj 				case '\\':
4753b015934Smillert 					statep->ls_scsparen.csstate = 1;
4767cb960a2Sdownsj 					break;
4777cb960a2Sdownsj 				case '"':
4783b015934Smillert 					statep->ls_scsparen.csstate = 2;
4797cb960a2Sdownsj 					break;
4807cb960a2Sdownsj 				case '\'':
4813b015934Smillert 					statep->ls_scsparen.csstate = 4;
482e55c1b2cSdownsj 					ignore_backslash_newline++;
4837cb960a2Sdownsj 					break;
4847cb960a2Sdownsj 				}
4857cb960a2Sdownsj 				break;
4867cb960a2Sdownsj 
4877cb960a2Sdownsj 			case 1: /* backslash in normal mode */
4887cb960a2Sdownsj 			case 3: /* backslash in double quotes */
4893b015934Smillert 				--statep->ls_scsparen.csstate;
4907cb960a2Sdownsj 				break;
4917cb960a2Sdownsj 
4927cb960a2Sdownsj 			case 2: /* double quotes */
4937cb960a2Sdownsj 				if (c == '"')
4943b015934Smillert 					statep->ls_scsparen.csstate = 0;
4957cb960a2Sdownsj 				else if (c == '\\')
4963b015934Smillert 					statep->ls_scsparen.csstate = 3;
4977cb960a2Sdownsj 				break;
4987cb960a2Sdownsj 
4997cb960a2Sdownsj 			case 4: /* single quotes */
500e55c1b2cSdownsj 				if (c == '\'') {
5013b015934Smillert 					statep->ls_scsparen.csstate = 0;
502e55c1b2cSdownsj 					ignore_backslash_newline--;
503e55c1b2cSdownsj 				}
5047cb960a2Sdownsj 				break;
5057cb960a2Sdownsj 			}
5063b015934Smillert 			if (statep->ls_scsparen.nparen == 0) {
5073b015934Smillert 				POP_STATE();
5087cb960a2Sdownsj 				*wp++ = 0; /* end of COMSUB */
5097cb960a2Sdownsj 			} else
5107cb960a2Sdownsj 				*wp++ = c;
5117cb960a2Sdownsj 			break;
5127cb960a2Sdownsj 
5133b015934Smillert 		case SASPAREN: /* $(( .. )) */
5147cb960a2Sdownsj 			/* todo: deal with $((...); (...)) properly */
5157cb960a2Sdownsj 			/* XXX should nest using existing state machine
5167cb960a2Sdownsj 			 * (embed "..", $(...), etc.) */
5177cb960a2Sdownsj 			if (c == '(')
5183b015934Smillert 				statep->ls_sasparen.nparen++;
5197cb960a2Sdownsj 			else if (c == ')') {
5203b015934Smillert 				statep->ls_sasparen.nparen--;
5213b015934Smillert 				if (statep->ls_sasparen.nparen == 1) {
5227cb960a2Sdownsj 					/*(*/
5237cb960a2Sdownsj 					if ((c2 = getsc()) == ')') {
5243b015934Smillert 						POP_STATE();
5257cb960a2Sdownsj 						*wp++ = 0; /* end of EXPRSUB */
5267cb960a2Sdownsj 						break;
5277cb960a2Sdownsj 					} else {
5283b015934Smillert 						char *s;
5293b015934Smillert 
5307cb960a2Sdownsj 						ungetsc(c2);
5317cb960a2Sdownsj 						/* mismatched parenthesis -
5327cb960a2Sdownsj 						 * assume we were really
5337cb960a2Sdownsj 						 * parsing a $(..) expression
5347cb960a2Sdownsj 						 */
5353b015934Smillert 						s = Xrestpos(ws, wp,
5363b015934Smillert 						    statep->ls_sasparen.start);
5373b015934Smillert 						memmove(s + 1, s, wp - s);
5383b015934Smillert 						*s++ = COMSUB;
5393b015934Smillert 						*s = '('; /*)*/
5407cb960a2Sdownsj 						wp++;
5413b015934Smillert 						statep->ls_scsparen.nparen = 1;
5423b015934Smillert 						statep->ls_scsparen.csstate = 0;
5437a8124d8Sderaadt 						state = statep->ls_state =
5447a8124d8Sderaadt 						    SCSPAREN;
5457cb960a2Sdownsj 					}
5467cb960a2Sdownsj 				}
5477cb960a2Sdownsj 			}
5487cb960a2Sdownsj 			*wp++ = c;
5497cb960a2Sdownsj 			break;
5507cb960a2Sdownsj 
55122a9cf94Sguenther 		case SBRACEQ:
5527cb960a2Sdownsj 		case SBRACE:
5537cb960a2Sdownsj 			/*{*/
5547cb960a2Sdownsj 			if (c == '}') {
5553b015934Smillert 				POP_STATE();
5567cb960a2Sdownsj 				*wp++ = CSUBST;
5573b015934Smillert 				*wp++ = /*{*/ '}';
5587cb960a2Sdownsj 			} else
5597cb960a2Sdownsj 				goto Sbase1;
5607cb960a2Sdownsj 			break;
5617cb960a2Sdownsj 
5627cb960a2Sdownsj 		case STBRACE:
5633b015934Smillert 			/* Same as SBRACE, except (,|,) treated specially */
5647cb960a2Sdownsj 			/*{*/
5657cb960a2Sdownsj 			if (c == '}') {
5663b015934Smillert 				POP_STATE();
5677cb960a2Sdownsj 				*wp++ = CSUBST;
5683b015934Smillert 				*wp++ = /*{*/ '}';
5697cb960a2Sdownsj 			} else if (c == '|') {
5707cb960a2Sdownsj 				*wp++ = SPAT;
5713b015934Smillert 			} else if (c == '(') {
5723b015934Smillert 				*wp++ = OPAT;
5733b015934Smillert 				*wp++ = ' ';	/* simile for @ */
5743b015934Smillert 				PUSH_STATE(SPATTERN);
5757cb960a2Sdownsj 			} else
5767cb960a2Sdownsj 				goto Sbase1;
5777cb960a2Sdownsj 			break;
5787cb960a2Sdownsj 
5797cb960a2Sdownsj 		case SBQUOTE:
5807cb960a2Sdownsj 			if (c == '`') {
5817cb960a2Sdownsj 				*wp++ = 0;
5823b015934Smillert 				POP_STATE();
5837cb960a2Sdownsj 			} else if (c == '\\') {
5847cb960a2Sdownsj 				switch (c = getsc()) {
5857cb960a2Sdownsj 				case '\\':
5867cb960a2Sdownsj 				case '$': case '`':
5877cb960a2Sdownsj 					*wp++ = c;
5887cb960a2Sdownsj 					break;
5897cb960a2Sdownsj 				case '"':
5903b015934Smillert 					if (statep->ls_sbquote.indquotes) {
5917cb960a2Sdownsj 						*wp++ = c;
5927cb960a2Sdownsj 						break;
5937cb960a2Sdownsj 					}
5947eab881bSjaredy 					/* FALLTHROUGH */
5957cb960a2Sdownsj 				default:
596e55c1b2cSdownsj 					if (c) { /* trailing \ is lost */
5977cb960a2Sdownsj 						*wp++ = '\\';
5987cb960a2Sdownsj 						*wp++ = c;
599e55c1b2cSdownsj 					}
6007cb960a2Sdownsj 					break;
6017cb960a2Sdownsj 				}
6027cb960a2Sdownsj 			} else
6037cb960a2Sdownsj 				*wp++ = c;
6047cb960a2Sdownsj 			break;
6057cb960a2Sdownsj 
6067cb960a2Sdownsj 		case SWORD:	/* ONEWORD */
6077cb960a2Sdownsj 			goto Subst;
6087cb960a2Sdownsj 
6093b015934Smillert 		case SLETPAREN:	/* LETEXPR: (( ... )) */
6107cb960a2Sdownsj 			/*(*/
6117cb960a2Sdownsj 			if (c == ')') {
6123b015934Smillert 				if (statep->ls_sletparen.nparen > 0)
6133b015934Smillert 				    --statep->ls_sletparen.nparen;
6147cb960a2Sdownsj 				/*(*/
6157cb960a2Sdownsj 				else if ((c2 = getsc()) == ')') {
6167cb960a2Sdownsj 					c = 0;
6177cb960a2Sdownsj 					*wp++ = CQUOTE;
6187cb960a2Sdownsj 					goto Done;
6197cb960a2Sdownsj 				} else
6207cb960a2Sdownsj 					ungetsc(c2);
6217cb960a2Sdownsj 			} else if (c == '(')
6227cb960a2Sdownsj 				/* parenthesis inside quotes and backslashes
6237cb960a2Sdownsj 				 * are lost, but at&t ksh doesn't count them
6247cb960a2Sdownsj 				 * either
6257cb960a2Sdownsj 				 */
6263b015934Smillert 				++statep->ls_sletparen.nparen;
6277cb960a2Sdownsj 			goto Sbase2;
6287cb960a2Sdownsj 
6297cb960a2Sdownsj 		case SHEREDELIM:	/* <<,<<- delimiter */
6307cb960a2Sdownsj 			/* XXX chuck this state (and the next) - use
6317cb960a2Sdownsj 			 * the existing states ($ and \`..` should be
6327cb960a2Sdownsj 			 * stripped of their specialness after the
6337cb960a2Sdownsj 			 * fact).
6347cb960a2Sdownsj 			 */
6357cb960a2Sdownsj 			/* here delimiters need a special case since
6367cb960a2Sdownsj 			 * $ and `..` are not to be treated specially
6377cb960a2Sdownsj 			 */
6387cb960a2Sdownsj 			if (c == '\\') {
6397cb960a2Sdownsj 				c = getsc();
640e55c1b2cSdownsj 				if (c) { /* trailing \ is lost */
6417cb960a2Sdownsj 					*wp++ = QCHAR;
6427cb960a2Sdownsj 					*wp++ = c;
6437cb960a2Sdownsj 				}
6447cb960a2Sdownsj 			} else if (c == '\'') {
6453b015934Smillert 				PUSH_STATE(SSQUOTE);
6467cb960a2Sdownsj 				*wp++ = OQUOTE;
647e55c1b2cSdownsj 				ignore_backslash_newline++;
6487cb960a2Sdownsj 			} else if (c == '"') {
6493b015934Smillert 				state = statep->ls_state = SHEREDQUOTE;
6507cb960a2Sdownsj 				*wp++ = OQUOTE;
6517cb960a2Sdownsj 			} else {
6527cb960a2Sdownsj 				*wp++ = CHAR;
6537cb960a2Sdownsj 				*wp++ = c;
6547cb960a2Sdownsj 			}
6557cb960a2Sdownsj 			break;
6567cb960a2Sdownsj 
6577cb960a2Sdownsj 		case SHEREDQUOTE:	/* " in <<,<<- delimiter */
6587cb960a2Sdownsj 			if (c == '"') {
6597cb960a2Sdownsj 				*wp++ = CQUOTE;
6603b015934Smillert 				state = statep->ls_state = SHEREDELIM;
6617cb960a2Sdownsj 			} else {
662e55c1b2cSdownsj 				if (c == '\\') {
663e55c1b2cSdownsj 					switch (c = getsc()) {
664e55c1b2cSdownsj 					case '\\': case '"':
665e55c1b2cSdownsj 					case '$': case '`':
6667cb960a2Sdownsj 						break;
667e55c1b2cSdownsj 					default:
668e55c1b2cSdownsj 						if (c) { /* trailing \ lost */
669e55c1b2cSdownsj 							*wp++ = CHAR;
670e55c1b2cSdownsj 							*wp++ = '\\';
671e55c1b2cSdownsj 						}
672e55c1b2cSdownsj 						break;
673e55c1b2cSdownsj 					}
674e55c1b2cSdownsj 				}
6757cb960a2Sdownsj 				*wp++ = CHAR;
6767cb960a2Sdownsj 				*wp++ = c;
6777cb960a2Sdownsj 			}
6787cb960a2Sdownsj 			break;
6797cb960a2Sdownsj 
6807cb960a2Sdownsj 		case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
6817cb960a2Sdownsj 			if ( /*(*/ c == ')') {
6827cb960a2Sdownsj 				*wp++ = CPAT;
6833b015934Smillert 				POP_STATE();
6843b015934Smillert 			} else if (c == '|') {
6857cb960a2Sdownsj 				*wp++ = SPAT;
6863b015934Smillert 			} else if (c == '(') {
6873b015934Smillert 				*wp++ = OPAT;
6883b015934Smillert 				*wp++ = ' ';	/* simile for @ */
6893b015934Smillert 				PUSH_STATE(SPATTERN);
6903b015934Smillert 			} else
6917cb960a2Sdownsj 				goto Sbase1;
6927cb960a2Sdownsj 			break;
6937cb960a2Sdownsj 		}
6947cb960a2Sdownsj 	}
6957cb960a2Sdownsj Done:
6967cb960a2Sdownsj 	Xcheck(ws, wp);
6973b015934Smillert 	if (statep != &states[1])
6983b015934Smillert 		/* XXX figure out what is missing */
6997cb960a2Sdownsj 		yyerror("no closing quote\n");
7007cb960a2Sdownsj 
7017cb960a2Sdownsj 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
7027cb960a2Sdownsj 	if (state == SHEREDELIM)
7037cb960a2Sdownsj 		state = SBASE;
7047cb960a2Sdownsj 
7053b015934Smillert 	dp = Xstring(ws, wp);
7067a8124d8Sderaadt 	if ((c == '<' || c == '>') && state == SBASE &&
7077a8124d8Sderaadt 	    ((c2 = Xlength(ws, wp)) == 0 ||
7087a8124d8Sderaadt 	    (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
7098c046d24Snicm 		struct ioword *iop = alloc(sizeof(*iop), ATEMP);
7107cb960a2Sdownsj 
7113b015934Smillert 		if (c2 == 2)
7123b015934Smillert 			iop->unit = dp[1] - '0';
7137cb960a2Sdownsj 		else
7143b015934Smillert 			iop->unit = c == '>'; /* 0 for <, 1 for > */
7157cb960a2Sdownsj 
7167cb960a2Sdownsj 		c2 = getsc();
7177cb960a2Sdownsj 		/* <<, >>, <> are ok, >< is not */
7187cb960a2Sdownsj 		if (c == c2 || (c == '<' && c2 == '>')) {
7197cb960a2Sdownsj 			iop->flag = c == c2 ?
7207cb960a2Sdownsj 			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
72118bbba6bSmillert 			if (iop->flag == IOHERE) {
722e55c1b2cSdownsj 				if ((c2 = getsc()) == '-')
7237cb960a2Sdownsj 					iop->flag |= IOSKIP;
7247cb960a2Sdownsj 				else
7257cb960a2Sdownsj 					ungetsc(c2);
72618bbba6bSmillert 			}
7277cb960a2Sdownsj 		} else if (c2 == '&')
7287cb960a2Sdownsj 			iop->flag = IODUP | (c == '<' ? IORDUP : 0);
7297cb960a2Sdownsj 		else {
7307cb960a2Sdownsj 			iop->flag = c == '>' ? IOWRITE : IOREAD;
7317cb960a2Sdownsj 			if (c == '>' && c2 == '|')
7327cb960a2Sdownsj 				iop->flag |= IOCLOB;
7337cb960a2Sdownsj 			else
7347cb960a2Sdownsj 				ungetsc(c2);
7357cb960a2Sdownsj 		}
7367cb960a2Sdownsj 
737355ffa75Stedu 		iop->name = NULL;
738355ffa75Stedu 		iop->delim = NULL;
739355ffa75Stedu 		iop->heredoc = NULL;
7403b015934Smillert 		Xfree(ws, wp);	/* free word */
7417cb960a2Sdownsj 		yylval.iop = iop;
7427cb960a2Sdownsj 		return REDIR;
7437cb960a2Sdownsj 	}
7443b015934Smillert 
7453b015934Smillert 	if (wp == dp && state == SBASE) {
7463b015934Smillert 		Xfree(ws, wp);	/* free word */
7473b015934Smillert 		/* no word, process LEX1 character */
7483b015934Smillert 		switch (c) {
7493b015934Smillert 		default:
7503b015934Smillert 			return c;
7513b015934Smillert 
7523b015934Smillert 		case '|':
7533b015934Smillert 		case '&':
7543b015934Smillert 		case ';':
7553b015934Smillert 			if ((c2 = getsc()) == c)
7563b015934Smillert 				c = (c == ';') ? BREAK :
7573b015934Smillert 				    (c == '|') ? LOGOR :
7583b015934Smillert 				    (c == '&') ? LOGAND :
7593b015934Smillert 				    YYERRCODE;
7603b015934Smillert 			else if (c == '|' && c2 == '&')
7613b015934Smillert 				c = COPROC;
7623b015934Smillert 			else
7633b015934Smillert 				ungetsc(c2);
7643b015934Smillert 			return c;
7653b015934Smillert 
7667cb960a2Sdownsj 		case '\n':
7677cb960a2Sdownsj 			gethere();
7687cb960a2Sdownsj 			if (cf & CONTIN)
7697cb960a2Sdownsj 				goto Again;
7707cb960a2Sdownsj 			return c;
7717cb960a2Sdownsj 
7727cb960a2Sdownsj 		case '(':  /*)*/
773b69beb66Sgrr 			if (!Flag(FSH)) {
7747cb960a2Sdownsj 				if ((c2 = getsc()) == '(') /*)*/
7753b015934Smillert 					/* XXX need to handle ((...); (...)) */
7767cb960a2Sdownsj 					c = MDPAREN;
7777cb960a2Sdownsj 				else
7787cb960a2Sdownsj 					ungetsc(c2);
779b69beb66Sgrr 			}
7807cb960a2Sdownsj 			return c;
7817cb960a2Sdownsj 		  /*(*/
7827cb960a2Sdownsj 		case ')':
7837cb960a2Sdownsj 			return c;
7847cb960a2Sdownsj 		}
7857cb960a2Sdownsj 	}
7867cb960a2Sdownsj 
7877cb960a2Sdownsj 	*wp++ = EOS;		/* terminate word */
7887cb960a2Sdownsj 	yylval.cp = Xclose(ws, wp);
78994e42df6Smillert 	if (state == SWORD || state == SLETPAREN)	/* ONEWORD? */
7907cb960a2Sdownsj 		return LWORD;
7917cb960a2Sdownsj 	ungetsc(c);		/* unget terminator */
7927cb960a2Sdownsj 
7937cb960a2Sdownsj 	/* copy word to unprefixed string ident */
7947cb960a2Sdownsj 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
7957cb960a2Sdownsj 		*dp++ = *sp++;
79612e7fb2dSjmc 	/* Make sure the ident array stays '\0' padded */
7977cb960a2Sdownsj 	memset(dp, 0, (ident+IDENT) - dp + 1);
7987cb960a2Sdownsj 	if (c != EOS)
7997cb960a2Sdownsj 		*ident = '\0';	/* word is not unquoted */
8007cb960a2Sdownsj 
8017cb960a2Sdownsj 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
8027cb960a2Sdownsj 		struct tbl *p;
8037cb960a2Sdownsj 		int h = hash(ident);
8047cb960a2Sdownsj 
8057cb960a2Sdownsj 		/* { */
8066df3ee40Sotto 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
8077a8124d8Sderaadt 		    (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
8087cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8097cb960a2Sdownsj 			return p->val.i;
8107cb960a2Sdownsj 		}
8116df3ee40Sotto 		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
8127a8124d8Sderaadt 		    (p->flag & ISSET)) {
8137894b443Smillert 			Source *s;
8147cb960a2Sdownsj 
8157cb960a2Sdownsj 			for (s = source; s->type == SALIAS; s = s->next)
8167cb960a2Sdownsj 				if (s->u.tblp == p)
8177cb960a2Sdownsj 					return LWORD;
8187cb960a2Sdownsj 			/* push alias expansion */
8197cb960a2Sdownsj 			s = pushs(SALIAS, source->areap);
8207cb960a2Sdownsj 			s->start = s->str = p->val.s;
8217cb960a2Sdownsj 			s->u.tblp = p;
8227cb960a2Sdownsj 			s->next = source;
8237cb960a2Sdownsj 			source = s;
8247cb960a2Sdownsj 			afree(yylval.cp, ATEMP);
8257cb960a2Sdownsj 			goto Again;
8267cb960a2Sdownsj 		}
8277cb960a2Sdownsj 	}
8287cb960a2Sdownsj 
8297cb960a2Sdownsj 	return LWORD;
8307cb960a2Sdownsj }
8317cb960a2Sdownsj 
8327cb960a2Sdownsj static void
833c5d5393cSotto gethere(void)
8347cb960a2Sdownsj {
8357894b443Smillert 	struct ioword **p;
8367cb960a2Sdownsj 
8377cb960a2Sdownsj 	for (p = heres; p < herep; p++)
8387cb960a2Sdownsj 		readhere(*p);
8397cb960a2Sdownsj 	herep = heres;
8407cb960a2Sdownsj }
8417cb960a2Sdownsj 
8427cb960a2Sdownsj /*
8437cb960a2Sdownsj  * read "<<word" text into temp file
8447cb960a2Sdownsj  */
8457cb960a2Sdownsj 
8467cb960a2Sdownsj static void
847c5d5393cSotto readhere(struct ioword *iop)
8487cb960a2Sdownsj {
8497894b443Smillert 	int c;
8507cb960a2Sdownsj 	char *volatile eof;
8517cb960a2Sdownsj 	char *eofp;
852e55c1b2cSdownsj 	int skiptabs;
853f00c5086Smillert 	XString xs;
854f00c5086Smillert 	char *xp;
855f00c5086Smillert 	int xpos;
8567cb960a2Sdownsj 
8577cb960a2Sdownsj 	eof = evalstr(iop->delim, 0);
8587cb960a2Sdownsj 
859e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
860e55c1b2cSdownsj 		ignore_backslash_newline++;
861e55c1b2cSdownsj 
862f00c5086Smillert 	Xinit(xs, xp, 256, ATEMP);
863f00c5086Smillert 
8647cb960a2Sdownsj 	for (;;) {
8657cb960a2Sdownsj 		eofp = eof;
8667cb960a2Sdownsj 		skiptabs = iop->flag & IOSKIP;
867f00c5086Smillert 		xpos = Xsavepos(xs, xp);
868e55c1b2cSdownsj 		while ((c = getsc()) != 0) {
8697cb960a2Sdownsj 			if (skiptabs) {
8707cb960a2Sdownsj 				if (c == '\t')
8717cb960a2Sdownsj 					continue;
8727cb960a2Sdownsj 				skiptabs = 0;
8737cb960a2Sdownsj 			}
8747cb960a2Sdownsj 			if (c != *eofp)
8757cb960a2Sdownsj 				break;
876f00c5086Smillert 			Xcheck(xs, xp);
877f00c5086Smillert 			Xput(xs, xp, c);
8787cb960a2Sdownsj 			eofp++;
8797cb960a2Sdownsj 		}
8807cb960a2Sdownsj 		/* Allow EOF here so commands with out trailing newlines
8817cb960a2Sdownsj 		 * will work (eg, ksh -c '...', $(...), etc).
8827cb960a2Sdownsj 		 */
883f00c5086Smillert 		if (*eofp == '\0' && (c == 0 || c == '\n')) {
884f00c5086Smillert 			xp = Xrestpos(xs, xp, xpos);
8857cb960a2Sdownsj 			break;
886f00c5086Smillert 		}
8877cb960a2Sdownsj 		ungetsc(c);
888e55c1b2cSdownsj 		while ((c = getsc()) != '\n') {
8897cb960a2Sdownsj 			if (c == 0)
8907cb960a2Sdownsj 				yyerror("here document `%s' unclosed\n", eof);
891f00c5086Smillert 			Xcheck(xs, xp);
892f00c5086Smillert 			Xput(xs, xp, c);
8937cb960a2Sdownsj 		}
894f00c5086Smillert 		Xcheck(xs, xp);
895f00c5086Smillert 		Xput(xs, xp, c);
8967cb960a2Sdownsj 	}
897f00c5086Smillert 	Xput(xs, xp, '\0');
898f00c5086Smillert 	iop->heredoc = Xclose(xs, xp);
899f00c5086Smillert 
900e55c1b2cSdownsj 	if (!(iop->flag & IOEVAL))
901e55c1b2cSdownsj 		ignore_backslash_newline--;
9027cb960a2Sdownsj }
9037cb960a2Sdownsj 
9047cb960a2Sdownsj void
9057cb960a2Sdownsj yyerror(const char *fmt, ...)
9067cb960a2Sdownsj {
9077cb960a2Sdownsj 	va_list va;
9087cb960a2Sdownsj 
9097cb960a2Sdownsj 	/* pop aliases and re-reads */
9107cb960a2Sdownsj 	while (source->type == SALIAS || source->type == SREREAD)
9117cb960a2Sdownsj 		source = source->next;
9127cb960a2Sdownsj 	source->str = null;	/* zap pending input */
9137cb960a2Sdownsj 
9140e7d3a01Smillert 	error_prefix(true);
91569b9f96bSmillert 	va_start(va, fmt);
9167cb960a2Sdownsj 	shf_vfprintf(shl_out, fmt, va);
9177cb960a2Sdownsj 	va_end(va);
91863ca93eaSmillert 	errorf(NULL);
9197cb960a2Sdownsj }
9207cb960a2Sdownsj 
9217cb960a2Sdownsj /*
9227cb960a2Sdownsj  * input for yylex with alias expansion
9237cb960a2Sdownsj  */
9247cb960a2Sdownsj 
9257cb960a2Sdownsj Source *
926c5d5393cSotto pushs(int type, Area *areap)
9277cb960a2Sdownsj {
9287894b443Smillert 	Source *s;
9297cb960a2Sdownsj 
9308c046d24Snicm 	s = alloc(sizeof(Source), areap);
9317cb960a2Sdownsj 	s->type = type;
9327cb960a2Sdownsj 	s->str = null;
9337cb960a2Sdownsj 	s->start = NULL;
9347cb960a2Sdownsj 	s->line = 0;
935e7b1290aSotto 	s->cmd_offset = 0;
9367cb960a2Sdownsj 	s->errline = 0;
9377cb960a2Sdownsj 	s->file = NULL;
9387cb960a2Sdownsj 	s->flags = 0;
9397cb960a2Sdownsj 	s->next = NULL;
9407cb960a2Sdownsj 	s->areap = areap;
9417cb960a2Sdownsj 	if (type == SFILE || type == SSTDIN) {
9427cb960a2Sdownsj 		char *dummy;
9437cb960a2Sdownsj 		Xinit(s->xs, dummy, 256, s->areap);
9447cb960a2Sdownsj 	} else
9457cb960a2Sdownsj 		memset(&s->xs, 0, sizeof(s->xs));
9467cb960a2Sdownsj 	return s;
9477cb960a2Sdownsj }
9487cb960a2Sdownsj 
9497cb960a2Sdownsj static int
950c5d5393cSotto getsc__(void)
9517cb960a2Sdownsj {
9527894b443Smillert 	Source *s = source;
9537894b443Smillert 	int c;
9547cb960a2Sdownsj 
9557cb960a2Sdownsj 	while ((c = *s->str++) == 0) {
9567cb960a2Sdownsj 		s->str = NULL;		/* return 0 for EOF by default */
9577cb960a2Sdownsj 		switch (s->type) {
9587cb960a2Sdownsj 		case SEOF:
9597cb960a2Sdownsj 			s->str = null;
9607cb960a2Sdownsj 			return 0;
9617cb960a2Sdownsj 
9627cb960a2Sdownsj 		case SSTDIN:
9637cb960a2Sdownsj 		case SFILE:
9647cb960a2Sdownsj 			getsc_line(s);
9657cb960a2Sdownsj 			break;
9667cb960a2Sdownsj 
9677cb960a2Sdownsj 		case SWSTR:
9687cb960a2Sdownsj 			break;
9697cb960a2Sdownsj 
9707cb960a2Sdownsj 		case SSTRING:
9717cb960a2Sdownsj 			break;
9727cb960a2Sdownsj 
9737cb960a2Sdownsj 		case SWORDS:
9747cb960a2Sdownsj 			s->start = s->str = *s->u.strv++;
9757cb960a2Sdownsj 			s->type = SWORDSEP;
9767cb960a2Sdownsj 			break;
9777cb960a2Sdownsj 
9787cb960a2Sdownsj 		case SWORDSEP:
9797cb960a2Sdownsj 			if (*s->u.strv == NULL) {
98096085982Snicm 				s->start = s->str = "\n";
9817cb960a2Sdownsj 				s->type = SEOF;
9827cb960a2Sdownsj 			} else {
98396085982Snicm 				s->start = s->str = " ";
9847cb960a2Sdownsj 				s->type = SWORDS;
9857cb960a2Sdownsj 			}
9867cb960a2Sdownsj 			break;
9877cb960a2Sdownsj 
9887cb960a2Sdownsj 		case SALIAS:
9897cb960a2Sdownsj 			if (s->flags & SF_ALIASEND) {
9907cb960a2Sdownsj 				/* pass on an unused SF_ALIAS flag */
9917cb960a2Sdownsj 				source = s->next;
9927cb960a2Sdownsj 				source->flags |= s->flags & SF_ALIAS;
9937cb960a2Sdownsj 				s = source;
9947a8124d8Sderaadt 			} else if (*s->u.tblp->val.s &&
995e569fc7cSderaadt 			    isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) {
9967cb960a2Sdownsj 				source = s = s->next;	/* pop source stack */
997e55c1b2cSdownsj 				/* Note that this alias ended with a space,
998e55c1b2cSdownsj 				 * enabling alias expansion on the following
999e55c1b2cSdownsj 				 * word.
1000e55c1b2cSdownsj 				 */
10017cb960a2Sdownsj 				s->flags |= SF_ALIAS;
10027cb960a2Sdownsj 			} else {
1003e55c1b2cSdownsj 				/* At this point, we need to keep the current
1004e55c1b2cSdownsj 				 * alias in the source list so recursive
1005e55c1b2cSdownsj 				 * aliases can be detected and we also need
1006e55c1b2cSdownsj 				 * to return the next character.  Do this
1007e55c1b2cSdownsj 				 * by temporarily popping the alias to get
1008e55c1b2cSdownsj 				 * the next character and then put it back
1009e55c1b2cSdownsj 				 * in the source list with the SF_ALIASEND
1010e55c1b2cSdownsj 				 * flag set.
10117cb960a2Sdownsj 				 */
1012e55c1b2cSdownsj 				source = s->next;	/* pop source stack */
1013e55c1b2cSdownsj 				source->flags |= s->flags & SF_ALIAS;
1014e55c1b2cSdownsj 				c = getsc__();
1015e55c1b2cSdownsj 				if (c) {
10167cb960a2Sdownsj 					s->flags |= SF_ALIASEND;
1017e55c1b2cSdownsj 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1018e55c1b2cSdownsj 					s->start = s->str = s->ugbuf;
1019e55c1b2cSdownsj 					s->next = source;
1020e55c1b2cSdownsj 					source = s;
1021e55c1b2cSdownsj 				} else {
1022e55c1b2cSdownsj 					s = source;
1023e55c1b2cSdownsj 					/* avoid reading eof twice */
1024e55c1b2cSdownsj 					s->str = NULL;
1025e7bc3c65Sdownsj 					break;
1026e55c1b2cSdownsj 				}
10277cb960a2Sdownsj 			}
10287cb960a2Sdownsj 			continue;
10297cb960a2Sdownsj 
10307cb960a2Sdownsj 		case SREREAD:
1031e55c1b2cSdownsj 			if (s->start != s->ugbuf) /* yuck */
10327cb960a2Sdownsj 				afree(s->u.freeme, ATEMP);
10337cb960a2Sdownsj 			source = s = s->next;
10347cb960a2Sdownsj 			continue;
10357cb960a2Sdownsj 		}
10367cb960a2Sdownsj 		if (s->str == NULL) {
10377cb960a2Sdownsj 			s->type = SEOF;
10387cb960a2Sdownsj 			s->start = s->str = null;
10397cb960a2Sdownsj 			return '\0';
10407cb960a2Sdownsj 		}
10417cb960a2Sdownsj 		if (s->flags & SF_ECHO) {
10427cb960a2Sdownsj 			shf_puts(s->str, shl_out);
10437cb960a2Sdownsj 			shf_flush(shl_out);
10447cb960a2Sdownsj 		}
10457cb960a2Sdownsj 	}
10467cb960a2Sdownsj 	return c;
10477cb960a2Sdownsj }
10487cb960a2Sdownsj 
10497cb960a2Sdownsj static void
1050c5d5393cSotto getsc_line(Source *s)
10517cb960a2Sdownsj {
10527cb960a2Sdownsj 	char *xp = Xstring(s->xs, xp);
10537cb960a2Sdownsj 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
10547cb960a2Sdownsj 	int have_tty = interactive && (s->flags & SF_TTY);
10557cb960a2Sdownsj 
10567cb960a2Sdownsj 	/* Done here to ensure nothing odd happens when a timeout occurs */
10577cb960a2Sdownsj 	XcheckN(s->xs, xp, LINE);
10587cb960a2Sdownsj 	*xp = '\0';
10597cb960a2Sdownsj 	s->start = s->str = xp;
10607cb960a2Sdownsj 
10617cb960a2Sdownsj 	if (have_tty && ksh_tmout) {
10627cb960a2Sdownsj 		ksh_tmout_state = TMOUT_READING;
10637cb960a2Sdownsj 		alarm(ksh_tmout);
10647cb960a2Sdownsj 	}
10657cb960a2Sdownsj #ifdef EDIT
10667cb960a2Sdownsj 	if (have_tty && (0
10677cb960a2Sdownsj # ifdef VI
10687cb960a2Sdownsj 	    || Flag(FVI)
10697cb960a2Sdownsj # endif /* VI */
10707cb960a2Sdownsj # ifdef EMACS
10717cb960a2Sdownsj 	    || Flag(FEMACS) || Flag(FGMACS)
10727cb960a2Sdownsj # endif /* EMACS */
10737a8124d8Sderaadt 	    )) {
10747cb960a2Sdownsj 		int nread;
10757cb960a2Sdownsj 
10767cb960a2Sdownsj 		nread = x_read(xp, LINE);
10777cb960a2Sdownsj 		if (nread < 0)	/* read error */
10787cb960a2Sdownsj 			nread = 0;
10797cb960a2Sdownsj 		xp[nread] = '\0';
10807cb960a2Sdownsj 		xp += nread;
10817cb960a2Sdownsj 	}
10827cb960a2Sdownsj 	else
10837cb960a2Sdownsj #endif /* EDIT */
10847cb960a2Sdownsj 	{
10857cb960a2Sdownsj 		if (interactive) {
10867cb960a2Sdownsj 			pprompt(prompt, 0);
10877cb960a2Sdownsj 		} else
10887cb960a2Sdownsj 			s->line++;
10897cb960a2Sdownsj 
10907cb960a2Sdownsj 		while (1) {
10917cb960a2Sdownsj 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
10927cb960a2Sdownsj 
10937a8124d8Sderaadt 			if (!p && shf_error(s->u.shf) &&
10947a8124d8Sderaadt 			    shf_errno(s->u.shf) == EINTR) {
10957cb960a2Sdownsj 				shf_clearerr(s->u.shf);
10967cb960a2Sdownsj 				if (trap)
10977cb960a2Sdownsj 					runtraps(0);
10987cb960a2Sdownsj 				continue;
10997cb960a2Sdownsj 			}
11007cb960a2Sdownsj 			if (!p || (xp = p, xp[-1] == '\n'))
11017cb960a2Sdownsj 				break;
11027cb960a2Sdownsj 			/* double buffer size */
11037cb960a2Sdownsj 			xp++; /* move past null so doubling works... */
11047cb960a2Sdownsj 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
11057cb960a2Sdownsj 			xp--; /* ...and move back again */
11067cb960a2Sdownsj 		}
11077cb960a2Sdownsj 		/* flush any unwanted input so other programs/builtins
11087cb960a2Sdownsj 		 * can read it.  Not very optimal, but less error prone
11097cb960a2Sdownsj 		 * than flushing else where, dealing with redirections,
11107cb960a2Sdownsj 		 * etc..
11117cb960a2Sdownsj 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
11127cb960a2Sdownsj 		 */
11137cb960a2Sdownsj 		if (s->type == SSTDIN)
11147cb960a2Sdownsj 			shf_flush(s->u.shf);
11157cb960a2Sdownsj 	}
11167cb960a2Sdownsj 	/* XXX: temporary kludge to restore source after a
11177cb960a2Sdownsj 	 * trap may have been executed.
11187cb960a2Sdownsj 	 */
11197cb960a2Sdownsj 	source = s;
112094e42df6Smillert 	if (have_tty && ksh_tmout) {
11217cb960a2Sdownsj 		ksh_tmout_state = TMOUT_EXECUTING;
11227cb960a2Sdownsj 		alarm(0);
11237cb960a2Sdownsj 	}
11247cb960a2Sdownsj 	s->start = s->str = Xstring(s->xs, xp);
11257cb960a2Sdownsj 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
11267cb960a2Sdownsj 	/* Note: if input is all nulls, this is not eof */
11277cb960a2Sdownsj 	if (Xlength(s->xs, xp) == 0) { /* EOF */
11287cb960a2Sdownsj 		if (s->type == SFILE)
11297cb960a2Sdownsj 			shf_fdclose(s->u.shf);
11307cb960a2Sdownsj 		s->str = NULL;
11317cb960a2Sdownsj 	} else if (interactive) {
11327cb960a2Sdownsj #ifdef HISTORY
11337cb960a2Sdownsj 		char *p = Xstring(s->xs, xp);
11347cb960a2Sdownsj 		if (cur_prompt == PS1)
11357cb960a2Sdownsj 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
11367cb960a2Sdownsj 				p++;
11377cb960a2Sdownsj 		if (*p) {
11387cb960a2Sdownsj 			s->line++;
11397cb960a2Sdownsj 			histsave(s->line, s->str, 1);
11407cb960a2Sdownsj 		}
11417cb960a2Sdownsj #endif /* HISTORY */
11427cb960a2Sdownsj 	}
11437cb960a2Sdownsj 	if (interactive)
1144f7654a50Snicm 		set_prompt(PS2, NULL);
11457cb960a2Sdownsj }
11467cb960a2Sdownsj 
1147e7b1290aSotto static char *
1148e7b1290aSotto special_prompt_expand(char *str)
1149e7b1290aSotto {
1150e7b1290aSotto 	char *p = str;
1151e7b1290aSotto 
1152e7b1290aSotto 	while ((p = strstr(p, "\\$")) != NULL) {
11536aa3ef4bSdrahn 		*(p+1) = 'p';
1154e7b1290aSotto 	}
1155e7b1290aSotto 	return str;
1156e7b1290aSotto }
1157e7b1290aSotto 
11587cb960a2Sdownsj void
1159c5d5393cSotto set_prompt(int to, Source *s)
11607cb960a2Sdownsj {
11614300ac9bSmillert 	char *ps1;
11627cb960a2Sdownsj 	Area *saved_atemp;
1163fc804817Smillert 
116494e42df6Smillert 	cur_prompt = to;
116594e42df6Smillert 
116694e42df6Smillert 	switch (to) {
116794e42df6Smillert 	case PS1: /* command */
11684300ac9bSmillert 		ps1 = str_save(str_val(global("PS1")), ATEMP);
11694300ac9bSmillert 		saved_atemp = ATEMP;	/* ps1 is freed by substitute() */
11707cb960a2Sdownsj 		newenv(E_ERRH);
117169b9f96bSmillert 		if (sigsetjmp(e->jbuf, 0)) {
11727cb960a2Sdownsj 			prompt = safe_prompt;
1173dcacb757Sdownsj 			/* Don't print an error - assume it has already
1174dcacb757Sdownsj 			 * been printed.  Reason is we may have forked
1175dcacb757Sdownsj 			 * to run a command and the child may be
1176dcacb757Sdownsj 			 * unwinding its stack through this code as it
1177dcacb757Sdownsj 			 * exits.
1178dcacb757Sdownsj 			 */
1179e7b1290aSotto 		} else {
1180e7b1290aSotto 			/* expand \$ before other substitutions are done */
1181e7b1290aSotto 			char *tmp = special_prompt_expand(ps1);
1182e7b1290aSotto 			prompt = str_save(substitute(tmp, 0), saved_atemp);
1183e7b1290aSotto 		}
11840ee3f80eSotto 		quitenv(NULL);
11857cb960a2Sdownsj 		break;
11867cb960a2Sdownsj 	case PS2: /* command continuation */
11877cb960a2Sdownsj 		prompt = str_val(global("PS2"));
11887cb960a2Sdownsj 		break;
11897cb960a2Sdownsj 	}
11907cb960a2Sdownsj }
11917cb960a2Sdownsj 
1192eb6bc482Sderaadt static int
1193c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
11947cb960a2Sdownsj {
1195eb6bc482Sderaadt 	char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1196a1344090Sderaadt 	int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
119741c26999Sotto 	const char *cp = sp;
1198eb6bc482Sderaadt 	struct tm *tm;
1199eb6bc482Sderaadt 	time_t t;
1200eb6bc482Sderaadt 
1201eb6bc482Sderaadt 	if (*cp && cp[1] == '\r') {
1202eb6bc482Sderaadt 		delimiter = *cp;
1203eb6bc482Sderaadt 		cp += 2;
1204eb6bc482Sderaadt 	}
12057cb960a2Sdownsj 
12067cb960a2Sdownsj 	while (*cp != 0) {
1207a1344090Sderaadt 		delimitthis = 0;
1208eb6bc482Sderaadt 		if (indelimit && *cp != delimiter)
1209eb6bc482Sderaadt 			;
1210eb6bc482Sderaadt 		else if (*cp == '\n' || *cp == '\r') {
1211eb6bc482Sderaadt 			totlen = 0;
1212eb6bc482Sderaadt 			sp = cp + 1;
1213a1344090Sderaadt 		} else if (*cp == '\t') {
1214a1344090Sderaadt 			if (counting)
1215eb6bc482Sderaadt 				totlen = (totlen | 7) + 1;
1216a1344090Sderaadt 		} else if (*cp == delimiter) {
1217eb6bc482Sderaadt 			indelimit = !indelimit;
1218a1344090Sderaadt 			delimitthis = 1;
1219a1344090Sderaadt 		}
1220eb6bc482Sderaadt 
1221eb6bc482Sderaadt 		if (*cp == '\\') {
1222eb6bc482Sderaadt 			cp++;
1223eb6bc482Sderaadt 			if (!*cp)
1224eb6bc482Sderaadt 				break;
1225eb6bc482Sderaadt 			if (Flag(FSH))
1226eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1227eb6bc482Sderaadt 			else switch (*cp) {
1228eb6bc482Sderaadt 			case 'a':	/* '\' 'a' bell */
1229eb6bc482Sderaadt 				strbuf[0] = '\007';
1230eb6bc482Sderaadt 				strbuf[1] = '\0';
1231eb6bc482Sderaadt 				break;
1232eb6bc482Sderaadt 			case 'd':	/* '\' 'd' Dow Mon DD */
1233eb6bc482Sderaadt 				time(&t);
1234eb6bc482Sderaadt 				tm = localtime(&t);
1235eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%a %b %d", tm);
1236eb6bc482Sderaadt 				break;
1237eb6bc482Sderaadt 			case 'D': /* '\' 'D' '{' strftime format '}' */
1238eb6bc482Sderaadt 				p = strchr(cp + 2, '}');
1239eb6bc482Sderaadt 				if (cp[1] != '{' || p == NULL) {
1240eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1241eb6bc482Sderaadt 					    "\\%c", *cp);
1242eb6bc482Sderaadt 					break;
1243eb6bc482Sderaadt 				}
1244eb6bc482Sderaadt 				strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1245eb6bc482Sderaadt 				p = strchr(tmpbuf, '}');
1246eb6bc482Sderaadt 				if (p)
1247eb6bc482Sderaadt 					*p = '\0';
1248eb6bc482Sderaadt 				time(&t);
1249eb6bc482Sderaadt 				tm = localtime(&t);
1250eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, tmpbuf, tm);
1251eb6bc482Sderaadt 				cp = strchr(cp + 2, '}');
1252eb6bc482Sderaadt 				break;
1253eb6bc482Sderaadt 			case 'e':	/* '\' 'e' escape */
1254eb6bc482Sderaadt 				strbuf[0] = '\033';
1255eb6bc482Sderaadt 				strbuf[1] = '\0';
1256eb6bc482Sderaadt 				break;
1257eb6bc482Sderaadt 			case 'h':	/* '\' 'h' shortened hostname */
1258eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1259eb6bc482Sderaadt 				p = strchr(strbuf, '.');
1260eb6bc482Sderaadt 				if (p)
1261eb6bc482Sderaadt 					*p = '\0';
1262eb6bc482Sderaadt 				break;
1263eb6bc482Sderaadt 			case 'H':	/* '\' 'H' full hostname */
1264eb6bc482Sderaadt 				gethostname(strbuf, sizeof strbuf);
1265eb6bc482Sderaadt 				break;
1266eb6bc482Sderaadt 			case 'j':	/* '\' 'j' number of jobs */
1267eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1268eb6bc482Sderaadt 				    j_njobs());
1269eb6bc482Sderaadt 				break;
1270eb6bc482Sderaadt 			case 'l':	/* '\' 'l' basename of tty */
1271eb6bc482Sderaadt 				p = ttyname(0);
1272eb6bc482Sderaadt 				if (p)
1273eb6bc482Sderaadt 					p = basename(p);
1274eb6bc482Sderaadt 				if (p)
1275eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1276eb6bc482Sderaadt 				break;
1277eb6bc482Sderaadt 			case 'n':	/* '\' 'n' newline */
1278eb6bc482Sderaadt 				strbuf[0] = '\n';
1279eb6bc482Sderaadt 				strbuf[1] = '\0';
12805c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
12815c4a5744Sderaadt 				sp = cp + 1;
1282eb6bc482Sderaadt 				break;
12836aa3ef4bSdrahn 			case 'p':	/* '\' '$' $ or # */
12846aa3ef4bSdrahn 				strbuf[0] = ksheuid ? '$' : '#';
12856aa3ef4bSdrahn 				strbuf[1] = '\0';
12866aa3ef4bSdrahn 				break;
1287eb6bc482Sderaadt 			case 'r':	/* '\' 'r' return */
1288eb6bc482Sderaadt 				strbuf[0] = '\r';
1289eb6bc482Sderaadt 				strbuf[1] = '\0';
12905c4a5744Sderaadt 				totlen = 0;	/* reset for prompt re-print */
12915c4a5744Sderaadt 				sp = cp + 1;
1292eb6bc482Sderaadt 				break;
1293eb6bc482Sderaadt 			case 's':	/* '\' 's' basename $0 */
1294eb6bc482Sderaadt 				strlcpy(strbuf, kshname, sizeof strbuf);
1295eb6bc482Sderaadt 				break;
1296eb6bc482Sderaadt 			case 't':	/* '\' 't' 24 hour HH:MM:SS */
1297eb6bc482Sderaadt 				time(&t);
1298eb6bc482Sderaadt 				tm = localtime(&t);
1299eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%T", tm);
1300eb6bc482Sderaadt 				break;
1301eb6bc482Sderaadt 			case 'T':	/* '\' 'T' 12 hour HH:MM:SS */
1302eb6bc482Sderaadt 				time(&t);
1303eb6bc482Sderaadt 				tm = localtime(&t);
1304eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm);
1305eb6bc482Sderaadt 				break;
1306eb6bc482Sderaadt 			case '@':	/* '\' '@' 12 hour am/pm format */
1307eb6bc482Sderaadt 				time(&t);
1308eb6bc482Sderaadt 				tm = localtime(&t);
1309eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%r", tm);
1310eb6bc482Sderaadt 				break;
1311eb6bc482Sderaadt 			case 'A':	/* '\' 'A' 24 hour HH:MM */
1312eb6bc482Sderaadt 				time(&t);
1313eb6bc482Sderaadt 				tm = localtime(&t);
1314eb6bc482Sderaadt 				strftime(strbuf, sizeof strbuf, "%R", tm);
1315eb6bc482Sderaadt 				break;
1316eb6bc482Sderaadt 			case 'u':	/* '\' 'u' username */
1317f351fb1cSotto 				strlcpy(strbuf, username, sizeof strbuf);
1318eb6bc482Sderaadt 				break;
1319eb6bc482Sderaadt 			case 'v':	/* '\' 'v' version (short) */
1320eb6bc482Sderaadt 				p = strchr(ksh_version, ' ');
1321eb6bc482Sderaadt 				if (p)
1322eb6bc482Sderaadt 					p = strchr(p + 1, ' ');
1323eb6bc482Sderaadt 				if (p) {
1324eb6bc482Sderaadt 					p++;
1325eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1326eb6bc482Sderaadt 					p = strchr(strbuf, ' ');
1327eb6bc482Sderaadt 					if (p)
1328eb6bc482Sderaadt 						*p = '\0';
1329eb6bc482Sderaadt 				}
1330eb6bc482Sderaadt 				break;
1331eb6bc482Sderaadt 			case 'V':	/* '\' 'V' version (long) */
1332eb6bc482Sderaadt 				strlcpy(strbuf, ksh_version, sizeof strbuf);
1333eb6bc482Sderaadt 				break;
1334eb6bc482Sderaadt 			case 'w':	/* '\' 'w' cwd */
1335eb6bc482Sderaadt 				p = str_val(global("PWD"));
1336ad55f791Sderaadt 				n = strlen(str_val(global("HOME")));
1337ad55f791Sderaadt 				if (strcmp(p, "/") == 0) {
1338ad55f791Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1339ad55f791Sderaadt 				} else if (strcmp(p, str_val(global("HOME"))) == 0) {
1340eb6bc482Sderaadt 					strbuf[0] = '~';
1341eb6bc482Sderaadt 					strbuf[1] = '\0';
1342ad55f791Sderaadt 				} else if (strncmp(p, str_val(global("HOME")), n)
1343ad55f791Sderaadt 				    == 0 && p[n] == '/') {
1344ad55f791Sderaadt 					snprintf(strbuf, sizeof strbuf, "~/%s",
1345ad55f791Sderaadt 					    str_val(global("PWD")) + n + 1);
1346eb6bc482Sderaadt 				} else
1347eb6bc482Sderaadt 					strlcpy(strbuf, p, sizeof strbuf);
1348eb6bc482Sderaadt 				break;
1349eb6bc482Sderaadt 			case 'W':	/* '\' 'W' basename(cwd) */
1350eb6bc482Sderaadt 				p = str_val(global("PWD"));
135130e9b9efSokan 				if (strcmp(p, str_val(global("HOME"))) == 0) {
135230e9b9efSokan 					strbuf[0] = '~';
135330e9b9efSokan 					strbuf[1] = '\0';
135430e9b9efSokan 				} else
1355eb6bc482Sderaadt 					strlcpy(strbuf, basename(p), sizeof strbuf);
1356eb6bc482Sderaadt 				break;
1357e7b1290aSotto 			case '!':	/* '\' '!' history line number */
1358eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1359eb6bc482Sderaadt 				    source->line + 1);
1360eb6bc482Sderaadt 				break;
1361e7b1290aSotto 			case '#':	/* '\' '#' command line number */
1362eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%d",
1363e7b1290aSotto 				    source->line - source->cmd_offset + 1);
1364eb6bc482Sderaadt 				break;
1365eb6bc482Sderaadt 			case '0':	/* '\' '#' '#' ' #' octal numeric handling */
1366eb6bc482Sderaadt 			case '1':
1367eb6bc482Sderaadt 			case '2':
1368eb6bc482Sderaadt 			case '3':
1369eb6bc482Sderaadt 			case '4':
1370eb6bc482Sderaadt 			case '5':
1371eb6bc482Sderaadt 			case '6':
1372eb6bc482Sderaadt 			case '7':
1373eb6bc482Sderaadt 				if ((cp[1] > '7' || cp[1] < '0') ||
1374eb6bc482Sderaadt 				    (cp[2] > '7' || cp[2] < '0')) {
1375eb6bc482Sderaadt 					snprintf(strbuf, sizeof strbuf,
1376eb6bc482Sderaadt 					    "\\%c", *cp);
1377eb6bc482Sderaadt 					break;
1378eb6bc482Sderaadt 				}
1379064356ecSzhuk 				n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 +
1380064356ecSzhuk 				    (cp[2] - '0');
1381eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "%c", n);
1382eb6bc482Sderaadt 				cp += 2;
1383eb6bc482Sderaadt 				break;
1384eb6bc482Sderaadt 			case '\\':	/* '\' '\' */
1385eb6bc482Sderaadt 				strbuf[0] = '\\';
1386eb6bc482Sderaadt 				strbuf[1] = '\0';
1387eb6bc482Sderaadt 				break;
1388a1344090Sderaadt 			case '[': /* '\' '[' .... stop counting */
1389a1344090Sderaadt 				strbuf[0] = '\0';
1390a1344090Sderaadt 				counting = 0;
1391eb6bc482Sderaadt 				break;
1392a1344090Sderaadt 			case ']': /* '\' ']' restart counting */
1393a1344090Sderaadt 				strbuf[0] = '\0';
1394a1344090Sderaadt 				counting = 1;
1395eb6bc482Sderaadt 				break;
1396eb6bc482Sderaadt 
1397eb6bc482Sderaadt 			default:
1398eb6bc482Sderaadt 				snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1399eb6bc482Sderaadt 				break;
1400eb6bc482Sderaadt 			}
1401eb6bc482Sderaadt 			cp++;
1402eb6bc482Sderaadt 
1403eb6bc482Sderaadt 			str = strbuf;
1404eb6bc482Sderaadt 			len = strlen(str);
1405eb6bc482Sderaadt 			if (ntruncate) {
1406eb6bc482Sderaadt 				if (ntruncate >= len) {
1407eb6bc482Sderaadt 					ntruncate -= len;
1408eb6bc482Sderaadt 					continue;
1409eb6bc482Sderaadt 				}
1410eb6bc482Sderaadt 				str += ntruncate;
1411eb6bc482Sderaadt 				len -= ntruncate;
1412eb6bc482Sderaadt 				ntruncate = 0;
1413eb6bc482Sderaadt 			}
1414eb6bc482Sderaadt 			if (doprint)
1415eb6bc482Sderaadt 				shf_write(str, len, shl_out);
1416a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1417eb6bc482Sderaadt 				totlen += len;
1418eb6bc482Sderaadt 			continue;
1419eb6bc482Sderaadt 		} else if (*cp != '!')
14207cb960a2Sdownsj 			c = *cp++;
14217cb960a2Sdownsj 		else if (*++cp == '!')
14227cb960a2Sdownsj 			c = *cp++;
14237cb960a2Sdownsj 		else {
14247cb960a2Sdownsj 			char *p;
14257cb960a2Sdownsj 
14267cb960a2Sdownsj 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
14277cb960a2Sdownsj 			    source->line + 1);
14287cb960a2Sdownsj 			len = strlen(nbuf);
14297cb960a2Sdownsj 			if (ntruncate) {
14307cb960a2Sdownsj 				if (ntruncate >= len) {
14317cb960a2Sdownsj 					ntruncate -= len;
14327cb960a2Sdownsj 					continue;
14337cb960a2Sdownsj 				}
14347cb960a2Sdownsj 				p += ntruncate;
14357cb960a2Sdownsj 				len -= ntruncate;
14367cb960a2Sdownsj 				ntruncate = 0;
14377cb960a2Sdownsj 			}
1438eb6bc482Sderaadt 			if (doprint)
14397cb960a2Sdownsj 				shf_write(p, len, shl_out);
1440a1344090Sderaadt 			if (counting && !indelimit && !delimitthis)
1441eb6bc482Sderaadt 				totlen += len;
14427cb960a2Sdownsj 			continue;
14437cb960a2Sdownsj 		}
1444638b9e76Sbeck 		if (counting && ntruncate)
14457cb960a2Sdownsj 			--ntruncate;
1446eb6bc482Sderaadt 		else if (doprint) {
14477cb960a2Sdownsj 			shf_putc(c, shl_out);
14487cb960a2Sdownsj 		}
1449a1344090Sderaadt 		if (counting && !indelimit && !delimitthis)
1450eb6bc482Sderaadt 			totlen++;
1451eb6bc482Sderaadt 	}
1452eb6bc482Sderaadt 	if (doprint)
14537cb960a2Sdownsj 		shf_flush(shl_out);
1454eb6bc482Sderaadt 	if (spp)
1455eb6bc482Sderaadt 		*spp = sp;
1456eb6bc482Sderaadt 	return (totlen);
1457eb6bc482Sderaadt }
1458eb6bc482Sderaadt 
1459eb6bc482Sderaadt void
1460c5d5393cSotto pprompt(const char *cp, int ntruncate)
1461eb6bc482Sderaadt {
1462eb6bc482Sderaadt 	dopprompt(cp, ntruncate, NULL, 1);
1463eb6bc482Sderaadt }
1464eb6bc482Sderaadt 
1465eb6bc482Sderaadt int
1466c5d5393cSotto promptlen(const char *cp, const char **spp)
1467eb6bc482Sderaadt {
1468eb6bc482Sderaadt 	return dopprompt(cp, 0, spp, 0);
14697cb960a2Sdownsj }
14707cb960a2Sdownsj 
14717cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including
14727cb960a2Sdownsj  * the :[-+?=#%] or close-brace.
14737cb960a2Sdownsj  */
14747cb960a2Sdownsj static char *
1475c5d5393cSotto get_brace_var(XString *wsp, char *wp)
14767cb960a2Sdownsj {
14777cb960a2Sdownsj 	enum parse_state {
14787cb960a2Sdownsj 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
14797cb960a2Sdownsj 			   PS_NUMBER, PS_VAR1, PS_END
14807cb960a2Sdownsj 			 }
14817cb960a2Sdownsj 		state;
14827cb960a2Sdownsj 	char c;
14837cb960a2Sdownsj 
14847cb960a2Sdownsj 	state = PS_INITIAL;
14857cb960a2Sdownsj 	while (1) {
1486e55c1b2cSdownsj 		c = getsc();
14877cb960a2Sdownsj 		/* State machine to figure out where the variable part ends. */
14887cb960a2Sdownsj 		switch (state) {
14897cb960a2Sdownsj 		case PS_INITIAL:
14907cb960a2Sdownsj 			if (c == '#') {
14917cb960a2Sdownsj 				state = PS_SAW_HASH;
14927cb960a2Sdownsj 				break;
14937cb960a2Sdownsj 			}
14947eab881bSjaredy 			/* FALLTHROUGH */
14957cb960a2Sdownsj 		case PS_SAW_HASH:
14967cb960a2Sdownsj 			if (letter(c))
14977cb960a2Sdownsj 				state = PS_IDENT;
14987cb960a2Sdownsj 			else if (digit(c))
14997cb960a2Sdownsj 				state = PS_NUMBER;
15007cb960a2Sdownsj 			else if (ctype(c, C_VAR1))
15017cb960a2Sdownsj 				state = PS_VAR1;
15027cb960a2Sdownsj 			else
15037cb960a2Sdownsj 				state = PS_END;
15047cb960a2Sdownsj 			break;
15057cb960a2Sdownsj 		case PS_IDENT:
15067cb960a2Sdownsj 			if (!letnum(c)) {
15077cb960a2Sdownsj 				state = PS_END;
15087cb960a2Sdownsj 				if (c == '[') {
15097cb960a2Sdownsj 					char *tmp, *p;
15107cb960a2Sdownsj 
15117cb960a2Sdownsj 					if (!arraysub(&tmp))
15127cb960a2Sdownsj 						yyerror("missing ]\n");
15137cb960a2Sdownsj 					*wp++ = c;
15147cb960a2Sdownsj 					for (p = tmp; *p; ) {
15157cb960a2Sdownsj 						Xcheck(*wsp, wp);
15167cb960a2Sdownsj 						*wp++ = *p++;
15177cb960a2Sdownsj 					}
15187cb960a2Sdownsj 					afree(tmp, ATEMP);
1519e55c1b2cSdownsj 					c = getsc(); /* the ] */
15207cb960a2Sdownsj 				}
15217cb960a2Sdownsj 			}
15227cb960a2Sdownsj 			break;
15237cb960a2Sdownsj 		case PS_NUMBER:
15247cb960a2Sdownsj 			if (!digit(c))
15257cb960a2Sdownsj 				state = PS_END;
15267cb960a2Sdownsj 			break;
15277cb960a2Sdownsj 		case PS_VAR1:
15287cb960a2Sdownsj 			state = PS_END;
15297cb960a2Sdownsj 			break;
15307cb960a2Sdownsj 		case PS_END: /* keep gcc happy */
15317cb960a2Sdownsj 			break;
15327cb960a2Sdownsj 		}
15337cb960a2Sdownsj 		if (state == PS_END) {
15347cb960a2Sdownsj 			*wp++ = '\0';	/* end of variable part */
15357cb960a2Sdownsj 			ungetsc(c);
15367cb960a2Sdownsj 			break;
15377cb960a2Sdownsj 		}
15387cb960a2Sdownsj 		Xcheck(*wsp, wp);
15397cb960a2Sdownsj 		*wp++ = c;
15407cb960a2Sdownsj 	}
15417cb960a2Sdownsj 	return wp;
15427cb960a2Sdownsj }
15437cb960a2Sdownsj 
15447cb960a2Sdownsj /*
15457cb960a2Sdownsj  * Save an array subscript - returns true if matching bracket found, false
15467cb960a2Sdownsj  * if eof or newline was found.
15477cb960a2Sdownsj  * (Returned string double null terminated)
15487cb960a2Sdownsj  */
15497cb960a2Sdownsj static int
1550c5d5393cSotto arraysub(char **strp)
15517cb960a2Sdownsj {
15527cb960a2Sdownsj 	XString ws;
15537cb960a2Sdownsj 	char	*wp;
15547cb960a2Sdownsj 	char	c;
15557cb960a2Sdownsj 	int	depth = 1;	/* we are just past the initial [ */
15567cb960a2Sdownsj 
15577cb960a2Sdownsj 	Xinit(ws, wp, 32, ATEMP);
15587cb960a2Sdownsj 
15597cb960a2Sdownsj 	do {
1560e55c1b2cSdownsj 		c = getsc();
15617cb960a2Sdownsj 		Xcheck(ws, wp);
15627cb960a2Sdownsj 		*wp++ = c;
15637cb960a2Sdownsj 		if (c == '[')
15647cb960a2Sdownsj 			depth++;
15657cb960a2Sdownsj 		else if (c == ']')
15667cb960a2Sdownsj 			depth--;
15677cb960a2Sdownsj 	} while (depth > 0 && c && c != '\n');
15687cb960a2Sdownsj 
15697cb960a2Sdownsj 	*wp++ = '\0';
15707cb960a2Sdownsj 	*strp = Xclose(ws, wp);
15717cb960a2Sdownsj 
15727cb960a2Sdownsj 	return depth == 0 ? 1 : 0;
15737cb960a2Sdownsj }
15747cb960a2Sdownsj 
15757cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */
15767cb960a2Sdownsj static const char *
1577c5d5393cSotto ungetsc(int c)
15787cb960a2Sdownsj {
1579e55c1b2cSdownsj 	if (backslash_skip)
1580e55c1b2cSdownsj 		backslash_skip--;
15817cb960a2Sdownsj 	/* Don't unget eof... */
15827cb960a2Sdownsj 	if (source->str == null && c == '\0')
15837cb960a2Sdownsj 		return source->str;
15847cb960a2Sdownsj 	if (source->str > source->start)
15857cb960a2Sdownsj 		source->str--;
15867cb960a2Sdownsj 	else {
15877cb960a2Sdownsj 		Source *s;
15887cb960a2Sdownsj 
15897cb960a2Sdownsj 		s = pushs(SREREAD, source->areap);
1590e55c1b2cSdownsj 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1591e55c1b2cSdownsj 		s->start = s->str = s->ugbuf;
15927cb960a2Sdownsj 		s->next = source;
15937cb960a2Sdownsj 		source = s;
15947cb960a2Sdownsj 	}
15957cb960a2Sdownsj 	return source->str;
15967cb960a2Sdownsj }
15977cb960a2Sdownsj 
1598e55c1b2cSdownsj 
15997cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */
16007cb960a2Sdownsj static int
160169b9f96bSmillert getsc_bn(void)
16027cb960a2Sdownsj {
1603e55c1b2cSdownsj 	int c, c2;
1604e55c1b2cSdownsj 
1605e55c1b2cSdownsj 	if (ignore_backslash_newline)
1606e55c1b2cSdownsj 		return getsc_();
1607e55c1b2cSdownsj 
1608e55c1b2cSdownsj 	if (backslash_skip == 1) {
1609e55c1b2cSdownsj 		backslash_skip = 2;
1610e55c1b2cSdownsj 		return getsc_();
1611e55c1b2cSdownsj 	}
1612e55c1b2cSdownsj 
1613e55c1b2cSdownsj 	backslash_skip = 0;
16147cb960a2Sdownsj 
16157cb960a2Sdownsj 	while (1) {
16167cb960a2Sdownsj 		c = getsc_();
1617e55c1b2cSdownsj 		if (c == '\\') {
1618e55c1b2cSdownsj 			if ((c2 = getsc_()) == '\n')
16197cb960a2Sdownsj 				/* ignore the \newline; get the next char... */
1620e55c1b2cSdownsj 				continue;
1621e55c1b2cSdownsj 			ungetsc(c2);
1622e55c1b2cSdownsj 			backslash_skip = 1;
1623e55c1b2cSdownsj 		}
1624e55c1b2cSdownsj 		return c;
16257cb960a2Sdownsj 	}
16267cb960a2Sdownsj }
16273b015934Smillert 
16283b015934Smillert static Lex_state *
1629c5d5393cSotto push_state_(State_info *si, Lex_state *old_end)
16303b015934Smillert {
16313b015934Smillert 	Lex_state	*new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP);
16323b015934Smillert 
16333b015934Smillert 	new[0].ls_info.base = old_end;
16343b015934Smillert 	si->base = &new[0];
16353b015934Smillert 	si->end = &new[STATE_BSIZE];
16363b015934Smillert 	return &new[1];
16373b015934Smillert }
16383b015934Smillert 
16393b015934Smillert static Lex_state *
1640c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end)
16413b015934Smillert {
16423b015934Smillert 	Lex_state *old_base = si->base;
16433b015934Smillert 
16443b015934Smillert 	si->base = old_end->ls_info.base - STATE_BSIZE;
16453b015934Smillert 	si->end = old_end->ls_info.base;
16463b015934Smillert 
16473b015934Smillert 	afree(old_base, ATEMP);
16483b015934Smillert 
16497b425235Smillert 	return si->base + STATE_BSIZE - 1;
16503b015934Smillert }
1651