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
33tchar	*word(void);
34tchar	getC1(int);
35tchar	*subword(tchar *, int, bool *);
36void	getdol(void);
37void	addla(tchar *);
38void	getexcl(tchar);
39void	noev(tchar *);
40void	setexclp(tchar *);
41void	unreadc(tchar);
42int	readc(bool);
43struct wordent	*dosub(int, struct wordent *, bool);
44struct Hist	*findev(tchar *, bool);
45struct wordent	*gethent(int);
46struct 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 */
65tchar peekc, peekd;
66tchar peekread;
67
68tchar *exclp;			/* (Tail of) current word from ! subst */
69struct	wordent *exclnxt;	/* The rest of the ! subst words */
70int	exclc;			/* Count of remainig words in ! subst */
71tchar *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 */
80bool	hadhist;
81
82tchar 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
87bool
88lex(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
132void
133prlex(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
150void
151copylex(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
173void
174freelex(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
190tchar *
191word(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;
204loop:
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	}
306ret:
307	*wp = 0;
308#ifdef TRACE
309	tprintf("word() returning:%t\n", wbuf);
310#endif
311	return (savestr(wbuf));
312}
313
314tchar
315getC1(int flag)
316{
317	tchar c;
318
319top:
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
373void
374getdol(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	}
469ret:
470	*np = 0;
471	addla(name);
472	return;
473
474vsyn:
475	seterr("Variable syntax");
476	goto ret;
477}
478
479void
480addla(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
505tchar	lhsb[256];
506tchar	slhs[256];
507tchar	rhsb[512];
508int	quesarg;
509
510void
511getexcl(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	}
571subst:
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
590struct wordent *
591getsub(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);
643bads:
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);
717ret:
718	return (en);
719}
720
721struct wordent *
722dosub(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
751tchar *
752subword(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) {
807ovflo:
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
820tchar *
821domod(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
868int
869matchs(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
880int
881getsel(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) {
959bad:
960		seterr("Bad ! arg selector");
961		return (0);
962	}
963	return (1);
964
965}
966
967struct wordent *
968gethent(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	}
1070skip:
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
1082struct Hist *
1083findev(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
1126void
1127noev(tchar *cp)
1128{
1129
1130#ifdef TRACE
1131	tprintf("TRACE- noev()\n");
1132#endif
1133	seterr2(cp, ": Event not found");
1134}
1135
1136void
1137setexclp(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
1148void
1149unreadc(tchar c)
1150{
1151
1152	peekread = c;
1153}
1154
1155int
1156readc(bool wanteof)
1157{
1158	int c;
1159	static int sincereal;
1160
1161	if (c = peekread) {
1162		peekread = 0;
1163		return (c);
1164	}
1165top:
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		}
1216reread:
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);
1237printf("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			}
1251oops:
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
1262static void
1263expand_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
1278int
1279bgetc(void)
1280{
1281	int buf, off, c;
1282#ifdef FILEC
1283	tchar ttyline[BUFSIZ + MB_LEN_MAX]; /* read_() can return extra bytes */
1284	int roomleft;
1285#endif
1286
1287#ifdef TELL
1288	if (cantell) {
1289		if (fseekp < fbobp || fseekp > feobp) {
1290			fbobp = feobp = fseekp;
1291			(void) lseek(SHIN, fseekp, 0);
1292		}
1293		if (fseekp == feobp) {
1294			fbobp = feobp;
1295			do
1296				c = read_(SHIN, fbuf[0], BUFSIZ);
1297			while (c < 0 && errno == EINTR);
1298			if (c <= 0)
1299				return (-1);
1300			feobp += c;
1301		}
1302		c = fbuf[0][fseekp - fbobp];
1303		fseekp++;
1304		return (c);
1305	}
1306#endif
1307again:
1308	buf = (int)fseekp / BUFSIZ;
1309	if (buf >= fblocks) {
1310		expand_fbuf();
1311		goto again;
1312	}
1313	if (fseekp >= feobp) {
1314		buf = (int)feobp / BUFSIZ;
1315		off = (int)feobp % BUFSIZ;
1316#ifndef FILEC
1317		for (;;) {
1318			c = read_(SHIN, fbuf[buf] + off, BUFSIZ - off);
1319#else
1320		roomleft = BUFSIZ - off;
1321		for (;;) {
1322			if (filec && intty) {
1323				c = tenex(ttyline, BUFSIZ);
1324				if (c > roomleft) {
1325					expand_fbuf();
1326					copy(fbuf[buf] + off, ttyline,
1327					    roomleft * sizeof (tchar));
1328					copy(fbuf[buf + 1], ttyline + roomleft,
1329					    (c - roomleft) * sizeof (tchar));
1330				} else if (c > 0) {
1331					copy(fbuf[buf] + off, ttyline,
1332						c * sizeof (tchar));
1333				}
1334			} else {
1335				c = read_(SHIN, fbuf[buf] + off, roomleft);
1336				if (c > roomleft) {
1337					expand_fbuf();
1338					copy(fbuf[buf + 1],
1339					    fbuf[buf] + off + roomleft,
1340					    (c - roomleft) * sizeof (tchar));
1341				}
1342			}
1343#endif
1344			if (c >= 0)
1345				break;
1346			if (errno == EWOULDBLOCK) {
1347				int off = 0;
1348
1349				(void) ioctl(SHIN, FIONBIO,  (char *)&off);
1350			} else if (errno != EINTR)
1351				break;
1352		}
1353		if (c <= 0)
1354			return (-1);
1355		feobp += c;
1356#ifndef FILEC
1357		goto again;
1358#else
1359		if (filec && !intty)
1360			goto again;
1361#endif
1362	}
1363	c = fbuf[buf][(int)fseekp % BUFSIZ];
1364	fseekp++;
1365	return (c);
1366}
1367
1368void
1369bfree(void)
1370{
1371	int sb, i;
1372
1373#ifdef TELL
1374	if (cantell)
1375		return;
1376#endif
1377	if (whyles)
1378		return;
1379	sb = (int)(fseekp - 1) / BUFSIZ;
1380	if (sb > 0) {
1381		for (i = 0; i < sb; i++)
1382			xfree(fbuf[i]);
1383		(void) blkcpy(fbuf, &fbuf[sb]);
1384		fseekp -= BUFSIZ * sb;
1385		feobp -= BUFSIZ * sb;
1386		fblocks -= sb;
1387	}
1388}
1389
1390void
1391bseek(off_t l)
1392{
1393	struct whyle *wp;
1394
1395	fseekp = l;
1396#ifdef TELL
1397	if (!cantell) {
1398#endif
1399		if (!whyles)
1400			return;
1401		for (wp = whyles; wp->w_next; wp = wp->w_next)
1402			continue;
1403		if (wp->w_start > l)
1404			l = wp->w_start;
1405#ifdef TELL
1406	}
1407#endif
1408}
1409
1410/* any similarity to bell telephone is purely accidental */
1411#ifndef btell
1412off_t
1413btell(void)
1414{
1415
1416	return (fseekp);
1417}
1418#endif
1419
1420void
1421btoeof(void)
1422{
1423
1424	(void) lseek(SHIN, (off_t)0, 2);
1425	fseekp = feobp;
1426	wfree();
1427	bfree();
1428}
1429
1430#ifdef TELL
1431void
1432settell(void)
1433{
1434
1435	cantell = 0;
1436	if (arginp || onelflg || intty)
1437		return;
1438	if (lseek(SHIN, (off_t)0, 1) < 0 || errno == ESPIPE)
1439		return;
1440	fbuf = (tchar **)xcalloc(2, sizeof (tchar **));
1441	fblocks = 1;
1442	fbuf[0] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX, sizeof (tchar));
1443	fseekp = fbobp = feobp = lseek(SHIN, (off_t)0, 1);
1444	cantell = 1;
1445}
1446#endif
1447