aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2021-01-08 15:37:56 -0800
committerElliott Hughes <enh@google.com>2021-01-08 17:36:22 -0800
commit9582c59cdea38e9121f81ed502bc45ddcca49876 (patch)
tree41cd594d66ddd43f29177374f1f7c823297a82ee
parent5f4651f63033df044807057792caef48b14efc50 (diff)
parent661130b38cbf74056430e4b280512369dfcd9fe9 (diff)
downloadtoybox-9582c59cdea38e9121f81ed502bc45ddcca49876.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD
Change-Id: Id8974b50d3d8640a544b81f7cc99e569371fd214
-rw-r--r--.config-device3
-rw-r--r--.config-linux3
-rw-r--r--.config-mac3
-rw-r--r--.github/workflows/toybox.yml14
-rw-r--r--Config.in6
-rw-r--r--Makefile1
-rw-r--r--android/device/generated/config.h6
-rw-r--r--android/device/generated/flags.h86
-rw-r--r--android/device/generated/globals.h38
-rw-r--r--android/device/generated/help.h14
-rw-r--r--android/device/generated/newtoys.h12
-rw-r--r--android/linux/generated/config.h6
-rw-r--r--android/linux/generated/flags.h86
-rw-r--r--android/linux/generated/globals.h38
-rw-r--r--android/linux/generated/help.h14
-rw-r--r--android/linux/generated/newtoys.h12
-rw-r--r--android/mac/generated/config.h6
-rw-r--r--android/mac/generated/flags.h86
-rw-r--r--android/mac/generated/globals.h38
-rw-r--r--android/mac/generated/help.h14
-rw-r--r--android/mac/generated/newtoys.h12
-rw-r--r--kconfig/freebsd_miniconfig1
-rw-r--r--kconfig/macos_miniconfig1
-rw-r--r--lib/deflate.c2
-rw-r--r--lib/lib.c155
-rw-r--r--lib/lib.h2
-rw-r--r--lib/portability.c7
-rw-r--r--lib/portability.h6
-rw-r--r--lib/toyflags.h3
-rw-r--r--lib/tty.c14
-rw-r--r--main.c15
-rw-r--r--scripts/runtest.sh8
-rwxr-xr-xtests/base32.test24
-rwxr-xr-xtests/chmod.test47
-rwxr-xr-xtests/find.test1
-rw-r--r--tests/gunzip.test7
-rwxr-xr-xtests/id.test8
-rwxr-xr-xtests/sed.test2
-rwxr-xr-xtests/seq.test5
-rw-r--r--tests/sh.test5
-rw-r--r--tests/test.test27
-rwxr-xr-xtests/tr.test11
-rwxr-xr-xtests/unicode.test13
-rw-r--r--toys.h1
-rw-r--r--toys/example/demo_utf8towc.c1
-rw-r--r--toys/lsb/seq.c89
-rw-r--r--toys/net/microcom.c19
-rw-r--r--toys/other/base64.c46
-rw-r--r--toys/other/count.c20
-rw-r--r--toys/other/lsattr.c3
-rw-r--r--toys/other/pwgen.c76
-rw-r--r--toys/pending/sh.c295
-rw-r--r--toys/pending/tr.c27
-rw-r--r--toys/pending/unicode.c65
-rw-r--r--toys/posix/chmod.c4
-rw-r--r--toys/posix/expand.c24
-rw-r--r--toys/posix/find.c2
-rw-r--r--toys/posix/grep.c6
-rw-r--r--toys/posix/sed.c16
-rw-r--r--toys/posix/tee.c27
-rw-r--r--toys/posix/test.c10
-rwxr-xr-xwww/faq.html19
-rw-r--r--www/roadmap.html2
-rw-r--r--www/toycans.pngbin48192 -> 1273196 bytes
64 files changed, 1237 insertions, 377 deletions
diff --git a/.config-device b/.config-device
index f52c7ac8..57fe20cb 100644
--- a/.config-device
+++ b/.config-device
@@ -44,6 +44,7 @@ CONFIG_ACPI=y
# CONFIG_ARPING is not set
# CONFIG_ARP is not set
# CONFIG_ASCII is not set
+# CONFIG_BASE32 is not set
CONFIG_BASE64=y
CONFIG_BASENAME=y
# CONFIG_BC is not set
@@ -247,6 +248,7 @@ CONFIG_PRINTF=y
CONFIG_PS=y
CONFIG_PWDX=y
CONFIG_PWD=y
+# CONFIG_PWGEN is not set
# CONFIG_READAHEAD is not set
CONFIG_READELF=y
CONFIG_READLINK=y
@@ -324,6 +326,7 @@ CONFIG_TUNCTL=y
CONFIG_ULIMIT=y
CONFIG_UMOUNT=y
CONFIG_UNAME=y
+# CONFIG_UNICODE is not set
CONFIG_UNIQ=y
CONFIG_UNIX2DOS=y
CONFIG_UNLINK=y
diff --git a/.config-linux b/.config-linux
index 82b80a86..f8ec4115 100644
--- a/.config-linux
+++ b/.config-linux
@@ -45,6 +45,7 @@ CONFIG_TOYBOX=y
# CONFIG_ARPING is not set
# CONFIG_ARP is not set
# CONFIG_ASCII is not set
+# CONFIG_BASE32 is not set
# CONFIG_BASE64 is not set
CONFIG_BASENAME=y
# CONFIG_BC is not set
@@ -241,6 +242,7 @@ CONFIG_PRINTF=y
CONFIG_PS=y
# CONFIG_PWDX is not set
CONFIG_PWD=y
+# CONFIG_PWGEN is not set
# CONFIG_READAHEAD is not set
# CONFIG_READELF is not set
CONFIG_READLINK=y
@@ -313,6 +315,7 @@ CONFIG_TR=y
# CONFIG_ULIMIT is not set
# CONFIG_UMOUNT is not set
CONFIG_UNAME=y
+# CONFIG_UNICODE is not set
CONFIG_UNIQ=y
CONFIG_UNIX2DOS=y
# CONFIG_UNLINK is not set
diff --git a/.config-mac b/.config-mac
index ee4d3efb..a4193533 100644
--- a/.config-mac
+++ b/.config-mac
@@ -45,6 +45,7 @@ CONFIG_TOYBOX=y
# CONFIG_ARPING is not set
# CONFIG_ARP is not set
# CONFIG_ASCII is not set
+# CONFIG_BASE32 is not set
# CONFIG_BASE64 is not set
CONFIG_BASENAME=y
# CONFIG_BC is not set
@@ -241,6 +242,7 @@ CONFIG_PRINTF=y
# CONFIG_PS is not set
# CONFIG_PWDX is not set
CONFIG_PWD=y
+# CONFIG_PWGEN is not set
# CONFIG_READAHEAD is not set
# CONFIG_READELF is not set
CONFIG_READLINK=y
@@ -313,6 +315,7 @@ CONFIG_TR=y
# CONFIG_ULIMIT is not set
# CONFIG_UMOUNT is not set
CONFIG_UNAME=y
+# CONFIG_UNICODE is not set
CONFIG_UNIQ=y
CONFIG_UNIX2DOS=y
# CONFIG_UNLINK is not set
diff --git a/.github/workflows/toybox.yml b/.github/workflows/toybox.yml
index b3b16676..1b463b2a 100644
--- a/.github/workflows/toybox.yml
+++ b/.github/workflows/toybox.yml
@@ -7,6 +7,20 @@ on:
branches: [ master ]
jobs:
+ MacOS-11_0:
+ runs-on: macos-11.0
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup
+ run: brew install gnu-sed
+ - name: Configure
+ run: make macos_defconfig
+ - name: Build
+ run: make
+ - name: Test
+ run: VERBOSE=1 make tests
+
MacOS-10_15:
runs-on: macos-10.15
diff --git a/Config.in b/Config.in
index f7407d85..27948ad3 100644
--- a/Config.in
+++ b/Config.in
@@ -106,12 +106,6 @@ config TOYBOX_HELP_DASHDASH
optstring. (Use TOYFLAG_NOHELP to disable.) Produces the same output
as "help command". --version shows toybox version.
-config TOYBOX_I18N
- bool "Internationalization support"
- default y
- help
- Support for UTF-8 character sets, and some locale support.
-
config TOYBOX_FREE
bool "Free memory unnecessarily"
default n
diff --git a/Makefile b/Makefile
index 21d3359f..52602d25 100644
--- a/Makefile
+++ b/Makefile
@@ -63,6 +63,7 @@ root_clean:
@echo root cleaned
clean::
+ @chmod -fR 700 generated || true
@rm -rf toybox generated change .singleconfig*
@echo cleaned
diff --git a/android/device/generated/config.h b/android/device/generated/config.h
index e4be83f1..8aacc15a 100644
--- a/android/device/generated/config.h
+++ b/android/device/generated/config.h
@@ -62,6 +62,8 @@
#define USE_ARP(...)
#define CFG_ASCII 0
#define USE_ASCII(...)
+#define CFG_BASE32 0
+#define USE_BASE32(...)
#define CFG_BASE64 1
#define USE_BASE64(...) __VA_ARGS__
#define CFG_BASENAME 1
@@ -468,6 +470,8 @@
#define USE_PWDX(...) __VA_ARGS__
#define CFG_PWD 1
#define USE_PWD(...) __VA_ARGS__
+#define CFG_PWGEN 0
+#define USE_PWGEN(...)
#define CFG_READAHEAD 0
#define USE_READAHEAD(...)
#define CFG_READELF 1
@@ -622,6 +626,8 @@
#define USE_UMOUNT(...) __VA_ARGS__
#define CFG_UNAME 1
#define USE_UNAME(...) __VA_ARGS__
+#define CFG_UNICODE 0
+#define USE_UNICODE(...)
#define CFG_UNIQ 1
#define USE_UNIQ(...) __VA_ARGS__
#define CFG_UNIX2DOS 1
diff --git a/android/device/generated/flags.h b/android/device/generated/flags.h
index 0ac63e7f..ed8aa2ed 100644
--- a/android/device/generated/flags.h
+++ b/android/device/generated/flags.h
@@ -73,6 +73,17 @@
#undef FOR_ascii
#endif
+// base32 diw#<0=76[!dw]
+#undef OPTSTR_base32
+#define OPTSTR_base32 "diw#<0=76[!dw]"
+#ifdef CLEANUP_base32
+#undef CLEANUP_base32
+#undef FOR_base32
+#undef FLAG_w
+#undef FLAG_i
+#undef FLAG_d
+#endif
+
// base64 diw#<0=76[!dw] diw#<0=76[!dw]
#undef OPTSTR_base64
#define OPTSTR_base64 "diw#<0=76[!dw]"
@@ -2344,6 +2355,26 @@
#undef FLAG_a
#endif
+// pwgen >2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]
+#undef OPTSTR_pwgen
+#define OPTSTR_pwgen ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]"
+#ifdef CLEANUP_pwgen
+#undef CLEANUP_pwgen
+#undef FOR_pwgen
+#undef FLAG_0
+#undef FLAG_A
+#undef FLAG_v
+#undef FLAG_1
+#undef FLAG_C
+#undef FLAG_h
+#undef FLAG_B
+#undef FLAG_s
+#undef FLAG_y
+#undef FLAG_n
+#undef FLAG_c
+#undef FLAG_r
+#endif
+
// readahead
#undef OPTSTR_readahead
#define OPTSTR_readahead 0
@@ -2560,6 +2591,14 @@
#undef FLAG_f
#endif
+// set
+#undef OPTSTR_set
+#define OPTSTR_set 0
+#ifdef CLEANUP_set
+#undef CLEANUP_set
+#undef FOR_set
+#endif
+
// setenforce <1>1 <1>1
#undef OPTSTR_setenforce
#define OPTSTR_setenforce "<1>1"
@@ -3246,6 +3285,14 @@
#undef FLAG_o
#endif
+// unicode <1
+#undef OPTSTR_unicode
+#define OPTSTR_unicode "<1"
+#ifdef CLEANUP_unicode
+#undef CLEANUP_unicode
+#undef FOR_unicode
+#endif
+
// uniq f#s#w#zicdu f#s#w#zicdu
#undef OPTSTR_uniq
#define OPTSTR_uniq "f#s#w#zicdu"
@@ -3598,6 +3645,15 @@
#endif
#endif
+#ifdef FOR_base32
+#ifndef TT
+#define TT this.base32
+#endif
+#define FLAG_w (FORCED_FLAG<<0)
+#define FLAG_i (FORCED_FLAG<<1)
+#define FLAG_d (FORCED_FLAG<<2)
+#endif
+
#ifdef FOR_base64
#ifndef TT
#define TT this.base64
@@ -5513,6 +5569,24 @@
#define FLAG_a (1<<0)
#endif
+#ifdef FOR_pwgen
+#ifndef TT
+#define TT this.pwgen
+#endif
+#define FLAG_0 (FORCED_FLAG<<0)
+#define FLAG_A (FORCED_FLAG<<1)
+#define FLAG_v (FORCED_FLAG<<2)
+#define FLAG_1 (FORCED_FLAG<<3)
+#define FLAG_C (FORCED_FLAG<<4)
+#define FLAG_h (FORCED_FLAG<<5)
+#define FLAG_B (FORCED_FLAG<<6)
+#define FLAG_s (FORCED_FLAG<<7)
+#define FLAG_y (FORCED_FLAG<<8)
+#define FLAG_n (FORCED_FLAG<<9)
+#define FLAG_c (FORCED_FLAG<<10)
+#define FLAG_r (FORCED_FLAG<<11)
+#endif
+
#ifdef FOR_readahead
#ifndef TT
#define TT this.readahead
@@ -5691,6 +5765,12 @@
#define FLAG_f (1<<2)
#endif
+#ifdef FOR_set
+#ifndef TT
+#define TT this.set
+#endif
+#endif
+
#ifdef FOR_setenforce
#ifndef TT
#define TT this.setenforce
@@ -6275,6 +6355,12 @@
#define FLAG_o (1<<6)
#endif
+#ifdef FOR_unicode
+#ifndef TT
+#define TT this.unicode
+#endif
+#endif
+
#ifdef FOR_uniq
#ifndef TT
#define TT this.uniq
diff --git a/android/device/generated/globals.h b/android/device/generated/globals.h
index 9005cea9..876e468b 100644
--- a/android/device/generated/globals.h
+++ b/android/device/generated/globals.h
@@ -124,7 +124,7 @@ struct pidof_data {
struct seq_data {
char *s, *f;
- int precision;
+ int precision, buflen;
};
// toys/lsb/su.c
@@ -161,8 +161,8 @@ struct ifconfig_data {
struct microcom_data {
long s;
- int fd;
- struct termios original_stdin_state, original_fd_state;
+ int fd, stok;
+ struct termios old_stdin, old_fd;
};
// toys/net/netcat.c
@@ -214,8 +214,9 @@ struct acpi_data {
struct base64_data {
long w;
-
unsigned total;
+ unsigned n; // number of bits used in encoding. 5 for base32, 6 for base64
+ unsigned align; // number of bits to align to
};
// toys/other/blkdiscard.c
@@ -379,6 +380,12 @@ struct oneit_data {
char *c;
};
+// toys/other/pwgen.c
+
+struct pwgen_data {
+ char *r;
+};
+
// toys/other/rtcwake.c
struct rtcwake_data {
@@ -841,11 +848,10 @@ struct sh_data {
} exec;
};
- // keep lineno here: used to work around compiler limitation in run_command()
- long lineno;
+ // keep ifs here: used to work around compiler limitation in run_command()
char *ifs, *isexec, *wcpat;
- unsigned options, jobcnt;
- int hfd, pid, bangpid, varslen, shift, cdcount;
+ unsigned options, jobcnt, LINENO;
+ int hfd, pid, bangpid, varslen, cdcount;
long long SECONDS;
// global and local variables
@@ -857,8 +863,9 @@ struct sh_data {
// Parsed functions
struct sh_function {
char *name;
- struct sh_pipeline { // pipeline segments
+ struct sh_pipeline { // pipeline segments: linked list of arg w/metadata
struct sh_pipeline *next, *prev, *end;
+ unsigned lineno;
int count, here, type; // TODO abuse type to replace count during parsing
struct sh_arg {
char **v;
@@ -878,8 +885,17 @@ struct sh_data {
struct sh_arg *raw, arg;
} *pp; // currently running process
+ struct sh_callstack {
+ struct sh_callstack *next;
+ struct sh_function scratch;
+ struct sh_arg arg;
+ struct arg_list *delete;
+ unsigned lineno;
+ long shift;
+ } *cc;
+
// job list, command line for $*, scratch space for do_wildcard_files()
- struct sh_arg jobs, *arg, *wcdeck;
+ struct sh_arg jobs, *wcdeck;
};
// toys/pending/stty.c
@@ -1492,6 +1508,7 @@ struct tar_data {
struct tee_data {
void *outputs;
+ int out;
};
// toys/posix/touch.c
@@ -1587,6 +1604,7 @@ extern union global_union {
struct modinfo_data modinfo;
struct nsenter_data nsenter;
struct oneit_data oneit;
+ struct pwgen_data pwgen;
struct rtcwake_data rtcwake;
struct setfattr_data setfattr;
struct sha3sum_data sha3sum;
diff --git a/android/device/generated/help.h b/android/device/generated/help.h
index d18e68ac..754e9a1b 100644
--- a/android/device/generated/help.h
+++ b/android/device/generated/help.h
@@ -12,8 +12,6 @@
#define HELP_toybox_free "When a program exits, the operating system will clean up after it\n(free memory, close files, etc). To save size, toybox usually relies\non this behavior. If you're running toybox under a debugger or\nwithout a real OS (ala newlib+libgloss), enable this to make toybox\nclean up after itself."
-#define HELP_toybox_i18n "Support for UTF-8 character sets, and some locale support."
-
#define HELP_toybox_help_dashdash "Support --help argument in all commands, even ones with a NULL\noptstring. (Use TOYFLAG_NOHELP to disable.) Produces the same output\nas \"help command\". --version shows toybox version."
#define HELP_toybox_help "Include help text for each command."
@@ -200,6 +198,8 @@
#define HELP_readahead "usage: readahead FILE...\n\nPreload files into disk cache."
+#define HELP_pwgen "usage: pwgen [-cAn0yrsBhC1v] [LENGTH] [COUNT]\n\nGenerate human-readable random passwords. When output is to tty produces\na screenfull to defeat shoulder surfing (pick one and clear the screen).\n\n-c --capitalize Permit capital letters.\n-A --no-capitalize Don't include capital letters.\n-n --numerals Permit numbers.\n-0 --no-numerals Don't include numbers.\n-y --symbols Permit special characters ($#%...).\n-r <chars> --remove=<chars> Don't include the given characters.\n-s --secure Generate more random passwords.\n-B --ambiguous Avoid ambiguous characters (e.g. 0, O).\n-h --help Print this help message.\n-C Print the output in columns.\n-1 Print the output one line each.\n-v Don't include vowels."
+
#define HELP_pwdx "usage: pwdx PID...\n\nPrint working directory of processes listed on command line."
#define HELP_printenv "usage: printenv [-0] [env_var...]\n\nPrint environment variables.\n\n-0 Use \\0 as delimiter instead of \\n"
@@ -318,6 +318,8 @@
#define HELP_blkdiscard "usage: blkdiscard [-olszf] DEVICE\n\nDiscard device sectors.\n\n-o, --offset OFF Byte offset to start discarding at (default 0)\n-l, --length LEN Bytes to discard (default all)\n-s, --secure Perform secure discard\n-z, --zeroout Zero-fill rather than discard\n-f, --force Disable check for mounted filesystem\n\nOFF and LEN must be aligned to the device sector size.\nBy default entire device is discarded.\nWARNING: All discarded data is permanently lost!"
+#define HELP_base32 "usage: base32 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base32.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
+
#define HELP_base64 "usage: base64 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base64.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
#define HELP_ascii "usage: ascii\n\nDisplay ascii character set."
@@ -334,6 +336,8 @@
#define HELP_useradd "usage: useradd [-SDH] [-h DIR] [-s SHELL] [-G GRP] [-g NAME] [-u UID] USER [GROUP]\n\nCreate new user, or add USER to GROUP\n\n-D Don't assign a password\n-g NAME Real name\n-G GRP Add user to existing group\n-h DIR Home directory\n-H Don't create home directory\n-s SHELL Login shell\n-S Create a system user\n-u UID User id"
+#define HELP_unicode "usage: unicode [[min]-max]\n\nConvert between Unicode code points and UTF-8, in both directions."
+
#define HELP_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\ntraceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC]\n [-i IFACE] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6 Force IP or IPv6 name resolution\n-F Set the don't fragment bit (supports IPV4 only)\n-U Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)\n-I Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)\n-l Display the TTL value of the returned packet (supports IPV4 only)\n-d Set SO_DEBUG options to socket\n-n Print numeric addresses\n-v verbose\n-r Bypass routing tables, send directly to HOST\n-m Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s IP address to use as the source address\n-t Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g Loose source route gateway (8 max) (supports IPV4 only)\n-z Pause Time in ms (default 0)(RANGE 0 to 86400) (supports IPV4 only)\n-f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)\n-i Specify a network interface to operate with"
#define HELP_tr "usage: tr [-cds] SET1 [SET2]\n\nTranslate, squeeze, or delete characters from stdin, writing to stdout\n\n-c/-C Take complement of SET1\n-d Delete input characters coded SET1\n-s Squeeze multiple output characters of SET2 into one character"
@@ -368,6 +372,8 @@
#define HELP_unset "usage: unset [-fvn] NAME...\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that"
+#define HELP_set "usage: set [+a] [+o OPTION] [VAR...]\n\nSet variables and shell attributes. Use + to disable and - to enable.\nNAME=VALUE arguments assign to the variable, any leftovers set $1, $2...\nWith no arguments, prints current variables.\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that\n\nOPTIONs:\n history - enable command history"
+
#define HELP_exit "usage: exit [status]\n\nExit shell. If no return value supplied on command line, use value\nof most recent command, or 0 if none."
#define HELP_cd "usage: cd [-PL] [path]\n\nChange current directory. With no arguments, go $HOME.\n\n-P Physical path: resolve symlinks in path\n-L Local path: .. trims directories off $PWD (default)"
@@ -490,7 +496,7 @@
#define HELP_time "usage: time [-pv] COMMAND...\n\nRun command line and report real, user, and system time elapsed in seconds.\n(real = clock on the wall, user = cpu used by command's code,\nsystem = cpu used by OS on behalf of command.)\n\n-p POSIX format output (default)\n-v Verbose"
-#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
+#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size -k sticky bit\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
#define HELP_tee "usage: tee [-ai] [FILE...]\n\nCopy stdin to each listed file, and also to stdout.\nFilename \"-\" is a synonym for stdout.\n\n-a Append to files\n-i Ignore SIGINT"
@@ -612,7 +618,7 @@
#define HELP_cksum "usage: cksum [-IPLN] [FILE...]\n\nFor each file, output crc32 checksum value, length and name of file.\nIf no files listed, copy from stdin. Filename \"-\" is a synonym for stdin.\n\n-H Hexadecimal checksum (defaults to decimal)\n-L Little endian (defaults to big endian)\n-P Pre-inversion\n-I Skip post-inversion\n-N Do not include length in CRC calculation (or output)"
-#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
+#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, +t = sticky. (o+s ignored so a+s doesn't set +t)\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
#define HELP_chown "see: chgrp"
diff --git a/android/device/generated/newtoys.h b/android/device/generated/newtoys.h
index 1b8324f5..3e86e653 100644
--- a/android/device/generated/newtoys.h
+++ b/android/device/generated/newtoys.h
@@ -4,7 +4,7 @@ USE_SH(OLDTOY(-sh, sh, 0))
USE_SH(OLDTOY(-toysh, sh, 0))
USE_SH(OLDTOY(., source, TOYFLAG_NOFORK))
USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
-USE_TEST(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
+USE_SH(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
USE_ACPI(NEWTOY(acpi, "abctV", TOYFLAG_USR|TOYFLAG_BIN))
USE_GROUPADD(OLDTOY(addgroup, groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
@@ -12,6 +12,7 @@ USE_ARCH(NEWTOY(arch, 0, TOYFLAG_USR|TOYFLAG_BIN))
USE_ARP(NEWTOY(arp, "vi:nDsdap:A:H:[+Ap][!sd]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN))
USE_ASCII(NEWTOY(ascii, 0, TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE32(NEWTOY(base32, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASENAME(NEWTOY(basename, "^<1as:", TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(OLDTOY(bash, sh, TOYFLAG_BIN))
@@ -68,7 +69,7 @@ USE_DOS2UNIX(NEWTOY(dos2unix, 0, TOYFLAG_BIN))
USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsxb[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
USE_DUMPLEASES(NEWTOY(dumpleases, ">0arf:[!ar]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_EJECT(NEWTOY(eject, ">1stT[!tT]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ENV(NEWTOY(env, "^i0u*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK))
@@ -81,7 +82,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|TOYFLAG_MAYFORK))
USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_FILE(NEWTOY(file, "<1bhLs[!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))
@@ -100,7 +101,7 @@ USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETFATTR(NEWTOY(getfattr, "(only-values)dhn:", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh", TOYFLAG_SBIN))
-USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
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))
@@ -208,6 +209,7 @@ USE_ULIMIT(OLDTOY(prlimit, ulimit, TOYFLAG_USR|TOYFLAG_BIN))
USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_PWD(NEWTOY(pwd, ">0LP[-LP]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_PWDX(NEWTOY(pwdx, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
USE_READELF(NEWTOY(readelf, "<1(dyn-syms)adehlnp:SsWx:", TOYFLAG_USR|TOYFLAG_BIN))
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -227,6 +229,7 @@ USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SED(NEWTOY(sed, "(help)(version)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(set, 0, TOYFLAG_NOFORK))
USE_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_SETSID(NEWTOY(setsid, "^<1wcd[!dc]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -284,6 +287,7 @@ USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
USE_ULIMIT(NEWTOY(ulimit, ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]", TOYFLAG_USR|TOYFLAG_BIN))
USE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
USE_UNAME(NEWTOY(uname, "oamvrns[+os]", TOYFLAG_BIN))
+USE_UNICODE(NEWTOY(unicode, "<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIX2DOS(NEWTOY(unix2dos, 0, TOYFLAG_BIN))
USE_UNLINK(NEWTOY(unlink, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/android/linux/generated/config.h b/android/linux/generated/config.h
index ec88bca4..342dda39 100644
--- a/android/linux/generated/config.h
+++ b/android/linux/generated/config.h
@@ -64,6 +64,8 @@
#define USE_ARP(...)
#define CFG_ASCII 0
#define USE_ASCII(...)
+#define CFG_BASE32 0
+#define USE_BASE32(...)
#define CFG_BASE64 0
#define USE_BASE64(...)
#define CFG_BASENAME 1
@@ -456,6 +458,8 @@
#define USE_PWDX(...)
#define CFG_PWD 1
#define USE_PWD(...) __VA_ARGS__
+#define CFG_PWGEN 0
+#define USE_PWGEN(...)
#define CFG_READAHEAD 0
#define USE_READAHEAD(...)
#define CFG_READELF 0
@@ -600,6 +604,8 @@
#define USE_UMOUNT(...)
#define CFG_UNAME 1
#define USE_UNAME(...) __VA_ARGS__
+#define CFG_UNICODE 0
+#define USE_UNICODE(...)
#define CFG_UNIQ 1
#define USE_UNIQ(...) __VA_ARGS__
#define CFG_UNIX2DOS 1
diff --git a/android/linux/generated/flags.h b/android/linux/generated/flags.h
index 5aefddd2..e6bbb339 100644
--- a/android/linux/generated/flags.h
+++ b/android/linux/generated/flags.h
@@ -73,6 +73,17 @@
#undef FOR_ascii
#endif
+// base32 diw#<0=76[!dw]
+#undef OPTSTR_base32
+#define OPTSTR_base32 "diw#<0=76[!dw]"
+#ifdef CLEANUP_base32
+#undef CLEANUP_base32
+#undef FOR_base32
+#undef FLAG_w
+#undef FLAG_i
+#undef FLAG_d
+#endif
+
// base64 diw#<0=76[!dw]
#undef OPTSTR_base64
#define OPTSTR_base64 "diw#<0=76[!dw]"
@@ -2344,6 +2355,26 @@
#undef FLAG_a
#endif
+// pwgen >2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]
+#undef OPTSTR_pwgen
+#define OPTSTR_pwgen ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]"
+#ifdef CLEANUP_pwgen
+#undef CLEANUP_pwgen
+#undef FOR_pwgen
+#undef FLAG_0
+#undef FLAG_A
+#undef FLAG_v
+#undef FLAG_1
+#undef FLAG_C
+#undef FLAG_h
+#undef FLAG_B
+#undef FLAG_s
+#undef FLAG_y
+#undef FLAG_n
+#undef FLAG_c
+#undef FLAG_r
+#endif
+
// readahead
#undef OPTSTR_readahead
#define OPTSTR_readahead 0
@@ -2560,6 +2591,14 @@
#undef FLAG_f
#endif
+// set
+#undef OPTSTR_set
+#define OPTSTR_set 0
+#ifdef CLEANUP_set
+#undef CLEANUP_set
+#undef FOR_set
+#endif
+
// setenforce <1>1
#undef OPTSTR_setenforce
#define OPTSTR_setenforce "<1>1"
@@ -3246,6 +3285,14 @@
#undef FLAG_o
#endif
+// unicode <1
+#undef OPTSTR_unicode
+#define OPTSTR_unicode "<1"
+#ifdef CLEANUP_unicode
+#undef CLEANUP_unicode
+#undef FOR_unicode
+#endif
+
// uniq f#s#w#zicdu f#s#w#zicdu
#undef OPTSTR_uniq
#define OPTSTR_uniq "f#s#w#zicdu"
@@ -3598,6 +3645,15 @@
#endif
#endif
+#ifdef FOR_base32
+#ifndef TT
+#define TT this.base32
+#endif
+#define FLAG_w (FORCED_FLAG<<0)
+#define FLAG_i (FORCED_FLAG<<1)
+#define FLAG_d (FORCED_FLAG<<2)
+#endif
+
#ifdef FOR_base64
#ifndef TT
#define TT this.base64
@@ -5513,6 +5569,24 @@
#define FLAG_a (FORCED_FLAG<<0)
#endif
+#ifdef FOR_pwgen
+#ifndef TT
+#define TT this.pwgen
+#endif
+#define FLAG_0 (FORCED_FLAG<<0)
+#define FLAG_A (FORCED_FLAG<<1)
+#define FLAG_v (FORCED_FLAG<<2)
+#define FLAG_1 (FORCED_FLAG<<3)
+#define FLAG_C (FORCED_FLAG<<4)
+#define FLAG_h (FORCED_FLAG<<5)
+#define FLAG_B (FORCED_FLAG<<6)
+#define FLAG_s (FORCED_FLAG<<7)
+#define FLAG_y (FORCED_FLAG<<8)
+#define FLAG_n (FORCED_FLAG<<9)
+#define FLAG_c (FORCED_FLAG<<10)
+#define FLAG_r (FORCED_FLAG<<11)
+#endif
+
#ifdef FOR_readahead
#ifndef TT
#define TT this.readahead
@@ -5691,6 +5765,12 @@
#define FLAG_f (1<<2)
#endif
+#ifdef FOR_set
+#ifndef TT
+#define TT this.set
+#endif
+#endif
+
#ifdef FOR_setenforce
#ifndef TT
#define TT this.setenforce
@@ -6275,6 +6355,12 @@
#define FLAG_o (1<<6)
#endif
+#ifdef FOR_unicode
+#ifndef TT
+#define TT this.unicode
+#endif
+#endif
+
#ifdef FOR_uniq
#ifndef TT
#define TT this.uniq
diff --git a/android/linux/generated/globals.h b/android/linux/generated/globals.h
index 9005cea9..876e468b 100644
--- a/android/linux/generated/globals.h
+++ b/android/linux/generated/globals.h
@@ -124,7 +124,7 @@ struct pidof_data {
struct seq_data {
char *s, *f;
- int precision;
+ int precision, buflen;
};
// toys/lsb/su.c
@@ -161,8 +161,8 @@ struct ifconfig_data {
struct microcom_data {
long s;
- int fd;
- struct termios original_stdin_state, original_fd_state;
+ int fd, stok;
+ struct termios old_stdin, old_fd;
};
// toys/net/netcat.c
@@ -214,8 +214,9 @@ struct acpi_data {
struct base64_data {
long w;
-
unsigned total;
+ unsigned n; // number of bits used in encoding. 5 for base32, 6 for base64
+ unsigned align; // number of bits to align to
};
// toys/other/blkdiscard.c
@@ -379,6 +380,12 @@ struct oneit_data {
char *c;
};
+// toys/other/pwgen.c
+
+struct pwgen_data {
+ char *r;
+};
+
// toys/other/rtcwake.c
struct rtcwake_data {
@@ -841,11 +848,10 @@ struct sh_data {
} exec;
};
- // keep lineno here: used to work around compiler limitation in run_command()
- long lineno;
+ // keep ifs here: used to work around compiler limitation in run_command()
char *ifs, *isexec, *wcpat;
- unsigned options, jobcnt;
- int hfd, pid, bangpid, varslen, shift, cdcount;
+ unsigned options, jobcnt, LINENO;
+ int hfd, pid, bangpid, varslen, cdcount;
long long SECONDS;
// global and local variables
@@ -857,8 +863,9 @@ struct sh_data {
// Parsed functions
struct sh_function {
char *name;
- struct sh_pipeline { // pipeline segments
+ struct sh_pipeline { // pipeline segments: linked list of arg w/metadata
struct sh_pipeline *next, *prev, *end;
+ unsigned lineno;
int count, here, type; // TODO abuse type to replace count during parsing
struct sh_arg {
char **v;
@@ -878,8 +885,17 @@ struct sh_data {
struct sh_arg *raw, arg;
} *pp; // currently running process
+ struct sh_callstack {
+ struct sh_callstack *next;
+ struct sh_function scratch;
+ struct sh_arg arg;
+ struct arg_list *delete;
+ unsigned lineno;
+ long shift;
+ } *cc;
+
// job list, command line for $*, scratch space for do_wildcard_files()
- struct sh_arg jobs, *arg, *wcdeck;
+ struct sh_arg jobs, *wcdeck;
};
// toys/pending/stty.c
@@ -1492,6 +1508,7 @@ struct tar_data {
struct tee_data {
void *outputs;
+ int out;
};
// toys/posix/touch.c
@@ -1587,6 +1604,7 @@ extern union global_union {
struct modinfo_data modinfo;
struct nsenter_data nsenter;
struct oneit_data oneit;
+ struct pwgen_data pwgen;
struct rtcwake_data rtcwake;
struct setfattr_data setfattr;
struct sha3sum_data sha3sum;
diff --git a/android/linux/generated/help.h b/android/linux/generated/help.h
index bcc1f770..dfe4a405 100644
--- a/android/linux/generated/help.h
+++ b/android/linux/generated/help.h
@@ -12,8 +12,6 @@
#define HELP_toybox_free "When a program exits, the operating system will clean up after it\n(free memory, close files, etc). To save size, toybox usually relies\non this behavior. If you're running toybox under a debugger or\nwithout a real OS (ala newlib+libgloss), enable this to make toybox\nclean up after itself."
-#define HELP_toybox_i18n "Support for UTF-8 character sets, and some locale support."
-
#define HELP_toybox_help_dashdash "Support --help argument in all commands, even ones with a NULL\noptstring. (Use TOYFLAG_NOHELP to disable.) Produces the same output\nas \"help command\". --version shows toybox version."
#define HELP_toybox_help "Include help text for each command."
@@ -202,6 +200,8 @@
#define HELP_readahead "usage: readahead FILE...\n\nPreload files into disk cache."
+#define HELP_pwgen "usage: pwgen [-cAn0yrsBhC1v] [LENGTH] [COUNT]\n\nGenerate human-readable random passwords. When output is to tty produces\na screenfull to defeat shoulder surfing (pick one and clear the screen).\n\n-c --capitalize Permit capital letters.\n-A --no-capitalize Don't include capital letters.\n-n --numerals Permit numbers.\n-0 --no-numerals Don't include numbers.\n-y --symbols Permit special characters ($#%...).\n-r <chars> --remove=<chars> Don't include the given characters.\n-s --secure Generate more random passwords.\n-B --ambiguous Avoid ambiguous characters (e.g. 0, O).\n-h --help Print this help message.\n-C Print the output in columns.\n-1 Print the output one line each.\n-v Don't include vowels."
+
#define HELP_pwdx "usage: pwdx PID...\n\nPrint working directory of processes listed on command line."
#define HELP_printenv "usage: printenv [-0] [env_var...]\n\nPrint environment variables.\n\n-0 Use \\0 as delimiter instead of \\n"
@@ -320,6 +320,8 @@
#define HELP_blkdiscard "usage: blkdiscard [-olszf] DEVICE\n\nDiscard device sectors.\n\n-o, --offset OFF Byte offset to start discarding at (default 0)\n-l, --length LEN Bytes to discard (default all)\n-s, --secure Perform secure discard\n-z, --zeroout Zero-fill rather than discard\n-f, --force Disable check for mounted filesystem\n\nOFF and LEN must be aligned to the device sector size.\nBy default entire device is discarded.\nWARNING: All discarded data is permanently lost!"
+#define HELP_base32 "usage: base32 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base32.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
+
#define HELP_base64 "usage: base64 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base64.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
#define HELP_ascii "usage: ascii\n\nDisplay ascii character set."
@@ -336,6 +338,8 @@
#define HELP_useradd "usage: useradd [-SDH] [-h DIR] [-s SHELL] [-G GRP] [-g NAME] [-u UID] USER [GROUP]\n\nCreate new user, or add USER to GROUP\n\n-D Don't assign a password\n-g NAME Real name\n-G GRP Add user to existing group\n-h DIR Home directory\n-H Don't create home directory\n-s SHELL Login shell\n-S Create a system user\n-u UID User id"
+#define HELP_unicode "usage: unicode [[min]-max]\n\nConvert between Unicode code points and UTF-8, in both directions."
+
#define HELP_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\ntraceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC]\n [-i IFACE] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6 Force IP or IPv6 name resolution\n-F Set the don't fragment bit (supports IPV4 only)\n-U Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)\n-I Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)\n-l Display the TTL value of the returned packet (supports IPV4 only)\n-d Set SO_DEBUG options to socket\n-n Print numeric addresses\n-v verbose\n-r Bypass routing tables, send directly to HOST\n-m Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s IP address to use as the source address\n-t Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g Loose source route gateway (8 max) (supports IPV4 only)\n-z Pause Time in ms (default 0)(RANGE 0 to 86400) (supports IPV4 only)\n-f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)\n-i Specify a network interface to operate with"
#define HELP_tr "usage: tr [-cds] SET1 [SET2]\n\nTranslate, squeeze, or delete characters from stdin, writing to stdout\n\n-c/-C Take complement of SET1\n-d Delete input characters coded SET1\n-s Squeeze multiple output characters of SET2 into one character"
@@ -370,6 +374,8 @@
#define HELP_unset "usage: unset [-fvn] NAME...\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that"
+#define HELP_set "usage: set [+a] [+o OPTION] [VAR...]\n\nSet variables and shell attributes. Use + to disable and - to enable.\nNAME=VALUE arguments assign to the variable, any leftovers set $1, $2...\nWith no arguments, prints current variables.\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that\n\nOPTIONs:\n history - enable command history"
+
#define HELP_exit "usage: exit [status]\n\nExit shell. If no return value supplied on command line, use value\nof most recent command, or 0 if none."
#define HELP_cd "usage: cd [-PL] [path]\n\nChange current directory. With no arguments, go $HOME.\n\n-P Physical path: resolve symlinks in path\n-L Local path: .. trims directories off $PWD (default)"
@@ -492,7 +498,7 @@
#define HELP_time "usage: time [-pv] COMMAND...\n\nRun command line and report real, user, and system time elapsed in seconds.\n(real = clock on the wall, user = cpu used by command's code,\nsystem = cpu used by OS on behalf of command.)\n\n-p POSIX format output (default)\n-v Verbose"
-#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
+#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size -k sticky bit\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
#define HELP_tee "usage: tee [-ai] [FILE...]\n\nCopy stdin to each listed file, and also to stdout.\nFilename \"-\" is a synonym for stdout.\n\n-a Append to files\n-i Ignore SIGINT"
@@ -618,7 +624,7 @@
#define HELP_cksum "usage: cksum [-IPLN] [FILE...]\n\nFor each file, output crc32 checksum value, length and name of file.\nIf no files listed, copy from stdin. Filename \"-\" is a synonym for stdin.\n\n-H Hexadecimal checksum (defaults to decimal)\n-L Little endian (defaults to big endian)\n-P Pre-inversion\n-I Skip post-inversion\n-N Do not include length in CRC calculation (or output)"
-#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
+#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, +t = sticky. (o+s ignored so a+s doesn't set +t)\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
#define HELP_chown "see: chgrp"
diff --git a/android/linux/generated/newtoys.h b/android/linux/generated/newtoys.h
index 1b8324f5..3e86e653 100644
--- a/android/linux/generated/newtoys.h
+++ b/android/linux/generated/newtoys.h
@@ -4,7 +4,7 @@ USE_SH(OLDTOY(-sh, sh, 0))
USE_SH(OLDTOY(-toysh, sh, 0))
USE_SH(OLDTOY(., source, TOYFLAG_NOFORK))
USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
-USE_TEST(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
+USE_SH(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
USE_ACPI(NEWTOY(acpi, "abctV", TOYFLAG_USR|TOYFLAG_BIN))
USE_GROUPADD(OLDTOY(addgroup, groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
@@ -12,6 +12,7 @@ USE_ARCH(NEWTOY(arch, 0, TOYFLAG_USR|TOYFLAG_BIN))
USE_ARP(NEWTOY(arp, "vi:nDsdap:A:H:[+Ap][!sd]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN))
USE_ASCII(NEWTOY(ascii, 0, TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE32(NEWTOY(base32, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASENAME(NEWTOY(basename, "^<1as:", TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(OLDTOY(bash, sh, TOYFLAG_BIN))
@@ -68,7 +69,7 @@ USE_DOS2UNIX(NEWTOY(dos2unix, 0, TOYFLAG_BIN))
USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsxb[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
USE_DUMPLEASES(NEWTOY(dumpleases, ">0arf:[!ar]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_EJECT(NEWTOY(eject, ">1stT[!tT]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ENV(NEWTOY(env, "^i0u*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK))
@@ -81,7 +82,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|TOYFLAG_MAYFORK))
USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_FILE(NEWTOY(file, "<1bhLs[!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))
@@ -100,7 +101,7 @@ USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETFATTR(NEWTOY(getfattr, "(only-values)dhn:", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh", TOYFLAG_SBIN))
-USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
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))
@@ -208,6 +209,7 @@ USE_ULIMIT(OLDTOY(prlimit, ulimit, TOYFLAG_USR|TOYFLAG_BIN))
USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_PWD(NEWTOY(pwd, ">0LP[-LP]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_PWDX(NEWTOY(pwdx, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
USE_READELF(NEWTOY(readelf, "<1(dyn-syms)adehlnp:SsWx:", TOYFLAG_USR|TOYFLAG_BIN))
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -227,6 +229,7 @@ USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SED(NEWTOY(sed, "(help)(version)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(set, 0, TOYFLAG_NOFORK))
USE_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_SETSID(NEWTOY(setsid, "^<1wcd[!dc]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -284,6 +287,7 @@ USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
USE_ULIMIT(NEWTOY(ulimit, ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]", TOYFLAG_USR|TOYFLAG_BIN))
USE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
USE_UNAME(NEWTOY(uname, "oamvrns[+os]", TOYFLAG_BIN))
+USE_UNICODE(NEWTOY(unicode, "<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIX2DOS(NEWTOY(unix2dos, 0, TOYFLAG_BIN))
USE_UNLINK(NEWTOY(unlink, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/android/mac/generated/config.h b/android/mac/generated/config.h
index b39ec910..d03b74a2 100644
--- a/android/mac/generated/config.h
+++ b/android/mac/generated/config.h
@@ -64,6 +64,8 @@
#define USE_ARP(...)
#define CFG_ASCII 0
#define USE_ASCII(...)
+#define CFG_BASE32 0
+#define USE_BASE32(...)
#define CFG_BASE64 0
#define USE_BASE64(...)
#define CFG_BASENAME 1
@@ -456,6 +458,8 @@
#define USE_PWDX(...)
#define CFG_PWD 1
#define USE_PWD(...) __VA_ARGS__
+#define CFG_PWGEN 0
+#define USE_PWGEN(...)
#define CFG_READAHEAD 0
#define USE_READAHEAD(...)
#define CFG_READELF 0
@@ -600,6 +604,8 @@
#define USE_UMOUNT(...)
#define CFG_UNAME 1
#define USE_UNAME(...) __VA_ARGS__
+#define CFG_UNICODE 0
+#define USE_UNICODE(...)
#define CFG_UNIQ 1
#define USE_UNIQ(...) __VA_ARGS__
#define CFG_UNIX2DOS 1
diff --git a/android/mac/generated/flags.h b/android/mac/generated/flags.h
index c2a9b82d..ae5d58a2 100644
--- a/android/mac/generated/flags.h
+++ b/android/mac/generated/flags.h
@@ -73,6 +73,17 @@
#undef FOR_ascii
#endif
+// base32 diw#<0=76[!dw]
+#undef OPTSTR_base32
+#define OPTSTR_base32 "diw#<0=76[!dw]"
+#ifdef CLEANUP_base32
+#undef CLEANUP_base32
+#undef FOR_base32
+#undef FLAG_w
+#undef FLAG_i
+#undef FLAG_d
+#endif
+
// base64 diw#<0=76[!dw]
#undef OPTSTR_base64
#define OPTSTR_base64 "diw#<0=76[!dw]"
@@ -2344,6 +2355,26 @@
#undef FLAG_a
#endif
+// pwgen >2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]
+#undef OPTSTR_pwgen
+#define OPTSTR_pwgen ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]"
+#ifdef CLEANUP_pwgen
+#undef CLEANUP_pwgen
+#undef FOR_pwgen
+#undef FLAG_0
+#undef FLAG_A
+#undef FLAG_v
+#undef FLAG_1
+#undef FLAG_C
+#undef FLAG_h
+#undef FLAG_B
+#undef FLAG_s
+#undef FLAG_y
+#undef FLAG_n
+#undef FLAG_c
+#undef FLAG_r
+#endif
+
// readahead
#undef OPTSTR_readahead
#define OPTSTR_readahead 0
@@ -2560,6 +2591,14 @@
#undef FLAG_f
#endif
+// set
+#undef OPTSTR_set
+#define OPTSTR_set 0
+#ifdef CLEANUP_set
+#undef CLEANUP_set
+#undef FOR_set
+#endif
+
// setenforce <1>1
#undef OPTSTR_setenforce
#define OPTSTR_setenforce "<1>1"
@@ -3246,6 +3285,14 @@
#undef FLAG_o
#endif
+// unicode <1
+#undef OPTSTR_unicode
+#define OPTSTR_unicode "<1"
+#ifdef CLEANUP_unicode
+#undef CLEANUP_unicode
+#undef FOR_unicode
+#endif
+
// uniq f#s#w#zicdu f#s#w#zicdu
#undef OPTSTR_uniq
#define OPTSTR_uniq "f#s#w#zicdu"
@@ -3598,6 +3645,15 @@
#endif
#endif
+#ifdef FOR_base32
+#ifndef TT
+#define TT this.base32
+#endif
+#define FLAG_w (FORCED_FLAG<<0)
+#define FLAG_i (FORCED_FLAG<<1)
+#define FLAG_d (FORCED_FLAG<<2)
+#endif
+
#ifdef FOR_base64
#ifndef TT
#define TT this.base64
@@ -5513,6 +5569,24 @@
#define FLAG_a (FORCED_FLAG<<0)
#endif
+#ifdef FOR_pwgen
+#ifndef TT
+#define TT this.pwgen
+#endif
+#define FLAG_0 (FORCED_FLAG<<0)
+#define FLAG_A (FORCED_FLAG<<1)
+#define FLAG_v (FORCED_FLAG<<2)
+#define FLAG_1 (FORCED_FLAG<<3)
+#define FLAG_C (FORCED_FLAG<<4)
+#define FLAG_h (FORCED_FLAG<<5)
+#define FLAG_B (FORCED_FLAG<<6)
+#define FLAG_s (FORCED_FLAG<<7)
+#define FLAG_y (FORCED_FLAG<<8)
+#define FLAG_n (FORCED_FLAG<<9)
+#define FLAG_c (FORCED_FLAG<<10)
+#define FLAG_r (FORCED_FLAG<<11)
+#endif
+
#ifdef FOR_readahead
#ifndef TT
#define TT this.readahead
@@ -5691,6 +5765,12 @@
#define FLAG_f (1<<2)
#endif
+#ifdef FOR_set
+#ifndef TT
+#define TT this.set
+#endif
+#endif
+
#ifdef FOR_setenforce
#ifndef TT
#define TT this.setenforce
@@ -6275,6 +6355,12 @@
#define FLAG_o (1<<6)
#endif
+#ifdef FOR_unicode
+#ifndef TT
+#define TT this.unicode
+#endif
+#endif
+
#ifdef FOR_uniq
#ifndef TT
#define TT this.uniq
diff --git a/android/mac/generated/globals.h b/android/mac/generated/globals.h
index 9005cea9..876e468b 100644
--- a/android/mac/generated/globals.h
+++ b/android/mac/generated/globals.h
@@ -124,7 +124,7 @@ struct pidof_data {
struct seq_data {
char *s, *f;
- int precision;
+ int precision, buflen;
};
// toys/lsb/su.c
@@ -161,8 +161,8 @@ struct ifconfig_data {
struct microcom_data {
long s;
- int fd;
- struct termios original_stdin_state, original_fd_state;
+ int fd, stok;
+ struct termios old_stdin, old_fd;
};
// toys/net/netcat.c
@@ -214,8 +214,9 @@ struct acpi_data {
struct base64_data {
long w;
-
unsigned total;
+ unsigned n; // number of bits used in encoding. 5 for base32, 6 for base64
+ unsigned align; // number of bits to align to
};
// toys/other/blkdiscard.c
@@ -379,6 +380,12 @@ struct oneit_data {
char *c;
};
+// toys/other/pwgen.c
+
+struct pwgen_data {
+ char *r;
+};
+
// toys/other/rtcwake.c
struct rtcwake_data {
@@ -841,11 +848,10 @@ struct sh_data {
} exec;
};
- // keep lineno here: used to work around compiler limitation in run_command()
- long lineno;
+ // keep ifs here: used to work around compiler limitation in run_command()
char *ifs, *isexec, *wcpat;
- unsigned options, jobcnt;
- int hfd, pid, bangpid, varslen, shift, cdcount;
+ unsigned options, jobcnt, LINENO;
+ int hfd, pid, bangpid, varslen, cdcount;
long long SECONDS;
// global and local variables
@@ -857,8 +863,9 @@ struct sh_data {
// Parsed functions
struct sh_function {
char *name;
- struct sh_pipeline { // pipeline segments
+ struct sh_pipeline { // pipeline segments: linked list of arg w/metadata
struct sh_pipeline *next, *prev, *end;
+ unsigned lineno;
int count, here, type; // TODO abuse type to replace count during parsing
struct sh_arg {
char **v;
@@ -878,8 +885,17 @@ struct sh_data {
struct sh_arg *raw, arg;
} *pp; // currently running process
+ struct sh_callstack {
+ struct sh_callstack *next;
+ struct sh_function scratch;
+ struct sh_arg arg;
+ struct arg_list *delete;
+ unsigned lineno;
+ long shift;
+ } *cc;
+
// job list, command line for $*, scratch space for do_wildcard_files()
- struct sh_arg jobs, *arg, *wcdeck;
+ struct sh_arg jobs, *wcdeck;
};
// toys/pending/stty.c
@@ -1492,6 +1508,7 @@ struct tar_data {
struct tee_data {
void *outputs;
+ int out;
};
// toys/posix/touch.c
@@ -1587,6 +1604,7 @@ extern union global_union {
struct modinfo_data modinfo;
struct nsenter_data nsenter;
struct oneit_data oneit;
+ struct pwgen_data pwgen;
struct rtcwake_data rtcwake;
struct setfattr_data setfattr;
struct sha3sum_data sha3sum;
diff --git a/android/mac/generated/help.h b/android/mac/generated/help.h
index bcc1f770..dfe4a405 100644
--- a/android/mac/generated/help.h
+++ b/android/mac/generated/help.h
@@ -12,8 +12,6 @@
#define HELP_toybox_free "When a program exits, the operating system will clean up after it\n(free memory, close files, etc). To save size, toybox usually relies\non this behavior. If you're running toybox under a debugger or\nwithout a real OS (ala newlib+libgloss), enable this to make toybox\nclean up after itself."
-#define HELP_toybox_i18n "Support for UTF-8 character sets, and some locale support."
-
#define HELP_toybox_help_dashdash "Support --help argument in all commands, even ones with a NULL\noptstring. (Use TOYFLAG_NOHELP to disable.) Produces the same output\nas \"help command\". --version shows toybox version."
#define HELP_toybox_help "Include help text for each command."
@@ -202,6 +200,8 @@
#define HELP_readahead "usage: readahead FILE...\n\nPreload files into disk cache."
+#define HELP_pwgen "usage: pwgen [-cAn0yrsBhC1v] [LENGTH] [COUNT]\n\nGenerate human-readable random passwords. When output is to tty produces\na screenfull to defeat shoulder surfing (pick one and clear the screen).\n\n-c --capitalize Permit capital letters.\n-A --no-capitalize Don't include capital letters.\n-n --numerals Permit numbers.\n-0 --no-numerals Don't include numbers.\n-y --symbols Permit special characters ($#%...).\n-r <chars> --remove=<chars> Don't include the given characters.\n-s --secure Generate more random passwords.\n-B --ambiguous Avoid ambiguous characters (e.g. 0, O).\n-h --help Print this help message.\n-C Print the output in columns.\n-1 Print the output one line each.\n-v Don't include vowels."
+
#define HELP_pwdx "usage: pwdx PID...\n\nPrint working directory of processes listed on command line."
#define HELP_printenv "usage: printenv [-0] [env_var...]\n\nPrint environment variables.\n\n-0 Use \\0 as delimiter instead of \\n"
@@ -320,6 +320,8 @@
#define HELP_blkdiscard "usage: blkdiscard [-olszf] DEVICE\n\nDiscard device sectors.\n\n-o, --offset OFF Byte offset to start discarding at (default 0)\n-l, --length LEN Bytes to discard (default all)\n-s, --secure Perform secure discard\n-z, --zeroout Zero-fill rather than discard\n-f, --force Disable check for mounted filesystem\n\nOFF and LEN must be aligned to the device sector size.\nBy default entire device is discarded.\nWARNING: All discarded data is permanently lost!"
+#define HELP_base32 "usage: base32 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base32.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
+
#define HELP_base64 "usage: base64 [-di] [-w COLUMNS] [FILE...]\n\nEncode or decode in base64.\n\n-d Decode\n-i Ignore non-alphabetic characters\n-w Wrap output at COLUMNS (default 76 or 0 for no wrap)"
#define HELP_ascii "usage: ascii\n\nDisplay ascii character set."
@@ -336,6 +338,8 @@
#define HELP_useradd "usage: useradd [-SDH] [-h DIR] [-s SHELL] [-G GRP] [-g NAME] [-u UID] USER [GROUP]\n\nCreate new user, or add USER to GROUP\n\n-D Don't assign a password\n-g NAME Real name\n-G GRP Add user to existing group\n-h DIR Home directory\n-H Don't create home directory\n-s SHELL Login shell\n-S Create a system user\n-u UID User id"
+#define HELP_unicode "usage: unicode [[min]-max]\n\nConvert between Unicode code points and UTF-8, in both directions."
+
#define HELP_traceroute "usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n[-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]\n\ntraceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC]\n [-i IFACE] HOST [BYTES]\n\nTrace the route to HOST\n\n-4,-6 Force IP or IPv6 name resolution\n-F Set the don't fragment bit (supports IPV4 only)\n-U Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)\n-I Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)\n-l Display the TTL value of the returned packet (supports IPV4 only)\n-d Set SO_DEBUG options to socket\n-n Print numeric addresses\n-v verbose\n-r Bypass routing tables, send directly to HOST\n-m Max time-to-live (max number of hops)(RANGE 1 to 255)\n-p Base UDP port number used in probes(default 33434)(RANGE 1 to 65535)\n-q Number of probes per TTL (default 3)(RANGE 1 to 255)\n-s IP address to use as the source address\n-t Type-of-service in probe packets (default 0)(RANGE 0 to 255)\n-w Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)\n-g Loose source route gateway (8 max) (supports IPV4 only)\n-z Pause Time in ms (default 0)(RANGE 0 to 86400) (supports IPV4 only)\n-f Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)\n-i Specify a network interface to operate with"
#define HELP_tr "usage: tr [-cds] SET1 [SET2]\n\nTranslate, squeeze, or delete characters from stdin, writing to stdout\n\n-c/-C Take complement of SET1\n-d Delete input characters coded SET1\n-s Squeeze multiple output characters of SET2 into one character"
@@ -370,6 +374,8 @@
#define HELP_unset "usage: unset [-fvn] NAME...\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that"
+#define HELP_set "usage: set [+a] [+o OPTION] [VAR...]\n\nSet variables and shell attributes. Use + to disable and - to enable.\nNAME=VALUE arguments assign to the variable, any leftovers set $1, $2...\nWith no arguments, prints current variables.\n\n-f NAME is a function\n-v NAME is a variable\n-n dereference NAME and unset that\n\nOPTIONs:\n history - enable command history"
+
#define HELP_exit "usage: exit [status]\n\nExit shell. If no return value supplied on command line, use value\nof most recent command, or 0 if none."
#define HELP_cd "usage: cd [-PL] [path]\n\nChange current directory. With no arguments, go $HOME.\n\n-P Physical path: resolve symlinks in path\n-L Local path: .. trims directories off $PWD (default)"
@@ -492,7 +498,7 @@
#define HELP_time "usage: time [-pv] COMMAND...\n\nRun command line and report real, user, and system time elapsed in seconds.\n(real = clock on the wall, user = cpu used by command's code,\nsystem = cpu used by OS on behalf of command.)\n\n-p POSIX format output (default)\n-v Verbose"
-#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
+#define HELP_test "usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]\n\nReturn true or false by performing tests. (With no arguments return false.)\n\n--- Tests with a single argument (after the option):\nPATH is/has:\n -b block device -f regular file -p fifo -u setuid bit\n -c char device -g setgid -r read bit -w write bit\n -d directory -h symlink -S socket -x execute bit\n -e exists -L symlink -s nonzero size -k sticky bit\nSTRING is:\n -n nonzero size -z zero size (STRING by itself implies -n)\nFD (integer file descriptor) is:\n -t a TTY\n\n--- Tests with one argument on each side of an operator:\nTwo strings:\n = are identical != differ\nTwo integers:\n -eq equal -gt first > second -lt first < second\n -ne not equal -ge first >= second -le first <= second\n\n--- Modify or combine tests:\n ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)\n ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)"
#define HELP_tee "usage: tee [-ai] [FILE...]\n\nCopy stdin to each listed file, and also to stdout.\nFilename \"-\" is a synonym for stdout.\n\n-a Append to files\n-i Ignore SIGINT"
@@ -618,7 +624,7 @@
#define HELP_cksum "usage: cksum [-IPLN] [FILE...]\n\nFor each file, output crc32 checksum value, length and name of file.\nIf no files listed, copy from stdin. Filename \"-\" is a synonym for stdin.\n\n-H Hexadecimal checksum (defaults to decimal)\n-L Little endian (defaults to big endian)\n-P Pre-inversion\n-I Skip post-inversion\n-N Do not include length in CRC calculation (or output)"
-#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
+#define HELP_chmod "usage: chmod [-R] MODE FILE...\n\nChange mode of listed file[s] (recursively with -R).\n\nMODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]\n\nStanzas are applied in order: For each category (u = user,\ng = group, o = other, a = all three, if none specified default is a),\nset (+), clear (-), or copy (=), r = read, w = write, x = execute.\ns = u+s = suid, g+s = sgid, +t = sticky. (o+s ignored so a+s doesn't set +t)\nsuid/sgid: execute as the user/group who owns the file.\nsticky: can't delete files you don't own out of this directory\nX = x for directories or if any category already has x set.\n\nOr MODE can be an octal value up to 7777 ug uuugggooo top +\nbit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom\n\nExamples:\nchmod u+w file - allow owner of \"file\" to write to it.\nchmod 744 file - user can read/write/execute, everyone else read only"
#define HELP_chown "see: chgrp"
diff --git a/android/mac/generated/newtoys.h b/android/mac/generated/newtoys.h
index 1b8324f5..3e86e653 100644
--- a/android/mac/generated/newtoys.h
+++ b/android/mac/generated/newtoys.h
@@ -4,7 +4,7 @@ USE_SH(OLDTOY(-sh, sh, 0))
USE_SH(OLDTOY(-toysh, sh, 0))
USE_SH(OLDTOY(., source, TOYFLAG_NOFORK))
USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
-USE_TEST(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
+USE_SH(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
USE_ACPI(NEWTOY(acpi, "abctV", TOYFLAG_USR|TOYFLAG_BIN))
USE_GROUPADD(OLDTOY(addgroup, groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
@@ -12,6 +12,7 @@ USE_ARCH(NEWTOY(arch, 0, TOYFLAG_USR|TOYFLAG_BIN))
USE_ARP(NEWTOY(arp, "vi:nDsdap:A:H:[+Ap][!sd]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ARPING(NEWTOY(arping, "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]", TOYFLAG_USR|TOYFLAG_SBIN))
USE_ASCII(NEWTOY(ascii, 0, TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE32(NEWTOY(base32, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
USE_BASENAME(NEWTOY(basename, "^<1as:", TOYFLAG_USR|TOYFLAG_BIN))
USE_SH(OLDTOY(bash, sh, TOYFLAG_BIN))
@@ -68,7 +69,7 @@ USE_DOS2UNIX(NEWTOY(dos2unix, 0, TOYFLAG_BIN))
USE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsxb[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
USE_DUMPLEASES(NEWTOY(dumpleases, ">0arf:[!ar]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ECHO(NEWTOY(echo, "^?Een[-eE]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_EJECT(NEWTOY(eject, ">1stT[!tT]", TOYFLAG_USR|TOYFLAG_BIN))
USE_ENV(NEWTOY(env, "^i0u*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK))
@@ -81,7 +82,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|TOYFLAG_MAYFORK))
USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
USE_FILE(NEWTOY(file, "<1bhLs[!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))
@@ -100,7 +101,7 @@ USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
USE_GETFATTR(NEWTOY(getfattr, "(only-values)dhn:", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETOPT(NEWTOY(getopt, "^a(alternative)n:(name)o:(options)l*(long)(longoptions)Tu", TOYFLAG_USR|TOYFLAG_BIN))
USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh", TOYFLAG_SBIN))
-USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
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))
@@ -208,6 +209,7 @@ USE_ULIMIT(OLDTOY(prlimit, ulimit, TOYFLAG_USR|TOYFLAG_BIN))
USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_PWD(NEWTOY(pwd, ">0LP[-LP]", TOYFLAG_BIN|TOYFLAG_MAYFORK))
USE_PWDX(NEWTOY(pwdx, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
USE_READELF(NEWTOY(readelf, "<1(dyn-syms)adehlnp:SsWx:", TOYFLAG_USR|TOYFLAG_BIN))
USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -227,6 +229,7 @@ USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SED(NEWTOY(sed, "(help)(version)e*f*i:;nErz(null-data)s[+Er]", TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SEQ(NEWTOY(seq, "<1>3?f:s:w[!fw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SH(NEWTOY(set, 0, TOYFLAG_NOFORK))
USE_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
USE_SETSID(NEWTOY(setsid, "^<1wcd[!dc]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -284,6 +287,7 @@ USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
USE_ULIMIT(NEWTOY(ulimit, ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]", TOYFLAG_USR|TOYFLAG_BIN))
USE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
USE_UNAME(NEWTOY(uname, "oamvrns[+os]", TOYFLAG_BIN))
+USE_UNICODE(NEWTOY(unicode, "<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNIX2DOS(NEWTOY(unix2dos, 0, TOYFLAG_BIN))
USE_UNLINK(NEWTOY(unlink, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/kconfig/freebsd_miniconfig b/kconfig/freebsd_miniconfig
index 98d3c1cd..3a6fa80a 100644
--- a/kconfig/freebsd_miniconfig
+++ b/kconfig/freebsd_miniconfig
@@ -125,4 +125,3 @@ CONFIG_TOYBOX_SUID=y
CONFIG_TOYBOX_FLOAT=y
CONFIG_TOYBOX_HELP=y
CONFIG_TOYBOX_HELP_DASHDASH=y
-CONFIG_TOYBOX_I18N=y
diff --git a/kconfig/macos_miniconfig b/kconfig/macos_miniconfig
index 71663137..775d8d6b 100644
--- a/kconfig/macos_miniconfig
+++ b/kconfig/macos_miniconfig
@@ -114,4 +114,3 @@ CONFIG_TOYBOX_SUID=y
CONFIG_TOYBOX_FLOAT=y
CONFIG_TOYBOX_HELP=y
CONFIG_TOYBOX_HELP_DASHDASH=y
-CONFIG_TOYBOX_I18N=y
diff --git a/lib/deflate.c b/lib/deflate.c
index eebcd3de..8c724429 100644
--- a/lib/deflate.c
+++ b/lib/deflate.c
@@ -403,7 +403,7 @@ static int is_gzip(struct bitbuf *bb)
bitbuf_skip(bb, 6*8);
// Skip extra, name, comment, header CRC fields
- if (flags & 4) bitbuf_skip(bb, 16);
+ if (flags & 4) bitbuf_skip(bb, bitbuf_get(bb, 16) * 8);
if (flags & 8) while (bitbuf_get(bb, 8));
if (flags & 16) while (bitbuf_get(bb, 8));
if (flags & 2) bitbuf_skip(bb, 16);
diff --git a/lib/lib.c b/lib/lib.c
index c4e70dfe..1aa9b80d 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -349,17 +349,18 @@ int stridx(char *haystack, char needle)
// Convert wc to utf8, returning bytes written. Does not null terminate.
int wctoutf8(char *s, unsigned wc)
{
- int len = (wc>0x7ff)+(wc>0xffff), mask = 12+len+!!len;
+ int len = (wc>0x7ff)+(wc>0xffff), i;
if (wc<128) {
*s = wc;
return 1;
} else {
+ i = len;
do {
- s[1+len] = 0x80+(wc&0x3f);
- wc >>= 7;
- } while (len--);
- *s = wc|mask;
+ s[1+i] = 0x80+(wc&0x3f);
+ wc >>= 6;
+ } while (i--);
+ *s = (((signed char) 0x80) >> (len+1)) | wc;
}
return 2+len;
@@ -395,37 +396,41 @@ int utf8towc(wchar_t *wc, char *str, unsigned len)
return str-s;
}
+// Convert string to lower case, utf8 aware.
char *strlower(char *s)
{
char *try, *new;
+ int len, mlen = (strlen(s)|7)+9;
+ wchar_t c;
- if (!CFG_TOYBOX_I18N) {
- try = new = xstrdup(s);
- for (; *s; s++) *(new++) = tolower(*s);
- } else {
- // I can't guarantee the string _won't_ expand during reencoding, so...?
- try = new = xmalloc(strlen(s)*2+1);
-
- while (*s) {
- wchar_t c;
- int len = utf8towc(&c, s, MB_CUR_MAX);
-
- if (len < 1) *(new++) = *(s++);
- else {
- s += len;
- // squash title case too
- c = towlower(c);
-
- // if we had a valid utf8 sequence, convert it to lower case, and can't
- // encode back to utf8, something is wrong with your libc. But just
- // in case somebody finds an exploit...
- len = wcrtomb(new, c, 0);
- if (len < 1) error_exit("bad utf8 %x", (int)c);
- new += len;
- }
+ try = new = xmalloc(mlen);
+
+ while (*s) {
+
+ if (1>(len = utf8towc(&c, s, MB_CUR_MAX))) {
+ *(new++) = *(s++);
+
+ continue;
}
- *new = 0;
+
+ s += len;
+ // squash title case too
+ c = towlower(c);
+
+ // if we had a valid utf8 sequence, convert it to lower case, and can't
+ // encode back to utf8, something is wrong with your libc. But just
+ // in case somebody finds an exploit...
+ len = wcrtomb(new, c, 0);
+ if (len < 1) error_exit("bad utf8 %x", (int)c);
+ new += len;
+
+ // Case conversion can expand utf8 representation, but with extra mlen
+ // space above we should basically never need to realloc
+ if (mlen+4 > (len = new-try)) continue;
+ try = xrealloc(try, mlen = len+16);
+ new = try+len;
}
+ *new = 0;
return try;
}
@@ -865,6 +870,18 @@ void base64_init(char *p)
*(p++) = '/';
}
+// Init base32 table
+
+void base32_init(char *p)
+{
+ int i;
+
+ for (i = 'A'; i != '8'; i++) {
+ if (i == 'Z'+1) i = '2';
+ *(p++) = i;
+ }
+}
+
int yesno(int def)
{
return fyesno(stdin, def);
@@ -970,55 +987,51 @@ mode_t string_to_mode(char *modestr, mode_t mode)
umask(amask = umask(0));
}
- if (!*str || !(s = strchr(hows, *str))) goto barf;
- if (!(dohow = *(str++))) goto barf;
-
- while (*str && (s = strchr(whats, *str))) {
- dowhat |= 1<<(s-whats);
- str++;
- }
+ // Repeated "hows" are allowed; something like "a=r+w+s" is valid.
+ for (;;) {
+ if (-1 == stridx(hows, dohow = *str)) goto barf;
+ while (*++str && (s = strchr(whats, *str))) dowhat |= 1<<(s-whats);
- // Convert X to x for directory or if already executable somewhere
- if ((dowhat&32) && (S_ISDIR(mode) || (mode&0111))) dowhat |= 1;
+ // Convert X to x for directory or if already executable somewhere
+ if ((dowhat&32) && (S_ISDIR(mode) || (mode&0111))) dowhat |= 1;
- // Copy mode from another category?
- if (!dowhat && *str && (s = strchr(whys, *str))) {
- dowhat = (mode>>(3*(s-whys)))&7;
- str++;
- }
+ // Copy mode from another category?
+ if (!dowhat && -1 != (i = stridx(whys, *str))) {
+ dowhat = (mode>>(3*i))&7;
+ str++;
+ }
- // Are we ready to do a thing yet?
- if (*str && *(str++) != ',') goto barf;
-
- // Loop through what=xwrs and who=ogu to apply bits to the mode.
- for (i=0; i<4; i++) {
- for (j=0; j<3; j++) {
- mode_t bit = 0;
- int where = 1<<((3*i)+j);
-
- if (amask & where) continue;
-
- // Figure out new value at this location
- if (i == 3) {
- // suid and sticky
- if (!j) bit = dowhat&16; // o+s = t
- else if ((dowhat&8) && (dowho&(8|(1<<j)))) bit++;
- } else {
- if (!(dowho&(8|(1<<i)))) continue;
- else if (dowhat&(1<<j)) bit++;
+ // Loop through what=xwrs and who=ogu to apply bits to the mode.
+ for (i=0; i<4; i++) {
+ for (j=0; j<3; j++) {
+ mode_t bit = 0;
+ int where = 1<<((3*i)+j);
+
+ if (amask & where) continue;
+
+ // Figure out new value at this location
+ if (i == 3) {
+ // suid and sticky
+ if (!j) bit = dowhat&16; // o+s = t but a+s doesn't set t, hence t
+ else if ((dowhat&8) && (dowho&(8|(1<<j)))) bit++;
+ } else {
+ if (!(dowho&(8|(1<<i)))) continue;
+ else if (dowhat&(1<<j)) bit++;
+ }
+
+ // When selection active, modify bit
+ if (dohow == '=' || (bit && dohow == '-')) mode &= ~where;
+ if (bit && dohow != '-') mode |= where;
}
-
- // When selection active, modify bit
-
- if (dohow == '=' || (bit && dohow == '-')) mode &= ~where;
- if (bit && dohow != '-') mode |= where;
+ }
+ if (!*str) return mode|extrabits;
+ if (*str == ',') {
+ str++;
+ break;
}
}
-
- if (!*str) break;
}
- return mode|extrabits;
barf:
error_exit("bad mode '%s'", modestr);
}
diff --git a/lib/lib.h b/lib/lib.h
index 150133b6..1ba59ee3 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -255,6 +255,7 @@ void delete_tempfile(int fdin, int fdout, char **tempname);
void replace_tempfile(int fdin, int fdout, char **tempname);
void crc_init(unsigned int *crc_table, int little_endian);
void base64_init(char *p);
+void base32_init(char *p);
int yesno(int def);
int fyesno(FILE *fp, int def);
int qstrcmp(const void *a, const void *b);
@@ -337,6 +338,7 @@ int terminal_probesize(unsigned *xx, unsigned *yy);
#define KEY_ALT (1<<18)
int scan_key(char *scratch, int timeout_ms);
int scan_key_getsize(char *scratch, int timeout_ms, unsigned *xx, unsigned *yy);
+void xsetspeed(struct termios *tio, int speed);
int set_terminal(int fd, int raw, int speed, struct termios *old);
void xset_terminal(int fd, int raw, int speed, struct termios *old);
void tty_esc(char *s);
diff --git a/lib/portability.c b/lib/portability.c
index 91c01900..0c364c29 100644
--- a/lib/portability.c
+++ b/lib/portability.c
@@ -126,14 +126,15 @@ int mountlist_istype(struct mtab_list *ml, char *typelist)
if (!typelist) return 1;
+ // leading "no" indicates whether entire list is inverted
skip = strncmp(typelist, "no", 2);
for (;;) {
if (!(t = comma_iterate(&typelist, &len))) break;
if (!skip) {
- // If one -t starts with "no", the rest must too
- if (strncmp(t, "no", 2)) error_exit("bad typelist");
- if (!strncmp(t+2, ml->type, len-2)) {
+ // later "no" after first are ignored
+ strstart(&t, "no");
+ if (!strncmp(t, ml->type, len-2)) {
skip = 1;
break;
}
diff --git a/lib/portability.h b/lib/portability.h
index 5886f63f..bbba12d8 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -208,12 +208,16 @@ ssize_t xattr_lset(const char*, const char*, const void*, size_t, int);
ssize_t xattr_fset(int, const char*, const void*, size_t, int);
#endif
-// macOS doesn't have these functions, but we can fake them.
#ifdef __APPLE__
+// macOS doesn't have these functions, but we can fake them.
int mknodat(int, const char*, mode_t, dev_t);
int posix_fallocate(int, off_t, off_t);
+
+// macOS keeps newlocale(3) in the non-POSIX <xlocale.h> rather than <locale.h>.
+#include <xlocale.h>
#endif
+
// Android is missing some headers and functions
// "generated/config.h" is included first
#if CFG_TOYBOX_SHADOW
diff --git a/lib/toyflags.h b/lib/toyflags.h
index b158ba88..c8830874 100644
--- a/lib/toyflags.h
+++ b/lib/toyflags.h
@@ -32,6 +32,9 @@
// Suppress default --help processing
#define TOYFLAG_NOHELP (1<<10)
+// Line buffered stdout
+#define TOYFLAG_LINEBUF (1<<11)
+
// Error code to return if argument parsing fails (default 1)
#define TOYFLAG_ARGFAIL(x) (x<<24)
diff --git a/lib/tty.c b/lib/tty.c
index a6b4576a..b0d0c5d5 100644
--- a/lib/tty.c
+++ b/lib/tty.c
@@ -61,6 +61,20 @@ int terminal_probesize(unsigned *xx, unsigned *yy)
return 0;
}
+void xsetspeed(struct termios *tio, int speed)
+{
+ int i, speeds[] = {50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
+ 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
+ 2500000, 3000000, 3500000, 4000000};
+
+ // Find speed in table, adjust to constant
+ for (i = 0; i < ARRAY_LEN(speeds); i++) if (speeds[i] == speed) break;
+ if (i == ARRAY_LEN(speeds)) error_exit("unknown speed: %d", speed);
+ cfsetspeed(tio, i+1+4081*(i>15));
+}
+
+
// Reset terminal to known state, saving copy of old state if old != NULL.
int set_terminal(int fd, int raw, int speed, struct termios *old)
{
diff --git a/main.c b/main.c
index 7c60bdf4..a7a8a694 100644
--- a/main.c
+++ b/main.c
@@ -98,12 +98,15 @@ void toy_singleinit(struct toy_list *which, char *argv[])
if (!(which->flags & TOYFLAG_NOFORK)) {
toys.old_umask = umask(0);
if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask);
- if (CFG_TOYBOX_I18N) {
- // Deliberately try C.UTF-8 before the user's locale to work around users
- // that choose non-UTF-8 locales. macOS doesn't support C.UTF-8 though.
- if (!setlocale(LC_CTYPE, "C.UTF-8")) setlocale(LC_CTYPE, "");
- }
- setlinebuf(stdout);
+
+ // Try user's locale, but merge in the en_US.UTF-8 locale's character
+ // type data if the user's locale isn't UTF-8. (We can't merge in C.UTF-8
+ // because that locale doesn't exist on macOS.)
+ setlocale(LC_CTYPE, "");
+ if (strcmp("UTF-8", nl_langinfo(CODESET)))
+ uselocale(newlocale(LC_CTYPE_MASK, "en_US.UTF-8", NULL));
+
+ setvbuf(stdout, 0, (which->flags & TOYFLAG_LINEBUF) ? _IOLBF : _IONBF, 0);
}
}
diff --git a/scripts/runtest.sh b/scripts/runtest.sh
index ddbf0549..6aad9ff1 100644
--- a/scripts/runtest.sh
+++ b/scripts/runtest.sh
@@ -81,7 +81,13 @@ skipnot()
toyonly()
{
IS_TOYBOX="$("$C" --version 2>/dev/null)"
- [ "${IS_TOYBOX/toybox/}" == "$IS_TOYBOX" ] && SKIPNEXT=1
+ # Ideally we'd just check for "toybox", but toybox sed lies to make autoconf
+ # happy, so we have at least two things to check for.
+ case "$IS_TOYBOX" in
+ toybox*) ;;
+ This\ is\ not\ GNU*) ;;
+ *) SKIPNEXT=1 ;;
+ esac
"$@"
}
diff --git a/tests/base32.test b/tests/base32.test
new file mode 100755
index 00000000..00bcffa9
--- /dev/null
+++ b/tests/base32.test
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+# testing "name" "flags" "result" "infile" "stdin"
+
+testcmd "simple" "" "ONUW24DMMUFA====\n" "" "simple\n"
+testcmd "file" "input" "ONUW24DMMUFA====\n" "simple\n" ""
+testcmd "simple -d" "-d" "simple\n" "" "ONUW24DMMUFA====\n"
+testcmd "file -d" "-d input" "simple\n" "ONUW24DMMUFA====" ""
+testcmd "default wrap" "" \
+ "K5SSO5TFEBZGK4DMMFRWKZBAORUGKIDENFWGS5DINF2W2IDUNBSXSIDON5ZG2YLMNR4SA5LTMUQH\nO2LUNAQEM33MM5SXEJ3TEBBXE6LTORQWY4ZO\n" \
+ "" "We've replaced the dilithium they normally use with Folger's Crystals."
+testcmd "multiline -d " "-d" \
+ "We've replaced the dilithium they normally use with Folger's Crystals." "" \
+ "K5SSO5TFEBZGK4DMMFRWKZBAORUGKIDENFWGS5DINF2W2IDUNBSXSIDON5ZG2YLMNR4SA5LTMUQH\nO2LUNAQEM33MM5SXEJ3TEBBXE6LTORQWY4ZO\n"
+
+testcmd "-w" "-w 10" \
+ "JVQXEY3INF\nXGOIDUN4QH\nI2DFEBRGKY\nLUEBXWMIDB\nEBSGSZTGMV\nZGK3TUEBVW\nK5DUNRSSA3\n3GEBTGS43I\nFY======\n" \
+ "" "Marching to the beat of a different kettle of fish."
+
+testcmd "-w0" "-w0 input" \
+ "KZUWW2LOM5ZT6ICUNBSXEZJAMFUW4J3UEBXG6IDWNFVWS3THOMQGQZLSMUXCASTVON2CA5LTEBUG63TFON2CAZTBOJWWK4TTFYQFI2DFEB2G653OEB3WC4ZAMJ2XE3TJNZTSYIDUNBSSA5TJNRWGCZ3FOJZSA53FOJSSAZDFMFSC4ICUNBSXSIDENFSG4J3UEBXGKZLEEB2GQ33TMUQHG2DFMVYCAYLOPF3WC6JOEBKGQYLUE5ZSA33VOIQHG5DPOJ4SAYLOMQQHOZJHOJSSA43UNFRWW2LOM4QHI3ZANF2C4CQ=" \
+ "Vikings? There ain't no vikings here. Just us honest farmers. The town was burning, the villagers were dead. They didn't need those sheep anyway. That's our story and we're sticking to it.\n" ""
diff --git a/tests/chmod.test b/tests/chmod.test
index cbc32805..cd4f8100 100755
--- a/tests/chmod.test
+++ b/tests/chmod.test
@@ -24,28 +24,20 @@ mkdir dir
touch file
# We don't need to test all 512 permissions
-for u in 0 1 2 3 4 5 6 7
-do
- for g in 0 3 6
- do
- for o in 0 7
- do
- if [ "$type" == file ]
- then
- type=dir
- rm -rf "./$type" && mkdir $type
- DASH=d
- else
- type=file
- rm -f "./$type" && touch $type
- DASH=-
- fi
- DASHES=$(num2perm $u$g$o)
- testing "$u$g$o $type" "chmod $u$g$o $type &&
- ls -ld $type | cut -d' ' -f 1 | cut -d. -f 1" "$DASH$DASHES\n" "" ""
- done
- done
-done
+for U in $(seq 0 7); do for G in 0 3 6; do for O in 0 7; do for T in dir file; do
+ chmod 777 $T 2>/dev/null
+ rm -rf $T
+ if [ "$T" == file ]; then
+ touch file
+ C=-
+ else
+ mkdir dir
+ C=d
+ fi
+ testing "$U$G$O $T" "chmod $U$G$O $T && ls -ld $T | cut -d' ' -f 1" \
+ "${C}$(num2perm $U$G$O)\n" "" ""
+done; done; done; done
+unset U G O T C
rm -rf dir file && mkdir dir && touch file 640
testing "750 dir 640 file" "chmod 750 dir 640 file &&
@@ -54,6 +46,7 @@ testing "750 dir 640 file" "chmod 750 dir 640 file &&
chtest()
{
+ chmod -fR 700 dir file 2>/dev/null
rm -rf dir file && mkdir dir && touch file
testing "$1 dir file" \
"chmod $1 dir file && ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" \
@@ -107,10 +100,20 @@ chtest +x "drwxr-xr-x\n-rwxr-xr-x\n"
chtest -r "d-wx--x--x\n--w-------\n"
chtest -w "dr-xr-xr-x\n-r--r--r--\n"
chtest -x "drw-r--r--\n-rw-r--r--\n"
+chtest a-w,a+x "dr-xr-xr-x\n-r-xr-xr-x\n"
+
+# macOS doesn't allow +s in /tmp
+touch s-supported
+chmod +s s-supported 2>/dev/null || SKIP=1
+rm s-supported
chtest g+s "drwxr-sr-x\n-rw-r-Sr--\n"
chtest u+s "drwsr-xr-x\n-rwSr--r--\n"
+chtest +s "drwsr-sr-x\n-rwSr-Sr--\n"
chtest o+s "drwxr-xr-x\n-rw-r--r--\n"
+unset SKIP
+
chtest +t "drwxr-xr-t\n-rw-r--r-T\n"
+chtest a=r+w+x "drwxrwxrwx\n-rwxrwxrwx\n"
mkdir foo
ln -s bar foo/baz
diff --git a/tests/find.test b/tests/find.test
index 71a55010..f4277375 100755
--- a/tests/find.test
+++ b/tests/find.test
@@ -142,5 +142,6 @@ testing "-L broken" "find -L broken" "broken\n" "" ""
testing "one slash" 'find /etc/ -maxdepth 1 | grep /passwd\$' '/etc/passwd\n' \
'' ''
+testing 'empty arg' 'find "" dir -name file 2>/dev/null' 'dir/file\n' '' ''
rm -rf dir
diff --git a/tests/gunzip.test b/tests/gunzip.test
index 9f9ef5eb..bf9b9834 100644
--- a/tests/gunzip.test
+++ b/tests/gunzip.test
@@ -21,6 +21,13 @@ testing "no files (stdin to stdout)" "cat f.gz | gunzip > f &&
test -f f.gz && cat f" "hello world\n" "" ""
rm -f f f.gz
+# test FEXTRA support
+echo "1f8b08040000000000ff04000000ffff4bcbcfe70200a865327e04000000" | xxd -r -p > f1.gz
+testing "FEXTRA flag skipped properly" "gunzip f1.gz &&
+ ! test -f f1.gz && test -f f1 &&
+ cat f1" "foo\n" "" ""
+rm -f f1 f1.gz
+
# -c Output to stdout
echo -n "foo " | gzip > f1.gz
echo "bar" | gzip > f2.gz
diff --git a/tests/id.test b/tests/id.test
index b4b13a6e..5eae928e 100755
--- a/tests/id.test
+++ b/tests/id.test
@@ -5,13 +5,15 @@
#testing "name" "command" "result" "infile" "stdin"
# Systems with SELinux will have security context cruft,
-# and BSDs call the root group "wheel" instead.
-CLEAN="sed 's/ context=.*//g' | sed 's/wheel/root/g'"
+# BSDs call the root group "wheel" instead,
+# and Raspberry Pi OS has root also in the 117(lpadmin) group.
+CLEAN="sed 's/ context=.*//g' | sed 's/wheel/root/g' | \
+sed 's/117//g' | sed -E 's/\(?lpadmin\)?//g' | sed 's/[ ,]$//'"
testing "0" "id 0 | $CLEAN" "uid=0(root) gid=0(root) groups=0(root)\n" "" ""
testing "root" "id root | $CLEAN" \
"uid=0(root) gid=0(root) groups=0(root)\n" "" ""
-testing "-G root" "id -G root" "0\n" "" ""
+testing "-G root" "id -G root | $CLEAN" "0\n" "" ""
testing "-nG root" "id -nG root | $CLEAN" "root\n" "" ""
testing "-g root" "id -g root" "0\n" "" ""
testing "-ng root" "id -ng root | $CLEAN" "root\n" "" ""
diff --git a/tests/sed.test b/tests/sed.test
index beb11d5c..c3928e58 100755
--- a/tests/sed.test
+++ b/tests/sed.test
@@ -185,6 +185,8 @@ testing '\n with empty capture' \
testing '\n too high' \
'sed -E "s/(.*)/\2/p" 2>/dev/null || echo OK' "OK\n" "" "foo"
+toyonly testing 's///x' 'sed "s/(hello )?(world)/\2/x"' "world" "" "hello world"
+
# Performance test
X=x; Y=20; while [ $Y -gt 0 ]; do X=$X$X; Y=$(($Y-1)); done
testing 'megabyte s/x/y/g (20 sec timeout)' \
diff --git a/tests/seq.test b/tests/seq.test
index 15a208bb..05d9b1e9 100755
--- a/tests/seq.test
+++ b/tests/seq.test
@@ -69,3 +69,8 @@ testing "invalid increment" "seq 1 1f 1 2>/dev/null || echo y" "y\n" "" ""
# TODO: busybox fails this too, but GNU seems to not use double for large ints.
#testing "too large for double" "seq -s, 9007199254740991 1 9007199254740992" "9007199254740992\n" "" ""
+
+testing "INT_MIN" "seq -2147483648 -2147483647" "-2147483648\n-2147483647\n"\
+ "" ""
+
+testing "fast path" "timeout 10 seq 10000000 > /dev/null" "" "" ""
diff --git a/tests/sh.test b/tests/sh.test
index 67921c1a..1d021acd 100644
--- a/tests/sh.test
+++ b/tests/sh.test
@@ -107,6 +107,9 @@ testing 'default exports' \
testing "leading assignment fail" \
"{ \$SH -c 'X=\${a?blah} > walroid';ls walroid;} 2>/dev/null" '' '' ''
+testing "lineno" "$SH input" "5 one\n6 one\n5 two\n6 two\n" \
+ '#!/bin/bash\n\nfor i in one two\ndo\n echo $LINENO $i\n echo $LINENO $i\ndone\n' ""
+
#########################################################################
# Change EVAL to call sh -c for us, using "bash" explicitly for the host.
export EVAL="$SH -c"
@@ -191,6 +194,8 @@ testing 'wildcards' 'echo w[v-x]w w[x-v]w abc/*/ghi' \
testing '$(( ) )' 'echo ab$((echo hello) | tr e x)cd' "abhxllocd\n" "" ""
testing '$((x=y)) lifetime' 'a=boing; echo $a $a$((a=4))$a $a' 'boing boing44 4\n' '' ''
+testing 'quote' "echo \"'\"" "'\n" "" ""
+
# Loops and flow control
testing "case" 'for i in A C J B; do case "$i" in A) echo got A ;; B) echo and B ;; C) echo then C ;; *) echo default ;; esac; done' \
"got A\nthen C\ndefault\nand B\n" "" ""
diff --git a/tests/test.test b/tests/test.test
index 7f574f08..1295be41 100644
--- a/tests/test.test
+++ b/tests/test.test
@@ -42,7 +42,32 @@ testing "! -e" 'type_test ! -e' "n" "" ""
rm f L s p
rmdir d
-# TODO: Test rwx gu t
+# test -rwx each bit position and failure
+touch walrus
+MASK=111
+for i in x w r k g u; do
+ [ $i == k ] && MASK=1000
+ # test everything off produces "off"
+ chmod 000 walrus
+ testcmd "-$i 0" "-$i walrus || echo yes" "yes\n" "" ""
+ chmod $((7777-$MASK)) walrus
+ testcmd "-$i inverted" "-$i walrus || echo yes" "yes\n" "" ""
+ MASK=$(($MASK<<1))
+done
+unset MASK
+# Test setuid setgid sticky enabled
+for i in uu+s gg+s k+t; do
+ chmod 000 walrus
+ chmod ${i:1}+s walrus
+ testcmd "-${i:0:1}" "-${i:0:1} walrus && echo yes" "yes\n" "" ""
+done
+# test each ugo+rwx bit position individually
+for i in 1 10 100; do for j in x w r; do
+ chmod $i walrus
+ testcmd "-$j $i" "-$j walrus && echo yes" "yes\n" "" ""
+ i=$((i<<1))
+done; done
+rm -f walrus
testcmd "" "'' || echo yes" "yes\n" "" ""
testcmd "" "a && echo yes" "yes\n" "" ""
diff --git a/tests/tr.test b/tests/tr.test
new file mode 100755
index 00000000..bb00a3ff
--- /dev/null
+++ b/tests/tr.test
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "" "tr 1 2" "223223223" "" "123123123"
+testing "-d" "tr -d 1" "232323" "" "123123123"
+testing "-s" "tr -s 1" "12223331222333" "" "111222333111222333"
+
+testing "no pathological flushing" "seq 10000000 | tr 1 2 > /dev/null" "" "" ""
diff --git a/tests/unicode.test b/tests/unicode.test
new file mode 100755
index 00000000..099231d2
--- /dev/null
+++ b/tests/unicode.test
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "text" "unicode 안녕 hi" "U+C548 : 안 : 0xec 0x95 0x88\nU+B155 : 녕 : 0xeb 0x85 0x95\nU+0068 : h\nU+0069 : i\n" "" ""
+testing "code points" "unicode 70 666" "U+0070 : p\nU+0666 : ٦ : 0xd9 0xa6\n" "" ""
+testing "ASCII controls" "unicode 0" "U+0000 : NUL\n" "" ""
+testing "del" "unicode 7f" "U+007F : DEL\n" "" ""
+testing "3-byte" "unicode 30b9" "U+30B9 : ス : 0xe3 0x82 0xb9\n" "" ""
+testing "4-byte" "unicode 10000" "U+10000 : 𐀀 : 0xf0 0x90 0x80 0x80\n" "" ""
+testing "range" "unicode 660-662" "U+0660 : ٠ : 0xd9 0xa0\nU+0661 : ١ : 0xd9 0xa1\nU+0662 : ٢ : 0xd9 0xa2\n" "" ""
diff --git a/toys.h b/toys.h
index b2e4721c..ead39b50 100644
--- a/toys.h
+++ b/toys.h
@@ -58,6 +58,7 @@
// Internationalization support (also in POSIX and LSB)
+#include <langinfo.h>
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
diff --git a/toys/example/demo_utf8towc.c b/toys/example/demo_utf8towc.c
index 25737856..c0522543 100644
--- a/toys/example/demo_utf8towc.c
+++ b/toys/example/demo_utf8towc.c
@@ -6,7 +6,6 @@ USE_DEMO_UTF8TOWC(NEWTOY(demo_utf8towc, 0, TOYFLAG_USR|TOYFLAG_BIN))
config DEMO_UTF8TOWC
bool "demo_utf8towc"
- depends on TOYBOX_I18N
default n
help
usage: demo_utf8towc
diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c
index 988466b7..beeaed3f 100644
--- a/toys/lsb/seq.c
+++ b/toys/lsb/seq.c
@@ -28,7 +28,7 @@ config SEQ
GLOBALS(
char *s, *f;
- int precision;
+ int precision, buflen;
)
// Ensure there's one %f escape with correct attributes
@@ -37,10 +37,8 @@ static void insanitize(char *f)
char *s = next_printf(f, 0);
if (!s) error_exit("bad -f no %%f");
- if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
- // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
+ if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0)))
error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
- }
}
// Parse a numeric argument setting *prec to the precision of this argument.
@@ -54,11 +52,39 @@ static double parsef(char *s)
return xstrtod(s);
}
+// fast integer conversion to decimal string
+char *itoa(char *s, int i)
+{
+ char buf[16], *ff = buf;
+ unsigned n = i;
+
+ if (i<0) {
+ *s++ = '-';
+ n = -i;
+ }
+ do *ff++ = '0'+n%10; while ((n /= 10));
+ do *s++ = *--ff; while (ff>buf);
+ *s++ = '\n';
+
+ return s;
+}
+
+char *flush_toybuf(char *ss)
+{
+ if (ss-toybuf<TT.buflen) return ss;
+
+ xwrite(1, toybuf, ss-toybuf);
+
+ return toybuf;
+}
+
void seq_main(void)
{
+ char fbuf[32], *ss;
double first = 1, increment = 1, last, dd;
- int i;
+ int ii, inc = 1, len, slen;
+ // parse arguments
if (!TT.s) TT.s = "\n";
switch (toys.optc) {
case 3: increment = parsef(toys.optargs[1]);
@@ -66,19 +92,32 @@ void seq_main(void)
default: last = parsef(toys.optargs[toys.optc-1]);
}
- // Prepare format string with appropriate precision. Can't use %g because 1e6
- if (toys.optflags & FLAG_f) insanitize(TT.f);
- else sprintf(TT.f = toybuf, "%%.%df", TT.precision);
-
- // Pad to largest width
- if (toys.optflags & FLAG_w) {
- int len = 0;
-
- for (i=0; i<3; i++) {
- dd = (double []){first, increment, last}[i];
- len = maxof(len, snprintf(0, 0, TT.f, dd));
- }
- sprintf(TT.f = toybuf, "%%0%d.%df", len, TT.precision);
+ // measure arguments
+ if (FLAG(f)) insanitize(TT.f);
+ for (ii = len = 0; ii<3; ii++) {
+ dd = (double []){first, increment, last}[ii];
+ len = maxof(len, snprintf(0, 0, "%.*f", TT.precision, fabs(dd)));
+ if (ii == 2) dd += increment;
+ slen = dd;
+ if (dd != slen) inc = 0;
+ }
+ if (!FLAG(f)) sprintf(TT.f = fbuf, "%%0%d.%df", len, TT.precision);
+ TT.buflen = sizeof(toybuf) - 32 - len - TT.precision - strlen(TT.s);
+ if (TT.buflen<0) error_exit("bad -s");
+
+ // fast path: when everything fits in an int with no flags.
+ if (!toys.optflags && inc) {
+ ii = first;
+ len = last;
+ inc = increment;
+ ss = toybuf;
+ if (inc>0) for (; ii<=len; ii += inc)
+ ss = flush_toybuf(itoa(ss, ii));
+ else if (inc<0) for (; ii>=len; ii += inc)
+ ss = flush_toybuf(itoa(ss, ii));
+ if (ss != toybuf) xwrite(1, toybuf, ss-toybuf);
+
+ return;
}
// Other implementations output nothing if increment is 0 and first > last,
@@ -86,14 +125,14 @@ void seq_main(void)
// nothing for all three, if you want endless output use "yes".
if (!increment) return;
- i = 0;
- for (;;) {
+ // Slow path, floating point and fancy sprintf() patterns
+ for (ii = 0, ss = toybuf;; ii++) {
// Multiply to avoid accumulating rounding errors from increment.
- dd = first+i*increment;
+ dd = first+ii*increment;
if ((increment<0 && dd<last) || (increment>0 && dd>last)) break;
- if (i++) printf("%s", TT.s);
- printf(TT.f, dd);
+ if (ii) ss = flush_toybuf(stpcpy(ss, TT.s));
+ ss += sprintf(ss, TT.f, dd);
}
-
- if (i) printf("\n");
+ *ss++ = '\n';
+ xwrite(1, toybuf, ss-toybuf);
}
diff --git a/toys/net/microcom.c b/toys/net/microcom.c
index 62b020f2..963445c2 100644
--- a/toys/net/microcom.c
+++ b/toys/net/microcom.c
@@ -22,30 +22,35 @@ config MICROCOM
GLOBALS(
long s;
- int fd;
- struct termios original_stdin_state, original_fd_state;
+ int fd, stok;
+ struct termios old_stdin, old_fd;
)
// TODO: tty_sigreset outputs ansi escape sequences, how to disable?
static void restore_states(int i)
{
- tcsetattr(0, TCSAFLUSH, &TT.original_stdin_state);
- tcsetattr(TT.fd, TCSAFLUSH, &TT.original_fd_state);
+ if (TT.stok) tcsetattr(0, TCSAFLUSH, &TT.old_stdin);
+ tcsetattr(TT.fd, TCSAFLUSH, &TT.old_fd);
}
void microcom_main(void)
{
+ struct termios tio;
struct pollfd fds[2];
int i;
// Open with O_NDELAY, but switch back to blocking for reads.
TT.fd = xopen(*toys.optargs, O_RDWR | O_NOCTTY | O_NDELAY);
- if (-1==(i = fcntl(TT.fd, F_GETFL, 0)) || fcntl(TT.fd, F_SETFL, i&~O_NDELAY))
+ if (-1==(i = fcntl(TT.fd, F_GETFL, 0)) || fcntl(TT.fd, F_SETFL, i&~O_NDELAY)
+ || tcgetattr(TT.fd, &TT.old_fd))
perror_exit_raw(*toys.optargs);
// Set both input and output to raw mode.
- xset_terminal(TT.fd, 1, TT.s, &TT.original_fd_state);
- set_terminal(0, 1, 0, &TT.original_stdin_state);
+ memcpy(&tio, &TT.old_fd, sizeof(struct termios));
+ cfmakeraw(&tio);
+ xsetspeed(&tio, TT.s);
+ if (tcsetattr(TT.fd, TCSAFLUSH, &tio)) perror_exit("set speed");
+ if (!set_terminal(0, 1, 0, &TT.old_stdin)) TT.stok++;
// ...and arrange to restore things, however we may exit.
sigatexit(restore_states);
diff --git a/toys/other/base64.c b/toys/other/base64.c
index ef7854a1..25e37d41 100644
--- a/toys/other/base64.c
+++ b/toys/other/base64.c
@@ -5,6 +5,7 @@
* No standard
USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE32(NEWTOY(base32, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
config BASE64
bool "base64"
@@ -17,6 +18,18 @@ config BASE64
-d Decode
-i Ignore non-alphabetic characters
-w Wrap output at COLUMNS (default 76 or 0 for no wrap)
+
+config BASE32
+ bool "base32"
+ default y
+ help
+ usage: base32 [-di] [-w COLUMNS] [FILE...]
+
+ Encode or decode in base32.
+
+ -d Decode
+ -i Ignore non-alphabetic characters
+ -w Wrap output at COLUMNS (default 76 or 0 for no wrap)
*/
#define FOR_base64
@@ -24,8 +37,9 @@ config BASE64
GLOBALS(
long w;
-
unsigned total;
+ unsigned n; // number of bits used in encoding. 5 for base32, 6 for base64
+ unsigned align; // number of bits to align to
)
static void wraputchar(int c, int *x)
@@ -38,7 +52,7 @@ static void wraputchar(int c, int *x)
};
}
-static void do_base64(int fd, char *name)
+static void do_base_n(int fd, char *name)
{
int out = 0, bits = 0, x = 0, i, len;
char *buf = toybuf+128;
@@ -49,8 +63,8 @@ static void do_base64(int fd, char *name)
// If no more data, flush buffer
if (!(len = xread(fd, buf, sizeof(toybuf)-128))) {
if (!FLAG(d)) {
- if (bits) wraputchar(toybuf[out<<(6-bits)], &x);
- while (TT.total&3) wraputchar('=', &x);
+ if (bits) wraputchar(toybuf[out<<(TT.n-bits)], &x);
+ while (TT.total&TT.align) wraputchar('=', &x);
if (x) xputc('\n');
}
@@ -62,8 +76,8 @@ static void do_base64(int fd, char *name)
if (buf[i] == '=') return;
if ((x = stridx(toybuf, buf[i])) != -1) {
- out = (out<<6) + x;
- bits += 6;
+ out = (out<<TT.n) + x;
+ bits += TT.n;
if (bits >= 8) {
putchar(out >> (bits -= 8));
out &= (1<<bits)-1;
@@ -78,8 +92,8 @@ static void do_base64(int fd, char *name)
} else {
out = (out<<8) + buf[i];
bits += 8;
- while (bits >= 6) {
- wraputchar(toybuf[out >> (bits -= 6)], &x);
+ while (bits >= TT.n) {
+ wraputchar(toybuf[out >> (bits -= TT.n)], &x);
out &= (1<<bits)-1;
}
}
@@ -89,6 +103,20 @@ static void do_base64(int fd, char *name)
void base64_main(void)
{
+ TT.n = 6;
+ TT.align = 3;
base64_init(toybuf);
- loopfiles(toys.optargs, do_base64);
+ loopfiles(toys.optargs, do_base_n);
+}
+
+#define CLEANUP_base64
+#define FOR_base32
+#include "generated/flags.h"
+
+void base32_main(void)
+{
+ TT.n = 5;
+ TT.align = 7;
+ base32_init(toybuf);
+ loopfiles(toys.optargs, do_base_n);
}
diff --git a/toys/other/count.c b/toys/other/count.c
index f3b6f821..3d388e78 100644
--- a/toys/other/count.c
+++ b/toys/other/count.c
@@ -17,16 +17,22 @@ config COUNT
void count_main(void)
{
- uint64_t size = 0;
+ struct pollfd pfd = {0, POLLIN, 0};
+ unsigned long long size = 0, last = 0, then = 0, now;
+ char *buf = xmalloc(65536);
int len;
- char buf[32];
+ // poll, print if data not ready, update 4x/second otherwise
for (;;) {
- len = xread(0, toybuf, sizeof(toybuf));
+ if (!(len = poll(&pfd, 1, (last != size) ? 250 : 0))) continue;
+ if (len<0 && errno != EINTR && errno != ENOMEM) perror_exit(0);
+ if ((len = xread(0, buf, 65536))) {
+ xwrite(1, buf, len);
+ size += len;
+ if ((now = millitime())-then<250) continue;
+ }
+ dprintf(2, "%llu bytes\r", size);
if (!len) break;
- size += len;
- xwrite(1, toybuf, len);
- xwrite(2, buf, sprintf(buf, "%"PRIu64" bytes\r", size));
}
- xwrite(2, "\n", 1);
+ dprintf(2, "\n");
}
diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c
index 3e327920..547012ef 100644
--- a/toys/other/lsattr.c
+++ b/toys/other/lsattr.c
@@ -102,7 +102,8 @@ static struct ext2_attr {
{"No_Dump", FS_NODUMP_FL, 'd'},
{"No_Atime", FS_NOATIME_FL, 'A'},
{"Compression_Requested", FS_COMPR_FL, 'c'},
- {"Encrypted", FS_ENCRYPT_FL, 'E'},
+ // FS_ENCRYPT_FL added to linux 4.5 march 2016, +y7 = 2023
+ {"Encrypted", 0x800, 'E'},
{"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'},
{"Indexed_directory", FS_INDEX_FL, 'I'},
{"No_Tailmerging", FS_NOTAIL_FL, 't'},
diff --git a/toys/other/pwgen.c b/toys/other/pwgen.c
new file mode 100644
index 00000000..c6621ccd
--- /dev/null
+++ b/toys/other/pwgen.c
@@ -0,0 +1,76 @@
+/* pwgen.c - A password generator.
+ *
+ * Copyright 2020 Moritz Rhrich <moritz@ildefons.de>
+
+USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PWGEN
+ bool "pwgen"
+ default y
+ help
+ usage: pwgen [-cAn0yrsBhC1v] [LENGTH] [COUNT]
+
+ Generate human-readable random passwords. When output is to tty produces
+ a screenfull to defeat shoulder surfing (pick one and clear the screen).
+
+ -c --capitalize Permit capital letters.
+ -A --no-capitalize Don't include capital letters.
+ -n --numerals Permit numbers.
+ -0 --no-numerals Don't include numbers.
+ -y --symbols Permit special characters ($#%...).
+ -r <chars> --remove=<chars> Don't include the given characters.
+ -s --secure Generate more random passwords.
+ -B --ambiguous Avoid ambiguous characters (e.g. 0, O).
+ -h --help Print this help message.
+ -C Print the output in columns.
+ -1 Print the output one line each.
+ -v Don't include vowels.
+*/
+
+#define FOR_pwgen
+#include "toys.h"
+
+GLOBALS(
+ char *r;
+)
+
+void pwgen_main(void)
+{
+ int length = 8, count, ii, jj, c, rand = 0, x = 0;
+ unsigned xx = 80, yy = 24;
+ char randbuf[16];
+
+ if (isatty(1)) terminal_size(&xx, &yy);
+ else toys.optflags |= FLAG_1;
+
+ if (toys.optc && (length = atolx(*toys.optargs))>sizeof(toybuf))
+ error_exit("bad length");
+ if (toys.optc>1) count = atolx(toys.optargs[1]);
+ else count = FLAG(1) ? 1 : (xx/(length+1))*(yy-1);
+
+ for (jj = 0; jj<count; jj++) {
+ for (ii = 0; ii<length;) {
+ // Don't fetch more random than necessary, give each byte 2 tries to fit
+ if (!rand) xgetrandom(randbuf, rand = sizeof(randbuf), 0);
+ c = 33+randbuf[--rand]%93; // remainder 69 makes >102 less likely
+ if (FLAG(s)) randbuf[rand] = 0;
+
+ if (c>='A' && c<='Z') {
+ if (FLAG(A)) continue;
+ // take out half the capital letters to be more human readable
+ else c |= (0x80&randbuf[rand])>>2;
+ }
+ if (FLAG(0) && c>='0' && c<='9') continue;
+ if (FLAG(B) && strchr("0O1lI'`.,", c)) continue;
+ if (FLAG(v) && strchr("aeiou", tolower(c))) continue;
+ if (!FLAG(y) || (0x80&randbuf[rand]))
+ if (c<'0' || (c>'9' && c<'A') || (c>'Z' && c<'a') || c>'z') continue;
+ if (TT.r && strchr(TT.r, c)) continue;
+
+ toybuf[ii++] = c;
+ }
+ if (FLAG(1) || (x += length+1)+length>=xx) x = 0;
+ xprintf("%.*s%c", length, toybuf, x ? ' ' : '\n');
+ }
+ if (x) xputc('\n');
+}
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 21143199..14fb55cd 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -45,6 +45,7 @@ USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK))
USE_SH(NEWTOY(exec, "^cla:", TOYFLAG_NOFORK))
USE_SH(NEWTOY(exit, 0, TOYFLAG_NOFORK))
USE_SH(NEWTOY(export, "np", TOYFLAG_NOFORK))
+USE_SH(NEWTOY(set, 0, TOYFLAG_NOFORK))
USE_SH(NEWTOY(shift, ">1", TOYFLAG_NOFORK))
USE_SH(NEWTOY(source, "0<1", TOYFLAG_NOFORK))
USE_SH(OLDTOY(., source, TOYFLAG_NOFORK))
@@ -93,6 +94,24 @@ config EXIT
Exit shell. If no return value supplied on command line, use value
of most recent command, or 0 if none.
+config SET
+ bool
+ default n
+ depends on SH
+ help
+ usage: set [+a] [+o OPTION] [VAR...]
+
+ Set variables and shell attributes. Use + to disable and - to enable.
+ NAME=VALUE arguments assign to the variable, any leftovers set $1, $2...
+ With no arguments, prints current variables.
+
+ -f NAME is a function
+ -v NAME is a variable
+ -n dereference NAME and unset that
+
+ OPTIONs:
+ history - enable command history
+
config UNSET
bool
default n
@@ -186,11 +205,10 @@ GLOBALS(
} exec;
};
- // keep lineno here: used to work around compiler limitation in run_command()
- long lineno;
+ // keep ifs here: used to work around compiler limitation in run_command()
char *ifs, *isexec, *wcpat;
- unsigned options, jobcnt;
- int hfd, pid, bangpid, varslen, shift, cdcount;
+ unsigned options, jobcnt, LINENO;
+ int hfd, pid, bangpid, varslen, cdcount;
long long SECONDS;
// global and local variables
@@ -202,8 +220,9 @@ GLOBALS(
// Parsed functions
struct sh_function {
char *name;
- struct sh_pipeline { // pipeline segments
+ struct sh_pipeline { // pipeline segments: linked list of arg w/metadata
struct sh_pipeline *next, *prev, *end;
+ unsigned lineno;
int count, here, type; // TODO abuse type to replace count during parsing
struct sh_arg {
char **v;
@@ -223,8 +242,17 @@ GLOBALS(
struct sh_arg *raw, arg;
} *pp; // currently running process
+ struct sh_callstack {
+ struct sh_callstack *next;
+ struct sh_function scratch;
+ struct sh_arg arg;
+ struct arg_list *delete;
+ unsigned lineno;
+ long shift;
+ } *cc;
+
// job list, command line for $*, scratch space for do_wildcard_files()
- struct sh_arg jobs, *arg, *wcdeck;
+ struct sh_arg jobs, *wcdeck;
)
// Can't yet avoid this prototype. Fundamental problem is $($($(blah))) nests,
@@ -237,8 +265,10 @@ static int sh_run(char *new);
static const char *redirectors[] = {"<<<", "<<-", "<<", "<&", "<>", "<", ">>",
">&", ">|", ">", "&>>", "&>", 0};
-#define OPT_BRACE 0x100 // set -B
-#define OPT_NOCLOBBER 0x200 // set -C
+// The order of these has to match the string in set_main()
+#define OPT_B 0x100
+#define OPT_C 0x200
+#define OPT_x 0x400
static void syntax_err(char *s)
{
@@ -258,7 +288,7 @@ static void arg_add(struct sh_arg *arg, char *data)
}
// add argument to an arg_list
-static char *push_arg(struct arg_list **list, char *arg)
+static void *push_arg(struct arg_list **list, void *arg)
{
struct arg_list *al;
@@ -409,7 +439,7 @@ static char *getvar(char *s)
if (c == 'S') sprintf(toybuf, "%lld", (millitime()-TT.SECONDS)/1000);
else if (c == 'R') sprintf(toybuf, "%ld", random()&((1<<16)-1));
- else if (c == 'L') sprintf(toybuf, "%ld", TT.lineno);
+ else if (c == 'L') sprintf(toybuf, "%u", TT.LINENO);
else if (c == 'G') sprintf(toybuf, "TODO: GROUPS");
return toybuf;
@@ -485,11 +515,18 @@ static char *parse_word(char *start, int early, int quote)
// Things we should only return at the _start_ of a word
- if (strstart(&end, "<(") || strstart(&end, ">(")) toybuf[quote++]=')';
-
// Redirections. 123<<file- parses as 2 args: "123<<" "file-".
s = end + redir_prefix(end);
- if ((i = anystart(s, (void *)redirectors))) return s+i;
+
+ if (strstart(&s, "<(") || strstart(&s, ">(")) {
+ toybuf[quote++]=')';
+ end = s;
+ } else if ((i = anystart(s, (void *)redirectors))) return s+i;
+
+ if (strstart(&s, "<(") || strstart(&s, ">(")) {
+ toybuf[quote++]=')';
+ end = s;
+ }
// (( is a special quote at the start of a word
if (strstart(&end, "((")) toybuf[quote++] = 254;
@@ -506,14 +543,13 @@ static char *parse_word(char *start, int early, int quote)
// Handle quote contexts
if ((q = quote ? toybuf[quote-1] : 0)) {
-
// when waiting for parentheses, they nest
if ((q == ')' || q >= 254) && (*end == '(' || *end == ')')) {
if (*end == '(') qc++;
else if (qc) qc--;
else if (q >= 254) {
// (( can end with )) or retroactively become two (( if we hit one )
- if (strstart(&end, "))")) quote--;
+ if (*end == ')' && end[1] == ')') quote--, end++;
else if (q == 254) return start+1;
else if (q == 255) toybuf[quote-1] = ')';
} else if (*end == ')') quote--;
@@ -533,7 +569,6 @@ static char *parse_word(char *start, int early, int quote)
// Things that only matter when unquoted
if (isspace(*end)) break;
- if (*end == ')') return end+(start==end);
// Flow control characters that end pipeline segments
s = end + anystart(end, (char *[]){";;&", ";;", ";&", ";", "||",
@@ -544,7 +579,7 @@ static char *parse_word(char *start, int early, int quote)
// Things the same unquoted or in most non-single-quote contexts
// start new quote context?
- if (strchr("\"'`", *end)) toybuf[quote++] = *end;
+ if (strchr("'\"`"+(q == '"'), *end)) toybuf[quote++] = *end;
// backslash escapes
else if (*end == '\\') {
@@ -757,21 +792,21 @@ char *getvar_special(char *str, int len, int *used, struct arg_list **delete)
if (cc == '-') {
s = ss = xmalloc(8);
if (TT.options&FLAG_i) *ss++ = 'i';
- if (TT.options&OPT_BRACE) *ss++ = 'B';
+ if (TT.options&OPT_B) *ss++ = 'B';
if (TT.options&FLAG_s) *ss++ = 's';
if (TT.options&FLAG_c) *ss++ = 'c';
*ss = 0;
} else if (cc == '?') s = xmprintf("%d", toys.exitval);
else if (cc == '$') s = xmprintf("%d", TT.pid);
- else if (cc == '#') s = xmprintf("%d", TT.arg->c?TT.arg->c-1:0);
+ else if (cc == '#') s = xmprintf("%d", TT.cc->arg.c?TT.cc->arg.c-1:0);
else if (cc == '!') s = xmprintf("%d"+2*!TT.bangpid, TT.bangpid);
else {
delete = 0;
for (*used = uu = 0; *used<len && isdigit(str[*used]); ++*used)
uu = (10*uu)+str[*used]-'0';
if (*used) {
- if (uu) uu += TT.shift;
- if (uu<TT.arg->c) s = TT.arg->v[uu];
+ if (uu) uu += TT.cc->shift;
+ if (uu<TT.cc->arg.c) s = TT.cc->arg.v[uu];
} else if ((*used = varend(str)-str)) return getvar(str);
}
if (s) push_arg(delete, s);
@@ -1270,7 +1305,7 @@ dprintf(2, "TODO: do math for %.*s\n", kk, s);
for (slice++, kk = 0; kk<TT.varslen; kk++)
if (!strncmp(s = TT.vars[kk].str, ss, jj))
arg_add(&aa, push_arg(delete, s = xstrndup(s, stridx(s, '='))));
- if (aa.c) push_arg(delete, (void *)aa.v);
+ if (aa.c) push_arg(delete, aa.v);
// else dereference to get new varname, discarding if none, check err
} else {
@@ -1288,8 +1323,8 @@ dprintf(2, "TODO: do math for %.*s\n", kk, s);
if (!jj) ifs = (void *)1;
else if (ifs && *(ss = ifs)) {
if (strchr("@*", cc)) {
- aa.c = TT.arg->c-1;
- aa.v = TT.arg->v+1;
+ aa.c = TT.cc->arg.c-1;
+ aa.v = TT.cc->arg.v+1;
jj = 1;
} else ifs = getvar_special(ifs, strlen(ifs), &jj, delete);
if (ss && ss[jj]) {
@@ -1312,8 +1347,8 @@ barf:
// Resolve unprefixed variables
if (strchr("{$", ss[-1])) {
if (strchr("@*", cc)) {
- aa.c = TT.arg->c-1;
- aa.v = TT.arg->v+1;
+ aa.c = TT.cc->arg.c-1;
+ aa.v = TT.cc->arg.v+1;
} else {
ifs = getvar_special(ss, jj, &jj, delete);
if (!jj) {
@@ -1600,7 +1635,7 @@ static int expand_arg(struct sh_arg *arg, char *old, unsigned flags,
char *s, *ss;
// collect brace spans
- if ((TT.options&OPT_BRACE) && !(flags&NO_BRACE)) for (i = 0; ; i++) {
+ if ((TT.options&OPT_B) && !(flags&NO_BRACE)) for (i = 0; ; i++) {
// skip quoted/escaped text
while ((s = parse_word(old+i, 1, 0)) != old+i) i += s-(old+i);
// stop at end of string if we haven't got any more open braces
@@ -1778,31 +1813,32 @@ static char *expand_one_arg(char *new, unsigned flags, struct arg_list **del)
// TODO |&
// turn a parsed pipeline back into a string.
-static char *pl2str(struct sh_pipeline *pl)
+static char *pl2str(struct sh_pipeline *pl, int one)
{
- struct sh_pipeline *end = 0;
- int level = 0, len = 0, i, j;
- char *s, *ss, *sss;
+ struct sh_pipeline *end = 0, *pp;
+ int len, i;
+ char *s, *ss;
- // measure, then allocate
- for (j = 0; ; j++) for (end = pl; end; end = end->next) {
- if (end->type == 1) level++;
- else if (end->type == 3 && --level<0) break;
+ // Find end of block (or one argument)
+ if (one) end = pl->next;
+ else for (end = pl, len = 0; end; end = end->next)
+ if (end->type == 1) len++;
+ else if (end->type == 3 && --len<0) break;
- for (i = 0; i<pl->arg->c; i++)
- if (j) ss += sprintf(ss, "%s ", pl->arg->v[i]);
- else len += strlen(pl->arg->v[i])+1;
+ // measure, then allocate
+ for (ss = 0;; ss = xmalloc(len+1)) {
+ for (pp = pl; pp != end; pp = pp->next) {
+ for (i = len = 0; i<pp->arg->c; i++)
+ len += snprintf(ss+len, ss ? INT_MAX : 0, "%s ", pp->arg->v[i]);
+ if (!(s = pp->arg->v[pp->arg->c])) s = ";"+(pp->next==end);
+ len += snprintf(ss+len, ss ? INT_MAX : 0, s);
+ }
- sss = pl->arg->v[pl->arg->c];
- if (!sss) sss = ";";
- if (j) ss = stpcpy(ss, sss);
- else len += strlen(sss);
+ if (ss) return ss;
+ }
// TODO test output with case and function
// TODO add HERE documents back in
- if (j) return s;
- s = ss = xmalloc(len+1);
- }
}
// Expand arguments and perform redirections. Return new process object with
@@ -1977,7 +2013,7 @@ notfd:
else if (strstr(ss, ">>")) from = O_CREAT|O_APPEND|O_WRONLY;
else {
from = (*ss == '<') ? O_RDONLY : O_CREAT|O_WRONLY|O_TRUNC;
- if (!strcmp(ss, ">") && (TT.options&OPT_NOCLOBBER)) {
+ if (!strcmp(ss, ">") && (TT.options&OPT_C)) {
struct stat st;
// Not _just_ O_EXCL: > /dev/null allowed
@@ -2117,7 +2153,7 @@ static struct sh_process *run_command(struct sh_arg *arg)
// "declaration does not declare anything", but if we DON'T give it a name
// it accepts it. So we can't use the union's type name here, and have
// to offsetof() the first thing _after_ the union to get the size.
- memset(&TT, 0, offsetof(struct sh_data, lineno));
+ memset(&TT, 0, offsetof(struct sh_data, ifs));
TT.pp = pp;
if (!sigsetjmp(rebound, 1)) {
@@ -2187,11 +2223,13 @@ dprintf(2, "stub add_function");
return pl->end;
}
+// Append a new pipeline to function, returning pipeline and pipeline's arg
static struct sh_pipeline *add_pl(struct sh_function *sp, struct sh_arg **arg)
{
struct sh_pipeline *pl = xzalloc(sizeof(struct sh_pipeline));
*arg = pl->arg;
+ if (TT.cc) pl->lineno = TT.cc->lineno;
dlist_add_nomalloc((void *)&sp->pipeline, (void *)pl);
return pl->end = pl;
@@ -2283,8 +2321,6 @@ static int parse_line(char *line, struct sh_function *sp)
// Parse next word and detect overflow (too many nested quotes).
if ((end = parse_word(start, 0, 0)) == (void *)1) goto flush;
-// dprintf(2, "word[%ld]=%.*s (%s)\n", end ? end-start : 0, (int)(end ? end-start : 0), start, ex);
-
// Is this a new pipeline segment?
if (!pl) pl = add_pl(sp, &arg);
@@ -2301,8 +2337,10 @@ static int parse_line(char *line, struct sh_function *sp)
// Ok, we have a word. What does it _mean_?
// case/esac parsing is weird (unbalanced parentheses!), handle first
- i = ex && !strcmp(ex, "esac") && (pl->type || (*start==';' && end-start>1));
+ i = ex && !strcmp(ex, "esac") &&
+ ((pl->type && pl->type != 3) || (*start==';' && end-start>1));
if (i) {
+
// Premature EOL in type 1 (case x\nin) or 2 (at start or after ;;) is ok
if (end == start) {
if (pl->type==128 && arg->c==2) break; // case x\nin
@@ -2642,6 +2680,7 @@ static int pipe_segments(char *ctl, int *pipes, int **urd)
return 0;
}
+// Stack of nested if/else/fi and for/do/done contexts.
struct blockstack {
struct blockstack *next;
struct sh_pipeline *start, *middle;
@@ -2683,7 +2722,7 @@ static void do_prompt(char *prompt)
if (c=='!') {
if (*prompt=='!') prompt++;
else {
- pp += snprintf(pp, len, "%ld", TT.lineno);
+ pp += snprintf(pp, len, "%u", TT.cc->lineno);
continue;
}
} else if (c=='\\') {
@@ -2737,7 +2776,9 @@ static void run_function(struct sh_pipeline *pl)
struct blockstack *blk = 0, *new;
struct sh_process *pplist = 0; // processes piping into current level
int *urd = 0, pipes[2] = {-1, -1};
- long i;
+ long i, j, k;
+
+ if (!pl) return;
// TODO: "echo | read i" is backgroundable with ctrl-Z despite read = builtin.
// probably have to inline run_command here to do that? Implicit ()
@@ -2762,11 +2803,33 @@ TODO: a | b | c needs subshell for builtins?
*s = *pl->arg->v, *ss = pl->arg->v[1];
// Skip disabled blocks, handle pipes
+ TT.LINENO = pl->lineno;
if (pl->type<2) {
if (blk && !blk->run) {
pl = pl->end->next;
continue;
}
+
+ if (TT.options&OPT_x) {
+ struct sh_callstack *sc;
+ char *ss, *ps4 = getvar("PS4");
+
+ // duplicate first char of ps4 call depth times
+ if (ps4 && *ps4) {
+ for (i = 0, sc = TT.cc; sc; sc = sc->next) i++;
+ j = getutf8(ps4, k = strlen(ps4), 0);
+ ss = xmalloc(i*j+k);
+ for (k = 0; k<i; k++) memcpy(ss+k*j, ps4, j);
+ strcpy(ss+k*j, ps4+j);
+ do_prompt(ss);
+ free(ss);
+
+ ss = pl2str(pl, 1);
+ dprintf(2, "%s\n", ss);
+ free(ss);
+ }
+ }
+
if (pipe_segments(ctl, pipes, &urd)) break;
}
@@ -2780,13 +2843,14 @@ TODO: a | b | c needs subshell for builtins?
// How many layers to peel off?
i = ss ? atol(ss) : 0;
if (i<1) i = 1;
- if (!blk || pl->arg->c>2 || ss[strspn(ss, "0123456789")]) {
+ if (!blk || pl->arg->c>2 || (ss && ss[strspn(ss, "0123456789")])) {
syntax_err(s);
break;
}
while (i && blk)
- if (!--i && *s == 'c') pl = blk->start;
+ if (blk->middle && !strcmp(*blk->middle->arg->v, "do")
+ && !--i && *s=='c') pl = blk->start;
else pl = pop_block(&blk, pipes);
if (i) {
syntax_err("break");
@@ -2838,7 +2902,7 @@ TODO: a | b | c needs subshell for builtins?
} else {
// Create new process
if (!CFG_TOYBOX_FORK) {
- ss = pl2str(pl->next);
+ ss = pl2str(pl->next, 0);
pp->pid = run_subshell(ss, strlen(ss));
free(ss);
} else if (!(pp->pid = fork())) {
@@ -2904,7 +2968,7 @@ dprintf(2, "TODO skipped init for((;;)), need math parser\n");
break;
} else vv += **vv == '(';
}
- arg.c = 0;
+ arg.c = arg2.c = 0;
if ((err = expand_arg_nobrace(&arg, *vv++, NO_SPLIT, &blk->fdelete,
&arg2))) break;
s = arg.c ? *arg.v : "";
@@ -3001,6 +3065,7 @@ static int sh_run(char *new)
struct sh_function scratch;
// TODO switch the fmemopen for -c to use this? Error checking? $(blah)
+// TODO Merge this with do_source()
memset(&scratch, 0, sizeof(struct sh_function));
if (!parse_line(new, &scratch)) run_function(scratch.pipeline);
@@ -3010,7 +3075,7 @@ static int sh_run(char *new)
return toys.exitval;
}
-// only set local variable when global not present, does not extend array
+// set variable
static struct sh_vars *initlocal(char *name, char *val)
{
return addvar(xmprintf("%s=%s", name, val ? val : ""));
@@ -3093,22 +3158,18 @@ char *prompt_getline(FILE *ff, int prompt)
// Read script input and execute lines, with or without prompts
int do_source(char *name, FILE *ff)
{
- struct sh_function scratch;
- long lineno = TT.lineno, shift = TT.shift;
- struct sh_arg arg, *old = TT.arg;
+ struct sh_callstack *cc = xzalloc(sizeof(struct sh_callstack));
int more = 0;
char *new;
- arg.c = toys.optc;
- arg.v = toys.optargs;
- TT.arg = &arg;
- TT.lineno = TT.shift = 0;
- memset(&scratch, 0, sizeof(scratch));
+ cc->next = TT.cc;
+ cc->arg.v = toys.optargs;
+ cc->arg.c = toys.optc;
+ TT.cc = cc;
- // TODO: factor out and combine with sh_main() plumbing?
do {
new = prompt_getline(ff, more+1);
- if (!TT.lineno++ && new && !memcmp(new, "\177ELF", 4)) {
+ if (!(TT.LINENO = TT.cc->lineno++) && new && !memcmp(new, "\177ELF", 4)) {
error_msg("'%s' is ELF", name);
free(new);
@@ -3119,21 +3180,22 @@ int do_source(char *name, FILE *ff)
// prints "hello" vs "hello\"
// returns 0 if line consumed, command if it needs more data
- more = parse_line(new ? : " ", &scratch);
+ more = parse_line(new ? : " ", &cc->scratch);
if (more==1) {
if (!new && !ff) syntax_err("unexpected end of file");
} else {
- if (!more) run_function(scratch.pipeline);
- free_function(&scratch);
+ if (!more) run_function(cc->scratch.pipeline);
+ free_function(&cc->scratch);
more = 0;
}
free(new);
} while(new);
if (ff) fclose(ff);
- TT.lineno = lineno;
- TT.shift = shift;
- TT.arg = old;
+ TT.cc = TT.cc->next;
+ free_function(&cc->scratch);
+ llist_traverse(cc->delete, llist_free_arg);
+ free(cc);
return more;
}
@@ -3178,6 +3240,7 @@ static void subshell_setup(void)
initlocal("BASH", s);
initlocal("PS2", "> ");
initlocal("PS3", "#? ");
+ initlocal("PS4", "+ ");
// Ensure environ copied and toys.envc set, and clean out illegal entries
TT.ifs = " \t\n";
@@ -3257,7 +3320,7 @@ void sh_main(void)
FILE *ff;
signal(SIGPIPE, SIG_IGN);
- TT.options = OPT_BRACE;
+ TT.options = OPT_B;
TT.pid = getpid();
TT.SECONDS = time(0);
@@ -3274,7 +3337,8 @@ void sh_main(void)
if (!FLAG(c)) {
if (toys.optc==1) toys.optflags |= FLAG_s;
if (FLAG(s) && isatty(0)) toys.optflags |= FLAG_i;
- } else if (toys.optc>1) {
+ }
+ if (toys.optc>1) {
toys.optargs++;
toys.optc--;
}
@@ -3301,7 +3365,7 @@ void sh_main(void)
// Read and execute lines from file
if (do_source(cc ? : *toys.optargs, ff))
- error_exit("%ld:unfinished line"+4*!TT.lineno, TT.lineno);
+ error_exit("%u:unfinished line"+3*!TT.cc->lineno, TT.cc->lineno);
}
// TODO: ./blah.sh one two three: put one two three in scratch.arg
@@ -3382,6 +3446,65 @@ void exit_main(void)
exit(*toys.optargs ? atoi(*toys.optargs) : 0);
}
+// lib/args.c can't +prefix & "+o history" needs space so parse cmdline here
+void set_main(void)
+{
+ char *cc, *ostr[] = {"braceexpand", "noclobber", "xtrace"};
+ int ii, jj, kk, oo = 0, dd = 0;
+
+ if (!*toys.optargs) {
+// TODO escape properly
+ for (ii = 0; ii<TT.varslen; ii++) printf("%s\n", TT.vars[ii].str);
+
+ return;
+ }
+
+ // Handle options
+ for (ii = 0;; ii++) {
+ if ((cc = toys.optargs[ii]) && !(dd = stridx("-+", *cc)+1) && oo--) {
+ for (jj = 0; jj<ARRAY_LEN(ostr); jj++) if (!strcmp(cc, ostr[jj])) break;
+ if (jj != ARRAY_LEN(ostr)) {
+ if (dd==1) TT.options |= OPT_B<<kk;
+ else TT.options &= ~(OPT_B<<kk);
+
+ continue;
+ }
+ error_exit("bad -o %s", cc);
+ }
+ if (oo>0) for (jj = 0; jj<ARRAY_LEN(ostr); jj++)
+ printf("%s\t%s\n", ostr[jj], TT.options&(OPT_B<<jj) ? "on" : "off");
+ oo = 0;
+ if (!cc || !dd) break;
+ for (jj = 1; cc[jj]; jj++) {
+ if (cc[jj] == 'o') oo++;
+ else if (-1 != (kk = stridx("BCx", cc[jj]))) {
+ if (*cc == '-') TT.options |= OPT_B<<kk;
+ else TT.options &= ~(OPT_B<<kk);
+ } else error_exit("bad -%c", toys.optargs[ii][1]);
+ }
+ }
+
+ // handle positional parameters
+ if (cc) {
+ struct arg_list *al, **head;
+ struct sh_arg *arg = &TT.cc->arg;
+
+ for (al = *(head = &TT.cc->delete); al; al = *(head = &al->next))
+ if (al->arg == (void *)arg->v) break;
+
+ // free last set's memory (if any) so it doesn't accumulate in loop
+ if (al) for (jj = arg->c+1; jj; jj--) {
+ *head = al->next;
+ free(al->arg);
+ free(al);
+ }
+
+ while (toys.optargs[ii])
+ arg_add(arg, push_arg(&TT.cc->delete, strdup(toys.optargs[ii++])));
+ push_arg(&TT.cc->delete, arg->v);
+ }
+}
+
void unset_main(void)
{
char **arg, *s;
@@ -3428,12 +3551,18 @@ void export_main(void)
void eval_main(void)
{
- struct sh_arg *old = TT.arg, new = {toys.argv, toys.optc+1};
+ struct sh_arg old = TT.cc->arg, *volatile arg = &TT.cc->arg;
char *s;
- TT.arg = &new;
+ // borrow the $* expand infrastructure (avoiding $* from trap handler race).
+ arg->c = 0;
+ arg->v = toys.argv;
+ arg->c = toys.optc+1;
s = expand_one_arg("\"$*\"", SEMI_IFS, 0);
- TT.arg = old;
+ arg->c = 0;
+ arg->v = old.v;
+ arg->c = old.c;
+
sh_run(s);
free(s);
}
@@ -3559,14 +3688,14 @@ void shift_main(void)
long long by = 1;
if (toys.optc) by = atolx(*toys.optargs);
- by += TT.shift;
- if (by<0 || by>=TT.arg->c) toys.exitval++;
- else TT.shift = by;
+ by += TT.cc->shift;
+ if (by<0 || by>=TT.cc->arg.c) toys.exitval++;
+ else TT.cc->shift = by;
}
void source_main(void)
{
- char *name = *toys.optargs;
+ char *name = toys.optargs[1];
FILE *ff = fpathopen(name);
if (!ff) return perror_msg_raw(name);
diff --git a/toys/pending/tr.c b/toys/pending/tr.c
index 9a823f67..e68ae464 100644
--- a/toys/pending/tr.c
+++ b/toys/pending/tr.c
@@ -210,26 +210,21 @@ save:
static void print_map(char *set1, char *set2)
{
- int r = 0, i, prev_char = -1;
+ int n, src, dst, prev = -1;
- while (1)
- {
- i = 0;
- r = read(STDIN_FILENO, (toybuf), sizeof(toybuf));
- if (!r) break;
- for (;r > i;i++) {
+ while ((n = read(0, toybuf, sizeof(toybuf)))) {
+ if (!FLAG(d) && !FLAG(s)) {
+ for (dst = 0; dst < n; dst++) toybuf[dst] = TT.map[toybuf[dst]];
+ } else {
+ for (src = dst = 0; src < n; src++) {
+ int ch = TT.map[toybuf[src]];
- if ((toys.optflags & FLAG_d) && (TT.map[(int)toybuf[i]] & 0x100)) continue;
- if (toys.optflags & FLAG_s) {
- if ((TT.map[(int)toybuf[i]] & 0x200) &&
- (prev_char == TT.map[(int)toybuf[i]])) {
- continue;
- }
+ if (FLAG(d) && (ch & 0x100)) continue;
+ if (FLAG(s) && ((ch & 0x200) && prev == ch)) continue;
+ toybuf[dst++] = prev = ch;
}
- xputc(TT.map[(int)toybuf[i]] & 0xFF);
- prev_char = TT.map[(int)toybuf[i]];
- fflush(stdout);
}
+ xwrite(1, toybuf, dst);
}
}
diff --git a/toys/pending/unicode.c b/toys/pending/unicode.c
new file mode 100644
index 00000000..0a9eb24a
--- /dev/null
+++ b/toys/pending/unicode.c
@@ -0,0 +1,65 @@
+/* unicode.c - convert between Unicode and UTF-8
+ *
+ * Copyright 2020 The Android Open Source Project.
+ *
+ * Loosely based on the Plan9/Inferno unicode(1).
+
+USE_UNICODE(NEWTOY(unicode, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNICODE
+ bool "unicode"
+ default n
+ help
+ usage: unicode [[min]-max]
+
+ Convert between Unicode code points and UTF-8, in both directions.
+*/
+
+#define FOR_unicode
+#include "toys.h"
+
+static void codepoint(unsigned wc) {
+ char *low="NULSOHSTXETXEOTENQACKBELBS HT LF VT FF CR SO SI DLEDC1DC2DC3DC4"
+ "NAKSYNETBCANEM SUBESCFS GS RS US ";
+ unsigned n, i;
+
+ printf("U+%04X : ", wc);
+ if (wc < ' ') printf("%.3s", low+(wc*3));
+ else if (wc == 0x7f) printf("DEL");
+ else {
+ toybuf[n = wctoutf8(toybuf, wc)] = 0;
+ printf("%s%s", toybuf, n>1 ? " :":"");
+ if (n>1) for (i = 0; i < n; i++) printf(" %#02x", toybuf[i]);
+ }
+ xputc('\n');
+}
+
+void unicode_main(void)
+{
+ unsigned from, to;
+ char next, **args;
+
+ for (args = toys.optargs; *args; args++) {
+ // unicode 660-666 => table of `U+0600 : ٠ : 0xd9 0xa0` etc.
+ if (sscanf(*args, "%x-%x%c", &from, &to, &next) == 2) {
+ while (from <= to) codepoint(from++);
+
+ // unicode 666 => just `U+0666 : ٦ : 0xd9 0xa6`.
+ } else if (sscanf(*args, "%x%c", &from, &next) == 1) {
+ codepoint(from);
+
+ // unicode hello => table showing every character in the string.
+ } else {
+ char *s = *args;
+ size_t l = strlen(s);
+ wchar_t wc;
+ int n;
+
+ while ((n = utf8towc(&wc, s, l)) > 0) {
+ codepoint(wc);
+ s += n;
+ l -= n;
+ }
+ }
+ }
+}
diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c
index 3645ebc8..2cdda951 100644
--- a/toys/posix/chmod.c
+++ b/toys/posix/chmod.c
@@ -19,7 +19,7 @@ config CHMOD
Stanzas are applied in order: For each category (u = user,
g = group, o = other, a = all three, if none specified default is a),
set (+), clear (-), or copy (=), r = read, w = write, x = execute.
- s = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).
+ s = u+s = suid, g+s = sgid, +t = sticky. (o+s ignored so a+s doesn't set +t)
suid/sgid: execute as the user/group who owns the file.
sticky: can't delete files you don't own out of this directory
X = x for directories or if any category already has x set.
@@ -50,7 +50,7 @@ static int do_chmod(struct dirtree *try)
// symlinks mentioned directly as arguments. We'll fail, of course,
// but that's what you asked for in that case.
} else {
- mode = string_to_mode(TT.mode, try->st.st_mode);
+ mode = string_to_mode(TT.mode, try->st.st_mode & ~S_IFMT);
if (FLAG(v)) {
char *s = dirtree_path(try, 0);
diff --git a/toys/posix/expand.c b/toys/posix/expand.c
index f1fd8d33..f3cd44d0 100644
--- a/toys/posix/expand.c
+++ b/toys/posix/expand.c
@@ -43,22 +43,18 @@ static void do_expand(int fd, char *name)
}
if (!len) break;
for (i=0; i<len; i++) {
- int width = 1;
+ wchar_t blah;
+ int width = utf8towc(&blah, toybuf+i, len-i);
char c;
- if (CFG_TOYBOX_I18N) {
- wchar_t blah;
-
- width = utf8towc(&blah, toybuf+i, len-i);
- if (width > 1) {
- if (width != fwrite(toybuf+i, width, 1, stdout))
- perror_exit("stdout");
- i += width-1;
- x++;
- continue;
- } else if (width == -2) break;
- else if (width == -1) continue;
- }
+ if (width > 1) {
+ if (width != fwrite(toybuf+i, width, 1, stdout))
+ perror_exit("stdout");
+ i += width-1;
+ x++;
+ continue;
+ } else if (width == -2) break;
+ else if (width == -1) continue;
c = toybuf[i];
if (c != '\t') {
diff --git a/toys/posix/find.c b/toys/posix/find.c
index ff127b91..2d8ca0b1 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -690,7 +690,7 @@ void find_main(void)
// Distinguish paths from filters
for (len = 0; toys.optargs[len]; len++)
- if (strchr("-!(", *toys.optargs[len])) break;
+ if (*toys.optargs[len] && strchr("-!(", *toys.optargs[len])) break;
TT.filter = toys.optargs+len;
// use "." if no paths
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index 9f445fca..fce8d564 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, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
-USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
-USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
+USE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
+USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
+USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)|TOYFLAG_LINEBUF))
config GREP
bool "grep"
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index 8fbef0cb..9bd05034 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -147,7 +147,7 @@ struct sedcmd {
int rmatch[2]; // offset of regex struct for prefix matches (/abc/,/def/p)
int arg1, arg2, w; // offset of two arguments per command, plus s//w filename
unsigned not, hit;
- unsigned sflags; // s///flag bits: i=1, g=2, p=4
+ unsigned sflags; // s///flag bits: i=1, g=2, p=4, x=8
char c; // action
};
@@ -441,7 +441,7 @@ static void sed_line(char **pline, long plen)
} else zmatch = 0;
// If we're replacing only a specific match, skip if this isn't it
- off = command->sflags>>3;
+ off = command->sflags>>4;
if (off && off != ++count) {
memcpy(l2+l2used, rline, match[0].rm_eo);
l2used += match[0].rm_eo;
@@ -793,6 +793,7 @@ static void parse_pattern(char **pline, long len)
if (!TT.nextlen--) break;
} else if (c == 's') {
char *end, delim = 0;
+ int flags;
// s/pattern/replacement/flags
@@ -845,19 +846,20 @@ resume_s:
if (isspace(*line) && *line != '\n') continue;
- if (0 <= (l = stridx("igp", *line))) command->sflags |= 1<<l;
+ if (0 <= (l = stridx("igpx", *line))) command->sflags |= 1<<l;
else if (*line == 'I') command->sflags |= 1<<0;
- else if (!(command->sflags>>3) && 0<(l = strtol(line, &line, 10))) {
- command->sflags |= l << 3;
+ else if (!(command->sflags>>4) && 0<(l = strtol(line, &line, 10))) {
+ command->sflags |= l << 4;
line--;
} else break;
}
+ flags = (FLAG(r) || (command->sflags&8)) ? REG_EXTENDED : 0;
+ if (command->sflags&1) flags |= REG_ICASE;
// We deferred actually parsing the regex until we had the s///i flag
// allocating the space was done by extend_string() above
if (!*TT.remember) command->arg1 = 0;
- else xregcomp((void *)(command->arg1 + (char *)command), TT.remember,
- (REG_EXTENDED*!!FLAG(r))|((command->sflags&1)*REG_ICASE));
+ else xregcomp((void *)(command->arg1+(char *)command),TT.remember,flags);
free(TT.remember);
TT.remember = 0;
if (*line == 'w') {
diff --git a/toys/posix/tee.c b/toys/posix/tee.c
index 43529426..88f73618 100644
--- a/toys/posix/tee.c
+++ b/toys/posix/tee.c
@@ -24,6 +24,7 @@ config TEE
GLOBALS(
void *outputs;
+ int out;
)
struct fd_list {
@@ -39,33 +40,27 @@ static void do_tee_open(int fd, char *name)
temp = xmalloc(sizeof(struct fd_list));
temp->next = TT.outputs;
- temp->fd = fd;
+ if (1 == (temp->fd = fd)) TT.out++;
TT.outputs = temp;
}
void tee_main(void)
{
+ struct fd_list *fdl;
+ int len;
+
if (FLAG(i)) xsignal(SIGINT, SIG_IGN);
- // Open output files
+ // Open output files (plus stdout if not already in output list)
loopfiles_rw(toys.optargs,
O_RDWR|O_CREAT|WARN_ONLY|(FLAG(a)?O_APPEND:O_TRUNC),
0666, do_tee_open);
+ if (!TT.out) do_tee_open(1, 0);
+ // Read data from stdin, write to each output file.
for (;;) {
- struct fd_list *fdl;
- int len, out = 0;
-
- // Read data from stdin
- len = xread(0, toybuf, sizeof(toybuf));
- if (len<1) break;
-
- // Write data to each output file, plus stdout.
- for (fdl = TT.outputs; ;fdl = fdl->next) {
- if (!fdl && out) break;
- if (len != writeall(fdl ? fdl->fd : 1, toybuf, len)) toys.exitval=1;
- if (!fdl) break;
- if (fdl->fd == 1) out++;
- }
+ if (1>(len = xread(0, toybuf, sizeof(toybuf)))) break;
+ for (fdl = TT.outputs; fdl;fdl = fdl->next)
+ if (len != writeall(fdl->fd, toybuf, len)) toys.exitval = 1;
}
}
diff --git a/toys/posix/test.c b/toys/posix/test.c
index cf6e1f50..d4bc1840 100644
--- a/toys/posix/test.c
+++ b/toys/posix/test.c
@@ -5,7 +5,7 @@
* See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
USE_TEST(NEWTOY(test, 0, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOHELP|TOYFLAG_MAYFORK))
-USE_TEST(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
+USE_SH(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
config TEST
bool "test"
@@ -20,7 +20,7 @@ config TEST
-b block device -f regular file -p fifo -u setuid bit
-c char device -g setgid -r read bit -w write bit
-d directory -h symlink -S socket -x execute bit
- -e exists -L symlink -s nonzero size
+ -e exists -L symlink -s nonzero size -k sticky bit
STRING is:
-n nonzero size -z zero size (STRING by itself implies -n)
FD (integer file descriptor) is:
@@ -66,16 +66,16 @@ int do_test(char **args, int *count)
if (*count>=2 && *s == '-' && s[1] && !s[2]) {
*count = 2;
c = s[1];
- if (-1 != (i = stridx("hLbcdefgpSusxwr", c))) {
+ if (-1 != (i = stridx("hLbcdefgkpSusxwr", c))) {
struct stat st;
// stat or lstat, then handle rwx and s
if (-1 == ((i<2) ? lstat : stat)(args[1], &st)) return 0;
- if (i>=12) return !!(st.st_mode&(0x111<<(i-12)));
+ if (i>=13) return !!(st.st_mode&(0111<<(i-13)));
if (c == 's') return !!st.st_size; // otherwise 1<<32 == 0
// handle file type checking and SUID/SGID
- if ((i = (unsigned short []){80,80,48,16,32,0,64,2,8,96,4}[i]<<9)>=4096)
+ if ((i = ((char []){80,80,48,16,32,0,64,2,1,8,96,4}[i])<<9)>=4096)
return (st.st_mode&S_IFMT) == i;
else return (st.st_mode & i) == i;
} else if (c == 'z') return !*args[1];
diff --git a/www/faq.html b/www/faq.html
index 6704cc5d..803d7b56 100755
--- a/www/faq.html
+++ b/www/faq.html
@@ -56,15 +56,15 @@ to make Android self-hosting using toybox. This helped
it to Android's attention</a>, and they
<a href=https://lwn.net/Articles/629362/>merged it</a> into Android M.</p>
-<p>The answer to the second question is "licensing". BusyBox predates Android
-by almost a decade but Android still doesn't ship with it because GPLv3 came
+<p>The unfixable problem with busybox was licensing: BusyBox predates Android
+by almost a decade, but Android still doesn't ship with it because GPLv3 came
out around the same time Android did and caused many people to throw
out the GPLv2 baby with the GPLv3 bathwater.
Android <a href=https://source.android.com/source/licenses.html>explicitly
discourages</a> use of GPL and LGPL licenses in its products, and has gradually
-reimplemented historical GPL components such as its bluetooth stack under the
-Apache license. Apple's even
-<a href=http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/>more pronounced</a> response was to freeze xcode at the last GPLv2 releases
+reimplemented historical GPL components (such as its bluetooth stack) under the
+Apache license. Apple's
+<a href=http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/>less subtle</a> response was to freeze xcode at the last GPLv2 releases
(GCC 4.2.1 with binutils 2.17) for over 5 years while sponsoring the
development of new projects (clang/llvm/lld) to replace them,
implementing a
@@ -530,7 +530,8 @@ nm and objdump.)</p>
<p>Toybox is tested against two compilers (llvm, gcc) and three C libraries
(bionic, musl, glibc) in the following combinations:</p>
-<p><u>1) gcc+glibc = host toolchain</u></p>
+<a name="cross1" />
+<p><a href="#cross1">1) gcc+glibc = host toolchain</a></p>
<p>Most Linux distros come with that as a host compiler, which is used by
default when you build normally
@@ -555,7 +556,8 @@ by a committee</a> instead of a single
maintainer, if that's an improvement. (As with Windows and
Cobol, most people deal with it and get on with their lives.)</p>
-<p><u>2) gcc+musl = musl-cross-make</u></p>
+<a name="cross2" />
+<p><a href="#cross2">2) gcc+musl = musl-cross-make</a></p>
<p>The cross compilers I test this with are built from the
<a href=http://musl.libc.org/>musl-libc</a> maintainer's
@@ -598,7 +600,8 @@ the shared libraries out of the toolchain, but I haven't bothered implementing
that in mkroot yet because a static linked musl hello world is 10k on x86
(5k if stripped).</p>
-<p><u>3) llvm+bionic = Android NDK</u></p>
+<a name="cross3" />
+<p><a href="#cross3">3) llvm+bionic = Android NDK</a></p>
<p>The <a href=https://developer.android.com/ndk/downloads>Android
Native Development Kit</a> provides an llvm toolchain with the bionic
diff --git a/www/roadmap.html b/www/roadmap.html
index ea10f116..5b7a8dca 100644
--- a/www/roadmap.html
+++ b/www/roadmap.html
@@ -1167,7 +1167,7 @@ vigr vipw, grpconv grpunconv pwconv pwunconv, chgpasswd gpasswd)</li>
<li><b>psmisc</b>: killall [fuser] [pstree] [peekfd] [prtstat]
(not: pslog pstree.x11)</li>
<li><b>inetutils</b>: dnsdomainname [ftp] hostname ifconfig ping ping6 [telnet] [tftp] [traceroute] (not: talk)</li>
-<li><b>coreutils</b>: [ base64 basename true cat chgrp chmod chown chroot cksum comm cp cut date
+<li><b>coreutils</b>: [ base64 basename cat chgrp chmod chown chroot cksum comm cp cut date
dd df dirname du echo env expand factor false fmt fold groups head hostid id install
link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc od
paste printenv printf pwd readlink realpath rm rmdir seq sha1sum shred
diff --git a/www/toycans.png b/www/toycans.png
index 1a133da4..fcd2a0e3 100644
--- a/www/toycans.png
+++ b/www/toycans.png
Binary files differ