* Copyright (c) 1980, 1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)func.c 5.20 (Berkeley) 6/27/91";
static void islogin
__P((void));
static void reexecute
__P((struct command
*));
static void preread
__P((void));
static void doagain
__P((void));
static int getword
__P((Char
*));
static int keyword
__P((Char
*));
static void Unsetenv
__P((Char
*));
static void toend
__P((void));
static void xecho
__P((int, Char
**));
register Char
*cp
= t
->t_dcom
[0];
register struct biltins
*bp
, *bp1
, *bp2
;
static struct biltins label
= {"", dozip
, 0, 0};
static struct biltins foregnd
= {"%job", dofg1
, 0, 0};
static struct biltins backgnd
= {"%job &", dobg1
, 0, 0};
if (lastchr(cp
) == ':') {
label
.bname
= short2str(cp
);
if (t
->t_dflg
& F_AMPERSAND
) {
t
->t_dflg
&= ~F_AMPERSAND
;
backgnd
.bname
= short2str(cp
);
foregnd
.bname
= short2str(cp
);
* Binary search Bp1 is the beginning of the current search range. Bp2 is
for (bp1
= bfunc
, bp2
= bfunc
+ nbfunc
; bp1
< bp2
;) {
bp
= bp1
+ ((bp2
- bp1
) >> 1);
if ((i
= *cp
- *bp
->bname
) == 0 &&
(i
= Strcmp(cp
, str2short(bp
->bname
))) == 0)
register struct command
*t
;
register struct biltins
*bp
;
i
= blklen(t
->t_dcom
) - 1;
stderror(ERR_NAME
| ERR_TOOFEW
);
stderror(ERR_NAME
| ERR_TOOMANY
);
(*bp
->bfunct
) (t
->t_dcom
, t
);
register Char
*vv
= v
[1];
stderror(ERR_NAME
| ERR_TERMINAL
);
(void) sigblock(sigmask(SIGINT
));
(void) signal(SIGINT
, SIG_DFL
);
else if (eq((vv
= strip(vv
)), STRminus
)) {
(void) signal(SIGINT
, SIG_IGN
);
gointr
= Strsave(STRminus
);
(void) signal(SIGINT
, pintr
);
stderror(ERR_NAME
| ERR_TERMINAL
);
(void) signal(SIGHUP
, SIG_IGN
);
register struct varent
*vp
;
vp
= adrof1(strip(p
), &aliases
);
blkpr(vp
->vec
), xprintf("\n");
if (eq(p
, STRalias
) || eq(p
, STRunalias
)) {
stderror(ERR_NAME
| ERR_DANGER
);
set1(strip(p
), saveblk(v
), &aliases
);
(void) signal(SIGTERM
, parterm
);
(void) execl(_PATH_LOGIN
, "login", short2str(v
[1]), NULL
);
if (chkstop
== 0 && setintr
)
stderror(ERR_NAME
| ERR_EMPTYIF
);
stderror(ERR_NAME
| ERR_IMPRTHEN
);
setname(short2str(STRthen
));
* If expression was zero, then scan to else, otherwise just fall into
* Simple command attached to this if. Left shift the node in this tree,
* munging it so we can reexecute it.
lshift(kp
->t_dcom
, vv
- kp
->t_dcom
);
* Reexecute a command, being careful not
* to redo i/o redirection, which is already set up.
register struct command
*kp
;
* If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
* pgrp's as the jobs would then have no way to get the tty (we can't give
* it to them, and our parent wouldn't know their pgrp, etc.
execute(kp
, (tpgrp
> 0 ? tpgrp
: -1), NULL
, NULL
);
register struct whyle
*wp
;
* While we still can, locate any unknown ends of existing loops. This
* obscure code is the WORST result of the fact that we don't really parse.
for (wp
= whyles
; wp
; wp
= wp
->w_next
)
search(T_BREAK
, 0, NULL
);
search(T_GOTO
, 0, lp
= globone(v
[1], G_ERROR
));
* Eliminate loops which were exited.
if (!*v
|| *(*v
++) != '(')
cp
= **v
== ')' ? STRNULL
: *v
++;
search(T_SWITCH
, 0, lp
= globone(cp
, G_ERROR
));
stderror(ERR_NAME
| ERR_NOTWHILE
);
if (chkstop
== 0 && (intty
|| intact
) && evalvec
== 0)
* Don't DEMAND parentheses here either.
set(STRstatus
, putn(exp(&v
)));
stderror(ERR_NAME
| ERR_EXPRESSION
);
register struct whyle
*nwp
;
stderror(ERR_NAME
| ERR_VARBEGIN
);
while (*cp
&& alnum(*cp
))
stderror(ERR_NAME
| ERR_VARALNUM
);
if ((cp
- sp
) > MAXVARLEN
)
stderror(ERR_NAME
| ERR_VARTOOLONG
);
if (v
[0][0] != '(' || v
[blklen(v
) - 1][0] != ')')
stderror(ERR_NAME
| ERR_NOPAREN
);
stderror(ERR_NAME
| ERR_NOMATCH
);
nwp
= (struct whyle
*) xcalloc(1, sizeof *nwp
);
nwp
->w_fe
= nwp
->w_fe0
= v
;
nwp
->w_fename
= Strsave(cp
);
* Pre-read the loop so as to be more comprehensible to a terminal user.
register bool again
= whyles
!= 0 && whyles
->w_start
== lineloc
&&
* Implement prereading here also, taking care not to evaluate the
* expression before the loop has been read up from a terminal.
stderror(ERR_NAME
| ERR_EXPRESSION
);
register struct whyle
*nwp
=
(struct whyle
*) xcalloc(1, sizeof(*nwp
));
/* We ain't gonna loop no more, no more! */
(void) sigsetmask(sigblock((sigset_t
) 0) & ~sigmask(SIGINT
));
search(T_BREAK
, 0, NULL
); /* read the expression in */
(void) sigblock(sigmask(SIGINT
));
stderror(ERR_NAME
| ERR_NOTWHILE
);
stderror(ERR_NAME
| ERR_NOTWHILE
);
/* Repeating a while is simple */
if (whyles
->w_fename
== 0) {
* The foreach variable list actually has a spurious word ")" at the end of
* the w_fe list. Thus we are at the of the list if one word beyond this
set(whyles
->w_fename
, Strsave(*whyles
->w_fe
++));
register sigset_t omask
= 0;
omask
= sigblock(sigmask(SIGINT
)) & ~sigmask(SIGINT
);
(void) sigsetmask(omask
);
(void) sigsetmask(omask
);
search(T_BRKSW
, 0, NULL
);
register struct srch
*sp
, *sp1
, *sp2
;
* Binary search Sp1 is the beginning of the current search range. Sp2 is
for (sp1
= srchn
, sp2
= srchn
+ nsrchn
; sp1
< sp2
;) {
sp
= sp1
+ ((sp2
- sp1
) >> 1);
if ((i
= *cp
- *sp
->s_name
) == 0 &&
(i
= Strcmp(cp
, str2short(sp
->s_name
))) == 0)
search(type
, level
, goal
)
register Char
*aword
= wordbuf
;
if (intty
&& fseekp
== feobp
)
if (level
== 0 && type
== T_IF
)
if ((type
== T_IF
|| type
== T_ELSE
) &&
if (type
== T_IF
|| type
== T_ELSE
)
if (type
== T_SWITCH
|| type
== T_BRKSW
)
if (type
== T_SWITCH
|| type
== T_BRKSW
)
if (type
== T_GOTO
&& getword(aword
) && eq(aword
, goal
))
if (type
!= T_GOTO
&& (type
!= T_SWITCH
|| level
!= 0))
if (lastchr(aword
) != ':')
aword
[Strlen(aword
) - 1] = 0;
if (type
== T_GOTO
&& eq(aword
, goal
) ||
type
== T_SWITCH
&& eq(aword
, STRdefault
))
if (type
!= T_SWITCH
|| level
!= 0)
if (lastchr(aword
) == ':')
aword
[Strlen(aword
) - 1] = 0;
cp
= strip(Dfix1(aword
));
if (type
== T_SWITCH
&& level
== 0)
while (c
== ' ' || c
== '\t')
while (c
>= 0 && c
!= '\n');
if (c
== '\\' && (c
= readc(1)) == '\n')
if (c
== '\'' || c
== '"')
*wp
= 0; /* end the string b4 test */
} while ((d
|| !(kwd
= keyword(owp
)) && c
!= ' '
&& c
!= '\t') && c
!= '\n');
* if we have read a keyword ( "if", "switch" or "while" ) then we do not
* need to unreadc the look-ahead char
stderror(ERR_NAME
| ERR_NOTFOUND
, "then/endif");
stderror(ERR_NAME
| ERR_NOTFOUND
, "endif");
stderror(ERR_NAME
| ERR_NOTFOUND
, "endsw");
stderror(ERR_NAME
| ERR_NOTFOUND
, "end");
setname(short2str(Sgoal
));
stderror(ERR_NAME
| ERR_NOTFOUND
, "label");
* keyword(wp) determines if wp is one of the built-n functions if,
* switch or while. It seems that when an if statement looks like
* "if(" then getword above sucks in the '(' and so the search routine
* never finds what it is scanning for. Rather than rewrite doword, I hack
* in a test to see if the string forms a keyword. Then doword stops
* and returns the word "if" -strike
static Char STRif
[] = {'i', 'f', '\0'};
static Char STRwhile
[] = {'w', 'h', 'i', 'l', 'e', '\0'};
static Char STRswitch
[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'};
if ((Strcmp(wp
, STRif
) == 0) || (Strcmp(wp
, STRwhile
) == 0)
|| (Strcmp(wp
, STRswitch
) == 0))
if (whyles
->w_end
== 0) {
search(T_BREAK
, 0, NULL
);
whyles
->w_end
= fseekp
- 1;
register struct whyle
*wp
= whyles
;
register struct whyle
*nwp
= wp
->w_next
;
if (o
>= wp
->w_start
&& (wp
->w_end
== 0 || o
< wp
->w_end
))
xfree((ptr_t
) wp
->w_fename
);
(void) sigsetmask(sigblock((sigset_t
) 0) & ~sigmask(SIGINT
));
stderror(ERR_NAME
| ERR_NOMATCH
);
if (sep
== ' ' && *v
&& eq(*v
, STRmn
))
(void) sigblock(sigmask(SIGINT
));
blkfree(gargv
), gargv
= 0;
/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
(and anything else with a modern compiler) */
(void) sigsetmask(sigblock((sigset_t
) 0) & ~sigmask(SIGINT
));
for (ep
= STR_environ
; *ep
; ep
++)
xprintf("%s\n", short2str(*ep
));
Setenv(vp
, lp
= globone(lp
, G_ERROR
));
else if (eq(vp
, STRLANG
) || eq(vp
, STRLC_CTYPE
)) {
(void) setlocale(LC_ALL
, "");
for (k
= 0200; k
<= 0377 && !Isprint(k
); k
++);
static Char
*name
= NULL
;
* Find the longest environment variable
for (maxi
= 0, ep
= STR_environ
; *ep
; ep
++) {
for (i
= 0, p
= *ep
; *p
&& *p
!= '='; p
++, i
++);
name
= (Char
*) xmalloc((size_t) (maxi
+ 1) * sizeof(Char
));
for (maxi
= 0, ep
= STR_environ
; *ep
; ep
++) {
for (n
= name
, p
= *ep
; *p
&& *p
!= '='; *n
++ = *p
++);
if (eq(name
, STRLANG
) || eq(name
, STRLC_CTYPE
)) {
(void) setlocale(LC_ALL
, "");
for (k
= 0200; k
<= 0377 && !Isprint(k
); k
++);
AsciiOnly
= getenv("LANG") == NULL
&&
getenv("LC_CTYPE") == NULL
;
* Delete name, and start again cause the environment changes
xfree((ptr_t
) name
), name
= NULL
;
register Char
**ep
= STR_environ
;
for (cp
= name
, dp
= *ep
; *cp
&& *cp
== *dp
; cp
++, dp
++)
if (*cp
!= 0 || *dp
!= '=')
cp
= Strspl(STRequal
, val
);
*ep
= strip(Strspl(name
, cp
));
blkfree((Char
**) environ
);
environ
= short2blk(STR_environ
);
cp
= Strspl(name
, STRequal
);
blk
[0] = strip(Strspl(cp
, val
));
STR_environ
= blkspl(STR_environ
, blk
);
blkfree((Char
**) environ
);
environ
= short2blk(STR_environ
);
register Char
**ep
= STR_environ
;
for (cp
= name
, dp
= *ep
; *cp
&& *cp
== *dp
; cp
++, dp
++)
if (*cp
!= 0 || *dp
!= '=')
STR_environ
= blkspl(STR_environ
, ep
+ 1);
environ
= short2blk(STR_environ
);
register Char
*cp
= v
[1];
while (Isdigit(*cp
) && *cp
!= '8' && *cp
!= '9')
if (*cp
|| i
< 0 || i
> 0777)
stderror(ERR_NAME
| ERR_MASK
);
RLIMIT_CPU
, "cputime", 1, "seconds",
RLIMIT_FSIZE
, "filesize", 1024, "kbytes",
RLIMIT_DATA
, "datasize", 1024, "kbytes",
RLIMIT_STACK
, "stacksize", 1024, "kbytes",
RLIMIT_CORE
, "coredumpsize", 1024, "kbytes",
RLIMIT_RSS
, "memoryuse", 1024, "kbytes",
RLIMIT_MEMLOCK
, "memorylocked", 1024, "kbytes",
RLIMIT_NPROC
, "maxproc", 1, "",
RLIMIT_OFILE
, "openfiles", 1, "",
static struct limits
*findlim();
static RLIM_TYPE
getval();
register struct limits
*lp
, *res
;
res
= (struct limits
*) NULL
;
for (lp
= limits
; lp
->limconst
>= 0; lp
++)
if (prefix(cp
, str2short(lp
->limname
))) {
stderror(ERR_NAME
| ERR_AMBIG
);
stderror(ERR_NAME
| ERR_LIMIT
);
register struct limits
*lp
;
register RLIM_TYPE limit
;
if (*v
&& eq(*v
, STRmh
)) {
for (lp
= limits
; lp
->limconst
>= 0; lp
++)
limit
= getval(lp
, v
+ 1);
if (setlim(lp
, hard
, limit
) < 0)
register struct limits
*lp
;
while (Isdigit(*cp
) || *cp
== '.' || *cp
== 'e' || *cp
== 'E')
return ((RLIM_TYPE
) ((f
+ 0.5) * lp
->limdiv
));
if (lp
->limconst
!= RLIMIT_CPU
)
return ((RLIM_TYPE
) (f
* 60.0 + atof(short2str(cp
+ 1))));
if (lp
->limconst
!= RLIMIT_CPU
)
if (lp
->limconst
== RLIMIT_CPU
) {
limtail(cp
, "megabytes");
if (lp
->limconst
!= RLIMIT_CPU
)
if (lp
->limconst
== RLIMIT_CPU
)
limtail(cp
, "megabytes");
if (lp
->limconst
== RLIMIT_CPU
)
limtail(cp
, "unlimited");
stderror(ERR_NAME
| ERR_SCALEF
);
return ((RLIM_TYPE
) (f
+ 0.5));
while (*cp
&& *cp
== *str
)
stderror(ERR_BADSCALE
, str
);
register struct limits
*lp
;
xprintf("%s \t", lp
->limname
);
(void) getrlimit(lp
->limconst
, &rlim
);
limit
= hard
? rlim
.rlim_max
: rlim
.rlim_cur
;
if (limit
== RLIM_INFINITY
)
else if (lp
->limconst
== RLIMIT_CPU
)
xprintf("%ld %s", (long) (limit
/ lp
->limdiv
), lp
->limscale
);
register struct limits
*lp
;
if (*v
&& eq(*v
, STRmh
)) {
for (lp
= limits
; lp
->limconst
>= 0; lp
++)
if (setlim(lp
, hard
, (RLIM_TYPE
) RLIM_INFINITY
) < 0)
if (setlim(lp
, hard
, (RLIM_TYPE
) RLIM_INFINITY
) < 0)
register struct limits
*lp
;
(void) getrlimit(lp
->limconst
, &rlim
);
else if (limit
== RLIM_INFINITY
&& geteuid() != 0)
rlim
.rlim_cur
= rlim
.rlim_max
;
if (setrlimit(lp
->limconst
, &rlim
) < 0) {
xprintf("%s: %s: Can't %s%s limit\n", bname
, lp
->limname
,
limit
== RLIM_INFINITY
? "remove" : "set",
old
= signal(SIGTSTP
, SIG_DFL
);
/* the shell stops here */
(void) signal(SIGTSTP
, old
);
ctpgrp
= tcgetpgrp(FSHTTY
);
old
= signal(SIGTTIN
, SIG_DFL
);
(void) signal(SIGTTIN
, old
);
(void) setpgid(0, shpgrp
);
(void) tcsetpgrp(FSHTTY
, shpgrp
);
/* This is the dreaded EVAL built-in.
* If you don't fiddle with file descriptors, and reset didfds,
* this command will either ignore redirection inside or outside
* its aguments, e.g. eval "date >x" vs. eval "date" >x
* The stuff here seems to work, but I did it by trial and error rather
* than really knowing what was going on. If tpgrp is zero, we are
* probably a background eval, e.g. "eval date &", and we want to
* make sure that any processes we start stay in our pgrp.
* This is also the case for "time eval date" -- stay in same pgrp.
* Otherwise, under stty tostop, processes will stop in the wrong
* pgrp, with no way for the shell to get them going again. -IAN!
saveIN
= dcopy(SHIN
, -1);
saveOUT
= dcopy(SHOUT
, -1);
saveDIAG
= dcopy(SHDIAG
, -1);
if ((my_reenter
= setexit()) == 0) {
SHIN
= dmove(saveIN
, oSHIN
);
SHOUT
= dmove(saveOUT
, oSHOUT
);
SHDIAG
= dmove(saveDIAG
, oSHDIAG
);