#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);
}
static int
-cdcmd(int argc, char **argv)
+cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
const char *dest;
const char *path;
}
static int
-pwdcmd(int argc, char **argv)
+pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int flags;
const char *dir = curdir;
* TODO - sort output
*/
static int
-aliascmd(int argc, char **argv)
+aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *n, *v;
int ret = 0;
struct alias *ap;
- if (argc == 1) {
+ if (!argv[1]) {
int i;
- for (i = 0; i < ATABSIZE; i++)
+ for (i = 0; i < ATABSIZE; i++) {
for (ap = atab[i]; ap; ap = ap->next) {
printalias(ap);
}
+ }
return 0;
}
while ((n = *++argv) != NULL) {
}
static int
-unaliascmd(int argc, char **argv)
+unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int i;
static pid_t backgndpid; /* pid of last background process */
static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
-static struct job *makejob(union node *, int);
+static struct job *makejob(/*union node *,*/ int);
+#if !JOBS
+#define forkshell(job, node, mode) forkshell(job, mode)
+#endif
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
/*
* Convert a job name to a job structure.
*/
+#if !JOBS
+#define getjob(name, getctl) getjob(name)
+#endif
static struct job *
getjob(const char *name, int getctl)
{
}
if (is_number(p)) {
+// TODO: number() instead? It does error checking...
num = atoi(p);
if (num < njobs) {
jp = jobtab + num - 1;
static int
killcmd(int argc, char **argv)
{
+ int i = 1;
if (argv[1] && strcmp(argv[1], "-l") != 0) {
- int i = 1;
do {
if (argv[i][0] == '%') {
struct job *jp = getjob(argv[i], 0);
}
static int
-fg_bgcmd(int argc, char **argv)
+fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct job *jp;
FILE *out;
}
static int
-jobscmd(int argc, char **argv)
+jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int mode, m;
}
static int
-waitcmd(int argc, char **argv)
+waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct job *job;
int retval;
* Called with interrupts off.
*/
static struct job *
-makejob(union node *node, int nprocs)
+makejob(/*union node *node,*/ int nprocs)
{
int i;
struct job *jp;
if (nprocs > 1) {
jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
}
- TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+ TRACE(("makejob(%d) returns %%%d\n", nprocs,
jobno(jp)));
return jp;
}
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;
/* Called after fork(), in child */
static void
-forkchild(struct job *jp, union node *n, int mode)
+forkchild(struct job *jp, /*union node *n,*/ int mode)
{
int oldlvl;
}
/* Called after fork(), in parent */
+#if !JOBS
+#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
+#endif
static void
forkparent(struct job *jp, union node *n, int mode, pid_t pid)
{
ash_msg_and_raise_error("cannot fork");
}
if (pid == 0)
- forkchild(jp, n, mode);
+ forkchild(jp, /*n,*/ mode);
else
forkparent(jp, n, mode, pid);
return pid;
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;
if (pipe(pip) < 0)
ash_msg_and_raise_error("pipe call failed");
- jp = makejob(n, 1);
+ jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, FORK_NOJOB) == 0) {
FORCE_INT_ON;
close(pip[0]);
char *p;
char *dest;
int startloc;
- int syntax = quoted? DQSYNTAX : BASESYNTAX;
+ int syntax = quoted ? DQSYNTAX : BASESYNTAX;
struct stackmark smark;
INT_OFF;
}
static char *
-scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+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();
}
static void
-expandmeta(struct strlist *str, int flag)
+expandmeta(struct strlist *str /*, int flag*/)
{
static const char metachars[] ALIGN1 = {
'*', '?', '[', 0
ifsbreakup(p, &exparg);
*exparg.lastp = NULL;
exparg.lastp = &exparg.list;
- expandmeta(exparg.list, flag);
+ expandmeta(exparg.list /*, flag*/);
} else {
if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
rmescapes(p);
#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;
}
}
static int
-hashcmd(int argc, char **argv)
+hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
struct tblentry **pp;
struct tblentry *cmdp;
case CMDNORMAL: {
int j = entry.u.index;
char *p;
- if (j == -1) {
+ if (j < 0) {
p = command;
} else {
do {
}
static int
-typecmd(int argc, char **argv)
+typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int i = 1;
int err = 0;
i++;
verbose = 0;
}
- while (i < argc) {
+ while (argv[i]) {
err |= describe_command(argv[i++], verbose);
}
return err;
#if ENABLE_ASH_CMDCMD
static int
-commandcmd(int argc, char **argv)
+commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int c;
enum {
if (!backgnd && flags & EV_EXIT && !trap[0])
goto nofork;
INT_OFF;
- jp = makejob(n, 1);
+ jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, backgnd) == 0) {
INT_ON;
flags |= EV_EXIT;
pipelen++;
flags |= EV_EXIT;
INT_OFF;
- jp = makejob(n, pipelen);
+ jp = makejob(/*n,*/ pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
prehash(lp->n);
* The "local" command.
*/
static int
-localcmd(int argc, char **argv)
+localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *name;
}
static int
-falsecmd(int argc, char **argv)
+falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
return 1;
}
static int
-truecmd(int argc, char **argv)
+truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
return 0;
}
static int
-execcmd(int argc, char **argv)
+execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- if (argc > 1) {
+ if (argv[1]) {
iflag = 0; /* exit on error */
mflag = 0;
optschanged();
* The return command.
*/
static int
-returncmd(int argc, char **argv)
+returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
/*
* If called outside a function, do what ksh does;
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.
return *q == '=';
}
static int
-bltincmd(int argc, char **argv)
+bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
/* Preserve exitstatus of a previous possible redirection
* as POSIX mandates */
/* 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;
- jp = makejob(cmd, 1);
+ jp = makejob(/*cmd,*/ 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
exitstatus = waitforjob(jp);
INT_ON;
* in the standard shell so we don't make it one here.
*/
static int
-breakcmd(int argc, char **argv)
+breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- int n = argc > 1 ? number(argv[1]) : 1;
+ int n = argv[1] ? number(argv[1]) : 1;
if (n <= 0)
ash_msg_and_raise_error(illnum, argv[1]);
char *buf = parsefile->buf;
parsenextc = buf;
- retry:
#if ENABLE_FEATURE_EDITING
+ retry:
if (!iflag || parsefile->fd)
nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
else {
* Push a string back onto the input at this current parsefile level.
* We handle aliases this way.
*/
+#if !ENABLE_ASH_ALIAS
+#define pushstring(s, ap) pushstring(s)
+#endif
static void
-pushstring(char *s, void *ap)
+pushstring(char *s, struct alias *ap)
{
struct strpush *sp;
size_t len;
sp->prevstring = parsenextc;
sp->prevnleft = parsenleft;
#if ENABLE_ASH_ALIAS
- sp->ap = (struct alias *)ap;
+ sp->ap = ap;
if (ap) {
- ((struct alias *)ap)->flag |= ALIASINUSE;
+ ap->flag |= ALIASINUSE;
sp->string = s;
}
#endif
break;
if (*p == '\0')
continue;
- for (q = p; *q; q++);
+ for (q = p; *q; q++)
+ continue;
#if DEBUG
if (q[-1] != '/')
abort();
}
static void
-changemail(const char *val)
+changemail(const char *val ATTRIBUTE_UNUSED)
{
mail_var_path_changed = 1;
}
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 */
}
* The shift builtin command.
*/
static int
-shiftcmd(int argc, char **argv)
+shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int n;
char **ap1, **ap2;
n = 1;
- if (argc > 1)
+ if (argv[1])
n = number(argv[1]);
if (n > shellparam.nparam)
ash_msg_and_raise_error("can't shift that many");
free(*ap1);
}
ap2 = shellparam.p;
- while ((*ap2++ = *ap1++) != NULL);
+ while ((*ap2++ = *ap1++) != NULL)
+ continue;
#if ENABLE_ASH_GETOPTS
shellparam.optind = 1;
shellparam.optoff = -1;
* The set command builtin.
*/
static int
-setcmd(int argc, char **argv)
+setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int retval;
- if (argc == 1)
+ if (!argv[1])
return showvars(nullstr, 0, VUNSET);
INT_OFF;
retval = 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 '\\':
* The eval command.
*/
static int
-evalcmd(int argc, char **argv)
+evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *p;
char *concat;
- char **ap;
- if (argc > 1) {
+ if (argv[1]) {
p = argv[1];
- if (argc > 2) {
+ argv += 2;
+ if (argv[0]) {
STARTSTACKSTR(concat);
- ap = argv + 2;
for (;;) {
concat = stack_putstr(p, concat);
- p = *ap++;
+ p = *argv++;
if (p == NULL)
break;
STPUTC(' ', concat);
for (sp = cmdenviron; sp; sp = sp->next)
setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
- if (argc >= 2) { /* That's what SVR2 does */
- char *fullname;
-
- fullname = find_dot_file(argv[1]);
-
- if (argc > 2) {
+ if (argv[1]) { /* That's what SVR2 does */
+ char *fullname = find_dot_file(argv[1]);
+ argv += 2;
+ argc -= 2;
+ if (argc) { /* argc > 0, argv[0] != NULL */
saveparam = shellparam;
shellparam.malloced = 0;
- shellparam.nparam = argc - 2;
- shellparam.p = argv + 2;
+ shellparam.nparam = argc;
+ shellparam.p = argv;
};
setinputfile(fullname, INPUT_PUSH_FILE);
cmdloop(0);
popfile();
- if (argc > 2) {
+ if (argc) {
freeparam(&shellparam);
shellparam = saveparam;
};
}
static int
-exitcmd(int argc, char **argv)
+exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
if (stoppedjobs())
return 0;
- if (argc > 1)
+ if (argv[1])
exitstatus = number(argv[1]);
raise_exception(EXEXIT);
/* 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;
}
}
* The trap builtin.
*/
static int
-trapcmd(int argc, char **argv)
+trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char *action;
char **ap;
* Lists available builtins
*/
static int
-helpcmd(int argc, char **argv)
+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++) {
* The export and readonly commands.
*/
static int
-exportcmd(int argc, char **argv)
+exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct var *vp;
char *name;
* with the same name.
*/
static int
-unsetcmd(int argc, char **argv)
+unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char **ap;
int i;
};
static int
-timescmd(int ac, char **av)
+timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
long clk_tck, s, t;
const unsigned char *p;
* Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
*/
static int
-letcmd(int argc, char **argv)
+letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- char **ap;
- arith_t i = 0;
+ arith_t i;
- ap = argv + 1;
- if (!*ap)
+ argv++;
+ if (!*argv)
ash_msg_and_raise_error("expression expected");
- for (ap = argv + 1; *ap; ap++) {
- i = dash_arith(*ap);
- }
+ do {
+ i = dash_arith(*argv);
+ } while (*++argv);
return !i;
}
#endif
/*
- * The read builtin. The -e option causes backslashes to escape the
- * following character.
- *
+ * The read builtin. Options:
+ * -r Do not interpret '\' specially
+ * -s Turn off echo (tty only)
+ * -n NCHARS Read NCHARS max
+ * -p PROMPT Display PROMPT on stderr (if input is from tty)
+ * -t SECONDS Timeout after SECONDS (tty or pipe only)
+ * -u FD Read from given FD instead of fd 0
* This uses unbuffered input, which may be avoidable in some cases.
+ * TODO: bash also has:
+ * -a ARRAY Read into array[0],[1],etc
+ * -d DELIM End on DELIM char, not newline
+ * -e Use line editing (tty only)
*/
static int
-readcmd(int argc, char **argv)
+readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char **ap;
int backslash;
int startword;
int status;
int i;
+ int fd = 0;
#if ENABLE_ASH_READ_NCHARS
- int n_flag = 0;
- int nchars = 0;
+ int nchars = 0; /* if != 0, -n is in effect */
int silent = 0;
struct termios tty, old_tty;
#endif
#if ENABLE_ASH_READ_TIMEOUT
- fd_set set;
- struct timeval ts;
-
- ts.tv_sec = ts.tv_usec = 0;
+ unsigned end_ms = 0;
+ unsigned timeout = 0;
#endif
rflag = 0;
prompt = NULL;
-#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
- while ((i = nextopt("p:rt:n:s")) != '\0')
-#elif ENABLE_ASH_READ_NCHARS
- while ((i = nextopt("p:rn:s")) != '\0')
-#elif ENABLE_ASH_READ_TIMEOUT
- while ((i = nextopt("p:rt:")) != '\0')
-#else
- while ((i = nextopt("p:r")) != '\0')
-#endif
- {
+ while ((i = nextopt("p:u:r"
+ USE_ASH_READ_TIMEOUT("t:")
+ USE_ASH_READ_NCHARS("n:s")
+ )) != '\0') {
switch (i) {
case 'p':
prompt = optionarg;
nchars = bb_strtou(optionarg, NULL, 10);
if (nchars < 0 || errno)
ash_msg_and_raise_error("invalid count");
- n_flag = nchars; /* just a flag "nchars is nonzero" */
+ /* nchars == 0: off (bash 3.2 does this too) */
break;
case 's':
silent = 1;
#endif
#if ENABLE_ASH_READ_TIMEOUT
case 't':
+ timeout = bb_strtou(optionarg, NULL, 10);
+ if (errno || timeout > UINT_MAX / 2048)
+ ash_msg_and_raise_error("invalid timeout");
+ timeout *= 1000;
+#if 0 /* even bash have no -t N.NNN support */
ts.tv_sec = bb_strtou(optionarg, &p, 10);
ts.tv_usec = 0;
/* EINVAL means number is ok, but not terminated by NUL */
if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
ash_msg_and_raise_error("invalid timeout");
}
+#endif /* if 0 */
break;
#endif
case 'r':
rflag = 1;
break;
+ case 'u':
+ fd = bb_strtou(optionarg, NULL, 10);
+ if (fd < 0 || errno)
+ ash_msg_and_raise_error("invalid file descriptor");
+ break;
default:
break;
}
}
- if (prompt && isatty(0)) {
+ if (prompt && isatty(fd)) {
out2str(prompt);
}
ap = argptr;
if (ifs == NULL)
ifs = defifs;
#if ENABLE_ASH_READ_NCHARS
- if (n_flag || silent) {
- if (tcgetattr(0, &tty) != 0) {
- /* Not a tty */
- n_flag = 0;
- silent = 0;
- } else {
- old_tty = tty;
- if (n_flag) {
- tty.c_lflag &= ~ICANON;
- tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
- }
- if (silent) {
- tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
- }
- tcsetattr(0, TCSANOW, &tty);
+ tcgetattr(fd, &tty);
+ old_tty = tty;
+ if (nchars || silent) {
+ if (nchars) {
+ tty.c_lflag &= ~ICANON;
+ tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
}
- }
-#endif
-#if ENABLE_ASH_READ_TIMEOUT
- if (ts.tv_sec || ts.tv_usec) {
- FD_ZERO(&set);
- FD_SET(0, &set);
-
- /* poll-based wait produces bigger code, using select */
- i = select(1, &set, NULL, NULL, &ts);
- if (!i) { /* timed out! */
-#if ENABLE_ASH_READ_NCHARS
- if (n_flag)
- tcsetattr(0, TCSANOW, &old_tty);
-#endif
- return 1;
+ if (silent) {
+ tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
}
+ /* if tcgetattr failed, tcsetattr will fail too.
+ * Ignoring, it's harmless. */
+ tcsetattr(fd, TCSANOW, &tty);
}
#endif
+
status = 0;
startword = 1;
backslash = 0;
+#if ENABLE_ASH_READ_TIMEOUT
+ if (timeout) /* NB: ensuring end_ms is nonzero */
+ end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
+#endif
STARTSTACKSTR(p);
do {
- if (nonblock_safe_read(0, &c, 1) != 1) {
+#if ENABLE_ASH_READ_TIMEOUT
+ if (end_ms) {
+ struct pollfd pfd[1];
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ timeout = end_ms - (unsigned)(monotonic_us() / 1000);
+ if ((int)timeout <= 0 /* already late? */
+ || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
+ ) { /* timed out! */
+#if ENABLE_ASH_READ_NCHARS
+ tcsetattr(fd, TCSANOW, &old_tty);
+#endif
+ return 1;
+ }
+ }
+#endif
+ if (nonblock_safe_read(fd, &c, 1) != 1) {
status = 1;
break;
}
}
/* end of do {} while: */
#if ENABLE_ASH_READ_NCHARS
- while (!n_flag || --nchars);
+ while (--nchars);
#else
while (1);
#endif
#if ENABLE_ASH_READ_NCHARS
- if (n_flag || silent)
- tcsetattr(0, TCSANOW, &old_tty);
+ tcsetattr(fd, TCSANOW, &old_tty);
#endif
STACKSTRNUL(p);
}
static int
-umaskcmd(int argc, char **argv)
+umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
static const char permuser[3] ALIGN1 = "ugo";
static const char permmode[3] ALIGN1 = "rwx";
}
static int
-ulimitcmd(int argc, char **argv)
+ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int c;
rlim_t val = 0;
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)
* Process the shell command line arguments.
*/
static void
-procargs(int argc, char **argv)
+procargs(char **argv)
{
int i;
const char *xminusc;
xargv = argv;
arg0 = xargv[0];
- if (argc > 0)
+ /* if (xargv[0]) - mmm, this is always true! */
xargv++;
for (i = 0; i < NOPTS; i++)
optlist[i] = 2;
* is used to figure out how far we had gotten.
*/
int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int ash_main(int argc, char **argv)
+int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *shinit;
volatile int state;
#endif
init();
setstackmark(&smark);
- procargs(argc, argv);
+ procargs(argv);
+
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
if (iflag) {
const char *hp = lookupvar("HISTFILE");