xref: /illumos-gate/usr/src/cmd/csh/sh.func.c (revision 7939798324de96253652dd33a2a42e25e4ab61f9)
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 #include "sh.h"
16 #include <locale.h>	/* For LC_ALL */
17 #include "sh.tconst.h"
18 #include <sys/types.h>
19 #include <stdlib.h>
20 
21 /*
22  * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0.  In
23  * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new.  Beware of consusing
24  * the keywords that the command prints for these two.  The old one was
25  * "memoryuse" and the new one is "memorysize".  Note also that a given limit
26  * doesn't necessarily appear in the same position in the two releases.
27  */
28 struct limits {
29 	int	limconst;
30 	tchar *limname;
31 	int	limdiv;
32 	tchar *limscale;
33 } limits[] = {
34 	RLIMIT_CPU,	S_cputime,	/* "cputime" */
35 		1,	S_seconds,	/* "seconds" */
36 	RLIMIT_FSIZE,	S_filesize,	/* "filesize" */
37 		1024,	S_kbytes,	/* "kbytes" */
38 	RLIMIT_DATA,	S_datasize,	/* "datasize" */
39 		1024,	S_kbytes,	/* "kbytes" */
40 	RLIMIT_STACK,	S_stacksize,	/* "stacksize" */
41 		1024,	S_kbytes,	/* "kbytes" */
42 	RLIMIT_CORE,	S_coredumpsize, /* "coredumpsize" */
43 		1024,	S_kbytes,	/* "kbytes" */
44 	RLIMIT_NOFILE,	S_descriptors,	/* "descriptors" */
45 		1,	S_,		/* "" */
46 	RLIMIT_VMEM,	S_memorysize,	/* "memorysize" */
47 		1024,	S_kbytes,	/* "kbytes" */
48 	-1,		0,
49 };
50 
51 struct Bin	B;
52 struct whyle	*whyles;
53 bool		chkstop;
54 bool		doneinp;
55 bool		intty;
56 bool		setintr;
57 int		shpgrp;
58 int		opgrp;
59 off_t		lineloc;
60 tchar		*evalp;
61 tchar		**evalvec;
62 tchar		*gointr;
63 
64 
65 static int getval(struct limits *lp, tchar **v, rlim_t *);
66 void islogin(void);
67 int dolabel(void);
68 void reexecute(struct command *kp);
69 void preread_(void);
70 void doagain(void);
71 void toend(void);
72 void wfree(void);
73 void echo(tchar sep, tchar **v);
74 void local_setenv(tchar *name, tchar *val);
75 void local_unsetenv(tchar *name);
76 void limtail(tchar *cp, tchar *str0);
77 void plim(struct limits *lp, tchar hard);
78 void search();
79 
80 #define	BUFSZ	1028
81 
82 /*
83  * C shell
84  */
85 
86 struct biltins *
87 isbfunc(struct command *t)
88 {
89 	tchar *cp = t->t_dcom[0];
90 	struct biltins *bp, *bp1, *bp2;
91 	int dofg1(), dobg1();
92 
93 	static struct biltins label = { S_, dolabel, 0, 0 };
94 	static struct biltins foregnd = { S_Pjob, dofg1, 0, 0 };
95 	static struct biltins backgnd = { S_PjobAND, dobg1, 0, 0 };
96 #ifdef TRACE
97 	tprintf("TRACE- isbfunc()\n");
98 #endif
99 	if (lastchr(cp) == ':') {
100 		label.bname = cp;
101 		return (&label);
102 	}
103 	if (*cp == '%') {
104 		if (t->t_dflg & FAND) {
105 			t->t_dflg &= ~FAND;
106 			backgnd.bname = cp;
107 			return (&backgnd);
108 		}
109 		foregnd.bname = cp;
110 		return (&foregnd);
111 	}
112 	/*
113 	 * Binary search
114 	 * Bp1 is the beginning of the current search range.
115 	 * Bp2 is one past the end.
116 	 */
117 	for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2; ) {
118 		int i;
119 
120 		bp = bp1 + (bp2 - bp1 >> 1);
121 		if ((i = *cp - *bp->bname) == 0 &&
122 		    (i = strcmp_(cp, bp->bname)) == 0) {
123 			return (bp);
124 		}
125 		if (i < 0) {
126 			bp2 = bp;
127 		} else {
128 			bp1 = bp + 1;
129 		}
130 	}
131 	return (0);
132 }
133 
134 void
135 func(struct command *t, struct biltins *bp)
136 {
137 	int i;
138 
139 #ifdef TRACE
140 	tprintf("TRACE- func()\n");
141 #endif
142 	xechoit(t->t_dcom);
143 	setname(bp->bname);
144 	i = blklen(t->t_dcom) - 1;
145 	if (i < bp->minargs) {
146 		bferr("Too few arguments");
147 	}
148 	if (i > bp->maxargs) {
149 		bferr("Too many arguments");
150 	}
151 	(*bp->bfunct)(t->t_dcom, t);
152 }
153 
154 int
155 dolabel(void)
156 {
157 #ifdef TRACE
158 	tprintf("TRACE- dolabel()\n");
159 #endif
160 	return (0);
161 }
162 
163 void
164 doonintr(tchar **v)
165 {
166 	tchar *cp;
167 	tchar *vv = v[1];
168 
169 #ifdef TRACE
170 	tprintf("TRACE- doonintr()\n");
171 #endif
172 	if (parintr == SIG_IGN) {
173 		return;
174 	}
175 	if (setintr && intty) {
176 		bferr("Can't from terminal");
177 	}
178 	cp = gointr, gointr = 0, xfree(cp);
179 	if (vv == 0) {
180 		if (setintr) {
181 			(void) sigblock(sigmask(SIGINT));
182 		} else {
183 			(void) signal(SIGINT, SIG_DFL);
184 		}
185 		gointr = 0;
186 	} else if (eq((vv = strip(vv)), S_MINUS)) {
187 		(void) signal(SIGINT, SIG_IGN);
188 		gointr = S_MINUS;
189 	} else {
190 		gointr = savestr(vv);
191 		(void) signal(SIGINT, pintr);
192 	}
193 }
194 
195 void
196 donohup(void)
197 {
198 
199 #ifdef TRACE
200 	tprintf("TRACE- donohup()\n");
201 #endif
202 	if (intty) {
203 		bferr("Can't from terminal");
204 	}
205 	if (setintr == 0) {
206 		(void) signal(SIGHUP, SIG_IGN);
207 #ifdef CC
208 		submit(getpid());
209 #endif
210 	}
211 }
212 
213 void
214 dozip(void)
215 {
216 	;
217 }
218 
219 void
220 prvars(void)
221 {
222 #ifdef TRACE
223 	tprintf("TRACE- prvars()\n");
224 #endif
225 
226 	plist(&shvhed);
227 }
228 
229 void
230 doalias(tchar **v)
231 {
232 	struct varent *vp;
233 	tchar *p;
234 
235 #ifdef TRACE
236 	tprintf("TRACE- doalias()\n");
237 #endif
238 	v++;
239 	p = *v++;
240 	if (p == 0) {
241 		plist(&aliases);
242 	} else if (*v == 0) {
243 		vp = adrof1(strip(p), &aliases);
244 		if (vp) {
245 			blkpr(vp->vec), printf("\n");
246 		}
247 	} else {
248 		if (eq(p, S_alias) ||
249 		    eq(p, S_unalias)) {
250 			setname(p);
251 			bferr("Too dangerous to alias that");
252 		}
253 		set1(strip(p), saveblk(v), &aliases);
254 	}
255 }
256 
257 void
258 unalias(tchar **v)
259 {
260 
261 #ifdef TRACE
262 	tprintf("TRACE- unalias()\n");
263 #endif
264 	unset1(v, &aliases);
265 }
266 
267 void
268 dologout(void)
269 {
270 
271 #ifdef TRACE
272 	tprintf("TRACE- dologout()\n");
273 #endif
274 	islogin();
275 	goodbye();
276 }
277 
278 void
279 dologin(tchar **v)
280 {
281 
282 	char *v_;	/* work */
283 #ifdef TRACE
284 	tprintf("TRACE- dologin()\n");
285 #endif
286 	islogin();
287 	rechist();
288 	(void) signal(SIGTERM, parterm);
289 	if (v[1] != NULL) {
290 		v_ = tstostr(NULL, v[1]);	/* No need to free */
291 	} else {
292 		v_ = 0;
293 	}
294 	execl("/bin/login", "login", v_, 0);
295 	untty();
296 	exit(1);
297 }
298 
299 #ifdef NEWGRP
300 void
301 donewgrp(tchar **v)
302 {
303 
304 	char *v_;	/* work */
305 #ifdef TRACE
306 	tprintf("TRACE- donewgrp()\n");
307 #endif
308 	if (chkstop == 0 && setintr) {
309 		panystop(0);
310 	}
311 	(void) signal(SIGTERM, parterm);
312 
313 	if (v[1] != NULL) {
314 		v_ = tstostr(NOSTR, v[1]);	/* No need to free */
315 	} else {
316 		v_ = 0;
317 	}
318 	execl("/bin/newgrp", "newgrp", v_, 0);
319 	execl("/usr/bin/newgrp", "newgrp", v_, 0);
320 	untty();
321 	exit(1);
322 }
323 #endif
324 
325 void
326 islogin(void)
327 {
328 
329 #ifdef TRACE
330 	tprintf("TRACE- islogin()\n");
331 #endif
332 	if (chkstop == 0 && setintr) {
333 		panystop(0);
334 	}
335 	if (loginsh) {
336 		return;
337 	}
338 	error("Not login shell");
339 }
340 
341 void
342 doif(tchar **v, struct command *kp)
343 {
344 	int i;
345 	tchar **vv;
346 
347 #ifdef TRACE
348 	tprintf("TRACE- doif()\n");
349 #endif
350 	v++;
351 	i = exp(&v);
352 	vv = v;
353 	if (*vv == NOSTR) {
354 		bferr("Empty if");
355 	}
356 	if (eq(*vv, S_then)) {
357 		if (*++vv) {
358 			bferr("Improper then");
359 		}
360 		setname(S_then);
361 		/*
362 		 * If expression was zero, then scan to else,
363 		 * otherwise just fall into following code.
364 		 */
365 		if (!i) {
366 			search(ZIF, 0);
367 		}
368 		return;
369 	}
370 	/*
371 	 * Simple command attached to this if.
372 	 * Left shift the node in this tree, munging it
373 	 * so we can reexecute it.
374 	 */
375 	if (i) {
376 		lshift(kp->t_dcom, vv - kp->t_dcom);
377 		reexecute(kp);
378 		donefds();
379 	}
380 }
381 
382 /*
383  * Reexecute a command, being careful not
384  * to redo i/o redirection, which is already set up.
385  */
386 void
387 reexecute(struct command *kp)
388 {
389 
390 #ifdef TRACE
391 	tprintf("TRACE- reexecute()\n");
392 #endif
393 	kp->t_dflg &= FSAVE;
394 	kp->t_dflg |= FREDO;
395 	/*
396 	 * If tty is still ours to arbitrate, arbitrate it;
397 	 * otherwise dont even set pgrp's as the jobs would
398 	 * then have no way to get the tty (we can't give it
399 	 * to them, and our parent wouldn't know their pgrp, etc.
400 	 */
401 	execute(kp, tpgrp > 0 ? tpgrp : -1);
402 }
403 
404 void
405 doelse(void)
406 {
407 
408 #ifdef TRACE
409 	tprintf("TRACE- doelse()\n");
410 #endif
411 	search(ZELSE, 0);
412 }
413 
414 void
415 dogoto(tchar **v)
416 {
417 	struct whyle *wp;
418 	tchar *lp;
419 #ifdef TRACE
420 	tprintf("TRACE- dogoto()\n");
421 #endif
422 
423 	/*
424 	 * While we still can, locate any unknown ends of existing loops.
425 	 * This obscure code is the WORST result of the fact that we
426 	 * don't really parse.
427 	 */
428 	for (wp = whyles; wp; wp = wp->w_next) {
429 		if (wp->w_end == 0) {
430 			search(ZBREAK, 0);
431 			wp->w_end = btell();
432 		} else {
433 			bseek(wp->w_end);
434 		}
435 	}
436 	search(ZGOTO, 0, lp = globone(v[1]));
437 	xfree(lp);
438 	/*
439 	 * Eliminate loops which were exited.
440 	 */
441 	wfree();
442 }
443 
444 void
445 doswitch(tchar **v)
446 {
447 	tchar *cp, *lp;
448 
449 #ifdef TRACE
450 	tprintf("TRACE- doswitch()\n");
451 #endif
452 	v++;
453 	if (!*v || *(*v++) != '(') {
454 		goto syntax;
455 	}
456 	cp = **v == ')' ? S_ : *v++;
457 	if (*(*v++) != ')') {
458 		v--;
459 	}
460 	if (*v) {
461 syntax:
462 		error("Syntax error");
463 	}
464 	search(ZSWITCH, 0, lp = globone(cp));
465 	xfree(lp);
466 }
467 
468 void
469 dobreak(void)
470 {
471 
472 #ifdef TRACE
473 	tprintf("TRACE- dobreak()\n");
474 #endif
475 	if (whyles) {
476 		toend();
477 	} else {
478 		bferr("Not in while/foreach");
479 	}
480 }
481 
482 void
483 doexit(tchar **v)
484 {
485 
486 #ifdef TRACE
487 	tprintf("TRACE- doexit()\n");
488 #endif
489 	if (chkstop == 0) {
490 		panystop(0);
491 	}
492 	/*
493 	 * Don't DEMAND parentheses here either.
494 	 */
495 	v++;
496 	if (*v) {
497 		set(S_status, putn(exp(&v)));
498 		if (*v) {
499 			bferr("Expression syntax");
500 		}
501 	}
502 	btoeof();
503 	if (intty) {
504 		(void) close(SHIN);
505 		unsetfd(SHIN);
506 	}
507 }
508 
509 void
510 doforeach(tchar **v)
511 {
512 	tchar *cp;
513 	struct whyle *nwp;
514 
515 #ifdef TRACE
516 	tprintf("TRACE- doforeach()\n");
517 #endif
518 	v++;
519 	cp = strip(*v);
520 	while (*cp && alnum(*cp)) {
521 		cp++;
522 	}
523 	if (*cp || strlen_(*v) >= MAX_VAR_LEN || !letter(**v)) {
524 		bferr("Invalid variable");
525 	}
526 	cp = *v++;
527 	if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') {
528 		bferr("Words not ()'ed");
529 	}
530 	v++;
531 	gflag = 0, tglob(v);
532 	v = glob(v);
533 	if (v == 0) {
534 		bferr("No match");
535 	}
536 	nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));
537 	nwp->w_fe = nwp->w_fe0 = v; gargv = 0;
538 	nwp->w_start = btell();
539 	nwp->w_fename = savestr(cp);
540 	nwp->w_next = whyles;
541 	whyles = nwp;
542 	/*
543 	 * Pre-read the loop so as to be more
544 	 * comprehensible to a terminal user.
545 	 */
546 	if (intty) {
547 		preread_();
548 	}
549 	doagain();
550 }
551 
552 void
553 dowhile(tchar **v)
554 {
555 	int status;
556 	bool again = whyles != 0 && whyles->w_start == lineloc &&
557 	    whyles->w_fename == 0;
558 
559 #ifdef TRACE
560 	tprintf("TRACE- dowhile()\n");
561 #endif
562 	v++;
563 	/*
564 	 * Implement prereading here also, taking care not to
565 	 * evaluate the expression before the loop has been read up
566 	 * from a terminal.
567 	 */
568 	if (intty && !again) {
569 		status = !exp0(&v, 1);
570 	} else {
571 		status = !exp(&v);
572 	}
573 	if (*v) {
574 		bferr("Expression syntax");
575 	}
576 	if (!again) {
577 		struct whyle *nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));
578 
579 		nwp->w_start = lineloc;
580 		nwp->w_end = 0;
581 		nwp->w_next = whyles;
582 		whyles = nwp;
583 		if (intty) {
584 			/*
585 			 * The tty preread
586 			 */
587 			preread_();
588 			doagain();
589 			return;
590 		}
591 	}
592 	if (status) {
593 		/* We ain't gonna loop no more, no more! */
594 		toend();
595 	}
596 }
597 
598 void
599 preread_(void)
600 {
601 #ifdef TRACE
602 	tprintf("TRACE- preread()\n");
603 #endif
604 
605 	whyles->w_end = -1;
606 	if (setintr) {
607 		(void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
608 	}
609 	search(ZBREAK, 0);
610 	if (setintr) {
611 		(void) sigblock(sigmask(SIGINT));
612 	}
613 	whyles->w_end = btell();
614 }
615 
616 void
617 doend(void)
618 {
619 
620 #ifdef TRACE
621 	tprintf("TRACE- doend()\n");
622 #endif
623 	if (!whyles) {
624 		bferr("Not in while/foreach");
625 	}
626 	whyles->w_end = btell();
627 	doagain();
628 }
629 
630 void
631 docontin(void)
632 {
633 #ifdef TRACE
634 	tprintf("TRACE- docontin()\n");
635 #endif
636 
637 	if (!whyles) {
638 		bferr("Not in while/foreach");
639 	}
640 	doagain();
641 }
642 
643 void
644 doagain(void)
645 {
646 
647 #ifdef TRACE
648 	tprintf("TRACE- doagain()\n");
649 #endif
650 	/* Repeating a while is simple */
651 	if (whyles->w_fename == 0) {
652 		bseek(whyles->w_start);
653 		return;
654 	}
655 	/*
656 	 * The foreach variable list actually has a spurious word
657 	 * ")" at the end of the w_fe list.  Thus we are at the
658 	 * of the list if one word beyond this is 0.
659 	 */
660 	if (!whyles->w_fe[1]) {
661 		dobreak();
662 		return;
663 	}
664 	set(whyles->w_fename, savestr(*whyles->w_fe++));
665 	bseek(whyles->w_start);
666 }
667 
668 void
669 dorepeat(tchar **v, struct command *kp)
670 {
671 	int i, omask;
672 
673 #ifdef TRACE
674 	tprintf("TRACE- dorepeat()\n");
675 #endif
676 	i = getn(v[1]);
677 	if (setintr) {
678 		omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
679 	}
680 	lshift(v, 2);
681 	while (i > 0) {
682 		if (setintr) {
683 			(void) sigsetmask(omask);
684 		}
685 		reexecute(kp);
686 		--i;
687 	}
688 	donefds();
689 	if (setintr) {
690 		(void) sigsetmask(omask);
691 	}
692 }
693 
694 void
695 doswbrk(void)
696 {
697 
698 #ifdef TRACE
699 	tprintf("TRACE- doswbrk()\n");
700 #endif
701 	search(ZBRKSW, 0);
702 }
703 
704 int
705 srchx(tchar *cp)
706 {
707 	struct srch *sp, *sp1, *sp2;
708 	int i;
709 
710 #ifdef TRACE
711 	tprintf("TRACE- srchx()\n");
712 #endif
713 	/*
714 	 * Binary search
715 	 * Sp1 is the beginning of the current search range.
716 	 * Sp2 is one past the end.
717 	 */
718 	for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2; ) {
719 		sp = sp1 + (sp2 - sp1 >> 1);
720 		if ((i = *cp - *sp->s_name) == 0 &&
721 		    (i = strcmp_(cp, sp->s_name)) == 0) {
722 			return (sp->s_value);
723 		}
724 		if (i < 0) {
725 			sp2 = sp;
726 		} else {
727 			sp1 = sp + 1;
728 		}
729 	}
730 	return (-1);
731 }
732 
733 tchar Stype;
734 tchar *Sgoal;
735 
736 /*VARARGS2*/
737 void
738 search(type, level, goal)
739 	int type; int level; tchar *goal;
740 {
741 	tchar wordbuf[BUFSIZ];
742 	tchar *aword = wordbuf;
743 	tchar *cp;
744 
745 #ifdef TRACE
746 	tprintf("TRACE- search()\n");
747 #endif
748 	Stype = type; Sgoal = goal;
749 	if (type == ZGOTO) {
750 		bseek((off_t)0);
751 	}
752 	do {
753 		if (intty && fseekp == feobp) {
754 			printf("? "), flush();
755 		}
756 		aword[0] = 0;
757 		(void) getword(aword);
758 
759 		switch (srchx(aword)) {
760 
761 		case ZELSE:
762 			if (level == 0 && type == ZIF) {
763 				return;
764 			}
765 			break;
766 
767 		case ZIF:
768 			while (getword(aword)) {
769 				continue;
770 			}
771 			if ((type == ZIF || type == ZELSE) &&
772 			    eq(aword, S_then)) {
773 				level++;
774 			}
775 			break;
776 
777 		case ZENDIF:
778 			if (type == ZIF || type == ZELSE) {
779 				level--;
780 			}
781 			break;
782 
783 		case ZFOREACH:
784 		case ZWHILE:
785 			if (type == ZBREAK) {
786 				level++;
787 			}
788 			break;
789 
790 		case ZEND:
791 			if (type == ZBREAK) {
792 				level--;
793 			}
794 			break;
795 
796 		case ZSWITCH:
797 			if (type == ZSWITCH || type == ZBRKSW) {
798 				level++;
799 			}
800 			break;
801 
802 		case ZENDSW:
803 			if (type == ZSWITCH || type == ZBRKSW) {
804 				level--;
805 			}
806 			break;
807 
808 		case ZLABEL:
809 			if (type == ZGOTO && getword(aword) &&
810 			    eq(aword, goal)) {
811 				level = -1;
812 			}
813 			break;
814 
815 		default:
816 			if (type != ZGOTO && (type != ZSWITCH || level != 0)) {
817 				break;
818 			}
819 			if (lastchr(aword) != ':') {
820 				break;
821 			}
822 			aword[strlen_(aword) - 1] = 0;
823 			if (type == ZGOTO && eq(aword, goal) ||
824 			    type == ZSWITCH && eq(aword, S_default)) {
825 				level = -1;
826 			}
827 			break;
828 
829 		case ZCASE:
830 			if (type != ZSWITCH || level != 0) {
831 				break;
832 			}
833 			(void) getword(aword);
834 			if (lastchr(aword) == ':') {
835 				aword[strlen_(aword) - 1] = 0;
836 			}
837 			cp = strip(Dfix1(aword));
838 			if (Gmatch(goal, cp)) {
839 				level = -1;
840 			}
841 			xfree(cp);
842 			break;
843 
844 		case ZDEFAULT:
845 			if (type == ZSWITCH && level == 0) {
846 				level = -1;
847 			}
848 			break;
849 		}
850 		(void) getword(NOSTR);
851 	} while (level >= 0);
852 }
853 
854 int
855 getword(tchar *wp)
856 {
857 	int found = 0;
858 	int c, d;
859 #ifdef TRACE
860 	tprintf("TRACE- getword()\n");
861 #endif
862 
863 	c = readc(1);
864 	d = 0;
865 	do {
866 		while (issp(c)) {
867 			c = readc(1);
868 		}
869 		if (c == '#') {
870 			do {
871 				c = readc(1);
872 			} while (c >= 0 && c != '\n');
873 		}
874 		if (c < 0) {
875 			goto past;
876 		}
877 		if (c == '\n') {
878 			if (wp) {
879 				break;
880 			}
881 			return (0);
882 		}
883 
884 		/* ( and ) form separate words */
885 		if (c == '(' || c == ')') {
886 			return (1);
887 		}
888 
889 		unreadc(c);
890 		found = 1;
891 		do {
892 			c = readc(1);
893 			if (c == '\\' && (c = readc(1)) == '\n') {
894 				c = ' ';
895 			}
896 			if (c == '\'' || c == '"') {
897 				if (d == 0) {
898 					d = c;
899 				} else if (d == c) {
900 					d = 0;
901 				}
902 			}
903 			if (c < 0) {
904 				goto past;
905 			}
906 			if (wp) {
907 				*wp++ = c;
908 			}
909 		} while ((d || !issp(c) && c != '(' && c != ')') && c != '\n');
910 	} while (wp == 0);
911 	unreadc(c);
912 	if (found) {
913 		*--wp = 0;
914 	}
915 	return (found);
916 
917 past:
918 	switch (Stype) {
919 
920 	case ZIF:
921 		bferr("then/endif not found");
922 
923 	case ZELSE:
924 		bferr("endif not found");
925 
926 	case ZBRKSW:
927 	case ZSWITCH:
928 		bferr("endsw not found");
929 
930 	case ZBREAK:
931 		bferr("end not found");
932 
933 	case ZGOTO:
934 		setname(Sgoal);
935 		bferr("label not found");
936 	}
937 	/*NOTREACHED*/
938 
939 	return (0);
940 }
941 
942 void
943 toend(void)
944 {
945 
946 #ifdef TRACE
947 	tprintf("TRACE- toend()\n");
948 #endif
949 	if (whyles->w_end == 0) {
950 		search(ZBREAK, 0);
951 		whyles->w_end = btell() - 1;
952 	} else {
953 		bseek(whyles->w_end);
954 	}
955 	wfree();
956 }
957 
958 void
959 wfree(void)
960 {
961 	long o = btell();
962 
963 #ifdef TRACE
964 	tprintf("TRACE- wfree()\n");
965 #endif
966 	while (whyles) {
967 		struct whyle *wp = whyles;
968 		struct whyle *nwp = wp->w_next;
969 
970 		if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) {
971 			break;
972 		}
973 		if (wp->w_fe0) {
974 			blkfree(wp->w_fe0);
975 		}
976 		if (wp->w_fename) {
977 			xfree(wp->w_fename);
978 		}
979 		xfree((char *)wp);
980 		whyles = nwp;
981 	}
982 }
983 
984 void
985 doecho(tchar **v)
986 {
987 
988 #ifdef TRACE
989 	tprintf("TRACE- doecho()\n");
990 #endif
991 	echo(' ', v);
992 }
993 
994 void
995 doglob(tchar **v)
996 {
997 
998 #ifdef TRACE
999 	tprintf("TRACE- doglob()\n");
1000 #endif
1001 	echo(0, v);
1002 	flush();
1003 }
1004 
1005 void
1006 echo(tchar sep, tchar **v)
1007 {
1008 	tchar *cp;
1009 	int nonl = 0;
1010 
1011 #ifdef TRACE
1012 	tprintf("TRACE- echo()\n");
1013 #endif
1014 	if (setintr) {
1015 		(void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
1016 	}
1017 	v++;
1018 	if (*v == 0) {
1019 		/*
1020 		 * echo command needs to have newline when there are no
1021 		 * flags or arguments.  glob should have no newline.  If
1022 		 * the separator is a blank, we are doing an echo.  If the
1023 		 * separator is zero, we are globbing.
1024 		 */
1025 		if (sep == (tchar)' ')
1026 			Putchar('\n');
1027 		return;
1028 	}
1029 	gflag = 0, tglob(v);
1030 	if (gflag) {
1031 		v = glob(v);
1032 		if (v == 0) {
1033 			bferr("No match");
1034 		}
1035 	}
1036 	/* check for -n arg, NOTE: it might be quoted */
1037 	if (sep == ' ' && *v && strlen_(*v) == 2 &&
1038 	    ((**v&TRIM) == '-' && (*(*v + 1) & TRIM) == 'n' &&
1039 	    (*(*v+2)&TRIM) == 0)) {
1040 		nonl++, v++;
1041 	}
1042 	while (cp = *v++) {
1043 		int c;
1044 
1045 		while (c = *cp++) {
1046 			Putchar(c | QUOTE);
1047 		}
1048 		if (*v) {
1049 			Putchar(sep | QUOTE);
1050 		}
1051 	}
1052 	if (sep && nonl == 0) {
1053 		Putchar('\n');
1054 	} else {
1055 		flush();
1056 	}
1057 	if (setintr) {
1058 		(void) sigblock(sigmask(SIGINT));
1059 	}
1060 	if (gargv) {
1061 		blkfree(gargv), gargv = 0;
1062 	}
1063 }
1064 
1065 extern char **environ;
1066 
1067 /*
1068  * Check if the environment variable vp affects this csh's behavior
1069  * and therefore we should call setlocale() or not.
1070  * This function has two side effects when it returns 1:
1071  *	variable islocalevar_catnum is set to the LC_xxx value.
1072  *	variable islocalevar_catname is set to the string "LC_xxx"
1073  */
1074 static int	islocalevar_catnum;
1075 static char	*islocalevar_catname;
1076 
1077 static
1078 bool
1079 islocalevar(tchar *vp)
1080 {
1081 	static struct lcinfo {
1082 		tchar *	evname; /* The name of the env. var. */
1083 	} categories_we_care[] = {
1084 	    S_LANG, S_LC_ALL, S_LC_CTYPE, S_LC_MESSAGES,
1085 	    NOSTR		/* assumption: LC_xxx >= 0 */
1086 	};
1087 	struct lcinfo *p = categories_we_care;
1088 
1089 	do {
1090 		if (strcmp_(vp, p->evname) == 0) {
1091 			return (1);
1092 		}
1093 	} while (((++p)->evname) != NOSTR);
1094 	return (0);
1095 }
1096 
1097 void
1098 dosetenv(tchar **v)
1099 {
1100 	tchar *vp, *lp;
1101 
1102 #ifdef TRACE
1103 	tprintf("TRACE- dosetenv()\n");
1104 #endif
1105 	v++;
1106 	if ((vp = *v++) == 0) {
1107 		char **ep;
1108 
1109 		if (setintr) {
1110 			(void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT));
1111 		}
1112 		for (ep = environ; *ep; ep++) {
1113 			printf("%s\n", *ep);
1114 		}
1115 		return;
1116 	}
1117 
1118 	if ((lp = *v++) == 0) {
1119 		lp = S_;	/* "" */
1120 	}
1121 	local_setenv(vp, lp = globone(lp));
1122 	if (eq(vp, S_PATH)) {
1123 		importpath(lp);
1124 		dohash(xhash);
1125 	} else if (islocalevar(vp)) {
1126 		if (!setlocale(LC_ALL, "")) {
1127 			error("Locale could not be set properly");
1128 		}
1129 	}
1130 
1131 	xfree(lp);
1132 }
1133 
1134 void
1135 dounsetenv(tchar **v)
1136 {
1137 #ifdef TRACE
1138 	tprintf("TRACE- dounsetenv()\n");
1139 #endif
1140 	v++;
1141 	do {
1142 		local_unsetenv(*v);
1143 		if (islocalevar(*v++)) {
1144 			setlocale(LC_ALL, "");	/* Hope no error! */
1145 		}
1146 	} while (*v);
1147 }
1148 
1149 void
1150 local_setenv(tchar *name, tchar *val)
1151 {
1152 	char **ep = environ;
1153 	tchar *cp;
1154 	char *dp;
1155 	tchar *ep_;	/* temporary */
1156 	char *blk[2], **oep = ep;
1157 
1158 #ifdef TRACE
1159 	/* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */
1160 	/* printf("IN local_setenv args = (%t)\n", val); */
1161 #endif
1162 	for (; *ep; ep++) {
1163 #ifdef MBCHAR
1164 		for (cp = name, dp = *ep; *cp && *dp; cp++) {
1165 			/*
1166 			 * This loop compares two chars in different
1167 			 * representations, EUC (as char *) and wchar_t
1168 			 * (in tchar), and ends when they are different.
1169 			 */
1170 			wchar_t	dwc;
1171 			int	n;
1172 
1173 			n = mbtowc(&dwc, dp, MB_CUR_MAX);
1174 			if (n <= 0) {
1175 				break; /* Illegal multibyte. */
1176 			}
1177 			dp += n; /* Advance to next multibyte char. */
1178 			if (dwc == (wchar_t)(*cp & TRIM)) {
1179 				continue;
1180 			} else  {
1181 				break;
1182 			}
1183 		}
1184 #else /* !MBCHAR */
1185 		for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
1186 			continue;
1187 		}
1188 #endif /* !MBCHAR */
1189 		if (*cp != 0 || *dp != '=') {
1190 			continue;
1191 		}
1192 		cp = strspl(S_EQ, val);
1193 		xfree(*ep);
1194 		ep_ = strspl(name, cp);		/* ep_ is xalloc'ed */
1195 		xfree(cp);
1196 		/*
1197 		 * Trimming is not needed here.
1198 		 * trim();
1199 		 */
1200 		*ep = tstostr(NULL, ep_);
1201 		xfree(ep_);			/* because temp.  use */
1202 		return;
1203 	}
1204 	ep_ = strspl(name, S_EQ);		/* ep_ is xalloc'ed */
1205 	blk[0] = tstostr(NULL, ep_);
1206 	blk[1] = 0;
1207 	xfree(ep_);
1208 	environ = (char **)blkspl_((char **)environ, blk);
1209 	xfree((void *)oep);
1210 	local_setenv(name, val);
1211 }
1212 
1213 void
1214 local_unsetenv(tchar *name)
1215 {
1216 	char **ep = environ;
1217 	tchar *cp;
1218 	char *dp;
1219 	char **oep = ep;
1220 	char *cp_;	/* tmp use */
1221 	static int cnt = 0;	/* delete counter */
1222 
1223 #ifdef TRACE
1224 	tprintf("TRACE- local_unsetenv()\n");
1225 #endif
1226 	for (; *ep; ep++) {
1227 #ifdef MBCHAR
1228 		for (cp = name, dp = *ep; *cp && *dp; cp++) {
1229 			/*
1230 			 * This loop compares two chars in different
1231 			 * representations, EUC (as char *) and wchar_t
1232 			 * (in tchar), and ends when they are different.
1233 			 */
1234 			wchar_t	dwc;
1235 			int	n;
1236 
1237 			n = mbtowc(&dwc, dp, MB_CUR_MAX);
1238 			if (n <= 0) {
1239 				break; /* Illegal multibyte. */
1240 			}
1241 			dp += n; /* Advance to next multibyte char. */
1242 			if (dwc == (wchar_t)(*cp & TRIM)) {
1243 				continue;
1244 			} else {
1245 				break;
1246 			}
1247 		}
1248 #else /* !MBCHAR */
1249 		for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
1250 			continue;
1251 		}
1252 #endif /* !MBCHAR */
1253 		if (*cp != 0 || *dp != '=') {
1254 			continue;
1255 		}
1256 		cp_ = *ep;
1257 		*ep = 0;
1258 		environ = (char **)blkspl_((char **)environ, ep+1);
1259 		*ep = cp_;
1260 		xfree(cp_);
1261 		xfree((void *)oep);
1262 		return;
1263 	}
1264 }
1265 
1266 void
1267 doumask(tchar **v)
1268 {
1269 	tchar *cp = v[1];
1270 	int i;
1271 
1272 #ifdef TRACE
1273 	tprintf("TRACE- dounmask()\n");
1274 #endif
1275 	if (cp == 0) {
1276 		i = umask(0);
1277 		(void) umask(i);
1278 		printf("%o\n", i);
1279 		return;
1280 	}
1281 	i = 0;
1282 	while (digit(*cp) && *cp != '8' && *cp != '9') {
1283 		i = i * 8 + *cp++ - '0';
1284 	}
1285 	if (*cp || i < 0 || i > 0777) {
1286 		bferr("Improper mask");
1287 	}
1288 	(void) umask(i);
1289 }
1290 
1291 
1292 struct limits *
1293 findlim(tchar *cp)
1294 {
1295 	struct limits *lp, *res;
1296 
1297 #ifdef TRACE
1298 	tprintf("TRACE- findlim()\n");
1299 #endif
1300 	res = 0;
1301 	for (lp = limits; lp->limconst >= 0; lp++) {
1302 		if (prefix(cp, lp->limname)) {
1303 			if (res) {
1304 				bferr("Ambiguous");
1305 			}
1306 			res = lp;
1307 		}
1308 	}
1309 	if (res) {
1310 		return (res);
1311 	}
1312 	bferr("No such limit");
1313 	/*NOTREACHED*/
1314 }
1315 
1316 void
1317 dolimit(tchar **v)
1318 {
1319 	struct limits *lp;
1320 	rlim_t limit;
1321 	tchar hard = 0;
1322 
1323 #ifdef TRACE
1324 	tprintf("TRACE- dolimit()\n");
1325 #endif
1326 	v++;
1327 	if (*v && eq(*v, S_h)) {
1328 		hard = 1;
1329 		v++;
1330 	}
1331 	if (*v == 0) {
1332 		for (lp = limits; lp->limconst >= 0; lp++) {
1333 			plim(lp, hard);
1334 		}
1335 		return;
1336 	}
1337 	lp = findlim(v[0]);
1338 	if (v[1] == 0) {
1339 		plim(lp,  hard);
1340 		return;
1341 	}
1342 	switch (getval(lp, v+1, &limit)) {
1343 	case 0:
1344 		error("Value specified for limit is too large");
1345 		return;
1346 	case (-1):
1347 		error("Numeric conversion failed");
1348 		return;
1349 	default:
1350 		if (setlim(lp, hard, limit) < 0) {
1351 			error(NOSTR);
1352 		}
1353 	}
1354 }
1355 
1356 static int
1357 getval(struct limits *lp, tchar **v, rlim_t *retval)
1358 {
1359 	rlim_t value, tmp, tmp2;
1360 	tchar *cp = *v++;
1361 	char chbuf[BUFSIZ * MB_LEN_MAX];
1362 
1363 #ifdef TRACE
1364 	tprintf("TRACE- getval()\n");
1365 #endif
1366 
1367 	tstostr(chbuf, cp);
1368 	errno = 0;
1369 	value = strtoull(chbuf, NULL, 0);
1370 /*
1371  * we must accept zero, but the conversion can fail and give us
1372  * zero as well...try to deal with it as gracefully as possible
1373  * by checking for EINVAL
1374  */
1375 	if (value == 0 && errno == EINVAL)
1376 		return (-1);
1377 
1378 	while (digit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') {
1379 		cp++;
1380 	}
1381 	if (*cp == 0) {
1382 		if (*v == 0) {
1383 			tmp = value * (rlim_t)lp->limdiv;
1384 			/* Check for overflow */
1385 			if (tmp >= value) {
1386 				*retval = tmp;
1387 				return (1);
1388 			} else {
1389 				return (0);
1390 			}
1391 		}
1392 		cp = *v;
1393 	}
1394 	switch (*cp) {
1395 
1396 	case ':':
1397 		if (lp->limconst != RLIMIT_CPU) {
1398 			goto badscal;
1399 		}
1400 		tstostr(chbuf, cp + 1);
1401 		tmp = strtoull(chbuf, NULL, 0);
1402 		tmp2 = value * 60 + tmp;
1403 		if (tmp2 >= value) {
1404 			*retval = tmp2;
1405 			return (1);
1406 		} else {
1407 			return (0);
1408 		}
1409 
1410 	case 'h':
1411 		if (lp->limconst != RLIMIT_CPU) {
1412 			goto badscal;
1413 		}
1414 		limtail(cp, S_hours);
1415 		tmp = value * 3600;
1416 		if (tmp < value) {
1417 			return (0);
1418 		}
1419 		value = tmp;
1420 		break;
1421 
1422 	case 'm':
1423 		if (lp->limconst == RLIMIT_CPU) {
1424 			limtail(cp, S_minutes);
1425 			tmp = value * 60;
1426 			if (tmp < value) {
1427 				return (0);
1428 			}
1429 			value = tmp;
1430 			break;
1431 		}
1432 	case 'M':
1433 		if (lp->limconst == RLIMIT_CPU) {
1434 			goto badscal;
1435 		}
1436 		*cp = 'm';
1437 		limtail(cp, S_megabytes);
1438 		tmp = value * 1024 * 1024;
1439 		if (tmp < value) {
1440 			return (0);
1441 		}
1442 		value = tmp;
1443 		break;
1444 
1445 	case 's':
1446 		if (lp->limconst != RLIMIT_CPU) {
1447 			goto badscal;
1448 		}
1449 		limtail(cp, S_seconds);
1450 		break;
1451 
1452 	case 'k':
1453 		if (lp->limconst == RLIMIT_CPU) {
1454 			goto badscal;
1455 		}
1456 		limtail(cp, S_kbytes);
1457 		tmp = value * 1024;
1458 		if (tmp < value) {
1459 			return (0);
1460 		}
1461 		value = tmp;
1462 		break;
1463 
1464 	case 'u':
1465 		limtail(cp, S_unlimited);
1466 		*retval = RLIM_INFINITY;
1467 		return (1);
1468 
1469 	default:
1470 badscal:
1471 		bferr("Improper or unknown scale factor");
1472 	}
1473 	*retval = value;
1474 	return (1);
1475 }
1476 
1477 void
1478 limtail(tchar *cp, tchar *str0)
1479 {
1480 	tchar *str = str0;
1481 #ifdef TRACE
1482 	tprintf("TRACE- limtail()\n");
1483 #endif
1484 
1485 	while (*cp && *cp == *str) {
1486 		cp++, str++;
1487 	}
1488 	if (*cp) {
1489 		error("Bad scaling; did you mean ``%t''?", str0);
1490 	}
1491 }
1492 
1493 void
1494 plim(struct limits *lp, tchar hard)
1495 {
1496 	struct rlimit rlim;
1497 	char buf[BUFSZ];
1498 	char *pbuf;
1499 	rlim_t limit;
1500 
1501 #ifdef TRACE
1502 	tprintf("TRACE- plim()\n");
1503 #endif
1504 	printf("%t \t", lp->limname);
1505 	(void) getrlimit(lp->limconst, &rlim);
1506 	limit = hard ? rlim.rlim_max : rlim.rlim_cur;
1507 	if (limit == RLIM_INFINITY) {
1508 		printf("unlimited");
1509 	} else if (lp->limconst == RLIMIT_CPU) {
1510 		psecs_ull(limit);
1511 	} else {
1512 		buf[BUFSZ - 1] = '\0';
1513 		pbuf = ulltostr((limit / lp->limdiv), &buf[BUFSZ - 1]);
1514 		printf("%s %t", pbuf, lp->limscale);
1515 	}
1516 	printf("\n");
1517 }
1518 
1519 void
1520 dounlimit(tchar **v)
1521 {
1522 	struct limits *lp;
1523 	int err = 0;
1524 	tchar hard = 0;
1525 #ifdef TRACE
1526 	tprintf("TRACE- dounlimit()\n");
1527 #endif
1528 
1529 	v++;
1530 	if (*v && eq(*v, S_h)) {
1531 		hard = 1;
1532 		v++;
1533 	}
1534 	if (*v == 0) {
1535 		for (lp = limits; lp->limconst >= 0; lp++) {
1536 			if (setlim(lp, hard, RLIM_INFINITY) < 0) {
1537 				err++;
1538 			}
1539 		}
1540 		if (err) {
1541 			error(NULL);
1542 		}
1543 		return;
1544 	}
1545 	while (*v) {
1546 		lp = findlim(*v++);
1547 		if (setlim(lp, hard, RLIM_INFINITY) < 0) {
1548 			error(NULL);
1549 		}
1550 	}
1551 }
1552 
1553 int
1554 setlim(struct limits *lp, tchar hard, rlim_t limit)
1555 {
1556 	struct rlimit rlim;
1557 
1558 #ifdef TRACE
1559 	tprintf("TRACE- setlim()\n");
1560 #endif
1561 	(void) getrlimit(lp->limconst, &rlim);
1562 	if (hard) {
1563 		rlim.rlim_max = limit;
1564 	} else if (limit == RLIM_INFINITY && geteuid() != 0) {
1565 		rlim.rlim_cur = rlim.rlim_max;
1566 	} else {
1567 		rlim.rlim_cur = limit;
1568 	}
1569 	if (setrlimit(lp->limconst, &rlim) < 0) {
1570 		printf("%t: %t: Can't %s%s limit\n", bname, lp->limname,
1571 		    limit == RLIM_INFINITY ? "remove" : "set",
1572 		    hard ? " hard" : "");
1573 		return (-1);
1574 	}
1575 	return (0);
1576 }
1577 
1578 void
1579 dosuspend()
1580 {
1581 	int ctpgrp;
1582 	void (*old)();
1583 
1584 #ifdef TRACE
1585 	tprintf("TRACE- dosuspend()\n");
1586 #endif
1587 	if (loginsh) {
1588 		error("Can't suspend a login shell (yet)");
1589 	}
1590 	if (getpid() == getsid(0)) {
1591 		error("Can't suspend this shell");
1592 	}
1593 	untty();
1594 	old = (void (*)())signal(SIGTSTP, SIG_DFL);
1595 	(void) kill(0, SIGTSTP);
1596 	/* the shell stops here */
1597 	(void) signal(SIGTSTP, old);
1598 	if (tpgrp != -1) {
1599 retry:
1600 		(void) ioctl(FSHTTY, TIOCGPGRP,  (char *)&ctpgrp);
1601 		if (ctpgrp != opgrp) {
1602 			old = (void (*)())signal(SIGTTIN, SIG_DFL);
1603 			(void) kill(0, SIGTTIN);
1604 			(void) signal(SIGTTIN, old);
1605 			goto retry;
1606 		}
1607 		(void) setpgid(0, shpgrp);
1608 		(void) ioctl(FSHTTY, TIOCSPGRP, (char *)&shpgrp);
1609 	}
1610 }
1611 
1612 void
1613 doeval(tchar **v)
1614 {
1615 	tchar **oevalvec = evalvec;
1616 	tchar *oevalp = evalp;
1617 	jmp_buf osetexit;
1618 	int reenter;
1619 	tchar **gv = 0;
1620 
1621 #ifdef TRACE
1622 	tprintf("TRACE- doeval()\n");
1623 #endif
1624 	v++;
1625 	if (*v == 0) {
1626 		return;
1627 	}
1628 	gflag = 0, tglob(v);
1629 	if (gflag) {
1630 		gv = v = glob(v);
1631 		gargv = 0;
1632 		if (v == 0) {
1633 			error("No match");
1634 		}
1635 		v = copyblk(v);
1636 	} else {
1637 		trim(v);
1638 	}
1639 	getexit(osetexit);
1640 	reenter = 0;
1641 	setexit();
1642 	reenter++;
1643 	if (reenter == 1) {
1644 		evalvec = v;
1645 		evalp = 0;
1646 		process(0);
1647 	}
1648 	evalvec = oevalvec;
1649 	evalp = oevalp;
1650 	doneinp = 0;
1651 	if (gv) {
1652 		blkfree(gv);
1653 	}
1654 	resexit(osetexit);
1655 	if (reenter >= 2) {
1656 		error(NULL);
1657 	}
1658 }
1659