Home | History | Annotate | Download | only in csh
      1 /*
      2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved.  The Berkeley Software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 #include <unistd.h>	/* for lseek prototype */
     18 #include "sh.h"
     19 #include "sh.tconst.h"
     20 #include <sys/filio.h>
     21 #include <sys/ttold.h>
     22 #define	RAW 	O_RAW
     23 /*
     24  * C shell
     25  */
     26 
     27 /*
     28  * These lexical routines read input and form lists of words.
     29  * There is some involved processing here, because of the complications
     30  * of input buffering, and especially because of history substitution.
     31  */
     32 
     33 tchar	*word(void);
     34 tchar	getC1(int);
     35 tchar	*subword(tchar *, int, bool *);
     36 void	getdol(void);
     37 void	addla(tchar *);
     38 void	getexcl(tchar);
     39 void	noev(tchar *);
     40 void	setexclp(tchar *);
     41 void	unreadc(tchar);
     42 int	readc(bool);
     43 struct wordent	*dosub(int, struct wordent *, bool);
     44 struct Hist	*findev(tchar *, bool);
     45 struct wordent	*gethent(int);
     46 struct wordent	*getsub(struct wordent *);
     47 
     48 /*
     49  * Peekc is a peek characer for getC, peekread for readc.
     50  * There is a subtlety here in many places... history routines
     51  * will read ahead and then insert stuff into the input stream.
     52  * If they push back a character then they must push it behind
     53  * the text substituted by the history substitution.  On the other
     54  * hand in several places we need 2 peek characters.  To make this
     55  * all work, the history routines read with getC, and make use both
     56  * of ungetC and unreadc.  The key observation is that the state
     57  * of getC at the call of a history reference is such that calls
     58  * to getC from the history routines will always yield calls of
     59  * readc, unless this peeking is involved.  That is to say that during
     60  * getexcl the variables lap, exclp, and exclnxt are all zero.
     61  *
     62  * Getdol invokes history substitution, hence the extra peek, peekd,
     63  * which it can ungetD to be before history substitutions.
     64  */
     65 tchar peekc, peekd;
     66 tchar peekread;
     67 
     68 tchar *exclp;			/* (Tail of) current word from ! subst */
     69 struct	wordent *exclnxt;	/* The rest of the ! subst words */
     70 int	exclc;			/* Count of remainig words in ! subst */
     71 tchar *alvecp;		/* "Globp" for alias resubstitution */
     72 
     73 /*
     74  * Lex returns to its caller not only a wordlist (as a "var" parameter)
     75  * but also whether a history substitution occurred.  This is used in
     76  * the main (process) routine to determine whether to echo, and also
     77  * when called by the alias routine to determine whether to keep the
     78  * argument list.
     79  */
     80 bool	hadhist;
     81 
     82 tchar getCtmp;
     83 #define	getC(f)		((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
     84 #define	ungetC(c)	peekc = c
     85 #define	ungetD(c)	peekd = c
     86 
     87 bool
     88 lex(struct wordent *hp)
     89 {
     90 	struct wordent *wdp;
     91 	int c;
     92 
     93 #ifdef TRACE
     94 	tprintf("TRACE- lex()\n");
     95 #endif
     96 	lineloc = btell();
     97 	hp->next = hp->prev = hp;
     98 	hp->word = S_ /* "" */;
     99 	alvecp = 0, hadhist = 0;
    100 	do
    101 		c = readc(0);
    102 	while (issp(c));
    103 	/* make sure history is enabled */
    104 	if (HIST && c == HISTSUB && intty)
    105 		/* ^lef^rit	from tty is short !:s^lef^rit */
    106 		getexcl(c);
    107 	else
    108 		unreadc(c);
    109 	wdp = hp;
    110 	/*
    111 	 * The following loop is written so that the links needed
    112 	 * by freelex will be ready and rarin to go even if it is
    113 	 * interrupted.
    114 	 */
    115 	do {
    116 		struct wordent *new = (struct wordent *)xalloc(sizeof *wdp);
    117 
    118 		new->word = 0;
    119 		new->prev = wdp;
    120 		new->next = hp;
    121 		wdp->next = new;
    122 		wdp = new;
    123 		wdp->word = word();
    124 	} while (wdp->word[0] != '\n');
    125 #ifdef TRACE
    126 	tprintf("Exiting lex()\n");
    127 #endif
    128 	hp->prev = wdp;
    129 	return (hadhist);
    130 }
    131 
    132 void
    133 prlex(struct wordent *sp0)
    134 {
    135 	struct wordent *sp = sp0->next;
    136 
    137 #ifdef TRACE
    138 	tprintf("TRACE- prlex()\n");
    139 #endif
    140 	for (;;) {
    141 		printf("%t", sp->word);
    142 		sp = sp->next;
    143 		if (sp == sp0)
    144 			break;
    145 		if (sp->word[0] != '\n')
    146 			Putchar(' ');
    147 	}
    148 }
    149 
    150 void
    151 copylex(struct wordent *hp, struct wordent *fp)
    152 {
    153 	struct wordent *wdp;
    154 
    155 #ifdef TRACE
    156 	tprintf("TRACE- copylex()\n");
    157 #endif
    158 	wdp = hp;
    159 	fp = fp->next;
    160 	do {
    161 		struct wordent *new = (struct wordent *)xalloc(sizeof *wdp);
    162 
    163 		new->prev = wdp;
    164 		new->next = hp;
    165 		wdp->next = new;
    166 		wdp = new;
    167 		wdp->word = savestr(fp->word);
    168 		fp = fp->next;
    169 	} while (wdp->word[0] != '\n');
    170 	hp->prev = wdp;
    171 }
    172 
    173 void
    174 freelex(struct wordent *vp)
    175 {
    176 	struct wordent *fp;
    177 
    178 #ifdef TRACE
    179 	tprintf("TRACE- freelex()\n");
    180 #endif
    181 	while (vp->next != vp) {
    182 		fp = vp->next;
    183 		vp->next = fp->next;
    184 		xfree(fp->word);
    185 		xfree(fp);
    186 	}
    187 	vp->prev = vp;
    188 }
    189 
    190 tchar *
    191 word(void)
    192 {
    193 	tchar c, c1;
    194 	tchar *wp;
    195 	tchar wbuf[BUFSIZ];
    196 	bool dolflg;
    197 	int i;
    198 
    199 #ifdef TRACE
    200 	tprintf("TRACE- word()\n");
    201 #endif
    202 	wp = wbuf;
    203 	i = BUFSIZ - 4;
    204 loop:
    205 	while (issp(c = getC(DOALL)))
    206 		;
    207 	if (cmap(c, _META|_ESC)||isauxsp(c))
    208 		switch (c) {
    209 		case '&':
    210 		case '|':
    211 		case '<':
    212 		case '>':
    213 			*wp++ = c;
    214 			c1 = getC(DOALL);
    215 			if (c1 == c)
    216 				*wp++ = c1;
    217 			else
    218 				ungetC(c1);
    219 			goto ret;
    220 
    221 		case '#':
    222 			if (intty)
    223 				break;
    224 			c = 0;
    225 			do {
    226 				c1 = c;
    227 				c = getC(0);
    228 			} while (c != '\n');
    229 			if (c1 == '\\')
    230 				goto loop;
    231 			/* fall into ... */
    232 
    233 		case ';':
    234 		case '(':
    235 		case ')':
    236 		case '\n':
    237 			*wp++ = c;
    238 			goto ret;
    239 
    240 		case '\\':
    241 			c = getC(0);
    242 			if (c == '\n') {
    243 				if (onelflg == 1)
    244 					onelflg = 2;
    245 				goto loop;
    246 			}
    247 			if (c != HIST)
    248 				*wp++ = '\\', --i;
    249 			c |= QUOTE;
    250 		}
    251 	c1 = 0;
    252 	dolflg = DOALL;
    253 	for (;;) {
    254 		if (c1) {
    255 			if (c == c1) {
    256 				c1 = 0;
    257 				dolflg = DOALL;
    258 			} else if (c == '\\') {
    259 				c = getC(0);
    260 				if (c == HIST)
    261 					c |= QUOTE;
    262 				else {
    263 					if (c == '\n')
    264 #if 0
    265 						if (c1 == '`')
    266 							c = ' ';
    267 						else
    268 #endif
    269 							c |= QUOTE;
    270 					ungetC(c);
    271 					c = '\\';
    272 				}
    273 			} else if (c == '\n') {
    274 				seterrc(gettext("Unmatched "), c1);
    275 				ungetC(c);
    276 				break;
    277 			}
    278 		} else if (cmap(c, _META|_Q|_Q1|_ESC)||isauxsp(c)) {
    279 			if (c == '\\') {
    280 				c = getC(0);
    281 				if (c == '\n') {
    282 					if (onelflg == 1)
    283 						onelflg = 2;
    284 					break;
    285 				}
    286 				if (c != HIST)
    287 					*wp++ = '\\', --i;
    288 				c |= QUOTE;
    289 			} else if (cmap(c, _Q|_Q1)) {		/* '"` */
    290 				c1 = c;
    291 				dolflg = c == '"' ? DOALL : DOEXCL;
    292 			} else if (c != '#' || !intty) {
    293 				ungetC(c);
    294 				break;
    295 			}
    296 		}
    297 		if (--i > 0) {
    298 			*wp++ = c;
    299 			c = getC(dolflg);
    300 		} else {
    301 			seterr("Word too long");
    302 			wp = &wbuf[1];
    303 			break;
    304 		}
    305 	}
    306 ret:
    307 	*wp = 0;
    308 #ifdef TRACE
    309 	tprintf("word() returning:%t\n", wbuf);
    310 #endif
    311 	return (savestr(wbuf));
    312 }
    313 
    314 tchar
    315 getC1(int flag)
    316 {
    317 	tchar c;
    318 
    319 top:
    320 	if (c = peekc) {
    321 		peekc = 0;
    322 		return (c);
    323 	}
    324 	if (lap) {
    325 		if ((c = *lap++) == 0)
    326 			lap = 0;
    327 		else {
    328 			/*
    329 			 * don't quote things if there was an error (err!=0)
    330 			 * the input is original, not from a substitution and
    331 			 * therefore should not be quoted
    332 			 */
    333 			if (!err && cmap(c, _META|_Q|_Q1)||isauxsp(c))
    334 				c |= QUOTE;
    335 			return (c);
    336 		}
    337 	}
    338 	if (c = peekd) {
    339 		peekd = 0;
    340 		return (c);
    341 	}
    342 	if (exclp) {
    343 		if (c = *exclp++)
    344 			return (c);
    345 		if (exclnxt && --exclc >= 0) {
    346 			exclnxt = exclnxt->next;
    347 			setexclp(exclnxt->word);
    348 			return (' ');
    349 		}
    350 		exclp = 0;
    351 		exclnxt = 0;
    352 	}
    353 	if (exclnxt) {
    354 		exclnxt = exclnxt->next;
    355 		if (--exclc < 0)
    356 			exclnxt = 0;
    357 		else
    358 			setexclp(exclnxt->word);
    359 		goto top;
    360 	}
    361 	c = readc(0);
    362 	if (c == '$' && (flag & DODOL)) {
    363 		getdol();
    364 		goto top;
    365 	}
    366 	if (c == HIST && (flag & DOEXCL)) {
    367 		getexcl(0);
    368 		goto top;
    369 	}
    370 	return (c);
    371 }
    372 
    373 void
    374 getdol(void)
    375 {
    376 	tchar *np, *p;
    377 	tchar name[MAX_VREF_LEN];
    378 	int c;
    379 	int sc;
    380 	bool special = 0;
    381 
    382 #ifdef TRACE
    383 	tprintf("TRACE- getdol()\n");
    384 #endif
    385 	np = name, *np++ = '$';
    386 	c = sc = getC(DOEXCL);
    387 	if (isspnl(c)) {
    388 		ungetD(c);
    389 		ungetC('$' | QUOTE);
    390 		return;
    391 	}
    392 	if (c == '{')
    393 		*np++ = c, c = getC(DOEXCL);
    394 	if (c == '#' || c == '?')
    395 		special++, *np++ = c, c = getC(DOEXCL);
    396 	*np++ = c;
    397 	switch (c) {
    398 
    399 	case '<':
    400 	case '$':
    401 	case '*':
    402 		if (special)
    403 			goto vsyn;
    404 		goto ret;
    405 
    406 	case '\n':
    407 		ungetD(c);
    408 		np--;
    409 		goto vsyn;
    410 
    411 	default:
    412 		p = np;
    413 		if (digit(c)) {
    414 			/* make sure the variable names are MAX_VAR_LEN chars or less */
    415 			while (digit(c = getC(DOEXCL)) && (np - p) < MAX_VAR_LEN) {
    416 				*np++ = c;
    417 			}
    418 		} else if (letter(c)) {
    419 			while ((letter(c = getC(DOEXCL)) || digit(c)) &&
    420 				(np - p) < MAX_VAR_LEN) {
    421 				*np++ = c;
    422 			}
    423 		}
    424 		else
    425 			goto vsyn;
    426 
    427 		if ((np - p) > MAX_VAR_LEN)
    428 		{
    429 			seterr("Variable name too long");
    430 			goto ret;
    431 		}
    432 	}
    433 	if (c == '[') {
    434 		*np++ = c;
    435 		do {
    436 			c = getC(DOEXCL);
    437 			if (c == '\n') {
    438 				ungetD(c);
    439 				np--;
    440 				goto vsyn;
    441 			}
    442 			/* need to leave space for possible modifiers */
    443 			if (np >= &name[MAX_VREF_LEN - 8])
    444 			{
    445 				seterr("Variable reference too long");
    446 				goto ret;
    447 			}
    448 			*np++ = c;
    449 		} while (c != ']');
    450 		c = getC(DOEXCL);
    451 	}
    452 	if (c == ':') {
    453 		*np++ = c, c = getC(DOEXCL);
    454 		if (c == 'g')
    455 			*np++ = c, c = getC(DOEXCL);
    456 		*np++ = c;
    457 		if (!any(c, S_htrqxe))
    458 			goto vsyn;
    459 	} else
    460 		ungetD(c);
    461 	if (sc == '{') {
    462 		c = getC(DOEXCL);
    463 		if (c != '}') {
    464 			ungetC(c);
    465 			goto vsyn;
    466 		}
    467 		*np++ = c;
    468 	}
    469 ret:
    470 	*np = 0;
    471 	addla(name);
    472 	return;
    473 
    474 vsyn:
    475 	seterr("Variable syntax");
    476 	goto ret;
    477 }
    478 
    479 void
    480 addla(tchar *cp)
    481 {
    482 	tchar *buf;
    483 	int len = 0;
    484 
    485 #ifdef TRACE
    486 	tprintf("TRACE- addla()\n");
    487 #endif
    488 	if (lap) {
    489 		len = strlen_(lap);
    490 		buf = xalloc((len+1) * sizeof (tchar));
    491 		(void) strcpy_(buf, lap);
    492 	}
    493 	len += strlen_(cp);
    494 
    495 	/* len+5 is allow 4 additional charecters just to be safe */
    496 	labuf = xrealloc(labuf, (len+5) * sizeof (tchar));
    497 	(void) strcpy_(labuf, cp);
    498 	if (lap) {
    499 		(void) strcat_(labuf, buf);
    500 		xfree(buf);
    501 	}
    502 	lap = labuf;
    503 }
    504 
    505 tchar	lhsb[256];
    506 tchar	slhs[256];
    507 tchar	rhsb[512];
    508 int	quesarg;
    509 
    510 void
    511 getexcl(tchar sc)
    512 {
    513 	struct wordent *hp, *ip;
    514 	int left, right, dol;
    515 	int c;
    516 
    517 #ifdef TRACE
    518 	tprintf("TRACE- getexcl()\n");
    519 #endif
    520 	if (sc == 0) {
    521 		sc = getC(0);
    522 		if (sc != '{') {
    523 			ungetC(sc);
    524 			sc = 0;
    525 		}
    526 	}
    527 	quesarg = -1;
    528 	lastev = eventno;
    529 	hp = gethent(sc);
    530 	if (hp == 0)
    531 		return;
    532 	hadhist = 1;
    533 	dol = 0;
    534 	if (hp == alhistp)
    535 		for (ip = hp->next->next; ip != alhistt; ip = ip->next)
    536 			dol++;
    537 	else
    538 		for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
    539 			dol++;
    540 	left = 0, right = dol;
    541 	if (sc == HISTSUB) {
    542 		ungetC('s'), unreadc(HISTSUB), c = ':';
    543 		goto subst;
    544 	}
    545 	c = getC(0);
    546 	/* if (!any(c, ":^$*-%")) */	/* change needed for char -> tchar */
    547 	if (! (c == ':' || c == '^' || c == '$' || c == '*' ||
    548 	    c == '-' || c == '%'))
    549 		goto subst;
    550 	left = right = -1;
    551 	if (c == ':') {
    552 		c = getC(0);
    553 		unreadc(c);
    554 		if (letter(c) || c == '&') {
    555 			c = ':';
    556 			left = 0, right = dol;
    557 			goto subst;
    558 		}
    559 	} else
    560 		ungetC(c);
    561 	if (!getsel(&left, &right, dol))
    562 		return;
    563 	c = getC(0);
    564 	if (c == '*')
    565 		ungetC(c), c = '-';
    566 	if (c == '-') {
    567 		if (!getsel(&left, &right, dol))
    568 			return;
    569 		c = getC(0);
    570 	}
    571 subst:
    572 	exclc = right - left + 1;
    573 	while (--left >= 0)
    574 		hp = hp->next;
    575 	if (sc == HISTSUB || c == ':') {
    576 		do {
    577 			hp = getsub(hp);
    578 			c = getC(0);
    579 		} while (c == ':');
    580 	}
    581 	unreadc(c);
    582 	if (sc == '{') {
    583 		c = getC(0);
    584 		if (c != '}')
    585 			seterr("Bad ! form");
    586 	}
    587 	exclnxt = hp;
    588 }
    589 
    590 struct wordent *
    591 getsub(struct wordent *en)
    592 {
    593 	tchar *cp;
    594 	int delim;
    595 	int c;
    596 	int sc;
    597 	bool global = 0;
    598 	tchar orhsb[(sizeof rhsb)/(sizeof rhsb[0])];
    599 
    600 #ifdef TRACE
    601 	tprintf("TRACE- getsub()\n");
    602 #endif
    603 	exclnxt = 0;
    604 	sc = c = getC(0);
    605 	if (c == 'g')
    606 		global++, c = getC(0);
    607 	switch (c) {
    608 
    609 	case 'p':
    610 		justpr++;
    611 		goto ret;
    612 
    613 	case 'x':
    614 	case 'q':
    615 		global++;
    616 		/* fall into ... */
    617 
    618 	case 'h':
    619 	case 'r':
    620 	case 't':
    621 	case 'e':
    622 		break;
    623 
    624 	case '&':
    625 		if (slhs[0] == 0) {
    626 			seterr("No prev sub");
    627 			goto ret;
    628 		}
    629 		(void) strcpy_(lhsb, slhs);
    630 		break;
    631 
    632 #if 0
    633 	case '~':
    634 		if (lhsb[0] == 0)
    635 			goto badlhs;
    636 		break;
    637 #endif
    638 
    639 	case 's':
    640 		delim = getC(0);
    641 		if (alnum(delim) || isspnl(delim)) {
    642 			unreadc(delim);
    643 bads:
    644 			lhsb[0] = 0;
    645 			seterr("Bad substitute");
    646 			goto ret;
    647 		}
    648 		cp = lhsb;
    649 		for (;;) {
    650 			c = getC(0);
    651 			if (c == '\n') {
    652 				unreadc(c);
    653 				break;
    654 			}
    655 			if (c == delim)
    656 				break;
    657 			if (cp > &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
    658 				goto bads;
    659 			if (c == '\\') {
    660 				c = getC(0);
    661 				if (c != delim && c != '\\')
    662 					*cp++ = '\\';
    663 			}
    664 			*cp++ = c;
    665 		}
    666 		if (cp != lhsb)
    667 			*cp++ = 0;
    668 		else if (lhsb[0] == 0) {
    669 /* badlhs: */
    670 			seterr("No prev lhs");
    671 			goto ret;
    672 		}
    673 		cp = rhsb;
    674 		(void) strcpy_(orhsb, cp);
    675 		for (;;) {
    676 			c = getC(0);
    677 			if (c == '\n') {
    678 				unreadc(c);
    679 				break;
    680 			}
    681 			if (c == delim)
    682 				break;
    683 #if 0
    684 			if (c == '~') {
    685 				if (&cp[strlen_(orhsb)]
    686 				> &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2])
    687 					goto toorhs;
    688 				(void) strcpy_(cp, orhsb);
    689 				cp = strend(cp);
    690 				continue;
    691 			}
    692 #endif
    693 			if (cp > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) {
    694 /* toorhs: */
    695 				seterr("Rhs too long");
    696 				goto ret;
    697 			}
    698 			if (c == '\\') {
    699 				c = getC(0);
    700 				if (c != delim /* && c != '~' */)
    701 					*cp++ = '\\';
    702 			}
    703 			*cp++ = c;
    704 		}
    705 		*cp++ = 0;
    706 		break;
    707 
    708 	default:
    709 		if (c == '\n')
    710 			unreadc(c);
    711 		seterrc(gettext("Bad ! modifier: "), c);
    712 		goto ret;
    713 	}
    714 	(void) strcpy_(slhs, lhsb);
    715 	if (exclc)
    716 		en = dosub(sc, en, global);
    717 ret:
    718 	return (en);
    719 }
    720 
    721 struct wordent *
    722 dosub(int sc, struct wordent *en, bool global)
    723 {
    724 	struct wordent lex;
    725 	bool didsub = 0;
    726 	struct wordent *hp = &lex;
    727 	struct wordent *wdp;
    728 	int i = exclc;
    729 
    730 #ifdef TRACE
    731 	tprintf("TRACE- dosub()\n");
    732 #endif
    733 	wdp = hp;
    734 	while (--i >= 0) {
    735 		struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp);
    736 
    737 		new->prev = wdp;
    738 		new->next = hp;
    739 		wdp->next = new;
    740 		wdp = new;
    741 		en = en->next;
    742 		wdp->word = global || didsub == 0 ?
    743 		    subword(en->word, sc, &didsub) : savestr(en->word);
    744 	}
    745 	if (didsub == 0)
    746 		seterr("Modifier failed");
    747 	hp->prev = wdp;
    748 	return (&enthist(-1000, &lex, 0)->Hlex);
    749 }
    750 
    751 tchar *
    752 subword(tchar *cp, int type, bool *adid)
    753 {
    754 	tchar wbuf[BUFSIZ];
    755 	tchar *wp, *mp, *np;
    756 	int i;
    757 
    758 #ifdef TRACE
    759 	tprintf("TRACE- subword()\n");
    760 #endif
    761 	switch (type) {
    762 
    763 	case 'r':
    764 	case 'e':
    765 	case 'h':
    766 	case 't':
    767 	case 'q':
    768 	case 'x':
    769 		wp = domod(cp, type);
    770 		if (wp == 0)
    771 			return (savestr(cp));
    772 		*adid = 1;
    773 		return (wp);
    774 
    775 	default:
    776 		wp = wbuf;
    777 		i = BUFSIZ - 4;
    778 		for (mp = cp; *mp; mp++)
    779 			if (matchs(mp, lhsb)) {
    780 				for (np = cp; np < mp; )
    781 					*wp++ = *np++, --i;
    782 				for (np = rhsb; *np; np++) switch (*np) {
    783 
    784 				case '\\':
    785 					if (np[1] == '&')
    786 						np++;
    787 					/* fall into ... */
    788 
    789 				default:
    790 					if (--i < 0)
    791 						goto ovflo;
    792 					*wp++ = *np;
    793 					continue;
    794 
    795 				case '&':
    796 					i -= strlen_(lhsb);
    797 					if (i < 0)
    798 						goto ovflo;
    799 					*wp = 0;
    800 					(void) strcat_(wp, lhsb);
    801 					wp = strend(wp);
    802 					continue;
    803 				}
    804 				mp += strlen_(lhsb);
    805 				i -= strlen_(mp);
    806 				if (i < 0) {
    807 ovflo:
    808 					seterr("Subst buf ovflo");
    809 					return (S_ /* "" */);
    810 				}
    811 				*wp = 0;
    812 				(void) strcat_(wp, mp);
    813 				*adid = 1;
    814 				return (savestr(wbuf));
    815 			}
    816 		return (savestr(cp));
    817 	}
    818 }
    819 
    820 tchar *
    821 domod(tchar *cp, int type)
    822 {
    823 	tchar *wp, *xp;
    824 	int c;
    825 
    826 #ifdef TRACE
    827 	tprintf("TRACE- domod()\n");
    828 #endif
    829 	switch (type) {
    830 
    831 	case 'x':
    832 	case 'q':
    833 		wp = savestr(cp);
    834 		for (xp = wp; c = *xp; xp++)
    835 			if (!issp(c) || type == 'q')
    836 				*xp |= QUOTE;
    837 		return (wp);
    838 
    839 	case 'h':
    840 	case 't':
    841 		if (!any('/', cp))
    842 			return (type == 't' ? savestr(cp) : 0);
    843 		wp = strend(cp);
    844 		while (*--wp != '/')
    845 			continue;
    846 		if (type == 'h')
    847 			xp = savestr(cp), xp[wp - cp] = 0;
    848 		else
    849 			xp = savestr(wp + 1);
    850 		return (xp);
    851 
    852 	case 'e':
    853 	case 'r':
    854 		wp = strend(cp);
    855 		for (wp--; wp >= cp && *wp != '/'; wp--)
    856 			if (*wp == '.') {
    857 				if (type == 'e')
    858 					xp = savestr(wp + 1);
    859 				else
    860 					xp = savestr(cp), xp[wp - cp] = 0;
    861 				return (xp);
    862 			}
    863 		return (savestr(type == 'e' ? S_ /* "" */ : cp));
    864 	}
    865 	return (0);
    866 }
    867 
    868 int
    869 matchs(tchar *str, tchar *pat)
    870 {
    871 
    872 #ifdef TRACE
    873 	tprintf("TRACE- matchs()\n");
    874 #endif
    875 	while (*str && *pat && *str == *pat)
    876 		str++, pat++;
    877 	return (*pat == 0);
    878 }
    879 
    880 int
    881 getsel(int *al, int *ar, int dol)
    882 {
    883 	int c = getC(0);
    884 	int i;
    885 	bool first = *al < 0;
    886 
    887 #ifdef TRACE
    888 	tprintf("TRACE- getsel()\n");
    889 #endif
    890 	switch (c) {
    891 
    892 	case '%':
    893 		if (quesarg == -1)
    894 			goto bad;
    895 		if (*al < 0)
    896 			*al = quesarg;
    897 		*ar = quesarg;
    898 		break;
    899 
    900 	case '-':
    901 		if (*al < 0) {
    902 			*al = 0;
    903 			*ar = dol - 1;
    904 			unreadc(c);
    905 		}
    906 		return (1);
    907 
    908 	case '^':
    909 		if (*al < 0)
    910 			*al = 1;
    911 		*ar = 1;
    912 		break;
    913 
    914 	case '$':
    915 		if (*al < 0)
    916 			*al = dol;
    917 		*ar = dol;
    918 		break;
    919 
    920 	case '*':
    921 		if (*al < 0)
    922 			*al = 1;
    923 		*ar = dol;
    924 		if (*ar < *al) {
    925 			*ar = 0;
    926 			*al = 1;
    927 			return (1);
    928 		}
    929 		break;
    930 
    931 	default:
    932 		if (digit(c)) {
    933 			i = 0;
    934 			while (digit(c)) {
    935 				i = i * 10 + c - '0';
    936 				c = getC(0);
    937 			}
    938 			if (i < 0)
    939 				i = dol + 1;
    940 			if (*al < 0)
    941 				*al = i;
    942 			*ar = i;
    943 		} else
    944 			if (*al < 0)
    945 				*al = 0, *ar = dol;
    946 			else
    947 				*ar = dol - 1;
    948 		unreadc(c);
    949 		break;
    950 	}
    951 	if (first) {
    952 		c = getC(0);
    953 		unreadc(c);
    954 		/* if (any(c, "-$*")) */	/* char -> tchar */
    955 		if (c == '-' || c == '$' || c == '*')
    956 			return (1);
    957 	}
    958 	if (*al > *ar || *ar > dol) {
    959 bad:
    960 		seterr("Bad ! arg selector");
    961 		return (0);
    962 	}
    963 	return (1);
    964 
    965 }
    966 
    967 struct wordent *
    968 gethent(int sc)
    969 {
    970 	struct Hist *hp;
    971 	tchar *np;
    972 	int c;
    973 	int event;
    974 	bool back = 0;
    975 
    976 #ifdef TRACE
    977 	tprintf("TRACE- gethent()\n");
    978 #endif
    979 	c = sc == HISTSUB ? HIST : getC(0);
    980 	if (c == HIST) {
    981 		if (alhistp)
    982 			return (alhistp);
    983 		event = eventno;
    984 		goto skip;
    985 	}
    986 	switch (c) {
    987 
    988 	case ':':
    989 	case '^':
    990 	case '$':
    991 	case '*':
    992 	case '%':
    993 		ungetC(c);
    994 		if (lastev == eventno && alhistp)
    995 			return (alhistp);
    996 		event = lastev;
    997 		break;
    998 
    999 	case '-':
   1000 		back = 1;
   1001 		c = getC(0);
   1002 		goto number;
   1003 
   1004 	case '#':			/* !# is command being typed in (mrh) */
   1005 		return (&paraml);
   1006 
   1007 	default:
   1008 		/* if (any(c, "(=~")) { */
   1009 		if (c == '(' || c == '=' || c == '~') {
   1010 			unreadc(c);
   1011 			ungetC(HIST);
   1012 			return (0);
   1013 		}
   1014 		if (digit(c))
   1015 			goto number;
   1016 		np = lhsb;
   1017 		/* while (!any(c, ": \t\\\n}")) { */
   1018 		while (! (c == ':' || c == '\\' || isspnl(c) || c == '}')) {
   1019 			if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
   1020 				*np++ = c;
   1021 			c = getC(0);
   1022 		}
   1023 		unreadc(c);
   1024 		if (np == lhsb) {
   1025 			ungetC(HIST);
   1026 			return (0);
   1027 		}
   1028 		*np++ = 0;
   1029 		hp = findev(lhsb, 0);
   1030 		if (hp)
   1031 			lastev = hp->Hnum;
   1032 		return (&hp->Hlex);
   1033 
   1034 	case '?':
   1035 		np = lhsb;
   1036 		for (;;) {
   1037 			c = getC(0);
   1038 			if (c == '\n') {
   1039 				unreadc(c);
   1040 				break;
   1041 			}
   1042 			if (c == '?')
   1043 				break;
   1044 			if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2])
   1045 				*np++ = c;
   1046 		}
   1047 		if (np == lhsb) {
   1048 			if (lhsb[0] == 0) {
   1049 				seterr("No prev search");
   1050 				return (0);
   1051 			}
   1052 		} else
   1053 			*np++ = 0;
   1054 		hp = findev(lhsb, 1);
   1055 		if (hp)
   1056 			lastev = hp->Hnum;
   1057 		return (&hp->Hlex);
   1058 
   1059 	number:
   1060 		event = 0;
   1061 		while (digit(c)) {
   1062 			event = event * 10 + c - '0';
   1063 			c = getC(0);
   1064 		}
   1065 		if (back)
   1066 			event = eventno + (alhistp == 0) - (event ? event : 0);
   1067 		unreadc(c);
   1068 		break;
   1069 	}
   1070 skip:
   1071 	for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
   1072 		if (hp->Hnum == event) {
   1073 			hp->Href = eventno;
   1074 			lastev = hp->Hnum;
   1075 			return (&hp->Hlex);
   1076 		}
   1077 	np = putn(event);
   1078 	noev(np);
   1079 	return (0);
   1080 }
   1081 
   1082 struct Hist *
   1083 findev(tchar *cp, bool anyarg)
   1084 {
   1085 	struct Hist *hp;
   1086 
   1087 #ifdef TRACE
   1088 	tprintf("TRACE- findev()\n");
   1089 #endif
   1090 	for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
   1091 		tchar *dp;
   1092 		tchar *p, *q;
   1093 		struct wordent *lp = hp->Hlex.next;
   1094 		int argno = 0;
   1095 
   1096 		if (lp->word[0] == '\n')
   1097 			continue;
   1098 		if (!anyarg) {
   1099 			p = cp;
   1100 			q = lp->word;
   1101 			do
   1102 				if (!*p)
   1103 					return (hp);
   1104 			while (*p++ == *q++);
   1105 			continue;
   1106 		}
   1107 		do {
   1108 			for (dp = lp->word; *dp; dp++) {
   1109 				p = cp;
   1110 				q = dp;
   1111 				do
   1112 					if (!*p) {
   1113 						quesarg = argno;
   1114 						return (hp);
   1115 					}
   1116 				while (*p++ == *q++);
   1117 			}
   1118 			lp = lp->next;
   1119 			argno++;
   1120 		} while (lp->word[0] != '\n');
   1121 	}
   1122 	noev(cp);
   1123 	return (0);
   1124 }
   1125 
   1126 void
   1127 noev(tchar *cp)
   1128 {
   1129 
   1130 #ifdef TRACE
   1131 	tprintf("TRACE- noev()\n");
   1132 #endif
   1133 	seterr2(cp, ": Event not found");
   1134 }
   1135 
   1136 void
   1137 setexclp(tchar *cp)
   1138 {
   1139 
   1140 #ifdef TRACE
   1141 	tprintf("TRACE- setexclp()\n");
   1142 #endif
   1143 	if (cp && cp[0] == '\n')
   1144 		return;
   1145 	exclp = cp;
   1146 }
   1147 
   1148 void
   1149 unreadc(tchar c)
   1150 {
   1151 
   1152 	peekread = c;
   1153 }
   1154 
   1155 int
   1156 readc(bool wanteof)
   1157 {
   1158 	int c;
   1159 	static int sincereal;
   1160 
   1161 	if (c = peekread) {
   1162 		peekread = 0;
   1163 		return (c);
   1164 	}
   1165 top:
   1166 	if (alvecp) {
   1167 		if (c = *alvecp++)
   1168 			return (c);
   1169 		if (*alvec) {
   1170 			alvecp = *alvec++;
   1171 			return (' ');
   1172 		}
   1173 	}
   1174 	if (alvec) {
   1175 		if (alvecp = *alvec) {
   1176 			alvec++;
   1177 			goto top;
   1178 		}
   1179 		/* Infinite source! */
   1180 		return ('\n');
   1181 	}
   1182 	if (evalp) {
   1183 		if (c = *evalp++)
   1184 			return (c);
   1185 		if (*evalvec) {
   1186 			evalp = *evalvec++;
   1187 			return (' ');
   1188 		}
   1189 		evalp = 0;
   1190 	}
   1191 	if (evalvec) {
   1192 		if (evalvec ==  (tchar **)1) {
   1193 			doneinp = 1;
   1194 			reset();
   1195 		}
   1196 		if (evalp = *evalvec) {
   1197 			evalvec++;
   1198 			goto top;
   1199 		}
   1200 		evalvec =  (tchar **)1;
   1201 		return ('\n');
   1202 	}
   1203 	do {
   1204 		if (arginp ==  (tchar *) 1 || onelflg == 1) {
   1205 			if (wanteof)
   1206 				return (-1);
   1207 			exitstat();
   1208 		}
   1209 		if (arginp) {
   1210 			if ((c = *arginp++) == 0) {
   1211 				arginp =  (tchar *) 1;
   1212 				return ('\n');
   1213 			}
   1214 			return (c);
   1215 		}
   1216 reread:
   1217 		c = bgetc();
   1218 		if (c < 0) {
   1219 			struct sgttyb tty;
   1220 
   1221 			if (wanteof)
   1222 				return (-1);
   1223 			/* was isatty but raw with ignoreeof yields problems */
   1224 			if (ioctl(SHIN, TIOCGETP,  (char *)&tty) == 0 &&
   1225 			    (tty.sg_flags & RAW) == 0) {
   1226 				/* was 'short' for FILEC */
   1227 				int ctpgrp;
   1228 
   1229 				if (++sincereal > 25)
   1230 					goto oops;
   1231 				if (tpgrp != -1 &&
   1232 				    ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp) == 0 &&
   1233 				    tpgrp != ctpgrp) {
   1234 					(void) ioctl(FSHTTY, TIOCSPGRP,
   1235 						(char *)&tpgrp);
   1236 					(void) killpg(ctpgrp, SIGHUP);
   1237 printf("Reset tty pgrp from %d to %d\n", ctpgrp, tpgrp);
   1238 					goto reread;
   1239 				}
   1240 				if (adrof(S_ignoreeof /* "ignoreeof" */)) {
   1241 					if (loginsh)
   1242 				printf("\nUse \"logout\" to logout.\n");
   1243 					else
   1244 				printf("\nUse \"exit\" to leave csh.\n");
   1245 					reset();
   1246 				}
   1247 				if (chkstop == 0) {
   1248 					panystop(1);
   1249 				}
   1250 			}
   1251 oops:
   1252 			doneinp = 1;
   1253 			reset();
   1254 		}
   1255 		sincereal = 0;
   1256 		if (c == '\n' && onelflg)
   1257 			onelflg--;
   1258 	} while (c == 0);
   1259 	return (c);
   1260 }
   1261 
   1262 static void
   1263 expand_fbuf(void)
   1264 {
   1265 	tchar **nfbuf =
   1266 	    (tchar **)xcalloc((unsigned)(fblocks + 2), sizeof (tchar **));
   1267 
   1268 	if (fbuf) {
   1269 		(void) blkcpy(nfbuf, fbuf);
   1270 		xfree((char *)fbuf);
   1271 	}
   1272 	fbuf = nfbuf;
   1273 	fbuf[fblocks] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX,
   1274 		sizeof (tchar));
   1275 	fblocks++;
   1276 }
   1277 
   1278 int
   1279 bgetc(void)
   1280 {
   1281 	int buf, off, c;
   1282 #ifdef FILEC
   1283 	tchar ttyline[BUFSIZ + MB_LEN_MAX]; /* read_() can return extra bytes */
   1284 	int