home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume19
/
ash
/
part04
< prev
next >
Wrap
Text File
|
1989-05-29
|
52KB
|
2,144 lines
Subject: v19i004: A reimplementation of the System V shell, Part04/08
Newsgroups: comp.sources.unix
Sender: sources
Approved: rsalz@uunet.UU.NET
Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
Posting-number: Volume 19, Issue 4
Archive-name: ash/part04
# This is part 4 of ash. To unpack, feed it into the shell (not csh).
# The ash distribution consists of eight pieces. Be sure you get them all.
# After you unpack everything, read the file README.
echo extracting jobs.h
cat > jobs.h <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
#define FORK_NOJOB 2
/*
* A job structure contains information about a job. A job is either a
* single process or a set of processes contained in a pipeline. In the
* latter case, pidlist will be non-NULL, and will point to a -1 terminated
* array of pids.
*/
struct procstat {
short pid; /* process id */
short status; /* status flags (defined above) */
char *cmd; /* text of command being run */
};
/* states */
#define JOBSTOPPED 1 /* all procs are stopped */
#define JOBDONE 2 /* all procs are completed */
struct job {
struct procstat ps0; /* status of process */
struct procstat *ps; /* status or processes when more than one */
short nprocs; /* number of processes */
short pgrp; /* process group of this job */
char state; /* true if job is finished */
char used; /* true if this entry is in used */
char changed; /* true if status has changed */
#if JOBS
char jobctl; /* job running under job control */
#endif
};
extern short backgndpid; /* pid of last background process */
#ifdef __STDC__
void setjobctl(int);
void showjobs(int);
struct job *makejob(union node *, int);
int forkshell(struct job *, union node *, int);
int waitforjob(struct job *);
#else
void setjobctl();
void showjobs();
struct job *makejob();
int forkshell();
int waitforjob();
#endif
#if ! JOBS
#define setjobctl(on) /* do nothing */
#endif
EOF
if test `wc -c < jobs.h` -ne 1738
then echo 'jobs.h is the wrong size'
fi
echo extracting jobs.c
cat > jobs.c <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
#include "shell.h"
#if JOBS
#include "sgtty.h"
#undef CEOF /* syntax.h redefines this */
#endif
#include "main.h"
#include "parser.h"
#include "nodes.h"
#include "jobs.h"
#include "options.h"
#include "trap.h"
#include "signames.h"
#include "syntax.h"
#include "input.h"
#include "output.h"
#include "memalloc.h"
#include "error.h"
#include "mystring.h"
#include <fcntl.h>
#include <signal.h>
#include "myerrno.h"
#ifdef BSD
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
struct job *jobtab; /* array of jobs */
int njobs; /* size of array */
MKINIT short backgndpid = -1; /* pid of last background process */
#if JOBS
int initialpgrp; /* pgrp of shell on invocation */
short curjob; /* current job */
#endif
#ifdef __STDC__
STATIC void restartjob(struct job *);
STATIC struct job *getjob(char *);
STATIC void freejob(struct job *);
STATIC int procrunning(int);
STATIC int dowait(int, struct job *);
STATIC int waitproc(int, int *);
STATIC char *commandtext(union node *);
#else
STATIC void restartjob();
STATIC struct job *getjob();
STATIC void freejob();
STATIC int procrunning();
STATIC int dowait();
STATIC int waitproc();
STATIC char *commandtext();
#endif
#if JOBS
/*
* Turn job control on and off.
*
* Note: This code assumes that the third arg to ioctl is a character
* pointer, which is true on Berkeley systems but not System V. Since
* System V doesn't have job control yet, this isn't a problem now.
*/
MKINIT int jobctl;
void
setjobctl(on) {
int ldisc;
if (on == jobctl || rootshell == 0)
return;
if (on) {
do { /* while we are in the background */
if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
out2str("ash: can't access tty; job control turned off\n");
jflag = 0;
return;
}
if (initialpgrp == -1)
initialpgrp = getpgrp(0);
else if (initialpgrp != getpgrp(0)) {
killpg(initialpgrp, SIGTTIN);
continue;
}
} while (0);
if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
out2str("ash: need new tty driver to run job control; job control turned off\n");
jflag = 0;
return;
}
setsignal(SIGTSTP);
setsignal(SIGTTOU);
ioctl(2, TIOCSPGRP, (char *)&rootpid);
setpgrp(0, rootpid);
} else { /* turning job control off */
ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
setpgrp(0, initialpgrp);
setsignal(SIGTSTP);
setsignal(SIGTTOU);
}
jobctl = on;
}
#endif
#ifdef mkinit
SHELLPROC {
backgndpid = -1;
#if JOBS
jobctl = 0;
#endif
}
#endif
#if JOBS
fgcmd(argc, argv) char **argv; {
struct job *jp;
int pgrp;
int status;
jp = getjob(argv[1]);
if (jp->jobctl == 0)
error("job not created under job control");
pgrp = jp->ps[0].pid;
ioctl(2, TIOCSPGRP, (char *)&pgrp);
restartjob(jp);
INTOFF;
status = waitforjob(jp);
INTON;
return status;
}
bgcmd(argc, argv) char **argv; {
struct job *jp;
do {
jp = getjob(*++argv);
if (jp->jobctl == 0)
error("job not created under job control");
restartjob(jp);
} while (--argc > 1);
return 0;
}
STATIC void
restartjob(jp)
struct job *jp;
{
struct procstat *ps;
int i;
if (jp->state == JOBDONE)
return;
INTOFF;
killpg(jp->ps[0].pid, SIGCONT);
for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
if ((ps->status & 0377) == 0177) {
ps->status = -1;
jp->state = 0;
}
}
INTON;
}
#endif
int
jobscmd(argc, argv) char **argv; {
showjobs(0);
return 0;
}
/*
* Print a list of jobs. If "change" is nonzero, only print jobs whose
* statuses have changed since the last call to showjobs.
*
* If the shell is interrupted in the process of creating a job, the
* result may be a job structure containing zero processes. Such structures
* will be freed here.
*/
void
showjobs(change) {
int jobno;
int procno;
int i;
struct job *jp;
struct procstat *ps;
int col;
char s[64];
TRACE(("showjobs(%d) called\n", change));
while (dowait(0, (struct job *)NULL) > 0);
for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
if (! jp->used)
continue;
if (jp->nprocs == 0) {
freejob(jp);
continue;
}
if (change && ! jp->changed)
continue;
procno = jp->nprocs;
for (ps = jp->ps ; ; ps++) { /* for each process */
if (ps == jp->ps)
fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
else
fmtstr(s, 64, " %d ", ps->pid);
out1str(s);
col = strlen(s);
s[0] = '\0';
if (ps->status == -1) {
/* don't print anything */
} else if ((ps->status & 0xFF) == 0) {
fmtstr(s, 64, "Exit %d", ps->status >> 8);
} else {
i = ps->status;
#if JOBS
if ((i & 0xFF) == 0177)
i >>= 8;
#endif
if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F])
scopy(sigmesg[i & 0x7F], s);
else
fmtstr(s, 64, "Signal %d", i & 0x7F);
if (i & 0x80)
strcat(s, " (core dumped)");
}
out1str(s);
col += strlen(s);
do {
out1c(' ');
col++;
} while (col < 30);
out1str(ps->cmd);
out1c('\n');
if (--procno <= 0)
break;
}
jp->changed = 0;
if (jp->state == JOBDONE) {
freejob(jp);
}
}
}
/*
* Mark a job structure as unused.
*/
STATIC void
freejob(jp)
struct job *jp;
{
struct procstat *ps;
int i;
INTOFF;
for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
if (ps->cmd != nullstr)
ckfree(ps->cmd);
}
if (jp->ps != &jp->ps0)
ckfree(jp->ps);
jp->used = 0;
#if JOBS
if (curjob == jp - jobtab + 1)
curjob = 0;
#endif
INTON;
}
int
waitcmd(argc, argv) char **argv; {
struct job *job;
int status;
struct job *jp;
if (argc > 1) {
job = getjob(argv[1]);
} else {
job = NULL;
}
for (;;) { /* loop until process terminated or stopped */
if (job != NULL) {
if (job->state) {
status = job->ps[job->nprocs - 1].status;
if ((status & 0xFF) == 0)
status = status >> 8 & 0xFF;
#if JOBS
else if ((status & 0xFF) == 0177)
status = (status >> 8 & 0x7F) + 128;
#endif
else
status = (status & 0x7F) + 128;
if (! iflag)
freejob(job);
return status;
}
} else {
for (jp = jobtab ; ; jp++) {
if (jp >= jobtab + njobs) { /* no running procs */
return 0;
}
if (jp->used && jp->state == 0)
break;
}
}
dowait(1, (struct job *)NULL);
}
}
jobidcmd(argc, argv) char **argv; {
struct job *jp;
int i;
jp = getjob(argv[1]);
for (i = 0 ; i < jp->nprocs ; ) {
out1fmt("%d", jp->ps[i].pid);
out1c(++i < jp->nprocs? ' ' : '\n');
}
return 0;
}
/*
* Convert a job name to a job structure.
*/
STATIC struct job *
getjob(name)
char *name;
{
int jobno;
register struct job *jp;
int pid;
int i;
if (name == NULL) {
#if JOBS
currentjob:
if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
error("No current job");
return &jobtab[jobno - 1];
#else
error("No current job");
#endif
} else if (name[0] == '%') {
if (is_digit(name[1])) {
jobno = number(name + 1);
if (jobno > 0 && jobno <= njobs
&& jobtab[jobno - 1].used != 0)
return &jobtab[jobno - 1];
#if JOBS
} else if (name[1] == '%' && name[2] == '\0') {
goto currentjob;
#endif
} else {
register struct job *found = NULL;
for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
if (jp->used && jp->nprocs > 0
&& prefix(name + 1, jp->ps[0].cmd)) {
if (found)
error("%s: ambiguous", name);
found = jp;
}
}
if (found)
return found;
}
} else if (is_number(name)) {
pid = number(name);
for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
if (jp->used && jp->nprocs > 0
&& jp->ps[jp->nprocs - 1].pid == pid)
return jp;
}
}
error("No such job: %s", name);
}
/*
* Return a new job structure,
*/
struct job *
makejob(node, nprocs)
union node *node;
{
int i;
struct job *jp;
for (i = njobs, jp = jobtab ; ; jp++) {
if (--i < 0) {
INTOFF;
if (njobs == 0) {
jobtab = ckmalloc(4 * sizeof jobtab[0]);
} else {
jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
bcopy(jobtab, jp, njobs * sizeof jp[0]);
ckfree(jobtab);
jobtab = jp;
}
jp = jobtab + njobs;
for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
INTON;
break;
}
if (jp->used == 0)
break;
}
INTOFF;
jp->state = 0;
jp->used = 1;
jp->changed = 0;
jp->nprocs = 0;
#if JOBS
jp->jobctl = jobctl;
#endif
if (nprocs > 1) {
jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
} else {
jp->ps = &jp->ps0;
}
INTON;
TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1));
return jp;
}
/*
* Fork of a subshell. If we are doing job control, give the subshell its
* own process group. Jp is a job structure that the job is to be added to.
* N is the command that will be evaluated by the child. Both jp and n may
* be NULL. The mode parameter can be one of the following:
* FORK_FG - Fork off a foreground process.
* FORK_BG - Fork off a background process.
* FORK_NOJOB - Like FORK_FG, but don't give the process its own
* process group even if job control is on.
*
* When job control is turned off, background processes have their standard
* input redirected to /dev/null (except for the second and later processes
* in a pipeline).
*/
int
forkshell(jp, n, mode)
union node *n;
struct job *jp;
{
int pid;
int pgrp;
TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode));
INTOFF;
pid = fork();
if (pid == -1) {
TRACE(("Fork failed, errno=%d\n", errno));
INTON;
error("Cannot fork");
}
if (pid == 0) {
struct job *p;
int wasroot;
int i;
TRACE(("Child shell %d\n", getpid()));
wasroot = rootshell;
rootshell = 0;
for (i = njobs, p = jobtab ; --i >= 0 ; p++)
if (p->used)
freejob(p);
closescript();
INTON;
clear_traps();
#if JOBS
jobctl = 0; /* do job control only in root shell */
if (wasroot && mode != FORK_NOJOB && jflag) {
if (jp == NULL || jp->nprocs == 0)
pgrp = getpid();
else
pgrp = jp->ps[0].pid;
if (mode == FORK_FG) {
if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
error("TIOCSPGRP failed, errno=%d\n", errno);
}
setpgrp(0, pgrp);
setsignal(SIGTSTP);
setsignal(SIGTTOU);
} else if (mode == FORK_BG) {
ignoresig(SIGINT);
ignoresig(SIGQUIT);
if (jp == NULL || jp->nprocs == 0) {
close(0);
if (open("/dev/null", O_RDONLY) != 0)
error("Can't open /dev/null");
}
}
#else
if (mode == FORK_BG) {
ignoresig(SIGINT);
ignoresig(SIGQUIT);
if (jp == NULL || jp->nprocs == 0) {
close(0);
if (open("/dev/null", O_RDONLY) != 0)
error("Can't open /dev/null");
}
}
#endif
if (wasroot && iflag) {
setsignal(SIGINT);
setsignal(SIGQUIT);
setsignal(SIGTERM);
}
return pid;
}
if (mode == FORK_BG)
backgndpid = pid; /* set $! */
if (jp) {
struct procstat *ps = &jp->ps[jp->nprocs++];
ps->pid = pid;
ps->status = -1;
ps->cmd = nullstr;
if (iflag && rootshell && n)
ps->cmd = commandtext(n);
}
INTON;
TRACE(("In parent shell: child = %d\n", pid));
return pid;
}
/*
* Wait for job to finish.
*
* Under job control we have the problem that while a child process is
* running interrupts generated by the user are sent to the child but not
* to the shell. This means that an infinite loop started by an inter-
* active user may be hard to kill. With job control turned off, an
* interactive user may place an interactive program inside a loop. If
* the interactive program catches interrupts, the user doesn't want
* these interrupts to also abort the loop. The approach we take here
* is to have the shell ignore interrupt signals while waiting for a
* forground process to terminate, and then send itself an interrupt
* signal if the child process was terminated by an interrupt signal.
* Unfortunately, some programs want to do a bit of cleanup and then
* exit on interrupt; unless these processes terminate themselves by
* sending a signal to themselves (instead of calling exit) they will
* confuse this approach.
*/
int
waitforjob(jp)
register struct job *jp;
{
#if JOBS
int mypgrp = getpgrp(0);
#endif
int status;
int st;
INTOFF;
TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
while (jp->state == 0) {
dowait(1, jp);
}
#if JOBS
if (jp->jobctl) {
if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
error("TIOCSPGRP failed, errno=%d\n", errno);
}
if (jp->state == JOBSTOPPED)
curjob = jp - jobtab + 1;
#endif
status = jp->ps[jp->nprocs - 1].status;
/* convert to 8 bits */
if ((status & 0xFF) == 0)
st = status >> 8 & 0xFF;
#if JOBS
else if ((status & 0xFF) == 0177)
st = (status >> 8 & 0x7F) + 128;
#endif
else
st = (status & 0x7F) + 128;
if (! JOBS || jp->state == JOBDONE)
freejob(jp);
CLEAR_PENDING_INT;
if ((status & 0x7F) == SIGINT)
kill(getpid(), SIGINT);
INTON;
return st;
}
/*
* Wait for a process to terminate.
*/
STATIC int
dowait(block, job)
struct job *job;
{
int pid;
int status;
struct procstat *sp;
struct job *jp;
struct job *thisjob;
int done;
int stopped;
int core;
TRACE(("dowait(%d) called\n", block));
do {
pid = waitproc(block, &status);
TRACE(("wait returns %d, status=%d\n", pid, status));
} while (pid == -1 && errno == EINTR);
if (pid <= 0)
return pid;
INTOFF;
thisjob = NULL;
for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
if (jp->used) {
done = 1;
stopped = 1;
for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
if (sp->pid == -1)
continue;
if (sp->pid == pid) {
TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
sp->status = status;
thisjob = jp;
}
if (sp->status == -1)
stopped = 0;
else if ((sp->status & 0377) == 0177)
done = 0;
}
if (stopped) { /* stopped or done */
int state = done? JOBDONE : JOBSTOPPED;
if (jp->state != state) {
TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
jp->state = state;
#if JOBS
if (done && curjob == jp - jobtab + 1)
curjob = 0; /* no current job */
#endif
}
}
}
}
INTON;
if (! rootshell || ! iflag || (job && thisjob == job)) {
#if JOBS
if ((status & 0xFF) == 0177)
status >>= 8;
#endif
core = status & 0x80;
status &= 0x7F;
if (status != 0 && status != SIGINT && status != SIGPIPE) {
if (thisjob != job)
outfmt(out2, "%d: ", pid);
#if JOBS
if (status == SIGTSTP && rootshell && iflag)
outfmt(out2, "%%%d ", job - jobtab + 1);
#endif
if (status <= MAXSIG && sigmesg[status])
out2str(sigmesg[status]);
else
outfmt(out2, "Signal %d", status);
if (core)
out2str(" - core dumped");
out2c('\n');
flushout(&errout);
} else {
TRACE(("Not printing status: status=%d\n", status));
}
} else {
TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
if (thisjob)
thisjob->changed = 1;
}
return pid;
}
/*
* Do a wait system call. If job control is compiled in, we accept
* stopped processes. If block is zero, we return a value of zero
* rather than blocking.
*
* System V doesn't have a non-blocking wait system call. It does
* have a SIGCLD signal that is sent to a process when one of it's
* children dies. The obvious way to use SIGCLD would be to install
* a handler for SIGCLD which simply bumped a counter when a SIGCLD
* was received, and have waitproc bump another counter when it got
* the status of a process. Waitproc would then know that a wait
* system call would not block if the two counters were different.
* This approach doesn't work because if a process has children that
* have not been waited for, System V will send it a SIGCLD when it
* installs a signal handler for SIGCLD. What this means is that when
* a child exits, the shell will be sent SIGCLD signals continuously
* until is runs out of stack space, unless it does a wait call before
* restoring the signal handler. The code below takes advantage of
* this (mis)feature by installing a signal handler for SIGCLD and
* then checking to see whether it was called. If there are any
* children to be waited for, it will be.
*
* If neither SYSV nor BSD is defined, we don't implement nonblocking
* waits at all. In this case, the user will not be informed when
* a background process until the next time she runs a real program
* (as opposed to running a builtin command or just typing return),
* and the jobs command may give out of date information.
*/
#ifdef SYSV
STATIC int gotsigchild;
STATIC int onsigchild() {
gotsigchild = 1;
}
#endif
STATIC int
waitproc(block, status)
int *status;
{
#ifdef BSD
int flags;
#if JOBS
flags = WUNTRACED;
#else
flags = 0;
#endif
if (block == 0)
flags |= WNOHANG;
return wait3((union wait *)status, flags, (struct rusage *)NULL);
#else
#ifdef SYSV
int (*save)();
if (block == 0) {
gotsigchild = 0;
save = signal(SIGCLD, onsigchild);
signal(SIGCLD, save);
if (gotsigchild == 0)
return 0;
}
return wait(status);
#else
if (block == 0)
return 0;
return wait(status);
#endif
#endif
}
/*
* Return a string identifying a command (to be printed by the
* jobs command.
*/
STATIC char *cmdnextc;
STATIC int cmdnleft;
STATIC void cmdtxt(), cmdputs();
STATIC char *
commandtext(n)
union node *n;
{
char *name;
cmdnextc = name = ckmalloc(50);
cmdnleft = 50 - 4;
cmdtxt(n);
*cmdnextc = '\0';
return name;
}
STATIC void
cmdtxt(n)
union node *n;
{
union node *np;
struct nodelist *lp;
char *p;
int i;
char s[2];
switch (n->type) {
case NSEMI:
cmdtxt(n->nbinary.ch1);
cmdputs("; ");
cmdtxt(n->nbinary.ch2);
break;
case NAND:
cmdtxt(n->nbinary.ch1);
cmdputs(" && ");
cmdtxt(n->nbinary.ch2);
break;
case NOR:
cmdtxt(n->nbinary.ch1);
cmdputs(" || ");
cmdtxt(n->nbinary.ch2);
break;
case NPIPE:
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
cmdtxt(lp->n);
if (lp->next)
cmdputs(" | ");
}
break;
case NSUBSHELL:
cmdputs("(");
cmdtxt(n->nredir.n);
cmdputs(")");
break;
case NREDIR:
case NBACKGND:
cmdtxt(n->nredir.n);
break;
case NIF:
cmdputs("if ");
cmdtxt(n->nif.test);
cmdputs("; then ");
cmdtxt(n->nif.ifpart);
cmdputs("...");
break;
case NWHILE:
cmdputs("while ");
goto until;
case NUNTIL:
cmdputs("until ");
until:
cmdtxt(n->nbinary.ch1);
cmdputs("; do ");
cmdtxt(n->nbinary.ch2);
cmdputs("; done");
break;
case NFOR:
cmdputs("for ");
cmdputs(n->nfor.var);
cmdputs(" in ...");
break;
case NCASE:
cmdputs("case ");
cmdputs(n->ncase.expr->narg.text);
cmdputs(" in ...");
break;
case NDEFUN:
cmdputs(n->narg.text);
cmdputs("() ...");
break;
case NCMD:
for (np = n->ncmd.args ; np ; np = np->narg.next) {
cmdtxt(np);
if (np->narg.next)
cmdputs(" ");
}
for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
cmdputs(" ");
cmdtxt(np);
}
break;
case NARG:
cmdputs(n->narg.text);
break;
case NTO:
p = ">"; i = 1; goto redir;
case NAPPEND:
p = ">>"; i = 1; goto redir;
case NTOFD:
p = ">&"; i = 1; goto redir;
case NFROM:
p = "<"; i = 0; goto redir;
case NFROMFD:
p = "<&"; i = 0; goto redir;
redir:
if (n->nfile.fd != i) {
s[0] = n->nfile.fd + '0';
s[1] = '\0';
cmdputs(s);
}
cmdputs(p);
if (n->type == NTOFD || n->type == NFROMFD) {
s[0] = n->ndup.dupfd + '0';
s[1] = '\0';
cmdputs(s);
} else {
cmdtxt(n->nfile.fname);
}
break;
case NHERE:
case NXHERE:
cmdputs("<<...");
break;
default:
cmdputs("???");
break;
}
}
STATIC void
cmdputs(s)
char *s;
{
register char *p, *q;
register char c;
int subtype = 0;
if (cmdnleft <= 0)
return;
p = s;
q = cmdnextc;
while ((c = *p++) != '\0') {
if (c == CTLESC)
*q++ = *p++;
else if (c == CTLVAR) {
*q++ = '$';
if (--cmdnleft > 0)
*q++ = '{';
subtype = *p++;
} else if (c == '=' && subtype != 0) {
*q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
subtype = 0;
} else if (c == CTLENDVAR) {
*q++ = '}';
} else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE)
cmdnleft++; /* ignore it */
else
*q++ = c;
if (--cmdnleft <= 0) {
*q++ = '.';
*q++ = '.';
*q++ = '.';
break;
}
}
cmdnextc = q;
}
EOF
if test `wc -c < jobs.c` -ne 24668
then echo 'jobs.c is the wrong size'
fi
echo extracting machdep.h
cat > machdep.h <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
/*
* Most machines require the value returned from malloc to be aligned
* in some way. The following macro will get this right on many machines.
*/
#ifndef ALIGN
union align {
int i;
char *cp;
};
#define ALIGN(nbytes) ((nbytes) + sizeof(union align) - 1 &~ (sizeof(union align) - 1))
#endif
EOF
if test `wc -c < machdep.h` -ne 525
then echo 'machdep.h is the wrong size'
fi
echo extracting mail.h
cat > mail.h <<\EOF
#ifdef __STDC__
void chkmail(int);
#else
void chkmail();
#endif
EOF
if test `wc -c < mail.h` -ne 64
then echo 'mail.h is the wrong size'
fi
echo extracting mail.c
cat > mail.c <<\EOF
/*
* Routines to check for mail. (Perhaps make part of main.c?)
*/
#include "shell.h"
#include "exec.h" /* defines padvance() */
#include "var.h"
#include "output.h"
#include "memalloc.h"
#include "error.h"
#include <sys/types.h>
#include <sys/stat.h>
#define MAXMBOXES 10
STATIC int nmboxes; /* number of mailboxes */
STATIC time_t mailtime[MAXMBOXES]; /* times of mailboxes */
/*
* Print appropriate message(s) if mail has arrived. If the argument is
* nozero, then the value of MAIL has changed, so we just update the
* values.
*/
void
chkmail(silent) {
register int i;
char *mpath;
char *p;
register char *q;
struct stackmark smark;
struct stat statb;
if (silent)
nmboxes = 10;
if (nmboxes == 0)
return;
setstackmark(&smark);
mpath = mpathset()? mpathval() : mailval();
for (i = 0 ; i < nmboxes ; i++) {
p = padvance(&mpath, nullstr);
if (p == NULL)
break;
if (*p == '\0')
continue;
for (q = p ; *q ; q++);
if (q[-1] != '/')
abort();
q[-1] = '\0'; /* delete trailing '/' */
#ifdef notdef /* this is what the System V shell claims to do (it lies) */
if (stat(p, &statb) < 0)
statb.st_mtime = 0;
if (statb.st_mtime > mailtime[i] && ! silent) {
out2str(pathopt? pathopt : "you have mail");
out2c('\n');
}
mailtime[i] = statb.st_mtime;
#else /* this is what it should do */
if (stat(p, &statb) < 0)
statb.st_size = 0;
if (statb.st_size > mailtime[i] && ! silent) {
out2str(pathopt? pathopt : "you have mail");
out2c('\n');
}
mailtime[i] = statb.st_size;
#endif
}
nmboxes = i;
popstackmark(&smark);
}
EOF
if test `wc -c < mail.c` -ne 1722
then echo 'mail.c is the wrong size'
fi
echo extracting main.h
cat > main.h <<\EOF
extern int rootpid; /* pid of main shell */
extern int rootshell; /* true if we aren't a child of the main shell */
#ifdef __STDC__
void readcmdfile(char *);
void cmdloop(int);
#else
void readcmdfile();
void cmdloop();
#endif
EOF
if test `wc -c < main.h` -ne 229
then echo 'main.h is the wrong size'
fi
echo extracting main.c
cat > main.c <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
#include <signal.h>
#include <fcntl.h>
#include "shell.h"
#include "main.h"
#include "mail.h"
#include "options.h"
#include "output.h"
#include "parser.h"
#include "nodes.h"
#include "eval.h"
#include "jobs.h"
#include "input.h"
#include "trap.h"
#if ATTY
#include "var.h"
#endif
#include "memalloc.h"
#include "error.h"
#include "init.h"
#include "mystring.h"
#define PROFILE 0
const char copyright[] = "@(#)Copyright 1989 by Kenneth Almquist";
int rootpid;
int rootshell;
STATIC union node *curcmd;
STATIC union node *prevcmd;
extern int errno;
#if PROFILE
short profile_buf[16384];
extern int etext();
#endif
#ifdef __STDC__
STATIC void read_profile(char *);
char *getenv(char *);
#else
STATIC void read_profile();
char *getenv();
#endif
/*
* Main routine. We initialize things, parse the arguments, execute
* profiles if we're a login shell, and then call cmdloop to execute
* commands. The setjmp call sets up the location to jump to when an
* exception occurs. When an exception occurs the variable "state"
* is used to figure out how far we had gotten.
*/
main(argc, argv) char **argv; {
struct jmploc jmploc;
struct stackmark smark;
volatile int state;
char *shinit;
#if PROFILE
monitor(4, etext, profile_buf, sizeof profile_buf, 50);
#endif
state = 0;
if (setjmp(jmploc.loc)) {
/*
* When a shell procedure is executed, we raise the
* exception EXSHELLPROC to clean up before executing
* the shell procedure.
*/
if (exception == EXSHELLPROC) {
rootpid = getpid();
rootshell = 1;
minusc = NULL;
state = 3;
} else if (state == 0 || iflag == 0 || ! rootshell)
exitshell(2);
reset();
#if ATTY
if (exception == EXINT
&& (! attyset() || equal(termval(), "emacs"))) {
#else
if (exception == EXINT) {
#endif
out2c('\n');
flushout(&errout);
}
popstackmark(&smark);
FORCEINTON; /* enable interrupts */
if (state == 1)
goto state1;
else if (state == 2)
goto state2;
else
goto state3;
}
handler = &jmploc;
#ifdef DEBUG
opentrace();
trputs("Shell args: "); trargs(argv);
#endif
rootpid = getpid();
rootshell = 1;
init();
setstackmark(&smark);
procargs(argc, argv);
if (argv[0] && argv[0][0] == '-') {
state = 1;
read_profile("/etc/profile");
state1:
state = 2;
read_profile(".profile");
} else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) {
state = 2;
evalstring(shinit);
}
state2:
state = 3;
if (minusc) {
evalstring(minusc);
}
if (sflag || minusc == NULL) {
state3:
cmdloop(1);
}
#if PROFILE
monitor(0);
#endif
exitshell(exitstatus);
}
/*
* Read and execute commands. "Top" is nonzero for the top level command
* loop; it turns on prompting if the shell is interactive.
*/
void
cmdloop(top) {
union node *n;
struct stackmark smark;
int inter;
int numeof;
TRACE(("cmdloop(%d) called\n", top));
setstackmark(&smark);
numeof = 0;
for (;;) {
if (sigpending)
dotrap();
inter = 0;
if (iflag && top) {
inter++;
showjobs(1);
chkmail(0);
flushout(&output);
}
n = parsecmd(inter);
if (n == NEOF) {
if (Iflag == 0 || numeof >= 50)
break;
out2str("\nUse \"exit\" to leave ash.\n");
numeof++;
} else if (n != NULL && nflag == 0) {
if (inter) {
INTOFF;
if (prevcmd)
freefunc(prevcmd);
prevcmd = curcmd;
curcmd = copyfunc(n);
INTON;
}
evaltree(n, 0);
#ifdef notdef
if (exitstatus) /*DEBUG*/
outfmt(&errout, "Exit status 0x%X\n", exitstatus);
#endif
}
popstackmark(&smark);
}
popstackmark(&smark); /* unnecessary */
}
/*
* Read /etc/profile or .profile. Return on error.
*/
STATIC void
read_profile(name)
char *name;
{
int fd;
INTOFF;
if ((fd = open(name, O_RDONLY)) >= 0)
setinputfd(fd, 1);
INTON;
if (fd < 0)
return;
cmdloop(0);
popfile();
}
/*
* Read a file containing shell functions.
*/
void
readcmdfile(name)
char *name;
{
int fd;
INTOFF;
if ((fd = open(name, O_RDONLY)) >= 0)
setinputfd(fd, 1);
else
error("Can't open %s", name);
INTON;
cmdloop(0);
popfile();
}
/*
* Take commands from a file. To be compatable we should do a path
* search for the file, but a path search doesn't make any sense.
*/
dotcmd(argc, argv) char **argv; {
exitstatus = 0;
if (argc >= 2) { /* That's what SVR2 does */
setinputfile(argv[1], 1);
commandname = argv[1];
cmdloop(0);
popfile();
}
return exitstatus;
}
exitcmd(argc, argv) char **argv; {
if (argc > 1)
exitstatus = number(argv[1]);
exitshell(exitstatus);
}
lccmd(argc, argv) char **argv; {
if (argc > 1) {
defun(argv[1], prevcmd);
return 0;
} else {
INTOFF;
freefunc(curcmd);
curcmd = prevcmd;
prevcmd = NULL;
INTON;
evaltree(curcmd, 0);
return exitstatus;
}
}
#ifdef notdef
/*
* Should never be called.
*/
void
exit(exitstatus) {
_exit(exitstatus);
}
#endif
EOF
if test `wc -c < main.c` -ne 5561
then echo 'main.c is the wrong size'
fi
echo extracting makefile
cat > makefile <<\EOF
# Makefile for ash.
#
# Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
# This file is part of ash, which is distributed under the terms specified
# by the Ash General Public License. See the file named LICENSE.
FILES=main.o options.o parser.o eval.o expand.o jobs.o redir.o exec.o\
builtins.o cd.o miscbltin.o mail.o var.o input.o output.o nodes.o syntax.o\
signames.o memalloc.o error.o trap.o show.o dirent.o mystring.o\
init.o
CFILES=main.c options.c parser.c eval.c expand.c jobs.c redir.c exec.c\
builtins.c cd.c miscbltin.c mail.c var.c input.c output.c nodes.c syntax.c\
signames.c memalloc.c error.c trap.c show.c dirent.c mystring.c
GENERATEDFILES=syntax.h syntax.c signames.h signames.c nodes.c nodes.c\
builtin.h builtin.c init.c token.def
#MALLOC=mymalloc.o
#CC=gcc
DEBUG=-g
CFLAGS=$(DEBUG)
LDFLAGS=
BLTIN=bltin
all: make_bltin ash
make_bltin:
cd bltin; make 'CC=$(CC)' 'DEBUG=$(DEBUG)'
clean:
rm -f $(FILES)
rm -f $(GENERATEDFILES) mksyntax mksignames mknodes mkinit
rm -f bltin/bltinlib.a bltin/*.o bltin/operators.h bltin/operators.c
clobber: clean
rm -f ash bltin/catf bltin/expr bltin/test 'bltin/[' bltin/echo bltin/line bltin/nlecho bltin/true bltin/: bltin/umask
ash:$P $(FILES) $(BLTIN)/bltinlib.a $(MALLOC)
$(CC) -o temp $(LDFLAGS) $(DEBUG) $(FILES) $(BLTIN)/bltinlib.a $(MALLOC)
# ld -o temp crt0.o $(FILES) $(BLTIN)/bltinlib.a $(MALLOC) -lc
mv -f temp $@
lint:
lint $(CFILES) init.c
syntax.c syntax.h: mksyntax
./mksyntax
mksyntax: mksyntax.c parser.h
$(CC) -o mksyntax mksyntax.c
signames.c signames.h: mksignames
./mksignames
mksignames: mksignames.c
$(CC) -o mksignames mksignames.c
nodes.c nodes.h: mknodes nodetypes nodes.c.pat
./mknodes
mknodes: mknodes.c
$(CC) -o mknodes -g mknodes.c
token.def: mktokens
sh mktokens
builtins.h builtins.c: mkbuiltins builtins
sh mkbuiltins
rm -f builtins.o
.c:
echo make is confused, it but should recover
init.o: mkinit $(CFILES)
./mkinit '$(CC) -c $(CFLAGS) init.c' $(CFILES)
mkinit: mkinit.c
$(CC) -o mkinit mkinit.c
cd.o: shell.h var.h nodes.h jobs.h options.h output.h memalloc.h error.h\
mystring.h
dirent.o: shell.h mydirent.h
eval.o: shell.h nodes.h syntax.h expand.h parser.h jobs.h eval.h builtins.h\
options.h exec.h redir.h input.h output.h trap.h var.h memalloc.h\
error.h mystring.h
error.o: shell.h main.h options.h output.h error.h
exec.o: shell.h main.h nodes.h parser.h redir.h eval.h exec.h builtins.h var.h\
options.h input.h output.h memalloc.h error.h init.h\
mystring.h
expand.o: shell.h main.h nodes.h eval.h expand.h syntax.h parser.h jobs.h\
options.h var.h input.h output.h memalloc.h error.h\
mystring.h mydirent.h
input.c: shell.h syntax.h input.h output.h memalloc.h error.h
jobs.o: shell.h main.h parser.h nodes.h jobs.h options.h trap.h signames.h\
syntax.h input.h output.h memalloc.h error.h mystring.h
mail.o: shell.h exec.h var.h output.h memalloc.h error.h
main.o: shell.h mail.h options.h var.h output.h parser.h nodes.h eval.h jobs.h\
input.h trap.h error.h memalloc.h init.h
memalloc.o: shell.h output.h memalloc.h error.h machdep.h mystring.h
miscbltin.o: shell.h options.h var.h output.h memalloc.h error.h mystring.h
mystring.o: shell.h syntax.h error.h mystring.h
nodes.o: shell.h nodes.h memalloc.h machdep.h mystring.h
options.o: shell.h options.h nodes.h eval.h jobs.h input.h output.h trap.h\
var.h memalloc.h error.h mystring.h
output.o: shell.h syntax.h output.h memalloc.h error.h
parser.o: shell.h parser.h nodes.h expand.h redir.h syntax.h options.h input.h\
output.h var.h error.h memalloc.h mystring.h token.def
redir.o: shell.h nodes.h jobs.h expand.h redir.h output.h memalloc.h error.h
show.o: shell.h parser.h nodes.h mystring.h
syntax.o: shell.h syntax.h
trap.o: shell.h main.h nodes.h eval.h jobs.h options.h syntax.h signames.h\
output.h memalloc.h error.h trap.h
var.o: shell.h output.h expand.h nodes.h eval.h exec.h syntax.h mail.h\
options.h var.h memalloc.h error.h mystring.h
EOF
if test `wc -c < makefile` -ne 3992
then echo 'makefile is the wrong size'
fi
echo extracting memalloc.h
cat > memalloc.h <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
struct stackmark {
struct stack_block *stackp;
char *stacknxt;
int stacknleft;
};
extern char *stacknxt;
extern int stacknleft;
extern int sstrnleft;
extern int herefd;
#ifdef __STDC__
pointer ckmalloc(int);
pointer ckrealloc(pointer, int);
void free(pointer); /* defined in C library */
char *savestr(char *);
pointer stalloc(int);
void stunalloc(pointer);
void setstackmark(struct stackmark *);
void popstackmark(struct stackmark *);
void growstackblock(void);
void grabstackblock(int);
char *growstackstr(void);
char *makestrspace(void);
void ungrabstackstr(char *, char *);
#else
pointer ckmalloc();
pointer ckrealloc();
void free(); /* defined in C library */
char *savestr();
pointer stalloc();
void stunalloc();
void setstackmark();
void popstackmark();
void growstackblock();
void grabstackblock();
char *growstackstr();
char *makestrspace();
void ungrabstackstr();
#endif
#define stackblock() stacknxt
#define stackblocksize() stacknleft
#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
#define CHECKSTRSPACE(n, p) if (sstrnleft < n) p = makestrspace(); else
#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
#define STUNPUTC(p) (++sstrnleft, --p)
#define STTOPC(p) p[-1]
#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
#define ckfree(p) free((pointer)(p))
EOF
if test `wc -c < memalloc.h` -ne 1787
then echo 'memalloc.h is the wrong size'
fi
echo extracting memalloc.c
cat > memalloc.c <<\EOF
/*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
#include "shell.h"
#include "output.h"
#include "memalloc.h"
#include "error.h"
#include "machdep.h"
#include "mystring.h"
/*
* Like malloc, but returns an error when out of space.
*/
pointer
ckmalloc(nbytes) {
register pointer p;
pointer malloc();
if ((p = malloc(nbytes)) == NULL)
error("Out of space");
return p;
}
/*
* Same for realloc.
*/
pointer
ckrealloc(p, nbytes)
register pointer p;
{
pointer realloc();
if ((p = realloc(p, nbytes)) == NULL)
error("Out of space");
return p;
}
/*
* Make a copy of a string in safe storage.
*/
char *
savestr(s)
char *s;
{
register char *p;
p = ckmalloc(strlen(s) + 1);
scopy(s, p);
return p;
}
/*
* Parse trees for commands are allocated in lifo order, so we use a stack
* to make this more efficient, and also to avoid all sorts of exception
* handling code to handle interrupts in the middle of a parse.
*
* The size 504 was chosen because the Ultrix malloc handles that size
* well.
*/
#define MINSIZE 504 /* minimum size of a block */
struct stack_block {
struct stack_block *prev;
char space[MINSIZE];
};
struct stack_block stackbase;
struct stack_block *stackp = &stackbase;
char *stacknxt = stackbase.space;
int stacknleft = MINSIZE;
int sstrnleft;
int herefd = -1;
pointer
stalloc(nbytes) {
register char *p;
nbytes = ALIGN(nbytes);
if (nbytes > stacknleft) {
int blocksize;
struct stack_block *sp;
blocksize = nbytes;
if (blocksize < MINSIZE)
blocksize = MINSIZE;
INTOFF;
sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
sp->prev = stackp;
stacknxt = sp->space;
stacknleft = blocksize;
stackp = sp;
INTON;
}
p = stacknxt;
stacknxt += nbytes;
stacknleft -= nbytes;
return p;
}
void
stunalloc(p)
pointer p;
{
if (p == NULL) { /*DEBUG */
write(2, "stunalloc\n", 10);
abort();
}
stacknleft += stacknxt - (char *)p;
stacknxt = p;
}
void
setstackmark(mark)
struct stackmark *mark;
{
mark->stackp = stackp;
mark->stacknxt = stacknxt;
mark->stacknleft = stacknleft;
}
void
popstackmark(mark)
struct stackmark *mark;
{
struct stack_block *sp;
INTOFF;
while (stackp != mark->stackp) {
sp = stackp;
stackp = sp->prev;
ckfree(sp);
}
stacknxt = mark->stacknxt;
stacknleft = mark->stacknleft;
INTON;
}
/*
* When the parser reads in a string, it wants to stick the string on the
* stack and only adjust the stack pointer when it knows how big the
* string is. Stackblock (defined in stack.h) returns a pointer to a block
* of space on top of the stack and stackblocklen returns the length of
* this block. Growstackblock will grow this space by at least one byte,
* possibly moving it (like realloc). Grabstackblock actually allocates the
* part of the block that has been used.
*/
void
growstackblock() {
char *p;
int newlen = stacknleft * 2 + 100;
char *oldspace = stacknxt;
int oldlen = stacknleft;
struct stack_block *sp;
if (stacknxt == stackp->space && stackp != &stackbase) {
INTOFF;
sp = stackp;
stackp = sp->prev;
sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
sp->prev = stackp;
stackp = sp;
stacknxt = sp->space;
stacknleft = newlen;
INTON;
} else {
p = stalloc(newlen);
bcopy(oldspace, p, oldlen);
stacknxt = p; /* free the space */
stacknleft += newlen; /* we just allocated */
}
}
void
grabstackblock(len) {
len = ALIGN(len);
stacknxt += len;
stacknleft -= len;
}
/*
* The following routines are somewhat easier to use that the above.
* The user declares a variable of type STACKSTR, which may be declared
* to be a register. The macro STARTSTACKSTR initializes things. Then
* the user uses the macro STPUTC to add characters to the string. In
* effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
* grown as necessary. When the user is done, she can just leave the
* string there and refer to it using stackblock(). Or she can allocate
* the space for it using grabstackstr(). If it is necessary to allow
* someone else to use the stack temporarily and then continue to grow
* the string, the user should use grabstack to allocate the space, and
* then call ungrabstr(p) to return to the previous mode of operation.
*
* USTPUTC is like STPUTC except that it doesn't check for overflow.
* CHECKSTACKSPACE can be called before USTPUTC to ensure that there
* is space for at least one character.
*/
char *
growstackstr() {
int len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
xwrite(herefd, stackblock(), len);
sstrnleft = len - 1;
return stackblock();
}
growstackblock();
sstrnleft = stackblocksize() - len - 1;
return stackblock() + len;
}
/*
* Called from CHECKSTRSPACE.
*/
char *
makestrspace() {
int len = stackblocksize() - sstrnleft;
growstackblock();
sstrnleft = stackblocksize() - len;
return stackblock() + len;
}
void
ungrabstackstr(s, p)
char *s;
char *p;
{
stacknleft += stacknxt - s;
stacknxt = s;
sstrnleft = stacknleft - (p - s);
}
EOF
if test `wc -c < memalloc.c` -ne 5694
then echo 'memalloc.c is the wrong size'
fi
echo extracting miscbltin.c
cat > miscbltin.c <<\EOF
/*
* Miscelaneous builtins.
*
* Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
* This file is part of ash, which is distributed under the terms specified
* by the Ash General Public License. See the file named LICENSE.
*/
#include "shell.h"
#include "options.h"
#include "var.h"
#include "output.h"
#include "memalloc.h"
#include "error.h"
#include "mystring.h"
#undef eflag
extern char **argptr; /* argument list for builtin command */
/*
* The read builtin. The -e option causes backslashes to escape the
* following character.
*
* This uses unbuffered input, which may be avoidable in some cases.
*/
readcmd(argc, argv) char **argv; {
char **ap;
int backslash;
char c;
int eflag;
char *prompt;
char *ifs;
char *p;
int startword;
int status;
int i;
eflag = 0;
prompt = NULL;
while ((i = nextopt("ep:")) != '\0') {
if (i == 'p')
prompt = optarg;
else
eflag = 1;
}
if (prompt && isatty(0)) {
out2str(prompt);
flushall();
}
if ((ap = argptr) == NULL)
error("arg count");
if ((ifs = bltinlookup("IFS", 1)) == NULL)
ifs = nullstr;
status = 0;
startword = 1;
backslash = 0;
STARTSTACKSTR(p);
for (;;) {
if (read(0, &c, 1) != 1) {
status = 1;
break;
}
if (c == '\0')
continue;
if (backslash) {
backslash = 0;
if (c != '\n')
STPUTC(c, p);
continue;
}
if (eflag && c == '\\') {
backslash++;
continue;
}
if (c == '\n')
break;
if (startword && *ifs == ' ' && strchr(ifs, c)) {
continue;
}
startword = 0;
if (backslash && c == '\\') {
if (read(0, &c, 1) != 1) {
status = 1;
break;
}
STPUTC(c, p);
} else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
STACKSTRNUL(p);
setvar(*ap, stackblock(), 0);
ap++;
startword = 1;
STARTSTACKSTR(p);
} else {
STPUTC(c, p);
}
}
STACKSTRNUL(p);
setvar(*ap, stackblock(), 0);
while (*++ap != NULL)
setvar(*ap, nullstr, 0);
return status;
}
umaskcmd(argc, argv) char **argv; {
int mask;
char *p;
int i;
if ((p = argv[1]) == NULL) {
INTOFF;
mask = umask(0);
umask(mask);
INTON;
out1fmt("%.4o\n", mask); /* %#o might be better */
} else {
mask = 0;
do {
if ((unsigned)(i = *p - '0') >= 8)
error("Illegal number: %s", argv[1]);
mask = (mask << 3) + i;
} while (*++p != '\0');
umask(mask);
}
return 0;
}
EOF
if test `wc -c < miscbltin.c` -ne 2633
then echo 'miscbltin.c is the wrong size'
fi
echo extracting mkbuiltins
cat > mkbuiltins <<\EOF
# Copyright (C) 1989 by Kenneth Almquist. All rights reserved.
# This file is part of ash, which is distributed under the terms specified
# by the Ash General Public License. See the file named LICENSE.
temp=/tmp/ka$$
havejobs=0
if grep '^#define JOBS[ ]*1' shell.h > /dev/null
then havejobs=1
fi
exec > builtins.c
cat <<\!
/*
* This file was generated by the mkbuiltins program.
*/
#include "shell.h"
#include "builtins.h"
!
awk '/^[^#]/ {if('$havejobs' || $2 != "-j") print $0}' builtins |
sed 's/-j//' > $temp
awk '{ printf "int %s();\n", $1}' $temp
echo '
int (*const builtinfunc[])() = {'
awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp
echo '};
const struct builtincmd builtincmd[] = {'
awk '{ for (i = 2 ; i <= NF ; i++) {
printf "\t\"%s\", %d,\n", $i, NR-1
}}' $temp
echo ' NULL, 0
};'
exec > builtins.h
cat <<\!
/*
* This file was generated by the mkbuiltins program.
*/
!
tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp |
awk '{ printf "#define %s %d\n", $1, NR-1}'
echo '
struct builtincmd {
char *name;
int code;
};
extern int (*const builtinfunc[])();
extern const struct builtincmd builtincmd[];'
rm -f $temp
EOF
if test `wc -c < mkbuiltins` -ne 1168
then echo 'mkbuiltins is the wrong size'
fi
echo Archive 4 unpacked
exit