Home | History | Annotate | Line # | Download | only in csh
      1 /* $NetBSD: csh.c,v 1.56 2022/09/15 11:35:06 martin 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 __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993\
     35  The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)csh.c	8.2 (Berkeley) 10/12/93";
     41 #else
     42 __RCSID("$NetBSD: csh.c,v 1.56 2022/09/15 11:35:06 martin Exp $");
     43 #endif
     44 #endif /* not lint */
     45 
     46 #include <sys/types.h>
     47 #include <sys/ioctl.h>
     48 #include <sys/stat.h>
     49 
     50 #include <errno.h>
     51 #include <fcntl.h>
     52 #include <locale.h>
     53 #include <paths.h>	/* should this be included in pathnames.h instead? */
     54 #include <pwd.h>
     55 #include <stdarg.h>
     56 #include <stdlib.h>
     57 #include <string.h>
     58 #include <time.h>
     59 #include <unistd.h>
     60 #include <vis.h>
     61 
     62 #include "csh.h"
     63 #include "extern.h"
     64 #include "pathnames.h"
     65 #include "proc.h"
     66 
     67 /*
     68  * C Shell
     69  *
     70  * Bill Joy, UC Berkeley, California, USA
     71  * October 1978, May 1980
     72  *
     73  * Jim Kulp, IIASA, Laxenburg, Austria
     74  * April 1980
     75  *
     76  * Christos Zoulas, Cornell University
     77  * June, 1991
     78  */
     79 
     80 FILE *cshin, *cshout, *csherr;
     81 struct timespec time0;
     82 struct rusage ru0;
     83 struct varent shvhed, aliases;
     84 Char HISTSUB;
     85 int editing;
     86 
     87 int child;
     88 int chkstop;
     89 int didfds;
     90 int doneinp;
     91 int exiterr;
     92 int haderr;
     93 int havhash;
     94 int intact;
     95 int intty;
     96 int justpr;
     97 int loginsh;
     98 int neednote;
     99 int noexec;
    100 int pjobs;
    101 int setintr;
    102 int timflg;
    103 
    104 Char *arginp;
    105 Char *ffile;
    106 int onelflg;
    107 Char *shtemp;
    108 
    109 time_t chktim;
    110 Char *doldol;
    111 pid_t backpid;
    112 gid_t egid, gid;
    113 uid_t euid, uid;
    114 int shpgrp;
    115 int tpgrp;
    116 
    117 int opgrp;
    118 
    119 int SHIN;
    120 int SHOUT;
    121 int SHERR;
    122 int OLDSTD;
    123 
    124 jmp_buf reslab;
    125 
    126 Char *gointr;
    127 
    128 sig_t parintr;
    129 sig_t parterm;
    130 
    131 struct Bin B;
    132 
    133 struct Ain lineloc;
    134 int cantell;
    135 Char *lap;
    136 struct whyle *whyles;
    137 
    138 struct wordent *alhistp,*alhistt;
    139 
    140 int AsciiOnly;
    141 int gflag;
    142 long pnleft;
    143 Char *pargs;
    144 Char *pargcp;
    145 struct Hist Histlist;
    146 struct wordent paraml;
    147 int eventno;
    148 int lastev;
    149 Char HIST;
    150 Char HISTSUB;
    151 const char *bname;
    152 Char *Vsav;
    153 Char *Vdp;
    154 Char *Vexpath;
    155 char **Vt;
    156 Char **evalvec;
    157 Char *evalp;
    158 Char *word_chars;
    159 Char *STR_SHELLPATH;
    160 #ifdef _PATH_BSHELL
    161 Char *STR_BSHELL;
    162 #endif
    163 Char *STR_WORD_CHARS;
    164 Char **STR_environ;
    165 #ifdef EDIT
    166 EditLine *el;
    167 History *hi;
    168 #endif
    169 int editing;
    170 
    171 Char *dumphist[] = {STRhistory, STRmh, 0, 0};
    172 Char *tildehist[] = {STRsource, STRmh, STRtildothist, 0};
    173 
    174 int nofile = 0;
    175 int batch = 0;
    176 int enterhist = 0;
    177 int fast = 0;
    178 int mflag = 0;
    179 int nexececho = 0;
    180 int nverbose = 0;
    181 int prompt = 1;
    182 int quitit = 0;
    183 int reenter = 0;
    184 
    185 extern char **environ;
    186 
    187 static ssize_t readf(void *, void *, size_t);
    188 static off_t seekf(void *, off_t, int);
    189 static ssize_t writef(void *, const void *, size_t);
    190 static int closef(void *);
    191 static int srccat(Char *, Char *);
    192 static int srcfile(const char *, int, int);
    193 __dead static void phup(int);
    194 static void srcunit(int, int, int);
    195 static void mailchk(void);
    196 #ifndef _PATH_DEFPATH
    197 static Char **defaultpath(void);
    198 #endif
    199 
    200 int
    201 main(int argc, char *argv[])
    202 {
    203     struct sigaction oact;
    204     Char *cp;
    205     char *tcp, **tempv;
    206     const char *ecp;
    207     sigset_t nsigset;
    208     int f;
    209 
    210     cshin = stdin;
    211     cshout = stdout;
    212     csherr = stderr;
    213 
    214     setprogname(argv[0]);
    215     settimes();			/* Immed. estab. timing base */
    216 
    217     /*
    218      * Initialize non constant strings
    219      */
    220 #ifdef _PATH_BSHELL
    221     STR_BSHELL = SAVE(_PATH_BSHELL);
    222 #endif
    223 #ifdef _PATH_CSHELL
    224     STR_SHELLPATH = SAVE(_PATH_CSHELL);
    225 #endif
    226     STR_environ = blk2short(environ);
    227     environ = short2blk(STR_environ);	/* So that we can free it */
    228     STR_WORD_CHARS = SAVE(WORD_CHARS);
    229 
    230     HIST = '!';
    231     HISTSUB = '^';
    232     word_chars = STR_WORD_CHARS;
    233 
    234     tempv = argv;
    235     if (eq(str2short(tempv[0]), STRaout))	/* A.out's are quittable */
    236 	quitit = 1;
    237     uid = getuid();
    238     gid = getgid();
    239     euid = geteuid();
    240     egid = getegid();
    241     /*
    242      * We are a login shell if: 1. we were invoked as -<something> and we had
    243      * no arguments 2. or we were invoked only with the -l flag
    244      */
    245     loginsh = (**tempv == '-' && argc == 1) ||
    246 	(argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' &&
    247 	 tempv[1][2] == '\0');
    248 
    249     if (loginsh && **tempv != '-') {
    250 	/*
    251 	 * Mangle the argv space
    252 	 */
    253 	tempv[1][0] = '\0';
    254 	tempv[1][1] = '\0';
    255 	tempv[1] = NULL;
    256 	for (tcp = *tempv; *tcp++;)
    257 	    continue;
    258 	for (tcp--; tcp >= *tempv; tcp--)
    259 	    tcp[1] = tcp[0];
    260 	*++tcp = '-';
    261 	argc--;
    262     }
    263     if (loginsh)
    264 	(void)time(&chktim);
    265 
    266     AsciiOnly = 1;
    267 #ifdef NLS
    268     (void)setlocale(LC_ALL, "");
    269     {
    270 	int k;
    271 
    272 	for (k = 0200; k <= 0377 && !Isprint(k); k++)
    273 	    continue;
    274 	AsciiOnly = k > 0377;
    275     }
    276 #else
    277     AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
    278 #endif				/* NLS */
    279 
    280     /*
    281      * Move the descriptors to safe places. The variable didfds is 0 while we
    282      * have only FSH* to work with. When didfds is true, we have 0,1,2 and
    283      * prefer to use these.
    284      */
    285     initdesc();
    286     /*
    287      * XXX: This is to keep programs that use stdio happy.
    288      *	    what we really want is freunopen() ....
    289      *	    Closing cshin cshout and csherr (which are really stdin stdout
    290      *	    and stderr at this point and then reopening them in the same order
    291      *	    gives us again stdin == cshin stdout == cshout and stderr == csherr.
    292      *	    If that was not the case builtins like printf that use stdio
    293      *	    would break. But in any case we could fix that with memcpy and
    294      *	    a bit of pointer manipulation...
    295      *	    Fortunately this is not needed under the current implementation
    296      *	    of stdio.
    297      */
    298     (void)fclose(cshin);
    299     (void)fclose(cshout);
    300     (void)fclose(csherr);
    301     if (!(cshin  = funopen2((void *) &SHIN,  readf, writef, seekf, NULL,
    302 	closef)))
    303 	exit(1);
    304     if (!(cshout = funopen2((void *) &SHOUT, readf, writef, seekf, NULL,
    305 	closef)))
    306 	exit(1);
    307     if (!(csherr = funopen2((void *) &SHERR, readf, writef, seekf, NULL,
    308 	closef)))
    309 	exit(1);
    310     (void)setvbuf(cshin,  NULL, _IOLBF, 0);
    311     (void)setvbuf(cshout, NULL, _IOLBF, 0);
    312     (void)setvbuf(csherr, NULL, _IOLBF, 0);
    313 
    314     /*
    315      * Initialize the shell variables. ARGV and PROMPT are initialized later.
    316      * STATUS is also munged in several places. CHILD is munged when
    317      * forking/waiting
    318      */
    319     set(STRstatus, Strsave(STR0));
    320 
    321     if ((ecp = getenv("HOME")) != NULL)
    322 	cp = quote(SAVE(ecp));
    323     else
    324 	cp = NULL;
    325 
    326     if (cp == NULL)
    327 	fast = 1;		/* No home -> can't read scripts */
    328     else
    329 	set(STRhome, cp);
    330     dinit(cp);			/* dinit thinks that HOME == cwd in a login
    331 				 * shell */
    332     /*
    333      * Grab other useful things from the environment. Should we grab
    334      * everything??
    335      */
    336     if ((ecp = getenv("LOGNAME")) != NULL ||
    337 	(ecp = getenv("USER")) != NULL)
    338 	set(STRuser, quote(SAVE(ecp)));
    339     if ((ecp = getenv("TERM")) != NULL)
    340 	set(STRterm, quote(SAVE(ecp)));
    341 
    342     /*
    343      * Re-initialize path if set in environment
    344      */
    345     if ((ecp = getenv("PATH")) == NULL) {
    346 #ifdef _PATH_DEFPATH
    347 	importpath(str2short(_PATH_DEFPATH));
    348 #else
    349 	setq(STRpath, defaultpath(), &shvhed);
    350 #endif
    351     } else {
    352 	importpath(str2short(ecp));
    353     }
    354 
    355     set(STRshell, Strsave(STR_SHELLPATH));
    356 
    357     doldol = putn((int) getpid());	/* For $$ */
    358     shtemp = Strspl(STRtmpsh, doldol);	/* For << */
    359 
    360     /*
    361      * Record the interrupt states from the parent process. If the parent is
    362      * non-interruptible our hand must be forced or we (and our children) won't
    363      * be either. Our children inherit termination from our parent. We catch it
    364      * only if we are the login shell.
    365      */
    366     /* parents interruptibility */
    367     (void)sigaction(SIGINT, NULL, &oact);
    368     parintr = oact.sa_handler;
    369     (void)sigaction(SIGTERM, NULL, &oact);
    370     parterm = oact.sa_handler;
    371 
    372     /* catch these all, login shell or not */
    373     (void)signal(SIGHUP, phup);	/* exit processing on HUP */
    374     (void)signal(SIGXCPU, phup);	/* ...and on XCPU */
    375     (void)signal(SIGXFSZ, phup);	/* ...and on XFSZ */
    376 
    377     /*
    378      * Process the arguments.
    379      *
    380      * Note that processing of -v/-x is actually delayed till after script
    381      * processing.
    382      *
    383      * We set the first character of our name to be '-' if we are a shell
    384      * running interruptible commands.  Many programs which examine ps'es
    385      * use this to filter such shells out.
    386      */
    387     argc--, tempv++;
    388     while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) {
    389 	do
    390 	    switch (*tcp++) {
    391 	    case 0:		/* -	Interruptible, no prompt */
    392 		prompt = 0;
    393 		setintr = 1;
    394 		nofile = 1;
    395 		break;
    396 	    case 'b':		/* -b	Next arg is input file */
    397 		batch = 1;
    398 		break;
    399 	    case 'c':		/* -c	Command input from arg */
    400 		if (argc == 1)
    401 		    xexit(0);
    402 		argc--, tempv++;
    403 		arginp = SAVE(tempv[0]);
    404 		prompt = 0;
    405 		nofile = 1;
    406 		break;
    407 	    case 'e':		/* -e	Exit on any error */
    408 		exiterr = 1;
    409 		break;
    410 	    case 'f':		/* -f	Fast start */
    411 		fast = 1;
    412 		break;
    413 	    case 'i':		/* -i	Interactive, even if !intty */
    414 		intact = 1;
    415 		nofile = 1;
    416 		break;
    417 	    case 'm':		/* -m	read .cshrc (from su) */
    418 		mflag = 1;
    419 		break;
    420 	    case 'n':		/* -n	Don't execute */
    421 		noexec = 1;
    422 		break;
    423 	    case 'q':		/* -q	(Undoc'd) ... die on quit */
    424 		quitit = 1;
    425 		break;
    426 	    case 's':		/* -s	Read from std input */
    427 		nofile = 1;
    428 		break;
    429 	    case 't':		/* -t	Read one line from input */
    430 		onelflg = 2;
    431 		prompt = 0;
    432 		nofile = 1;
    433 		break;
    434 	    case 'v':		/* -v	Echo hist expanded input */
    435 		nverbose = 1;	/* ... later */
    436 		break;
    437 	    case 'x':		/* -x	Echo just before execution */
    438 		nexececho = 1;	/* ... later */
    439 		break;
    440 	    case 'V':		/* -V	Echo hist expanded input */
    441 		setNS(STRverbose);	/* NOW! */
    442 		break;
    443 	    case 'X':		/* -X	Echo just before execution */
    444 		setNS(STRecho);	/* NOW! */
    445 		break;
    446 
    447 	} while (*tcp);
    448 	tempv++, argc--;
    449     }
    450 
    451     if (quitit)			/* With all due haste, for debugging */
    452 	(void)signal(SIGQUIT, SIG_DFL);
    453 
    454     /*
    455      * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
    456      * arguments the first of them is the name of a shell file from which to
    457      * read commands.
    458      */
    459     if (nofile == 0 && argc > 0) {
    460 	nofile = open(tempv[0], O_RDONLY);
    461 	if (nofile < 0) {
    462 	    child = 1;		/* So this doesn't return */
    463 	    stderror(ERR_SYSTEM, tempv[0], strerror(errno));
    464 	}
    465 	ffile = SAVE(tempv[0]);
    466 	/*
    467 	 * Replace FSHIN. Handle /dev/std{in,out,err} specially
    468 	 * since once they are closed we cannot open them again.
    469 	 * In that case we use our own saved descriptors
    470 	 */
    471 	if ((SHIN = dmove(nofile, FSHIN)) < 0)
    472 	    switch(nofile) {
    473 	    case 0:
    474 		SHIN = FSHIN;
    475 		break;
    476 	    case 1:
    477 		SHIN = FSHOUT;
    478 		break;
    479 	    case 2:
    480 		SHIN = FSHERR;
    481 		break;
    482 	    default:
    483 		stderror(ERR_SYSTEM, tempv[0], strerror(errno));
    484 		/* NOTREACHED */
    485 	    }
    486 	(void)ioctl(SHIN, FIOCLEX, NULL);
    487 	prompt = 0;
    488 	 /* argc not used any more */ tempv++;
    489     }
    490 
    491     intty = isatty(SHIN);
    492     intty |= intact;
    493     if (intty || (intact && isatty(SHOUT))) {
    494 	if (!batch && (uid != euid || gid != egid)) {
    495 	    errno = EACCES;
    496 	    child = 1;		/* So this doesn't return */
    497 	    stderror(ERR_SYSTEM, "csh", strerror(errno));
    498 	}
    499     }
    500     /*
    501      * Decide whether we should play with signals or not. If we are explicitly
    502      * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
    503      * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
    504      * Note that in only the login shell is it likely that parent may have set
    505      * signals to be ignored
    506      */
    507     if (loginsh || intact || (intty && isatty(SHOUT)))
    508 	setintr = 1;
    509     settell();
    510     /*
    511      * Save the remaining arguments in argv.
    512      */
    513     setq(STRargv, blk2short(tempv), &shvhed);
    514 
    515     /*
    516      * Set up the prompt.
    517      */
    518     if (prompt) {
    519 	set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent));
    520 	/* that's a meta-questionmark */
    521 	set(STRprompt2, Strsave(STRmquestion));
    522     }
    523 
    524     /*
    525      * If we are an interactive shell, then start fiddling with the signals;
    526      * this is a tricky game.
    527      */
    528     shpgrp = getpgrp();
    529     opgrp = tpgrp = -1;
    530     if (setintr) {
    531 	**argv = '-';
    532 	if (!quitit)		/* Wary! */
    533 	    (void)signal(SIGQUIT, SIG_IGN);
    534 	(void)signal(SIGINT, pintr);
    535 	sigemptyset(&nsigset);
    536 	(void)sigaddset(&nsigset, SIGINT);
    537 	(void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
    538 	(void)signal(SIGTERM, SIG_IGN);
    539 	if (quitit == 0 && arginp == 0) {
    540 	    (void)signal(SIGTSTP, SIG_IGN);
    541 	    (void)signal(SIGTTIN, SIG_IGN);
    542 	    (void)signal(SIGTTOU, SIG_IGN);
    543 	    /*
    544 	     * Wait till in foreground, in case someone stupidly runs csh &
    545 	     * dont want to try to grab away the tty.
    546 	     */
    547 	    if (isatty(FSHERR))
    548 		f = FSHERR;
    549 	    else if (isatty(FSHOUT))
    550 		f = FSHOUT;
    551 	    else if (isatty(OLDSTD))
    552 		f = OLDSTD;
    553 	    else
    554 		f = -1;
    555     retry:
    556 	    if ((tpgrp = tcgetpgrp(f)) != -1) {
    557 		if (tpgrp != shpgrp) {
    558 		    sig_t old = signal(SIGTTIN, SIG_DFL);
    559 		    (void)kill(0, SIGTTIN);
    560 		    (void)signal(SIGTTIN, old);
    561 		    goto retry;
    562 		}
    563 		opgrp = shpgrp;
    564 		shpgrp = getpid();
    565 		tpgrp = shpgrp;
    566 		/*
    567 		 * Setpgid will fail if we are a session leader and
    568 		 * mypid == mypgrp (POSIX 4.3.3)
    569 		 */
    570 		if (opgrp != shpgrp)
    571 		    if (setpgid(0, shpgrp) == -1)
    572 			goto notty;
    573 		/*
    574 		 * We do that after we set our process group, to make sure
    575 		 * that the process group belongs to a process in the same
    576 		 * session as the tty (our process and our group) (POSIX 7.2.4)
    577 		 */
    578 		if (tcsetpgrp(f, shpgrp) == -1)
    579 		    goto notty;
    580 		(void)ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL);
    581 	    }
    582 	    if (tpgrp == -1) {
    583 notty:
    584 		(void)fprintf(csherr, "Warning: no access to tty (%s).\n",
    585 			       strerror(errno));
    586 		(void)fprintf(csherr, "Thus no job control in this shell.\n");
    587 	    }
    588 	}
    589     }
    590     if ((setintr == 0) && (parintr == SIG_DFL))
    591 	setintr = 1;
    592     (void)signal(SIGCHLD, pchild);	/* while signals not ready */
    593 
    594     /*
    595      * Set an exit here in case of an interrupt or error reading the shell
    596      * start-up scripts.
    597      */
    598     reenter = setexit();	/* PWP */
    599     haderr = 0;			/* In case second time through */
    600     if (!fast && reenter == 0) {
    601 	/* Will have value(STRhome) here because set fast if don't */
    602 	{
    603 	    sig_t oparintr;
    604 	    sigset_t osigset;
    605 	    int osetintr;
    606 
    607 	    oparintr = parintr;
    608 	    osetintr = setintr;
    609 	    sigemptyset(&nsigset);
    610 	    (void)sigaddset(&nsigset, SIGINT);
    611 	    (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
    612 
    613 	    setintr = 0;
    614 	    parintr = SIG_IGN;	/* Disable onintr */
    615 #ifdef _PATH_DOTCSHRC
    616 	    (void)srcfile(_PATH_DOTCSHRC, 0, 0);
    617 #endif
    618 	    if (!fast && !arginp && !onelflg)
    619 		dohash(NULL, NULL);
    620 #ifdef _PATH_DOTLOGIN
    621 	    if (loginsh)
    622 		(void)srcfile(_PATH_DOTLOGIN, 0, 0);
    623 #endif
    624 	    (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
    625 	    setintr = osetintr;
    626 	    parintr = oparintr;
    627 	}
    628 	(void)srccat(value(STRhome), STRsldotcshrc);
    629 
    630 	if (!fast && !arginp && !onelflg && !havhash)
    631 	    dohash(NULL, NULL);
    632 	/*
    633 	 * Source history before .login so that it is available in .login
    634 	 */
    635 	if ((cp = value(STRhistfile)) != STRNULL)
    636 	    tildehist[2] = cp;
    637 	dosource(tildehist, NULL);
    638         if (loginsh)
    639 	      (void)srccat(value(STRhome), STRsldotlogin);
    640     }
    641 
    642     /*
    643      * Now are ready for the -v and -x flags
    644      */
    645     if (nverbose)
    646 	setNS(STRverbose);
    647     if (nexececho)
    648 	setNS(STRecho);
    649 
    650     /*
    651      * All the rest of the world is inside this call. The argument to process
    652      * indicates whether it should catch "error unwinds".  Thus if we are a
    653      * interactive shell our call here will never return by being blown past on
    654      * an error.
    655      */
    656     process(setintr);
    657 
    658     /*
    659      * Mop-up.
    660      */
    661     if (intty) {
    662 	if (loginsh) {
    663 	    (void)fprintf(cshout, "logout\n");
    664 	    (void)close(SHIN);
    665 	    child = 1;
    666 	    goodbye();
    667 	}
    668 	else {
    669 	    (void)fprintf(cshout, "exit\n");
    670 	}
    671     }
    672     rechist();
    673     exitstat();
    674     /* NOTREACHED */
    675 }
    676 
    677 void
    678 untty(void)
    679 {
    680     if (tpgrp > 0) {
    681 	(void)setpgid(0, opgrp);
    682 	(void)tcsetpgrp(FSHTTY, opgrp);
    683     }
    684 }
    685 
    686 void
    687 importpath(Char *cp)
    688 {
    689     Char *dp, **pv;
    690     int c, i;
    691 
    692     i = 0;
    693     for (dp = cp; *dp; dp++)
    694 	if (*dp == ':')
    695 	    i++;
    696     /*
    697      * i+2 where i is the number of colons in the path. There are i+1
    698      * directories in the path plus we need room for a zero terminator.
    699      */
    700     pv = xcalloc((size_t) (i + 2), sizeof(*pv));
    701     dp = cp;
    702     i = 0;
    703     if (*dp)
    704 	for (;;) {
    705 	    if ((c = *dp) == ':' || c == 0) {
    706 		*dp = 0;
    707 		pv[i++] = Strsave(*cp ? cp : STRdot);
    708 		if (c) {
    709 		    cp = dp + 1;
    710 		    *dp = ':';
    711 		}
    712 		else
    713 		    break;
    714 	    }
    715 	    dp++;
    716 	}
    717     pv[i] = 0;
    718     setq(STRpath, pv, &shvhed);
    719 }
    720 
    721 /*
    722  * Source to the file which is the catenation of the argument names.
    723  */
    724 static int
    725 srccat(Char *cp, Char *dp)
    726 {
    727     Char *ep;
    728     char *ptr;
    729 
    730     ep = Strspl(cp, dp);
    731     ptr = short2str(ep);
    732     free(ep);
    733     return srcfile(ptr, mflag ? 0 : 1, 0);
    734 }
    735 
    736 /*
    737  * Source to a file putting the file descriptor in a safe place (> 2).
    738  */
    739 static int
    740 srcfile(const char *f, int onlyown, int flag)
    741 {
    742     int unit;
    743 
    744     if ((unit = open(f, O_RDONLY)) == -1)
    745 	return 0;
    746     unit = dmove(unit, -1);
    747 
    748     (void) ioctl(unit, FIOCLEX, NULL);
    749     srcunit(unit, onlyown, flag);
    750     return 1;
    751 }
    752 
    753 /*
    754  * Source to a unit.  If onlyown it must be our file or our group or
    755  * we don't chance it.	This occurs on ".cshrc"s and the like.
    756  */
    757 int insource;
    758 
    759 static void
    760 srcunit(int unit, int onlyown, int hflg)
    761 {
    762     /* We have to push down a lot of state here */
    763     /* All this could go into a structure */
    764     struct whyle *oldwhyl;
    765     struct Bin saveB;
    766     sigset_t nsigset, osigset;
    767     jmp_buf oldexit;
    768     Char *oarginp, *oevalp, **oevalvec, *ogointr;
    769     Char OHIST;
    770     int oSHIN, oinsource, oldintty, oonelflg;
    771     int oenterhist, otell;
    772     /* The (few) real local variables */
    773     int my_reenter;
    774 
    775     oSHIN = -1;
    776     oldintty = intty;
    777     oinsource = insource;
    778     oldwhyl = whyles;
    779     ogointr = gointr;
    780     oarginp = arginp;
    781     oevalp = evalp;
    782     oevalvec = evalvec;
    783     oonelflg = onelflg;
    784     oenterhist = enterhist;
    785     OHIST = HIST;
    786     otell = cantell;
    787 
    788     if (unit < 0)
    789 	return;
    790     if (didfds)
    791 	donefds();
    792     if (onlyown) {
    793 	struct stat stb;
    794 
    795 	if (fstat(unit, &stb) < 0) {
    796 	    (void)close(unit);
    797 	    return;
    798 	}
    799     }
    800 
    801     /*
    802      * There is a critical section here while we are pushing down the input
    803      * stream since we have stuff in different structures. If we weren't
    804      * careful an interrupt could corrupt SHIN's Bin structure and kill the
    805      * shell.
    806      *
    807      * We could avoid the critical region by grouping all the stuff in a single
    808      * structure and pointing at it to move it all at once.  This is less
    809      * efficient globally on many variable references however.
    810      */
    811     insource = 1;
    812     getexit(oldexit);
    813 
    814     if (setintr) {
    815 	sigemptyset(&nsigset);
    816 	(void)sigaddset(&nsigset, SIGINT);
    817 	(void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
    818     }
    819     /* Setup the new values of the state stuff saved above */
    820     (void)memcpy(&saveB, &B, sizeof(B));
    821     fbuf = NULL;
    822     fseekp = feobp = fblocks = 0;
    823     oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
    824     intty = isatty(SHIN), whyles = 0, gointr = 0;
    825     evalvec = 0;
    826     evalp = 0;
    827     enterhist = hflg;
    828     if (enterhist)
    829 	HIST = '\0';
    830 
    831     /*
    832      * Now if we are allowing commands to be interrupted, we let ourselves be
    833      * interrupted.
    834      */
    835     if (setintr)
    836 	(void)sigprocmask(SIG_SETMASK, &osigset, NULL);
    837     settell();
    838 
    839     if ((my_reenter = setexit()) == 0)
    840 	process(0);				/* 0 -> blow away on errors */
    841 
    842     if (setintr)
    843 	(void)sigprocmask(SIG_SETMASK, &osigset, NULL);
    844     if (oSHIN >= 0) {
    845 	int i;
    846 
    847 	/* We made it to the new state... free up its storage */
    848 	for (i = 0; i < fblocks; i++)
    849 	    free(fbuf[i]);
    850 	free(fbuf);
    851 
    852 	/* Reset input arena */
    853 	/* (note that this clears fbuf and fblocks) */
    854 	(void)memcpy(&B, &saveB, sizeof(B));
    855 
    856 	(void)close(SHIN), SHIN = oSHIN;
    857 	arginp = oarginp, onelflg = oonelflg;
    858 	evalp = oevalp, evalvec = oevalvec;
    859 	intty = oldintty, whyles = oldwhyl, gointr = ogointr;
    860 	if (enterhist)
    861 	    HIST = OHIST;
    862 	enterhist = oenterhist;
    863 	cantell = otell;
    864     }
    865 
    866     resexit(oldexit);
    867     /*
    868      * If process reset() (effectively an unwind) then we must also unwind.
    869      */
    870     if (my_reenter)
    871 	stderror(ERR_SILENT);
    872     insource = oinsource;
    873 }
    874 
    875 void
    876 rechist(void)
    877 {
    878     Char buf[BUFSIZE], hbuf[BUFSIZE], *hfile;
    879     int fp, ftmp, oldidfds;
    880     struct varent *shist;
    881 
    882     if (!fast) {
    883 	/*
    884 	 * If $savehist is just set, we use the value of $history
    885 	 * else we use the value in $savehist
    886 	 */
    887 	if ((shist = adrof(STRsavehist)) != NULL) {
    888 	    if (shist->vec[0][0] != '\0')
    889 		(void)Strcpy(hbuf, shist->vec[0]);
    890 	    else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0')
    891 		(void)Strcpy(hbuf, shist->vec[0]);
    892 	    else
    893 		return;
    894 	}
    895 	else
    896   	    return;
    897 
    898   	if ((hfile = value(STRhistfile)) == STRNULL) {
    899   	    hfile = Strcpy(buf, value(STRhome));
    900   	    (void) Strcat(buf, STRsldthist);
    901   	}
    902 
    903   	if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC,
    904 	    0600)) == -1)
    905   	    return;
    906 
    907 	oldidfds = didfds;
    908 	didfds = 0;
    909 	ftmp = SHOUT;
    910 	SHOUT = fp;
    911 	dumphist[2] = hbuf;
    912 	dohist(dumphist, NULL);
    913 	SHOUT = ftmp;
    914 	(void)close(fp);
    915 	didfds = oldidfds;
    916     }
    917 }
    918 
    919 void
    920 goodbye(void)
    921 {
    922     rechist();
    923 
    924     if (loginsh) {
    925 	(void)signal(SIGQUIT, SIG_IGN);
    926 	(void)signal(SIGINT, SIG_IGN);
    927 	(void)signal(SIGTERM, SIG_IGN);
    928 	setintr = 0;		/* No interrupts after "logout" */
    929 	if (!(adrof(STRlogout)))
    930 	    set(STRlogout, STRnormal);
    931 #ifdef _PATH_DOTLOGOUT
    932 	(void)srcfile(_PATH_DOTLOGOUT, 0, 0);
    933 #endif
    934 	if (adrof(STRhome))
    935 	    (void)srccat(value(STRhome), STRsldtlogout);
    936     }
    937     exitstat();
    938     /* NOTREACHED */
    939 }
    940 
    941 __dead void
    942 exitstat(void)
    943 {
    944     Char *s;
    945 #ifdef PROF
    946     monitor(0);
    947 #endif
    948     /*
    949      * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
    950      * directly because we poke child here. Otherwise we might continue
    951      * unwarrantedly (sic).
    952      */
    953     child = 1;
    954     s = value(STRstatus);
    955     xexit(s ? getn(s) : 0);
    956     /* NOTREACHED */
    957 }
    958 
    959 /*
    960  * in the event of a HUP we want to save the history
    961  */
    962 static void
    963 phup(int sig)
    964 {
    965     rechist();
    966 
    967     /*
    968      * We kill the last foreground process group. It then becomes
    969      * responsible to propagate the SIGHUP to its progeny.
    970      */
    971     {
    972 	struct process *pp, *np;
    973 
    974 	for (pp = proclist.p_next; pp; pp = pp->p_next) {
    975 	    np = pp;
    976 	    /*
    977 	     * Find if this job is in the foreground. It could be that
    978 	     * the process leader has exited and the foreground flag
    979 	     * is cleared for it.
    980 	     */
    981 	    do
    982 		/*
    983 		 * If a process is in the foreground; we try to kill
    984 		 * its process group. If we succeed, then the
    985 		 * whole job is gone. Otherwise we keep going...
    986 		 * But avoid sending HUP to the shell again.
    987 		 */
    988 		if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp &&
    989 		    kill(-np->p_jobid, SIGHUP) != -1) {
    990 		    /* In case the job was suspended... */
    991 		    (void)kill(-np->p_jobid, SIGCONT);
    992 		    break;
    993 		}
    994 	    while ((np = np->p_friends) != pp);
    995 	}
    996     }
    997     xexit(sig);
    998     /* NOTREACHED */
    999 }
   1000 
   1001 Char *jobargv[2] = {STRjobs, 0};
   1002 
   1003 /*
   1004  * Catch an interrupt, e.g. during lexical input.
   1005  * If we are an interactive shell, we reset the interrupt catch
   1006  * immediately.  In any case we drain the shell output,
   1007  * and finally go through the normal error mechanism, which
   1008  * gets a chance to make the shell go away.
   1009  */
   1010 /* ARGSUSED */
   1011 void
   1012 pintr(int notused)
   1013 {
   1014     pintr1(1);
   1015     /* NOTREACHED */
   1016 }
   1017 
   1018 void
   1019 pintr1(int wantnl)
   1020 {
   1021     Char **v;
   1022     sigset_t nsigset, osigset;
   1023 
   1024     sigemptyset(&nsigset);
   1025     (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
   1026     if (setintr) {
   1027 	nsigset = osigset;
   1028 	(void)sigdelset(&nsigset, SIGINT);
   1029 	(void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
   1030 	if (pjobs) {
   1031 	    pjobs = 0;
   1032 	    (void)fprintf(cshout, "\n");
   1033 	    dojobs(jobargv, NULL);
   1034 	    stderror(ERR_NAME | ERR_INTR);
   1035 	}
   1036     }
   1037     (void)sigdelset(&osigset, SIGCHLD);
   1038     (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
   1039     (void)fpurge(cshout);
   1040     (void)endpwent();
   1041 
   1042     /*
   1043      * If we have an active "onintr" then we search for the label. Note that if
   1044      * one does "onintr -" then we shan't be interruptible so we needn't worry
   1045      * about that here.
   1046      */
   1047     if (gointr) {
   1048 	gotolab(gointr);
   1049 	timflg = 0;
   1050 	if ((v = pargv) != NULL)
   1051 	    pargv = 0, blkfree(v);
   1052 	if ((v = gargv) != NULL)
   1053 	    gargv = 0, blkfree(v);
   1054 	reset();
   1055     }
   1056     else if (intty && wantnl) {
   1057 	(void)fputc('\r', cshout);
   1058 	(void)fputc('\n', cshout);
   1059     }
   1060     stderror(ERR_SILENT);
   1061     /* NOTREACHED */
   1062 }
   1063 
   1064 /*
   1065  * Process is the main driving routine for the shell.
   1066  * It runs all command processing, except for those within { ... }
   1067  * in expressions (which is run by a routine evalav in sh.exp.c which
   1068  * is a stripped down process), and `...` evaluation which is run
   1069  * also by a subset of this code in sh.glob.c in the routine backeval.
   1070  *
   1071  * The code here is a little strange because part of it is interruptible
   1072  * and hence freeing of structures appears to occur when none is necessary
   1073  * if this is ignored.
   1074  *
   1075  * Note that if catch is not set then we will unwind on any error.
   1076  * If an end-of-file occurs, we return.
   1077  */
   1078 static struct command *savet = NULL;
   1079 
   1080 void
   1081 process(int catch)
   1082 {
   1083     struct command *t;
   1084     jmp_buf osetexit;
   1085     sigset_t nsigset;
   1086 
   1087     t = savet;
   1088     savet = NULL;
   1089     getexit(osetexit);
   1090     for (;;) {
   1091 	pendjob();
   1092 	paraml.next = paraml.prev = &paraml;
   1093 	paraml.word = STRNULL;
   1094 	(void)setexit();
   1095 	justpr = enterhist;	/* execute if not entering history */
   1096 
   1097 	/*
   1098 	 * Interruptible during interactive reads
   1099 	 */
   1100 	if (setintr) {
   1101 	    sigemptyset(&nsigset);
   1102 	    (void)sigaddset(&nsigset, SIGINT);
   1103 	    (void)sigprocmask(SIG_UNBLOCK, &nsigset, NULL);
   1104 	}
   1105 
   1106 	/*
   1107 	 * For the sake of reset()
   1108 	 */
   1109 	freelex(&paraml);
   1110 	if (savet)
   1111 	    freesyn(savet), savet = NULL;
   1112 
   1113 	if (haderr) {
   1114 	    if (!catch) {
   1115 		/* unwind */
   1116 		doneinp = 0;
   1117 		resexit(osetexit);
   1118 		savet = t;
   1119 		reset();
   1120 	    }
   1121 	    haderr = 0;
   1122 	    /*
   1123 	     * Every error is eventually caught here or the shell dies.  It is
   1124 	     * at this point that we clean up any left-over open files, by
   1125 	     * closing all but a fixed number of pre-defined files.  Thus
   1126 	     * routines don't have to worry about leaving files open due to
   1127 	     * deeper errors... they will get closed here.
   1128 	     */
   1129 	    closem();
   1130 	    continue;
   1131 	}
   1132 	if (doneinp) {
   1133 	    doneinp = 0;
   1134 	    break;
   1135 	}
   1136 	if (chkstop)
   1137 	    chkstop--;
   1138 	if (neednote)
   1139 	    pnote();
   1140 	if (intty && prompt && evalvec == 0) {
   1141 	    mailchk();
   1142 #ifdef EDIT
   1143 	    updateediting();
   1144 #endif
   1145 	    /*
   1146 	     * If we are at the end of the input buffer then we are going to
   1147 	     * read fresh stuff. Otherwise, we are rereading input and don't
   1148 	     * need or want to prompt.
   1149 	     */
   1150 	    if (aret == F_SEEK && fseekp == feobp)
   1151 		printprompt();
   1152 	    (void)fflush(cshout);
   1153 	}
   1154 	if (seterr) {
   1155 	    free(seterr);
   1156 	    seterr = NULL;
   1157 	}
   1158 
   1159 
   1160 	/*
   1161 	 * Echo not only on VERBOSE, but also with history expansion. If there
   1162 	 * is a lexical error then we forego history echo.
   1163 	 */
   1164 	if ((lex(&paraml) && !seterr && intty) || adrof(STRverbose)) {
   1165 	    int odidfds = didfds;
   1166 	    fflush(csherr);
   1167 	    didfds = 0;
   1168 	    prlex(csherr, &paraml);
   1169 	    fflush(csherr);
   1170 	    didfds = odidfds;
   1171 	}
   1172 
   1173 	/*
   1174 	 * The parser may lose space if interrupted.
   1175 	 */
   1176 	if (setintr)
   1177 	    (void)sigprocmask(SIG_BLOCK, &nsigset, NULL);
   1178 
   1179 	/*
   1180 	 * Save input text on the history list if reading in old history, or it
   1181 	 * is from the terminal at the top level and not in a loop.
   1182 	 *
   1183 	 * PWP: entry of items in the history list while in a while loop is done
   1184 	 * elsewhere...
   1185 	 */
   1186 	if (enterhist || (catch && intty && !whyles))
   1187 	    savehist(&paraml);
   1188 
   1189 	/*
   1190 	 * Print lexical error messages, except when sourcing history lists.
   1191 	 */
   1192 	if (!enterhist && seterr)
   1193 	    stderror(ERR_OLD);
   1194 
   1195 	/*
   1196 	 * If had a history command :p modifier then this is as far as we
   1197 	 * should go
   1198 	 */
   1199 	if (justpr)
   1200 	    reset();
   1201 
   1202 	alias(&paraml);
   1203 
   1204 	/*
   1205 	 * Parse the words of the input into a parse tree.
   1206 	 */
   1207 	savet = syntax(paraml.next, &paraml, 0);
   1208 	if (seterr)
   1209 	    stderror(ERR_OLD);
   1210 
   1211 	execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
   1212 
   1213 	/*
   1214 	 * Made it!
   1215 	 */
   1216 	freelex(&paraml);
   1217 	freesyn(savet), savet = NULL;
   1218     }
   1219     resexit(osetexit);
   1220     savet = t;
   1221 }
   1222 
   1223 void
   1224 /*ARGSUSED*/
   1225 dosource(Char **v, struct command *t)
   1226 {
   1227     Char buf[BUFSIZE], *f;
   1228     int hflg;
   1229 
   1230     hflg = 0;
   1231     v++;
   1232     if (*v && eq(*v, STRmh)) {
   1233 	if (*++v == NULL)
   1234 	    stderror(ERR_NAME | ERR_HFLAG);
   1235 	hflg++;
   1236     }
   1237     (void)Strcpy(buf, *v);
   1238     f = globone(buf, G_ERROR);
   1239     (void)strcpy((char *)buf, short2str(f));
   1240     free(f);
   1241     if (!srcfile((char *)buf, 0, hflg) && !hflg)
   1242 	stderror(ERR_SYSTEM, (char *)buf, strerror(errno));
   1243 }
   1244 
   1245 /*
   1246  * Check for mail.
   1247  * If we are a login shell, then we don't want to tell
   1248  * about any mail file unless its been modified
   1249  * after the time we started.
   1250  * This prevents us from telling the user things he already
   1251  * knows, since the login program insists on saying
   1252  * "You have mail."
   1253  */
   1254 static void
   1255 mailchk(void)
   1256 {
   1257     struct stat stb;
   1258     struct varent *v;
   1259     Char **vp;
   1260     time_t t;
   1261     int cnt, intvl;
   1262     int new;
   1263 
   1264     v = adrof(STRmail);
   1265     if (v == 0)
   1266 	return;
   1267     (void)time(&t);
   1268     vp = v->vec;
   1269     cnt = blklen(vp);
   1270     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
   1271     if (intvl < 1)
   1272 	intvl = 1;
   1273     if (chktim + intvl > t)
   1274 	return;
   1275     for (; *vp; vp++) {
   1276 	if (stat(short2str(*vp), &stb) < 0)
   1277 	    continue;
   1278 	new = stb.st_mtime > time0.tv_sec;
   1279 	if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
   1280 	    (stb.st_atime < chktim && stb.st_mtime < chktim) ||
   1281 	    (loginsh && !new))
   1282 	    continue;
   1283 	if (cnt == 1)
   1284 	    (void)fprintf(cshout, "You have %smail.\n", new ? "new " : "");
   1285 	else
   1286 	    (void)fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail",
   1287 			   vis_str(*vp));
   1288     }
   1289     chktim = t;
   1290 }
   1291 
   1292 /*
   1293  * Extract a home directory from the password file
   1294  * The argument points to a buffer where the name of the
   1295  * user whose home directory is sought is currently.
   1296  * We write the home directory of the user back there.
   1297  */
   1298 int
   1299 gethdir(Char *home)
   1300 {
   1301     struct passwd *pw;
   1302     Char *h;
   1303 
   1304     /*
   1305      * Is it us?
   1306      */
   1307     if (*home == '\0') {
   1308 	if ((h = value(STRhome)) != NULL) {
   1309 	    (void)Strcpy(home, h);
   1310 	    return 0;
   1311 	}
   1312 	else
   1313 	    return 1;
   1314     }
   1315 
   1316     if ((pw = getpwnam(short2str(home))) != NULL) {
   1317 	(void)Strcpy(home, str2short(pw->pw_dir));
   1318 	return 0;
   1319     }
   1320     else
   1321 	return 1;
   1322 }
   1323 
   1324 /*
   1325  * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17
   1326  * We also check if the shell has already changed the descriptor to point to
   1327  * 0, 1, 2 when didfds is set.
   1328  */
   1329 #define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0))
   1330 
   1331 static ssize_t
   1332 readf(void *oreo, void *buf, size_t siz)
   1333 {
   1334     return read(DESC(oreo), buf, siz);
   1335 }
   1336 
   1337 
   1338 static ssize_t
   1339 writef(void *oreo, const void *buf, size_t siz)
   1340 {
   1341     return write(DESC(oreo), buf, siz);
   1342 }
   1343 
   1344 static off_t
   1345 seekf(void *oreo, off_t off, int whence)
   1346 {
   1347     return lseek(DESC(oreo), off, whence);
   1348 }
   1349 
   1350 
   1351 static int
   1352 closef(void *oreo)
   1353 {
   1354     return close(DESC(oreo));
   1355 }
   1356 
   1357 
   1358 /*
   1359  * Print the visible version of a string.
   1360  */
   1361 int
   1362 vis_fputc(int ch, FILE *fp)
   1363 {
   1364     char uenc[5];	/* 4 + NULL */
   1365 
   1366     if (ch & QUOTE)
   1367 	return fputc(ch & TRIM, fp);
   1368     /*
   1369      * XXX: When we are in AsciiOnly we want all characters >= 0200 to
   1370      * be encoded, but currently there is no way in vis to do that.
   1371      */
   1372     (void)vis(uenc, ch & TRIM, VIS_NOSLASH, 0);
   1373     return (fputs(uenc, fp));
   1374 }
   1375 
   1376 /*
   1377  * Move the initial descriptors to their eventual
   1378  * resting places, closing all other units.
   1379  */
   1380 void
   1381 initdesc(void)
   1382 {
   1383     didfds = 0;			/* 0, 1, 2 aren't set up */
   1384     (void)ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL);
   1385     (void)ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
   1386     (void)ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL);
   1387     (void)ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
   1388     closem();
   1389 }
   1390 
   1391 
   1392 __dead void
   1393 #ifdef PROF
   1394 done(int i)
   1395 #else
   1396 xexit(int i)
   1397 #endif
   1398 {
   1399     untty();
   1400     _exit(i);
   1401     /* NOTREACHED */
   1402 }
   1403 
   1404 #ifndef _PATH_DEFPATH
   1405 static Char **
   1406 defaultpath(void)
   1407 {
   1408     struct stat stb;
   1409     Char **blk, **blkp;
   1410     char *ptr;
   1411 
   1412     blkp = blk = xmalloc((size_t) sizeof(Char *) * 10);
   1413 
   1414 #define DIRAPPEND(a)  \
   1415 	if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
   1416 		*blkp++ = SAVE(ptr)
   1417 #ifdef RESCUEDIR
   1418     DIRAPPEND(RESCUEDIR);
   1419 #endif
   1420     DIRAPPEND(_PATH_BIN);
   1421     DIRAPPEND(_PATH_USRBIN);
   1422 
   1423 #undef DIRAPPEND
   1424 
   1425 #if 0
   1426     if (euid != 0 && uid != 0)
   1427 	*blkp++ = Strsave(STRdot);
   1428 #endif
   1429 
   1430     *blkp = NULL;
   1431     return (blk);
   1432 }
   1433 #endif /* _PATH_DEFPATH */
   1434 
   1435 void
   1436 printprompt(void)
   1437 {
   1438     Char *cp;
   1439 
   1440     if (editing)
   1441 	return;
   1442 
   1443     if (!whyles) {
   1444 	for (cp = value(STRprompt); *cp; cp++)
   1445 	    if (*cp == HIST)
   1446 		(void)fprintf(cshout, "%d", eventno + 1);
   1447 	    else {
   1448 		if (*cp == '\\' && cp[1] == HIST)
   1449 		    cp++;
   1450 		(void)vis_fputc(*cp | QUOTE, cshout);
   1451 	    }
   1452     }
   1453     else
   1454 	/*
   1455 	 * Prompt for forward reading loop body content.
   1456 	 */
   1457 	(void)fprintf(cshout, "? ");
   1458     (void)fflush(cshout);
   1459 }
   1460 
   1461 #ifdef EDIT
   1462 char *
   1463 printpromptstr(EditLine *elx) {
   1464     static char pbuf[1024];
   1465     static char qspace[] = "? ";
   1466     Char *cp;
   1467     size_t i;
   1468 
   1469     if (whyles)
   1470 	return qspace;
   1471 
   1472     i = 0;
   1473     for (cp = value(STRprompt); *cp; cp++) {
   1474 	if (i >= sizeof(pbuf))
   1475 	    break;
   1476 	if (*cp == HIST) {
   1477 	    int r;
   1478 	    r = snprintf(pbuf + i, sizeof(pbuf) - i, "%d", eventno + 1);
   1479 	    if (r > 0)
   1480 		i += (size_t)r;
   1481 	} else
   1482 	    pbuf[i++] = (char)*cp;
   1483     }
   1484     if (i >= sizeof(pbuf))
   1485 	i = sizeof(pbuf) - 1;
   1486     pbuf[i] = '\0';
   1487     return pbuf;
   1488 }
   1489 #endif
   1490