/* These defines allow you to adjust the feature set to be compiled
* into the ash shell. As a rule, enabling these options will make
* ash get bigger... With all of these options off, ash adds about
- * 62k to busybox on an x86 system.*/
-
+ * 60k to busybox on an x86 system.*/
/* Enable job control. This allows you to run jobs in the background,
* which is great when ash is being used as an interactive shell, but
- * it completely useless for is all you are doing is running scripts.
+ * it completely useless for is all you are doing is running scripts.
* This adds about 2.5k on an x86 system. */
-#define JOBS
+#undef JOBS
/* This enables alias support in ash. If you want to support things
* like "alias ls='ls -l'" with ash, enable this. This is only useful
* doesn't compile right now... */
#undef ASH_MATH_SUPPORT
-/* This shell builtin is used to indicate how the shell would interpret
- * what you give it. This command is only useful when debugging, and
- * is obsolete anyways. Adds about 670 bytes... You probably want to
- * leave this disabled. */
-#undef ASH_TYPE
-
/* Getopts is used by shell procedures to parse positional parameters.
* You probably want to leave this disabled, and use the busybox getopt
* applet if you want to do this sort of thing. There are some scripts
/* This allows you to override shell builtins and use whatever is on
* the filesystem. This is most useful when ash is acting as a
- * standalone shell. Adds about 320 bytes. */
+ * standalone shell. Adds about 272 bytes. */
#undef ASH_CMDCMD
-/* This makes a few common apps that are usually part of busybox
- * anyways to be used as builtins. This may cause these builtins to be
- * a little bit faster, but leaving this disabled will save you 2k. */
-#undef ASH_BBAPPS_AS_BUILTINS
+
+/* Optimize size vs speed as size */
+#define ASH_OPTIMIZE_FOR_SIZE
/* Enable this to compile in extra debugging noise. When debugging is
* on, debugging info will be written to $HOME/trace and a quit signal
* will generate a core dump. */
#undef DEBUG
-
-
/* These are here to work with glibc -- Don't change these... */
#undef FNMATCH_BROKEN
#undef GLOB_BROKEN
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "busybox.h"
#include "cmdedit.h"
-/* if BB_PWD is defined, then disable ASH_PWD to save space */
-#ifdef BB_PWD
-#undef ASH_PWD
-#endif
-
-
/*
* This file was generated by the mksyntax program.
*/
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
-#define is_digit(c) ((((unsigned char)(c)) - '0') <= 9)
+#define is_digit(c) ((c)>='0' && (c)<='9')
#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
#define _DIAGASSERT(x)
-#define ATABSIZE 39
+
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
/* flags passed to redirect */
#define REDIR_PUSH 01 /* save previous values of file descriptors */
-#define REDIR_BACKQ 02 /* save the command output in memory */
+#define REDIR_BACKQ 02 /* save the command output to pipe */
/*
* BSD setjmp saves the signal mask, which violates ANSI C and takes time,
* so we use _setjmp instead.
*/
-#if !defined(__GLIBC__)
+#if defined(BSD)
#define setjmp(jmploc) _setjmp(jmploc)
#define longjmp(jmploc, val) _longjmp(jmploc, val)
#endif
static volatile int intpending;
#define INTOFF suppressint++
-#ifdef ASH_BBAPPS_AS_BUILTINS
+#ifndef ASH_OPTIMIZE_FOR_SIZE
#define INTON { if (--suppressint == 0 && intpending) onint(); }
+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
#else
static void __inton (void);
+static void forceinton (void);
#define INTON __inton()
+#define FORCEINTON forceinton()
#endif
-#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+
#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending
static void stunalloc (pointer);
static void ungrabstackstr (char *, char *);
static char * growstackstr(void);
+static char * makestrspace(size_t newlen);
static char *sstrdup (const char *);
/*
#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(n); }
-#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+
+
+#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
#define STUNPUTC(p) (++sstrnleft, --p)
#define STTOPC(p) p[-1]
#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
#define ckfree(p) free((pointer)(p))
-static char * makestrspace(size_t newlen);
#ifdef DEBUG
#define TRACE(param) trace param
int optoff; /* used by getopts */
};
-struct output {
-#ifdef USE_GLIBC_STDIO
- FILE *stream;
-#endif
- char *nextc;
- int nleft;
- char *buf;
- int bufsize;
- int fd;
- short flags;
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+#define CMDTABLESIZE 31 /* should be prime */
+#define ARB 1 /* actual size determined at run time */
+
+
+
+struct tblentry {
+ struct tblentry *next; /* next entry in hash chain */
+ union param param; /* definition of builtin function */
+ short cmdtype; /* index identifying command */
+ char rehash; /* if set, cd done since entry created */
+ char cmdname[ARB]; /* name of command */
};
-#define OUTBUFSIZ BUFSIZ
-#define MEM_OUT -3 /* output to dynamically allocated memory */
+static struct tblentry *cmdtable[CMDTABLESIZE];
+static int builtinloc = -1; /* index in path of %builtin, or -1 */
+static int exerrno = 0; /* Last exec error */
+
+
+static void tryexec (char *, char **, char **);
+static void printentry (struct tblentry *, int);
+static void clearcmdentry (int);
+static struct tblentry *cmdlookup (const char *, int);
+static void delete_cmd_entry (void);
+static int path_change (const char *, int *);
-#ifdef USE_GLIBC_STDIO
-static struct output output = {NULL, NULL, 0, NULL, 0, 1, 0};
-static struct output errout = {NULL, NULL, 0, NULL, 0, 2, 0};
-static struct output memout = {NULL, NULL, 0, NULL, 0, MEM_OUT, 0};
-#else
-static struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
-static struct output errout = {NULL, 0, NULL, 0, 2, 0};
-static struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
-#endif
-static struct output *out1 = &output;
-static struct output *out2 = &errout;
-#ifndef USE_GLIBC_STDIO
-static void outcslow (char, struct output *);
-#endif
static void flushall (void);
-static void flushout (struct output *);
-static void freestdout (void);
-static void outfmt (struct output *, const char *, ...)
- __attribute__((__format__(__printf__,2,3)));
-static void out1fmt (const char *, ...)
+static void out2fmt (const char *, ...)
__attribute__((__format__(__printf__,1,2)));
-static void fmtstr (char *, size_t, const char *, ...)
- __attribute__((__format__(__printf__,3,4)));
-#ifndef USE_GLIBC_STDIO
-static void doformat (struct output *, const char *, va_list);
-#endif
static int xwrite (int, const char *, int);
-#ifdef USE_GLIBC_STDIO
-static void initstreams (void);
-static void openmemout (void);
-static int __closememout (void);
-#endif
-static void outstr(const char *p, struct output *file);
+static void outstr (const char *p, FILE *file) { fputs(p, file); }
+static void out1str(const char *p) { outstr(p, stdout); }
+static void out2str(const char *p) { outstr(p, stderr); }
-#define OUTPUT_ERR 01 /* error occurred on output */
-
-#ifdef USE_GLIBC_STDIO
-#define outc(c, o) putc((c), (o)->stream)
-#define doformat(d, f, a) vfprintf((d)->stream, (f), (a))
+#ifndef ASH_OPTIMIZE_FOR_SIZE
+#define out2c(c) putc((c), stderr)
#else
-#define outc(c, file) (--(file)->nleft < 0? outcslow((c), (file)) : (*(file)->nextc = (c), (file)->nextc++))
+static void out2c(int c) { putc(c, stderr); }
#endif
-#define out1c(c) outc((c), out1)
-#define out2c(c) outc((c), out2)
-#define out1str(s) outstr((s), out1)
-#define out2str(s) outstr((s), out2)
-#define outerr(f) ((f)->flags & OUTPUT_ERR)
/* syntax table used when not in quotes */
static const char basesyntax[257] = {
};
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define rmescapes(p) _rmescapes((p), 0)
static char *_rmescapes (char *, int);
#else
static void setvar (const char *, const char *, int);
static void setvareq (char *, int);
static void listsetvar (struct strlist *);
-static char *lookupvar (const char *);
-static char *bltinlookup (const char *);
+static const char *lookupvar (const char *);
+static const char *bltinlookup (const char *);
static char **environment (void);
static int showvarscmd (int, char **);
static void mklocal (char *);
#define ALIASINUSE 1
#define ALIASDEAD 2
+#define ATABSIZE 39
+
struct alias {
struct alias *next;
char *name;
char *p;
p = single_quote(ap->val);
- out1fmt("alias %s=%s\n", ap->name, p);
+ printf("alias %s=%s\n", ap->name, p);
stunalloc(p);
}
while ((n = *++argv) != NULL) {
if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
if ((ap = *__lookupalias(n)) == NULL) {
- outfmt(out2, "%s: %s not found\n", "alias", n);
+ out2fmt("%s: %s not found\n", "alias", n);
ret = 1;
} else
printalias(ap);
}
for (i = 0; *argptr; argptr++) {
if (unalias(*argptr)) {
- outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
+ out2fmt("%s: %s not found\n", "unalias", *argptr);
i = 1;
}
}
static int exportcmd (int, char **);
static int histcmd (int, char **);
static int hashcmd (int, char **);
+static int helpcmd (int, char **);
static int jobscmd (int, char **);
static int localcmd (int, char **);
-#ifdef ASH_PWD
+#ifndef BB_PWD
static int pwdcmd (int, char **);
#endif
static int readcmd (int, char **);
#ifdef ASH_MATH_SUPPORT
static int expcmd (int, char **);
#endif
-#ifdef ASH_TYPE
static int typecmd (int, char **);
-#endif
#ifdef ASH_GETOPTS
static int getoptscmd (int, char **);
#endif
#ifndef BB_TRUE_FALSE
-# ifdef ASH_BBAPPS_AS_BUILTINS
static int true_main (int, char **);
static int false_main (int, char **);
-# endif
#endif
static void setpwd (const char *, int);
* have been warned.
*/
static const struct builtincmd builtincmds[] = {
- { BUILTIN_SPECIAL ".", dotcmd },
+ { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */
{ BUILTIN_SPECIAL ":", true_main },
#ifdef ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
{ BUILTIN_REGULAR "bg", bgcmd },
#endif
{ BUILTIN_SPECIAL "break", breakcmd },
- { BUILTIN_SPECIAL "builtin", bltincmd }, /* Do not disable this builtin ever or bad things happen */
+ { BUILTIN_SPECIAL "builtin", bltincmd },
{ BUILTIN_REGULAR "cd", cdcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_NOSPEC "chdir", cdcmd },
-#endif
#ifdef ASH_CMDCMD
{ BUILTIN_REGULAR "command", commandcmd },
#endif
{ BUILTIN_NOSPEC "exp", expcmd },
#endif
{ BUILTIN_SPEC_ASSG "export", exportcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_REGULAR "false", false_main },
-#endif
{ BUILTIN_REGULAR "fc", histcmd },
#ifdef JOBS
{ BUILTIN_REGULAR "fg", fgcmd },
{ BUILTIN_REGULAR "getopts", getoptscmd },
#endif
{ BUILTIN_NOSPEC "hash", hashcmd },
+ { BUILTIN_NOSPEC "help", helpcmd },
{ BUILTIN_REGULAR "jobs", jobscmd },
#ifdef JOBS
{ BUILTIN_REGULAR "kill", killcmd },
{ BUILTIN_NOSPEC "let", expcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
-#ifdef ASH_PWD
+#ifndef BB_PWD
{ BUILTIN_NOSPEC "pwd", pwdcmd },
#endif
{ BUILTIN_REGULAR "read", readcmd },
{ BUILTIN_SPECIAL "shift", shiftcmd },
{ BUILTIN_SPECIAL "times", timescmd },
{ BUILTIN_SPECIAL "trap", trapcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
{ BUILTIN_REGULAR "true", true_main },
-#endif
-#ifdef ASH_TYPE
{ BUILTIN_NOSPEC "type", typecmd },
-#endif
{ BUILTIN_NOSPEC "ulimit", ulimitcmd },
{ BUILTIN_REGULAR "umask", umaskcmd },
#ifdef ASH_ALIAS
#endif
static int intreceived;
-static struct job *makejob (union node *, int);
-static int forkshell (struct job *, union node *, int);
+static struct job *makejob (const union node *, int);
+static int forkshell (struct job *, const union node *, int);
static int waitforjob (struct job *);
static int docd (char *, int);
updatepwd(badstat ? NULL : dest);
INTON;
if (print && iflag)
- out1fmt(snlfmt, curdir);
+ printf(snlfmt, curdir);
return 0;
}
}
-#ifdef ASH_PWD
+#ifndef BB_PWD
static int
pwdcmd(argc, argv)
int argc;
char **argv;
{
- out1fmt(snlfmt, curdir);
+ printf(snlfmt, curdir);
return 0;
}
#endif
if (handler == NULL)
abort();
#endif
+ flushall();
exception = e;
longjmp(handler->loc, 1);
}
#endif
if (msg) {
if (commandname)
- outfmt(&errout, "%s: ", commandname);
- doformat(&errout, msg, ap);
-#if FLUSHERR
- outc('\n', &errout);
-#else
- outcslow('\n', &errout);
-#endif
+ out2fmt("%s: ", commandname);
+ vfprintf(stderr, msg, ap);
+ out2c('\n');
}
- flushall();
exraise(cond);
/* NOTREACHED */
}
struct errname {
short errcode; /* error number */
- short action; /* operation which encountered the error */
+ char action; /* operation which encountered the error */
};
/*
return strerror(e);
}
- fmtstr(buf, sizeof buf, "error %d", e);
+ snprintf(buf, sizeof buf, "error %d", e);
return buf;
}
-#ifndef ASH_BBAPPS_AS_BUILTINS
+#ifdef ASH_OPTIMIZE_FOR_SIZE
static void
__inton() {
if (--suppressint == 0 && intpending) {
onint();
}
}
+static void forceinton (void) {
+ suppressint = 0;
+ if (intpending)
+ onint();
+}
#endif
/* flags in argument to evaltree */
static int funcnest; /* depth of function calls */
-
static struct strlist *cmdenviron; /* environment for builtin command */
static int exitstatus; /* exit status of last command */
static int oexitstatus; /* saved exit status */
-
-static void evalloop (union node *, int);
-static void evalfor (union node *, int);
-static void evalcase (union node *, int);
-static void evalsubshell (union node *, int);
+static void evalsubshell (const union node *, int);
static void expredir (union node *);
-static void evalpipe (union node *);
-#ifdef notyet
-static void evalcommand (union node *, int, struct backcmd *);
-#else
-static void evalcommand (union node *, int);
-#endif
static void prehash (union node *);
static void eprintlist (struct strlist *);
popstackmark(&smark);
}
+static struct builtincmd *find_builtin (const char *);
+static void expandarg (union node *, struct arglist *, int);
+static void calcsize (const union node *);
+static union node *copynode (const union node *);
+
/*
- * Evaluate a parse tree. The value is left in the global variable
- * exitstatus.
+ * Make a copy of a parse tree.
+ */
+
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static pointer funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
+
+
+static inline union node *
+copyfunc(union node *n)
+{
+ if (n == NULL)
+ return NULL;
+ funcblocksize = 0;
+ funcstringsize = 0;
+ calcsize(n);
+ funcblock = ckmalloc(funcblocksize + funcstringsize);
+ funcstring = (char *) funcblock + funcblocksize;
+ return copynode(n);
+}
+
+/*
+ * Free a parse tree.
*/
-static struct builtincmd *find_builtin (const char *);
-static void defun (char *, union node *);
static void
-evaltree(n, flags)
- union node *n;
- int flags;
+freefunc(union node *n)
{
- int checkexit = 0;
- if (n == NULL) {
- TRACE(("evaltree(NULL) called\n"));
- goto out;
- }
- TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
- switch (n->type) {
- case NSEMI:
- evaltree(n->nbinary.ch1, flags & EV_TESTED);
- if (evalskip)
- goto out;
- evaltree(n->nbinary.ch2, flags);
- break;
- case NAND:
- evaltree(n->nbinary.ch1, EV_TESTED);
- if (evalskip || exitstatus != 0)
- goto out;
- evaltree(n->nbinary.ch2, flags);
- break;
- case NOR:
- evaltree(n->nbinary.ch1, EV_TESTED);
- if (evalskip || exitstatus == 0)
- goto out;
- evaltree(n->nbinary.ch2, flags);
- break;
- case NREDIR:
- expredir(n->nredir.redirect);
- redirect(n->nredir.redirect, REDIR_PUSH);
- evaltree(n->nredir.n, flags);
- popredir();
- break;
- case NSUBSHELL:
- evalsubshell(n, flags);
- break;
- case NBACKGND:
- evalsubshell(n, flags);
- break;
- case NIF: {
- evaltree(n->nif.test, EV_TESTED);
- if (evalskip)
- goto out;
- if (exitstatus == 0)
- evaltree(n->nif.ifpart, flags);
- else if (n->nif.elsepart)
- evaltree(n->nif.elsepart, flags);
- else
- exitstatus = 0;
- break;
- }
- case NWHILE:
- case NUNTIL:
- evalloop(n, flags);
- break;
- case NFOR:
- evalfor(n, flags);
- break;
- case NCASE:
- evalcase(n, flags);
- break;
- case NDEFUN: {
- struct builtincmd *bcmd;
- if (
- (bcmd = find_builtin(n->narg.text)) &&
- IS_BUILTIN_SPECIAL(bcmd)
- ) {
- outfmt(out2, "%s is a special built-in\n", n->narg.text);
- exitstatus = 1;
- break;
- }
- defun(n->narg.text, n->narg.next);
- exitstatus = 0;
- break;
- }
- case NNOT:
- evaltree(n->nnot.com, EV_TESTED);
- exitstatus = !exitstatus;
- break;
+ if (n)
+ ckfree(n);
+}
- case NPIPE:
- evalpipe(n);
- checkexit = 1;
- break;
- case NCMD:
-#ifdef notyet
- evalcommand(n, flags, (struct backcmd *)NULL);
-#else
- evalcommand(n, flags);
-#endif
- checkexit = 1;
- break;
-#ifdef DEBUG
- default:
- out1fmt("Node type = %d\n", n->type);
-#ifndef USE_GLIBC_STDIO
- flushout(out1);
-#endif
- break;
-#endif
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name.
+ */
+
+static inline void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+ struct tblentry *cmdp;
+
+ INTOFF;
+ cmdp = cmdlookup(name, 1);
+ if (cmdp->cmdtype == CMDFUNCTION) {
+ freefunc(cmdp->param.func);
}
-out:
- if (pendingsigs)
- dotrap();
- if (
- flags & EV_EXIT ||
- (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
- )
- exitshell(exitstatus);
+ cmdp->cmdtype = entry->cmdtype;
+ cmdp->param = entry->u;
+ INTON;
}
-
-static void
-evalloop(n, flags)
- union node *n;
- int flags;
+static inline void
+evalloop(const union node *n, int flags)
{
int status;
exitstatus = status;
}
-static void expandarg (union node *, struct arglist *, int);
-static void fixredir(union node *n, const char *text, int err);
-
static void
-evalfor(n, flags)
- union node *n;
- int flags;
+evalfor(const union node *n, int flags)
{
struct arglist arglist;
union node *argp;
popstackmark(&smark);
}
-
-static void
-evalcase(n, flags)
- union node *n;
- int flags;
+static inline void
+evalcase(const union node *n, int flags)
{
union node *cp;
union node *patp;
}
/*
- * Kick off a subshell to evaluate a tree.
+ * Evaluate a pipeline. All the processes in the pipeline are children
+ * of the process creating the pipeline. (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
*/
-static void
-evalsubshell(n, flags)
+static inline void
+evalpipe(n)
union node *n;
- int flags;
{
struct job *jp;
- int backgnd = (n->type == NBACKGND);
-
- expredir(n->nredir.redirect);
- jp = makejob(n, 1);
- if (forkshell(jp, n, backgnd) == 0) {
- if (backgnd)
- flags &=~ EV_TESTED;
- redirect(n->nredir.redirect, 0);
- evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
- }
- if (! backgnd) {
- INTOFF;
- exitstatus = waitforjob(jp);
- INTON;
- }
-}
-
-
-
-/*
- * Compute the names of the files in a redirection list.
- */
-
-static void
-expredir(n)
- union node *n;
-{
- union node *redir;
-
- for (redir = n ; redir ; redir = redir->nfile.next) {
- struct arglist fn;
- fn.lastp = &fn.list;
- oexitstatus = exitstatus;
- switch (redir->type) {
- case NFROMTO:
- case NFROM:
- case NTO:
- case NAPPEND:
- case NTOOV:
- expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
- redir->nfile.expfname = fn.list->text;
- break;
- case NFROMFD:
- case NTOFD:
- if (redir->ndup.vname) {
- expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
- fixredir(redir, fn.list->text, 1);
- }
- break;
- }
- }
-}
-
-/*
- * Evaluate a pipeline. All the processes in the pipeline are children
- * of the process creating the pipeline. (This differs from some versions
- * of the shell, which make the last process in a pipeline the parent
- * of all the rest.)
- */
-
-static void
-evalpipe(n)
- union node *n;
-{
- struct job *jp;
- struct nodelist *lp;
- int pipelen;
- int prevfd;
- int pip[2];
+ struct nodelist *lp;
+ int pipelen;
+ int prevfd;
+ int pip[2];
TRACE(("evalpipe(0x%lx) called\n", (long)n));
pipelen = 0;
}
}
-
-
-/*
- * Execute a command inside back quotes. If it's a builtin command, we
- * want to save its output in a block obtained from malloc. Otherwise
- * we fork off a subprocess and get the output of the command via a pipe.
- * Should be called with interrupts off.
- */
-
-static void
-evalbackcmd(union node *n, struct backcmd *result)
-{
- int pip[2];
- struct job *jp;
- struct stackmark smark; /* unnecessary */
-
- setstackmark(&smark);
- result->fd = -1;
- result->buf = NULL;
- result->nleft = 0;
- result->jp = NULL;
- if (n == NULL) {
- exitstatus = 0;
- goto out;
- }
-#ifdef notyet
- /*
- * For now we disable executing builtins in the same
- * context as the shell, because we are not keeping
- * enough state to recover from changes that are
- * supposed only to affect subshells. eg. echo "`cd /`"
- */
- if (n->type == NCMD) {
- exitstatus = oexitstatus;
- evalcommand(n, EV_BACKCMD, result);
- } else
-#endif
- {
- exitstatus = 0;
- if (pipe(pip) < 0)
- error("Pipe call failed");
- jp = makejob(n, 1);
- if (forkshell(jp, n, FORK_NOJOB) == 0) {
- FORCEINTON;
- close(pip[0]);
- if (pip[1] != 1) {
- close(1);
- dup_as_newfd(pip[1], 1);
- close(pip[1]);
- }
- eflag = 0;
- evaltree(n, EV_EXIT);
- }
- close(pip[1]);
- result->fd = pip[0];
- result->jp = jp;
- }
-out:
- popstackmark(&smark);
- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
- result->fd, result->buf, result->nleft, result->jp));
-}
-
-
-
-/*
- * Execute a simple command.
- */
-
static void find_command (const char *, struct cmdentry *, int, const char *);
static int
return *word == '=';
}
+
static void
-#ifdef notyet
-evalcommand(cmd, flags, backcmd)
- union node *cmd;
- int flags;
- struct backcmd *backcmd;
-#else
-evalcommand(cmd, flags)
- union node *cmd;
- int flags;
-#endif
+evalcommand(union node *cmd, int flags)
{
struct stackmark smark;
union node *argp;
char **envp;
struct strlist *sp;
int mode;
-#ifdef notyet
- int pip[2];
-#endif
struct cmdentry cmdentry;
struct job *jp;
char *volatile savecmdname;
}
if (argp) {
struct builtincmd *bcmd;
- bool pseudovarflag;
+ int pseudovarflag;
bcmd = find_builtin(arglist.list->text);
pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
for (; argp; argp = argp->narg.next) {
/* Print the command if xflag is set. */
if (xflag) {
-#ifdef FLUSHERR
- outc('+', &errout);
-#else
- outcslow('+', &errout);
-#endif
+ out2c('+');
eprintlist(varlist.list);
eprintlist(arglist.list);
-#ifdef FLUSHERR
- outc('\n', &errout);
- flushout(&errout);
-#else
- outcslow('\n', &errout);
-#endif
+ out2c('\n');
}
/* Now locate the command. */
find_command(argv[0], &cmdentry, findflag, path);
if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
exitstatus = 127;
-#ifdef FLUSHERR
- flushout(&errout);
-#endif
goto out;
}
/* implement bltin and command here */
if (--argc == 0)
goto found;
if (!(bcmd = find_builtin(*argv))) {
- outfmt(&errout, "%s: not found\n", *argv);
+ out2fmt("%s: not found\n", *argv);
exitstatus = 127;
-#ifdef FLUSHERR
- flushout(&errout);
-#endif
goto out;
}
cmdentry.u.cmd = bcmd;
/* Fork off a child process if necessary. */
if (cmd->ncmd.backgnd
|| (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
-#ifdef notyet
- || ((flags & EV_BACKCMD) != 0
- && (cmdentry.cmdtype != CMDBUILTIN
- || cmdentry.u.bcmd == DOTCMD
- || cmdentry.u.bcmd == EVALCMD))
-#endif
) {
jp = makejob(cmd, 1);
mode = cmd->ncmd.backgnd;
-#ifdef notyet
- if (flags & EV_BACKCMD) {
- mode = FORK_NOJOB;
- if (pipe(pip) < 0)
- error("Pipe call failed");
- }
-#endif
if (forkshell(jp, cmd, mode) != 0)
goto parent; /* at end of routine */
-#ifdef notyet
- if (flags & EV_BACKCMD) {
- FORCEINTON;
- close(pip[0]);
- if (pip[1] != 1) {
- close(1);
- dup_as_newfd(pip[1], 1);
- close(pip[1]);
- }
- }
-#endif
flags |= EV_EXIT;
}
trputs("builtin command: "); trargs(argv);
#endif
mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
-#ifdef notyet
- if (flags == EV_BACKCMD) {
-#ifdef USE_GLIBC_STDIO
- openmemout();
-#else
- memout.nleft = 0;
- memout.nextc = memout.buf;
- memout.bufsize = 64;
-#endif
- mode |= REDIR_BACKQ;
- }
-#endif
redirect(cmd->ncmd.redirect, mode);
savecmdname = commandname;
if (IS_BUILTIN_SPECIAL(firstbltin)) {
exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
flushall();
cmddone:
- exitstatus |= outerr(out1);
- out1 = &output;
- out2 = &errout;
- freestdout();
cmdenviron = NULL;
if (e != EXSHELLPROC) {
commandname = savecmdname;
}
if (cmdentry.u.cmd != EXECCMD)
popredir();
-#ifdef notyet
- if (flags == EV_BACKCMD) {
- INTOFF;
-#ifdef USE_GLIBC_STDIO
- if (__closememout())
- error("__closememout() failed: %m");
-#endif
- backcmd->buf = memout.buf;
-#ifdef USE_GLIBC_STDIO
- backcmd->nleft = memout.bufsize;
-#else
- backcmd->nleft = memout.nextc - memout.buf;
-#endif
- memout.buf = NULL;
- INTON;
- }
-#endif
} else {
#ifdef DEBUG
trputs("normal command: "); trargs(argv);
INTOFF;
exitstatus = waitforjob(jp);
INTON;
-#ifdef notyet
- } else if (mode == 2) {
- backcmd->fd = pip[0];
- close(pip[1]);
- backcmd->jp = jp;
-#endif
}
out:
popstackmark(&smark);
}
-
-
/*
- * Search for a command. This is called before we fork so that the
- * location of the command will be available in the parent as well as
- * the child. The check for "goodname" is an overly conservative
- * check that the name will not be subject to expansion.
+ * Evaluate a parse tree. The value is left in the global variable
+ * exitstatus.
*/
-
static void
-prehash(n)
+evaltree(n, flags)
union node *n;
+ int flags;
{
- struct cmdentry entry;
-
- if (n->type == NCMD && n->ncmd.args)
- if (goodname(n->ncmd.args->narg.text))
- find_command(n->ncmd.args->narg.text, &entry, 0,
- pathval());
-}
-
-
-
-/*
- * Builtin commands. Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
- */
-
-/*
- * No command given, or a bltin command with no arguments. Set the
- * specified variables.
- */
-
-int
-bltincmd(argc, argv)
- int argc;
- char **argv;
-{
- /*
- * Preserve exitstatus of a previous possible redirection
- * as POSIX mandates
- */
- return exitstatus;
-}
-
-
-/*
- * Handle break and continue commands. Break, continue, and return are
- * all handled by setting the evalskip flag. The evaluation routines
- * above all check this flag, and if it is set they start skipping
- * commands rather than executing them. The variable skipcount is
- * the number of loops to break/continue, or the number of function
- * levels to return. (The latter is always 1.) It should probably
- * be an error to break out of more loops than exist, but it isn't
- * in the standard shell so we don't make it one here.
- */
-
-static int
-breakcmd(argc, argv)
- int argc;
- char **argv;
-{
- int n = argc > 1 ? number(argv[1]) : 1;
-
- if (n > loopnest)
- n = loopnest;
- if (n > 0) {
- evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
- skipcount = n;
- }
- return 0;
-}
-
-
-/*
- * The return command.
- */
-
-static int
-returncmd(argc, argv)
- int argc;
- char **argv;
-{
- int ret = argc > 1 ? number(argv[1]) : oexitstatus;
-
- if (funcnest) {
- evalskip = SKIPFUNC;
- skipcount = 1;
- return ret;
- }
- else {
- /* Do what ksh does; skip the rest of the file */
- evalskip = SKIPFILE;
- skipcount = 1;
- return ret;
+ int checkexit = 0;
+ if (n == NULL) {
+ TRACE(("evaltree(NULL) called\n"));
+ goto out;
}
-}
-
-
-#ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
-static int
-false_main(argc, argv)
- int argc;
+ TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
+ switch (n->type) {
+ case NSEMI:
+ evaltree(n->nbinary.ch1, flags & EV_TESTED);
+ if (evalskip)
+ goto out;
+ evaltree(n->nbinary.ch2, flags);
+ break;
+ case NAND:
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip || exitstatus != 0)
+ goto out;
+ evaltree(n->nbinary.ch2, flags);
+ break;
+ case NOR:
+ evaltree(n->nbinary.ch1, EV_TESTED);
+ if (evalskip || exitstatus == 0)
+ goto out;
+ evaltree(n->nbinary.ch2, flags);
+ break;
+ case NREDIR:
+ expredir(n->nredir.redirect);
+ redirect(n->nredir.redirect, REDIR_PUSH);
+ evaltree(n->nredir.n, flags);
+ popredir();
+ break;
+ case NSUBSHELL:
+ evalsubshell(n, flags);
+ break;
+ case NBACKGND:
+ evalsubshell(n, flags);
+ break;
+ case NIF: {
+ evaltree(n->nif.test, EV_TESTED);
+ if (evalskip)
+ goto out;
+ if (exitstatus == 0)
+ evaltree(n->nif.ifpart, flags);
+ else if (n->nif.elsepart)
+ evaltree(n->nif.elsepart, flags);
+ else
+ exitstatus = 0;
+ break;
+ }
+ case NWHILE:
+ case NUNTIL:
+ evalloop(n, flags);
+ break;
+ case NFOR:
+ evalfor(n, flags);
+ break;
+ case NCASE:
+ evalcase(n, flags);
+ break;
+ case NDEFUN: {
+ struct builtincmd *bcmd;
+ struct cmdentry entry;
+ if (
+ (bcmd = find_builtin(n->narg.text)) &&
+ IS_BUILTIN_SPECIAL(bcmd)
+ ) {
+ out2fmt("%s is a special built-in\n", n->narg.text);
+ exitstatus = 1;
+ break;
+ }
+ entry.cmdtype = CMDFUNCTION;
+ entry.u.func = copyfunc(n->narg.next);
+ addcmdentry(n->narg.text, &entry);
+ exitstatus = 0;
+ break;
+ }
+ case NNOT:
+ evaltree(n->nnot.com, EV_TESTED);
+ exitstatus = !exitstatus;
+ break;
+
+ case NPIPE:
+ evalpipe(n);
+ checkexit = 1;
+ break;
+ case NCMD:
+ evalcommand(n, flags);
+ checkexit = 1;
+ break;
+#ifdef DEBUG
+ default:
+ printf("Node type = %d\n", n->type);
+ break;
+#endif
+ }
+out:
+ if (pendingsigs)
+ dotrap();
+ if (
+ flags & EV_EXIT ||
+ (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
+ )
+ exitshell(exitstatus);
+}
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+static void
+evalsubshell(const union node *n, int flags)
+{
+ struct job *jp;
+ int backgnd = (n->type == NBACKGND);
+
+ expredir(n->nredir.redirect);
+ jp = makejob(n, 1);
+ if (forkshell(jp, n, backgnd) == 0) {
+ if (backgnd)
+ flags &=~ EV_TESTED;
+ redirect(n->nredir.redirect, 0);
+ evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
+ }
+ if (! backgnd) {
+ INTOFF;
+ exitstatus = waitforjob(jp);
+ INTON;
+ }
+}
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+static void fixredir(union node *n, const char *text, int err);
+
+static void
+expredir(union node *n)
+{
+ union node *redir;
+
+ for (redir = n ; redir ; redir = redir->nfile.next) {
+ struct arglist fn;
+ fn.lastp = &fn.list;
+ oexitstatus = exitstatus;
+ switch (redir->type) {
+ case NFROMTO:
+ case NFROM:
+ case NTO:
+ case NAPPEND:
+ case NTOOV:
+ expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+ redir->nfile.expfname = fn.list->text;
+ break;
+ case NFROMFD:
+ case NTOFD:
+ if (redir->ndup.vname) {
+ expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+ fixredir(redir, fn.list->text, 1);
+ }
+ break;
+ }
+ }
+}
+
+
+/*
+ * Execute a command inside back quotes. If it's a builtin command, we
+ * want to save its output in a block obtained from malloc. Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+ int pip[2];
+ struct job *jp;
+ struct stackmark smark; /* unnecessary */
+
+ setstackmark(&smark);
+ result->fd = -1;
+ result->buf = NULL;
+ result->nleft = 0;
+ result->jp = NULL;
+ if (n == NULL) {
+ exitstatus = 0;
+ goto out;
+ }
+ exitstatus = 0;
+ if (pipe(pip) < 0)
+ error("Pipe call failed");
+ jp = makejob(n, 1);
+ if (forkshell(jp, n, FORK_NOJOB) == 0) {
+ FORCEINTON;
+ close(pip[0]);
+ if (pip[1] != 1) {
+ close(1);
+ dup_as_newfd(pip[1], 1);
+ close(pip[1]);
+ }
+ eflag = 0;
+ evaltree(n, EV_EXIT);
+ }
+ close(pip[1]);
+ result->fd = pip[0];
+ result->jp = jp;
+out:
+ popstackmark(&smark);
+ TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+ result->fd, result->buf, result->nleft, result->jp));
+}
+
+
+/*
+ * Execute a simple command.
+ */
+
+/*
+ * Search for a command. This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child. The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+static void
+prehash(n)
+ union node *n;
+{
+ struct cmdentry entry;
+
+ if (n->type == NCMD && n->ncmd.args)
+ if (goodname(n->ncmd.args->narg.text))
+ find_command(n->ncmd.args->narg.text, &entry, 0,
+ pathval());
+}
+
+
+/*
+ * Builtin commands. Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given, or a bltin command with no arguments. Set the
+ * specified variables.
+ */
+
+int
+bltincmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ /*
+ * Preserve exitstatus of a previous possible redirection
+ * as POSIX mandates
+ */
+ return exitstatus;
+}
+
+
+/*
+ * Handle break and continue commands. Break, continue, and return are
+ * all handled by setting the evalskip flag. The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them. The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return. (The latter is always 1.) It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+
+static int
+breakcmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int n = argc > 1 ? number(argv[1]) : 1;
+
+ if (n > loopnest)
+ n = loopnest;
+ if (n > 0) {
+ evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+ skipcount = n;
+ }
+ return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+static int
+returncmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ret = argc > 1 ? number(argv[1]) : oexitstatus;
+
+ if (funcnest) {
+ evalskip = SKIPFUNC;
+ skipcount = 1;
+ return ret;
+ }
+ else {
+ /* Do what ksh does; skip the rest of the file */
+ evalskip = SKIPFILE;
+ skipcount = 1;
+ return ret;
+ }
+}
+
+
+#ifndef BB_TRUE_FALSE
+static int
+false_main(argc, argv)
+ int argc;
char **argv;
{
return 1;
return 0;
}
#endif
-#endif
/*
* Controls whether the shell is interactive or not.
setinteractive(int on)
{
static int is_interactive;
+ static int do_banner=0;
if (on == is_interactive)
return;
setsignal(SIGTERM);
chkmail(1);
is_interactive = on;
+ if (do_banner==0 && is_interactive) {
+ /* Looks like they want an interactive shell */
+ printf( "\n\n" BB_BANNER " Built-in shell (ash)\n");
+ printf( "Enter 'help' for a list of built-in commands.\n\n");
+ do_banner=1;
+ }
}
static void
eprintlist(struct strlist *sp)
{
for (; sp; sp = sp->next) {
- outfmt(&errout, " %s",sp->text);
+ out2fmt(" %s",sp->text);
}
}
-/*
- * When commands are first encountered, they are entered in a hash table.
- * This ensures that a full path search will not have to be done for them
- * on each invocation.
- *
- * We should investigate converting to a linear search, even though that
- * would make the command name "hash" a misnomer.
- */
-#define CMDTABLESIZE 31 /* should be prime */
-#define ARB 1 /* actual size determined at run time */
-
-
-
-struct tblentry {
- struct tblentry *next; /* next entry in hash chain */
- union param param; /* definition of builtin function */
- short cmdtype; /* index identifying command */
- char rehash; /* if set, cd done since entry created */
- char cmdname[ARB]; /* name of command */
-};
-
-
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1; /* index in path of %builtin, or -1 */
-static int exerrno = 0; /* Last exec error */
-
-
-static void tryexec (char *, char **, char **);
-static void printentry (struct tblentry *, int);
-static void clearcmdentry (int);
-static struct tblentry *cmdlookup (const char *, int);
-static void delete_cmd_entry (void);
-#ifdef ASH_TYPE
-static int describe_command (char *, int);
-#endif
-static int path_change (const char *, int *);
-
/*
* Exec a program. Never returns. If you change this routine, you may
static int preadbuffer(void);
static void pushfile (void);
-static int preadfd (void);
/*
* Read a character from the script, returning PEOF on end of file.
* Nul characters in the input are silently discarded.
*/
-#ifdef ASH_BBAPPS_AS_BUILTINS
+#ifndef ASH_OPTIMIZE_FOR_SIZE
#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
static int
pgetc(void)
static void
-tryexec(cmd, argv, envp)
- char *cmd;
- char **argv;
- char **envp;
- {
+tryexec(char *cmd, char **argv, char **envp)
+{
int e;
+#ifdef BB_FEATURE_SH_STANDALONE_SHELL
+ char *name = cmd;
+ char** argv_l=argv;
+ int argc_l;
+#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
+ name = get_last_path_component(name);
+#endif
+ argv_l=envp;
+ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
+ putenv(*argv_l);
+ argv_l=argv;
+ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
+ optind = 1;
+ run_applet_by_name(name, argc_l, argv);
+#endif
execve(cmd, argv, envp);
e = errno;
if (e == ENOEXEC) {
return stalloc(len);
}
+/*
+ * Wrapper around strcmp for qsort/bsearch/...
+ */
+static int
+pstrcmp(const void *a, const void *b)
+{
+ return strcmp((const char *) a, *(const char *const *) b);
+}
+
+/*
+ * Find a keyword is in a sorted array.
+ */
+
+static const char *const *
+findkwd(const char *s)
+{
+ return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
+ sizeof(const char *), pstrcmp);
+}
/*** Command hashing code ***/
int verbose;
struct cmdentry entry;
char *name;
+#ifdef ASH_ALIAS
+ const struct alias *ap;
+#endif
verbose = 0;
- while ((c = nextopt("rv")) != '\0') {
+ while ((c = nextopt("rvV")) != '\0') {
if (c == 'r') {
clearcmdentry(0);
return 0;
- } else if (c == 'v') {
- verbose++;
+ } else if (c == 'v' || c == 'V') {
+ verbose = c;
}
}
if (*argptr == NULL) {
return 0;
}
c = 0;
- while ((name = *argptr) != NULL) {
+ while ((name = *argptr++) != NULL) {
if ((cmdp = cmdlookup(name, 0)) != NULL
&& (cmdp->cmdtype == CMDNORMAL
|| (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
delete_cmd_entry();
+#ifdef ASH_ALIAS
+ /* Then look at the aliases */
+ if ((ap = lookupalias(name, 0)) != NULL) {
+ if (verbose=='v')
+ printf("%s is an alias for %s\n", name, ap->val);
+ else
+ printalias(ap);
+ continue;
+ }
+#endif
+ /* First look at the keywords */
+ if (findkwd(name)!=0) {
+ if (verbose=='v')
+ printf("%s is a shell keyword\n", name);
+ else
+ printf(snlfmt, name);
+ continue;
+ }
+
find_command(name, &entry, DO_ERR, pathval());
if (entry.cmdtype == CMDUNKNOWN) c = 1;
else if (verbose) {
cmdp = cmdlookup(name, 0);
- if (cmdp) printentry(cmdp, verbose);
+ if (cmdp) printentry(cmdp, verbose=='v');
flushall();
}
- argptr++;
}
return c;
}
-
static void
printentry(cmdp, verbose)
struct tblentry *cmdp;
const char *path;
char *name;
+ printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
if (cmdp->cmdtype == CMDNORMAL) {
idx = cmdp->param.index;
path = pathval();
name = padvance(&path, cmdp->cmdname);
stunalloc(name);
} while (--idx >= 0);
- out1str(name);
+ if(verbose)
+ out1str(name);
} else if (cmdp->cmdtype == CMDBUILTIN) {
- out1fmt("builtin %s", cmdp->cmdname);
+ if(verbose)
+ out1str("a shell builtin");
} else if (cmdp->cmdtype == CMDFUNCTION) {
- out1fmt("function %s", cmdp->cmdname);
if (verbose) {
INTOFF;
+ out1str("a function\n");
name = commandtext(cmdp->param.func);
- out1fmt(" %s", name);
+ printf("%s() {\n %s\n}", cmdp->cmdname, name);
ckfree(name);
INTON;
}
error("internal error: cmdtype %d", cmdp->cmdtype);
#endif
}
- out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
+ printf(snlfmt, cmdp->rehash ? "*" : nullstr);
}
+/*** List the available builtins ***/
+
+
+static int helpcmd(int argc, char** argv)
+{
+ int col, i;
+
+ printf("\nBuilt-in commands:\n-------------------\n");
+ for (col=0, i=0; i < NUMBUILTINS; i++) {
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ builtincmds[i].name+1);
+ if (col > 60) {
+ printf("\n");
+ col = 0;
+ }
+ }
+#ifdef BB_FEATURE_SH_STANDALONE_SHELL
+ {
+ extern const struct BB_applet applets[];
+ extern const size_t NUM_APPLETS;
+
+ for (i=0; i < NUM_APPLETS; i++) {
+
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ applets[i].name);
+ if (col > 60) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ }
+#endif
+ printf("\n\n");
+ return EXIT_SUCCESS;
+}
+
/*
* Resolve a command name. If you change this routine, you may have to
* change the shellexec routine as well.
int bltin;
int firstchange;
int updatetbl;
- bool regular;
+ int regular;
struct builtincmd *bcmd;
/* If name contains a slash, don't use the hash table */
if (cmdp && updatetbl)
delete_cmd_entry();
if (act & DO_ERR)
- outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+ out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
entry->cmdtype = CMDUNKNOWN;
return;
INTON;
}
-/*
- * Free a parse tree.
- */
-
-static void
-freefunc(union node *n)
-{
- if (n)
- ckfree(n);
-}
-
/*
* Delete all functions.
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name.
- */
-
-static void
-addcmdentry(char *name, struct cmdentry *entry)
-{
- struct tblentry *cmdp;
-
- INTOFF;
- cmdp = cmdlookup(name, 1);
- if (cmdp->cmdtype == CMDFUNCTION) {
- freefunc(cmdp->param.func);
- }
- cmdp->cmdtype = entry->cmdtype;
- cmdp->param = entry->u;
- INTON;
-}
-
-
-/*
- * Define a shell function.
- */
-static union node *copyfunc(union node *);
-static void
-defun(char *name, union node *func)
-{
- struct cmdentry entry;
+static const short nodesize[26] = {
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct ncmd)),
+ ALIGN(sizeof (struct npipe)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nredir)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nif)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nbinary)),
+ ALIGN(sizeof (struct nfor)),
+ ALIGN(sizeof (struct ncase)),
+ ALIGN(sizeof (struct nclist)),
+ ALIGN(sizeof (struct narg)),
+ ALIGN(sizeof (struct narg)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct nfile)),
+ ALIGN(sizeof (struct ndup)),
+ ALIGN(sizeof (struct ndup)),
+ ALIGN(sizeof (struct nhere)),
+ ALIGN(sizeof (struct nhere)),
+ ALIGN(sizeof (struct nnot)),
+};
- entry.cmdtype = CMDFUNCTION;
- entry.u.func = copyfunc(func);
- addcmdentry(name, &entry);
-}
/*
}
}
-/*
- * Wrapper around strcmp for qsort/bsearch/...
- */
-static int
-pstrcmp(const void *a, const void *b)
-{
- return strcmp((const char *) a, *(const char *const *) b);
-}
-
-/*
- * Find a keyword is in a sorted array.
- */
-
-static const char *const *
-findkwd(const char *s)
-{
- return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
- sizeof(const char *), pstrcmp);
-}
-#ifdef ASH_TYPE
/*
* Locate and print what a word is...
*/
static int
-typecmd(argc, argv)
- int argc;
- char **argv;
+typecmd(int argc, char **argv)
{
int i;
int err = 0;
+ char *argv_a[2];
+
+ argv_a[1] = 0;
for (i = 1; i < argc; i++) {
- err |= describe_command(argv[i], 1);
+ argv_a[0] = argv[i];
+ argptr = argv_a;
+ optptr = "v";
+ err |= hashcmd(argc, argv);
}
return err;
}
-static int
-describe_command(char *command, int verbose)
-{
- struct cmdentry entry;
- struct tblentry *cmdp;
-#ifdef ASH_ALIAS
- const struct alias *ap;
-#endif
- const char *path = pathval();
-
- if (verbose) {
- out1str(command);
- }
-
- /* First look at the keywords */
- if (findkwd(command)) {
- out1str(verbose ? " is a shell keyword" : command);
- goto out;
- }
-
-#ifdef ASH_ALIAS
- /* Then look at the aliases */
- if ((ap = lookupalias(command, 0)) != NULL) {
- if (verbose) {
- out1fmt(" is an alias for %s", ap->val);
- } else {
- printalias(ap);
- }
- goto out;
- }
-#endif
- /* Then check if it is a tracked alias */
- if ((cmdp = cmdlookup(command, 0)) != NULL) {
- entry.cmdtype = cmdp->cmdtype;
- entry.u = cmdp->param;
- } else {
- /* Finally use brute force */
- find_command(command, &entry, DO_ABS, path);
- }
-
- switch (entry.cmdtype) {
- case CMDNORMAL: {
- int j = entry.u.index;
- char *p;
- if (j == -1) {
- p = command;
- } else {
- do {
- p = padvance(&path, command);
- stunalloc(p);
- } while (--j >= 0);
- }
- if (verbose) {
- out1fmt(
- " is%s %s",
- cmdp ? " a tracked alias for" : nullstr, p
- );
- } else {
- out1str(p);
- }
- break;
- }
-
- case CMDFUNCTION:
- if (verbose) {
- out1str(" is a shell function");
- } else {
- out1str(command);
- }
- break;
-
- case CMDBUILTIN:
- if (verbose) {
- out1fmt(
- " is a %sshell builtin",
- IS_BUILTIN_SPECIAL(entry.u.cmd) ?
- "special " : nullstr
- );
- } else {
- out1str(command);
- }
- break;
-
- default:
- if (verbose) {
- out1str(": not found\n");
- }
- return 127;
- }
-
-out:
- out1c('\n');
- return 0;
-}
-#endif
-
#ifdef ASH_CMDCMD
static int
commandcmd(argc, argv)
case 'V':
verbose_verify_only = 1;
break;
- default:
- outfmt(out2,
-"command: nextopt returned character code 0%o\n", c);
- return EX_SOFTWARE;
}
if (default_path + verify_only + verbose_verify_only > 1 ||
!*argptr) {
- outfmt(out2,
-"command [-p] command [arg ...]\n");
- outfmt(out2,
-"command {-v|-V} command\n");
+ out2str(
+ "command [-p] command [arg ...]\n"
+ "command {-v|-V} command\n");
return EX_USAGE;
}
-#ifdef ASH_TYPE
if (verify_only || verbose_verify_only) {
- return describe_command(*argptr, verbose_verify_only);
+ char *argv_a[2];
+
+ argv_a[1] = 0;
+ argv_a[0] = *argptr;
+ argptr = argv_a;
+ optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
+ return hashcmd(argc, argv);
}
-#endif
return 0;
}
static char *exptilde (char *, int);
static void expbackq (union node *, int, int);
static int subevalvar (char *, char *, int, int, int, int, int);
-static char *evalvar (char *, int);
static int varisset (char *, int);
static void strtodest (const char *, const char *, int);
static void varvalue (char *, int, int);
static void ifsbreakup (char *, struct arglist *);
static void ifsfree (void);
static void expandmeta (struct strlist *, int);
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
#if !defined(GLOB_BROKEN)
static void addglob (const glob_t *);
#endif
#endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static void expmeta (char *, char *);
#endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static struct strlist *expsort (struct strlist *);
static struct strlist *msort (struct strlist *, int);
#endif
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
static int patmatch (char *, char *, int);
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
static int patmatch2 (char *, char *, int);
#else
static int pmatch (char *, char *, int);
*/
/* arg: the document, fd: where to write the expanded version */
-static void
+static inline void
expandhere(union node *arg, int fd)
{
herefd = fd;
* here document expansion.
*/
-static void
-expandarg(arg, arglist, flag)
- union node *arg;
- struct arglist *arglist;
- int flag;
-{
- struct strlist *sp;
- char *p;
+static void
+expandarg(arg, arglist, flag)
+ union node *arg;
+ struct arglist *arglist;
+ int flag;
+{
+ struct strlist *sp;
+ char *p;
+
+ argbackq = arg->narg.backquote;
+ STARTSTACKSTR(expdest);
+ ifsfirst.next = NULL;
+ ifslastp = NULL;
+ argstr(arg->narg.text, flag);
+ if (arglist == NULL) {
+ return; /* here document expanded */
+ }
+ STPUTC('\0', expdest);
+ p = grabstackstr(expdest);
+ exparg.lastp = &exparg.list;
+ /*
+ * TODO - EXP_REDIR
+ */
+ if (flag & EXP_FULL) {
+ ifsbreakup(p, &exparg);
+ *exparg.lastp = NULL;
+ exparg.lastp = &exparg.list;
+ expandmeta(exparg.list, flag);
+ } else {
+ if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+ rmescapes(p);
+ sp = (struct strlist *)stalloc(sizeof (struct strlist));
+ sp->text = p;
+ *exparg.lastp = sp;
+ exparg.lastp = &sp->next;
+ }
+ ifsfree();
+ *exparg.lastp = NULL;
+ if (exparg.list) {
+ *arglist->lastp = exparg.list;
+ arglist->lastp = exparg.lastp;
+ }
+}
+
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+
+static inline char *
+evalvar(p, flag)
+ char *p;
+ int flag;
+{
+ int subtype;
+ int varflags;
+ char *var;
+ const char *val;
+ int patloc;
+ int c;
+ int set;
+ int special;
+ int startloc;
+ int varlen;
+ int easy;
+ int quotes = flag & (EXP_FULL | EXP_CASE);
+
+ varflags = *p++;
+ subtype = varflags & VSTYPE;
+ var = p;
+ special = 0;
+ if (! is_name(*p))
+ special = 1;
+ p = strchr(p, '=') + 1;
+again: /* jump here after setting a variable with ${var=text} */
+ if (special) {
+ set = varisset(var, varflags & VSNUL);
+ val = NULL;
+ } else {
+ val = lookupvar(var);
+ if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+ val = NULL;
+ set = 0;
+ } else
+ set = 1;
+ }
+ varlen = 0;
+ startloc = expdest - stackblock();
+ if (set && subtype != VSPLUS) {
+ /* insert the value of the variable */
+ if (special) {
+ varvalue(var, varflags & VSQUOTE, flag);
+ if (subtype == VSLENGTH) {
+ varlen = expdest - stackblock() - startloc;
+ STADJUST(-varlen, expdest);
+ }
+ } else {
+ if (subtype == VSLENGTH) {
+ varlen = strlen(val);
+ } else {
+ strtodest(
+ val,
+ varflags & VSQUOTE ?
+ DQSYNTAX : BASESYNTAX,
+ quotes
+ );
+ }
+ }
+ }
+
+ if (subtype == VSPLUS)
+ set = ! set;
+
+ easy = ((varflags & VSQUOTE) == 0 ||
+ (*var == '@' && shellparam.nparam != 1));
+
+
+ switch (subtype) {
+ case VSLENGTH:
+ expdest = cvtnum(varlen, expdest);
+ goto record;
+
+ case VSNORMAL:
+ if (!easy)
+ break;
+record:
+ recordregion(startloc, expdest - stackblock(),
+ varflags & VSQUOTE);
+ break;
+
+ case VSPLUS:
+ case VSMINUS:
+ if (!set) {
+ argstr(p, flag);
+ break;
+ }
+ if (easy)
+ goto record;
+ break;
+
+ case VSTRIMLEFT:
+ case VSTRIMLEFTMAX:
+ case VSTRIMRIGHT:
+ case VSTRIMRIGHTMAX:
+ if (!set)
+ break;
+ /*
+ * Terminate the string and start recording the pattern
+ * right after it
+ */
+ STPUTC('\0', expdest);
+ patloc = expdest - stackblock();
+ if (subevalvar(p, NULL, patloc, subtype,
+ startloc, varflags, quotes) == 0) {
+ int amount = (expdest - stackblock() - patloc) + 1;
+ STADJUST(-amount, expdest);
+ }
+ /* Remove any recorded regions beyond start of variable */
+ removerecordregions(startloc);
+ goto record;
+
+ case VSASSIGN:
+ case VSQUESTION:
+ if (!set) {
+ if (subevalvar(p, var, 0, subtype, startloc,
+ varflags, quotes)) {
+ varflags &= ~VSNUL;
+ /*
+ * Remove any recorded regions beyond
+ * start of variable
+ */
+ removerecordregions(startloc);
+ goto again;
+ }
+ break;
+ }
+ if (easy)
+ goto record;
+ break;
- argbackq = arg->narg.backquote;
- STARTSTACKSTR(expdest);
- ifsfirst.next = NULL;
- ifslastp = NULL;
- argstr(arg->narg.text, flag);
- if (arglist == NULL) {
- return; /* here document expanded */
- }
- STPUTC('\0', expdest);
- p = grabstackstr(expdest);
- exparg.lastp = &exparg.list;
- /*
- * TODO - EXP_REDIR
- */
- if (flag & EXP_FULL) {
- ifsbreakup(p, &exparg);
- *exparg.lastp = NULL;
- exparg.lastp = &exparg.list;
- expandmeta(exparg.list, flag);
- } else {
- if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
- rmescapes(p);
- sp = (struct strlist *)stalloc(sizeof (struct strlist));
- sp->text = p;
- *exparg.lastp = sp;
- exparg.lastp = &sp->next;
+#ifdef DEBUG
+ default:
+ abort();
+#endif
}
- ifsfree();
- *exparg.lastp = NULL;
- if (exparg.list) {
- *arglist->lastp = exparg.list;
- arglist->lastp = exparg.lastp;
+
+ if (subtype != VSNORMAL) { /* skip to end of alternative */
+ int nesting = 1;
+ for (;;) {
+ if ((c = *p++) == CTLESC)
+ p++;
+ else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+ if (set)
+ argbackq = argbackq->next;
+ } else if (c == CTLVAR) {
+ if ((*p++ & VSTYPE) != VSNORMAL)
+ nesting++;
+ } else if (c == CTLENDVAR) {
+ if (--nesting == 0)
+ break;
+ }
+ }
}
+ return p;
}
-
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
if (quotes)
rmescapes(p+2);
result = arith(p+2);
- fmtstr(p, 12, "%d", result);
+ snprintf(p, 12, "%d", result);
while (*p++)
;
case VSQUESTION:
if (*p != CTLENDVAR) {
- outfmt(&errout, snlfmt, startp);
+ out2fmt(snlfmt, startp);
error((char *)NULL);
}
error("%.*s: parameter %snot set", p - str - 1,
while (loc != str - 1)
*startp++ = *loc++;
return 1;
-
-recordright:
- amount = loc - expdest;
- STADJUST(amount, expdest);
- STPUTC('\0', expdest);
- STADJUST(-1, expdest);
- return 1;
-}
-
-
-/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
- */
-
-static char *
-evalvar(p, flag)
- char *p;
- int flag;
-{
- int subtype;
- int varflags;
- char *var;
- char *val;
- int patloc;
- int c;
- int set;
- int special;
- int startloc;
- int varlen;
- int easy;
- int quotes = flag & (EXP_FULL | EXP_CASE);
-
- varflags = *p++;
- subtype = varflags & VSTYPE;
- var = p;
- special = 0;
- if (! is_name(*p))
- special = 1;
- p = strchr(p, '=') + 1;
-again: /* jump here after setting a variable with ${var=text} */
- if (special) {
- set = varisset(var, varflags & VSNUL);
- val = NULL;
- } else {
- val = lookupvar(var);
- if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
- val = NULL;
- set = 0;
- } else
- set = 1;
- }
- varlen = 0;
- startloc = expdest - stackblock();
- if (set && subtype != VSPLUS) {
- /* insert the value of the variable */
- if (special) {
- varvalue(var, varflags & VSQUOTE, flag);
- if (subtype == VSLENGTH) {
- varlen = expdest - stackblock() - startloc;
- STADJUST(-varlen, expdest);
- }
- } else {
- if (subtype == VSLENGTH) {
- varlen = strlen(val);
- } else {
- strtodest(
- val,
- varflags & VSQUOTE ?
- DQSYNTAX : BASESYNTAX,
- quotes
- );
- }
- }
- }
-
- if (subtype == VSPLUS)
- set = ! set;
-
- easy = ((varflags & VSQUOTE) == 0 ||
- (*var == '@' && shellparam.nparam != 1));
-
-
- switch (subtype) {
- case VSLENGTH:
- expdest = cvtnum(varlen, expdest);
- goto record;
-
- case VSNORMAL:
- if (!easy)
- break;
-record:
- recordregion(startloc, expdest - stackblock(),
- varflags & VSQUOTE);
- break;
-
- case VSPLUS:
- case VSMINUS:
- if (!set) {
- argstr(p, flag);
- break;
- }
- if (easy)
- goto record;
- break;
-
- case VSTRIMLEFT:
- case VSTRIMLEFTMAX:
- case VSTRIMRIGHT:
- case VSTRIMRIGHTMAX:
- if (!set)
- break;
- /*
- * Terminate the string and start recording the pattern
- * right after it
- */
- STPUTC('\0', expdest);
- patloc = expdest - stackblock();
- if (subevalvar(p, NULL, patloc, subtype,
- startloc, varflags, quotes) == 0) {
- int amount = (expdest - stackblock() - patloc) + 1;
- STADJUST(-amount, expdest);
- }
- /* Remove any recorded regions beyond start of variable */
- removerecordregions(startloc);
- goto record;
-
- case VSASSIGN:
- case VSQUESTION:
- if (!set) {
- if (subevalvar(p, var, 0, subtype, startloc,
- varflags, quotes)) {
- varflags &= ~VSNUL;
- /*
- * Remove any recorded regions beyond
- * start of variable
- */
- removerecordregions(startloc);
- goto again;
- }
- break;
- }
- if (easy)
- goto record;
- break;
-
-#ifdef DEBUG
- default:
- abort();
-#endif
- }
-
- if (subtype != VSNORMAL) { /* skip to end of alternative */
- int nesting = 1;
- for (;;) {
- if ((c = *p++) == CTLESC)
- p++;
- else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (set)
- argbackq = argbackq->next;
- } else if (c == CTLVAR) {
- if ((*p++ & VSTYPE) != VSNORMAL)
- nesting++;
- } else if (c == CTLENDVAR) {
- if (--nesting == 0)
- break;
- }
- }
- }
- return p;
+
+recordright:
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+ STPUTC('\0', expdest);
+ STADJUST(-1, expdest);
+ return 1;
}
+
/*
* Test whether a specialized variable is set.
*/
* should be escapes. The results are stored in the list exparg.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
static void
expandmeta(str, flag)
struct strlist *str;
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
/*
* Sort the results of file name expansion. It calculates the number of
* strings to sort and then calls msort (short for merge sort) to do the
* Returns true if the pattern matches the string.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
/* squoted: string might have quote chars */
static int
patmatch(char *pattern, char *string, int squoted)
* Remove any CTLESC characters from a string.
*/
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
static char *
_rmescapes(char *str, int flag)
{
size_t len = p - str;
q = r = stalloc(strlen(p) + len + 1);
if (len > 0) {
-#ifdef _GNU_SOURCE
- q = mempcpy(q, str, len);
-#else
memcpy(q, str, len);
q += len;
-#endif
}
}
while (*p) {
}
-static int whichprompt; /* 1 == PS1, 2 == PS2 */
-
-
struct redirtab {
struct redirtab *next;
- short renamed[10];
+ /* short renamed[10]; *//* Current ash support only 0-9 descriptors */
+ char renamed[10];
};
static struct redirtab *redirlist;
basepf.nextc = basepf.buf = basebuf;
}
- /* from output.c: */
- {
-#ifdef USE_GLIBC_STDIO
- initstreams();
-#endif
- }
-
/* from var.c: */
{
char **envp;
}
}
- fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
+ snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
setvar("PPID", ppid, 0);
}
}
popredir();
}
- /* from output.c: */
- {
- out1 = &output;
- out2 = &errout;
-#ifdef USE_GLIBC_STDIO
- if (memout.stream != NULL)
- __closememout();
-#endif
- if (memout.buf != NULL) {
- ckfree(memout.buf);
- memout.buf = NULL;
- }
- }
}
* Read a line from the script.
*/
-static char *
+static inline char *
pfgets(char *line, int len)
{
char *p = line;
return line;
}
-static int
+static inline int
preadfd(void)
{
int nr;
}
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
return PEOF;
- flushout(&output);
-#ifdef FLUSHERR
- flushout(&errout);
-#endif
+ flushall();
again:
if (parselleft <= 0) {
if (vflag) {
out2str(parsenextc);
-#ifdef FLUSHERR
- flushout(out2);
-#endif
}
*q = savec;
}
-
-
/*
* Like setinputfile, but takes input from a string.
*/
static void
-setinputstring(string)
- char *string;
- {
+setinputstring(char *string)
+{
INTOFF;
pushfile();
parsenextc = string;
static void freejob (struct job *);
static struct job *getjob (const char *);
static int dowait (int, struct job *);
-static int waitproc (int, int *);
static void waitonint(int);
return fd0_redirected != 0;
}
-/*
- * We also keep track of where fileno2 goes.
- */
-static int fileno2 = 2;
-
-static int openredirect (union node *);
-static void dupredirect (union node *, int, char[10 ]);
-static int openhere (union node *);
-static int noclobberopen (const char *);
-
-
+static void dupredirect (const union node *, int, int fd1dup);
#ifdef JOBS
/*
if (enable) {
do { /* while we are in the background */
#ifdef OLD_TTY_DRIVER
- if (ioctl(fileno2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
+ if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
#else
- initialpgrp = tcgetpgrp(fileno2);
+ initialpgrp = tcgetpgrp(2);
if (initialpgrp < 0) {
#endif
out2str("sh: can't access tty; job cenabletrol turned off\n");
}
} while (0);
#ifdef OLD_TTY_DRIVER
- if (ioctl(fileno2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
+ if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
mflag = 0;
return;
setsignal(SIGTTIN);
setpgid(0, rootpid);
#ifdef OLD_TTY_DRIVER
- ioctl(fileno2, TIOCSPGRP, (char *)&rootpid);
+ ioctl(2, TIOCSPGRP, (char *)&rootpid);
#else
- tcsetpgrp(fileno2, rootpid);
+ tcsetpgrp(2, rootpid);
#endif
} else { /* turning job cenabletrol off */
setpgid(0, initialpgrp);
#ifdef OLD_TTY_DRIVER
- ioctl(fileno2, TIOCSPGRP, (char *)&initialpgrp);
+ ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
#else
- tcsetpgrp(fileno2, initialpgrp);
+ tcsetpgrp(2, initialpgrp);
#endif
setsignal(SIGTSTP);
setsignal(SIGTTOU);
"SIGIO",
"SIGPWR",
"SIGSYS",
+#ifdef SIGRTMIN
"SIGRTMIN",
"SIGRTMIN+1",
"SIGRTMIN+2",
"SIGRTMAX-2",
"SIGRTMAX-1",
"SIGRTMAX",
+#endif
"DEBUG",
(char *)0x0,
};
if (!*argptr) {
out1str("0\n");
for (i = 1; i < NSIG; i++) {
- out1fmt(snlfmt, signal_names[i] + 3);
+ printf(snlfmt, signal_names[i] + 3);
}
return 0;
}
if (signo > 128)
signo -= 128;
if (0 < signo && signo < NSIG)
- out1fmt(snlfmt, signal_names[signo] + 3);
+ printf(snlfmt, signal_names[signo] + 3);
else
error("invalid signal number or exit status: %s",
*argptr);
error("job not created under job control");
pgrp = jp->ps[0].pid;
#ifdef OLD_TTY_DRIVER
- ioctl(fileno2, TIOCSPGRP, (char *)&pgrp);
+ ioctl(2, TIOCSPGRP, (char *)&pgrp);
#else
- tcsetpgrp(fileno2, pgrp);
+ tcsetpgrp(2, pgrp);
#endif
restartjob(jp);
INTOFF;
procno = jp->nprocs;
for (ps = jp->ps ; ; ps++) { /* for each process */
if (ps == jp->ps)
- fmtstr(s, 64, "[%d] %ld ", jobno,
+ snprintf(s, 64, "[%d] %ld ", jobno,
(long)ps->pid);
else
- fmtstr(s, 64, " %ld ",
+ snprintf(s, 64, " %ld ",
(long)ps->pid);
out1str(s);
col = strlen(s);
if (ps->status == -1) {
/* don't print anything */
} else if (WIFEXITED(ps->status)) {
- fmtstr(s, 64, "Exit %d",
+ snprintf(s, 64, "Exit %d",
WEXITSTATUS(ps->status));
} else {
#ifdef JOBS
if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
strcpy(s, sys_siglist[i & 0x7F]);
else
- fmtstr(s, 64, "Signal %d", i & 0x7F);
+ snprintf(s, 64, "Signal %d", i & 0x7F);
if (WCOREDUMP(ps->status))
strcat(s, " (core dumped)");
}
out1str(s);
col += strlen(s);
- out1fmt(
+ printf(
"%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
ps->cmd
);
*/
static void
-freejob(jp)
- struct job *jp;
- {
- struct procstat *ps;
+freejob(struct job *jp)
+{
+ const struct procstat *ps;
int i;
INTOFF;
*/
static struct job *
-makejob(node, nprocs)
- union node *node;
- int nprocs;
+makejob(const union node *node, int nprocs)
{
int i;
struct job *jp;
static int
-forkshell(struct job *jp, union node *n, int mode)
+forkshell(struct job *jp, const union node *n, int mode)
{
int pid;
+#ifdef JOBS
int pgrp;
+#endif
const char *devnull = _PATH_DEVNULL;
const char *nullerr = "Can't open %s";
if (mode == FORK_FG) {
/*** this causes superfluous TIOCSPGRPS ***/
#ifdef OLD_TTY_DRIVER
- if (ioctl(fileno2, TIOCSPGRP, (char *)&pgrp) < 0)
+ if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
error("TIOCSPGRP failed, errno=%d", errno);
#else
- if (tcsetpgrp(fileno2, pgrp) < 0)
+ if (tcsetpgrp(2, pgrp) < 0)
error("tcsetpgrp failed, errno=%d", errno);
#endif
}
}
return pid;
}
+#ifdef JOBS
if (rootshell && mode != FORK_NOJOB && mflag) {
if (jp == NULL || jp->nprocs == 0)
pgrp = pid;
pgrp = jp->ps[0].pid;
setpgid(pid, pgrp);
}
+#endif
if (mode == FORK_BG)
backgndpid = pid; /* set $! */
if (jp) {
*/
static int
-waitforjob(jp)
- struct job *jp;
- {
+waitforjob(struct job *jp)
+{
#ifdef JOBS
int mypgrp = getpgrp();
#endif
#ifdef JOBS
if (jp->jobctl) {
#ifdef OLD_TTY_DRIVER
- if (ioctl(fileno2, TIOCSPGRP, (char *)&mypgrp) < 0)
+ if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
error("TIOCSPGRP failed, errno=%d\n", errno);
#else
- if (tcsetpgrp(fileno2, mypgrp) < 0)
+ if (tcsetpgrp(2, mypgrp) < 0)
error("tcsetpgrp failed, errno=%d\n", errno);
#endif
}
* Wait for a process to terminate.
*/
+/*
+ * 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.
+ *
+ */
+
+static inline int
+waitproc(int block, int *status)
+{
+ int flags;
+
+ flags = 0;
+#ifdef JOBS
+ if (jobctl)
+ flags |= WUNTRACED;
+#endif
+ if (block == 0)
+ flags |= WNOHANG;
+ return wait3(status, flags, (struct rusage *)NULL);
+}
+
static int
-dowait(block, job)
- int block;
- struct job *job;
+dowait(int block, struct job *job)
{
int pid;
int status;
if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
if (thisjob != job)
- outfmt(out2, "%d: ", pid);
+ out2fmt("%d: ", pid);
#ifdef JOBS
if (sig == SIGTSTP && rootshell && iflag)
- outfmt(out2, "%%%ld ",
+ out2fmt("%%%ld ",
(long)(job - jobtab + 1));
#endif
if (sig < NSIG && sys_siglist[sig])
out2str(sys_siglist[sig]);
else
- outfmt(out2, "Signal %d", sig);
+ out2fmt("Signal %d", sig);
if (core)
out2str(" - core dumped");
out2c('\n');
-#ifdef FLUSHERR
- flushout(&errout);
-#endif
} else {
TRACE(("Not printing status: status=%d, sig=%d\n",
status, sig));
-/*
- * 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.
- *
- */
-
-static int
-waitproc(block, status)
- int block;
- int *status;
-{
- int flags;
-
- flags = 0;
-#ifdef JOBS
- if (jobctl)
- flags |= WUNTRACED;
-#endif
- if (block == 0)
- flags |= WNOHANG;
- return wait3(status, flags, (struct rusage *)NULL);
-}
/*
* return 1 if there are stopped jobs, otherwise 0
if (stat(p, &statb) < 0)
statb.st_size = 0;
if (statb.st_size > mailtime[i] && ! silent) {
- outfmt(
- &errout, snlfmt,
- pathopt? pathopt : "you have mail"
- );
+ out2fmt(snlfmt,
+ pathopt? pathopt : "you have mail");
}
mailtime[i] = statb.st_size;
}
#endif
static void read_profile (const char *);
-static char *find_dot_file (char *);
static void cmdloop (int);
static void options (int);
-static void minus_o (char *, int);
static void setoption (int, int);
static void procargs (int, char **);
struct jmploc jmploc;
struct stackmark smark;
volatile int state;
- char *shinit;
+ const char *shinit;
- DOTCMD = find_builtin(".");
BLTINCMD = find_builtin("builtin");
EXECCMD = find_builtin("exec");
EVALCMD = find_builtin("eval");
+#ifndef BB_FEATURE_SH_FANCY_PROMPT
+ unsetenv("PS1");
+ unsetenv("PS2");
+#endif
+
#if PROFILE
monitor(4, etext, profile_buf, sizeof profile_buf, 50);
#endif
reset();
if (exception == EXINT) {
out2c('\n');
-#ifdef FLUSHERR
- flushout(out2);
-#endif
}
popstackmark(&smark);
FORCEINTON; /* enable interrupts */
inter++;
showjobs(1);
chkmail(0);
- flushout(&output);
+ flushall();
}
n = parsecmd(inter);
/* showtree(n); DEBUG */
*/
-static char *
+static inline char *
find_dot_file(mybasename)
char *mybasename;
{
exitshell(exitstatus);
/* NOTREACHED */
}
+
static pointer
stalloc(int nbytes)
{
#undef rflag
-#ifdef __GLIBC__
+//#ifdef __GLIBC__
static mode_t getmode(const void *, mode_t);
static void *setmode(const char *);
+//#endif
#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
-typedef enum __rlimit_resource rlim_t;
-#endif
+typedef long rlim_t;
#endif
int i;
int symbolic_mode = 0;
- while ((i = nextopt("S")) != '\0') {
+ while (nextopt("S") != '\0') {
symbolic_mode = 1;
}
o[i++] = 'x';
o[i] = '\0';
- out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+ printf("u=%s,g=%s,o=%s\n", u, g, o);
} else {
- out1fmt("%.4o\n", mask);
+ printf("%.4o\n", mask);
}
} else {
- if (isdigit((unsigned char)*ap)) {
+ if (is_digit((unsigned char)*ap)) {
mask = 0;
do {
if (*ap >= '8' || *ap < '0')
else if (how & HARD)
val = limit.rlim_max;
- out1fmt("%-20s ", l->name);
+ printf("%-20s ", l->name);
if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
+ printf("unlimited\n");
else
{
val /= l->factor;
- out1fmt("%lld\n", (long long) val);
+ printf("%lld\n", (long long) val);
}
}
return 0;
val = limit.rlim_max;
if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
+ printf("unlimited\n");
else
{
val /= l->factor;
- out1fmt("%lld\n", (long long) val);
+ printf("%lld\n", (long long) val);
}
}
return 0;
*/
static int
-prefix(pfx, string)
- char const *pfx;
- char const *string;
- {
+prefix(char const *pfx, char const *string)
+{
while (*pfx) {
if (*pfx++ != *string++)
return 0;
if (len1) {
*p = '\'';
-#ifdef _GNU_SOURCE
- q = mempcpy(p + 1, s, len1);
-#else
q = p + 1 + len1;
memcpy(p + 1, s, len1);
-#endif
*q++ = '\'';
s += len1;
}
break;
default:
*q = '"';
-#ifdef _GNU_SOURCE
- *(char *) mempcpy(q + 1, s, len2) = '"';
-#else
q += 1 + len2;
memcpy(q + 1, s, len2);
*q = '"';
-#endif
s += len2;
}
}
-/*
- * This file was generated by the mknodes program.
- */
-
/*
* Routine for dealing with parsed shell commands.
*/
-static int funcblocksize; /* size of structures in function */
-static int funcstringsize; /* size of strings in node */
-static pointer funcblock; /* block to allocate function from */
-static char *funcstring; /* block to allocate strings from */
-
-static const short nodesize[26] = {
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct ncmd)),
- ALIGN(sizeof (struct npipe)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nredir)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nif)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nbinary)),
- ALIGN(sizeof (struct nfor)),
- ALIGN(sizeof (struct ncase)),
- ALIGN(sizeof (struct nclist)),
- ALIGN(sizeof (struct narg)),
- ALIGN(sizeof (struct narg)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct nfile)),
- ALIGN(sizeof (struct ndup)),
- ALIGN(sizeof (struct ndup)),
- ALIGN(sizeof (struct nhere)),
- ALIGN(sizeof (struct nhere)),
- ALIGN(sizeof (struct nnot)),
-};
-
-
-static void calcsize (union node *);
-static void sizenodelist (struct nodelist *);
-static union node *copynode (union node *);
-static struct nodelist *copynodelist (struct nodelist *);
-static char *nodesavestr (char *);
-
-
-
-/*
- * Make a copy of a parse tree.
- */
-
-static union node *
-copyfunc(union node *n)
-{
- if (n == NULL)
- return NULL;
- funcblocksize = 0;
- funcstringsize = 0;
- calcsize(n);
- funcblock = ckmalloc(funcblocksize + funcstringsize);
- funcstring = (char *) funcblock + funcblocksize;
- return copynode(n);
-}
-
-
+static void sizenodelist (const struct nodelist *);
+static struct nodelist *copynodelist (const struct nodelist *);
+static char *nodesavestr (const char *);
static void
-calcsize(n)
- union node *n;
+calcsize(const union node *n)
{
if (n == NULL)
return;
};
}
-
-
static void
-sizenodelist(lp)
- struct nodelist *lp;
+sizenodelist(const struct nodelist *lp)
{
while (lp) {
funcblocksize += ALIGN(sizeof(struct nodelist));
}
-
static union node *
-copynode(n)
- union node *n;
+copynode(const union node *n)
{
- union node *new;
+ union node *new;
if (n == NULL)
return NULL;
break;
};
new->type = n->type;
- return new;
+ return new;
}
static struct nodelist *
-copynodelist(lp)
- struct nodelist *lp;
+copynodelist(const struct nodelist *lp)
{
struct nodelist *start;
struct nodelist **lpp;
}
-
static char *
-nodesavestr(s)
- char *s;
+nodesavestr(const char *s)
{
#ifdef _GNU_SOURCE
char *rtn = funcstring;
funcstring = stpcpy(funcstring, s) + 1;
return rtn;
#else
- register char *p = s;
- register char *q = funcstring;
+ const char *p = s;
+ char *q = funcstring;
char *rtn = funcstring;
while ((*q++ = *p++) != '\0')
* to the argument list; we advance it past the options.
*/
+static inline void
+minus_o(const char *name, int val)
+{
+ int i;
+
+ if (name == NULL) {
+ out1str("Current option settings\n");
+ for (i = 0; i < NOPTS; i++)
+ printf("%-16s%s\n", optent_name(optlist[i]),
+ optent_val(i) ? "on" : "off");
+ } else {
+ for (i = 0; i < NOPTS; i++)
+ if (equal(name, optent_name(optlist[i]))) {
+ setoption(optent_letter(optlist[i]), val);
+ return;
+ }
+ error("Illegal option -o %s", name);
+ }
+}
+
+
static void
-options(cmdline)
- int cmdline;
+options(int cmdline)
{
char *p;
int val;
if (q == NULL || minusc != NULL)
error("Bad -c option");
minusc = q;
-#ifdef NOHACK
- break;
-#endif
- } else if (c == 'o') {
- minus_o(*argptr, val);
- if (*argptr)
- argptr++;
- } else {
- setoption(c, val);
- }
- }
- }
-}
-
-static void
-minus_o(name, val)
- char *name;
- int val;
-{
- int i;
-
- if (name == NULL) {
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optent_name(optlist[i]),
- optent_val(i) ? "on" : "off");
- } else {
- for (i = 0; i < NOPTS; i++)
- if (equal(name, optent_name(optlist[i]))) {
- setoption(optent_letter(optlist[i]), val);
- return;
+#ifdef NOHACK
+ break;
+#endif
+ } else if (c == 'o') {
+ minus_o(*argptr, val);
+ if (*argptr)
+ argptr++;
+ } else {
+ setoption(c, val);
}
- error("Illegal option -o %s", name);
+ }
}
}
err |= setvarsafe("OPTARG", s, 0);
}
else {
- outfmt(&errout, "Illegal option -%c\n", c);
+ out2fmt("Illegal option -%c\n", c);
(void) unsetvar("OPTARG");
}
c = '?';
c = ':';
}
else {
- outfmt(&errout, "No arg for -%c option\n", c);
+ out2fmt("No arg for -%c option\n", c);
(void) unsetvar("OPTARG");
c = '?';
}
p = NULL;
out:
*optoff = p ? p - *(optnext - 1) : -1;
- fmtstr(s, sizeof(s), "%d", *myoptind);
+ snprintf(s, sizeof(s), "%d", *myoptind);
err |= setvarsafe("OPTIND", s, VNOFUNC);
s[0] = c;
s[1] = '\0';
if (err) {
*myoptind = 1;
*optoff = -1;
- flushall();
exraise(EXERROR);
}
return done;
*/
static int
-nextopt(optstring)
- const char *optstring;
- {
+nextopt(const char *optstring)
+{
char *p;
const char *q;
char c;
return c;
}
-
-/*
- * Shell output routines. We use our own output routines because:
- * When a builtin command is interrupted we have to discard
- * any pending output.
- * When a builtin command appears in back quotes, we want to
- * save the output of the command in a region obtained
- * via malloc, rather than doing a fork and reading the
- * output of the command via a pipe.
- * Our output routines may be smaller than the stdio routines.
- */
-
-
-
-#ifndef USE_GLIBC_STDIO
-static void __outstr (const char *, size_t, struct output*);
-#endif
-
-
-#ifndef USE_GLIBC_STDIO
-static void
-__outstr(const char *p, size_t len, struct output *dest) {
- if (!dest->bufsize) {
- dest->nleft = 0;
- } else if (dest->buf == NULL) {
- if (len > dest->bufsize && dest->fd == MEM_OUT) {
- dest->bufsize = len;
- }
- INTOFF;
- dest->buf = ckmalloc(dest->bufsize);
- dest->nextc = dest->buf;
- dest->nleft = dest->bufsize;
- INTON;
- } else if (dest->fd == MEM_OUT) {
- int offset;
-
- offset = dest->bufsize;
- INTOFF;
- if (dest->bufsize >= len) {
- dest->bufsize <<= 1;
- } else {
- dest->bufsize += len;
- }
- dest->buf = ckrealloc(dest->buf, dest->bufsize);
- dest->nleft = dest->bufsize - offset;
- dest->nextc = dest->buf + offset;
- INTON;
- } else {
- flushout(dest);
- }
-
- if (len < dest->nleft) {
- dest->nleft -= len;
- memcpy(dest->nextc, p, len);
- dest->nextc += len;
- return;
- }
-
- if (xwrite(dest->fd, p, len) < len)
- dest->flags |= OUTPUT_ERR;
-}
-#endif
-
-
-static void
-outstr(const char *p, struct output *file)
-{
-#ifdef USE_GLIBC_STDIO
- INTOFF;
- fputs(p, file->stream);
- INTON;
-#else
- size_t len;
-
- if (!*p) {
- return;
- }
- len = strlen(p);
- if ((file->nleft -= len) > 0) {
- memcpy(file->nextc, p, len);
- file->nextc += len;
- return;
- }
- __outstr(p, len, file);
-#endif
-}
-
-
-#ifndef USE_GLIBC_STDIO
-
-
-static void
-outcslow(c, dest)
- char c;
- struct output *dest;
- {
- __outstr(&c, 1, dest);
-}
-#endif
-
-
static void
flushall() {
- flushout(&output);
-#ifdef FLUSHERR
- flushout(&errout);
-#endif
-}
-
-
-static void
-flushout(dest)
- struct output *dest;
- {
-#ifdef USE_GLIBC_STDIO
INTOFF;
- fflush(dest->stream);
+ fflush(stdout);
INTON;
-#else
- size_t len;
-
- len = dest->nextc - dest->buf;
- if (dest->buf == NULL || !len || dest->fd < 0)
- return;
- dest->nextc = dest->buf;
- dest->nleft = dest->bufsize;
- if (xwrite(dest->fd, dest->buf, len) < len)
- dest->flags |= OUTPUT_ERR;
-#endif
-}
-
-
-static void
-freestdout() {
- if (output.buf) {
- INTOFF;
- ckfree(output.buf);
- output.buf = NULL;
- output.nleft = 0;
- INTON;
- }
- output.flags = 0;
-}
-
-
-static void
-#ifdef __STDC__
-outfmt(struct output *file, const char *fmt, ...)
-#else
-static void
-outfmt(va_alist)
- va_dcl
-#endif
-{
- va_list ap;
-#ifndef __STDC__
- struct output *file;
- const char *fmt;
-
- va_start(ap);
- file = va_arg(ap, struct output *);
- fmt = va_arg(ap, const char *);
-#else
- va_start(ap, fmt);
-#endif
- doformat(file, fmt, ap);
- va_end(ap);
}
static void
-#ifdef __STDC__
-out1fmt(const char *fmt, ...)
-#else
-out1fmt(va_alist)
- va_dcl
-#endif
+out2fmt(const char *fmt, ...)
{
va_list ap;
-#ifndef __STDC__
- const char *fmt;
-
- va_start(ap);
- fmt = va_arg(ap, const char *);
-#else
va_start(ap, fmt);
-#endif
- doformat(out1, fmt, ap);
+ vfprintf(stderr, fmt, ap);
va_end(ap);
}
-static void
-#ifdef __STDC__
-fmtstr(char *outbuf, size_t length, const char *fmt, ...)
-#else
-fmtstr(va_alist)
- va_dcl
-#endif
-{
- va_list ap;
-#ifndef __STDC__
- char *outbuf;
- size_t length;
- const char *fmt;
-
- va_start(ap);
- outbuf = va_arg(ap, char *);
- length = va_arg(ap, size_t);
- fmt = va_arg(ap, const char *);
-#else
- va_start(ap, fmt);
-#endif
- INTOFF;
- vsnprintf(outbuf, length, fmt, ap);
- INTON;
-}
-
-#ifndef USE_GLIBC_STDIO
-
-static void
-doformat(struct output *dest, const char *f, va_list ap)
-{
- char *pm;
- int size = BUFSIZ;
-
- while(size) {
- int nchars;
-
- pm = xmalloc(size);
- nchars = vsnprintf(pm, size, f, ap);
- if(nchars > -1) {
- outstr(pm, dest);
- size = 0;
- }
- else
- size *= 2;
- free(pm);
- }
-}
-#endif
-
-
-
/*
* Version of write which resumes after a signal is caught.
*/
}
-#ifdef USE_GLIBC_STDIO
-static void initstreams() {
- output.stream = stdout;
- errout.stream = stderr;
-}
-
-
-static void
-openmemout() {
- INTOFF;
- memout.stream = open_memstream(&memout.buf, &memout.bufsize);
- INTON;
-}
-
-
-static int
-__closememout() {
- int error;
- error = fclose(memout.stream);
- memout.stream = NULL;
- return error;
-}
-#endif
/*
* Shell command parser.
*/
c = pgetc_macro();
switch (c) {
case ' ': case '\t':
+#ifdef ASH_ALIAS
case PEOA:
+#endif
continue;
case '#':
while ((c = pgetc()) != '\n' && c != PEOF);
default:
if (varnest == 0)
goto endword; /* exit outer loop */
- if (c != PEOA) {
+#ifdef ASH_ALIAS
+ if (c != PEOA)
+#endif
USTPUTC(c, out);
- }
+
}
c = pgetc_macro();
}
char msg[64];
if (token >= 0) {
- fmtstr(msg, 64, "%s unexpected (expecting %s)",
+ snprintf(msg, 64, "%s unexpected (expecting %s)",
tokname[lasttoken], tokname[token]);
} else {
- fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
+ snprintf(msg, 64, "%s unexpected", tokname[lasttoken]);
}
synerror(msg);
/* NOTREACHED */
synerror(const char *msg)
{
if (commandname)
- outfmt(&errout, "%s: %d: ", commandname, startlinno);
- outfmt(&errout, "Syntax error: %s\n", msg);
+ out2fmt("%s: %d: ", commandname, startlinno);
+ out2fmt("Syntax error: %s\n", msg);
error((char *)NULL);
/* NOTREACHED */
}
* called by editline -- any expansions to the prompt
* should be added here.
*/
-static const char *
-getprompt(void *unused)
+static void
+setprompt(int whichprompt)
{
- switch (whichprompt) {
- case 0:
- return "";
+ char *prompt;
+ switch (whichprompt) {
case 1:
- return ps1val();
+ prompt = ps1val();
+ break;
case 2:
- return ps2val();
- default:
- return "<internal prompt error>";
- }
-}
-
-static void
-setprompt(int which)
-{
- whichprompt = which;
- putprompt(getprompt(NULL));
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = "";
+ }
+ putprompt(prompt);
}
#endif
-
/*
- * Process a list of redirection commands. If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
*/
+static inline int
+noclobberopen(const char *fname)
+{
+ int r, fd;
+ struct stat finfo, finfo2;
-static void
-redirect(redir, flags)
- union node *redir;
- int flags;
- {
- union node *n;
- struct redirtab *sv = NULL;
- int i;
- int fd;
- int newfd;
- int try;
- char memory[10]; /* file descriptors to write to memory */
-
- for (i = 10 ; --i >= 0 ; )
- memory[i] = 0;
- memory[1] = flags & REDIR_BACKQ;
- if (flags & REDIR_PUSH) {
- sv = ckmalloc(sizeof (struct redirtab));
- for (i = 0 ; i < 10 ; i++)
- sv->renamed[i] = EMPTY;
- sv->next = redirlist;
- redirlist = sv;
+ /*
+ * If the file exists and is a regular file, return an error
+ * immediately.
+ */
+ r = stat(fname, &finfo);
+ if (r == 0 && S_ISREG(finfo.st_mode)) {
+ errno = EEXIST;
+ return -1;
}
- for (n = redir ; n ; n = n->nfile.next) {
- fd = n->nfile.fd;
- try = 0;
- if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
- n->ndup.dupfd == fd)
- continue; /* redirect from/to same file descriptor */
- INTOFF;
- newfd = openredirect(n);
- if (((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) ||
- (fd == fileno2)) {
- if (newfd == fd) {
- try++;
- } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
- switch (errno) {
- case EBADF:
- if (!try) {
- dupredirect(n, newfd, memory);
- try++;
- break;
- }
- /* FALLTHROUGH*/
- default:
- if (newfd >= 0) {
- close(newfd);
- }
- INTON;
- error("%d: %m", fd);
- /* NOTREACHED */
- }
- }
- if (!try) {
- close(fd);
- if (flags & REDIR_PUSH) {
- sv->renamed[fd] = i;
- }
- if (fd == fileno2) {
- fileno2 = i;
- }
- }
- } else if (fd != newfd) {
- close(fd);
+ /*
+ * If the file was not present (r != 0), make sure we open it
+ * exclusively so that if it is created before we open it, our open
+ * will fail. Make sure that we do not truncate an existing file.
+ * Note that we don't turn on O_EXCL unless the stat failed -- if the
+ * file was not a regular file, we leave O_EXCL off.
+ */
+ if (r != 0)
+ return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+ /* If the open failed, return the file descriptor right away. */
+ if (fd < 0)
+ return fd;
+
+ /*
+ * OK, the open succeeded, but the file may have been changed from a
+ * non-regular file to a regular file between the stat and the open.
+ * We are assuming that the O_EXCL open handles the case where FILENAME
+ * did not exist and is symlinked to an existing file between the stat
+ * and open.
+ */
+
+ /*
+ * If we can open it and fstat the file descriptor, and neither check
+ * revealed that it was a regular file, and the file has not been
+ * replaced, return the file descriptor.
+ */
+ if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
+ finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+ return fd;
+
+ /* The file has been replaced. badness. */
+ close(fd);
+ errno = EEXIST;
+ return -1;
+}
+
+/*
+ * Handle here documents. Normally we fork off a process to write the
+ * data to a pipe. If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+
+static inline int
+openhere(const union node *redir)
+{
+ int pip[2];
+ int len = 0;
+
+ if (pipe(pip) < 0)
+ error("Pipe call failed");
+ if (redir->type == NHERE) {
+ len = strlen(redir->nhere.doc->narg.text);
+ if (len <= PIPESIZE) {
+ xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ goto out;
}
- if (fd == 0)
- fd0_redirected++;
- if (!try)
- dupredirect(n, newfd, memory);
- INTON;
}
- if (memory[1])
- out1 = &memout;
- if (memory[2])
- out2 = &memout;
+ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+ close(pip[0]);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+ signal(SIGPIPE, SIG_DFL);
+ if (redir->type == NHERE)
+ xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ else
+ expandhere(redir->nhere.doc, pip[1]);
+ _exit(0);
+ }
+out:
+ close(pip[1]);
+ return pip[0];
}
-static int
-openredirect(redir)
- union node *redir;
- {
+static inline int
+openredirect(const union node *redir)
+{
char *fname;
int f;
}
+/*
+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout.
+ */
+
+static void
+redirect(union node *redir, int flags)
+{
+ union node *n;
+ struct redirtab *sv = NULL;
+ int i;
+ int fd;
+ int newfd;
+ int try;
+ int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
+
+ if (flags & REDIR_PUSH) {
+ sv = ckmalloc(sizeof (struct redirtab));
+ for (i = 0 ; i < 10 ; i++)
+ sv->renamed[i] = EMPTY;
+ sv->next = redirlist;
+ redirlist = sv;
+ }
+ for (n = redir ; n ; n = n->nfile.next) {
+ fd = n->nfile.fd;
+ try = 0;
+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
+ n->ndup.dupfd == fd)
+ continue; /* redirect from/to same file descriptor */
+
+ INTOFF;
+ newfd = openredirect(n);
+ if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
+ if (newfd == fd) {
+ try++;
+ } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
+ switch (errno) {
+ case EBADF:
+ if (!try) {
+ dupredirect(n, newfd, fd1dup);
+ try++;
+ break;
+ }
+ /* FALLTHROUGH*/
+ default:
+ if (newfd >= 0) {
+ close(newfd);
+ }
+ INTON;
+ error("%d: %m", fd);
+ /* NOTREACHED */
+ }
+ }
+ if (!try) {
+ close(fd);
+ if (flags & REDIR_PUSH) {
+ sv->renamed[fd] = i;
+ }
+ }
+ } else if (fd != newfd) {
+ close(fd);
+ }
+ if (fd == 0)
+ fd0_redirected++;
+ if (!try)
+ dupredirect(n, newfd, fd1dup);
+ INTON;
+ }
+}
+
+
static void
-dupredirect(union node *redir, int f, char memory[10])
+dupredirect(const union node *redir, int f, int fd1dup)
{
int fd = redir->nfile.fd;
- memory[fd] = 0;
+ if(fd==1)
+ fd1dup = 0;
if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
- if (memory[redir->ndup.dupfd])
- memory[fd] = 1;
- else
+ if (redir->ndup.dupfd!=1 || fd1dup!=1)
dup_as_newfd(redir->ndup.dupfd, fd);
}
return;
}
-/*
- * Handle here documents. Normally we fork off a process to write the
- * data to a pipe. If the document is short, we can stuff the data in
- * the pipe without forking.
- */
-
-static int
-openhere(redir)
- union node *redir;
- {
- int pip[2];
- int len = 0;
-
- if (pipe(pip) < 0)
- error("Pipe call failed");
- if (redir->type == NHERE) {
- len = strlen(redir->nhere.doc->narg.text);
- if (len <= PIPESIZE) {
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
- goto out;
- }
- }
- if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
- close(pip[0]);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
- signal(SIGTSTP, SIG_IGN);
-#endif
- signal(SIGPIPE, SIG_DFL);
- if (redir->type == NHERE)
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
- else
- expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
- }
-out:
- close(pip[1]);
- return pip[0];
-}
-
/*
* Undo the effects of the last redirection.
dup_as_newfd(rp->renamed[i], i);
close(rp->renamed[i]);
}
- if (rp->renamed[i] == fileno2) {
- fileno2 = i;
- }
}
}
redirlist = rp->next;
for (i = 0 ; i < 10 ; i++) {
if (rp->renamed[i] >= 0) {
close(rp->renamed[i]);
- if (rp->renamed[i] == fileno2) {
- fileno2 = -1;
- }
}
rp->renamed[i] = EMPTY;
}
}
- if (fileno2 != 2 && fileno2 >= 0) {
- close(fileno2);
- fileno2 = -1;
- }
}
return newfd;
}
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
- int r, fd;
- struct stat finfo, finfo2;
-
- /*
- * If the file exists and is a regular file, return an error
- * immediately.
- */
- r = stat(fname, &finfo);
- if (r == 0 && S_ISREG(finfo.st_mode)) {
- errno = EEXIST;
- return -1;
- }
-
- /*
- * If the file was not present (r != 0), make sure we open it
- * exclusively so that if it is created before we open it, our open
- * will fail. Make sure that we do not truncate an existing file.
- * Note that we don't turn on O_EXCL unless the stat failed -- if the
- * file was not a regular file, we leave O_EXCL off.
- */
- if (r != 0)
- return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
- fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
- /* If the open failed, return the file descriptor right away. */
- if (fd < 0)
- return fd;
-
- /*
- * OK, the open succeeded, but the file may have been changed from a
- * non-regular file to a regular file between the stat and the open.
- * We are assuming that the O_EXCL open handles the case where FILENAME
- * did not exist and is symlinked to an existing file between the stat
- * and open.
- */
-
- /*
- * If we can open it and fstat the file descriptor, and neither check
- * revealed that it was a regular file, and the file has not been
- * replaced, return the file descriptor.
- */
- if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
- finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
- return fd;
-
- /* The file has been replaced. badness. */
- close(fd);
- errno = EEXIST;
- return -1;
-}
/*#ifdef __weak_alias
__weak_alias(getmode,_getmode)
__weak_alias(setmode,_setmode)
#endif*/
-#ifdef __GLIBC__
+#ifndef S_ISTXT
+#if defined(__GLIBC__) && __GLIBC__ >= 2
#define S_ISTXT __S_ISVTX
+#else
+#define S_ISTXT S_ISVTX
+#endif
#endif
#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
* If an absolute number, get it and return; disallow non-octal digits
* or illegal bits.
*/
- if (isdigit((unsigned char)*p)) {
+ if (is_digit((unsigned char)*p)) {
perm = (mode_t)strtol(p, &ep, 8);
if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
free(saveset);
char *p;
p = single_quote(trap[signo]);
- out1fmt("trap -- %s %s\n", p,
+ printf("trap -- %s %s\n", p,
signal_names[signo] + (signo ? 3 : 0)
);
stunalloc(p);
* Find the value of a variable. Returns NULL if not set.
*/
-static char *
+static const char *
lookupvar(name)
const char *name;
{
* Search the environment of a builtin command.
*/
-static char *
-bltinlookup(name)
- const char *name;
+static const char *
+bltinlookup(const char *name)
{
- struct strlist *sp;
+ const struct strlist *sp;
for (sp = cmdenviron ; sp ; sp = sp->next) {
if (varequal(sp->text, name))
*/
static int
-unsetvar(s)
- const char *s;
- {
+unsetvar(const char *s)
+{
struct var **vpp;
struct var *vp;
*/
static struct var **
-hashvar(p)
- const char *p;
- {
+hashvar(const char *p)
+{
unsigned int hashval;
hashval = ((unsigned char) *p) << 4;
*/
static int
-varequal(p, q)
- const char *p, *q;
- {
+varequal(const char *p, const char *q)
+{
while (*p == *q++) {
if (*p++ == '=')
return 1;
len = p - vp->text;
p = single_quote(p);
- out1fmt(
- "%s%s%.*s%s\n", myprefix, sep, len,
- vp->text, p
- );
+ printf("%s%s%.*s%s\n", myprefix, sep, len,
+ vp->text, p);
stunalloc(p);
}
}
/*
* Copyright (c) 1999 Herbert Xu <herbert@debian.org>
* This file contains code for the times builtin.
- * $Id: ash.c,v 1.5 2001/07/05 05:24:12 andersen Exp $
+ * $Id: ash.c,v 1.8 2001/07/10 06:09:16 andersen Exp $
*/
static int timescmd (int argc, char **argv)
{