/* ============ Parsing structures */
+
#define NCMD 0
#define NPIPE 1
#define NREDIR 2
};
#if ENABLE_ASH_ALIAS
-#define ALIASINUSE 1
-#define ALIASDEAD 2
struct alias;
-static int aliascmd(int, char **);
-static int unaliascmd(int, char **);
-static void printalias(const struct alias *);
#endif
struct strpush {
}
-/* ============ Variables */
+/* ============ Shell variables */
/* flags */
#define VEXPORT 0x01 /* variable is exported */
# define VDYNAMIC 0
#endif
-#if ENABLE_LOCALE_SUPPORT
-static void change_lc_all(const char *value);
-static void change_lc_ctype(const char *value);
-#endif
-
static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
static const char defifs[] = " \t\n";
#endif
+struct shparam {
+ int nparam; /* # of positional parameters (without $0) */
+ unsigned char malloc; /* if parameter list dynamically allocated */
+ char **p; /* parameter list */
+#if ENABLE_ASH_GETOPTS
+ int optind; /* next parameter to be processed by getopts */
+ int optoff; /* used by getopts */
+#endif
+};
+
+static struct shparam shellparam; /* $@ current positional parameters */
+
+#if ENABLE_ASH_GETOPTS
+static void
+getoptsreset(const char *value)
+{
+ shellparam.optind = number(value);
+ shellparam.optoff = -1;
+}
+#endif
+
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
};
/* Forward decls for varinit[] */
+#if ENABLE_LOCALE_SUPPORT
+static void change_lc_all(const char *value);
+static void change_lc_ctype(const char *value);
+#endif
#if ENABLE_ASH_MAIL
static void chkmail(void);
static void changemail(const char *);
#endif
static void changepath(const char *);
-#if ENABLE_ASH_GETOPTS
-static void getoptsreset(const char *);
-#endif
#if ENABLE_ASH_RANDOM_SUPPORT
static void change_random(const char *);
#endif
static struct var varinit[] = {
#ifdef IFS_BROKEN
- { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
+ { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
#else
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
#endif
-
#if ENABLE_ASH_MAIL
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
#endif
-
- { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
- { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
+ { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
+ { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
#if ENABLE_ASH_GETOPTS
- { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
+ { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
#endif
#if ENABLE_ASH_RANDOM_SUPPORT
- {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+ { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
#endif
#if ENABLE_LOCALE_SUPPORT
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
#endif
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
+ { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
#endif
};
#define mpathset() ((vmpath.flags & VUNSET) == 0)
-static struct var **hashvar(const char *);
-
-static int loopnest; /* current loop nesting level */
-
/*
* The parsefile structure pointed to by the global variable parsefile
* contains information about the current file being read.
static struct redirtab *redirlist;
static int nullredirs;
-
extern char **environ;
-
static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
-struct shparam {
- int nparam; /* # of positional parameters (without $0) */
- unsigned char malloc; /* if parameter list dynamically allocated */
- char **p; /* parameter list */
-#if ENABLE_ASH_GETOPTS
- int optind; /* next parameter to be processed by getopts */
- int optoff; /* used by getopts */
-#endif
-};
-
-static struct shparam shellparam; /* $@ current positional parameters */
-
#define VTABSIZE 39
static struct var *vartab[VTABSIZE];
-#if ENABLE_ASH_GETOPTS
-static void
-getoptsreset(const char *value)
-{
- shellparam.optind = number(value);
- shellparam.optoff = -1;
-}
-#endif
-
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
static void expari(int);
#endif
+
/* parser.h */
/* control characters in argument strings */
#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
-
static int tokpushback; /* last token pushed back */
#define NEOF ((union node *)&tokpushback)
static int parsebackquote; /* nonzero if we are inside backquotes */
static int quoteflag; /* set if (part of) last token was quoted */
static void fixredir(union node *, const char *, int);
-static char *endofname(const char *);
+
/* shell.h */
#define xlikely(x) __builtin_expect((x),1)
-
#define TEOF 0
#define TNL 1
#define TREDIR 2
( (((unsigned int)c) - 33 < 32) \
&& ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
-#define digit_val(c) ((c) - '0')
-
-/*
- * This file was generated by the mksyntax program.
- */
-
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
#define USE_SIT_FUNCTION
#endif
#endif /* USE_SIT_FUNCTION */
-/* alias.c */
+/* alias.c */
#define ATABSIZE 39
-static int funcblocksize; /* size of structures in function */
-static int funcstringsize; /* size of strings in node */
-static void *funcblock; /* block to allocate function from */
-static char *funcstring; /* block to allocate strings from */
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static void *funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
static const short nodesize[26] = {
SHELL_ALIGN(sizeof(struct ncmd)),
SHELL_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 *nodeckstrdup(char *);
-
static int evalskip; /* set if we are skipping commands */
static int skipcount; /* number of levels to skip */
static int funcnest; /* depth of function calls */
#define SKIPFILE (1 << 3)
#define SKIPEVAL (1 << 4)
-/*
- * This file was generated by the mkbuiltins program.
- */
-
-#if JOBS
-static int fg_bgcmd(int, char **);
-#endif
-static int breakcmd(int, char **);
-static int cdcmd(int, char **);
-#if ENABLE_ASH_CMDCMD
-static int commandcmd(int, char **);
-#endif
-static int dotcmd(int, char **);
-static int evalcmd(int, char **);
-#if ENABLE_ASH_BUILTIN_ECHO
-static int echocmd(int, char **);
-#endif
-#if ENABLE_ASH_BUILTIN_TEST
-static int testcmd(int, char **);
-#endif
-static int execcmd(int, char **);
-static int exitcmd(int, char **);
-static int exportcmd(int, char **);
-static int falsecmd(int, char **);
-#if ENABLE_ASH_GETOPTS
-static int getoptscmd(int, char **);
-#endif
-static int hashcmd(int, char **);
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-static int helpcmd(int argc, char **argv);
-#endif
-#if JOBS
-static int jobscmd(int, char **);
-#endif
-#if ENABLE_ASH_MATH_SUPPORT
-static int letcmd(int, char **);
-#endif
-static int localcmd(int, char **);
-static int pwdcmd(int, char **);
-static int readcmd(int, char **);
-static int returncmd(int, char **);
-static int setcmd(int, char **);
-static int shiftcmd(int, char **);
-static int timescmd(int, char **);
-static int trapcmd(int, char **);
-static int truecmd(int, char **);
-static int typecmd(int, char **);
-static int umaskcmd(int, char **);
-static int unsetcmd(int, char **);
-static int waitcmd(int, char **);
-static int ulimitcmd(int, char **);
-#if JOBS
-static int killcmd(int, char **);
-#endif
/* exec.h */
# endif
#endif
-/* PEOF (the end of file marker) */
-
-enum {
- INPUT_PUSH_FILE = 1,
- INPUT_NOFILE_OK = 2,
-};
-
-/*
- * The input line number. Input.c just defines this variable, and saves
- * and restores it when files are pushed and popped. The user of this
- * package must set its value.
- */
-static void pungetc(void);
-static void pushstring(char *, void *);
-static void popstring(void);
-static void setinputfd(int, int);
-static void setinputstring(char *);
-static void popfile(void);
-static void popallfiles(void);
-static void closescript(void);
/* jobs.h */
-
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
static char *minusc; /* argument to -c option */
-
static void optschanged(void);
static void setparam(char **);
static void freeparam(volatile struct shparam *);
}
+/* ============ Alias handling */
#if ENABLE_ASH_ALIAS
+
+#define ALIASINUSE 1
+#define ALIASDEAD 2
+
struct alias {
struct alias *next;
char *name;
INT_ON;
}
+static void
+printalias(const struct alias *ap)
+{
+ out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
/*
* TODO - sort output
*/
return i;
}
-
-static void
-printalias(const struct alias *ap)
-{
- out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
-}
#endif /* ASH_ALIAS */
-/* eval.c */
-/*
- * Evaluate a command.
- */
+/* ============ eval.c */
/* flags in argument to evaltree */
#define EV_EXIT 01 /* exit after evaluating tree */
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04 /* command executing within back quotes */
+/* forward declarations - evaluation is faily recursive business... */
static void evalloop(union node *, int);
static void evalfor(union node *, int);
static void evalcase(union node *, int);
}
}
-
#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
static
#endif
void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
+static int loopnest; /* current loop nesting level */
static void
evalloop(union node *n, int flags)
exitstatus = status;
}
-
static void
evalfor(union node *n, int flags)
{
popstackmark(&smark);
}
-
static void
evalcase(union node *n, int flags)
{
popstackmark(&smark);
}
-
/*
* Kick off a subshell to evaluate a tree.
*/
INT_ON;
}
-
/*
* Compute the names of the files in a redirection list.
*/
}
}
-
/*
* Evaluate a pipeline. All the processes in the pipeline are children
* of the process creating the pipeline. (This differs from some versions
INT_ON;
}
-
#if ENABLE_ASH_CMDCMD
static char **
parse_command_args(char **argv, const char **path)
}
#endif
+/* Forward declarations for builtintab[] */
+#if JOBS
+static int fg_bgcmd(int, char **);
+#endif
+static int breakcmd(int, char **);
+#if ENABLE_ASH_CMDCMD
+static int commandcmd(int, char **);
+#endif
+static int dotcmd(int, char **);
+static int evalcmd(int, char **);
+#if ENABLE_ASH_BUILTIN_ECHO
+static int echocmd(int, char **);
+#endif
+#if ENABLE_ASH_BUILTIN_TEST
+static int testcmd(int, char **);
+#endif
+static int execcmd(int, char **);
+static int exitcmd(int, char **);
+static int exportcmd(int, char **);
+static int falsecmd(int, char **);
+#if ENABLE_ASH_GETOPTS
+static int getoptscmd(int, char **);
+#endif
+static int hashcmd(int, char **);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+static int helpcmd(int argc, char **argv);
+#endif
+#if JOBS
+static int jobscmd(int, char **);
+#endif
+#if ENABLE_ASH_MATH_SUPPORT
+static int letcmd(int, char **);
+#endif
+static int localcmd(int, char **);
+static int pwdcmd(int, char **);
+static int readcmd(int, char **);
+static int returncmd(int, char **);
+static int setcmd(int, char **);
+static int shiftcmd(int, char **);
+static int timescmd(int, char **);
+static int trapcmd(int, char **);
+static int truecmd(int, char **);
+static int typecmd(int, char **);
+static int umaskcmd(int, char **);
+static int unsetcmd(int, char **);
+static int waitcmd(int, char **);
+static int ulimitcmd(int, char **);
+#if JOBS
+static int killcmd(int, char **);
+#endif
+
#define BUILTIN_NOSPEC "0"
#define BUILTIN_SPECIAL "1"
#define BUILTIN_REGULAR "2"
}
-/* exec.c */
+/* ============ Executing commands */
/*
* When commands are first encountered, they are entered in a hash table.
#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 */
char cmdname[ARB]; /* name of command */
};
-
static struct tblentry *cmdtable[CMDTABLESIZE];
static int builtinloc = -1; /* index in path of %builtin, or -1 */
-
-static void tryexec(char *, char **, char **);
-static void clearcmdentry(int);
-static struct tblentry *cmdlookup(const char *, int);
-static void delete_cmd_entry(void);
-
-
-/*
- * Exec a program. Never returns. If you change this routine, you may
- * have to change the find_command routine as well.
- */
-#define environment() listvars(VEXPORT, VUNSET, 0)
-static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
static void
-shellexec(char **argv, const char *path, int idx)
+tryexec(char *cmd, char **argv, char **envp)
{
- char *cmdname;
- int e;
- char **envp;
- int exerrno;
+ int repeated = 0;
+ struct BB_applet *a;
+ int argc = 0;
+ char **c;
- clearredir(1);
- envp = environment();
- if (strchr(argv[0], '/') || is_safe_applet(argv[0])
+ if (strchr(cmd, '/') == NULL
+ && (a = find_applet_by_name(cmd)) != NULL
+ && is_safe_applet(cmd)
+ ) {
+ c = argv;
+ while (*c != NULL) {
+ c++; argc++;
+ }
+ applet_name = cmd;
+ exit(a->main(argc, argv));
+ }
#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- || find_applet_by_name(argv[0])
+ if (find_applet_by_name(cmd) != NULL) {
+ /* re-exec ourselves with the new arguments */
+ execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
+ /* If they called chroot or otherwise made the binary no longer
+ * executable, fall through */
+ }
#endif
- ) {
+
+ repeat:
+#ifdef SYSV
+ do {
+ execve(cmd, argv, envp);
+ } while (errno == EINTR);
+#else
+ execve(cmd, argv, envp);
+#endif
+ if (repeated++) {
+ free(argv);
+ } else if (errno == ENOEXEC) {
+ char **ap;
+ char **new;
+
+ for (ap = argv; *ap; ap++)
+ ;
+ ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
+ ap[1] = cmd;
+ *ap = cmd = (char *)DEFAULT_SHELL;
+ ap += 2;
+ argv++;
+ while ((*ap++ = *argv++))
+ ;
+ argv = new;
+ goto repeat;
+ }
+}
+
+/*
+ * Exec a program. Never returns. If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+#define environment() listvars(VEXPORT, VUNSET, 0)
+static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
+static void
+shellexec(char **argv, const char *path, int idx)
+{
+ char *cmdname;
+ int e;
+ char **envp;
+ int exerrno;
+
+ clearredir(1);
+ envp = environment();
+ if (strchr(argv[0], '/') || is_safe_applet(argv[0])
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ || find_applet_by_name(argv[0])
+#endif
+ ) {
tryexec(argv[0], argv, envp);
e = errno;
} else {
/* NOTREACHED */
}
+static void
+printentry(struct tblentry *cmdp)
+{
+ int idx;
+ const char *path;
+ char *name;
+
+ idx = cmdp->param.index;
+ path = pathval();
+ do {
+ name = padvance(&path, cmdp->cmdname);
+ stunalloc(name);
+ } while (--idx >= 0);
+ out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+}
+/*
+ * Clear out command entries. The argument specifies the first entry in
+ * PATH which has changed.
+ */
static void
-tryexec(char *cmd, char **argv, char **envp)
+clearcmdentry(int firstchange)
{
- int repeated = 0;
- struct BB_applet *a;
- int argc = 0;
- char **c;
+ struct tblentry **tblp;
+ struct tblentry **pp;
+ struct tblentry *cmdp;
- if (strchr(cmd, '/') == NULL
- && (a = find_applet_by_name(cmd)) != NULL
- && is_safe_applet(cmd)
- ) {
- c = argv;
- while (*c != NULL) {
- c++; argc++;
+ INT_OFF;
+ for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
+ pp = tblp;
+ while ((cmdp = *pp) != NULL) {
+ if ((cmdp->cmdtype == CMDNORMAL &&
+ cmdp->param.index >= firstchange)
+ || (cmdp->cmdtype == CMDBUILTIN &&
+ builtinloc >= firstchange)
+ ) {
+ *pp = cmdp->next;
+ free(cmdp);
+ } else {
+ pp = &cmdp->next;
+ }
}
- applet_name = cmd;
- exit(a->main(argc, argv));
- }
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(cmd) != NULL) {
- /* re-exec ourselves with the new arguments */
- execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
- /* If they called chroot or otherwise made the binary no longer
- * executable, fall through */
}
-#endif
+ INT_ON;
+}
- repeat:
-#ifdef SYSV
- do {
- execve(cmd, argv, envp);
- } while (errno == EINTR);
-#else
- execve(cmd, argv, envp);
-#endif
- if (repeated++) {
- free(argv);
- } else if (errno == ENOEXEC) {
- char **ap;
- char **new;
+/*
+ * Locate a command in the command hash table. If "add" is nonzero,
+ * add the command to the table if it is not already present. The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ *
+ * Interrupts must be off if called with add != 0.
+ */
+static struct tblentry **lastcmdentry;
- for (ap = argv; *ap; ap++)
- ;
- ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
- ap[1] = cmd;
- *ap = cmd = (char *)DEFAULT_SHELL;
- ap += 2;
- argv++;
- while ((*ap++ = *argv++))
- ;
- argv = new;
- goto repeat;
+static struct tblentry *
+cmdlookup(const char *name, int add)
+{
+ unsigned int hashval;
+ const char *p;
+ struct tblentry *cmdp;
+ struct tblentry **pp;
+
+ p = name;
+ hashval = (unsigned char)*p << 4;
+ while (*p)
+ hashval += (unsigned char)*p++;
+ hashval &= 0x7FFF;
+ pp = &cmdtable[hashval % CMDTABLESIZE];
+ for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+ if (strcmp(cmdp->cmdname, name) == 0)
+ break;
+ pp = &cmdp->next;
+ }
+ if (add && cmdp == NULL) {
+ cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
+ + strlen(name) + 1);
+ cmdp->next = NULL;
+ cmdp->cmdtype = CMDUNKNOWN;
+ strcpy(cmdp->cmdname, name);
}
+ lastcmdentry = pp;
+ return cmdp;
}
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+static void
+delete_cmd_entry(void)
+{
+ struct tblentry *cmdp;
-/*** Command hashing code ***/
+ INT_OFF;
+ cmdp = *lastcmdentry;
+ *lastcmdentry = cmdp->next;
+ if (cmdp->cmdtype == CMDFUNCTION)
+ freefunc(cmdp->param.func);
+ free(cmdp);
+ INT_ON;
+}
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
static void
-printentry(struct tblentry *cmdp)
+addcmdentry(char *name, struct cmdentry *entry)
{
- int idx;
- const char *path;
- char *name;
+ struct tblentry *cmdp;
- idx = cmdp->param.index;
- path = pathval();
- do {
- name = padvance(&path, cmdp->cmdname);
- stunalloc(name);
- } while (--idx >= 0);
- out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+ cmdp = cmdlookup(name, 1);
+ if (cmdp->cmdtype == CMDFUNCTION) {
+ freefunc(cmdp->param.func);
+ }
+ cmdp->cmdtype = entry->cmdtype;
+ cmdp->param = entry->u;
+ cmdp->rehash = 0;
}
-
static int
hashcmd(int argc, char **argv)
{
return c;
}
-
/*
* Resolve a command name. If you change this routine, you may have to
* change the shellexec routine as well.
entry->u = cmdp->param;
}
-
/*
* Search the table of builtin commands.
*/
return bp;
}
-
/*
* Called when a cd is done. Marks all commands so the next time they
* are executed they will be rehashed.
}
}
-
/*
* Fix command hash table when PATH changed.
* Called before PATH is changed. The argument is the new value of PATH;
}
-/*
- * Clear out command entries. The argument specifies the first entry in
- * PATH which has changed.
- */
-static void
-clearcmdentry(int firstchange)
-{
- struct tblentry **tblp;
- struct tblentry **pp;
- struct tblentry *cmdp;
-
- INT_OFF;
- for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
- pp = tblp;
- while ((cmdp = *pp) != NULL) {
- if ((cmdp->cmdtype == CMDNORMAL &&
- cmdp->param.index >= firstchange)
- || (cmdp->cmdtype == CMDBUILTIN &&
- builtinloc >= firstchange)
- ) {
- *pp = cmdp->next;
- free(cmdp);
- } else {
- pp = &cmdp->next;
- }
- }
- }
- INT_ON;
-}
-
-
-/*
- * Locate a command in the command hash table. If "add" is nonzero,
- * add the command to the table if it is not already present. The
- * variable "lastcmdentry" is set to point to the address of the link
- * pointing to the entry, so that delete_cmd_entry can delete the
- * entry.
- *
- * Interrupts must be off if called with add != 0.
- */
-static struct tblentry **lastcmdentry;
-
-static struct tblentry *
-cmdlookup(const char *name, int add)
-{
- unsigned int hashval;
- const char *p;
- struct tblentry *cmdp;
- struct tblentry **pp;
-
- p = name;
- hashval = (unsigned char)*p << 4;
- while (*p)
- hashval += (unsigned char)*p++;
- hashval &= 0x7FFF;
- pp = &cmdtable[hashval % CMDTABLESIZE];
- for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
- if (strcmp(cmdp->cmdname, name) == 0)
- break;
- pp = &cmdp->next;
- }
- if (add && cmdp == NULL) {
- cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
- + strlen(name) + 1);
- cmdp->next = NULL;
- cmdp->cmdtype = CMDUNKNOWN;
- strcpy(cmdp->cmdname, name);
- }
- lastcmdentry = pp;
- return cmdp;
-}
-
-
-/*
- * Delete the command entry returned on the last lookup.
- */
-static void
-delete_cmd_entry(void)
-{
- struct tblentry *cmdp;
-
- INT_OFF;
- cmdp = *lastcmdentry;
- *lastcmdentry = cmdp->next;
- if (cmdp->cmdtype == CMDFUNCTION)
- freefunc(cmdp->param.func);
- free(cmdp);
- INT_ON;
-}
-
-
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name - except special builtins.
- */
-static void
-addcmdentry(char *name, struct cmdentry *entry)
-{
- struct tblentry *cmdp;
-
- cmdp = cmdlookup(name, 1);
- if (cmdp->cmdtype == CMDFUNCTION) {
- freefunc(cmdp->param.func);
- }
- cmdp->cmdtype = entry->cmdtype;
- cmdp->param = entry->u;
- cmdp->rehash = 0;
-}
-
-
/*
* Make a copy of a parse tree.
*/
delete_cmd_entry();
}
+
/*
* Locate and print what a word is...
*/
}
-/* input.c */
-
-/*
+/* ============ input.c
+ *
* This implements the input routines used by the parser.
*/
-
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
-static void pushfile(void);
-
-/*
- * Read a character from the script, returning PEOF on end of file.
- * Nul characters in the input are silently discarded.
- */
-static int preadbuffer(void);
-#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
+enum {
+ INPUT_PUSH_FILE = 1,
+ INPUT_NOFILE_OK = 2,
+};
-#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#define pgetc_macro() pgetc()
-static int
-pgetc(void)
-{
- return pgetc_as_macro();
-}
-#else
-#define pgetc_macro() pgetc_as_macro()
-static int
-pgetc(void)
+static void
+popstring(void)
{
- return pgetc_macro();
-}
-#endif
+ struct strpush *sp = parsefile->strpush;
-/*
- * Same as pgetc(), but ignores PEOA.
- */
+ INT_OFF;
#if ENABLE_ASH_ALIAS
-static int
-pgetc2(void)
-{
- int c;
-
- do {
- c = pgetc_macro();
- } while (c == PEOA);
- return c;
-}
-#else
-static int
-pgetc2(void)
-{
- return pgetc_macro();
-}
-#endif
-
-/*
- * Read a line from the script.
- */
-static char *
-pfgets(char *line, int len)
-{
- char *p = line;
- int nleft = len;
- int c;
-
- while (--nleft > 0) {
- c = pgetc2();
- if (c == PEOF) {
- if (p == line)
- return NULL;
- break;
- }
- *p++ = c;
- if (c == '\n')
- break;
- }
- *p = '\0';
- return line;
-}
-
-#if ENABLE_FEATURE_EDITING_VI
-#define setvimode(on) do { \
- if (on) line_input_state->flags |= VI_MODE; \
- else line_input_state->flags &= ~VI_MODE; \
-} while (0)
-#else
-#define setvimode(on) viflag = 0 /* forcibly keep the option off */
+ if (sp->ap) {
+ if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+ checkkwd |= CHKALIAS;
+ }
+ if (sp->string != sp->ap->val) {
+ free(sp->string);
+ }
+ sp->ap->flag &= ~ALIASINUSE;
+ if (sp->ap->flag & ALIASDEAD) {
+ unalias(sp->ap->name);
+ }
+ }
#endif
+ parsenextc = sp->prevstring;
+ parsenleft = sp->prevnleft;
+/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+ parsefile->strpush = sp->prev;
+ if (sp != &(parsefile->basestrpush))
+ free(sp);
+ INT_ON;
+}
static int
preadfd(void)
return SC2INT(*parsenextc++);
}
+
+#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+#define pgetc_macro() pgetc()
+static int
+pgetc(void)
+{
+ return pgetc_as_macro();
+}
+#else
+#define pgetc_macro() pgetc_as_macro()
+static int
+pgetc(void)
+{
+ return pgetc_macro();
+}
+#endif
+
+/*
+ * Same as pgetc(), but ignores PEOA.
+ */
+#if ENABLE_ASH_ALIAS
+static int
+pgetc2(void)
+{
+ int c;
+
+ do {
+ c = pgetc_macro();
+ } while (c == PEOA);
+ return c;
+}
+#else
+static int
+pgetc2(void)
+{
+ return pgetc_macro();
+}
+#endif
+
+/*
+ * Read a line from the script.
+ */
+static char *
+pfgets(char *line, int len)
+{
+ char *p = line;
+ int nleft = len;
+ int c;
+
+ while (--nleft > 0) {
+ c = pgetc2();
+ if (c == PEOF) {
+ if (p == line)
+ return NULL;
+ break;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+ return line;
+}
+
/*
* Undo the last call to pgetc. Only one character may be pushed back.
* PEOF may be pushed back.
INT_ON;
}
-static void
-popstring(void)
-{
- struct strpush *sp = parsefile->strpush;
-
- INT_OFF;
-#if ENABLE_ASH_ALIAS
- if (sp->ap) {
- if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
- checkkwd |= CHKALIAS;
- }
- if (sp->string != sp->ap->val) {
- free(sp->string);
- }
- sp->ap->flag &= ~ALIASINUSE;
- if (sp->ap->flag & ALIASDEAD) {
- unalias(sp->ap->name);
- }
- }
-#endif
- parsenextc = sp->prevstring;
- parsenleft = sp->prevnleft;
-/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
- parsefile->strpush = sp->prev;
- if (sp != &(parsefile->basestrpush))
- free(sp);
- INT_ON;
-}
-
-
-/*
- * Set the input to take input from a file. If push is set, push the
- * old input onto the stack first.
- */
-static int
-setinputfile(const char *fname, int flags)
-{
- int fd;
- int fd2;
-
- INT_OFF;
- fd = open(fname, O_RDONLY);
- if (fd < 0) {
- if (flags & INPUT_NOFILE_OK)
- goto out;
- ash_msg_and_raise_error("Can't open %s", fname);
- }
- if (fd < 10) {
- fd2 = copyfd(fd, 10);
- close(fd);
- if (fd2 < 0)
- ash_msg_and_raise_error("Out of file descriptors");
- fd = fd2;
- }
- setinputfd(fd, flags & INPUT_PUSH_FILE);
- out:
- INT_ON;
- return fd;
-}
-
-
-/*
- * Like setinputfile, but takes an open file descriptor. Call this with
- * interrupts off.
- */
-static void
-setinputfd(int fd, int push)
-{
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (push) {
- pushfile();
- parsefile->buf = 0;
- }
- parsefile->fd = fd;
- if (parsefile->buf == NULL)
- parsefile->buf = ckmalloc(IBUFSIZ);
- parselleft = parsenleft = 0;
- plinno = 1;
-}
-
-
-/*
- * Like setinputfile, but takes input from a string.
- */
-static void
-setinputstring(char *string)
-{
- INT_OFF;
- pushfile();
- parsenextc = string;
- parsenleft = strlen(string);
- parsefile->buf = NULL;
- plinno = 1;
- INT_ON;
-}
-
-
/*
* To handle the "." command, a stack of input files is used. Pushfile
* adds a new entry to the stack and popfile restores the previous level.
parsefile = pf;
}
-
static void
popfile(void)
{
INT_ON;
}
-
/*
* Return to top level.
*/
popfile();
}
-
/*
* Close the file(s) that the shell is reading commands from. Called
* after a fork is done.
}
}
+/*
+ * Like setinputfile, but takes an open file descriptor. Call this with
+ * interrupts off.
+ */
+static void
+setinputfd(int fd, int push)
+{
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ if (push) {
+ pushfile();
+ parsefile->buf = 0;
+ }
+ parsefile->fd = fd;
+ if (parsefile->buf == NULL)
+ parsefile->buf = ckmalloc(IBUFSIZ);
+ parselleft = parsenleft = 0;
+ plinno = 1;
+}
+
+/*
+ * Set the input to take input from a file. If push is set, push the
+ * old input onto the stack first.
+ */
+static int
+setinputfile(const char *fname, int flags)
+{
+ int fd;
+ int fd2;
+
+ INT_OFF;
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ if (flags & INPUT_NOFILE_OK)
+ goto out;
+ ash_msg_and_raise_error("Can't open %s", fname);
+ }
+ if (fd < 10) {
+ fd2 = copyfd(fd, 10);
+ close(fd);
+ if (fd2 < 0)
+ ash_msg_and_raise_error("Out of file descriptors");
+ fd = fd2;
+ }
+ setinputfd(fd, flags & INPUT_PUSH_FILE);
+ out:
+ INT_ON;
+ return fd;
+}
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+static void
+setinputstring(char *string)
+{
+ INT_OFF;
+ pushfile();
+ parsenextc = string;
+ parsenleft = strlen(string);
+ parsefile->buf = NULL;
+ plinno = 1;
+ INT_ON;
+}
+
+
/* jobs.c */
/* mode flags for set_curjob */
}
+#if ENABLE_FEATURE_EDITING_VI
+#define setvimode(on) do { \
+ if (on) line_input_state->flags |= VI_MODE; \
+ else line_input_state->flags &= ~VI_MODE; \
+} while (0)
+#else
+#define setvimode(on) viflag = 0 /* forcibly keep the option off */
+#endif
+
static void
optschanged(void)
{
n->ndup.vname = NULL;
if (isdigit(text[0]) && text[1] == '\0')
- n->ndup.dupfd = digit_val(text[0]);
+ n->ndup.dupfd = text[0] - '0';
else if (LONE_DASH(text))
n->ndup.dupfd = -1;
else {
}
}
if (fd != '\0')
- np->nfile.fd = digit_val(fd);
+ np->nfile.fd = fd - '0';
redirnode = np;
goto parseredir_return;
}