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