/* 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.
* 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
* 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>
#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))))
* so we use _setjmp instead.
*/
-#if !defined(__GLIBC__)
+#if defined(BSD)
#define setjmp(jmploc) _setjmp(jmploc)
#define longjmp(jmploc, val) _longjmp(jmploc, val)
#endif
int optoff; /* used by getopts */
};
+/*
+ * 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);
+static int path_change (const char *, int *);
+
+
static void flushall (void);
static void out2fmt (const char *, ...)
__attribute__((__format__(__printf__,1,2)));
-static void out1fmt (const char *, ...)
- __attribute__((__format__(__printf__,1,2)));
static int xwrite (int, const char *, int);
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); }
+#ifndef ASH_OPTIMIZE_FOR_SIZE
#define out2c(c) putc((c), stderr)
+#else
+static void out2c(int c) { putc(c, stderr); }
+#endif
/* 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 *);
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);
}
#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_SPECIAL "break", breakcmd },
{ 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_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;
}
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);
}
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 */
};
/*
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 *);
-static void evalcommand (union node *, int);
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)
- ) {
- out2fmt("%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:
- evalcommand(n, flags);
- checkexit = 1;
- break;
-#ifdef DEBUG
- default:
- out1fmt("Node type = %d\n", n->type);
- 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;
- }
- 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
evalcommand(union node *cmd, int flags)
{
}
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) {
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;
+ 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;
+ 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;
- if (n->type == NCMD && n->ncmd.args)
- if (goodname(n->ncmd.args->narg.text))
- find_command(n->ncmd.args->narg.text, &entry, 0,
- pathval());
+ 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);
}
-
-
-/*
- * 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.
+ * Kick off a subshell to evaluate a tree.
*/
-int
-bltincmd(argc, argv)
- int argc;
- char **argv;
+static void
+evalsubshell(const union node *n, int flags)
{
- /*
- * Preserve exitstatus of a previous possible redirection
- * as POSIX mandates
- */
- return exitstatus;
-}
+ 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;
+ }
+}
/*
- * 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.
+ * Compute the names of the files in a redirection list.
*/
-static int
+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;
#ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
static int
false_main(argc, argv)
int argc;
return 0;
}
#endif
-#endif
/*
* Controls whether the shell is interactive or not.
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.
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
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++);
+ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
optind = 1;
run_applet_by_name(name, argc_l, argv);
#endif
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);
}
static int helpcmd(int argc, char** argv)
{
int col, i;
- const struct builtincmd *x;
- printf("\nBuilt-in commands:\n");
- printf("-------------------\n");
- for (col=0, i=0, x = builtincmds; i < NUMBUILTINS; x++, i++) {
- if (!x->name || ! (x->name+1))
- continue;
- col += printf("%s%s", ((col == 0) ? "\t" : " "),
- (x->name+1));
+ 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
{
- const struct BB_applet *applet;
extern const struct BB_applet applets[];
extern const size_t NUM_APPLETS;
- for (i=0, applet = applets; i < NUM_APPLETS; applet++, i++) {
- if (!applet->name)
- continue;
-
- col += printf("%s%s", ((col == 0) ? "\t" : " "),
- applet->name);
+ for (i=0; i < NUM_APPLETS; i++) {
+
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ applets[i].name);
if (col > 60) {
printf("\n");
col = 0;
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 */
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:
- putchar('\n');
- return 0;
-}
-#endif
-
#ifdef ASH_CMDCMD
static int
commandcmd(argc, argv)
case 'V':
verbose_verify_only = 1;
break;
- default:
- out2fmt(
-"command: nextopt returned character code 0%o\n", c);
- return EX_SOFTWARE;
}
if (default_path + verify_only + verbose_verify_only > 1 ||
!*argptr) {
- out2fmt(
-"command [-p] command [arg ...]\n");
- out2fmt(
-"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;
}
-
+/*
+ * 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;
+
+#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;
+}
+
+
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
}
-/*
- * 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;
-}
-
/*
* 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;
* 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;
}
-
-
/*
* 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;
}
-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
/*
"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);
}
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";
}
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
* 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;
-/*
- * 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
#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");
*/
-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;
}
}
-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;
- }
- error("Illegal option -o %s", name);
- }
-}
-
static void
setoption(int flag, int val)
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;
va_end(ap);
}
-
-static void
-out1fmt(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
-}
-
/*
* Version of write which resumes after a signal is caught.
*/
* called by editline -- any expansions to the prompt
* should be added here.
*/
-static inline 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>";
- }
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = "";
+ }
+ putprompt(prompt);
}
-static void
-setprompt(int which)
+
+/*
+ * Code for dealing with input/output redirection.
+ */
+
+#define EMPTY -2 /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096 /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static inline int
+noclobberopen(const char *fname)
{
- whichprompt = which;
- putprompt(getprompt(NULL));
-}
+ 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;
+}
/*
- * Code for dealing with input/output redirection.
+ * 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.
*/
-#define EMPTY -2 /* marks an unused slot in redirtab */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096 /* amount of buffering in a pipe */
+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 (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 inline int
+openredirect(const union node *redir)
+{
+ char *fname;
+ int f;
+
+ switch (redir->nfile.type) {
+ case NFROM:
+ fname = redir->nfile.expfname;
+ if ((f = open(fname, O_RDONLY)) < 0)
+ goto eopen;
+ break;
+ case NFROMTO:
+ fname = redir->nfile.expfname;
+ if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
+ break;
+ case NTO:
+ /* Take care of noclobber mode. */
+ if (Cflag) {
+ fname = redir->nfile.expfname;
+ if ((f = noclobberopen(fname)) < 0)
+ goto ecreate;
+ break;
+ }
+ case NTOOV:
+ fname = redir->nfile.expfname;
+#ifdef O_CREAT
+ if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
#else
-# define PIPESIZE PIPE_BUF
+ if ((f = creat(fname, 0666)) < 0)
+ goto ecreate;
+#endif
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+#ifdef O_APPEND
+ if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+ goto ecreate;
+#else
+ if ((f = open(fname, O_WRONLY)) < 0
+ && (f = creat(fname, 0666)) < 0)
+ goto ecreate;
+ lseek(f, (off_t)0, 2);
+#endif
+ break;
+ default:
+#ifdef DEBUG
+ abort();
#endif
+ /* Fall through to eliminate warning. */
+ case NTOFD:
+ case NFROMFD:
+ f = -1;
+ break;
+ case NHERE:
+ case NXHERE:
+ f = openhere(redir);
+ break;
+ }
+ return f;
+ecreate:
+ error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+ error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+}
/*
*/
static void
-redirect(redir, flags)
- union node *redir;
- int flags;
- {
+redirect(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 */
+ int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
- 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++)
switch (errno) {
case EBADF:
if (!try) {
- dupredirect(n, newfd, memory);
+ dupredirect(n, newfd, fd1dup);
try++;
break;
}
if (fd == 0)
fd0_redirected++;
if (!try)
- dupredirect(n, newfd, memory);
+ dupredirect(n, newfd, fd1dup);
INTON;
}
}
-static int
-openredirect(redir)
- union node *redir;
- {
- char *fname;
- int f;
-
- switch (redir->nfile.type) {
- case NFROM:
- fname = redir->nfile.expfname;
- if ((f = open(fname, O_RDONLY)) < 0)
- goto eopen;
- break;
- case NFROMTO:
- fname = redir->nfile.expfname;
- if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
- goto ecreate;
- break;
- case NTO:
- /* Take care of noclobber mode. */
- if (Cflag) {
- fname = redir->nfile.expfname;
- if ((f = noclobberopen(fname)) < 0)
- goto ecreate;
- break;
- }
- case NTOOV:
- fname = redir->nfile.expfname;
-#ifdef O_CREAT
- if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
- goto ecreate;
-#else
- if ((f = creat(fname, 0666)) < 0)
- goto ecreate;
-#endif
- break;
- case NAPPEND:
- fname = redir->nfile.expfname;
-#ifdef O_APPEND
- if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
- goto ecreate;
-#else
- if ((f = open(fname, O_WRONLY)) < 0
- && (f = creat(fname, 0666)) < 0)
- goto ecreate;
- lseek(f, (off_t)0, 2);
-#endif
- break;
- default:
-#ifdef DEBUG
- abort();
-#endif
- /* Fall through to eliminate warning. */
- case NTOFD:
- case NFROMFD:
- f = -1;
- break;
- case NHERE:
- case NXHERE:
- f = openhere(redir);
- break;
- }
-
- return f;
-ecreate:
- error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
-eopen:
- error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
-}
-
-
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.
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.7 2001/07/07 00:05:55 andersen Exp $
+ * $Id: ash.c,v 1.8 2001/07/10 06:09:16 andersen Exp $
*/
static int timescmd (int argc, char **argv)
{
/* 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.
* 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
* 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>
#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))))
* so we use _setjmp instead.
*/
-#if !defined(__GLIBC__)
+#if defined(BSD)
#define setjmp(jmploc) _setjmp(jmploc)
#define longjmp(jmploc, val) _longjmp(jmploc, val)
#endif
int optoff; /* used by getopts */
};
+/*
+ * 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);
+static int path_change (const char *, int *);
+
+
static void flushall (void);
static void out2fmt (const char *, ...)
__attribute__((__format__(__printf__,1,2)));
-static void out1fmt (const char *, ...)
- __attribute__((__format__(__printf__,1,2)));
static int xwrite (int, const char *, int);
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); }
+#ifndef ASH_OPTIMIZE_FOR_SIZE
#define out2c(c) putc((c), stderr)
+#else
+static void out2c(int c) { putc(c, stderr); }
+#endif
/* 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 *);
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);
}
#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_SPECIAL "break", breakcmd },
{ 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_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;
}
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);
}
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 */
};
/*
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 *);
-static void evalcommand (union node *, int);
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)
- ) {
- out2fmt("%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:
- evalcommand(n, flags);
- checkexit = 1;
- break;
-#ifdef DEBUG
- default:
- out1fmt("Node type = %d\n", n->type);
- 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;
- }
- 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
evalcommand(union node *cmd, int flags)
{
}
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) {
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;
+ 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;
+ 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;
- if (n->type == NCMD && n->ncmd.args)
- if (goodname(n->ncmd.args->narg.text))
- find_command(n->ncmd.args->narg.text, &entry, 0,
- pathval());
+ 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);
}
-
-
-/*
- * 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.
+ * Kick off a subshell to evaluate a tree.
*/
-int
-bltincmd(argc, argv)
- int argc;
- char **argv;
+static void
+evalsubshell(const union node *n, int flags)
{
- /*
- * Preserve exitstatus of a previous possible redirection
- * as POSIX mandates
- */
- return exitstatus;
-}
+ 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;
+ }
+}
/*
- * 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.
+ * Compute the names of the files in a redirection list.
*/
-static int
+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;
#ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
static int
false_main(argc, argv)
int argc;
return 0;
}
#endif
-#endif
/*
* Controls whether the shell is interactive or not.
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.
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
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++);
+ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
optind = 1;
run_applet_by_name(name, argc_l, argv);
#endif
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);
}
static int helpcmd(int argc, char** argv)
{
int col, i;
- const struct builtincmd *x;
- printf("\nBuilt-in commands:\n");
- printf("-------------------\n");
- for (col=0, i=0, x = builtincmds; i < NUMBUILTINS; x++, i++) {
- if (!x->name || ! (x->name+1))
- continue;
- col += printf("%s%s", ((col == 0) ? "\t" : " "),
- (x->name+1));
+ 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
{
- const struct BB_applet *applet;
extern const struct BB_applet applets[];
extern const size_t NUM_APPLETS;
- for (i=0, applet = applets; i < NUM_APPLETS; applet++, i++) {
- if (!applet->name)
- continue;
-
- col += printf("%s%s", ((col == 0) ? "\t" : " "),
- applet->name);
+ for (i=0; i < NUM_APPLETS; i++) {
+
+ col += printf("%c%s", ((col == 0) ? '\t' : ' '),
+ applets[i].name);
if (col > 60) {
printf("\n");
col = 0;
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 */
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:
- putchar('\n');
- return 0;
-}
-#endif
-
#ifdef ASH_CMDCMD
static int
commandcmd(argc, argv)
case 'V':
verbose_verify_only = 1;
break;
- default:
- out2fmt(
-"command: nextopt returned character code 0%o\n", c);
- return EX_SOFTWARE;
}
if (default_path + verify_only + verbose_verify_only > 1 ||
!*argptr) {
- out2fmt(
-"command [-p] command [arg ...]\n");
- out2fmt(
-"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;
}
-
+/*
+ * 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;
+
+#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;
+}
+
+
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
}
-/*
- * 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;
-}
-
/*
* 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;
* 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;
}
-
-
/*
* 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;
}
-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
/*
"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);
}
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";
}
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
* 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;
-/*
- * 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
#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");
*/
-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;
}
}
-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;
- }
- error("Illegal option -o %s", name);
- }
-}
-
static void
setoption(int flag, int val)
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;
va_end(ap);
}
-
-static void
-out1fmt(const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
-}
-
/*
* Version of write which resumes after a signal is caught.
*/
* called by editline -- any expansions to the prompt
* should be added here.
*/
-static inline 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>";
- }
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = "";
+ }
+ putprompt(prompt);
}
-static void
-setprompt(int which)
+
+/*
+ * Code for dealing with input/output redirection.
+ */
+
+#define EMPTY -2 /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096 /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static inline int
+noclobberopen(const char *fname)
{
- whichprompt = which;
- putprompt(getprompt(NULL));
-}
+ 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;
+}
/*
- * Code for dealing with input/output redirection.
+ * 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.
*/
-#define EMPTY -2 /* marks an unused slot in redirtab */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096 /* amount of buffering in a pipe */
+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 (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 inline int
+openredirect(const union node *redir)
+{
+ char *fname;
+ int f;
+
+ switch (redir->nfile.type) {
+ case NFROM:
+ fname = redir->nfile.expfname;
+ if ((f = open(fname, O_RDONLY)) < 0)
+ goto eopen;
+ break;
+ case NFROMTO:
+ fname = redir->nfile.expfname;
+ if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
+ break;
+ case NTO:
+ /* Take care of noclobber mode. */
+ if (Cflag) {
+ fname = redir->nfile.expfname;
+ if ((f = noclobberopen(fname)) < 0)
+ goto ecreate;
+ break;
+ }
+ case NTOOV:
+ fname = redir->nfile.expfname;
+#ifdef O_CREAT
+ if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ goto ecreate;
#else
-# define PIPESIZE PIPE_BUF
+ if ((f = creat(fname, 0666)) < 0)
+ goto ecreate;
+#endif
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+#ifdef O_APPEND
+ if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+ goto ecreate;
+#else
+ if ((f = open(fname, O_WRONLY)) < 0
+ && (f = creat(fname, 0666)) < 0)
+ goto ecreate;
+ lseek(f, (off_t)0, 2);
+#endif
+ break;
+ default:
+#ifdef DEBUG
+ abort();
#endif
+ /* Fall through to eliminate warning. */
+ case NTOFD:
+ case NFROMFD:
+ f = -1;
+ break;
+ case NHERE:
+ case NXHERE:
+ f = openhere(redir);
+ break;
+ }
+ return f;
+ecreate:
+ error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+ error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+}
/*
*/
static void
-redirect(redir, flags)
- union node *redir;
- int flags;
- {
+redirect(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 */
+ int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
- 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++)
switch (errno) {
case EBADF:
if (!try) {
- dupredirect(n, newfd, memory);
+ dupredirect(n, newfd, fd1dup);
try++;
break;
}
if (fd == 0)
fd0_redirected++;
if (!try)
- dupredirect(n, newfd, memory);
+ dupredirect(n, newfd, fd1dup);
INTON;
}
}
-static int
-openredirect(redir)
- union node *redir;
- {
- char *fname;
- int f;
-
- switch (redir->nfile.type) {
- case NFROM:
- fname = redir->nfile.expfname;
- if ((f = open(fname, O_RDONLY)) < 0)
- goto eopen;
- break;
- case NFROMTO:
- fname = redir->nfile.expfname;
- if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
- goto ecreate;
- break;
- case NTO:
- /* Take care of noclobber mode. */
- if (Cflag) {
- fname = redir->nfile.expfname;
- if ((f = noclobberopen(fname)) < 0)
- goto ecreate;
- break;
- }
- case NTOOV:
- fname = redir->nfile.expfname;
-#ifdef O_CREAT
- if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
- goto ecreate;
-#else
- if ((f = creat(fname, 0666)) < 0)
- goto ecreate;
-#endif
- break;
- case NAPPEND:
- fname = redir->nfile.expfname;
-#ifdef O_APPEND
- if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
- goto ecreate;
-#else
- if ((f = open(fname, O_WRONLY)) < 0
- && (f = creat(fname, 0666)) < 0)
- goto ecreate;
- lseek(f, (off_t)0, 2);
-#endif
- break;
- default:
-#ifdef DEBUG
- abort();
-#endif
- /* Fall through to eliminate warning. */
- case NTOFD:
- case NFROMFD:
- f = -1;
- break;
- case NHERE:
- case NXHERE:
- f = openhere(redir);
- break;
- }
-
- return f;
-ecreate:
- error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
-eopen:
- error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
-}
-
-
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.
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.7 2001/07/07 00:05:55 andersen Exp $
+ * $Id: ash.c,v 1.8 2001/07/10 06:09:16 andersen Exp $
*/
static int timescmd (int argc, char **argv)
{