//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.
+ * DEBUG=1 to compile in debugging ('set -o debug' turns on)
+ * DEBUG=2 to compile in and turn on debugging.
+ * When debugging is on ("set -o debug" was executed, or DEBUG=2),
+ * debugging info is written to ./trace, quit signal generates core dump.
*/
#define DEBUG 0
/* Tweak debug output verbosity here */
#include <fnmatch.h>
#include <sys/times.h>
#include <sys/utsname.h> /* for setting $HOSTNAME */
-
#include "busybox.h" /* for applet_names */
+/* So far, all bash compat is controlled by one config option */
+/* Separate defines document which part of code implements what */
+/* function keyword */
+#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
+#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
+/* &>file */
+#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
+#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
+/* $'...' */
+#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
+#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
+#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
+#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
+#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
+#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
+/* [[ EXPR ]] */
+#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
+#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
+#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
+#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
+#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
+
#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
/* Bionic at least up to version 24 has no glob() */
# undef ENABLE_ASH_INTERNAL_GLOB
"b" "notify",
"u" "nounset",
"\0" "vi"
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_PIPEFAIL
,"\0" "pipefail"
#endif
#if DEBUG
#define bflag optlist[11]
#define uflag optlist[12]
#define viflag optlist[13]
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_PIPEFAIL
# define pipefail optlist[14]
#else
# define pipefail 0
#endif
#if DEBUG
-# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
-# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
+# define nolog optlist[14 + BASH_PIPEFAIL]
+# define debug optlist[15 + BASH_PIPEFAIL]
#endif
/* trap handler commands */
#define VSTRIMLEFT 0x8 /* ${var#pattern} */
#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
#define VSLENGTH 0xa /* ${#var} */
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SUBSTR
#define VSSUBSTR 0xc /* ${var:position:length} */
+#endif
+#if BASH_PATTERN_SUBST
#define VSREPLACE 0xd /* ${var/pattern/replacement} */
#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
#endif
#define NDEFUN 14
#define NARG 15
#define NTO 16
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
#define NTO2 17
#endif
#define NCLOBBER 18
case NTO: s = ">>"+1; dftfd = 1; break;
case NCLOBBER: s = ">|"; dftfd = 1; break;
case NAPPEND: s = ">>"; dftfd = 1; break;
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
case NTOFD: s = ">&"; dftfd = 1; break;
static const char vstype[VSTYPE + 1][3] = {
"", "}", "-", "+", "?", "=",
"%", "%%", "#", "##"
- IF_ASH_BASH_COMPAT(, ":", "/", "//")
+ IF_BASH_SUBSTR(, ":")
+ IF_BASH_PATTERN_SUBST(, "/", "//")
};
const char *p, *str;
case NAPPEND:
p = ">>";
goto redir;
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
case NTOFD:
goto ecreate;
break;
case NTO:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
/* Take care of noclobber mode. */
union node *tmp = redir;
do {
sv_pos++;
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
if (tmp->nfile.type == NTO2)
sv_pos++;
#endif
continue;
}
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
redirect_more:
#endif
if (need_to_remember(sv, fd)) {
}
} else if (fd != newfd) { /* move newfd to fd */
dup2_or_raise(newfd, fd);
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
if (!(redir->nfile.type == NTO2 && fd == 2))
#endif
close(newfd);
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
if (redir->nfile.type == NTO2 && fd == 1) {
/* We already redirected it to fd 1, now copy it to 2 */
newfd = 1;
rmescapes(char *str, int flag)
{
static const char qchars[] ALIGN1 = {
- IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' };
+ IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
char *p, *q, *r;
unsigned inquotes;
unsigned protect_against_glob;
unsigned globbing;
- IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;)
+ IF_BASH_PATTERN_SUBST(unsigned slash = flag & RMESCAPE_SLASH;)
- p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash));
+ p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash));
if (!p)
return str;
protect_against_glob = 0;
goto copy;
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_PATTERN_SUBST
else if (*p == '/' && slash) {
/* stop handling globbing and mark location of slash */
globbing = slash = 0;
char *loc;
char *rmesc, *rmescend;
char *str;
- IF_ASH_BASH_COMPAT(char *repl = NULL;)
- IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
+ IF_BASH_SUBSTR(int pos, len, orig_len;)
int amount, resetloc;
- IF_ASH_BASH_COMPAT(int workloc;)
+ IF_BASH_PATTERN_SUBST(int workloc;)
+ IF_BASH_PATTERN_SUBST(char *repl = NULL;)
int zero;
char *(*scan)(char*, char*, char*, char*, int, int);
varunset(p, varname, startp, varflags);
/* NOTREACHED */
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SUBSTR
case VSSUBSTR:
//TODO: support more general format ${v:EXPR:EXPR},
// where EXPR follows $(()) rules
amount = loc - expdest;
STADJUST(amount, expdest);
return loc;
-#endif
+#endif /* BASH_SUBSTR */
}
resetloc = expdest - (char *)stackblock();
+#if BASH_PATTERN_SUBST
/* We'll comeback here if we grow the stack while handling
* a VSREPLACE or VSREPLACEALL, since our pointers into the
* stack will need rebasing, and we'll need to remove our work
* areas each time
*/
- IF_ASH_BASH_COMPAT(restart:)
+ restart:
+#endif
amount = expdest - ((char *)stackblock() + resetloc);
STADJUST(-amount, expdest);
* RMESCAPE_SLASH causes preglob to work differently on the pattern
* and string. It's only used on the first call.
*/
- preglob(str, IF_ASH_BASH_COMPAT(
+ preglob(str, IF_BASH_PATTERN_SUBST(
(subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ?
- RMESCAPE_SLASH :) 0);
+ RMESCAPE_SLASH : ) 0);
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_PATTERN_SUBST
workloc = expdest - (char *)stackblock();
if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
char *idx, *end;
STADJUST(-amount, expdest);
return startp;
}
-#endif /* ENABLE_ASH_BASH_COMPAT */
+#endif /* BASH_PATTERN_SUBST */
subtype -= VSTRIMRIGHT;
#if DEBUG
case VSTRIMLEFTMAX:
case VSTRIMRIGHT:
case VSTRIMRIGHTMAX:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SUBSTR
case VSSUBSTR:
+#endif
+#if BASH_PATTERN_SUBST
case VSREPLACE:
case VSREPLACEALL:
#endif
TESAC,
TFI,
TFOR,
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_FUNCTION
TFUNCTION,
#endif
TIF,
/* 19 */ | (1u << TESAC)
/* 20 */ | (1u << TFI)
/* 21 */ | (0u << TFOR)
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_FUNCTION
/* 22 */ | (0u << TFUNCTION)
#endif
/* 23 */ | (0u << TIF)
"esac",
"fi",
"for",
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_FUNCTION
"function",
#endif
"if",
[NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
[NARG ] = SHELL_ALIGN(sizeof(struct narg)),
[NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
[NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
#endif
[NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
funcblocksize = calcsize(funcblocksize, n->narg.next);
break;
case NTO:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
case NCLOBBER:
new->narg.next = copynode(n->narg.next);
break;
case NTO:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
case NCLOBBER:
case NFROMTO:
case NFROM:
case NTO:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
case NTO2:
#endif
case NCLOBBER:
case NAPPEND:
expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
TRACE(("expredir expanded to '%s'\n", fn.list->text));
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
store_expfname:
#endif
#if 0
expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
if (fn.list == NULL)
ash_msg_and_raise_error("redir error");
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
//FIXME: we used expandarg with different args!
if (!isdigit_str9(fn.list->text)) {
/* >&file, not >&fd */
#if ENABLE_ASH_PRINTF
static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
#endif
-#if ENABLE_ASH_TEST
+#if ENABLE_ASH_TEST || BASH_TEST2
static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
#endif
{ BUILTIN_SPEC_REG ":" , truecmd },
#if ENABLE_ASH_TEST
{ BUILTIN_REGULAR "[" , testcmd },
-# if ENABLE_ASH_BASH_COMPAT
+#endif
+#if BASH_TEST2
{ BUILTIN_REGULAR "[[" , testcmd },
-# endif
#endif
#if ENABLE_ASH_ALIAS
{ BUILTIN_REG_ASSG "alias" , aliascmd },
{ BUILTIN_SPEC_REG "return" , returncmd },
{ BUILTIN_SPEC_REG "set" , setcmd },
{ BUILTIN_SPEC_REG "shift" , shiftcmd },
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SOURCE
{ BUILTIN_SPEC_REG "source" , dotcmd },
#endif
#if ENABLE_ASH_TEST
#define COMMANDCMD (builtintab + \
/* . : */ 2 + \
/* [ */ 1 * ENABLE_ASH_TEST + \
- /* [[ */ 1 * ENABLE_ASH_TEST * ENABLE_ASH_BASH_COMPAT + \
+ /* [[ */ 1 * BASH_TEST2 + \
/* alias */ 1 * ENABLE_ASH_ALIAS + \
/* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
/* break cd cddir */ 3)
union node *vars, **vpp;
union node **rpp, *redir;
int savecheckkwd;
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_TEST2
smallint double_brackets_flag = 0;
- smallint function_flag = 0;
#endif
+ IF_BASH_FUNCTION(smallint function_flag = 0;)
args = NULL;
app = &args;
checkkwd = savecheckkwd;
t = readtoken();
switch (t) {
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_FUNCTION
case TFUNCTION:
if (peektoken() != TWORD)
raise_error_unexpected_syntax(TWORD);
function_flag = 1;
break;
+#endif
+#if BASH_TEST2
case TAND: /* "&&" */
case TOR: /* "||" */
if (!double_brackets_flag) {
n->type = NARG;
/*n->narg.next = NULL; - stzalloc did it */
n->narg.text = wordtext;
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_TEST2
if (strcmp("[[", wordtext) == 0)
double_brackets_flag = 1;
else if (strcmp("]]", wordtext) == 0)
app = &n->narg.next;
savecheckkwd = 0;
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_FUNCTION
if (function_flag) {
checkkwd = CHKNL | CHKKWD;
switch (peektoken()) {
parsefname(); /* read name of redirection file */
break;
case TLP:
- IF_ASH_BASH_COMPAT(do_func:)
+ IF_BASH_FUNCTION(do_func:)
if (args && app == &args->narg.next
&& !vars && !redir
) {
const char *name;
/* We have a function */
- if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP)
+ if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
raise_error_unexpected_syntax(TRP);
name = n->narg.text;
if (!goodname(name)
n->narg.next = parse_command();
return n;
}
- IF_ASH_BASH_COMPAT(function_flag = 0;)
+ IF_BASH_FUNCTION(function_flag = 0;)
/* fall through */
default:
tokpushback = 1;
n1 = list(0);
t = TEND;
break;
- IF_ASH_BASH_COMPAT(case TFUNCTION:)
+ IF_BASH_FUNCTION(case TFUNCTION:)
case TWORD:
case TREDIR:
tokpushback = 1;
return n1;
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_DOLLAR_SQUOTE
static int
decode_dollar_squote(void)
{
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;)
+ IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
startlinno = g_parsefile->linno;
bqlist = NULL;
USTPUTC(c, out);
break;
case CCTL:
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_DOLLAR_SQUOTE
if (c == '\\' && bash_dollar_squote) {
c = decode_dollar_squote();
if (c == '\0') {
dblquote = 1;
goto quotemark;
case CENDQUOTE:
- IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
+ IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
if (eofmark != NULL && varnest == 0) {
USTPUTC(c, out);
} else {
break;
default:
if (varnest == 0) {
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
if (c == '&') {
//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
if (pgetc() == '>')
len = out - (char *)stackblock();
out = stackblock();
if (eofmark == NULL) {
- if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
+ if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
&& quotef == 0
) {
if (isdigit_str9(out)) {
pungetc();
}
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
else if (c == 0x100 + '>') { /* this flags &> redirection */
np->nfile.fd = 1;
pgetc(); /* this is '>', no need to check */
if (c > 255 /* PEOA or PEOF */
|| (c != '(' && c != '{' && !is_name(c) && !is_special(c))
) {
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_DOLLAR_SQUOTE
if (syntax != DQSYNTAX && c == '\'')
bash_dollar_squote = 1;
else
switch (c) {
case ':':
c = pgetc_eatbnl();
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SUBSTR
/* This check is only needed to not misinterpret
* ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
* constructs.
subtype++;
break;
}
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_PATTERN_SUBST
case '/':
/* ${v/[/]pattern/repl} */
//TODO: encode pattern and repl separately.
p += xxreadtoken_doubles + 1;
} else {
pungetc();
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_REDIR_OUTPUT
if (c == '&' && cc == '>') /* &> */
break; /* return readtoken1(...) */
#endif
setvareq((char*)defoptindvar, VTEXTFIXED);
setvar0("PPID", utoa(getppid()));
-#if ENABLE_ASH_BASH_COMPAT
+#if BASH_SHLVL_VAR
p = lookupvar("SHLVL");
setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
+#endif
+#if BASH_HOSTNAME_VAR
if (!lookupvar("HOSTNAME")) {
struct utsname uts;
uname(&uts);