aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-02-12 01:45:46 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-02-12 01:45:46 +0000
commitfb7d69c855de3bad843788fd005d45ecb6eea74f (patch)
tree9f160822015c2b5af32dd946825ebf2bec998fee
parentbd9aa03690535319bf9cf97779bf9f712b2fc3e2 (diff)
parentacf30bdca25f699196efddfa2a2104c567c09c88 (diff)
downloadtoybox-fb7d69c855de3bad843788fd005d45ecb6eea74f.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD am: d8dd46c6a4 am: 724d425513 am: acf30bdca2
Change-Id: I8af39d15f747e0537d79955df24c975777b2b4cc
-rw-r--r--android/device/generated/flags.h28
-rw-r--r--android/device/generated/globals.h10
-rw-r--r--android/device/generated/newtoys.h4
-rw-r--r--android/linux/generated/flags.h28
-rw-r--r--android/linux/generated/globals.h10
-rw-r--r--android/linux/generated/newtoys.h4
-rw-r--r--android/mac/generated/flags.h28
-rw-r--r--android/mac/generated/globals.h10
-rw-r--r--android/mac/generated/newtoys.h4
-rw-r--r--lib/lib.c26
-rw-r--r--lib/lib.h1
-rw-r--r--main.c42
-rwxr-xr-xtests/chattr.test155
-rwxr-xr-xtests/sh.test45
-rw-r--r--toys/other/lsattr.c139
-rw-r--r--toys/other/stat.c2
-rw-r--r--toys/pending/readelf.c12
-rw-r--r--toys/pending/sh.c111
-rw-r--r--toys/pending/vi.c4
-rw-r--r--toys/posix/unlink.c2
20 files changed, 362 insertions, 303 deletions
diff --git a/android/device/generated/flags.h b/android/device/generated/flags.h
index 3a1ff621..155cfc9f 100644
--- a/android/device/generated/flags.h
+++ b/android/device/generated/flags.h
@@ -218,12 +218,15 @@
#undef FLAG_L
#endif
-// chattr
+// chattr ?p#v#R ?p#v#R
#undef OPTSTR_chattr
-#define OPTSTR_chattr 0
+#define OPTSTR_chattr "?p#v#R"
#ifdef CLEANUP_chattr
#undef CLEANUP_chattr
#undef FOR_chattr
+#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#endif
// chcon <2hvR <2hvR
@@ -1645,18 +1648,18 @@
#undef FLAG_color
#endif
-// lsattr vpldaR vpldaR
+// lsattr ldapvR ldapvR
#undef OPTSTR_lsattr
-#define OPTSTR_lsattr "vpldaR"
+#define OPTSTR_lsattr "ldapvR"
#ifdef CLEANUP_lsattr
#undef CLEANUP_lsattr
#undef FOR_lsattr
#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#undef FLAG_a
#undef FLAG_d
#undef FLAG_l
-#undef FLAG_p
-#undef FLAG_v
#endif
// lsmod
@@ -3599,6 +3602,9 @@
#ifndef TT
#define TT this.chattr
#endif
+#define FLAG_R (1<<0)
+#define FLAG_v (1<<1)
+#define FLAG_p (1<<2)
#endif
#ifdef FOR_chcon
@@ -4807,11 +4813,11 @@
#define TT this.lsattr
#endif
#define FLAG_R (1<<0)
-#define FLAG_a (1<<1)
-#define FLAG_d (1<<2)
-#define FLAG_l (1<<3)
-#define FLAG_p (1<<4)
-#define FLAG_v (1<<5)
+#define FLAG_v (1<<1)
+#define FLAG_p (1<<2)
+#define FLAG_a (1<<3)
+#define FLAG_d (1<<4)
+#define FLAG_l (1<<5)
#endif
#ifdef FOR_lsmod
diff --git a/android/device/generated/globals.h b/android/device/generated/globals.h
index 43b5d6e8..6b93ddd9 100644
--- a/android/device/generated/globals.h
+++ b/android/device/generated/globals.h
@@ -307,6 +307,15 @@ struct losetup_data {
char *dir;
};
+// toys/other/lsattr.c
+
+struct lsattr_data {
+ long v;
+ long p;
+
+ long add, rm, set;
+};
+
// toys/other/lspci.c
struct lspci_data {
@@ -1537,6 +1546,7 @@ extern union global_union {
struct ionice_data ionice;
struct login_data login;
struct losetup_data losetup;
+ struct lsattr_data lsattr;
struct lspci_data lspci;
struct makedevs_data makedevs;
struct mix_data mix;
diff --git a/android/device/generated/newtoys.h b/android/device/generated/newtoys.h
index 54da874f..6b93beab 100644
--- a/android/device/generated/newtoys.h
+++ b/android/device/generated/newtoys.h
@@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN))
USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN))
USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK))
-USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
+USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN))
USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN))
USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
@@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN))
USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
-USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN))
+USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN))
USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN))
USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/android/linux/generated/flags.h b/android/linux/generated/flags.h
index a8b6da8e..ec5ceb0a 100644
--- a/android/linux/generated/flags.h
+++ b/android/linux/generated/flags.h
@@ -218,12 +218,15 @@
#undef FLAG_L
#endif
-// chattr
+// chattr ?p#v#R
#undef OPTSTR_chattr
-#define OPTSTR_chattr 0
+#define OPTSTR_chattr "?p#v#R"
#ifdef CLEANUP_chattr
#undef CLEANUP_chattr
#undef FOR_chattr
+#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#endif
// chcon <2hvR
@@ -1645,18 +1648,18 @@
#undef FLAG_color
#endif
-// lsattr vpldaR
+// lsattr ldapvR
#undef OPTSTR_lsattr
-#define OPTSTR_lsattr "vpldaR"
+#define OPTSTR_lsattr "ldapvR"
#ifdef CLEANUP_lsattr
#undef CLEANUP_lsattr
#undef FOR_lsattr
#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#undef FLAG_a
#undef FLAG_d
#undef FLAG_l
-#undef FLAG_p
-#undef FLAG_v
#endif
// lsmod
@@ -3599,6 +3602,9 @@
#ifndef TT
#define TT this.chattr
#endif
+#define FLAG_R (FORCED_FLAG<<0)
+#define FLAG_v (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
#endif
#ifdef FOR_chcon
@@ -4807,11 +4813,11 @@
#define TT this.lsattr
#endif
#define FLAG_R (FORCED_FLAG<<0)
-#define FLAG_a (FORCED_FLAG<<1)
-#define FLAG_d (FORCED_FLAG<<2)
-#define FLAG_l (FORCED_FLAG<<3)
-#define FLAG_p (FORCED_FLAG<<4)
-#define FLAG_v (FORCED_FLAG<<5)
+#define FLAG_v (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
+#define FLAG_a (FORCED_FLAG<<3)
+#define FLAG_d (FORCED_FLAG<<4)
+#define FLAG_l (FORCED_FLAG<<5)
#endif
#ifdef FOR_lsmod
diff --git a/android/linux/generated/globals.h b/android/linux/generated/globals.h
index 43b5d6e8..6b93ddd9 100644
--- a/android/linux/generated/globals.h
+++ b/android/linux/generated/globals.h
@@ -307,6 +307,15 @@ struct losetup_data {
char *dir;
};
+// toys/other/lsattr.c
+
+struct lsattr_data {
+ long v;
+ long p;
+
+ long add, rm, set;
+};
+
// toys/other/lspci.c
struct lspci_data {
@@ -1537,6 +1546,7 @@ extern union global_union {
struct ionice_data ionice;
struct login_data login;
struct losetup_data losetup;
+ struct lsattr_data lsattr;
struct lspci_data lspci;
struct makedevs_data makedevs;
struct mix_data mix;
diff --git a/android/linux/generated/newtoys.h b/android/linux/generated/newtoys.h
index 54da874f..6b93beab 100644
--- a/android/linux/generated/newtoys.h
+++ b/android/linux/generated/newtoys.h
@@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN))
USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN))
USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK))
-USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
+USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN))
USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN))
USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
@@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN))
USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
-USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN))
+USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN))
USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN))
USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/android/mac/generated/flags.h b/android/mac/generated/flags.h
index fc45c19b..018ba654 100644
--- a/android/mac/generated/flags.h
+++ b/android/mac/generated/flags.h
@@ -218,12 +218,15 @@
#undef FLAG_L
#endif
-// chattr
+// chattr ?p#v#R
#undef OPTSTR_chattr
-#define OPTSTR_chattr 0
+#define OPTSTR_chattr "?p#v#R"
#ifdef CLEANUP_chattr
#undef CLEANUP_chattr
#undef FOR_chattr
+#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#endif
// chcon <2hvR
@@ -1645,18 +1648,18 @@
#undef FLAG_color
#endif
-// lsattr vpldaR
+// lsattr ldapvR
#undef OPTSTR_lsattr
-#define OPTSTR_lsattr "vpldaR"
+#define OPTSTR_lsattr "ldapvR"
#ifdef CLEANUP_lsattr
#undef CLEANUP_lsattr
#undef FOR_lsattr
#undef FLAG_R
+#undef FLAG_v
+#undef FLAG_p
#undef FLAG_a
#undef FLAG_d
#undef FLAG_l
-#undef FLAG_p
-#undef FLAG_v
#endif
// lsmod
@@ -3599,6 +3602,9 @@
#ifndef TT
#define TT this.chattr
#endif
+#define FLAG_R (FORCED_FLAG<<0)
+#define FLAG_v (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
#endif
#ifdef FOR_chcon
@@ -4807,11 +4813,11 @@
#define TT this.lsattr
#endif
#define FLAG_R (FORCED_FLAG<<0)
-#define FLAG_a (FORCED_FLAG<<1)
-#define FLAG_d (FORCED_FLAG<<2)
-#define FLAG_l (FORCED_FLAG<<3)
-#define FLAG_p (FORCED_FLAG<<4)
-#define FLAG_v (FORCED_FLAG<<5)
+#define FLAG_v (FORCED_FLAG<<1)
+#define FLAG_p (FORCED_FLAG<<2)
+#define FLAG_a (FORCED_FLAG<<3)
+#define FLAG_d (FORCED_FLAG<<4)
+#define FLAG_l (FORCED_FLAG<<5)
#endif
#ifdef FOR_lsmod
diff --git a/android/mac/generated/globals.h b/android/mac/generated/globals.h
index 43b5d6e8..6b93ddd9 100644
--- a/android/mac/generated/globals.h
+++ b/android/mac/generated/globals.h
@@ -307,6 +307,15 @@ struct losetup_data {
char *dir;
};
+// toys/other/lsattr.c
+
+struct lsattr_data {
+ long v;
+ long p;
+
+ long add, rm, set;
+};
+
// toys/other/lspci.c
struct lspci_data {
@@ -1537,6 +1546,7 @@ extern union global_union {
struct ionice_data ionice;
struct login_data login;
struct losetup_data losetup;
+ struct lsattr_data lsattr;
struct lspci_data lspci;
struct makedevs_data makedevs;
struct mix_data mix;
diff --git a/android/mac/generated/newtoys.h b/android/mac/generated/newtoys.h
index 54da874f..6b93beab 100644
--- a/android/mac/generated/newtoys.h
+++ b/android/mac/generated/newtoys.h
@@ -25,7 +25,7 @@ USE_CAL(NEWTOY(cal, ">2h", TOYFLAG_USR|TOYFLAG_BIN))
USE_CAT(NEWTOY(cat, "u"USE_CAT_V("vte"), TOYFLAG_BIN))
USE_CATV(NEWTOY(catv, USE_CATV("vte"), TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK))
-USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
+USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN))
USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN))
USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
@@ -147,7 +147,7 @@ USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN))
USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN))
USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
-USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN))
+USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN))
USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN))
USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/lib/lib.c b/lib/lib.c
index 4eef1355..b57a5691 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -508,21 +508,14 @@ off_t fdlength(int fd)
return base;
}
-// Read contents of file as a single nul-terminated string.
-// measure file size if !len, allocate buffer if !buf
-// Existing buffers need len in *plen
-// Returns amount of data read in *plen
-char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
+char *readfd(int fd, char *ibuf, off_t *plen)
{
off_t len, rlen;
- int fd;
char *buf, *rbuf;
// Unsafe to probe for size with a supplied buffer, don't ever do that.
if (CFG_TOYBOX_DEBUG && (ibuf ? !*plen : *plen)) error_exit("bad readfileat");
- if (-1 == (fd = openat(dirfd, name, O_RDONLY))) return 0;
-
// If we dunno the length, probe it. If we can't probe, start with 1 page.
if (!*plen) {
if ((len = fdlength(fd))>0) *plen = len;
@@ -543,7 +536,6 @@ char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
len -= rlen;
}
*plen = len = rlen+(rbuf-buf);
- close(fd);
if (rlen<0) {
if (ibuf != buf) free(buf);
@@ -553,6 +545,20 @@ char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
return buf;
}
+// Read contents of file as a single nul-terminated string.
+// measure file size if !len, allocate buffer if !buf
+// Existing buffers need len in *plen
+// Returns amount of data read in *plen
+char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
+{
+ if (-1 == (dirfd = openat(dirfd, name, O_RDONLY))) return 0;
+
+ ibuf = readfd(dirfd, ibuf, plen);
+ close(dirfd);
+
+ return ibuf;
+}
+
char *readfile(char *name, char *ibuf, off_t len)
{
return readfileat(AT_FDCWD, name, ibuf, &len);
@@ -1434,4 +1440,4 @@ char *elf_arch_name(int type)
}
sprintf(libbuf, "unknown arch %d", type);
return libbuf;
-} \ No newline at end of file
+}
diff --git a/lib/lib.h b/lib/lib.h
index e2914a09..66b39d67 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -209,6 +209,7 @@ off_t lskip(int fd, off_t offset);
int mkpathat(int atfd, char *dir, mode_t lastmode, int flags);
int mkpath(char *dir);
struct string_list **splitpath(char *path, struct string_list **list);
+char *readfd(int fd, char *ibuf, off_t *plen);
char *readfileat(int dirfd, char *name, char *buf, off_t *len);
char *readfile(char *name, char *buf, off_t len);
void msleep(long milliseconds);
diff --git a/main.c b/main.c
index ded73d99..60cb2b07 100644
--- a/main.c
+++ b/main.c
@@ -29,14 +29,11 @@ struct toy_list *toy_find(char *name)
if (!CFG_TOYBOX || strchr(name, '/')) return 0;
- // If the name starts with "toybox" accept that as a match. Otherwise
- // skip the first entry, which is out of order.
-
- if (!strncmp(name, "toybox", 6)) return toy_list;
+ // Multiplexer name works as prefix, else skip first entry (it's out of order)
+ if (!toys.which && strstart(&name, "toybox")) return toy_list;
bottom = 1;
// Binary search to find this command.
-
top = ARRAY_LEN(toy_list)-1;
for (;;) {
int result;
@@ -179,7 +176,7 @@ void toy_exec(char *argv[])
// If first argument starts with - output list of command install paths.
void toybox_main(void)
{
- static char *toy_paths[]={"usr/","bin/","sbin/",0};
+ static char *toy_paths[] = {"usr/","bin/","sbin/",0};
int i, len = 0;
// fast path: try to exec immediately.
@@ -201,12 +198,12 @@ void toybox_main(void)
if (toys.argv[1] && toys.argv[1][0] != '-') unknown(toys.argv[1]);
// Output list of command.
- for (i=1; i<ARRAY_LEN(toy_list); i++) {
+ for (i = 1; i<ARRAY_LEN(toy_list); i++) {
int fl = toy_list[i].flags;
if (fl & TOYMASK_LOCATION) {
if (toys.argv[1]) {
int j;
- for (j=0; toy_paths[j]; j++)
+ for (j = 0; toy_paths[j]; j++)
if (fl & (1<<j)) len += printf("%s", toy_paths[j]);
}
len += printf("%s",toy_list[i].name);
@@ -219,40 +216,27 @@ void toybox_main(void)
int main(int argc, char *argv[])
{
+ // don't segfault if our environment is crazy
if (!*argv) return 127;
// Snapshot stack location so we can detect recursion depth later.
- // This is its own block so probe doesn't permanently consume stack.
+ // Nommu has special reentry path, !stacktop = "vfork/exec self happened"
+ if (!CFG_TOYBOX_FORK && (0x80 & **argv)) **argv &= 0x7f;
else {
- int stack;
+ int stack_start; // here so probe var won't permanently eat stack
- toys.stacktop = &stack;
+ toys.stacktop = &stack_start;
}
- // Up to and including Android M, bionic's dynamic linker added a handler to
- // cause a crash dump on SIGPIPE. That was removed in Android N, but adbd
- // was still setting the SIGPIPE disposition to SIG_IGN, and its children
- // were inheriting that. In Android O, adbd is fixed, but manually asking
- // for the default disposition is harmless, and it'll be a long time before
- // no one's using anything older than O!
+ // Android before O had non-default SIGPIPE, 7 years = remove in Sep 2024.
if (CFG_TOYBOX_ON_ANDROID) signal(SIGPIPE, SIG_DFL);
- // If nommu can't fork, special reentry path.
- // Use !stacktop to signal "vfork happened", both before and after xexec()
- if (!CFG_TOYBOX_FORK) {
- if (0x80 & **argv) {
- **argv &= 0x7f;
- toys.stacktop = 0;
- }
- }
-
if (CFG_TOYBOX) {
- // Call the multiplexer, adjusting this argv[] to be its' argv[1].
- // (It will adjust it back before calling toy_exec().)
+ // Call the multiplexer with argv[] as its arguments so it can toy_find()
toys.argv = argv-1;
toybox_main();
} else {
- // a single toybox command built standalone with no multiplexer
+ // single command built standalone with no multiplexer is first list entry
toy_singleinit(toy_list, argv);
toy_list->toy_main();
}
diff --git a/tests/chattr.test b/tests/chattr.test
index d7dec0bc..cefc84b9 100755
--- a/tests/chattr.test
+++ b/tests/chattr.test
@@ -28,131 +28,56 @@ _t="abcdefghijklmnopqrstuvwxyz"
_empty="--------------------"
-# 'i' -- immutable
+# Check +i (immutable) works by trying to write to and remove the file.
_i="----i---------------"
-testing "[-/+]i FILE[write]" "$IN && touch testFile &&
+testing "immutable" "$IN && echo "$_t" > testFile &&
chattr +i testFile && lsattr testFile | clean &&
- date > testFile 2>/dev/null; chattr -i testFile;
- rm -rf testFile; $OUT " "$_i testFile\n" "" ""
-testing "[-/+]i FILE[re-write]" "$IN && touch testFile &&
- chattr +i testFile && date > testFile 2>/dev/null ||
- chattr -i testFile && date > testFile 2>/dev/null &&
- lsattr testFile | clean; rm -rf testFile 2>/dev/null; $OUT" \
- "$_empty testFile\n" "" ""
-testing "[-/+]i FILE[append]" "$IN && date > testFile &&
- chattr +i testFile && date >> testFile 2>/dev/null ||
- lsattr testFile | clean && chattr -i testFile; rm -rf testFile; $OUT" \
- "$_i testFile\n" "" ""
-testing "[-/+]i FILE[move]" "$IN && date > testFile &&
- chattr +i testFile && mv testFile testFile1 2>/dev/null ||
- lsattr testFile | clean && chattr -i testFile; rm -rf testFile; $OUT" \
- "$_i testFile\n" "" ""
-testing "[-/+]i FILE[delete]" "$IN && touch testFile && chattr +i testFile &&
- rm -f testFile 2>/dev/null || lsattr testFile | clean &&
- chattr -i testFile; rm -rf testFile; $OUT" "$_i testFile\n" "" ""
-testing "[-/+]i FILE[read]" "$IN && echo "$_t" > testFile &&
- chattr +i testFile && cat testFile && lsattr testFile | clean &&
- chattr -i testFile; rm -rf testFile; $OUT" "$_t\n$_i testFile\n" "" ""
+ date > testFile 2>/dev/null; rm testFile; chattr -i testFile;
+ cat testFile; rm -rf testFile; $OUT " "$_i testFile\n$_t\n" "" ""
-# 'a' --- append-only
+# Check +a (append-only) works by failing to write and succeeding in appending.
_a="-----a--------------"
-testing "[-/+]a FILE[write]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && echo $_t > testFile || lsattr testFile | clean &&
- chattr -a testFile; rm -rf testFile; $OUT" "$_a testFile\n" "" ""
-testing "[-/+]a FILE[re-write]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && echo $_t > testFile || lsattr testFile | clean &&
- chattr -a testFile && echo $_t > testFile && cat testFile &&
- lsattr testFile | clean; rm -rf testFile;
- $OUT" "$_a testFile\n$_t\n$_empty testFile\n" "" ""
-testing "[-/+]a FILE[append]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && echo $_t >> testFile && cat testFile &&
- lsattr testFile | clean && chattr -a testFile; rm -rf testFile; $OUT" \
- "$_t\n$_t\n$_a testFile\n" "" ""
-testing "[-/+]a FILE[move]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && mv testFile testFile1 ||
- lsattr testFile | clean && chattr -a testFile; rm -rf testFile; $OUT" \
- "$_a testFile\n" "" ""
-testing "[-/+]a FILE[delete]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && rm -f testFile || lsattr testFile | clean &&
- chattr -a testFile; rm -rf testFile; $OUT" "$_a testFile\n" "" ""
-testing "[-/+]a FILE[read]" "$IN && echo "$_t" > testFile &&
- chattr +a testFile && cat testFile && lsattr testFile | clean &&
- chattr -a testFile; rm -rf testFile; $OUT" "$_t\n$_a testFile\n" "" ""
+testing "append-only" "$IN && echo "$_t" > testFile &&
+ chattr +a testFile &&
+ echo $_t >> testFile &&
+ date > testFile || lsattr testFile | clean &&
+ chattr -a testFile; cat testFile; rm -rf testFile; $OUT" \
+ "$_a testFile\n$_t\n$_t\n" "" ""
-for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
-do
- testing "[-/+]$attr FILE" "$IN && echo "$_t" > testFile &&
- chattr +$attr testFile && cat testFile && chattr -$attr testFile &&
- lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" ""
+# For the rest, just toggle the bits back and forth (where supported).
+# Note that some file system/kernel combinations do return success but
+# silently ignore your request: +T on 4.19 f2fs, or +F on 5.2i ext4,
+# for example, so we're deliberately a bit selective here.
+for attr in "A" "c" "d" "e" "j" "P" "S" "s" "t" "u"; do
+ echo "$_t" > testFile && chattr +$attr testFile 2>/dev/null || SKIPNEXT=1
+ # Check that $attr is in the lsattr output, then that - turns it back off.
+ testing "toggle $attr" "lsattr testFile | awk '{print \$1}' > attrs;
+ grep -q $attr attrs || cat attrs; cat testFile && chattr -$attr testFile &&
+ lsattr testFile | clean; rm -rf testFile" "$_t\n$_empty testFile\n" "" ""
done
-for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
-do
- testing "-$attr FILE" "$IN && echo "$_t" > testFile && chattr -$attr testFile &&
- cat testFile && lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" ""
-done
+_aA="-----a-A------------"
+testing "multiple bits" "$IN && touch testFile &&
+ chattr +Aa testFile && lsattr testFile | clean &&
+ chattr -Aa testFile && lsattr testFile | clean;
+ rm -rf testFile; $OUT" "$_aA testFile\n$_empty testFile\n" "" ""
-testing "[-/+]AacDdijsStTu FILE" "$IN && echo "$_t" > testFile &&
- chattr +AacDdijsStTu testFile && cat testFile && chattr -AacDdijsStTu testFile &&
- lsattr testFile | clean; rm -rf testFile; $OUT" "$_t\n$_empty testFile\n" "" ""
-testing "[-/+]AacDdijsStTu(random) FILE" \
- "$IN && echo "$_t" > testFile &&
- chattr +AacDdijsStTu testFile && cat testFile && chattr -A testFile &&
- chattr -a testFile && chattr -c testFile && chattr -D testFile &&
- chattr -d testFile && chattr -i testFile && chattr -j testFile &&
- chattr -s testFile && chattr -S testFile && chattr -t testFile &&
- chattr -T testFile && chattr -u testFile && lsattr testFile | clean &&
- chattr -AacDdijsStTu testFile; rm -rf testFile; $OUT" \
- "$_t\n$_empty testFile\n" "" ""
-testing "[-/+]AacDdijsStTu FILE*" "$IN &&
- echo "$_t" > testFile && echo "$_t" > testFile1 &&
- echo "$_t" > testFile2 && echo "$_t" > testFile3 &&
- echo "$_t" > testFile4 && echo "$_t" > testFile5 &&
- echo "$_t" > testFile6 && echo "$_t" > testFile7 &&
- echo "$_t" > testFile8 && echo "$_t" > testFile9 &&
- echo "$_t" > testFile10 && echo "$_t" > testFile11 &&
- chattr +AacDdijsStTu testFile* &&
- cat testFile9 && chattr -AacDdijsStTu testFile* &&
- lsattr testFile* | clean; rm -rf testFile*; $OUT" \
- "$_t\n$_empty testFile\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n$_empty testFile_\n" "" ""
+testing "multiple files" "$IN && touch fileA && touch fileB &&
+ chattr +Aa fileA fileB && lsattr fileA fileB | clean &&
+ chattr -Aa fileA fileB && lsattr fileA fileB | clean;
+ rm -rf testFile*; $OUT" \
+ "$_aA fileA\n$_aA fileB\n$_empty fileA\n$_empty fileB\n" "" ""
-# 'A' --- no-atime
-_A="-------A------------"
-# 's' --- secure erase
-_s="s-------------------"
-testing "[-/+]AacDdijsStTu(random) FILE*" \
- "$IN && echo "$_t" > testFile &&
- chattr +AacDdijsStTu testFile* && cat testFile && chattr -A testFile* &&
- chattr -a testFile* && chattr -c testFile* && chattr -D testFile* &&
- chattr -d testFile* && chattr -i testFile* && chattr -j testFile* &&
- chattr -s testFile* && chattr -S testFile* && chattr -t testFile* &&
- chattr -T testFile* && chattr -u testFile* && lsattr testFile | clean;
- rm -rf testFile; $OUT" \
- "$_t\n$_empty testFile\n" "" ""
-testing "[-/+]i FILE[write]" \
- "$IN && echo "$_t" > testFile &&
- chattr +i testFile &&
- echo \"$_t\" > testFile || lsattr testFile | clean && chattr -i testFile;
- rm -rf testFile; $OUT" "$_i testFile\n" "" ""
-testing "[-/+]A FILE[write]" \
- "$IN && echo "$_t" > testFile && chattr +A testFile &&
- echo \"$_t\" > testFile && lsattr testFile | clean && chattr -A testFile;
- rm -rf testFile; $OUT" "$_A testFile\n" "" ""
-testing "[-/+]s FILE[write]" \
- "$IN && echo "$_t" > testFile && chattr +s testFile &&
- echo \"$_t\" > testFile && lsattr testFile | clean && chattr -s testFile
- rm -rf testFile; $OUT" "$_s testFile\n" "" ""
-NOSPACE=1 testing "-v version FILE[write]" \
- "$IN && echo "$_t" > testFile &&
- chattr -v 1234 testFile && echo \"$_t\" > testFile &&
- lsattr -v testFile | clean; rm -rf testFile" \
- "_ $_empty testFile\n" "" ""
+touch testFile; chattr -v 1234 testFile 2>/dev/null || SKIPNEXT=1
+testing "-v version" "lsattr -v testFile | awk '{print \$1}' &&
+ chattr -v 4567 testFile && lsattr -v testFile | awk '{print \$1}';
+ rm -rf testFile" "1234\n4567\n" "" ""
-testing "-R [-/+]a FILE" "$IN && touch aa && chattr -R +A aa &&
- lsattr aa | clean && chattr -R -A aa; rm -rf aa; $OUT" "$_A aa\n" "" ""
-testing "-R [-/+]a FILE.." "$IN && touch aa bb &&
- chattr -R +A aa bb && lsattr aa bb | clean &&
- chattr -R -A aa bb; rm -rf aa bb; $OUT" "$_A aa\n$_A bb\n" "" ""
+mkdir -p a/b/c && touch a/b/c/fA a/b/c/fB
+testing "-R" "chattr -R +a a && lsattr a/b/c/fA a/b/c/fB | clean &&
+ chattr -R -a a && rm -rf a" \
+ "$_a a/b/c/fA\n$_a a/b/c/fB\n" "" ""
+rm -rf a
# Clean up
rm -rf testattr
diff --git a/tests/sh.test b/tests/sh.test
index ba469e49..ca6a2831 100755
--- a/tests/sh.test
+++ b/tests/sh.test
@@ -4,6 +4,8 @@
[ -f testing.sh ] && . testing.sh
+# TODO make fake pty wrapper for test infrastructure
+
#testing "name" "command" "result" "infile" "stdin"
[ -z "$SH" ] && { [ -z "$TEST_HOST" ] && SH="sh" || export SH="bash" ; }
@@ -55,6 +57,13 @@ testing "leading variable assignments" \
#testing "can't have space before first : but yes around arguments" \
# 'BLAH=abcdefghi; echo ${BLAH: 1 : 3 }' "bcd\n" "" ""
+NOSPACE=1 testing "curly brackets and pipe" \
+ '{ echo one; echo two ; } | tee blah.txt; wc blah.txt' \
+ "one\ntwo\n2 2 8 blah.txt\n" "" ""
+NOSPACE=1 testing "parentheses and pipe" \
+ '(echo two;echo three)|tee blah.txt;wc blah.txt' \
+ "two\nthree\n2 2 10 blah.txt\n" "" ""
+
# texpect "name" "command" E/O/I"string"
# Prompt changes for root/normal user
@@ -65,3 +74,39 @@ SH="env -i PATH=${PATH@Q} PS1='\\$ ' $SH --noediting --noprofile --norc -is"
txpect "prompt and exit" "$SH" "E$P" "Iexit\n" X0
txpect "prompt and echo" "$SH" "E$P" "Iecho hello\n" "Ohello"$'\n' "E$P" X0
txpect "redirect err" "$SH" "E$P" "Iecho > /dev/full\n" "E" "E$P" X0
+txpect "wait for <(exit)" "$SH" "E$P" "Icat <(echo hello 1>&2)\n" $'Ehello\n' \
+ "E$P" X0
+
+
+
+# $@ $* $# $? $- $$ $! $0
+# always exported: PWD SHLVL _
+# ./bash -c 'echo $_' prints $BASH, but PATH search shows path? Hmmm...
+# ro: UID PPID EUID $
+# IFS LINENO
+# PATH HOME SHELL USER LOGNAME SHLVL HOSTNAME HOSTTYPE MACHTYPE OSTYPE OLDPWD
+# PS0 PS1='$ ' PS2='> ' PS3 PS4 BASH BASH_VERSION
+# ENV - if [ -n "$ENV" ]; then . "$ENV"; fi # BASH_ENV - synonym for ENV
+# FUNCNEST - maximum function nesting level (abort when above)
+# REPLY - set by input with no args
+# OPTARG OPTIND - set by getopts builtin
+# OPTERR
+
+# maybe not: EXECIGNORE, FIGNORE, GLOBIGNORE
+
+#BASHPID - synonym for $$ HERE
+#BASH_SUBSHELL - SHLVL synonym
+#BASH_EXECUTION_STRING - -c argument
+#
+#automatically set:
+#OPTARG - set by getopts builtin
+#OPTIND - set by getopts builtin
+#
+#PROMPT_COMMAND PROMPT_DIRTRIM PS0 PS1 PS2 PS3 PS4
+#
+#unsettable (assignments ignored before then)
+#LINENO SECONDS RANDOM
+#GROUPS - id -g
+#HISTCMD - history number
+#
+#TMOUT - used by read
diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c
index 24591d53..cd236b9e 100644
--- a/toys/other/lsattr.c
+++ b/toys/other/lsattr.c
@@ -7,8 +7,8 @@
*
* TODO cleanup
-USE_LSATTR(NEWTOY(lsattr, "vpldaR", TOYFLAG_BIN))
-USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN))
+USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN))
+USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN))
config LSATTR
bool "lsattr"
@@ -62,6 +62,13 @@ config CHATTR
#include "toys.h"
#include <linux/fs.h>
+GLOBALS(
+ long v;
+ long p;
+
+ long add, rm, set;
+)
+
#define FS_PROJINHERT_FL 0x20000000 // Linux 4.5
#define FS_CASEFOLD_FL 0x40000000 // Linux 5.4
#define FS_VERITY_FL 0x00100000 // Linux 5.4
@@ -117,6 +124,18 @@ static int ext2_getflag(int fd, struct stat *sb, unsigned long *flag)
return (ioctl(fd, FS_IOC_GETFLAGS, (void*)flag));
}
+static char *attrstr(unsigned long attrs, int full)
+{
+ struct ext2_attr *a = e2attrs;
+ char *s = toybuf;
+
+ for (; a->name; a++)
+ if (attrs & a->flag) *s++ = a->opt;
+ else if (full) *s++ = '-';
+ *s = '\0';
+ return toybuf;
+}
+
static void print_file_attr(char *path)
{
unsigned long flag = 0, version = 0;
@@ -157,14 +176,7 @@ static void print_file_attr(char *path)
}
if (!name_found) xprintf("---");
xputc('\n');
- } else {
- int index = 0;
-
- for (; ptr->name; ptr++)
- toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-';
- toybuf[index] = '\0';
- xprintf("%s %s\n", toybuf, path);
- }
+ } else xprintf("%s %s\n", attrstr(flag, 1), path);
}
xclose(fd);
return;
@@ -202,7 +214,7 @@ void lsattr_main(void)
{
if (!*toys.optargs) dirtree_read(".", retell_dir);
else
- for (; *toys.optargs; toys.optargs++) {
+ for (; *toys.optargs; toys.optargs++) {
struct stat sb;
if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs);
@@ -217,11 +229,6 @@ void lsattr_main(void)
#define FOR_chattr
#include "generated/flags.h"
-static struct _chattr {
- unsigned long add, rm, set, projid, version;
- unsigned char pflag, vflag, recursive;
-} chattr;
-
// Set file flags on a Linux second extended file system.
static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag)
{
@@ -244,40 +251,21 @@ static unsigned long get_flag_val(char ch)
// Parse command line argument and fill the chattr structure.
static void parse_cmdline_arg(char ***argv)
{
- char *arg = **argv, *ptr = NULL;
+ char *arg = **argv, *ptr;
while (arg) {
switch (arg[0]) {
case '-':
- for (ptr = ++arg; *ptr; ptr++) {
- if (*ptr == 'R') {
- chattr.recursive = 1;
- continue;
- } else if (*ptr == 'p' || *ptr == 'v') {
- unsigned val;
-
- arg = *(*argv += 1);
- if (!arg) help_exit("missing arg to -%c", *ptr);
-
- val = atolx_range(arg, 0, UINT_MAX);
- if (*ptr == 'v') {
- chattr.version = val;
- chattr.vflag = 1;
- } else {
- chattr.projid = val;
- chattr.pflag = 1;
- }
- continue;
- } else chattr.rm |= get_flag_val(*ptr);
- }
+ for (ptr = ++arg; *ptr; ptr++)
+ TT.rm |= get_flag_val(*ptr);
break;
case '+':
for (ptr = ++arg; *ptr; ptr++)
- chattr.add |= get_flag_val(*ptr);
+ TT.add |= get_flag_val(*ptr);
break;
case '=':
for (ptr = ++arg; *ptr; ptr++)
- chattr.set |= get_flag_val(*ptr);
+ TT.set |= get_flag_val(*ptr);
break;
default: return;
}
@@ -288,9 +276,8 @@ static void parse_cmdline_arg(char ***argv)
// Update attribute of given file.
static int update_attr(struct dirtree *root)
{
- unsigned long fval = 0;
char *fpath = NULL;
- int fd;
+ int v = TT.v, fd;
if (!dirtree_notdotdot(root)) return 0;
@@ -298,7 +285,7 @@ static int update_attr(struct dirtree *root)
* if file is a link and recursive is set or file is not regular+link+dir
* (like fifo or dev file) then escape the file.
*/
- if ((S_ISLNK(root->st.st_mode) && chattr.recursive)
+ if ((S_ISLNK(root->st.st_mode) && FLAG(R))
|| (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode)
&& !S_ISDIR(root->st.st_mode)))
return 0;
@@ -308,56 +295,62 @@ static int update_attr(struct dirtree *root)
free(fpath);
return DIRTREE_ABORT;
}
- // Get current attr of file.
- if (ext2_getflag(fd, &(root->st), &fval) < 0) {
- perror_msg("read flags of '%s'", fpath);
- free(fpath);
- xclose(fd);
- return DIRTREE_ABORT;
- }
- if (chattr.set) { // for '=' operator.
- if (ext2_setflag(fd, &(root->st), chattr.set) < 0)
- perror_msg("setting flags '%s'", fpath);
- } else { // for '-' / '+' operator.
- fval &= ~(chattr.rm);
- fval |= chattr.add;
- if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL;
- if (ext2_setflag(fd, &(root->st), fval) < 0)
- perror_msg("setting flags '%s'", fpath);
+
+ // Any potential flag changes?
+ if (TT.set | TT.add | TT.set) {
+ unsigned long orig, new;
+
+ // Read current flags.
+ if (ext2_getflag(fd, &(root->st), &orig) < 0) {
+ perror_msg("read flags of '%s'", fpath);
+ free(fpath);
+ xclose(fd);
+ return DIRTREE_ABORT;
+ }
+ // Apply the requested changes.
+ if (TT.set) new = TT.set; // '='.
+ else { // '-' and/or '+'.
+ new = orig;
+ new &= ~(TT.rm);
+ new |= TT.add;
+ if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL;
+ }
+ // Write them back if there was any change.
+ if (orig != new && ext2_setflag(fd, &(root->st), new)<0)
+ perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0));
}
// (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR
// isn't available until 4.5.)
- if (chattr.vflag && (ioctl(fd, FS_IOC_SETVERSION, &chattr.version)<0))
- perror_msg("while setting version on '%s'", fpath);
+ if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &v)<0))
+ perror_msg("%s: setting version to %d failed", fpath, v);
- if (chattr.pflag) {
+ if (FLAG(p)) {
struct fsxattr_4_5 fsx;
+ int fail = ioctl(fd, FS_IOC_FSGETXATTR_4_5, &fsx);
- if (ioctl(fd, FS_IOC_FSGETXATTR_4_5, &fsx))
- perror_exit("%s: FS_IOC_FSGETXATTR failed", fpath);
- fsx.fsx_projid = chattr.projid;
- if (ioctl(fd, FS_IOC_FSSETXATTR_4_5, &fsx))
- perror_exit("%s: FS_IOC_FSSETXATTR failed", fpath);
+ fsx.fsx_projid = TT.p;
+ if (fail || ioctl(fd, FS_IOC_FSSETXATTR_4_5, &fsx))
+ perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid);
}
free(fpath);
xclose(fd);
- return (S_ISDIR(root->st.st_mode) && chattr.recursive) ? DIRTREE_RECURSE : 0;
+ return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0;
}
void chattr_main(void)
{
char **argv = toys.optargs;
- memset(&chattr, 0, sizeof(struct _chattr));
parse_cmdline_arg(&argv);
+ if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p);
+ if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v);
if (!*argv) help_exit("no file");
- if (chattr.set && (chattr.add || chattr.rm))
+ if (TT.set && (TT.add || TT.rm))
error_exit("no '=' with '-' or '+'");
- if (chattr.rm & chattr.add) error_exit("set/unset same flag");
- if (!(chattr.add || chattr.rm || chattr.set || chattr.pflag || chattr.vflag))
+ if (TT.rm & TT.add) error_exit("set/unset same flag");
+ if (!(TT.add || TT.rm || TT.set || FLAG(p) || FLAG(v)))
error_exit("need '-p', '-v', '=', '-', or '+'");
for (; *argv; argv++) dirtree_read(*argv, update_attr);
- toys.exitval = 0; //always set success at this point.
}
diff --git a/toys/other/stat.c b/toys/other/stat.c
index 08b278f8..37dd1808 100644
--- a/toys/other/stat.c
+++ b/toys/other/stat.c
@@ -117,7 +117,7 @@ static void print_stat(char type)
printf("%s", TT.file);
if (S_ISLNK(stat->st_mode))
if (readlink0(TT.file, toybuf, sizeof(toybuf)))
- printf(" -> `%s'", toybuf);
+ printf(" -> '%s'", toybuf);
} else if (type == 'o') out('u', stat->st_blksize);
else if (type == 's') out('u', stat->st_size);
else if (type == 't') out('x', dev_major(stat->st_rdev));
diff --git a/toys/pending/readelf.c b/toys/pending/readelf.c
index a9554265..acb1ed0f 100644
--- a/toys/pending/readelf.c
+++ b/toys/pending/readelf.c
@@ -43,14 +43,14 @@ GLOBALS(
// Section header.
struct sh {
int type, link, info;
- long flags, addr, offset, size, addralign, entsize;
+ long long flags, addr, offset, size, addralign, entsize;
char *name;
};
// Program header.
struct ph {
int type, flags;
- long offset, vaddr, paddr, filesz, memsz, align;
+ long long offset, vaddr, paddr, filesz, memsz, align;
};
static void get_sh(int i, struct sh *s)
@@ -396,7 +396,7 @@ static void scan_elf()
char sh_flags[12] = {}, *p = sh_flags;
for (j=0; j<12; j++) if (s.flags&(1<<j)) *p++="WAXxMSILOTC"[j];
- printf(" [%2d] %-20s %-14s %0*lx %06lx %06lx %02lx %3s %2d %2d %2ld\n",
+ printf(" [%2d] %-20s %-14s %0*llx %06llx %06llx %02llx %3s %2d %2d %2lld\n",
i, s.name, sh_type(s.type), w, s.addr, s.offset, s.size,
s.entsize, sh_flags, s.link, s.info, s.addralign);
}
@@ -423,7 +423,7 @@ static void scan_elf()
"Offset", w, "VirtAddr", w, "PhysAddr", "FileSiz", "MemSiz");
for (i=0; i<phnum; i++) {
get_ph(i, &ph);
- printf(" %-14s 0x%06lx 0x%0*lx 0x%0*lx 0x%05lx 0x%05lx %c%c%c %#lx\n",
+ printf(" %-14s 0x%06llx 0x%0*llx 0x%0*llx 0x%05llx 0x%05llx %c%c%c %#llx\n",
ph_type(ph.type), ph.offset, w, ph.vaddr, w, ph.paddr,
ph.filesz, ph.memsz, ph.flags&4?'R':' ', ph.flags&2?'W':' ',
ph.flags&1?'E':' ', ph.align);
@@ -456,7 +456,7 @@ static void scan_elf()
xputc('\n');
if (!dynamic.size) printf("There is no dynamic section in this file.\n");
- else printf("Dynamic section at offset 0x%lx contains %ld entries:\n"
+ else printf("Dynamic section at offset 0x%llx contains %lld entries:\n"
" %-*s %-20s %s\n",
dynamic.offset, dynamic.size/dynamic.entsize,
w+2, "Tag", "Type", "Name/Value");
@@ -517,7 +517,7 @@ static void scan_elf()
get_ph(i, &ph);
if (ph.type == 4 /*PT_NOTE*/) {
printf("\n"
- "Displaying notes found at file offset 0x%lx with length 0x%lx:\n",
+ "Displaying notes found at file offset 0x%llx with length 0x%llx:\n",
ph.offset, ph.filesz);
show_notes(ph.offset, ph.filesz);
}
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 7e6f827d..9f686932 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -142,6 +142,7 @@ static void syntax_err(char *msg, ...)
{
va_list va;
+// TODO rethink syntax errordom
va_start(va, msg);
verror_msg(msg, 0, va);
va_end(va);
@@ -579,7 +580,7 @@ int save_redirect(int **rd, int from, int to)
if (hfd != dup2(to, hfd)) hfd = -1;
else fcntl(hfd, F_SETFD, FD_CLOEXEC);
-if (BUGBUG) dprintf(255, "redir from=%d to=%d hfd=%d\n", from, to, hfd);
+if (BUGBUG) dprintf(255, "%d redir from=%d to=%d hfd=%d\n", getpid(), from, to, hfd);
// dup "to"
if (from != -1 && to != dup2(from, to)) {
if (hfd != -1) close(hfd);
@@ -626,7 +627,7 @@ static void subshell_callback(void)
// TODO avoid prototype
static int sh_run(char *new);
-// Pass environment and command string to child shell
+// Pass environment and command string to child shell, return PID of child
static int run_subshell(char *str, int len)
{
pid_t pid;
@@ -636,7 +637,7 @@ static int run_subshell(char *str, int len)
char *s;
if ((pid = fork())<0) perror_msg("fork");
- else if (pid>0) {
+ else if (!pid) {
s = xstrndup(str, len);
sh_run(s);
free(s);
@@ -667,6 +668,33 @@ static int run_subshell(char *str, int len)
return pid;
}
+// turn a parsed pipeline back into a string.
+static char *pl2str(struct sh_pipeline *pl)
+{
+ struct sh_pipeline *end = 0;
+ int level = 0, len = 0, i, j;
+ char *s, *ss, *sss;
+
+ // measure, then allocate
+ for (j = 0; ; j++) for (end = pl; end; end = end->next) {
+ if (end->type == 1) level++;
+ else if (end->type == 3 && --level<0) break;
+
+ for (i = 0; i<pl->arg->c; i++)
+ if (j) ss += sprintf(ss, "%s ", pl->arg->v[i]);
+ else len += strlen(pl->arg->v[i])+1;
+
+ sss = pl->arg->v[pl->arg->c];
+ if (!sss) sss = ";";
+ if (j) ss = stpcpy(ss, sss);
+ else len += strlen(sss);
+
+// TODO add HERE documents back in
+ if (j) return s;
+ s = ss = xmalloc(len+1);
+ }
+}
+
// Expand arguments and perform redirections. Return new process object with
// expanded args. This can be called from command or block context.
static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
@@ -1078,7 +1106,7 @@ struct sh_pipeline *block_end(struct sh_pipeline *pl)
pl = pl->next;
}
- return 0;
+ return pl;
}
void free_function(struct sh_function *sp)
@@ -1457,6 +1485,7 @@ void dump_filehandles(char *when)
*/
+// wait for every process in a pipeline to end
static int wait_pipeline(struct sh_process *pp)
{
int rc = 0;
@@ -1474,6 +1503,7 @@ static int wait_pipeline(struct sh_process *pp)
return rc;
}
+// pipe data into and out of this segment, I.E. handle leading and trailing |
static int pipe_segments(char *ctl, int *pipes, int **urd)
{
unredirect(*urd);
@@ -1504,6 +1534,7 @@ static int pipe_segments(char *ctl, int *pipes, int **urd)
return 0;
}
+// Handle && and || traversal in pipeline segments
static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl)
{
char *ctl = pl->arg->v[pl->arg->c];
@@ -1521,9 +1552,9 @@ static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl)
// run a parsed shell function. Handle flow control blocks and characters,
// setup pipes and block redirection, break/continue, call builtins,
// vfork/exec external commands.
-static void run_function(struct sh_function *sp)
+static void run_function(struct sh_pipeline *pl)
{
- struct sh_pipeline *pl = sp->pipeline, *end;
+ struct sh_pipeline *end;
struct blockstack {
struct blockstack *next;
struct sh_pipeline *start, *end;
@@ -1541,19 +1572,17 @@ static void run_function(struct sh_function *sp)
// iterate through pipeline segments
while (pl) {
- char *s = *pl->arg->v, *ss = pl->arg->v[1];
-if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[pl->arg->c]);
+ struct sh_arg *arg = pl->arg;
+ char *s = *arg->v, *ss = arg->v[1], *ctl = arg->v[arg->c];
+if (BUGBUG) dprintf(255, "%d runtype=%d %s %s\n", getpid(), pl->type, s, ctl);
// Is this an executable segment?
if (!pl->type) {
- struct sh_arg *arg = pl->arg;
- char *ctl = arg->v[arg->c];
// Skip disabled block
if (blk && !blk->run) {
while (pl->next && !pl->next->type) pl = pl->next;
continue;
}
-
if (pipe_segments(ctl, pipes, &urd)) break;
// If we just started a new pipeline, implicit parentheses (subshell)
@@ -1571,7 +1600,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// How many layers to peel off?
i = ss ? atol(ss) : 0;
if (i<1) i = 1;
- if (!blk || pl->arg->c>2 || ss[strspn(ss, "0123456789")]) {
+ if (!blk || arg->c>2 || ss[strspn(ss, "0123456789")]) {
syntax_err("bad %s", s);
break;
}
@@ -1605,6 +1634,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// TODO: "echo | read i" is backgroundable with ctrl-Z despite read = builtin.
// probably have to inline run_command here to do that? Implicit ()
// also "X=42 | true; echo $X" doesn't get X.
+// I.E. run_subshell() here sometimes? (But when?)
dlist_add_nomalloc((void *)&pplist, (void *)run_command(arg));
}
@@ -1618,10 +1648,10 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// Start of flow control block?
} else if (pl->type == 1) {
+ struct sh_process *pp = 0;
// are we entering this block (rather than looping back to it)?
if (!blk || blk->start != pl) {
- struct sh_process *pp;
// If it's a nested block we're not running, skip ahead.
end = block_end(pl->next);
@@ -1632,7 +1662,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
}
// If previous piped into this block, save context until block end
- if (pipe_segments(0, pipes, &urd)) break;
+ if (pipe_segments(end->arg->v[end->arg->c], pipes, &urd)) break;
// It's a new block we're running, save context and add it to the stack.
new = xzalloc(sizeof(*blk));
@@ -1649,19 +1679,20 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
*pipes = -1;
// Perform redirects listed at end of block
- pp = expand_redir(blk->end->arg, 0, blk->urd);
+ pp = expand_redir(end->arg, 1, blk->urd);
blk->urd = pp->urd;
- if (pp->arg.c) perror_msg("unexpected %s", *pp->arg.v);
- llist_traverse(pp->delete, free);
- if (pp->arg.c) break;
- free(pp);
+ if (pp->arg.c) {
+// TODO this is a syntax_error
+ perror_msg("unexpected %s", *pp->arg.v);
+ llist_traverse(pp->delete, free);
+ free(pp);
+ break;
+ }
}
// What flow control statement is this?
-// TODO ( subshell
-
- // if/then/elif/else/fi, while until/do/done - no special handling needed
+ // {/} if/then/elif/else/fi, while until/do/done - no special handling
// for select/do/done
if (!strcmp(s, "for") || !strcmp(s, "select")) {
@@ -1677,10 +1708,33 @@ dprintf(2, "TODO skipped init for((;;)), need math parser\n");
expand_arg(&blk->farg, pl->next->arg->v[i], 0, &blk->fdelete);
} else expand_arg(&blk->farg, "\"$@\"", 0, &blk->fdelete);
}
- pl = pl->next;
- }
-// TODO case/esac {/} [[/]] (/) ((/)) function/}
+// TODO case/esac [[/]] (/) ((/)) function/}
+
+/*
+TODO: a | b | c needs subshell for builtins?
+ - anything that can produce output
+ - echo declare dirs
+ (a; b; c) like { } but subshell
+ when to auto-exec? ps vs sh -c 'ps' vs sh -c '(ps)'
+*/
+
+ // subshell
+ } else if (!strcmp(s, "(")) {
+ if (!CFG_TOYBOX_FORK) {
+ ss = pl2str(pl->next);
+ pp->pid = run_subshell(ss, strlen(ss));
+ free(ss);
+ } else {
+ if (!(pp->pid = fork())) {
+ run_function(pl->next);
+ _exit(toys.exitval);
+ }
+ }
+
+ dlist_add_nomalloc((void *)&pplist, (void *)pp);
+ pl = blk->end->prev;
+ }
// gearshift from block start to block body (end of flow control test)
} else if (pl->type == 2) {
@@ -1705,6 +1759,9 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n");
// end of block, may have trailing redirections and/or pipe
} else if (pl->type == 3) {
+ // if we end a block we're not in, we started in a block.
+ if (!blk) break;
+
// repeating block?
if (blk->run && !strcmp(s, "done")) {
pl = blk->start;
@@ -1750,7 +1807,7 @@ static int sh_run(char *new)
// TODO switch the fmemopen for -c to use this? Error checking? $(blah)
memset(&scratch, 0, sizeof(struct sh_function));
- if (!parse_line(new, &scratch)) run_function(&scratch);
+ if (!parse_line(new, &scratch)) run_function(scratch.pipeline);
free_function(&scratch);
rc = toys.exitval;
toys.exitval = 0;
@@ -1972,7 +2029,7 @@ if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); }
if (BUGBUG) dump_state(&scratch);
if (prompt != 1) {
// TODO: ./blah.sh one two three: put one two three in scratch.arg
- if (!prompt) run_function(&scratch);
+ if (!prompt) run_function(scratch.pipeline);
free_function(&scratch);
prompt = 0;
}
diff --git a/toys/pending/vi.c b/toys/pending/vi.c
index 5086a0da..c6f44ef6 100644
--- a/toys/pending/vi.c
+++ b/toys/pending/vi.c
@@ -418,9 +418,9 @@ static int text_codepoint(char *dest, size_t offset)
static size_t text_sol(size_t offset)
{
size_t pos;
- if (!TT.filesize) return 0;
+ if (!TT.filesize || !offset) return 0;
else if (TT.filesize <= offset) return TT.filesize-1;
- else if ((pos = text_strrchr(offset, '\n')) == SIZE_MAX) return 0;
+ else if ((pos = text_strrchr(offset-1, '\n')) == SIZE_MAX) return 0;
else if (pos < offset) return pos+1;
return offset;
}
diff --git a/toys/posix/unlink.c b/toys/posix/unlink.c
index f5faab06..017ba9d6 100644
--- a/toys/posix/unlink.c
+++ b/toys/posix/unlink.c
@@ -20,5 +20,5 @@ config UNLINK
void unlink_main(void)
{
if (unlink(*toys.optargs))
- perror_exit("Couldn't unlink `%s'", *toys.optargs);
+ perror_exit("couldn't unlink '%s'", *toys.optargs);
}