Home | History | Annotate | Line # | Download | only in ksh
lex.c revision 1.2
      1 /*	$NetBSD: lex.c,v 1.2 1997/01/12 19:12:04 tls Exp $	*/
      2 
      3 /*
      4  * lexical analysis and source input
      5  */
      6 
      7 #include "sh.h"
      8 #include <ctype.h>
      9 
     10 static void	readhere ARGS((struct ioword *iop));
     11 static int	getsc__ ARGS((void));
     12 static void	getsc_line ARGS((Source *s));
     13 static char	*get_brace_var ARGS((XString *wsp, char *wp));
     14 static int	arraysub ARGS((char **strp));
     15 static const char *ungetsc ARGS((int c));
     16 static int	getsc_bn ARGS((void));
     17 static void	gethere ARGS((void));
     18 
     19 static int backslash_skip;
     20 static int ignore_backslash_newline;
     21 
     22 /* optimized getsc_bn() */
     23 #define getsc()		(*source->str != '\0' && *source->str != '\\' \
     24 			 && !backslash_skip ? *source->str++ : getsc_bn())
     25 /* optimized getsc__() */
     26 #define	getsc_()	((*source->str != '\0') ? *source->str++ : getsc__())
     27 
     28 
     29 /*
     30  * Lexical analyzer
     31  *
     32  * tokens are not regular expressions, they are LL(1).
     33  * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
     34  * hence the state stack.
     35  */
     36 
     37 int
     38 yylex(cf)
     39 	int cf;
     40 {
     41 	register int c, state;
     42 	char states [64], *statep = states; /* XXX overflow check */
     43 	XString ws;		/* expandable output word */
     44 	register char *wp;	/* output word pointer */
     45 	register char *sp, *dp;
     46 	char UNINITIALIZED(*ddparen_start);
     47 	int istate;
     48 	int UNINITIALIZED(c2);
     49 	int UNINITIALIZED(nparen), UNINITIALIZED(csstate);
     50 	int UNINITIALIZED(ndparen);
     51 	int UNINITIALIZED(indquotes);
     52 
     53 
     54   Again:
     55 	Xinit(ws, wp, 64, ATEMP);
     56 
     57 	backslash_skip = 0;
     58 	ignore_backslash_newline = 0;
     59 
     60 	if (cf&ONEWORD)
     61 		istate = SWORD;
     62 #ifdef KSH
     63 	else if (cf&LETEXPR) {
     64 		*wp++ = OQUOTE;	 /* enclose arguments in (double) quotes */
     65 		istate = SDPAREN;
     66 		ndparen = 0;
     67 	}
     68 #endif /* KSH */
     69 	else {		/* normal lexing */
     70 		istate = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
     71 		while ((c = getsc()) == ' ' || c == '\t')
     72 			;
     73 		if (c == '#') {
     74 			ignore_backslash_newline++;
     75 			while ((c = getsc()) != '\0' && c != '\n')
     76 				;
     77 			ignore_backslash_newline--;
     78 		}
     79 		ungetsc(c);
     80 	}
     81 	if (source->flags & SF_ALIAS) {	/* trailing ' ' in alias definition */
     82 		source->flags &= ~SF_ALIAS;
     83 		/* In POSIX mode, a trailing space only counts if we are
     84 		 * parsing a simple command
     85 		 */
     86 		if (!Flag(FPOSIX) || (cf & CMDWORD))
     87 			cf |= ALIAS;
     88 	}
     89 
     90 	/* collect non-special or quoted characters to form word */
     91 	for (*statep = state = istate;
     92 	     !((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM)
     93 				      && ctype(c, C_LEX1))); )
     94 	{
     95 		Xcheck(ws, wp);
     96 		switch (state) {
     97 		  case SBASE:
     98 			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
     99 				*wp = EOS; /* temporary */
    100 				if (is_wdvarname(Xstring(ws, wp), FALSE))
    101 				{
    102 					char *p, *tmp;
    103 
    104 					if (arraysub(&tmp)) {
    105 						*wp++ = CHAR;
    106 						*wp++ = c;
    107 						for (p = tmp; *p; ) {
    108 							Xcheck(ws, wp);
    109 							*wp++ = CHAR;
    110 							*wp++ = *p++;
    111 						}
    112 						afree(tmp, ATEMP);
    113 						break;
    114 					} else {
    115 						Source *s;
    116 
    117 						s = pushs(SREREAD,
    118 							  source->areap);
    119 						s->start = s->str
    120 							= s->u.freeme = tmp;
    121 						s->next = source;
    122 						source = s;
    123 					}
    124 				}
    125 				*wp++ = CHAR;
    126 				*wp++ = c;
    127 				break;
    128 			}
    129 			/* fall through.. */
    130 		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */
    131 #ifdef KSH
    132 			if (c == '*' || c == '@' || c == '+' || c == '?'
    133 			    || c == '!')
    134 			{
    135 				c2 = getsc();
    136 				if (c2 == '(' /*)*/ ) {
    137 					*wp++ = OPAT;
    138 					*wp++ = c;
    139 					*++statep = state = SPATTERN;
    140 					break;
    141 				}
    142 				ungetsc(c2);
    143 			}
    144 #endif /* KSH */
    145 			/* fall through.. */
    146 		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */
    147 			switch (c) {
    148 			  case '\\':
    149 				c = getsc();
    150 #ifdef OS2
    151 				if (isalnum(c)) {
    152 					*wp++ = CHAR, *wp++ = '\\';
    153 					*wp++ = CHAR, *wp++ = c;
    154 				} else
    155 #endif
    156 				if (c) /* trailing \ is lost */
    157 					*wp++ = QCHAR, *wp++ = c;
    158 				break;
    159 			  case '\'':
    160 				*++statep = state = SSQUOTE;
    161 				*wp++ = OQUOTE;
    162 				ignore_backslash_newline++;
    163 				break;
    164 			  case '"':
    165 				*++statep = state = SDQUOTE;
    166 				*wp++ = OQUOTE;
    167 				break;
    168 			  default:
    169 				goto Subst;
    170 			}
    171 			break;
    172 
    173 		  Subst:
    174 			switch (c) {
    175 			  case '\\':
    176 				c = getsc();
    177 				switch (c) {
    178 				  case '"': case '\\':
    179 				  case '$': case '`':
    180 					*wp++ = QCHAR, *wp++ = c;
    181 					break;
    182 				  default:
    183 					Xcheck(ws, wp);
    184 					if (c) { /* trailing \ is lost */
    185 						*wp++ = CHAR, *wp++ = '\\';
    186 						*wp++ = CHAR, *wp++ = c;
    187 					}
    188 					break;
    189 				}
    190 				break;
    191 			  case '$':
    192 				c = getsc();
    193 				if (c == '(') /*)*/ {
    194 					c = getsc();
    195 					if (c == '(') /*)*/ {
    196 						*++statep = state = SDDPAREN;
    197 						nparen = 2;
    198 						ddparen_start = wp;
    199 						*wp++ = EXPRSUB;
    200 					} else {
    201 						ungetsc(c);
    202 						*++statep = state = SPAREN;
    203 						nparen = 1;
    204 						csstate = 0;
    205 						*wp++ = COMSUB;
    206 					}
    207 				} else if (c == '{') /*}*/ {
    208 					*wp++ = OSUBST;
    209 					wp = get_brace_var(&ws, wp);
    210 					/* If this is a trim operation,
    211 					 * wrap @(...) around the pattern
    212 					 * (allows easy handling of ${a#b|c})
    213 					 */
    214 					c = getsc();
    215 					if (c == '#' || c == '%') {
    216 						*wp++ = CHAR, *wp++ = c;
    217 						if ((c2 = getsc()) == c)
    218 							*wp++ = CHAR, *wp++ = c;
    219 						else
    220 							ungetsc(c2);
    221 						*wp++ = OPAT, *wp++ = '@';
    222 						*++statep = state = STBRACE;
    223 					} else {
    224 						ungetsc(c);
    225 						*++statep = state = SBRACE;
    226 					}
    227 				} else if (ctype(c, C_ALPHA)) {
    228 					*wp++ = OSUBST;
    229 					do {
    230 						Xcheck(ws, wp);
    231 						*wp++ = c;
    232 						c = getsc();
    233 					} while (ctype(c, C_ALPHA|C_DIGIT));
    234 					*wp++ = '\0';
    235 					*wp++ = CSUBST;
    236 					ungetsc(c);
    237 				} else if (ctype(c, C_DIGIT|C_VAR1)) {
    238 					Xcheck(ws, wp);
    239 					*wp++ = OSUBST;
    240 					*wp++ = c;
    241 					*wp++ = '\0';
    242 					*wp++ = CSUBST;
    243 				} else {
    244 					*wp++ = CHAR, *wp++ = '$';
    245 					ungetsc(c);
    246 				}
    247 				break;
    248 			  case '`':
    249 				*++statep = state = SBQUOTE;
    250 				*wp++ = COMSUB;
    251 				/* Need to know if we are inside double quotes
    252 				 * since sh/at&t-ksh translate the \" to " in
    253 				 * "`..\"..`".
    254 				 */
    255 				indquotes = 0;
    256 				if (!Flag(FPOSIX))
    257 					for (sp = statep; sp > states; --sp)
    258 						if (*sp == SDQUOTE)
    259 							indquotes = 1;
    260 				break;
    261 			  default:
    262 				*wp++ = CHAR, *wp++ = c;
    263 			}
    264 			break;
    265 
    266 		  case SSQUOTE:
    267 			if (c == '\'') {
    268 				state = *--statep;
    269 				*wp++ = CQUOTE;
    270 				ignore_backslash_newline--;
    271 			} else
    272 				*wp++ = QCHAR, *wp++ = c;
    273 			break;
    274 
    275 		  case SDQUOTE:
    276 			if (c == '"') {
    277 				state = *--statep;
    278 				*wp++ = CQUOTE;
    279 			} else
    280 				goto Subst;
    281 			break;
    282 
    283 		  case SPAREN: /* $( .. ) */
    284 			/* todo: deal with $(...) quoting properly
    285 			 * kludge to partly fake quoting inside $(..): doesn't
    286 			 * really work because nested $(..) or ${..} inside
    287 			 * double quotes aren't dealt with.
    288 			 */
    289 			switch (csstate) {
    290 			  case 0: /* normal */
    291 				switch (c) {
    292 				  case '(':
    293 					nparen++;
    294 					break;
    295 				  case ')':
    296 					nparen--;
    297 					break;
    298 				  case '\\':
    299 					csstate = 1;
    300 					break;
    301 				  case '"':
    302 					csstate = 2;
    303 					break;
    304 				  case '\'':
    305 					csstate = 4;
    306 					ignore_backslash_newline++;
    307 					break;
    308 				}
    309 				break;
    310 
    311 			  case 1: /* backslash in normal mode */
    312 			  case 3: /* backslash in double quotes */
    313 				--csstate;
    314 				break;
    315 
    316 			  case 2: /* double quotes */
    317 				if (c == '"')
    318 					csstate = 0;
    319 				else if (c == '\\')
    320 					csstate = 3;
    321 				break;
    322 
    323 			  case 4: /* single quotes */
    324 				if (c == '\'') {
    325 					csstate = 0;
    326 					ignore_backslash_newline--;
    327 				}
    328 				break;
    329 			}
    330 			if (nparen == 0) {
    331 				state = *--statep;
    332 				*wp++ = 0; /* end of COMSUB */
    333 			} else
    334 				*wp++ = c;
    335 			break;
    336 
    337 		  case SDDPAREN: /* $(( .. )) */
    338 			/* todo: deal with $((...); (...)) properly */
    339 			/* XXX should nest using existing state machine
    340 			 *     (embed "..", $(...), etc.) */
    341 			if (c == '(')
    342 				nparen++;
    343 			else if (c == ')') {
    344 				nparen--;
    345 				if (nparen == 1) {
    346 					/*(*/
    347 					if ((c2 = getsc()) == ')') {
    348 						state = *--statep;
    349 						*wp++ = 0; /* end of EXPRSUB */
    350 						break;
    351 					} else {
    352 						ungetsc(c2);
    353 						/* mismatched parenthesis -
    354 						 * assume we were really
    355 						 * parsing a $(..) expression
    356 						 */
    357 						memmove(ddparen_start + 1,
    358 							ddparen_start,
    359 							wp - ddparen_start);
    360 						*ddparen_start++ = COMSUB;
    361 						*ddparen_start = '('; /*)*/
    362 						wp++;
    363 						csstate = 0;
    364 						*statep = state = SPAREN;
    365 					}
    366 				}
    367 			}
    368 			*wp++ = c;
    369 			break;
    370 
    371 		  case SBRACE:
    372 			/*{*/
    373 			if (c == '}') {
    374 				state = *--statep;
    375 				*wp++ = CSUBST;
    376 			} else
    377 				goto Sbase1;
    378 			break;
    379 
    380 		  case STBRACE:
    381 			/* same as SBRACE, except | is saved as SPAT and
    382 			 * CPAT is added at the end.
    383 			 */
    384 			/*{*/
    385 			if (c == '}') {
    386 				state = *--statep;
    387 				*wp++ = CPAT;
    388 				*wp++ = CSUBST;
    389 			} else if (c == '|') {
    390 				*wp++ = SPAT;
    391 			} else
    392 				goto Sbase1;
    393 			break;
    394 
    395 		  case SBQUOTE:
    396 			if (c == '`') {
    397 				*wp++ = 0;
    398 				state = *--statep;
    399 			} else if (c == '\\') {
    400 				switch (c = getsc()) {
    401 				  case '\\':
    402 				  case '$': case '`':
    403 					*wp++ = c;
    404 					break;
    405 				  case '"':
    406 					if (indquotes) {
    407 						*wp++ = c;
    408 						break;
    409 					}
    410 					/* fall through.. */
    411 				  default:
    412 					if (c) { /* trailing \ is lost */
    413 						*wp++ = '\\';
    414 						*wp++ = c;
    415 					}
    416 					break;
    417 				}
    418 			} else
    419 				*wp++ = c;
    420 			break;
    421 
    422 		  case SWORD:	/* ONEWORD */
    423 			goto Subst;
    424 
    425 #ifdef KSH
    426 		  case SDPAREN:	/* LETEXPR: (( ... )) */
    427 			/*(*/
    428 			if (c == ')') {
    429 				if (ndparen > 0)
    430 				    --ndparen;
    431 				/*(*/
    432 				else if ((c2 = getsc()) == ')') {
    433 					c = 0;
    434 					*wp++ = CQUOTE;
    435 					goto Done;
    436 				} else
    437 					ungetsc(c2);
    438 			} else if (c == '(')
    439 				/* parenthesis inside quotes and backslashes
    440 				 * are lost, but at&t ksh doesn't count them
    441 				 * either
    442 				 */
    443 				++ndparen;
    444 			goto Sbase2;
    445 #endif /* KSH */
    446 
    447 		  case SHEREDELIM:	/* <<,<<- delimiter */
    448 			/* XXX chuck this state (and the next) - use
    449 			 * the existing states ($ and \`..` should be
    450 			 * stripped of their specialness after the
    451 			 * fact).
    452 			 */
    453 			/* here delimiters need a special case since
    454 			 * $ and `..` are not to be treated specially
    455 			 */
    456 			if (c == '\\') {
    457 				c = getsc();
    458 				if (c) { /* trailing \ is lost */
    459 					*wp++ = QCHAR;
    460 					*wp++ = c;
    461 				}
    462 			} else if (c == '\'') {
    463 				*++statep = state = SSQUOTE;
    464 				*wp++ = OQUOTE;
    465 				ignore_backslash_newline++;
    466 			} else if (c == '"') {
    467 				state = SHEREDQUOTE;
    468 				*wp++ = OQUOTE;
    469 			} else {
    470 				*wp++ = CHAR;
    471 				*wp++ = c;
    472 			}
    473 			break;
    474 
    475 		  case SHEREDQUOTE:	/* " in <<,<<- delimiter */
    476 			if (c == '"') {
    477 				*wp++ = CQUOTE;
    478 				state = SHEREDELIM;
    479 			} else {
    480 				if (c == '\\') {
    481 					switch (c = getsc()) {
    482 					  case '\\': case '"':
    483 					  case '$': case '`':
    484 						break;
    485 					  default:
    486 						if (c) { /* trailing \ lost */
    487 							*wp++ = CHAR;
    488 							*wp++ = '\\';
    489 						}
    490 						break;
    491 					}
    492 				}
    493 				*wp++ = CHAR;
    494 				*wp++ = c;
    495 			}
    496 			break;
    497 
    498 		  case SPATTERN:	/* in *(...|...) pattern (*+?@!) */
    499 			if ( /*(*/ c == ')') {
    500 				*wp++ = CPAT;
    501 				state = *--statep;
    502 			} else if (c == '|')
    503 				*wp++ = SPAT;
    504 			else
    505 				goto Sbase1;
    506 			break;
    507 		}
    508 	}
    509 Done:
    510 	Xcheck(ws, wp);
    511 	if (state != istate)
    512 		yyerror("no closing quote\n");
    513 
    514 	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
    515 	if (state == SHEREDELIM)
    516 		state = SBASE;
    517 
    518 	if ((c == '<' || c == '>') && state == SBASE) {
    519 		char *cp = Xstring(ws, wp);
    520 		if (Xlength(ws, wp) == 2 && cp[0] == CHAR && digit(cp[1])) {
    521 			wp = cp; /* throw away word */
    522 			c2/*unit*/ = cp[1] - '0';
    523 		} else
    524 			c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */
    525 	}
    526 
    527 	if (wp == Xstring(ws, wp) && state == SBASE) {
    528 		Xfree(ws, wp);	/* free word */
    529 		/* no word, process LEX1 character */
    530 		switch (c) {
    531 		  default:
    532 			return c;
    533 
    534 		  case '|':
    535 		  case '&':
    536 		  case ';':
    537 			if ((c2 = getsc()) == c)
    538 				c = (c == ';') ? BREAK :
    539 				    (c == '|') ? LOGOR :
    540 				    (c == '&') ? LOGAND :
    541 				    YYERRCODE;
    542 #ifdef KSH
    543 			else if (c == '|' && c2 == '&')
    544 				c = COPROC;
    545 #endif /* KSH */
    546 			else
    547 				ungetsc(c2);
    548 			return c;
    549 
    550 		  case '>':
    551 		  case '<': {
    552 			register struct ioword *iop;
    553 
    554 			iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);
    555 			iop->unit = c2/*unit*/;
    556 
    557 			c2 = getsc();
    558 			/* <<, >>, <> are ok, >< is not */
    559 			if (c == c2 || (c == '<' && c2 == '>')) {
    560 				iop->flag = c == c2 ?
    561 					  (c == '>' ? IOCAT : IOHERE) : IORDWR;
    562 				if (iop->flag == IOHERE)
    563 					if ((c2 = getsc()) == '-')
    564 						iop->flag |= IOSKIP;
    565 					else
    566 						ungetsc(c2);
    567 			} else if (c2 == '&')
    568 				iop->flag = IODUP | (c == '<' ? IORDUP : 0);
    569 			else {
    570 				iop->flag = c == '>' ? IOWRITE : IOREAD;
    571 				if (c == '>' && c2 == '|')
    572 					iop->flag |= IOCLOB;
    573 				else
    574 					ungetsc(c2);
    575 			}
    576 
    577 			iop->name = (char *) 0;
    578 			iop->delim = (char *) 0;
    579 			yylval.iop = iop;
    580 			return REDIR;
    581 		    }
    582 		  case '\n':
    583 			gethere();
    584 			if (cf & CONTIN)
    585 				goto Again;
    586 			return c;
    587 
    588 		  case '(':  /*)*/
    589 #ifdef KSH
    590 			if ((c2 = getsc()) == '(') /*)*/
    591 				c = MDPAREN;
    592 			else
    593 				ungetsc(c2);
    594 #endif /* KSH */
    595 			return c;
    596 		  /*(*/
    597 		  case ')':
    598 			return c;
    599 		}
    600 	}
    601 
    602 	*wp++ = EOS;		/* terminate word */
    603 	yylval.cp = Xclose(ws, wp);
    604 	if (state == SWORD
    605 #ifdef KSH
    606 		|| state == SDPAREN
    607 #endif /* KSH */
    608 		)	/* ONEWORD? */
    609 		return LWORD;
    610 	ungetsc(c);		/* unget terminator */
    611 
    612 	/* copy word to unprefixed string ident */
    613 	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
    614 		*dp++ = *sp++;
    615 	/* Make sure the ident array stays '\0' paded */
    616 	memset(dp, 0, (ident+IDENT) - dp + 1);
    617 	if (c != EOS)
    618 		*ident = '\0';	/* word is not unquoted */
    619 
    620 	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
    621 		struct tbl *p;
    622 		int h = hash(ident);
    623 
    624 		/* { */
    625 		if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h))
    626 		    && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}'))
    627 		{
    628 			afree(yylval.cp, ATEMP);
    629 			return p->val.i;
    630 		}
    631 		if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h))
    632 		    && (p->flag & ISSET))
    633 		{
    634 			register Source *s;
    635 
    636 			for (s = source; s->type == SALIAS; s = s->next)
    637 				if (s->u.tblp == p)
    638 					return LWORD;
    639 			/* push alias expansion */
    640 			s = pushs(SALIAS, source->areap);
    641 			s->start = s->str = p->val.s;
    642 			s->u.tblp = p;
    643 			s->next = source;
    644 			source = s;
    645 			afree(yylval.cp, ATEMP);
    646 			goto Again;
    647 		}
    648 	}
    649 
    650 	return LWORD;
    651 }
    652 
    653 static void
    654 gethere()
    655 {
    656 	register struct ioword **p;
    657 
    658 	for (p = heres; p < herep; p++)
    659 		readhere(*p);
    660 	herep = heres;
    661 }
    662 
    663 /*
    664  * read "<<word" text into temp file
    665  */
    666 
    667 static void
    668 readhere(iop)
    669 	register struct ioword *iop;
    670 {
    671 	struct shf *volatile shf;
    672 	struct temp *h;
    673 	register int c;
    674 	char *volatile eof;
    675 	char *eofp;
    676 	int skiptabs;
    677 	int i;
    678 
    679 	eof = evalstr(iop->delim, 0);
    680 
    681 	if (e->flags & EF_FUNC_PARSE) {
    682 		h = maketemp(APERM);
    683 		h->next = func_heredocs;
    684 		func_heredocs = h;
    685 	} else {
    686 		h = maketemp(ATEMP);
    687 		h->next = e->temps;
    688 		e->temps = h;
    689 	}
    690 	iop->name = h->name;
    691 	if (!(shf = h->shf))
    692 		yyerror("cannot create temporary file %s - %s\n",
    693 			h->name, strerror(errno));
    694 
    695 	newenv(E_ERRH);
    696 	i = ksh_sigsetjmp(e->jbuf, 0);
    697 	if (i) {
    698 		quitenv();
    699 		shf_close(shf);
    700 		unwind(i);
    701 	}
    702 
    703 	if (!(iop->flag & IOEVAL))
    704 		ignore_backslash_newline++;
    705 
    706 	for (;;) {
    707 		eofp = eof;
    708 		skiptabs = iop->flag & IOSKIP;
    709 		while ((c = getsc()) != 0) {
    710 			if (skiptabs) {
    711 				if (c == '\t')
    712 					continue;
    713 				skiptabs = 0;
    714 			}
    715 			if (c != *eofp)
    716 				break;
    717 			eofp++;
    718 		}
    719 		/* Allow EOF here so commands with out trailing newlines
    720 		 * will work (eg, ksh -c '...', $(...), etc).
    721 		 */
    722 		if (*eofp == '\0' && (c == 0 || c == '\n'))
    723 			break;
    724 		ungetsc(c);
    725 		shf_write(eof, eofp - eof, shf);
    726 		while ((c = getsc()) != '\n') {
    727 			if (c == 0)
    728 				yyerror("here document `%s' unclosed\n", eof);
    729 			shf_putc(c, shf);
    730 		}
    731 		shf_putc(c, shf);
    732 	}
    733 	shf_flush(shf);
    734 	if (shf_error(shf))
    735 		yyerror("error saving here document `%s': %s\n",
    736 			eof, strerror(shf_errno(shf)));
    737 	/*XXX add similar checks for write errors everywhere */
    738 	quitenv();
    739 	shf_close(shf);
    740 	if (!(iop->flag & IOEVAL))
    741 		ignore_backslash_newline--;
    742 }
    743 
    744 void
    745 #ifdef HAVE_PROTOTYPES
    746 yyerror(const char *fmt, ...)
    747 #else
    748 yyerror(fmt, va_alist)
    749 	const char *fmt;
    750 	va_dcl
    751 #endif
    752 {
    753 	va_list va;
    754 
    755 	yynerrs++;
    756 	/* pop aliases and re-reads */
    757 	while (source->type == SALIAS || source->type == SREREAD)
    758 		source = source->next;
    759 	source->str = null;	/* zap pending input */
    760 
    761 	error_prefix(TRUE);
    762 	SH_VA_START(va, fmt);
    763 	shf_vfprintf(shl_out, fmt, va);
    764 	va_end(va);
    765 	errorf(null);
    766 }
    767 
    768 /*
    769  * input for yylex with alias expansion
    770  */
    771 
    772 Source *
    773 pushs(type, areap)
    774 	int type;
    775 	Area *areap;
    776 {
    777 	register Source *s;
    778 
    779 	s = (Source *) alloc(sizeof(Source), areap);
    780 	s->type = type;
    781 	s->str = null;
    782 	s->start = NULL;
    783 	s->line = 0;
    784 	s->errline = 0;
    785 	s->file = NULL;
    786 	s->flags = 0;
    787 	s->next = NULL;
    788 	s->areap = areap;
    789 	if (type == SFILE || type == SSTDIN) {
    790 		char *dummy;
    791 		Xinit(s->xs, dummy, 256, s->areap);
    792 	} else
    793 		memset(&s->xs, 0, sizeof(s->xs));
    794 	return s;
    795 }
    796 
    797 static int
    798 getsc__()
    799 {
    800 	register Source *s = source;
    801 	register int c;
    802 
    803 	while ((c = *s->str++) == 0) {
    804 		s->str = NULL;		/* return 0 for EOF by default */
    805 		switch (s->type) {
    806 		  case SEOF:
    807 			s->str = null;
    808 			return 0;
    809 
    810 		  case SSTDIN:
    811 		  case SFILE:
    812 			getsc_line(s);
    813 			break;
    814 
    815 		  case SWSTR:
    816 			break;
    817 
    818 		  case SSTRING:
    819 			break;
    820 
    821 		  case SWORDS:
    822 			s->start = s->str = *s->u.strv++;
    823 			s->type = SWORDSEP;
    824 			break;
    825 
    826 		  case SWORDSEP:
    827 			if (*s->u.strv == NULL) {
    828 				s->start = s->str = newline;
    829 				s->type = SEOF;
    830 			} else {
    831 				s->start = s->str = space;
    832 				s->type = SWORDS;
    833 			}
    834 			break;
    835 
    836 		  case SALIAS:
    837 			if (s->flags & SF_ALIASEND) {
    838 				/* pass on an unused SF_ALIAS flag */
    839 				source = s->next;
    840 				source->flags |= s->flags & SF_ALIAS;
    841 				s = source;
    842 			} else if (*s->u.tblp->val.s
    843 				 && isspace(strchr(s->u.tblp->val.s, 0)[-1]))
    844 			{
    845 				source = s = s->next;	/* pop source stack */
    846 				/* Note that this alias ended with a space,
    847 				 * enabling alias expansion on the following
    848 				 * word.
    849 				 */
    850 				s->flags |= SF_ALIAS;
    851 			} else {
    852 				/* At this point, we need to keep the current
    853 				 * alias in the source list so recursive
    854 				 * aliases can be detected and we also need
    855 				 * to return the next character.  Do this
    856 				 * by temporarily popping the alias to get
    857 				 * the next character and then put it back
    858 				 * in the source list with the SF_ALIASEND
    859 				 * flag set.
    860 				 */
    861 				source = s->next;	/* pop source stack */
    862 				source->flags |= s->flags & SF_ALIAS;
    863 				c = getsc__();
    864 				if (c) {
    865 					s->flags |= SF_ALIASEND;
    866 					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
    867 					s->start = s->str = s->ugbuf;
    868 					s->next = source;
    869 					source = s;
    870 				} else {
    871 					s = source;
    872 					/* avoid reading eof twice */
    873 					s->str = NULL;
    874 					break;
    875 				}
    876 			}
    877 			continue;
    878 
    879 		  case SREREAD:
    880 			if (s->start != s->ugbuf) /* yuck */
    881 				afree(s->u.freeme, ATEMP);
    882 			source = s = s->next;
    883 			continue;
    884 		}
    885 		if (s->str == NULL) {
    886 			s->type = SEOF;
    887 			s->start = s->str = null;
    888 			return '\0';
    889 		}
    890 		if (s->flags & SF_ECHO) {
    891 			shf_puts(s->str, shl_out);
    892 			shf_flush(shl_out);
    893 		}
    894 	}
    895 	return c;
    896 }
    897 
    898 static void
    899 getsc_line(s)
    900 	Source *s;
    901 {
    902 	char *xp = Xstring(s->xs, xp);
    903 	int interactive = Flag(FTALKING) && s->type == SSTDIN;
    904 	int have_tty = interactive && (s->flags & SF_TTY);
    905 
    906 	/* Done here to ensure nothing odd happens when a timeout occurs */
    907 	XcheckN(s->xs, xp, LINE);
    908 	*xp = '\0';
    909 	s->start = s->str = xp;
    910 
    911 #ifdef KSH
    912 	if (have_tty && ksh_tmout) {
    913 		ksh_tmout_state = TMOUT_READING;
    914 		alarm(ksh_tmout);
    915 	}
    916 #endif /* KSH */
    917 #ifdef EDIT
    918 	if (have_tty && (0
    919 # ifdef VI
    920 			 || Flag(FVI)
    921 # endif /* VI */
    922 # ifdef EMACS
    923 			 || Flag(FEMACS) || Flag(FGMACS)
    924 # endif /* EMACS */
    925 		))
    926 	{
    927 		int nread;
    928 
    929 		nread = x_read(xp, LINE);
    930 		if (nread < 0)	/* read error */
    931 			nread = 0;
    932 		xp[nread] = '\0';
    933 		xp += nread;
    934 	}
    935 	else
    936 #endif /* EDIT */
    937 	{
    938 		if (interactive) {
    939 			pprompt(prompt, 0);
    940 #ifdef OS2
    941 			setmode (0, O_TEXT);
    942 #endif /* OS2 */
    943 		} else
    944 			s->line++;
    945 
    946 		while (1) {
    947 			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
    948 
    949 			if (!p && shf_error(s->u.shf)
    950 			    && shf_errno(s->u.shf) == EINTR)
    951 			{
    952 				shf_clearerr(s->u.shf);
    953 				if (trap)
    954 					runtraps(0);
    955 				continue;
    956 			}
    957 			if (!p || (xp = p, xp[-1] == '\n'))
    958 				break;
    959 			/* double buffer size */
    960 			xp++; /* move past null so doubling works... */
    961 			XcheckN(s->xs, xp, Xlength(s->xs, xp));
    962 			xp--; /* ...and move back again */
    963 		}
    964 #ifdef OS2
    965 		setmode(0, O_BINARY);
    966 #endif /* OS2 */
    967 		/* flush any unwanted input so other programs/builtins
    968 		 * can read it.  Not very optimal, but less error prone
    969 		 * than flushing else where, dealing with redirections,
    970 		 * etc..
    971 		 * todo: reduce size of shf buffer (~128?) if SSTDIN
    972 		 */
    973 		if (s->type == SSTDIN)
    974 			shf_flush(s->u.shf);
    975 	}
    976 	/* XXX: temporary kludge to restore source after a
    977 	 * trap may have been executed.
    978 	 */
    979 	source = s;
    980 #ifdef KSH
    981 	if (have_tty && ksh_tmout)
    982 	{
    983 		ksh_tmout_state = TMOUT_EXECUTING;
    984 		alarm(0);
    985 	}
    986 #endif /* KSH */
    987 	s->start = s->str = Xstring(s->xs, xp);
    988 	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
    989 	/* Note: if input is all nulls, this is not eof */
    990 	if (Xlength(s->xs, xp) == 0) { /* EOF */
    991 		if (s->type == SFILE)
    992 			shf_fdclose(s->u.shf);
    993 		s->str = NULL;
    994 	} else if (interactive) {
    995 #ifdef HISTORY
    996 		char *p = Xstring(s->xs, xp);
    997 		if (cur_prompt == PS1)
    998 			while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
    999 				p++;
   1000 		if (*p) {
   1001 # ifdef EASY_HISTORY
   1002 			if (cur_prompt == PS2)
   1003 				histappend(Xstring(s->xs, xp), 1);
   1004 			else
   1005 # endif /* EASY_HISTORY */
   1006 			{
   1007 				s->line++;
   1008 				histsave(s->line, s->str, 1);
   1009 			}
   1010 		}
   1011 #endif /* HISTORY */
   1012 	}
   1013 	if (interactive)
   1014 		set_prompt(PS2, (Source *) 0);
   1015 }
   1016 
   1017 void
   1018 set_prompt(to, s)
   1019 	int to;
   1020 	Source *s;
   1021 {
   1022 	cur_prompt = to;
   1023 
   1024 	switch (to) {
   1025 	case PS1: /* command */
   1026 #ifdef KSH
   1027 		/* Substitute ! and !! here, before substitutions are done
   1028 		 * so ! in expanded variables are not expanded.
   1029 		 * NOTE: this is not what at&t ksh does (it does it after
   1030 		 * substitutions, POSIX doesn't say which is to be done.
   1031 		 */
   1032 		{
   1033 			struct shf *shf;
   1034 			char *ps1;
   1035 			Area *saved_atemp;
   1036 
   1037 			ps1 = str_val(global("PS1"));
   1038 			shf = shf_sopen((char *) 0, strlen(ps1) * 2,
   1039 				SHF_WR | SHF_DYNAMIC, (struct shf *) 0);
   1040 			while (*ps1) {
   1041 				if (*ps1 != '!' || *++ps1 == '!')
   1042 					shf_putchar(*ps1++, shf);
   1043 				else
   1044 					shf_fprintf(shf, "%d",
   1045 						s ? s->line + 1 : 0);
   1046 			}
   1047 			ps1 = shf_sclose(shf);
   1048 			saved_atemp = ATEMP;
   1049 			newenv(E_ERRH);
   1050 			if (ksh_sigsetjmp(e->jbuf, 0)) {
   1051 				prompt = safe_prompt;
   1052 				/* Don't print an error - assume it has already
   1053 				 * been printed.  Reason is we may have forked
   1054 				 * to run a command and the child may be
   1055 				 * unwinding its stack through this code as it
   1056 				 * exits.
   1057 				 */
   1058 			} else
   1059 				prompt = str_save(substitute(ps1, 0),
   1060 						 saved_atemp);
   1061 			quitenv();
   1062 		}
   1063 #else /* KSH */
   1064 		prompt = str_val(global("PS1"));
   1065 #endif /* KSH */
   1066 		break;
   1067 
   1068 	case PS2: /* command continuation */
   1069 		prompt = str_val(global("PS2"));
   1070 		break;
   1071 	}
   1072 }
   1073 
   1074 /* See also related routine, promptlen() in edit.c */
   1075 void
   1076 pprompt(cp, ntruncate)
   1077 	const char *cp;
   1078 	int ntruncate;
   1079 {
   1080 #if 0
   1081 	char nbuf[32];
   1082 	int c;
   1083 
   1084 	while (*cp != 0) {
   1085 		if (*cp != '!')
   1086 			c = *cp++;
   1087 		else if (*++cp == '!')
   1088 			c = *cp++;
   1089 		else {
   1090 			int len;
   1091 			char *p;
   1092 
   1093 			shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
   1094 				source->line + 1);
   1095 			len = strlen(nbuf);
   1096 			if (ntruncate) {
   1097 				if (ntruncate >= len) {
   1098 					ntruncate -= len;
   1099 					continue;
   1100 				}
   1101 				p += ntruncate;
   1102 				len -= ntruncate;
   1103 				ntruncate = 0;
   1104 			}
   1105 			shf_write(p, len, shl_out);
   1106 			continue;
   1107 		}
   1108 		if (ntruncate)
   1109 			--ntruncate;
   1110 		else
   1111 			shf_putc(c, shl_out);
   1112 	}
   1113 #endif /* 0 */
   1114 	shf_puts(cp + ntruncate, shl_out);
   1115 	shf_flush(shl_out);
   1116 }
   1117 
   1118 /* Read the variable part of a ${...} expression (ie, up to but not including
   1119  * the :[-+?=#%] or close-brace.
   1120  */
   1121 static char *
   1122 get_brace_var(wsp, wp)
   1123 	XString *wsp;
   1124 	char *wp;
   1125 {
   1126 	enum parse_state {
   1127 			   PS_INITIAL, PS_SAW_HASH, PS_IDENT,
   1128 			   PS_NUMBER, PS_VAR1, PS_END
   1129 			 }
   1130 		state;
   1131 	char c;
   1132 
   1133 	state = PS_INITIAL;
   1134 	while (1) {
   1135 		c = getsc();
   1136 		/* State machine to figure out where the variable part ends. */
   1137 		switch (state) {
   1138 		  case PS_INITIAL:
   1139 			if (c == '#') {
   1140 				state = PS_SAW_HASH;
   1141 				break;
   1142 			}
   1143 			/* fall through.. */
   1144 		  case PS_SAW_HASH:
   1145 			if (letter(c))
   1146 				state = PS_IDENT;
   1147 			else if (digit(c))
   1148 				state = PS_NUMBER;
   1149 			else if (ctype(c, C_VAR1))
   1150 				state = PS_VAR1;
   1151 			else
   1152 				state = PS_END;
   1153 			break;
   1154 		  case PS_IDENT:
   1155 			if (!letnum(c)) {
   1156 				state = PS_END;
   1157 				if (c == '[') {
   1158 					char *tmp, *p;
   1159 
   1160 					if (!arraysub(&tmp))
   1161 						yyerror("missing ]\n");
   1162 					*wp++ = c;
   1163 					for (p = tmp; *p; ) {
   1164 						Xcheck(*wsp, wp);
   1165 						*wp++ = *p++;
   1166 					}
   1167 					afree(tmp, ATEMP);
   1168 					c = getsc(); /* the ] */
   1169 				}
   1170 			}
   1171 			break;
   1172 		  case PS_NUMBER:
   1173 			if (!digit(c))
   1174 				state = PS_END;
   1175 			break;
   1176 		  case PS_VAR1:
   1177 			state = PS_END;
   1178 			break;
   1179 		  case PS_END: /* keep gcc happy */
   1180 			break;
   1181 		}
   1182 		if (state == PS_END) {
   1183 			*wp++ = '\0';	/* end of variable part */
   1184 			ungetsc(c);
   1185 			break;
   1186 		}
   1187 		Xcheck(*wsp, wp);
   1188 		*wp++ = c;
   1189 	}
   1190 	return wp;
   1191 }
   1192 
   1193 /*
   1194  * Save an array subscript - returns true if matching bracket found, false
   1195  * if eof or newline was found.
   1196  * (Returned string double null terminated)
   1197  */
   1198 static int
   1199 arraysub(strp)
   1200 	char **strp;
   1201 {
   1202 	XString ws;
   1203 	char	*wp;
   1204 	char	c;
   1205 	int 	depth = 1;	/* we are just past the initial [ */
   1206 
   1207 	Xinit(ws, wp, 32, ATEMP);
   1208 
   1209 	do {
   1210 		c = getsc();
   1211 		Xcheck(ws, wp);
   1212 		*wp++ = c;
   1213 		if (c == '[')
   1214 			depth++;
   1215 		else if (c == ']')
   1216 			depth--;
   1217 	} while (depth > 0 && c && c != '\n');
   1218 
   1219 	*wp++ = '\0';
   1220 	*strp = Xclose(ws, wp);
   1221 
   1222 	return depth == 0 ? 1 : 0;
   1223 }
   1224 
   1225 /* Unget a char: handles case when we are already at the start of the buffer */
   1226 static const char *
   1227 ungetsc(c)
   1228 	int c;
   1229 {
   1230 	if (backslash_skip)
   1231 		backslash_skip--;
   1232 	/* Don't unget eof... */
   1233 	if (source->str == null && c == '\0')
   1234 		return source->str;
   1235 	if (source->str > source->start)
   1236 		source->str--;
   1237 	else {
   1238 		Source *s;
   1239 
   1240 		s = pushs(SREREAD, source->areap);
   1241 		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
   1242 		s->start = s->str = s->ugbuf;
   1243 		s->next = source;
   1244 		source = s;
   1245 	}
   1246 	return source->str;
   1247 }
   1248 
   1249 
   1250 /* Called to get a char that isn't a \newline sequence. */
   1251 static int
   1252 getsc_bn ARGS((void))
   1253 {
   1254 	int c, c2;
   1255 
   1256 	if (ignore_backslash_newline)
   1257 		return getsc_();
   1258 
   1259 	if (backslash_skip == 1) {
   1260 		backslash_skip = 2;
   1261 		return getsc_();
   1262 	}
   1263 
   1264 	backslash_skip = 0;
   1265 
   1266 	while (1) {
   1267 		c = getsc_();
   1268 		if (c == '\\') {
   1269 			if ((c2 = getsc_()) == '\n')
   1270 				/* ignore the \newline; get the next char... */
   1271 				continue;
   1272 			ungetsc(c2);
   1273 			backslash_skip = 1;
   1274 		}
   1275 		return c;
   1276 	}
   1277 }
   1278