aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2019-03-19 13:11:08 -0700
committerElliott Hughes <enh@google.com>2019-03-19 13:21:06 -0700
commit0798b6af30ab9ae9b7e3b0943d56d049e273f09f (patch)
tree451236488c3f081025712fbaa5aad181b393ca35
parent94ba7b1e349b357a2e310272a42dc41161e5523f (diff)
parent95574e3f2a47291651f7c78c873318344565e80e (diff)
downloadtoybox-0798b6af30ab9ae9b7e3b0943d56d049e273f09f.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Iafbc08d0da8d780bc424ce9b12fd03dfd80a573a
-rw-r--r--generated/globals.h14
-rw-r--r--generated/help.h14
-rw-r--r--generated/newtoys.h18
-rw-r--r--lib/xwrap.c3
-rwxr-xr-xtests/cmp.test6
-rw-r--r--tests/date.test2
-rw-r--r--tests/diff.test7
-rwxr-xr-xtests/env.test2
-rwxr-xr-xtests/grep.test11
-rwxr-xr-xtests/sort.test1
-rw-r--r--tests/timeout.test1
-rw-r--r--toys/android/load_policy.c4
-rw-r--r--toys/other/chroot.c9
-rw-r--r--toys/pending/bc.c15
-rw-r--r--toys/pending/diff.c18
-rw-r--r--toys/pending/tar.c462
-rw-r--r--toys/posix/cmp.c3
-rw-r--r--toys/posix/env.c4
-rw-r--r--toys/posix/grep.c8
-rw-r--r--toys/posix/nice.c8
-rw-r--r--toys/posix/nohup.c6
-rw-r--r--toys/posix/sort.c19
22 files changed, 299 insertions, 336 deletions
diff --git a/generated/globals.h b/generated/globals.h
index 5a8774a5..60f31597 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -810,10 +810,20 @@ struct tar_data {
// exc is an argument but inc isn't?
struct arg_list *inc, *pass;
- void *inodes, *handle;
+ void *inodes;
char *cwd;
int fd;
- unsigned short offset; // only ever used to calculate 512 byte padding
+
+ // Parsed information about a tar header.
+ struct {
+ char *name, *link_target, *uname, *gname;
+ long long size;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ time_t mtime;
+ dev_t device;
+ } hdr;
};
// toys/pending/tcpsvd.c
diff --git a/generated/help.h b/generated/help.h
index a889a7e5..b5c532e7 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -50,7 +50,7 @@
#define HELP_log "usage: log [-p PRI] [-t TAG] MESSAGE...\n\nLogs message to logcat.\n\n-p Use the given priority instead of INFO:\n d: DEBUG e: ERROR f: FATAL i: INFO v: VERBOSE w: WARN s: SILENT\n-t Use the given tag instead of \"log\"\n\n"
-#define HELP_load_policy "usage: load_policy FILE\n\nLoad the specified policy file.\n\n"
+#define HELP_load_policy "usage: load_policy FILE\n\nLoad the specified SELinux policy file.\n\n"
#define HELP_getprop "usage: getprop [NAME [DEFAULT]]\n\nGets an Android system property, or lists them all.\n\n"
@@ -296,7 +296,7 @@
#define HELP_chrt "usage: chrt [-Rmofrbi] {-p PID [PRIORITY] | [PRIORITY COMMAND...]}\n\nGet/set a process' real-time scheduling policy and priority.\n\n-p Set/query given pid (instead of running COMMAND)\n-R Set SCHED_RESET_ON_FORK\n-m Show min/max priorities available\n\nSet policy (default -r):\n\n -o SCHED_OTHER -f SCHED_FIFO -r SCHED_RR\n -b SCHED_BATCH -i SCHED_IDLE\n\n"
-#define HELP_chroot "usage: chroot NEWPATH [commandline...]\n\nRun command within a new root directory. If no command, run /bin/sh.\n\n"
+#define HELP_chroot "usage: chroot NEWROOT [COMMAND [ARG...]]\n\nRun command within a new root directory. If no command, run /bin/sh.\n\n"
#define HELP_chcon "usage: chcon [-hRv] CONTEXT FILE...\n\nChange the SELinux security context of listed file[s].\n\n-h Change symlinks instead of what they point to\n-R Recurse into subdirectories\n-v Verbose\n\n"
@@ -482,7 +482,7 @@
#define HELP_split "usage: split [-a SUFFIX_LEN] [-b BYTES] [-l LINES] [INPUT [OUTPUT]]\n\nCopy INPUT (or stdin) data to a series of OUTPUT (or \"x\") files with\nalphabetically increasing suffix (aa, ab, ac... az, ba, bb...).\n\n-a Suffix length (default 2)\n-b BYTES/file (10, 10k, 10m, 10g...)\n-l LINES/file (default 1000)\n\n"
-#define HELP_sort "usage: sort [-Mbcdfginrsuz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE]\n\nSort all lines of text from input files (or stdin) to stdout.\n-M Month sort (jan, feb, etc)\n-V Version numbers (name-1.234-rc6.5b.tgz)\n-b Ignore leading blanks (or trailing blanks in second part of key)\n-c Check whether input is sorted\n-d Dictionary order (use alphanumeric and whitespace chars only)\n-f Force uppercase (case insensitive sort)\n-g General numeric sort (double precision with nan and inf)\n-i Ignore nonprinting characters\n-k Sort by \"key\" (see below)\n-n Numeric order (instead of alphabetical)\n-o Output to FILE instead of stdout\n-r Reverse\n-s Skip fallback sort (only sort with keys)\n-t Use a key separator other than whitespace\n-u Unique lines only\n-x Hexadecimal numerical sort\n-z Zero (null) terminated lines\n\nSorting by key looks at a subset of the words on each line. -k2\nuses the second word to the end of the line, -k2,2 looks at only\nthe second word, -k2,4 looks from the start of the second to the end\nof the fourth word. Specifying multiple keys uses the later keys as\ntie breakers, in order. A type specifier appended to a sort key\n(such as -2,2n) applies only to sorting that key.\n"
+#define HELP_sort "usage: sort [-Mbcdfginrsuz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE]\n\nSort all lines of text from input files (or stdin) to stdout.\n-M Month sort (jan, feb, etc)\n-V Version numbers (name-1.234-rc6.5b.tgz)\n-b Ignore leading blanks (or trailing blanks in second part of key)\n-c Check whether input is sorted\n-d Dictionary order (use alphanumeric and whitespace chars only)\n-f Force uppercase (case insensitive sort)\n-g General numeric sort (double precision with nan and inf)\n-i Ignore nonprinting characters\n-k Sort by \"key\" (see below)\n-n Numeric order (instead of alphabetical)\n-o Output to FILE instead of stdout\n-r Reverse\n-s Skip fallback sort (only sort with keys)\n-t Use a key separator other than whitespace\n-u Unique lines only\n-x Hexadecimal numerical sort\n-z Zero (null) terminated lines\n\nSorting by key looks at a subset of the words on each line. -k2 uses the\nsecond word to the end of the line, -k2,2 looks at only the second word,\n-k2,4 looks from the start of the second to the end of the fourth word.\n-k2.4,5 starts from the fourth character of the second word, to the end\nof the fifth word. Specifying multiple keys uses the later keys as tie\nbreakers, in order. A type specifier appended to a sort key (such as -2,2n)\napplies only to sorting that key.\n"
#define HELP_sleep "usage: sleep DURATION\n\nWait before exiting.\n\nDURATION can be a decimal fraction. An optional suffix can be \"m\"\n(minutes), \"h\" (hours), \"d\" (days), or \"s\" (seconds, the default).\n\n"
@@ -514,11 +514,11 @@
#define HELP_od "usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]]\n\nDump data in octal/hex.\n\n-A Address base (decimal, octal, hexadecimal, none)\n-j Skip this many bytes of input\n-N Stop dumping after this many bytes\n-t Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x\n plus optional size in bytes\n aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2\n-v Don't collapse repeated lines together\n-w Total line width in bytes (default 16)\n\n"
-#define HELP_nohup "usage: nohup COMMAND [ARGS...]\n\nRun a command that survives the end of its terminal.\n\nRedirect tty on stdin to /dev/null, tty on stdout to \"nohup.out\".\n\n"
+#define HELP_nohup "usage: nohup COMMAND [ARG...]\n\nRun a command that survives the end of its terminal.\n\nRedirect tty on stdin to /dev/null, tty on stdout to \"nohup.out\".\n\n"
#define HELP_nl "usage: nl [-E] [-l #] [-b MODE] [-n STYLE] [-s SEPARATOR] [-w WIDTH] [FILE...]\n\nNumber lines of input.\n\n-E Use extended regex syntax (when doing -b pREGEX)\n-b Which lines to number: a (all) t (non-empty, default) pREGEX (pattern)\n-l Only count last of this many consecutive blank lines\n-n Number STYLE: ln (left justified) rn (right justified) rz (zero pad)\n-s Separator to use between number and line (instead of TAB)\n-w Width of line numbers (default 6)\n\n"
-#define HELP_nice "usage: nice [-n PRIORITY] command [args...]\n\nRun a command line at an increased or decreased scheduling priority.\n\nHigher numbers make a program yield more CPU time, from -20 (highest\npriority) to 19 (lowest). By default processes inherit their parent's\nniceness (usually 0). By default this command adds 10 to the parent's\npriority. Only root can set a negative niceness level.\n\n"
+#define HELP_nice "usage: nice [-n PRIORITY] COMMAND [ARG...]\n\nRun a command line at an increased or decreased scheduling priority.\n\nHigher numbers make a program yield more CPU time, from -20 (highest\npriority) to 19 (lowest). By default processes inherit their parent's\nniceness (usually 0). By default this command adds 10 to the parent's\npriority. Only root can set a negative niceness level.\n\n"
#define HELP_mkfifo "usage: mkfifo [-Z CONTEXT] [NAME...]\n\nCreate FIFOs (named pipes).\n\n-Z Security context\n"
@@ -550,7 +550,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\n\n"
-#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.\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-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\n\n"
+#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-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\n\n"
#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)\n\n"
@@ -562,7 +562,7 @@
#define HELP_expand "usage: expand [-t TABLIST] [FILE...]\n\nExpand tabs to spaces according to tabstops.\n\n-t TABLIST\n\nSpecify tab stops, either a single number instead of the default 8,\nor a comma separated list of increasing numbers representing tabstop\npositions (absolute, not increments) with each additional tab beyond\nthat becoming one space.\n\n"
-#define HELP_env "usage: env [-i] [-u NAME] [NAME=VALUE...] [command [option...]]\n\nSet the environment for command invocation, or list environment variables.\n\n-i Clear existing environment\n-u NAME Remove NAME from the environment\n-0 Use null instead of newline in output\n\n"
+#define HELP_env "usage: env [-i] [-u NAME] [NAME=VALUE...] [COMMAND [ARG...]]\n\nSet the environment for command invocation, or list environment variables.\n\n-i Clear existing environment\n-u NAME Remove NAME from the environment\n-0 Use null instead of newline in output\n\n"
#define HELP_echo "usage: echo [-ne] [args...]\n\nWrite each argument to stdout, with one space between each, followed\nby a newline.\n\n-n No trailing newline\n-e Process the following escape sequences:\n \\\\ Backslash\n \\0NNN Octal values (1 to 3 digits)\n \\a Alert (beep/flash)\n \\b Backspace\n \\c Stop output here (avoids trailing newline)\n \\f Form feed\n \\n Newline\n \\r Carriage return\n \\t Horizontal tab\n \\v Vertical tab\n \\xHH Hexadecimal values (1 to 2 digits)\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index 614eff27..0c8ef6c7 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -26,12 +26,12 @@ USE_CHCON(NEWTOY(chcon, "<2hvR", TOYFLAG_USR|TOYFLAG_BIN))
USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN))
-USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_ARGFAIL(125)))
USE_CHRT(NEWTOY(chrt, "^mp#<0iRbrfo[!ibrfo]", TOYFLAG_USR|TOYFLAG_BIN))
USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_CKSUM(NEWTOY(cksum, "HIPLN", TOYFLAG_BIN))
USE_CLEAR(NEWTOY(clear, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_CMP(NEWTOY(cmp, "<2>2ls(silent)(quiet)[!ls]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_CMP(NEWTOY(cmp, "<2>2ls(silent)(quiet)[!ls]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_COMM(NEWTOY(comm, "<2>2321", TOYFLAG_USR|TOYFLAG_BIN))
USE_COUNT(NEWTOY(count, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"D(parents)RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
@@ -54,16 +54,16 @@ USE_DF(NEWTOY(df, "HPkhit*a[-HPkh]", TOYFLAG_SBIN))
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))
+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_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))
USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsx[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
USE_DUMPLEASES(NEWTOY(dumpleases, ">0arf:[!ar]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ECHO(NEWTOY(echo, "^?en", TOYFLAG_BIN))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_EJECT(NEWTOY(eject, ">1stT[!tT]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_ENV(NEWTOY(env, "^0iu*", TOYFLAG_USR|TOYFLAG_BIN))
+USE_ENV(NEWTOY(env, "^0iu*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
@@ -71,7 +71,7 @@ USE_FACTOR(NEWTOY(factor, 0, TOYFLAG_USR|TOYFLAG_BIN))
USE_FALLOCATE(NEWTOY(fallocate, ">1l#|o#", TOYFLAG_USR|TOYFLAG_BIN))
USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN|TOYFLAG_NOHELP))
USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_FILE(NEWTOY(file, "<1hL[!hL]", TOYFLAG_USR|TOYFLAG_BIN))
USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
USE_FLOCK(NEWTOY(flock, "<1>1nsux[-sux]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -90,7 +90,7 @@ USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETFATTR(NEWTOY(getfattr, "(only-values)dhn:", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETPROP(NEWTOY(getprop, ">2Z", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
-USE_GREP(NEWTOY(grep, "(color):;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))
+USE_GREP(NEWTOY(grep, "(color):;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_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))
@@ -173,7 +173,7 @@ USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#<1W#<1p#<1>65535q#<1s:f:46
USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_BIN))
USE_NL(NEWTOY(nl, "v#<1=1l#w#<0=6Eb:n:s:", TOYFLAG_USR|TOYFLAG_BIN))
-USE_NOHUP(NEWTOY(nohup, "<1^", TOYFLAG_USR|TOYFLAG_BIN))
+USE_NOHUP(NEWTOY(nohup, "<1^", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
USE_NPROC(NEWTOY(nproc, "(all)", TOYFLAG_USR|TOYFLAG_BIN))
USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
@@ -229,7 +229,7 @@ USE_SKELETON(NEWTOY(skeleton, "(walrus)(blubber):;(also):e@d*c#b:a", TOYFLAG_USR
USE_SKELETON_ALIAS(NEWTOY(skeleton_alias, "b#dq", TOYFLAG_USR|TOYFLAG_BIN))
USE_SLEEP(NEWTOY(sleep, "<1", TOYFLAG_BIN))
USE_SNTP(NEWTOY(sntp, "M:m:Sp:asdDqr#<4>17=10[!as]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
USE_SPLIT(NEWTOY(split, ">2a#<1=2>9b#<1l#<1[!bl]", TOYFLAG_USR|TOYFLAG_BIN))
USE_START(NEWTOY(start, "", TOYFLAG_USR|TOYFLAG_SBIN))
USE_STAT(NEWTOY(stat, "<1c:(format)fLt", TOYFLAG_BIN))
diff --git a/lib/xwrap.c b/lib/xwrap.c
index d7b06c5a..c133125a 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -95,10 +95,9 @@ void *xrealloc(void *ptr, size_t size)
// Die unless we can allocate a copy of this many bytes of string.
char *xstrndup(char *s, size_t n)
{
- char *ret = strndup(s, ++n);
+ char *ret = strndup(s, n);
if (!ret) error_exit("xstrndup");
- ret[--n] = 0;
return ret;
}
diff --git a/tests/cmp.test b/tests/cmp.test
index 3b2dd1b2..62d4b89d 100755
--- a/tests/cmp.test
+++ b/tests/cmp.test
@@ -2,8 +2,10 @@
[ -f testing.sh ] && . testing.sh
-testing "not enough arguments [fail]" "cmp input 2>/dev/null || echo yes" "yes\n" "foo" ""
-testing "missing file1 [fail]" "cmp file1 input 2>/dev/null || echo yes" "yes\n" "foo" ""
+# TODO: coreutils cmp uses stdin if only one file is given
+SKIP_HOST=1 testing "not enough arguments [fail]" 'cmp input 2>/dev/null || echo $?' "2\n" "foo" ""
+
+testing "missing file1 [fail]" 'cmp file1 input 2>/dev/null || echo $?' "2\n" "foo" ""
#mkdir dir
#testing "directory [fail]" "cmp dir dir 2>/dev/null || echo yes" \
diff --git a/tests/date.test b/tests/date.test
index 285d3dec..a317d35b 100644
--- a/tests/date.test
+++ b/tests/date.test
@@ -16,7 +16,7 @@ testing "-d @0x123 invalid" "TZ=$tz date -d @0x123 2>/dev/null || echo expected
# All SKIP_HOST=1 because coreutils rejects POSIX format dates supplied to -d.
# These expected values are from running on the host without -d (not as root!).
SKIP_HOST=1 testing "-d MMDDhhmm" \
- "TZ=$tz date -d 06021234 2>&1" "Sun Jun 2 12:34:00 CEST 2019\n" "" ""
+ "TZ=$tz date -d 06021234 2>&1" "Sun Jun 2 12:34:00 CEST $(date +%Y)\n" "" ""
SKIP_HOST=1 testing "-d MMDDhhmmYY.SS" \
"TZ=$tz date -d 1110143115.30 2>&1" "Tue Nov 10 14:31:30 CET 2015\n" "" ""
# busybox thinks this is the year 603 (ISO time 0602-12-34 19:82 with out of range fields normalized).
diff --git a/tests/diff.test b/tests/diff.test
index ce51f1e8..98477587 100644
--- a/tests/diff.test
+++ b/tests/diff.test
@@ -5,6 +5,11 @@
seq 10 > left
seq 11 > right
+testing "unknown argument" 'diff --oops left right 2>/dev/null ; echo $?' "2\n" "" ""
+testing "missing" 'diff missing1 missing2 2>/dev/null ; echo $?' "2\n" "" ""
+
+testing "- -" 'diff - - ; echo $?' "0\n" "" "whatever"
+
expected='--- lll
+++ rrr
@@ -8,3 +8,4 @@
@@ -27,4 +32,4 @@ mkdir -p tree1 tree2
echo foo > tree1/file
echo food > tree2/file
-testing "simple" "diff -r -L tree1/file -L tree2/file tree1 tree2 |tee out" "$expected" "" ""
+testing "-r" "diff -r -L tree1/file -L tree2/file tree1 tree2 |tee out" "$expected" "" ""
diff --git a/tests/env.test b/tests/env.test
index e92164c5..286fb36c 100755
--- a/tests/env.test
+++ b/tests/env.test
@@ -16,3 +16,5 @@ testcmd "-i =" "-i one=two three=four env | sort" \
"one=two\nthree=four\n" "" ""
testcmd "-0" "-i five=six seven=eight env -0 | sort -z" "five=six\0seven=eight\0" "" ""
unset WALRUS BANANA LETTERS FILTER
+
+testcmd "early fail" '--oops 2> /dev/null ; echo $?' "125\n" "" ""
diff --git a/tests/grep.test b/tests/grep.test
index 05288983..798a9c6a 100755
--- a/tests/grep.test
+++ b/tests/grep.test
@@ -154,3 +154,14 @@ testing "explicit ERE |" "grep -E 'uno|dos'" "uno\ndos\nuno|dos\n" \
testing "" "grep -o -e iss -e ipp" "iss\niss\nipp\n" "" "mississippi"
testing "" "grep -o -e gum -e rgu" "rgu\n" "" "argument"
+
+testing "early failure" 'grep --what 2>/dev/null || echo $?' "2\n" "" ""
+
+testing "" 'grep abc ; echo $?' "abcdef\n0\n" "" "abcdef\n"
+testing "" 'grep abc doesnotexist input 2>/dev/null; echo $?' \
+ "input:abcdef\n2\n" "abcdef\n" ""
+mkdir sub
+ln -s nope sub/link
+testing "" 'grep -r walrus sub 2>/dev/null; echo $?' "1\n" "" ""
+rm -rf sub
+
diff --git a/tests/sort.test b/tests/sort.test
index a40d1b0f..dd2b8263 100755
--- a/tests/sort.test
+++ b/tests/sort.test
@@ -7,6 +7,7 @@
# The basic tests. These should work even with the small config.
+testing "unknown argument" 'sort --oops 2>/dev/null ; echo $?' "2\n" "" ""
testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" ""
testing "#2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
testing "stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
diff --git a/tests/timeout.test b/tests/timeout.test
index 5ca7cc90..189b592a 100644
--- a/tests/timeout.test
+++ b/tests/timeout.test
@@ -7,6 +7,7 @@
# timeout's exit value is complicated!
testcmd "times out" '.1 sleep 100 ; echo $?' '124\n' '' ''
testcmd "failure" '-s MONKEY .1 sleep 100 2>/dev/null ; echo $?' '125\n' '' ''
+testcmd "early failure" '2>/dev/null ; echo $?' '125\n' '' ''
testcmd "can't execute" '.1 / 2>/dev/null ; echo $?' '126\n' '' ''
testcmd "can't find" '.1 /does/not/exist 2>/dev/null ; echo $?' '127\n' '' ''
testcmd "custom signal" '-s 3 .1 sleep 100; echo $?' '124\n' '' ''
diff --git a/toys/android/load_policy.c b/toys/android/load_policy.c
index bc0d5640..523c68f8 100644
--- a/toys/android/load_policy.c
+++ b/toys/android/load_policy.c
@@ -1,4 +1,4 @@
-/* load_policy.c - Load a policy file
+/* load_policy.c - Load an SELinux policy file
*
* Copyright 2015 The Android Open Source Project
@@ -11,7 +11,7 @@ config LOAD_POLICY
help
usage: load_policy FILE
- Load the specified policy file.
+ Load the specified SELinux policy file.
*/
#define FOR_load_policy
diff --git a/toys/other/chroot.c b/toys/other/chroot.c
index b6ef17d6..d791f34a 100644
--- a/toys/other/chroot.c
+++ b/toys/other/chroot.c
@@ -7,13 +7,13 @@
* The container guys use pivot_root() to deal with this, which does actually
* edit mount tree. (New option? Kernel patch?)
-USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_ARGFAIL(125)))
config CHROOT
bool "chroot"
default y
help
- usage: chroot NEWPATH [commandline...]
+ usage: chroot NEWROOT [COMMAND [ARG...]]
Run command within a new root directory. If no command, run /bin/sh.
*/
@@ -24,7 +24,10 @@ void chroot_main(void)
{
char *binsh[] = {"/bin/sh", "-i", 0};
- if (chdir(*toys.optargs) || chroot(".")) perror_exit_raw(*toys.optargs);
+ if (chdir(*toys.optargs) || chroot(".")) {
+ toys.exitval = 125;
+ perror_exit_raw(*toys.optargs);
+ }
if (toys.optargs[1]) xexec(toys.optargs+1);
else xexec(binsh);
}
diff --git a/toys/pending/bc.c b/toys/pending/bc.c
index 614cae70..142c0ce2 100644
--- a/toys/pending/bc.c
+++ b/toys/pending/bc.c
@@ -2120,21 +2120,22 @@ BcStatus bc_num_parse(BcNum *n, char *val,
BcStatus bc_num_ulong(BcNum *n, unsigned long *result) {
size_t i;
- unsigned long pow, r;
+ unsigned long r;
*result = 0;
if (n->neg) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
- for (r = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
+ for (r = 0, i = n->len; i > n->rdx;) {
- unsigned long prev = r, powprev = pow;
+ unsigned long prev = r * 10;
- r += ((unsigned long) n->num[i]) * pow;
- pow *= 10;
+ if (prev == SIZE_MAX || prev / 10 != r)
+ return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
- if (r < prev || pow < powprev)
- return bc_vm_verr(BC_ERROR_MATH_OVERFLOW, "number cannot fit");
+ r = prev + ((uchar) n->num[--i]);
+
+ if (r == SIZE_MAX || r < prev) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
}
*result = r;
diff --git a/toys/pending/diff.c b/toys/pending/diff.c
index ea11ba29..4a528134 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))
+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)))
config DIFF
bool "diff"
@@ -791,32 +791,28 @@ void diff_main(void)
int j = 0, k = 1, start[2] = {1, 1};
char *files[2];
+ toys.exitval = 2;
+
if ((toys.optflags & FLAG_color) && !isatty(1)) toys.optflags ^= FLAG_color;
for (j = 0; j < 2; j++) {
files[j] = toys.optargs[j];
if (IS_STDIN(files[j])) {
if (fstat(0, &TT.st[j]) == -1)
- perror_exit("can fstat %s", files[j]);
+ perror_exit("can't fstat %s", files[j]);
} else {
- if (stat(files[j], &TT.st[j]) == -1)
- perror_exit("can't stat %s", files[j]);
+ xstat(files[j], &TT.st[j]);
}
}
- if (IS_STDIN(files[0]) && IS_STDIN(files[1])) { //compat :(
- show_status(files); //check ASAP
- return;
- }
-
if ((IS_STDIN(files[0]) || IS_STDIN(files[1]))
&& (S_ISDIR(TT.st[0].st_mode) || S_ISDIR(TT.st[1].st_mode)))
error_exit("can't compare stdin to directory");
if ((TT.st[0].st_ino == TT.st[1].st_ino) //physicaly same device
&& (TT.st[0].st_dev == TT.st[1].st_dev)) {
- show_status(files);
- return ;
+ toys.exitval = 0;
+ return show_status(files);
}
if (S_ISDIR(TT.st[0].st_mode) && S_ISDIR(TT.st[1].st_mode)) {
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index 6bb2b0e9..97e699b4 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -54,10 +54,20 @@ GLOBALS(
// exc is an argument but inc isn't?
struct arg_list *inc, *pass;
- void *inodes, *handle;
+ void *inodes;
char *cwd;
int fd;
- unsigned short offset; // only ever used to calculate 512 byte padding
+
+ // Parsed information about a tar header.
+ struct {
+ char *name, *link_target, *uname, *gname;
+ long long size;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ time_t mtime;
+ dev_t device;
+ } hdr;
)
struct tar_hdr {
@@ -66,22 +76,6 @@ struct tar_hdr {
prefix[155], padd[12];
};
-// Parsed information about a tar header.
-struct file_header {
- char *name, *link_target, *uname, *gname;
- long long size;
- uid_t uid;
- gid_t gid;
- mode_t mode;
- time_t mtime;
- dev_t device;
-};
-
-struct archive_handler {
- struct file_header file_hdr;
- void (*extract_handler)(struct archive_handler*);
-};
-
struct inode_list {
struct inode_list *next;
char *arg;
@@ -115,11 +109,20 @@ static struct inode_list *seen_inode(void **list, struct stat *st, char *name)
return 0;
}
+// Calculate packet checksum, with cksum field treated as 8 spaces
+static unsigned cksum(void *data)
+{
+ unsigned i, cksum = 8*' ';
+
+ for (i = 0; i<500; i += (i==147) ? 9 : 1) cksum += ((char *)data)[i];
+
+ return cksum;
+}
+
static void write_longname(char *name, char type)
{
struct tar_hdr tmp[2];
- unsigned int sum = 0;
- int i, sz = strlen(name) +1;
+ int sz = strlen(name) +1;
memset(tmp, 0, sizeof(tmp));
strcpy(tmp->name, "././@LongLink");
@@ -132,10 +135,7 @@ static void write_longname(char *name, char type)
strcpy(tmp->magic, "ustar ");
// Calculate checksum
- memset(tmp->chksum, ' ', 8);
- for (i = 0; i < 512; i++) sum += ((char *)tmp)[i];
-// TODO: why is this -1 when the rest aren't?
- itoo(tmp->chksum, sizeof(tmp->chksum)-1, sum);
+ itoo(tmp->chksum, sizeof(tmp->chksum), cksum(&tmp));
// write header and name, padded with NUL to block size
xwrite(TT.fd, tmp, sizeof(*tmp));
@@ -153,13 +153,6 @@ static int filter(struct arg_list *lst, char *name)
static void skippy(long long len)
{
if (lskip(TT.fd, len)) perror_exit("EOF");
- TT.offset += len;
-}
-
-static void sendlen(int fd, long long len)
-{
- xsendfile_len(TT.fd, fd, len);
- TT.offset += len;
}
// actually void **, but automatic typecasting doesn't work with void ** :(
@@ -170,7 +163,7 @@ static void alloread(void *buf, int len)
free(*b);
*b = xmalloc(len+1);
xreadall(TT.fd, *b, len);
- TT.offset += len;
+ b[len] = 0;
}
static void add_file(char **nam, struct stat *st)
@@ -181,7 +174,6 @@ static void add_file(char **nam, struct stat *st)
struct inode_list *node = node;
int i, fd =-1;
char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,};
- unsigned int sum = 0;
static int warn = 1;
for (p = name; *p; p++)
@@ -210,7 +202,6 @@ static void add_file(char **nam, struct stat *st)
itoo(hdr.gid, sizeof(hdr.gid), st->st_gid);
itoo(hdr.size, sizeof(hdr.size), 0); //set size later
itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime);
- memset(hdr.chksum, ' ', sizeof(hdr.chksum));
// Hard link or symlink?
i = !!S_ISLNK(st->st_mode);
@@ -252,9 +243,7 @@ static void add_file(char **nam, struct stat *st)
snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name);
else sprintf(hdr.gname, "%d", st->st_gid);
- //calculate chksum.
- for (i = 0; i < 512; i++) sum += ((char*)&hdr)[i];
- itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum);
+ itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr));
if (FLAG(v)) printf("%s\n",hname);
xwrite(TT.fd, (void*)&hdr, 512);
@@ -288,28 +277,14 @@ static int add_to_tar(struct dirtree *node)
return ((DIRTREE_RECURSE | (FLAG(h)?DIRTREE_SYMFOLLOW:0)));
}
-static void extract_stream(struct archive_handler *tar_hdl)
-{
- int pipefd[2] = {TT.fd, -1};
-
- xpopen_both((char *[]){FLAG(z)?"gunzip":"bunzip2", "-cf", "-", NULL}, pipefd);
- close(TT.fd);
- TT.fd = pipefd[1];
-}
-
-static void extract_to_stdout(struct archive_handler *tar)
-{
- sendlen(0, tar->file_hdr.size);
-}
-
-static void extract_to_command(struct archive_handler *tar)
+// Does anybody actually use this?
+static void extract_to_command(void)
{
int pipefd[2], status = 0;
pid_t cpid;
- struct file_header *file_hdr = &tar->file_hdr;
xpipe(pipefd);
- if (!S_ISREG(file_hdr->mode)) return; //only regular files are supported.
+ if (!S_ISREG(TT.hdr.mode)) return; //only regular files are supported.
cpid = fork();
if (cpid == -1) perror_exit("fork");
@@ -318,18 +293,18 @@ static void extract_to_command(struct archive_handler *tar)
char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL};
setenv("TAR_FILETYPE", "f", 1);
- sprintf(buf, "%0o", file_hdr->mode);
+ sprintf(buf, "%0o", TT.hdr.mode);
setenv("TAR_MODE", buf, 1);
- sprintf(buf, "%ld", (long)file_hdr->size);
+ sprintf(buf, "%ld", (long)TT.hdr.size);
setenv("TAR_SIZE", buf, 1);
- setenv("TAR_FILENAME", file_hdr->name, 1);
- setenv("TAR_UNAME", file_hdr->uname, 1);
- setenv("TAR_GNAME", file_hdr->gname, 1);
- sprintf(buf, "%0o", (int)file_hdr->mtime);
+ setenv("TAR_FILENAME", TT.hdr.name, 1);
+ setenv("TAR_UNAME", TT.hdr.uname, 1);
+ setenv("TAR_GNAME", TT.hdr.gname, 1);
+ sprintf(buf, "%0o", (int)TT.hdr.mtime);
setenv("TAR_MTIME", buf, 1);
- sprintf(buf, "%0o", file_hdr->uid);
+ sprintf(buf, "%0o", TT.hdr.uid);
setenv("TAR_UID", buf, 1);
- sprintf(buf, "%0o", file_hdr->gid);
+ sprintf(buf, "%0o", TT.hdr.gid);
setenv("TAR_GID", buf, 1);
xclose(pipefd[1]); // Close unused write
@@ -338,7 +313,7 @@ static void extract_to_command(struct archive_handler *tar)
xexec(argv);
} else {
xclose(pipefd[0]); // Close unused read end
- sendlen(pipefd[1], file_hdr->size);
+ xsendfile_len(TT.fd, pipefd[1], TT.hdr.size);
xclose(pipefd[1]);
waitpid(cpid, &status, 0);
if (WIFSIGNALED(status))
@@ -346,95 +321,89 @@ static void extract_to_command(struct archive_handler *tar)
}
}
-static void extract_to_disk(struct archive_handler *tar)
+static void extract_to_disk(void)
{
int flags, dst_fd = -1;
char *s;
struct stat ex;
- struct file_header *file_hdr = &tar->file_hdr;
// while not if
- flags = strlen(file_hdr->name);
+ flags = strlen(TT.hdr.name);
if (flags>2)
- if (strstr(file_hdr->name, "/../") || !strcmp(file_hdr->name, "../") ||
- !strcmp(file_hdr->name+flags-3, "/.."))
- error_msg("drop %s", file_hdr->name);
+ if (strstr(TT.hdr.name, "/../") || !strcmp(TT.hdr.name, "../") ||
+ !strcmp(TT.hdr.name+flags-3, "/.."))
+ error_msg("drop %s", TT.hdr.name);
- if (file_hdr->name[flags-1] == '/') file_hdr->name[flags-1] = 0;
+ if (TT.hdr.name[flags-1] == '/') TT.hdr.name[flags-1] = 0;
//Regular file with preceding path
- if ((s = strrchr(file_hdr->name, '/'))) {
- if (mkpath(file_hdr->name) && errno !=EEXIST) {
- error_msg(":%s: not created", file_hdr->name);
- return;
- }
- }
+ if ((s = strrchr(TT.hdr.name, '/')) && mkpath(TT.hdr.name) && errno !=EEXIST)
+ return error_msg(":%s: not created", TT.hdr.name);
//remove old file, if exists
- if (!FLAG(k) && !S_ISDIR(file_hdr->mode) && !lstat(file_hdr->name, &ex))
- if (unlink(file_hdr->name))
- perror_msg("can't remove: %s", file_hdr->name);
+ if (!FLAG(k) && !S_ISDIR(TT.hdr.mode) && !lstat(TT.hdr.name, &ex))
+ if (unlink(TT.hdr.name)) perror_msg("can't remove: %s", TT.hdr.name);
//hard link
- if (S_ISREG(file_hdr->mode) && file_hdr->link_target) {
- if (link(file_hdr->link_target, file_hdr->name))
- perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
+ if (S_ISREG(TT.hdr.mode) && TT.hdr.link_target) {
+ if (link(TT.hdr.link_target, TT.hdr.name))
+ perror_msg("can't link '%s' -> '%s'", TT.hdr.name, TT.hdr.link_target);
goto COPY;
}
- switch (file_hdr->mode & S_IFMT) {
+ switch (TT.hdr.mode & S_IFMT) {
case S_IFREG:
flags = O_WRONLY|O_CREAT|O_EXCL;
if (FLAG(overwrite)) flags = O_WRONLY|O_CREAT|O_TRUNC;
- dst_fd = open(file_hdr->name, flags, file_hdr->mode & 07777);
- if (dst_fd == -1) perror_msg("%s: can't open", file_hdr->name);
+ dst_fd = open(TT.hdr.name, flags, TT.hdr.mode & 07777);
+ if (dst_fd == -1) perror_msg("%s: can't open", TT.hdr.name);
break;
case S_IFDIR:
- if ((mkdir(file_hdr->name, file_hdr->mode) == -1) && errno != EEXIST)
- perror_msg("%s: can't create", file_hdr->name);
+ if ((mkdir(TT.hdr.name, TT.hdr.mode) == -1) && errno != EEXIST)
+ perror_msg("%s: can't create", TT.hdr.name);
break;
case S_IFLNK:
- if (symlink(file_hdr->link_target, file_hdr->name))
- perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
+ if (symlink(TT.hdr.link_target, TT.hdr.name))
+ perror_msg("can't link '%s' -> '%s'", TT.hdr.name, TT.hdr.link_target);
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
- if (mknod(file_hdr->name, file_hdr->mode, file_hdr->device))
- perror_msg("can't create '%s'", file_hdr->name);
+ if (mknod(TT.hdr.name, TT.hdr.mode, TT.hdr.device))
+ perror_msg("can't create '%s'", TT.hdr.name);
break;
default:
- printf("type %o not supported\n", file_hdr->mode);
+ printf("type %o not supported\n", TT.hdr.mode);
break;
}
//copy file....
COPY:
- sendlen(dst_fd, file_hdr->size);
+ xsendfile_len(TT.fd, dst_fd, TT.hdr.size);
close(dst_fd);
- if (S_ISLNK(file_hdr->mode)) return;
if (!FLAG(o)) {
//set ownership..., --no-same-owner, --numeric-owner
- uid_t u = file_hdr->uid;
- gid_t g = file_hdr->gid;
+ uid_t u = TT.hdr.uid;
+ gid_t g = TT.hdr.gid;
if (!FLAG(numeric_owner)) {
- struct group *gr = getgrnam(file_hdr->gname);
- struct passwd *pw = getpwnam(file_hdr->uname);
+ struct group *gr = getgrnam(TT.hdr.gname);
+ struct passwd *pw = getpwnam(TT.hdr.uname);
if (pw) u = pw->pw_uid;
if (gr) g = gr->gr_gid;
}
- if (!geteuid() && chown(file_hdr->name, u, g))
- perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);;
+ if (!geteuid() && lchown(TT.hdr.name, u, g))
+ perror_msg("chown %d:%d '%s'", u, g, TT.hdr.name);;
}
- if (FLAG(p)) // || !FLAG(no_same_permissions))
- chmod(file_hdr->name, file_hdr->mode);
+ // || !FLAG(no_same_permissions))
+ if (FLAG(p) && !S_ISLNK(TT.hdr.mode)) chmod(TT.hdr.name, TT.hdr.mode);
+// TODO: defer directory mtime until we've finished with contents
//apply mtime
if (!FLAG(m)) {
- struct timeval times[2] = {{file_hdr->mtime, 0},{file_hdr->mtime, 0}};
- utimes(file_hdr->name, times);
+ struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}};
+ utimes(TT.hdr.name, times);
}
}
@@ -460,203 +429,149 @@ static void file_to_list(char *file, struct arg_list **llist)
}
//convert octal to int
-static int otoi(char *str, int len)
+static unsigned long long otoi(char *str, int len)
{
- long val;
- char *endp, inp[len+1]; //1 for NUL termination
-
- memcpy(inp, str, len);
- inp[len] = '\0'; //nul-termination made sure
- val = strtol(inp, &endp, 8);
- if (*endp && *endp != ' ') error_exit("invalid param");
- return (int)val;
-}
+ unsigned long long val;
-static char *process_extended_hdr(struct archive_handler *tar, int size)
-{
- char *value = NULL, *p, *buf = 0;
-
- alloread(&buf, size);
- buf[size] = 0;
- p = buf;
-
- while (size) {
- char *key;
- int len, n;
-
- // extended records are of the format: "LEN NAME=VALUE\n"
- sscanf(p, "%d %n", &len, &n);
- key = p + n;
- p += len;
- size -= len;
- p[-1] = 0;
- if (size < 0) {
- error_msg("corrupted extended header");
- break;
- }
+// todo: base-256 encoding, just do it symmetrically for all fields
+ str[len-1] = 0;
+ val = strtoull(str, &str, 8);
+ if (*str && *str != ' ') error_exit("bad header");
- len = strlen("path=");
- if (!strncmp(key, "path=", len)) {
- value = key + strlen("path=");
- break;
- }
- }
- if (value) value = xstrdup(value);
- free(buf);
- return value;
+ return val;
}
static void unpack_tar(void)
{
- struct archive_handler *tar_hdl = TT.handle;
struct tar_hdr tar;
- struct file_header *file_hdr;
- int i, j, maj, min, sz, e = 0;
- unsigned int cksum;
+ int i;
char *s;
for (;;) {
// align to next block and read it
-// This is the only consumer of TT.offset
- if (TT.offset&511) skippy(512-TT.offset%512);
+ if (TT.hdr.size%512) skippy(512-TT.hdr.size%512);
+
i = readall(TT.fd, &tar, 512);
- if (i != 512) {
- if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file
- error_exit("read error");
- }
+ if (i != 512) error_exit("read error");
+ // ensure null temination even of pathological packets
+ tar.padd[0] = 0;
+ // End of tar
+ if (!*tar.name) return;
- if (!tar.name[0]) {
- if (e) return; //end of tar 2 empty blocks
- e = 1;//empty jump to next block
-// never clear e, so they don't have to be _consecutive_???
- continue;
- }
// can you append a bzip to a gzip _within_ a tarball? Nested compress?
// Or compressed data after uncompressed data?
- if (strncmp(tar.magic, "ustar", 5)) {
- // Try detecting .gz or .bz2 by looking for their magic.
-CHECK_MAGIC:
- if ((!strncmp(tar.name, "\x1f\x8b", 2) || !strncmp(tar.name, "BZh", 3))
- && !lseek(TT.fd, -i, SEEK_CUR)) {
- toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
- extract_stream(tar_hdl);
- continue;
- }
- error_exit("invalid tar format");
- }
-// moved this down here so it doesn't have to be undone
- TT.offset += i;
-
- // Calculate checksum, with cksum field treated as eight spaces
- cksum = 8*' ';
- for (j = 0; j<500; j += (j==147) ? 9 : 1) cksum += ((char*)&tar)[j];
-
- if (cksum != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("bad cksum");
-
- file_hdr = &tar_hdl->file_hdr;
- memset(file_hdr, 0, sizeof(struct file_header));
- file_hdr->mode = otoi(tar.mode, sizeof(tar.mode));
- file_hdr->uid = otoi(tar.uid, sizeof(tar.uid));
- file_hdr->gid = otoi(tar.gid, sizeof(tar.gid));
- file_hdr->size = otoi(tar.size, sizeof(tar.size));
- file_hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime));
- file_hdr->uname = xstrdup(tar.uname);
- file_hdr->gname = xstrdup(tar.gname);
- maj = otoi(tar.major, sizeof(tar.major));
- min = otoi(tar.minor, sizeof(tar.minor));
- file_hdr->device = dev_makedev(maj, min);
-
- if (tar.type <= '7') {
- if (tar.link[0]) {
- sz = sizeof(tar.link);
- file_hdr->link_target = xmalloc(sz + 1);
- memcpy(file_hdr->link_target, tar.link, sz);
- file_hdr->link_target[sz] = '\0';
- }
-
- file_hdr->name = xzalloc(256);// pathname supported size
- if (tar.prefix[0]) {
- memcpy(file_hdr->name, tar.prefix, sizeof(tar.prefix));
- sz = strlen(file_hdr->name);
- if (file_hdr->name[sz-1] != '/') file_hdr->name[sz] = '/';
- }
- sz = strlen(file_hdr->name);
- memcpy(file_hdr->name + sz, tar.name, sizeof(tar.name));
-// TODO: won't the vfs tell us?
- if (file_hdr->name[255]) error_exit("filename too long");
- }
-
- // Headers that don't immediately result in creating a new node
- if (strchr("KLxDMNSVg", tar.type)) {
- if (tar.type == 'K') alloread(&file_hdr->link_target, file_hdr->size);
- else if (tar.type == 'L') alloread(&file_hdr->name, file_hdr->size);
- else if (tar.type == 'x') process_extended_hdr(tar_hdl, file_hdr->size);
- else skippy(file_hdr->size);
+ if (memcmp(tar.magic, "ustar", 5)) error_exit("bad header");
+ if (cksum(&tar) != otoi(tar.chksum, sizeof(tar.chksum)))
+ error_exit("bad cksum");
+ TT.hdr.size = otoi(tar.size, sizeof(tar.size));
+
+ // If this header isn't writing something to the filesystem
+ if (tar.type<'0' || tar.type>'7') {
+ if (tar.type == 'K') alloread(&TT.hdr.link_target, TT.hdr.size);
+ else if (tar.type == 'L') alloread(&TT.hdr.name, TT.hdr.size);
+ else if (tar.type == 'x') {
+ char *p, *buf = 0;
+ int i, len, n;
+
+ // Posix extended record "LEN NAME=VALUE\n" format
+ alloread(&buf, TT.hdr.size);
+ for (p = buf; (p-buf)<TT.hdr.size; p += len) {
+ if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 ||
+ len>TT.hdr.size)
+ {
+ error_msg("corrupted extended header");
+ break;
+ }
+ p[len-1] = 0;
+ if (i == 2) {
+ TT.hdr.name = xstrdup(p+n);
+ break;
+ }
+ }
+ free(buf);
+
+ // This could be if (strchr("DMNSVg", tar.type)) but an unknown header
+ // type with trailing contents is unlikely to have a valid type & cksum
+ } else skippy(TT.hdr.size);
continue;
}
- // Specified file type?
- if (isdigit(tar.type))
- file_hdr->mode |= (char []){8,8,10,2,6,4,1,8,0,0}[tar.type-'0']<<12;
+ // At this point, we're writing something to the filesystem. Parse fields.
+ TT.hdr.mode = otoi(tar.mode, sizeof(tar.mode));
+ TT.hdr.mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12;
+ TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid));
+ TT.hdr.gid = otoi(tar.gid, sizeof(tar.gid));
+ TT.hdr.mtime = otoi(tar.mtime, sizeof(tar.mtime));
+ TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)),
+ otoi(tar.minor, sizeof(tar.minor)));
+
+ TT.hdr.uname = xstrndup(tar.uname, sizeof(tar.uname));
+ TT.hdr.gname = xstrndup(tar.gname, sizeof(tar.gname));
+ if (!TT.hdr.link_target && *tar.link)
+ TT.hdr.link_target = xstrndup(tar.link, sizeof(tar.link));
+ if (!TT.hdr.name) {
+ i = strnlen(tar.prefix, sizeof(tar.prefix));
+ TT.hdr.name = xmprintf("%.*s%s%.*s", i, tar.prefix,
+ (i && tar.prefix[i-1] != '/') ? "/" : "",
+ (int)sizeof(tar.name), tar.name);
+ }
// Directories sometimes recorded as "file with trailing slash"
- if (S_ISREG(file_hdr->mode) && (s = strend(file_hdr->name, "/"))) {
+ if (S_ISREG(TT.hdr.mode) && (s = strend(TT.hdr.name, "/"))) {
*s = 0;
- file_hdr->mode = (file_hdr->mode & ~S_IFMT) | S_IFDIR;
+ TT.hdr.mode = (TT.hdr.mode & ~S_IFMT) | S_IFDIR;
}
-// TODO: what?
- if ((file_hdr->link_target && *(file_hdr->link_target))
- || S_ISLNK(file_hdr->mode) || S_ISDIR(file_hdr->mode))
- file_hdr->size = 0;
+ // Hardlinks, symlinks, and directories do not have contents in archive
+ // (Neither do fifo, block or char devices, but not testing for that...?)
+ if ((TT.hdr.link_target && *TT.hdr.link_target)
+ || S_ISLNK(TT.hdr.mode) || S_ISDIR(TT.hdr.mode))
+ TT.hdr.size = 0;
// Skip excluded files
- if (filter(TT.exc, file_hdr->name) ||
- (TT.inc && !filter(TT.inc, file_hdr->name))) {
- skippy(file_hdr->size);
-// TODO: awkward
- goto FREE;
- }
-// TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead
- add_to_list(&TT.pass, xstrdup(file_hdr->name));
-
- if (FLAG(t)) {
- if (FLAG(v)) {
- char perm[11];
- struct tm *lc = localtime((const time_t*)&(file_hdr->mtime));
+ if (filter(TT.exc, TT.hdr.name) || (TT.inc && !filter(TT.inc, TT.hdr.name)))
+ skippy(TT.hdr.size);
+ else {
- mode_to_string(file_hdr->mode, perm);
- printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ",perm,file_hdr->uname,
- file_hdr->gname, (long)file_hdr->size, 1900+lc->tm_year,
- 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec);
+// TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead
+ add_to_list(&TT.pass, xstrdup(TT.hdr.name));
+
+ if (FLAG(t)) {
+ if (FLAG(v)) {
+ char perm[11];
+ struct tm *lc = localtime(&TT.hdr.mtime);
+
+ mode_to_string(TT.hdr.mode, perm);
+ printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ", perm,
+ TT.hdr.uname, TT.hdr.gname, (long)TT.hdr.size, 1900+lc->tm_year,
+ 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec);
+ }
+ printf("%s", TT.hdr.name);
+ if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target);
+ xputc('\n');
+ skippy(TT.hdr.size);
+ } else {
+ if (FLAG(v)) printf("%s\n", TT.hdr.name);
+ if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size);
+ else if (FLAG(to_command)) extract_to_command();
+ else extract_to_disk();
}
- printf("%s",file_hdr->name);
- if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target);
- xputc('\n');
- skippy(file_hdr->size);
- } else {
- if (FLAG(v)) printf("%s\n",file_hdr->name);
- tar_hdl->extract_handler(tar_hdl);
}
-FREE:
- free(file_hdr->name);
- free(file_hdr->link_target);
- free(file_hdr->uname);
- free(file_hdr->gname);
+
+ free(TT.hdr.name);
+ free(TT.hdr.link_target);
+ free(TT.hdr.uname);
+ free(TT.hdr.gname);
+ TT.hdr.name = TT.hdr.link_target = 0;
}
}
void tar_main(void)
{
- struct archive_handler *tar_hdl;
struct arg_list *tmp;
char *s, **args = toys.optargs;
-// TODO: zap
- TT.handle = tar_hdl = xzalloc(sizeof(struct archive_handler));
- tar_hdl->extract_handler = extract_to_disk;
-
// When extracting to command
signal(SIGPIPE, SIG_IGN);
@@ -682,11 +597,23 @@ void tar_main(void)
// Are we reading?
if (FLAG(x)||FLAG(t)) {
- if (FLAG(O)) tar_hdl->extract_handler = extract_to_stdout;
- if (FLAG(to_command)) tar_hdl->extract_handler = extract_to_command;
-
// TODO: autodtect
- if (FLAG(j)||FLAG(z)) extract_stream(tar_hdl);
+
+// Try detecting .gz or .bz2 by looking for their magic.
+// if ((!memcmp(tar.name, "\x1f\x8b", 2) || !memcmp(tar.name, "BZh", 3))
+// && !lseek(TT.fd, -i, SEEK_CUR)) {
+// toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
+// extract_stream(tar_hdl);
+// continue;
+// }
+ if (FLAG(j)||FLAG(z)) {
+ int pipefd[2] = {TT.fd, -1};
+
+ xpopen_both((char *[]){FLAG(z)?"gunzip":"bunzip2", "-cf", "-", NULL},
+ pipefd);
+ close(TT.fd);
+ TT.fd = pipefd[1];
+ }
unpack_tar();
for (tmp = TT.inc; tmp; tmp = tmp->next)
@@ -703,7 +630,6 @@ void tar_main(void)
close(TT.fd);
TT.fd = pipefd[0];
}
-// TODO cowardly empty archive
for (tmp = TT.inc; tmp; tmp = tmp->next)
dirtree_flagread(tmp->arg, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar);
diff --git a/toys/posix/cmp.c b/toys/posix/cmp.c
index 6cd410fc..c573f02e 100644
--- a/toys/posix/cmp.c
+++ b/toys/posix/cmp.c
@@ -4,7 +4,7 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/cmp.html
-USE_CMP(NEWTOY(cmp, "<2>2ls(silent)(quiet)[!ls]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_CMP(NEWTOY(cmp, "<2>2ls(silent)(quiet)[!ls]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
config CMP
bool "cmp"
@@ -81,4 +81,3 @@ void cmp_main(void)
loopfiles_rw(toys.optargs, O_CLOEXEC|(WARN_ONLY*!(toys.optflags&FLAG_s)), 0,
do_cmp);
}
-
diff --git a/toys/posix/env.c b/toys/posix/env.c
index 2de8f690..5c7bb789 100644
--- a/toys/posix/env.c
+++ b/toys/posix/env.c
@@ -6,13 +6,13 @@
*
* Deviations from posix: "-" argument and -0
-USE_ENV(NEWTOY(env, "^0iu*", TOYFLAG_USR|TOYFLAG_BIN))
+USE_ENV(NEWTOY(env, "^0iu*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
config ENV
bool "env"
default y
help
- usage: env [-i] [-u NAME] [NAME=VALUE...] [command [option...]]
+ usage: env [-i] [-u NAME] [NAME=VALUE...] [COMMAND [ARG...]]
Set the environment for command invocation, or list environment variables.
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index c9a30882..9239614f 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -10,9 +10,9 @@
* echo hello | grep -f </dev/null
*
-USE_GREP(NEWTOY(grep, "(color):;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))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
+USE_GREP(NEWTOY(grep, "(color):;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_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
config GREP
bool "grep"
@@ -22,7 +22,7 @@ config GREP
Show lines matching regular expressions. If no -e, first argument is
regular expression to match. With no files (or "-" filename) read stdin.
- Returns 0 if matched, 1 if no match found.
+ Returns 0 if matched, 1 if no match found, 2 for command errors.
-e Regex to match. (May be repeated.)
-f File listing regular expressions to match.
diff --git a/toys/posix/nice.c b/toys/posix/nice.c
index ca5e2224..c26f66d3 100644
--- a/toys/posix/nice.c
+++ b/toys/posix/nice.c
@@ -10,7 +10,7 @@ config NICE
bool "nice"
default y
help
- usage: nice [-n PRIORITY] command [args...]
+ usage: nice [-n PRIORITY] COMMAND [ARG...]
Run a command line at an increased or decreased scheduling priority.
@@ -32,7 +32,9 @@ void nice_main(void)
if (!toys.optflags) TT.n = 10;
errno = 0;
- if (nice(TT.n)==-1 && errno) perror_exit("Can't set priority");
-
+ if (nice(TT.n)==-1 && errno) {
+ toys.exitval = 125;
+ perror_exit("Can't set priority");
+ }
xexec(toys.optargs);
}
diff --git a/toys/posix/nohup.c b/toys/posix/nohup.c
index b302cbe4..e5b526f7 100644
--- a/toys/posix/nohup.c
+++ b/toys/posix/nohup.c
@@ -4,13 +4,13 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/nohup.html
-USE_NOHUP(NEWTOY(nohup, "<1^", TOYFLAG_USR|TOYFLAG_BIN))
+USE_NOHUP(NEWTOY(nohup, "<1^", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
config NOHUP
bool "nohup"
default y
help
- usage: nohup COMMAND [ARGS...]
+ usage: nohup COMMAND [ARG...]
Run a command that survives the end of its terminal.
@@ -21,6 +21,7 @@ config NOHUP
void nohup_main(void)
{
+ toys.exitval = 125;
xsignal(SIGHUP, SIG_IGN);
if (isatty(1)) {
close(1);
@@ -38,5 +39,6 @@ void nohup_main(void)
close(0);
xopen_stdio("/dev/null", O_RDONLY);
}
+ toys.exitval = 0;
xexec(toys.optargs);
}
diff --git a/toys/posix/sort.c b/toys/posix/sort.c
index 9433aef2..9d2f2276 100644
--- a/toys/posix/sort.c
+++ b/toys/posix/sort.c
@@ -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))
+USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
config SORT
bool "sort"
@@ -34,12 +34,13 @@ config SORT
-o Output to FILE instead of stdout
-V Version numbers (name-1.234-rc6.5b.tgz)
- Sorting by key looks at a subset of the words on each line. -k2
- uses the second word to the end of the line, -k2,2 looks at only
- the second word, -k2,4 looks from the start of the second to the end
- of the fourth word. Specifying multiple keys uses the later keys as
- tie breakers, in order. A type specifier appended to a sort key
- (such as -2,2n) applies only to sorting that key.
+ Sorting by key looks at a subset of the words on each line. -k2 uses the
+ second word to the end of the line, -k2,2 looks at only the second word,
+ -k2,4 looks from the start of the second to the end of the fourth word.
+ -k2.4,5 starts from the fourth character of the second word, to the end
+ of the fifth word. Specifying multiple keys uses the later keys as tie
+ breakers, in order. A type specifier appended to a sort key (such as -2,2n)
+ applies only to sorting that key.
config SORT_FLOAT
bool
@@ -121,7 +122,8 @@ static char *get_key_data(char *str, struct sort_key *key, int flags)
if (TT.t && str[start]==*TT.t) start++;
// Strip leading and trailing whitespace if necessary
- if (flags&FLAG_b) while (isspace(str[start])) start++;
+ if ((flags&FLAG_b) || (!TT.t && !key->range[3]))
+ while (isspace(str[start])) start++;
if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--;
// Handle offsets on start and end
@@ -343,6 +345,7 @@ void sort_main(void)
if (!temp2 || flag>FLAG_x
|| (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z)))
{
+ toys.exitval = 2;
error_exit("Unknown key option.");
}
// b after , means strip _trailing_ space, not leading.