#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
/* values of VSTYPE field */
-#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
-#define VSMINUS 0x2 /* ${var-text} */
-#define VSPLUS 0x3 /* ${var+text} */
-#define VSQUESTION 0x4 /* ${var?message} */
-#define VSASSIGN 0x5 /* ${var=text} */
-#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
-#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
-#define VSTRIMLEFT 0x8 /* ${var#pattern} */
-#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
-#define VSLENGTH 0xa /* ${#var} */
+#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
+#define VSMINUS 0x2 /* ${var-text} */
+#define VSPLUS 0x3 /* ${var+text} */
+#define VSQUESTION 0x4 /* ${var?message} */
+#define VSASSIGN 0x5 /* ${var=text} */
+#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
+#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
+#define VSTRIMLEFT 0x8 /* ${var#pattern} */
+#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
+#define VSLENGTH 0xa /* ${#var} */
+#if ENABLE_ASH_BASH_COMPAT
+#define VSSUBSTR 0xc /* ${var:position:length} */
+#define VSREPLACE 0xd /* ${var/pattern/replacement} */
+#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
+#endif
static const char dolatstr[] ALIGN1 = {
CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
{
#if DEBUG
if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
- write(2, "stunalloc\n", 10);
+ write(STDERR_FILENO, "stunalloc\n", 10);
abort();
}
#endif
INT_ON;
} else {
char *oldspace = g_stacknxt;
- int oldlen = g_stacknleft;
+ size_t oldlen = g_stacknleft;
char *p = stalloc(newlen);
/* free the space we just allocated */
return stackblock();
}
growstackblock();
- return stackblock() + len;
+ return (char *)stackblock() + len;
}
/*
break;
growstackblock();
}
- return stackblock() + len;
+ return (char *)stackblock() + len;
}
static char *
stack_nputstr(const char *s, size_t n, char *p)
{
p = makestrspace(n, p);
- p = memcpy(p, s, n) + n;
+ p = (char *)memcpy(p, s, n) + n;
return p;
}
q = p = makestrspace(len + 3, p);
*q++ = '\'';
- q = memcpy(q, s, len) + len;
+ q = (char *)memcpy(q, s, len) + len;
*q++ = '\'';
s += len;
q = p = makestrspace(len + 3, p);
*q++ = '"';
- q = memcpy(q, s, len) + len;
+ q = (char *)memcpy(q, s, len) + len;
*q++ = '"';
s += len;
#define vartab (G_var.vartab )
#define varinit (G_var.varinit )
#define INIT_G_var() do { \
- int i; \
+ unsigned i; \
(*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
barrier(); \
for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
}
INT_OFF;
nameeq = ckmalloc(namelen + vallen + 2);
- p = memcpy(nameeq, name, namelen) + namelen;
+ p = (char *)memcpy(nameeq, name, namelen) + namelen;
if (val) {
*p++ = '=';
- p = memcpy(p, val, vallen) + vallen;
+ p = (char *)memcpy(p, val, vallen) + vallen;
}
*p = '\0';
setvareq(nameeq, flags | VNOSAVE);
if (*path == NULL)
return NULL;
start = *path;
- for (p = start; *p && *p != ':' && *p != '%'; p++);
+ for (p = start; *p && *p != ':' && *p != '%'; p++)
+ continue;
len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
while (stackblocksize() < len)
growstackblock();
pathopt = NULL;
if (*p == '%') {
pathopt = ++p;
- while (*p && *p != ':') p++;
+ while (*p && *p != ':')
+ p++;
}
if (*p == ':')
*path = p + 1;
new = stack_putstr(curdir, new);
}
new = makestrspace(strlen(dir) + 2, new);
- lim = stackblock() + 1;
+ lim = (char *)stackblock() + 1;
if (*dir != '/') {
if (new[-1] != '/')
USTPUTC('/', new);
}
if (is_number(p)) {
+// TODO: number() instead? It does error checking...
num = atoi(p);
if (num < njobs) {
jp = jobtab + num - 1;
static void
cmdputs(const char *s)
{
+ static const char vstype[VSTYPE + 1][3] = {
+ "", "}", "-", "+", "?", "=",
+ "%", "%%", "#", "##"
+ USE_ASH_BASH_COMPAT(, ":", "/", "//")
+ };
+
const char *p, *str;
char c, cc[2] = " ";
char *nextc;
int subtype = 0;
int quoted = 0;
- static const char vstype[VSTYPE + 1][4] = {
- "", "}", "-", "+", "?", "=",
- "%", "%%", "#", "##"
- };
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
p = s;
full_write(pip[1], redir->nhere.doc->narg.text, len);
else
expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
out:
close(pip[1]);
}
q = r;
if (len > 0) {
- q = memcpy(q, str, len) + len;
+ q = (char *)memcpy(q, str, len) + len;
}
}
inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
char *p;
char *dest;
int startloc;
- int syntax = quoted? DQSYNTAX : BASESYNTAX;
+ int syntax = quoted ? DQSYNTAX : BASESYNTAX;
struct stackmark smark;
INT_OFF;
scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
int zero)
{
- char *loc;
- char *loc2;
+// This commented out code was added by James Simmons <jsimmons@infradead.org>
+// as part of a larger change when he added support for ${var/a/b}.
+// However, it broke # and % operators:
+//
+//var=ababcdcd
+// ok bad
+//echo ${var#ab} abcdcd abcdcd
+//echo ${var##ab} abcdcd abcdcd
+//echo ${var#a*b} abcdcd ababcdcd (!)
+//echo ${var##a*b} cdcd cdcd
+//echo ${var#?} babcdcd ababcdcd (!)
+//echo ${var##?} babcdcd babcdcd
+//echo ${var#*} ababcdcd babcdcd (!)
+//echo ${var##*}
+//echo ${var%cd} ababcd ababcd
+//echo ${var%%cd} ababcd abab (!)
+//echo ${var%c*d} ababcd ababcd
+//echo ${var%%c*d} abab ababcdcd (!)
+//echo ${var%?} ababcdc ababcdc
+//echo ${var%%?} ababcdc ababcdcd (!)
+//echo ${var%*} ababcdcd ababcdcd
+//echo ${var%%*}
+//
+// Commenting it back out helped. Remove it completely if it really
+// is not needed.
+
+ char *loc, *loc2; //, *full;
char c;
loc = startp;
loc2 = rmesc;
do {
- int match;
+ int match; // = strlen(str);
const char *s = loc2;
+
c = *loc2;
if (zero) {
*loc2 = '\0';
s = rmesc;
}
- match = pmatch(str, s);
+ match = pmatch(str, s); // this line was deleted
+
+// // chop off end if its '*'
+// full = strrchr(str, '*');
+// if (full && full != str)
+// match--;
+//
+// // If str starts with '*' replace with s.
+// if ((*str == '*') && strlen(s) >= match) {
+// full = xstrdup(s);
+// strncpy(full+strlen(s)-match+1, str+1, match-1);
+// } else
+// full = xstrndup(str, match);
+// match = strncmp(s, full, strlen(full));
+// free(full);
+//
*loc2 = c;
- if (match)
+ if (match) // if (!match)
return loc;
if (quotes && *loc == CTLESC)
loc++;
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
+#if ENABLE_ASH_BASH_COMPAT
+static char *
+parse_sub_pattern(char *arg, int inquotes)
+{
+ char *idx, *repl = NULL;
+ unsigned char c;
+
+ idx = arg;
+ while (1) {
+ c = *arg;
+ if (!c)
+ break;
+ if (c == '/') {
+ /* Only the first '/' seen is our separator */
+ if (!repl) {
+ repl = idx + 1;
+ c = '\0';
+ }
+ }
+ *idx++ = c;
+ if (!inquotes && c == '\\' && arg[1] == '\\')
+ arg++; /* skip both \\, not just first one */
+ arg++;
+ }
+ *idx = c; /* NUL */
+
+ return repl;
+}
+#endif /* ENABLE_ASH_BASH_COMPAT */
+
static const char *
subevalvar(char *p, char *str, int strloc, int subtype,
int startloc, int varflags, int quotes, struct strlist *var_str_list)
{
+ struct nodelist *saveargbackq = argbackq;
char *startp;
char *loc;
- int saveherefd = herefd;
- struct nodelist *saveargbackq = argbackq;
- int amount;
char *rmesc, *rmescend;
+ USE_ASH_BASH_COMPAT(char *repl = NULL;)
+ USE_ASH_BASH_COMPAT(char null = '\0';)
+ USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
+ int saveherefd = herefd;
+ int amount, workloc, resetloc;
int zero;
- char *(*scan)(char *, char *, char *, char *, int , int);
+ char *(*scan)(char*, char*, char*, char*, int, int);
herefd = -1;
argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
STPUTC('\0', expdest);
herefd = saveherefd;
argbackq = saveargbackq;
- startp = stackblock() + startloc;
+ startp = (char *)stackblock() + startloc;
switch (subtype) {
case VSASSIGN:
STADJUST(amount, expdest);
return startp;
+#if ENABLE_ASH_BASH_COMPAT
+ case VSSUBSTR:
+ loc = str = stackblock() + strloc;
+// TODO: number() instead? It does error checking...
+ pos = atoi(loc);
+ len = str - startp - 1;
+
+ /* *loc != '\0', guaranteed by parser */
+ if (quotes) {
+ char *ptr;
+
+ /* We must adjust the length by the number of escapes we find. */
+ for (ptr = startp; ptr < (str - 1); ptr++) {
+ if(*ptr == CTLESC) {
+ len--;
+ ptr++;
+ }
+ }
+ }
+ orig_len = len;
+
+ if (*loc++ == ':') {
+// TODO: number() instead? It does error checking...
+ len = atoi(loc);
+ } else {
+ len = orig_len;
+ while (*loc && *loc != ':')
+ loc++;
+ if (*loc++ == ':')
+// TODO: number() instead? It does error checking...
+ len = atoi(loc);
+ }
+ if (pos >= orig_len) {
+ pos = 0;
+ len = 0;
+ }
+ if (len > (orig_len - pos))
+ len = orig_len - pos;
+
+ for (str = startp; pos; str++, pos--) {
+ if (quotes && *str == CTLESC)
+ str++;
+ }
+ for (loc = startp; len; len--) {
+ if (quotes && *str == CTLESC)
+ *loc++ = *str++;
+ *loc++ = *str++;
+ }
+ *loc = '\0';
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+ return loc;
+#endif
+
case VSQUESTION:
varunset(p, str, startp, varflags);
/* NOTREACHED */
}
+ resetloc = expdest - (char *)stackblock();
- subtype -= VSTRIMRIGHT;
-#if DEBUG
- if (subtype < 0 || subtype > 3)
- abort();
-#endif
+ /* 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
+ */
+ USE_ASH_BASH_COMPAT(restart:)
+
+ amount = expdest - ((char *)stackblock() + resetloc);
+ STADJUST(-amount, expdest);
+ startp = (char *)stackblock() + startloc;
rmesc = startp;
- rmescend = stackblock() + strloc;
+ rmescend = (char *)stackblock() + strloc;
if (quotes) {
rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
if (rmesc != startp) {
rmescend = expdest;
- startp = stackblock() + startloc;
+ startp = (char *)stackblock() + startloc;
}
}
rmescend--;
- str = stackblock() + strloc;
+ str = (char *)stackblock() + strloc;
preglob(str, varflags & VSQUOTE, 0);
+ workloc = expdest - (char *)stackblock();
+
+#if ENABLE_ASH_BASH_COMPAT
+ if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+ char *idx, *end, *restart_detect;
+
+ if(!repl) {
+ repl = parse_sub_pattern(str, varflags & VSQUOTE);
+ if (!repl)
+ repl = &null;
+ }
+
+ /* If there's no pattern to match, return the expansion unmolested */
+ if (*str == '\0')
+ return 0;
+
+ len = 0;
+ idx = startp;
+ end = str - 1;
+ while (idx < end) {
+ loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
+ if (!loc) {
+ /* No match, advance */
+ restart_detect = stackblock();
+ STPUTC(*idx, expdest);
+ if (quotes && *idx == CTLESC) {
+ idx++;
+ len++;
+ STPUTC(*idx, expdest);
+ }
+ if (stackblock() != restart_detect)
+ goto restart;
+ idx++;
+ len++;
+ rmesc++;
+ continue;
+ }
+
+ if (subtype == VSREPLACEALL) {
+ while (idx < loc) {
+ if (quotes && *idx == CTLESC)
+ idx++;
+ idx++;
+ rmesc++;
+ }
+ } else
+ idx = loc;
+
+ for (loc = repl; *loc; loc++) {
+ restart_detect = stackblock();
+ STPUTC(*loc, expdest);
+ if (stackblock() != restart_detect)
+ goto restart;
+ len++;
+ }
+
+ if (subtype == VSREPLACE) {
+ while (*idx) {
+ restart_detect = stackblock();
+ STPUTC(*idx, expdest);
+ if (stackblock() != restart_detect)
+ goto restart;
+ len++;
+ idx++;
+ }
+ break;
+ }
+ }
+
+ /* We've put the replaced text into a buffer at workloc, now
+ * move it to the right place and adjust the stack.
+ */
+ startp = stackblock() + startloc;
+ STPUTC('\0', expdest);
+ memmove(startp, stackblock() + workloc, len);
+ startp[len++] = '\0';
+ amount = expdest - ((char *)stackblock() + startloc + len - 1);
+ STADJUST(-amount, expdest);
+ return startp;
+ }
+#endif /* ENABLE_ASH_BASH_COMPAT */
+ subtype -= VSTRIMRIGHT;
+#if DEBUG
+ if (subtype < 0 || subtype > 7)
+ abort();
+#endif
/* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
zero = subtype >> 1;
/* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
case '7':
case '8':
case '9':
+// TODO: number() instead? It does error checking...
num = atoi(name);
if (num < 0 || num > shellparam.nparam)
return -1;
if (!eq) /* stop at first non-assignment */
break;
eq++;
- if (name_len == (eq - str)
+ if (name_len == (unsigned)(eq - str)
&& strncmp(str, name, name_len) == 0) {
p = eq;
/* goto value; - WRONG! */
case VSTRIMLEFTMAX:
case VSTRIMRIGHT:
case VSTRIMRIGHTMAX:
+#if ENABLE_ASH_BASH_COMPAT
+ case VSSUBSTR:
+ case VSREPLACE:
+ case VSREPLACEALL:
+#endif
break;
default:
abort();
#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
struct cmdentry {
- int cmdtype;
+ smallint cmdtype; /* CMDxxx */
union param {
int index;
+ /* index >= 0 for commands without path (slashes) */
+ /* (TODO: what exactly does the value mean? PATH position?) */
+ /* index == -1 for commands with slashes */
+ /* index == (-2 - applet_no) for NOFORK applets */
const struct builtincmd *cmd;
struct funcnode *func;
} u;
struct tblentry {
struct tblentry *next; /* next entry in hash chain */
union param param; /* definition of builtin function */
- short cmdtype; /* index identifying command */
+ smallint cmdtype; /* CMDxxx */
char rehash; /* if set, cd done since entry created */
char cmdname[ARB]; /* name of command */
};
static void
-tryexec(char *cmd, char **argv, char **envp)
+tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
{
int repeated = 0;
#if ENABLE_FEATURE_SH_STANDALONE
- if (strchr(cmd, '/') == NULL) {
- int a = find_applet_by_name(cmd);
- if (a >= 0) {
- if (APPLET_IS_NOEXEC(a))
- run_applet_no_and_exit(a, argv);
- /* re-exec ourselves with the new arguments */
- execve(bb_busybox_exec_path, argv, envp);
- /* If they called chroot or otherwise made the binary no longer
- * executable, fall through */
- }
+ if (applet_no >= 0) {
+ if (APPLET_IS_NOEXEC(applet_no))
+ run_applet_no_and_exit(applet_no, argv);
+ /* re-exec ourselves with the new arguments */
+ execve(bb_busybox_exec_path, argv, envp);
+ /* If they called chroot or otherwise made the binary no longer
+ * executable, fall through */
}
#endif
* 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)
int e;
char **envp;
int exerrno;
+#if ENABLE_FEATURE_SH_STANDALONE
+ int applet_no = -1;
+#endif
clearredir(1);
- envp = environment();
- if (strchr(argv[0], '/')
+ envp = listvars(VEXPORT, VUNSET, 0);
+ if (strchr(argv[0], '/') != NULL
#if ENABLE_FEATURE_SH_STANDALONE
- || find_applet_by_name(argv[0]) >= 0
+ || (applet_no = find_applet_by_name(argv[0])) >= 0
#endif
) {
- tryexec(argv[0], argv, envp);
+ tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
e = errno;
} else {
e = ENOENT;
while ((cmdname = padvance(&path, argv[0])) != NULL) {
if (--idx < 0 && pathopt == NULL) {
- tryexec(cmdname, argv, envp);
+ tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
if (errno != ENOENT && errno != ENOTDIR)
e = errno;
}
case CMDNORMAL: {
int j = entry.u.index;
char *p;
- if (j == -1) {
+ if (j < 0) {
p = command;
} else {
do {
static int breakcmd(int, char **);
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 exitcmd(int, char **);
static int exportcmd(int, char **);
#if ENABLE_ASH_GETOPTS
static int getoptscmd(int, char **);
#endif
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-static int helpcmd(int argc, char **argv);
+static int helpcmd(int, char **);
#endif
#if ENABLE_ASH_MATH_SUPPORT
static int letcmd(int, char **);
#define BUILTIN_REG_ASSG "6"
#define BUILTIN_SPEC_REG_ASSG "7"
-/* make sure to keep these in proper order since it is searched via bsearch() */
+/* We do not handle [[ expr ]] bashism bash-compatibly,
+ * we make it a synonym of [ expr ].
+ * Basically, word splitting and pathname expansion should NOT be performed
+ * Examples:
+ * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
+ * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
+ * Additional operators:
+ * || and && should work as -o and -a
+ * =~ regexp match
+ * Apart from the above, [[ expr ]] should work as [ expr ]
+ */
+
+#define echocmd echo_main
+#define printfcmd printf_main
+#define testcmd test_main
+
+/* Keep these in proper order since it is searched via bsearch() */
static const struct builtincmd builtintab[] = {
{ BUILTIN_SPEC_REG ".", dotcmd },
{ BUILTIN_SPEC_REG ":", truecmd },
#if ENABLE_ASH_BUILTIN_TEST
- { BUILTIN_REGULAR "[", testcmd },
- { BUILTIN_REGULAR "[[", testcmd },
+ { BUILTIN_REGULAR "[", testcmd },
+#if ENABLE_ASH_BASH_COMPAT
+ { BUILTIN_REGULAR "[[", testcmd },
+#endif
#endif
#if ENABLE_ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
{ BUILTIN_NOSPEC "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
+#if ENABLE_ASH_BUILTIN_PRINTF
+ { BUILTIN_REGULAR "printf", printfcmd },
+#endif
{ BUILTIN_NOSPEC "pwd", pwdcmd },
{ BUILTIN_REGULAR "read", readcmd },
{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
{ BUILTIN_SPEC_REG "shift", shiftcmd },
{ BUILTIN_SPEC_REG "source", dotcmd },
#if ENABLE_ASH_BUILTIN_TEST
- { BUILTIN_REGULAR "test", testcmd },
+ { BUILTIN_REGULAR "test", testcmd },
#endif
{ BUILTIN_SPEC_REG "times", timescmd },
{ BUILTIN_SPEC_REG "trap", trapcmd },
{ BUILTIN_REGULAR "wait", waitcmd },
};
-
-#define COMMANDCMD (builtintab + 5 + \
- 2 * ENABLE_ASH_BUILTIN_TEST + \
- ENABLE_ASH_ALIAS + \
- ENABLE_ASH_JOB_CONTROL)
-#define EXECCMD (builtintab + 7 + \
- 2 * ENABLE_ASH_BUILTIN_TEST + \
- ENABLE_ASH_ALIAS + \
- ENABLE_ASH_JOB_CONTROL + \
- ENABLE_ASH_CMDCMD + \
- ENABLE_ASH_BUILTIN_ECHO)
+/* Should match the above table! */
+#define COMMANDCMD (builtintab + \
+ 2 + \
+ 1 * ENABLE_ASH_BUILTIN_TEST + \
+ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+ 1 * ENABLE_ASH_ALIAS + \
+ 1 * ENABLE_ASH_JOB_CONTROL + \
+ 3)
+#define EXECCMD (builtintab + \
+ 2 + \
+ 1 * ENABLE_ASH_BUILTIN_TEST + \
+ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+ 1 * ENABLE_ASH_ALIAS + \
+ 1 * ENABLE_ASH_JOB_CONTROL + \
+ 3 + \
+ 1 * ENABLE_ASH_CMDCMD + \
+ 1 + \
+ ENABLE_ASH_BUILTIN_ECHO + \
+ 1)
/*
* Search the table of builtin commands.
/* Execute the command. */
switch (cmdentry.cmdtype) {
default:
+#if ENABLE_FEATURE_SH_NOFORK
+ {
+ /* find_command() encodes applet_no as (-2 - applet_no) */
+ int applet_no = (- cmdentry.u.index - 2);
+ if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
+ listsetvar(varlist.list, VEXPORT|VSTACK);
+ /* run <applet>_main() */
+ exitstatus = run_nofork_applet(applet_no, argv);
+ break;
+ }
+ }
+#endif
+
/* Fork off a child process if necessary. */
if (!(flags & EV_EXIT) || trap[0]) {
INT_OFF;
break;
if (*p == '\0')
continue;
- for (q = p; *q; q++);
+ for (q = p; *q; q++)
+ continue;
#if DEBUG
if (q[-1] != '/')
abort();
char **ap;
int nparam;
- for (nparam = 0; argv[nparam]; nparam++);
+ for (nparam = 0; argv[nparam]; nparam++)
+ continue;
ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
while (*argv) {
*ap++ = ckstrdup(*argv++);
* Oh well. Let's mimic that.
*/
static int
-minus_o(char *name, int val)
+plus_minus_o(char *name, int val)
{
int i;
return 0;
}
}
- ash_msg("illegal option -o %s", name);
+ ash_msg("illegal option %co %s", val ? '-' : '+', name);
return 1;
}
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optnames(i),
- optlist[i] ? "on" : "off");
+ for (i = 0; i < NOPTS; i++) {
+ if (val) {
+ out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
+ } else {
+ out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
+ }
+ }
return 0;
}
static void
return;
}
}
- ash_msg_and_raise_error("illegal option -%c", flag);
+ ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
/* NOTREACHED */
}
static int
if (c == 'c' && cmdline) {
minusc = p; /* command is after shell args */
} else if (c == 'o') {
- if (minus_o(*argptr, val)) {
+ if (plus_minus_o(*argptr, val)) {
/* it already printed err message */
return 1; /* error */
}
free(*ap1);
}
ap2 = shellparam.p;
- while ((*ap2++ = *ap1++) != NULL);
+ while ((*ap2++ = *ap1++) != NULL)
+ continue;
#if ENABLE_ASH_GETOPTS
shellparam.optind = 1;
shellparam.optoff = -1;
return 1;
optnext = optfirst + *param_optind - 1;
- if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+ if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
p = NULL;
else
p = optnext[-1] + *optoff;
union node *vars, **vpp;
union node **rpp, *redir;
int savecheckkwd;
+#if ENABLE_ASH_BASH_COMPAT
+ smallint double_brackets_flag = 0;
+#endif
args = NULL;
app = &args;
savecheckkwd = CHKALIAS;
for (;;) {
+ int t;
checkkwd = savecheckkwd;
- switch (readtoken()) {
+ t = readtoken();
+ switch (t) {
+#if ENABLE_ASH_BASH_COMPAT
+ case TAND: /* "&&" */
+ case TOR: /* "||" */
+ if (!double_brackets_flag) {
+ tokpushback = 1;
+ goto out;
+ }
+ wordtext = (char *) (t == TAND ? "-a" : "-o");
+#endif
case TWORD:
n = stzalloc(sizeof(struct narg));
n->type = NARG;
/*n->narg.next = NULL; - stzalloc did it */
n->narg.text = wordtext;
+#if ENABLE_ASH_BASH_COMPAT
+ if (strcmp("[[", wordtext) == 0)
+ double_brackets_flag = 1;
+ else if (strcmp("]]", wordtext) == 0)
+ double_brackets_flag = 0;
+#endif
n->narg.backquote = backquotelist;
if (savecheckkwd && isassignment(wordtext)) {
*vpp = n;
char *p, *q;
p = line;
- for (q = eofmark + 1; *q && *p == *q; p++, q++);
+ for (q = eofmark + 1; *q && *p == *q; p++, q++)
+ continue;
if (*p == '\n' && *q == '\0') {
c = PEOF;
plinno++;
#if ENABLE_ASH_MATH_SUPPORT
PARSEARITH();
#else
- raise_error_syntax("We unsupport $((arith))");
+ raise_error_syntax("you disabled math support for $((arith)) syntax");
#endif
} else {
pungetc();
if (subtype == 0) {
switch (c) {
case ':':
- flags = VSNUL;
c = pgetc();
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == ':' || c == '$' || isdigit(c)) {
+ pungetc();
+ subtype = VSSUBSTR;
+ break;
+ }
+#endif
+ flags = VSNUL;
/*FALLTHROUGH*/
default:
p = strchr(types, c);
subtype = p - types + VSNORMAL;
break;
case '%':
- case '#':
- {
- int cc = c;
- subtype = c == '#' ? VSTRIMLEFT :
- VSTRIMRIGHT;
- c = pgetc();
- if (c == cc)
- subtype++;
- else
- pungetc();
- break;
- }
+ case '#': {
+ int cc = c;
+ subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
+ c = pgetc();
+ if (c == cc)
+ subtype++;
+ else
+ pungetc();
+ break;
+ }
+#if ENABLE_ASH_BASH_COMPAT
+ case '/':
+ subtype = VSREPLACE;
+ c = pgetc();
+ if (c == '/')
+ subtype++; /* VSREPLACEALL */
+ else
+ pungetc();
+ break;
+#endif
}
} else {
pungetc();
#endif
) {
if (c == '#') {
- while ((c = pgetc()) != '\n' && c != PEOF);
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
pungetc();
} else if (c == '\\') {
if (pgetc() != '\n') {
return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
}
- if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+ if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
if (pgetc() == *p) { /* double occurrence? */
p += xxreadtoken_doubles + 1;
} else {
#endif
continue;
case '#':
- while ((c = pgetc()) != '\n' && c != PEOF);
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
pungetc();
continue;
case '\\':
/* NOTREACHED */
}
-#if ENABLE_ASH_BUILTIN_ECHO
-static int
-echocmd(int argc, char **argv)
-{
- return echo_main(argc, argv);
-}
-#endif
-
-#if ENABLE_ASH_BUILTIN_TEST
-static int
-testcmd(int argc, char **argv)
-{
- return test_main(argc, argv);
-}
-#endif
-
/*
* Read a file containing shell functions.
*/
}
#if ENABLE_FEATURE_SH_STANDALONE
- if (find_applet_by_name(name) >= 0) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
+ {
+ int applet_no = find_applet_by_name(name);
+ if (applet_no >= 0) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -2 - applet_no;
+ return;
+ }
}
#endif
if (bcmd)
goto builtin_success;
continue;
- } else if (!(act & DO_NOFUNC)
- && prefix(pathopt, "func")) {
- /* handled below */
- } else {
- /* ignore unimplemented options */
+ }
+ if ((act & DO_NOFUNC)
+ || !prefix(pathopt, "func")
+ ) { /* ignore unimplemented options */
continue;
}
}
static int
helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
- int col, i;
+ unsigned col;
+ unsigned i;
out1fmt("\nBuilt-in commands:\n-------------------\n");
for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
while ((c = *p++) >= '0' && c <= '9') {
val = (val * 10) + (long)(c - '0');
+ // val is actually 'unsigned long int' and can't get < 0
if (val < (rlim_t) 0)
break;
}
|| prec == PREC(TOK_CONDITIONAL));
}
-typedef struct ARITCH_VAR_NUM {
+typedef struct {
arith_t val;
arith_t contidional_second_val;
char contidional_second_val_initialized;
else is variable name */
} v_n_t;
-typedef struct CHK_VAR_RECURSIVE_LOOPED {
+typedef struct chk_var_recursive_looped_t {
const char *var;
- struct CHK_VAR_RECURSIVE_LOOPED *next;
+ struct chk_var_recursive_looped_t *next;
} chk_var_recursive_looped_t;
static chk_var_recursive_looped_t *prev_chk_var_recursive;
0
};
/* ptr to ")" */
-#define endexpression &op_tokens[sizeof(op_tokens)-7]
+#define endexpression (&op_tokens[sizeof(op_tokens)-7])
static arith_t
arith(const char *expr, int *perrcode)