diff -Nru toybox-0.8.8+dfsg/configure toybox-0.8.9+dfsg/configure
--- toybox-0.8.8+dfsg/configure 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/configure 2023-01-10 19:24:45.000000000 +0000
@@ -13,7 +13,7 @@
# Warn about stuff, disable stupid warnings, be 8-bit clean for utf8.
[ "${CFLAGS/-funsigned-char//}" == "$CFLAGS" ] &&
- CFLAGS+=" -Wall -Wundef -Werror=implicit-function-declaration -Wno-char-subscripts -Wno-pointer-sign -Wno-string-plus-int -funsigned-char"
+ CFLAGS+=" -Wall -Wundef -Werror=implicit-function-declaration -Wno-char-subscripts -Wno-pointer-sign -funsigned-char"
# Set default values if variable not already set
: ${CC:=cc} ${HOSTCC:=cc} ${GENDIR:=generated} ${KCONFIG_CONFIG:=.config}
diff -Nru toybox-0.8.8+dfsg/debian/changelog toybox-0.8.9+dfsg/debian/changelog
--- toybox-0.8.8+dfsg/debian/changelog 2022-09-11 22:27:42.000000000 +0000
+++ toybox-0.8.9+dfsg/debian/changelog 2023-04-12 01:58:55.000000000 +0000
@@ -1,3 +1,19 @@
+toybox (0.8.9+dfsg-1ubuntu1) lunar; urgency=medium
+
+ * Clear V from the environment, the package fails to build if set.
+
+ -- Steve Langasek ",
toybuf, toybuf);
- for (i=0; i < toys.toycount; i++)
- xprintf("%s \n", toy_list[i].name, toy_list[i].name);
+ for (i=0; i'
+echo '
"
diff -Nru toybox-0.8.8+dfsg/scripts/install.sh toybox-0.8.9+dfsg/scripts/install.sh
--- toybox-0.8.8+dfsg/scripts/install.sh 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/scripts/install.sh 2023-01-10 19:24:45.000000000 +0000
@@ -31,7 +31,6 @@
echo "Compile instlist..."
-NOBUILD=1 scripts/make.sh
$DEBUG $HOSTCC -I . scripts/install.c -o "$UNSTRIPPED"/instlist || exit 1
COMMANDS="$("$UNSTRIPPED"/instlist $LONG_PATH)"
@@ -106,11 +105,9 @@
# The following are commands toybox should provide, but doesn't yet.
# For now symlink the host version. This list must go away by 1.0.
-PENDING="dd diff expr git tr vi bash sh xzcat bc ar gzip less awk unxz bison flex make nm"
-
-# "gcc" can go away if the kernel guys merge my patch:
-# http://lkml.iu.edu/hypermail/linux/kernel/2202.0/01505.html
-TOOLCHAIN="as cc ld gcc objdump"
+PENDING="expr git tr bash sh gzip awk bison flex make"
+TOOLCHAIN="as cc ld objdump"
+TOOLCHAIN+="bc gcc" # both patched out but not in vanilla yet
# Tools needed to build packages
for i in $TOOLCHAIN $PENDING $HOST_EXTRA
diff -Nru toybox-0.8.8+dfsg/scripts/make.sh toybox-0.8.9+dfsg/scripts/make.sh
--- toybox-0.8.8+dfsg/scripts/make.sh 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/scripts/make.sh 2023-01-10 19:24:45.000000000 +0000
@@ -85,7 +85,7 @@
[ -z "$V" ] && X=/dev/null || X=/dev/stderr
for i in util crypt m resolv selinux smack attr crypto z log iconv tls ssl
do
- do_loudly ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -l$i &>$X \
+ do_loudly ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -l$i >>$X 2>&1 \
-o "$UNSTRIPPED"/libprobe <<<"int main(int argc,char*argv[]){return 0;}"&&
do_loudly echo -n ' '-l$i >> "$GENDIR"/optlibs.new
done
@@ -243,8 +243,7 @@
[ -n "$NOBUILD" ] && exit 0
-echo -n "Compile $OUTNAME"
-[ -n "$V" ] && echo
+echo "Compile $OUTNAME"
DOTPROG=.
# This is a parallel version of: do_loudly $BUILD lib/*.c $TOYFILES $LINK
@@ -304,4 +303,4 @@
# multiplexer binary via truncate-and-write through a symlink.
do_loudly chmod 555 "$OUTNAME" || exit 1
-echo
+[ -z "$V" ] && echo >&2
diff -Nru toybox-0.8.8+dfsg/scripts/mkflags.c toybox-0.8.9+dfsg/scripts/mkflags.c
--- toybox-0.8.8+dfsg/scripts/mkflags.c 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/scripts/mkflags.c 2023-01-10 19:24:45.000000000 +0000
@@ -1,4 +1,4 @@
-// Take three word input lines on stdin and produce flag #defines to stdout.
+// Read three word input lines from stdin and produce flag #defines to stdout.
// The three words on each input line are command name, option string with
// current config, option string from allyesconfig. The three are space
// separated and the last two are in double quotes.
@@ -170,9 +170,8 @@
// See "intentionally crappy", above.
if (!(out = outbuf)) return 1;
- printf("#undef FORCED_FLAG\n#undef FORCED_FLAGLL\n"
- "#ifdef FORCE_FLAGS\n#define FORCED_FLAG 1\n#define FORCED_FLAGLL 1ULL\n"
- "#else\n#define FORCED_FLAG 0\n#define FORCED_FLAGLL 0LL\n#endif\n\n");
+ printf("#undef FORCED_FLAG\n#ifdef FORCE_FLAGS\n#define FORCED_FLAG 1LL\n"
+ "#else\n#define FORCED_FLAG 0LL\n#endif\n\n");
for (;;) {
struct flag *flist, *aflist, *offlist;
@@ -230,7 +229,7 @@
out += strlen(out);
while (aflist) {
- char *llstr = bit>30 ? "LL" : "", *s = (char []){0, 0, 0, 0};
+ char *s = (char []){0, 0, 0, 0};
int enabled = 0;
// Output flag macro for bare longopts
@@ -245,8 +244,8 @@
if (flist && flist->command && *aflist->command == *flist->command)
enabled++;
}
- out += sprintf(out, "#define FLAG_%s (%s%s<<%d)\n",
- s, enabled ? "1" : "FORCED_FLAG", llstr, bit++);
+ out += sprintf(out, "#define FLAG_%s (%s<<%d)\n",
+ s, enabled ? "1LL" : "FORCED_FLAG", bit++);
aflist = aflist->next;
if (enabled) flist = flist->next;
}
diff -Nru toybox-0.8.8+dfsg/scripts/mkroot.sh toybox-0.8.9+dfsg/scripts/mkroot.sh
--- toybox-0.8.8+dfsg/scripts/mkroot.sh 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/scripts/mkroot.sh 2023-01-10 19:24:45.000000000 +0000
@@ -12,8 +12,8 @@
done
# Set default directory locations (overrideable from command line)
-: ${LOG:=${BUILD:=${TOP:=$PWD/root}/build}/log} ${AIRLOCK:=$BUILD/airlock}
-: ${CCC:=$PWD/ccc} ${PKGDIR:=$PWD/scripts/root}
+: ${TOP:=$PWD/root} ${BUILD:=$TOP/build} ${LOG:=$BUILD/log}
+: ${AIRLOCK:=$BUILD/airlock} ${CCC:=$PWD/ccc} ${PKGDIR:=$PWD/scripts/root}
# define functions
announce() { printf "\033]2;$CROSS $*\007" >/dev/tty; printf "\n=== $*\n";}
@@ -63,6 +63,7 @@
rm .singleconfig_airlock || exit 1
fi
export PATH="$AIRLOCK"
+ CPIO_OPTS+=--no-preserve-owner
fi
# Create per-target work directories
@@ -116,6 +117,7 @@
echo 0 99999 > /proc/sys/net/ipv4/ping_group_range
if [ $$ -eq 1 ]; then # Setup networking for QEMU (needs /proc)
+ mountpoint -q mnt || [ -e /dev/?da ] && mount /dev/?da /mnt
ifconfig lo 127.0.0.1
ifconfig eth0 10.0.2.15
route add default gw 10.0.2.2
@@ -126,6 +128,7 @@
for i in $(ls -1 /etc/rc 2>/dev/null | sort); do . /etc/rc/"$i"; done
[ -z "$CONSOLE" ] && CONSOLE="$( '
+git log --pretty='%H%n%an<%ae>%n%ad%n%s' --date=format:'%rcommit author date description
%d-%m-%Y' | while read HASH
+do
+ HASH="${HASH::12}"
+ read AUTHOR
+ AUTHOR1="${AUTHOR/<*/}"
+ AUTHOR1="${AUTHOR1::17}"
+ AUTHOR2="<${AUTHOR/*}"
+ AUTHOR2="${AUTHOR2::20}"
+ read DATE
+ DATE="${DATE/ / }"
+ read DESC
+ echo " "
+done
+echo "$HASH $AUTHOR1
$AUTHOR2$DATE $DESC %d %s
", stat, str, stat, str);
-}
-
// She never told me...
char *mime(char *file)
{
@@ -80,6 +68,25 @@
return toybuf;
}
+// Stop: header time.
+static void header_time(int stat, char *str, char *more)
+{
+ char buf[64];
+
+ if (!more) more = "";
+ if (FLAG(v)) dprintf(2, "REPLY: %d %s\n%s\n", stat, str, more);
+ xprintf("HTTP/1.1 %d %s\r\nServer: toybox httpd/%s\r\nDate: %s\r\n%s"
+ "Connection: close\r\n\r\n", stat, str, TOYBOX_VERSION,
+ rfc1123(buf, time(0)), more);
+}
+
+static void error_time(int stat, char *str)
+{
+ header_time(stat, str, 0);
+ xprintf("%d %s
", stat, str, stat, str);
+}
+
static int isunder(char *file, char *dir)
{
char *s1 = xabspath(dir, ABS_FILE), *s2 = xabspath(file, 0), *ss = s2;
@@ -95,8 +102,15 @@
void handle(int infd, int outfd)
{
FILE *fp = fdopen(infd, "r");
- char *s = xgetline(fp), *ss, *esc, *path, *word[3];
- int i, fd;
+ char *s = xgetline(fp), *cut, *ss, *esc, *path, *word[3];
+ int i = sizeof(toybuf), fd;
+
+ if (!s) return;
+
+ if (!getsockname(0, (void *)&toybuf, &i)) {
+ if (FLAG(v))
+ dprintf(2, "Hello %s\n%s\n", ntop((void *)toybuf), s);
+ }
// Split line into method/path/protocol
for (i = 0, ss = s;;) {
@@ -110,6 +124,7 @@
// Process additional http/1.1 lines
while ((ss = xgetline(fp))) {
i = *chomp(ss);
+ if (FLAG(v)) dprintf(2, "%s\n", ss);
// TODO: any of
//User-Agent: Wget/1.20.1 (linux-gnu) - do we want to log anything?
//Accept: */* - 406 Too Snobbish
@@ -123,12 +138,14 @@
if (!strcasecmp(word[0], "get")) {
struct stat st;
+
if (*(ss = word[1])!='/') error_time(400, "Bad Request");
while (*ss=='/') ss++;
- if (!*ss) ss = ".";
- else unescape_url(ss);
+ if (!*ss) ss = "./";
+ else if ((cut = unescape_url(ss, 1))) setenv("QUERY_STRING", cut, 1);
// TODO domain.com:/path/to/blah domain2.com:/path/to/that
+ // TODO cgi PATH_INFO /path/to/filename.cgi/and/more/stuff?path&info
if (!isunder(ss, ".") || stat(ss, &st)) error_time(404, "Not Found");
else if (-1 == (fd = open(ss, O_RDONLY))) error_time(403, "Forbidden");
else if (!S_ISDIR(st.st_mode)) {
@@ -139,7 +156,7 @@
mime(ss), (long long)st.st_size, rfc1123(buf, st.st_mtime)));
free(ss);
xsendfile(fd, outfd);
- } else if (ss[strlen(ss)-1]!='/' && strcmp(ss, ".")) {
+ } else if (ss[strlen(ss)-1]!='/') {
header_time(302, "Found", path = xmprintf("Location: %s/\r\n", word[1]));
free(path);
} else {
@@ -150,7 +167,7 @@
path = ss;
ss = "index.html";
path = xmprintf("%s%s", path, ss);
- if (!stat(path, &st) || !S_ISREG(st.st_mode)) i = -1;
+ if (stat(path, &st) || !S_ISREG(st.st_mode)) i = -1;
else if (-1 == (i = open(path, O_RDONLY))) error_time(403, "Forbidden");
free(path);
if (i != -1) {
diff -Nru toybox-0.8.8+dfsg/toys/net/netcat.c toybox-0.8.9+dfsg/toys/net/netcat.c
--- toybox-0.8.8+dfsg/toys/net/netcat.c 2022-08-12 07:58:03.000000000 +0000
+++ toybox-0.8.9+dfsg/toys/net/netcat.c 2023-01-10 19:24:45.000000000 +0000
@@ -2,11 +2,9 @@
*
* Copyright 2007 Rob Landley %s
\n");
}
@@ -65,12 +65,14 @@
sprintf(toybuf, "Toybox %s command help", toybox_version);
xprintf("\n\n", t->name, t->name);
toys.which = t;
- show_help(stdout, !FLAG(u)+(!!toys.argv[1]<<1));
+ show_help(stdout, !FLAG(u)+(!!toys.argv[1]<<1)+(!!FLAG(h)<<2));
if (FLAG(h)) xprintf("
%s
\n"); else if (!FLAG(u)) { memset(toybuf, '-', 78); diff -Nru toybox-0.8.8+dfsg/toys/other/hexedit.c toybox-0.8.9+dfsg/toys/other/hexedit.c --- toybox-0.8.8+dfsg/toys/other/hexedit.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/hexedit.c 2023-01-10 19:24:45.000000000 +0000 @@ -181,7 +181,7 @@ size_t len = strlen(TT.search); for (; pos >= 0; pos--) { - if (!memcmp(TT.data+pos, TT.search, len)) { + if (!smemcmp(TT.data+pos, TT.search, len)) { TT.pos = pos; return; } diff -Nru toybox-0.8.8+dfsg/toys/other/i2ctools.c toybox-0.8.9+dfsg/toys/other/i2ctools.c --- toybox-0.8.8+dfsg/toys/other/i2ctools.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/i2ctools.c 2023-01-10 19:24:45.000000000 +0000 @@ -11,10 +11,10 @@ * TODO: i2cget non-byte modes? default to current read address? * TODO: i2cset -r? -m MASK? c/s modes, p mode modifier? -USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_BIN)) -USE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_BIN)) -USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_BIN)) +USE_I2CDETECT(NEWTOY(i2cdetect, ">3aFlqry[!qr]", TOYFLAG_USR|TOYFLAG_SBIN)) +USE_I2CDUMP(NEWTOY(i2cdump, "<2>2fy", TOYFLAG_USR|TOYFLAG_SBIN)) +USE_I2CGET(NEWTOY(i2cget, "<3>3fy", TOYFLAG_USR|TOYFLAG_SBIN)) +USE_I2CSET(NEWTOY(i2cset, "<4fy", TOYFLAG_USR|TOYFLAG_SBIN)) config I2CDETECT bool "i2cdetect" diff -Nru toybox-0.8.8+dfsg/toys/other/losetup.c toybox-0.8.9+dfsg/toys/other/losetup.c --- toybox-0.8.8+dfsg/toys/other/losetup.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/losetup.c 2023-01-10 19:24:45.000000000 +0000 @@ -71,6 +71,16 @@ } close(cfd); } + if (CFG_TOYBOX_ON_ANDROID && device) { + // ANDROID SPECIFIC: /dev is not devtmpfs, instead an userspace daemon + // ueventd is responsible for creating the loop devices under /dev. + // Wait for the uevent to be processed to avoid race. + long long timeout = millitime() + 5000; + do { + if (!access(device, F_OK) || errno != ENOENT) break; + msleep(20); + } while (millitime() < timeout); + } } if (device) lfd = open(device, TT.openflags); diff -Nru toybox-0.8.8+dfsg/toys/other/lsusb.c toybox-0.8.9+dfsg/toys/other/lsusb.c --- toybox-0.8.8+dfsg/toys/other/lsusb.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/lsusb.c 2023-01-10 19:24:45.000000000 +0000 @@ -109,18 +109,22 @@ struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and) { char *path = "/etc:/vendor:/usr/share/misc"; - struct string_list *sl; + struct string_list *sl = 0; FILE *fp; char *s, *ss, *sss; struct dev_ids *ids = 0, *new; - int fd = -1, tick = 0; + int fd = -1; // Open compressed or uncompressed file - sprintf(toybuf, "%s.gz", name); - if ((sl = find_in_path(path, toybuf))) { - signal(SIGCHLD, SIG_IGN); - xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); - } else if ((sl = find_in_path(path, name))) fd = xopen(sl->str,O_RDONLY); + signal(SIGCHLD, SIG_IGN); + s = TT.i; + if (!s) { + sprintf(toybuf, "%s.gz", name); + if ((sl = find_in_path(path, toybuf)) || (sl = find_in_path(path, name))) + s = sl->str; + } + if (s && strend(s, ".gz")) xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); + else if (s) fd = xopen(s, O_RDONLY); llist_traverse(sl, free); if (fd == -1) return 0; @@ -132,8 +136,7 @@ if (strstart(&ss, "C ") && and) { *and = ids; and = 0; - tick++; - } + } fd = estrtol(sss = ss, &ss, 16); if (ss>sss && *ss++==' ') { while (isspace(*ss)) ss++; diff -Nru toybox-0.8.8+dfsg/toys/other/mcookie.c toybox-0.8.9+dfsg/toys/other/mcookie.c --- toybox-0.8.8+dfsg/toys/other/mcookie.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/mcookie.c 2023-01-10 19:24:45.000000000 +0000 @@ -29,7 +29,7 @@ long long *ll = (void *)toybuf; if (FLAG(V)) return (void)puts("mcookie from toybox"); - xgetrandom(toybuf, 16, 0); + xgetrandom(toybuf, 16); if (FLAG(v)) fputs("Got 16 bytes from xgetrandom()\n", stderr); xprintf("%016llx%06llx\n", ll[0], ll[1]); } diff -Nru toybox-0.8.8+dfsg/toys/other/modinfo.c toybox-0.8.9+dfsg/toys/other/modinfo.c --- toybox-0.8.8+dfsg/toys/other/modinfo.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/modinfo.c 2023-01-10 19:24:45.000000000 +0000 @@ -44,6 +44,7 @@ char *buf = 0, *end, *modinfo_tags[] = { "license", "author", "description", "firmware", "alias", "srcversion", "depends", "retpoline", "intree", "name", "vermagic", "parm", "parmtype", + "scmversion", }; if (-1 != (fd = open(full_name, O_RDONLY))) { diff -Nru toybox-0.8.8+dfsg/toys/other/mountpoint.c toybox-0.8.9+dfsg/toys/other/mountpoint.c --- toybox-0.8.8+dfsg/toys/other/mountpoint.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/mountpoint.c 2023-01-10 19:24:45.000000000 +0000 @@ -34,7 +34,7 @@ struct stat st1, st2; char *arg = *toys.optargs; - if (lstat(arg, &st1)) perror_exit_raw(arg); + if (lstat(arg, &st1)) (FLAG(q) ? die : perror_exit_raw)(arg); if (FLAG(x)) { if (!S_ISBLK(st1.st_mode)) die("block device"); @@ -55,7 +55,7 @@ // inode are the same, it's probably "/". This misses --bind mounts from // elsewhere in the same filesystem, but so does the other one and in the // absence of a spec I guess that's the expected behavior? - toys.exitval = !same_file(&st1, &st2); + toys.exitval = !(st1.st_dev != st2.st_dev || st1.st_ino == st2.st_ino); if (FLAG(d)) printf("%u:%u\n", dev_major(st1.st_dev), dev_minor(st1.st_dev)); else if (!FLAG(q)) printf("%s is %sa mountpoint\n", *toys.optargs, toys.exitval ? "not " : ""); diff -Nru toybox-0.8.8+dfsg/toys/other/nbd_client.c toybox-0.8.9+dfsg/toys/other/nbd_client.c --- toybox-0.8.8+dfsg/toys/other/nbd_client.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/nbd_client.c 2023-01-10 19:24:45.000000000 +0000 @@ -7,28 +7,26 @@ // This little dance is because a NEWTOY with - in the name tries to do // things like prototype "nbd-client_main" which isn't a valid symbol. So // we hide the underscore name and OLDTOY the name we want. -USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", 0)) +USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3b#<1>4294967295=4096ns", 0)) USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN)) config NBD_CLIENT bool "nbd-client" - depends on TOYBOX_FORK default y help - usage: nbd-client [-ns] HOST PORT DEVICE + usage: nbd-client [-ns] [-b BLKSZ] HOST PORT DEVICE - -n Do not fork into background + -b Block size (default 4096) + -n Do not daemonize -s nbd swap support (lock server into memory) */ /* TODO: - usage: nbd-client [-sSpn] [-b BLKSZ] [-t SECS] [-N name] HOST PORT DEVICE + usage: nbd-client [-Sp] [-t SECS] [-N name] HOST PORT DEVICE - -b block size -t timeout in seconds -S sdp -p persist - -n nofork -d DEVICE -c DEVICE */ @@ -37,78 +35,88 @@ #include "toys.h" #include+GLOBALS( + long b; + + int nbd; +) + +static void sig_cleanup(int catch) +{ + // Flush on the way out + ioctl(TT.nbd, NBD_CLEAR_QUE); + ioctl(TT.nbd, NBD_CLEAR_SOCK); + _exit(catch ? 128+catch : 0); +} + void nbd_client_main(void) { - int sock = -1, nbd, flags; + int sock = -1, flags, temp; unsigned long timeout = 0; char *host=toys.optargs[0], *port=toys.optargs[1], *device=toys.optargs[2]; - uint64_t devsize; + unsigned long long devsize; + + // Daemonize in a nommu-friendly way, but retain stderr + if (toys.stacktop && !FLAG(n)) { + dup2(2, 222); + xvdaemon(); + } + dup2(222, 2); + close(222); - // Repeat until spanked + TT.nbd = xopen(device, O_RDWR); + xsignal(SIGINT, sig_cleanup); + xsignal(SIGTERM, sig_cleanup); - nbd = xopen(device, O_RDWR); for (;;) { - int temp; - // Find and connect to server - sock = xconnectany(xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0)); temp = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int)); // Read login data - xreadall(sock, toybuf, 152); - if (memcmp(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16)) + if (smemcmp(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16)) error_exit("bad login %s:%s", host, port); - devsize = SWAP_BE64(*(uint64_t *)(toybuf+16)); + devsize = SWAP_BE64(*(unsigned long long *)(toybuf+16)); flags = SWAP_BE32(*(int *)(toybuf+24)); - // Set 4k block size. Everything uses that these days. - ioctl(nbd, NBD_SET_BLKSIZE, 4096); - ioctl(nbd, NBD_SET_SIZE_BLOCKS, devsize/4096); - ioctl(nbd, NBD_CLEAR_SOCK); - - // If the sucker was exported read only, respect that locally. - temp = (flags & 2) ? 1 : 0; - xioctl(nbd, BLKROSET, &temp); + // Use 4k block size + ioctl(TT.nbd, NBD_SET_BLKSIZE, TT.b); + ioctl(TT.nbd, NBD_SET_SIZE_BLOCKS, devsize/TT.b); // rounds down + ioctl(TT.nbd, NBD_CLEAR_SOCK); + + // Locally respect read only exports + flags = (flags>>1)&1; + xioctl(TT.nbd, BLKROSET, &flags); - if (timeout && ioctl(nbd, NBD_SET_TIMEOUT, timeout)<0) break; - if (ioctl(nbd, NBD_SET_SOCK, sock) < 0) break; + if (timeout && ioctl(TT.nbd, NBD_SET_TIMEOUT, timeout)<0) break; + if (ioctl(TT.nbd, NBD_SET_SOCK, sock) < 0) break; - if (toys.optflags & FLAG_s) mlockall(MCL_CURRENT|MCL_FUTURE); + if (FLAG(s)) mlockall(MCL_CURRENT|MCL_FUTURE); // Open the device to force reread of the partition table. - if ((toys.optflags & FLAG_n) || !xfork()) { + if (!CFG_TOYBOX_FORK || !xfork()) { char *s = strrchr(device, '/'); int i; + // Give device up to 10 seconds to come up sprintf(toybuf, "/sys/block/%.32s/pid", s ? s+1 : device); - // Is it up yet? (Give it 10 seconds.) for (i=0; i<100; i++) { - temp = open(toybuf, O_RDONLY); - if (temp == -1) msleep(100); - else { - close(temp); - break; - } + if (access(toybuf, F_OK)) break; + msleep(100); } close(open(device, O_RDONLY)); - if (!(toys.optflags & FLAG_n)) exit(0); + if (CFG_TOYBOX_FORK) _exit(0); } - // Daemonize here. - - if (daemon(0,0)) perror_exit("daemonize"); - // Process NBD requests until further notice. - if (ioctl(nbd, NBD_DO_IT)>=0 || errno==EBADR) break; + if (ioctl(TT.nbd, NBD_DO_IT)>=0 || errno==EBADR) break; close(sock); + ioctl(TT.nbd, NBD_CLEAR_QUE); } // Flush queue and exit. - ioctl(nbd, NBD_CLEAR_QUE); - ioctl(nbd, NBD_CLEAR_SOCK); - if (CFG_TOYBOX_FREE) close(nbd); + if (CFG_TOYBOX_FREE) close(TT.nbd); } diff -Nru toybox-0.8.8+dfsg/toys/other/nbd_server.c toybox-0.8.9+dfsg/toys/other/nbd_server.c --- toybox-0.8.8+dfsg/toys/other/nbd_server.c 1970-01-01 00:00:00.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/nbd_server.c 2023-01-10 19:24:45.000000000 +0000 @@ -0,0 +1,88 @@ +/* nbd-server.c - network block device server + * + * Copyright 2022 Rob Landley + * + * Not in SUSv4. + * + * See https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +// Work around dash in name trying to put - in function name. +USE_NBD_SERVER(NEWTOY(nbd_server, "<1>1r", 0)) +USE_NBD_SERVER(OLDTOY(nbd-server, nbd_server, TOYFLAG_USR|TOYFLAG_BIN)) + +config NBD_SERVER + bool "nbd-server" + default y + help + usage: nbd-server [-r] FILE + + Serve a Network Block Device from FILE on stdin/out (ala inetd). + + -r Read only export +*/ + +// TODO: -r, block size, exit signal? + +#define FOR_nbd_server +#include "toys.h" + +static int copy_loop(int from, int to, unsigned len) +{ + int try, rc = 0; + + errno = 0; + while (len) { + xreadall(from, toybuf, try = len>4096 ? 4096 : len); + if (!rc && try != writeall(to, toybuf, try)) rc = errno; + len -= try; + } + + return rc; +} + +void nbd_server_main(void) +{ + unsigned long long *ll = (void *)toybuf, offset, handle; + unsigned short *ss = (void *)toybuf; + unsigned *uu = (void *)toybuf, type, length; + int fd = xopen(*toys.optargs, O_RDWR*!FLAG(r)); + + type = 1; + setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &type, sizeof(int)); + + // Send original recipe negotiation, with device length and flags + memcpy(toybuf, "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53", 16); + ll[2] = SWAP_BE64(fdlength(fd)); + uu[6] = SWAP_BE32(5+2*!!FLAG(r)); // has flags, can flush, maybe read only + xwrite(1, toybuf, 152); + + // Simple loop, handles one request at a time with "simple" reply. + for (;;) { + // Fetch request into toybuf + xreadall(0, toybuf, 28); + if (SWAP_BE32(*uu) != 0x25609513) break; + type = SWAP_BE16(ss[3]); + handle = SWAP_BE64(ll[1]); + offset = SWAP_BE64(ll[2]); + length = SWAP_BE32(uu[6]); + + // type 0 = read, 1 = write, 2 = disconnect, 3 = flush + if (type==2 || type>3) break; // disconnect + if (type==3) { // flush + if (fdatasync(fd)) uu[1] = SWAP_BE32(errno); + } else { + xlseek(fd, offset, SEEK_SET); + if (type==1) { // write + uu[1] = copy_loop(0, fd, length); + ll[1] = SWAP_BE64(handle); + } else uu[1] = 0; // read never reports errors because send header first + } + + // Simple reply in toybuf (handle stays put) + *uu = SWAP_BE32(0x67446698); + xwrite(1, toybuf, 16); + + // Append read payload + if (!type) if (copy_loop(fd, 1, length)) break; + } +} diff -Nru toybox-0.8.8+dfsg/toys/other/nsenter.c toybox-0.8.9+dfsg/toys/other/nsenter.c --- toybox-0.8.8+dfsg/toys/other/nsenter.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/nsenter.c 2023-01-10 19:24:45.000000000 +0000 @@ -40,7 +40,7 @@ Each namespace can take an optional argument, a persistent mountpoint usable by the nsenter command to add new processes to that the namespace. (Specify - multiple namespaces to unshare seperately, ala -c -i -m because -cim is -c + multiple namespaces to unshare separately, ala -c -i -m because -cim is -c with persistent mount "im".) config NSENTER diff -Nru toybox-0.8.8+dfsg/toys/other/openvt.c toybox-0.8.9+dfsg/toys/other/openvt.c --- toybox-0.8.8+dfsg/toys/other/openvt.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/openvt.c 2023-01-10 19:24:45.000000000 +0000 @@ -6,7 +6,7 @@ * No Standard USE_OPENVT(NEWTOY(openvt, "^<1c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) -USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_CHVT(NEWTOY(chvt, "<1>1", TOYFLAG_USR|TOYFLAG_BIN)) USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT)) config OPENVT diff -Nru toybox-0.8.8+dfsg/toys/other/pmap.c toybox-0.8.9+dfsg/toys/other/pmap.c --- toybox-0.8.8+dfsg/toys/other/pmap.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/pmap.c 2023-01-10 19:24:45.000000000 +0000 @@ -17,7 +17,7 @@ Report the memory map of a process or processes. - -q Show full paths + -p Show full paths -q Do not show header or footer -x Show the extended format */ diff -Nru toybox-0.8.8+dfsg/toys/other/pwgen.c toybox-0.8.9+dfsg/toys/other/pwgen.c --- toybox-0.8.8+dfsg/toys/other/pwgen.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/pwgen.c 2023-01-10 19:24:45.000000000 +0000 @@ -51,7 +51,7 @@ for (jj = 0; jj 102 less likely if (FLAG(s)) randbuf[rand] = 0; diff -Nru toybox-0.8.8+dfsg/toys/other/readelf.c toybox-0.8.9+dfsg/toys/other/readelf.c --- toybox-0.8.8+dfsg/toys/other/readelf.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/readelf.c 2023-01-10 19:24:45.000000000 +0000 @@ -355,7 +355,7 @@ char *hdr = TT.elf; int type, machine, version, flags, entry, ehsize, phnum, shstrndx, i, j, w; - if (TT.size < 45 || memcmp(hdr, "\177ELF", 4)) + if (TT.size < 45 || smemcmp(hdr, "\177ELF", 4)) return error_msg("%s: not ELF", TT.f); TT.bits = hdr[4] - 1; @@ -453,7 +453,7 @@ s.entsize, sh_flags, s.link, s.info, s.addralign); } } - if (FLAG(S) && TT.shnum) + if (FLAG(S) && TT.shnum) printf("Key:\n (W)rite, (A)lloc, e(X)ecute, (M)erge, (S)trings, (I)nfo\n" " (L)ink order, (O)S, (G)roup, (T)LS, (C)ompressed, x=unknown\n"); diff -Nru toybox-0.8.8+dfsg/toys/other/readlink.c toybox-0.8.9+dfsg/toys/other/readlink.c --- toybox-0.8.8+dfsg/toys/other/readlink.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/readlink.c 2023-01-10 19:24:45.000000000 +0000 @@ -2,9 +2,8 @@ * * Copyright 2007 Rob Landley -// -ef positions match ABS_FILE ABS_PATH -USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_REALPATH(OLDTOY(realpath, readlink, TOYFLAG_USR|TOYFLAG_BIN)) +USE_READLINK(NEWTOY(readlink, "<1vnf(canonicalize)emqz[-mef][-qv]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_REALPATH(NEWTOY(realpath, "<1(relative-base):R(relative-to):s(no-symlinks)LPemqz[-Ps][-LP][-me]", TOYFLAG_USR|TOYFLAG_BIN)) config READLINK bool "readlink" @@ -20,36 +19,124 @@ -f Full path (fail if directory missing) -m Ignore missing entries, show where it would be -n No trailing newline - -q Quiet (no output, just error code) + -q Quiet (no error messages) + -z NUL instead of newline config REALPATH bool "realpath" default y help - usage: realpath FILE... + usage: realpath [-LPemqsz] [--relative-base DIR] [-R DIR] FILE... Display the canonical absolute pathname + + -R Show ../path relative to DIR (--relative-to) + -L Logical path (resolve .. before symlinks) + -P Physical path (default) + -e Canonical path to existing entry (fail if missing) + -m Ignore missing entries, show where it would be + -q Quiet (no error messages) + -s Don't expand symlinks + -z NUL instead of newline + --relative-base If path under DIR trim off prefix */ -#define FOR_readlink +/* TODO +# relative-to is affected by flags +$ realpath --relative-to=nothing/potato . +realpath: nothing/potato: No such file or directory +$ realpath -m --relative-to=nothing/potato . +../.. + +# -L and -s are similar but not the same +$ realpath -s --relative-to=. ccc +ccc +$ realpath -L --relative-to=. ccc +../../mcm/ccc +*/ + + + +#define FOR_realpath #define FORCE_FLAGS +#define TT this.readlink // workaround: first FOR_ doesn't match filename #include "toys.h" -void readlink_main(void) +GLOBALS( + char *R, *relative_base; +) + +// test TT.relative_base -RsmLP +// Trim .. out early for -s and -L. TODO: in place in the input string. + +static char *resolve(char *arg) +{ + int flags = FLAG(e) ? ABS_FILE : FLAG(m) ? 0 : ABS_PATH; + char *s, *ss = 0, *dd = 0; + + if (FLAG(s)) flags |= ABS_KEEP; + else if (FLAG(L)) arg = dd = xabspath(arg, ABS_KEEP); + if (!(s = xabspath(arg, flags)) && !FLAG(q)) perror_msg("%s", arg); + free(dd); + + // Trim off this prefix if path under here + + if (TT.relative_base) { + ss = s; + if (strstart(&ss, TT.relative_base) && (!*ss || *ss=='/')) { + if (*ss=='/') ss++; + ss = xstrdup(!*ss ? "." : ss); + } else ss = 0; + } else if (TT.R) ss = relative_path(TT.R, s, 0); + if (ss) { + free(s); + s = ss; + } + + return s; +} + +// Resolve command line arguments that can't take part in their own resolution +static char *presolve(char **s) +{ + char *ss = *s; + + if (ss) { + *s = 0; + if (!(*s = resolve(ss))) xexit(); + } + + return ss; +} + +// Uses realpath flag context: flags (1 = resolve, 2 = -n) +static void do_paths(int flags) { char **arg, *s; - if (toys.which->name[3]=='l') toys.optflags |= FLAG_f; + if (!presolve(&TT.relative_base)) presolve(&TT.R); + for (arg = toys.optargs; *arg; arg++) { - // Calculating full canonical path? - // Take advantage of flag positions: m = 0, f = ABS_PATH, e = ABS_FILE - if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m)) - s = xabspath(*arg, toys.optflags&(FLAG_f|FLAG_e)); - else s = xreadlink(*arg); - - if (s) { - if (!FLAG(q)) xprintf("%s%s", s, (FLAG(n) && !arg[1]) ? "" : "\n"); - free(s); - } else toys.exitval = 1; + if (!(s = (flags&1) ? resolve(*arg) : xreadlink(*arg))) toys.exitval = 1; + else xprintf(((flags&2) && !arg[1]) ? "%s" : "%s%c", s, '\n'*!FLAG(z)); + free(s); } } + +void realpath_main(void) +{ + do_paths(1); +} + +#define FOR_readlink +#include "generated/flags.h" + +// Convert readlink flag context to realpath (feeding in -nf separately) +void readlink_main(void) +{ + int nf = (toys.optflags/FLAG_f)|!!(FLAG(m)|FLAG(e)); + + toys.optflags &= FLAG_f-1; + if (!FLAG(v)) toys.optflags |= FLAG_q; + do_paths(nf); +} diff -Nru toybox-0.8.8+dfsg/toys/other/shred.c toybox-0.8.9+dfsg/toys/other/shred.c --- toybox-0.8.8+dfsg/toys/other/shred.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/shred.c 2023-01-10 19:24:45.000000000 +0000 @@ -90,7 +90,7 @@ throw = sizeof(toybuf); if (FLAG(x) && len-pos < throw) throw = len-pos; - if (iter != TT.n) xgetrandom(toybuf, throw, 0); + if (iter != TT.n) xgetrandom(toybuf, throw); if (throw != writeall(fd, toybuf, throw)) perror_msg_raw(*try); pos += throw; } diff -Nru toybox-0.8.8+dfsg/toys/other/swapoff.c toybox-0.8.9+dfsg/toys/other/swapoff.c --- toybox-0.8.8+dfsg/toys/other/swapoff.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/swapoff.c 2023-01-10 19:24:45.000000000 +0000 @@ -2,20 +2,40 @@ * * Copyright 2012 Elie De Brauwer -USE_SWAPOFF(NEWTOY(swapoff, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT)) +USE_SWAPOFF(NEWTOY(swapoff, "<1>1av", TOYFLAG_SBIN|TOYFLAG_NEEDROOT)) config SWAPOFF bool "swapoff" default y help - usage: swapoff swapregion + usage: swapoff FILE - Disable swapping on a given swapregion. + Disable swapping on a device or file. */ +#define FOR_swapoff #include "toys.h" +static void xswapoff(char *str) +{ + if (FLAG(v)) printf("swapoff %s", str); + if (swapoff(str)) perror_msg("failed to remove swaparea"); +} + void swapoff_main(void) { - if (swapoff(toys.optargs[0])) perror_exit("failed to remove swaparea"); + char *ss, *line, **args; + FILE *fp; + + if (FLAG(a) && (fp = fopen("/proc/swaps", "r"))) { + while ((line = xgetline(fp))) { + if (*line != '/' || !(ss = strchr(line, ' '))) continue; + *ss = 0; + octal_deslash(line); + xswapoff(line); + free(line); + } + fclose(fp); + } + for (args = toys.optargs; *args; args++) xswapoff(*args); } diff -Nru toybox-0.8.8+dfsg/toys/other/taskset.c toybox-0.8.9+dfsg/toys/other/taskset.c --- toybox-0.8.8+dfsg/toys/other/taskset.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/taskset.c 2023-01-10 19:24:45.000000000 +0000 @@ -127,7 +127,7 @@ char *ss; while ((de = readdir(dd))) { - if (memcmp(de->d_name, "cpu", 3)) continue; + if (smemcmp(de->d_name, "cpu", 3)) continue; for (ss = de->d_name+3; isdigit(*ss); ss++); if (!*ss) nproc++; } diff -Nru toybox-0.8.8+dfsg/toys/other/timeout.c toybox-0.8.9+dfsg/toys/other/timeout.c --- toybox-0.8.8+dfsg/toys/other/timeout.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/timeout.c 2023-01-10 19:24:45.000000000 +0000 @@ -34,6 +34,7 @@ struct pollfd pfd; sigjmp_buf sj; + int fds[2], pid; ) static void handler(int sig) @@ -46,29 +47,35 @@ return ts->tv_sec*1000+ts->tv_nsec/1000000; } +static void callback(char *argv[]) +{ + xsignal(SIGCHLD, SIG_DFL); + if (!FLAG(foreground)) setpgid(0, 0); +} + void timeout_main(void) { - int fds[] = {0, -1}, ii, ms, nextsig, pid; + int ii, ms, nextsig = SIGTERM; struct timespec tts, kts; // Use same ARGFAIL value for any remaining parsing errors toys.exitval = 125; xparsetimespec(*toys.optargs, &tts); if (TT.k) xparsetimespec(TT.k, &kts); - - nextsig = SIGTERM; - if (TT.s && -1 == (nextsig = sig_to_num(TT.s))) - error_exit("bad -s: '%s'", TT.s); - - if (!FLAG(foreground)) setpgid(0, 0); + if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s); toys.exitval = 0; TT.pfd.events = POLLIN; + TT.fds[1] = -1; if (sigsetjmp(TT.sj, 1)) goto done; xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP); - pid = xpopen_both(toys.optargs+1, FLAG(i) ? fds : 0); - if (!FLAG(i)) xpipe(fds); - TT.pfd.fd = fds[1]; + + TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback); + xsignal(SIGTTIN, SIG_IGN); + xsignal(SIGTTOU, SIG_IGN); + xsignal(SIGTSTP, SIG_IGN); + if (!FLAG(i)) xpipe(TT.fds); + TT.pfd.fd = TT.fds[1]; ms = nantomil(&tts); for (;;) { if (1 != xpoll(&TT.pfd, 1, ms)) { @@ -76,7 +83,7 @@ perror_msg("sending signal %s to command %s", num_to_sig(nextsig), toys.optargs[1]); toys.exitval = (nextsig==9) ? 137 : 124; - kill(pid, nextsig); + kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig); if (!TT.k || nextsig==SIGKILL) break; nextsig = SIGKILL; ms = nantomil(&kts); @@ -85,7 +92,7 @@ } if (TT.pfd.revents&POLLIN) { errno = 0; - if (1>(ii = read(fds[1], toybuf, sizeof(toybuf)))) { + if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) { if (errno==EINTR) continue; break; } @@ -95,7 +102,7 @@ } done: xsignal(SIGCHLD, SIG_DFL); - ii = xpclose_both(pid, fds); + ii = xpclose_both(TT.pid, TT.fds); if (FLAG(preserve_status) || !toys.exitval) toys.exitval = ii; } diff -Nru toybox-0.8.8+dfsg/toys/other/usleep.c toybox-0.8.9+dfsg/toys/other/usleep.c --- toybox-0.8.8+dfsg/toys/other/usleep.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/usleep.c 2023-01-10 19:24:45.000000000 +0000 @@ -2,7 +2,7 @@ * * Copyright 2012 Elie De Brauwer -USE_USLEEP(NEWTOY(usleep, "<1", TOYFLAG_BIN)) +USE_USLEEP(NEWTOY(usleep, "<1>1", TOYFLAG_BIN)) config USLEEP bool "usleep" diff -Nru toybox-0.8.8+dfsg/toys/other/watchdog.c toybox-0.8.9+dfsg/toys/other/watchdog.c --- toybox-0.8.8+dfsg/toys/other/watchdog.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/watchdog.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,7 +4,7 @@ * * See kernel.org/doc/Documentation/watchdog/watchdog-api.txt -USE_WATCHDOG(NEWTOY(watchdog, "<1>1Ft#=4<1T#=60<1", TOYFLAG_NEEDROOT|TOYFLAG_BIN)) +USE_WATCHDOG(NEWTOY(watchdog, "<1>1Ft#=4<1T#=60<1", TOYFLAG_NEEDROOT|TOYFLAG_SBIN)) config WATCHDOG bool "watchdog" diff -Nru toybox-0.8.8+dfsg/toys/other/xxd.c toybox-0.8.9+dfsg/toys/other/xxd.c --- toybox-0.8.8+dfsg/toys/other/xxd.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/other/xxd.c 2023-01-10 19:24:45.000000000 +0000 @@ -10,7 +10,7 @@ * xxd -p "plain" output: * "4c696e75782076657273696f6e20342e392e302d342d616d643634202864" -USE_XXD(NEWTOY(xxd, ">1c#<0>256l#o#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_XXD(NEWTOY(xxd, ">1c#<0>256l#o#g#<0=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN)) config XXD bool "xxd" @@ -26,7 +26,7 @@ -i Output include file (CSV hex bytes, plus C header/footer if not stdin) -l n Limit of n bytes before stopping (default is no limit) -o n Add n to display offset - -p Plain hexdump (30 bytes/line, no grouping) + -p Plain hexdump (30 bytes/line, no grouping. With -c 0 no wrap/group) -r Reverse operation: turn a hexdump into a binary file -s n Skip to offset n */ @@ -42,7 +42,7 @@ { long long pos = 0; long long limit = TT.l; - int i, len, space; + int i, len, space, c = TT.c ? : sizeof(toybuf); if (FLAG(s)) { xlseek(fd, TT.s, SEEK_SET); @@ -51,15 +51,16 @@ } while (0<(len = readall(fd, toybuf, - (limit && limit-pos =' ' && toybuf[i]<='~') ? toybuf[i] : '.'); } - putchar('\n'); + if (TT.c || !FLAG(p)) putchar('\n'); } + if (!TT.c && FLAG(p)) putchar('\n'); if (len<0) perror_exit("read"); } @@ -105,7 +107,7 @@ static void do_xxd_reverse(int fd, char *name) { FILE *fp = xfdopen(xdup(fd), "r"); - long long current_pos = 0; + long long pos, current_pos = 0; int tmp; // -ri is a very easy special case. @@ -115,21 +117,17 @@ // Each line of a regular hexdump starts with an offset/address. // Each line of a plain hexdump just goes straight into the bytes. - if (!FLAG(p)) { - long long pos; - - if (fscanf(fp, "%llx: ", &pos) == 1) { - if (pos != current_pos && fseek(stdout, pos, SEEK_SET) != 0) { - // TODO: just write out zeros if non-seekable? - perror_exit("%s: seek failed", name); - } + if (!FLAG(p) && fscanf(fp, "%llx: ", &pos) == 1) { + if (pos != current_pos && fseek(stdout, pos, SEEK_SET)) { + // TODO: just write out zeros if non-seekable? + perror_exit("%s: seek failed", name); } } // A plain hexdump can have as many bytes per line as you like, // but a non-plain hexdump assumes garbage after it's seen the // specified number of bytes. - while (FLAG(p) || col < TT.c) { + while (FLAG(p) || !TT.c || col < TT.c) { int n1, n2; // If we're at EOF or EOL or we read some non-hex... @@ -159,10 +157,9 @@ void xxd_main(void) { - if (!TT.c) TT.c = FLAG(i) ? 12 : 16; - // Plain style is 30 bytes/line, no grouping. - if (FLAG(p)) TT.c = TT.g = 30; + if (!FLAG(c)) TT.c = FLAG(p) ? 30 : FLAG(i) ? 12 : 16; + if (FLAG(p) && !FLAG(g)) TT.g = TT.c; loopfiles(toys.optargs, FLAG(r) ? do_xxd_reverse : (FLAG(i) ? do_xxd_include : do_xxd)); diff -Nru toybox-0.8.8+dfsg/toys/pending/diff.c toybox-0.8.9+dfsg/toys/pending/diff.c --- toybox-0.8.8+dfsg/toys/pending/diff.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/diff.c 2023-01-10 19:24:45.000000000 +0000 @@ -8,18 +8,19 @@ * * Deviations from posix: always does -u -USE_DIFF(NEWTOY(diff, "<2>2(unchanged-line-format):;(old-line-format):;(new-line-format):;(color)(strip-trailing-cr)B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)S(starting-file):L(label)*N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) +USE_DIFF(NEWTOY(diff, "<2>2(unchanged-line-format):;(old-line-format):;(new-line-format):;(color)(strip-trailing-cr)B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)S(starting-file):F(show-function-line):;L(label)*N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) config DIFF bool "diff" default n help - usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2 + usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] [-F REGEX ] FILE1 FILE2 -a Treat all files as text -b Ignore changes in the amount of whitespace -B Ignore changes whose lines are all blank -d Try hard to find a smaller set of changes + -F Show the most recent line matching the regex -i Ignore case differences -L Use LABEL instead of the filename in the unified header -N Treat absent files as empty @@ -48,7 +49,7 @@ GLOBALS( long U; struct arg_list *L; - char *S, *new_line_format, *old_line_format, *unchanged_line_format; + char *F, *S, *new_line_format, *old_line_format, *unchanged_line_format; int dir_num, size, is_binary, differ, change, len[2], *offset[2]; struct stat st[2]; @@ -383,6 +384,33 @@ return create_j_vector(); } +static void print_line_matching_regex(int a, regex_t *reg, int *off_set, FILE *fp) { + int i = 0, j = 0, line_buf_size = 100, cc = 0; + char* line = xzalloc(line_buf_size * sizeof(char)); + for (i = a; a > 0; --i) { + int line_len = 0; + if (fseek(fp, off_set[i - 1], SEEK_SET)) perror_exit("fseek failed"); + for (j = 0; j < (off_set[i] - off_set[i - 1]); j++) { + cc = fgetc(fp); + if (cc == EOF || cc == '\n') { + break; + } + ++line_len; + if (line_len >= line_buf_size) { + line_buf_size = line_buf_size * 11 / 10; + line = xrealloc(line, line_buf_size*sizeof(char)); + } + line[j] = cc; + } + line[line_len] = '\0'; + if (!regexec0(reg, line, line_len, 0, NULL, 0)) { + printf(" %s", line); + break; + } + } + free(line); +} + static void print_diff(int a, int b, char c, int *off_set, FILE *fp) { int i, j, cc, cl; @@ -557,12 +585,17 @@ struct diff *d; struct arg_list *llist = TT.L; int *J; + regex_t reg; TT.offset[0] = TT.offset[1] = NULL; J = diff(files); if (!J) return; //No need to compare, have to status only + if (TT.F) { + xregcomp(®, TT.F, 0); + } + d = xzalloc(size *sizeof(struct diff)); do { ignore_white = 0; @@ -656,6 +689,9 @@ else putchar(' '); printf("@@"); if (FLAG(color)) printf("\e[0m"); + if (TT.F) { + print_line_matching_regex(ptr1->suff-1, ®, TT.offset[0], TT.file[0].fp); + } putchar('\n'); } diff -Nru toybox-0.8.8+dfsg/toys/pending/getopt.c toybox-0.8.9+dfsg/toys/pending/getopt.c --- toybox-0.8.8+dfsg/toys/pending/getopt.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/getopt.c 2023-01-10 19:24:45.000000000 +0000 @@ -22,6 +22,7 @@ #define FOR_getopt #include "toys.h" +#include // Everything else uses lib/args.c GLOBALS( struct arg_list *l; diff -Nru toybox-0.8.8+dfsg/toys/pending/git.c toybox-0.8.9+dfsg/toys/pending/git.c --- toybox-0.8.8+dfsg/toys/pending/git.c 1970-01-01 00:00:00.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/git.c 2023-01-10 19:24:45.000000000 +0000 @@ -0,0 +1,658 @@ +/* git.c - A minimal git clone + * + * Copyright 2022 Moritz C. Weber + * + * See https://git-scm.com/docs/git-init + * https://git-scm.com/docs/git-remote + * https://git-scm.com/docs/git-fetch + * https://git-scm.com/docs/git-checkout + * https://git-scm.com/docs/pack-format + * https://git-scm.com/docs/index-format + * https://www.alibabacloud.com/blog/a-detailed-explanation-of-the-underlying-data-structures-and-principles-of-git_597391 + * https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt + * https://stackoverflow.com/a/14303988 + * https://stackoverflow.com/a/21599232 + * https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js + + +USE_GITCLONE(NEWTOY(gitclone, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_GITINIT(NEWTOY(gitinit, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_GITREMOTE(NEWTOY(gitremote, "<1", TOYFLAG_USR|TOYFLAG_BIN)) +USE_GITFETCH(NEWTOY(gitfetch, 0, TOYFLAG_USR|TOYFLAG_BIN)) +USE_GITCHECKOUT(NEWTOY(gitcheckout, "<1", TOYFLAG_USR|TOYFLAG_BIN)) + +config GITCOMPAT + bool "gitcompat" + default n + help + Enable git compatible repos instead of minimal clone downloader. + +config GITCLONE + bool "gitclone" + default n + help + usage: gitclone URL + A minimal git clone. + +config GITINIT + bool "gitinit" + default n + help + usage: gitinit NAME + A minimal git init. + +config GITREMOTE + bool "gitremote" + default n + help + usage: gitremote URL + A minimal git remote add origin. + +config GITFETCH + bool "gitfetch" + default n + help + usage: gitfetch + A minimal git fetch. + +config GITCHECKOUT + bool "gitcheckout" + default n + help + usage: gitcheckout + A minimal git checkout. +*/ + +#define TT this.git +#define FOR_gitclone +#include "toys.h" +#include "openssl/sha.h" //ToDo: borrowed from OpenSSL to not pipe or refactor the SHA1SUM in toybox +#include "zlib.h" //ToDo: borrowed from libz to not refactor deflate.c + +GLOBALS( + char *url, *name; //git repo remote url and init directory name + struct IndexV2 *i; //git creates a index for each pack file, git clone just needs one index for the received pack file +) + +//git index format v2 described at https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L266 +struct IndexV2 { + char header[8];// Git 4 byte magic number and 4 byte version number + unsigned fot[256];//A fan-out table + char (*sha1)[20];//Table of sorted object names(SHA1 hashes) + unsigned *crc, *offset;//Table of 4-bit CRC32 values and object offsets in pack file + long long *offset64; //8 byte offests -- not supported yet + char packsha1[20], idxsha1[20];//SHA1 hash of pack file and SHA1 hash of index file +}; + +//TODO:This function is not used before git clone persists an index V2 +static void read_index(struct IndexV2 *i) +{ + FILE *fpi; + + i = malloc(sizeof(i)); + i->sha1 = malloc(20*sizeof(char)); + i->crc = malloc(sizeof(unsigned)); + i->offset = malloc(sizeof(unsigned)); + i->offset64 = malloc(sizeof(long long)); + //TODO: not used yet as index is not persisted yet + if (access(".git/object/pack/temp.idx", F_OK)==0) { + //persistance needed for other git commands (not clone) + fpi = fopen(".git/object/pack/temp.idx", "rb"); + printf("read header\n"); + fread(i->header, sizeof(i->header), 1, fpi); + printf("Header: %s..Read fot\n", i->header); + fread(i->fot, 4, 256, fpi); + printf("Elements %d..Read sha1\n", i->fot[255]); + fread(i->sha1, sizeof(i->fot), i->fot[255], fpi); + printf("read crc\n"); + fread(i->crc, sizeof(i->fot), i->fot[255], fpi); + printf("read offset\n"); + fread(i->offset, sizeof(i->fot), i->fot[255], fpi); + //TODO: Offsets for file size 2G missing here + printf("read packsha\n"); + fread(i->packsha1, 20, 1, fpi); + printf("read idxsha\n"); + fread(i->idxsha1, 20, 1, fpi); + fclose(fpi); + } +} + +long bsearchpos(const void *k, const void *a, size_t h, size_t w) +{ + long l = 0, m = 0, r = 0; + + if (!h) return 0; + while (h>0) { + m = l+(h/2); + r = strncmp(k, a+(m*w), 20); + if (!r||h==1) break; //match on search or position for insert + if (r<0) { h /= 2; } else { l = m; h -= h/2; } + } + + //For inserts check if insert is bigger obj at identified position + return m += (r>0) ? 1 : 0; +} + +//find offset position in packfile for given SHA1 hash +long get_index(struct IndexV2 *i, char *h) +{ + long pos = bsearchpos(h, i->sha1[0], i->fot[255], 20); + return i->offset[pos]; +} + +//https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L35 +//https://yqintl.alicdn.com/eef7fe4f22cc97912cee011c99d3fe5821ae9e88.png +//read type and length of an packed object at a given offset +unsigned long long unpack(FILE *fpp, int *type, long *offset) +{ + int bitshift= 4; + unsigned long long length = 0; + char data; + + printf("Start unpack\n"); + fseek(fpp, *offset, SEEK_SET); + printf("Offset set to: %ld\n", *offset); + fread(&data, 1, 1, fpp); + printf("Data: %d\n", data); + *type = ((data & 0x70)>>4); + printf("Type: %d\n", *type); + length |= data & 0x0F; + while ((data & 0x80) && fread(&data, 1, 1, fpp)!=-1) + { + length |= (unsigned long long)(data & 0x7F) << bitshift; + bitshift += 7; // (*offset)++; + } + printf("Length: %llu\n", length); + + return length; +} + +// ToDo: borrowed from int inf(FILE *source, FILE *dest) in +// zpipe.c: example of proper use of zlib's inflate() and deflate() +// Not copyrighted -- provided to the public domain +// Version 1.4 11 December 2005 Mark Adler */ +#define CHUNK 4096 +int inf(FILE *source, char *dest) //modified signature to ease use +{ + int ret; + char *position = dest; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); + if (ret != Z_OK) return ret; + + // decompress until deflate stream ends or end of file + do { + + strm.avail_in = fread(in, 1, CHUNK, source); + if (ferror(source)) { + (void)inflateEnd(&strm); + + return Z_ERRNO; + } + if (strm.avail_in == 0) + break; + strm.next_in = in; + + + // run inflate() on input until output buffer not full + do { + + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = inflate(&strm, Z_NO_FLUSH); + //assert(ret != Z_STREAM_ERROR); // state not clobbered + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; // and fall through + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + + return ret; + } + + have = CHUNK - strm.avail_out; + memcpy(position, out, have); //added to original + position += have; //added to original + //if (fwrite(out, 1, have, dest) != have || ferror(dest)) { + // (void)inflateEnd(&strm); + // return Z_ERRNO; + //} + } while (strm.avail_out == 0); + // done when inflate() says it's done + } while (ret != Z_STREAM_END); + // modified from zpipe.c to set FP to end of zlib object + fseek(source, ftell(source)-strm.avail_in, SEEK_SET); + // clean up and return + (void)inflateEnd(&strm); + + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + +//https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L72 +//Set object to the index after adding prefix, calculating the hash and finding the position +long set_object(struct IndexV2 *idx, int type, char *o, unsigned count, + unsigned ofs) +{ +// TODO: Too many allocs in here 1) to concat the search string for hashing +// 2) to insert into the array (can be reduce to a single malloc in fetch as +// the pack header contains the number of objects in pack + char *c, *p = "", *h = (char*)xmalloc(sizeof(char)*20); //composition,prefix,hash + long pos = 0; + + printf("Alloc... "); + //append a object prefix based on its type (not included in packfile) + switch(type) { + case 1: p = xmprintf("commit %d", count); break; //count is used as o can contain \0 in the string + case 2: p = xmprintf("tree %d", count); break; + case 3: p = xmprintf("blob %d", count); break; + case 4: p = xmprintf("tag %d", count); break; + case 6: printf("REF_DELTA"); break; //not expected in fetch packs as fetch packs are self-containing + case 7: printf("OBJ_DELTA\n"); break; + } + c = (char*)xmalloc(strlen(p)+count+2); //Robs null terminator embedding + if (c == NULL) error_exit("c malloc failed in set_object"); + memcpy(c, p, strlen(p)+1); //Robs null terminator embedding + memcpy(c+strlen(p)+1, o, count+1); //Robs null terminator embedding + h = SHA1(c, strlen(p)+count+1, h); //ToDo: borrowed from OpenSSL to not to pipe or refactor SHA1SUM in toybox + printf("..Binary search\n"); + for (int j = 0; j<20; j++) printf("%02x", h[j]); //find insert position + pos = bsearchpos(h, idx->sha1[0], idx->fot[255], 20); + printf("\n..Insert pos %ld\n", pos); + printf("..Preloop\n"); + + //adjust of fanout table https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L204 + for (int i = h[0]; i<=255; i++) idx->fot[i] += 1; + printf("Post loop\n"); + printf("Resize sha1 array..idx->fot[255]%d\n", idx->fot[255]); //Memory management for insert + //TODO:Could be also a single malloc at gitfetch based on the nbr of objects in pack + + //Did not fix the TODO yet, because set_object could be reused for other commands adding single objects to the index + idx->sha1 = realloc(idx->sha1, (idx->fot[255]+1)*20*sizeof(char)); + printf("Mem copy sha1 array..sizeof(idx->sha1)%ld\n", sizeof(idx->sha1)); + memmove(&idx->sha1[pos+1], &idx->sha1[pos], (idx->fot[255]-pos)*20*sizeof(char)); + printf("Resize offset\n"); + idx->offset = realloc(idx->offset, (idx->fot[255]+1)*sizeof(unsigned)); + printf("Mem copy offset\n"); + memmove(&idx->offset[pos+1], &idx->offset[pos], sizeof(unsigned)*(idx->fot[255]-pos)); + printf("Set offset value\n"); + memcpy(&idx->sha1[pos], h, 20); //insert SHA1 + idx->offset[pos] = ofs; //insert offset of SHA1 + //ToDo: id->crc[idx->fot[h[0]]]=; + printf("Write object\n"); + free(h); + free(c); + + return ofs; +} + +//init a git repository in a given directory name +static void gitinit(char *name) +{ + //For git clone actually only refs and object/pack are needed + if (mkdir(name, 0755)!=0){ + //I create the others for a git compliant folder structure + mkdir(xmprintf("%s%s", name, "/.git"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/objects"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/objects/pack"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/branches"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/hooks"), 0755); //hook files skipped as implementations does not support hooks + mkdir(xmprintf("%s%s", name, "/.git/info"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/objects/info"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/refs"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/heads"), 0755); + mkdir(xmprintf("%s%s", name, "/.git/tags"), 0755); + xcreate(xmprintf("%s%s", name, "/.git/config"), O_CREAT, 0644); + xcreate(xmprintf("%s%s", name, "/.git/description"), O_CREAT, 0644); + xcreate(xmprintf("%s%s", name, "/.git/HEAD"), O_CREAT, 0644); + xcreate(xmprintf("%s%s", name, "/.git/info/exclude"), O_CREAT, 0644); + } +} + +//set basic configuration and add remote URL +static void gitremote(char *url) +{ + if (access(".git/config", F_OK)!=0) { + FILE *fp = fopen(".git/config", "wb"); + + fwrite("[core]\n", 1, 7, fp); + fwrite("\trepositoryformatversion = 0\n", 1, 29, fp); + fwrite("\tfilemode = false\n", 1, 18, fp); + fwrite("\tbare = false\n", 1, 14, fp); + fwrite("\tlogallrefupdates = true\n", 1, 25, fp); + fwrite("\tsymlinks = false\n", 1, 18, fp); + fwrite("\tignorecase = true\n", 1, 19, fp); + fwrite("[remote \"origin\"]\n", 1, 18, fp); + fwrite(xmprintf("\turl = %s/refs\n", TT.url), 1, strlen(TT.url)+13, fp); + fwrite("\tfetch = +ref/heads/*:refs/remotes/origin/*\n", 1, 44, fp); + fclose(fp); + } +} + +// this is most likely still buggy and create a late observable heap overflow larger deltafied repos +// https://stackoverflow.com/a/14303988 +// resolve deltafied objects in the pack file, see URL in comments for further explainations +char *resolve_delta(char *s, char *d, long dsize, unsigned *count) +{ + long pos = 0, bitshift = 0; + //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L113 + // Skipping source size; did not find out why it is on the delta header as the source object header contains it too; maybe misunderstood and this makes things buggy, but I dont need it here + while ((d[pos] & 0x80)) pos++; + pos++; //fixes https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L114 + *count = 0; + bitshift = 0; + while ((d[pos] & 0x80)) { //reading target_size from header + *count |= (unsigned long long)(d[pos++]& 0x7F) << bitshift; + bitshift += 7; // (*offset)++; + } + + *count |= (unsigned long long)(d[pos++]& 0x7F) << bitshift; + printf("Target Count %d:\n", *count); + char *t = malloc(sizeof(char)*(*count+1)); + if (t == NULL) error_exit("t malloc failed in resolve_delta"); + *count = 0; + while (pos 0) ? xmprintf("%s/%s", path, object+pos+7) : object+pos+7; + printf("prepare file %s\n", name); + } else { //tree object reference is a folder + // concat folder name + name = (strlen(path)>0) ? xmprintf("%s/%s", path, object+pos+6) : object+pos+6; + printf("create folder %s\n", name); + mkdir(name, 0755); //TODO: umask + } + memcpy(hash, hs, 20); + write_children(hash, name, fpp); + pos = hs-object+20; + printf("Position/count for %s: %d/%u\n", path, pos, count); + } + printf("**EXIT WHILE**\n"); + } else { //at blob/file object + printf("process file %s\n", path); + fc = fopen(path, "w"); + printf("process opened \n"); + fputs(object, fc); //TODO:Not sure if length might be an issue here + printf("process file written\n"); + fclose(fc); + } + free(object); + printf("Child: %s done\n", path); +} + +//fetches the meta data from the remote repository,requests a pack file for the remote master head, +//unpacks all objects and set objects to the index +static void gitfetch(void) +{ + printf("refs\n"); + pid_t pid; + + // TODO:I use herein after two temp files for fetch which git does not offer + // to 1) avoid a rewrite and 2) messing up the repo files while testing + + // TODO: Refactor wget into lib + if ((pid = fork())==0) + execv("toybox", (char *[]){"toybox", "wget", "-O", ".git/refs/temp.refs", + "https://github.com/landley/toybox/info/refs?service=git-upload-pack", + (char*)0}); + perror("execv\n"); + //char h[] = "8cf1722f0fde510ea81d13b31bde1e48917a0306"; + //TODO: Replace static testing hash and uncomment the following line if rare delta resolve /?heap overflow? bug was found + FILE *fpr = fopen(".git/ref/temp.refs", "r"); + char *h;size_t l =0; + getline(&h,&l,fpr); + getline(&h,&l,fpr); + getline(&h,&l,fpr); + getline(&h,&l,fpr); + fclose(fpr); + strcpy(h,&h[4]); + h[40]='\0'; + printf("Master HEAD hash: %s\n",h); + //TODO: Persist hash to /refs/master/HEAD + printf("pack\n"); + if ((pid = fork())==0) execv("toybox", (char *[]){"toybox", "wget", "-O", ".git/objects/pack/temp.pack", "-p", xmprintf("$'0032want %s\n00000009done\n'", h), "https://github.com/landley/toybox/git-upload-pack", (char*)0}); //TODO: does not skip 0008NAK printf("init\n"); + perror("execv\n"); + FILE *fpp; + printf("openpack\n"); + fpp = fopen(".git/objects/pack/temp.pack", "r"); + printf("read index\n"); + read_index(TT.i); //init index with out reading + printf("init\n"); + unsigned ocount = 0, count; + int type; + char *object; + + printf("skip header\n"); + long offset = 12+8; //8byte from the wget post response are skipped too + fseek(fpp, 8+8, SEEK_SET); //header check skipped //header check skipped https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L37 + printf("read count\n"); + fread(&ocount, 4, 1, fpp); + ocount = ntohl(ocount); //https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt#L46 + printf("Count: %d ..Loop pack\n", ocount); + for (int j = 0; j /head +static void gitcheckout(char *name) +{ + + FILE *fpp; + //FILE *fh; + printf("Find branch for checkout\n"); + //fh = fopen(xmprintf(".git/ref/heads/%s", name ? "master" : name), "r"); //TODO: Checkout master as in ref/heads + printf("Read head\n"); + //hf[fread(&hf, 40, 1, fh)] = '\0'; + //fclose(fh); + printf("Close heads and read pack\n"); + fpp = fopen(".git/objects/pack/temp.pack", "r"); + printf("set signature\n"); + char *p = "52fb04274b3491fdfe91b2e5acc23dc3f3064a86"; //static hashes for testing toybox 0.0.1"; + //char *p = "c555a0ca46e75097596274bf5e634127015aa144"; //static hashes for testing 0.0.2"; + //char *p = "4307a7b07cec4ad8cbab47a29ba941f8cb041812"; //static hashes for testing 0.0.3"; + //char *p = "3632d5d8fe05d14da983e37c7cd34db0769e6238"; //static hashes for testing 0.0.4"; + //char *p = "8cf1722f0fde510ea81d13b31bde1e48917a0306"; //3604ba4f42c3d83e2b14f6d0f423a33a3a8706c3"; + printf("enter tree root\n"); + write_children(txtoh(p), "", fpp); + fclose(fpp); +} + +void gitclone_main(void) +{ + TT.url = xstrdup(toys.optargs[0]); + if (strend(TT.url, ".git")) TT.url[strlen(TT.url)-4] = '\0'; + TT.name = strrchr(TT.url, '/')+1; + gitinit(TT.name); + chdir(TT.name); + TT.i = malloc(sizeof(struct IndexV2)); + gitremote(TT.url); + gitfetch(); + gitcheckout("master"); + chdir(".."); +} + +#define FOR_gitinit +#include "generated/flags.h" + +void gitinit_main(void) +{ + gitinit(xstrdup(toys.optargs[0])); +} + +#define FOR_gitremote + +void gitremote_main(void) +{ + TT.url = xstrdup(toys.optargs[0]); + if (strend(TT.url, ".git")) TT.url[strlen(TT.url)-4] = '\0'; + gitremote(TT.url); +} + +#define FOR_gitinit + +void gitfetch_main(void) +{ + gitfetch(); +} + +#define FOR_gitcheckout + +void gitcheckout_main(void) +{ + gitcheckout(xstrdup(toys.optargs[0])); +} + +// Command to wget refs and pack file using toybox wget to place them manually into the repository +// ./toybox wget -O - -d https://github.com/landley/toybox/info/refs?service=git-upload-pack | less + +// ./toybox wget -O pack.dat -d -p $'0032want 8cf1722f0fde510ea81d13b31bde1e48917a0306\n00000009done\n' https://github.com/landley/toybox/git-upload-pack diff -Nru toybox-0.8.8+dfsg/toys/pending/modprobe.c toybox-0.8.9+dfsg/toys/pending/modprobe.c --- toybox-0.8.8+dfsg/toys/pending/modprobe.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/modprobe.c 2023-01-10 19:24:45.000000000 +0000 @@ -444,7 +444,7 @@ } // none of above is true insert the module. errno = 0; - rc = ins_mod(fn, options); + rc = ins_mod(fn, options ? : ""); if (FLAG(v)) printf("loaded %s '%s': %s\n", fn, options, strerror(errno)); if (errno == EEXIST) rc = 0; @@ -489,15 +489,15 @@ } // Read /proc/modules to get loaded modules. - fs = xfopen("/proc/modules", "r"); - - while (read_line(fs, &procline) > 0) { + fs = fopen("/proc/modules", "r"); + + while (fs && read_line(fs, &procline) > 0) { *strchr(procline, ' ') = 0; get_mod(procline, 1)->flags = MOD_ALOADED; free(procline); procline = NULL; } - fclose(fs); + if (fs) fclose(fs); if (FLAG(a) || FLAG(r)) for (; *argv; argv++) add_mod(*argv); else { add_mod(*argv); diff -Nru toybox-0.8.8+dfsg/toys/pending/sh.c toybox-0.8.9+dfsg/toys/pending/sh.c --- toybox-0.8.8+dfsg/toys/pending/sh.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/sh.c 2023-01-10 19:24:45.000000000 +0000 @@ -555,21 +555,18 @@ // Recursively calculate string into dd, returns 0 if failed, ss = error point // Recursion resolves operators of lower priority level to a value // Loops through operators at same priority +#define NO_ASSIGN 128 static int recalculate(long long *dd, char **ss, int lvl) { long long ee, ff; char *var = 0, *val, cc = **nospace(ss); - int ii, assign = 1; - - if (lvl>99) { - lvl -= 100; - assign = 0; - } + int ii, noa = lvl&NO_ASSIGN; + lvl &= NO_ASSIGN-1; // Unary prefixes can only occur at the start of a parse context if (cc=='!' || cc=='~') { ++*ss; - if (!recalculate(dd, ss, 15)) return 0; + if (!recalculate(dd, ss, noa|15)) return 0; *dd = (cc=='!') ? !*dd : ~*dd; } else if (cc=='+' || cc=='-') { // Is this actually preincrement/decrement? (Requires assignable var.) @@ -582,12 +579,12 @@ } } if (!var) { - if (!recalculate(dd, ss, 15)) return 0; + if (!recalculate(dd, ss, noa|15)) return 0; if (cc=='-') *dd = -*dd; } } else if (cc=='(') { ++*ss; - if (!recalculate(dd, ss, 1)) return 0; + if (!recalculate(dd, ss, noa|1)) return 0; if (**ss!=')') return 0; else ++*ss; } else if (isdigit(cc)) { @@ -601,6 +598,7 @@ // At lvl 0 "" is ok, anything higher needs a non-empty equation if (lvl || (cc && cc!=')')) return 0; *dd = 0; + return 1; } @@ -614,7 +612,7 @@ return 0; } val = getvar(var = *ss) ? : ""; - ii = recalculate(dd, &val, 0); + ii = recalculate(dd, &val, noa); TT.recursion--; if (!ii) return 0; if (*val) { @@ -638,17 +636,15 @@ *ss += 2; // Assignment operators: = *= /= %= += -= <<= >>= &= ^= |= - } else if (lvl<=2 && (*ss)[ii = !!strchr("*/%+-", **ss) - +2*!memcmp(*ss, "<<", 2)+2*!memcmp(*ss, ">>", 2)]=='=') + } else if (lvl<=2 && (*ss)[ii = (-1 != stridx("*/%+-", **ss)) + +2*!smemcmp(*ss, "<<", 2)+2*!smemcmp(*ss, ">>", 2)]=='=') { // TODO: assignments are lower priority BUT must go after variable, // come up with precedence checking tests? cc = **ss; *ss += ii+1; - if (!recalculate(&ee, ss, 1)) return 0; // lvl instead of 1? + if (!recalculate(&ee, ss, noa|1)) return 0; // TODO lvl instead of 1? if (cc=='*') *dd *= ee; - else if (cc=='/') *dd /= ee; - else if (cc=='%') *dd %= ee; else if (cc=='+') *dd += ee; else if (cc=='-') *dd -= ee; else if (cc=='<') *dd <<= ee; @@ -656,87 +652,94 @@ else if (cc=='&') *dd &= ee; else if (cc=='^') *dd ^= ee; else if (cc=='|') *dd |= ee; - else *dd = ee; + else if (!cc) *dd = ee; + else if (!ee) { + perror_msg("%c0", cc); + + return 0; + } else if (cc=='/') *dd /= ee; + else if (cc=='%') *dd %= ee; ee = *dd; } - if (cc && assign) setvar(xmprintf("%.*s=%lld", (int)(val-var), var, ee)); + if (cc && !noa) setvar(xmprintf("%.*s=%lld", (int)(val-var), var, ee)); } // x**y binds first - if (lvl<=13) while (strstart(nospace(ss), "**")) { - if (!recalculate(&ee, ss, 14)) return 0; + if (lvl<=14) while (strstart(nospace(ss), "**")) { + if (!recalculate(&ee, ss, noa|15)) return 0; if (ee<0) perror_msg("** < 0"); for (ff = *dd, *dd = 1; ee; ee--) *dd *= ff; } // w*x/y%z bind next - if (lvl<=12) while ((cc = **nospace(ss)) && strchr("*/%", cc)) { + if (lvl<=13) while ((cc = **nospace(ss)) && strchr("*/%", cc)) { ++*ss; - if (!recalculate(&ee, ss, 13)) return 0; + if (!recalculate(&ee, ss, noa|14)) return 0; if (cc=='*') *dd *= ee; - else if (cc=='%') *dd %= ee; else if (!ee) { - perror_msg("/0"); + perror_msg("%c0", cc); + return 0; - } else *dd /= ee; + } else if (cc=='%') *dd %= ee; + else *dd /= ee; } // x+y-z - if (lvl<=11) while ((cc = **nospace(ss)) && strchr("+-", cc)) { + if (lvl<=12) while ((cc = **nospace(ss)) && strchr("+-", cc)) { ++*ss; - if (!recalculate(&ee, ss, 12)) return 0; + if (!recalculate(&ee, ss, noa|13)) return 0; if (cc=='+') *dd += ee; else *dd -= ee; } // x< > - if (lvl<=10) while ((cc = **nospace(ss)) && strchr("<>", cc) && cc==(*ss)[1]){ + if (lvl<=11) while ((cc = **nospace(ss)) && strchr("<>", cc) && cc==(*ss)[1]){ *ss += 2; - if (!recalculate(&ee, ss, 11)) return 0; + if (!recalculate(&ee, ss, noa|12)) return 0; if (cc == '<') *dd <<= ee; else *dd >>= ee; } // x >= - if (lvl<=9) while ((cc = **nospace(ss)) && strchr("<>", cc)) { + if (lvl<=10) while ((cc = **nospace(ss)) && strchr("<>", cc)) { if ((ii = *++*ss=='=')) ++*ss; - if (!recalculate(&ee, ss, 10)) return 0; + if (!recalculate(&ee, ss, noa|11)) return 0; if (cc=='<') *dd = ii ? (*dd<=ee) : (*dd =ee) : (*dd>ee); } - if (lvl<=8) while ((cc = **nospace(ss)) && strchr("=!", cc) && (*ss)[1]=='='){ + if (lvl<=9) while ((cc = **nospace(ss)) && strchr("=!", cc) && (*ss)[1]=='='){ *ss += 2; - if (!recalculate(&ee, ss, 9)) return 0; + if (!recalculate(&ee, ss, noa|10)) return 0; *dd = (cc=='!') ? *dd != ee : *dd == ee; } - if (lvl<=7) while (**nospace(ss)=='&') { + if (lvl<=8) while (**nospace(ss)=='&' && (*ss)[1]!='&') { ++*ss; - if (!recalculate(&ee, ss, 8)) return 0; + if (!recalculate(&ee, ss, noa|9)) return 0; *dd &= ee; } - if (lvl<=6) while (**nospace(ss)=='^') { + if (lvl<=7) while (**nospace(ss)=='^') { ++*ss; - if (!recalculate(&ee, ss, 7)) return 0; + if (!recalculate(&ee, ss, noa|8)) return 0; *dd ^= ee; } - if (lvl<=5) while (**nospace(ss)=='|') { + if (lvl<=6) while (**nospace(ss)=='|' && (*ss)[1]!='|') { ++*ss; - if (!recalculate(&ee, ss, 6)) return 0; + if (!recalculate(&ee, ss, noa|7)) return 0; *dd |= ee; } if (lvl<=5) while (strstart(nospace(ss), "&&")) { - if (!recalculate(&ee, ss, 6+100*!!*dd)) return 0; + if (!recalculate(&ee, ss, noa|6|NO_ASSIGN*!*dd)) return 0; *dd = *dd && ee; } if (lvl<=4) while (strstart(nospace(ss), "||")) { - if (!recalculate(&ee, ss, 5+100*!*dd)) return 0; + if (!recalculate(&ee, ss, noa|5|NO_ASSIGN*!!*dd)) return 0; *dd = *dd || ee; } @@ -745,19 +748,19 @@ if (lvl<=3) if (**nospace(ss)=='?') { ++*ss; if (**nospace(ss)==':' && *dd) ee = *dd; - else if (!recalculate(&ee, ss, 1+100*!*dd) || **nospace(ss)!=':') + else if (!recalculate(&ee, ss, noa|1|NO_ASSIGN*!*dd) || **nospace(ss)!=':') return 0; ++*ss; - if (!recalculate(&ff, ss, 1+100*!!*dd)) return 0; + if (!recalculate(&ff, ss, noa|1|NO_ASSIGN*!!*dd)) return 0; *dd = *dd ? ee : ff; } // lvl<=2 assignment would go here, but handled above because variable // , (slightly weird, replaces dd instead of modifying it via ee/ff) - if (lvl<=1) while ((cc = **nospace(ss)) && cc==',') { + if (lvl<=1) while (**nospace(ss)==',') { ++*ss; - if (!recalculate(dd, ss, 2)) return 0; + if (!recalculate(dd, ss, noa|2)) return 0; } return 1; @@ -881,9 +884,9 @@ if (flags&VAR_INT) { sd = ss; if (!recalculate(&ll, &sd, 0) || *sd) { - perror_msg("bad math: %s @ %d", ss, (int)(sd-ss)); + perror_msg("bad math: %s @ %d", ss, (int)(sd-ss)); - goto bad; + goto bad; } sprintf(buf, "%lld", ll); @@ -1572,7 +1575,7 @@ j = 0; } - // Got wildcard? Return if start of name if out of count, else skip [] () + // Got wildcard? Return start of name if out of count, else skip [] () if (*idx c && p-pattern == (long)deck->v[*idx]) { if (!j++ && !count--) return old; ++*idx; @@ -1777,7 +1780,8 @@ #define SEMI_IFS (1<<6) // Use ' ' instead of IFS to combine $* // expand str appending to arg using above flag defines, add mallocs to delete // if ant not null, save wildcard deck there instead of expanding vs filesystem -// returns 0 for success, 1 for error +// returns 0 for success, 1 for error. +// If measure stop at *measure and return input bytes consumed in *measure static int expand_arg_nobrace(struct sh_arg *arg, char *str, unsigned flags, struct arg_list **delete, struct sh_arg *ant, long *measure) { @@ -1834,10 +1838,7 @@ ifs = slice = 0; // handle escapes and quoting - if (cc == '\\') { - if (!(qq&1) || (str[ii] && strchr("\"\\$`", str[ii]))) - new[oo++] = str[ii] ? str[ii++] : cc; - } else if (cc == '"') qq++; + if (cc == '"') qq++; else if (cc == '\'') { if (qq&1) new[oo++] = cc; else { @@ -1847,12 +1848,12 @@ // both types of subshell work the same, so do $( here not in '$' below // TODO $((echo hello) | cat) ala $(( becomes $( ( retroactively - } else if (cc == '`' || (cc == '$' && str[ii] && strchr("([", str[ii]))) { + } else if (cc == '`' || (cc == '$' && (str[ii]=='(' || str[ii]=='['))) { off_t pp = 0; s = str+ii-1; kk = parse_word(s, 1, 0)-s; - if (str[ii] == '[' || *toybuf == 255) { + if (str[ii] == '[' || *toybuf == 255) { // (( parsed together, not (( ) ) struct sh_arg aa = {0}; long long ll; @@ -1900,10 +1901,13 @@ for (kk = strlen(ifs); kk && ifs[kk-1]=='\n'; ifs[--kk] = 0); close(jj); } + } else if (cc=='\\' || !str[ii]) { + if (!(qq&1) || (str[ii] && strchr("\"\\$`", str[ii]))) + new[oo++] = str[ii] ? str[ii++] : cc; // $VARIABLE expansions - } else if (cc == '$') { + } else if (cc == '$' && str[ii]) { cc = *(ss = str+ii++); if (cc=='\'') { for (s = str+ii; *s != '\''; oo += wcrtomb(new+oo, unescape2(&s, 0),0)); @@ -2122,7 +2126,7 @@ } if (yy != -1) { - if (*delete && (*delete)->arg==ifs) ifs[yy] = 0; + if (delete && *delete && (*delete)->arg==ifs) ifs[yy] = 0; else push_arg(delete, ifs = xstrndup(ifs, yy)); } } @@ -2149,7 +2153,7 @@ ll++; continue; } - if (*delete && (*delete)->arg==ifs) { + if (delete && *delete && (*delete)->arg==ifs) { if (jj==dd) memcpy(ifs+ll, ss, jj); else if (jj commas[0] = i; + // end of string: abort unfinished spans and end loop + } else if (!old[i]) { + for (bb = blist; bb;) { + if (!bb->active) { + if (bb==blist) { + dlist_pop(&blist); + bb = blist; + } else dlist_pop(&bb); + } else bb = (bb->next==blist) ? 0 : bb->next; + } + break; + // no active span? + } else if (!bb) continue; + // end current span + else if (old[i] == '}') { bb->active = bb->commas[bb->cnt+1] = i; - // pop brace from bb into bnext - for (bnext = bb; bb && bb->active; bb = (bb==blist) ? 0 : bb->prev); // Is this a .. span? - j = 1+*bnext->commas; - if (old[i] && !bnext->cnt && i-j>=4) { + j = 1+*bb->commas; + if (!bb->cnt && i-j>=4) { // a..z span? Single digit numbers handled here too. TODO: utf8 if (old[j+1]=='.' && old[j+2]=='.') { - bnext->commas[2] = old[j]; - bnext->commas[3] = old[j+3]; + bb->commas[2] = old[j]; + bb->commas[3] = old[j+3]; k = 0; if (old[j+4]=='}' || - (sscanf(old+j+4, "..%u}%n", bnext->commas+4, &k) && k)) - bnext->cnt = -1; + (sscanf(old+j+4, "..%u}%n", bb->commas+4, &k) && k)) + bb->cnt = -1; } // 3..11 numeric span? - if (!bnext->cnt) { - for (k=0, j = 1+*bnext->commas; k<3; k++, j += x) - if (!sscanf(old+j, "..%u%n"+2*!k, bnext->commas+2+k, &x)) break; - if (old[j] == '}') bnext->cnt = -2; + if (!bb->cnt) { + for (k=0, j = 1+*bb->commas; k<3; k++, j += x) + if (!sscanf(old+j, "..%u%n"+2*!k, bb->commas+2+k, &x)) break; + if (old[j]=='}') bb->cnt = -2; } // Increment goes in the right direction by at least 1 - if (bnext->cnt) { - if (!bnext->commas[4]) bnext->commas[4] = 1; - if ((bnext->commas[3]-bnext->commas[2]>0) != (bnext->commas[4]>0)) - bnext->commas[4] *= -1; + if (bb->cnt) { + if (!bb->commas[4]) bb->commas[4] = 1; + if ((bb->commas[3]-bb->commas[2]>0) != (bb->commas[4]>0)) + bb->commas[4] *= -1; } } - // discard unterminated span, or commaless span that wasn't x..y - if (!old[i] || !bnext->cnt) - free(dlist_pop((blist == bnext) ? &blist : &bnext)); - // starting brace - } else if (old[i] == '{') { - dlist_add_nomalloc((void *)&blist, - (void *)(bb = xzalloc(sizeof(struct sh_brace)+34*4))); - bb->commas[0] = i; - // no active span? - } else if (!bb) continue; + // discard commaless span that wasn't x..y + if (!bb->cnt) free(dlist_pop((blist==bb) ? &blist : &bb)); + // Set bb to last unfinished brace (if any) + for (bb = blist ? blist->prev : 0; bb && bb->active; + bb = (bb==blist) ? 0 : bb->prev); // add a comma to current span - else if (bb && old[i] == ',') { + } else if (old[i] == ',') { if (bb->cnt && !(bb->cnt&31)) { dlist_lpop(&blist); dlist_add_nomalloc((void *)&blist, @@ -2740,7 +2754,7 @@ // Skip [[ ]] and (( )) contents for now if ((s = arg->v[envlen])) { - if (!memcmp(s, "((", 2)) skiplen = 1; + if (!smemcmp(s, "((", 2)) skiplen = 1; else if (!strcmp(s, "[[")) while (strcmp(arg->v[envlen+skiplen++], "]]")); } pp = expand_redir(arg, envlen+skiplen, 0); @@ -2804,7 +2818,7 @@ // Several NOFORK can just NOP in a pipeline? Except ${a?b} still errors // ((math)) - else if (!memcmp(s = *pp->arg.v, "((", 2)) { + else if (!smemcmp(s = *pp->arg.v, "((", 2)) { char *ss = s+2; long long ll; @@ -2855,6 +2869,7 @@ } toys.rebound = 0; pp->exit = toys.exitval; + clearerr(stdout); if (toys.optargs != toys.argv+1) free(toys.optargs); if (toys.old_umask) umask(toys.old_umask); memcpy(&toys, &temp, jj); @@ -3073,7 +3088,7 @@ } // "for" on its own line is an error. - if (arg->c == 1 && ex && !memcmp(ex, "do\0A", 4)) { + if (arg->c == 1 && ex && !smemcmp(ex, "do\0A", 4)) { s = "newline"; goto flush; } @@ -3167,7 +3182,7 @@ free(s); s = 0; // TODO can't have ; between "for i" and in or do. (Newline yes, ; no. Why?) - if (!arg->c && ex && !memcmp(ex, "do\0C", 4)) continue; + if (!arg->c && ex && !smemcmp(ex, "do\0C", 4)) continue; // ;; and friends only allowed in case statements } else if (*s == ';') goto flush; @@ -3179,7 +3194,7 @@ continue; // a for/select must have at least one additional argument on same line - } else if (ex && !memcmp(ex, "do\0A", 4)) { + } else if (ex && !smemcmp(ex, "do\0A", 4)) { // Sanity check and break the segment if (strncmp(s, "((", 2) && *varend(s)) goto flush; @@ -3198,7 +3213,7 @@ // The "test" part of for/select loops can have (at most) one "in" line, // for {((;;))|name [in...]} do - if (ex && !memcmp(ex, "do\0C", 4)) { + if (ex && !smemcmp(ex, "do\0C", 4)) { if (strcmp(s, "do")) { // can only have one "in" line between for/do, but not with for(()) if (pl->prev->type == 's') goto flush; @@ -3230,7 +3245,7 @@ // Expecting NULL means any statement (don't care which). if (!ex && *expect) { - if (pl->prev->type == 'f' && !end && memcmp(s, "((", 2)) goto flush; + if (pl->prev->type == 'f' && !end && smemcmp(s, "((", 2)) goto flush; free(dlist_lpop(expect)); } diff -Nru toybox-0.8.8+dfsg/toys/pending/strace.c toybox-0.8.9+dfsg/toys/pending/strace.c --- toybox-0.8.8+dfsg/toys/pending/strace.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/pending/strace.c 2023-01-10 19:24:45.000000000 +0000 @@ -245,7 +245,7 @@ "st_nlink=%ld, st_uid=%d, st_gid=%d, st_blksize=%ld, st_blocks=%ld, " "st_size=%lld, st_atime=%ld, st_mtime=%ld, st_ctime=%ld}", dev_major(sb.st_dev), dev_minor(sb.st_dev), sb.st_ino, sb.st_mode, - sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_blksize, sb.st_blocks, + (long) sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_blksize, sb.st_blocks, (long long)sb.st_size, sb.st_atime, sb.st_mtime, sb.st_ctime); } else { fprintf(stderr, "{st_mode=%o, st_size=%lld, ...}", sb.st_mode, @@ -411,7 +411,7 @@ if (!(s = num_to_sig(v))) fprintf(stderr, "%ld", v); else fprintf(stderr, "SIG%s", s); break; - case 'z': fprintf(stderr, "%zd", v); break; // size_t + case 'z': fprintf(stderr, "%ld", (long) v); break; // size_t case 'x': fprintf(stderr, "%lx", v); break; // hex case '{': print_struct(v); break; @@ -497,7 +497,9 @@ SC(mremap, "pzzdp"); // TODO: flags SC(munmap, "pz"); SC(nanosleep, "{timespec}/{timespec}"); +#ifdef __NR_newfstatat SC(newfstatat, "fs/{stat}d"); +#endif SC(open, "sd|open|m"); SC(openat, "fs|open|m"); SC(poll, "pdd"); diff -Nru toybox-0.8.8+dfsg/toys/posix/cksum.c toybox-0.8.9+dfsg/toys/posix/cksum.c --- toybox-0.8.8+dfsg/toys/posix/cksum.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/cksum.c 2023-01-10 19:24:45.000000000 +0000 @@ -39,24 +39,23 @@ unsigned crc_table[256]; ) -static unsigned cksum_be(unsigned crc, unsigned char c) +static unsigned cksum_be(unsigned crc, char c) { - return (crc<<8)^TT.crc_table[(crc>>24)^c]; + return (crc<<8) ^ TT.crc_table[(crc>>24)^c]; } -static unsigned cksum_le(unsigned crc, unsigned char c) +static unsigned cksum_le(unsigned crc, char c) { return TT.crc_table[(crc^c)&0xff] ^ (crc>>8); } static void do_cksum(int fd, char *name) { - unsigned crc = (toys.optflags & FLAG_P) ? 0xffffffff : 0; - uint64_t llen = 0, llen2; - unsigned (*cksum)(unsigned crc, unsigned char c); + unsigned (*cksum)(unsigned crc, char c), crc = FLAG(P) ? ~0 : 0; + unsigned long long llen = 0, llen2; int len, i; - cksum = (toys.optflags & FLAG_L) ? cksum_le : cksum_be; + cksum = FLAG(L) ? cksum_le : cksum_be; // CRC the data for (;;) { @@ -70,24 +69,17 @@ // CRC the length - llen2 = llen; - if (!(toys.optflags & FLAG_N)) { - while (llen) { - crc = cksum(crc, llen); - llen >>= 8; - } - } + if (!FLAG(N)) for (llen2 = llen; llen2; llen2 >>= 8) crc = cksum(crc, llen2); - printf((toys.optflags & FLAG_H) ? "%08x" : "%u", - (toys.optflags & FLAG_I) ? crc : ~crc); - if (!(toys.optflags&FLAG_N)) printf(" %"PRIu64, llen2); + printf(FLAG(H) ? "%08x" : "%u", FLAG(I) ? crc : ~crc); + if (!FLAG(N)) printf(" %llu", llen); if (toys.optc) printf(" %s", name); xputc('\n'); } void cksum_main(void) { - crc_init(TT.crc_table, toys.optflags & FLAG_L); + crc_init(TT.crc_table, FLAG(L)); loopfiles(toys.optargs, do_cksum); } diff -Nru toybox-0.8.8+dfsg/toys/posix/comm.c toybox-0.8.9+dfsg/toys/posix/comm.c --- toybox-0.8.8+dfsg/toys/posix/comm.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/comm.c 2023-01-10 19:24:45.000000000 +0000 @@ -43,26 +43,26 @@ { FILE *file[2]; char *line[2]; - int i; + int i = 0; - if (toys.optflags == 7) return; - - for (i = 0; i < 2; i++) { - file[i] = xfopen(toys.optargs[i], "r"); + for (i = 0; i<2; i++) { + file[i] = strcmp(toys.optargs[i], "-")?xfopen(toys.optargs[i], "r"):stdin; line[i] = xgetline(file[i]); } + if (toys.optflags == 7) return; + while (line[0] && line[1]) { int order = strcmp(line[0], line[1]); - if (order == 0) { + if (!order) { writeline(line[0], 2); for (i = 0; i < 2; i++) { free(line[i]); line[i] = xgetline(file[i]); } } else { - i = order < 0 ? 0 : 1; + i = order>0; writeline(line[i], i); free(line[i]); line[i] = xgetline(file[i]); @@ -76,5 +76,5 @@ line[i] = xgetline(file[i]); } - if (CFG_TOYBOX_FREE) for (i = 0; i < 2; i++) fclose(file[i]); + if (CFG_TOYBOX_FREE) fclose(file[0]), fclose(file[1]); } diff -Nru toybox-0.8.8+dfsg/toys/posix/cp.c toybox-0.8.9+dfsg/toys/posix/cp.c --- toybox-0.8.8+dfsg/toys/posix/cp.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/cp.c 2023-01-10 19:24:45.000000000 +0000 @@ -16,8 +16,8 @@ // for FLAG macros to work out right in shared infrastructure. USE_CP(NEWTOY(cp, "<1(preserve):;D(parents)RHLPprudaslvnF(remove-destination)fit:T[-HLPd][-niu][+Rr]", TOYFLAG_BIN)) -USE_MV(NEWTOY(mv, "<1vnF(remove-destination)fit:T[-ni]", TOYFLAG_BIN)) -USE_INSTALL(NEWTOY(install, "<1cdDpsvt:m:o:g:", TOYFLAG_USR|TOYFLAG_BIN)) +USE_MV(NEWTOY(mv, "<1v(verbose)nF(remove-destination)fit:T[-ni]", TOYFLAG_BIN)) +USE_INSTALL(NEWTOY(install, "<1cdDp(preserve-timestamps)svt:m:o:g:", TOYFLAG_USR|TOYFLAG_BIN)) config CP bool "cp" diff -Nru toybox-0.8.8+dfsg/toys/posix/cpio.c toybox-0.8.9+dfsg/toys/posix/cpio.c --- toybox-0.8.8+dfsg/toys/posix/cpio.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/cpio.c 2023-01-10 19:24:45.000000000 +0000 @@ -132,7 +132,7 @@ if (empty) error_exit("empty archive"); else break; } - if (size != 110 || memcmp(toybuf, "070701", 6)) error_exit("bad header"); + if (size != 110 || smemcmp(toybuf, "070701", 6)) error_exit("bad header"); tofree = name = strpad(afd, x8u(toybuf+94), 110); if (!strcmp("TRAILER!!!", name)) { free(tofree); diff -Nru toybox-0.8.8+dfsg/toys/posix/date.c toybox-0.8.9+dfsg/toys/posix/date.c --- toybox-0.8.8+dfsg/toys/posix/date.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/date.c 2023-01-10 19:24:45.000000000 +0000 @@ -7,7 +7,7 @@ * Note: setting a 2 year date is 50 years back/forward from today, * not posix's hardwired magic dates. -USE_DATE(NEWTOY(date, "d:D:I(iso)(iso-8601):;r:s:u(utc)[!dr]", TOYFLAG_BIN)) +USE_DATE(NEWTOY(date, "d:D:I(iso-8601):;r:s:u(utc)[!dr]", TOYFLAG_BIN)) config DATE bool "date" @@ -156,7 +156,7 @@ struct tm tm = {}; char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm); - t = (s && *s) ? xvali_date(&tm, s) : xvali_date(0, TT.d); + t = (s && !*s) ? xvali_date(&tm, s) : xvali_date(0, TT.d); } else parse_date(TT.d, &t); } else { struct timespec ts; diff -Nru toybox-0.8.8+dfsg/toys/posix/df.c toybox-0.8.9+dfsg/toys/posix/df.c --- toybox-0.8.8+dfsg/toys/posix/df.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/df.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,7 +4,7 @@ * * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html -USE_DF(NEWTOY(df, "HPkhit*a[-HPh]", TOYFLAG_SBIN)) +USE_DF(NEWTOY(df, "HPkhit*a[-HPh]", TOYFLAG_BIN)) config DF bool "df" diff -Nru toybox-0.8.8+dfsg/toys/posix/file.c toybox-0.8.9+dfsg/toys/posix/file.c --- toybox-0.8.8+dfsg/toys/posix/file.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/file.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,7 +4,7 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html -USE_FILE(NEWTOY(file, "<1bhLs[!hL]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_FILE(NEWTOY(file, "<1b(brief)hLs[!hL]", TOYFLAG_USR|TOYFLAG_BIN)) config FILE bool "file" @@ -177,10 +177,10 @@ goto bad; } - if (n_namesz==4 && !memcmp(note+12, "GNU", 4) && n_type==3) { + if (n_namesz==4 && !smemcmp(note+12, "GNU", 4) && n_type==3) { printf(", BuildID="); for (j = 0; j = 4) { printf(", for Android %d", (int)elf_int(note+20, 4)); // NDK r14 and later also include NDK version info. OS binaries @@ -244,7 +244,7 @@ s-3, (int)peek_le(s, 2), (int)peek_le(s+2, 2)); // TODO: parsing JPEG for width/height is harder than GIF or PNG. - else if (len>32 && !memcmp(s, "\xff\xd8", 2)) xputs("JPEG image data"); + else if (len>32 && !smemcmp(s, "\xff\xd8", 2)) xputs("JPEG image data"); else if (len>8 && strstart(&s, "\xca\xfe\xba\xbe")) { unsigned count = peek_be(s, 4), i, arch; @@ -307,7 +307,7 @@ else if (len>31 && peek_be(s, 7) == 0xfd377a585a0000UL) xputs("xz compressed data"); else if (len>10 && strstart(&s, "\x1f\x8b")) xputs("gzip compressed data"); - else if (len>32 && !memcmp(s+1, "\xfa\xed\xfe", 3)) { + else if (len>32 && !smemcmp(s+1, "\xfa\xed\xfe", 3)) { int bit = (*s==0xce) ? 32 : 64; char *what = 0; @@ -325,26 +325,26 @@ else what = NULL; if (what) xprintf("%s\n", what); else xprintf("(bad type %d)\n", s[9]); - } else if (len>36 && !memcmp(s, "OggS\x00\x02", 6)) { + } else if (len>36 && !smemcmp(s, "OggS\x00\x02", 6)) { xprintf("Ogg data"); // https://wiki.xiph.org/MIMETypesCodecs - if (!memcmp(s+28, "CELT ", 8)) xprintf(", celt audio"); - else if (!memcmp(s+28, "CMML ", 8)) xprintf(", cmml text"); - else if (!memcmp(s+28, "BBCD\0", 5)) xprintf(", dirac video"); - else if (!memcmp(s+28, "\177FLAC", 5)) xprintf(", flac audio"); - else if (!memcmp(s+28, "\x8bJNG\r\n\x1a\n", 8)) xprintf(", jng video"); - else if (!memcmp(s+28, "\x80kate\0\0\0", 8)) xprintf(", kate text"); - else if (!memcmp(s+28, "OggMIDI\0", 8)) xprintf(", midi text"); - else if (!memcmp(s+28, "\x8aMNG\r\n\x1a\n", 8)) xprintf(", mng video"); - else if (!memcmp(s+28, "OpusHead", 8)) xprintf(", opus audio"); - else if (!memcmp(s+28, "PCM ", 8)) xprintf(", pcm audio"); - else if (!memcmp(s+28, "\x89PNG\r\n\x1a\n", 8)) xprintf(", png video"); - else if (!memcmp(s+28, "Speex ", 8)) xprintf(", speex audio"); - else if (!memcmp(s+28, "\x80theora", 7)) xprintf(", theora video"); - else if (!memcmp(s+28, "\x01vorbis", 7)) xprintf(", vorbis audio"); - else if (!memcmp(s+28, "YUV4MPEG", 8)) xprintf(", yuv4mpeg video"); + if (!smemcmp(s+28, "CELT ", 8)) xprintf(", celt audio"); + else if (!smemcmp(s+28, "CMML ", 8)) xprintf(", cmml text"); + else if (!smemcmp(s+28, "BBCD", 5)) xprintf(", dirac video"); + else if (!smemcmp(s+28, "\177FLAC", 5)) xprintf(", flac audio"); + else if (!smemcmp(s+28, "\x8bJNG\r\n\x1a\n", 8)) xprintf(", jng video"); + else if (!smemcmp(s+28, "\x80kate\0\0", 8)) xprintf(", kate text"); + else if (!smemcmp(s+28, "OggMIDI", 8)) xprintf(", midi text"); + else if (!smemcmp(s+28, "\x8aMNG\r\n\x1a\n", 8)) xprintf(", mng video"); + else if (!smemcmp(s+28, "OpusHead", 8)) xprintf(", opus audio"); + else if (!smemcmp(s+28, "PCM ", 8)) xprintf(", pcm audio"); + else if (!smemcmp(s+28, "\x89PNG\r\n\x1a\n", 8)) xprintf(", png video"); + else if (!smemcmp(s+28, "Speex ", 8)) xprintf(", speex audio"); + else if (!smemcmp(s+28, "\x80theora", 7)) xprintf(", theora video"); + else if (!smemcmp(s+28, "\x01vorbis", 7)) xprintf(", vorbis audio"); + else if (!smemcmp(s+28, "YUV4MPEG", 8)) xprintf(", yuv4mpeg video"); xputc('\n'); - } else if (len>32 && !memcmp(s, "RIF", 3) && !memcmp(s+8, "WAVEfmt ", 8)) { + } else if (len>32 && !smemcmp(s, "RIF", 3) && !smemcmp(s+8, "WAVEfmt ", 8)) { // https://en.wikipedia.org/wiki/WAV int le = (s[3] == 'F'); int format = le ? peek_le(s+20, 2) : peek_be(s+20, 2); @@ -372,7 +372,7 @@ else xprintf("unknown format %d", format); xputc('\n'); } else if (len>12 && peek_be(s, 4)==0x10000) xputs("TrueType font"); - else if (len>12 && !memcmp(s, "ttcf\x00", 5)) { + else if (len>12 && !smemcmp(s, "ttcf", 5)) { xprintf("TrueType font collection, version %d, %d fonts\n", (int)peek_be(s+4, 2), (int)peek_be(s+8, 4)); @@ -382,16 +382,14 @@ else if (strstart(&s,"-----BEGIN CERTIFICATE-----")) xputs("PEM certificate"); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680547(v=vs.85).aspx - else if (len>0x70 && !memcmp(s, "MZ", 2) && - (magic=peek_le(s+0x3c,4)) 0x70 && !smemcmp(s, "MZ", 2) && + (magic=peek_le(s+0x3c,4)) 0x32 && !memcmp(s, "BM", 2) && !peek_be(s+6, 4)) { + } else if (len>0x32 && !smemcmp(s, "BM", 2) && !peek_be(s+6, 4)) { xprintf("BMP image, %d x %d, %d bpp\n", (int)peek_le(s+18, 4), (int)peek_le(s+22,4), (int)peek_le(s+28, 2)); @@ -436,7 +432,7 @@ (int)peek_le(s+12, 4), (int)peek_le(s+20, 4)); // https://android.googlesource.com/platform/system/tools/mkbootimg/+/refs/heads/master/include/bootimg/bootimg.h - } else if (len>1632 && !memcmp(s, "ANDROID!", 8)) { + } else if (len>1632 && !smemcmp(s, "ANDROID!", 8)) { xprintf("Android boot image v%d\n", (int)peek_le(s+40, 4)); // https://source.android.com/devices/architecture/dto/partitions @@ -445,7 +441,7 @@ (int)peek_be(s+16, 4)); // frameworks/base/core/java/com/android/internal/util/BinaryXmlSerializer.java - } else if (len>4 && !memcmp(s, "ABX", 3)) { + } else if (len>4 && !smemcmp(s, "ABX", 3)) { xprintf("Android Binary XML v%d\n", s[3]); // Text files, including shell scripts. diff -Nru toybox-0.8.8+dfsg/toys/posix/find.c toybox-0.8.9+dfsg/toys/posix/find.c --- toybox-0.8.8+dfsg/toys/posix/find.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/find.c 2023-01-10 19:24:45.000000000 +0000 @@ -617,7 +617,7 @@ ff = 0; ch = *fmt; - // long long is its own stack size on LP64, so handle seperately + // long long is its own stack size on LP64, so handle separately if (ch == 'i' || ch == 's') { strcpy(next+len, "lld"); printf(next, (ch == 'i') ? (long long)new->st.st_ino diff -Nru toybox-0.8.8+dfsg/toys/posix/grep.c toybox-0.8.9+dfsg/toys/posix/grep.c --- toybox-0.8.8+dfsg/toys/posix/grep.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/grep.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,13 +4,10 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html * - * Posix doesn't even specify -r, documenting deviations from it is silly. -* echo hello | grep -w '' -* echo '' | grep -w '' -* echo hello | grep -f next) - shoe->rc = 0; + for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) shoe->rc = 0; // Loop to handle multiple matches in same line do { regmatch_t *mm = (void *)toybuf; + struct arg_list *seek; - // Handle "fixed" (literal) matches - if (FLAG(F)) { - struct arg_list *seek, fseek; - char *s = 0; - - for (seek = TT.e; seek; seek = seek->next) { - if (FLAG(x)) { - if (!(FLAG(i) ? strcasecmp : strcmp)(seek->arg, line)) s = line; - } else if (!*seek->arg) { - // No need to set fseek.next because this will match every line. - seek = &fseek; - fseek.arg = s = line; - } else if (FLAG(i)) s = strcasestr(start, seek->arg); - else s = strstr(start, seek->arg); + mm->rm_so = mm->rm_eo = 0; + rc = 1; - if (s) break; + // Handle "fixed" (literal) matches (if any) + if (TT.e && *start) for (ss = start; ss-line next) { + if (*(pp = seek->arg)=='^' && !FLAG(F)) { + if (ss!=start) continue; + pp++; + } + for (ii = 0; pp[ii] && ss[ii]; ii++) { + if (!FLAG(F)) { + if (pp[ii]=='.') continue; + if (pp[ii]=='\\' && pp[ii+1]) pp++; + else if (pp[ii]=='$' && !pp[ii+1]) break; + } + if (FLAG(i)) { + if (toupper(pp[ii])!=toupper(ss[ii])) break; + } else if (pp[ii]!=ss[ii]) break; + } + if (pp[ii] && (pp[ii]!='$' || pp[ii+1] || ss[ii])) continue; + mm->rm_eo = (mm->rm_so = ss-start)+ii; + rc = 0; + + goto got; } + if (FLAG(x)) break; + } - if (s) { - rc = 0; - mm->rm_so = (s-start); - mm->rm_eo = (s-start)+strlen(seek->arg); - } else rc = 1; - - // Handle regex matches - } else { - int baseline = mm->rm_eo; - - mm->rm_so = mm->rm_eo = INT_MAX; - rc = 1; - for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) { - - // Do we need to re-check this regex? - if (!shoe->rc) { - shoe->m.rm_so -= baseline; - shoe->m.rm_eo -= baseline; - if (!matched || shoe->m.rm_so<0) - shoe->rc = regexec0(&shoe->r, start, ulen-(start-line), 1, - &shoe->m, start==line ? 0 : REG_NOTBOL); - } + // Empty pattern always matches + if (rc && *TT.fixed && !FLAG(o)) rc = 0; +got: + // Handle regex matches (if any) + for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) { + // Do we need to re-check this regex? + if (!shoe->rc) { + shoe->m.rm_so -= move; + shoe->m.rm_eo -= move; + if (!matched || shoe->m.rm_so<0) + shoe->rc = regexec0(&shoe->r, start, ulen-(start-line), 1, + &shoe->m, start==line ? 0 : REG_NOTBOL); + } - // If we got a match, is it a _better_ match? - if (!shoe->rc && (shoe->m.rm_so < mm->rm_so || - (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= mm->rm_eo))) - { - mm = &shoe->m; - rc = 0; - } + // If we got a match, is it a _better_ match? + if (!shoe->rc && (rc || shoe->m.rm_so < mm->rm_so || + (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= mm->rm_eo))) + { + mm = &shoe->m; + rc = 0; } } if (!rc && FLAG(o) && !mm->rm_eo && ulen>start-line) { - start++; + move = 1; continue; } @@ -238,7 +235,7 @@ if (!isalnum(c) && c != '_') c = 0; } if (c) { - start += mm->rm_so+1; + move = mm->rm_so+1; continue; } } @@ -247,7 +244,7 @@ if (FLAG(o)) { if (rc) mm->rm_eo = ulen-(start-line); else if (!mm->rm_so) { - start += mm->rm_eo; + move = mm->rm_eo; continue; } else mm->rm_eo = mm->rm_so; } else { @@ -271,7 +268,7 @@ xexit(); } if (FLAG(L) || FLAG(l)) { - if (FLAG(l)) xprintf("%s%c", name, TT.outdelim); + if (FLAG(l)) xprintf("%s%c", name, '\n'*!FLAG(Z)); free(line); fclose(file); return; @@ -308,9 +305,8 @@ } } - start += mm->rm_eo; - if (mm->rm_so == mm->rm_eo) break; - } while (*start); + if (mm->rm_so == (move = mm->rm_eo)) break; + } while (*(start += move)); offset += len; if (matched) { @@ -318,7 +314,7 @@ if (FLAG(color) && !FLAG(o)) { xputsn(TT.grey); if (ulen > start-line) xputsl(start, ulen-(start-line)); - xputc(TT.outdelim); + xputc(TT.delim); } mcount++; } else { @@ -355,7 +351,7 @@ if (FLAG(m) && mcount >= TT.m) break; } - if (FLAG(L)) xprintf("%s%c", name, TT.outdelim); + if (FLAG(L)) xprintf("%s%c", name, TT.delim); else if (FLAG(c)) outline(0, ':', name, mcount, 0, 1); // loopfiles will also close the fd, but this frees an (opaque) struct. @@ -368,21 +364,32 @@ } } +static int lensort(struct arg_list **a, struct arg_list **b) +{ + long la = strlen((*a)->arg), lb = strlen((*b)->arg); + + if (la lb) return 1; + + return 0; +} + static void parse_regex(void) { - struct arg_list *al, *new, *list = NULL; - char *s, *ss; + struct arg_list *al, *new, *list = NULL, **last; + char *s, *ss, *special = "\\.^$[()|*+?{"; + int len, ii, key; // Add all -f lines to -e list. (Yes, this is leaking allocation context for // exit to free. Not supporting nofork for this command any time soon.) al = TT.f ? TT.f : TT.e; while (al) { if (TT.f) { - if (!*(s = ss = xreadfile(al->arg, 0, 0))) { - free(ss); + if (!*(s = xreadfile(al->arg, 0, 0))) { + free(s); s = 0; - } - } else s = ss = al->arg; + } else if (*(ss = s+strlen(s)-1)=='\n') *ss = 0; + } else s = al->arg; // Advance, when we run out of -f switch to -e. al = al->next; @@ -392,30 +399,70 @@ } if (!s) continue; + // NOTE: even with -z, -f is still \n delimited. Blank line = match all // Split lines at \n, add individual lines to new list. do { - ss = FLAG(z) ? 0 : strchr(s, '\n'); - if (ss) *(ss++) = 0; + if ((ss = strchr(s, '\n'))) *(ss++) = 0; new = xmalloc(sizeof(struct arg_list)); new->next = list; new->arg = s; list = new; s = ss; - } while (ss && *s); + } while (s); } TT.e = list; - if (!FLAG(F)) { - // Convert regex list - for (al = TT.e; al; al = al->next) { + // Convert to regex where appropriate + for (last = &TT.e; *last;) { + // Can we use the fast path? + s = (*last)->arg; + if ('.'!=*s && !FLAG(F) && strcmp(s, "^$")) for (; *s; s++) { + if (*s=='\\') { + if (!s[1] || !strchr(special, *++s)) break; + if (!FLAG(E) && *s=='(') break; + } else if (*s>127 || strchr(special+4, *s)) break; + } + + // Add entry to fast path (literal-ish match) or slow path (regexec) + if (!*s || FLAG(F)) last = &((*last)->next); + else { struct reg *shoe; - if (FLAG(o) && !*al->arg) continue; dlist_add_nomalloc(&TT.reg, (void *)(shoe = xmalloc(sizeof(struct reg)))); - xregcomp(&shoe->r, al->arg, + xregcomp(&shoe->r, (*last)->arg, (REG_EXTENDED*!!FLAG(E))|(REG_ICASE*!!FLAG(i))); + al = *last; + *last = (*last)->next; + free(al); + } + } + dlist_terminate(TT.reg); + + // Sort fast path patterns into buckets by first character + for (al = TT.e; al; al = new) { + new = al->next; + if (FLAG(F)) key = 0; + else { + key = '^'==*al->arg; + if ('\\'==al->arg[key]) key++; + else if ('$'==al->arg[key] && !al->arg[key+1]) key++; } - dlist_terminate(TT.reg); + key = al->arg[key]; + if (FLAG(i)) key = toupper(key); + al->next = TT.fixed[key]; + TT.fixed[key] = al; + } + + // Sort each fast path pattern set by length so first hit is longest match + if (TT.e) for (key = 0; key<256; key++) { + if (!TT.fixed[key]) continue; + for (len = 0, al = TT.fixed[key]; al; al = al->next) len++; + last = xmalloc(len*sizeof(void *)); + for (len = 0, al = TT.fixed[key]; al; al = al->next) last[len++] = al; + qsort(last, len, sizeof(void *), (void *)lensort); + for (ii = 0; ii
next = ii ? last[ii-1] : 0; + TT.fixed[key] = last[len-1]; + free(last); } } @@ -476,8 +523,7 @@ if (!TT.A) TT.A = TT.C; if (!TT.B) TT.B = TT.C; - TT.indelim = '\n' * !FLAG(z); - TT.outdelim = '\n' * !FLAG(Z); + TT.delim = '\n' * !FLAG(z); // Handle egrep and fgrep if (*toys.which->name == 'e') toys.optflags |= FLAG_E; diff -Nru toybox-0.8.8+dfsg/toys/posix/ln.c toybox-0.8.9+dfsg/toys/posix/ln.c --- toybox-0.8.8+dfsg/toys/posix/ln.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/ln.c 2023-01-10 19:24:45.000000000 +0000 @@ -52,13 +52,15 @@ } else buf.st_mode = 0; for (i=0; i type < 2) { char c = TT.buf[(*offset)++]; - pad += 4; + pad += 4; if (!t->type) { c &= 127; if (c<=32) sprintf(buf, "%.3s", ascii+(3*c)); @@ -93,8 +93,7 @@ // Integer types } else { unsigned long long ll = 0, or; - char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"}, - *class = c[t->type-2]; + char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"}, *class = c[t->type-2]; // Work out width of field if (t->size == 8) { @@ -126,7 +125,6 @@ static void od_outline(void) { - unsigned flags = toys.optflags; char buf[128], *abases[] = {"", "%07lld", "%07llo", "%06llx"}; struct odtype *types = (struct odtype *)toybuf; int i, j, len, pad; @@ -134,8 +132,8 @@ if (TT.leftover next) append_base(arg->arg); - if (toys.optflags & FLAG_b) append_base("o1"); - if (toys.optflags & FLAG_c) append_base("c"); - if (toys.optflags & FLAG_d) append_base("u2"); - if (toys.optflags & FLAG_o) append_base("o2"); - if (toys.optflags & FLAG_s) append_base("d2"); - if (toys.optflags & FLAG_x) append_base("x2"); + if (FLAG(b)) append_base("o1"); + if (FLAG(c)) append_base("c"); + if (FLAG(d)) append_base("u2"); + if (FLAG(o)) append_base("o2"); + if (FLAG(s)) append_base("d2"); + if (FLAG(x)) append_base("x2"); if (!TT.types) append_base("o2"); loopfiles(toys.optargs, do_od); diff -Nru toybox-0.8.8+dfsg/toys/posix/patch.c toybox-0.8.9+dfsg/toys/posix/patch.c --- toybox-0.8.8+dfsg/toys/posix/patch.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/patch.c 2023-01-10 19:24:45.000000000 +0000 @@ -449,7 +449,7 @@ if (del) { if (!FLAG(s)) printf("removing %s\n", name); - xunlink(name); + if (!FLAG(dry_run)) xunlink(name); state = 0; // If we've got a file to open, do so. } else if (!FLAG(p) || i <= TT.p) { @@ -457,8 +457,11 @@ if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK)) { if (!FLAG(s)) printf("creating %s\n", name); - if (mkpath(name)) perror_exit("mkpath %s", name); - TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); + if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR); + else { + if (mkpath(name)) perror_exit("mkpath %s", name); + TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); + } } else { if (!FLAG(s)) printf("patching %s\n", name); TT.filein = xopenro(name); diff -Nru toybox-0.8.8+dfsg/toys/posix/printf.c toybox-0.8.9+dfsg/toys/posix/printf.c --- toybox-0.8.8+dfsg/toys/posix/printf.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/printf.c 2023-01-10 19:24:45.000000000 +0000 @@ -23,7 +23,7 @@ #include "toys.h" // Detect matching character (return true/false) and advance pointer if match. -static int eat(char **s, char c) +static int chrstart(char **s, char c) { int x = (**s == c); @@ -42,7 +42,7 @@ if (*ptr == 'c') xexit(); // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits. - if (eat(&ptr, 'x')) base = 16; + if (chrstart(&ptr, 'x')) base = 16; else { if (posix && *ptr=='0') ptr++; if (*ptr >= '0' && *ptr <= '7') base = 8; @@ -85,8 +85,8 @@ // Loop through characters in format while (*f) { - if (eat(&f, '\\')) putchar(handle_slash(&f, 0)); - else if (!eat(&f, '%') || *f == '%') putchar(*f++); + if (chrstart(&f, '\\')) putchar(handle_slash(&f, 0)); + else if (!chrstart(&f, '%') || *f == '%') putchar(*f++); // Handle %escape else { @@ -97,10 +97,10 @@ *to++ = '%'; while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++; for (;;) { - if (eat(&f, '*')) { + if (chrstart(&f, '*')) { if (*arg) wp[i] = atolx(*arg++); } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0'; - if (i++ || !eat(&f, '.')) break; + if (i++ || !chrstart(&f, '.')) break; wp[1] = 0; } c = *f++; @@ -110,7 +110,8 @@ // Output %esc using parsed format string if (c == 'b') { - while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa, 1) : *aa++); + while (*aa) + putchar(chrstart(&aa, '\\') ? handle_slash(&aa, 1) : *aa++); continue; } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa); diff -Nru toybox-0.8.8+dfsg/toys/posix/ps.c toybox-0.8.9+dfsg/toys/posix/ps.c --- toybox-0.8.8+dfsg/toys/posix/ps.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/ps.c 2023-01-10 19:24:45.000000000 +0000 @@ -845,7 +845,7 @@ off_t temp = 6; sprintf(buf, "%lld/exe", slot[SLOT_tid]); - if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) { + if (readfileat(fd, buf, buf, &temp) && !smemcmp(buf, "\177ELF", 4)) { if (buf[4] == 1) slot[SLOT_bits] = 32; else if (buf[4] == 2) slot[SLOT_bits] = 64; } diff -Nru toybox-0.8.8+dfsg/toys/posix/rm.c toybox-0.8.9+dfsg/toys/posix/rm.c --- toybox-0.8.8+dfsg/toys/posix/rm.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/rm.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,7 +4,7 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html -USE_RM(NEWTOY(rm, "fiRrv[-fi]", TOYFLAG_BIN)) +USE_RM(NEWTOY(rm, "f(force)iRrv[-fi]", TOYFLAG_BIN)) config RM bool "rm" diff -Nru toybox-0.8.8+dfsg/toys/posix/sed.c toybox-0.8.9+dfsg/toys/posix/sed.c --- toybox-0.8.8+dfsg/toys/posix/sed.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/sed.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,6 +4,8 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html * + * xform See https://www.gnu.org/software/tar/manual/html_section/transform.html + * * TODO: lines > 2G could wrap signed int length counters. Not just getline() * but N and s/// * TODO: make y// handle unicode, unicode delimiters @@ -12,12 +14,15 @@ * test '//q' with no previous regex, also repeat previous regex? * * Deviations from POSIX: allow extended regular expressions with -r, - * editing in place with -i, separate with -s, NUL-separated input with -z, + * editing in place with -i, separate with -s, NUL-delimited strings with -z, * printf escapes in text, line continuations, semicolons after all commands, * 2-address anywhere an address is allowed, "T" command, multiline * continuations for [abc], \; to end [abc] argument before end of line. + * Explicit violations of stuff posix says NOT to do: N at EOF does default + * print, l escapes \n + * Added --tarxform mode to support tar --xform -USE_SED(NEWTOY(sed, "(help)(version)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP)) +USE_SED(NEWTOY(sed, "(help)(version)(tarxform)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP)) config SED bool "sed" @@ -74,8 +79,8 @@ G Get remembered line (appending to current line) h Remember this line (overwriting remembered line) H Remember this line (appending to remembered line, if any) - l Print line escaping \abfrtv (but not \n), octal escape other nonprintng - chars, wrap lines to terminal width with \, append $ to end of line. + l Print line escaping \abfrtvn, octal escape other nonprintng chars, + wrap lines to terminal width with \, append $ to end of line. n Print default output and read next line over current line (quit at EOF) N Append \n and next line of input to this line. Quit at EOF without default output. Advances line counter for ADDRESS and "=". @@ -126,12 +131,12 @@ // processed pattern list struct double_list *pattern; - char *nextline, *remember; + char *nextline, *remember, *tarxform; void *restart, *lastregex; long nextlen, rememberlen, count; int fdout, noeol; - unsigned xx; - char delim; + unsigned xx, tarxlen, xflags; + char delim, xftype; ) // Linked list of parsed sed commands. Offset fields indicate location where @@ -147,21 +152,38 @@ int rmatch[2]; // offset of regex struct for prefix matches (/abc/,/def/p) int arg1, arg2, w; // offset of two arguments per command, plus s//w filename unsigned not, hit; - unsigned sflags; // s///flag bits: i=1, g=2, p=4, x=8 + unsigned sflags; // s///flag bits, see SFLAG macros below char c; // action }; +#define SFLAG_i 1 +#define SFLAG_g 2 +#define SFLAG_p 4 +#define SFLAG_x 8 +#define SFLAG_slash 16 +#define SFLAG_R 32 +#define SFLAG_S 64 +#define SFLAG_H 128 + // Write out line with potential embedded NUL, handling eol/noeol static int emit(char *line, long len, int eol) { - int l, old = line[len]; + int l = len, old = line[len]; - if (TT.noeol && !writeall(TT.fdout, "\n", 1)) return 1; + if (FLAG(tarxform)) { + TT.tarxform = xrealloc(TT.tarxform, TT.tarxlen+len+TT.noeol+eol); + if (TT.noeol) TT.tarxform[TT.tarxlen++] = TT.delim; + memcpy(TT.tarxform+TT.tarxlen, line, len); + TT.tarxlen += len; + if (eol) TT.tarxform[TT.tarxlen++] = TT.delim; + } else { + if (TT.noeol && !writeall(TT.fdout, &TT.delim, 1)) return 1; + if (eol) line[len++] = TT.delim; + if (!len) return 0; + l = writeall(TT.fdout, line, len); + if (eol) line[len-1] = old; + } TT.noeol = !eol; - if (eol) line[len++] = '\n'; - if (!len) return 0; - l = writeall(TT.fdout, line, len); - if (eol) line[len-1] = old; if (l != len) { if (TT.fdout != 1) perror_msg("short write"); @@ -180,7 +202,7 @@ if (newline) newlen = -newlen; s = *old = xrealloc(*old, oldlen+newlen+newline+1); - if (newline) s[oldlen++] = '\n'; + if (newline) s[oldlen++] = TT.delim; memcpy(s+oldlen, new, newlen); s[oldlen+newlen] = 0; @@ -206,32 +228,52 @@ int file; char *str; } *append = 0; - char *line = TT.nextline; - long len = TT.nextlen; + char *line; + long len; struct sedcmd *command; int eol = 0, tea = 0; - // Ignore EOF for all files before last unless -i - if (!pline && !FLAG(i) && !FLAG(s)) return; + if (FLAG(tarxform)) { + if (!pline) return; - // Grab next line for deferred processing (EOF detection: we get a NULL - // pline at EOF to flush last line). Note that only end of _last_ input - // file matches $ (unless we're doing -i). - TT.nextline = 0; - TT.nextlen = 0; - if (pline) { - TT.nextline = *pline; - TT.nextlen = plen; + line = *pline; + len = plen; *pline = 0; + pline = 0; + } else { + line = TT.nextline; + len = TT.nextlen; + + // Ignore EOF for all files before last unless -i or -s + if (!pline && !FLAG(i) && !FLAG(s)) return; + + // Grab next line for deferred processing (EOF detection: we get a NULL + // pline at EOF to flush last line). Note that only end of _last_ input + // file matches $ (unless we're doing -i). + TT.nextline = 0; + TT.nextlen = 0; + if (pline) { + TT.nextline = *pline; + TT.nextlen = plen; + *pline = 0; + } } if (!line || !len) return; - if (line[len-1] == '\n') line[--len] = eol++; + if (line[len-1] == TT.delim) line[--len] = eol++; + if (FLAG(tarxform) && len) { + TT.xftype = line[--len]; + line[len] = 0; + } TT.count++; - // The restart-1 is because we added one to make sure it wasn't NULL, - // otherwise N as last command would restart script - command = TT.restart ? ((struct sedcmd *)TT.restart)-1 : (void *)TT.pattern; + // To prevent N as last command from restarting script, we added 1 to restart + // so we'd use it here even when NULL. Alas, compilers that think C has + // references instead of pointers assume ptr-1 can never be NULL (demonstrably + // untrue) and inappropriately dead code eliminate, so use LP64 math until + // we get a -fpointers-are-not-references compiler option. + command = (void *)(TT.restart ? ((unsigned long)TT.restart)-1 + : (unsigned long)TT.pattern); TT.restart = 0; while (command) { @@ -328,7 +370,7 @@ } else if (c=='D') { // Delete up to \n or end of buffer str = line; - while ((str-line) = ' ') toybuf[off++] = line[i]; else off += sprintf(toybuf+off, "\\%03o", line[i]); } toybuf[off++] = '$'; emit(toybuf, off, 1); } else if (c=='n') { - TT.restart = command->next+1; + // The +1 forces restart processing even when next is null + TT.restart = (void *)(((unsigned long)command->next)+1); break; } else if (c=='N') { // Can't just grab next line because we could have multiple N and // we need to actually read ahead to get N;$p EOF detection right. if (pline) { - TT.restart = command->next+1; + // The +1 forces restart processing even when next is null + TT.restart = (void *)(((unsigned long)command->next)+1); extend_string(&line, TT.nextline, len, -TT.nextlen); free(TT.nextline); TT.nextline = line; @@ -407,7 +451,7 @@ // Pending append goes out right after N goto done; } else if (c=='p' || c=='P') { - char *l = (c=='P') ? strchr(line, '\n') : 0; + char *l = (c=='P') ? strchr(line, TT.delim) : 0; if (emit(line, l ? l-line : len, eol)) break; } else if (c=='q' || c=='Q') { @@ -425,14 +469,24 @@ regmatch_t *match = (void *)toybuf; regex_t *reg = get_regex(command, command->arg1); int mflags = 0, count = 0, l2used = 0, zmatch = 1, l2l = len, l2old = 0, - mlen, off, newlen; + bonk = 0, mlen, off, newlen; + + // Skip suppressed --tarxform types + if (TT.xftype && (command->sflags & (SFLAG_R< sflags & SFLAG_slash) && mlen==len) { + while (len && ++bonk && line[--len]=='/'); + continue; + } + mflags = REG_NOTBOL; // Zero length matches don't count immediately after a previous match - mlen = match[0].rm_eo-match[0].rm_so; if (!mlen && !zmatch) { if (rline-line == len) break; l2[l2used++] = *rline++; @@ -441,7 +495,7 @@ } else zmatch = 0; // If we're replacing only a specific match, skip if this isn't it - off = command->sflags>>4; + off = command->sflags>>8; if (off && off != ++count) { if (l2) memcpy(l2+l2used, rline, match[0].rm_eo); l2used += match[0].rm_eo; @@ -503,9 +557,9 @@ l2used += newlen; rline += match[0].rm_eo; - // Stop after first substitution unless we have flag g - if (!(command->sflags & 2)) break; + if (!(command->sflags & SFLAG_g)) break; } + len += bonk; // If we made any changes, finish off l2 and swap it for line if (l2) { @@ -518,8 +572,7 @@ } if (mflags) { - // flag p - if (command->sflags & 4) emit(line, len, eol); + if (command->sflags & SFLAG_p) emit(line, len, eol); tea = 1; if (command->w) goto writenow; @@ -529,6 +582,8 @@ char *name; writenow: + if (FLAG(tarxform)) error_exit("tilt"); + // Swap out emit() context fd = TT.fdout; noeol = TT.noeol; @@ -572,9 +627,10 @@ command = command->next; } +done: if (line && !FLAG(n)) emit(line, len, eol); -done: + // TODO: should "sed -z ax" use \n instead of NUL? if (dlist_terminate(append)) while (append) { struct append *a = append->next; @@ -583,7 +639,7 @@ // Force newline if noeol pending if (fd != -1) { - if (TT.noeol) xwrite(TT.fdout, "\n", 1); + if (TT.noeol) xwrite(TT.fdout, &TT.delim, 1); TT.noeol = 0; xsendfile(fd, TT.fdout); close(fd); @@ -594,6 +650,12 @@ append = a; } free(line); + + if (TT.tarxlen) { + dprintf(TT.fdout, "%08x", --TT.tarxlen); + writeall(TT.fdout, TT.tarxform, TT.tarxlen); + TT.tarxlen = 0; + } } // Callback called on each input file @@ -736,6 +798,16 @@ } if (!*line) return; + if (FLAG(tarxform) && strstart(&line, "flags=")) { + TT.xflags = 7; + while (0<=(i = stridx("rRsShH", *line))) { + if (i&1) TT.xflags |= 1<<(i>>1); + else TT.xflags &= ~(1<<(i>>1)); + line++; + } + continue; + } + // Start by writing data into toybuf. errstart = line; @@ -839,27 +911,36 @@ i = command->arg1; command->arg1 = command->arg2; command->arg2 = i; + command->sflags = TT.xflags*SFLAG_R; // get flags for (line++; *line; line++) { long l; if (isspace(*line) && *line != '\n') continue; - if (0 <= (l = stridx("igpx", *line))) command->sflags |= 1< sflags |= 1<<0; - else if (!(command->sflags>>4) && 0<(l = strtol(line, &line, 10))) { - command->sflags |= l << 4; + else if (FLAG(tarxform) && 0 <= (l = stridx("RSH", *line))) + command->sflags |= SFLAG_R< sflags &= ~(SFLAG_R< sflags>>8) && 0<(l = strtol(line, &line, 10))) { + command->sflags |= l << 8; line--; } else break; } - flags = (FLAG(r) || (command->sflags&8)) ? REG_EXTENDED : 0; - if (command->sflags&1) flags |= REG_ICASE; + flags = (FLAG(r) || (command->sflags & SFLAG_x)) ? REG_EXTENDED : 0; + if (command->sflags & SFLAG_i) flags |= REG_ICASE; // We deferred actually parsing the regex until we had the s///i flag // allocating the space was done by extend_string() above if (!*TT.remember) command->arg1 = 0; - else xregcomp((void *)(command->arg1+(char *)command),TT.remember,flags); + else { + xregcomp((void *)(command->arg1+(char *)command), TT.remember, flags); + if (FLAG(tarxform) && TT.remember[strlen(TT.remember)-1]=='/') + command->sflags |= SFLAG_slash; + } free(TT.remember); TT.remember = 0; if (*line == 'w') { @@ -980,11 +1061,17 @@ error_exit("bad pattern '%s'@%ld (%c)", errstart, line-errstart+1L, *line); } +// Is the pointer "find" within the string "range". +static int instr(char *find, char *range) +{ + return find>=range && range+strlen(range)>=find; +} + void sed_main(void) { - struct arg_list *al; - char **args = toys.optargs; + char **args = toys.optargs, **aa; + if (FLAG(tarxform)) toys.optflags |= FLAG_z; if (!FLAG(z)) TT.delim = '\n'; // Lie to autoconf when it asks stupid questions, so configure regexes @@ -1006,13 +1093,18 @@ (TT.e = xzalloc(sizeof(struct arg_list)))->arg = *(args++); } - // Option parsing infrastructure can't interlace "-e blah -f blah -e blah" - // so handle all -e, then all -f. (At least the behavior's consistent.) - - for (al = TT.e; al; al = al->next) parse_pattern(&al->arg, strlen(al->arg)); + // -e and -f care about order, so use argv[] to recreate original order + for (aa = toys.argv+1; *aa; aa++) { + if (TT.e && instr(TT.e->arg, *aa)) { + parse_pattern(&TT.e->arg, strlen(TT.e->arg)); + free(llist_pop(&TT.e)); + } + if (TT.f && instr(TT.f->arg, *aa)) { + do_lines(xopenro(TT.f->arg), TT.delim, parse_pattern); + free(llist_pop(&TT.f)); + } + } parse_pattern(0, 0); - for (al = TT.f; al; al = al->next) - do_lines(xopenro(al->arg), TT.delim, parse_pattern); dlist_terminate(TT.pattern); if (TT.nextlen) error_exit("no }"); diff -Nru toybox-0.8.8+dfsg/toys/posix/sleep.c toybox-0.8.9+dfsg/toys/posix/sleep.c --- toybox-0.8.8+dfsg/toys/posix/sleep.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/sleep.c 2023-01-10 19:24:45.000000000 +0000 @@ -24,7 +24,10 @@ void sleep_main(void) { struct timespec ts; + char **args; - xparsetimespec(*toys.optargs, &ts); - toys.exitval = !!nanosleep(&ts, NULL); + for (args = toys.optargs; !toys.exitval && *args; args++) { + xparsetimespec(*args, &ts); + toys.exitval = !!nanosleep(&ts, NULL); + } } diff -Nru toybox-0.8.8+dfsg/toys/posix/sort.c toybox-0.8.9+dfsg/toys/posix/sort.c --- toybox-0.8.8+dfsg/toys/posix/sort.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/sort.c 2023-01-10 19:24:45.000000000 +0000 @@ -7,7 +7,7 @@ * Deviations from POSIX: Lots. * We invented -x -USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) +USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMCcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) config SORT bool "sort" @@ -21,7 +21,8 @@ -u Unique lines only -n Numeric order (instead of alphabetical) -b Ignore leading blanks (or trailing blanks in second part of key) - -c Check whether input is sorted + -C Check whether input is sorted + -c Warn if input is unsorted -d Dictionary order (use alphanumeric and whitespace chars only) -f Force uppercase (case insensitive sort) -i Ignore nonprinting characters @@ -282,9 +283,12 @@ *pline = 0; // handle -c here so we don't allocate more memory than necessary. - if (FLAG(c)) { - if (TT.lines && compare_keys((void *)&TT.lines, &line)>-!!FLAG(u)) - error_exit("%s: Check line %u\n", TT.name, TT.linecount); + if (FLAG(C)||FLAG(c)) { + if (TT.lines && compare_keys((void *)&TT.lines, &line)>-!!FLAG(u)) { + toys.exitval = 1; + if (FLAG(C)) xexit(); + error_exit("%s: Check line %u", TT.name, TT.linecount+1); + } free(TT.lines); TT.lines = (void *)line; } else { @@ -299,7 +303,7 @@ static void sort_read(int fd, char *name) { TT.name = name; - do_lines(fd, FLAG(z) ? '\0' : '\n', sort_lines); + do_lines(fd, '\n'*!FLAG(z), sort_lines); } void sort_main(void) @@ -339,10 +343,9 @@ flag = 1<<(optlist-temp2+strlen(optlist)-1); // Was it a flag that can apply to a key? - if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) { - toys.exitval = 2; + if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) error_exit("Unknown key option."); - } + // b after , means strip _trailing_ space, not leading. if (idx && flag==FLAG_b) flag = FLAG_bb; key->flags |= flag; @@ -362,7 +365,7 @@ // The compare (-c) logic was handled in sort_read(), // so if we got here, we're done. - if (FLAG(c)) goto exit_now; + if (FLAG(C)||FLAG(c)) goto exit_now; // Perform the actual sort qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys); diff -Nru toybox-0.8.8+dfsg/toys/posix/tail.c toybox-0.8.9+dfsg/toys/posix/tail.c --- toybox-0.8.8+dfsg/toys/posix/tail.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/tail.c 2023-01-10 19:24:45.000000000 +0000 @@ -289,8 +289,8 @@ if (!FLAG(n) && !FLAG(c)) { char *arg = *args; - // handle old "-42" style arguments, else default to last 10 lines - if (arg && *arg == '-' && arg[1]) { + // handle old "-42" / "+42" style arguments, else default to last 10 lines + if (arg && (*arg == '-' || *arg == '+') && arg[1]) { TT.n = atolx(*(args++)); toys.optc--; } else TT.n = -10; diff -Nru toybox-0.8.8+dfsg/toys/posix/tar.c toybox-0.8.9+dfsg/toys/posix/tar.c --- toybox-0.8.8+dfsg/toys/posix/tar.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/tar.c 2023-01-10 19:24:45.000000000 +0000 @@ -61,7 +61,7 @@ struct double_list *incl, *excl, *seen; struct string_list *dirs; char *cwd, **xfsed; - int fd, ouid, ggid, hlc, warn, sparselen, pid; + int fd, ouid, ggid, hlc, warn, sparselen, pid, xfpipe[2]; struct dev_ino archive_di; long long *sparse; time_t mtt; @@ -192,6 +192,23 @@ (*b)[len] = 0; } +static char *xform(char **name, char type) +{ + char buf[9], *end; + off_t len; + + if (!TT.xform) return 0; + + buf[8] = 0; + if (dprintf(TT.xfpipe[0], "%s%c%c", *name, type, 0) != strlen(*name)+2 + || readall(TT.xfpipe[1], buf, 8) != 8 + || !(len = estrtol(buf, &end, 16)) || errno ||*end) error_exit("bad xform"); + xreadall(TT.xfpipe[1], *name = xmalloc(len+1), len); + (*name)[len] = 0; + + return *name; +} + // callback from dirtree to create archive static int add_to_tar(struct dirtree *node) { @@ -219,20 +236,23 @@ } // Consume the 1 extra byte alocated in dirtree_path() - if (S_ISDIR(st->st_mode) && name[i-1] != '/') strcat(name, "/"); + if (S_ISDIR(st->st_mode) && lnk[-1] != '/') strcpy(lnk, "/"); // remove leading / and any .. entries from saved name - if (!FLAG(P)) while (*hname == '/') hname++; - for (lnk = hname;;) { - if (!(lnk = strstr(lnk, ".."))) break; - if (lnk == hname || lnk[-1] == '/') { - if (!lnk[2]) goto done; - if (lnk[2]=='/') { - lnk = hname = lnk+3; - continue; + if (!FLAG(P)) { + while (*hname == '/') hname++; + for (lnk = hname;;) { + if (!(lnk = strstr(lnk, ".."))) break; + if (lnk == hname || lnk[-1] == '/') { + if (!lnk[2]) goto done; + if (lnk[2]=='/') { + lnk = hname = lnk+3; + continue; + } } + lnk += 2; } - lnk += 2; + if (!*hname) hname = "./"; } if (!*hname) goto done; @@ -242,17 +262,12 @@ TT.warn = 0; } - // Note: linux sed doesn't add newline, so no need to remove it or use -z. - if (TT.xfsed) - if (!(hname = xfname = xrunread(TT.xfsed, hname))) error_exit("bad xform"); - + // Override dentry data from command line and fill out header data if (TT.owner) st->st_uid = TT.ouid; if (TT.group) st->st_gid = TT.ggid; if (TT.mode) st->st_mode = string_to_mode(TT.mode, st->st_mode); if (TT.mtime) st->st_mtime = TT.mtt; - memset(&hdr, 0, sizeof(hdr)); - strncpy(hdr.name, hname, sizeof(hdr.name)); ITOO(hdr.mode, st->st_mode &07777); ITOO(hdr.uid, st->st_uid); ITOO(hdr.gid, st->st_gid); @@ -260,16 +275,13 @@ ITOO(hdr.mtime, st->st_mtime); strcpy(hdr.magic, "ustar "); - // Hard link or symlink? i=0 neither, i=1 hardlink, i=2 symlink - // Are there hardlinks to a non-directory entry? + lnk = 0; if (st->st_nlink>1 && !S_ISDIR(st->st_mode)) { // Have we seen this dev&ino before? for (i = 0; i st_ino; TT.hlx[TT.hlc].di.dev = st->st_dev; TT.hlc++; - i = 0; } - } else i = 0; + } - // Handle file types - if (i || S_ISLNK(st->st_mode)) { - hdr.type = '1'+!i; - if (!i && !(lnk = xreadlink(name))) { + xfname = xform(&hname, 'r'); + strncpy(hdr.name, hname, sizeof(hdr.name)); + + // Handle file types: 0=reg, 1=hardlink, 2=sym, 3=chr, 4=blk, 5=dir, 6=fifo + if (lnk || S_ISLNK(st->st_mode)) { + hdr.type = '1'+!lnk; + if (lnk) { + if (!xform(&lnk, 'h')) lnk = xstrdup(lnk); + } else if (!(lnk = xreadlink(name))) { perror_msg("readlink"); goto done; - } + } else xform(&lnk, 's'); + maybe_prefix_block(lnk, sizeof(hdr.link), 'K'); strncpy(hdr.link, lnk, sizeof(hdr.link)); - if (!i) free(lnk); + free(lnk); } else if (S_ISREG(st->st_mode)) { hdr.type = '0'; ITOO(hdr.size, st->st_size); @@ -781,11 +798,11 @@ // We accept --show-transformed but always do, so it's a NOP. name = TT.hdr.name; - if (TT.xfsed) { - if (!(name = xrunread(TT.xfsed, name))) error_exit("bad xform"); + if (xform(&name, 'r')) { free(TT.hdr.name); TT.hdr.name = name; } + if ((i = "\0hs"[stridx("12", tar.type)+1])) xform(&TT.hdr.link_target, i); for (i = 0; i tm_mday, lc->tm_hour, lc->tm_min, FLAG(full_time) ? perm : ""); } printf("%s", name); - if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target); + if (TT.hdr.link_target) + printf(" %s %s", tar.type=='2' ? "->" : "link to", TT.hdr.link_target); xputc('\n'); skippy(TT.hdr.size); } else { @@ -868,7 +886,7 @@ dlist_add(list, n); if (i && n[i-1]=='\n') i--; - while (i && n[i-1] == '/') i--; + while (i>1 && n[i-1] == '/') i--; n[i] = 0; } @@ -878,10 +896,14 @@ if (pline) trim2list(TT.X ? &TT.excl : &TT.incl, *pline); } +static char *get_archiver() +{ + return FLAG(I) ? TT.I : FLAG(z) ? "gzip" : FLAG(j) ? "bzip2" : "xz"; +} + void tar_main(void) { - char *s, **args = toys.optargs, - *archiver = FLAG(I) ? TT.I : (FLAG(z) ? "gzip" : (FLAG(J) ? "xz":"bzip2")); + char *s, **xfsed, **args = toys.optargs; int len = 0, ii; // Needed when extracting to command @@ -923,13 +945,17 @@ struct arg_list *al; for (ii = 0, al = TT.xform; al; al = al->next) ii++; - TT.xfsed = xmalloc((ii+1)*2*sizeof(char *)); - TT.xfsed[0] = "sed"; - for (ii = 1, al = TT.xform; al; al = al->next) { - TT.xfsed[ii++] = "-e"; - TT.xfsed[ii++] = al->arg; - } - TT.xfsed[ii] = 0; + xfsed = xmalloc((ii+2)*2*sizeof(char *)); + xfsed[0] = "sed"; + xfsed[1] = "--tarxform"; + for (ii = 2, al = TT.xform; al; al = al->next) { + xfsed[ii++] = "-e"; + xfsed[ii++] = al->arg; + } + xfsed[ii] = 0; + TT.xfpipe[0] = TT.xfpipe[1] = -1; + xpopen_both(xfsed, TT.xfpipe); + free(xfsed); } // nommu reentry for nonseekable input skips this, parent did it for us @@ -964,7 +990,7 @@ if (len!=512 || !is_tar_header(hdr)) { // detect gzip and bzip signatures if (SWAP_BE16(*(short *)hdr)==0x1f8b) toys.optflags |= FLAG_z; - else if (!memcmp(hdr, "BZh", 3)) toys.optflags |= FLAG_j; + else if (!smemcmp(hdr, "BZh", 3)) toys.optflags |= FLAG_j; else if (peek_be(hdr, 7) == 0xfd377a585a0000UL) toys.optflags |= FLAG_J; else error_exit("Not tar"); @@ -976,11 +1002,11 @@ if (FLAG(j)||FLAG(z)||FLAG(I)||FLAG(J)) { int pipefd[2] = {hdr ? -1 : TT.fd, -1}, i, pid; struct string_list *zcat = FLAG(I) ? 0 : find_in_path(getenv("PATH"), - FLAG(j) ? "bzcat" : FLAG(J) ? "xzcat" : "zcat"); + FLAG(z) ? "zcat" : FLAG(j) ? "bzcat" : "xzcat"); // Toybox provides more decompressors than compressors, so try them first TT.pid = xpopen_both(zcat ? (char *[]){zcat->str, 0} : - (char *[]){archiver, "-d", 0}, pipefd); + (char *[]){get_archiver(), "-d", 0}, pipefd); if (CFG_TOYBOX_FREE) llist_traverse(zcat, free); if (!hdr) { @@ -1052,7 +1078,7 @@ if (FLAG(j)||FLAG(z)||FLAG(I)||FLAG(J)) { int pipefd[2] = {-1, TT.fd}; - xpopen_both((char *[]){archiver, 0}, pipefd); + xpopen_both((char *[]){get_archiver(), 0}, pipefd); close(TT.fd); TT.fd = pipefd[0]; } diff -Nru toybox-0.8.8+dfsg/toys/posix/test.c toybox-0.8.9+dfsg/toys/posix/test.c --- toybox-0.8.8+dfsg/toys/posix/test.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/test.c 2023-01-10 19:24:45.000000000 +0000 @@ -93,6 +93,7 @@ if (*count>=2 && *s == '-' && s[1] && !s[2]) { *count = 2; c = s[1]; + if (c=='a') c = 'e'; if (-1 != (i = stridx("hLbcdefgkpSusxwr", c))) { struct stat st; diff -Nru toybox-0.8.8+dfsg/toys/posix/uname.c toybox-0.8.9+dfsg/toys/posix/uname.c --- toybox-0.8.8+dfsg/toys/posix/uname.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/uname.c 2023-01-10 19:24:45.000000000 +0000 @@ -4,7 +4,7 @@ * * See http://opengroup.org/onlinepubs/9699919799/utilities/uname.html -USE_UNAME(NEWTOY(uname, "aomvrns", TOYFLAG_BIN)) +USE_UNAME(NEWTOY(uname, "paomvrns", TOYFLAG_BIN)) USE_ARCH(NEWTOY(arch, 0, TOYFLAG_USR|TOYFLAG_BIN)) USE_LINUX32(NEWTOY(linux32, 0, TOYFLAG_USR|TOYFLAG_BIN)) @@ -60,6 +60,7 @@ } xputsn(c); } + if (FLAG(p)) xputsn(" unknown"+!needspace); xputc('\n'); } diff -Nru toybox-0.8.8+dfsg/toys/posix/xargs.c toybox-0.8.9+dfsg/toys/posix/xargs.c --- toybox-0.8.8+dfsg/toys/posix/xargs.c 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys/posix/xargs.c 2023-01-10 19:24:45.000000000 +0000 @@ -9,7 +9,7 @@ * TODO: -L Max number of lines of input per command * TODO: -x Exit if can't fit everything in one command -USE_XARGS(NEWTOY(xargs, "^E:P#<0=1optrn#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_XARGS(NEWTOY(xargs, "^E:P#<0(null)=1optr(no-run-if-empty)n#<1(max-args)s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN)) config XARGS bool "xargs" diff -Nru toybox-0.8.8+dfsg/toys.h toybox-0.8.9+dfsg/toys.h --- toybox-0.8.8+dfsg/toys.h 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/toys.h 2023-01-10 19:24:45.000000000 +0000 @@ -134,5 +134,5 @@ #ifndef TOYBOX_VENDOR #define TOYBOX_VENDOR "" #endif -#define TOYBOX_VERSION "0.8.8"TOYBOX_VENDOR +#define TOYBOX_VERSION "0.8.9"TOYBOX_VENDOR #endif diff -Nru toybox-0.8.8+dfsg/www/faq.html toybox-0.8.9+dfsg/www/faq.html --- toybox-0.8.8+dfsg/www/faq.html 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/www/faq.html 2023-01-10 19:24:45.000000000 +0000 @@ -521,7 +521,7 @@ Q: How do I cross compile toybox?
A: You need a compiler "toolchain" capable of producing binaries that -run on your target. A toolchain is an +run on your target. A toolchain is an integrated suite of compiler, assembler, and linker, plus the standard headers and libraries necessary to build C programs. (And a few miscellaneous binaries like @@ -548,17 +548,21 @@ LDFLAGS=--static CROSS_COMPILE=m68k-linux-musl- make distclean defconfig toybox
-(Both of those examples use static linking so you can install just +
Both of those examples use static linking so you can install just the single file to target, or test them with "qemu-m68k toybox". Feel free to dynamically link instead if you prefer, mkroot offers a "dynamic" add-on to copy the compiler's shared libraries into the new root -filesystem.)
+filesystem. + +Although you can individually override $CC and $STRIP and such, +providing the prefix twice applies it twice, ala +"CROSS_COMPILE=prefix- CC=prefix-cc" gives "prefix-prefix-cc".
Toybox's system builder can use a simpler $CROSS -variable to specify the target(s) to build for if you've installed +variable to specify the target name(s) to build for if you've installed compatible cross compilers under the "ccc" directory. -Behind the scenes this uses wildcard expansion to set $CROSS_COMPILER to -an appropriate path/prefix-.
+Behind the scenes this uses wildcard expansion to set $CROSS_COMPILE to +an appropriate "path/prefix-".Q: What architectures does toybox support?
@@ -717,7 +721,9 @@ Booting a simple system to a shell prompt requires a kernel to drive the hardware (such as Linux, or BSD with a Linux emulation layer), programs for the system to run (such as toybox's commands), and a C library ("libc") to connect them together.Toybox has a policy of requiring no external dependencies other than the -kernel and C library (at least for defconfig builds). You can optionally enable support for +kernel and C library (at least for defconfig builds). Our "software bill +of materials" (SBOM) defaults to just "the C library", both at build time +and and runtime. You can optionally enable support for additional libraries in menuconfig (such as openssl, zlib, or selinux), but toybox either provides its own built-in versions of such functionality (which the libraries provide larger, more complex, often assembly optimized @@ -757,12 +763,16 @@ root". To enter the resulting root filesystem, "sudo chroot root/host/fs /init". Type "exit" to get back out.
-You can cross compile simple three package (toybox+libc+linux) -systems configured to boot to a shell prompt under the emulator -qemu -by specifying a target type with CROSS= -(or by setting CROSS_COMPILE= to a cross compiler prefix with optional absolute -path), and pointing the build at a Linux kernel source directory, ala:
+Prebuilt binary versions of these system images, suitable for running +under the emulator qemu, are uploaded to +the website +each release if you'd like to try before building from source.
+ +You can cross compile simple three package (toybox+libc+linux) systems +configured to boot to a shell prompt under qemu by setting CROSS_COMPILE= to a +cross compiler prefix (or by installing cross compilers +in the "ccc" subdirectory and specifying a target type with CROSS=) +and also pointing the build at a Linux kernel source directory, ala:
@@ -778,14 +788,19 @@ compiler has a libc built into it, 3) you tell it where to find a Linux kernel source directory with LINUX= on the command line. If you don't say LINUX=, it skips that part of the build and just produces a root filesystem directory -ala the first example in this FAQ answer. +(root/$CROSS/fs or root/host/fs if no $CROSS target specified), which you +can chroot into if your architecture can run those binaries. (For PID other +than 1, the /init script at the top of the directory sets up and cleans up +the /proc mount points, so chroot root/i686/fs /init is a reasonable +"poke around and look at things" smoketest.)make root CROSS=sh4 LINUX=~/linux
The CROSS= shortcut expects a "ccc" symlink in the toybox source directory -pointing at a directory full of cross compilers. The ones I test this with are built from the musl-libc -maintainer's +pointing at a directory full of cross compilers. The ones I test this with are +built from the musl-libc maintainer's musl-cross-make -project, built by running toybox's scripts/mcm-buildall.sh in that directory, -and then symlink the resulting "ccc" subdirectory into toybox where CROSS= +project, built by running toybox's +scripts/mcm-buildall.sh in a musl-cross-make checkout directory, +and then symlinking the resulting "ccc" subdirectory into toybox where CROSS= can find them:
-@@ -797,8 +812,8 @@ ln -s $(realpath ccc) ../toybox/cccIf you don't want to do that, you can download prebuilt binary versions from Zach van Rijn's site and -just extract them into a "ccc" subdirectory under the toybox source.
+If you don't want to do that, you can download prebuilt binary versions +and extract them into a "ccc" subdirectory under the toybox source.
Once you've installed the cross compilers, "make root CROSS=help" should list all the available cross compilers it recognizes under ccc, diff -Nru toybox-0.8.8+dfsg/www/git/index.html toybox-0.8.9+dfsg/www/git/index.html --- toybox-0.8.8+dfsg/www/git/index.html 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/www/git/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Not browseable: git clone https://landley.net/toybox/git diff -Nru toybox-0.8.8+dfsg/www/index.html toybox-0.8.9+dfsg/www/index.html --- toybox-0.8.8+dfsg/www/index.html 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/www/index.html 2023-01-10 19:24:45.000000000 +0000 @@ -8,6 +8,223 @@
News
+January 8, 2023
+++ +"Why," Arthur said, "is there a sofa in that field?" +"I told you!" shouted Ford, leaping to his feet. "Eddies in the space-time +continuum!" +"And this is his sofa, is it?"
+- The Hitchhiker's Guide to the Galaxy
+Toybox 0.8.9 +(git commit) +is out, with prebuilt static binaries and +mkroot images +bootable under QEMU (built using a lightly patched linux-6.1).
+ +The new nbd-server command interoperates with nbd-client to serve +network block devices (using the v1 protocol), and Moritz Weber contributed a read-only git +implementation to pending.
+ +Features: New grep fast path for fixed or simple patterns that +don't need the full regex engine (I.E. "^", ".", "$" but not "*" or "[]") +most noticeable when searching for many patterns at once. +Improved tar --xform support parsing flags= and trailing s/// scope +flags (but using --xform now requires toybox sed in the $PATH).
+ +Added sort -C, lsusb -i, netcat -n, +swapoff -a -v, httpd -v, nbd-client -b, +a uname -p stub to mollify package builds, +sleep accepts multiple arguments, +sed now parses interlaced -e and -f arguments in order, +several new options in readlink and realpath (and better +handling of relative paths), and UDP mode in netcat is more useful now: +netcat -u -s 127.0.0.1 -p 9876 -l can type at netcat -u 127.0.0.1 9876 +in both directions. (Figuring out when the other side hangs up is an unsolved +problem, but that's UDP for you.) +Elliott added ls -N.
+ +Bugfixes: Fixed off by one error in sort -c output, +fixed sed -z and other sed cases where Linux has never obeyed +Posix (N at EOF does a default print, l escapes \n), +patch --dry-run should no longer create or delete files, +gzip/zcat couldn't handle concatenated archives, +the ./configure of gmake 4.3 depends on test treating one argument -a as a synonym for -e, +autodetectiong compression types in tar should be more reliable now +and sanitize weird path corner cases (like .. past /) better, +scripts/make.sh (and thus "make toybox") should no longer truncate log +files stderr is redirected to, +httpd now handles ? and # in URLs, +xxd disables columns for -c 0 and groups for -g 0 (so you can get +a long interrupted string of hex digits out of it), two fixes to +mountpoint (the conversion to same_file() was inappropriate because +the logic isn't quite the same, and -q should also quiet "not found" errors), +fixed httpd not always displaying index.html files instead of directory +contents, fixed comm - not recognizing it as stdin, and multiple fixes to +timeout which now kills process group and thus child processes, +isn't suspended by SIGTTIN, and recursive commands it calls don't +inherit an inappropriate SIGCHLD handler.
+ +Yi-Yo Chiang fixed loopback mount (the recent switch to xrunread() +kept the newline from losetup's output) and added tail +123 +(old-style synonym for tail -n +123). +Daniel Mentz added scmversion to the modinfo tag list (Android uses +it for external modules). Alexander Holler fixed su to not require +/etc/shadow when run as root, and nomas2000 reported an inverted test in +date's check for trailing rubbish. Daniel Mentz fixed an off by +one in grep -f that discarded the last character of the pattern file's +contents. Kelvin Zhang made modinfo better at handling symlinks. +Antoni Villalonga fixed fmt on 64 bit big endian systems (like s390x and +powerpc). Li Cheng fixed mount's type detection not autodetecting +the need to bind mount files when type "none" was specified in fstab. +Tomasz Sterna reported that modprobe resolving dependencies shouldn't +feed a NULL pointer to the syscall's options arguments (the kernel goes -EWTF), +and Vincent Donnefort made modprobe work when /proc/modules isn't available.
+ +Library: +The TOYFLAG_ARGFAIL() exit value is now the default error_msg() sets, +sendfile() now falls back to the read/write loop for any error (not just +EINVAL, the kernel sends ENOSYS and EXDEV and who knows what else), +xgetrandom() can now return arbitrary amounts of data (looping internally +as necessary), +xwaitpid() returns status 127 for cases (like bad PID) which don't +return a status for the PID, +several fixes to xabspath() as part of the readlink/realpath work, +new octal_deslash() to remove octal escapes common in kernel strings +(ala /proc/mounts), +the FLAG_x macros always use a 64 bit type now (so you don't have to +worry about toys.optflags &= ~FLAG_x; blanking the top 32 bits), +replaced memcmp() with new library function smemcmp() to placate ASAN +when returning first difference in known differing arguments of unequal length. +Elliott added three more filesystem types to fs_type_name() which is +used by stat -f and friends.
+ +Mkroot: +New scripts/test_mkroot.sh runs each target under qemu to confirm +1) it boots, 2) the block device works, 3) networking works, and 4) the clock is set +reasonably. It runs them in parallel, with an httpd instance on the host to +fetch a file from, and a timeout to detect hangs. +New scripts/root/overlay package copies $OVERLAY directory into +target filesystem, ala scripts/mkroot.sh overlay OVERLAY=$PWD/blah +The init script now mounts any /dev/?da on /mnt and when /mnt/init exists +sets $HANDOFF to that instead of running a shell prompt, so you can provide +automated control images that run code in the emulator automatically. (The +emulator process still when whatever $HANDOFF called exits.)
+ +Some kernel changes finally made it upstream so we can drop workarounds +for them, such as kernel commit +f8f0d06438e5 +fixing the longstanding +"allnoconfig has =y" issue so miniconfig doesn't need to add a magic #comment +line forcing a symbol OFF anymore. The remaining kernel patches (including +several maintained locally but hadn't merged into mkroot) moved to a +seperate repository +so mkroot.sh doesn't have to call sed on the build snapshot. (They're are also +included in the mkroot binary release directory, and were all submitted to lkml +months if not years ago.)
+ +The host airlock setup (creating the temporary restricted environment +mkroot builds packages in both so they don't pick up strange dependencies from +the host, and to prove the build can use the toybox commands) +removed a bunch of commands (dd, diff, vi, xzcat, ar, nm) no longer +needed by the linux-6.1 build. The kernel patches also remove the need +for gcc (it can use the "cc" symlink all modern distros install, and +autodetects whether that points to gcc or clang) and bc (which is just +generally obsolete). +Left those two in for one more release, but NEXT time building a vanilla +kernel without the gcc removal patch +and bc removal patch +may require you to add HOST_EXTRA="gcc bc" to the mkroot command line +for the airlock build to add extra symlinks to root/build/airlock.
+ +Added a kernel build for powerpc64 big endian, +m68k and powerpc now support "run-qemu.sh -hda file.img", and m68k only +has one /dev/?db (disabled Apple Desktop Bus in the config). +Added basic module support (the kernel build saves modules in modules.cpio.gz +so it can extract them when you rebuild the root filesystem but not the kernel) +and added scripts/root/tests which (among other things) builds two +innocuous modules that depend on each other (fscache and cachefiles) for +modprobe/insmod testing that hopefully won't break anything else. (If +there was a scripts/root/pending/ tests would be in it: it does not actually +run the test suite under mkroot yet, because toysh needs work.)
+ +Pending: Elliott made strace build on 32-bit x86. +James Farrell added diff -f. +Alexander Holler fixed two parameter substitution bugs in toysh. +Rob fixed assignment suppression, && and || parsing, math +priority, a number of ASAN test failures, untangled and commented the +brace expansion logic, and other ongoing shell work.
+ +A large rewrite of diff happened but couldn't be checked in unfinished +because if people are using stuff out of pending, how do you do a pending for +pending? (Meta-pending?)
+ +Cleanup: +cksum, od, file, getopt, gpiod, +nbd-client, better output messages for host, +and make base64 use line buffering. +Moved -Wno-string-plus-int into portability.sh and had it only apply to +clang. Moved num_cache() out of lib into netstat (its only user).
+ +In the test suite plumbing, $SKIPNEXT was replaced by $SKIP holding +a count that counts down with each test (so you can easily skip a block of +tests). Fixed txpect to actually listen to error code (oops) and to not +complain about multiple X. The old dochroot() and mkchroot() shell functions were removed from +scripts/runtest.sh because they required root access to use (which mkroot +does not). More conversion to use testcmd in individual command.tests +(both to remove workarounds for shell builtins, and to be more concise). +Remove redundant cleanup from several tests (which run as proper child processes +now, and the work directory is deleted and recreated between each, so +individual tests need less cleanup).
+ +Portability: More tests now pass on MacOS and FreeBSD +(weird filesystems, root vs wheel group name, dealing with headers that +define stdin as a macro and don't define PATH_MAX, the ancient bash in MacOS +doesn't understand &>>, won't let normal users use "chmod +s", +symlinks don't have all permission bits set, no O_PATH and the Linux definition +is used for something else, and so on), at least half of which Elliott did. +Not sure we've quite got Mac and BSD +doing "gmake test_singlecommand" right yet, but it's a lot closer. +Added all the commands to make bsd_defconfig that compile on FreeBSD 13 +(which is no guarantee they WORK). +ASAN=1 should work in more places now, including make_test_command. +The build uses $CC insted of cc in more places, and doesn't apply $ASAN +for $HOSTCC (the legacy kconfig/ directory stands no chance).
+ +Fedora inexplicably sticks \x09 style hex escapes into the dmesg output and +expects them to be parsed. +Android is still running the test suite under mksh which doesn't +understand bashisms like <(command). LLVM insisted that "ptr - 1" could +never be NULL (demonstrably untrue) and needed some (unsigned long) typecasts +to force it to actually do the math. +Khem Raj aliased timer_settime to timer_settime64 when the first isn't +available. Antoni Villalonga debianized some install paths and added +a bunch of --longopt synonyms for existing options that package builds +use. + +Documentation: The help page +generation logic (toybox help -av) now filter out invisible aliases and +outputs "See command" for visible aliases, plus fixes for broken corner +cases like nohup --help. +The local git repo +now has a simple index.html generated by a +small shell script, +linking to the git format-patch files for +each commit, applicable via git am.
+ +There was a +bug report that specifying the cross compiler prefix twice +includes the prefix twice (ala CROSS_COMPILE=prefix- CC=prefix-cc results in +prefix-prefix-cc) which is what happens when you ask it to do that, yes. It's now +better documented. (See also +this kernel patch.)
+ +Yi-Yo Chiang's losetup fix spawned an email thread where the +android developers explained why Android doesn't (and can't) +use devtmpfs. Rob forwarded a link to linux weekly news to see if he could get +any attention from the kernel guys, but they didn't reply.
+August 12, 2022
Huge as office blocks, silent as birds. They hung in the air exactly the same diff -Nru toybox-0.8.8+dfsg/www/news.html toybox-0.8.9+dfsg/www/news.html --- toybox-0.8.8+dfsg/www/news.html 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/www/news.html 2023-01-10 19:24:45.000000000 +0000 @@ -8,6 +8,223 @@
News
+January 8, 2023
+++ +"Why," Arthur said, "is there a sofa in that field?" +"I told you!" shouted Ford, leaping to his feet. "Eddies in the space-time +continuum!" +"And this is his sofa, is it?"
+- The Hitchhiker's Guide to the Galaxy
+Toybox 0.8.9 +(git commit) +is out, with prebuilt static binaries and +mkroot images +bootable under QEMU (built using a lightly patched linux-6.1).
+ +The new nbd-server command interoperates with nbd-client to serve +network block devices (using the v1 protocol), and Moritz Weber contributed a read-only git +implementation to pending.
+ +Features: New grep fast path for fixed or simple patterns that +don't need the full regex engine (I.E. "^", ".", "$" but not "*" or "[]") +most noticeable when searching for many patterns at once. +Improved tar --xform support parsing flags= and trailing s/// scope +flags (but using --xform now requires toybox sed in the $PATH).
+ +Added sort -C, lsusb -i, netcat -n, +swapoff -a -v, httpd -v, nbd-client -b, +a uname -p stub to mollify package builds, +sleep accepts multiple arguments, +sed now parses interlaced -e and -f arguments in order, +several new options in readlink and realpath (and better +handling of relative paths), and UDP mode in netcat is more useful now: +netcat -u -s 127.0.0.1 -p 9876 -l can type at netcat -u 127.0.0.1 9876 +in both directions. (Figuring out when the other side hangs up is an unsolved +problem, but that's UDP for you.) +Elliott added ls -N.
+ +Bugfixes: Fixed off by one error in sort -c output, +fixed sed -z and other sed cases where Linux has never obeyed +Posix (N at EOF does a default print, l escapes \n), +patch --dry-run should no longer create or delete files, +gzip/zcat couldn't handle concatenated archives, +the ./configure of gmake 4.3 depends on test treating one argument -a as a synonym for -e, +autodetectiong compression types in tar should be more reliable now +and sanitize weird path corner cases (like .. past /) better, +scripts/make.sh (and thus "make toybox") should no longer truncate log +files stderr is redirected to, +httpd now handles ? and # in URLs, +xxd disables columns for -c 0 and groups for -g 0 (so you can get +a long interrupted string of hex digits out of it), two fixes to +mountpoint (the conversion to same_file() was inappropriate because +the logic isn't quite the same, and -q should also quiet "not found" errors), +fixed httpd not always displaying index.html files instead of directory +contents, fixed comm - not recognizing it as stdin, and multiple fixes to +timeout which now kills process group and thus child processes, +isn't suspended by SIGTTIN, and recursive commands it calls don't +inherit an inappropriate SIGCHLD handler.
+ +Yi-Yo Chiang fixed loopback mount (the recent switch to xrunread() +kept the newline from losetup's output) and added tail +123 +(old-style synonym for tail -n +123). +Daniel Mentz added scmversion to the modinfo tag list (Android uses +it for external modules). Alexander Holler fixed su to not require +/etc/shadow when run as root, and nomas2000 reported an inverted test in +date's check for trailing rubbish. Daniel Mentz fixed an off by +one in grep -f that discarded the last character of the pattern file's +contents. Kelvin Zhang made modinfo better at handling symlinks. +Antoni Villalonga fixed fmt on 64 bit big endian systems (like s390x and +powerpc). Li Cheng fixed mount's type detection not autodetecting +the need to bind mount files when type "none" was specified in fstab. +Tomasz Sterna reported that modprobe resolving dependencies shouldn't +feed a NULL pointer to the syscall's options arguments (the kernel goes -EWTF), +and Vincent Donnefort made modprobe work when /proc/modules isn't available.
+ +Library: +The TOYFLAG_ARGFAIL() exit value is now the default error_msg() sets, +sendfile() now falls back to the read/write loop for any error (not just +EINVAL, the kernel sends ENOSYS and EXDEV and who knows what else), +xgetrandom() can now return arbitrary amounts of data (looping internally +as necessary), +xwaitpid() returns status 127 for cases (like bad PID) which don't +return a status for the PID, +several fixes to xabspath() as part of the readlink/realpath work, +new octal_deslash() to remove octal escapes common in kernel strings +(ala /proc/mounts), +the FLAG_x macros always use a 64 bit type now (so you don't have to +worry about toys.optflags &= ~FLAG_x; blanking the top 32 bits), +replaced memcmp() with new library function smemcmp() to placate ASAN +when returning first difference in known differing arguments of unequal length. +Elliott added three more filesystem types to fs_type_name() which is +used by stat -f and friends.
+ +Mkroot: +New scripts/test_mkroot.sh runs each target under qemu to confirm +1) it boots, 2) the block device works, 3) networking works, and 4) the clock is set +reasonably. It runs them in parallel, with an httpd instance on the host to +fetch a file from, and a timeout to detect hangs. +New scripts/root/overlay package copies $OVERLAY directory into +target filesystem, ala scripts/mkroot.sh overlay OVERLAY=$PWD/blah +The init script now mounts any /dev/?da on /mnt and when /mnt/init exists +sets $HANDOFF to that instead of running a shell prompt, so you can provide +automated control images that run code in the emulator automatically. (The +emulator process still when whatever $HANDOFF called exits.)
+ +Some kernel changes finally made it upstream so we can drop workarounds +for them, such as kernel commit +f8f0d06438e5 +fixing the longstanding +"allnoconfig has =y" issue so miniconfig doesn't need to add a magic #comment +line forcing a symbol OFF anymore. The remaining kernel patches (including +several maintained locally but hadn't merged into mkroot) moved to a +seperate repository +so mkroot.sh doesn't have to call sed on the build snapshot. (They're are also +included in the mkroot binary release directory, and were all submitted to lkml +months if not years ago.)
+ +The host airlock setup (creating the temporary restricted environment +mkroot builds packages in both so they don't pick up strange dependencies from +the host, and to prove the build can use the toybox commands) +removed a bunch of commands (dd, diff, vi, xzcat, ar, nm) no longer +needed by the linux-6.1 build. The kernel patches also remove the need +for gcc (it can use the "cc" symlink all modern distros install, and +autodetects whether that points to gcc or clang) and bc (which is just +generally obsolete). +Left those two in for one more release, but NEXT time building a vanilla +kernel without the gcc removal patch +and bc removal patch +may require you to add HOST_EXTRA="gcc bc" to the mkroot command line +for the airlock build to add extra symlinks to root/build/airlock.
+ +Added a kernel build for powerpc64 big endian, +m68k and powerpc now support "run-qemu.sh -hda file.img", and m68k only +has one /dev/?db (disabled Apple Desktop Bus in the config). +Added basic module support (the kernel build saves modules in modules.cpio.gz +so it can extract them when you rebuild the root filesystem but not the kernel) +and added scripts/root/tests which (among other things) builds two +innocuous modules that depend on each other (fscache and cachefiles) for +modprobe/insmod testing that hopefully won't break anything else. (If +there was a scripts/root/pending/ tests would be in it: it does not actually +run the test suite under mkroot yet, because toysh needs work.)
+ +Pending: Elliott made strace build on 32-bit x86. +James Farrell added diff -f. +Alexander Holler fixed two parameter substitution bugs in toysh. +Rob fixed assignment suppression, && and || parsing, math +priority, a number of ASAN test failures, untangled and commented the +brace expansion logic, and other ongoing shell work.
+ +A large rewrite of diff happened but couldn't be checked in unfinished +because if people are using stuff out of pending, how do you do a pending for +pending? (Meta-pending?)
+ +Cleanup: +cksum, od, file, getopt, gpiod, +nbd-client, better output messages for host, +and make base64 use line buffering. +Moved -Wno-string-plus-int into portability.sh and had it only apply to +clang. Moved num_cache() out of lib into netstat (its only user).
+ +In the test suite plumbing, $SKIPNEXT was replaced by $SKIP holding +a count that counts down with each test (so you can easily skip a block of +tests). Fixed txpect to actually listen to error code (oops) and to not +complain about multiple X. The old dochroot() and mkchroot() shell functions were removed from +scripts/runtest.sh because they required root access to use (which mkroot +does not). More conversion to use testcmd in individual command.tests +(both to remove workarounds for shell builtins, and to be more concise). +Remove redundant cleanup from several tests (which run as proper child processes +now, and the work directory is deleted and recreated between each, so +individual tests need less cleanup).
+ +Portability: More tests now pass on MacOS and FreeBSD +(weird filesystems, root vs wheel group name, dealing with headers that +define stdin as a macro and don't define PATH_MAX, the ancient bash in MacOS +doesn't understand &>>, won't let normal users use "chmod +s", +symlinks don't have all permission bits set, no O_PATH and the Linux definition +is used for something else, and so on), at least half of which Elliott did. +Not sure we've quite got Mac and BSD +doing "gmake test_singlecommand" right yet, but it's a lot closer. +Added all the commands to make bsd_defconfig that compile on FreeBSD 13 +(which is no guarantee they WORK). +ASAN=1 should work in more places now, including make_test_command. +The build uses $CC insted of cc in more places, and doesn't apply $ASAN +for $HOSTCC (the legacy kconfig/ directory stands no chance).
+ +Fedora inexplicably sticks \x09 style hex escapes into the dmesg output and +expects them to be parsed. +Android is still running the test suite under mksh which doesn't +understand bashisms like <(command). LLVM insisted that "ptr - 1" could +never be NULL (demonstrably untrue) and needed some (unsigned long) typecasts +to force it to actually do the math. +Khem Raj aliased timer_settime to timer_settime64 when the first isn't +available. Antoni Villalonga debianized some install paths and added +a bunch of --longopt synonyms for existing options that package builds +use. + +Documentation: The help page +generation logic (toybox help -av) now filter out invisible aliases and +outputs "See command" for visible aliases, plus fixes for broken corner +cases like nohup --help. +The local git repo +now has a simple index.html generated by a +small shell script, +linking to the git format-patch files for +each commit, applicable via git am.
+ +There was a +bug report that specifying the cross compiler prefix twice +includes the prefix twice (ala CROSS_COMPILE=prefix- CC=prefix-cc results in +prefix-prefix-cc) which is what happens when you ask it to do that, yes. It's now +better documented. (See also +this kernel patch.)
+ +Yi-Yo Chiang's losetup fix spawned an email thread where the +android developers explained why Android doesn't (and can't) +use devtmpfs. Rob forwarded a link to linux weekly news to see if he could get +any attention from the kernel guys, but they didn't reply.
+August 12, 2022
Huge as office blocks, silent as birds. They hung in the air exactly the same diff -Nru toybox-0.8.8+dfsg/www/roadmap.html toybox-0.8.9+dfsg/www/roadmap.html --- toybox-0.8.8+dfsg/www/roadmap.html 2022-08-12 07:58:03.000000000 +0000 +++ toybox-0.8.9+dfsg/www/roadmap.html 2023-01-10 19:24:45.000000000 +0000 @@ -131,8 +131,8 @@ qalter qdel qhold qmove qmsg qrerun qrls qselect qsig qstat qsub).
Some commands are for a compiler toolchain (ar c99 cflow ctags cxref gencat -iconv lex m4 make nm strings strip tsort yacc) which is outside of toybox's -mandate and should be supplied externally. (Some of these may be +iconv lex m4 make nm strings strip tsort yacc) which is out of scope for +toybox and should be supplied externally. (Some of these might be revisited later, but not for toybox 1.0.)
Some commands are part of a command shell, and can't be implemented as @@ -269,24 +269,25 @@
The RFCs are more about protocols than commands. The noise level is extremely high: there's thousands of RFCs, many describing a proposed idea that never took off, and less than 1% of the resulting documents are -currently relevant to toybox. And the documents are numbered based on the +currently relevant to toybox. The documents are numbered based on the order they were received, with no real attempt at coherently indexing the result. As with man pages they can be long and complicated or terse and impenetrable, have developed a certain amount of bureaucracy over the years, and often the easiest way to understand what -they document is to find an earlier version to read first.
+they document is to find an earlier version to read first. +(The greybeard community problem where all documentation is written by people +who don't remember NOT already knowing this stuff.)That said, RFC documents can be useful (especially for networking protocols) and the four URL templates the recommended starting files -for new commands (toys/example/skeleton.c or toys/example/hello.c depending on how much -plumbing you want to start with) provide point to posix, lsb, man, and -rfc pages.
+for new commands (toys/example/{skeleton,hello}.c) provide point to posix, lsb, +man, and rfc pages.
Use case: provide a self-hosting development environment
-The following commands were enough to build the Aboriginal Linux development +
Once upon a time, the following commands were enough to build the Aboriginal Linux development environment, boot it to a shell prompt, and build Linux From Scratch 6.8 under it.
@@ -318,12 +319,12 @@ To replace that toysh needs to supply several bash extensions _and_ work when called under the name "bash". -@@ -1287,7 +1289,6 @@The above command list was collected using a command line recording wrapper, -see scripts/record-commands and toys/example/logpath.c, which -scripts/mkroot.sh uses to populate root/log/*-commands.txt. Try +
The above command list was collected using a command line recording wrapper +(scripts/record-commands and toys/example/logpath.c) which scripts/mkroot.sh +also uses to populate root/log/*-commands.txt. Try awk '{print $1}' root/build/log/*-commands.txt | sort -u | grep -v musl | xargs -after building a mkroot target to get a similar command list used by that -build.
+after building a mkroot target to see the list of commands called out +of the $PATH during that build.Stages and moving targets
@@ -1250,6 +1251,7 @@ pwgen readelf unicode rsync linux32 hd strace +gpiodetect gpiofind gpioget gpioinfo gpioset httpd uclampsetless: less (not: lessecho lesskey) gzip: zcat [gzip] [gunzip] [zcmp] [zdiff] [zegrep] [zfgrep] [zgrep] [zless] [zmore] (not: gzexe uncompress zforce znew) -make: [make] patch: patch tar: tar procps-ng: free pgrep pidof pkill ps sysctl top uptime vmstat w watch