* Copyright (c) 1980, 1991, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)sem.c 8.1 (Berkeley) %G%";
static void vffree
__P((int));
static Char
*splicepipe
__P((struct command
*t
, Char
*));
static void doio
__P((struct command
*t
, int *, int *));
static void chkclob
__P((char *));
execute(t
, wanttty
, pipein
, pipeout
)
register struct command
*t
;
int wanttty
, *pipein
, *pipeout
;
static sigset_t csigmask
;
static sigset_t ocsigmask
;
static int onosigchld
= 0;
static int nosigchld
= 0;
if (t
->t_dflg
& F_AMPERSAND
)
if ((t
->t_dcom
[0][0] & (QUOTE
| TRIM
)) == QUOTE
)
(void) Strcpy(t
->t_dcom
[0], t
->t_dcom
[0] + 1);
if ((t
->t_dflg
& F_REPEAT
) == 0)
if (t
->t_dflg
& F_PIPEOUT
)
* Must do << early so parent will know where input pointer should be.
* If noexec then this is all we do.
if (t
->t_dflg
& F_READ
) {
set(STRstatus
, Strsave(STR0
));
* This mess is the necessary kludge to handle the prefix builtins:
* nice, nohup, time. These commands can also be used by themselves,
* and this is not handled here. This will also work when loops are
while (t
->t_dtyp
== NODE_COMMAND
)
if (eq(t
->t_dcom
[0], STRnice
))
if (strchr("+-", t
->t_dcom
[1][0]))
else if (eq(t
->t_dcom
[0], STRnohup
))
else if (eq(t
->t_dcom
[0], STRtime
))
if (t
->t_dtyp
== NODE_COMMAND
) {
* Check if we have a builtin function and remember which one.
* Continue for builtins that are part of the scripting language
if (bifunc
->bfunct
!= dobreak
&& bifunc
->bfunct
!= docontin
&&
bifunc
->bfunct
!= doelse
&& bifunc
->bfunct
!= doend
&&
bifunc
->bfunct
!= doforeach
&& bifunc
->bfunct
!= dogoto
&&
bifunc
->bfunct
!= doif
&& bifunc
->bfunct
!= dorepeat
&&
bifunc
->bfunct
!= doswbrk
&& bifunc
->bfunct
!= doswitch
&&
bifunc
->bfunct
!= dowhile
&& bifunc
->bfunct
!= dozip
)
else { /* not a command */
* We fork only if we are timed, or are not the end of a parenthesized
* list and not a simple builtin function. Simple meaning one that is
* not pipedout, niced, nohupped, or &'d. It would be nice(?) to not
* fork in some of these cases.
* Prevent forking cd, pushd, popd, chdir cause this will cause the
* shell not to change dir!
if (bifunc
&& (bifunc
->bfunct
== dochngd
||
bifunc
->bfunct
== dopushd
||
bifunc
->bfunct
== dopopd
))
if (((t
->t_dflg
& F_TIME
) || ((t
->t_dflg
& F_NOFORK
) == 0 &&
(F_PIPEOUT
| F_AMPERSAND
| F_NICE
| F_NOHUP
)))) ||
* We have to fork for eval too.
(bifunc
&& (t
->t_dflg
& (F_PIPEIN
| F_PIPEOUT
)) != 0 &&
bifunc
->bfunct
== doeval
))
if (t
->t_dtyp
== NODE_PAREN
||
t
->t_dflg
& (F_REPEAT
| F_AMPERSAND
) || bifunc
) {
* We need to block SIGCHLD here, so that if the process does
* not die before we can set the process group
if (wanttty
>= 0 && !nosigchld
) {
csigmask
= sigblock(sigmask(SIGCHLD
));
if (pid
== 0 && nosigchld
) {
(void) sigsetmask(csigmask
);
else if (pid
!= 0 && (t
->t_dflg
& F_AMPERSAND
))
int ochild
, osetintr
, ohaderr
, odidfds
;
int oSHIN
, oSHOUT
, oSHERR
, oOLDSTD
, otpgrp
;
* Prepare for the vfork by saving everything that the child
* corrupts before it exec's. Note that in some signal
* implementations which keep the signal info in user space
* (e.g. Sun's) it will also be necessary to save and restore
* the current sigvec's for the signals the child touches
if (wanttty
>= 0 && !nosigchld
&& !noexec
) {
csigmask
= sigblock(sigmask(SIGCHLD
));
omask
= sigblock(sigmask(SIGCHLD
) | sigmask(SIGINT
));
(void) sigsetmask(omask
);
/* this is from pfork() */
(void) sigsetmask(omask
);
/* this is from pfork() */
(void) sigsetmask(csigmask
);
(t
->t_dflg
& F_NOINTERRUPT
))
|| (gointr
&& eq(gointr
, STRminus
));
pgrp
= pcurrjob
? pcurrjob
->p_jobid
: getpid();
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
(void) signal(SIGINT
, vffree
);
(void) signal(SIGQUIT
, SIG_DFL
);
(void) signal(SIGTSTP
, SIG_DFL
);
(void) signal(SIGTTIN
, SIG_DFL
);
(void) signal(SIGTTOU
, SIG_DFL
);
(void) signal(SIGTERM
, parterm
);
(t
->t_dflg
& F_NOINTERRUPT
)) {
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
(void) signal(SIGHUP
, SIG_IGN
);
(void) setpriority(PRIO_PROCESS
, 0, t
->t_nice
);
* It would be better if we could wait for the whole job when we
* knew the last process had been started. Pwait, in fact, does
* wait for the whole job anyway, but this test doesn't really
* express our intentions.
if (didfds
== 0 && t
->t_dflg
& F_PIPEIN
) {
if ((t
->t_dflg
& F_PIPEOUT
) == 0) {
(void) sigsetmask(csigmask
);
if ((t
->t_dflg
& F_AMPERSAND
) == 0)
doio(t
, pipein
, pipeout
);
if (t
->t_dflg
& F_PIPEOUT
) {
(void) close(pipeout
[0]);
(void) close(pipeout
[1]);
* Perform a builtin function. If we are not forked, arrange for
if (t
->t_dtyp
!= NODE_PAREN
) {
* For () commands must put new 0,1,2 in FSH* and recurse
OLDSTD
= dcopy(0, FOLDSTD
);
SHOUT
= dcopy(1, FSHOUT
);
SHERR
= dcopy(2, FSHERR
);
t
->t_dspr
->t_dflg
|= t
->t_dflg
& F_NOINTERRUPT
;
execute(t
->t_dspr
, wanttty
, NULL
, NULL
);
t
->t_dcar
->t_dflg
|= F_PIPEOUT
|
(t
->t_dflg
& (F_PIPEIN
| F_AMPERSAND
| F_STDERR
| F_NOINTERRUPT
));
execute(t
->t_dcar
, wanttty
, pipein
, pv
);
t
->t_dcdr
->t_dflg
|= F_PIPEIN
| (t
->t_dflg
&
(F_PIPEOUT
| F_AMPERSAND
| F_NOFORK
| F_NOINTERRUPT
));
wanttty
= 0; /* got tty already */
execute(t
->t_dcdr
, wanttty
, pv
, pipeout
);
t
->t_dcar
->t_dflg
|= t
->t_dflg
& F_NOINTERRUPT
;
execute(t
->t_dcar
, wanttty
, NULL
, NULL
);
* In strange case of A&B make a new job after A
if (t
->t_dcar
->t_dflg
& F_AMPERSAND
&& t
->t_dcdr
&&
(t
->t_dcdr
->t_dflg
& F_AMPERSAND
) == 0)
t
->t_dcdr
->t_dflg
|= t
->t_dflg
&
(F_NOFORK
| F_NOINTERRUPT
);
execute(t
->t_dcdr
, wanttty
, NULL
, NULL
);
t
->t_dcar
->t_dflg
|= t
->t_dflg
& F_NOINTERRUPT
;
execute(t
->t_dcar
, wanttty
, NULL
, NULL
);
if ((getn(value(STRstatus
)) == 0) !=
t
->t_dcdr
->t_dflg
|= t
->t_dflg
&
(F_NOFORK
| F_NOINTERRUPT
);
execute(t
->t_dcdr
, wanttty
, NULL
, NULL
);
* Fall through for all breaks from switch
* If there will be no more executions of this command, flush all file
* descriptors. Places that turn on the F_REPEAT bit are responsible for
* doing donefds after the last re-execution
if (didfds
&& !(t
->t_dflg
& F_REPEAT
))
if ((v
= gargv
) != NULL
) {
if ((v
= pargv
) != NULL
) {
* Expand and glob the words after an i/o redirection.
* If more than one word is generated, then update the command vector.
* This is done differently in all the shells:
* 1. in the bourne shell and ksh globbing is not performed
* 2. Bash/csh say ambiguous
* 3. zsh does i/o to/from all the files
* 4. itcsh concatenates the words.
* I don't know what is best to do. I think that Ambiguous is better
* than restructuring the command vector, because the user can get
* unexpected results. In any case, the command vector restructuring
* code is present and the user can choose it by setting noambiguous
register struct command
*t
;
Char
*cp
; /* word after < or > */
if (adrof(STRnoambiguous
)) {
blk
[0] = Dfix1(cp
); /* expand $ */
setname(vis_str(blk
[0]));
stderror(ERR_NAME
| ERR_NOMATCH
);
if (pv
[1] != NULL
) { /* we need to fix the command vector */
Char
**av
= blkspl(t
->t_dcom
, &pv
[1]);
xfree((ptr_t
) t
->t_dcom
);
blk
[0] = globone(blk
[1] = Dfix1(cp
), G_ERROR
);
* Perform io redirection.
* We may or maynot be forked here.
register struct command
*t
;
register int flags
= t
->t_dflg
;
if (didfds
|| (flags
& F_REPEAT
))
if ((flags
& F_READ
) == 0) {/* F_READ already done */
* so < /dev/std{in,out,err} work
cp
= splicepipe(t
, t
->t_dlef
);
(void) strncpy(tmp
, short2str(cp
), MAXPATHLEN
);
if ((fd
= open(tmp
, O_RDONLY
)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
else if (flags
& F_PIPEIN
) {
else if ((flags
& F_NOINTERRUPT
) && tpgrp
== -1) {
(void) open(_PATH_DEVNULL
, O_RDONLY
);
(void) ioctl(0, FIONCLEX
, NULL
);
cp
= splicepipe(t
, t
->t_drit
);
(void) strncpy(tmp
, short2str(cp
), MAXPATHLEN
);
* so > /dev/std{out,err} work
if ((flags
& F_APPEND
) &&
(fd
= open(tmp
, O_WRONLY
| O_APPEND
)) >= 0);
(fd
= open(tmp
, O_WRONLY
)) >= 0)
(void) lseek(1, (off_t
) 0, L_XTND
);
if (!(flags
& F_OVERWRITE
) && adrof(STRnoclobber
)) {
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
if ((fd
= creat(tmp
, 0666)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
else if (flags
& F_PIPEOUT
) {
(void) ioctl(1, FIONCLEX
, NULL
);
(void) ioctl(2, FIONCLEX
, NULL
);
pv
[0] = dmove(pv
[0], -1);
pv
[1] = dmove(pv
[1], -1);
if (pv
[0] >= 0 && pv
[1] >= 0)
if (S_ISCHR(stb
.st_mode
))
stderror(ERR_EXISTS
, cp
);