Home | History | Annotate | Download | only in csh
      1 /*
      2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1980 Regents of the University of California.
     11  * All rights reserved.  The Berkeley Software License Agreement
     12  * specifies the terms and conditions for redistribution.
     13  */
     14 
     15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     16 
     17 #include "sh.h"
     18 #include <locale.h>	/* For LC_ALL */
     19 #include "sh.tconst.h"
     20 #include <sys/types.h>
     21 #include <stdlib.h>
     22 
     23 /*
     24  * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0.  In
     25  * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new.  Beware of consusing
     26  * the keywords that the command prints for these two.  The old one was
     27  * "memoryuse" and the new one is "memorysize".  Note also that a given limit
     28  * doesn't necessarily appear in the same position in the two releases.
     29  */
     30 struct limits {
     31 	int	limconst;
     32 	tchar *limname;
     33 	int	limdiv;
     34 	tchar *limscale;
     35 } limits[] = {
     36 	RLIMIT_CPU,	S_cputime,	/* "cputime" */
     37 		1,	S_seconds,	/* "seconds" */
     38 	RLIMIT_FSIZE,	S_filesize,	/* "filesize" */
     39 		1024,	S_kbytes,	/* "kbytes" */
     40 	RLIMIT_DATA,	S_datasize,	/* "datasize" */
     41 		1024,	S_kbytes,	/* "kbytes" */
     42 	RLIMIT_STACK,	S_stacksize,	/* "stacksize" */
     43 		1024,	S_kbytes,	/* "kbytes" */
     44 	RLIMIT_CORE,	S_coredumpsize, /* "coredumpsize" */
     45 		1024,	S_kbytes,	/* "kbytes" */
     46 	RLIMIT_NOFILE,	S_descriptors,	/* "descriptors" */
     47 		1,	S_,		/* "" */
     48 	RLIMIT_VMEM,	S_memorysize,	/* "memorysize" */
     49 		1024,	S_kbytes,	/* "kbytes" */
     50 	-1,		0,
     51 };
     52 
     53 
     54 static int getval(struct limits *lp, tchar **v, rlim_t *);
     55 void islogin(void);
     56 int dolabel(void);
     57 void reexecute(struct command *kp);
     58 void preread_(void);
     59 void doagain(void);
     60 void toend(void);
     61 void wfree(void);
     62 void echo(tchar sep, tchar **v);
     63 void local_setenv(tchar *name, tchar *val);
     64 void local_unsetenv(tchar *name);
     65 void limtail(tchar *cp, tchar *str0);
     66 void plim(struct limits *lp, tchar hard);
     67 void search();
     68 
     69 #define	BUFSZ	1028
     70 
     71 /*
     72  * C shell
     73  */
     74 
     75 struct biltins *
     76 isbfunc(struct command *t)
     77 {
     78 	tchar *cp = t->t_dcom[0];
     79 	struct biltins *bp, *bp1, *bp2;
     80 	int dofg1(), dobg1();
     81 
     82 	static struct biltins label = { S_, dolabel, 0, 0 };
     83 	static struct biltins foregnd = { S_Pjob, dofg1, 0, 0 };
     84 	static struct biltins backgnd = { S_PjobAND, dobg1, 0, 0 };
     85 #ifdef TRACE
     86 	tprintf("TRACE- isbfunc()\n");
     87 #endif
     88 	if (lastchr(cp) == ':') {
     89 		label.bname = cp;
     90 		return (&label);
     91 	}
     92 	if (*cp == '%') {
     93 		if (t->t_dflg & FAND) {
     94 			t->t_dflg &= ~FAND;
     95 			backgnd.bname = cp;
     96 			return (&backgnd);
     97 		}
     98 		foregnd.bname = cp;
     99 		return (&foregnd);
    100 	}
    101 	/*
    102 	 * Binary search
    103 	 * Bp1 is the beginning of the current search range.
    104 	 * Bp2 is one past the end.
    105 	 */
    106 	for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2; ) {
    107 		int i;
    108 
    109 		bp = bp1 + (bp2 - bp1 >> 1);
    110 		if ((i = *cp - *bp->bname) == 0 &&
    111 		    (i = strcmp_(cp, bp->bname)) == 0) {
    112 			return (bp);
    113 		}
    114 		if (i < 0) {
    115 			bp2 = bp;
    116 		} else {
    117 			bp1 = bp + 1;
    118 		}
    119 	}
    120 	return (0);
    121 }
    122 
    123 void
    124 func(struct command *t, struct biltins *bp)
    125 {
    126 	int i;
    127 
    128 #ifdef TRACE
    129 	tprintf("TRACE- func()\n");
    130 #endif
    131 	xechoit(t->t_dcom);
    132 	setname(bp->bname);
    133 	i = blklen(t->t_dcom) - 1;
    134 	if (i < bp->minargs) {
    135 		bferr("Too few arguments");
    136 	}
    137 	if (i > bp->maxargs) {
    138 		bferr("Too many arguments");
    139 	}
    140 	(*bp->bfunct)(t->t_dcom, t);
    141 }
    142 
    143 int
    144 dolabel(void)
    145 {
    146 #ifdef TRACE
    147 	tprintf("TRACE- dolabel()\n");
    148 #endif
    149 	return (0);
    150 }
    151 
    152 void
    153 doonintr(tchar **v)
    154 {
    155 	tchar *cp;
    156 	tchar *vv = v[1];
    157 
    158 #ifdef TRACE
    159 	tprintf("TRACE- doonintr()\n");
    160 #endif
    161 	if (parintr == SIG_IGN) {
    162 		return;
    163 	}
    164 	if (setintr && intty) {
    165 		bferr("Can't from terminal");
    166 	}
    167 	cp = gointr, gointr = 0, xfree(cp);
    168 	if (vv == 0) {
    169 		if (setintr) {
    170 			(void) sigblock(sigmask(SIGINT));
    171 		} else {
    172 			(void) signal(SIGINT, SIG_DFL);
    173 		}
    174 		gointr = 0;
    175 	} else if (eq((vv = strip(vv)), S_MINUS)) {
    176 		(void) signal(SIGINT, SIG_IGN);
    177 		gointr = S_MINUS;
    178 	} else {
    179 		gointr = savestr(vv);
    180 		(void) signal(SIGINT, pintr);
    181 	}
    182 }
    183 
    184 void
    185 donohup(void)
    186 {
    187 
    188 #ifdef TRACE
    189 	tprintf("TRACE- donohup()\n");
    190 #endif
    191 	if (intty) {
    192 		bferr("Can't from terminal");
    193 	}
    194 	if (setintr == 0) {
    195 		(void) signal(SIGHUP, SIG_IGN);
    196 #ifdef CC
    197 		submit(getpid());
    198 #endif
    199 	}
    200 }
    201 
    202 void
    203 dozip(void)
    204 {
    205 	;
    206 }
    207 
    208 void
    209 prvars(void)
    210 {
    211 #ifdef TRACE
    212 	tprintf("TRACE- prvars()\n");
    213 #endif
    214 
    215 	plist(&shvhed);
    216 }
    217 
    218 void
    219 doalias(tchar **v)
    220 {
    221 	struct varent *vp;
    222 	tchar *p;
    223 
    224 #ifdef TRACE
    225 	tprintf("TRACE- doalias()\n");
    226 #endif
    227 	v++;
    228 	p = *v++;
    229 	if (p == 0) {
    230 		plist(&aliases);
    231 	} else if (*v == 0) {
    232 		vp = adrof1(strip(p), &aliases);
    233 		if (vp) {
    234 			blkpr(vp->vec), printf("\n");
    235 		}
    236 	} else {
    237 		if (eq(p, S_alias) ||
    238 		    eq(p, S_unalias)) {
    239 			setname(p);
    240 			bferr("Too dangerous to alias that");
    241 		}
    242 		set1(strip(p), saveblk(v), &aliases);
    243 	}
    244 }
    245 
    246 void
    247 unalias(tchar **v)
    248 {
    249 
    250 #ifdef TRACE
    251 	tprintf("TRACE- unalias()\n");
    252 #endif
    253 	unset1(v, &aliases);
    254 }
    255 
    256 void
    257 dologout(void)
    258 {
    259 
    260 #ifdef TRACE
    261 	tprintf("TRACE- dologout()\n");
    262 #endif
    263 	islogin();
    264 	goodbye();
    265 }
    266 
    267 void
    268 dologin(tchar **v)
    269 {
    270 
    271 	char *v_;	/* work */
    272 #ifdef TRACE
    273 	tprintf("TRACE- dologin()\n");
    274 #endif
    275 	islogin();
    276 	rechist();
    277 	(void) signal(SIGTERM, parterm);
    278 	if (v[1] != NULL) {
    279 		v_ = tstostr(NULL, v[1]);	/* No need to free */
    280 	} else {
    281 		v_ = 0;
    282 	}
    283 	execl("/bin/login", "login", v_, 0);
    284 	untty();
    285 	exit(1);
    286 }
    287 
    288 #ifdef NEWGRP
    289 void
    290 donewgrp(tchar **v)
    291 {
    292 
    293 	char *v_;	/* work */
    294 #ifdef TRACE
    295 	tprintf("TRACE- donewgrp()\n");
    296 #endif
    297 	if (chkstop == 0 && setintr) {
    298 		panystop(0);
    299 	}
    300 	(void) signal(SIGTERM, parterm);
    301 
    302 	if (v[1] != NULL) {
    303 		v_ = tstostr(NOSTR, v[1]);	/* No need to free */
    304 	} else {
    305 		v_ = 0;
    306 	}
    307 	execl("/bin/newgrp", "newgrp", v_, 0);
    308 	execl("/usr/bin/newgrp", "newgrp", v_, 0);
    309 	untty();
    310 	exit(1);
    311 }
    312 #endif
    313 
    314 void
    315 islogin(void)
    316 {
    317 
    318 #ifdef TRACE
    319 	tprintf("TRACE- islogin()\n");
    320 #endif
    321 	if (chkstop == 0 && setintr) {
    322 		panystop(0);
    323 	}
    324 	if (loginsh) {
    325 		return;
    326 	}
    327 	error("Not login shell");
    328 }
    329 
    330 void
    331 doif(tchar **v, struct command *kp)
    332 {
    333 	int i;
    334 	tchar **vv;
    335 
    336 #ifdef TRACE
    337 	tprintf("TRACE- doif()\n");
    338 #endif
    339 	v++;
    340 	i = exp(&v);
    341 	vv = v;
    342 	if (*vv == NOSTR) {
    343 		bferr("Empty if");
    344 	}
    345 	if (eq(*vv, S_then)) {
    346 		if (*++vv) {
    347 			bferr("Improper then");
    348 		}
    349 		setname(S_then);
    350 		/*
    351 		 * If expression was zero, then scan to else,
    352 		 * otherwise just fall into following code.
    353 		 */
    354 		if (!i) {
    355 			search(ZIF, 0);
    356 		}
    357 		return;
    358 	}
    359 	/*
    360 	 * Simple command attached to this if.
    361 	 * Left shift the node in this tree, munging it
    362 	 * so we can reexecute it.
    363 	 */
    364 	if (i) {
    365 		lshift(kp->t_dcom, vv - kp->t_dcom);
    366 		reexecute(kp);
    367 		donefds();
    368 	}
    369 }
    370 
    371 /*
    372  * Reexecute a command, being careful not
    373  * to redo i/o redirection, which is already set up.
    374  */
    375 void
    376 reexecute(struct command *kp)
    377 {
    378 
    379 #ifdef TRACE
    380 	tprintf("TRACE- reexecute()\n");
    381 #endif
    382 	kp->t_dflg &= FSAVE;
    383 	kp->t_dflg |= FREDO;
    384 	/*
    385 	 * If tty is still ours to arbitrate, arbitrate it;
    386 	 * otherwise dont even set pgrp's as the jobs would
    387 	 * then have no way to get the tty (we can't give it
    388 	 * to them, and our parent wouldn't know their pgrp, etc.
    389 	 */
    390 	execute(kp, tpgrp > 0 ? tpgrp : -1);
    391 }
    392 
    393 void
    394 doelse(void)
    395 {
    396 
    397 #ifdef TRACE
    398 	tprintf("TRACE- doelse()\n");
    399 #endif
    400 	search(ZELSE, 0);
    401 }
    402 
    403 void
    404 dogoto(tchar **v)
    405 {
    406 	struct whyle *wp;
    407 	tchar *lp;
    408 #ifdef TRACE
    409 	tprintf("TRACE- dogoto()\n");
    410 #endif
    411 
    412 	/*
    413 	 * While we still can, locate any unknown ends of existing loops.
    414 	 * This obscure code is the WORST result of the fact that we
    415 	 * don't really parse.
    416 	 */
    417 	for (wp = whyles; wp; wp = wp->w_next) {
    418 		if (wp->w_end == 0) {
    419 			search(ZBREAK, 0);
    420 			wp->w_end = btell();
    421 		} else {
    422 			bseek(wp->w_end);
    423 		}
    424 	}
    425 	search(ZGOTO, 0, lp = globone(v[1]));
    426 	xfree(lp);
    427 	/*
    428 	 * Eliminate loops which were exited.
    429 	 */
    430 	wfree();
    431 }
    432 
    433 void
    434 doswitch(tchar **v)
    435 {
    436 	tchar *cp, *lp;
    437 
    438 #ifdef TRACE
    439 	tprintf("TRACE- doswitch()\n");
    440 #endif
    441 	v++;
    442 	if (!*v || *(*v++) != '(') {
    443 		goto syntax;
    444 	}
    445 	cp = **v == ')' ? S_ : *v++;
    446 	if (*(*v++) != ')') {
    447 		v--;
    448 	}
    449 	if (*v) {
    450 syntax:
    451 		error("Syntax error");
    452 	}
    453 	search(ZSWITCH, 0, lp = globone(cp));
    454 	xfree(lp);
    455 }
    456 
    457 void
    458 dobreak(void)
    459 {
    460 
    461 #ifdef TRACE
    462 	tprintf("TRACE- dobreak()\n");
    463 #endif
    464 	if (whyles) {
    465 		toend();
    466 	} else {
    467 		bferr("Not in while/foreach");
    468 	}
    469 }
    470 
    471 void
    472 doexit(tchar **v)
    473 {
    474 
    475 #ifdef TRACE
    476 	tprintf("TRACE- doexit()\n");
    477 #endif
    478 	if (chkstop == 0) {
    479 		panystop(0);
    480 	}
    481 	/*
    482 	 * Don't DEMAND parentheses here either.
    483 	 */
    484 	v++;
    485 	if (*v) {
    486 		set(S_status, putn(exp(&v)));
    487 		if (*v) {
    488 			bferr("Expression syntax");
    489 		}
    490 	}
    491 	btoeof();
    492 	if (intty) {
    493 		(void) close(SHIN);
    494 		unsetfd(SHIN);
    495 	}
    496 }
    497 
    498 void
    499 doforeach(tchar **v)
    500 {
    501 	tchar *cp;
    502 	struct whyle *nwp;
    503 
    504 #ifdef TRACE
    505 	tprintf("TRACE- doforeach()\n");
    506 #endif
    507 	v++;
    508 	cp = strip(*v);
    509 	while (*cp && alnum(*cp)) {
    510 		cp++;
    511 	}
    512 	if (*cp || strlen_(*v) >= MAX_VAR_LEN || !letter(**v)) {
    513 		bferr("Invalid variable");
    514 	}
    515 	cp = *v++;
    516 	if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') {
    517 		bferr("Words not ()'ed");
    518 	}
    519 	v++;
    520 	gflag = 0, tglob(v);
    521 	v = glob(v);
    522 	if (v == 0) {
    523 		bferr("No match");
    524 	}
    525 	nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));
    526 	nwp->w_fe = nwp->w_fe0 = v; gargv = 0;
    527 	nwp->w_start = btell();
    528 	nwp->w_fename = savestr(cp);
    529 	nwp->w_next = whyles;
    530 	whyles = nwp;
    531 	/*
    532 	 * Pre-read the loop so as to be more
    533 	 * comprehensible to a terminal user.
    534 	 */
    535 	if (intty) {
    536 		preread_();
    537 	}
    538 	doagain();
    539 }
    540 
    541 void
    542 dowhile(tchar **v)
    543 {
    544 	int status;
    545 	bool again = whyles != 0 && whyles->w_start == lineloc &&
    546 	    whyles->w_fename == 0;
    547 
    548 #ifdef TRACE
    549 	tprintf("TRACE- dowhile()\n");
    550 #endif
    551 	v++;
    552 	/*
    553 	 * Implement prereading here also, taking care not to
    554 	 * evaluate the expression before the loop has been read up
    555 	 * from a terminal.
    556 	 */
    557 	if (intty && !again) {
    558 		status = !exp0(&v, 1);
    559 	} else {
    560 		status = !exp(&v);
    561 	}
    562 	if (*v) {
    563 		bferr("Expression syntax");
    564 	}
    565 	if (!again) {
    566 		struct whyle *nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));
    567 
    568 		nwp->w_start = lineloc;
    569 		nwp->w_end = 0;
    570 		nwp->w_next = whyles;
    571 		whyles = nwp;
    572 		if (intty) {
    573 			/*
    574 			 * The tty preread
    575 			 */
    576 			preread_();
    577 			doagain();
    578 			return;
    579 		}
    580 	}
    581 	if (status) {
    582 		/* We ain't gonna loop no more, no more! */
    583 		toend();
    584 	}
    585 }
    586 
    587 void
    588 preread_(void)
    589 {
    590 #ifdef TRACE
    591 	tprintf("TRACE- preread()\n");
    592 #endif
    593 
    594 	whyles->w_end = -1;
    595 	if (setintr) {
    596 		(void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
    597 	}
    598 	search(ZBREAK, 0);
    599 	if (setintr) {
    600 		(void) sigblock(sigmask(SIGINT));
    601 	}
    602 	whyles->w_end = btell();
    603 }
    604 
    605 void
    606 doend(void)
    607 {
    608 
    609 #ifdef TRACE
    610 	tprintf("TRACE- doend()\n");
    611 #endif
    612 	if (!whyles) {
    613 		bferr("Not in while/foreach");
    614 	}
    615 	whyles->w_end = btell();
    616 	doagain();
    617 }
    618 
    619 void
    620 docontin(void)
    621 {
    622 #ifdef TRACE
    623 	tprintf("TRACE- docontin()\n");
    624 #endif
    625 
    626 	if (!whyles) {
    627 		bferr("Not in while/foreach");
    628 	}
    629 	doagain();
    630 }
    631 
    632 void
    633 doagain(void)
    634 {
    635 
    636 #ifdef TRACE
    637 	tprintf("TRACE- doagain()\n");
    638 #endif
    639 	/* Repeating a while is simple */
    640 	if (whyles->w_fename == 0) {
    641 		bseek(whyles->w_start);
    642 		return;
    643 	}
    644 	/*
    645 	 * The foreach variable list actually has a spurious word
    646 	 * ")" at the end of the w_fe list.  Thus we are at the
    647 	 * of the list if one word beyond this is 0.
    648 	 */
    649 	if (!whyles->w_fe[1]) {
    650 		dobreak();
    651 		return;
    652 	}
    653 	set(whyles->w_fename, savestr(*whyles->w_fe++));
    654 	bseek(whyles->w_start);
    655 }
    656 
    657 void
    658 dorepeat(tchar **v, struct command *kp)
    659 {
    660 	int i, omask;
    661 
    662 #ifdef TRACE
    663 	tprintf("TRACE- dorepeat()\n");
    664 #endif
    665 	i = getn(v[1]);
    666 	if (setintr) {
    667 		omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
    668 	}
    669 	lshift(v, 2);
    670 	while (i > 0) {
    671 		if (setintr) {
    672 			(void) sigsetmask(omask);
    673 		}
    674 		reexecute(kp);
    675 		--i;
    676 	}
    677 	donefds();
    678 	if (setintr) {
    679 		(void) sigsetmask(omask);
    680 	}
    681 }
    682 
    683 void
    684 doswbrk(void)
    685 {
    686 
    687 #ifdef TRACE
    688 	tprintf("TRACE- doswbrk()\n");
    689 #endif
    690 	search(ZBRKSW, 0);
    691 }
    692 
    693 int
    694 srchx(tchar *cp)
    695 {
    696 	struct srch *sp, *sp1, *sp2;
    697 	int i;
    698 
    699 #ifdef TRACE
    700 	tprintf("TRACE- srchx()\n");
    701 #endif
    702 	/*
    703 	 * Binary search
    704 	 * Sp1 is the beginning of the current search range.
    705 	 * Sp2 is one past the end.
    706 	 */
    707 	for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2; ) {
    708 		sp = sp1 + (sp2 - sp1 >> 1);
    709 		if ((i = *cp - *sp->s_name) == 0 &&
    710 		    (i = strcmp_(cp, sp->s_name)) == 0) {
    711 			return (sp->s_value);
    712 		}
    713 		if (i < 0) {
    714 			sp2 = sp;
    715 		} else {
    716 			sp1 = sp + 1;
    717 		}
    718 	}
    719 	return (-1);
    720 }
    721 
    722 tchar Stype;
    723 tchar *Sgoal;
    724 
    725 /*VARARGS2*/
    726 void
    727 search(type, level, goal)
    728 	int type; int level; tchar *goal;
    729 {
    730 	tchar wordbuf[BUFSIZ];
    731 	tchar *aword = wordbuf;
    732 	tchar *cp;
    733 
    734 #ifdef TRACE
    735 	tprintf("TRACE- search()\n");
    736 #endif
    737 	Stype = type; Sgoal = goal;
    738 	if (type == ZGOTO) {
    739 		bseek((off_t)0);
    740 	}
    741 	do {
    742 		if (intty && fseekp == feobp) {
    743 			printf("? "), flush();
    744 		}
    745 		aword[0] = 0;
    746 		(void) getword(aword);
    747 
    748 		switch (srchx(aword)) {
    749 
    750 		case ZELSE:
    751 			if (level == 0 && type == ZIF) {
    752 				return;
    753 			}
    754 			break;
    755 
    756 		case ZIF:
    757 			while (getword(aword)) {
    758 				continue;
    759 			}
    760 			if ((type == ZIF || type == ZELSE) &&
    761 			    eq(aword, S_then)) {
    762 				level++;
    763 			}
    764 			break;
    765 
    766 		case ZENDIF:
    767 			if (type == ZIF || type == ZELSE) {
    768 				level--;
    769 			}
    770 			break;
    771 
    772 		case ZFOREACH:
    773 		case ZWHILE:
    774 			if (type == ZBREAK) {
    775 				level++;
    776 			}
    777 			break;
    778 
    779 		case ZEND:
    780 			if (type == ZBREAK) {
    781 				level--;
    782 			}
    783 			break;
    784 
    785 		case ZSWITCH:
    786 			if (type == ZSWITCH || type == ZBRKSW) {
    787 				level++;
    788 			}
    789 			break;
    790 
    791 		case ZENDSW:
    792 			if (type == ZSWITCH || type == ZBRKSW) {
    793 				level--;
    794 			}
    795 			break;
    796 
    797 		case ZLABEL:
    798 			if (type == ZGOTO && getword(aword) &&
    799 			    eq(aword, goal)) {
    800 				level = -1;
    801 			}
    802 			break;
    803 
    804 		default:
    805 			if (type != ZGOTO && (type != ZSWITCH || level != 0)) {
    806 				break;
    807 			}
    808 			if (lastchr(aword) != ':') {
    809 				break;
    810 			}
    811 			aword[strlen_(aword) - 1] = 0;
    812 			if (type == ZGOTO && eq(aword, goal) ||
    813 			    type == ZSWITCH && eq(aword, S_default)) {
    814 				level = -1;
    815 			}
    816 			break;
    817 
    818 		case ZCASE:
    819 			if (type != ZSWITCH || level != 0) {
    820 				break;
    821 			}
    822 			(void) getword(aword);
    823 			if (lastchr(aword) == ':') {
    824 				aword[strlen_(aword) - 1] = 0;
    825 			}
    826 			cp = strip(Dfix1(aword));
    827 			if (Gmatch(goal, cp)) {
    828 				level = -1;
    829 			}
    830 			xfree(cp);
    831 			break;
    832 
    833 		case ZDEFAULT:
    834 			if (type == ZSWITCH && level == 0) {
    835 				level = -1;
    836 			}
    837 			break;
    838 		}
    839 		(void) getword(NOSTR);
    840 	} while (level >= 0);
    841 }
    842 
    843 int
    844 getword(tchar *wp)
    845 {
    846 	int found = 0;
    847 	int c, d;
    848 #ifdef TRACE
    849 	tprintf("TRACE- getword()\n");
    850 #endif
    851 
    852 	c = readc(1);
    853 	d = 0;
    854 	do {
    855 		while (issp(c)) {
    856 			c = readc(1);
    857 		}
    858 		if (c == '#') {
    859 			do {
    860 				c = readc(1);
    861 			} while (c >= 0 && c != '\n');
    862 		}
    863 		if (c < 0) {
    864 			goto past;
    865 		}
    866 		if (c == '\n') {
    867 			if (wp) {
    868 				break;
    869 			}
    870 			return (0);
    871 		}
    872 
    873 		/* ( and ) form separate words */
    874 		if (c == '(' || c == ')') {
    875 			return (1);
    876 		}
    877 
    878 		unreadc(c);
    879 		found = 1;
    880 		do {
    881 			c = readc(1);
    882 			if (c == '\\' && (c = readc(1)) == '\n') {
    883 				c = ' ';
    884 			}
    885 			if (c == '\'' || c == '"') {
    886 				if (d == 0) {
    887 					d = c;
    888 				} else if (d == c) {
    889 					d = 0;
    890 				}
    891 			}
    892 			if (c < 0) {
    893 				goto past;
    894 			}
    895 			if (wp) {
    896 				*wp++ = c;
    897 			}
    898 		} while ((d || !issp(c) && c != '(' && c != ')') && c != '\n');
    899 	} while (wp == 0);
    900 	unreadc(c);
    901 	if (found) {
    902 		*--wp = 0;
    903 	}
    904 	return (found);
    905 
    906 past:
    907 	switch (Stype) {
    908 
    909 	case ZIF:
    910 		bferr("then/endif not found");
    911 
    912 	case ZELSE:
    913 		bferr("endif not found");
    914 
    915 	case ZBRKSW:
    916 	case ZSWITCH:
    917 		bferr("endsw not found");
    918 
    919 	case ZBREAK:
    920 		bferr("end not found");
    921 
    922 	case ZGOTO:
    923 		setname(Sgoal);
    924 		bferr("label not found");
    925 	}
    926 	/*NOTREACHED*/
    927 
    928 	return (0);
    929 }
    930 
    931 void
    932 toend(void)
    933 {
    934 
    935 #ifdef TRACE
    936 	tprintf("TRACE- toend()\n");
    937 #endif
    938 	if (whyles->w_end == 0) {
    939 		search(ZBREAK, 0);
    940 		whyles->w_end = btell() - 1;
    941 	} else {
    942 		bseek(whyles->w_end);
    943 	}
    944 	wfree();
    945 }
    946 
    947 void
    948 wfree(void)
    949 {
    950 	long o = btell();
    951 
    952 #ifdef TRACE
    953 	tprintf("TRACE- wfree()\n");
    954 #endif
    955 	while (whyles) {
    956 		struct whyle *wp = whyles;
    957 		struct whyle *nwp = wp->w_next;
    958 
    959 		if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) {
    960 			break;
    961 		}
    962 		if (wp->w_fe0) {
    963 			blkfree(wp->w_fe0);
    964 		}
    965 		if (wp->w_fename) {
    966 			xfree(wp->w_fename);
    967 		}
    968 		xfree((char *)wp);
    969 		whyles = nwp;
    970 	}
    971 }
    972 
    973 void
    974 doecho(tchar **v)
    975 {
    976 
    977 #ifdef TRACE
    978 	tprintf("TRACE- doecho()\n");
    979 #endif
    980 	echo(' ', v);
    981 }
    982 
    983 void
    984 doglob(tchar **v)
    985 {
    986 
    987 #ifdef TRACE
    988 	tprintf("TRACE- doglob()\n");
    989 #endif
    990 	echo(0, v);
    991 	flush();
    992 }
    993 
    994 void
    995 echo(tchar sep, tchar **v)
    996 {
    997 	tchar *cp;
    998 	int nonl = 0;
    999 
   1000 #ifdef TRACE
   1001 	tprintf("TRACE- echo()\n");
   1002 #endif
   1003 	if (setintr) {
   1004 		(void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
   1005 	}
   1006 	v++;
   1007 	if (*v == 0) {
   1008 		/*
   1009 		 * echo command needs to have newline when there are no
   1010 		 * flags or arguments.  glob should have no newline.  If
   1011 		 * the separator is a blank, we are doing an echo.  If the
   1012 		 * separator is zero, we are globbing.
   1013 		 */
   1014 		if (sep == (tchar)' ')
   1015 			Putchar('\n');
   1016 		return;
   1017 	}
   1018 	gflag = 0, tglob(v);
   1019 	if (gflag) {
   1020 		v = glob(v);
   1021 		if (v == 0) {
   1022 			bferr("No match");
   1023 		}
   1024 	}
   1025 	/* check for -n arg, NOTE: it might be quoted */
   1026 	if (sep == ' ' && *v && strlen_(*v) == 2 &&
   1027 	    ((**v&TRIM) == '-' && (*(*v + 1) & TRIM) == 'n' &&
   1028 	    (*(*v+2)&TRIM) == 0)) {
   1029 		nonl++, v++;
   1030 	}
   1031 	while (cp = *v++) {
   1032 		int c;
   1033 
   1034 		while (c = *cp++) {
   1035 			Putchar(c | QUOTE);
   1036 		}
   1037 		if (*v) {
   1038 			Putchar(sep | QUOTE);
   1039 		}
   1040 	}
   1041 	if (sep && nonl == 0) {
   1042 		Putchar('\n');
   1043 	} else {
   1044 		flush();
   1045 	}
   1046 	if (setintr) {
   1047 		(void) sigblock(sigmask(SIGINT));
   1048 	}
   1049 	if (gargv) {
   1050 		blkfree(gargv), gargv = 0;
   1051 	}
   1052 }
   1053 
   1054 extern char **environ;
   1055 
   1056 /*
   1057  * Check if the environment variable vp affects this csh's behavior
   1058  * and therefore we should call setlocale() or not.
   1059  * This function has two side effects when it returns 1:
   1060  *	variable islocalevar_catnum is set to the LC_xxx value.
   1061  *	variable islocalevar_catname is set to the string "LC_xxx"
   1062  */
   1063 static int	islocalevar_catnum;
   1064 static char	*islocalevar_catname;
   1065 
   1066 static
   1067 bool
   1068 islocalevar(tchar *vp)
   1069 {
   1070 	static struct lcinfo {
   1071 		tchar *	evname; /* The name of the env. var. */
   1072 	} categories_we_care[] = {
   1073 	    S_LANG, S_LC_ALL, S_LC_CTYPE, S_LC_MESSAGES,
   1074 	    NOSTR		/* assumption: LC_xxx >= 0 */
   1075 	};
   1076 	struct lcinfo *p = categories_we_care;
   1077 
   1078 	do {
   1079 		if (strcmp_(vp, p->evname) == 0) {
   1080 			return (1);
   1081 		}
   1082 	} while (((++p)->evname) != NOSTR);
   1083 	return (0);
   1084 }
   1085 
   1086 void
   1087 dosetenv(tchar **v)
   1088 {
   1089 	tchar *vp, *lp;
   1090 
   1091 #ifdef TRACE
   1092 	tprintf("TRACE- dosetenv()\n");
   1093 #endif
   1094 	v++;
   1095 	if ((vp = *v++) == 0) {
   1096 		char **ep;
   1097 
   1098 		if (setintr) {
   1099 			(void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT));
   1100 		}
   1101 		for (ep = environ; *ep; ep++) {
   1102 			printf("%s\n", *ep);
   1103 		}
   1104 		return;
   1105 	}
   1106 
   1107 	if ((lp = *v++) == 0) {
   1108 		lp = S_;	/* "" */
   1109 	}
   1110 	local_setenv(vp, lp = globone(lp));
   1111 	if (eq(vp, S_PATH)) {
   1112 		importpath(lp);
   1113 		dohash(xhash);
   1114 	} else if (islocalevar(vp)) {
   1115 		if (!setlocale(LC_ALL, "")) {
   1116 			error("Locale could not be set properly");
   1117 		}
   1118 	}
   1119 
   1120 	xfree(lp);
   1121 }
   1122 
   1123 void
   1124 dounsetenv(tchar **v)
   1125 {
   1126 #ifdef TRACE
   1127 	tprintf("TRACE- dounsetenv()\n");
   1128 #endif
   1129 	v++;
   1130 	do {
   1131 		local_unsetenv(*v);
   1132 		if (islocalevar(*v++)) {
   1133 			setlocale(LC_ALL, "");	/* Hope no error! */
   1134 		}
   1135 	} while (*v);
   1136 }
   1137 
   1138 void
   1139 local_setenv(tchar *name, tchar *val)
   1140 {
   1141 	char **ep = environ;
   1142 	tchar *cp;
   1143 	char *dp;
   1144 	tchar *ep_;	/* temporary */
   1145 	char *blk[2], **oep = ep;
   1146 
   1147 #ifdef TRACE
   1148 	/* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */
   1149 	/* printf("IN local_setenv args = (%t)\n", val); */
   1150 #endif
   1151 	for (; *ep; ep++) {
   1152 #ifdef MBCHAR
   1153 		for (cp = name, dp = *ep; *cp && *dp; cp++) {
   1154 			/*
   1155 			 * This loop compares two chars in different
   1156 			 * representations, EUC (as char *) and wchar_t
   1157 			 * (in tchar), and ends when they are different.
   1158 			 */
   1159 			wchar_t	dwc;
   1160 			int	n;
   1161 
   1162 			n = mbtowc(&dwc, dp, MB_CUR_MAX);
   1163 			if (n <= 0) {
   1164 				break; /* Illegal multibyte. */
   1165 			}
   1166 			dp += n; /* Advance to next multibyte char. */
   1167 			if (dwc == (wchar_t)(*cp & TRIM)) {
   1168 				continue;
   1169 			} else  {
   1170 				break;
   1171 			}
   1172 		}
   1173 #else /* !MBCHAR */
   1174 		for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
   1175 			continue;
   1176 		}
   1177 #endif /* !MBCHAR */
   1178 		if (*cp != 0 || *dp != '=') {
   1179 			continue;
   1180 		}
   1181 		cp = strspl(S_EQ, val);
   1182 		xfree(*ep);
   1183 		ep_ = strspl(name, cp);		/* ep_ is xalloc'ed */
   1184 		xfree(cp);
   1185 		/*
   1186 		 * Trimming is not needed here.
   1187 		 * trim();
   1188 		 */
   1189 		*ep = tstostr(NULL, ep_);
   1190 		xfree(ep_);			/* because temp.  use */
   1191 		return;
   1192 	}
   1193 	ep_ = strspl(name, S_EQ);		/* ep_ is xalloc'ed */
   1194 	blk[0] = tstostr(NULL, ep_);
   1195 	blk[1] = 0;
   1196 	xfree(ep_);
   1197 	environ = (char **)blkspl_((char **)environ, blk);
   1198 	xfree((void *)oep);
   1199 	local_setenv(name, val);
   1200 }
   1201 
   1202 void
   1203 local_unsetenv(tchar *name)
   1204 {
   1205 	char **ep = environ;
   1206 	tchar *cp;
   1207 	char *dp;
   1208 	char **oep = ep;
   1209 	char *cp_;	/* tmp use */
   1210 	static int cnt = 0;	/* delete counter */
   1211 
   1212 #ifdef TRACE
   1213 	tprintf("TRACE- local_unsetenv()\n");
   1214 #endif
   1215 	for (; *ep; ep++) {
   1216 #ifdef MBCHAR
   1217 		for (cp = name, dp = *ep; *cp && *dp; cp++) {
   1218 			/*
   1219 			 * This loop compares two chars in different
   1220 			 * representations, EUC (as char *) and wchar_t
   1221 			 * (in tchar), and ends when they are different.
   1222 			 */
   1223 			wchar_t	dwc;
   1224 			int	n;
   1225 
   1226 			n = mbtowc(&dwc, dp, MB_CUR_MAX);
   1227 			if (n <= 0) {
   1228 				break; /* Illegal multibyte. */
   1229 			}
   1230 			dp += n; /* Advance to next multibyte char. */
   1231 			if (dwc == (wchar_t)(*cp & TRIM)) {
   1232 				continue;
   1233 			} else {
   1234 				break;
   1235 			}
   1236 		}
   1237 #else /* !MBCHAR */
   1238 		for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
   1239 			continue;
   1240 		}
   1241 #endif /* !MBCHAR */
   1242 		if (*cp != 0 || *dp != '=') {
   1243 			continue;
   1244 		}
   1245 		cp_ = *ep;
   1246 		*ep = 0;
   1247 		environ = (char **)blkspl_((char **)environ, ep+1);
   1248 		*ep = cp_;
   1249 		xfree(cp_);
   1250 		xfree((void *)oep);
   1251 		return;
   1252 	}
   1253 }
   1254 
   1255 void
   1256 doumask(tchar **v)
   1257 {
   1258 	tchar *cp = v[1];
   1259 	int i;
   1260 
   1261 #ifdef TRACE
   1262 	tprintf("TRACE- dounmask()\n");
   1263 #endif
   1264 	if (cp == 0) {
   1265 		i = umask(0);
   1266 		(void) umask(i);
   1267 		printf("%o\n", i);
   1268 		return;
   1269 	}
   1270 	i = 0;
   1271 	while (digit(*cp) && *cp != '8' && *cp != '9') {
   1272 		i = i * 8 + *cp++ - '0';
   1273 	}
   1274 	if (*cp || i < 0 || i > 0777) {
   1275 		bferr("Improper mask");
   1276 	}
   1277 	(void) umask(i);
   1278 }
   1279 
   1280 
   1281 struct limits *
   1282 findlim(tchar *cp)
   1283 {
   1284 	struct limits *lp, *res;
   1285 
   1286 #ifdef TRACE
   1287 	tprintf("TRACE- findlim()\n");
   1288 #endif
   1289 	res = 0;
   1290 	for (lp = limits; lp->limconst >= 0; lp++) {
   1291 		if (prefix(cp, lp->limname)) {
   1292 			if (res) {
   1293 				bferr("Ambiguous");
   1294 			}
   1295 			res = lp;
   1296 		}
   1297 	}
   1298 	if (res) {
   1299 		return (res);
   1300 	}
   1301 	bferr("No such limit");
   1302 	/*NOTREACHED*/
   1303 }
   1304 
   1305 void
   1306 dolimit(tchar **v)
   1307 {
   1308 	struct limits *lp;
   1309 	rlim_t limit;
   1310 	tchar hard = 0;
   1311 
   1312 #ifdef TRACE
   1313 	tprintf("TRACE- dolimit()\n");
   1314 #endif
   1315 	v++;
   1316 	if (*v && eq(*v, S_h)) {
   1317 		hard = 1;
   1318 		v++;
   1319 	}
   1320 	if (*v == 0) {
   1321 		for (lp = limits; lp->