if ((c2 = getsc()) == '(')  c = MDPAREN; else ungetsc(c2);
if (!Flag(FSH)) { if ((c2 = getsc()) == '(')  c = MDPAREN; else ungetsc(c2); }
yylex(cf)	int cf;{	register int c, state;	char states [64], *statep = states; /* XXX overflow check */	XString ws;		/* expandable output word */	register char *wp;	/* output word pointer */	register char *sp, *dp;	char UNINITIALIZED(*ddparen_start);	int istate;	int UNINITIALIZED(c2);	int UNINITIALIZED(nparen), UNINITIALIZED(csstate);	int UNINITIALIZED(ndparen);	int UNINITIALIZED(indquotes);  Again:	Xinit(ws, wp, 64, ATEMP);	backslash_skip = 0;	ignore_backslash_newline = 0;	if (cf&ONEWORD)		istate = SWORD;#ifdef KSH	else if (cf&LETEXPR) {		*wp++ = OQUOTE;	 /* enclose arguments in (double) quotes */		istate = SDPAREN;			ndparen = 0;	}#endif /* KSH */	else {		/* normal lexing */		istate = (cf & HEREDELIM) ? SHEREDELIM : SBASE;		while ((c = getsc()) == ' ' || c == '\t')			;		if (c == '#') {			ignore_backslash_newline++;			while ((c = getsc()) != '\0' && c != '\n')				;			ignore_backslash_newline--;		}		ungetsc(c);	}	if (source->flags & SF_ALIAS) {	/* trailing ' ' in alias definition */		source->flags &= ~SF_ALIAS;		/* In POSIX mode, a trailing space only counts if we are		 * parsing a simple command		 */		if (!Flag(FPOSIX) || (cf & CMDWORD))			cf |= ALIAS;	}	/* collect non-special or quoted characters to form word */	for (*statep = state = istate;	     !((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM)				      && ctype(c, C_LEX1))); )	{		Xcheck(ws, wp);		switch (state) {		  case SBASE:			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {				*wp = EOS; /* temporary */				if (is_wdvarname(Xstring(ws, wp), FALSE))				{					char *p, *tmp;					if (arraysub(&tmp)) {						*wp++ = CHAR;						*wp++ = c;						for (p = tmp; *p; ) {							Xcheck(ws, wp);							*wp++ = CHAR;							*wp++ = *p++;						}						afree(tmp, ATEMP);						break;					} else {						Source *s;						s = pushs(SREREAD,							  source->areap);						s->start = s->str							= s->u.freeme = tmp;						s->next = source;						source = s;					}				}				*wp++ = CHAR;				*wp++ = c;				break;			}			/* fall through.. */		  Sbase1:	/* includes *(...|...) pattern (*+?@!) */#ifdef KSH			if (c == '*' || c == '@' || c == '+' || c == '?'			    || c == '!')			{				c2 = getsc();				if (c2 == '(' /*)*/ ) {					*wp++ = OPAT;					*wp++ = c;					*++statep = state = SPATTERN;					break;				}				ungetsc(c2);			}#endif /* KSH */			/* fall through.. */		  Sbase2:	/* doesn't include *(...|...) pattern (*+?@!) */			switch (c) {			  case '\\':				c = getsc();#ifdef OS2				if (isalnum(c)) {					*wp++ = CHAR, *wp++ = '\\';					*wp++ = CHAR, *wp++ = c;				} else #endif				if (c) /* trailing \ is lost */					*wp++ = QCHAR, *wp++ = c;				break;			  case '\'':				*++statep = state = SSQUOTE;				*wp++ = OQUOTE;				ignore_backslash_newline++;				break;			  case '"':				*++statep = state = SDQUOTE;				*wp++ = OQUOTE;				break;			  default:				goto Subst;			}			break;		  Subst:			switch (c) {			  case '\\':				c = getsc();				switch (c) {				  case '"': case '\\':				  case '$': case '`':					*wp++ = QCHAR, *wp++ = c;					break;				  default:					Xcheck(ws, wp);					if (c) { /* trailing \ is lost */						*wp++ = CHAR, *wp++ = '\\';						*wp++ = CHAR, *wp++ = c;					}					break;				}				break;			  case '$':				c = getsc();				if (c == '(') /*)*/ {					c = getsc();					if (c == '(') /*)*/ {						*++statep = state = SDDPAREN;						nparen = 2;						ddparen_start = wp;						*wp++ = EXPRSUB;					} else {						ungetsc(c);						*++statep = state = SPAREN;						nparen = 1;						csstate = 0;						*wp++ = COMSUB;					}				} else if (c == '{') /*}*/ {					*wp++ = OSUBST;					wp = get_brace_var(&ws, wp);					/* If this is a trim operation,					 * wrap @(...) around the pattern					 * (allows easy handling of ${a#b|c})					 */					c = getsc();					if (c == '#' || c == '%') {						*wp++ = CHAR, *wp++ = c;						if ((c2 = getsc()) == c)							*wp++ = CHAR, *wp++ = c;						else							ungetsc(c2);						*wp++ = OPAT, *wp++ = '@';						*++statep = state = STBRACE;					} else {						ungetsc(c);						*++statep = state = SBRACE;					}				} else if (ctype(c, C_ALPHA)) {					*wp++ = OSUBST;					do {						Xcheck(ws, wp);						*wp++ = c;						c = getsc();					} while (ctype(c, C_ALPHA|C_DIGIT));					*wp++ = '\0';					*wp++ = CSUBST;					ungetsc(c);				} else if (ctype(c, C_DIGIT|C_VAR1)) {					Xcheck(ws, wp);					*wp++ = OSUBST;					*wp++ = c;					*wp++ = '\0';					*wp++ = CSUBST;				} else {					*wp++ = CHAR, *wp++ = '$';					ungetsc(c);				}				break;			  case '`':				*++statep = state = SBQUOTE;				*wp++ = COMSUB;				/* Need to know if we are inside double quotes				 * since sh/at&t-ksh translate the \" to " in				 * "`..\"..`".				 */				indquotes = 0;				if (!Flag(FPOSIX))					for (sp = statep; sp > states; --sp)						if (*sp == SDQUOTE)							indquotes = 1;				break;			  default:				*wp++ = CHAR, *wp++ = c;			}			break;		  case SSQUOTE:			if (c == '\'') {				state = *--statep;				*wp++ = CQUOTE;				ignore_backslash_newline--;			} else				*wp++ = QCHAR, *wp++ = c;			break;		  case SDQUOTE:			if (c == '"') {				state = *--statep;				*wp++ = CQUOTE;			} else				goto Subst;			break;		  case SPAREN: /* $( .. ) */			/* todo: deal with $(...) quoting properly			 * kludge to partly fake quoting inside $(..): doesn't			 * really work because nested $(..) or ${..} inside			 * double quotes aren't dealt with.			 */			switch (csstate) {			  case 0: /* normal */				switch (c) {				  case '(':					nparen++;					break;				  case ')':					nparen--;					break;				  case '\\':					csstate = 1;					break;				  case '"':					csstate = 2;					break;				  case '\'':					csstate = 4;					ignore_backslash_newline++;					break;				}				break;			  case 1: /* backslash in normal mode */			  case 3: /* backslash in double quotes */				--csstate;				break;			  case 2: /* double quotes */				if (c == '"')					csstate = 0;				else if (c == '\\')					csstate = 3;				break;			  case 4: /* single quotes */				if (c == '\'') {					csstate = 0;					ignore_backslash_newline--;				}				break;			}			if (nparen == 0) {				state = *--statep;				*wp++ = 0; /* end of COMSUB */			} else				*wp++ = c;			break;		  case SDDPAREN: /* $(( .. )) */			/* todo: deal with $((...); (...)) properly */			/* XXX should nest using existing state machine			 *     (embed "..", $(...), etc.) */			if (c == '(')				nparen++;			else if (c == ')') {				nparen--;				if (nparen == 1) {					/*(*/					if ((c2 = getsc()) == ')') {						state = *--statep;						*wp++ = 0; /* end of EXPRSUB */						break;					} else {						ungetsc(c2);						/* mismatched parenthesis -						 * assume we were really						 * parsing a $(..) expression						 */						memmove(ddparen_start + 1,							ddparen_start,							wp - ddparen_start);						*ddparen_start++ = COMSUB;						*ddparen_start = '('; /*)*/						wp++;						csstate = 0;						*statep = state = SPAREN;					}				}			}			*wp++ = c;			break;		  case SBRACE:			/*{*/			if (c == '}') {				state = *--statep;				*wp++ = CSUBST;			} else				goto Sbase1;			break;		  case STBRACE:			/* same as SBRACE, except | is saved as SPAT and			 * CPAT is added at the end.			 */			/*{*/			if (c == '}') {				state = *--statep;				*wp++ = CPAT;				*wp++ = CSUBST;			} else if (c == '|') {				*wp++ = SPAT;			} else				goto Sbase1;			break;		  case SBQUOTE:			if (c == '`') {				*wp++ = 0;				state = *--statep;			} else if (c == '\\') {				switch (c = getsc()) {				  case '\\':				  case '$': case '`':					*wp++ = c;					break;				  case '"':					if (indquotes) {						*wp++ = c;						break;					}					/* fall through.. */				  default:					if (c) { /* trailing \ is lost */						*wp++ = '\\';						*wp++ = c;					}					break;				}			} else				*wp++ = c;			break;		  case SWORD:	/* ONEWORD */			goto Subst;#ifdef KSH		  case SDPAREN:	/* LETEXPR: (( ... )) */			/*(*/			if (c == ')') {				if (ndparen > 0)				    --ndparen;				/*(*/				else if ((c2 = getsc()) == ')') {					c = 0;					*wp++ = CQUOTE;					goto Done;				} else					ungetsc(c2);			} else if (c == '(')				/* parenthesis inside quotes and backslashes				 * are lost, but at&t ksh doesn't count them				 * either				 */				++ndparen;			goto Sbase2;#endif /* KSH */		  case SHEREDELIM:	/* <<,<<- delimiter */			/* XXX chuck this state (and the next) - use			 * the existing states ($ and \`..` should be			 * stripped of their specialness after the			 * fact).			 */			/* here delimiters need a special case since			 * $ and `..` are not to be treated specially			 */			if (c == '\\') {				c = getsc();				if (c) { /* trailing \ is lost */					*wp++ = QCHAR;					*wp++ = c;				}			} else if (c == '\'') {				*++statep = state = SSQUOTE;				*wp++ = OQUOTE;				ignore_backslash_newline++;			} else if (c == '"') {				state = SHEREDQUOTE;				*wp++ = OQUOTE;			} else {				*wp++ = CHAR;				*wp++ = c;			}			break;		  case SHEREDQUOTE:	/* " in <<,<<- delimiter */			if (c == '"') {				*wp++ = CQUOTE;				state = SHEREDELIM;			} else {				if (c == '\\') {					switch (c = getsc()) {					  case '\\': case '"':					  case '$': case '`':						break;					  default:						if (c) { /* trailing \ lost */							*wp++ = CHAR;							*wp++ = '\\';						}						break;					}				}				*wp++ = CHAR;				*wp++ = c;			}			break;		  case SPATTERN:	/* in *(...|...) pattern (*+?@!) */			if ( /*(*/ c == ')') {				*wp++ = CPAT;				state = *--statep;			} else if (c == '|')				*wp++ = SPAT;			else				goto Sbase1;			break;		}	}Done:	Xcheck(ws, wp);	if (state != istate)		yyerror("no closing quote\n");	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */	if (state == SHEREDELIM)		state = SBASE;	if ((c == '<' || c == '>') && state == SBASE) {		char *cp = Xstring(ws, wp);		if (Xlength(ws, wp) == 2 && cp[0] == CHAR && digit(cp[1])) {			wp = cp; /* throw away word */			c2/*unit*/ = cp[1] - '0';		} else			c2/*unit*/ = c == '>'; /* 0 for <, 1 for > */	}	if (wp == Xstring(ws, wp) && state == SBASE) {		Xfree(ws, wp);	/* free word */		/* no word, process LEX1 character */		switch (c) {		  default:			return c;		  case '|':		  case '&':		  case ';':			if ((c2 = getsc()) == c)				c = (c == ';') ? BREAK :				    (c == '|') ? LOGOR :				    (c == '&') ? LOGAND :				    YYERRCODE;#ifdef KSH			else if (c == '|' && c2 == '&')				c = COPROC;#endif /* KSH */			else				ungetsc(c2);			return c;		  case '>':		  case '<': {			register struct ioword *iop;			iop = (struct ioword *) alloc(sizeof(*iop), ATEMP);			iop->unit = c2/*unit*/;			c2 = getsc();			/* <<, >>, <> are ok, >< is not */			if (c == c2 || (c == '<' && c2 == '>')) {				iop->flag = c == c2 ?					  (c == '>' ? IOCAT : IOHERE) : IORDWR;				if (iop->flag == IOHERE)					if ((c2 = getsc()) == '-')						iop->flag |= IOSKIP;					else						ungetsc(c2);			} else if (c2 == '&')				iop->flag = IODUP | (c == '<' ? IORDUP : 0);			else {				iop->flag = c == '>' ? IOWRITE : IOREAD;				if (c == '>' && c2 == '|')					iop->flag |= IOCLOB;				else					ungetsc(c2);			}			iop->name = (char *) 0;			iop->delim = (char *) 0;			yylval.iop = iop;			return REDIR;		    }		  case '\n':			gethere();			if (cf & CONTIN)				goto Again;			return c;		  case '(':  /*)*/#ifdef KSH			if ((c2 = getsc()) == '(') /*)*/				c = MDPAREN;			else				ungetsc(c2);#endif /* KSH */			return c;		  /*(*/		  case ')':			return c;		}	}	*wp++ = EOS;		/* terminate word */	yylval.cp = Xclose(ws, wp);	if (state == SWORD#ifdef KSH		|| state == SDPAREN#endif /* KSH */		)	/* ONEWORD? */		return LWORD;	ungetsc(c);		/* unget terminator */	/* copy word to unprefixed string ident */	for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )		*dp++ = *sp++;	/* Make sure the ident array stays '\0' paded */	memset(dp, 0, (ident+IDENT) - dp + 1);	if (c != EOS)		*ident = '\0';	/* word is not unquoted */	if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {		struct tbl *p;		int h = hash(ident);		/* { */		if ((cf & KEYWORD) && (p = tsearch(&keywords, ident, h))		    && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}'))		{			afree(yylval.cp, ATEMP);			return p->val.i;		}		if ((cf & ALIAS) && (p = tsearch(&aliases, ident, h))		    && (p->flag & ISSET))		{			register Source *s;			for (s = source; s->type == SALIAS; s = s->next)				if (s->u.tblp == p)					return LWORD;			/* push alias expansion */			s = pushs(SALIAS, source->areap);			s->start = s->str = p->val.s;			s->u.tblp = p;			s->next = source;			source = s;			afree(yylval.cp, ATEMP);			goto Again;		}	}	return LWORD;}