xref: /netbsd/bin/csh/func.c (revision bf9ec67e)
1 /* $NetBSD: func.c,v 1.23 2002/05/25 23:29:16 wiz Exp $ */
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)func.c	8.1 (Berkeley) 5/31/93";
40 #else
41 __RCSID("$NetBSD: func.c,v 1.23 2002/05/25 23:29:16 wiz Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 
48 #include <locale.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include "csh.h"
56 #include "extern.h"
57 #include "pathnames.h"
58 
59 extern char **environ;
60 extern int progprintf(int, char **);
61 
62 static void islogin(void);
63 static void reexecute(struct command *);
64 static void preread(void);
65 static void doagain(void);
66 static void search(int, int, Char *);
67 static int getword(Char *);
68 static int keyword(Char *);
69 static void toend(void);
70 static void xecho(int, Char **);
71 static void Unsetenv(Char *);
72 
73 struct biltins *
74 isbfunc(struct command *t)
75 {
76     static struct biltins label = {"", dozip, 0, 0};
77     static struct biltins foregnd = {"%job", dofg1, 0, 0};
78     static struct biltins backgnd = {"%job &", dobg1, 0, 0};
79     struct biltins *bp, *bp1, *bp2;
80     Char *cp;
81 
82     cp = t->t_dcom[0];
83 
84     if (lastchr(cp) == ':') {
85 	label.bname = short2str(cp);
86 	return (&label);
87     }
88     if (*cp == '%') {
89 	if (t->t_dflg & F_AMPERSAND) {
90 	    t->t_dflg &= ~F_AMPERSAND;
91 	    backgnd.bname = short2str(cp);
92 	    return (&backgnd);
93 	}
94 	foregnd.bname = short2str(cp);
95 	return (&foregnd);
96     }
97     /*
98      * Binary search Bp1 is the beginning of the current search range. Bp2 is
99      * one past the end.
100      */
101     for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
102 	int i;
103 
104 	bp = bp1 + ((bp2 - bp1) >> 1);
105 	if ((i = *cp - *bp->bname) == 0 &&
106 	    (i = Strcmp(cp, str2short(bp->bname))) == 0)
107 	    return bp;
108 	if (i < 0)
109 	    bp2 = bp;
110 	else
111 	    bp1 = bp + 1;
112     }
113     return (0);
114 }
115 
116 void
117 func(struct command *t, struct biltins *bp)
118 {
119     int i;
120 
121     xechoit(t->t_dcom);
122     setname(bp->bname);
123     i = blklen(t->t_dcom) - 1;
124     if (i < bp->minargs)
125 	stderror(ERR_NAME | ERR_TOOFEW);
126     if (i > bp->maxargs)
127 	stderror(ERR_NAME | ERR_TOOMANY);
128     (*bp->bfunct) (t->t_dcom, t);
129 }
130 
131 void
132 /*ARGSUSED*/
133 doonintr(Char **v, struct command *t)
134 {
135     Char *cp, *vv;
136     sigset_t sigset;
137 
138     vv = v[1];
139     if (parintr == SIG_IGN)
140 	return;
141     if (setintr && intty)
142 	stderror(ERR_NAME | ERR_TERMINAL);
143     cp = gointr;
144     gointr = 0;
145     xfree((ptr_t) cp);
146     if (vv == 0) {
147 	if (setintr) {
148 	    sigemptyset(&sigset);
149 	    (void)sigaddset(&sigset, SIGINT);
150 	    (void)sigprocmask(SIG_BLOCK, &sigset, NULL);
151 	} else
152 	    (void)signal(SIGINT, SIG_DFL);
153 	gointr = 0;
154     }
155     else if (eq((vv = strip(vv)), STRminus)) {
156 	(void)signal(SIGINT, SIG_IGN);
157 	gointr = Strsave(STRminus);
158     }
159     else {
160 	gointr = Strsave(vv);
161 	(void)signal(SIGINT, pintr);
162     }
163 }
164 
165 void
166 /*ARGSUSED*/
167 donohup(Char **v, struct command *t)
168 {
169     if (intty)
170 	stderror(ERR_NAME | ERR_TERMINAL);
171     if (setintr == 0) {
172 	(void) signal(SIGHUP, SIG_IGN);
173     }
174 }
175 
176 void
177 /*ARGSUSED*/
178 dozip(Char **v, struct command *t)
179 {
180     ;
181 }
182 
183 void
184 prvars(void)
185 {
186     plist(&shvhed);
187 }
188 
189 void
190 /*ARGSUSED*/
191 doalias(Char **v, struct command *t)
192 {
193     struct varent *vp;
194     Char *p;
195 
196     v++;
197     p = *v++;
198     if (p == 0)
199 	plist(&aliases);
200     else if (*v == 0) {
201 	vp = adrof1(strip(p), &aliases);
202 	if (vp) {
203 	    blkpr(cshout, vp->vec);
204 	    (void) fputc('\n', cshout);
205 	}
206     }
207     else {
208 	if (eq(p, STRalias) || eq(p, STRunalias)) {
209 	    setname(vis_str(p));
210 	    stderror(ERR_NAME | ERR_DANGER);
211 	}
212 	set1(strip(p), saveblk(v), &aliases);
213     }
214 }
215 
216 void
217 /*ARGSUSED*/
218 unalias(Char **v, struct command *t)
219 {
220     unset1(v, &aliases);
221 }
222 
223 void
224 /*ARGSUSED*/
225 dologout(Char **v, struct command *t)
226 {
227     islogin();
228     goodbye();
229 }
230 
231 void
232 /*ARGSUSED*/
233 dologin(Char **v, struct command *t)
234 {
235     islogin();
236     rechist();
237     (void)signal(SIGTERM, parterm);
238     (void)execl(_PATH_LOGIN, "login", short2str(v[1]), NULL);
239     untty();
240     xexit(1);
241     /* NOTREACHED */
242 }
243 
244 static void
245 islogin(void)
246 {
247     if (chkstop == 0 && setintr)
248 	panystop(0);
249     if (loginsh)
250 	return;
251     stderror(ERR_NOTLOGIN);
252     /* NOTREACHED */
253 }
254 
255 void
256 doif(Char **v, struct command *kp)
257 {
258     Char **vv;
259     int i;
260 
261     v++;
262     i = expr(&v);
263     vv = v;
264     if (*vv == NULL)
265 	stderror(ERR_NAME | ERR_EMPTYIF);
266     if (eq(*vv, STRthen)) {
267 	if (*++vv)
268 	    stderror(ERR_NAME | ERR_IMPRTHEN);
269 	setname(vis_str(STRthen));
270 	/*
271 	 * If expression was zero, then scan to else, otherwise just fall into
272 	 * following code.
273 	 */
274 	if (!i)
275 	    search(T_IF, 0, NULL);
276 	return;
277     }
278     /*
279      * Simple command attached to this if. Left shift the node in this tree,
280      * munging it so we can reexecute it.
281      */
282     if (i) {
283 	lshift(kp->t_dcom, vv - kp->t_dcom);
284 	reexecute(kp);
285 	donefds();
286     }
287 }
288 
289 /*
290  * Reexecute a command, being careful not
291  * to redo i/o redirection, which is already set up.
292  */
293 static void
294 reexecute(struct command *kp)
295 {
296     kp->t_dflg &= F_SAVE;
297     kp->t_dflg |= F_REPEAT;
298     /*
299      * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
300      * pgrp's as the jobs would then have no way to get the tty (we can't give
301      * it to them, and our parent wouldn't know their pgrp, etc.
302      */
303     execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
304 }
305 
306 void
307 /*ARGSUSED*/
308 doelse(Char **v, struct command *t)
309 {
310     search(T_ELSE, 0, NULL);
311 }
312 
313 void
314 /*ARGSUSED*/
315 dogoto(Char **v, struct command *t)
316 {
317     Char *lp;
318 
319     gotolab(lp = globone(v[1], G_ERROR));
320     xfree((ptr_t) lp);
321 }
322 
323 void
324 gotolab(Char *lab)
325 {
326     struct whyle *wp;
327     /*
328      * While we still can, locate any unknown ends of existing loops. This
329      * obscure code is the WORST result of the fact that we don't really parse.
330      */
331     for (wp = whyles; wp; wp = wp->w_next)
332 	if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) {
333 	    search(T_BREAK, 0, NULL);
334 	    btell(&wp->w_end);
335 	}
336 	else
337 	    bseek(&wp->w_end);
338     search(T_GOTO, 0, lab);
339     /*
340      * Eliminate loops which were exited.
341      */
342     wfree();
343 }
344 
345 void
346 /*ARGSUSED*/
347 doswitch(Char **v, struct command *t)
348 {
349     Char *cp, *lp;
350 
351     v++;
352     if (!*v || *(*v++) != '(')
353 	stderror(ERR_SYNTAX);
354     cp = **v == ')' ? STRNULL : *v++;
355     if (*(*v++) != ')')
356 	v--;
357     if (*v)
358 	stderror(ERR_SYNTAX);
359     search(T_SWITCH, 0, lp = globone(cp, G_ERROR));
360     xfree((ptr_t) lp);
361 }
362 
363 void
364 /*ARGSUSED*/
365 dobreak(Char **v, struct command *t)
366 {
367     if (whyles)
368 	toend();
369     else
370 	stderror(ERR_NAME | ERR_NOTWHILE);
371 }
372 
373 void
374 /*ARGSUSED*/
375 doexit(Char **v, struct command *t)
376 {
377     if (chkstop == 0 && (intty || intact) && evalvec == 0)
378 	panystop(0);
379     /*
380      * Don't DEMAND parentheses here either.
381      */
382     v++;
383     if (*v) {
384 	set(STRstatus, putn(expr(&v)));
385 	if (*v)
386 	    stderror(ERR_NAME | ERR_EXPRESSION);
387     }
388     btoeof();
389     if (intty)
390 	(void) close(SHIN);
391 }
392 
393 void
394 /*ARGSUSED*/
395 doforeach(Char **v, struct command *t)
396 {
397     struct whyle *nwp;
398     Char *cp, *sp;
399 
400     v++;
401     sp = cp = strip(*v);
402     if (!letter(*sp))
403 	stderror(ERR_NAME | ERR_VARBEGIN);
404     while (*cp && alnum(*cp))
405 	cp++;
406     if (*cp)
407 	stderror(ERR_NAME | ERR_VARALNUM);
408     if ((cp - sp) > MAXVARLEN)
409 	stderror(ERR_NAME | ERR_VARTOOLONG);
410     cp = *v++;
411     if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
412 	stderror(ERR_NAME | ERR_NOPAREN);
413     v++;
414     gflag = 0, tglob(v);
415     v = globall(v);
416     if (v == 0)
417 	stderror(ERR_NAME | ERR_NOMATCH);
418     nwp = (struct whyle *) xcalloc(1, sizeof *nwp);
419     nwp->w_fe = nwp->w_fe0 = v;
420     gargv = 0;
421     btell(&nwp->w_start);
422     nwp->w_fename = Strsave(cp);
423     nwp->w_next = whyles;
424     nwp->w_end.type = F_SEEK;
425     whyles = nwp;
426     /*
427      * Pre-read the loop so as to be more comprehensible to a terminal user.
428      */
429     if (intty)
430 	preread();
431     doagain();
432 }
433 
434 void
435 /*ARGSUSED*/
436 dowhile(Char **v, struct command *t)
437 {
438     int status;
439     bool again;
440 
441     again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) &&
442         whyles->w_fename == 0;
443     v++;
444     /*
445      * Implement prereading here also, taking care not to evaluate the
446      * expression before the loop has been read up from a terminal.
447      */
448     if (intty && !again)
449 	status = !exp0(&v, 1);
450     else
451 	status = !expr(&v);
452     if (*v)
453 	stderror(ERR_NAME | ERR_EXPRESSION);
454     if (!again) {
455 	struct whyle *nwp =
456 	(struct whyle *)xcalloc(1, sizeof(*nwp));
457 
458 	nwp->w_start = lineloc;
459 	nwp->w_end.type = F_SEEK;
460 	nwp->w_end.f_seek = 0;
461 	nwp->w_next = whyles;
462 	whyles = nwp;
463 	if (intty) {
464 	    /*
465 	     * The tty preread
466 	     */
467 	    preread();
468 	    doagain();
469 	    return;
470 	}
471     }
472     if (status)
473 	/* We ain't gonna loop no more, no more! */
474 	toend();
475 }
476 
477 static void
478 preread(void)
479 {
480     sigset_t sigset;
481 
482     whyles->w_end.type = I_SEEK;
483     if (setintr) {
484 	sigemptyset(&sigset);
485 	(void) sigaddset(&sigset, SIGINT);
486 	(void) sigprocmask(SIG_UNBLOCK, &sigset, NULL);
487     }
488 
489     search(T_BREAK, 0, NULL);		/* read the expression in */
490     if (setintr)
491 	(void)sigprocmask(SIG_BLOCK, &sigset, NULL);
492     btell(&whyles->w_end);
493 }
494 
495 void
496 /*ARGSUSED*/
497 doend(Char **v, struct command *t)
498 {
499     if (!whyles)
500 	stderror(ERR_NAME | ERR_NOTWHILE);
501     btell(&whyles->w_end);
502     doagain();
503 }
504 
505 void
506 /*ARGSUSED*/
507 docontin(Char **v, struct command *t)
508 {
509     if (!whyles)
510 	stderror(ERR_NAME | ERR_NOTWHILE);
511     doagain();
512 }
513 
514 static void
515 doagain(void)
516 {
517     /* Repeating a while is simple */
518     if (whyles->w_fename == 0) {
519 	bseek(&whyles->w_start);
520 	return;
521     }
522     /*
523      * The foreach variable list actually has a spurious word ")" at the end of
524      * the w_fe list.  Thus we are at the of the list if one word beyond this
525      * is 0.
526      */
527     if (!whyles->w_fe[1]) {
528 	dobreak(NULL, NULL);
529 	return;
530     }
531     set(whyles->w_fename, Strsave(*whyles->w_fe++));
532     bseek(&whyles->w_start);
533 }
534 
535 void
536 dorepeat(Char **v, struct command *kp)
537 {
538     int i;
539     sigset_t sigset;
540 
541     i = getn(v[1]);
542     if (setintr) {
543 	sigemptyset(&sigset);
544 	(void)sigaddset(&sigset, SIGINT);
545 	(void)sigprocmask(SIG_BLOCK, &sigset, NULL);
546     }
547     lshift(v, 2);
548     while (i > 0) {
549 	if (setintr)
550 	    (void)sigprocmask(SIG_UNBLOCK, &sigset, NULL);
551 	reexecute(kp);
552 	--i;
553     }
554     donefds();
555     if (setintr)
556 	(void) sigprocmask(SIG_UNBLOCK, &sigset, NULL);
557 }
558 
559 void
560 /*ARGSUSED*/
561 doswbrk(Char **v, struct command *t)
562 {
563     search(T_BRKSW, 0, NULL);
564 }
565 
566 int
567 srchx(Char *cp)
568 {
569     struct srch *sp, *sp1, *sp2;
570     int i;
571 
572     /*
573      * Binary search Sp1 is the beginning of the current search range. Sp2 is
574      * one past the end.
575      */
576     for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
577 	sp = sp1 + ((sp2 - sp1) >> 1);
578 	if ((i = *cp - *sp->s_name) == 0 &&
579 	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
580 	    return sp->s_value;
581 	if (i < 0)
582 	    sp2 = sp;
583 	else
584 	    sp1 = sp + 1;
585     }
586     return (-1);
587 }
588 
589 static Char Stype;
590 static Char *Sgoal;
591 
592 /*VARARGS2*/
593 static void
594 search(int type, int level, Char *goal)
595 {
596     Char wordbuf[BUFSIZE];
597     Char *aword, *cp;
598 
599     aword = wordbuf;
600     Stype = type;
601     Sgoal = goal;
602     if (type == T_GOTO) {
603 	struct Ain a;
604 	a.type = F_SEEK;
605 	a.f_seek = 0;
606 	bseek(&a);
607     }
608     do {
609 	if (intty && fseekp == feobp && aret == F_SEEK)
610 	    (void)fprintf(cshout, "? "), (void)fflush(cshout);
611 	aword[0] = 0;
612 	(void)getword(aword);
613 	switch (srchx(aword)) {
614 	case T_CASE:
615 	    if (type != T_SWITCH || level != 0)
616 		break;
617 	    (void) getword(aword);
618 	    if (lastchr(aword) == ':')
619 		aword[Strlen(aword) - 1] = 0;
620 	    cp = strip(Dfix1(aword));
621 	    if (Gmatch(goal, cp))
622 		level = -1;
623 	    xfree((ptr_t) cp);
624 	    break;
625 	case T_DEFAULT:
626 	    if (type == T_SWITCH && level == 0)
627 		level = -1;
628 	    break;
629 	case T_ELSE:
630 	    if (level == 0 && type == T_IF)
631 		return;
632 	    break;
633 	case T_END:
634 	    if (type == T_BREAK)
635 		level--;
636 	    break;
637 	case T_ENDIF:
638 	    if (type == T_IF || type == T_ELSE)
639 		level--;
640 	    break;
641 	case T_ENDSW:
642 	    if (type == T_SWITCH || type == T_BRKSW)
643 		level--;
644 	    break;
645 	case T_IF:
646 	    while (getword(aword))
647 		continue;
648 	    if ((type == T_IF || type == T_ELSE) &&
649 		eq(aword, STRthen))
650 		level++;
651 	    break;
652 	case T_LABEL:
653 	    if (type == T_GOTO && getword(aword) && eq(aword, goal))
654 		level = -1;
655 	    break;
656 	case T_SWITCH:
657 	    if (type == T_SWITCH || type == T_BRKSW)
658 		level++;
659 	    break;
660 	case T_FOREACH:
661 	case T_WHILE:
662 	    if (type == T_BREAK)
663 		level++;
664 	    break;
665 	default:
666 	    if (type != T_GOTO && (type != T_SWITCH || level != 0))
667 		break;
668 	    if (lastchr(aword) != ':')
669 		break;
670 	    aword[Strlen(aword) - 1] = 0;
671 	    if ((type == T_GOTO && eq(aword, goal)) ||
672 		(type == T_SWITCH && eq(aword, STRdefault)))
673 		level = -1;
674 	    break;
675 	}
676 	(void) getword(NULL);
677     } while (level >= 0);
678 }
679 
680 static int
681 getword(Char *wp)
682 {
683     int c, d, found, kwd;
684     Char *owp;
685 
686     c = readc(1);
687     d = 0;
688     found = 0;
689     kwd = 0;
690     owp = wp;
691     do {
692 	while (c == ' ' || c == '\t')
693 	    c = readc(1);
694 	if (c == '#')
695 	    do
696 		c = readc(1);
697 	    while (c >= 0 && c != '\n');
698 	if (c < 0)
699 	    goto past;
700 	if (c == '\n') {
701 	    if (wp)
702 		break;
703 	    return (0);
704 	}
705 	unreadc(c);
706 	found = 1;
707 	do {
708 	    c = readc(1);
709 	    if (c == '\\' && (c = readc(1)) == '\n')
710 		c = ' ';
711 	    if (c == '\'' || c == '"') {
712 		if (d == 0)
713 		    d = c;
714 		else if (d == c)
715 		    d = 0;
716 	    }
717 	    if (c < 0)
718 		goto past;
719 	    if (wp) {
720 		*wp++ = c;
721 		*wp = 0;	/* end the string b4 test */
722 	    }
723 	} while ((d || (!(kwd = keyword(owp)) && c != ' '
724 		  && c != '\t')) && c != '\n');
725     } while (wp == 0);
726 
727     /*
728      * if we have read a keyword ( "if", "switch" or "while" ) then we do not
729      * need to unreadc the look-ahead char
730      */
731     if (!kwd) {
732 	unreadc(c);
733 	if (found)
734 	    *--wp = 0;
735     }
736 
737     return (found);
738 
739 past:
740     switch (Stype) {
741     case T_BREAK:
742 	stderror(ERR_NAME | ERR_NOTFOUND, "end");
743 	/* NOTREACHED */
744     case T_ELSE:
745 	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
746 	/* NOTREACHED */
747     case T_GOTO:
748 	setname(vis_str(Sgoal));
749 	stderror(ERR_NAME | ERR_NOTFOUND, "label");
750 	/* NOTREACHED */
751     case T_IF:
752 	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
753 	/* NOTREACHED */
754     case T_BRKSW:
755     case T_SWITCH:
756 	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
757 	/* NOTREACHED */
758     }
759     return (0);
760 }
761 
762 /*
763  * keyword(wp) determines if wp is one of the built-n functions if,
764  * switch or while. It seems that when an if statement looks like
765  * "if(" then getword above sucks in the '(' and so the search routine
766  * never finds what it is scanning for. Rather than rewrite doword, I hack
767  * in a test to see if the string forms a keyword. Then doword stops
768  * and returns the word "if" -strike
769  */
770 
771 static int
772 keyword(Char *wp)
773 {
774     static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
775     static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'};
776     static Char STRif[] = {'i', 'f', '\0'};
777 
778     if (!wp)
779 	return (0);
780 
781     if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0)
782 	|| (Strcmp(wp, STRswitch) == 0))
783 	return (1);
784 
785     return (0);
786 }
787 
788 static void
789 toend(void)
790 {
791     if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) {
792 	search(T_BREAK, 0, NULL);
793 	btell(&whyles->w_end);
794 	whyles->w_end.f_seek--;
795     }
796     else
797 	bseek(&whyles->w_end);
798     wfree();
799 }
800 
801 void
802 wfree(void)
803 {
804     struct Ain o;
805     struct whyle *nwp;
806 
807     btell(&o);
808 
809     for (; whyles; whyles = nwp) {
810 	struct whyle *wp = whyles;
811 	nwp = wp->w_next;
812 
813 	/*
814 	 * We free loops that have different seek types.
815 	 */
816 	if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type &&
817 	    wp->w_start.type == o.type) {
818 	    if (wp->w_end.type == F_SEEK) {
819 		if (o.f_seek >= wp->w_start.f_seek &&
820 		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
821 		    break;
822 	    }
823 	    else {
824 		if (o.a_seek >= wp->w_start.a_seek &&
825 		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
826 		    break;
827 	    }
828 	}
829 
830 	if (wp->w_fe0)
831 	    blkfree(wp->w_fe0);
832 	if (wp->w_fename)
833 	    xfree((ptr_t) wp->w_fename);
834 	xfree((ptr_t) wp);
835     }
836 }
837 
838 void
839 /*ARGSUSED*/
840 doecho(Char **v, struct command *t)
841 {
842     xecho(' ', v);
843 }
844 
845 void
846 /*ARGSUSED*/
847 doglob(Char **v, struct command *t)
848 {
849     xecho(0, v);
850     (void)fflush(cshout);
851 }
852 
853 static void
854 xecho(int sep, Char **v)
855 {
856     Char *cp;
857     sigset_t sigset;
858     int nonl;
859 
860     nonl = 0;
861     if (setintr) {
862 	sigemptyset(&sigset);
863 	(void)sigaddset(&sigset, SIGINT);
864 	(void)sigprocmask(SIG_UNBLOCK, &sigset, NULL);
865     }
866     v++;
867     if (*v == 0)
868 	goto done;
869     gflag = 0, tglob(v);
870     if (gflag) {
871 	v = globall(v);
872 	if (v == 0)
873 	    stderror(ERR_NAME | ERR_NOMATCH);
874     }
875     else {
876 	v = gargv = saveblk(v);
877 	trim(v);
878     }
879     if (sep == ' ' && *v && eq(*v, STRmn))
880 	nonl++, v++;
881     while ((cp = *v++) != NULL) {
882 	int c;
883 
884 	while ((c = *cp++) != '\0')
885 	    (void)vis_fputc(c | QUOTE, cshout);
886 
887 	if (*v)
888 	    (void)vis_fputc(sep | QUOTE, cshout);
889     }
890 done:
891     if (sep && nonl == 0)
892 	(void)fputc('\n', cshout);
893     else
894 	(void)fflush(cshout);
895     if (setintr)
896 	(void)sigprocmask(SIG_BLOCK, &sigset, NULL);
897     if (gargv)
898 	blkfree(gargv), gargv = 0;
899 }
900 
901 void
902 /*ARGSUSED*/
903 dosetenv(Char **v, struct command *t)
904 {
905     Char *lp, *vp;
906     sigset_t sigset;
907 
908     v++;
909     if ((vp = *v++) == 0) {
910 	Char **ep;
911 
912 	if (setintr) {
913 	    sigemptyset(&sigset);
914 	    (void)sigaddset(&sigset, SIGINT);
915 	    (void)sigprocmask(SIG_UNBLOCK, &sigset, NULL);
916 	}
917 	for (ep = STR_environ; *ep; ep++)
918 	    (void)fprintf(cshout, "%s\n", vis_str(*ep));
919 	return;
920     }
921     if ((lp = *v++) == 0)
922 	lp = STRNULL;
923     Setenv(vp, lp = globone(lp, G_APPEND));
924     if (eq(vp, STRPATH)) {
925 	importpath(lp);
926 	dohash(NULL, NULL);
927     }
928     else if (eq(vp, STRLANG) || eq(vp, STRLC_CTYPE)) {
929 #ifdef NLS
930 	int k;
931 
932 	(void)setlocale(LC_ALL, "");
933 	for (k = 0200; k <= 0377 && !Isprint(k); k++)
934 		continue;
935 	AsciiOnly = k > 0377;
936 #else
937 	AsciiOnly = 0;
938 #endif				/* NLS */
939     }
940     xfree((ptr_t) lp);
941 }
942 
943 void
944 /*ARGSUSED*/
945 dounsetenv(Char **v, struct command *t)
946 {
947     static Char *name = NULL;
948     Char **ep, *p, *n;
949     int i, maxi;
950 
951     if (name)
952 	xfree((ptr_t) name);
953     /*
954      * Find the longest environment variable
955      */
956     for (maxi = 0, ep = STR_environ; *ep; ep++) {
957 	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
958 	    continue;
959 	if (i > maxi)
960 	    maxi = i;
961     }
962 
963     name = (Char *)xmalloc((size_t) (maxi + 1) * sizeof(Char));
964 
965     while (++v && *v)
966 	for (maxi = 1; maxi;)
967 	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
968 		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
969 		    continue;
970 		*n = '\0';
971 		if (!Gmatch(name, *v))
972 		    continue;
973 		maxi = 1;
974 		if (eq(name, STRLANG) || eq(name, STRLC_CTYPE)) {
975 #ifdef NLS
976 		    int     k;
977 
978 		    (void) setlocale(LC_ALL, "");
979 		    for (k = 0200; k <= 0377 && !Isprint(k); k++)
980 			continue;
981 		    AsciiOnly = k > 0377;
982 #else
983 		    AsciiOnly = getenv("LANG") == NULL &&
984 			getenv("LC_CTYPE") == NULL;
985 #endif				/* NLS */
986 		}
987 		/*
988 		 * Delete name, and start again cause the environment changes
989 		 */
990 		Unsetenv(name);
991 		break;
992 	    }
993     xfree((ptr_t) name);
994     name = NULL;
995 }
996 
997 void
998 Setenv(Char *name, Char *val)
999 {
1000     Char *blk[2], *cp, *dp, **ep, **oep;
1001 
1002     ep = STR_environ;
1003     oep = ep;
1004 
1005     for (; *ep; ep++) {
1006 	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
1007 	    continue;
1008 	if (*cp != 0 || *dp != '=')
1009 	    continue;
1010 	cp = Strspl(STRequal, val);
1011 	xfree((ptr_t)* ep);
1012 	*ep = strip(Strspl(name, cp));
1013 	xfree((ptr_t)cp);
1014 	blkfree((Char **)environ);
1015 	environ = short2blk(STR_environ);
1016 	return;
1017     }
1018     cp = Strspl(name, STRequal);
1019     blk[0] = strip(Strspl(cp, val));
1020     xfree((ptr_t)cp);
1021     blk[1] = 0;
1022     STR_environ = blkspl(STR_environ, blk);
1023     blkfree((Char **)environ);
1024     environ = short2blk(STR_environ);
1025     xfree((ptr_t) oep);
1026 }
1027 
1028 static void
1029 Unsetenv(Char *name)
1030 {
1031     Char *cp, *dp, **ep, **oep;
1032 
1033     ep = STR_environ;
1034     oep = ep;
1035 
1036     for (; *ep; ep++) {
1037 	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
1038 	    continue;
1039 	if (*cp != 0 || *dp != '=')
1040 	    continue;
1041 	cp = *ep;
1042 	*ep = 0;
1043 	STR_environ = blkspl(STR_environ, ep + 1);
1044 	environ = short2blk(STR_environ);
1045 	*ep = cp;
1046 	xfree((ptr_t) cp);
1047 	xfree((ptr_t) oep);
1048 	return;
1049     }
1050 }
1051 
1052 void
1053 /*ARGSUSED*/
1054 doumask(Char **v, struct command *t)
1055 {
1056     Char *cp;
1057     int i;
1058 
1059     cp = v[1];
1060     if (cp == 0) {
1061 	i = umask(0);
1062 	(void)umask(i);
1063 	(void)fprintf(cshout, "%o\n", i);
1064 	return;
1065     }
1066     i = 0;
1067     while (Isdigit(*cp) && *cp != '8' && *cp != '9')
1068 	i = i * 8 + *cp++ - '0';
1069     if (*cp || i < 0 || i > 0777)
1070 	stderror(ERR_NAME | ERR_MASK);
1071     (void)umask(i);
1072 }
1073 
1074 typedef quad_t RLIM_TYPE;
1075 
1076 static const struct limits {
1077     int     limconst;
1078     const   char *limname;
1079     int     limdiv;
1080     const   char *limscale;
1081 }       limits[] = {
1082     { RLIMIT_CPU,	"cputime",	1,	"seconds" },
1083     { RLIMIT_FSIZE,	"filesize",	1024,	"kbytes" },
1084     { RLIMIT_DATA,	"datasize",	1024,	"kbytes" },
1085     { RLIMIT_STACK,	"stacksize",	1024,	"kbytes" },
1086     { RLIMIT_CORE,	"coredumpsize", 1024,	"kbytes" },
1087     { RLIMIT_RSS,	"memoryuse",	1024,	"kbytes" },
1088     { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes" },
1089     { RLIMIT_NPROC,	"maxproc",	1,	"" },
1090     { RLIMIT_NOFILE,	"openfiles",	1,	"" },
1091     { -1,		NULL,		0,	NULL }
1092 };
1093 
1094 static const struct limits *findlim(Char *);
1095 static RLIM_TYPE getval(const struct limits *, Char **);
1096 static void limtail(Char *, char *);
1097 static void plim(const struct limits *, Char);
1098 static int setlim(const struct limits *, Char, RLIM_TYPE);
1099 
1100 static const struct limits *
1101 findlim(Char *cp)
1102 {
1103     const struct limits *lp, *res;
1104 
1105     res = (struct limits *) NULL;
1106     for (lp = limits; lp->limconst >= 0; lp++)
1107 	if (prefix(cp, str2short(lp->limname))) {
1108 	    if (res)
1109 		stderror(ERR_NAME | ERR_AMBIG);
1110 	    res = lp;
1111 	}
1112     if (res)
1113 	return (res);
1114     stderror(ERR_NAME | ERR_LIMIT);
1115     /* NOTREACHED */
1116 }
1117 
1118 void
1119 /*ARGSUSED*/
1120 dolimit(Char **v, struct command *t)
1121 {
1122     const struct limits *lp;
1123     RLIM_TYPE limit;
1124     char hard;
1125 
1126     hard = 0;
1127     v++;
1128     if (*v && eq(*v, STRmh)) {
1129 	hard = 1;
1130 	v++;
1131     }
1132     if (*v == 0) {
1133 	for (lp = limits; lp->limconst >= 0; lp++)
1134 	    plim(lp, hard);
1135 	return;
1136     }
1137     lp = findlim(v[0]);
1138     if (v[1] == 0) {
1139 	plim(lp, hard);
1140 	return;
1141     }
1142     limit = getval(lp, v + 1);
1143     if (setlim(lp, hard, limit) < 0)
1144 	stderror(ERR_SILENT);
1145 }
1146 
1147 static  RLIM_TYPE
1148 getval(const struct limits *lp, Char **v)
1149 {
1150     Char *cp;
1151     float f;
1152 
1153     cp = *v++;
1154     f = atof(short2str(cp));
1155 
1156     while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
1157 	cp++;
1158     if (*cp == 0) {
1159 	if (*v == 0)
1160 	    return ((RLIM_TYPE)((f + 0.5) * lp->limdiv));
1161 	cp = *v;
1162     }
1163     switch (*cp) {
1164     case ':':
1165 	if (lp->limconst != RLIMIT_CPU)
1166 	    goto badscal;
1167 	return ((RLIM_TYPE)(f * 60.0 + atof(short2str(cp + 1))));
1168     case 'M':
1169 	if (lp->limconst == RLIMIT_CPU)
1170 	    goto badscal;
1171 	*cp = 'm';
1172 	limtail(cp, "megabytes");
1173 	f *= 1024.0 * 1024.0;
1174 	break;
1175     case 'h':
1176 	if (lp->limconst != RLIMIT_CPU)
1177 	    goto badscal;
1178 	limtail(cp, "hours");
1179 	f *= 3600.0;
1180 	break;
1181     case 'k':
1182 	if (lp->limconst == RLIMIT_CPU)
1183 	    goto badscal;
1184 	limtail(cp, "kbytes");
1185 	f *= 1024.0;
1186 	break;
1187     case 'm':
1188 	if (lp->limconst == RLIMIT_CPU) {
1189 	    limtail(cp, "minutes");
1190 	    f *= 60.0;
1191 	    break;
1192 	}
1193 	*cp = 'm';
1194 	limtail(cp, "megabytes");
1195 	f *= 1024.0 * 1024.0;
1196 	break;
1197     case 's':
1198 	if (lp->limconst != RLIMIT_CPU)
1199 	    goto badscal;
1200 	limtail(cp, "seconds");
1201 	break;
1202     case 'u':
1203 	limtail(cp, "unlimited");
1204 	return (RLIM_INFINITY);
1205     default:
1206     badscal:
1207 	stderror(ERR_NAME | ERR_SCALEF);
1208 	/* NOTREACHED */
1209     }
1210     f += 0.5;
1211     if (f > (float) RLIM_INFINITY)
1212 	return RLIM_INFINITY;
1213     else
1214 	return ((RLIM_TYPE)f);
1215 }
1216 
1217 static void
1218 limtail(Char *cp, char *str)
1219 {
1220     while (*cp && *cp == *str)
1221 	cp++, str++;
1222     if (*cp)
1223 	stderror(ERR_BADSCALE, str);
1224 }
1225 
1226 
1227 /*ARGSUSED*/
1228 static void
1229 plim(const struct limits *lp, Char hard)
1230 {
1231     struct rlimit rlim;
1232     RLIM_TYPE limit;
1233 
1234     (void)fprintf(cshout, "%s \t", lp->limname);
1235 
1236     (void)getrlimit(lp->limconst, &rlim);
1237     limit = hard ? rlim.rlim_max : rlim.rlim_cur;
1238 
1239     if (limit == RLIM_INFINITY)
1240 	(void)fprintf(cshout, "unlimited");
1241     else if (lp->limconst == RLIMIT_CPU)
1242 	psecs((long) limit);
1243     else
1244 	(void)fprintf(cshout, "%ld %s", (long) (limit / lp->limdiv),
1245 	    lp->limscale);
1246     (void)fputc('\n', cshout);
1247 }
1248 
1249 void
1250 /*ARGSUSED*/
1251 dounlimit(Char **v, struct command *t)
1252 {
1253     const struct limits *lp;
1254     int lerr;
1255     Char hard;
1256 
1257     lerr = 0;
1258     hard = 0;
1259     v++;
1260     if (*v && eq(*v, STRmh)) {
1261 	hard = 1;
1262 	v++;
1263     }
1264     if (*v == 0) {
1265 	for (lp = limits; lp->limconst >= 0; lp++)
1266 	    if (setlim(lp, hard, (RLIM_TYPE)RLIM_INFINITY) < 0)
1267 		lerr++;
1268 	if (lerr)
1269 	    stderror(ERR_SILENT);
1270 	return;
1271     }
1272     while (*v) {
1273 	lp = findlim(*v++);
1274 	if (setlim(lp, hard, (RLIM_TYPE)RLIM_INFINITY) < 0)
1275 	    stderror(ERR_SILENT);
1276     }
1277 }
1278 
1279 static int
1280 setlim(const struct limits *lp, Char hard, RLIM_TYPE limit)
1281 {
1282     struct rlimit rlim;
1283 
1284     (void)getrlimit(lp->limconst, &rlim);
1285 
1286     if (hard)
1287 	rlim.rlim_max = limit;
1288     else if (limit == RLIM_INFINITY && geteuid() != 0)
1289 	rlim.rlim_cur = rlim.rlim_max;
1290     else
1291 	rlim.rlim_cur = limit;
1292 
1293     if (rlim.rlim_max < rlim.rlim_cur)
1294 	rlim.rlim_max = rlim.rlim_cur;
1295 
1296     if (setrlimit(lp->limconst, &rlim) < 0) {
1297 	(void)fprintf(csherr, "%s: %s: Can't %s%s limit (%s)\n", bname,
1298 	    lp->limname, limit == RLIM_INFINITY ? "remove" : "set",
1299 	    hard ? " hard" : "", strerror(errno));
1300 	return (-1);
1301     }
1302     return (0);
1303 }
1304 
1305 void
1306 /*ARGSUSED*/
1307 dosuspend(Char **v, struct command *t)
1308 {
1309     int     ctpgrp;
1310     void    (*old)(int);
1311 
1312     if (loginsh)
1313 	stderror(ERR_SUSPLOG);
1314     untty();
1315 
1316     old = signal(SIGTSTP, SIG_DFL);
1317     (void)kill(0, SIGTSTP);
1318     /* the shell stops here */
1319     (void)signal(SIGTSTP, old);
1320 
1321     if (tpgrp != -1) {
1322 retry:
1323 	ctpgrp = tcgetpgrp(FSHTTY);
1324 	if  (ctpgrp != opgrp) {
1325 	    old = signal(SIGTTIN, SIG_DFL);
1326 	    (void)kill(0, SIGTTIN);
1327 	    (void)signal(SIGTTIN, old);
1328 	    goto retry;
1329 	}
1330 	(void)setpgid(0, shpgrp);
1331 	(void)tcsetpgrp(FSHTTY, shpgrp);
1332     }
1333 }
1334 
1335 /* This is the dreaded EVAL built-in.
1336  *   If you don't fiddle with file descriptors, and reset didfds,
1337  *   this command will either ignore redirection inside or outside
1338  *   its aguments, e.g. eval "date >x"  vs.  eval "date" >x
1339  *   The stuff here seems to work, but I did it by trial and error rather
1340  *   than really knowing what was going on.  If tpgrp is zero, we are
1341  *   probably a background eval, e.g. "eval date &", and we want to
1342  *   make sure that any processes we start stay in our pgrp.
1343  *   This is also the case for "time eval date" -- stay in same pgrp.
1344  *   Otherwise, under stty tostop, processes will stop in the wrong
1345  *   pgrp, with no way for the shell to get them going again.  -IAN!
1346  */
1347 static Char **gv = NULL;
1348 
1349 void
1350 /*ARGSUSED*/
1351 doeval(Char **v, struct command *t)
1352 {
1353     jmp_buf osetexit;
1354     Char *oevalp, **oevalvec, **savegv;
1355     int my_reenter, odidfds, oSHERR, oSHIN, oSHOUT, saveERR, saveIN, saveOUT;
1356 
1357     savegv = gv;
1358     UNREGISTER(v);
1359 
1360     oevalvec = evalvec;
1361     oevalp = evalp;
1362     odidfds = didfds;
1363     oSHIN = SHIN;
1364     oSHOUT = SHOUT;
1365     oSHERR = SHERR;
1366 
1367     v++;
1368     if (*v == 0)
1369 	return;
1370     gflag = 0, tglob(v);
1371     if (gflag) {
1372 	gv = v = globall(v);
1373 	gargv = 0;
1374 	if (v == 0)
1375 	    stderror(ERR_NOMATCH);
1376 	v = copyblk(v);
1377     }
1378     else {
1379 	gv = NULL;
1380 	v = copyblk(v);
1381 	trim(v);
1382     }
1383 
1384     saveIN = dcopy(SHIN, -1);
1385     saveOUT = dcopy(SHOUT, -1);
1386     saveERR = dcopy(SHERR, -1);
1387 
1388     getexit(osetexit);
1389 
1390     if ((my_reenter = setexit()) == 0) {
1391 	evalvec = v;
1392 	evalp = 0;
1393 	SHIN = dcopy(0, -1);
1394 	SHOUT = dcopy(1, -1);
1395 	SHERR = dcopy(2, -1);
1396 	didfds = 0;
1397 	process(0);
1398     }
1399 
1400     evalvec = oevalvec;
1401     evalp = oevalp;
1402     doneinp = 0;
1403     didfds = odidfds;
1404     (void)close(SHIN);
1405     (void)close(SHOUT);
1406     (void)close(SHERR);
1407     SHIN = dmove(saveIN, oSHIN);
1408     SHOUT = dmove(saveOUT, oSHOUT);
1409     SHERR = dmove(saveERR, oSHERR);
1410     if (gv)
1411 	blkfree(gv), gv = NULL;
1412     resexit(osetexit);
1413     gv = savegv;
1414     if (my_reenter)
1415 	stderror(ERR_SILENT);
1416 }
1417 
1418 void
1419 /*ARGSUSED*/
1420 doprintf(Char **v, struct command *t)
1421 {
1422     char **c;
1423     int ret;
1424 
1425     ret = progprintf(blklen(v), c = short2blk(v));
1426     (void)fflush(cshout);
1427     (void)fflush(csherr);
1428 
1429     blkfree((Char **) c);
1430     if (ret)
1431 	stderror(ERR_SILENT);
1432 }
1433