Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Iefe42ee7a2b54ddfae8812448b49301e5b5de92c
diff --git a/generated/flags.h b/generated/flags.h
index fe80a90..b0d0a15 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -639,9 +639,9 @@
#undef FLAG_P
#endif
-// diff <2>2(color)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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3 <2>2(color)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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3
+// diff <2>2(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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3 <2>2(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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3
#undef OPTSTR_diff
-#define OPTSTR_diff "<2>2(color)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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3"
+#define OPTSTR_diff "<2>2(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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3"
#ifdef CLEANUP_diff
#undef CLEANUP_diff
#undef FOR_diff
@@ -661,6 +661,7 @@
#undef FLAG_b
#undef FLAG_d
#undef FLAG_B
+#undef FLAG_strip_trailing_cr
#undef FLAG_color
#endif
@@ -1015,9 +1016,9 @@
#undef FLAG_t
#endif
-// grep (line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw] (line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]
+// grep (line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw] (line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]
#undef OPTSTR_grep
-#define OPTSTR_grep "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]"
+#define OPTSTR_grep "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]"
#ifdef CLEANUP_grep
#undef CLEANUP_grep
#undef FOR_grep
@@ -1034,6 +1035,7 @@
#undef FLAG_w
#undef FLAG_v
#undef FLAG_s
+#undef FLAG_R
#undef FLAG_r
#undef FLAG_o
#undef FLAG_n
@@ -2103,12 +2105,13 @@
#undef FLAG_c
#endif
-// pidof <1so: <1so:
+// pidof <1so:x <1so:x
#undef OPTSTR_pidof
-#define OPTSTR_pidof "<1so:"
+#define OPTSTR_pidof "<1so:x"
#ifdef CLEANUP_pidof
#undef CLEANUP_pidof
#undef FOR_pidof
+#undef FLAG_x
#undef FLAG_o
#undef FLAG_s
#endif
@@ -3894,7 +3897,8 @@
#define FLAG_b (1<<13)
#define FLAG_d (1<<14)
#define FLAG_B (1<<15)
-#define FLAG_color (1<<16)
+#define FLAG_strip_trailing_cr (1<<16)
+#define FLAG_color (1<<17)
#endif
#ifdef FOR_dirname
@@ -4203,24 +4207,25 @@
#define FLAG_w (1<<10)
#define FLAG_v (1<<11)
#define FLAG_s (1<<12)
-#define FLAG_r (1<<13)
-#define FLAG_o (1<<14)
-#define FLAG_n (1<<15)
-#define FLAG_i (1<<16)
-#define FLAG_h (1<<17)
-#define FLAG_b (1<<18)
-#define FLAG_a (1<<19)
-#define FLAG_I (1<<20)
-#define FLAG_H (1<<21)
-#define FLAG_F (1<<22)
-#define FLAG_E (1<<23)
-#define FLAG_z (1<<24)
-#define FLAG_Z (1<<25)
-#define FLAG_M (1<<26)
-#define FLAG_S (1<<27)
-#define FLAG_exclude_dir (1<<28)
-#define FLAG_color (1<<29)
-#define FLAG_line_buffered (1<<30)
+#define FLAG_R (1<<13)
+#define FLAG_r (1<<14)
+#define FLAG_o (1<<15)
+#define FLAG_n (1<<16)
+#define FLAG_i (1<<17)
+#define FLAG_h (1<<18)
+#define FLAG_b (1<<19)
+#define FLAG_a (1<<20)
+#define FLAG_I (1<<21)
+#define FLAG_H (1<<22)
+#define FLAG_F (1<<23)
+#define FLAG_E (1<<24)
+#define FLAG_z (1<<25)
+#define FLAG_Z (1<<26)
+#define FLAG_M (1<<27)
+#define FLAG_S (1<<28)
+#define FLAG_exclude_dir (1<<29)
+#define FLAG_color (1<<30)
+#define FLAG_line_buffered (1<<31)
#endif
#ifdef FOR_groupadd
@@ -5104,8 +5109,9 @@
#ifndef TT
#define TT this.pidof
#endif
-#define FLAG_o (1<<0)
-#define FLAG_s (1<<1)
+#define FLAG_x (1<<0)
+#define FLAG_o (1<<1)
+#define FLAG_s (1<<2)
#endif
#ifdef FOR_ping
diff --git a/generated/globals.h b/generated/globals.h
index 9f54f13..9570248 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -479,7 +479,7 @@
int proc_accounting;
int is_login;
- void *head;
+ pid_t cur_pid;
};
// toys/pending/brctl.c
@@ -520,6 +520,7 @@
long sz, count;
unsigned long long offset;
} in, out;
+ unsigned conv, iflag, oflag;
};;
// toys/pending/dhcp.c
diff --git a/generated/help.h b/generated/help.h
index 21c4eef..28eb07d 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -74,7 +74,7 @@
#define HELP_seq "usage: seq [-w|-f fmt_str] [-s sep_str] [first] [increment] last\n\nCount from first to last, by increment. Omitted arguments default\nto 1. Two arguments are used as first and last. Arguments can be\nnegative or floating point.\n\n-f Use fmt_str as a printf-style floating point format string\n-s Use sep_str as separator, default is a newline character\n-w Pad to equal width with leading zeroes"
-#define HELP_pidof "usage: pidof [-s] [-o omitpid[,omitpid...]] [NAME]...\n\nPrint the PIDs of all processes with the given names.\n\n-s Single shot, only return one pid\n-o Omit PID(s)"
+#define HELP_pidof "usage: pidof [-s] [-o omitpid[,omitpid...]] [NAME]...\n\nPrint the PIDs of all processes with the given names.\n\n-s Single shot, only return one pid\n-o Omit PID(s)\n-x Match shell scripts too"
#define HELP_passwd_sad "Password changes are checked to make sure they're at least 6 chars long,\ndon't include the entire username (but not a subset of it), or the entire\nprevious password (but changing password1, password2, password3 is fine).\nThis heuristic accepts \"aaaaaa\" and \"123456\"."
@@ -410,7 +410,7 @@
#define HELP_dumpleases "usage: dumpleases [-r|-a] [-f LEASEFILE]\n\nDisplay DHCP leases granted by udhcpd\n-f FILE, Lease file\n-r Show remaining time\n-a Show expiration time"
-#define HELP_diff "usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2\n\n-a Treat all files as text\n-b Ignore changes in the amount of whitespace\n-B Ignore changes whose lines are all blank\n-d Try hard to find a smaller set of changes\n-i Ignore case differences\n-L Use LABEL instead of the filename in the unified header\n-N Treat absent files as empty\n-q Output only whether files differ\n-r Recurse\n-S Start with FILE when comparing directories\n-T Make tabs line up by prefixing a tab when necessary\n-s Report when two files are the same\n-t Expand tabs to spaces in output\n-U Output LINES lines of context\n-w Ignore all whitespace\n\n--color Colored output"
+#define HELP_diff "usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2\n\n-a Treat all files as text\n-b Ignore changes in the amount of whitespace\n-B Ignore changes whose lines are all blank\n-d Try hard to find a smaller set of changes\n-i Ignore case differences\n-L Use LABEL instead of the filename in the unified header\n-N Treat absent files as empty\n-q Output only whether files differ\n-r Recurse\n-S Start with FILE when comparing directories\n-T Make tabs line up by prefixing a tab when necessary\n-s Report when two files are the same\n-t Expand tabs to spaces in output\n-u Unified diff\n-U Output LINES lines of context\n-w Ignore all whitespace\n\n--color Colored output\n--strip-trailing-cr Strip trailing '\\r's from input lines"
#define HELP_dhcpd "usage: dhcpd [-46fS] [-i IFACE] [-P N] [CONFFILE]\n\n -f Run in foreground\n -i Interface to use\n -S Log to syslog too\n -P N Use port N (default ipv4 67, ipv6 547)\n -4, -6 Run as a DHCPv4 or DHCPv6 server"
@@ -418,7 +418,7 @@
#define HELP_dhcp "usage: dhcp [-fbnqvoCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL] [-O OPT]\n\n Configure network dynamically using DHCP.\n\n -i Interface to use (default eth0)\n -p Create pidfile\n -s Run PROG at DHCP events (default /usr/share/dhcp/default.script)\n -B Request broadcast replies\n -t Send up to N discover packets\n -T Pause between packets (default 3 seconds)\n -A Wait N seconds after failure (default 20)\n -f Run in foreground\n -b Background if lease is not obtained\n -n Exit if lease is not obtained\n -q Exit after obtaining lease\n -R Release IP on exit\n -S Log to syslog too\n -a Use arping to validate offered address\n -O Request option OPT from server (cumulative)\n -o Don't request any options (unless -O is given)\n -r Request this IP address\n -x OPT:VAL Include option OPT in sent packets (cumulative)\n -F Ask server to update DNS mapping for NAME\n -H Send NAME as client hostname (default none)\n -V VENDOR Vendor identifier (default 'toybox VERSION')\n -C Don't send MAC as client identifier\n -v Verbose\n\n Signals:\n USR1 Renew current lease\n USR2 Release current lease"
-#define HELP_dd "usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]\n [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]\n\nCopy/convert files.\n\nif=FILE Read from FILE instead of stdin\nof=FILE Write to FILE instead of stdout\nbs=N Read and write N bytes at a time\nibs=N Read N bytes at a time\nobs=N Write N bytes at a time\ncount=N Copy only N input blocks\nskip=N Skip N input blocks\nseek=N Skip N output blocks\nconv=notrunc Don't truncate output file\nconv=noerror Continue after read errors\nconv=sync Pad blocks with zeros\nconv=fsync Physically write data out before finishing\nstatus=noxfer Don't show transfer rate\nstatus=none Don't show transfer rate or records in/out\n\nNumbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),\nMD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024)."
+#define HELP_dd "usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [iflag=FLAGS] [oflag=FLAGS]\n [bs=N] [count=N] [seek=N] [skip=N]\n [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]\n\nCopy/convert files.\n\nif=FILE Read from FILE instead of stdin\nof=FILE Write to FILE instead of stdout\nbs=N Read and write N bytes at a time\nibs=N Input block size\nobs=N Output block size\ncount=N Copy only N input blocks\nskip=N Skip N input blocks\nseek=N Skip N output blocks\niflag=FLAGS Set input flags\noflag=FLAGS Set output flags\nconv=notrunc Don't truncate output file\nconv=noerror Continue after read errors\nconv=sync Pad blocks with zeros\nconv=fsync Physically write data out before finishing\nstatus=noxfer Don't show transfer rate\nstatus=none Don't show transfer rate or records in/out\n\nFLAGS is a comma-separated list of:\n\ncount_bytes (iflag) interpret count=N in bytes, not blocks\nseek_bytes (oflag) interpret seek=N in bytes, not blocks\nskip_bytes (iflag) interpret skip=N in bytes, not blocks\n\nNumbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),\nMD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024)."
#define HELP_crontab "usage: crontab [-u user] FILE\n [-u user] [-e | -l | -r]\n [-c dir]\n\nFiles used to schedule the execution of programs.\n\n-c crontab dir\n-e edit user's crontab\n-l list user's crontab\n-r delete user's crontab\n-u user\nFILE Replace crontab by FILE ('-': stdin)"
@@ -542,7 +542,7 @@
#define HELP_head "usage: head [-n number] [file...]\n\nCopy first lines from files to stdout. If no files listed, copy from\nstdin. Filename \"-\" is a synonym for stdin.\n\n-n Number of lines to copy\n-c Number of bytes to copy\n-q Never print headers\n-v Always print headers"
-#define HELP_grep "usage: grep [-EFrivwcloqsHbhn] [-ABC NUM] [-m MAX] [-e REGEX]... [-MS PATTERN]... [-f REGFILE] [FILE]...\n\nShow lines matching regular expressions. If no -e, first argument is\nregular expression to match. With no files (or \"-\" filename) read stdin.\nReturns 0 if matched, 1 if no match found, 2 for command errors.\n\n-e Regex to match. (May be repeated.)\n-f File listing regular expressions to match.\n\nfile search:\n-r Recurse into subdirectories (defaults FILE to \".\")\n-M Match filename pattern (--include)\n-S Skip filename pattern (--exclude)\n--exclude-dir=PATTERN Skip directory pattern\n-I Ignore binary files\n\nmatch type:\n-A Show NUM lines after -B Show NUM lines before match\n-C NUM lines context (A+B) -E extended regex syntax\n-F fixed (literal match) -a always text (not binary)\n-i case insensitive -m match MAX many lines\n-v invert match -w whole word (implies -E)\n-x whole line -z input NUL terminated\n\ndisplay modes: (default: matched line)\n-c count of matching lines -l show only matching filenames\n-o only matching part -q quiet (errors only)\n-s silent (no error msg) -Z output NUL terminated\n\noutput prefix (default: filename if checking more than 1 file)\n-H force filename -b byte offset of match\n-h hide filename -n line number of match"
+#define HELP_grep "usage: grep [-EFrivwcloqsHbhn] [-ABC NUM] [-m MAX] [-e REGEX]... [-MS PATTERN]... [-f REGFILE] [FILE]...\n\nShow lines matching regular expressions. If no -e, first argument is\nregular expression to match. With no files (or \"-\" filename) read stdin.\nReturns 0 if matched, 1 if no match found, 2 for command errors.\n\n-e Regex to match. (May be repeated.)\n-f File listing regular expressions to match.\n\nfile search:\n-r Recurse into subdirectories (defaults FILE to \".\")\n-R Recurse into subdirectories and symlinks to directories\n-M Match filename pattern (--include)\n-S Skip filename pattern (--exclude)\n--exclude-dir=PATTERN Skip directory pattern\n-I Ignore binary files\n\nmatch type:\n-A Show NUM lines after -B Show NUM lines before match\n-C NUM lines context (A+B) -E extended regex syntax\n-F fixed (literal match) -a always text (not binary)\n-i case insensitive -m match MAX many lines\n-v invert match -w whole word (implies -E)\n-x whole line -z input NUL terminated\n\ndisplay modes: (default: matched line)\n-c count of matching lines -l show only matching filenames\n-o only matching part -q quiet (errors only)\n-s silent (no error msg) -Z output NUL terminated\n\noutput prefix (default: filename if checking more than 1 file)\n-H force filename -b byte offset of match\n-h hide filename -n line number of match"
#define HELP_getconf "usage: getconf -a [PATH] | -l | NAME [PATH]\n\nGet system configuration values. Values from pathconf(3) require a path.\n\n-a Show all (defaults to \"/\" if no path given)\n-l List available value names (grouped by source)"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index a05ac72..d18d293 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -54,7 +54,7 @@
USE_DHCP(NEWTOY(dhcp, "V:H:F:x*r:O*A#<0=20T#<0=3t#<0=3s:p:i:SBRCaovqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
USE_DHCP6(NEWTOY(dhcp6, "r:A#<0T#<0t#<0s:p:i:SRvqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
USE_DHCPD(NEWTOY(dhcpd, ">1P#<0>65535fi:S46[!46]", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
-USE_DIFF(NEWTOY(diff, "<2>2(color)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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_DIFF(NEWTOY(diff, "<2>2(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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_DIRNAME(NEWTOY(dirname, "<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_DMESG(NEWTOY(dmesg, "w(follow)CSTtrs#<1n#c[!Ttr][!Cc][!Sw]", TOYFLAG_BIN))
USE_DOS2UNIX(NEWTOY(dos2unix, 0, TOYFLAG_BIN))
@@ -89,7 +89,7 @@
USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETFATTR(NEWTOY(getfattr, "(only-values)dhn:", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
-USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_GROUPADD(NEWTOY(groupadd, "<1>2g#<0S", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_GROUPDEL(NEWTOY(groupdel, "<1>2", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_GROUPS(NEWTOY(groups, NULL, TOYFLAG_USR|TOYFLAG_BIN))
@@ -184,7 +184,7 @@
USE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_PATCH(NEWTOY(patch, "(dry-run)"USE_TOYBOX_DEBUG("x")"ulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN))
USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_BIN))
+USE_PIDOF(NEWTOY(pidof, "<1so:x", TOYFLAG_BIN))
USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN))
USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN))
USE_PIVOT_ROOT(NEWTOY(pivot_root, "<2>2", TOYFLAG_SBIN))
diff --git a/generated/tags.h b/generated/tags.h
index 0bcfcdd..e1e4d31 100644
--- a/generated/tags.h
+++ b/generated/tags.h
@@ -1,3 +1,17 @@
+#define DD_conv_fsync 0
+#define _DD_conv_fsync (1<<0)
+#define DD_conv_noerror 1
+#define _DD_conv_noerror (1<<1)
+#define DD_conv_notrunc 2
+#define _DD_conv_notrunc (1<<2)
+#define DD_conv_sync 3
+#define _DD_conv_sync (1<<3)
+#define DD_iflag_count_bytes 0
+#define _DD_iflag_count_bytes (1<<0)
+#define DD_iflag_skip_bytes 1
+#define _DD_iflag_skip_bytes (1<<1)
+#define DD_oflag_seek_bytes 0
+#define _DD_oflag_seek_bytes (1<<0)
#define CP_mode 0
#define _CP_mode (1<<0)
#define CP_ownership 1
diff --git a/lib/commas.c b/lib/commas.c
index 03b2e34..2267684 100644
--- a/lib/commas.c
+++ b/lib/commas.c
@@ -59,7 +59,7 @@
return start;
}
-// check all instances of opt and "no"opt in optlist, return true if opt
+// Check all instances of opt and "no"opt in optlist, return true if opt
// found and last instance wasn't no. If clean, remove each instance from list.
int comma_scan(char *optlist, char *opt, int clean)
{
@@ -97,3 +97,23 @@
return i;
}
+
+// Returns true and removes `opt` from `optlist` if present, false otherwise.
+// Doesn't have the magic "no" behavior of comma_scan.
+int comma_remove(char *optlist, char *opt)
+{
+ int optlen = strlen(opt), len, got = 0;
+
+ if (optlist) for (;;) {
+ char *s = comma_iterate(&optlist, &len);
+
+ if (!s) break;
+ if (optlen == len && !strncmp(opt, s, optlen)) {
+ got = 1;
+ if (optlist) memmove(s, optlist, strlen(optlist)+1);
+ else *s = 0;
+ }
+ }
+
+ return got;
+}
diff --git a/lib/lib.c b/lib/lib.c
index b488407..47e5ca2 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1041,7 +1041,8 @@
}
// Execute a callback for each PID that matches a process name from a list.
-void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
+void names_to_pid(char **names, int (*callback)(pid_t pid, char *name),
+ int scripts)
{
DIR *dp;
struct dirent *entry;
@@ -1050,18 +1051,20 @@
while ((entry = readdir(dp))) {
unsigned u = atoi(entry->d_name);
- char *cmd = 0, *comm, **cur;
+ char *cmd = 0, *comm = 0, **cur;
off_t len;
if (!u) continue;
// Comm is original name of executable (argv[0] could be #! interpreter)
// but it's limited to 15 characters
- sprintf(libbuf, "/proc/%u/comm", u);
- len = sizeof(libbuf);
- if (!(comm = readfileat(AT_FDCWD, libbuf, libbuf, &len)) || !len)
- continue;
- if (libbuf[len-1] == '\n') libbuf[--len] = 0;
+ if (scripts) {
+ sprintf(libbuf, "/proc/%u/comm", u);
+ len = sizeof(libbuf);
+ if (!(comm = readfileat(AT_FDCWD, libbuf, libbuf, &len)) || !len)
+ continue;
+ if (libbuf[len-1] == '\n') libbuf[--len] = 0;
+ }
for (cur = names; *cur; cur++) {
struct stat st1, st2;
@@ -1071,7 +1074,7 @@
// Fast path: only matching a filename (no path) that fits in comm.
// `len` must be 14 or less because with a full 15 bytes we don't
// know whether the name fit or was truncated.
- if (len<=14 && bb==*cur && !strcmp(comm, bb)) goto match;
+ if (scripts && len<=14 && bb==*cur && !strcmp(comm, bb)) goto match;
// If we have a path to existing file only match if same inode
if (bb!=*cur && !stat(*cur, &st1)) {
@@ -1093,7 +1096,7 @@
cmd[len] = 0;
}
if (!strcmp(bb, getbasename(cmd))) goto match;
- if (bb!=*cur && !strcmp(bb, getbasename(cmd+strlen(cmd)+1))) goto match;
+ if (scripts && !strcmp(bb, getbasename(cmd+strlen(cmd)+1))) goto match;
continue;
match:
if (callback(u, *cur)) break;
diff --git a/lib/lib.h b/lib/lib.h
index 0da3d9d..9c5e9a3 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -45,6 +45,7 @@
void llist_traverse(void *list, void (*using)(void *node));
void *llist_pop(void *list); // actually void **list
void *dlist_pop(void *list); // actually struct double_list **list
+void *dlist_lpop(void *list); // also struct double_list **list
void dlist_add_nomalloc(struct double_list **list, struct double_list *new);
struct double_list *dlist_add(struct double_list **list, char *data);
void *dlist_terminate(void *list);
@@ -347,8 +348,10 @@
void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len);
struct addrinfo *xgetaddrinfo(char *host, char *port, int family, int socktype,
int protocol, int flags);
-int xconnect(struct addrinfo *ai);
-int xbind(struct addrinfo *ai);
+void xbind(int fd, const struct sockaddr *sa, socklen_t len);
+void xconnect(int fd, const struct sockaddr *sa, socklen_t len);
+int xconnectany(struct addrinfo *ai);
+int xbindany(struct addrinfo *ai);
int xpoll(struct pollfd *fds, int nfds, int timeout);
int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout);
char *ntop(struct sockaddr *sa);
@@ -365,6 +368,7 @@
char *comma_iterate(char **list, int *len);
int comma_scan(char *optlist, char *opt, int clean);
int comma_scanall(char *optlist, char *scanlist);
+int comma_remove(char *optlist, char *opt);
// deflate.c
@@ -397,7 +401,8 @@
char *getdirname(char *name);
char *getbasename(char *name);
char *fileunderdir(char *file, char *dir);
-void names_to_pid(char **names, int (*callback)(pid_t pid, char *name));
+void names_to_pid(char **names, int (*callback)(pid_t pid, char *name),
+ int scripts);
pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid);
#define XVFORK() xvforkwrap(vfork())
diff --git a/lib/llist.c b/lib/llist.c
index 2969102..e1e6a56 100644
--- a/lib/llist.c
+++ b/lib/llist.c
@@ -51,6 +51,7 @@
return (void *)next;
}
+// Remove first item from &list and return it
void *dlist_pop(void *list)
{
struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist;
@@ -66,6 +67,21 @@
return dlist;
}
+// remove last item from &list and return it (stack pop)
+void *dlist_lpop(void *list)
+{
+ struct double_list *dl = *(struct double_list **)list;
+ void *v = 0;
+
+ if (dl) {
+ dl = dl->prev;
+ v = dlist_pop(&dl);
+ if (!dl) *(void **)list = 0;
+ }
+
+ return v;
+}
+
void dlist_add_nomalloc(struct double_list **list, struct double_list *new)
{
if (*list) {
diff --git a/lib/net.c b/lib/net.c
index 2bb720a..be69c9a 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -56,17 +56,27 @@
return fd;
}
-int xconnect(struct addrinfo *ai)
+int xconnectany(struct addrinfo *ai)
{
return xconnbind(ai, 0);
}
-int xbind(struct addrinfo *ai)
+int xbindany(struct addrinfo *ai)
{
return xconnbind(ai, 1);
}
+void xbind(int fd, const struct sockaddr *sa, socklen_t len)
+{
+ if (bind(fd, sa, len)) perror_exit("bind");
+}
+
+void xconnect(int fd, const struct sockaddr *sa, socklen_t len)
+{
+ if (connect(fd, sa, len)) perror_exit("connect");
+}
+
int xpoll(struct pollfd *fds, int nfds, int timeout)
{
int i;
diff --git a/tests/cp.test b/tests/cp.test
index 6c798b4..af59593 100755
--- a/tests/cp.test
+++ b/tests/cp.test
@@ -111,6 +111,9 @@
"-rw-r--r--\n" "" ""
rm -rf walrus woot carpenter
+# duplicated --preserve= options are fine.
+testing "--preserve=mode,mode" "cp --preserve=mode,mode walrus walrus2" "" "" ""
+
# cp -r ../source destdir
# cp -r one/two/three missing
# cp -r one/two/three two
diff --git a/tests/dd.test b/tests/dd.test
index 3ad15f2..7d7b794 100644
--- a/tests/dd.test
+++ b/tests/dd.test
@@ -92,3 +92,18 @@
# status=noxfer|none
testing "status=noxfer" "dd if=input status=noxfer ibs=1 2>&1" "input\n6+0 records in\n0+1 records out\n" "input\n" ""
testing "status=none" "dd if=input status=none ibs=1 2>&1" "input\n" "input\n" ""
+
+testing "seek stdout" "yes 2> /dev/null | dd bs=8 seek=2 count=1 > out 2> /dev/null && xxd -p out" \
+ "00000000000000000000000000000000790a790a790a790a\n" "" ""
+
+# Duplicated options are fine.
+testing "conv=sync,sync" "dd conv=sync,sync $opt | head -n 1" "I WANT\n" "" "I WANT\n"
+
+# _bytes options
+testing "iflag=count_bytes" \
+ "dd if=input count=2 ibs=4096 iflag=count_bytes $opt" "hi" "high" ""
+testing "iflag=skip_bytes" \
+ "dd if=input skip=2 ibs=4096 iflag=skip_bytes $opt" "gh" "high" ""
+testing "oflag=seek_bytes" \
+ "dd if=input of=output seek=2 obs=4096 oflag=seek_bytes status=none && \
+ xxd -p output" "000030313233\n" "0123" ""
diff --git a/tests/diff.test b/tests/diff.test
index 9847758..f78eaa6 100644
--- a/tests/diff.test
+++ b/tests/diff.test
@@ -33,3 +33,8 @@
echo food > tree2/file
testing "-r" "diff -r -L tree1/file -L tree2/file tree1 tree2 |tee out" "$expected" "" ""
+
+echo -e "hello\r\nworld\r\n"> a
+echo -e "hello\nworld\n"> b
+testing "--strip-trailing-cr off" "diff -q a b" "Files a and b differ\n" "" ""
+testing "--strip-trailing-cr on" "diff -u --strip-trailing-cr a b" "" "" ""
diff --git a/tests/env.test b/tests/env.test
index 63b9094..00b5654 100755
--- a/tests/env.test
+++ b/tests/env.test
@@ -23,6 +23,8 @@
testcmd "replace" "A=foo PATH= `which printenv` A" "foo\n" "" ""
# env bypasses shell builtins
-ln -s "$(which echo)" true
+echo "#!$(which sh)
+echo \$@" > true
+chmod a+x true
testcmd "norecurse" 'env PATH="$PWD:$PATH" true hello' "hello\n" "" ""
rm true
diff --git a/tests/grep.test b/tests/grep.test
index dee5992..68c8dd8 100755
--- a/tests/grep.test
+++ b/tests/grep.test
@@ -175,3 +175,12 @@
echo "hello world" > sub/no/test
testing "--exclude-dir" 'grep --exclude-dir=no -r world sub' "sub/yes/test:hello world\n" "" ""
rm -rf sub
+
+# -r and -R differ in that -R will dereference symlinks to directories.
+mkdir dir
+echo "hello" > dir/f
+mkdir sub
+ln -s ../dir sub/link
+testing "" "grep -rh hello sub" "" "" ""
+testing "" "grep -Rh hello sub" "hello\n" "" ""
+rm -rf sub real
diff --git a/tests/killall.test b/tests/killall.test
index 40f6cb3..7e171d4 100644
--- a/tests/killall.test
+++ b/tests/killall.test
@@ -5,10 +5,16 @@
#testing "name" "command" "result" "infile" "stdin"
echo "#!$(which sh)
-yes > /dev/null" > toybox.killall.test.script
+while true; do
+ sleep 0.1
+done" > toybox.killall.test.script
chmod a+x toybox.killall.test.script
+cp toybox.killall.test.script toybox.test
+
+./toybox.test &
+testing "short name" "killall toybox.test && echo killed ; pgrep -l toybox.test || echo really" "killed\nreally\n" "" ""
./toybox.killall.test.script &
-testing "script" "killall toybox.killall.test.script && echo killed ; pgrep -l toybox.killall.test.script || echo really" "killed\nreally\n" "" ""
+testing "long name" "killall toybox.killall.test.script && echo killed ; pgrep -l toybox.killall.test.script || echo really" "killed\nreally\n" "" ""
-rm -f toybox.killall.test.script
+rm -f toybox.killall.test.script toybox.test
diff --git a/tests/pidof.test b/tests/pidof.test
new file mode 100644
index 0000000..7de31fc
--- /dev/null
+++ b/tests/pidof.test
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+#
+# pidof (unlike killall) doesn't match argv[1] unless you supply -x.
+#
+
+echo "#!$(which sh)
+while true; do
+ sleep 0.1
+done" > toybox.pidof.test.script
+chmod a+x toybox.pidof.test.script
+cp toybox.pidof.test.script pidof.test
+
+./pidof.test &
+pid=$!
+testcmd "short argv[1]" "pidof.test" "" "" ""
+testcmd "short argv[1] -x" "-x pidof.test" "$pid\n" "" ""
+kill $pid
+
+./toybox.pidof.test.script &
+pid=$!
+testcmd "long argv[1]" "toybox.pidof.test.script" "" "" ""
+testcmd "long argv[1] -x" "-x toybox.pidof.test.script" "$pid\n" "" ""
+kill $pid
+
+rm -f toybox.pidof.test.script toybox.test
+
+# pidof (unlike killall) will match itself.
+testcmd "pidof pidof" "pidof > /dev/null && echo found" "found\n" "" ""
diff --git a/toys/lsb/killall.c b/toys/lsb/killall.c
index 119e01f..c81360b 100644
--- a/toys/lsb/killall.c
+++ b/toys/lsb/killall.c
@@ -39,7 +39,7 @@
if (pid == TT.cur_pid) return 0;
- if (toys.optflags & FLAG_i) {
+ if (FLAG(i)) {
fprintf(stderr, "Signal %s(%d)", name, (int)pid);
if (!yesno(0)) return 0;
}
@@ -53,8 +53,8 @@
} else offset++;
}
if (errno) {
- if (!(toys.optflags & FLAG_q)) perror_msg("pid %d", (int)pid);
- } else if (toys.optflags & FLAG_v)
+ if (!FLAG(q)) perror_msg("pid %d", (int)pid);
+ } else if (FLAG(v))
printf("Killed %s(%d) with signal %d\n", name, pid, TT.signum);
return 0;
@@ -67,14 +67,14 @@
TT.names = toys.optargs;
TT.signum = SIGTERM;
- if (toys.optflags & FLAG_l) {
+ if (FLAG(l)) {
list_signals();
return;
}
if (TT.s || (*TT.names && **TT.names == '-')) {
if (0 > (TT.signum = sig_to_num(TT.s ? TT.s : (*TT.names)+1))) {
- if (toys.optflags & FLAG_q) exit(1);
+ if (FLAG(q)) exit(1);
error_exit("Invalid signal");
}
if (!TT.s) {
@@ -83,13 +83,13 @@
}
}
- if (!(toys.optflags & FLAG_l) && !toys.optc) help_exit("no name");
+ if (!toys.optc) help_exit("no name");
TT.cur_pid = getpid();
TT.err = xmalloc(2*toys.optc);
for (i=0; i<toys.optc; i++) TT.err[i] = ESRCH;
- names_to_pid(TT.names, kill_process);
+ names_to_pid(TT.names, kill_process, 1);
for (i=0; i<toys.optc; i++) {
if (TT.err[i]) {
toys.exitval = 1;
diff --git a/toys/lsb/pidof.c b/toys/lsb/pidof.c
index 4f266b8..cd705a7 100644
--- a/toys/lsb/pidof.c
+++ b/toys/lsb/pidof.c
@@ -5,7 +5,7 @@
*
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/pidof.html
-USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_BIN))
+USE_PIDOF(NEWTOY(pidof, "<1so:x", TOYFLAG_BIN))
config PIDOF
bool "pidof"
@@ -17,6 +17,7 @@
-s Single shot, only return one pid
-o Omit PID(s)
+ -x Match shell scripts too
*/
#define FOR_pidof
@@ -39,6 +40,6 @@
void pidof_main(void)
{
toys.exitval = 1;
- names_to_pid(toys.optargs, print_pid);
+ names_to_pid(toys.optargs, print_pid, FLAG(x));
if (!toys.exitval) xputc('\n');
}
diff --git a/toys/net/ftpget.c b/toys/net/ftpget.c
index ad3c303..05c5350 100644
--- a/toys/net/ftpget.c
+++ b/toys/net/ftpget.c
@@ -105,7 +105,7 @@
if (!remote) remote = toys.optargs[1];
// connect
- TT.fd = xconnect(xgetaddrinfo(*toys.optargs, TT.p, 0, SOCK_STREAM, 0,
+ TT.fd = xconnectany(xgetaddrinfo(*toys.optargs, TT.p, 0, SOCK_STREAM, 0,
AI_ADDRCONFIG));
if (getpeername(TT.fd, (void *)&si6, &sl)) perror_exit("getpeername");
@@ -147,7 +147,7 @@
if (!s || port<1 || port>65535) error_exit_raw(toybuf);
si6.sin6_port = SWAP_BE16(port); // same field size/offset for v4 and v6
port = xsocket(si6.sin6_family, SOCK_STREAM, 0);
- if (connect(port, (void *)&si6, sizeof(si6))) perror_exit("connect");
+ xconnect(port, (void *)&si6, sizeof(si6));
// RETR blocks until file data read from data port, so use SIZE to check
// if file exists before creating local copy
diff --git a/toys/net/netcat.c b/toys/net/netcat.c
index 65c41ac..0a235d1 100644
--- a/toys/net/netcat.c
+++ b/toys/net/netcat.c
@@ -111,16 +111,10 @@
sockaddr.sun_family = AF_UNIX;
sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
- if (connect(sockfd, (struct sockaddr*)&sockaddr,
- sizeof(sockaddr)) != 0) {
- perror_exit("could not bind to unix domain socket");
- }
-
+ xconnect(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
} else {
- struct addrinfo *addr = xgetaddrinfo(toys.optargs[0], toys.optargs[1],
- family, type, 0, 0);
-
- sockfd = xconnect(addr);
+ sockfd = xconnectany(xgetaddrinfo(toys.optargs[0], toys.optargs[1],
+ family, type, 0, 0));
}
// We have a connection. Disarm timeout.
@@ -145,13 +139,10 @@
sockaddr.sun_family = AF_UNIX;
sockfd = xsocket(AF_UNIX, type | SOCK_CLOEXEC, 0);
- if (bind(sockfd, (struct sockaddr*)&sockaddr,
- sizeof(struct sockaddr_un)) != 0) {
- perror_exit("unable to bind to UNIX domain socket");
- }
+ xbind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
} else {
sprintf(toybuf, "%ld", TT.p);
- sockfd = xbind(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
+ sockfd = xbindany(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
}
if (listen(sockfd, 5)) error_exit("listen");
diff --git a/toys/net/ping.c b/toys/net/ping.c
index 81dca99..9ae7c85 100644
--- a/toys/net/ping.c
+++ b/toys/net/ping.c
@@ -155,7 +155,7 @@
}
xexit();
}
- if (TT.I && bind(TT.sock, sa, sizeof(srcaddr))) perror_exit("bind");
+ if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
if (toys.optflags&FLAG_m) {
int mark = TT.m;
diff --git a/toys/net/sntp.c b/toys/net/sntp.c
index b1ecb1b..b1f3685 100644
--- a/toys/net/sntp.c
+++ b/toys/net/sntp.c
@@ -88,7 +88,7 @@
// Act as server if necessary
if (FLAG(S)|FLAG(m)) {
- fd = xbind(ai);
+ fd = xbindany(ai);
if (TT.m) {
struct ip_mreq group;
diff --git a/toys/other/nbd_client.c b/toys/other/nbd_client.c
index fcd0fca..ad3440a 100644
--- a/toys/other/nbd_client.c
+++ b/toys/other/nbd_client.c
@@ -52,7 +52,7 @@
// Find and connect to server
- sock = xconnect(xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0));
+ sock = xconnectany(xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0, 0));
temp = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &temp, sizeof(int));
diff --git a/toys/pending/arping.c b/toys/pending/arping.c
index 6007845..68ee5ea 100644
--- a/toys/pending/arping.c
+++ b/toys/pending/arping.c
@@ -242,15 +242,13 @@
saddr.sin_family = AF_INET;
if (src_addr.s_addr) {
saddr.sin_addr = src_addr;
- if (bind(p_fd, (struct sockaddr*)&saddr, sizeof(saddr)))
- perror_exit("bind");
+ xbind(p_fd, (struct sockaddr*)&saddr, sizeof(saddr));
} else {
uint32_t oip;
saddr.sin_port = htons(1025);
saddr.sin_addr = dest_addr;
- if (connect(p_fd, (struct sockaddr *) &saddr, sizeof(saddr)))
- perror_exit("cannot connect to remote host");
+ xconnect(p_fd, (struct sockaddr *) &saddr, sizeof(saddr));
get_interface(TT.iface, NULL, &oip, NULL);
src_addr.s_addr = htonl(oip);
}
@@ -259,8 +257,7 @@
src_pk.sll_family = AF_PACKET;
src_pk.sll_protocol = htons(ETH_P_ARP);
- if (bind(TT.sockfd, (struct sockaddr *)&src_pk, sizeof(src_pk)))
- perror_exit("bind");
+ xbind(TT.sockfd, (struct sockaddr *)&src_pk, sizeof(src_pk));
socklen_t alen = sizeof(src_pk);
getsockname(TT.sockfd, (struct sockaddr *)&src_pk, &alen);
diff --git a/toys/pending/bootchartd.c b/toys/pending/bootchartd.c
index 7e5a136..1fe6aff 100644
--- a/toys/pending/bootchartd.c
+++ b/toys/pending/bootchartd.c
@@ -34,24 +34,9 @@
int proc_accounting;
int is_login;
- void *head;
+ pid_t cur_pid;
)
-struct pid_list {
- struct pid_list *next, *prev;
- int pid;
-};
-
-static int push_pids_in_list(pid_t pid, char *name)
-{
- struct pid_list *new = xzalloc(sizeof(struct pid_list));
-
- new->pid = pid;
- dlist_add_nomalloc((void *)&TT.head, (void *)new);
-
- return 0;
-}
-
static void dump_data_in_file(char *fname, int wfd)
{
int rfd = open(fname, O_RDONLY);
@@ -253,13 +238,21 @@
}
}
+static int signal_pid(pid_t pid, char *name)
+{
+ if (pid != TT.cur_pid) kill(pid, SIGUSR1);
+ return 0;
+}
+
void bootchartd_main()
{
- pid_t lgr_pid, self_pid = getpid();
+ pid_t lgr_pid;
int bchartd_opt = 0; // 0=PID1, 1=start, 2=stop, 3=init
+
+ TT.cur_pid = getpid();
TT.smpl_period_usec = 200 * 1000;
- TT.is_login = (self_pid == 1);
+ TT.is_login = (TT.cur_pid == 1);
if (*toys.optargs) {
if (!strcmp("start", *toys.optargs)) bchartd_opt = 1;
else if (!strcmp("stop", *toys.optargs)) bchartd_opt = 2;
@@ -267,16 +260,9 @@
else error_exit("Unknown option '%s'", *toys.optargs);
if (bchartd_opt == 2) {
- struct pid_list *temp;
char *process_name[] = {"bootchartd", NULL};
- names_to_pid(process_name, push_pids_in_list);
- temp = TT.head;
- if (temp) temp->prev->next = 0;
- for (; temp; temp = temp->next)
- if (temp->pid != self_pid) kill(temp->pid, SIGUSR1);
- llist_traverse(TT.head, free);
-
+ names_to_pid(process_name, signal_pid, 0);
return;
}
} else if (!TT.is_login) error_exit("not PID 1");
diff --git a/toys/pending/dd.c b/toys/pending/dd.c
index e37f8b2..80a7595 100644
--- a/toys/pending/dd.c
+++ b/toys/pending/dd.c
@@ -4,8 +4,6 @@
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
- *
- * todo: ctrl-c doesn't work, the read() is restarting.
USE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN))
@@ -13,19 +11,22 @@
bool "dd"
default n
help
- usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
- [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
+ usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [iflag=FLAGS] [oflag=FLAGS]
+ [bs=N] [count=N] [seek=N] [skip=N]
+ [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
Copy/convert files.
if=FILE Read from FILE instead of stdin
of=FILE Write to FILE instead of stdout
bs=N Read and write N bytes at a time
- ibs=N Read N bytes at a time
- obs=N Write N bytes at a time
+ ibs=N Input block size
+ obs=N Output block size
count=N Copy only N input blocks
skip=N Skip N input blocks
seek=N Skip N output blocks
+ iflag=FLAGS Set input flags
+ oflag=FLAGS Set output flags
conv=notrunc Don't truncate output file
conv=noerror Continue after read errors
conv=sync Pad blocks with zeros
@@ -33,6 +34,12 @@
status=noxfer Don't show transfer rate
status=none Don't show transfer rate or records in/out
+ FLAGS is a comma-separated list of:
+
+ count_bytes (iflag) interpret count=N in bytes, not blocks
+ seek_bytes (oflag) interpret seek=N in bytes, not blocks
+ skip_bytes (iflag) interpret skip=N in bytes, not blocks
+
Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),
MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).
*/
@@ -51,12 +58,24 @@
long sz, count;
unsigned long long offset;
} in, out;
+ unsigned conv, iflag, oflag;
);
-#define C_FSYNC 1
-#define C_NOERROR 2
-#define C_NOTRUNC 4
-#define C_SYNC 8
+struct dd_flag {
+ char *name;
+};
+
+static const struct dd_flag dd_conv[] = TAGGED_ARRAY(DD_conv,
+ {"fsync"}, {"noerror"}, {"notrunc"}, {"sync"},
+);
+
+static const struct dd_flag dd_iflag[] = TAGGED_ARRAY(DD_iflag,
+ {"count_bytes"}, {"skip_bytes"},
+);
+
+static const struct dd_flag dd_oflag[] = TAGGED_ARRAY(DD_oflag,
+ {"seek_bytes"},
+);
static void status()
{
@@ -79,6 +98,12 @@
}
}
+static void dd_sigint(int sig) {
+ status();
+ toys.exitval = sig|128;
+ xexit();
+}
+
static void write_out(int all)
{
TT.out.bp = TT.out.buff;
@@ -97,18 +122,24 @@
if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
}
-int strstarteq(char **a, char *b)
+static void parse_flags(char *what, char *arg,
+ const struct dd_flag* flags, int flag_count, unsigned *result)
{
- char *aa = *a;
+ char *pre = xstrdup(arg);
+ int i;
- return strstart(&aa, b) && *aa == '=' && (*a = aa+1);
+ for (i=0; i<flag_count; ++i) {
+ while (comma_remove(pre, flags[i].name)) *result |= 1<<i;
+ }
+ if (*pre) error_exit("bad %s=%s", what, pre);
+ free(pre);
}
void dd_main()
{
char **args;
unsigned long long bs = 0;
- int trunc = O_TRUNC, conv = 0;
+ int trunc = O_TRUNC;
TT.show_xfer = TT.show_records = 1;
TT.c_count = ULLONG_MAX;
@@ -117,51 +148,46 @@
for (args = toys.optargs; *args; args++) {
char *arg = *args;
- if (strstarteq(&arg, "bs")) bs = atolx_range(arg, 1, LONG_MAX);
- else if (strstarteq(&arg, "ibs")) TT.in.sz = atolx_range(arg, 1, LONG_MAX);
- else if (strstarteq(&arg, "obs")) TT.out.sz = atolx_range(arg, 1, LONG_MAX);
- else if (strstarteq(&arg, "count"))
+ if (strstart(&arg, "bs=")) bs = atolx_range(arg, 1, LONG_MAX);
+ else if (strstart(&arg, "ibs=")) TT.in.sz = atolx_range(arg, 1, LONG_MAX);
+ else if (strstart(&arg, "obs=")) TT.out.sz = atolx_range(arg, 1, LONG_MAX);
+ else if (strstart(&arg, "count="))
TT.c_count = atolx_range(arg, 0, LLONG_MAX);
- else if (strstarteq(&arg, "if")) TT.in.name = arg;
- else if (strstarteq(&arg, "of")) TT.out.name = arg;
- else if (strstarteq(&arg, "seek"))
+ else if (strstart(&arg, "if=")) TT.in.name = arg;
+ else if (strstart(&arg, "of=")) TT.out.name = arg;
+ else if (strstart(&arg, "seek="))
TT.out.offset = atolx_range(arg, 0, LLONG_MAX);
- else if (strstarteq(&arg, "skip"))
+ else if (strstart(&arg, "skip="))
TT.in.offset = atolx_range(arg, 0, LLONG_MAX);
- else if (strstarteq(&arg, "status")) {
+ else if (strstart(&arg, "status=")) {
if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
else error_exit("unknown status '%s'", arg);
- } else if (strstarteq(&arg, "conv")) {
- char *ss, *convs[] = {"fsync", "noerror", "notrunc", "sync"};
- int i, len;
-
- while ((ss = comma_iterate(&arg, &len))) {
- for (i = 0; i<ARRAY_LEN(convs); i++)
- if (len == strlen(convs[i]) && !strncmp(ss, convs[i], len)) break;
- if (i == ARRAY_LEN(convs)) error_exit("bad conv=%.*s", len, ss);
- conv |= 1<<i;
- }
- } else error_exit("bad arg %s", arg);
+ } else if (strstart(&arg, "conv=")) {
+ parse_flags("conv", arg, dd_conv, ARRAY_LEN(dd_conv), &TT.conv);
+ fprintf(stderr, "conv=%x\n", TT.conv);
+ } else if (strstart(&arg, "iflag="))
+ parse_flags("iflag", arg, dd_iflag, ARRAY_LEN(dd_iflag), &TT.iflag);
+ else if (strstart(&arg, "oflag="))
+ parse_flags("oflag", arg, dd_oflag, ARRAY_LEN(dd_oflag), &TT.oflag);
+ else error_exit("bad arg %s", arg);
}
if (bs) TT.in.sz = TT.out.sz = bs;
- signal(SIGINT, generic_signal);
+ signal(SIGINT, dd_sigint);
signal(SIGUSR1, generic_signal);
gettimeofday(&TT.start, NULL);
- /* for bs=, in/out is done as it is. so only in.sz is enough.
- * With Single buffer there will be overflow in a read following partial read
- */
+ // For bs=, in/out is done as it is. so only in.sz is enough.
+ // With Single buffer there will be overflow in a read following partial read.
TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
TT.in.bp = TT.out.bp = TT.in.buff;
- //setup input
+
if (!TT.in.name) TT.in.name = "stdin";
else TT.in.fd = xopenro(TT.in.name);
- if (conv&C_NOTRUNC) trunc = 0;
+ if (TT.conv & _DD_conv_notrunc) trunc = 0;
- //setup output
if (!TT.out.name) {
TT.out.name = "stdout";
TT.out.fd = 1;
@@ -170,30 +196,43 @@
// Implement skip=
if (TT.in.offset) {
- if (lseek(TT.in.fd, (off_t)(TT.in.offset * TT.in.sz), SEEK_CUR) < 0) {
- while (TT.in.offset--) {
- ssize_t n = read(TT.in.fd, TT.in.bp, TT.in.sz);
+ off_t off = TT.in.offset;
+
+ if (!(TT.iflag & _DD_iflag_skip_bytes)) off *= TT.in.sz;
+ if (lseek(TT.in.fd, off, SEEK_CUR) < 0) {
+ while (off > 0) {
+ int chunk = off < TT.in.sz ? off : TT.in.sz;
+ ssize_t n = read(TT.in.fd, TT.in.bp, chunk);
if (n < 0) {
perror_msg("%s", TT.in.name);
- if (conv&C_NOERROR) status();
+ if (TT.conv & _DD_conv_noerror) status();
else return;
} else if (!n) {
xprintf("%s: Can't skip\n", TT.in.name);
return;
}
+ off -= chunk;
}
}
}
- // seek/truncate as necessary. We handled position zero truncate with
- // O_TRUNC on open, so output to /dev/null and such doesn't error.
- if (TT.out.fd!=1 && (bs = TT.out.offset*TT.out.sz)) {
+ // Implement seek= and truncate as necessary. We handled position zero
+ // truncate with O_TRUNC on open, so output to /dev/null and such doesn't
+ // error.
+ bs = TT.out.offset;
+ if (!(TT.oflag & _DD_oflag_seek_bytes)) bs *= TT.out.sz;
+ if (bs) {
xlseek(TT.out.fd, bs, SEEK_CUR);
if (trunc && ftruncate(TT.out.fd, bs)) perror_exit("ftruncate");
}
- while (TT.c_count==ULLONG_MAX || (TT.in_full + TT.in_part) < TT.c_count) {
+ unsigned long long bytes_left = TT.c_count;
+ if (TT.c_count != ULLONG_MAX && !(TT.iflag & _DD_iflag_count_bytes)) {
+ bytes_left *= TT.in.sz;
+ }
+ while (bytes_left) {
+ int chunk = bytes_left < TT.in.sz ? bytes_left : TT.in.sz;
ssize_t n;
// Show progress and exit on SIGINT or just continue on SIGUSR1.
@@ -204,16 +243,16 @@
}
TT.in.bp = TT.in.buff + TT.in.count;
- if (conv&C_SYNC) memset(TT.in.bp, 0, TT.in.sz);
- if (!(n = read(TT.in.fd, TT.in.bp, TT.in.sz))) break;
- if (n < 0) {
+ if (TT.conv & _DD_conv_sync) memset(TT.in.bp, 0, TT.in.sz);
+ if (!(n = read(TT.in.fd, TT.in.bp, chunk))) break;
+ if (n < 0) {
if (errno == EINTR) continue;
//read error case.
perror_msg("%s: read error", TT.in.name);
- if (!(conv&C_NOERROR)) exit(1);
+ if (!(TT.conv & _DD_conv_noerror)) exit(1);
status();
xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
- if (!(conv&C_SYNC)) continue;
+ if (!(TT.conv & _DD_conv_sync)) continue;
// if SYNC, then treat as full block of nuls
n = TT.in.sz;
}
@@ -222,9 +261,10 @@
TT.in.count += n;
} else {
TT.in_part++;
- if (conv&C_SYNC) TT.in.count += TT.in.sz;
+ if (TT.conv & _DD_conv_sync) TT.in.count += TT.in.sz;
else TT.in.count += n;
}
+ bytes_left -= n;
TT.out.count = TT.in.count;
if (bs) {
@@ -239,7 +279,7 @@
}
}
if (TT.out.count) write_out(1); //write any remaining input blocks
- if ((conv&C_FSYNC) && fsync(TT.out.fd)<0)
+ if ((TT.conv & _DD_conv_fsync) && fsync(TT.out.fd)<0)
perror_exit("%s: fsync", TT.out.name);
close(TT.in.fd);
diff --git a/toys/pending/dhcp6.c b/toys/pending/dhcp6.c
index c69c4ae..728dc7d 100644
--- a/toys/pending/dhcp6.c
+++ b/toys/pending/dhcp6.c
@@ -249,10 +249,7 @@
sockll.sll_family = AF_PACKET;
sockll.sll_protocol = htons(ETH_P_IPV6);
sockll.sll_ifindex = if_nametoindex(TT.interface_name);
- if (bind(TT.sock, (struct sockaddr *) &sockll, sizeof(sockll))) {
- xclose(TT.sock);
- error_exit("MODE RAW : Bind fail.\n");
- }
+ xbind(TT.sock, (struct sockaddr *) &sockll, sizeof(sockll));
if (setsockopt(TT.sock, SOL_PACKET, PACKET_HOST,&constone, sizeof(int)) < 0) {
if (errno != ENOPROTOOPT) error_exit("MODE RAW : Bind fail.\n");
}
@@ -575,10 +572,7 @@
xsetsockopt(TT.sock1, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone));
- if (bind(TT.sock1, (struct sockaddr *)&sinaddr6, sizeof(sinaddr6))) {
- xclose(TT.sock1);
- error_exit("bind failed");
- }
+ xbind(TT.sock1, (struct sockaddr *)&sinaddr6, sizeof(sinaddr6));
mode_raw();
set_timeout(0);
diff --git a/toys/pending/diff.c b/toys/pending/diff.c
index d865e8d..2d13d97 100644
--- a/toys/pending/diff.c
+++ b/toys/pending/diff.c
@@ -5,7 +5,7 @@
*
* See: http://cm.bell-labs.com/cm/cs/cstr/41.pdf
-USE_DIFF(NEWTOY(diff, "<2>2(color)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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_DIFF(NEWTOY(diff, "<2>2(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)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
config DIFF
bool "diff"
@@ -13,23 +13,25 @@
help
usage: diff [-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] 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
- -i Ignore case differences
- -L Use LABEL instead of the filename in the unified header
- -N Treat absent files as empty
- -q Output only whether files differ
- -r Recurse
- -S Start with FILE when comparing directories
- -T Make tabs line up by prefixing a tab when necessary
- -s Report when two files are the same
- -t Expand tabs to spaces in output
- -U Output LINES lines of context
- -w Ignore all whitespace
+ -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
+ -i Ignore case differences
+ -L Use LABEL instead of the filename in the unified header
+ -N Treat absent files as empty
+ -q Output only whether files differ
+ -r Recurse
+ -S Start with FILE when comparing directories
+ -T Make tabs line up by prefixing a tab when necessary
+ -s Report when two files are the same
+ -t Expand tabs to spaces in output
+ -u Unified diff
+ -U Output LINES lines of context
+ -w Ignore all whitespace
- --color Colored output
+ --color Colored output
+ --strip-trailing-cr Strip trailing '\r's from input lines
*/
#define FOR_diff
@@ -196,8 +198,18 @@
tok |= empty;
while (!(tok & eol)) {
-
t = fgetc(fp);
+
+ if (FLAG(strip_trailing_cr) && t == '\r') {
+ int t2 = fgetc(fp);
+ if (t2 == '\n') {
+ t = t2;
+ if (off) (*off)++;
+ } else {
+ ungetc(t2, fp);
+ }
+ }
+
if (off && t != EOF) *off += 1;
is_space = isspace(t) || (t == EOF);
tok |= (t & (eof + eol)); //set tok eof+eol when t is eof
diff --git a/toys/pending/host.c b/toys/pending/host.c
index fa830a7..fe0f23a 100644
--- a/toys/pending/host.c
+++ b/toys/pending/host.c
@@ -121,9 +121,8 @@
if ((ret = getaddrinfo(nsname, "53", &ns_hints, &ai)) < 0)
error_exit("Error looking up server name: %s", gai_strerror(ret));
- int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (s < 0 || connect(s, ai->ai_addr, ai->ai_addrlen) < 0)
- perror_exit("Socket error");
+ int s = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ xconnect(s, ai->ai_addr, ai->ai_addrlen);
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &(struct timeval){ .tv_sec = 5 },
sizeof(struct timeval));
printf("Using domain server %s:\n", nsname);
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index e8a11ce..1950438 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -212,22 +212,32 @@
if (*toys.optargs) xexit();
}
+// return length of match found at this point
+static int anyof(char *s, char **try)
+{
+ while (*try) {
+ if (strstart(&s, *try)) return strlen(*try);
+ try++;
+ }
-// Parse one word from the command line, appending one or more argv[] entries
-// to struct command. Handles environment variable substitution and
-// substrings. Returns pointer to next used byte, or NULL if it
-// hit an ending token.
-
-// caller eats leading spaces
+ return 0;
+}
// parse next word from command line. Returns end, or 0 if need continuation
+// caller eats leading spaces
static char *parse_word(char *start)
{
int i, quote = 0;
char *end = start, *s;
- // find end of string
+ // Skip leading whitespace/comment
+ for (;;) {
+ if (isspace(*start)) ++start;
+ else if (*start=='#') while (*start && *start != '\n') ++start;
+ else break;
+ }
+ // find end of this word
while (*end) {
i = 0;
@@ -243,11 +253,20 @@
// start quote
if (strchr("\"'`", *end)) toybuf[quote++] = *end++;
else if (strstart(&end, "<(") || strstart(&end,">(")) toybuf[quote++]=')';
- else if (*end==')') return end+(end==start);
else {
- // control chars
- for (s = end; strchr(";|&<>(", *s); s++);
- if (s != end) return (end == start) ? s : end;
+ // control chars.
+ // 123<<file- parses as 2 args: "123<<" "file-".
+ // note: >&; becomes ">&" ";" because first loop, then second loop.
+ s = end;
+ if (*s == '{') s++;
+ for (s = end; isdigit(*s); s++);
+ if (*end == '{' && *s == '}') s++;
+ s += anyof(s, (char *[]){"<<<", "<<-", "<<", "<&", "<>", "<", ">>",
+ ">&", ">|", ">", 0});
+ if (s == end || isdigit(s[-1]))
+ s += anyof(s, (char *[]){";;&", ";;", ";&", ";", "||", "|&", "|",
+ "&&", "&>>", "&>", "&", "(", ")", 0});
+ if (s != end && !isdigit(*s)) return (end == start) ? s : end;
i++;
}
}
@@ -259,7 +278,7 @@
// backslash escapes
if (*end == '\\') {
- if (!end[1]) return 0;
+ if (!end[1] || (end[1]=='\n' && !end[2])) return 0;
end += 2;
} else if (*end == '$') {
// barf if we're near overloading quote stack (nesting ridiculously deep)
@@ -286,228 +305,359 @@
return quote ? 0 : end;
}
-// Consume a line of shell script and do what it says. Returns 0 if finished,
-// pointer to start of unused part of line if it needs another line of input.
-static char *parse_line(char *line, struct double_list **pipeline)
+// Parse flow control statement(s), returns index of first statement to execute,
+// pp->arg->c if none, -1 if we need to flush due to syntax error
+int flow_control(int why, struct sh_arg *arg, struct double_list **expect,
+ char **end)
{
- char *start = line, *end, *s, *ex, *add;
- struct sh_arg *arg = 0;
- struct double_list *pl, *expect = 0;
- unsigned i, paren = 0;
+ char *add = 0;
+ int i, pend = 0;
- // Resume appending to last pipeline's last argument list
- if (*pipeline) arg = (void *)(*pipeline)->prev->data;
- if (arg) for (i = 0; i<arg->c; i++) {
- if (!strcmp(arg->v[i], "(")) paren++;
- else if (!strcmp(arg->v[i], ")")) paren--;
+ // Blank line shouldn't change end, but two ends in a row are an error
+ if (!arg->c) {
+ if (arg->v[0]) {
+ syntax_err("bad %s", arg->v[0]);
+ return -1;
+ }
+ return 0;
}
- // Loop handling each word
- for (;;) {
- // Skip leading whitespace/comment
- while (isspace(*start)) ++start;
- if (*start=='#') {
- while (*start && *start != '\n') start++;
- continue;
+ // parse flow control statements in this command line
+ for (i = 0; ; i++) {
+ char *ex = *expect ? (*expect)->prev->data : 0, *s = arg->v[i];
+
+ // push word to expect at end of block, and expect a command first
+ if (add) {
+ dlist_add(expect, add); // end of context
+ if (why) dlist_add(expect, arg->v[i-1]); // context for command
+ dlist_add(expect, add = 0); // expect a command
}
- // Parse next word and detect continuation/overflow.
- if ((end = parse_word(start)) == (void *)1) return 0;
- if (!end) return start;
+ // end of argument list?
+ if (i == arg->c) break;
- // Extend pipeline and argv[], handle EOL
+ // When waiting for { it must be next symbol, but can be on a new line.
+ if (ex && !strcmp(ex, "{")) {
+ if (strcmp(s, "{") || (!i && *end && strcmp(*end, ";"))) {
+ syntax_err("need {");
+ return -1;
+ }
+ }
+
+ if (!strcmp(s, "if")) add = "then";
+ else if (!strcmp(s, "for") || !strcmp(s, "select")
+ || !strcmp(s, "while") || !strcmp(s, "until")) add = "do";
+ else if (!strcmp(s, "case")) add = "esac";
+ else if (!strcmp(s, "{")) add = "}";
+ else if (!strcmp(s, "[[")) add = "]]";
+ else if (!strcmp(s, "(")) add = ")";
+
+ // function NAME () [nl] { [nl] body ; }
+ // Why can you to declare functions inside other functions?
+ else if (arg->c>i+1 && !strcmp(arg->v[i+1], "(")) goto funky;
+ else if (!strcmp(s, "function")) {
+ i++;
+funky:
+ // At this point we can only have a function: barf if it's invalid
+ if (arg->c<i+3 || !strcmp(arg->v[i+1], "(") || !strcmp(arg->v[i+2], ")")){
+ syntax_err("bad function ()");
+ return -1;
+ }
+ // perform abnormal add (one extra piece of info) manually.
+ dlist_add(expect, "}");
+ dlist_add(expect, "function");
+ dlist_add(expect, 0);
+ dlist_add(expect, "{");
+
+ continue;
+
+ // Expecting NULL means a statement: any otherwise unrecognized word
+ } else if (expect && !ex) {
+ free(dlist_pop(expect));
+
+ // if (why) context in which statement executes now at top of expect stack
+
+ // Does this statement end with a close parentheses?
+ if (!strcmp(")", arg->v[arg->c-1])) {
+
+ // Did we expect one?
+ if (!*expect || !strcmp(")", (*expect)->prev->data)) {
+ syntax_err("bad %s", ")");
+ return -1;
+ }
+
+ free(dlist_pop(expect));
+ // only need one statement in ( ( ( echo ) ) )
+ if (*expect && !(*expect)->prev->data) free(dlist_pop(expect));
+
+ pend++;
+ goto gotparen;
+ }
+ break;
+
+ // If we aren't expecting and didn't just start a new flow control block,
+ // rest of statement is a command and arguments, so stop now
+ } else if (!ex) break;
+
+ if (add) continue;
+
+ // If we got here we expect a specific word to end this block: is this it?
+ if (!strcmp(arg->v[i], ex)
+ || (!strcmp(ex, ")") && !strcmp(ex, arg->v[arg->c-1])))
+ {
+ // can't "if | then" or "while && do", only ; & or newline works
+ if (*end && strcmp(*end, ";") && strcmp(*end, "&")) {
+ syntax_err("bad %s", *end);
+ return -1;
+ }
+
+gotparen:
+ free(dlist_pop(expect));
+ // Only innermost statement needed in { { { echo ;} ;} ;} and such
+ if (*expect && !(*expect)->prev->data) free(dlist_pop(expect));
+
+ // If this was a command ending in parentheses
+ if (pend) break;
+
+ // if it's a multipart block, what comes next?
+ if (!strcmp(s, "do")) ex = "done";
+ else if (!strcmp(s, "then")) add = "fi\0A";
+ // fi could have elif, which queues a then.
+ } else if (!strcmp(ex, "fi")) {
+ if (!strcmp(s, "elif")) {
+ free(dlist_pop(expect));
+ add = "then";
+ // catch duplicate else while we're here
+ } else if (!strcmp(s, "else")) {
+ if (ex[3] != 'A') {
+ syntax_err("2 else");
+ return -1;
+ }
+ free(dlist_pop(expect));
+ add = "fi\0B";
+ }
+ }
+ }
+
+ // Record how the previous stanza ended: ; | & ;; || && ;& ;;& |& NULL
+ *end = arg->v[arg->c];
+
+ return i;
+}
+
+// Consume a line of shell script and do what it says. Returns 0 if finished,
+// 1 to request another line of input.
+
+struct sh_parse {
+ struct double_list *pipeline, *plstart, *expect, *here;
+ char *end;
+};
+
+// pipeline and expect are scratch space, state held between calls which
+// I don't want to make global yet because this could be reentrant.
+// returns 1 to request another line (> prompt), 0 if line consumed.
+static int parse_line(char *line, struct sh_parse *sp)
+{
+ char *start = line, *delete = 0, *end, *s;
+ struct sh_arg *arg = 0;
+ struct double_list *pl;
+ long i;
+
+ // Resume appending to last statement?
+ if (sp->pipeline) {
+ arg = (void *)sp->pipeline->prev->data;
+
+ // Extend/resume quoted block
+ if (arg->c<0) {
+ start = delete = xmprintf("%s%s", arg->v[arg->c = (-arg->c)-1], start);
+ free(arg->v[arg->c]);
+ arg->v[arg->c] = 0;
+
+ // is a HERE document in progress?
+ } else if (sp->here && ((struct sh_arg *)sp->here->data)->c<0) {
+ unsigned long c;
+
+ arg = (void *)sp->here->data;
+ c = -arg->c - 1;
+
+ // HERE's arg->c < 0 means still adding to it, EOF string is last entry
+ if (!(31&c)) arg->v = xrealloc(arg->v, (32+c)*sizeof(void *));
+ if (strcmp(line, arg->v[c])) {
+ // Add this line
+ arg->v[c+1] = arg->v[c];
+ arg->v[c] = xstrdup(line);
+ arg->c--;
+ } else {
+ // EOF hit, end HERE document
+ arg->v[arg->c = c] = 0;
+ sp->here = sp->here->next;
+ }
+ start = 0;
+ }
+ }
+
+ // Parse words, assemble argv[] pipelines, check flow control and HERE docs
+ if (start) for (;;) {
+ s = 0;
+
+ // Parse next word and detect overflow (too many nested quotes).
+ if ((end = parse_word(start)) == (void *)1) goto flush;
+
+ // Extend pipeline and argv[] to store result
if (!arg)
- dlist_add(pipeline, (void *)(arg = xzalloc(sizeof(struct sh_arg))));
+ dlist_add(&sp->pipeline, (void *)(arg = xzalloc(sizeof(struct sh_arg))));
if (!(31&arg->c)) arg->v = xrealloc(arg->v, (32+arg->c)*sizeof(void *));
+
+ // Do we need to request another line to finish word (find ending quote)?
+ if (!end) {
+ // Save unparsed bit of this line, we'll need to re-parse it.
+ arg->v[arg->c] = xstrndup(start, strlen(start));
+ arg->c = -(arg->c+1);
+ free(delete);
+
+ return 1;
+ }
+
+ // Did we hit the end of this line of input?
if (end == start) {
arg->v[arg->c] = 0;
+
+ // Parse flow control data from last statement
+ if (-1 == flow_control(0, arg, &sp->expect, &sp->end)) goto flush;
+
+ // Grab HERE document(s)
+ for (pl = sp->plstart ? sp->plstart : sp->pipeline; pl;
+ pl = (pl->next == sp->pipeline) ? 0 : pl->next)
+ {
+ struct sh_arg *here;
+
+ arg = (void *)pl->data;
+
+ for (i = 0; i<arg->c; i++) {
+ // find [n]<<[-] with an argument after it
+ s = arg->v[i];
+ if (*s == '{') s++;
+ while (isdigit(*s)) s++;
+ if (*arg->v[i] == '{' && *s == '}') s++;
+ if (strcmp(s, "<<") && strcmp(s, "<<-")) continue;
+ if (i+1 == arg->c) goto flush;
+
+ here = xzalloc(sizeof(struct sh_arg));
+ here->v = xzalloc(32*sizeof(void *));
+ *here->v = arg->v[++i];
+ here->c = -1;
+ }
+ }
+
+ // Stop reading.
break;
}
- // Save argument (strdup) and check if it's special
- s = arg->v[arg->c] = xstrndup(start, end-start);
- if (!strcmp(s, "(")) paren++;
- else if (!strcmp(s, ")") && !paren--) syntax_err("bad %s", s);
- if (paren || !strchr(";|&", *start)) arg->c++;
- else {
- if (!arg->c) {
- syntax_err("bad %s", arg->v[arg->c]);
- goto flush;
- }
+ // ) only saved at start of a statement, else ends statement with NULL
+ if (arg->c && *start == ')') {
+ arg->v[arg->c] = 0;
+ end--;
+ if (-1 == flow_control(0, arg, &sp->expect, &sp->end)) goto flush;
arg = 0;
+ } else {
+ // Save argument (strdup) and check if it's special
+ s = arg->v[arg->c] = xstrndup(start, end-start);
+ if (!strchr(");|&", *start)) arg->c++;
+ else {
+ // end of statement due to flow control character.
+ s = 0;
+ if (!arg->c) goto flush;
+ if (-1 == flow_control(0, arg, &sp->expect, &sp->end)) goto flush;
+ arg = 0;
+ }
}
start = end;
}
+ free(delete);
- // We parsed to the end of the line, which ended a pipeline.
- // Now handle flow control commands, which can also need more lines.
+ // return if HERE document or more flow control
+ if (sp->expect || (sp->pipeline && sp->pipeline->prev->data==(void *)1))
+ return 1;
- // array of command lines separated by | and such
- // Note: don't preparse past ; because environment variables differ
+ // At this point, we've don't need more input and can start executing.
- // Check for flow control continuations
- end = 0;
- for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
- arg = (void *)pl->data;
- if (!arg->c) continue;
- add = 0;
+ // **************************** do the thing *******************************
- // parse flow control statements in this command line
- for (i = 0; ; i++) {
- ex = expect ? expect->prev->data : 0;
- s = arg->v[i];
-
- // push word to expect to end this block, and expect a command first
- if (add) {
- dlist_add(&expect, add);
- dlist_add(&expect, add = 0);
- }
-
- // end of statement?
- if (i == arg->c) break;
-
- // When waiting for { it must be next symbol, but can be on a new line.
- if (ex && !strcmp(ex, "{") && (strcmp(s, "{") || (!i && end))) {
- syntax_err("need {");
- goto flush;
- }
-
- if (!strcmp(s, "if")) add = "then";
- else if (!strcmp(s, "for") || !strcmp(s, "select")
- || !strcmp(s, "while") || !strcmp(s, "until")) add = "do";
- else if (!strcmp(s, "case")) add = "esac";
- else if (!strcmp(s, "{")) add = "}";
- else if (!strcmp(s, "[[")) add = "]]";
-
- // function NAME () [nl] { [nl] body ; }
- // Why can you to declare functions inside other functions?
- else if (arg->c>i+1 && !strcmp(arg->v[i+1], "(")) goto funky;
- else if (!strcmp(s, "function")) {
- i++;
-funky:
- // At this point we can only have a function: barf if it's invalid
- if (arg->c<i+3 || !strcmp(arg->v[i+1], "(")
- || !strcmp(arg->v[i+2], ")"))
- {
- syntax_err("bad function ()");
- goto flush;
- }
- dlist_add(&expect, "}");
- dlist_add(&expect, 0);
- dlist_add(&expect, "{");
-
- // Expecting NULL will take any otherwise unrecognized word
- } else if (expect && !ex) {
- free(dlist_pop(&expect));
- continue;
-
- // If we expect nothing and didn't just start a new flow control block,
- // rest of statement is a command and arguments, so stop now
- } else if (!ex) break;
-
- if (add) continue;
-
- // If we got here we expect a word to end this block: is this it?
- if (!strcmp(arg->v[i], ex)) {
- free(dlist_pop(&expect));
-
- // can't "if | then" or "while && do", only ; or newline works
- if (end && !strcmp(end, ";")) {
- syntax_err("bad %s", end);
- goto flush;
- }
-
- // if it's a multipart block, what comes next?
- if (!strcmp(s, "do")) ex = "done";
- else if (!strcmp(s, "then")) add = "fi\0A";
- // fi could have elif, which queues a then.
- } else if (!strcmp(ex, "fi")) {
- if (!strcmp(s, "elif")) {
- free(dlist_pop(&expect));
- add = "then";
- // catch duplicate else while we're here
- } else if (!strcmp(s, "else")) {
- if (ex[3] != 'A') {
- syntax_err("2 else");
- goto flush;
- }
- free(dlist_pop(&expect));
- add = "fi\0B";
- }
- }
- }
- // Record how the previous stanza ended: ; | & ;; || && ;& ;;& |& NULL
- end = arg->v[arg->c];
- }
-
- // Do we need more lines to finish a flow control statement?
- if (expect || paren) {
- llist_traverse(expect, free);
- return start;
- }
+ // Now we have a complete thought and can start running stuff.
// iterate through the commands running each one
- for (pl = *pipeline; pl ; pl = (pl->next == *pipeline) ? 0 : pl->next) {
+
+ // run a pipeline of commands
+
+ for (pl = sp->pipeline; pl ; pl = (pl->next == sp->pipeline) ? 0 : pl->next) {
struct sh_process *pp = xzalloc(sizeof(struct sh_process));
- for (i = 0; i<((struct sh_arg *)pl->data)->c; i++)
- expand_arg(&pp->arg, ((struct sh_arg *)pl->data)->v[i]);
+ for (i = 0; i<arg->c; i++) expand_arg(&pp->arg, arg->v[i]);
run_command(pp);
+ llist_traverse(pp->delete, free);
}
+ s = 0;
flush:
- while ((pl = dlist_pop(pipeline))) {
+
+ if (s) syntax_err("bad %s", s);
+ while ((pl = dlist_pop(&sp->pipeline))) {
arg = (void *)pl->data;
free(pl);
for (i = 0; i<arg->c; i++) free(arg->v[i]);
free(arg->v);
free(arg);
}
- *pipeline = 0;
+
+ while ((pl = dlist_pop(&sp->here))) {
+ arg = (void *)pl->data;
+ free(pl);
+ if (arg->c<0) arg->c = -arg->c - 1;
+ for (i = 0; i<arg->c; i++) free(arg->v[i]);
+ free(arg->v);
+ free(arg);
+ }
+
+ llist_traverse(sp->expect, free);
return 0;
}
void sh_main(void)
{
- FILE *f = 0;
- char *command = 0, *old = 0;
- struct double_list *scratch = 0;
+ FILE *f;
+ struct sh_parse scratch;
+ int prompt = 0;
// Set up signal handlers and grab control of this tty.
- if (isatty(0)) toys.optflags |= FLAG_i;
- if (*toys.optargs) f = xfopen(*toys.optargs, "r");
- if (TT.command) command = parse_line(TT.command, &scratch);
- else for (;;) {
+ memset(&scratch, 0, sizeof(scratch));
+ if (TT.command) f = fmemopen(TT.command, strlen(TT.command), "r");
+ else if (*toys.optargs) f = xfopen(*toys.optargs, "r");
+ else {
+ f = stdin;
+ if (isatty(0)) toys.optflags |= FLAG_i;
+ }
+
+ for (;;) {
char *new = 0;
size_t linelen = 0;
// Prompt and read line
- if (!f) {
- char *s = getenv(command ? "PS2" : "PS1");
+ if (f == stdin) {
+ char *s = getenv(prompt ? "PS2" : "PS1");
- if (!s) s = command ? "> " : (getpid() ? "\\$ " : "# ");
+ if (!s) s = prompt ? "> " : (getpid() ? "\\$ " : "# ");
do_prompt(s);
- }
- if (1 > getline(&new, &linelen, f ? f : stdin)) break;
- if (f) TT.lineno++;
-
- // Append to unused portion of previous line if any
- if (command) {
- command = xmprintf("%s%s", command, new);
- free(old);
- free(new);
- old = command;
- } else {
- free(old);
- old = new;
- }
+ } else TT.lineno++;
+ if (1>(linelen = getline(&new, &linelen, f ? f : stdin))) break;
+ if (new[linelen-1] == '\n') new[--linelen] = 0;
// returns 0 if line consumed, command if it needs more data
- command = parse_line(old, &scratch);
+ prompt = parse_line(new, &scratch);
+ free(new);
}
- if (command) error_exit("unfinished line");
+ if (prompt) error_exit("%ld:unfinished line"+4*!TT.lineno, TT.lineno);
toys.exitval = f && ferror(f);
}
diff --git a/toys/pending/tcpsvd.c b/toys/pending/tcpsvd.c
index 31c2761..e5bd76b 100644
--- a/toys/pending/tcpsvd.c
+++ b/toys/pending/tcpsvd.c
@@ -248,7 +248,7 @@
sockfd = xsocket(rp->ai_family, TT.udp ?SOCK_DGRAM :SOCK_STREAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
- if ((bind(sockfd, rp->ai_addr, rp->ai_addrlen)) < 0) perror_exit("Bind failed");
+ xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(res);
return sockfd;
@@ -386,8 +386,7 @@
free(serv);
free(clie);
}
- if (TT.udp && (connect(newfd, (struct sockaddr *)buf, sizeof(buf)) < 0))
- perror_exit("connect");
+ if (TT.udp) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
close(0);
close(1);
diff --git a/toys/pending/telnet.c b/toys/pending/telnet.c
index e37b982..b8c9c14 100644
--- a/toys/pending/telnet.c
+++ b/toys/pending/telnet.c
@@ -306,7 +306,7 @@
}
terminal_size(&TT.win_width, &TT.win_height);
- TT.sfd = xconnect(xgetaddrinfo(*toys.optargs, port, 0, SOCK_STREAM,
+ TT.sfd = xconnectany(xgetaddrinfo(*toys.optargs, port, 0, SOCK_STREAM,
IPPROTO_TCP, 0));
setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
setsockopt(TT.sfd, SOL_SOCKET, SO_KEEPALIVE, &set, sizeof(set));
diff --git a/toys/pending/telnetd.c b/toys/pending/telnetd.c
index 4198e63..ad39d8c 100644
--- a/toys/pending/telnetd.c
+++ b/toys/pending/telnetd.c
@@ -143,11 +143,8 @@
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) == -1)
perror_exit("setsockopt");
- if (bind(s, (struct sockaddr *)buf, ((af == AF_INET)?
- (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6)))) == -1) {
- close(s);
- perror_exit("bind");
- }
+ xbind(s, (struct sockaddr *)buf, ((af == AF_INET)?
+ (sizeof(struct sockaddr_in)):(sizeof(struct sockaddr_in6))));
if (listen(s, 1) < 0) perror_exit("listen");
return s;
diff --git a/toys/pending/tftpd.c b/toys/pending/tftpd.c
index 9791ae4..b5d0558 100644
--- a/toys/pending/tftpd.c
+++ b/toys/pending/tftpd.c
@@ -252,9 +252,8 @@
TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0);
if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set,
sizeof(set)) < 0) perror_exit("setsockopt failed");
- if (bind(TT.sfd, (void *)&srcaddr, socklen)) perror_exit("bind");
- if (connect(TT.sfd, (void *)&dstaddr, socklen) < 0)
- perror_exit("can't connect to remote host");
+ xbind(TT.sfd, (void *)&srcaddr, socklen);
+ xconnect(TT.sfd, (void *)&dstaddr, socklen);
// Error condition.
if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c
index d5ead9e..1cfdc48 100644
--- a/toys/pending/traceroute.c
+++ b/toys/pending/traceroute.c
@@ -587,8 +587,7 @@
if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF,
(struct sockaddr*)&source, sizeof(struct sockaddr_in)))
perror_exit("can't set multicast source interface");
- if (bind(TT.snd_sock,(struct sockaddr*)&source,
- sizeof(struct sockaddr_in)) < 0) perror_exit("bind");
+ xbind(TT.snd_sock,(struct sockaddr*)&source, sizeof(struct sockaddr_in));
}
if(TT.first_ttl > TT.max_ttl)
@@ -607,9 +606,7 @@
if(inet_pton(AF_INET6, TT.src_ip, &(source.sin6_addr)) <= 0)
error_exit("bad address: %s", TT.src_ip);
- if (bind(TT.snd_sock,(struct sockaddr*)&source,
- sizeof(struct sockaddr_in6)) < 0)
- error_exit("bind: Cannot assign requested address");
+ xbind(TT.snd_sock,(struct sockaddr*)&source, sizeof(struct sockaddr_in6));
} else {
struct sockaddr_in6 prb;
socklen_t len = sizeof(prb);
@@ -617,16 +614,13 @@
if (toys.optflags & FLAG_i) bind_to_interface(p_fd);
((struct sockaddr_in6 *)&dest)->sin6_port = htons(1025);
- if (connect(p_fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_in6)) < 0)
- perror_exit("can't connect to remote host");
+ xconnect(p_fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_in6));
if(getsockname(p_fd, (struct sockaddr *)&prb, &len))
error_exit("probe addr failed");
close(p_fd);
prb.sin6_port = 0;
- if (bind(TT.snd_sock, (struct sockaddr*)&prb,
- sizeof(struct sockaddr_in6))) perror_exit("bind");
- if (bind(TT.recv_sock, (struct sockaddr*)&prb,
- sizeof(struct sockaddr_in6))) perror_exit("bind");
+ xbind(TT.snd_sock, (struct sockaddr*)&prb, sizeof(struct sockaddr_in6));
+ xbind(TT.recv_sock, (struct sockaddr*)&prb, sizeof(struct sockaddr_in6));
}
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr,
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 5e7d99e..bba5480 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -377,9 +377,9 @@
if (CFG_CP_PRESERVE && FLAG(preserve)) {
char *pre = xstrdup(TT.c.preserve ? TT.c.preserve : "mot"), *s;
- if (comma_scan(pre, "all", 1)) TT.pflags = ~0;
+ if (comma_remove(pre, "all")) TT.pflags = ~0;
for (i=0; i<ARRAY_LEN(cp_preserve); i++)
- if (comma_scan(pre, cp_preserve[i].name, 1)) TT.pflags |= 1<<i;
+ while (comma_remove(pre, cp_preserve[i].name)) TT.pflags |= 1<<i;
if (*pre) {
// Try to interpret as letters, commas won't set anything this doesn't.
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 2370be7..d1e425b 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -216,7 +216,7 @@
if (!len) xputs("empty");
// 45 bytes: https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
else if (len>=45 && strstart(&s, "\177ELF")) do_elf_file(fd);
- else if (len>=8 && strstart(&s, "!<arch>\n")) xprintf("ar archive\n");
+ else if (len>=8 && strstart(&s, "!<arch>\n")) xputs("ar archive");
else if (len>28 && strstart(&s, "\x89PNG\x0d\x0a\x1a\x0a")) {
// PNG is big-endian: https://www.w3.org/TR/PNG/#7Integers-and-byte-order
int chunk_length = peek_be(s, 4);
@@ -268,7 +268,7 @@
xprintf("ASCII cpio archive (%s)\n", cpioformat);
} else if (len>33 && (magic=peek(&s,2), magic==0143561 || magic==070707)) {
if (magic == 0143561) printf("byte-swapped ");
- xprintf("cpio archive\n");
+ xputs("cpio archive");
// tar archive (old, ustar/pax, or gnu)
} else if (len>500 && is_tar_header(s))
xprintf("%s tar archive%s\n", s[257] ? "POSIX" : "old",
@@ -283,7 +283,7 @@
} else if (len>4 && strstart(&s, "BZh") && isdigit(*s))
xprintf("bzip2 compressed data, block size = %c00k\n", *s);
else if (len > 31 && peek_be(s, 7) == 0xfd377a585a0000)
- xprintf("xz compressed data");
+ 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)) {
int bit = s[0]=='\xce'?32:64;
@@ -355,6 +355,10 @@
} else if (len>12 && !memcmp(s, "ttcf\x00", 5)) {
xprintf("TrueType font collection, version %d, %d fonts\n",
(int)peek_be(s+4, 2), (int)peek_be(s+8, 4));
+
+ // https://docs.microsoft.com/en-us/typography/opentype/spec/otff
+ } else if (len>12 && !memcmp(s, "OTTO", 4)) {
+ xputs("OpenType font");
} else if (len>4 && !memcmp(s, "BC\xc0\xde", 4)) {
xputs("LLVM IR bitcode");
} else if (strstart(&s, "-----BEGIN CERTIFICATE-----")) {
@@ -380,6 +384,26 @@
int w = peek_le(s+0x12,4), h = peek_le(s+0x16,4), bpp = peek_le(s+0x1c,2);
xprintf("BMP image, %d x %d, %d bpp\n", w, h, bpp);
+
+ // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf.data-file-format.txt
+ } else if (len>=104 && !memcmp(s, "PERFILE2", 8)) {
+ xputs("Linux perf data");
+
+ // https://android.googlesource.com/platform/system/core/+/master/libsparse/sparse_format.h
+ } else if (len>28 && peek_le(s, 4) == 0xed26ff3a) {
+ xprintf("Android sparse image v%d.%d, %d %d-byte blocks (%d chunks)\n",
+ (int) peek_le(s+4, 2), (int) peek_le(s+6, 2), (int) peek_le(s+16, 4),
+ (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)) {
+ xprintf("Android boot image v%d\n", (int) peek_le(s+40, 4));
+
+ // https://source.android.com/devices/architecture/dto/partitions
+ } else if (len>32 && peek_be(s, 4) == 0xd7b7ab1e) {
+ xprintf("Android DTB/DTBO v%d, %d entries\n", (int) peek_be(s+28, 4),
+ (int) peek_be(s+16, 4));
+
} else {
char *what = 0;
int i, bytes;
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index 30a9819..b92294e 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -10,7 +10,7 @@
* echo hello | grep -f </dev/null
*
-USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
@@ -29,6 +29,7 @@
file search:
-r Recurse into subdirectories (defaults FILE to ".")
+ -R Recurse into subdirectories and symlinks to directories
-M Match filename pattern (--include)
-S Skip filename pattern (--exclude)
--exclude-dir=PATTERN Skip directory pattern
@@ -425,7 +426,7 @@
if (S_ISDIR(new->st.st_mode)) {
for (al = TT.exclude_dir; al; al = al->next)
if (!fnmatch(al->arg, new->name, 0)) return 0;
- return DIRTREE_RECURSE;
+ return DIRTREE_RECURSE|(FLAG(R)?DIRTREE_SYMFOLLOW:0);
}
if (TT.S || TT.M) {
for (al = TT.S; al; al = al->next)
@@ -464,6 +465,8 @@
TT.grey = "\033[0m";
} else TT.purple = TT.cyan = TT.red = TT.green = TT.grey = "";
+ if (FLAG(R)) toys.optflags |= FLAG_r;
+
// Grep exits with 2 for errors
toys.exitval = 2;
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c
index 9a3e9da..6f8c76f 100644
--- a/toys/posix/xargs.c
+++ b/toys/posix/xargs.c
@@ -76,7 +76,8 @@
if (!*s) break;
save = s;
- TT.bytes += sizeof(char *);
+ // We ought to add sizeof(char *) to TT.bytes to be correct, but we don't
+ // for bug compatibility with busybox 1.30.1 and findutils 4.7.0.
for (;;) {
if (++TT.bytes >= TT.s && TT.s) return save;