*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/*
- * The following should be set to reflect the type of system you have:
- * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
- * define SYSV if you are running under System V.
- * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
- * define DEBUG=2 to compile in and turn on debugging.
- *
- * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
- * debugging info will be written to ./trace and a quit signal
- * will generate a core dump.
- */
-#define DEBUG 0
-/* Tweak debug output verbosity here */
-#define DEBUG_TIME 0
-#define DEBUG_PID 1
-#define DEBUG_SIG 1
-#define DEBUG_INTONOFF 0
-
-#define PROFILE 0
-
-#define JOBS ENABLE_ASH_JOB_CONTROL
-
-#include <setjmp.h>
-#include <fnmatch.h>
-#include <sys/times.h>
-#include <sys/utsname.h> /* for setting $HOSTNAME */
-
-#include "busybox.h" /* for applet_names */
-
-#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
-/* Bionic at least up to version 24 has no glob() */
-# undef ENABLE_ASH_INTERNAL_GLOB
-# define ENABLE_ASH_INTERNAL_GLOB 1
-#endif
-
-#if !ENABLE_ASH_INTERNAL_GLOB
-# include <glob.h>
-#endif
-
-#include "unicode.h"
-#include "shell_common.h"
-#if ENABLE_SH_MATH_SUPPORT
-# include "math.h"
-#endif
-#if ENABLE_ASH_RANDOM_SUPPORT
-# include "random.h"
-#else
-# define CLEAR_RANDOM_T(rnd) ((void)0)
-#endif
-
-#include "NUM_APPLETS.h"
-#if NUM_APPLETS == 1
-/* STANDALONE does not make sense, and won't compile */
-# undef CONFIG_FEATURE_SH_STANDALONE
-# undef ENABLE_FEATURE_SH_STANDALONE
-# undef IF_FEATURE_SH_STANDALONE
-# undef IF_NOT_FEATURE_SH_STANDALONE
-# define ENABLE_FEATURE_SH_STANDALONE 0
-# define IF_FEATURE_SH_STANDALONE(...)
-# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
-#endif
-
-#ifndef PIPE_BUF
-# define PIPE_BUF 4096 /* amount of buffering in a pipe */
-#endif
-
-#if !BB_MMU
-# error "Do not even bother, ash will not run on NOMMU machine"
-#endif
-
//config:config ASH
//config: bool "ash"
//config: default y
//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
//config: (written by Kenneth Almquist) from NetBSD.
//config:
+//config:# ash options
+//config:# note: Don't remove !NOMMU part in the next line; it would break
+//config:# menuconfig's indenting.
+//config:if !NOMMU && (ASH || SH_IS_ASH || BASH_IS_ASH)
+//config:
//config:config ASH_OPTIMIZE_FOR_SIZE
//config: bool "Optimize for size instead of speed"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Compile ash for reduced size at the price of speed.
//config:
//config:config ASH_INTERNAL_GLOB
//config: bool "Use internal glob() implementation"
-//config: default n
-//config: depends on ASH
+//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Do not use glob() function from libc, use internal implementation.
//config: Use this if you are getting "glob.h: No such file or directory"
//config:config ASH_RANDOM_SUPPORT
//config: bool "Pseudorandom generator and $RANDOM variable"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
//config: Each read of "$RANDOM" will generate a new pseudorandom value.
//config:config ASH_EXPAND_PRMT
//config: bool "Expand prompt string"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: "PS#" may contain volatile content, such as backquote commands.
//config: This option recreates the prompt string from the environment
//config:config ASH_BASH_COMPAT
//config: bool "bash-compatible extensions"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable bash-compatible extensions.
//config:
//config:config ASH_IDLE_TIMEOUT
//config: bool "Idle timeout variable"
//config: default n
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
//config:
//config:config ASH_JOB_CONTROL
//config: bool "Job control"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable job control in the ash shell.
//config:
//config:config ASH_ALIAS
//config: bool "Alias support"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable alias support in the ash shell.
//config:
//config:config ASH_GETOPTS
//config: bool "Builtin getopt to parse positional parameters"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable support for getopts builtin in ash.
//config:
//config:config ASH_BUILTIN_ECHO
//config: bool "Builtin version of 'echo'"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable support for echo builtin in ash.
//config:
//config:config ASH_BUILTIN_PRINTF
//config: bool "Builtin version of 'printf'"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable support for printf builtin in ash.
//config:
//config:config ASH_BUILTIN_TEST
//config: bool "Builtin version of 'test'"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable support for test builtin in ash.
//config:
//config:config ASH_HELP
//config: bool "help builtin"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable help builtin in ash.
//config:
//config:config ASH_CMDCMD
//config: bool "'command' command to override shell builtins"
//config: default y
-//config: depends on ASH
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable support for the ash 'command' builtin, which allows
//config: you to run the specified command with the specified arguments,
//config:
//config:config ASH_MAIL
//config: bool "Check for new mail on interactive shells"
-//config: default n
-//config: depends on ASH
+//config: default y
+//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH
//config: help
//config: Enable "check for new mail" function in the ash shell.
//config:
+//config:endif # ash options
//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
-//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
+//applet:IF_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
+//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
+//kbuild:lib-$(CONFIG_SH_IS_ASH) += ash.o ash_ptr_hack.o shell_common.o
+//kbuild:lib-$(CONFIG_BASH_IS_ASH) += ash.o ash_ptr_hack.o shell_common.o
//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
+/*
+ * The following should be set to reflect the type of system you have:
+ * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ * define SYSV if you are running under System V.
+ * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
+ * define DEBUG=2 to compile in and turn on debugging.
+ *
+ * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
+ * debugging info will be written to ./trace and a quit signal
+ * will generate a core dump.
+ */
+#define DEBUG 0
+/* Tweak debug output verbosity here */
+#define DEBUG_TIME 0
+#define DEBUG_PID 1
+#define DEBUG_SIG 1
+#define DEBUG_INTONOFF 0
+
+#define PROFILE 0
+
+#define JOBS ENABLE_ASH_JOB_CONTROL
+
+#include <setjmp.h>
+#include <fnmatch.h>
+#include <sys/times.h>
+#include <sys/utsname.h> /* for setting $HOSTNAME */
+
+#include "busybox.h" /* for applet_names */
+
+#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
+/* Bionic at least up to version 24 has no glob() */
+# undef ENABLE_ASH_INTERNAL_GLOB
+# define ENABLE_ASH_INTERNAL_GLOB 1
+#endif
+
+#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
+# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
+# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
+# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
+# error glob() should unbackslash them and match. uClibc does not unbackslash,
+# error fails to match dirname, subsequently not expanding <pattern> in it.
+// Testcase:
+// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
+// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
+#endif
+
+#if !ENABLE_ASH_INTERNAL_GLOB
+# include <glob.h>
+#endif
+
+#include "unicode.h"
+#include "shell_common.h"
+#if ENABLE_FEATURE_SH_MATH
+# include "math.h"
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+# include "random.h"
+#else
+# define CLEAR_RANDOM_T(rnd) ((void)0)
+#endif
+
+#include "NUM_APPLETS.h"
+#if NUM_APPLETS == 1
+/* STANDALONE does not make sense, and won't compile */
+# undef CONFIG_FEATURE_SH_STANDALONE
+# undef ENABLE_FEATURE_SH_STANDALONE
+# undef IF_FEATURE_SH_STANDALONE
+# undef IF_NOT_FEATURE_SH_STANDALONE
+# define ENABLE_FEATURE_SH_STANDALONE 0
+# define IF_FEATURE_SH_STANDALONE(...)
+# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
+#endif
+
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096 /* amount of buffering in a pipe */
+#endif
+
+#if !BB_MMU
+# error "Do not even bother, ash will not run on NOMMU machine"
+#endif
+
/* ============ Hash table sizes. Configurable. */
{
va_list ap;
+ exitstatus = 2;
+
va_start(ap, msg);
ash_vmsg_and_raise(EXERROR, msg, ap);
/* NOTREACHED */
return NULL;
}
+#if ENABLE_UNICODE_SUPPORT
static void
reinit_unicode_for_ash(void)
{
reinit_unicode(s);
}
}
+#else
+# define reinit_unicode_for_ash() ((void)0)
+#endif
/*
* Search the environment of a builtin command.
#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
#else
# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
# endif
};
+#if 1
# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
+#else /* debug version, caught one signed char bug */
+# define SIT(c, syntax) \
+ ({ \
+ if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
+ bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
+ if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
+ bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
+ ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
+ })
+#endif
#endif /* !USE_SIT_FUNCTION */
case CTLBACKQ:
str = "$(...)";
goto dostr;
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
case CTLARI:
str = "$((";
goto dostr;
/* Careful to not accidentally "save"
* to the same fd as right side fd in N>&M */
int minfd = right_fd < 10 ? 10 : right_fd + 1;
+#if defined(F_DUPFD_CLOEXEC)
+ i = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
+#else
i = fcntl(fd, F_DUPFD, minfd);
-/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
- * are closed in popredir() in the child, preventing them from leaking
- * into child. (popredir() also cleans up the mess in case of failures)
- */
+#endif
if (i == -1) {
i = errno;
if (i != EBADF) {
remember_to_close:
i = CLOSED;
} else { /* fd is open, save its copy */
+#if !defined(F_DUPFD_CLOEXEC)
+ fcntl(i, F_SETFD, FD_CLOEXEC);
+#endif
/* "exec fd>&-" should not close fds
* which point to script file(s).
* Force them to be restored afterwards */
* We have to deal with backquotes, shell variables, and file metacharacters.
*/
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
static arith_t
ash_arith(const char *s)
{
/*
* Our own itoa().
*/
-#if !ENABLE_SH_MATH_SUPPORT
+#if !ENABLE_FEATURE_SH_MATH
/* cvtnum() is used even if math support is off (to prepare $? values and such) */
typedef long arith_t;
# define ARITH_FMT "%ld"
do {
unsigned char c = *p++;
if (c) {
- int n = SIT(c, syntax);
- if ((quotes & QUOTES_ESC)
- && ((n == CCTL)
- || (((quotes & EXP_FULL) || syntax != BASESYNTAX)
- && n == CBACK)
- )
- ) {
- USTPUTC(CTLESC, q);
+ if (quotes & QUOTES_ESC) {
+ int n = SIT(c, syntax);
+ if (n == CCTL
+ || (((quotes & EXP_FULL) || syntax != BASESYNTAX)
+ && n == CBACK
+ )
+ ) {
+ USTPUTC(CTLESC, q);
+ }
}
} else if (!(quotes & QUOTES_KEEPNUL))
continue;
stackblock() + startloc));
}
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
/*
* Expand arithmetic expression. Backup to start of expression,
* evaluate, place result in (backed up) result, adjust string position.
CTLESC,
CTLVAR,
CTLBACKQ,
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
CTLENDARI,
#endif
'\0'
c = p[length];
if (c) {
if (!(c & 0x80)
- IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
+ IF_FEATURE_SH_MATH(|| c == CTLENDARI)
) {
/* c == '=' || c == ':' || c == CTLENDARI */
length++;
expbackq(argbackq->n, flags | inquotes);
argbackq = argbackq->next;
goto start;
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
case CTLENDARI:
p--;
expari(flags | inquotes);
#if MAX_HISTORY
static int historycmd(int, char **) FAST_FUNC;
#endif
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
static int letcmd(int, char **) FAST_FUNC;
#endif
static int readcmd(int, char **) FAST_FUNC;
{ BUILTIN_SPEC_REG ":" , truecmd },
#if ENABLE_ASH_BUILTIN_TEST
{ BUILTIN_REGULAR "[" , testcmd },
-#if ENABLE_ASH_BASH_COMPAT
+# if ENABLE_ASH_BASH_COMPAT
{ BUILTIN_REGULAR "[[" , testcmd },
-#endif
+# endif
#endif
#if ENABLE_ASH_ALIAS
{ BUILTIN_REG_ASSG "alias" , aliascmd },
{ BUILTIN_REGULAR "jobs" , jobscmd },
{ BUILTIN_REGULAR "kill" , killcmd },
#endif
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
{ BUILTIN_NOSPEC "let" , letcmd },
#endif
{ BUILTIN_ASSIGN "local" , localcmd },
}
if (status) {
+ bail:
+ exitstatus = status;
+
/* We have a redirection error. */
if (spclbltin > 0)
raise_exception(EXERROR);
- bail:
- exitstatus = status;
+
goto out;
}
reinit_unicode_for_ash();
nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
if (nr == 0) {
- /* Ctrl+C pressed */
+ /* ^C pressed, "convert" to SIGINT */
+ write(STDOUT_FILENO, "^C", 2);
if (trap[SIGINT]) {
buf[0] = '\n';
buf[1] = '\0';
raise(SIGINT);
return 1;
}
+ exitstatus = 128 + SIGINT;
+ bb_putchar('\n');
goto retry;
}
if (nr < 0) {
return g_parsefile->lastc[--g_parsefile->unget];
if (--g_parsefile->left_in_line >= 0)
- c = (signed char)*g_parsefile->next_to_pgetc++;
+ c = (unsigned char)*g_parsefile->next_to_pgetc++;
else
c = preadbuffer();
smallint quotef;
smallint dblquote;
smallint oldstyle;
- smallint prevsyntax; /* syntax before arithmetic */
+ IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */
#if ENABLE_ASH_EXPAND_PRMT
smallint pssyntax; /* we are expanding a prompt string */
#endif
int varnest; /* levels of variables expansion */
- int arinest; /* levels of arithmetic expansion */
- int parenlevel; /* levels of parens in arithmetic */
+ IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
+ IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
int dqvarnest; /* levels of variables expansion within double quotes */
IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
startlinno = g_parsefile->linno;
bqlist = NULL;
quotef = 0;
- prevsyntax = 0;
+ IF_FEATURE_SH_MATH(prevsyntax = 0;)
#if ENABLE_ASH_EXPAND_PRMT
pssyntax = (syntax == PSSYNTAX);
if (pssyntax)
#endif
dblquote = (syntax == DQSYNTAX);
varnest = 0;
- arinest = 0;
- parenlevel = 0;
+ IF_FEATURE_SH_MATH(arinest = 0;)
+ IF_FEATURE_SH_MATH(parenlevel = 0;)
dqvarnest = 0;
STARTSTACKSTR(out);
}
USTPUTC(c, out);
break;
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
case CLP: /* '(' in arithmetic */
parenlevel++;
USTPUTC(c, out);
} /* for (;;) */
endword:
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
if (syntax == ARISYNTAX)
raise_error_syntax("missing '))'");
#endif
} else if (c == '(') {
/* $(command) or $((arith)) */
if (pgetc_eatbnl() == '(') {
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
PARSEARITH();
#else
raise_error_syntax("you disabled math support for $((arith)) syntax");
goto parsebackq_newreturn;
}
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
/*
* Parse an arithmetic expansion (indicate start of one and set state)
*/
return 0;
}
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
/*
* The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
//usage:#define ash_full_usage "\n\n"
//usage: "Unix shell interpreter"
-//usage:#if ENABLE_FEATURE_SH_IS_ASH
-//usage:# define sh_trivial_usage ash_trivial_usage
-//usage:# define sh_full_usage ash_full_usage
-//usage:#endif
-//usage:#if ENABLE_FEATURE_BASH_IS_ASH
-//usage:# define bash_trivial_usage ash_trivial_usage
-//usage:# define bash_full_usage ash_full_usage
-//usage:#endif
-
/*
* Process the shell command line arguments.
*/