/* Copyright (c) 1979 Regents of the University of California */
char *pathlist
[] = { SRCHPATH
, 0 };
bool nverbose
= 0, nexececho
= 0, quitit
= 0, fast
= 0, prompt
= 1;
settimes(); /* Immed. estab. timing base */
if (eq(v
[0], "a.out")) /* A.out's are quittable */
loginsh
= eq(*v
, "-"); /* To do .login/.logout */
* Move the descriptors to safe places.
* The variable didfds is 0 while we have only FSH* to work with.
* When didfds is true, we have 0,1,2 and prefer to use these.
* Initialize the shell variables.
* ARGV and PROMPT are initialized later.
* STATUS is also munged in several places.
* CHILD is munged when forking/waiting
fast
++; /* No home -> can't read scripts */
set1("path", saveblk(pathlist
), &shvhed
);
* Re-initialize path if set in environment
pv = calloc(i+1, sizeof (char **));
set1("path", pv, &shvhed);
doldol
= putn(getpid()); /* For $$ */
shtemp
= strspl("/tmp/sh", doldol
); /* For << */
* Record the interrupt states from the parent process.
* If the parent is non-interruptible our hand must be forced
* or we (and our children) won't be either.
* Our children inherit termination from our parent.
* We catch it only if we are the login shell.
parintr
= signal(SIGINT
, SIG_IGN
); /* parents interruptibility */
signal(SIGINT
, parintr
); /* ... restore */
parterm
= signal(SIGTERM
, SIG_IGN
); /* parents terminability */
signal(SIGTERM
, parterm
); /* ... restore */
* Note that processing of -v/-x is actually delayed till after
* We set the first character of our name to be '-' if we are
* a shell running interruptible commands. Many programs which
* examine ps'es use this to filter such shells out.
while (c
> 0 && (cp
= v
[0])[0] == '-') {
case 0: /* - Interruptible, no prompt */
case 'c': /* -c Command input from arg */
case 'e': /* -e Exit on any error */
case 'f': /* -f Fast start */
case 'i': /* -i Interactive, even if !intty */
case 'n': /* -n Don't execute */
case 'q': /* -q (Undoc'd) ... die on quit */
case 's': /* -s Read from std input */
case 't': /* -t Read one line from input */
case 'v': /* -v Echo hist expanded input */
nverbose
= 1; /* ... later */
case 'x': /* -x Echo just before execution */
nexececho
= 1; /* ... later */
case 'V': /* -V Echo hist expanded input */
setNS("verbose"); /* NOW! */
case 'X': /* -X Echo just before execution */
setNS("echo"); /* NOW! */
if (quitit
) /* With all due haste, for debugging */
signal(SIGQUIT
, SIG_DFL
);
* Unless prevented by -, -c, -i, -s, or -t, if there
* are remaining arguments the first of them is the name
* of a shell file from which to read commands.
if (nofile
== 0 && c
> 0) {
child
++; /* So this ... */
Perror(v
[0]); /* ... doesn't return */
SHIN
= dmove(nofile
, FSHIN
); /* Replace FSHIN */
* Consider input a tty if it really is or we are interactive.
intty
= intact
|| isatty(SHIN
);
* Commands are interruptible if we are interactive
* or the process which created us was.
if (intact
|| parintr
== SIG_DFL
)
* Save the remaining arguments in ARGV.
* Normally the system-supplied argument list is ok as
* a zero terminated value block.
* On some version 6 systems, it is -1 terminated and setting it
* to zero messes up "ps" so we change it to zero, copy
* the block of pointers, and put it back the way it was.
v
[c
] = 0, setq("argv", copyblk(v
), &shvhed
), v
[c
] = (char *) -1;
setq("argv", v
, &shvhed
);
set("prompt", uid
== 0 ? "# " : "% ");
* If we are an interactive shell, then start fiddling
* with the signals; this is a tricky game.
signal(SIGQUIT
, SIG_IGN
);
signal(SIGTERM
, SIG_IGN
);
* Set an exit here in case of an interrupt or error reading
* the shell start-up scripts.
haderr
= 0; /* In case second time through */
if (!fast
&& reenter
== 0) {
/* Will have value("home") here because set fast if don't */
srccat(value("home"), "/.cshrc");
if (!fast
&& !arginp
&& !onelflg
)
srccat(value("home"), "/.login");
* Now are ready for the -v and -x flags
* All the rest of the world is inside this call.
* The argument to process indicates whether it should
* catch "error unwinds". Thus if we are a interactive shell
* our call here will never return by being blown past on an error.
* Source to the file which is the catenation of the argument names.
register char *ep
= strspl(cp
, dp
);
register int unit
= dmove(open(ep
, 0), -1);
/* ioctl(unit, FIOCLEX, NULL); */
* Source to a unit. If onlyown it must be our file or
* we don't chance it. This occurs on ".cshrc"s and the like.
/* We have to push down a lot of state here */
/* All this could go into a structure */
int oSHIN
= -1, oldintty
= intty
;
struct whyle
*oldwhyl
= whyles
;
char *ogointr
= gointr
, *oarginp
= arginp
;
/* The (few) real local variables */
register int (*oldint
)();
if (fstat(unit
, &stb
) < 0 || (stb
.st_uid
!= uid
&& stb
.st_uid
!= (uid
&~ 0377))) {
if (fstat(unit
, &stb
) < 0 || (stb
.st_uid
!= uid
&& stb
.st_uid
!= (uid
&~ 0377))) {
if (fstat(unit
, &stb
) < 0 || stb
.st_uid
!= uid
) {
* There is a critical section here while we are pushing down the
* input stream since we have stuff in different structures.
* If we weren't careful an interrupt could corrupt SHIN's Bin
* structure and kill the shell.
* We could avoid the critical region by grouping all the stuff
* in a single structure and pointing at it to move it all at
* once. This is less efficient globally on many variable references
oldint
= signal(SIGINT
, SIG_IGN
);
/* Setup the new values of the state stuff saved above */
copy(&saveB
, &B
, sizeof saveB
);
fseekp
= feobp
= fblocks
= 0;
oSHIN
= SHIN
, SHIN
= unit
, arginp
= 0, onelflg
= 0;
intty
= isatty(SHIN
), whyles
= 0, gointr
= 0;
* Now if we are allowing commands to be interrupted,
* we let ourselves be interrupted.
signal(SIGINT
, setintr
? pintr
: oldint
);
process(0); /* 0 -> blow away on errors */
/* We made it to the new state... free up its storage */
/* This code could get run twice but xfree doesn't care */
for (i
= 0; i
< fblocks
; i
++)
copy(&B
, &saveB
, sizeof B
);
close(SHIN
), SHIN
= oSHIN
;
arginp
= oarginp
, onelflg
= oonelflg
;
intty
= oldintty
, whyles
= oldwhyl
, gointr
= ogointr
;
* If process reset() (effectively an unwind) then
signal(SIGQUIT
, SIG_IGN
);
signal(SIGTERM
, SIG_IGN
);
setintr
= 0; /* No interrupts after "logout" */
srccat(value("home"), "/.logout");
* Note that if STATUS is corrupted (i.e. getn bombs)
* then error will exit directly because we poke child here.
* Otherwise we might continue unwarrantedly (sic).
exit(getn(value("status")));
* Catch an interrupt, e.g. during lexical input.
* If we are an interactive shell, we reset the interrupt catch
* immediately. In any case we drain the shell output,
* and finally go through the normal error mechanism, which
* gets a chance to make the shell go away.
* If we have an active "onintr" then we search for the label.
* Note that if one does "onintr -" then we shan't be interruptible
* so we needn't worry about that here.
search(ZGOTO
, 0, gointr
);
printf("\n"); /* Some like this, others don't */
* Process is the main driving routine for the shell.
* It runs all command processing, except for those within { ... }
* in expressions (which is run by a routine evalav in sh.exp.c which
* is a stripped down process), and `...` evaluation which is run
* also by a subset of this code in sh.glob.c in the routine backeval.
* The code here is a little strange because part of it is interruptible
* and hence freeing of structures appears to occur when none is necessary
* Note that if catch is not set then we will unwind on any error.
* In an end-of-file occurs, we return.
paraml
.next
= paraml
.prev
= ¶ml
;
justpr
= 0; /* A chance to execute */
* Interruptible during interactive reads
* For the sake of reset()
freelex(¶ml
), freesyn(t
), t
= 0;
* Every error is eventually caught here or
* the shell dies. It is at this
* point that we clean up any left-over open
* files, by closing all but a fixed number
* of pre-defined files. Thus routines don't
* have to worry about leaving files open due
* to deeper errors... they will get closed here.
* If we are at the end of the input buffer
* then we are going to read fresh stuff.
* Otherwise, we are rereading input and don't
* need or want to prompt.
for (cp
= value("prompt"); *cp
; cp
++)
printf("%d", eventno
+ 1);
if (*cp
== '\\' && cp
[1] == '!')
* Prompt for forward reading loop
* Echo not only on VERBOSE, but also with history expansion.
* If there is a lexical error then we forego history echo.
if (lex(¶ml
) && !err
&& intty
|| adrof("verbose")) {
* The parser may lose space if interrupted.
* Save input text on the history list if it
* is from the terminal at the top level and not
if (catch && intty
&& !whyles
)
* Print lexical error messages.
* If had a history command :p modifier then
* this is as far as we should go
* Parse the words of the input into a parse tree.
t
= syntax(paraml
.next
, ¶ml
);
freelex(¶ml
), freesyn(t
);
u
= dmove(open(f
, 0), -1);
* If we are a login shell, then we don't want to tell
* about any mail file unless its been modified
* after the time we started.
* This prevents us from telling the user things he already
* knows, since the login program insist on saying
register struct varent
*v
;
intvl
= (cnt
&& number(*vp
)) ? (--cnt
, getn(*vp
++)) : MAILINTVL
;
* We assume that a file has been read if the access time is
* greater than the mod time.
if (stb
.st_atime
> stb
.st_mtime
|| stb
.st_atime
< chktim
)
new = stb
.st_mtime
> time0
;
printf("You have %smail.\n", new ? "new " : "");
printf("%s in %s.\n", new ? "New mail" : "Mail", *vp
);
* Extract a home directory from the password file
* The argument points to a buffer where the name of the
* user whose home directory is sought is currently.
* We write the home directory of the user back there.
register struct passwd
*pp
= getpwnam(home
);
strcpy(home
, pp
->pw_dir
);
* Move the initial descriptors to their eventual
* resting places, closin all other units.
didcch
= 0; /* Havent closed for child */
didfds
= 0; /* 0, 1, 2 aren't set up */
SHOUT
= dcopy(1, FSHOUT
);
SHDIAG
= dcopy(2, FSHDIAG
);
OLDSTD
= dcopy(SHIN
, FOLDSTD
);