xref: /original-bsd/bin/csh/sem.c (revision d6fabe6e)
1*d6fabe6eSbostic /*-
2*d6fabe6eSbostic  * Copyright (c) 1980, 1991 The Regents of the University of California.
3*d6fabe6eSbostic  * All rights reserved.
4*d6fabe6eSbostic  *
5*d6fabe6eSbostic  * %sccs.include.redist.c%
6c1549ed3Sdist  */
7c1549ed3Sdist 
843fabe3fSedward #ifndef lint
9*d6fabe6eSbostic static char sccsid[] = "@(#)sem.c	5.11 (Berkeley) 04/04/91";
10*d6fabe6eSbostic #endif /* not lint */
1113082428Sbill 
1213082428Sbill #include "sh.h"
1313082428Sbill #include "sh.proc.h"
147b1c9148Smckusick #include <sys/file.h>
1513082428Sbill #include <sys/ioctl.h>
165e5c7fccSmarc #include "pathnames.h"
1713082428Sbill 
1813082428Sbill /*
1913082428Sbill  * C shell
2013082428Sbill  */
2113082428Sbill 
225e5c7fccSmarc static int nosigchld = 0, osigmask;
235e5c7fccSmarc static int onosigchld = 0, oosigmask;
2413082428Sbill /*VARARGS 1*/
2513082428Sbill execute(t, wanttty, pipein, pipeout)
2613082428Sbill 	register struct command *t;
2713082428Sbill 	int wanttty, *pipein, *pipeout;
2813082428Sbill {
2913082428Sbill 	bool forked = 0;
3013082428Sbill 	struct biltins *bifunc;
3113082428Sbill 	int pid = 0;
3213082428Sbill 	int pv[2];
3313082428Sbill 
3413082428Sbill 	if (t == 0)
3513082428Sbill 		return;
365eb3278cSbostic 	if ((t->t_dflg & F_AMPERSAND) && wanttty > 0)
3713082428Sbill 		wanttty = 0;
3813082428Sbill 	switch (t->t_dtyp) {
3913082428Sbill 
405eb3278cSbostic 	case NODE_COMMAND:
4113082428Sbill 		if ((t->t_dcom[0][0] & (QUOTE|TRIM)) == QUOTE)
4243fabe3fSedward 			(void) strcpy(t->t_dcom[0], t->t_dcom[0] + 1);
435eb3278cSbostic 		if ((t->t_dflg & F_REPEAT) == 0)
4413082428Sbill 			Dfix(t);		/* $ " ' \ */
4513082428Sbill 		if (t->t_dcom[0] == 0)
4613082428Sbill 			return;
4713082428Sbill 		/* fall into... */
4813082428Sbill 
495eb3278cSbostic 	case NODE_PAREN:
505eb3278cSbostic 		if (t->t_dflg & F_PIPEOUT)
5113082428Sbill 			mypipe(pipeout);
5213082428Sbill 		/*
5313082428Sbill 		 * Must do << early so parent will know
5413082428Sbill 		 * where input pointer should be.
5513082428Sbill 		 * If noexec then this is all we do.
5613082428Sbill 		 */
575eb3278cSbostic 		if (t->t_dflg & F_READ) {
5843fabe3fSedward 			(void) close(0);
5913082428Sbill 			heredoc(t->t_dlef);
6013082428Sbill 			if (noexec)
6143fabe3fSedward 				(void) close(0);
6213082428Sbill 		}
6313082428Sbill 		if (noexec)
6413082428Sbill 			break;
6513082428Sbill 
6613082428Sbill 		set("status", "0");
6713082428Sbill 
6813082428Sbill 		/*
6913082428Sbill 		 * This mess is the necessary kludge to handle the prefix
7013082428Sbill 		 * builtins: nice, nohup, time.  These commands can also
7113082428Sbill 		 * be used by themselves, and this is not handled here.
7213082428Sbill 		 * This will also work when loops are parsed.
7313082428Sbill 		 */
745eb3278cSbostic 		while (t->t_dtyp == NODE_COMMAND)
7513082428Sbill 			if (eq(t->t_dcom[0], "nice"))
7613082428Sbill 				if (t->t_dcom[1])
776f545c8fSbostic 					if (index("+-", t->t_dcom[1][0]))
7813082428Sbill 						if (t->t_dcom[2]) {
7913082428Sbill 							setname("nice");
8013082428Sbill 							t->t_nice = getn(t->t_dcom[1]);
8113082428Sbill 							lshift(t->t_dcom, 2);
825eb3278cSbostic 							t->t_dflg |= F_NICE;
8313082428Sbill 						} else
8413082428Sbill 							break;
8513082428Sbill 					else {
8613082428Sbill 						t->t_nice = 4;
8713082428Sbill 						lshift(t->t_dcom, 1);
885eb3278cSbostic 						t->t_dflg |= F_NICE;
8913082428Sbill 					}
9013082428Sbill 				else
9113082428Sbill 					break;
9213082428Sbill 			else if (eq(t->t_dcom[0], "nohup"))
9313082428Sbill 				if (t->t_dcom[1]) {
945eb3278cSbostic 					t->t_dflg |= F_NOHUP;
9513082428Sbill 					lshift(t->t_dcom, 1);
9613082428Sbill 				} else
9713082428Sbill 					break;
9813082428Sbill 			else if (eq(t->t_dcom[0], "time"))
9913082428Sbill 				if (t->t_dcom[1]) {
1005eb3278cSbostic 					t->t_dflg |= F_TIME;
10113082428Sbill 					lshift(t->t_dcom, 1);
10213082428Sbill 				} else
10313082428Sbill 					break;
10413082428Sbill 			else
10513082428Sbill 				break;
10613082428Sbill 		/*
10713082428Sbill 		 * Check if we have a builtin function and remember which one.
10813082428Sbill 		 */
1095eb3278cSbostic 		bifunc = t->t_dtyp ==
1105eb3278cSbostic 		    NODE_COMMAND ? isbfunc(t) : (struct biltins *) 0;
11113082428Sbill 
11213082428Sbill 		/*
11313082428Sbill 		 * We fork only if we are timed, or are not the end of
11413082428Sbill 		 * a parenthesized list and not a simple builtin function.
11513082428Sbill 		 * Simple meaning one that is not pipedout, niced, nohupped,
11613082428Sbill 		 * or &'d.
11713082428Sbill 		 * It would be nice(?) to not fork in some of these cases.
11813082428Sbill 		 */
1195eb3278cSbostic 		if (((t->t_dflg & F_TIME) || (t->t_dflg & F_NOFORK) == 0 &&
1205eb3278cSbostic 		    (!bifunc ||
1215eb3278cSbostic 		    t->t_dflg & (F_PIPEOUT|F_AMPERSAND|F_NICE|F_NOHUP))))
12213082428Sbill #ifdef VFORK
1235eb3278cSbostic 		    if (t->t_dtyp == NODE_PAREN ||
1245eb3278cSbostic 			t->t_dflg&(F_REPEAT|F_AMPERSAND) || bifunc)
12513082428Sbill #endif
1265e5c7fccSmarc 			{ forked++;
1275e5c7fccSmarc 			  if (wanttty >= 0 && !nosigchld) {
1285e5c7fccSmarc 				osigmask = sigblock(sigmask(SIGCHLD));
1295e5c7fccSmarc 				nosigchld = 1;
1305e5c7fccSmarc 			  }
1315e5c7fccSmarc 
1325e5c7fccSmarc 			  pid = pfork(t, wanttty);
1335e5c7fccSmarc 			  if (pid == 0 && nosigchld) {
1345e5c7fccSmarc 				sigsetmask(osigmask);
1355e5c7fccSmarc 				nosigchld = 0;
1365e5c7fccSmarc 			  }
1375e5c7fccSmarc 			}
13813082428Sbill #ifdef VFORK
13913082428Sbill 		    else {
1405e5c7fccSmarc 			sig_t vffree;
14143fabe3fSedward 			int ochild, osetintr, ohaderr, odidfds;
14213082428Sbill 			int oSHIN, oSHOUT, oSHDIAG, oOLDSTD, otpgrp;
1433bb023eaSbostic 			long omask;
14413082428Sbill 
14515d9e91aSlepreau 			/*
14615d9e91aSlepreau 			 * Prepare for the vfork by saving everything
14715d9e91aSlepreau 			 * that the child corrupts before it exec's.
14815d9e91aSlepreau 			 * Note that in some signal implementations
14915d9e91aSlepreau 			 * which keep the signal info in user space
15015d9e91aSlepreau 			 * (e.g. Sun's) it will also be necessary to
15115d9e91aSlepreau  			 * save and restore the current sigvec's for
15215d9e91aSlepreau 			 * the signals the child touches before it
15315d9e91aSlepreau 			 * exec's.
15415d9e91aSlepreau 			 */
1555e5c7fccSmarc 			if (wanttty >= 0 && !nosigchld && !noexec) {
1565e5c7fccSmarc 				osigmask = sigblock(sigmask(SIGCHLD));
1575e5c7fccSmarc 				nosigchld = 1;
1585e5c7fccSmarc 			}
1591e9806efSralph 			omask = sigblock(sigmask(SIGCHLD));
16013082428Sbill 			ochild = child; osetintr = setintr;
16143fabe3fSedward 			ohaderr = haderr; odidfds = didfds;
16213082428Sbill 			oSHIN = SHIN; oSHOUT = SHOUT;
16313082428Sbill 			oSHDIAG = SHDIAG; oOLDSTD = OLDSTD; otpgrp = tpgrp;
1645e5c7fccSmarc 			oosigmask = osigmask; onosigchld = nosigchld;
16513082428Sbill 			Vsav = Vdp = 0; Vav = 0;
16613082428Sbill 			pid = vfork();
16713082428Sbill 			if (pid < 0) {
16843fabe3fSedward 				(void) sigsetmask(omask);
16913082428Sbill 				error("No more processes");
17013082428Sbill 			}
17113082428Sbill 			forked++;
17215d9e91aSlepreau 			if (pid) {	/* parent */
17313082428Sbill 				child = ochild; setintr = osetintr;
17413082428Sbill 				haderr = ohaderr; didfds = odidfds;
17543fabe3fSedward 				SHIN = oSHIN;
17613082428Sbill 				SHOUT = oSHOUT; SHDIAG = oSHDIAG;
17713082428Sbill 				OLDSTD = oOLDSTD; tpgrp = otpgrp;
1785e5c7fccSmarc 				osigmask = oosigmask; nosigchld = onosigchld;
17913082428Sbill 				xfree(Vsav); Vsav = 0;
18013082428Sbill 				xfree(Vdp); Vdp = 0;
18143fabe3fSedward 				xfree((char *)Vav); Vav = 0;
18213082428Sbill 				/* this is from pfork() */
18313082428Sbill 				palloc(pid, t);
18443fabe3fSedward 				(void) sigsetmask(omask);
18515d9e91aSlepreau 			} else {	/* child */
18613082428Sbill 				/* this is from pfork() */
18713082428Sbill 				int pgrp;
18813082428Sbill 				bool ignint = 0;
18913082428Sbill 
1905e5c7fccSmarc 				if (nosigchld) {
1915e5c7fccSmarc 					sigsetmask(osigmask);
1925e5c7fccSmarc 					nosigchld = 0;
1935e5c7fccSmarc 				}
19413082428Sbill 				if (setintr)
1955eb3278cSbostic 					ignint = (tpgrp == -1 &&
1965eb3278cSbostic 					    (t->t_dflg&F_NOINTERRUPT)) ||
1975eb3278cSbostic 					    gointr && eq(gointr, "-");
19813082428Sbill 				pgrp = pcurrjob ? pcurrjob->p_jobid : getpid();
19913082428Sbill 				child++;
20013082428Sbill 				if (setintr) {
20113082428Sbill 					setintr = 0;
2022c8ae23aSsam #ifdef notdef
20343fabe3fSedward 					(void) signal(SIGCHLD, SIG_DFL);
2042c8ae23aSsam #endif
20543fabe3fSedward 					(void) signal(SIGINT, ignint ?
2061e9806efSralph 						SIG_IGN : vffree);
20743fabe3fSedward 					(void) signal(SIGQUIT, ignint ?
2081e9806efSralph 						SIG_IGN : SIG_DFL);
20913082428Sbill 					if (wanttty >= 0) {
21043fabe3fSedward 						(void) signal(SIGTSTP, SIG_DFL);
21143fabe3fSedward 						(void) signal(SIGTTIN, SIG_DFL);
21243fabe3fSedward 						(void) signal(SIGTTOU, SIG_DFL);
21313082428Sbill 					}
21443fabe3fSedward 					(void) signal(SIGTERM, parterm);
2155eb3278cSbostic 				} else if (tpgrp == -1 &&
2165eb3278cSbostic 				    (t->t_dflg&F_NOINTERRUPT)) {
21743fabe3fSedward 					(void) signal(SIGINT, SIG_IGN);
21843fabe3fSedward 					(void) signal(SIGQUIT, SIG_IGN);
21913082428Sbill 				}
2205e5c7fccSmarc 				if (wanttty >= 0 && tpgrp >= 0)
2215e5c7fccSmarc 					(void) setpgrp(0, pgrp);
22213082428Sbill 				if (wanttty > 0)
22343fabe3fSedward 					(void) ioctl(FSHTTY, TIOCSPGRP,
22443fabe3fSedward 						(char *)&pgrp);
22513082428Sbill 				if (tpgrp > 0)
22613082428Sbill 					tpgrp = 0;
2275eb3278cSbostic 				if (t->t_dflg & F_NOHUP)
22843fabe3fSedward 					(void) signal(SIGHUP, SIG_IGN);
2295eb3278cSbostic 				if (t->t_dflg & F_NICE)
23015d9e91aSlepreau 					(void) setpriority(PRIO_PROCESS,
23115d9e91aSlepreau 						0, t->t_nice);
23213082428Sbill 			}
23313082428Sbill 
23413082428Sbill 		    }
23513082428Sbill #endif
23613082428Sbill 		if (pid != 0) {
23713082428Sbill 			/*
23813082428Sbill 			 * It would be better if we could wait for the
23913082428Sbill 			 * whole job when we knew the last process
24013082428Sbill 			 * had been started.  Pwait, in fact, does
24113082428Sbill 			 * wait for the whole job anyway, but this test
24213082428Sbill 			 * doesn't really express our intentions.
24313082428Sbill 			 */
2445eb3278cSbostic 			if (didfds==0 && t->t_dflg&F_PIPEIN) {
24543fabe3fSedward 				(void) close(pipein[0]);
24643fabe3fSedward 				(void) close(pipein[1]);
24743fabe3fSedward 			}
2485eb3278cSbostic 			if ((t->t_dflg & F_PIPEOUT) == 0) {
2495e5c7fccSmarc 				if (nosigchld) {
2505e5c7fccSmarc #ifdef foobarbaz
2515e5c7fccSmarc 					printf("DID\n");
2525e5c7fccSmarc #endif
2535e5c7fccSmarc 					sigsetmask(osigmask);
2545e5c7fccSmarc 					nosigchld = 0;
2555e5c7fccSmarc 				}
2565eb3278cSbostic 				if ((t->t_dflg & F_AMPERSAND) == 0)
25713082428Sbill 					pwait();
2585e5c7fccSmarc 			}
25913082428Sbill 			break;
26013082428Sbill 		}
26113082428Sbill 		doio(t, pipein, pipeout);
2625eb3278cSbostic 		if (t->t_dflg & F_PIPEOUT) {
26343fabe3fSedward 			(void) close(pipeout[0]);
26443fabe3fSedward 			(void) close(pipeout[1]);
26543fabe3fSedward 		}
26613082428Sbill 
26713082428Sbill 		/*
26813082428Sbill 		 * Perform a builtin function.
26913082428Sbill 		 * If we are not forked, arrange for possible stopping
27013082428Sbill 		 */
27113082428Sbill 		if (bifunc) {
27213082428Sbill 			func(t, bifunc);
27313082428Sbill 			if (forked)
27413082428Sbill 				exitstat();
27513082428Sbill 			break;
27613082428Sbill 		}
2775eb3278cSbostic 		if (t->t_dtyp != NODE_PAREN) {
27813082428Sbill 			doexec(t);
27913082428Sbill 			/*NOTREACHED*/
28013082428Sbill 		}
28113082428Sbill 		/*
28213082428Sbill 		 * For () commands must put new 0,1,2 in FSH* and recurse
28313082428Sbill 		 */
28413082428Sbill 		OLDSTD = dcopy(0, FOLDSTD);
28513082428Sbill 		SHOUT = dcopy(1, FSHOUT);
28613082428Sbill 		SHDIAG = dcopy(2, FSHDIAG);
28743fabe3fSedward 		(void) close(SHIN);
28843fabe3fSedward 		SHIN = -1;
28943fabe3fSedward 		didfds = 0;
29013082428Sbill 		wanttty = -1;
2915eb3278cSbostic 		t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT;
29213082428Sbill 		execute(t->t_dspr, wanttty);
29313082428Sbill 		exitstat();
29413082428Sbill 
2955eb3278cSbostic 	case NODE_PIPE:
2965eb3278cSbostic 		t->t_dcar->t_dflg |= F_PIPEOUT |
2975eb3278cSbostic 		    (t->t_dflg & (F_PIPEIN|F_AMPERSAND|F_STDERR|F_NOINTERRUPT));
29813082428Sbill 		execute(t->t_dcar, wanttty, pipein, pv);
2995eb3278cSbostic 		t->t_dcdr->t_dflg |= F_PIPEIN |
3005eb3278cSbostic 		    (t->t_dflg &
3015eb3278cSbostic 		    (F_PIPEOUT|F_AMPERSAND|F_NOFORK|F_NOINTERRUPT));
30213082428Sbill 		if (wanttty > 0)
30313082428Sbill 			wanttty = 0;		/* got tty already */
30413082428Sbill 		execute(t->t_dcdr, wanttty, pv, pipeout);
30513082428Sbill 		break;
30613082428Sbill 
3075eb3278cSbostic 	case NODE_LIST:
30813082428Sbill 		if (t->t_dcar) {
3095eb3278cSbostic 			t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
31013082428Sbill 			execute(t->t_dcar, wanttty);
31113082428Sbill 			/*
31213082428Sbill 			 * In strange case of A&B make a new job after A
31313082428Sbill 			 */
3145eb3278cSbostic 			if (t->t_dcar->t_dflg&F_AMPERSAND && t->t_dcdr &&
3155eb3278cSbostic 			    (t->t_dcdr->t_dflg&F_AMPERSAND) == 0)
31613082428Sbill 				pendjob();
31713082428Sbill 		}
31813082428Sbill 		if (t->t_dcdr) {
3195eb3278cSbostic 			t->t_dcdr->t_dflg |=
3205eb3278cSbostic 			    t->t_dflg & (F_NOFORK|F_NOINTERRUPT);
32113082428Sbill 			execute(t->t_dcdr, wanttty);
32213082428Sbill 		}
32313082428Sbill 		break;
32413082428Sbill 
3255eb3278cSbostic 	case NODE_OR:
3265eb3278cSbostic 	case NODE_AND:
32713082428Sbill 		if (t->t_dcar) {
3285eb3278cSbostic 			t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT;
32913082428Sbill 			execute(t->t_dcar, wanttty);
3305eb3278cSbostic 			if ((getn(value("status")) == 0) !=
3315eb3278cSbostic 			    (t->t_dtyp == NODE_AND))
33213082428Sbill 				return;
33313082428Sbill 		}
33413082428Sbill 		if (t->t_dcdr) {
3355eb3278cSbostic 			t->t_dcdr->t_dflg |=
3365eb3278cSbostic 			    t->t_dflg & (F_NOFORK|F_NOINTERRUPT);
33713082428Sbill 			execute(t->t_dcdr, wanttty);
33813082428Sbill 		}
33913082428Sbill 		break;
34013082428Sbill 	}
34113082428Sbill 	/*
3425eb3278cSbostic 	 * Fall through for all breaks from switch.
34313082428Sbill 	 *
3445eb3278cSbostic 	 * If there will be no more executions of this command, flush all
3455eb3278cSbostic 	 * file descriptors.  Places that turn on the F_REPEAT bit are
3465eb3278cSbostic 	 * responsible for doing donefds after the last re-execution
34713082428Sbill 	 */
3485eb3278cSbostic 	if (didfds && !(t->t_dflg & F_REPEAT))
34913082428Sbill 		donefds();
35013082428Sbill }
35113082428Sbill 
35213082428Sbill #ifdef VFORK
35313082428Sbill vffree()
35413082428Sbill {
35513082428Sbill 	register char **v;
35613082428Sbill 
35713082428Sbill 	if (v = gargv)
35843fabe3fSedward 		gargv = 0, xfree((char *)v);
35913082428Sbill 	if (v = pargv)
36043fabe3fSedward 		pargv = 0, xfree((char *)v);
36113082428Sbill 	_exit(1);
36213082428Sbill }
36313082428Sbill #endif
36413082428Sbill 
36513082428Sbill /*
36613082428Sbill  * Perform io redirection.
36713082428Sbill  * We may or maynot be forked here.
36813082428Sbill  */
36913082428Sbill doio(t, pipein, pipeout)
37013082428Sbill 	register struct command *t;
37113082428Sbill 	int *pipein, *pipeout;
37213082428Sbill {
37313082428Sbill 	register char *cp;
37413082428Sbill 	register int flags = t->t_dflg;
37513082428Sbill 
3765eb3278cSbostic 	if (didfds || (flags & F_REPEAT))
37713082428Sbill 		return;
3785eb3278cSbostic 	if ((flags & F_READ) == 0) {	/* F_READ already done */
37943fabe3fSedward 		(void) close(0);
38013082428Sbill 		if (cp = t->t_dlef) {
38113082428Sbill 			cp = globone(Dfix1(cp));
38213082428Sbill 			xfree(cp);
38313082428Sbill 			if (open(cp, 0) < 0)
38413082428Sbill 				Perror(cp);
3855eb3278cSbostic 		} else if (flags & F_PIPEIN) {
38643fabe3fSedward 			(void) dup(pipein[0]);
38743fabe3fSedward 			(void) close(pipein[0]);
38843fabe3fSedward 			(void) close(pipein[1]);
3895eb3278cSbostic 		} else if ((flags & F_NOINTERRUPT) && tpgrp == -1) {
39043fabe3fSedward 			(void) close(0);
3915e5c7fccSmarc 			(void) open(_PATH_DEVNULL, 0);
39243fabe3fSedward 		} else
393cde959b4Sedward 			(void) dup(OLDSTD);
39413082428Sbill 	}
39543fabe3fSedward 	(void) close(1);
39613082428Sbill 	if (cp = t->t_drit) {
39713082428Sbill 		cp = globone(Dfix1(cp));
39813082428Sbill 		xfree(cp);
3995eb3278cSbostic 		if (!(flags & F_APPEND) || open(cp, O_WRONLY|O_APPEND, 0) < 0) {
4005eb3278cSbostic 			if (!(flags & F_OVERWRITE) && adrof("noclobber")) {
4015eb3278cSbostic 				if (flags & F_APPEND)
40213082428Sbill 					Perror(cp);
40313082428Sbill 				chkclob(cp);
40413082428Sbill 			}
40513082428Sbill 			if (creat(cp, 0666) < 0)
40613082428Sbill 				Perror(cp);
40713082428Sbill 		}
4085eb3278cSbostic 	} else if (flags & F_PIPEOUT)
40943fabe3fSedward 		(void) dup(pipeout[1]);
41013082428Sbill 	else
411cde959b4Sedward 		(void) dup(SHOUT);
41213082428Sbill 
41343fabe3fSedward 	(void) close(2);
4145eb3278cSbostic 	if (flags & F_STDERR)
41543fabe3fSedward 		(void) dup(1);
41643fabe3fSedward 	else
417cde959b4Sedward 		(void) dup(SHDIAG);
41813082428Sbill 	didfds = 1;
41913082428Sbill }
42013082428Sbill 
42113082428Sbill mypipe(pv)
42213082428Sbill 	register int *pv;
42313082428Sbill {
42413082428Sbill 
42513082428Sbill 	if (pipe(pv) < 0)
42613082428Sbill 		goto oops;
42713082428Sbill 	pv[0] = dmove(pv[0], -1);
42813082428Sbill 	pv[1] = dmove(pv[1], -1);
42913082428Sbill 	if (pv[0] >= 0 && pv[1] >= 0)
43013082428Sbill 		return;
43113082428Sbill oops:
43213082428Sbill 	error("Can't make pipe");
43313082428Sbill }
43413082428Sbill 
43513082428Sbill chkclob(cp)
43613082428Sbill 	register char *cp;
43713082428Sbill {
43813082428Sbill 	struct stat stb;
43913082428Sbill 
44013082428Sbill 	if (stat(cp, &stb) < 0)
44113082428Sbill 		return;
44213082428Sbill 	if ((stb.st_mode & S_IFMT) == S_IFCHR)
44313082428Sbill 		return;
44413082428Sbill 	error("%s: File exists", cp);
44513082428Sbill }
446