diff options
author | Elliott Hughes <enh@google.com> | 2019-03-19 13:11:08 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2019-03-19 13:21:06 -0700 |
commit | 0798b6af30ab9ae9b7e3b0943d56d049e273f09f (patch) | |
tree | 451236488c3f081025712fbaa5aad181b393ca35 | |
parent | 94ba7b1e349b357a2e310272a42dc41161e5523f (diff) | |
parent | 95574e3f2a47291651f7c78c873318344565e80e (diff) | |
download | toybox-0798b6af30ab9ae9b7e3b0943d56d049e273f09f.tar.gz |
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Iafbc08d0da8d780bc424ce9b12fd03dfd80a573a
-rw-r--r-- | generated/globals.h | 14 | ||||
-rw-r--r-- | generated/help.h | 14 | ||||
-rw-r--r-- | generated/newtoys.h | 18 | ||||
-rw-r--r-- | lib/xwrap.c | 3 | ||||
-rwxr-xr-x | tests/cmp.test | 6 | ||||
-rw-r--r-- | tests/date.test | 2 | ||||
-rw-r--r-- | tests/diff.test | 7 | ||||
-rwxr-xr-x | tests/env.test | 2 | ||||
-rwxr-xr-x | tests/grep.test | 11 | ||||
-rwxr-xr-x | tests/sort.test | 1 | ||||
-rw-r--r-- | tests/timeout.test | 1 | ||||
-rw-r--r-- | toys/android/load_policy.c | 4 | ||||
-rw-r--r-- | toys/other/chroot.c | 9 | ||||
-rw-r--r-- | toys/pending/bc.c | 15 | ||||
-rw-r--r-- | toys/pending/diff.c | 18 | ||||
-rw-r--r-- | toys/pending/tar.c | 462 | ||||
-rw-r--r-- | toys/posix/cmp.c | 3 | ||||
-rw-r--r-- | toys/posix/env.c | 4 | ||||
-rw-r--r-- | toys/posix/grep.c | 8 | ||||
-rw-r--r-- | toys/posix/nice.c | 8 | ||||
-rw-r--r-- | toys/posix/nohup.c | 6 | ||||
-rw-r--r-- | toys/posix/sort.c | 19 |
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. |