blob: bb930f55fbb31555d2e9d277e4c773f3f6299282 [file] [log] [blame]
/* vi: set sw=4 ts=4: */
/*
* ash shell port for busybox
*
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized.
*
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*
* Original BSD copyright notice is retained at the end of this file.
*/
/*
* rewrite arith.y to micro stack based cryptic algorithm by
* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
*
* Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
* dynamic variables.
*
* Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
* used in busybox and size optimizations,
* rewrote arith (see notes to this), added locale support,
* rewrote dynamic variables.
*
*/
/*
* The follow should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
* define SYSV if you are running under System V.
* define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
* define DEBUG=2 to compile in and turn on debugging.
*
* When debugging is on, debugging info will be written to ./trace and
* a quit signal will generate a core dump.
*/
#define DEBUG 0
#define IFS_BROKEN
#define PROFILE 0
#if ENABLE_ASH_JOB_CONTROL
#define JOBS 1
#else
#define JOBS 0
#endif
#if DEBUG
#define _GNU_SOURCE
#endif
#include "busybox.h" /* for struct bb_applet */
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
#if JOBS || ENABLE_ASH_READ_NCHARS
#include <termios.h>
#endif
extern char **environ;
#if defined(__uClinux__)
#error "Do not even bother, ash will not run on uClinux"
#endif
/* ============ Misc helpers */
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
/* C99 say: "char" declaration may be signed or unsigned default */
#define signed_char2int(sc) ((int)((signed char)sc))
/* ============ Shell options */
static const char *const optletters_optnames[] = {
"e" "errexit",
"f" "noglob",
"I" "ignoreeof",
"i" "interactive",
"m" "monitor",
"n" "noexec",
"s" "stdin",
"x" "xtrace",
"v" "verbose",
"C" "noclobber",
"a" "allexport",
"b" "notify",
"u" "nounset",
"\0" "vi"
#if DEBUG
,"\0" "nolog"
,"\0" "debug"
#endif
};
#define optletters(n) optletters_optnames[(n)][0]
#define optnames(n) (&optletters_optnames[(n)][1])
enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
static char optlist[NOPTS] ALIGN1;
#define eflag optlist[0]
#define fflag optlist[1]
#define Iflag optlist[2]
#define iflag optlist[3]
#define mflag optlist[4]
#define nflag optlist[5]
#define sflag optlist[6]
#define xflag optlist[7]
#define vflag optlist[8]
#define Cflag optlist[9]
#define aflag optlist[10]
#define bflag optlist[11]
#define uflag optlist[12]
#define viflag optlist[13]
#if DEBUG
#define nolog optlist[14]
#define debug optlist[15]
#endif
/* ============ Misc data */
static char nullstr[1] ALIGN1; /* zero length string */
static const char homestr[] ALIGN1 = "HOME";
static const char snlfmt[] ALIGN1 = "%s\n";
static const char illnum[] ALIGN1 = "Illegal number: %s";
static char *minusc; /* argument to -c option */
/* pid of main shell */
static int rootpid;
/* shell level: 0 for the main shell, 1 for its children, and so on */
static int shlvl;
#define rootshell (!shlvl)
/* trap handler commands */
static char *trap[NSIG];
static smallint isloginsh;
/* current value of signal */
static char sigmode[NSIG - 1];
/* indicates specified signal received */
static char gotsig[NSIG - 1];
static char *arg0; /* value of $0 */
/* ============ Interrupts / exceptions */
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
* contains a code identifying the exception. To implement nested
* exception handlers, the user should save the value of handler on entry
* to an inner scope, set handler to point to a jmploc structure for the
* inner scope, and restore handler on exit from the scope.
*/
struct jmploc {
jmp_buf loc;
};
static struct jmploc *exception_handler;
static int exception;
/* exceptions */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXSHELLPROC 2 /* execute a shell procedure */
#define EXEXEC 3 /* command execution failed */
#define EXEXIT 4 /* exit the shell */
#define EXSIG 5 /* trapped signal in wait(1) */
static volatile int suppressint;
static volatile sig_atomic_t intpending;
/* do we generate EXSIG events */
static int exsig;
/* last pending signal */
static volatile sig_atomic_t pendingsig;
/*
* Sigmode records the current value of the signal handlers for the various
* modes. A value of zero means that the current handler is not known.
* S_HARD_IGN indicates that the signal was ignored on entry to the shell,
*/
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
#define S_RESET 5 /* temporary - to reset a hard ignored sig */
/*
* These macros allow the user to suspend the handling of interrupt signals
* over a period of time. This is similar to SIGHOLD to or sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
#define INT_OFF \
do { \
suppressint++; \
xbarrier(); \
} while (0)
/*
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception".
*/
static void raise_exception(int) ATTRIBUTE_NORETURN;
static void
raise_exception(int e)
{
#if DEBUG
if (exception_handler == NULL)
abort();
#endif
INT_OFF;
exception = e;
longjmp(exception_handler->loc, 1);
}
/*
* Called from trap.c when a SIGINT is received. (If the user specifies
* that SIGINT is to be trapped or ignored using the trap builtin, then
* this routine is not called.) Suppressint is nonzero when interrupts
* are held using the INT_OFF macro. (The test for iflag is just
* defensive programming.)
*/
static void raise_interrupt(void) ATTRIBUTE_NORETURN;
static void
raise_interrupt(void)
{
int i;
sigset_t mask;
intpending = 0;
/* Signal is not automatically re-enabled after it is raised,
* do it ourself */
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, 0);
/* pendingsig = 0; - now done in onsig() */
i = EXSIG;
if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
if (!(rootshell && iflag)) {
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
i = EXINT;
}
raise_exception(i);
/* NOTREACHED */
}
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
static void
int_on(void)
{
if (--suppressint == 0 && intpending) {
raise_interrupt();
}
}
#define INT_ON int_on()
static void
force_int_on(void)
{
suppressint = 0;
if (intpending)
raise_interrupt();
}
#define FORCE_INT_ON force_int_on()
#else
#define INT_ON \
do { \
xbarrier(); \
if (--suppressint == 0 && intpending) \
raise_interrupt(); \
} while (0)
#define FORCE_INT_ON \
do { \
xbarrier(); \
suppressint = 0; \
if (intpending) \
raise_interrupt(); \
} while (0)
#endif /* ASH_OPTIMIZE_FOR_SIZE */
#define SAVE_INT(v) ((v) = suppressint)
#define RESTORE_INT(v) \
do { \
xbarrier(); \
suppressint = (v); \
if (suppressint == 0 && intpending) \
raise_interrupt(); \
} while (0)
#define EXSIGON \
do { \
exsig++; \
xbarrier(); \
if (pendingsig) \
raise_exception(EXSIG); \
} while (0)
/* EXSIG is turned off by evalbltin(). */
/*
* Ignore a signal. Only one usage site - in forkchild()
*/
static void
ignoresig(int signo)
{
if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
signal(signo, SIG_IGN);
}
sigmode[signo - 1] = S_HARD_IGN;
}
/*
* Signal handler. Only one usage site - in setsignal()
*/
static void
onsig(int signo)
{
gotsig[signo - 1] = 1;
pendingsig = signo;
if (exsig || (signo == SIGINT && !trap[SIGINT])) {
if (!suppressint) {
pendingsig = 0;
raise_interrupt();
}
intpending = 1;
}
}
/* ============ Stdout/stderr output */
static void
outstr(const char *p, FILE *file)
{
INT_OFF;
fputs(p, file);
INT_ON;
}
static void
flush_stdout_stderr(void)
{
INT_OFF;
fflush(stdout);
fflush(stderr);
INT_ON;
}
static void
flush_stderr(void)
{
INT_OFF;
fflush(stderr);
INT_ON;
}
static void
outcslow(int c, FILE *dest)
{
INT_OFF;
putc(c, dest);
fflush(dest);
INT_ON;
}
static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
static int
out1fmt(const char *fmt, ...)
{
va_list ap;
int r;
INT_OFF;
va_start(ap, fmt);
r = vprintf(fmt, ap);
va_end(ap);
INT_ON;
return r;
}
static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
static int
fmtstr(char *outbuf, size_t length, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
INT_OFF;
ret = vsnprintf(outbuf, length, fmt, ap);
va_end(ap);
INT_ON;
return ret;
}
static void
out1str(const char *p)
{
outstr(p, stdout);
}
static void
out2str(const char *p)
{
outstr(p, stderr);
flush_stderr();
}
/* ============ Parser structures */
/* control characters in argument strings */
#define CTLESC '\201' /* escape next character */
#define CTLVAR '\202' /* variable defn */
#define CTLENDVAR '\203'
#define CTLBACKQ '\204'
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
/* CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI '\206' /* arithmetic expression */
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
/* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f /* type of variable substitution */
#define VSNUL 0x10 /* colon--treat the empty string as unset */
#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
/* values of VSTYPE field */
#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
#define VSMINUS 0x2 /* ${var-text} */
#define VSPLUS 0x3 /* ${var+text} */
#define VSQUESTION 0x4 /* ${var?message} */
#define VSASSIGN 0x5 /* ${var=text} */
#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
#define VSTRIMLEFT 0x8 /* ${var#pattern} */
#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
#define VSLENGTH 0xa /* ${#var} */
static const char dolatstr[] ALIGN1 = {
CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
};
#define NCMD 0
#define NPIPE 1
#define NREDIR 2
#define NBACKGND 3
#define NSUBSHELL 4
#define NAND 5
#define NOR 6
#define NSEMI 7
#define NIF 8
#define NWHILE 9
#define NUNTIL 10
#define NFOR 11
#define NCASE 12
#define NCLIST 13
#define NDEFUN 14
#define NARG 15
#define NTO 16
#define NCLOBBER 17
#define NFROM 18
#define NFROMTO 19
#define NAPPEND 20
#define NTOFD 21
#define NFROMFD 22
#define NHERE 23
#define NXHERE 24
#define NNOT 25
union node;
struct ncmd {
int type;
union node *assign;
union node *args;
union node *redirect;
};
struct npipe {
int type;
int backgnd;
struct nodelist *cmdlist;
};
struct nredir {
int type;
union node *n;
union node *redirect;
};
struct nbinary {
int type;
union node *ch1;
union node *ch2;
};
struct nif {
int type;
union node *test;
union node *ifpart;
union node *elsepart;
};
struct nfor {
int type;
union node *args;
union node *body;
char *var;
};
struct ncase {
int type;
union node *expr;
union node *cases;
};
struct nclist {
int type;
union node *next;
union node *pattern;
union node *body;
};
struct narg {
int type;
union node *next;
char *text;
struct nodelist *backquote;
};
struct nfile {
int type;
union node *next;
int fd;
union node *fname;
char *expfname;
};
struct ndup {
int type;
union node *next;
int fd;
int dupfd;
union node *vname;
};
struct nhere {
int type;
union node *next;
int fd;
union node *doc;
};
struct nnot {
int type;
union node *com;
};
union node {
int type;
struct ncmd ncmd;
struct npipe npipe;
struct nredir nredir;
struct nbinary nbinary;
struct nif nif;
struct nfor nfor;
struct ncase ncase;
struct nclist nclist;
struct narg narg;
struct nfile nfile;
struct ndup ndup;
struct nhere nhere;
struct nnot nnot;
};
struct nodelist {
struct nodelist *next;
union node *n;
};
struct funcnode {
int count;
union node n;
};
/*
* Free a parse tree.
*/
static void
freefunc(struct funcnode *f)
{
if (f && --f->count < 0)
free(f);
}
/* ============ Debugging output */
#if DEBUG
static FILE *tracefile;
static void
trace_printf(const char *fmt, ...)
{
va_list va;
if (debug != 1)
return;
va_start(va, fmt);
vfprintf(tracefile, fmt, va);
va_end(va);
}
static void
trace_vprintf(const char *fmt, va_list va)
{
if (debug != 1)
return;
vfprintf(tracefile, fmt, va);
}
static void
trace_puts(const char *s)
{
if (debug != 1)
return;
fputs(s, tracefile);
}
static void
trace_puts_quoted(char *s)
{
char *p;
char c;
if (debug != 1)
return;
putc('"', tracefile);
for (p = s; *p; p++) {
switch (*p) {
case '\n': c = 'n'; goto backslash;
case '\t': c = 't'; goto backslash;
case '\r': c = 'r'; goto backslash;
case '"': c = '"'; goto backslash;
case '\\': c = '\\'; goto backslash;
case CTLESC: c = 'e'; goto backslash;
case CTLVAR: c = 'v'; goto backslash;
case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
case CTLBACKQ: c = 'q'; goto backslash;
case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
backslash:
putc('\\', tracefile);
putc(c, tracefile);
break;
default:
if (*p >= ' ' && *p <= '~')
putc(*p, tracefile);
else {
putc('\\', tracefile);
putc(*p >> 6 & 03, tracefile);
putc(*p >> 3 & 07, tracefile);
putc(*p & 07, tracefile);
}
break;
}
}
putc('"', tracefile);
}
static void
trace_puts_args(char **ap)
{
if (debug != 1)
return;
if (!*ap)
return;
while (1) {
trace_puts_quoted(*ap);
if (!*++ap) {
putc('\n', tracefile);
break;
}
putc(' ', tracefile);
}
}
static void
opentrace(void)
{
char s[100];
#ifdef O_APPEND
int flags;
#endif
if (debug != 1) {
if (tracefile)
fflush(tracefile);
/* leave open because libedit might be using it */
return;
}
strcpy(s, "./trace");
if (tracefile) {
if (!freopen(s, "a", tracefile)) {
fprintf(stderr, "Can't re-open %s\n", s);
debug = 0;
return;
}
} else {
tracefile = fopen(s, "a");
if (tracefile == NULL) {
fprintf(stderr, "Can't open %s\n", s);
debug = 0;
return;
}
}
#ifdef O_APPEND
flags = fcntl(fileno(tracefile), F_GETFL);
if (flags >= 0)
fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
#endif
setlinebuf(tracefile);
fputs("\nTracing started.\n", tracefile);
}
static void
indent(int amount, char *pfx, FILE *fp)
{
int i;
for (i = 0; i < amount; i++) {
if (pfx && i == amount - 1)
fputs(pfx, fp);
putc('\t', fp);
}
}
/* little circular references here... */
static void shtree(union node *n, int ind, char *pfx, FILE *fp);
static void
sharg(union node *arg, FILE *fp)
{
char *p;
struct nodelist *bqlist;
int subtype;
if (arg->type != NARG) {
out1fmt("<node type %d>\n", arg->type);
abort();
}
bqlist = arg->narg.backquote;
for (p = arg->narg.text; *p; p++) {
switch (*p) {
case CTLESC:
putc(*++p, fp);
break;
case CTLVAR:
putc('$', fp);
putc('{', fp);
subtype = *++p;
if (subtype == VSLENGTH)
putc('#', fp);
while (*p != '=')
putc(*p++, fp);
if (subtype & VSNUL)
putc(':', fp);
switch (subtype & VSTYPE) {
case VSNORMAL:
putc('}', fp);
break;
case VSMINUS:
putc('-', fp);
break;
case VSPLUS:
putc('+', fp);
break;
case VSQUESTION:
putc('?', fp);
break;
case VSASSIGN:
putc('=', fp);
break;
case VSTRIMLEFT:
putc('#', fp);
break;
case VSTRIMLEFTMAX:
putc('#', fp);
putc('#', fp);
break;
case VSTRIMRIGHT:
putc('%', fp);
break;
case VSTRIMRIGHTMAX:
putc('%', fp);
putc('%', fp);
break;
case VSLENGTH:
break;
default:
out1fmt("<subtype %d>", subtype);
}
break;
case CTLENDVAR:
putc('}', fp);
break;
case CTLBACKQ:
case CTLBACKQ|CTLQUOTE:
putc('$', fp);
putc('(', fp);
shtree(bqlist->n, -1, NULL, fp);
putc(')', fp);
break;
default:
putc(*p, fp);
break;
}
}
}
static void
shcmd(union node *cmd, FILE *fp)
{
union node *np;
int first;
const char *s;
int dftfd;
first = 1;
for (np = cmd->ncmd.args; np; np = np->narg.next) {
if (!first)
putc(' ', fp);
sharg(np, fp);
first = 0;
}
for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
if (!first)
putc(' ', fp);
dftfd = 0;
switch (np->nfile.type) {
case NTO: s = ">>"+1; dftfd = 1; break;
case NCLOBBER: s = ">|"; dftfd = 1; break;
case NAPPEND: s = ">>"; dftfd = 1; break;
case NTOFD: s = ">&"; dftfd = 1; break;
case NFROM: s = "<"; break;
case NFROMFD: s = "<&"; break;
case NFROMTO: s = "<>"; break;
default: s = "*error*"; break;
}
if (np->nfile.fd != dftfd)
fprintf(fp, "%d", np->nfile.fd);
fputs(s, fp);
if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
fprintf(fp, "%d", np->ndup.dupfd);
} else {
sharg(np->nfile.fname, fp);
}
first = 0;
}
}
static void
shtree(union node *n, int ind, char *pfx, FILE *fp)
{
struct nodelist *lp;
const char *s;
if (n == NULL)
return;
indent(ind, pfx, fp);
switch (n->type) {
case NSEMI:
s = "; ";
goto binop;
case NAND:
s = " && ";
goto binop;
case NOR:
s = " || ";
binop:
shtree(n->nbinary.ch1, ind, NULL, fp);
/* if (ind < 0) */
fputs(s, fp);
shtree(n->nbinary.ch2, ind, NULL, fp);
break;
case NCMD:
shcmd(n, fp);
if (ind >= 0)
putc('\n', fp);
break;
case NPIPE:
for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
shcmd(lp->n, fp);
if (lp->next)
fputs(" | ", fp);
}
if (n->npipe.backgnd)
fputs(" &", fp);
if (ind >= 0)
putc('\n', fp);
break;
default:
fprintf(fp, "<node type %d>", n->type);
if (ind >= 0)
putc('\n', fp);
break;
}
}
static void
showtree(union node *n)
{
trace_puts("showtree called\n");
shtree(n, 1, NULL, stdout);
}
#define TRACE(param) trace_printf param
#define TRACEV(param) trace_vprintf param
#else
#define TRACE(param)
#define TRACEV(param)
#endif /* DEBUG */
/* ============ Parser data */
/*
* ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
*/
struct strlist {
struct strlist *next;
char *text;
};
#if ENABLE_ASH_ALIAS
struct alias;
#endif
struct strpush {
struct strpush *prev; /* preceding string on stack */
char *prevstring;
int prevnleft;
#if ENABLE_ASH_ALIAS
struct alias *ap; /* if push was associated with an alias */
#endif
char *string; /* remember the string since it may change */
};
struct parsefile {
struct parsefile *prev; /* preceding file on stack */
int linno; /* current line */
int fd; /* file descriptor (or -1 if string) */
int nleft; /* number of chars left in this line */
int lleft; /* number of chars left in this buffer */
char *nextc; /* next char in buffer */
char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */
};
static struct parsefile basepf; /* top level input file */
static struct parsefile *parsefile = &basepf; /* current input file */
static int startlinno; /* line # where last token started */
static char *commandname; /* currently executing command */
static struct strlist *cmdenviron; /* environment for builtin command */
static int exitstatus; /* exit status of last command */
/* ============ Message printing */
static void
ash_vmsg(const char *msg, va_list ap)
{
fprintf(stderr, "%s: ", arg0);
if (commandname) {
if (strcmp(arg0, commandname))
fprintf(stderr, "%s: ", commandname);
if (!iflag || parsefile->fd)
fprintf(stderr, "line %d: ", startlinno);
}
vfprintf(stderr, msg, ap);
outcslow('\n', stderr);
}
/*
* Exverror is called to raise the error exception. If the second argument
* is not NULL then error prints an error message using printf style
* formatting. It then raises the error exception.
*/
static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
static void
ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
{
#if DEBUG
if (msg) {
TRACE(("ash_vmsg_and_raise(%d, \"", cond));
TRACEV((msg, ap));
TRACE(("\") pid=%d\n", getpid()));
} else
TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
if (msg)
#endif
ash_vmsg(msg, ap);
flush_stdout_stderr();
raise_exception(cond);
/* NOTREACHED */
}
static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
static void
ash_msg_and_raise_error(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
ash_vmsg_and_raise(EXERROR, msg, ap);
/* NOTREACHED */
va_end(ap);
}
static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
static void
ash_msg_and_raise(int cond, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
ash_vmsg_and_raise(cond, msg, ap);
/* NOTREACHED */
va_end(ap);
}
/*
* error/warning routines for external builtins
*/
static void
ash_msg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
ash_vmsg(fmt, ap);
va_end(ap);
}
/*
* Return a string describing an error. The returned string may be a
* pointer to a static buffer that will be overwritten on the next call.
* Action describes the operation that got the error.
*/
static const char *
errmsg(int e, const char *em)
{
if (e == ENOENT || e == ENOTDIR) {
return em;
}
return strerror(e);
}
/* ============ Memory allocation */
/*
* It appears that grabstackstr() will barf with such alignments
* because stalloc() will return a string allocated in a new stackblock.
*/
#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
enum {
/* Most machines require the value returned from malloc to be aligned
* in some way. The following macro will get this right
* on many machines. */
SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
/* Minimum size of a block */
MINSIZE = SHELL_ALIGN(504),
};
struct stack_block {
struct stack_block *prev;
char space[MINSIZE];
};
struct stackmark {
struct stack_block *stackp;
char *stacknxt;
size_t stacknleft;
struct stackmark *marknext;
};
static struct stack_block stackbase;
static struct stack_block *stackp = &stackbase;
static struct stackmark *markp;
static char *stacknxt = stackbase.space;
static size_t stacknleft = MINSIZE;
static char *sstrend = stackbase.space + MINSIZE;
static int herefd = -1;
#define stackblock() ((void *)stacknxt)
#define stackblocksize() stacknleft
static void *
ckrealloc(void * p, size_t nbytes)
{
p = realloc(p, nbytes);
if (!p)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
return p;
}
static void *
ckmalloc(size_t nbytes)
{
return ckrealloc(NULL, nbytes);
}
/*
* Make a copy of a string in safe storage.
*/
static char *
ckstrdup(const char *s)
{
char *p = strdup(s);
if (!p)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
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.
*/
static void *
stalloc(size_t nbytes)
{
char *p;
size_t aligned;
aligned = SHELL_ALIGN(nbytes);
if (aligned > stacknleft) {
size_t len;
size_t blocksize;
struct stack_block *sp;
blocksize = aligned;
if (blocksize < MINSIZE)
blocksize = MINSIZE;
len = sizeof(struct stack_block) - MINSIZE + blocksize;
if (len < blocksize)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
INT_OFF;
sp = ckmalloc(len);
sp->prev = stackp;
stacknxt = sp->space;
stacknleft = blocksize;
sstrend = stacknxt + blocksize;
stackp = sp;
INT_ON;
}
p = stacknxt;
stacknxt += aligned;
stacknleft -= aligned;
return p;
}
static void
stunalloc(void *p)
{
#if DEBUG
if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
write(2, "stunalloc\n", 10);
abort();
}
#endif
stacknleft += stacknxt - (char *)p;
stacknxt = p;
}
/*
* Like strdup but works with the ash stack.
*/
static char *
ststrdup(const char *p)
{
size_t len = strlen(p) + 1;
return memcpy(stalloc(len), p, len);
}
static void
setstackmark(struct stackmark *mark)
{
mark->stackp = stackp;
mark->stacknxt = stacknxt;
mark->stacknleft = stacknleft;
mark->marknext = markp;
markp = mark;
}
static void
popstackmark(struct stackmark *mark)
{
struct stack_block *sp;
if (!mark->stackp)
return;
INT_OFF;
markp = mark->marknext;
while (stackp != mark->stackp) {
sp = stackp;
stackp = sp->prev;
free(sp);
}
stacknxt = mark->stacknxt;
stacknleft = mark->stacknleft;
sstrend = mark->stacknxt + mark->stacknleft;
INT_ON;
}
/*
* 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.
*/
static void
growstackblock(void)
{
size_t newlen;
newlen = stacknleft * 2;
if (newlen < stacknleft)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
if (newlen < 128)
newlen += 128;
if (stacknxt == stackp->space && stackp != &stackbase) {
struct stack_block *oldstackp;
struct stackmark *xmark;
struct stack_block *sp;
struct stack_block *prevstackp;
size_t grosslen;
INT_OFF;
oldstackp = stackp;
sp = stackp;
prevstackp = sp->prev;
grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
sp = ckrealloc(sp, grosslen);
sp->prev = prevstackp;
stackp = sp;
stacknxt = sp->space;
stacknleft = newlen;
sstrend = sp->space + newlen;
/*
* Stack marks pointing to the start of the old block
* must be relocated to point to the new block
*/
xmark = markp;
while (xmark != NULL && xmark->stackp == oldstackp) {
xmark->stackp = stackp;
xmark->stacknxt = stacknxt;
xmark->stacknleft = stacknleft;
xmark = xmark->marknext;
}
INT_ON;
} else {
char *oldspace = stacknxt;
int oldlen = stacknleft;
char *p = stalloc(newlen);
/* free the space we just allocated */
stacknxt = memcpy(p, oldspace, oldlen);
stacknleft += newlen;
}
}
static void
grabstackblock(size_t len)
{
len = SHELL_ALIGN(len);
stacknxt += len;
stacknleft -= len;
}
/*
* The following routines are somewhat easier to use than 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.
*/
static void *
growstackstr(void)
{
size_t len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
full_write(herefd, stackblock(), len);
return stackblock();
}
growstackblock();
return stackblock() + len;
}
/*
* Called from CHECKSTRSPACE.
*/
static char *
makestrspace(size_t newlen, char *p)
{
size_t len = p - stacknxt;
size_t size = stackblocksize();
for (;;) {
size_t nleft;
size = stackblocksize();
nleft = size - len;
if (nleft >= newlen)
break;
growstackblock();
}
return stackblock() + len;
}
static char *
stack_nputstr(const char *s, size_t n, char *p)
{
p = makestrspace(n, p);
p = memcpy(p, s, n) + n;
return p;
}
static char *
stack_putstr(const char *s, char *p)
{
return stack_nputstr(s, strlen(s), p);
}
static char *
_STPUTC(int c, char *p)
{
if (p == sstrend)
p = growstackstr();
*p++ = c;
return p;
}
#define STARTSTACKSTR(p) ((p) = stackblock())
#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
#define CHECKSTRSPACE(n, p) \
do { \
char *q = (p); \
size_t l = (n); \
size_t m = sstrend - q; \
if (l > m) \
(p) = makestrspace(l, q); \
} while (0)
#define USTPUTC(c, p) (*p++ = (c))
#define STACKSTRNUL(p) \
do { \
if ((p) == sstrend) \
p = growstackstr(); \
*p = '\0'; \
} while (0)
#define STUNPUTC(p) (--p)
#define STTOPC(p) (p[-1])
#define STADJUST(amount, p) (p += (amount))
#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
#define ungrabstackstr(s, p) stunalloc((s))
#define stackstrend() ((void *)sstrend)
/* ============ String helpers */
/*
* prefix -- see if pfx is a prefix of string.
*/
static char *
prefix(const char *string, const char *pfx)
{
while (*pfx) {
if (*pfx++ != *string++)
return 0;
}
return (char *) string;
}
/*
* Check for a valid number. This should be elsewhere.
*/
static int
is_number(const char *p)
{
do {
if (!isdigit(*p))
return 0;
} while (*++p != '\0');
return 1;
}
/*
* Convert a string of digits to an integer, printing an error message on
* failure.
*/
static int
number(const char *s)
{
if (!is_number(s))
ash_msg_and_raise_error(illnum, s);
return atoi(s);
}
/*
* Produce a possibly single quoted string suitable as input to the shell.
* The return string is allocated on the stack.
*/
static char *
single_quote(const char *s)
{
char *p;
STARTSTACKSTR(p);
do {
char *q;
size_t len;
len = strchrnul(s, '\'') - s;
q = p = makestrspace(len + 3, p);
*q++ = '\'';
q = memcpy(q, s, len) + len;
*q++ = '\'';
s += len;
STADJUST(q - p, p);
len = strspn(s, "'");
if (!len)
break;
q = p = makestrspace(len + 3, p);
*q++ = '"';
q = memcpy(q, s, len) + len;
*q++ = '"';
s += len;
STADJUST(q - p, p);
} while (*s);
USTPUTC(0, p);
return stackblock();
}
/* ============ nextopt */
static char **argptr; /* argument list for builtin commands */
static char *optionarg; /* set by nextopt (like getopt) */
static char *optptr; /* used by nextopt */
/*
* XXX - should get rid of. have all builtins use getopt(3). the
* library getopt must have the BSD extension static variable "optreset"
* otherwise it can't be used within the shell safely.
*
* Standard option processing (a la getopt) for builtin routines. The
* only argument that is passed to nextopt is the option string; the
* other arguments are unnecessary. It return the character, or '\0' on
* end of input.
*/
static int
nextopt(const char *optstring)
{
char *p;
const char *q;
char c;
p = optptr;
if (p == NULL || *p == '\0') {
p = *argptr;
if (p == NULL || *p != '-' || *++p == '\0')
return '\0';
argptr++;
if (LONE_DASH(p)) /* check for "--" */
return '\0';
}
c = *p++;
for (q = optstring; *q != c; ) {
if (*q == '\0')
ash_msg_and_raise_error("illegal option -%c", c);
if (*++q == ':')
q++;
}
if (*++q == ':') {
if (*p == '\0' && (p = *argptr++) == NULL)
ash_msg_and_raise_error("no arg for -%c option", c);
optionarg = p;
p = NULL;
}
optptr = p;
return c;
}
/* ============ Math support definitions */
#if ENABLE_ASH_MATH_SUPPORT_64
typedef int64_t arith_t;
#define arith_t_type long long
#else
typedef long arith_t;
#define arith_t_type long
#endif
#if ENABLE_ASH_MATH_SUPPORT
static arith_t dash_arith(const char *);
static arith_t arith(const char *expr, int *perrcode);
#endif
#if ENABLE_ASH_RANDOM_SUPPORT
static unsigned long rseed;
#ifndef DYNAMIC_VAR
#define DYNAMIC_VAR
#endif
#endif
/* ============ Shell variables */
/* flags */
#define VEXPORT 0x01 /* variable is exported */
#define VREADONLY 0x02 /* variable cannot be modified */
#define VSTRFIXED 0x04 /* variable struct is statically allocated */
#define VTEXTFIXED 0x08 /* text is statically allocated */
#define VSTACK 0x10 /* text is allocated on the stack */
#define VUNSET 0x20 /* the variable is not set */
#define VNOFUNC 0x40 /* don't call the callback function */
#define VNOSET 0x80 /* do not set variable - just readonly test */
#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
#ifdef DYNAMIC_VAR
# define VDYNAMIC 0x200 /* dynamic variable */
#else
# define VDYNAMIC 0
#endif
#ifdef IFS_BROKEN
static const char defifsvar[] ALIGN1 = "IFS= \t\n";
#define defifs (defifsvar + 4)
#else
static const char defifs[] ALIGN1 = " \t\n";
#endif
struct shparam {
int nparam; /* # of positional parameters (without $0) */
unsigned char malloc; /* if parameter list dynamically allocated */
char **p; /* parameter list */
#if ENABLE_ASH_GETOPTS
int optind; /* next parameter to be processed by getopts */
int optoff; /* used by getopts */
#endif
};
static struct shparam shellparam; /* $@ current positional parameters */
/*
* Free the list of positional parameters.
*/
static void
freeparam(volatile struct shparam *param)
{
char **ap;
if (param->malloc) {
for (ap = param->p; *ap; ap++)
free(*ap);
free(param->p);
}
}
#if ENABLE_ASH_GETOPTS
static void
getoptsreset(const char *value)
{
shellparam.optind = number(value);
shellparam.optoff = -1;
}
#endif
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
const char *text; /* name=value */
void (*func)(const char *); /* function to be called when */
/* the variable gets set/unset */
};
struct localvar {
struct localvar *next; /* next local variable in list */
struct var *vp; /* the variable that was made local */
int flags; /* saved flags */
const char *text; /* saved text */
};
/* Forward decls for varinit[] */
#if ENABLE_LOCALE_SUPPORT
static void
change_lc_all(const char *value)
{
if (value && *value != '\0')
setlocale(LC_ALL, value);
}
static void
change_lc_ctype(const char *value)
{
if (value && *value != '\0')
setlocale(LC_CTYPE, value);
}
#endif
#if ENABLE_ASH_MAIL
static void chkmail(void);
static void changemail(const char *);
#endif
static void changepath(const char *);
#if ENABLE_ASH_RANDOM_SUPPORT
static void change_random(const char *);
#endif
static struct var varinit[] = {
#ifdef IFS_BROKEN
{ NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
#else
{ NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
#endif
#if ENABLE_ASH_MAIL
{ NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
{ NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
#endif
{ NULL, VSTRFIXED|VTEXTFIXED, bb_PATH_root_path, changepath },
{ NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
{ NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
{ NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
#if ENABLE_ASH_GETOPTS
{ NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
#endif
#if ENABLE_ASH_RANDOM_SUPPORT
{ NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
#endif
#if ENABLE_LOCALE_SUPPORT
{ NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
{ NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
#endif
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
{ NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
#endif
};
#define vifs varinit[0]
#if ENABLE_ASH_MAIL
#define vmail (&vifs)[1]
#define vmpath (&vmail)[1]
#else
#define vmpath vifs
#endif
#define vpath (&vmpath)[1]
#define vps1 (&vpath)[1]
#define vps2 (&vps1)[1]
#define vps4 (&vps2)[1]
#define voptind (&vps4)[1]
#if ENABLE_ASH_GETOPTS
#define vrandom (&voptind)[1]
#else
#define vrandom (&vps4)[1]
#endif
/*
* The following macros access the values of the above variables.
* They have to skip over the name. They return the null string
* for unset variables.
*/
#define ifsval() (vifs.text + 4)
#define ifsset() ((vifs.flags & VUNSET) == 0)
#define mailval() (vmail.text + 5)
#define mpathval() (vmpath.text + 9)
#define pathval() (vpath.text + 5)
#define ps1val() (vps1.text + 4)
#define ps2val() (vps2.text + 4)
#define ps4val() (vps4.text + 4)
#define optindval() (voptind.text + 7)
#define mpathset() ((vmpath.flags & VUNSET) == 0)
/*
* The parsefile structure pointed to by the global variable parsefile
* contains information about the current file being read.
*/
struct redirtab {
struct redirtab *next;
int renamed[10];
int nullredirs;
};
static struct redirtab *redirlist;
static int nullredirs;
static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
#define VTABSIZE 39
static struct var *vartab[VTABSIZE];
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
/*
* Return of a legal variable name (a letter or underscore followed by zero or
* more letters, underscores, and digits).
*/
static char *
endofname(const char *name)
{
char *p;
p = (char *) name;
if (!is_name(*p))
return p;
while (*++p) {
if (!is_in_name(*p))
break;
}
return p;
}
/*
* Compares two strings up to the first = or '\0'. The first
* string must be terminated by '='; the second may be terminated by
* either '=' or '\0'.
*/
static int
varcmp(const char *p, const char *q)
{
int c, d;
while ((c = *p) == (d = *q)) {
if (!c || c == '=')
goto out;
p++;
q++;
}
if (c == '=')
c = '\0';
if (d == '=')
d = '\0';
out:
return c - d;
}
static int
varequal(const char *a, const char *b)
{
return !varcmp(a, b);
}
/*
* Find the appropriate entry in the hash table from the name.
*/
static struct var **
hashvar(const char *p)
{
unsigned hashval;
hashval = ((unsigned char) *p) << 4;
while (*p && *p != '=')
hashval += (unsigned char) *p++;
return &vartab[hashval % VTABSIZE];
}
static int
vpcmp(const void *a, const void *b)
{
return varcmp(*(const char **)a, *(const char **)b);
}
/*
* This routine initializes the builtin variables.
*/
static void
initvar(void)
{
struct var *vp;
struct var *end;
struct var **vpp;
/*
* PS1 depends on uid
*/
#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
vps1.text = "PS1=\\w \\$ ";
#else
if (!geteuid())
vps1.text = "PS1=# ";
#endif
vp = varinit;
end = vp + ARRAY_SIZE(varinit);
do {
vpp = hashvar(vp->text);
vp->next = *vpp;
*vpp = vp;
} while (++vp < end);
}
static struct var **
findvar(struct var **vpp, const char *name)
{
for (; *vpp; vpp = &(*vpp)->next) {
if (varequal((*vpp)->text, name)) {
break;
}
}
return vpp;
}
/*
* Find the value of a variable. Returns NULL if not set.
*/
static char *
lookupvar(const char *name)
{
struct var *v;
v = *findvar(hashvar(name), name);
if (v) {
#ifdef DYNAMIC_VAR
/*
* Dynamic variables are implemented roughly the same way they are
* in bash. Namely, they're "special" so long as they aren't unset.
* As soon as they're unset, they're no longer dynamic, and dynamic
* lookup will no longer happen at that point. -- PFM.
*/
if ((v->flags & VDYNAMIC))
(*v->func)(NULL);
#endif
if (!(v->flags & VUNSET))
return strchrnul(v->text, '=') + 1;
}
return NULL;
}
/*
* Search the environment of a builtin command.
*/
static char *
bltinlookup(const char *name)
{
struct strlist *sp;
for (sp = cmdenviron; sp; sp = sp->next) {
if (varequal(sp->text, name))
return strchrnul(sp->text, '=') + 1;
}
return lookupvar(name);
}
/*
* Same as setvar except that the variable and value are passed in
* the first argument as name=value. Since the first argument will
* be actually stored in the table, it should not be a string that
* will go away.
* Called with interrupts off.
*/
static void
setvareq(char *s, int flags)
{
struct var *vp, **vpp;
vpp = hashvar(s);
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
vp = *findvar(vpp, s);
if (vp) {
if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
const char *n;
if (flags & VNOSAVE)
free(s);
n = vp->text;
ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
}
if (flags & VNOSET)
return;
if (vp->func && (flags & VNOFUNC) == 0)
(*vp->func)(strchrnul(s, '=') + 1);
if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
free((char*)vp->text);
flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
} else {
if (flags & VNOSET)
return;
/* not found */
vp = ckmalloc(sizeof(*vp));
vp->next = *vpp;
vp->func = NULL;
*vpp = vp;
}
if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
s = ckstrdup(s);
vp->text = s;
vp->flags = flags;
}
/*
* Set the value of a variable. The flags argument is ored with the
* flags of the variable. If val is NULL, the variable is unset.
*/
static void
setvar(const char *name, const char *val, int flags)
{
char *p, *q;
size_t namelen;
char *nameeq;
size_t vallen;
q = endofname(name);
p = strchrnul(q, '=');
namelen = p - name;
if (!namelen || p != q)
ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
vallen = 0;
if (val == NULL) {
flags |= VUNSET;
} else {
vallen = strlen(val);
}
INT_OFF;
nameeq = ckmalloc(namelen + vallen + 2);
p = memcpy(nameeq, name, namelen) + namelen;
if (val) {
*p++ = '=';
p = memcpy(p, val, vallen) + vallen;
}
*p = '\0';
setvareq(nameeq, flags | VNOSAVE);
INT_ON;
}
#if ENABLE_ASH_GETOPTS
/*
* Safe version of setvar, returns 1 on success 0 on failure.
*/
static int
setvarsafe(const char *name, const char *val, int flags)
{
int err;
volatile int saveint;
struct jmploc *volatile savehandler = exception_handler;
struct jmploc jmploc;
SAVE_INT(saveint);
if (setjmp(jmploc.loc))
err = 1;
else {
exception_handler = &jmploc;
setvar(name, val, flags);
err = 0;
}
exception_handler = savehandler;
RESTORE_INT(saveint);
return err;
}
#endif
/*
* Unset the specified variable.
*/
static int
unsetvar(const char *s)
{
struct var **vpp;
struct var *vp;
int retval;
vpp = findvar(hashvar(s), s);
vp = *vpp;
retval = 2;
if (vp) {
int flags = vp->flags;
retval = 1;
if (flags & VREADONLY)
goto out;
#ifdef DYNAMIC_VAR
vp->flags &= ~VDYNAMIC;
#endif
if (flags & VUNSET)
goto ok;
if ((flags & VSTRFIXED) == 0) {
INT_OFF;
if ((flags & (VTEXTFIXED|VSTACK)) == 0)
free((char*)vp->text);
*vpp = vp->next;
free(vp);
INT_ON;
} else {
setvar(s, 0, 0);
vp->flags &= ~VEXPORT;
}
ok:
retval = 0;
}
out:
return retval;
}
/*
* Process a linked list of variable assignments.
*/
static void
listsetvar(struct strlist *list_set_var, int flags)
{
struct strlist *lp = list_set_var;
if (!lp)
return;
INT_OFF;
do {
setvareq(lp->text, flags);
lp = lp->next;
} while (lp);
INT_ON;
}
/*
* Generate a list of variables satisfying the given conditions.
*/
static char **
listvars(int on, int off, char ***end)
{
struct var **vpp;
struct var *vp;
char **ep;
int mask;
STARTSTACKSTR(ep);
vpp = vartab;
mask = on | off;
do {
for (vp = *vpp; vp; vp = vp->next) {
if ((vp->flags & mask) == on) {
if (ep == stackstrend())
ep = growstackstr();
*ep++ = (char *) vp->text;
}
}
} while (++vpp < vartab + VTABSIZE);
if (ep == stackstrend())
ep = growstackstr();
if (end)
*end = ep;
*ep++ = NULL;
return grabstackstr(ep);
}
/* ============ Path search helper
*
* The variable path (passed by reference) should be set to the start
* of the path before the first call; padvance will update
* this value as it proceeds. Successive calls to padvance will return
* the possible path expansions in sequence. If an option (indicated by
* a percent sign) appears in the path entry then the global variable
* pathopt will be set to point to it; otherwise pathopt will be set to
* NULL.
*/
static const char *pathopt; /* set by padvance */
static char *
padvance(const char **path, const char *name)
{
const char *p;
char *q;
const char *start;
size_t len;
if (*path == NULL)
return NULL;
start = *path;
for (p = start; *p && *p != ':' && *p != '%'; p++);
len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
while (stackblocksize() < len)
growstackblock();
q = stackblock();
if (p != start) {
memcpy(q, start, p - start);
q += p - start;
*q++ = '/';
}
strcpy(q, name);
pathopt = NULL;
if (*p == '%') {
pathopt = ++p;
while (*p && *p != ':') p++;
}
if (*p == ':')
*path = p + 1;
else
*path = NULL;
return stalloc(len);
}
/* ============ Prompt */
static smallint doprompt; /* if set, prompt the user */
static smallint needprompt; /* true if interactive and at start of line */
#if ENABLE_FEATURE_EDITING
static line_input_t *line_input_state;
static const char *cmdedit_prompt;
static void
putprompt(const char *s)
{
if (ENABLE_ASH_EXPAND_PRMT) {
free((char*)cmdedit_prompt);
cmdedit_prompt = ckstrdup(s);
return;
}
cmdedit_prompt = s;
}
#else
static void
putprompt(const char *s)
{
out2str(s);
}
#endif
#if ENABLE_ASH_EXPAND_PRMT
/* expandstr() needs parsing machinery, so it is far away ahead... */
static const char *expandstr(const char *ps);
#else
#define expandstr(s) s
#endif
static void
setprompt(int whichprompt)
{
const char *prompt;
#if ENABLE_ASH_EXPAND_PRMT
struct stackmark smark;
#endif
needprompt = 0;
switch (whichprompt) {
case 1:
prompt = ps1val();
break;
case 2:
prompt = ps2val();
break;
default: /* 0 */
prompt = nullstr;
}
#if ENABLE_ASH_EXPAND_PRMT
setstackmark(&smark);
stalloc(stackblocksize());
#endif
putprompt(expandstr(prompt));
#if ENABLE_ASH_EXPAND_PRMT
popstackmark(&smark);
#endif
}
/* ============ The cd and pwd commands */
#define CD_PHYSICAL 1
#define CD_PRINT 2
static int docd(const char *, int);
static char *curdir = nullstr; /* current working directory */
static char *physdir = nullstr; /* physical working directory */
static int
cdopt(void)
{
int flags = 0;
int i, j;
j = 'L';
while ((i = nextopt("LP"))) {
if (i != j) {
flags ^= CD_PHYSICAL;
j = i;
}
}
return flags;
}
/*
* Update curdir (the name of the current directory) in response to a
* cd command.
*/
static const char *
updatepwd(const char *dir)
{
char *new;
char *p;
char *cdcomppath;
const char *lim;
cdcomppath = ststrdup(dir);
STARTSTACKSTR(new);
if (*dir != '/') {
if (curdir == nullstr)
return 0;
new = stack_putstr(curdir, new);
}
new = makestrspace(strlen(dir) + 2, new);
lim = stackblock() + 1;
if (*dir != '/') {
if (new[-1] != '/')
USTPUTC('/', new);
if (new > lim && *lim == '/')
lim++;
} else {
USTPUTC('/', new);
cdcomppath++;
if (dir[1] == '/' && dir[2] != '/') {
USTPUTC('/', new);
cdcomppath++;
lim++;
}
}
p = strtok(cdcomppath, "/");
while (p) {
switch (*p) {
case '.':
if (p[1] == '.' && p[2] == '\0') {
while (new > lim) {
STUNPUTC(new);
if (new[-1] == '/')
break;
}
break;
}
if (p[1] == '\0')
break;
/* fall through */
default:
new = stack_putstr(p, new);
USTPUTC('/', new);
}
p = strtok(0, "/");
}
if (new > lim)
STUNPUTC(new);
*new = 0;
return stackblock();
}
/*
* Find out what the current directory is. If we already know the current
* directory, this routine returns immediately.
*/
static char *
getpwd(void)
{
char *dir = getcwd(0, 0);
return dir ? dir : nullstr;
}
static void
setpwd(const char *val, int setold)
{
char *oldcur, *dir;
oldcur = dir = curdir;
if (setold) {
setvar("OLDPWD", oldcur, VEXPORT);
}
INT_OFF;
if (physdir != nullstr) {
if (physdir != oldcur)
free(physdir);
physdir = nullstr;
}
if (oldcur == val || !val) {
char *s = getpwd();
physdir = s;
if (!val)
dir = s;
} else
dir = ckstrdup(val);
if (oldcur != dir && oldcur != nullstr) {
free(oldcur);
}
curdir = dir;
INT_ON;
setvar("PWD", dir, VEXPORT);
}
static void hashcd(void);
/*
* Actually do the chdir. We also call hashcd to let the routines in exec.c
* know that the current directory has changed.
*/
static int
docd(const char *dest, int flags)
{
const char *dir = 0;
int err;
TRACE(("docd(\"%s\", %d) called\n", dest, flags));
INT_OFF;
if (!(flags & CD_PHYSICAL)) {
dir = updatepwd(dest);
if (dir)
dest = dir;
}
err = chdir(dest);
if (err)
goto out;
setpwd(dir, 1);
hashcd();
out:
INT_ON;
return err;
}
static int
cdcmd(int argc, char **argv)
{
const char *dest;
const char *path;
const char *p;
char c;
struct stat statb;
int flags;
flags = cdopt();
dest = *argptr;
if (!dest)
dest = bltinlookup(homestr);
else if (LONE_DASH(dest)) {
dest = bltinlookup("OLDPWD");
flags |= CD_PRINT;
}
if (!dest)
dest = nullstr;
if (*dest == '/')
goto step7;
if (*dest == '.') {
c = dest[1];
dotdot:
switch (c) {
case '\0':
case '/':
goto step6;
case '.':
c = dest[2];
if (c != '.')
goto dotdot;
}
}
if (!*dest)
dest = ".";
path = bltinlookup("CDPATH");
if (!path) {
step6:
step7:
p = dest;
goto docd;
}
do {
c = *path;
p = padvance(&path, dest);
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
if (c && c != ':')
flags |= CD_PRINT;
docd:
if (!docd(p, flags))
goto out;
break;
}
} while (path);
ash_msg_and_raise_error("can't cd to %s", dest);
/* NOTREACHED */
out:
if (flags & CD_PRINT)
out1fmt(snlfmt, curdir);
return 0;
}
static int
pwdcmd(int argc, char **argv)
{
int flags;
const char *dir = curdir;
flags = cdopt();
if (flags) {
if (physdir == nullstr)
setpwd(dir, 0);
dir = physdir;
}
out1fmt(snlfmt, dir);
return 0;
}
/* ============ ... */
#define IBUFSIZ COMMON_BUFSIZE
#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
/* Syntax classes */
#define CWORD 0 /* character is nothing special */
#define CNL 1 /* newline character */
#define CBACK 2 /* a backslash character */
#define CSQUOTE 3 /* single quote */
#define CDQUOTE 4 /* double quote */
#define CENDQUOTE 5 /* a terminating quote */
#define CBQUOTE 6 /* backwards single quote */
#define CVAR 7 /* a dollar sign */
#define CENDVAR 8 /* a '}' character */
#define CLP 9 /* a left paren in arithmetic */
#define CRP 10 /* a right paren in arithmetic */
#define CENDFILE 11 /* end of file */
#define CCTL 12 /* like CWORD, except it must be escaped */
#define CSPCL 13 /* these terminate a word */
#define CIGN 14 /* character should be ignored */
#if ENABLE_ASH_ALIAS
#define SYNBASE 130
#define PEOF -130
#define PEOA -129
#define PEOA_OR_PEOF PEOA
#else
#define SYNBASE 129
#define PEOF -129
#define PEOA_OR_PEOF PEOF
#endif
/* number syntax index */
#define BASESYNTAX 0 /* not in quotes */
#define DQSYNTAX 1 /* in double quotes */
#define SQSYNTAX 2 /* in single quotes */
#define ARISYNTAX 3 /* in arithmetic */
#define PSSYNTAX 4 /* prompt */
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
#define USE_SIT_FUNCTION
#endif
#if ENABLE_ASH_MATH_SUPPORT
static const char S_I_T[][4] = {
#if ENABLE_ASH_ALIAS
{ CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
#endif
{ CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
{ CNL, CNL, CNL, CNL }, /* 2, \n */
{ CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
{ CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
{ CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
{ CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
{ CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
{ CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
{ CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
{ CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
{ CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
#ifndef USE_SIT_FUNCTION
{ CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
{ CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
{ CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
#endif
};
#else
static const char S_I_T[][3] = {
#if ENABLE_ASH_ALIAS
{ CSPCL, CIGN, CIGN }, /* 0, PEOA */
#endif
{ CSPCL, CWORD, CWORD }, /* 1, ' ' */
{ CNL, CNL, CNL }, /* 2, \n */
{ CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
{ CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
{ CVAR, CVAR, CWORD }, /* 5, $ */
{ CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
{ CSPCL, CWORD, CWORD }, /* 7, ( */
{ CSPCL, CWORD, CWORD }, /* 8, ) */
{ CBACK, CBACK, CCTL }, /* 9, \ */
{ CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
{ CENDVAR, CENDVAR, CWORD }, /* 11, } */
#ifndef USE_SIT_FUNCTION
{ CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
{ CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
{ CCTL, CCTL, CCTL } /* 14, CTLESC ... */
#endif
};
#endif /* ASH_MATH_SUPPORT */
#ifdef USE_SIT_FUNCTION
static int
SIT(int c, int syntax)
{
static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
#if ENABLE_ASH_ALIAS
static const char syntax_index_table[] ALIGN1 = {
1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
11, 3 /* "}~" */
};
#else
static const char syntax_index_table[] ALIGN1 = {
0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
10, 2 /* "}~" */
};
#endif
const char *s;
int indx;
if (c == PEOF) /* 2^8+2 */
return CENDFILE;
#if ENABLE_ASH_ALIAS
if (c == PEOA) /* 2^8+1 */
indx = 0;
else
#endif
#define U_C(c) ((unsigned char)(c))
if ((unsigned char)c >= (unsigned char)(CTLESC)
&& (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
) {
return CCTL;
} else {
s = strchr(spec_symbls, c);
if (s == NULL || *s == '\0')
return CWORD;
indx = syntax_index_table[(s - spec_symbls)];
}
return S_I_T[indx][syntax];
}
#else /* !USE_SIT_FUNCTION */
#if ENABLE_ASH_ALIAS
#define CSPCL_CIGN_CIGN_CIGN 0
#define CSPCL_CWORD_CWORD_CWORD 1
#define CNL_CNL_CNL_CNL 2
#define CWORD_CCTL_CCTL_CWORD 3
#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
#define CVAR_CVAR_CWORD_CVAR 5
#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
#define CSPCL_CWORD_CWORD_CLP 7
#define CSPCL_CWORD_CWORD_CRP 8
#define CBACK_CBACK_CCTL_CBACK 9
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
#define CWORD_CWORD_CWORD_CWORD 13
#define CCTL_CCTL_CCTL_CCTL 14
#else
#define CSPCL_CWORD_CWORD_CWORD 0
#define CNL_CNL_CNL_CNL 1
#define CWORD_CCTL_CCTL_CWORD 2
#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
#define CVAR_CVAR_CWORD_CVAR 4
#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
#define CSPCL_CWORD_CWORD_CLP 6
#define CSPCL_CWORD_CWORD_CRP 7
#define CBACK_CBACK_CCTL_CBACK 8
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
#define CWORD_CWORD_CWORD_CWORD 12
#define CCTL_CCTL_CCTL_CCTL 13
#endif
static const char syntax_index_table[258] = {
/* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
/* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
#if ENABLE_ASH_ALIAS
/* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
#endif
/* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
/* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
/* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
/* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
/* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
/* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
/* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
/* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
/* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
/* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
/* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
/* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
/* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
/* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
/* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
/* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
/* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
/* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
/* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
/* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
/* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
/* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
/* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
/* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
/* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
/* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
/* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
/* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
/* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
/* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
/* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
/* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
/* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
/* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
/* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
/* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
/* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
/* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
/* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
/* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
/* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
/* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
/* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
/* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
/* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
/* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
/* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
/* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
/* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
/* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
/* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
/* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
/* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
/* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
/* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
/* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
/* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
/* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
/* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
/* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
/* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
/* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
/* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
/* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
/* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
/* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
/* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
/* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
/* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
/* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
/* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
/* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
/* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
/* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
/* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
/* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
/* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
/* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
/* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
/* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
/* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
/* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
/* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
/* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
/* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
/* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
/* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
/* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
/* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
/* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
/* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
/* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
/* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
/* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
/* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
/* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
/* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
/* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
/* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
/* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
/* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
/* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
/* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
/* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
/* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
/* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
/* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
/* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
/* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
/* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
/* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
/* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
/* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
/* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
/* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
/* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
/* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
/* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
/* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
/* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
/* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
/* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
/* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
/* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
/* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
/* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
/* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
/* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
/* 130 0 */ CWORD_CWORD_CWORD_CWORD,
/* 131 1 */ CWORD_CWORD_CWORD_CWORD,
/* 132 2 */ CWORD_CWORD_CWORD_CWORD,
/* 133 3 */ CWORD_CWORD_CWORD_CWORD,
/* 134 4 */ CWORD_CWORD_CWORD_CWORD,
/* 135 5 */ CWORD_CWORD_CWORD_CWORD,
/* 136 6 */ CWORD_CWORD_CWORD_CWORD,
/* 137 7 */ CWORD_CWORD_CWORD_CWORD,
/* 138 8 */ CWORD_CWORD_CWORD_CWORD,
/* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
/* 140 10 "\n" */ CNL_CNL_CNL_CNL,
/* 141 11 */ CWORD_CWORD_CWORD_CWORD,
/* 142 12 */ CWORD_CWORD_CWORD_CWORD,
/* 143 13 */ CWORD_CWORD_CWORD_CWORD,
/* 144 14 */ CWORD_CWORD_CWORD_CWORD,
/* 145 15 */ CWORD_CWORD_CWORD_CWORD,
/* 146 16 */ CWORD_CWORD_CWORD_CWORD,
/* 147 17 */ CWORD_CWORD_CWORD_CWORD,
/* 148 18 */ CWORD_CWORD_CWORD_CWORD,
/* 149 19 */ CWORD_CWORD_CWORD_CWORD,
/* 150 20 */ CWORD_CWORD_CWORD_CWORD,
/* 151 21 */ CWORD_CWORD_CWORD_CWORD,
/* 152 22 */ CWORD_CWORD_CWORD_CWORD,
/* 153 23 */ CWORD_CWORD_CWORD_CWORD,
/* 154 24 */ CWORD_CWORD_CWORD_CWORD,
/* 155 25 */ CWORD_CWORD_CWORD_CWORD,
/* 156 26 */ CWORD_CWORD_CWORD_CWORD,
/* 157 27 */ CWORD_CWORD_CWORD_CWORD,
/* 158 28 */ CWORD_CWORD_CWORD_CWORD,
/* 159 29 */ CWORD_CWORD_CWORD_CWORD,
/* 160 30 */ CWORD_CWORD_CWORD_CWORD,
/* 161 31 */ CWORD_CWORD_CWORD_CWORD,
/* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
/* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
/* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
/* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
/* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
/* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
/* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
/* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
/* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
/* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
/* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
/* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
/* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
/* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
/* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
/* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
/* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
/* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
/* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
/* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
/* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
/* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
/* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
/* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
/* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
/* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
/* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
/* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
/* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
/* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
/* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
/* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
/* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
/* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
/* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
/* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
/* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
/* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
/* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
/* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
/* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
/* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
/* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
/* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
/* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
/* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
/* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
/* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
/* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
/* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
/* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
/* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
/* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
/* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
/* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
/* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
/* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
/* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
/* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
/* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
/* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
/* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
/* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
/* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
/* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
/* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
/* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
/* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
/* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
/* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
/* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
/* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
/* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
/* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
/* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
/* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
/* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
/* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
/* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
/* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
/* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
/* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
/* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
/* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
/* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
/* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
/* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
/* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
/* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
/* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
/* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
/* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
/* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
/* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
/* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
/* 257 127 */ CWORD_CWORD_CWORD_CWORD,
};
#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
#endif /* USE_SIT_FUNCTION */
/* ============ Alias handling */
#if ENABLE_ASH_ALIAS
#define ALIASINUSE 1
#define ALIASDEAD 2
#define ATABSIZE 39
struct alias {
struct alias *next;
char *name;
char *val;
int flag;
};
static struct alias *atab[ATABSIZE];
static struct alias **
__lookupalias(const char *name) {
unsigned int hashval;
struct alias **app;
const char *p;
unsigned int ch;
p = name;
ch = (unsigned char)*p;
hashval = ch << 4;
while (ch) {
hashval += ch;
ch = (unsigned char)*++p;
}
app = &atab[hashval % ATABSIZE];
for (; *app; app = &(*app)->next) {
if (strcmp(name, (*app)->name) == 0) {
break;
}
}
return app;
}
static struct alias *
lookupalias(const char *name, int check)
{
struct alias *ap = *__lookupalias(name);
if (check && ap && (ap->flag & ALIASINUSE))
return NULL;
return ap;
}
static struct alias *
freealias(struct alias *ap)
{
struct alias *next;
if (ap->flag & ALIASINUSE) {
ap->flag |= ALIASDEAD;
return ap;
}
next = ap->next;
free(ap->name);
free(ap->val);
free(ap);
return next;
}
static void
setalias(const char *name, const char *val)
{
struct alias *ap, **app;
app = __lookupalias(name);
ap = *app;
INT_OFF;
if (ap) {
if (!(ap->flag & ALIASINUSE)) {
free(ap->val);
}
ap->val = ckstrdup(val);
ap->flag &= ~ALIASDEAD;
} else {
/* not found */
ap = ckmalloc(sizeof(struct alias));
ap->name = ckstrdup(name);
ap->val = ckstrdup(val);
ap->flag = 0;
ap->next = 0;
*app = ap;
}
INT_ON;
}
static int
unalias(const char *name)
{
struct alias **app;
app = __lookupalias(name);
if (*app) {
INT_OFF;
*app = freealias(*app);
INT_ON;
return 0;
}
return 1;
}
static void
rmaliases(void)
{
struct alias *ap, **app;
int i;
INT_OFF;
for (i = 0; i < ATABSIZE; i++) {
app = &atab[i];
for (ap = *app; ap; ap = *app) {
*app = freealias(*app);
if (ap == *app) {
app = &ap->next;
}
}
}
INT_ON;
}
static void
printalias(const struct alias *ap)
{
out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
}
/*
* TODO - sort output
*/
static int
aliascmd(int argc, char **argv)
{
char *n, *v;
int ret = 0;
struct alias *ap;
if (argc == 1) {
int i;
for (i = 0; i < ATABSIZE; i++)
for (ap = atab[i]; ap; ap = ap->next) {
printalias(ap);
}
return 0;
}
while ((n = *++argv) != NULL) {
v = strchr(n+1, '=');
if (v == NULL) { /* n+1: funny ksh stuff */
ap = *__lookupalias(n);
if (ap == NULL) {
fprintf(stderr, "%s: %s not found\n", "alias", n);
ret = 1;
} else
printalias(ap);
} else {
*v++ = '\0';
setalias(n, v);
}
}
return ret;
}
static int
unaliascmd(int argc, char **argv)
{
int i;
while ((i = nextopt("a")) != '\0') {
if (i == 'a') {
rmaliases();
return 0;
}
}
for (i = 0; *argptr; argptr++) {
if (unalias(*argptr)) {
fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
i = 1;
}
}
return i;
}
#endif /* ASH_ALIAS */
/* ============ jobs.c */
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
#define FORK_NOJOB 2
/* mode flags for showjob(s) */
#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
#define SHOW_PID 0x04 /* include process pid */
#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
/*
* 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 {
pid_t pid; /* process id */
int status; /* last process status from wait() */
char *cmd; /* text of command being run */
};
struct job {
struct procstat ps0; /* status of process */
struct procstat *ps; /* status or processes when more than one */
#if JOBS
int stopstatus; /* status of a stopped job */
#endif
uint32_t
nprocs: 16, /* number of processes */
state: 8,
#define JOBRUNNING 0 /* at least one proc running */
#define JOBSTOPPED 1 /* all procs are stopped */
#define JOBDONE 2 /* all procs are completed */
#if JOBS
sigint: 1, /* job was killed by SIGINT */
jobctl: 1, /* job running under job control */
#endif
waited: 1, /* true if this entry has been waited for */
used: 1, /* true if this entry is in used */
changed: 1; /* true if status has changed */
struct job *prev_job; /* previous job */
};
static pid_t backgndpid; /* pid of last background process */
static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
static struct job *makejob(union node *, int);
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
#if !JOBS
enum { jobctl = 0 };
#define setjobctl(on) do {} while (0)
#else
static smallint jobctl; /* true if doing job control */
static void setjobctl(int);
#endif
/*
* Set the signal handler for the specified signal. The routine figures
* out what it should be set to.
*/
static void
setsignal(int signo)
{
int action;
char *t, tsig;
struct sigaction act;
t = trap[signo];
if (t == NULL)
action = S_DFL;
else if (*t != '\0')
action = S_CATCH;
else
action = S_IGN;
if (rootshell && action == S_DFL) {
switch (signo) {
case SIGINT:
if (iflag || minusc || sflag == 0)
action = S_CATCH;
break;
case SIGQUIT:
#if DEBUG
if (debug)
break;
#endif
/* FALLTHROUGH */
case SIGTERM:
if (iflag)
action = S_IGN;
break;
#if JOBS
case SIGTSTP:
case SIGTTOU:
if (mflag)
action = S_IGN;
break;
#endif
}
}
t = &sigmode[signo -