OSDN Git Service

Merge tag 'android-7.1.2_r2' into cm-14.1 nougat-x86 android-x86-7.1-r1 android-x86-7.1-r2 android-x86-7.1-r3 android-x86-7.1-r4 android-x86-7.1-r5
authorDan Pasanen <dan.pasanen@gmail.com>
Wed, 5 Apr 2017 12:24:22 +0000 (07:24 -0500)
committerDan Pasanen <dan.pasanen@gmail.com>
Wed, 5 Apr 2017 12:24:22 +0000 (07:24 -0500)
Android 7.1.2 Release 2 (N2G47E)

# gpg: Signature made Mon 03 Apr 2017 01:41:47 AM CDT
# gpg:                using DSA key E8AD3F819AB10E78
# gpg: Can't check signature: No public key

235 files changed:
.config
Android.mk
Config.in
Makefile
README
generated/config.h
generated/flags.h
generated/globals.h
generated/help.h
generated/newtoys.h
generated/tags.h
lib/args.c
lib/dirtree.c
lib/help.c
lib/interestingtimes.c
lib/lib.c
lib/lib.h
lib/linestack.c
lib/llist.c
lib/lsm.h
lib/password.c
lib/portability.h
lib/toyflags.h
lib/xwrap.c
main.c
scripts/config2help.c
scripts/genconfig.sh
scripts/install.sh
scripts/make.sh
scripts/mkflags.c
scripts/runtest.sh
scripts/single.sh
scripts/test.sh
tests/base64.test [new file with mode: 0755]
tests/basename.test
tests/blkid.test
tests/bzcat.test
tests/cat.test
tests/chattr.test [new file with mode: 0755]
tests/chgrp.test
tests/chmod.test
tests/chown.test
tests/cksum.test
tests/cmp.test
tests/cp.test
tests/cpio.test
tests/cut.test
tests/date.test
tests/dd.test
tests/diff.test [new file with mode: 0755]
tests/dirname.test
tests/du.test
tests/echo.test
tests/expand.test
tests/expr.test
tests/factor.test
tests/file.test [new file with mode: 0644]
tests/files/blkid/cramfs.bz2 [moved from tests/blkid/cramfs.bz2 with 100% similarity]
tests/files/blkid/ext2.bz2 [moved from tests/blkid/ext2.bz2 with 100% similarity]
tests/files/blkid/ext3.bz2 [moved from tests/blkid/ext3.bz2 with 100% similarity]
tests/files/blkid/ext4.bz2 [moved from tests/blkid/ext4.bz2 with 100% similarity]
tests/files/blkid/f2fs.bz2 [moved from tests/blkid/f2fs.bz2 with 100% similarity]
tests/files/blkid/minix.bz2 [moved from tests/blkid/minix.bz2 with 100% similarity]
tests/files/blkid/msdos.bz2 [moved from tests/blkid/msdos.bz2 with 100% similarity]
tests/files/blkid/ntfs.bz2 [moved from tests/blkid/ntfs.bz2 with 100% similarity]
tests/files/blkid/reiser3.bz2 [moved from tests/blkid/reiser3.bz2 with 100% similarity]
tests/files/blkid/squashfs.bz2 [moved from tests/blkid/squashfs.bz2 with 100% similarity]
tests/files/blkid/vfat.bz2 [moved from tests/blkid/vfat.bz2 with 100% similarity]
tests/files/blkid/xfs.bz2 [moved from tests/blkid/xfs.bz2 with 100% similarity]
tests/files/bzcat/overflow.bz2 [new file with mode: 0644]
tests/find.test
tests/fstype.test [new file with mode: 0755]
tests/getfattr.test [new file with mode: 0755]
tests/grep.test
tests/groupadd.test
tests/groupdel.test
tests/head.test
tests/hostname.test
tests/ifconfig.test
tests/link.test
tests/ln.test
tests/losetup.test
tests/ls.test
tests/lsattr.test
tests/md5sum.test
tests/mkdir.test
tests/mkfifo.test
tests/modinfo.test
tests/more.test [new file with mode: 0755]
tests/mount.test
tests/mv.test
tests/nl.test
tests/pgrep.test
tests/pkill.test [new file with mode: 0755]
tests/printf.test
tests/pwd.test
tests/readlink.test
tests/renice.test
tests/rev.test
tests/rm.test
tests/rmdir.test
tests/sed.test
tests/seq.test
tests/setfattr.test [new file with mode: 0755]
tests/sh.test [new file with mode: 0755]
tests/sha1sum.test
tests/sort.test
tests/split.test
tests/tac.test
tests/tail.test
tests/tar.test
tests/test.test
tests/test_human_readable.test
tests/top.test [new file with mode: 0755]
tests/touch.test
tests/truncate.test
tests/useradd.test
tests/uudecode.test
tests/uuencode.test
tests/wc.test
tests/xargs.test
tests/xxd.test
tests/xzcat.test
tests/zcat.test
toys.h
toys/android/getprop.c
toys/android/load_policy.c
toys/android/log.c [new file with mode: 0644]
toys/android/sendevent.c [new file with mode: 0644]
toys/android/start.c [new file with mode: 0644]
toys/example/skeleton.c
toys/example/test_human_readable.c
toys/example/test_many_options.c
toys/example/test_scankey.c
toys/lsb/hostname.c
toys/lsb/md5sum.c
toys/lsb/mknod.c
toys/lsb/mount.c
toys/lsb/seq.c
toys/net/README [new file with mode: 0644]
toys/net/ifconfig.c [moved from toys/other/ifconfig.c with 100% similarity]
toys/net/netcat.c [moved from toys/other/netcat.c with 100% similarity]
toys/net/netstat.c [new file with mode: 0644]
toys/net/rfkill.c [moved from toys/other/rfkill.c with 98% similarity]
toys/net/tunctl.c [new file with mode: 0644]
toys/other/base64.c
toys/other/blkid.c
toys/other/blockdev.c
toys/other/bzcat.c
toys/other/factor.c
toys/other/fsfreeze.c
toys/other/fsync.c
toys/other/hexedit.c
toys/other/insmod.c
toys/other/losetup.c
toys/other/lspci.c
toys/other/makedevs.c
toys/other/mountpoint.c
toys/other/nsenter.c
toys/other/oneit.c
toys/other/pwdx.c
toys/other/rev.c
toys/other/setfattr.c [new file with mode: 0644]
toys/other/shred.c
toys/other/stat.c
toys/other/sysctl.c
toys/other/timeout.c
toys/other/truncate.c
toys/other/uptime.c
toys/other/xxd.c
toys/pending/arp.c
toys/pending/bootchartd.c
toys/pending/chrt.c [new file with mode: 0644]
toys/pending/crond.c
toys/pending/crontab.c
toys/pending/dd.c
toys/pending/dhcp.c
toys/pending/diff.c
toys/pending/dumpleases.c
toys/pending/expr.c
toys/pending/fdisk.c
toys/pending/file.c [deleted file]
toys/pending/getfattr.c [new file with mode: 0644]
toys/pending/getty.c
toys/pending/host.c
toys/pending/klogd.c
toys/pending/last.c
toys/pending/lsof.c
toys/pending/mdev.c
toys/pending/modprobe.c
toys/pending/more.c
toys/pending/netstat.c [deleted file]
toys/pending/openvt.c
toys/pending/resize.c [new file with mode: 0644]
toys/pending/sh.c
toys/pending/sulogin.c
toys/pending/tar.c
toys/pending/tcpsvd.c
toys/pending/tftp.c
toys/pending/traceroute.c
toys/pending/wget.c [new file with mode: 0644]
toys/posix/basename.c
toys/posix/chgrp.c
toys/posix/cmp.c
toys/posix/comm.c
toys/posix/cp.c
toys/posix/cpio.c
toys/posix/cut.c
toys/posix/du.c
toys/posix/echo.c
toys/posix/false.c
toys/posix/file.c [new file with mode: 0644]
toys/posix/find.c
toys/posix/grep.c
toys/posix/id.c
toys/posix/ls.c
toys/posix/nohup.c
toys/posix/od.c
toys/posix/patch.c
toys/posix/printf.c
toys/posix/ps.c
toys/posix/sed.c
toys/posix/tail.c
toys/posix/tee.c
toys/posix/touch.c
toys/posix/true.c
toys/posix/ulimit.c
toys/posix/uudecode.c
toys/posix/uuencode.c
toys/posix/wc.c
www/cleanup.html
www/code.html
www/design.html [changed mode: 0755->0644]
www/faq.html [new file with mode: 0755]
www/roadmap.html

diff --git a/.config b/.config
index 43760de..0459e0a 100644 (file)
--- a/.config
+++ b/.config
@@ -1,15 +1,17 @@
 #
-# Manually-generated.
+# Automatically generated make config: don't edit
 # ToyBox version: KCONFIG_VERSION
+# Wed Mar  9 02:03:44 2016
 #
 CONFIG_TOYBOX_CONTAINER=y
-CONFIG_TOYBOX_FALLOCATE=y
 CONFIG_TOYBOX_FIFREEZE=y
-CONFIG_TOYBOX_FORK=y
-CONFIG_TOYBOX_ICONV=y
-CONFIG_TOYBOX_ON_ANDROID=y
-# CONFIG_TOYBOX_SHADOW is not set
+# CONFIG_TOYBOX_ICONV is not set
+CONFIG_TOYBOX_FALLOCATE=y
 # CONFIG_TOYBOX_UTMPX is not set
+# CONFIG_TOYBOX_SHADOW is not set
+CONFIG_TOYBOX_ON_ANDROID=y
+CONFIG_TOYBOX_FORK=y
+CONFIG_TOYBOX_PRLIMIT=y
 
 #
 # Posix commands
@@ -41,7 +43,7 @@ CONFIG_ECHO=y
 CONFIG_ENV=y
 CONFIG_EXPAND=y
 CONFIG_FALSE=y
-# CONFIG_FILE is not set
+CONFIG_FILE=y
 CONFIG_FIND=y
 CONFIG_GREP=y
 CONFIG_EGREP=y
@@ -49,8 +51,6 @@ CONFIG_FGREP=y
 CONFIG_HEAD=y
 CONFIG_ID=y
 CONFIG_ID_Z=y
-CONFIG_IONICE=y
-CONFIG_IORENICE=y
 CONFIG_GROUPS=y
 CONFIG_LOGNAME=y
 CONFIG_WHOAMI=y
@@ -60,23 +60,24 @@ CONFIG_KILL=y
 CONFIG_LN=y
 CONFIG_LS=y
 CONFIG_LS_COLOR=y
-CONFIG_LS_Z=y
 CONFIG_MKDIR=y
 CONFIG_MKDIR_Z=y
 CONFIG_MKFIFO=y
 CONFIG_MKFIFO_Z=y
-CONFIG_MKNOD=y
-CONFIG_MKNOD_Z=y
 CONFIG_NICE=y
 CONFIG_NL=y
 CONFIG_NOHUP=y
-# CONFIG_NPROC is not set
 CONFIG_OD=y
 CONFIG_PASTE=y
 CONFIG_PATCH=y
 CONFIG_PRINTF=y
 CONFIG_PS=y
-# CONFIG_TTOP is not set
+CONFIG_TOP=y
+CONFIG_IOTOP=y
+CONFIG_TOP_COMMON=y
+CONFIG_PGREP=y
+CONFIG_PGKILL_COMMON=y
+CONFIG_PKILL=y
 CONFIG_PWD=y
 CONFIG_RENICE=y
 CONFIG_RM=y
@@ -91,7 +92,6 @@ CONFIG_SPLIT=y
 CONFIG_STRINGS=y
 CONFIG_TAIL=y
 CONFIG_TAIL_SEEK=y
-CONFIG_TASKSET=y
 CONFIG_TEE=y
 CONFIG_TIME=y
 CONFIG_TOUCH=y
@@ -103,7 +103,6 @@ CONFIG_UNIQ=y
 # CONFIG_UNLINK is not set
 # CONFIG_UUDECODE is not set
 # CONFIG_UUENCODE is not set
-# CONFIG_VI is not set
 CONFIG_WC=y
 # CONFIG_WHO is not set
 CONFIG_XARGS=y
@@ -112,11 +111,12 @@ CONFIG_XARGS=y
 #
 # pending (see toys/pending/README)
 #
-# CONFIG_ARP is not set
+CONFIG_ARP=y
 # CONFIG_ARPING is not set
 # CONFIG_BOOTCHARTD is not set
 # CONFIG_BRCTL is not set
 # CONFIG_COMPRESS is not set
+CONFIG_CHRT=y
 # CONFIG_GZIP is not set
 # CONFIG_GZIP_D is not set
 # CONFIG_DECOMPRESS is not set
@@ -129,22 +129,21 @@ CONFIG_DD=y
 # CONFIG_DHCP is not set
 # CONFIG_DHCPD is not set
 # CONFIG_DEBUG_DHCP is not set
-# CONFIG_DIFF is not set
+CONFIG_DIFF=y
 # CONFIG_DUMPLEASES is not set
 CONFIG_EXPR=y
-# CONFIG_FDISK is not set
+CONFIG_FDISK=y
+CONFIG_FILE=y
 # CONFIG_FOLD is not set
 # CONFIG_FSCK is not set
-# CONFIG_FTPGET is not set
+CONFIG_FTPGET=y
+CONFIG_GETFATTR=y
 # CONFIG_GETTY is not set
 # CONFIG_GROUPADD is not set
 # CONFIG_GROUPDEL is not set
-# CONFIG_HEXEDIT is not set
-# CONFIG_HOST is not set
-CONFIG_HWCLOCK=y
+CONFIG_HOST=y
 # CONFIG_ICONV is not set
 # CONFIG_INIT is not set
-# CONFIG_IOTOP is not set
 # CONFIG_IP is not set
 # CONFIG_IPCRM is not set
 # CONFIG_IPCS is not set
@@ -165,30 +164,30 @@ CONFIG_MORE=y
 CONFIG_NETSTAT=y
 # CONFIG_OPENVT is not set
 # CONFIG_DEALLOCVT is not set
-CONFIG_PGREP=y
-CONFIG_PKILL=y
 # CONFIG_PING is not set
-# CONFIG_RESET is not set
+CONFIG_RESIZE=y
 CONFIG_ROUTE=y
 # CONFIG_SH is not set
 # CONFIG_EXIT is not set
 # CONFIG_CD is not set
+CONFIG_SETFATTR=y
 # CONFIG_SULOGIN is not set
 # CONFIG_SYSLOGD is not set
 CONFIG_TAR=y
 # CONFIG_TCPSVD is not set
-# CONFIG_TELNET is not set
+CONFIG_TELNET=y
 # CONFIG_TELNETD is not set
-# CONFIG_TEST is not set
+CONFIG_TEST=y
 # CONFIG_TFTP is not set
 # CONFIG_TFTPD is not set
-CONFIG_TOP=y
 CONFIG_TRACEROUTE=y
 CONFIG_TR=y
 # CONFIG_USERADD is not set
 # CONFIG_USERDEL is not set
-# CONFIG_WATCH is not set
-# CONFIG_XZCAT is not set
+# CONFIG_VI is not set
+CONFIG_WATCH=y
+# CONFIG_WGET is not set
+CONFIG_XZCAT=y
 
 #
 # Other commands
@@ -196,9 +195,9 @@ CONFIG_TR=y
 CONFIG_ACPI=y
 CONFIG_BASE64=y
 CONFIG_BLKID=y
-# CONFIG_FSTYPE is not set
+CONFIG_FSTYPE=y
 CONFIG_BLOCKDEV=y
-CONFIG_BUNZIP2=y
+# CONFIG_BUNZIP2 is not set
 CONFIG_BZCAT=y
 CONFIG_CHCON=y
 CONFIG_CHROOT=y
@@ -217,16 +216,20 @@ CONFIG_FSFREEZE=y
 # CONFIG_FSYNC is not set
 CONFIG_HELP=y
 CONFIG_HELP_EXTRAS=y
+# CONFIG_HEXEDIT is not set
 # CONFIG_HOSTID is not set
+CONFIG_HWCLOCK=y
 CONFIG_IFCONFIG=y
 CONFIG_INOTIFYD=y
 CONFIG_INSMOD=y
+CONFIG_IONICE=y
+CONFIG_IORENICE=y
 # CONFIG_LOGIN is not set
 CONFIG_LOSETUP=y
 CONFIG_LSATTR=y
 CONFIG_CHATTR=y
 CONFIG_LSMOD=y
-# CONFIG_LSPCI is not set
+CONFIG_LSPCI=y
 CONFIG_LSPCI_TEXT=y
 CONFIG_LSUSB=y
 CONFIG_MAKEDEVS=y
@@ -238,6 +241,7 @@ CONFIG_MOUNTPOINT=y
 CONFIG_NBD_CLIENT=y
 CONFIG_NETCAT=y
 CONFIG_NETCAT_LISTEN=y
+CONFIG_NETCAT_LISTEN_TTY=y
 # CONFIG_UNSHARE is not set
 # CONFIG_NSENTER is not set
 # CONFIG_ONEIT is not set
@@ -246,10 +250,11 @@ CONFIG_PIVOT_ROOT=y
 CONFIG_PMAP=y
 CONFIG_PRINTENV=y
 CONFIG_PWDX=y
-# CONFIG_READAHEAD is not set
+CONFIG_READAHEAD=y
 CONFIG_READLINK=y
 CONFIG_REALPATH=y
 # CONFIG_REBOOT is not set
+CONFIG_RESET=y
 CONFIG_REV=y
 CONFIG_RFKILL=y
 CONFIG_RMMOD=y
@@ -258,12 +263,14 @@ CONFIG_SETSID=y
 CONFIG_STAT=y
 CONFIG_SWAPOFF=y
 CONFIG_SWAPON=y
-CONFIG_SWITCH_ROOT=y
+# CONFIG_SWITCH_ROOT is not set
 CONFIG_SYSCTL=y
 CONFIG_TAC=y
+CONFIG_NPROC=y
 CONFIG_TASKSET=y
 CONFIG_TIMEOUT=y
 CONFIG_TRUNCATE=y
+CONFIG_TUNCTL=y
 CONFIG_UPTIME=y
 CONFIG_USLEEP=y
 CONFIG_VCONFIG=y
@@ -281,10 +288,16 @@ CONFIG_HOSTNAME=y
 CONFIG_KILLALL=y
 CONFIG_MD5SUM=y
 CONFIG_SHA1SUM=y
+CONFIG_SHA224SUM=y
+CONFIG_SHA256SUM=y
+CONFIG_SHA384SUM=y
+CONFIG_SHA512SUM=y
 CONFIG_MKNOD=y
+CONFIG_MKNOD_Z=y
 CONFIG_MKTEMP=y
 CONFIG_MOUNT=y
 # CONFIG_PASSWD is not set
+# CONFIG_PASSWD_SAD is not set
 CONFIG_PIDOF=y
 CONFIG_SEQ=y
 # CONFIG_SU is not set
@@ -307,10 +320,14 @@ CONFIG_UMOUNT=y
 CONFIG_GETENFORCE=y
 CONFIG_GETPROP=y
 CONFIG_LOAD_POLICY=y
+CONFIG_LOG=y
 CONFIG_RESTORECON=y
 CONFIG_RUNCON=y
+CONFIG_SENDEVENT=y
 CONFIG_SETENFORCE=y
 CONFIG_SETPROP=y
+CONFIG_START=y
+CONFIG_STOP=y
 
 #
 # 
@@ -328,8 +345,9 @@ CONFIG_TOYBOX_FLOAT=y
 CONFIG_TOYBOX_HELP=y
 CONFIG_TOYBOX_HELP_DASHDASH=y
 CONFIG_TOYBOX_I18N=y
+CONFIG_TOYBOX_LIBCRYPTO=y
 # CONFIG_TOYBOX_FREE is not set
-# CONFIG_TOYBOX_NORECURSE is not set
+CONFIG_TOYBOX_NORECURSE=y
 # CONFIG_TOYBOX_DEBUG is not set
 CONFIG_TOYBOX_UID_SYS=100
 CONFIG_TOYBOX_UID_USR=500
index 6e8c26f..b73fc89 100644 (file)
 
 LOCAL_PATH := $(call my-dir)
 
+common_cflags := \
+    -std=c99 \
+    -Os \
+    -Wno-char-subscripts \
+    -Wno-sign-compare \
+    -Wno-string-plus-int \
+    -Wno-uninitialized \
+    -Wno-unused-parameter \
+    -funsigned-char \
+    -ffunction-sections -fdata-sections \
+    -fno-asynchronous-unwind-tables \
+
+toybox_upstream_version := $(shell grep -o 'TOYBOX_VERSION.*\".*\"' $(LOCAL_PATH)/main.c | cut -d'"' -f2)
+toybox_sha := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)
+
+toybox_version := $(toybox_upstream_version)-$(toybox_sha)-android
+common_cflags += -DTOYBOX_VERSION='"$(toybox_version)"'
+
 #
 # To update:
 #
@@ -38,8 +56,7 @@ LOCAL_PATH := $(call my-dir)
 # To add a toy:
 #
 
-#  make menuconfig
-#  # (Select the toy you want to add.)
+#  Edit .config to enable the toy you want to add.
 #  make clean && make  # Regenerate the generated files.
 #  # Edit LOCAL_SRC_FILES below to add the toy.
 #  # If you just want to use it as "toybox x" rather than "x", you can stop now.
@@ -57,16 +74,20 @@ LOCAL_SRC_FILES := \
     lib/linestack.c \
     lib/llist.c \
     lib/net.c \
+    lib/password.c \
     lib/portability.c \
     lib/xwrap.c \
     main.c \
     toys/android/getenforce.c \
     toys/android/getprop.c \
     toys/android/load_policy.c \
+    toys/android/log.c \
     toys/android/restorecon.c \
     toys/android/runcon.c \
+    toys/android/sendevent.c \
     toys/android/setenforce.c \
     toys/android/setprop.c \
+    toys/android/start.c \
     toys/lsb/dmesg.c \
     toys/lsb/hostname.c \
     toys/lsb/killall.c \
@@ -78,6 +99,11 @@ LOCAL_SRC_FILES := \
     toys/lsb/seq.c \
     toys/lsb/sync.c \
     toys/lsb/umount.c \
+    toys/net/ifconfig.c \
+    toys/net/netcat.c \
+    toys/net/netstat.c \
+    toys/net/rfkill.c \
+    toys/net/tunctl.c \
     toys/other/acpi.c \
     toys/other/base64.c \
     toys/other/blkid.c \
@@ -94,35 +120,35 @@ LOCAL_SRC_FILES := \
     toys/other/fsfreeze.c \
     toys/other/help.c \
     toys/other/hwclock.c \
-    toys/other/ifconfig.c \
     toys/other/inotifyd.c \
     toys/other/insmod.c \
     toys/other/ionice.c \
     toys/other/losetup.c \
     toys/other/lsattr.c \
     toys/other/lsmod.c \
+    toys/other/lspci.c \
     toys/other/lsusb.c \
     toys/other/makedevs.c \
     toys/other/mkswap.c \
     toys/other/modinfo.c \
     toys/other/mountpoint.c \
     toys/other/nbd_client.c \
-    toys/other/netcat.c \
     toys/other/partprobe.c \
     toys/other/pivot_root.c \
     toys/other/pmap.c \
     toys/other/printenv.c \
     toys/other/pwdx.c \
+    toys/other/readahead.c \
     toys/other/readlink.c \
     toys/other/realpath.c \
+    toys/other/reset.c \
     toys/other/rev.c \
-    toys/other/rfkill.c \
     toys/other/rmmod.c \
+    toys/other/setfattr.c \
     toys/other/setsid.c \
     toys/other/stat.c \
     toys/other/swapoff.c \
     toys/other/swapon.c \
-    toys/other/switch_root.c \
     toys/other/sysctl.c \
     toys/other/tac.c \
     toys/other/taskset.c \
@@ -135,15 +161,26 @@ LOCAL_SRC_FILES := \
     toys/other/which.c \
     toys/other/xxd.c \
     toys/other/yes.c \
+    toys/pending/arp.c \
+    toys/pending/chrt.c \
     toys/pending/dd.c \
+    toys/pending/diff.c \
     toys/pending/expr.c \
+    toys/pending/fdisk.c \
+    toys/pending/ftpget.c \
+    toys/pending/getfattr.c \
+    toys/pending/host.c \
     toys/pending/lsof.c \
     toys/pending/more.c \
-    toys/pending/netstat.c \
+    toys/pending/resize.c \
     toys/pending/route.c \
     toys/pending/tar.c \
+    toys/pending/telnet.c \
+    toys/pending/test.c \
     toys/pending/tr.c \
     toys/pending/traceroute.c \
+    toys/pending/watch.c \
+    toys/pending/xzcat.c \
     toys/posix/basename.c \
     toys/posix/cal.c \
     toys/posix/cat.c \
@@ -163,6 +200,7 @@ LOCAL_SRC_FILES := \
     toys/posix/env.c \
     toys/posix/expand.c \
     toys/posix/false.c \
+    toys/posix/file.c \
     toys/posix/find.c \
     toys/posix/grep.c \
     toys/posix/head.c \
@@ -199,175 +237,91 @@ LOCAL_SRC_FILES := \
     toys/posix/uname.c \
     toys/posix/uniq.c \
     toys/posix/wc.c \
-    toys/posix/xargs.c \
-
-LOCAL_CFLAGS += \
-    -std=c99 \
-    -Os \
-    -Wno-char-subscripts \
-    -Wno-sign-compare \
-    -Wno-string-plus-int \
-    -Wno-uninitialized \
-    -Wno-unused-parameter \
-    -funsigned-char \
-    -ffunction-sections -fdata-sections \
-    -fno-asynchronous-unwind-tables \
-
-toybox_upstream_version := $(shell awk 'match($$0, /TOYBOX_VERSION.*"(.*)"/, ary) {print ary[1]}' $(LOCAL_PATH)/main.c)
-toybox_sha := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)
-
-toybox_version := $(toybox_upstream_version)-$(toybox_sha)-android
-LOCAL_CFLAGS += -DTOYBOX_VERSION='"$(toybox_version)"'
+    toys/posix/xargs.c
 
+LOCAL_CFLAGS := $(common_cflags)
 LOCAL_CLANG := true
 
-LOCAL_SHARED_LIBRARIES := libcutils libselinux
+LOCAL_STATIC_LIBRARIES := libselinux libcrypto_static
 
 # This doesn't actually prevent us from dragging in libc++ at runtime
 # because libnetd_client.so is C++.
 LOCAL_CXX_STL := none
 
+LOCAL_C_INCLUDES += bionic/libc/dns/include
+
+LOCAL_MODULE := libtoybox
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+# Host binary to enumerate the toys
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := scripts/install.c
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CLANG := true
+LOCAL_MODULE := toybox-instlist
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := main.c
+LOCAL_STATIC_LIBRARIES := libtoybox
+LOCAL_SHARED_LIBRARIES := libcutils libselinux libcrypto
+LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CXX_STL := none
+LOCAL_CLANG := true
 LOCAL_MODULE := toybox
 
-# dupes: dd
-# useless?: freeramdisk fsfreeze install makedevs mkfifo nbd-client
-#           partprobe pivot_root pwdx rev rfkill switch_root vconfig
-# prefer BSD netcat instead?: nc netcat
-# prefer efs2progs instead?: blkid chattr lsattr
+# for dumping the list of toys
+TOYBOX_INSTLIST := $(HOST_OUT_EXECUTABLES)/toybox-instlist
+LOCAL_ADDITIONAL_DEPENDENCIES := toybox_links
 
-ALL_TOOLS := \
-    acpi \
-    base64 \
-    basename \
-    blockdev \
-    bzcat \
-    cal \
-    cat \
-    chcon \
-    chgrp \
-    chmod \
-    chown \
-    chroot \
-    cksum \
-    clear \
-    comm \
-    cmp \
-    cp \
-    cpio \
-    cut \
-    date \
-    df \
-    dirname \
-    dmesg \
-    dos2unix \
-    du \
-    echo \
-    env \
-    expand \
-    expr \
-    fallocate \
-    false \
-    find \
-    flock \
-    free \
-    getenforce \
-    getprop \
-    groups \
-    head \
-    hostname \
-    hwclock \
-    id \
-    ifconfig \
-    inotifyd \
-    insmod \
-    ionice \
-    iorenice \
-    kill \
-    killall \
-    load_policy \
-    ln \
-    logname \
-    losetup \
-    ls \
-    lsmod \
-    lsof \
-    lsusb \
-    md5sum \
-    mkdir \
-    mknod \
-    mkswap \
-    mktemp \
-    modinfo \
-    more \
-    mount \
-    mountpoint \
-    mv \
-    netstat \
-    nice \
-    nl \
-    nohup \
-    od \
-    paste \
-    patch \
-    pgrep \
-    pidof \
-    pkill \
-    pmap \
-    printenv \
-    printf \
-    pwd \
-    readlink \
-    realpath \
-    renice \
-    restorecon \
-    rm \
-    rmdir \
-    rmmod \
-    route \
-    runcon \
-    sed \
-    seq \
-    setenforce \
-    setprop \
-    setsid \
-    sha1sum \
-    sleep \
-    sort \
-    split \
-    stat \
-    strings \
-    swapoff \
-    swapon \
-    sync \
-    sysctl \
-    tac \
-    tail \
-    tar \
-    taskset \
-    tee \
-    time \
-    timeout \
-    touch \
-    tr \
-    true \
-    truncate \
-    tty \
-    ulimit \
-    umount \
-    uname \
-    uniq \
-    unix2dos \
-    uptime \
-    usleep \
-    vmstat \
-    wc \
-    which \
-    whoami \
-    xargs \
-    xxd \
-    yes \
+# we still want a link for ps, but the toolbox version needs to
+# stick around for compatibility reasons, for now.
+TOYS_FOR_XBIN := ps
 
-# Install the symlinks.
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toybox $(TARGET_OUT)/bin/$(t);)
+# skip links for these toys in the system image, they already have
+# a full-blown counterpart. we still want them for the recovery
+# image though.
+TOYS_WITHOUT_LINKS := blkid traceroute6
+
+include $(BUILD_EXECUTABLE)
 
+toybox_links: $(TOYBOX_INSTLIST)
+toybox_links: TOYBOX_BINARY := $(TARGET_OUT)/bin/toybox
+toybox_links:
+       @echo "Generate Toybox links:" $$($(TOYBOX_INSTLIST))
+       @mkdir -p $(TARGET_OUT_EXECUTABLES) $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+       $(hide) $(TOYBOX_INSTLIST) | grep -vFx -f <(tr ' ' '\n' <<< '$(TOYS_FOR_XBIN) $(TOYS_WITHOUT_LINKS)') | xargs -I'{}' ln -sf toybox '$(TARGET_OUT_EXECUTABLES)/{}'
+       $(hide) tr ' ' '\n' <<< '$(TOYS_FOR_XBIN)' | xargs -I'{}' ln -sf ../bin/toybox '$(TARGET_OUT_OPTIONAL_EXECUTABLES)/{}'
+
+
+# This is used by the recovery system
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := main.c
+LOCAL_WHOLE_STATIC_LIBRARIES := libselinux libtoybox
+LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CFLAGS += -Dmain=toybox_driver
+LOCAL_CXX_STL := none
+LOCAL_CLANG := true
+LOCAL_MODULE := libtoybox_driver
+include $(BUILD_STATIC_LIBRARY)
+
+# static executable for use in limited environments
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := main.c
+LOCAL_CFLAGS := $(common_cflags)
+LOCAL_CXX_STL := none
+LOCAL_CLANG := true
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE := toybox_static
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_STEM := toybox
+LOCAL_PACK_MODULE_RELOCATIONS := false
+LOCAL_STATIC_LIBRARIES := libc libtoybox libcutils libselinux libcrypto_static liblog
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 include $(BUILD_EXECUTABLE)
index 3c560f8..76b2ef1 100644 (file)
--- a/Config.in
+++ b/Config.in
@@ -62,6 +62,12 @@ config TOYBOX_SMACK
 
 endchoice
 
+config TOYBOX_LIBCRYPTO
+       bool "Use libcrypto (OpenSSL/BoringSSL)"
+       default n
+       help
+         Use faster hash functions out of exteral -lcrypto library.
+
 config TOYBOX_FLOAT
        bool "Floating point support"
        default y
index 677bbb0..6c5dd0d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,11 +15,11 @@ KCONFIG_CONFIG ?= .config
 
 toybox_stuff: $(KCONFIG_CONFIG) *.[ch] lib/*.[ch] toys/*.h toys/*/*.c scripts/*.sh
 
-toybox toybox_unstripped: toybox_stuff
+toybox generated/unstripped/toybox: toybox_stuff
        scripts/make.sh
 
 .PHONY: clean distclean baseline bloatcheck install install_flat \
-       uinstall uninstall_flat test tests help toybox_stuff change \
+       uinstall uninstall_flat tests help toybox_stuff change \
        list list_working list_pending
 
 include kconfig/Makefile
@@ -31,11 +31,11 @@ generated/Config.in: toys/*/*.c scripts/genconfig.sh
        scripts/genconfig.sh
 
 # Development targets
-baseline: toybox_unstripped
-       @cp toybox_unstripped toybox_old
+baseline: generated/unstripped/toybox
+       @cp generated/unstripped/toybox generated/unstripped/toybox_old
 
-bloatcheck: toybox_old toybox_unstripped
-       @scripts/bloatcheck toybox_old toybox_unstripped
+bloatcheck: toybox_old generated/unstripped/toybox
+       @scripts/bloatcheck generated/unstripped/toybox_old generated/unstripped/toybox
 
 install_flat:
        scripts/install.sh --symlink --force
@@ -53,25 +53,25 @@ change:
        scripts/change.sh
 
 clean::
-       rm -rf toybox toybox_unstripped generated change .singleconfig*
+       rm -rf toybox generated change .singleconfig*
 
+# If singlemake was in generated/ "make clean; make test_ls" wouldn't work.
 distclean: clean
        rm -f toybox_old .config* .singlemake
 
-test: tests
-
 tests:
        scripts/test.sh
 
 help::
        @echo  '  toybox          - Build toybox.'
        @echo  '  COMMANDNAME     - Build individual toybox command as a standalone binary.'
-       @echo  '  list            - List COMMANDNAMEs (also list_working and list_pending).'
+       @echo  '  list            - List COMMANDNAMEs you can build standalone.'
+       @echo  '  list_pending    - List unfinished COMMANDNAMEs out of toys/pending.'
        @echo  '  change          - Build each command standalone under change/.'
        @echo  '  baseline        - Create toybox_old for use by bloatcheck.'
        @echo  '  bloatcheck      - Report size differences between old and current versions'
-       @echo  '  test_COMMAND    - Run tests for COMMAND (test_ps, test_cat, etc.)
-       @echo  '  test            - Run test suite against all compiled commands.'
+       @echo  '  test_COMMAND    - Run tests for COMMAND (test_ps, test_cat, etc.)'
+       @echo  '  tests           - Run test suite against all compiled commands.'
        @echo  '                    export TEST_HOST=1 to test host command, VERBOSE=1'
        @echo  '                    to show diff, VERBOSE=fail to stop after first failure.'
        @echo  '  clean           - Delete temporary files.'
diff --git a/README b/README
index 5fc9ec0..5823309 100644 (file)
--- a/README
+++ b/README
@@ -10,7 +10,7 @@ The special name "." indicates the current directory (just like ".." means
 the parent directory), and you can run a program that isn't in the $PATH by
 specifying a path to it, so this should work:
 
-  wget http://landley.net/bin/toybox-x86_64
+  wget http://landley.net/toybox/bin/toybox-x86_64
   chmod +x toybox-x86_64
   ./toybox-x86_64 echo hello world
 
@@ -70,7 +70,9 @@ The "help" command provides information about each command (ala "help cat").
 
 It works like the Linux kernel: allnoconfig, defconfig, and menuconfig edit
 a ".config" file that selects which features to include in the resulting
-binary.
+binary. You can save and re-use your .config file, although may want to
+run "make oldconfig" to re-run the dependency resolver when migrating to
+new versions.
 
 The maximum sane configuration is "make defconfig": allyesconfig isn't
 recommended for toybox because it enables unfinished commands and debug code.
@@ -79,13 +81,14 @@ recommended for toybox because it enables unfinished commands and debug code.
 
 Toybox is not a complete operating system, it's a program that runs under
 an operating system. Booting a simple system to a shell prompt requires
-three packages: an operating system kernel (Linux) to drive the hardware,
-a program for the system to run (toybox), and a C library to tie them
-together (toybox has been tested with musl, uClibc, glibc, and bionic).
+three packages: an operating system kernel (Linux*) to drive the hardware,
+one or more programs for the system to run (toybox), and a C library ("libc")
+to tie them together (toybox has been tested with musl, uClibc, glibc,
+and bionic).
 
 The C library is part of a "toolchain", which is an integrated suite
 of compiler, assembler, and linker, plus the standard headers and libraries
-necessary to build C programs.
+necessary to build C programs. (And miscellaneous binaries like nm and objdump.)
 
 Static linking (with the --static option) copies the shared library contents
 into the program, resulting in larger but more portable programs, which
@@ -102,11 +105,14 @@ architectures (x86, x86-64, arm, mips, sparc, powerpc, sh4). Each toybox
 release is regression tested by building Linux From Scratch under this
 toybox-based system on each supported architecture, using QEMU to emulate
 big and little endian systems with different word size and alignment
-requirements.
+requirements. (The eventual goal is to replace Linux From Scratch with
+the Android Open Source Project.)
+
+* Or something providing the same API such as FreeBSD's Linux emulation layer.
 
 --- Presentations
 
-1) "Why Toybox?" 2013 talk here at CELF
+1) "Why Toybox?" talk at the Embedded Linux Conference in 2013
 
     video: http://youtu.be/SGmtP5Lg_t0
     outline: http://landley.net/talks/celf-2013.txt
@@ -131,7 +137,42 @@ requirements.
     video: http://elinux.org/ELC_2015_Presentations
     outline: http://landley.net/talks/celf-2015.txt
 
+--- Contributing
+
+The three important URLs for communicating with the toybox project are:
+
+  web page: http://landley.net/toybox
+
+  mailing list: http://lists.landley.net/listinfo.cgi/toybox-landley.net
+
+  git repo: http://github.com/landley/toybox
+
+The maintainer prefers patches be sent to the mailing list. If you use git,
+the easy thing to do is:
+
+  git format-patch -1 $HASH
+
+Then send a file attachment. The list holds messages from non-subscribers
+for moderation, but I usually get to them in a day or two.
+
+Although I do accept pull requests on github, I download the patches and
+apply them with "git am" (which avoids gratuitous merge commits). Closing
+the pull request is then the submitter's responsibility.
+
+If I haven't responded to your patch after one week, feel free to remind
+me of it.
+
+Android's policy for toybox patches is that non-build patches should go
+upstream first (into vanilla toybox, with discussion on the toybox mailing
+list) and then be pulled into android's toybox repo from there. (They
+generally resync on fridays). The exception is patches to their build scripts
+(Android.mk and the checked-in generated/* files) which go directly to AOSP.
+
 --- Code of conduct
 
 We're using twitter's https://engineering.twitter.com/opensource/code-of-conduct
 except email rob@landley.net with complaints.
+
+(Yes, I try to pay more attention to marginalized programmers, which somehow
+manages to include 51% of the population. If somebody has to be three times as
+good to get half the recognition, why WOULDN'T you adjust for that?)
index 5a8de80..236afcb 100644 (file)
@@ -1,19 +1,21 @@
 #define CFG_TOYBOX_CONTAINER 1
 #define USE_TOYBOX_CONTAINER(...) __VA_ARGS__
-#define CFG_TOYBOX_FALLOCATE 1
-#define USE_TOYBOX_FALLOCATE(...) __VA_ARGS__
 #define CFG_TOYBOX_FIFREEZE 1
 #define USE_TOYBOX_FIFREEZE(...) __VA_ARGS__
-#define CFG_TOYBOX_FORK 1
-#define USE_TOYBOX_FORK(...) __VA_ARGS__
-#define CFG_TOYBOX_ICONV 1
-#define USE_TOYBOX_ICONV(...) __VA_ARGS__
-#define CFG_TOYBOX_ON_ANDROID 1
-#define USE_TOYBOX_ON_ANDROID(...) __VA_ARGS__
-#define CFG_TOYBOX_SHADOW 0
-#define USE_TOYBOX_SHADOW(...)
+#define CFG_TOYBOX_ICONV 0
+#define USE_TOYBOX_ICONV(...)
+#define CFG_TOYBOX_FALLOCATE 1
+#define USE_TOYBOX_FALLOCATE(...) __VA_ARGS__
 #define CFG_TOYBOX_UTMPX 0
 #define USE_TOYBOX_UTMPX(...)
+#define CFG_TOYBOX_SHADOW 0
+#define USE_TOYBOX_SHADOW(...)
+#define CFG_TOYBOX_ON_ANDROID 1
+#define USE_TOYBOX_ON_ANDROID(...) __VA_ARGS__
+#define CFG_TOYBOX_FORK 1
+#define USE_TOYBOX_FORK(...) __VA_ARGS__
+#define CFG_TOYBOX_PRLIMIT 1
+#define USE_TOYBOX_PRLIMIT(...) __VA_ARGS__
 #define CFG_BASENAME 1
 #define USE_BASENAME(...) __VA_ARGS__
 #define CFG_CAL 1
@@ -68,8 +70,8 @@
 #define USE_EXPAND(...) __VA_ARGS__
 #define CFG_FALSE 1
 #define USE_FALSE(...) __VA_ARGS__
-#define CFG_FILE 0
-#define USE_FILE(...)
+#define CFG_FILE 1
+#define USE_FILE(...) __VA_ARGS__
 #define CFG_FIND 1
 #define USE_FIND(...) __VA_ARGS__
 #define CFG_GREP 1
 #define USE_ID(...) __VA_ARGS__
 #define CFG_ID_Z 1
 #define USE_ID_Z(...) __VA_ARGS__
-#define CFG_IONICE 1
-#define USE_IONICE(...) __VA_ARGS__
-#define CFG_IORENICE 1
-#define USE_IORENICE(...) __VA_ARGS__
 #define CFG_GROUPS 1
 #define USE_GROUPS(...) __VA_ARGS__
 #define CFG_LOGNAME 1
 #define USE_LS(...) __VA_ARGS__
 #define CFG_LS_COLOR 1
 #define USE_LS_COLOR(...) __VA_ARGS__
-#define CFG_LS_Z 1
-#define USE_LS_Z(...) __VA_ARGS__
 #define CFG_MKDIR 1
 #define USE_MKDIR(...) __VA_ARGS__
 #define CFG_MKDIR_Z 1
 #define USE_MKFIFO(...) __VA_ARGS__
 #define CFG_MKFIFO_Z 1
 #define USE_MKFIFO_Z(...) __VA_ARGS__
-#define CFG_MKNOD 1
-#define USE_MKNOD(...) __VA_ARGS__
-#define CFG_MKNOD_Z 1
-#define USE_MKNOD_Z(...) __VA_ARGS__
 #define CFG_NICE 1
 #define USE_NICE(...) __VA_ARGS__
 #define CFG_NL 1
 #define USE_NL(...) __VA_ARGS__
 #define CFG_NOHUP 1
 #define USE_NOHUP(...) __VA_ARGS__
-#define CFG_NPROC 0
-#define USE_NPROC(...)
 #define CFG_OD 1
 #define USE_OD(...) __VA_ARGS__
 #define CFG_PASTE 1
 #define USE_PRINTF(...) __VA_ARGS__
 #define CFG_PS 1
 #define USE_PS(...) __VA_ARGS__
-#define CFG_TTOP 0
-#define USE_TTOP(...)
+#define CFG_TOP 1
+#define USE_TOP(...) __VA_ARGS__
+#define CFG_IOTOP 1
+#define USE_IOTOP(...) __VA_ARGS__
+#define CFG_TOP_COMMON 1
+#define USE_TOP_COMMON(...) __VA_ARGS__
+#define CFG_PGREP 1
+#define USE_PGREP(...) __VA_ARGS__
+#define CFG_PGKILL_COMMON 1
+#define USE_PGKILL_COMMON(...) __VA_ARGS__
+#define CFG_PKILL 1
+#define USE_PKILL(...) __VA_ARGS__
 #define CFG_PWD 1
 #define USE_PWD(...) __VA_ARGS__
 #define CFG_RENICE 1
 #define USE_TAIL(...) __VA_ARGS__
 #define CFG_TAIL_SEEK 1
 #define USE_TAIL_SEEK(...) __VA_ARGS__
-#define CFG_TASKSET 1
-#define USE_TASKSET(...) __VA_ARGS__
 #define CFG_TEE 1
 #define USE_TEE(...) __VA_ARGS__
 #define CFG_TIME 1
 #define USE_UUDECODE(...)
 #define CFG_UUENCODE 0
 #define USE_UUENCODE(...)
-#define CFG_VI 0
-#define USE_VI(...)
 #define CFG_WC 1
 #define USE_WC(...) __VA_ARGS__
 #define CFG_WHO 0
 #define USE_XARGS(...) __VA_ARGS__
 #define CFG_XARGS_PEDANTIC 0
 #define USE_XARGS_PEDANTIC(...)
-#define CFG_ARP 0
-#define USE_ARP(...)
+#define CFG_ARP 1
+#define USE_ARP(...) __VA_ARGS__
 #define CFG_ARPING 0
 #define USE_ARPING(...)
 #define CFG_BOOTCHARTD 0
 #define USE_BRCTL(...)
 #define CFG_COMPRESS 0
 #define USE_COMPRESS(...)
+#define CFG_CHRT 1
+#define USE_CHRT(...) __VA_ARGS__
 #define CFG_GZIP 0
 #define USE_GZIP(...)
 #define CFG_GZIP_D 0
 #define USE_DHCPD(...)
 #define CFG_DEBUG_DHCP 0
 #define USE_DEBUG_DHCP(...)
-#define CFG_DIFF 0
-#define USE_DIFF(...)
+#define CFG_DIFF 1
+#define USE_DIFF(...) __VA_ARGS__
 #define CFG_DUMPLEASES 0
 #define USE_DUMPLEASES(...)
 #define CFG_EXPR 1
 #define USE_EXPR(...) __VA_ARGS__
-#define CFG_FDISK 0
-#define USE_FDISK(...)
+#define CFG_FDISK 1
+#define USE_FDISK(...) __VA_ARGS__
+#define CFG_FILE 1
+#define USE_FILE(...) __VA_ARGS__
 #define CFG_FOLD 0
 #define USE_FOLD(...)
 #define CFG_FSCK 0
 #define USE_FSCK(...)
-#define CFG_FTPGET 0
-#define USE_FTPGET(...)
+#define CFG_FTPGET 1
+#define USE_FTPGET(...) __VA_ARGS__
+#define CFG_GETFATTR 1
+#define USE_GETFATTR(...) __VA_ARGS__
 #define CFG_GETTY 0
 #define USE_GETTY(...)
 #define CFG_GROUPADD 0
 #define USE_GROUPADD(...)
 #define CFG_GROUPDEL 0
 #define USE_GROUPDEL(...)
-#define CFG_HEXEDIT 0
-#define USE_HEXEDIT(...)
-#define CFG_HOST 0
-#define USE_HOST(...)
-#define CFG_HWCLOCK 1
-#define USE_HWCLOCK(...) __VA_ARGS__
+#define CFG_HOST 1
+#define USE_HOST(...) __VA_ARGS__
 #define CFG_ICONV 0
 #define USE_ICONV(...)
 #define CFG_INIT 0
 #define USE_INIT(...)
-#define CFG_IOTOP 0
-#define USE_IOTOP(...)
 #define CFG_IP 0
 #define USE_IP(...)
 #define CFG_IPCRM 0
 #define USE_OPENVT(...)
 #define CFG_DEALLOCVT 0
 #define USE_DEALLOCVT(...)
-#define CFG_PGREP 1
-#define USE_PGREP(...) __VA_ARGS__
-#define CFG_PKILL 1
-#define USE_PKILL(...) __VA_ARGS__
 #define CFG_PING 0
 #define USE_PING(...)
-#define CFG_RESET 0
-#define USE_RESET(...)
+#define CFG_RESIZE 1
+#define USE_RESIZE(...) __VA_ARGS__
 #define CFG_ROUTE 1
 #define USE_ROUTE(...) __VA_ARGS__
 #define CFG_SH 0
 #define USE_EXIT(...)
 #define CFG_CD 0
 #define USE_CD(...)
+#define CFG_SETFATTR 1
+#define USE_SETFATTR(...) __VA_ARGS__
 #define CFG_SULOGIN 0
 #define USE_SULOGIN(...)
 #define CFG_SYSLOGD 0
 #define USE_TAR(...) __VA_ARGS__
 #define CFG_TCPSVD 0
 #define USE_TCPSVD(...)
-#define CFG_TELNET 0
-#define USE_TELNET(...)
+#define CFG_TELNET 1
+#define USE_TELNET(...) __VA_ARGS__
 #define CFG_TELNETD 0
 #define USE_TELNETD(...)
-#define CFG_TEST 0
-#define USE_TEST(...)
+#define CFG_TEST 1
+#define USE_TEST(...) __VA_ARGS__
 #define CFG_TFTP 0
 #define USE_TFTP(...)
 #define CFG_TFTPD 0
 #define USE_TFTPD(...)
-#define CFG_TOP 1
-#define USE_TOP(...) __VA_ARGS__
 #define CFG_TRACEROUTE 1
 #define USE_TRACEROUTE(...) __VA_ARGS__
 #define CFG_TR 1
 #define USE_USERADD(...)
 #define CFG_USERDEL 0
 #define USE_USERDEL(...)
-#define CFG_WATCH 0
-#define USE_WATCH(...)
-#define CFG_XZCAT 0
-#define USE_XZCAT(...)
+#define CFG_VI 0
+#define USE_VI(...)
+#define CFG_WATCH 1
+#define USE_WATCH(...) __VA_ARGS__
+#define CFG_WGET 0
+#define USE_WGET(...)
+#define CFG_XZCAT 1
+#define USE_XZCAT(...) __VA_ARGS__
 #define CFG_ACPI 1
 #define USE_ACPI(...) __VA_ARGS__
 #define CFG_BASE64 1
 #define USE_BASE64(...) __VA_ARGS__
 #define CFG_BLKID 1
 #define USE_BLKID(...) __VA_ARGS__
-#define CFG_FSTYPE 0
-#define USE_FSTYPE(...)
+#define CFG_FSTYPE 1
+#define USE_FSTYPE(...) __VA_ARGS__
 #define CFG_BLOCKDEV 1
 #define USE_BLOCKDEV(...) __VA_ARGS__
-#define CFG_BUNZIP2 1
-#define USE_BUNZIP2(...) __VA_ARGS__
+#define CFG_BUNZIP2 0
+#define USE_BUNZIP2(...)
 #define CFG_BZCAT 1
 #define USE_BZCAT(...) __VA_ARGS__
 #define CFG_CHCON 1
 #define USE_HELP(...) __VA_ARGS__
 #define CFG_HELP_EXTRAS 1
 #define USE_HELP_EXTRAS(...) __VA_ARGS__
+#define CFG_HEXEDIT 0
+#define USE_HEXEDIT(...)
 #define CFG_HOSTID 0
 #define USE_HOSTID(...)
+#define CFG_HWCLOCK 1
+#define USE_HWCLOCK(...) __VA_ARGS__
 #define CFG_IFCONFIG 1
 #define USE_IFCONFIG(...) __VA_ARGS__
 #define CFG_INOTIFYD 1
 #define USE_INOTIFYD(...) __VA_ARGS__
 #define CFG_INSMOD 1
 #define USE_INSMOD(...) __VA_ARGS__
+#define CFG_IONICE 1
+#define USE_IONICE(...) __VA_ARGS__
+#define CFG_IORENICE 1
+#define USE_IORENICE(...) __VA_ARGS__
 #define CFG_LOGIN 0
 #define USE_LOGIN(...)
 #define CFG_LOSETUP 1
 #define USE_CHATTR(...) __VA_ARGS__
 #define CFG_LSMOD 1
 #define USE_LSMOD(...) __VA_ARGS__
-#define CFG_LSPCI 0
-#define USE_LSPCI(...)
+#define CFG_LSPCI 1
+#define USE_LSPCI(...) __VA_ARGS__
 #define CFG_LSPCI_TEXT 1
 #define USE_LSPCI_TEXT(...) __VA_ARGS__
 #define CFG_LSUSB 1
 #define USE_NETCAT(...) __VA_ARGS__
 #define CFG_NETCAT_LISTEN 1
 #define USE_NETCAT_LISTEN(...) __VA_ARGS__
+#define CFG_NETCAT_LISTEN_TTY 1
+#define USE_NETCAT_LISTEN_TTY(...) __VA_ARGS__
 #define CFG_UNSHARE 0
 #define USE_UNSHARE(...)
 #define CFG_NSENTER 0
 #define USE_PRINTENV(...) __VA_ARGS__
 #define CFG_PWDX 1
 #define USE_PWDX(...) __VA_ARGS__
-#define CFG_READAHEAD 0
-#define USE_READAHEAD(...)
+#define CFG_READAHEAD 1
+#define USE_READAHEAD(...) __VA_ARGS__
 #define CFG_READLINK 1
 #define USE_READLINK(...) __VA_ARGS__
 #define CFG_REALPATH 1
 #define USE_REALPATH(...) __VA_ARGS__
 #define CFG_REBOOT 0
 #define USE_REBOOT(...)
+#define CFG_RESET 1
+#define USE_RESET(...) __VA_ARGS__
 #define CFG_REV 1
 #define USE_REV(...) __VA_ARGS__
 #define CFG_RFKILL 1
 #define USE_SWAPOFF(...) __VA_ARGS__
 #define CFG_SWAPON 1
 #define USE_SWAPON(...) __VA_ARGS__
-#define CFG_SWITCH_ROOT 1
-#define USE_SWITCH_ROOT(...) __VA_ARGS__
+#define CFG_SWITCH_ROOT 0
+#define USE_SWITCH_ROOT(...)
 #define CFG_SYSCTL 1
 #define USE_SYSCTL(...) __VA_ARGS__
 #define CFG_TAC 1
 #define USE_TAC(...) __VA_ARGS__
+#define CFG_NPROC 1
+#define USE_NPROC(...) __VA_ARGS__
 #define CFG_TASKSET 1
 #define USE_TASKSET(...) __VA_ARGS__
 #define CFG_TIMEOUT 1
 #define USE_TIMEOUT(...) __VA_ARGS__
 #define CFG_TRUNCATE 1
 #define USE_TRUNCATE(...) __VA_ARGS__
+#define CFG_TUNCTL 1
+#define USE_TUNCTL(...) __VA_ARGS__
 #define CFG_UPTIME 1
 #define USE_UPTIME(...) __VA_ARGS__
 #define CFG_USLEEP 1
 #define USE_MD5SUM(...) __VA_ARGS__
 #define CFG_SHA1SUM 1
 #define USE_SHA1SUM(...) __VA_ARGS__
+#define CFG_SHA224SUM 1
+#define USE_SHA224SUM(...) __VA_ARGS__
+#define CFG_SHA256SUM 1
+#define USE_SHA256SUM(...) __VA_ARGS__
+#define CFG_SHA384SUM 1
+#define USE_SHA384SUM(...) __VA_ARGS__
+#define CFG_SHA512SUM 1
+#define USE_SHA512SUM(...) __VA_ARGS__
 #define CFG_MKNOD 1
 #define USE_MKNOD(...) __VA_ARGS__
+#define CFG_MKNOD_Z 1
+#define USE_MKNOD_Z(...) __VA_ARGS__
 #define CFG_MKTEMP 1
 #define USE_MKTEMP(...) __VA_ARGS__
 #define CFG_MOUNT 1
 #define USE_MOUNT(...) __VA_ARGS__
 #define CFG_PASSWD 0
 #define USE_PASSWD(...)
+#define CFG_PASSWD_SAD 0
+#define USE_PASSWD_SAD(...)
 #define CFG_PIDOF 1
 #define USE_PIDOF(...) __VA_ARGS__
 #define CFG_SEQ 1
 #define USE_GETPROP(...) __VA_ARGS__
 #define CFG_LOAD_POLICY 1
 #define USE_LOAD_POLICY(...) __VA_ARGS__
+#define CFG_LOG 1
+#define USE_LOG(...) __VA_ARGS__
 #define CFG_RESTORECON 1
 #define USE_RESTORECON(...) __VA_ARGS__
 #define CFG_RUNCON 1
 #define USE_RUNCON(...) __VA_ARGS__
+#define CFG_SENDEVENT 1
+#define USE_SENDEVENT(...) __VA_ARGS__
 #define CFG_SETENFORCE 1
 #define USE_SETENFORCE(...) __VA_ARGS__
 #define CFG_SETPROP 1
 #define USE_SETPROP(...) __VA_ARGS__
+#define CFG_START 1
+#define USE_START(...) __VA_ARGS__
+#define CFG_STOP 1
+#define USE_STOP(...) __VA_ARGS__
 #define CFG_TOYBOX 1
 #define USE_TOYBOX(...) __VA_ARGS__
 #define CFG_TOYBOX_SUID 1
 #define USE_TOYBOX_HELP_DASHDASH(...) __VA_ARGS__
 #define CFG_TOYBOX_I18N 1
 #define USE_TOYBOX_I18N(...) __VA_ARGS__
+#define CFG_TOYBOX_LIBCRYPTO 1
+#define USE_TOYBOX_LIBCRYPTO(...) __VA_ARGS__
 #define CFG_TOYBOX_FREE 0
 #define USE_TOYBOX_FREE(...)
-#define CFG_TOYBOX_NORECURSE 0
-#define USE_TOYBOX_NORECURSE(...)
+#define CFG_TOYBOX_NORECURSE 1
+#define USE_TOYBOX_NORECURSE(...) __VA_ARGS__
 #define CFG_TOYBOX_DEBUG 0
 #define USE_TOYBOX_DEBUG(...)
 #define CFG_TOYBOX_UID_SYS 100
index a5e8659..a829388 100644 (file)
@@ -21,9 +21,9 @@
 #undef FLAG_a
 #endif
 
-// arp   vi:nDsdap:A:H:[+Ap][!sd]
+// arp vi:nDsdap:A:H:[+Ap][!sd] vi:nDsdap:A:H:[+Ap][!sd]
 #undef OPTSTR_arp
-#define OPTSTR_arp  0 
+#define OPTSTR_arp "vi:nDsdap:A:H:[+Ap][!sd]"
 #ifdef CLEANUP_arp
 #undef CLEANUP_arp
 #undef FOR_arp
@@ -41,7 +41,7 @@
 
 // arping   <1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]
 #undef OPTSTR_arping
-#define OPTSTR_arping  0 
+#define OPTSTR_arping "<1>1s:I:w#<0c#<0AUDbqf[+AU][+Df]"
 #ifdef CLEANUP_arping
 #undef CLEANUP_arping
 #undef FOR_arping
@@ -57,9 +57,9 @@
 #undef FLAG_s
 #endif
 
-// base64 diw#<1[!dw] diw#<1[!dw]
+// base64 diw#<0=76[!dw] diw#<0=76[!dw]
 #undef OPTSTR_base64
-#define OPTSTR_base64 "diw#<1[!dw]"
+#define OPTSTR_base64 "diw#<0=76[!dw]"
 #ifdef CLEANUP_base64
 #undef CLEANUP_base64
 #undef FOR_base64
@@ -78,7 +78,7 @@
 
 // blkid    
 #undef OPTSTR_blkid
-#define OPTSTR_blkid  0 
+#define OPTSTR_blkid 0
 #ifdef CLEANUP_blkid
 #undef CLEANUP_blkid
 #undef FOR_blkid
 
 // bootchartd    
 #undef OPTSTR_bootchartd
-#define OPTSTR_bootchartd  0 
+#define OPTSTR_bootchartd 0
 #ifdef CLEANUP_bootchartd
 #undef CLEANUP_bootchartd
 #undef FOR_bootchartd
 
 // brctl   <1
 #undef OPTSTR_brctl
-#define OPTSTR_brctl  0 
+#define OPTSTR_brctl "<1"
 #ifdef CLEANUP_brctl
 #undef CLEANUP_brctl
 #undef FOR_brctl
 #endif
 
-// bunzip2 cftkv cftkv
+// bunzip2   cftkv
 #undef OPTSTR_bunzip2
 #define OPTSTR_bunzip2 "cftkv"
 #ifdef CLEANUP_bunzip2
 
 // bzcat    
 #undef OPTSTR_bzcat
-#define OPTSTR_bzcat  0 
+#define OPTSTR_bzcat 0
 #ifdef CLEANUP_bzcat
 #undef CLEANUP_bzcat
 #undef FOR_bzcat
 
 // catv   vte
 #undef OPTSTR_catv
-#define OPTSTR_catv  0 
+#define OPTSTR_catv "vte"
 #ifdef CLEANUP_catv
 #undef CLEANUP_catv
 #undef FOR_catv
 
 // cd    
 #undef OPTSTR_cd
-#define OPTSTR_cd  0 
+#define OPTSTR_cd 0
 #ifdef CLEANUP_cd
 #undef CLEANUP_cd
 #undef FOR_cd
 
 // chattr    
 #undef OPTSTR_chattr
-#define OPTSTR_chattr  0 
+#define OPTSTR_chattr 0
 #ifdef CLEANUP_chattr
 #undef CLEANUP_chattr
 #undef FOR_chattr
 #undef FOR_chroot
 #endif
 
+// chrt mp#bfiorR[!bfior] mp#bfiorR[!bfior]
+#undef OPTSTR_chrt
+#define OPTSTR_chrt "mp#bfiorR[!bfior]"
+#ifdef CLEANUP_chrt
+#undef CLEANUP_chrt
+#undef FOR_chrt
+#undef FLAG_R
+#undef FLAG_r
+#undef FLAG_o
+#undef FLAG_i
+#undef FLAG_f
+#undef FLAG_b
+#undef FLAG_p
+#undef FLAG_m
+#endif
+
 // chvt   <1
 #undef OPTSTR_chvt
-#define OPTSTR_chvt  0 
+#define OPTSTR_chvt "<1"
 #ifdef CLEANUP_chvt
 #undef CLEANUP_chvt
 #undef FOR_chvt
 
 // clear    
 #undef OPTSTR_clear
-#define OPTSTR_clear  0 
+#define OPTSTR_clear 0
 #ifdef CLEANUP_clear
 #undef CLEANUP_clear
 #undef FOR_clear
 #endif
 
-// cmp <2>2ls <2>2ls
+// cmp <2>2ls[!ls] <2>2ls[!ls]
 #undef OPTSTR_cmp
-#define OPTSTR_cmp "<2>2ls"
+#define OPTSTR_cmp "<2>2ls[!ls]"
 #ifdef CLEANUP_cmp
 #undef CLEANUP_cmp
 #undef FOR_cmp
 
 // compress   zcd9lrg[-cd][!zgLr]
 #undef OPTSTR_compress
-#define OPTSTR_compress  0 
+#define OPTSTR_compress "zcd9lrg[-cd][!zgLr]"
 #ifdef CLEANUP_compress
 #undef CLEANUP_compress
 #undef FOR_compress
 
 // count    
 #undef OPTSTR_count
-#define OPTSTR_count  0 
+#define OPTSTR_count 0
 #ifdef CLEANUP_count
 #undef CLEANUP_count
 #undef FOR_count
 
 // crond   fbSl#<0=8d#<0L:c:[-bf][-LS][-ld]
 #undef OPTSTR_crond
-#define OPTSTR_crond  0 
+#define OPTSTR_crond "fbSl#<0=8d#<0L:c:[-bf][-LS][-ld]"
 #ifdef CLEANUP_crond
 #undef CLEANUP_crond
 #undef FOR_crond
 
 // crontab   c:u:elr[!elr]
 #undef OPTSTR_crontab
-#define OPTSTR_crontab  0 
+#define OPTSTR_crontab "c:u:elr[!elr]"
 #ifdef CLEANUP_crontab
 #undef CLEANUP_crontab
 #undef FOR_crontab
 
 // dd    
 #undef OPTSTR_dd
-#define OPTSTR_dd  0 
+#define OPTSTR_dd 0
 #ifdef CLEANUP_dd
 #undef CLEANUP_dd
 #undef FOR_dd
 
 // deallocvt   >1
 #undef OPTSTR_deallocvt
-#define OPTSTR_deallocvt  0 
+#define OPTSTR_deallocvt ">1"
 #ifdef CLEANUP_deallocvt
 #undef CLEANUP_deallocvt
 #undef FOR_deallocvt
 
 // dhcp   V:H:F:x*r:O*A#<0=20T#<0=3t#<0=3s:p:i:SBRCaovqnbf
 #undef OPTSTR_dhcp
-#define OPTSTR_dhcp  0 
+#define OPTSTR_dhcp "V:H:F:x*r:O*A#<0=20T#<0=3t#<0=3s:p:i:SBRCaovqnbf"
 #ifdef CLEANUP_dhcp
 #undef CLEANUP_dhcp
 #undef FOR_dhcp
 
 // dhcp6   r:A#<0T#<0t#<0s:p:i:SRvqnbf
 #undef OPTSTR_dhcp6
-#define OPTSTR_dhcp6  0 
+#define OPTSTR_dhcp6 "r:A#<0T#<0t#<0s:p:i:SRvqnbf"
 #ifdef CLEANUP_dhcp6
 #undef CLEANUP_dhcp6
 #undef FOR_dhcp6
 
 // dhcpd   >1P#<0>65535fi:S46[!46]
 #undef OPTSTR_dhcpd
-#define OPTSTR_dhcpd  0 
+#define OPTSTR_dhcpd ">1P#<0>65535fi:S46[!46]"
 #ifdef CLEANUP_dhcpd
 #undef CLEANUP_dhcpd
 #undef FOR_dhcpd
 #undef FLAG_P
 #endif
 
-// diff   <2>2B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3
+// diff <2>2B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3 <2>2B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3
 #undef OPTSTR_diff
-#define OPTSTR_diff  0 
+#define OPTSTR_diff "<2>2B(ignore-blank-lines)d(minimal)b(ignore-space-change)ut(expand-tabs)w(ignore-all-space)i(ignore-case)T(initial-tab)s(report-identical-files)q(brief)a(text)L(label)*S(starting-file):N(new-file)r(recursive)U(unified)#<0=3"
 #ifdef CLEANUP_diff
 #undef CLEANUP_diff
 #undef FOR_diff
 
 // dos2unix    
 #undef OPTSTR_dos2unix
-#define OPTSTR_dos2unix  0 
+#define OPTSTR_dos2unix 0
 #ifdef CLEANUP_dos2unix
 #undef CLEANUP_dos2unix
 #undef FOR_dos2unix
 
 // dumpleases   >0arf:[!ar]
 #undef OPTSTR_dumpleases
-#define OPTSTR_dumpleases  0 
+#define OPTSTR_dumpleases ">0arf:[!ar]"
 #ifdef CLEANUP_dumpleases
 #undef CLEANUP_dumpleases
 #undef FOR_dumpleases
 
 // eject   >1stT[!tT]
 #undef OPTSTR_eject
-#define OPTSTR_eject  0 
+#define OPTSTR_eject ">1stT[!tT]"
 #ifdef CLEANUP_eject
 #undef CLEANUP_eject
 #undef FOR_eject
 
 // exit    
 #undef OPTSTR_exit
-#define OPTSTR_exit  0 
+#define OPTSTR_exit 0
 #ifdef CLEANUP_exit
 #undef CLEANUP_exit
 #undef FOR_exit
 
 // expr    
 #undef OPTSTR_expr
-#define OPTSTR_expr  0 
+#define OPTSTR_expr 0
 #ifdef CLEANUP_expr
 #undef CLEANUP_expr
 #undef FOR_expr
 
 // factor    
 #undef OPTSTR_factor
-#define OPTSTR_factor  0 
+#define OPTSTR_factor 0
 #ifdef CLEANUP_factor
 #undef CLEANUP_factor
 #undef FOR_factor
 
 // false    
 #undef OPTSTR_false
-#define OPTSTR_false  0 
+#define OPTSTR_false 0
 #ifdef CLEANUP_false
 #undef CLEANUP_false
 #undef FOR_false
 #endif
 
-// fdisk   C#<0H#<0S#<0b#<512ul
+// fdisk C#<0H#<0S#<0b#<512ul C#<0H#<0S#<0b#<512ul
 #undef OPTSTR_fdisk
-#define OPTSTR_fdisk  0 
+#define OPTSTR_fdisk "C#<0H#<0S#<0b#<512ul"
 #ifdef CLEANUP_fdisk
 #undef CLEANUP_fdisk
 #undef FOR_fdisk
 #undef FLAG_C
 #endif
 
-// file   <1
+// file <1hL[!hL] <1hL[!hL]
 #undef OPTSTR_file
-#define OPTSTR_file  0 
+#define OPTSTR_file "<1hL[!hL]"
 #ifdef CLEANUP_file
 #undef CLEANUP_file
 #undef FOR_file
+#undef FLAG_L
+#undef FLAG_h
 #endif
 
 // find ?^HL[-HL] ?^HL[-HL]
 
 // fold   bsuw#<1
 #undef OPTSTR_fold
-#define OPTSTR_fold  0 
+#define OPTSTR_fold "bsuw#<1"
 #ifdef CLEANUP_fold
 #undef CLEANUP_fold
 #undef FOR_fold
 
 // fsck   ?t:ANPRTVsC#
 #undef OPTSTR_fsck
-#define OPTSTR_fsck  0 
+#define OPTSTR_fsck "?t:ANPRTVsC#"
 #ifdef CLEANUP_fsck
 #undef CLEANUP_fsck
 #undef FOR_fsck
 #undef FLAG_f
 #endif
 
-// fstype   <1
+// fstype <1 <1
 #undef OPTSTR_fstype
-#define OPTSTR_fstype  0 
+#define OPTSTR_fstype "<1"
 #ifdef CLEANUP_fstype
 #undef CLEANUP_fstype
 #undef FOR_fstype
 
 // fsync   <1d
 #undef OPTSTR_fsync
-#define OPTSTR_fsync  0 
+#define OPTSTR_fsync "<1d"
 #ifdef CLEANUP_fsync
 #undef CLEANUP_fsync
 #undef FOR_fsync
 #undef FLAG_d
 #endif
 
-// ftpget   <2cvu:p:P#<0=21>65535
+// ftpget <2cvu:p:P#<0=21>65535 <2cvu:p:P#<0=21>65535
 #undef OPTSTR_ftpget
-#define OPTSTR_ftpget  0 
+#define OPTSTR_ftpget "<2cvu:p:P#<0=21>65535"
 #ifdef CLEANUP_ftpget
 #undef CLEANUP_ftpget
 #undef FOR_ftpget
 #undef FOR_getenforce
 #endif
 
+// getfattr dhn: dhn:
+#undef OPTSTR_getfattr
+#define OPTSTR_getfattr "dhn:"
+#ifdef CLEANUP_getfattr
+#undef CLEANUP_getfattr
+#undef FOR_getfattr
+#undef FLAG_n
+#undef FLAG_h
+#undef FLAG_d
+#endif
+
 // getprop >2Z >2Z
 #undef OPTSTR_getprop
 #define OPTSTR_getprop ">2Z"
 
 // getty   <2t#<0H:I:l:f:iwnmLh
 #undef OPTSTR_getty
-#define OPTSTR_getty  0 
+#define OPTSTR_getty "<2t#<0H:I:l:f:iwnmLh"
 #ifdef CLEANUP_getty
 #undef CLEANUP_getty
 #undef FOR_getty
 
 // groupadd   <1>2g#<0S
 #undef OPTSTR_groupadd
-#define OPTSTR_groupadd  0 
+#define OPTSTR_groupadd "<1>2g#<0S"
 #ifdef CLEANUP_groupadd
 #undef CLEANUP_groupadd
 #undef FOR_groupadd
 
 // groupdel   <1>2
 #undef OPTSTR_groupdel
-#define OPTSTR_groupdel  0 
+#define OPTSTR_groupdel "<1>2"
 #ifdef CLEANUP_groupdel
 #undef CLEANUP_groupdel
 #undef FOR_groupdel
 
 // groups    
 #undef OPTSTR_groups
-#define OPTSTR_groups  0 
+#define OPTSTR_groups 0
 #ifdef CLEANUP_groups
 #undef CLEANUP_groups
 #undef FOR_groups
 
 // gunzip   cflqStv
 #undef OPTSTR_gunzip
-#define OPTSTR_gunzip  0 
+#define OPTSTR_gunzip "cflqStv"
 #ifdef CLEANUP_gunzip
 #undef CLEANUP_gunzip
 #undef FOR_gunzip
 
 // gzip   d19dcflqStvgLRz[!gLRz]
 #undef OPTSTR_gzip
-#define OPTSTR_gzip  0 
+#define OPTSTR_gzip "d19dcflqStvgLRz[!gLRz]"
 #ifdef CLEANUP_gzip
 #undef CLEANUP_gzip
 #undef FOR_gzip
 
 // hello    
 #undef OPTSTR_hello
-#define OPTSTR_hello  0 
+#define OPTSTR_hello 0
 #ifdef CLEANUP_hello
 #undef CLEANUP_hello
 #undef FOR_hello
 
 // hexedit   <1>1r
 #undef OPTSTR_hexedit
-#define OPTSTR_hexedit  0 
+#define OPTSTR_hexedit "<1>1r"
 #ifdef CLEANUP_hexedit
 #undef CLEANUP_hexedit
 #undef FOR_hexedit
 #undef FLAG_r
 #endif
 
-// host   <1>2avt:
+// host <1>2avt: <1>2avt:
 #undef OPTSTR_host
-#define OPTSTR_host  0 
+#define OPTSTR_host "<1>2avt:"
 #ifdef CLEANUP_host
 #undef CLEANUP_host
 #undef FOR_host
 
 // hostid   >0
 #undef OPTSTR_hostid
-#define OPTSTR_hostid  0 
+#define OPTSTR_hostid ">0"
 #ifdef CLEANUP_hostid
 #undef CLEANUP_hostid
 #undef FOR_hostid
 #endif
 
-// hostname    
+// hostname bF: bF:
 #undef OPTSTR_hostname
-#define OPTSTR_hostname  0 
+#define OPTSTR_hostname "bF:"
 #ifdef CLEANUP_hostname
 #undef CLEANUP_hostname
 #undef FOR_hostname
+#undef FLAG_F
+#undef FLAG_b
 #endif
 
 // hwclock >0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw] >0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]
 
 // iconv   cst:f:
 #undef OPTSTR_iconv
-#define OPTSTR_iconv  0 
+#define OPTSTR_iconv "cst:f:"
 #ifdef CLEANUP_iconv
 #undef CLEANUP_iconv
 #undef FOR_iconv
 
 // init    
 #undef OPTSTR_init
-#define OPTSTR_init  0 
+#define OPTSTR_init 0
 #ifdef CLEANUP_init
 #undef CLEANUP_init
 #undef FOR_init
 #undef FOR_iorenice
 #endif
 
-// iotop   >0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq
+// iotop >0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq >0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq
 #undef OPTSTR_iotop
-#define OPTSTR_iotop  0 
+#define OPTSTR_iotop ">0AaKOk*o*p*u*s#<1=7d#=3<1n#<1bq"
 #ifdef CLEANUP_iotop
 #undef CLEANUP_iotop
 #undef FOR_iotop
 
 // ip    
 #undef OPTSTR_ip
-#define OPTSTR_ip  0 
+#define OPTSTR_ip 0
 #ifdef CLEANUP_ip
 #undef CLEANUP_ip
 #undef FOR_ip
 
 // ipcrm   m*M*s*S*q*Q*
 #undef OPTSTR_ipcrm
-#define OPTSTR_ipcrm  0 
+#define OPTSTR_ipcrm "m*M*s*S*q*Q*"
 #ifdef CLEANUP_ipcrm
 #undef CLEANUP_ipcrm
 #undef FOR_ipcrm
 
 // ipcs   acptulsqmi#
 #undef OPTSTR_ipcs
-#define OPTSTR_ipcs  0 
+#define OPTSTR_ipcs "acptulsqmi#"
 #ifdef CLEANUP_ipcs
 #undef CLEANUP_ipcs
 #undef FOR_ipcs
 
 // killall5   ?o*ls: [!lo][!ls]
 #undef OPTSTR_killall5
-#define OPTSTR_killall5  0 
+#define OPTSTR_killall5 "?o*ls: [!lo][!ls]"
 #ifdef CLEANUP_killall5
 #undef CLEANUP_killall5
 #undef FOR_killall5
 
 // klogd   c#<1>8n
 #undef OPTSTR_klogd
-#define OPTSTR_klogd  0 
+#define OPTSTR_klogd "c#<1>8n"
 #ifdef CLEANUP_klogd
 #undef CLEANUP_klogd
 #undef FOR_klogd
 
 // last   f:W
 #undef OPTSTR_last
-#define OPTSTR_last  0 
+#define OPTSTR_last "f:W"
 #ifdef CLEANUP_last
 #undef CLEANUP_last
 #undef FOR_last
 
 // link   <2>2
 #undef OPTSTR_link
-#define OPTSTR_link  0 
+#define OPTSTR_link "<2>2"
 #ifdef CLEANUP_link
 #undef CLEANUP_link
 #undef FOR_link
 #undef FOR_load_policy
 #endif
 
+// log <1p:t: <1p:t:
+#undef OPTSTR_log
+#define OPTSTR_log "<1p:t:"
+#ifdef CLEANUP_log
+#undef CLEANUP_log
+#undef FOR_log
+#undef FLAG_t
+#undef FLAG_p
+#endif
+
 // logger   st:p:
 #undef OPTSTR_logger
-#define OPTSTR_logger  0 
+#define OPTSTR_logger "st:p:"
 #ifdef CLEANUP_logger
 #undef CLEANUP_logger
 #undef FOR_logger
 
 // login   >1f:ph:
 #undef OPTSTR_login
-#define OPTSTR_login  0 
+#define OPTSTR_login ">1f:ph:"
 #ifdef CLEANUP_login
 #undef CLEANUP_login
 #undef FOR_login
 #undef FLAG_S
 #endif
 
-// ls (color):;ZgoACFHLRSacdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL] (color):;ZgoACFHLRSacdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]
+// ls (color):;ZgoACFHLRSabcdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb] (color):;ZgoACFHLRSabcdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]
 #undef OPTSTR_ls
-#define OPTSTR_ls "(color):;ZgoACFHLRSacdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]"
+#define OPTSTR_ls "(color):;ZgoACFHLRSabcdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]"
 #ifdef CLEANUP_ls
 #undef CLEANUP_ls
 #undef FOR_ls
 #undef FLAG_f
 #undef FLAG_d
 #undef FLAG_c
+#undef FLAG_b
 #undef FLAG_a
 #undef FLAG_S
 #undef FLAG_R
 
 // lsmod    
 #undef OPTSTR_lsmod
-#define OPTSTR_lsmod  0 
+#define OPTSTR_lsmod 0
 #ifdef CLEANUP_lsmod
 #undef CLEANUP_lsmod
 #undef FOR_lsmod
 #endif
 
-// lsof lp:t lp:t
+// lsof lp*t lp*t
 #undef OPTSTR_lsof
-#define OPTSTR_lsof "lp:t"
+#define OPTSTR_lsof "lp*t"
 #ifdef CLEANUP_lsof
 #undef CLEANUP_lsof
 #undef FOR_lsof
 #undef FLAG_l
 #endif
 
-// lspci   emkn@i:
+// lspci emkn@i: emkn@i:
 #undef OPTSTR_lspci
-#define OPTSTR_lspci  0 
+#define OPTSTR_lspci "emkn@i:"
 #ifdef CLEANUP_lspci
 #undef CLEANUP_lspci
 #undef FOR_lspci
 
 // lsusb    
 #undef OPTSTR_lsusb
-#define OPTSTR_lsusb  0 
+#define OPTSTR_lsusb 0
 #ifdef CLEANUP_lsusb
 #undef CLEANUP_lsusb
 #undef FOR_lsusb
 #undef FLAG_d
 #endif
 
-// md5sum b b
+// md5sum bc*[!bc] bc*[!bc]
 #undef OPTSTR_md5sum
-#define OPTSTR_md5sum "b"
+#define OPTSTR_md5sum "bc*[!bc]"
 #ifdef CLEANUP_md5sum
 #undef CLEANUP_md5sum
 #undef FOR_md5sum
+#undef FLAG_c
 #undef FLAG_b
 #endif
 
 // mdev   s
 #undef OPTSTR_mdev
-#define OPTSTR_mdev  0 
+#define OPTSTR_mdev "s"
 #ifdef CLEANUP_mdev
 #undef CLEANUP_mdev
 #undef FOR_mdev
 
 // mix   c:d:l#r#
 #undef OPTSTR_mix
-#define OPTSTR_mix  0 
+#define OPTSTR_mix "c:d:l#r#"
 #ifdef CLEANUP_mix
 #undef CLEANUP_mix
 #undef FOR_mix
 
 // mke2fs   <1>2g:Fnqm#N#i#b#
 #undef OPTSTR_mke2fs
-#define OPTSTR_mke2fs  0 
+#define OPTSTR_mke2fs "<1>2g:Fnqm#N#i#b#"
 #ifdef CLEANUP_mke2fs
 #undef CLEANUP_mke2fs
 #undef FOR_mke2fs
 
 // mkpasswd   >2S:m:P#=0<0
 #undef OPTSTR_mkpasswd
-#define OPTSTR_mkpasswd  0 
+#define OPTSTR_mkpasswd ">2S:m:P#=0<0"
 #ifdef CLEANUP_mkpasswd
 #undef CLEANUP_mkpasswd
 #undef FOR_mkpasswd
 
 // modprobe   alrqvsDb
 #undef OPTSTR_modprobe
-#define OPTSTR_modprobe  0 
+#define OPTSTR_modprobe "alrqvsDb"
 #ifdef CLEANUP_modprobe
 #undef CLEANUP_modprobe
 #undef FOR_modprobe
 
 // more    
 #undef OPTSTR_more
-#define OPTSTR_more  0 
+#define OPTSTR_more 0
 #ifdef CLEANUP_more
 #undef CLEANUP_more
 #undef FOR_more
 #undef FLAG_q
 #endif
 
-// mv <2vnFfi[-ni] <2vnFfi[-ni]
+// mv <2vnF(remove-destination)fi[-ni] <2vnF(remove-destination)fi[-ni]
 #undef OPTSTR_mv
-#define OPTSTR_mv "<2vnFfi[-ni]"
+#define OPTSTR_mv "<2vnF(remove-destination)fi[-ni]"
 #ifdef CLEANUP_mv
 #undef CLEANUP_mv
 #undef FOR_mv
 #undef FLAG_i
 #undef FLAG_f
+#undef FLAG_remove_destination
 #undef FLAG_F
 #undef FLAG_n
 #undef FLAG_v
 #undef FOR_nohup
 #endif
 
-// nproc   (all)
+// nproc (all) (all)
 #undef OPTSTR_nproc
-#define OPTSTR_nproc  0 
+#define OPTSTR_nproc "(all)"
 #ifdef CLEANUP_nproc
 #undef CLEANUP_nproc
 #undef FOR_nproc
 
 // nsenter   <1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);
 #undef OPTSTR_nsenter
-#define OPTSTR_nsenter  0 
+#define OPTSTR_nsenter "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);"
 #ifdef CLEANUP_nsenter
 #undef CLEANUP_nsenter
 #undef FOR_nsenter
 #undef FLAG_F
 #endif
 
-// od j#vN#xsodcbA:t* j#vN#xsodcbA:t*
+// od j#vw#<1=16N#xsodcbA:t* j#vw#<1=16N#xsodcbA:t*
 #undef OPTSTR_od
-#define OPTSTR_od "j#vN#xsodcbA:t*"
+#define OPTSTR_od "j#vw#<1=16N#xsodcbA:t*"
 #ifdef CLEANUP_od
 #undef CLEANUP_od
 #undef FOR_od
 #undef FLAG_s
 #undef FLAG_x
 #undef FLAG_N
+#undef FLAG_w
 #undef FLAG_v
 #undef FLAG_j
 #endif
 
 // oneit   ^<1nc:p3[!pn]
 #undef OPTSTR_oneit
-#define OPTSTR_oneit  0 
+#define OPTSTR_oneit "^<1nc:p3[!pn]"
 #ifdef CLEANUP_oneit
 #undef CLEANUP_oneit
 #undef FOR_oneit
 
 // openvt   c#<1>63sw
 #undef OPTSTR_openvt
-#define OPTSTR_openvt  0 
+#define OPTSTR_openvt "c#<1>63sw"
 #ifdef CLEANUP_openvt
 #undef CLEANUP_openvt
 #undef FOR_openvt
 
 // passwd   >1a:dlu
 #undef OPTSTR_passwd
-#define OPTSTR_passwd  0 
+#define OPTSTR_passwd ">1a:dlu"
 #ifdef CLEANUP_passwd
 #undef CLEANUP_passwd
 #undef FOR_passwd
 #undef FLAG_d
 #endif
 
-// patch ulp#i:R xulp#i:R
+// patch (dry-run)d:ulp#i:R (dry-run)xd:ulp#i:R
 #undef OPTSTR_patch
-#define OPTSTR_patch "ulp#i:R"
+#define OPTSTR_patch "(dry-run)\ 1d:ulp#i:R"
 #ifdef CLEANUP_patch
 #undef CLEANUP_patch
 #undef FOR_patch
 #undef FLAG_p
 #undef FLAG_l
 #undef FLAG_u
+#undef FLAG_d
 #undef FLAG_x
+#undef FLAG_dry_run
 #endif
 
 // pgrep ?cld:u*U*t*s*P*g*G*fnovxL:[-no] ?cld:u*U*t*s*P*g*G*fnovxL:[-no]
 
 // ping   <1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46[-46]
 #undef OPTSTR_ping
-#define OPTSTR_ping  0 
+#define OPTSTR_ping "<1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46[-46]"
 #ifdef CLEANUP_ping
 #undef CLEANUP_ping
 #undef FOR_ping
 #undef FOR_pivot_root
 #endif
 
-// pkill Vu*U*t*s*P*g*G*fnovxl:[-no] Vu*U*t*s*P*g*G*fnovxl:[-no]
+// pkill ?Vu*U*t*s*P*g*G*fnovxl:[-no] ?Vu*U*t*s*P*g*G*fnovxl:[-no]
 #undef OPTSTR_pkill
-#define OPTSTR_pkill "Vu*U*t*s*P*g*G*fnovxl:[-no]"
+#define OPTSTR_pkill "?Vu*U*t*s*P*g*G*fnovxl:[-no]"
 #ifdef CLEANUP_pkill
 #undef CLEANUP_pkill
 #undef FOR_pkill
 #undef FOR_printf
 #endif
 
-// ps k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae] k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]
+// ps k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO] k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]
 #undef OPTSTR_ps
-#define OPTSTR_ps "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]"
+#define OPTSTR_ps "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]"
 #ifdef CLEANUP_ps
 #undef CLEANUP_ps
 #undef FOR_ps
 #undef FLAG_g
 #undef FLAG_U
 #undef FLAG_u
+#undef FLAG_T
 #undef FLAG_t
 #undef FLAG_s
 #undef FLAG_pid
 
 // readahead    
 #undef OPTSTR_readahead
-#define OPTSTR_readahead  0 
+#define OPTSTR_readahead 0
 #ifdef CLEANUP_readahead
 #undef CLEANUP_readahead
 #undef FOR_readahead
 
 // reboot   fn
 #undef OPTSTR_reboot
-#define OPTSTR_reboot  0 
+#define OPTSTR_reboot "fn"
 #ifdef CLEANUP_reboot
 #undef CLEANUP_reboot
 #undef FOR_reboot
 
 // reset    
 #undef OPTSTR_reset
-#define OPTSTR_reset  0 
+#define OPTSTR_reset 0
 #ifdef CLEANUP_reset
 #undef CLEANUP_reset
 #undef FOR_reset
 #endif
 
+// resize c#<1>63sw c#<1>63sw
+#undef OPTSTR_resize
+#define OPTSTR_resize "c#<1>63sw"
+#ifdef CLEANUP_resize
+#undef CLEANUP_resize
+#undef FOR_resize
+#undef FLAG_w
+#undef FLAG_s
+#undef FLAG_c
+#endif
+
 // restorecon <1DFnRrv <1DFnRrv
 #undef OPTSTR_restorecon
 #define OPTSTR_restorecon "<1DFnRrv"
 
 // rev    
 #undef OPTSTR_rev
-#define OPTSTR_rev  0 
+#define OPTSTR_rev 0
 #ifdef CLEANUP_rev
 #undef CLEANUP_rev
 #undef FOR_rev
 #undef FLAG_version
 #endif
 
+// sendevent <4>4 <4>4
+#undef OPTSTR_sendevent
+#define OPTSTR_sendevent "<4>4"
+#ifdef CLEANUP_sendevent
+#undef CLEANUP_sendevent
+#undef FOR_sendevent
+#endif
+
 // seq <1>3?f:s:w[!fw] <1>3?f:s:w[!fw]
 #undef OPTSTR_seq
 #define OPTSTR_seq "<1>3?f:s:w[!fw]"
 #undef FOR_setenforce
 #endif
 
+// setfattr hn:|v:x:|[!xv] hn:|v:x:|[!xv]
+#undef OPTSTR_setfattr
+#define OPTSTR_setfattr "hn:|v:x:|[!xv]"
+#ifdef CLEANUP_setfattr
+#undef CLEANUP_setfattr
+#undef FOR_setfattr
+#undef FLAG_x
+#undef FLAG_v
+#undef FLAG_n
+#undef FLAG_h
+#endif
+
 // setprop <2>2 <2>2
 #undef OPTSTR_setprop
 #define OPTSTR_setprop "<2>2"
 
 // sh   c:i
 #undef OPTSTR_sh
-#define OPTSTR_sh  0 
+#define OPTSTR_sh "c:i"
 #ifdef CLEANUP_sh
 #undef CLEANUP_sh
 #undef FOR_sh
 #undef FLAG_c
 #endif
 
-// sha1sum b b
+// sha1sum bc*[!bc] bc*[!bc]
 #undef OPTSTR_sha1sum
-#define OPTSTR_sha1sum "b"
+#define OPTSTR_sha1sum "bc*[!bc]"
 #ifdef CLEANUP_sha1sum
 #undef CLEANUP_sha1sum
 #undef FOR_sha1sum
+#undef FLAG_c
 #undef FLAG_b
 #endif
 
 // shred   <1zxus#<1n#<1o#<0f
 #undef OPTSTR_shred
-#define OPTSTR_shred  0 
+#define OPTSTR_shred "<1zxus#<1n#<1o#<0f"
 #ifdef CLEANUP_shred
 #undef CLEANUP_shred
 #undef FOR_shred
 
 // skeleton   (walrus)(blubber):;(also):e@d*c#b:a
 #undef OPTSTR_skeleton
-#define OPTSTR_skeleton  0 
+#define OPTSTR_skeleton "(walrus)(blubber):;(also):e@d*c#b:a"
 #ifdef CLEANUP_skeleton
 #undef CLEANUP_skeleton
 #undef FOR_skeleton
 
 // skeleton_alias   b#dq
 #undef OPTSTR_skeleton_alias
-#define OPTSTR_skeleton_alias  0 
+#define OPTSTR_skeleton_alias "b#dq"
 #ifdef CLEANUP_skeleton_alias
 #undef CLEANUP_skeleton_alias
 #undef FOR_skeleton_alias
 #undef FLAG_a
 #endif
 
-// stat <1c:f <1c:f
+// start    
+#undef OPTSTR_start
+#define OPTSTR_start 0
+#ifdef CLEANUP_start
+#undef CLEANUP_start
+#undef FOR_start
+#endif
+
+// stat <1c:fLt <1c:fLt
 #undef OPTSTR_stat
-#define OPTSTR_stat "<1c:f"
+#define OPTSTR_stat "<1c:fLt"
 #ifdef CLEANUP_stat
 #undef CLEANUP_stat
 #undef FOR_stat
+#undef FLAG_t
+#undef FLAG_L
 #undef FLAG_f
 #undef FLAG_c
 #endif
 
+// stop    
+#undef OPTSTR_stop
+#define OPTSTR_stop 0
+#ifdef CLEANUP_stop
+#undef CLEANUP_stop
+#undef FOR_stop
+#endif
+
 // strings an#=4<1fo an#=4<1fo
 #undef OPTSTR_strings
 #define OPTSTR_strings "an#=4<1fo"
 
 // su   lmpc:s:
 #undef OPTSTR_su
-#define OPTSTR_su  0 
+#define OPTSTR_su "lmpc:s:"
 #ifdef CLEANUP_su
 #undef CLEANUP_su
 #undef FOR_su
 
 // sulogin   t#<0=0
 #undef OPTSTR_sulogin
-#define OPTSTR_sulogin  0 
+#define OPTSTR_sulogin "t#<0=0"
 #ifdef CLEANUP_sulogin
 #undef CLEANUP_sulogin
 #undef FOR_sulogin
 #undef FLAG_p
 #endif
 
-// switch_root <2c:h <2c:h
+// switch_root   <2c:h
 #undef OPTSTR_switch_root
 #define OPTSTR_switch_root "<2c:h"
 #ifdef CLEANUP_switch_root
 
 // sync    
 #undef OPTSTR_sync
-#define OPTSTR_sync  0 
+#define OPTSTR_sync 0
 #ifdef CLEANUP_sync
 #undef CLEANUP_sync
 #undef FOR_sync
 
 // syslogd   >0l#<1>8=8R:b#<0>99=1s#<0=200m#<0>71582787=20O:p:f:a:nSKLD
 #undef OPTSTR_syslogd
-#define OPTSTR_syslogd  0 
+#define OPTSTR_syslogd ">0l#<1>8=8R:b#<0>99=1s#<0=200m#<0>71582787=20O:p:f:a:nSKLD"
 #ifdef CLEANUP_syslogd
 #undef CLEANUP_syslogd
 #undef FOR_syslogd
 
 // tac    
 #undef OPTSTR_tac
-#define OPTSTR_tac  0 
+#define OPTSTR_tac 0
 #ifdef CLEANUP_tac
 #undef CLEANUP_tac
 #undef FOR_tac
 
 // tcpsvd   ^<3c#=30<1C:b#=20<0u:l:hEv
 #undef OPTSTR_tcpsvd
-#define OPTSTR_tcpsvd  0 
+#define OPTSTR_tcpsvd "^<3c#=30<1C:b#=20<0u:l:hEv"
 #ifdef CLEANUP_tcpsvd
 #undef CLEANUP_tcpsvd
 #undef FOR_tcpsvd
 #undef FLAG_i
 #endif
 
-// telnet   <1>2
+// telnet <1>2 <1>2
 #undef OPTSTR_telnet
-#define OPTSTR_telnet  0 
+#define OPTSTR_telnet "<1>2"
 #ifdef CLEANUP_telnet
 #undef CLEANUP_telnet
 #undef FOR_telnet
 
 // telnetd   w#<0b:p#<0>65535=23f:l:FSKi[!wi]
 #undef OPTSTR_telnetd
-#define OPTSTR_telnetd  0 
+#define OPTSTR_telnetd "w#<0b:p#<0>65535=23f:l:FSKi[!wi]"
 #ifdef CLEANUP_telnetd
 #undef CLEANUP_telnetd
 #undef FOR_telnetd
 
 // test    
 #undef OPTSTR_test
-#define OPTSTR_test  0 
+#define OPTSTR_test 0
 #ifdef CLEANUP_test
 #undef CLEANUP_test
 #undef FOR_test
 
 // test_human_readable   <1>1ibs
 #undef OPTSTR_test_human_readable
-#define OPTSTR_test_human_readable  0 
+#define OPTSTR_test_human_readable "<1>1ibs"
 #ifdef CLEANUP_test_human_readable
 #undef CLEANUP_test_human_readable
 #undef FOR_test_human_readable
 
 // test_many_options   ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba
 #undef OPTSTR_test_many_options
-#define OPTSTR_test_many_options  0 
+#define OPTSTR_test_many_options "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba"
 #ifdef CLEANUP_test_many_options
 #undef CLEANUP_test_many_options
 #undef FOR_test_many_options
 
 // test_scankey    
 #undef OPTSTR_test_scankey
-#define OPTSTR_test_scankey  0 
+#define OPTSTR_test_scankey 0
 #ifdef CLEANUP_test_scankey
 #undef CLEANUP_test_scankey
 #undef FOR_test_scankey
 
 // tftp   <1b#<8>65464r:l:g|p|[!gp]
 #undef OPTSTR_tftp
-#define OPTSTR_tftp  0 
+#define OPTSTR_tftp "<1b#<8>65464r:l:g|p|[!gp]"
 #ifdef CLEANUP_tftp
 #undef CLEANUP_tftp
 #undef FOR_tftp
 
 // tftpd   rcu:l
 #undef OPTSTR_tftpd
-#define OPTSTR_tftpd  0 
+#define OPTSTR_tftpd "rcu:l"
 #ifdef CLEANUP_tftpd
 #undef CLEANUP_tftpd
 #undef FOR_tftpd
 #undef FLAG_v
 #endif
 
-// top >0mk*o*p*u*s#<1=9d#=3<1n#<1bq >0mk*o*p*u*s#<1=9d#=3<1n#<1bq
+// top >0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO] >0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]
 #undef OPTSTR_top
-#define OPTSTR_top ">0mk*o*p*u*s#<1=9d#=3<1n#<1bq"
+#define OPTSTR_top ">0mO*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]"
 #ifdef CLEANUP_top
 #undef CLEANUP_top
 #undef FOR_top
 #undef FLAG_p
 #undef FLAG_o
 #undef FLAG_k
+#undef FLAG_H
+#undef FLAG_O
 #undef FLAG_m
 #endif
 
 
 // toybox    
 #undef OPTSTR_toybox
-#define OPTSTR_toybox  0 
+#define OPTSTR_toybox 0
 #ifdef CLEANUP_toybox
 #undef CLEANUP_toybox
 #undef FOR_toybox
 
 // true    
 #undef OPTSTR_true
-#define OPTSTR_true  0 
+#define OPTSTR_true 0
 #ifdef CLEANUP_true
 #undef CLEANUP_true
 #undef FOR_true
 #undef FLAG_s
 #endif
 
+// tunctl <1>1t|d|u:T[!td] <1>1t|d|u:T[!td]
+#undef OPTSTR_tunctl
+#define OPTSTR_tunctl "<1>1t|d|u:T[!td]"
+#ifdef CLEANUP_tunctl
+#undef CLEANUP_tunctl
+#undef FOR_tunctl
+#undef FLAG_T
+#undef FLAG_u
+#undef FLAG_d
+#undef FLAG_t
+#endif
+
 // ulimit >1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc] >1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]
 #undef OPTSTR_ulimit
 #define OPTSTR_ulimit ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]"
 
 // unix2dos    
 #undef OPTSTR_unix2dos
-#define OPTSTR_unix2dos  0 
+#define OPTSTR_unix2dos 0
 #ifdef CLEANUP_unix2dos
 #undef CLEANUP_unix2dos
 #undef FOR_unix2dos
 
 // unlink   <1>1
 #undef OPTSTR_unlink
-#define OPTSTR_unlink  0 
+#define OPTSTR_unlink "<1>1"
 #ifdef CLEANUP_unlink
 #undef CLEANUP_unlink
 #undef FOR_unlink
 
 // unshare   <1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);
 #undef OPTSTR_unshare
-#define OPTSTR_unshare  0 
+#define OPTSTR_unshare "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);"
 #ifdef CLEANUP_unshare
 #undef CLEANUP_unshare
 #undef FOR_unshare
 
 // uptime    
 #undef OPTSTR_uptime
-#define OPTSTR_uptime  0 
+#define OPTSTR_uptime 0
 #ifdef CLEANUP_uptime
 #undef CLEANUP_uptime
 #undef FOR_uptime
 
 // useradd   <1>2u#<0G:s:g:h:SDH
 #undef OPTSTR_useradd
-#define OPTSTR_useradd  0 
+#define OPTSTR_useradd "<1>2u#<0G:s:g:h:SDH"
 #ifdef CLEANUP_useradd
 #undef CLEANUP_useradd
 #undef FOR_useradd
 
 // userdel   <1>1r
 #undef OPTSTR_userdel
-#define OPTSTR_userdel  0 
+#define OPTSTR_userdel "<1>1r"
 #ifdef CLEANUP_userdel
 #undef CLEANUP_userdel
 #undef FOR_userdel
 
 // uudecode   >1o:
 #undef OPTSTR_uudecode
-#define OPTSTR_uudecode  0 
+#define OPTSTR_uudecode ">1o:"
 #ifdef CLEANUP_uudecode
 #undef CLEANUP_uudecode
 #undef FOR_uudecode
 
 // uuencode   <1>2m
 #undef OPTSTR_uuencode
-#define OPTSTR_uuencode  0 
+#define OPTSTR_uuencode "<1>2m"
 #ifdef CLEANUP_uuencode
 #undef CLEANUP_uuencode
 #undef FOR_uuencode
 
 // vi   <1>1
 #undef OPTSTR_vi
-#define OPTSTR_vi  0 
+#define OPTSTR_vi "<1>1"
 #ifdef CLEANUP_vi
 #undef CLEANUP_vi
 #undef FOR_vi
 
 // w    
 #undef OPTSTR_w
-#define OPTSTR_w  0 
+#define OPTSTR_w 0
 #ifdef CLEANUP_w
 #undef CLEANUP_w
 #undef FOR_w
 #endif
 
-// watch   ^<1n#<0=2te
+// watch ^<1n#<0=2te ^<1n#<0=2te
 #undef OPTSTR_watch
-#define OPTSTR_watch  0 
+#define OPTSTR_watch "^<1n#<0=2te"
 #ifdef CLEANUP_watch
 #undef CLEANUP_watch
 #undef FOR_watch
 #undef FLAG_n
 #endif
 
-// wc mcwl[!cm] mcwl[!cm]
+// wc mcwl mcwl
 #undef OPTSTR_wc
-#define OPTSTR_wc "mcwl[!cm]"
+#define OPTSTR_wc "mcwl"
 #ifdef CLEANUP_wc
 #undef CLEANUP_wc
 #undef FOR_wc
 #undef FLAG_m
 #endif
 
+// wget   f:
+#undef OPTSTR_wget
+#define OPTSTR_wget "f:"
+#ifdef CLEANUP_wget
+#undef CLEANUP_wget
+#undef FOR_wget
+#undef FLAG_f
+#endif
+
 // which <1a <1a
 #undef OPTSTR_which
 #define OPTSTR_which "<1a"
 
 // who   a
 #undef OPTSTR_who
-#define OPTSTR_who  0 
+#define OPTSTR_who "a"
 #ifdef CLEANUP_who
 #undef CLEANUP_who
 #undef FOR_who
 #undef FLAG_I
 #endif
 
-// xxd >1c#<1>4096=16l#g#<1=2pr >1c#<1>4096=16l#g#<1=2pr
+// xxd >1c#<1>4096=16l#g#<1=2prs#[!rs] >1c#<1>4096=16l#g#<1=2prs#[!rs]
 #undef OPTSTR_xxd
-#define OPTSTR_xxd ">1c#<1>4096=16l#g#<1=2pr"
+#define OPTSTR_xxd ">1c#<1>4096=16l#g#<1=2prs#[!rs]"
 #ifdef CLEANUP_xxd
 #undef CLEANUP_xxd
 #undef FOR_xxd
+#undef FLAG_s
 #undef FLAG_r
 #undef FLAG_p
 #undef FLAG_g
 
 // xzcat    
 #undef OPTSTR_xzcat
-#define OPTSTR_xzcat  0 
+#define OPTSTR_xzcat 0
 #ifdef CLEANUP_xzcat
 #undef CLEANUP_xzcat
 #undef FOR_xzcat
 
 // yes    
 #undef OPTSTR_yes
-#define OPTSTR_yes  0 
+#define OPTSTR_yes 0
 #ifdef CLEANUP_yes
 #undef CLEANUP_yes
 #undef FOR_yes
 
 // zcat    
 #undef OPTSTR_zcat
-#define OPTSTR_zcat  0 
+#define OPTSTR_zcat 0
 #ifdef CLEANUP_zcat
 #undef CLEANUP_zcat
 #undef FOR_zcat
 #ifndef TT
 #define TT this.arp
 #endif
-#define FLAG_H (FORCED_FLAG<<0)
-#define FLAG_A (FORCED_FLAG<<1)
-#define FLAG_p (FORCED_FLAG<<2)
-#define FLAG_a (FORCED_FLAG<<3)
-#define FLAG_d (FORCED_FLAG<<4)
-#define FLAG_s (FORCED_FLAG<<5)
-#define FLAG_D (FORCED_FLAG<<6)
-#define FLAG_n (FORCED_FLAG<<7)
-#define FLAG_i (FORCED_FLAG<<8)
-#define FLAG_v (FORCED_FLAG<<9)
+#define FLAG_H (1<<0)
+#define FLAG_A (1<<1)
+#define FLAG_p (1<<2)
+#define FLAG_a (1<<3)
+#define FLAG_d (1<<4)
+#define FLAG_s (1<<5)
+#define FLAG_D (1<<6)
+#define FLAG_n (1<<7)
+#define FLAG_i (1<<8)
+#define FLAG_v (1<<9)
 #endif
 
 #ifdef FOR_arping
 #ifndef TT
 #define TT this.bunzip2
 #endif
-#define FLAG_v (1<<0)
-#define FLAG_k (1<<1)
-#define FLAG_t (1<<2)
-#define FLAG_f (1<<3)
-#define FLAG_c (1<<4)
+#define FLAG_v (FORCED_FLAG<<0)
+#define FLAG_k (FORCED_FLAG<<1)
+#define FLAG_t (FORCED_FLAG<<2)
+#define FLAG_f (FORCED_FLAG<<3)
+#define FLAG_c (FORCED_FLAG<<4)
 #endif
 
 #ifdef FOR_bzcat
 #endif
 #endif
 
+#ifdef FOR_chrt
+#ifndef TT
+#define TT this.chrt
+#endif
+#define FLAG_R (1<<0)
+#define FLAG_r (1<<1)
+#define FLAG_o (1<<2)
+#define FLAG_i (1<<3)
+#define FLAG_f (1<<4)
+#define FLAG_b (1<<5)
+#define FLAG_p (1<<6)
+#define FLAG_m (1<<7)
+#endif
+
 #ifdef FOR_chvt
 #ifndef TT
 #define TT this.chvt
 #ifndef TT
 #define TT this.diff
 #endif
-#define FLAG_unified (FORCED_FLAG<<0)
-#define FLAG_U (FORCED_FLAG<<0)
-#define FLAG_recursive (FORCED_FLAG<<1)
-#define FLAG_r (FORCED_FLAG<<1)
-#define FLAG_new_file (FORCED_FLAG<<2)
-#define FLAG_N (FORCED_FLAG<<2)
-#define FLAG_starting_file (FORCED_FLAG<<3)
-#define FLAG_S (FORCED_FLAG<<3)
-#define FLAG_label (FORCED_FLAG<<4)
-#define FLAG_L (FORCED_FLAG<<4)
-#define FLAG_text (FORCED_FLAG<<5)
-#define FLAG_a (FORCED_FLAG<<5)
-#define FLAG_brief (FORCED_FLAG<<6)
-#define FLAG_q (FORCED_FLAG<<6)
-#define FLAG_report_identical_files (FORCED_FLAG<<7)
-#define FLAG_s (FORCED_FLAG<<7)
-#define FLAG_initial_tab (FORCED_FLAG<<8)
-#define FLAG_T (FORCED_FLAG<<8)
-#define FLAG_ignore_case (FORCED_FLAG<<9)
-#define FLAG_i (FORCED_FLAG<<9)
-#define FLAG_ignore_all_space (FORCED_FLAG<<10)
-#define FLAG_w (FORCED_FLAG<<10)
-#define FLAG_expand_tabs (FORCED_FLAG<<11)
-#define FLAG_t (FORCED_FLAG<<11)
-#define FLAG_u (FORCED_FLAG<<12)
-#define FLAG_ignore_space_change (FORCED_FLAG<<13)
-#define FLAG_b (FORCED_FLAG<<13)
-#define FLAG_minimal (FORCED_FLAG<<14)
-#define FLAG_d (FORCED_FLAG<<14)
-#define FLAG_ignore_blank_lines (FORCED_FLAG<<15)
-#define FLAG_B (FORCED_FLAG<<15)
+#define FLAG_unified (1<<0)
+#define FLAG_U (1<<0)
+#define FLAG_recursive (1<<1)
+#define FLAG_r (1<<1)
+#define FLAG_new_file (1<<2)
+#define FLAG_N (1<<2)
+#define FLAG_starting_file (1<<3)
+#define FLAG_S (1<<3)
+#define FLAG_label (1<<4)
+#define FLAG_L (1<<4)
+#define FLAG_text (1<<5)
+#define FLAG_a (1<<5)
+#define FLAG_brief (1<<6)
+#define FLAG_q (1<<6)
+#define FLAG_report_identical_files (1<<7)
+#define FLAG_s (1<<7)
+#define FLAG_initial_tab (1<<8)
+#define FLAG_T (1<<8)
+#define FLAG_ignore_case (1<<9)
+#define FLAG_i (1<<9)
+#define FLAG_ignore_all_space (1<<10)
+#define FLAG_w (1<<10)
+#define FLAG_expand_tabs (1<<11)
+#define FLAG_t (1<<11)
+#define FLAG_u (1<<12)
+#define FLAG_ignore_space_change (1<<13)
+#define FLAG_b (1<<13)
+#define FLAG_minimal (1<<14)
+#define FLAG_d (1<<14)
+#define FLAG_ignore_blank_lines (1<<15)
+#define FLAG_B (1<<15)
 #endif
 
 #ifdef FOR_dirname
 #ifndef TT
 #define TT this.fdisk
 #endif
-#define FLAG_l (FORCED_FLAG<<0)
-#define FLAG_u (FORCED_FLAG<<1)
-#define FLAG_b (FORCED_FLAG<<2)
-#define FLAG_S (FORCED_FLAG<<3)
-#define FLAG_H (FORCED_FLAG<<4)
-#define FLAG_C (FORCED_FLAG<<5)
+#define FLAG_l (1<<0)
+#define FLAG_u (1<<1)
+#define FLAG_b (1<<2)
+#define FLAG_S (1<<3)
+#define FLAG_H (1<<4)
+#define FLAG_C (1<<5)
 #endif
 
 #ifdef FOR_file
 #ifndef TT
 #define TT this.file
 #endif
+#define FLAG_L (1<<0)
+#define FLAG_h (1<<1)
 #endif
 
 #ifdef FOR_find
 #ifndef TT
 #define TT this.ftpget
 #endif
-#define FLAG_P (FORCED_FLAG<<0)
-#define FLAG_p (FORCED_FLAG<<1)
-#define FLAG_u (FORCED_FLAG<<2)
-#define FLAG_v (FORCED_FLAG<<3)
-#define FLAG_c (FORCED_FLAG<<4)
+#define FLAG_P (1<<0)
+#define FLAG_p (1<<1)
+#define FLAG_u (1<<2)
+#define FLAG_v (1<<3)
+#define FLAG_c (1<<4)
 #endif
 
 #ifdef FOR_getenforce
 #endif
 #endif
 
+#ifdef FOR_getfattr
+#ifndef TT
+#define TT this.getfattr
+#endif
+#define FLAG_n (1<<0)
+#define FLAG_h (1<<1)
+#define FLAG_d (1<<2)
+#endif
+
 #ifdef FOR_getprop
 #ifndef TT
 #define TT this.getprop
 #ifndef TT
 #define TT this.host
 #endif
-#define FLAG_t (FORCED_FLAG<<0)
-#define FLAG_v (FORCED_FLAG<<1)
-#define FLAG_a (FORCED_FLAG<<2)
+#define FLAG_t (1<<0)
+#define FLAG_v (1<<1)
+#define FLAG_a (1<<2)
 #endif
 
 #ifdef FOR_hostid
 #ifndef TT
 #define TT this.hostname
 #endif
+#define FLAG_F (1<<0)
+#define FLAG_b (1<<1)
 #endif
 
 #ifdef FOR_hwclock
 #ifndef TT
 #define TT this.iotop
 #endif
-#define FLAG_q (FORCED_FLAG<<0)
-#define FLAG_b (FORCED_FLAG<<1)
-#define FLAG_n (FORCED_FLAG<<2)
-#define FLAG_d (FORCED_FLAG<<3)
-#define FLAG_s (FORCED_FLAG<<4)
-#define FLAG_u (FORCED_FLAG<<5)
-#define FLAG_p (FORCED_FLAG<<6)
-#define FLAG_o (FORCED_FLAG<<7)
-#define FLAG_k (FORCED_FLAG<<8)
-#define FLAG_O (FORCED_FLAG<<9)
-#define FLAG_K (FORCED_FLAG<<10)
-#define FLAG_a (FORCED_FLAG<<11)
-#define FLAG_A (FORCED_FLAG<<12)
+#define FLAG_q (1<<0)
+#define FLAG_b (1<<1)
+#define FLAG_n (1<<2)
+#define FLAG_d (1<<3)
+#define FLAG_s (1<<4)
+#define FLAG_u (1<<5)
+#define FLAG_p (1<<6)
+#define FLAG_o (1<<7)
+#define FLAG_k (1<<8)
+#define FLAG_O (1<<9)
+#define FLAG_K (1<<10)
+#define FLAG_a (1<<11)
+#define FLAG_A (1<<12)
 #endif
 
 #ifdef FOR_ip
 #endif
 #endif
 
+#ifdef FOR_log
+#ifndef TT
+#define TT this.log
+#endif
+#define FLAG_t (1<<0)
+#define FLAG_p (1<<1)
+#endif
+
 #ifdef FOR_logger
 #ifndef TT
 #define TT this.logger
 #define FLAG_f (1<<14)
 #define FLAG_d (1<<15)
 #define FLAG_c (1<<16)
-#define FLAG_a (1<<17)
-#define FLAG_S (1<<18)
-#define FLAG_R (1<<19)
-#define FLAG_L (1<<20)
-#define FLAG_H (1<<21)
-#define FLAG_F (1<<22)
-#define FLAG_C (1<<23)
-#define FLAG_A (1<<24)
-#define FLAG_o (1<<25)
-#define FLAG_g (1<<26)
-#define FLAG_Z (1<<27)
-#define FLAG_color (1<<28)
+#define FLAG_b (1<<17)
+#define FLAG_a (1<<18)
+#define FLAG_S (1<<19)
+#define FLAG_R (1<<20)
+#define FLAG_L (1<<21)
+#define FLAG_H (1<<22)
+#define FLAG_F (1<<23)
+#define FLAG_C (1<<24)
+#define FLAG_A (1<<25)
+#define FLAG_o (1<<26)
+#define FLAG_g (1<<27)
+#define FLAG_Z (1<<28)
+#define FLAG_color (1<<29)
 #endif
 
 #ifdef FOR_lsattr
 #ifndef TT
 #define TT this.lspci
 #endif
-#define FLAG_i (FORCED_FLAG<<0)
-#define FLAG_n (FORCED_FLAG<<1)
-#define FLAG_k (FORCED_FLAG<<2)
-#define FLAG_m (FORCED_FLAG<<3)
-#define FLAG_e (FORCED_FLAG<<4)
+#define FLAG_i (1<<0)
+#define FLAG_n (1<<1)
+#define FLAG_k (1<<2)
+#define FLAG_m (1<<3)
+#define FLAG_e (1<<4)
 #endif
 
 #ifdef FOR_lsusb
 #ifndef TT
 #define TT this.md5sum
 #endif
-#define FLAG_b (1<<0)
+#define FLAG_c (1<<0)
+#define FLAG_b (1<<1)
 #endif
 
 #ifdef FOR_mdev
 #endif
 #define FLAG_i (1<<0)
 #define FLAG_f (1<<1)
+#define FLAG_remove_destination (1<<2)
 #define FLAG_F (1<<2)
 #define FLAG_n (1<<3)
 #define FLAG_v (1<<4)
 #ifndef TT
 #define TT this.nproc
 #endif
-#define FLAG_all (FORCED_FLAG<<0)
+#define FLAG_all (1<<0)
 #endif
 
 #ifdef FOR_nsenter
 #define FLAG_s (1<<6)
 #define FLAG_x (1<<7)
 #define FLAG_N (1<<8)
-#define FLAG_v (1<<9)
-#define FLAG_j (1<<10)
+#define FLAG_w (1<<9)
+#define FLAG_v (1<<10)
+#define FLAG_j (1<<11)
 #endif
 
 #ifdef FOR_oneit
 #define FLAG_p (1<<2)
 #define FLAG_l (1<<3)
 #define FLAG_u (1<<4)
-#define FLAG_x (FORCED_FLAG<<5)
+#define FLAG_d (1<<5)
+#define FLAG_x (FORCED_FLAG<<6)
+#define FLAG_dry_run (1<<7)
 #endif
 
 #ifdef FOR_pgrep
 #define FLAG_g (1<<3)
 #define FLAG_U (1<<4)
 #define FLAG_u (1<<5)
-#define FLAG_t (1<<6)
-#define FLAG_s (1<<7)
-#define FLAG_pid (1<<8)
-#define FLAG_p (1<<8)
-#define FLAG_O (1<<9)
-#define FLAG_o (1<<10)
-#define FLAG_n (1<<11)
-#define FLAG_M (1<<12)
-#define FLAG_l (1<<13)
-#define FLAG_f (1<<14)
-#define FLAG_e (1<<15)
-#define FLAG_d (1<<16)
-#define FLAG_A (1<<17)
-#define FLAG_a (1<<18)
-#define FLAG_ppid (1<<19)
-#define FLAG_P (1<<19)
-#define FLAG_sort (1<<20)
-#define FLAG_k (1<<20)
+#define FLAG_T (1<<6)
+#define FLAG_t (1<<7)
+#define FLAG_s (1<<8)
+#define FLAG_pid (1<<9)
+#define FLAG_p (1<<9)
+#define FLAG_O (1<<10)
+#define FLAG_o (1<<11)
+#define FLAG_n (1<<12)
+#define FLAG_M (1<<13)
+#define FLAG_l (1<<14)
+#define FLAG_f (1<<15)
+#define FLAG_e (1<<16)
+#define FLAG_d (1<<17)
+#define FLAG_A (1<<18)
+#define FLAG_a (1<<19)
+#define FLAG_ppid (1<<20)
+#define FLAG_P (1<<20)
+#define FLAG_sort (1<<21)
+#define FLAG_k (1<<21)
 #endif
 
 #ifdef FOR_pwd
 #endif
 #endif
 
+#ifdef FOR_resize
+#ifndef TT
+#define TT this.resize
+#endif
+#define FLAG_w (1<<0)
+#define FLAG_s (1<<1)
+#define FLAG_c (1<<2)
+#endif
+
 #ifdef FOR_restorecon
 #ifndef TT
 #define TT this.restorecon
 #define FLAG_version (1<<6)
 #endif
 
+#ifdef FOR_sendevent
+#ifndef TT
+#define TT this.sendevent
+#endif
+#endif
+
 #ifdef FOR_seq
 #ifndef TT
 #define TT this.seq
 #endif
 #endif
 
+#ifdef FOR_setfattr
+#ifndef TT
+#define TT this.setfattr
+#endif
+#define FLAG_x (1<<0)
+#define FLAG_v (1<<1)
+#define FLAG_n (1<<2)
+#define FLAG_h (1<<3)
+#endif
+
 #ifdef FOR_setprop
 #ifndef TT
 #define TT this.setprop
 #ifndef TT
 #define TT this.sha1sum
 #endif
-#define FLAG_b (1<<0)
+#define FLAG_c (1<<0)
+#define FLAG_b (1<<1)
 #endif
 
 #ifdef FOR_shred
 #define FLAG_a (1<<2)
 #endif
 
+#ifdef FOR_start
+#ifndef TT
+#define TT this.start
+#endif
+#endif
+
 #ifdef FOR_stat
 #ifndef TT
 #define TT this.stat
 #endif
-#define FLAG_f (1<<0)
-#define FLAG_c (1<<1)
+#define FLAG_t (1<<0)
+#define FLAG_L (1<<1)
+#define FLAG_f (1<<2)
+#define FLAG_c (1<<3)
+#endif
+
+#ifdef FOR_stop
+#ifndef TT
+#define TT this.stop
+#endif
 #endif
 
 #ifdef FOR_strings
 #ifndef TT
 #define TT this.switch_root
 #endif
-#define FLAG_h (1<<0)
-#define FLAG_c (1<<1)
+#define FLAG_h (FORCED_FLAG<<0)
+#define FLAG_c (FORCED_FLAG<<1)
 #endif
 
 #ifdef FOR_sync
 #define FLAG_p (1<<6)
 #define FLAG_o (1<<7)
 #define FLAG_k (1<<8)
-#define FLAG_m (1<<9)
+#define FLAG_H (1<<9)
+#define FLAG_O (1<<10)
+#define FLAG_m (1<<11)
 #endif
 
 #ifdef FOR_touch
 #define FLAG_s (1<<0)
 #endif
 
+#ifdef FOR_tunctl
+#ifndef TT
+#define TT this.tunctl
+#endif
+#define FLAG_T (1<<0)
+#define FLAG_u (1<<1)
+#define FLAG_d (1<<2)
+#define FLAG_t (1<<3)
+#endif
+
 #ifdef FOR_ulimit
 #ifndef TT
 #define TT this.ulimit
 #ifndef TT
 #define TT this.watch
 #endif
-#define FLAG_e (FORCED_FLAG<<0)
-#define FLAG_t (FORCED_FLAG<<1)
-#define FLAG_n (FORCED_FLAG<<2)
+#define FLAG_e (1<<0)
+#define FLAG_t (1<<1)
+#define FLAG_n (1<<2)
 #endif
 
 #ifdef FOR_wc
 #define FLAG_m (1<<3)
 #endif
 
+#ifdef FOR_wget
+#ifndef TT
+#define TT this.wget
+#endif
+#define FLAG_f (FORCED_FLAG<<0)
+#endif
+
 #ifdef FOR_which
 #ifndef TT
 #define TT this.which
 #ifndef TT
 #define TT this.xxd
 #endif
-#define FLAG_r (1<<0)
-#define FLAG_p (1<<1)
-#define FLAG_g (1<<2)
-#define FLAG_l (1<<3)
-#define FLAG_c (1<<4)
+#define FLAG_s (1<<0)
+#define FLAG_r (1<<1)
+#define FLAG_p (1<<2)
+#define FLAG_g (1<<3)
+#define FLAG_l (1<<4)
+#define FLAG_c (1<<5)
 #endif
 
 #ifdef FOR_xzcat
index 218792f..ea2ea15 100644 (file)
@@ -6,6 +6,13 @@ struct getprop_data {
   struct selabel_handle *handle;
 };
 
+// toys/android/log.c
+
+struct log_data {
+  char *tag;
+  char *pri;
+};
+
 // toys/example/hello.c
 
 struct hello_data {
@@ -39,6 +46,12 @@ struct dmesg_data {
   long size;
 };
 
+// toys/lsb/hostname.c
+
+struct hostname_data {
+  char *fname;
+};
+
 // toys/lsb/killall.c
 
 struct killall_data {
@@ -53,6 +66,11 @@ struct killall_data {
 // toys/lsb/md5sum.c
 
 struct md5sum_data {
+  struct arg_list *c;
+
+  int sawline;
+
+  // Crypto variables blanked after summing
   unsigned state[5];
   unsigned oldstate[5];
   uint64_t count;
@@ -121,6 +139,35 @@ struct umount_data {
   char *types;
 };
 
+// toys/net/ifconfig.c
+
+struct ifconfig_data {
+  int sockfd;
+};
+
+// toys/net/netcat.c
+
+struct netcat_data {
+  char *filename;        // -f read from filename instead of network
+  long quit_delay;       // -q Exit after EOF from stdin after # seconds.
+  char *source_address;  // -s Bind to a specific source address.
+  long port;             // -p Bind to a specific source port.
+  long wait;             // -w Wait # seconds for a connection.
+};
+
+// toys/net/netstat.c
+
+struct netstat_data {
+  struct num_cache *inodes;
+  int wpad;
+};;
+
+// toys/net/tunctl.c
+
+struct tunctl_data {
+  char *user;
+};
+
 // toys/other/acpi.c
 
 struct acpi_data {
@@ -132,6 +179,8 @@ struct acpi_data {
 
 struct base64_data {
   long columns;
+
+  unsigned total;
 };
 
 // toys/other/blockdev.c
@@ -177,12 +226,6 @@ struct hwclock_data {
   int utc;
 };
 
-// toys/other/ifconfig.c
-
-struct ifconfig_data {
-  int sockfd;
-};
-
 // toys/other/ionice.c
 
 struct ionice_data {
@@ -260,16 +303,6 @@ struct modinfo_data {
   long mod;
 };
 
-// toys/other/netcat.c
-
-struct netcat_data {
-  char *filename;        // -f read from filename instead of network
-  long quit_delay;       // -q Exit after EOF from stdin after # seconds.
-  char *source_address;  // -s Bind to a specific source address.
-  long port;             // -p Bind to a specific source port.
-  long wait;             // -w Wait # seconds for a connection.
-};
-
 // toys/other/nsenter.c
 
 struct nsenter_data {
@@ -283,6 +316,12 @@ struct oneit_data {
   char *console;
 };
 
+// toys/other/setfattr.c
+
+struct setfattr_data {
+  char *x, *v, *n;
+};
+
 // toys/other/shred.c
 
 struct shred_data {
@@ -302,8 +341,8 @@ struct stat_data {
     struct stat st;
     struct statfs sf;
   } stat;
-  struct passwd *user_name;
-  struct group *group_name;
+  char *file, *pattern;
+  int patlen;
 };
 
 // toys/other/swapon.c
@@ -344,6 +383,7 @@ struct truncate_data {
 // toys/other/xxd.c
 
 struct xxd_data {
+  long s;
   long g;
   long l;
   long c;
@@ -392,6 +432,12 @@ struct brctl_data {
     int sockfd;
 };
 
+// toys/pending/chrt.c
+
+struct chrt_data {
+  long pid;
+};
+
 // toys/pending/compress.c
 
 struct compress_data {
@@ -435,8 +481,18 @@ struct crontab_data {
 // toys/pending/dd.c
 
 struct dd_data {
-  int sig;
-};
+  int show_xfer;
+  int show_records;
+  unsigned long long bytes, c_count, in_full, in_part, out_full, out_part;
+  struct timeval start;
+  struct {
+    char *name;
+    int fd;
+    unsigned char *buff, *bp;
+    long sz, count;
+    unsigned long long offset;
+  } in, out;
+};;
 
 // toys/pending/dhcp.c
 
@@ -494,7 +550,9 @@ struct dumpleases_data {
 // toys/pending/expr.c
 
 struct expr_data {
-  int argidx;
+  char **tok; // current token, not on the stack since recursive calls mutate it
+
+  char *refree;
 };
 
 // toys/pending/fdisk.c
@@ -506,12 +564,6 @@ struct fdisk_data {
   long cylinders;
 };
 
-// toys/pending/file.c
-
-struct file_data {
-  int max_name_len;
-};
-
 // toys/pending/fold.c
 
 struct fold_data {
@@ -547,6 +599,12 @@ struct ftpget_data {
   char buf[sizeof(struct sockaddr_storage)];
 };
 
+// toys/pending/getfattr.c
+
+struct getfattr_data {
+  char *n;
+};
+
 // toys/pending/getty.c
 
 struct getty_data {
@@ -635,10 +693,11 @@ struct logger_data {
 // toys/pending/lsof.c
 
 struct lsof_data {
-  char *pids;
+  struct arg_list *p;
 
   struct stat *sought_files;
 
+  struct double_list *all_sockets;
   struct double_list *files;
   int last_shown_pid;
   int shown_header;
@@ -691,13 +750,6 @@ struct more_data {
   int cin_fd;
 };
 
-// toys/pending/netstat.c
-
-struct netstat_data {
-  char current_name[21];
-  int some_process_unidentified;
-};;
-
 // toys/pending/openvt.c
 
 struct openvt_data {
@@ -727,6 +779,8 @@ struct route_data {
 
 struct sh_data {
   char *command;
+
+  long lineno;
 };
 
 // toys/pending/sulogin.c
@@ -889,6 +943,12 @@ struct watch_data {
   int interval;
 };
 
+// toys/pending/wget.c
+
+struct wget_data {
+  char *filename;
+};
+
 // toys/posix/chgrp.c
 
 struct chgrp_data {
@@ -1006,6 +1066,12 @@ struct expand_data {
   unsigned tabcount, *tab;
 };
 
+// toys/posix/file.c
+
+struct file_data {
+  int max_name_len;
+};
+
 // toys/posix/find.c
 
 struct find_data {
@@ -1057,7 +1123,7 @@ struct ls_data {
 
   unsigned screen_width;
   int nl_title;
-  char uid_buf[12], gid_buf[12];
+  char *escmore;
 };
 
 // toys/posix/mkdir.c
@@ -1102,12 +1168,13 @@ struct od_data {
   struct arg_list *output_base;
   char *address_base;
   long max_count;
+  long width;
   long jump_bytes;
 
   int address_idx;
   unsigned types, leftover, star;
-  char *buf;
-  uint64_t bufs[4]; // force 64-bit alignment
+  char *buf; // Points to buffers[0] or buffers[1].
+  char *bufs[2]; // Used to detect duplicate lines.
   off_t pos;
 };
 
@@ -1122,6 +1189,7 @@ struct paste_data {
 struct patch_data {
   char *infile;
   long prefix;
+  char *dir;
 
   struct double_list *current_hunk;
   long oldline, oldlen, newline, newlen;
@@ -1155,8 +1223,9 @@ struct ps_data {
       struct arg_list *p;
       struct arg_list *o;
       struct arg_list *k;
+      struct arg_list *O;
     } top;
-    struct{
+    struct {
       char *L;
       struct arg_list *G;
       struct arg_list *g;
@@ -1173,8 +1242,11 @@ struct ps_data {
     } pgrep;
   };
 
+#ifndef __APPLE__
   struct sysinfo si;
+#endif
   struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
+  struct dirtree *threadparent;
   unsigned width, height;
   dev_t tty;
   void *fields, *kfields;
@@ -1282,7 +1354,7 @@ struct uudecode_data {
 // toys/posix/wc.c
 
 struct wc_data {
-  unsigned long totals[3];
+  unsigned long totals[4];
 };
 
 // toys/posix/xargs.c
@@ -1300,9 +1372,11 @@ struct xargs_data {
 
 extern union global_union {
        struct getprop_data getprop;
+       struct log_data log;
        struct hello_data hello;
        struct skeleton_data skeleton;
        struct dmesg_data dmesg;
+       struct hostname_data hostname;
        struct killall_data killall;
        struct md5sum_data md5sum;
        struct mknod_data mknod;
@@ -1313,6 +1387,10 @@ extern union global_union {
        struct seq_data seq;
        struct su_data su;
        struct umount_data umount;
+       struct ifconfig_data ifconfig;
+       struct netcat_data netcat;
+       struct netstat_data netstat;
+       struct tunctl_data tunctl;
        struct acpi_data acpi;
        struct base64_data base64;
        struct blockdev_data blockdev;
@@ -1321,7 +1399,6 @@ extern union global_union {
        struct free_data free;
        struct hexedit_data hexedit;
        struct hwclock_data hwclock;
-       struct ifconfig_data ifconfig;
        struct ionice_data ionice;
        struct login_data login;
        struct losetup_data losetup;
@@ -1331,9 +1408,9 @@ extern union global_union {
        struct mkpasswd_data mkpasswd;
        struct mkswap_data mkswap;
        struct modinfo_data modinfo;
-       struct netcat_data netcat;
        struct nsenter_data nsenter;
        struct oneit_data oneit;
+       struct setfattr_data setfattr;
        struct shred_data shred;
        struct stat_data stat;
        struct swapon_data swapon;
@@ -1345,6 +1422,7 @@ extern union global_union {
        struct arping_data arping;
        struct bootchartd_data bootchartd;
        struct brctl_data brctl;
+       struct chrt_data chrt;
        struct compress_data compress;
        struct crond_data crond;
        struct crontab_data crontab;
@@ -1356,10 +1434,10 @@ extern union global_union {
        struct dumpleases_data dumpleases;
        struct expr_data expr;
        struct fdisk_data fdisk;
-       struct file_data file;
        struct fold_data fold;
        struct fsck_data fsck;
        struct ftpget_data ftpget;
+       struct getfattr_data getfattr;
        struct getty_data getty;
        struct groupadd_data groupadd;
        struct host_data host;
@@ -1374,7 +1452,6 @@ extern union global_union {
        struct mke2fs_data mke2fs;
        struct modprobe_data modprobe;
        struct more_data more;
-       struct netstat_data netstat;
        struct openvt_data openvt;
        struct ping_data ping;
        struct route_data route;
@@ -1392,6 +1469,7 @@ extern union global_union {
        struct useradd_data useradd;
        struct vi_data vi;
        struct watch_data watch;
+       struct wget_data wget;
        struct chgrp_data chgrp;
        struct chmod_data chmod;
        struct cksum_data cksum;
@@ -1404,6 +1482,7 @@ extern union global_union {
        struct du_data du;
        struct env_data env;
        struct expand_data expand;
+       struct file_data file;
        struct find_data find;
        struct grep_data grep;
        struct head_data head;
index 46eeed7..d5be45e 100644 (file)
@@ -18,6 +18,8 @@
 
 #define HELP_toybox_float "Include floating point support infrastructure and commands that\nrequire it.\n\n"
 
+#define HELP_toybox_libcrypto "Use faster hash functions out of exteral -lcrypto library.\n\n"
+
 #define HELP_toybox_smack "Include SMACK options in commands like ls for systems like Tizen.\n\n\n"
 
 #define HELP_toybox_selinux "Include SELinux options in commands such as ls, and add\nSELinux-specific commands such as chcon to the Android menu.\n\n"
 
 #define HELP_toybox "usage: toybox [--long | --version | [command] [arguments...]]\n\nWith no arguments, shows available commands. First argument is\nname of a command to run, followed by any arguments to that command.\n\n--long   Show path to each command\n--version    Show toybox version\n\nTo install command symlinks, try:\n  for i in $(/bin/toybox --long); do ln -s /bin/toybox $i; done\n\n"
 
+#define HELP_stop "usage: stop [SERVICE...]\n\nStop the given system service, or netd/surfaceflinger/zygotes.\n\n"
+
+#define HELP_start "usage: start [SERVICE...]\n\nStarts the given system service, or netd/surfaceflinger/zygotes.\n\n"
+
 #define HELP_setprop "usage: setprop NAME VALUE\n\nSets an Android system property.\n\n"
 
 #define HELP_setenforce "usage: setenforce [enforcing|permissive|1|0]\n\nSets whether SELinux is enforcing (1) or permissive (0).\n\n"
 
+#define HELP_sendevent "usage: sendevent DEVICE TYPE CODE VALUE\n\nSends a Linux input event.\n\n"
+
 #define HELP_runcon "usage: runcon CONTEXT COMMAND [ARGS...]\n\nRun a command in a specified security context.\n\n"
 
 #define HELP_restorecon "usage: restorecon [-D] [-F] [-R] [-n] [-v] FILE...\n\nRestores the default security contexts for the given files.\n\n-D       apply to /data/data too\n-F     force reset\n-R recurse into directories\n-n    don't make any changes; useful with -v to see what would change\n-v     verbose: show any changes\n\n"
 
+#define HELP_log "usage: log [-p PRI] [-t TAG] MESSAGE...\n\nLogs message to logcat.\n\n-p     use the given priority instead of INFO:\n       d: DEBUG  e: ERROR  f: FATAL  i: INFO  v: VERBOSE  w: WARN  s: SILENT\n-t       use the given tag instead of \"log\"\n\n"
+
 #define HELP_load_policy "usage: load_policy FILE\n\nLoad the specified policy file.\n\n"
 
 #define HELP_getprop "usage: getprop [NAME [DEFAULT]]\n\nGets an Android system property, or lists them all.\n\n"
 
 #define HELP_passwd "usage: passwd [-a ALGO] [-dlu] <account name>\n\nupdate user's authentication tokens. Default : current user\n\n-a ALGO   Encryption method (des, md5, sha256, sha512) default: des\n-d           Set password to ''\n-l          Lock (disable) account\n-u              Unlock (enable) account\n\n"
 
-#define HELP_mount "usage: mount [-afFrsvw] [-t TYPE] [-o OPTIONS...] [[DEVICE] DIR]\n\nMount new filesystem(s) on directories. With no arguments, display existing\nmounts.\n\n-a     mount all entries in /etc/fstab (with -t, only entries of that TYPE)\n-O        only mount -a entries that have this option\n-f fake it (don't actually mount)\n-r      read only (same as -o ro)\n-w   read/write (default, same as -o rw)\n-t specify filesystem type\n-v     verbose\n\nOPTIONS is a comma separated list of options, which can also be supplied\nas --longopts.\n\nThis mount autodetects loopback mounts (a file on a directory) and\nbind mounts (file on file, directory on directory), so you don't need\nto say --bind or --loop. You can also \"mount -a /path\" to mount everything\nin /etc/fstab under /path, even if it's noauto.\n\n\n"
+#define HELP_mount "usage: mount [-afFrsvw] [-t TYPE] [-o OPTION,] [[DEVICE] DIR]\n\nMount new filesystem(s) on directories. With no arguments, display existing\nmounts.\n\n-a        mount all entries in /etc/fstab (with -t, only entries of that TYPE)\n-O        only mount -a entries that have this option\n-f fake it (don't actually mount)\n-r      read only (same as -o ro)\n-w   read/write (default, same as -o rw)\n-t specify filesystem type\n-v     verbose\n\nOPTIONS is a comma separated list of options, which can also be supplied\nas --longopts.\n\nThis mount autodetects loopback mounts (a file on a directory) and\nbind mounts (file on file, directory on directory), so you don't need\nto say --bind or --loop. You can also \"mount -a /path\" to mount everything\nin /etc/fstab under /path, even if it's noauto.\n\n\n"
 
 #define HELP_mktemp "usage: mktemp [-dqu] [-p DIR] [TEMPLATE]\n\nSafely create a new file \"DIR/TEMPLATE\" and print its name.\n\n-d   Create directory instead of file (--directory)\n-p      Put new file in DIR (--tmpdir)\n-q      Quiet, no error messages\n-u    Don't create anything, just print what would be created\n\nEach X in TEMPLATE is replaced with a random printable character. The\ndefault TEMPLATE is tmp.XXXXXX, and the default DIR is $TMPDIR if set,\nelse \"/tmp\".\n\n"
 
-#define HELP_mknod_z "usage: mknod [-Z CONTEXT] ...\n\n-Z      Set security context to created file\n\n"
+#define HELP_mknod "usage: mknod [-Z CONTEXT] ... [-m MODE] NAME TYPE [MAJOR MINOR]\n\nCreate a special file NAME with a given type. TYPE is b for block device,\nc or u for character device, p for named pipe (which ignores MAJOR/MINOR).\n-Z       Set security context to created file\n-m        Mode (file permissions) of new device, in octal or u+x format\n"
 
-#define HELP_mknod "usage: mknod [-m MODE] NAME TYPE [MAJOR MINOR]\n\nCreate a special file NAME with a given type. TYPE is b for block device,\nc or u for character device, p for named pipe (which ignores MAJOR/MINOR).\n\n-m      Mode (file permissions) of new device, in octal or u+x format\n\n"
+#define HELP_sha512sum "See sha1sum\n\n"
 
-#define HELP_sha1sum "usage: sha1sum [FILE]...\n\ncalculate sha1 hash for each input file, reading from stdin if none.\nOutput one hash (20 hex digits) for each input file, followed by\nfilename.\n\n-b      brief (hash only, no filename)\n\n"
+#define HELP_sha384sum "See sha1sum\n\n"
 
-#define HELP_md5sum "usage: md5sum [FILE]...\n\nCalculate md5 hash for each input file, reading from stdin if none.\nOutput one hash (16 hex digits) for each input file, followed by\nfilename.\n\n-b brief (hash only, no filename)\n\n"
+#define HELP_sha256sum "See sha1sum\n\n"
+
+#define HELP_sha224sum "See sha1sum\n\n"
+
+#define HELP_sha1sum "usage: sha?sum [-b] [-c FILE] [FILE]...\n\ncalculate sha hash for each input file, reading from stdin if none. Output\none hash (40 hex digits for sha1, 56 for sha224, 64 for sha256, 96 for sha384,\nand 128 for sha512) for each input file, followed by filename.\n\n-b      brief (hash only, no filename)\n-c      Check each line of FILE is the same hash+filename we'd output.\n\n"
+
+#define HELP_md5sum "usage: md5sum [-b] [-c FILE] [FILE]...\n\nCalculate md5 hash for each input file, reading from stdin if none.\nOutput one hash (32 hex digits) for each input file, followed by filename.\n\n-b   brief (hash only, no filename)\n-c      Check each line of FILE is the same hash+filename we'd output.\n\n"
 
 #define HELP_killall "usage: killall [-l] [-iqv] [-SIGNAL|-s SIGNAL] PROCESS_NAME...\n\nSend a signal (default: TERM) to all processes with the given names.\n\n-i     ask for confirmation before killing\n-l print list of all available signals\n-q don't print any warnings or error messages\n-s  send SIGNAL instead of SIGTERM\n-v      report if the signal was successfully sent\n\n"
 
-#define HELP_hostname "usage: hostname [newname]\n\nGet/Set the current hostname\n\n"
+#define HELP_hostname "usage: hostname [-b] [-F FILENAME] [newname]\n\nGet/Set the current hostname\n\n-b  Set hostname to 'localhost' if otherwise unset\n-F  Set hostname to contents of FILENAME\n\n"
 
 #define HELP_dmesg "usage: dmesg [-c] [-r|-t] [-n LEVEL] [-s SIZE]\n\nPrint or control the kernel ring buffer.\n\n-c   Clear the ring buffer after printing\n-n        Set kernel logging LEVEL (1-9)\n-r      Raw output (with <level markers>)\n-s   Show the last SIZE many bytes\n-t       Don't print kernel's timestamps\n\n"
 
+#define HELP_tunctl "usage: tunctl [-dtT] [-u USER] NAME\n\nCreate and delete tun/tap virtual ethernet devices.\n\n-T  Use tap (ethernet frames) instead of tun (ip packets)\n-d       Delete tun/tap device\n-t       Create tun/tap device\n-u       Set owner (user who can read/write device without root access)\n\n\n"
+
+#define HELP_rfkill "Usage: rfkill COMMAND [DEVICE]\n\nEnable/disable wireless devices.\n\nCommands:\nlist [DEVICE]   List current state\nblock DEVICE    Disable device\nunblock DEVICE  Enable device\n\nDEVICE is an index number, or one of:\nall, wlan(wifi), bluetooth, uwb(ultrawideband), wimax, wwan, gps, fm.\n\n"
+
+#define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information. Default is netsat -tuwx\n\n-r  routing table\n-a  all sockets (not just connected)\n-l  listening server sockets\n-t  TCP sockets\n-u  UDP sockets\n-w  raw sockets\n-x  unix sockets\n-e  extended info\n-n  don't resolve names\n-W  wide display\n-p  PID/Program name of sockets\n\n"
+
+#define HELP_netcat "usage: netcat [-tu] [-lL COMMAND...] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\n-L        listen for multiple incoming connections (server mode).\n-f     use FILENAME (ala /dev/ttyS0) instead of network\n-l    listen for one incoming connection.\n-p local port number\n-q   SECONDS quit this many seconds after EOF on stdin.\n-s  local ipv4 address\n-t  allocate tty (must come before -l or -L)\n-w    SECONDS timeout for connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed to handle each incoming\nconnection. If none, the connection is forwarded to stdin/stdout.\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l\n"
+
+#define HELP_ifconfig "usage: ifconfig [-a] [INTERFACE [ACTION...]]\n\nDisplay or configure network interface.\n\nWith no arguments, display active interfaces. First argument is interface\nto operate on, one argument by itself displays that interface.\n\n-a      Show all interfaces, not just active ones\n\nAdditional arguments are actions to perform on the interface:\n\nADDRESS[/NETMASK] - set IPv4 address (1.2.3.4/5)\ndefault - unset ipv4 address\nadd|del ADDRESS[/PREFIXLEN] - add/remove IPv6 address (1111::8888/128)\nup - enable interface\ndown - disable interface\n\nnetmask|broadcast|pointopoint ADDRESS - set more IPv4 characteristics\nhw ether|infiniband ADDRESS - set LAN hardware address (AA:BB:CC...)\ntxqueuelen LEN - number of buffered packets before output blocks\nmtu LEN - size of outgoing packets (Maximum Transmission Unit)\n\nFlags you can set on an interface (or -remove by prefixing with -):\narp - don't use Address Resolution Protocol to map LAN routes\npromisc - don't discard packets that aren't to this LAN hardware address\nmulticast - force interface into multicast mode if the driver doesn't\nallmulti - promisc for multicast packets\n\nObsolete fields included for historical purposes:\nirq|io_addr|mem_start ADDR - micromanage obsolete hardware\noutfill|keepalive INTEGER - SLIP analog dialup line quality monitoring\nmetric INTEGER - added to Linux 0.9.10 with comment \"never used\", still true\n\n"
+
 #define HELP_yes "usage: yes [args...]\n\nRepeatedly output line until killed. If no args, output 'y'.\n\n\n"
 
-#define HELP_xxd "usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [file]\n\nHexdump a file to stdout.  If no file is listed, copy from stdin.\nFilename \"-\" is a synonym for stdin.\n\n-c n        Show n bytes per line (default 16).\n-g n       Group bytes by adding a ' ' every n bytes (default 2).\n-l n    Limit of n bytes before stopping (default is no limit).\n-p     Plain hexdump (30 bytes/line, no grouping).\n-r Reverse operation: turn a hexdump into a binary file.\n\n"
+#define HELP_xxd "usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]\n\nHexdump a file to stdout.  If no file is listed, copy from stdin.\nFilename \"-\" is a synonym for stdin.\n\n-c n Show n bytes per line (default 16).\n-g n       Group bytes by adding a ' ' every n bytes (default 2).\n-l n    Limit of n bytes before stopping (default is no limit).\n-p     Plain hexdump (30 bytes/line, no grouping).\n-r Reverse operation: turn a hexdump into a binary file.\n-s n     Skip to offset n.\n\n"
 
 #define HELP_which "usage: which [-a] filename ...\n\nSearch $PATH for executable files matching filename(s).\n\n-a    Show all matches\n\n"
 
 
 #define HELP_swapoff "usage: swapoff swapregion\n\nDisable swapping on a given swapregion.\n\n"
 
-#define HELP_stat "usage: stat [-f] [-c FORMAT] FILE...\n\nDisplay status of files or filesystems.\n\n-f display filesystem status instead of file status\n-c Output specified FORMAT string instead of default\n\nThe valid format escape sequences for files:\n%a  Access bits (octal) |%A  Access bits (flags)|%b  Blocks allocated\n%B  Bytes per block     |%d  Device ID (dec)    |%D  Device ID (hex)\n%f  All mode bits (hex) |%F  File type          |%g  Group ID\n%G  Group name          |%h  Hard links         |%i  Inode\n%n  Filename            |%N  Long filename      |%o  I/O block size\n%s  Size (bytes)        |%u  User ID            |%U  User name\n%x  Access time         |%X  Access unix time   |%y  File write time\n%Y  File write unix time|%z  Dir change time    |%Z  Dir change unix time\n\nThe valid format escape sequences for filesystems:\n%a  Available blocks    |%b  Total blocks       |%c  Total inodes\n%d  Free inodes         |%f  Free blocks        |%i  File system ID\n%l  Max filename length |%n  File name          |%s  Fragment size\n%S  Best transfer size  |%t  Filesystem type    |%T  Filesystem type name\n\n"
+#define HELP_stat "usage: stat [-tfL] [-c FORMAT] FILE...\n\nDisplay status of files or filesystems.\n\n-c     Output specified FORMAT string instead of default\n-f   display filesystem status instead of file status\n-L    Follow symlinks\n-t     terse (-c \"%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\")\n         (with -f = -c \"%n %i %l %t %s %S %b %f %a %c %d\")\n\nThe valid format escape sequences for files:\n%a  Access bits (octal) |%A  Access bits (flags)|%b  Size/512\n%B  Bytes per %b (512)  |%d  Device ID (dec)    |%D  Device ID (hex)\n%f  All mode bits (hex) |%F  File type          |%g  Group ID\n%G  Group name          |%h  Hard links         |%i  Inode\n%m  Mount point         |%n  Filename           |%N  Long filename\n%o  I/O block size      |%s  Size (bytes)       |%t  Devtype major (hex)\n%T  Devtype minor (hex) |%u  User ID            |%U  User name\n%x  Access time         |%X  Access unix time   |%y  File write time\n%Y  File write unix time|%z  Dir change time    |%Z  Dir change unix time\n\nThe valid format escape sequences for filesystems:\n%a  Available blocks    |%b  Total blocks       |%c  Total inodes\n%d  Free inodes         |%f  Free blocks        |%i  File system ID\n%l  Max filename length |%n  File name          |%s  Fragment size\n%S  Best transfer size  |%t  FS type (hex)      |%T  FS type (driver name)\n\n"
 
 #define HELP_shred "usage: shred [-fuz] [-n COUNT] [-s SIZE] FILE...\n\nSecurely delete a file by overwriting its contents with random data.\n\n-f        Force (chmod if necessary)\n-n COUNT  Random overwrite iterations (default 1)\n-o OFFSET Start at OFFSET\n-s SIZE   Use SIZE instead of detecting file size\n-u        unlink (actually delete file when done)\n-x        Use exact size (default without -s rounds up to next 4k)\n-z        zero at end\n\nNote: data journaling filesystems render this command useless, you must\noverwrite all free space (fill up disk) to erase old data on those.\n\n"
 
 #define HELP_setsid "usage: setsid [-t] command [args...]\n\nRun process in a new session.\n\n-t       Grab tty (become foreground process, receiving keyboard signals)\n\n"
 
-#define HELP_rmmod "usage: rmmod [-wf] [MODULE]\n\nUnload the module named MODULE from the Linux kernel.\n-f   Force unload of a module\n-w    Wait until the module is no longer used.\n\n\n"
+#define HELP_setfattr "usage: setfattr [-h] [-x|-n NAME] [-v VALUE] FILE...\n\nWrite POSIX extended attributes.\n\n-h  Do not dereference symlink.\n-n Set given attribute.\n-x        Remove given attribute.\n-v     Set value for attribute -n (default is empty).\n\n"
 
-#define HELP_rfkill "Usage: rfkill COMMAND [DEVICE]\n\nEnable/disable wireless devices.\n\nCommands:\nlist [DEVICE]   List current state\nblock DEVICE    Disable device\nunblock DEVICE  Enable device\n\nDEVICE is an index number, or one of:\nall, wlan(wifi), bluetooth, uwb(ultrawideband), wimax, wwan, gps, fm.\n\n"
+#define HELP_rmmod "usage: rmmod [-wf] [MODULE]\n\nUnload the module named MODULE from the Linux kernel.\n-f   Force unload of a module\n-w    Wait until the module is no longer used.\n\n\n"
 
 #define HELP_rev "usage: rev [FILE...]\n\nOutput each line reversed, when no files are given stdin is used.\n\n"
 
 
 #define HELP_unshare "usage: unshare [-imnpuUr] COMMAND...\n\nCreate new container namespace(s) for this process and its children, so\nsome attribute is not shared with the parent process.\n\n-f  Fork command in the background (--fork)\n-i        SysV IPC (message queues, semaphores, shared memory) (--ipc)\n-m        Mount/unmount tree (--mount)\n-n        Network address, sockets, routing, iptables (--net)\n-p Process IDs and init (--pid)\n-r        Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)\n-u    Host and domain names (--uts)\n-U       UIDs, GIDs, capabilities (--user)\n\nA namespace allows a set of processes to have a different view of the\nsystem than other sets of processes.\n\n"
 
-#define HELP_netcat_listen_tty "usage: netcat [-t]\n\n-t       allocate tty (must come before -l or -L)\n\n"
-
-#define HELP_netcat "usage: netcat [-lL COMMAND...] [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\n-L listen for multiple incoming connections (server mode).\n-f     use FILENAME (ala /dev/ttyS0) instead of network\n-l    listen for one incoming connection.\n-p local port number\n-q   SECONDS quit this many seconds after EOF on stdin.\n-s  local ipv4 address\n-w  SECONDS timeout for connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed to handle each incoming\nconnection. If none, the connection is forwarded to stdin/stdout.\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l\n"
-
 #define HELP_nbd_client "usage: nbd-client [-ns] HOST PORT DEVICE\n\n-n        Do not fork into background\n-s nbd swap support (lock server into memory)\n\n"
 
 #define HELP_mountpoint "usage: mountpoint [-q] [-d] directory\n       mountpoint [-q] [-x] device\n\n-q       Be quiet, return zero if directory is a mountpoint\n-d  Print major/minor device number of the directory\n-x    Print major/minor device number of the block device\n\n"
 
 #define HELP_lsusb "usage: lsusb\n\nList USB hosts/devices.\n\n"
 
-#define HELP_lspci_text "usage: lspci [-n] [-i FILE ]\n\n-n    Numeric output (repeat for readable and numeric)\n-i    PCI ID database (default /usr/share/misc/pci.ids)\n\n\n"
-
-#define HELP_lspci "usage: lspci [-ekm]\n\nList PCI devices.\n\n-e     Print all 6 digits in class\n-k Print kernel driver\n-m Machine parseable format\n\n"
+#define HELP_lspci "usage: lspci [-ekmn] [-i FILE ] \n\nList PCI devices.\n-e  Print all 6 digits in class\n-i PCI ID database (default /usr/share/misc/pci.ids)\n-k   Print kernel driver\n-m Machine parseable format\n-n    Numeric output (repeat for readable and numeric)\n"
 
 #define HELP_lsmod "usage: lsmod\n\nDisplay the currently loaded modules, their sizes and their dependencies.\n\n"
 
 
 #define HELP_inotifyd "usage: inotifyd PROG FILE[:MASK] ...\n\nWhen a filesystem event matching MASK occurs to a FILE, run PROG as:\n\n  PROG EVENTS FILE [DIRFILE]\n\nIf PROG is \"-\" events are sent to stdout.\n\nThis file is:\n  a  accessed    c  modified    e  metadata change  w  closed (writable)\n  r  opened      D  deleted     M  moved            0  closed (unwritable)\n  u  unmounted   o  overflow    x  unwatchable\n\nA file in this directory is:\n  m  moved in    y  moved out   n  created          d  deleted\n\nWhen x event happens for all FILEs, inotifyd exits (after waiting for PROG).\n\n"
 
-#define HELP_ifconfig "usage: ifconfig [-a] [INTERFACE [ACTION...]]\n\nDisplay or configure network interface.\n\nWith no arguments, display active interfaces. First argument is interface\nto operate on, one argument by itself displays that interface.\n\n-a      Show all interfaces, not just active ones\n\nAdditional arguments are actions to perform on the interface:\n\nADDRESS[/NETMASK] - set IPv4 address (1.2.3.4/5)\ndefault - unset ipv4 address\nadd|del ADDRESS[/PREFIXLEN] - add/remove IPv6 address (1111::8888/128)\nup - enable interface\ndown - disable interface\n\nnetmask|broadcast|pointopoint ADDRESS - set more IPv4 characteristics\nhw ether|infiniband ADDRESS - set LAN hardware address (AA:BB:CC...)\ntxqueuelen LEN - number of buffered packets before output blocks\nmtu LEN - size of outgoing packets (Maximum Transmission Unit)\n\nFlags you can set on an interface (or -remove by prefixing with -):\narp - don't use Address Resolution Protocol to map LAN routes\npromisc - don't discard packets that aren't to this LAN hardware address\nmulticast - force interface into multicast mode if the driver doesn't\nallmulti - promisc for multicast packets\n\nObsolete fields included for historical purposes:\nirq|io_addr|mem_start ADDR - micromanage obsolete hardware\noutfill|keepalive INTEGER - SLIP analog dialup line quality monitoring\nmetric INTEGER - added to Linux 0.9.10 with comment \"never used\", still true\n\n"
-
 #define HELP_hwclock "usage: hwclock [-rswtluf]\n\n-f FILE Use specified device file instead of /dev/rtc (--rtc)\n-l      Hardware clock uses localtime (--localtime)\n-r      Show hardware clock time (--show)\n-s      Set system time from hardware clock (--hctosys)\n-t      Set the system time based on the current timezone (--systz)\n-u      Hardware clock uses UTC (--utc)\n-w      Set hardware clock from system time (--systohc)\n\n"
 
 #define HELP_hostid "usage: hostid\n\nPrint the numeric identifier for the current host.\n\n"
 
 #define HELP_blkid "usage: blkid DEV...\n\nPrints type, label and UUID of filesystem on a block device or image.\n\n"
 
-#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)\n\n"
+#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)\n\n"
 
 #define HELP_acpi "usage: acpi [-abctV]\n\nShow status of power sources and thermal devices.\n\n-a     show power adapters\n-b show batteries\n-c      show cooling device state\n-t   show temperatures\n-V   show everything\n\n"
 
 #define HELP_xzcat "usage: xzcat [filename...]\n\nDecompress listed files to stdout. Use stdin if no files listed.\n\n\n\n"
 
+#define HELP_wget "usage: wget -f filename URL\n-f filename: specify the filename to be saved\nURL: HTTP uniform resource location and only HTTP, not HTTPS\n\nexamples:\n  wget -f index.html http://www.example.com\n  wget -f sample.jpg http://www.example.com:8080/sample.jpg\n\n"
+
 #define HELP_watch "usage: watch [-n SEC] [-t] PROG ARGS\n\nRun PROG periodically\n\n-n  Loop period in seconds (default 2)\n-t  Don't print header\n-e  Freeze updates on command error, and exit after enter.\n\n"
 
 #define HELP_vi "usage: vi FILE\n\nVisual text editor. Predates the existence of standardized cursor keys,\nso the controls are weird and historical.\n\n"
 
 #define HELP_sulogin "usage: sulogin [-t time] [tty]\n\nSingle User Login.\n-t Default Time for Single User Login\n\n"
 
-#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).\n\n"
-
 #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.\n\n"
 
+#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).\n\n"
+
 #define HELP_sh "usage: sh [-c command] [script]\n\nCommand shell.  Runs a shell script, or reads input interactively\nand responds to it.\n\n-c       command line to execute\n-i     interactive mode (default when STDIN is a tty)\n\n"
 
 #define HELP_route "usage: route [-ne] [-A inet[6]] / [add|del]\n\nDisplay/Edit kernel routing tables.\n\n-n   no name lookups\n-e     display other/more information\n-A      inet{6} Select Address Family\n\nreject mod dyn reinstate metric netmask gw mss window irtt dev\n\n"
 
+#define HELP_resize "usage: resize\n\nreset terminal to maximum width and height\n\n\n"
+
 #define HELP_ping "usage: ping [OPTIONS] HOST\n\nCheck network connectivity by sending packets to a host and reporting\nits response.\n\nSend ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each\necho it receives back, with round trip time.\n\nOptions:\n-4, -6      Force IPv4 or IPv6\n-c CNT      Send CNT many packets\n-I IFACE/IP Source interface or address\n-q          Quiet, only displays output at start and when finished\n-s SIZE     Packet SIZE in bytes (default 56)\n-t TTL      Set Time (number of hops) To Live\n-W SEC      Seconds to wait for response after all packets sent (default 10)\n-w SEC      Exit after this many seconds\n\n"
 
 #define HELP_deallocvt "usage: deallocvt [N]\n\nDeallocate unused virtual terminal /dev/ttyN, or all unused consoles.\n\n"
 
 #define HELP_openvt "usage: openvt [-c N] [-sw] [command [command_options]]\n\nstart a program on a new virtual terminal (VT)\n\n-c N  Use VT N\n-s    Switch to new VT\n-w    Wait for command to exit\n\nif -sw used together, switch back to originating VT when command completes\n\n"
 
-#define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information.\n\n-r  Display routing table.\n-a  Display all sockets (Default: Connected).\n-l  Display listening server sockets.\n-t  Display TCP sockets.\n-u  Display UDP sockets.\n-w  Display Raw sockets.\n-x  Display Unix sockets.\n-e  Display other/more information.\n-n  Don't resolve names.\n-W  Wide Display.\n-p  Display PID/Program name for sockets.\n\n"
-
-#define HELP_more "usage: more [FILE]...\n\nView FILE (or stdin) one screenful at a time.\n\n"
+#define HELP_more "usage: more [FILE...]\n\nView FILE(s) (or stdin) one screenful at a time.\n\n"
 
 #define HELP_modprobe "usage: modprobe [-alrqvsDb] MODULE [symbol=value][...]\n\nmodprobe utility - inserts modules and dependencies.\n\n-a  Load multiple MODULEs\n-l  List (MODULE is a pattern)\n-r  Remove MODULE (stacks) or do autoclean\n-q  Quiet\n-v  Verbose\n-s  Log to syslog\n-D  Show dependencies\n-b  Apply blacklist to module names too\n\n"
 
 
 #define HELP_getty "usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]\n\n-h    Enable hardware RTS/CTS flow control\n-L    Set CLOCAL (ignore Carrier Detect state)\n-m    Get baud rate from modem's CONNECT status message\n-n    Don't prompt for login name\n-w    Wait for CR or LF before sending /etc/issue\n-i    Don't display /etc/issue\n-f ISSUE_FILE  Display ISSUE_FILE instead of /etc/issue\n-l LOGIN  Invoke LOGIN instead of /bin/login\n-t SEC    Terminate after SEC if no login name is read\n-I INITSTR  Send INITSTR before anything else\n-H HOST    Log HOST into the utmp file as the hostname\n\n"
 
+#define HELP_getfattr "usage: getfattr [-d] [-h] [-n NAME] FILE...\n\nRead POSIX extended attributes.\n\n-d    Show values as well as names.\n-h       Do not dereference symbolic links.\n-n  Show only attributes with the given name.\n\n"
+
 #define HELP_ftpget "usage: ftpget [-cv] [-u USER -p PASSWORD -P PORT] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME\nusage: ftpput [-v] [-u USER -p PASSWORD -P PORT] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME\n\nftpget - Get a remote file from FTP.\nftpput - Upload a local file on remote machine through FTP.\n\n-c Continue previous transfer.\n-v Verbose.\n-u User name.\n-p Password.\n-P Port Number (default 21).\n\n"
 
 #define HELP_fsck "usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...\n\nCheck and repair filesystems\n\n-A      Walk /etc/fstab and check all filesystems\n-N      Don't execute, just show what would be done\n-P      With -A, check filesystems in parallel\n-R      With -A, skip the root filesystem\n-T      Don't show title on startup\n-V      Verbose\n-C n    Write status information to specified filedescriptor\n-t TYPE List of filesystem types to check\n\n\n"
 
 #define HELP_fold "usage: fold [-bsu] [-w WIDTH] [FILE...]\n\nFolds (wraps) or unfolds ascii text by adding or removing newlines.\nDefault line width is 80 columns for folding and infinite for unfolding.\n\n-b      Fold based on bytes instead of columns\n-s      Fold/unfold at whitespace boundaries if possible\n-u    Unfold text (and refold if -w is given)\n-w     Set lines to WIDTH columns or bytes\n\n"
 
-#define HELP_file "usage: file [file...]\n\nExamine the given files and describe their content types.\n\n"
-
 #define HELP_fdisk "usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK\n\nChange partition table\n\n-u            Start and End are in sectors (instead of cylinders)\n-l            Show partition table for each DISK, then exit\n-b size       sector size (512, 1024, 2048 or 4096)\n-C CYLINDERS  Set number of cylinders/heads/sectors\n-H HEADS\n-S SECTORS\n\n"
 
 #define HELP_expr "usage: expr ARG1 OPERATOR ARG2...\n\nEvaluate expression and print result. For example, \"expr 1 + 2\".\n\nThe supported operators are (grouped from highest to lowest priority):\n\n  ( )    :    * / %    + -    != <= < >= > =    &    |\n\nEach constant and operator must be a separate command line argument.\nAll operators are infix, meaning they expect a constant (or expression\nthat resolves to a constant) on each side of the operator. Operators of\nthe same priority (within each group above) are evaluated left to right.\nParentheses may be used (as separate arguments) to elevate the priority\nof expressions.\n\nCalling expr from a command shell requires a lot of \\( or '*' escaping\nto avoid interpreting shell control characters.\n\nThe & and | operators are logical (not bitwise) and may operate on\nstrings (a blank string is \"false\"). Comparison operators may also\noperate on strings (alphabetical sort).\n\nConstants may be strings or integers. Comparison, logical, and regex\noperators may operate on strings (a blank string is \"false\"), other\noperators require integers.\n\n"
 
 #define HELP_dhcp6 "usage: dhcp6 [-fbnqvR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n\n      Configure network dynamicaly using DHCP.\n\n    -i Interface to use (default eth0)\n    -p Create pidfile\n    -s Run PROG at DHCP events\n    -t Send up to N Solicit packets\n    -T Pause between packets (default 3 seconds)\n    -A Wait N seconds after failure (default 20)\n    -f Run in foreground\n    -b Background if lease is not obtained\n    -n Exit if lease is not obtained\n    -q Exit after obtaining lease\n    -R Release IP on exit\n    -S Log to syslog too\n    -r Request this IP address\n    -v Verbose\n\n    Signals:\n    USR1  Renew current lease\n    USR2  Release current lease\n\n"
 
-#define HELP_dd "usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]\n        [seek=N] [conv=notrunc|noerror|sync|fsync]\n\nOptions:\nif=FILE   Read from FILE instead of stdin\nof=FILE   Write to FILE instead of stdout\nbs=N      Read and write N bytes at a time\nibs=N     Read N bytes at a time\nobs=N     Write N bytes at a time\ncount=N   Copy only N input blocks\nskip=N    Skip N input blocks\nseek=N    Skip N output blocks\nconv=notrunc  Don't truncate output file\nconv=noerror  Continue after read errors\nconv=sync     Pad blocks with zeros\nconv=fsync    Physically write data out before finishing\n\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)\nCopy a file, converting and formatting according to the operands.\n\n"
+#define HELP_dd "usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]\n        [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]\n\nOptions:\nif=FILE   Read from FILE instead of stdin\nof=FILE   Write to FILE instead of stdout\nbs=N      Read and write N bytes at a time\nibs=N     Read N bytes at a time\nobs=N     Write N bytes at a time\ncount=N   Copy only N input blocks\nskip=N    Skip N input blocks\nseek=N    Skip N output blocks\nconv=notrunc  Don't truncate output file\nconv=noerror  Continue after read errors\nconv=sync     Pad blocks with zeros\nconv=fsync    Physically write data out before finishing\nstatus=noxfer Don't show transfer rate\nstatus=none   Don't show transfer rate or records in/out\n\nNumbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),\nMD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).\n\n"
 
 #define HELP_crontab "usage: crontab [-u user] FILE\n               [-u user] [-e | -l | -r]\n               [-c dir]\n\nFiles used to schedule the execution of programs.\n\n-c crontab dir\n-e edit user's crontab\n-l list user's crontab\n-r delete user's crontab\n-u user\nFILE Replace crontab by FILE ('-': stdin)\n\n"
 
 
 #define HELP_compress "usage: compress [-zgLR19] [FILE]\n\nCompress or decompress file (or stdin) using \"deflate\" algorithm.\n\n-1   min compression\n-9     max compression (default)\n-g   gzip (default)\n-L      zlib\n-R        raw\n-z zip\n\n"
 
+#define HELP_chrt "usage: chrt [-m] [-p PID] [POLICY PRIO] [COMMAND [ARGS...]]\n\nGet/set a process' real-time (scheduling) attributes.\n\n-p  Apply to given pid\n-R  Set SCHED_RESET_ON_FORK\n-m     Show min/max priorities available\n\nPolicies:\n  -b  SCHED_BATCH    -f  SCHED_FIFO    -i  SCHED_IDLE\n  -o  SCHED_OTHER    -r  SCHED_RR\n\n"
+
 #define HELP_brctl "usage: brctl COMMAND [BRIDGE [INTERFACE]]\n\nManage ethernet bridges\n\nCommands:\nshow                  Show a list of bridges\naddbr BRIDGE          Create BRIDGE\ndelbr BRIDGE          Delete BRIDGE\naddif BRIDGE IFACE    Add IFACE to BRIDGE\ndelif BRIDGE IFACE    Delete IFACE from BRIDGE\nsetageing BRIDGE TIME Set ageing time\nsetfd BRIDGE TIME     Set bridge forward delay\nsethello BRIDGE TIME  Set hello time\nsetmaxage BRIDGE TIME Set max message age\nsetpathcost BRIDGE PORT COST   Set path cost\nsetportprio BRIDGE PORT PRIO   Set port priority\nsetbridgeprio BRIDGE PRIO      Set bridge priority\nstp BRIDGE [1/yes/on|0/no/off] STP on/off\n\n"
 
 #define HELP_bootchartd "usage: bootchartd {start [PROG ARGS]}|stop|init\n\nCreate /var/log/bootlog.tgz with boot chart data\n\nstart: start background logging; with PROG, run PROG,\n       then kill logging with USR1\nstop:  send USR1 to all bootchartd processes\ninit:  start background logging; stop when getty/xdm is seen\n      (for init scripts)\n\nUnder PID 1: as init, then exec $bootchart_init, /init, /sbin/init\n\n"
 
 #define HELP_pwd "usage: pwd [-L|-P]\n\nPrint working (current) directory.\n\n-L  Use shell's path from $PWD (when applicable)\n-P  Print cannonical absolute path\n\n"
 
-#define HELP_pkill "usage: pkill [-l SIGNAL] [PATTERN]\n\n-l   SIGNAL to send\n-V      verbose\n\n"
-
-#define HELP_pgkill_common "usage: pgrep [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]\n\n-f      Check full command line for PATTERN\n-G Match real Group ID(s)\n-g      Match Process Group(s) (0 is current user)\n-n  Newest match only\n-o   Oldest match only\n-P   Match Parent Process ID(s)\n-s  Match Session ID(s) (0 for current)\n-t Match Terminal(s)\n-U   Match real User ID(s)\n-u       Match effective User ID(s)\n-v  Negate the match\n-x    Match whole command (not substring)\n\n"
+#define HELP_pkill "usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]\n\n-l   Send SIGNAL (default SIGTERM)\n-V       verbose\n\n"
 
 #define HELP_pgrep "usage: pgrep [-cL] [-d DELIM] [-L SIGNAL] [PATTERN]\n\nSearch for process(es). PATTERN is an extended regular expression checked\nagainst command names.\n\n-c     Show only count of matches\n-d  Use DELIM instead of newline\n-L        Send SIGNAL instead of printing name\n-l        Show command name\n\n"
 
-#define HELP_top_common "usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]\n\n-b     Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-n        Exit after NUMBER iterations\n-p        Show these PIDs\n-u     Show these USERs\n-q    Quiet (no header lines)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n\n"
+#define HELP_top_common "usage: * [-bfnoqvx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]\n\n-G     Match real Group ID(s)\n-P      Match Parent Process ID(s)\n-U  Match real User ID(s)\n-b       Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-f        Check full command line for PATTERN\n-g Match Process Group(s) (0 is current user)\n-n  Exit after NUMBER iterations\n-n        Newest match only\n-o   Oldest match only\n-p   Show these PIDs\n-q     Quiet (no header lines)\n-s     Match Session ID(s) (0 for current)\n-t Match Terminal(s)\n-u   Match effective User ID(s)\n-u  Show these USERs\n-v    Negate the match\n-x    Match whole command (not substring)\n\nCursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force\nupdate, R to reverse sort, Q to exit.\n"
 
 #define HELP_iotop "usage: iotop [-AaKO]\n\nRank processes by I/O.\n\n-A       All I/O, not just disk\n-a      Accumulated I/O (not percentage)\n-K    Kilobytes\n-k   Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)\n-O   Only show processes doing I/O\n-o       Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)\n-s  Sort by field number (0-X, default 6)\n\n"
 
-#define HELP_top "usage: top [-m] [ -d seconds ] [ -n iterations ]\n\nShow process activity in real time.\n\n-k        Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)\n-o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)\n-s     Sort by field number (1-X, default 9)\n\n"
+#define HELP_top "usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]\n\nShow process activity in real time.\n\n-H       Show threads\n-k        Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)\n-o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)\n-O     Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)\n-s    Sort by field number (1-X, default 9)\n\n"
 
-#define HELP_ps "usage: ps [-AadeflnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]\n\nList processes.\n\nWhich processes to show (selections may be comma separated lists):\n\n-A    All processes\n-a       Processes with terminals that aren't session leaders\n-d        All processes that aren't session leaders\n-e   Same as -A\n-g  Belonging to GROUPs\n-G Belonging to real GROUPs (before sgid)\n-p      PIDs (--pid)\n-P        Parent PIDs (--ppid)\n-s        In session IDs\n-t      Attached to selected TTYs\n-u   Owned by USERs\n-U      Owned by real USERs (before suid)\n\nOutput modifiers:\n\n-k    Sort FIELDs in +increasing or -decreasting order (--sort)\n-M   Measure field widths (expanding as necessary)\n-n       Show numeric USER and GROUP\n-w Wide output (don't truncate at terminal width)\n\nWhich FIELDs to show. (Default = -o PID,TTY,TIME,CMD)\n\n-f   Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)\n-l  Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)\n-o      Output FIELDs instead of defaults, each with optional :size and =title\n-O  Add FIELDS to defaults\n-Z  Include LABEL\n\nAvailable -o FIELDs:\n\n  ADDR  Instruction pointer               ARGS    Command line (argv[] -path)\n  CMD   COMM without -f, ARGS with -f     CMDLINE Command line (argv[])\n  COMM  Original command name             COMMAND Original command path\n  CPU   Which processor running on        ETIME   Elapsed time since PID start\n  F     Flags (1=FORKNOEXEC 4=SUPERPRIV)  GID     Group id\n  GROUP Group name                        LABEL   Security label\n  MAJFL Major page faults                 MINFL   Minor page faults\n  NAME  Command name (argv[0])            NI      Niceness (lower is faster)\n  PCPU  Percentage of CPU time used       PGID    Process Group ID\n  PID   Process ID                        PPID    Parent Process ID\n  PRI   Priority (higher is faster)       PSR     Processor last executed on\n  RGID  Real (before sgid) group ID       RGROUP  Real (before sgid) group name\n  RSS   Resident Set Size (pages in use)  RTPRIO  Realtime priority\n  RUID  Real (before suid) user ID        RUSER   Real (before suid) user name\n  S     Process state:\n        R (running) S (sleeping) D (device I/O) T (stopped)  t (traced)\n        Z (zombie)  X (deader)   x (dead)       K (wakekill) W (waking)\n  SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)\n  STAT  Process state (S) plus:\n        < high priority          N low priority L locked memory\n        s session leader         + foreground   l multithreaded\n  STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)\n  SZ    Memory Size (4k pages needed to completely swap out process)\n  TIME  CPU time consumed                 TTY     Controlling terminal\n  UID   User id                           USER    User name\n  VSZ   Virtual memory size (1k units)    %VSZ    VSZ as % of physical memory\n  WCHAN Waiting in kernel for\n\n"
+#define HELP_ps "usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]\n\nList processes.\n\nWhich processes to show (selections may be comma separated lists):\n\n-A   All processes\n-a       Processes with terminals that aren't session leaders\n-d        All processes that aren't session leaders\n-e   Same as -A\n-g  Belonging to GROUPs\n-G Belonging to real GROUPs (before sgid)\n-p      PIDs (--pid)\n-P        Parent PIDs (--ppid)\n-s        In session IDs\n-t      Attached to selected TTYs\n-T   Show threads\n-u        Owned by USERs\n-U      Owned by real USERs (before suid)\n\nOutput modifiers:\n\n-k    Sort FIELDs in +increasing or -decreasting order (--sort)\n-M   Measure field widths (expanding as necessary)\n-n       Show numeric USER and GROUP\n-w Wide output (don't truncate at terminal width)\n\nWhich FIELDs to show. (Default = -o PID,TTY,TIME,CMD)\n\n-f   Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)\n-l     Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)\n-o      Output FIELDs instead of defaults, each with optional :size and =title\n-O      Add FIELDS to defaults\n-Z      Include LABEL\n\nCommand line -o fields:\n\n  ARGS     CMDLINE minus initial path     CMD  Command (thread) name (stat[2])\n  CMDLINE  Command line (argv[])          COMM Command filename (/proc/$PID/exe)\n  COMMAND  Command file (/proc/$PID/exe)  NAME Process name (argv[0] of $PID)\n\nProcess attribute -o FIELDs:\n\n  ADDR  Instruction pointer               BIT   Is this process 32 or 64 bits\n  CPU   Which processor running on        ETIME   Elapsed time since PID start\n  F     Flags (1=FORKNOEXEC 4=SUPERPRIV)  GID     Group id\n  GROUP Group name                        LABEL   Security label\n  MAJFL Major page faults                 MINFL   Minor page faults\n  NI    Niceness (lower is faster)\n  PCPU  Percentage of CPU time used       PCY     Android scheduling policy\n  PGID  Process Group ID\n  PID   Process ID                        PPID    Parent Process ID\n  PRI   Priority (higher is faster)       PSR     Processor last executed on\n  RGID  Real (before sgid) group ID       RGROUP  Real (before sgid) group name\n  RSS   Resident Set Size (pages in use)  RTPRIO  Realtime priority\n  RUID  Real (before suid) user ID        RUSER   Real (before suid) user name\n  S     Process state:\n        R (running) S (sleeping) D (device I/O) T (stopped)  t (traced)\n        Z (zombie)  X (deader)   x (dead)       K (wakekill) W (waking)\n  SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)\n  STAT  Process state (S) plus:\n        < high priority          N low priority L locked memory\n        s session leader         + foreground   l multithreaded\n  STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)\n  SZ    Memory Size (4k pages needed to completely swap out process)\n  TCNT  Thread count                      TID     Thread ID\n  TIME  CPU time consumed                 TTY     Controlling terminal\n  UID   User id                           USER    User name\n  VSZ   Virtual memory size (1k units)    %VSZ    VSZ as % of physical memory\n  WCHAN What are we waiting in kernel for\n\n"
 
 #define HELP_printf "usage: printf FORMAT [ARGUMENT...]\n\nFormat and print ARGUMENT(s) according to FORMAT, using C printf syntax\n(% escapes for cdeEfgGiosuxX, \\ escapes for abefnrtv0 or \\OCTAL or \\xHEX).\n\n"
 
-#define HELP_patch "usage: patch [-i file] [-p depth] [-Ru]\n\nApply a unified diff to one or more files.\n\n-i        Input file (defaults=stdin)\n-l Loose match (ignore whitespace)\n-p     Number of '/' to strip from start of file paths (default=all)\n-R       Reverse patch.\n-u      Ignored (only handles \"unified\" diffs)\n\nThis version of patch only handles unified diffs, and only modifies\na file when all all hunks to that file apply.  Patch prints failed\nhunks to stderr, and exits with nonzero status if any hunks fail.\n\nA file compared against /dev/null (or with a date <= the epoch) is\ncreated/deleted as appropriate.\n\n"
+#define HELP_patch "usage: patch [-d DIR] [-i file] [-p depth] [-Rlu] [--dry-run]\n\nApply a unified diff to one or more files.\n\n-d  modify files in DIR\n-i Input file (defaults=stdin)\n-l Loose match (ignore whitespace)\n-p     Number of '/' to strip from start of file paths (default=all)\n-R       Reverse patch.\n-u      Ignored (only handles \"unified\" diffs)\n--dry-run Don't change files, just confirm patch applies\n\nThis version of patch only handles unified diffs, and only modifies\na file when all all hunks to that file apply.  Patch prints failed\nhunks to stderr, and exits with nonzero status if any hunks fail.\n\nA file compared against /dev/null (or with a date <= the epoch) is\ncreated/deleted as appropriate.\n\n"
 
 #define HELP_paste "usage: paste [-s] [-d list] [file...]\n\nReplace newlines in files.\n\n-d list    list of delimiters to separate lines\n-s         process files sequentially instead of in parallel\n\nBy default print corresponding lines separated by <tab>.\n\n"
 
-#define HELP_od "usage: od [-bcdosxv] [-j #] [-N #] [-A doxn] [-t acdfoux[#]]\n\n-A    Address base (decimal, octal, hexdecimal, none)\n-j     Skip this many bytes of input\n-N       Stop dumping after this many bytes\n-t  output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x\n plus optional size in bytes\n   aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2\n-v  Don't collapse repeated lines together\n\n"
+#define HELP_od "usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]]\n\n-A     Address base (decimal, octal, hexdecimal, none)\n-j     Skip this many bytes of input\n-N       Stop dumping after this many bytes\n-t  Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x\n plus optional size in bytes\n   aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2\n-v  Don't collapse repeated lines together\n-w      Total line width in bytes (default 16).\n\n"
 
 #define HELP_nohup "usage: nohup COMMAND [ARGS...]\n\nRun a command that survives the end of its terminal.\n\nRedirect tty on stdin to /dev/null, tty on stdout to \"nohup.out\".\n\n"
 
 
 #define HELP_mkdir "usage: mkdir [-vp] [-m mode] [dirname...]\n\nCreate one or more directories.\n\n-m set permissions of directory to mode.\n-p       make parent directories as needed.\n-v  verbose\n\n"
 
-#define HELP_ls_color "--color  device=yellow  symlink=turquoise/red  dir=blue  socket=purple\n         files: exe=green  suid=red  suidfile=redback  stickydir=greenback\n         =auto means detect if output is a tty.\n\nusage: ls --color[=auto] [-ACFHLRSZacdfhiklmnpqrstux1] [directory...]\n\nlist files\n\nwhat to show:\n-a all files including .hidden             -c  use ctime for timestamps\n-d        directory, not contents                 -i  inode number\n-k    block sizes in kilobytes                -p  put a '/' after dir names\n-q       unprintable chars as '?'                -s  size (in blocks)\n-u        use access time for timestamps          -A  list all files but . and ..\n-H     follow command line symlinks            -L  follow symlinks\n-R recursively list files in subdirs       -F  append /dir *exe @sym |FIFO\n-Z     security context\n\noutput formats:\n-1 list one file per line                  -C  columns (sorted vertically)\n-g     like -l but no owner                    -h  human readable sizes\n-l    long (show full details)                -m  comma separated\n-n like -l but numeric uid/gid             -o  like -l but no group\n-x    columns (horizontal sort)\n\nsorting (default is alphabetical):\n-f     unsorted        -r  reverse     -t  timestamp   -S  size\n"
+#define HELP_ls_color "--color  device=yellow  symlink=turquoise/red  dir=blue  socket=purple\n         files: exe=green  suid=red  suidfile=redback  stickydir=greenback\n         =auto means detect if output is a tty.\n\nusage: ls --color[=auto] [-ACFHLRSZacdfhiklmnpqrstux1] [directory...]\n\nlist files\n\nwhat to show:\n-a  all files including .hidden    -b  escape nongraphic chars\n-c  use ctime for timestamps       -d  directory, not contents\n-i  inode number                   -p  put a '/' after dir names\n-q  unprintable chars as '?'       -s  storage used (1024 byte units)\n-u  use access time for timestamps -A  list all files but . and ..\n-H  follow command line symlinks   -L  follow symlinks\n-R  recursively list in subdirs    -F  append /dir *exe @sym |FIFO\n-Z  security context\n\noutput formats:\n-1  list one file per line         -C  columns (sorted vertically)\n-g  like -l but no owner           -h  human readable sizes\n-l  long (show full details)       -m  comma separated\n-n  like -l but numeric uid/gid    -o  like -l but no group\n-x  columns (horizontal sort)\n\nsorting (default is alphabetical):\n-f  unsorted    -r  reverse    -t  timestamp    -S  size\n"
 
-#define HELP_ls "usage: ls --color[=auto] [-ACFHLRSZacdfhiklmnpqrstux1] [directory...]\n\nlist files\n\nwhat to show:\n-a      all files including .hidden             -c  use ctime for timestamps\n-d        directory, not contents                 -i  inode number\n-k    block sizes in kilobytes                -p  put a '/' after dir names\n-q       unprintable chars as '?'                -s  size (in blocks)\n-u        use access time for timestamps          -A  list all files but . and ..\n-H     follow command line symlinks            -L  follow symlinks\n-R recursively list files in subdirs       -F  append /dir *exe @sym |FIFO\n-Z     security context\n\noutput formats:\n-1 list one file per line                  -C  columns (sorted vertically)\n-g     like -l but no owner                    -h  human readable sizes\n-l    long (show full details)                -m  comma separated\n-n like -l but numeric uid/gid             -o  like -l but no group\n-x    columns (horizontal sort)\n\nsorting (default is alphabetical):\n-f     unsorted        -r  reverse     -t  timestamp   -S  size\n--color  device=yellow  symlink=turquoise/red  dir=blue  socket=purple\n         files: exe=green  suid=red  suidfile=redback  stickydir=greenback\n         =auto means detect if output is a tty.\n\n"
+#define HELP_ls "usage: ls --color[=auto] [-ACFHLRSZacdfhiklmnpqrstux1] [directory...]\n\nlist files\n\nwhat to show:\n-a  all files including .hidden    -b  escape nongraphic chars\n-c  use ctime for timestamps       -d  directory, not contents\n-i  inode number                   -p  put a '/' after dir names\n-q  unprintable chars as '?'       -s  storage used (1024 byte units)\n-u  use access time for timestamps -A  list all files but . and ..\n-H  follow command line symlinks   -L  follow symlinks\n-R  recursively list in subdirs    -F  append /dir *exe @sym |FIFO\n-Z  security context\n\noutput formats:\n-1  list one file per line         -C  columns (sorted vertically)\n-g  like -l but no owner           -h  human readable sizes\n-l  long (show full details)       -m  comma separated\n-n  like -l but numeric uid/gid    -o  like -l but no group\n-x  columns (horizontal sort)\n\nsorting (default is alphabetical):\n-f  unsorted    -r  reverse    -t  timestamp    -S  size\n--color  device=yellow  symlink=turquoise/red  dir=blue  socket=purple\n         files: exe=green  suid=red  suidfile=redback  stickydir=greenback\n         =auto means detect if output is a tty.\n\n"
 
 #define HELP_ln "usage: ln [-sfnv] [FROM...] TO\n\nCreate a link between FROM and TO.\nWith only one argument, create link in current directory.\n\n-s Create a symbolic link\n-f      Force the creation of the link, even if TO already exists\n-n   Symlink at destination treated as file\n-v      Verbose\n\n"
 
 
 #define HELP_find "usage: find [-HL] [DIR...] [<options>]\n\nSearch directories for matching files.\nDefault: search \".\" match all -print all matches.\n\n-H  Follow command line symlinks         -L  Follow all symlinks\n\nMatch filters:\n-name  PATTERN  filename with wildcards   -iname      case insensitive -name\n-path  PATTERN  path name with wildcards  -ipath      case insensitive -path\n-user  UNAME    belongs to user UNAME     -nouser     user ID not known\n-group GROUP    belongs to group GROUP    -nogroup    group ID not known\n-perm  [-/]MODE permissions (-=min /=any) -prune      ignore contents of dir\n-size  N[c]     512 byte blocks (c=bytes) -xdev       only this filesystem\n-links N        hardlink count            -atime N    accessed N days ago\n-ctime N        created N days ago        -mtime N    modified N days ago\n-newer FILE     newer mtime than FILE     -mindepth # at least # dirs down\n-depth          ignore contents of dir    -maxdepth # at most # dirs down\n-inum  N        inode number N            -empty      empty files and dirs\n-type [bcdflps] (block, char, dir, file, symlink, pipe, socket)\n\nNumbers N may be prefixed by a - (less than) or + (greater than):\n\nCombine matches with:\n!, -a, -o, ( )    not, and, or, group expressions\n\nActions:\n-print   Print match with newline  -print0    Print match with null\n-exec    Run command with path     -execdir   Run command in file's dir\n-ok      Ask before exec           -okdir     Ask before execdir\n-delete  Remove matching file/dir\n\nCommands substitute \"{}\" with matched file. End with \";\" to run each file,\nor \"+\" (next argument after \"{}\") to collect and run with multiple files.\n\n"
 
+#define HELP_file "usage: file [-hL] [file...]\n\nExamine the given files and describe their content types.\n\n-h      don't follow symlinks (default)\n-L     follow symlinks\n\n"
+
 #define HELP_false "Return nonzero.\n\n"
 
 #define HELP_expand "usage: expand [-t TABLIST] [FILE...]\n\nExpand tabs to spaces according to tabstops.\n\n-t        TABLIST\n\nSpecify tab stops, either a single number instead of the default 8,\nor a comma separated list of increasing numbers representing tabstop\npositions (absolute, not increments) with each additional tab beyound\nthat becoming one space.\n\n"
 
 #define HELP_install "usage: install [-dDpsv] [-o USER] [-g GROUP] [-m MODE] [SOURCE...] DEST\n\nCopy files and set attributes.\n\n-d  Act like mkdir -p\n-D   Create leading directories for DEST\n-g Make copy belong to GROUP\n-m   Set permissions to MODE\n-o     Make copy belong to USER\n-p    Preserve timestamps\n-s Call \"strip -p\"\n-v   Verbose\n\n"
 
-#define HELP_mv "usage: mv [-finv] SOURCE... DEST\"\n\n-f      force copy by deleting destination file\n-i     interactive, prompt before overwriting existing DEST\n-n        no clobber (don't overwrite DEST)\n-v   verbose\n"
+#define HELP_mv "usage: mv [-fivn] SOURCE... DEST\"\n\n-f      force copy by deleting destination file\n-i     interactive, prompt before overwriting existing DEST\n-v        verbose\n-n     no clobber (don't overwrite DEST)\n\n"
 
-#define HELP_cp_preserve "-i   interactive, prompt before overwriting existing DEST\n-l        hard link instead of copy\n-n   no clobber (don't overwrite DEST)\n-p   preserve timestamps, ownership, and mode\n-r    synonym for -R\n-s      symlink instead of copy\n-v     verbose\nletter(s) of:\n\n        mode - permissions (ignore umask for rwx, copy suid and sticky bit)\n   ownership - user and group\n  timestamps - file creation, modification, and access times.\n     context - security context\n       xattr - extended attributes\n         all - all of the above\nusage: cp [--preserve=motcxa] [-adlnrsv] [-fipRHLP] SOURCE... DEST\n\nCopy files from SOURCE to DEST.  If more than one SOURCE, DEST must\nbe a directory.\n\n--preserve takes either a comma separated list of attributes, or the first\n-F delete any existing destination file first (--remove-destination)\n-H   Follow symlinks listed on command line\n-L      Follow all symlinks\n-P Do not follow symlinks [default]\n-R    recurse into subdirectories (DEST must be a directory)\n-a      same as -dpr\n-d        don't dereference symlinks\n-f  delete destination files we can't write to\n"
+#define HELP_cp_preserve "--preserve takes either a comma separated list of attributes, or the first\nletter(s) of:\n\n        mode - permissions (ignore umask for rwx, copy suid and sticky bit)\n   ownership - user and group\n  timestamps - file creation, modification, and access times.\n     context - security context\n       xattr - extended attributes\n         all - all of the above\n\nusage: cp [--preserve=motcxa] [-adlnrsvfipRHLP] SOURCE... DEST\n\nCopy files from SOURCE to DEST.  If more than one SOURCE, DEST must\nbe a directory.\n-v   verbose\n-s     symlink instead of copy\n-r     synonym for -R\n-n      no clobber (don't overwrite DEST)\n-l   hard link instead of copy\n-d   don't dereference symlinks\n-a  same as -dpr\n-P        Do not follow symlinks [default]\n-L    Follow all symlinks\n-H Follow symlinks listed on command line\n-R      recurse into subdirectories (DEST must be a directory)\n-p      preserve timestamps, ownership, and mode\n-i    interactive, prompt before overwriting existing DEST\n-F        delete any existing destination file first (--remove-destination)\n-f   delete destination files we can't write to\n"
 
-#define HELP_cp "usage: cp [--preserve=motcxa] [-adlnrsv] [-fipRHLP] SOURCE... DEST\n\nCopy files from SOURCE to DEST.  If more than one SOURCE, DEST must\nbe a directory.\n\n--preserve takes either a comma separated list of attributes, or the first\n-F  delete any existing destination file first (--remove-destination)\n-H   Follow symlinks listed on command line\n-L      Follow all symlinks\n-P Do not follow symlinks [default]\n-R    recurse into subdirectories (DEST must be a directory)\n-a      same as -dpr\n-d        don't dereference symlinks\n-f  delete destination files we can't write to\n-i  interactive, prompt before overwriting existing DEST\n-l        hard link instead of copy\n-n   no clobber (don't overwrite DEST)\n-p   preserve timestamps, ownership, and mode\n-r    synonym for -R\n-s      symlink instead of copy\n-v     verbose\nletter(s) of:\n\n        mode - permissions (ignore umask for rwx, copy suid and sticky bit)\n   ownership - user and group\n  timestamps - file creation, modification, and access times.\n     context - security context\n       xattr - extended attributes\n         all - all of the above\n"
+#define HELP_cp "usage: cp [--preserve=motcxa] [-adlnrsvfipRHLP] SOURCE... DEST\n\nCopy files from SOURCE to DEST.  If more than one SOURCE, DEST must\nbe a directory.\n-v    verbose\n-s     symlink instead of copy\n-r     synonym for -R\n-n      no clobber (don't overwrite DEST)\n-l   hard link instead of copy\n-d   don't dereference symlinks\n-a  same as -dpr\n-P        Do not follow symlinks [default]\n-L    Follow all symlinks\n-H Follow symlinks listed on command line\n-R      recurse into subdirectories (DEST must be a directory)\n-p      preserve timestamps, ownership, and mode\n-i    interactive, prompt before overwriting existing DEST\n-F        delete any existing destination file first (--remove-destination)\n-f   delete destination files we can't write to\n--preserve takes either a comma separated list of attributes, or the first\nletter(s) of:\n\n        mode - permissions (ignore umask for rwx, copy suid and sticky bit)\n   ownership - user and group\n  timestamps - file creation, modification, and access times.\n     context - security context\n       xattr - extended attributes\n         all - all of the above\n\n"
 
 #define HELP_comm "usage: comm [-123] FILE1 FILE2\n\nReads FILE1 and FILE2, which should be ordered, and produces three text\ncolumns as output: lines only in FILE1; lines only in FILE2; and lines\nin both files. Filename \"-\" is a synonym for stdin.\n\n-1 suppress the output column of lines unique to FILE1\n-2 suppress the output column of lines unique to FILE2\n-3 suppress the output column of lines duplicated in FILE1 and FILE2\n\n"
 
index 6ad9ea1..385fc90 100644 (file)
@@ -1,13 +1,13 @@
 USE_TOYBOX(NEWTOY(toybox, NULL, TOYFLAG_STAYROOT))
 USE_SH(OLDTOY(-sh, sh, 0))
 USE_SH(OLDTOY(-toysh, sh, 0))
-USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK))
+USE_TRUE(OLDTOY(:, true, 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))
 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_BASE64(NEWTOY(base64, "diw#<1[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_BASENAME(NEWTOY(basename, "<1>2", TOYFLAG_USR|TOYFLAG_BIN))
 USE_BLKID(NEWTOY(blkid, 0, TOYFLAG_BIN))
 USE_BLOCKDEV(NEWTOY(blockdev, "<1>1(setro)(setrw)(getro)(getss)(getbsz)(setbsz)#<0(getsz)(getsize)(getsize64)(flushbufs)(rereadpt)",TOYFLAG_USR|TOYFLAG_BIN))
@@ -25,14 +25,15 @@ USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv[-HLP]", TOYFLAG_BIN))
 USE_CHMOD(NEWTOY(chmod, "<2?vRf[-vf]", TOYFLAG_BIN))
 USE_CHOWN(OLDTOY(chown, chgrp, TOYFLAG_BIN))
 USE_CHROOT(NEWTOY(chroot, "^<1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_CHRT(NEWTOY(chrt, "mp#bfiorR[!bfior]", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_CHVT(NEWTOY(chvt, "<1", TOYFLAG_USR|TOYFLAG_BIN))
 USE_CKSUM(NEWTOY(cksum, "HIPLN", TOYFLAG_BIN))
 USE_CLEAR(NEWTOY(clear, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_CMP(NEWTOY(cmp, "<2>2ls", TOYFLAG_USR|TOYFLAG_BIN))
+USE_CMP(NEWTOY(cmp, "<2>2ls[!ls]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_COMM(NEWTOY(comm, "<2>2321", TOYFLAG_USR|TOYFLAG_BIN))
 USE_COMPRESS(NEWTOY(compress, "zcd9lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_COUNT(NEWTOY(count, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
 USE_CPIO(NEWTOY(cpio, "(no-preserve-owner)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
 USE_CROND(NEWTOY(crond, "fbSl#<0=8d#<0L:c:[-bf][-LS][-ld]", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_CRONTAB(NEWTOY(crontab, "c:u:elr[!elr]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
@@ -61,10 +62,10 @@ USE_EXPAND(NEWTOY(expand, "t*", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_FACTOR(NEWTOY(factor, 0, TOYFLAG_USR|TOYFLAG_BIN))
 USE_FALLOCATE(NEWTOY(fallocate, ">1l#|", TOYFLAG_USR|TOYFLAG_BIN))
-USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN))
+USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN|TOYFLAG_NOHELP))
 USE_FDISK(NEWTOY(fdisk, "C#<0H#<0S#<0b#<512ul", TOYFLAG_SBIN))
 USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
-USE_FILE(NEWTOY(file, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+USE_FILE(NEWTOY(file, "<1hL[!hL]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_FLOCK(NEWTOY(flock, "<1>1nsux[-sux]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_FOLD(NEWTOY(fold, "bsuw#<1", TOYFLAG_USR|TOYFLAG_BIN))
@@ -77,6 +78,7 @@ USE_FSYNC(NEWTOY(fsync, "<1d", TOYFLAG_BIN))
 USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P#<0=21>65535", TOYFLAG_BIN))
 USE_FTPGET(OLDTOY(ftpput, ftpget, TOYFLAG_BIN))
 USE_GETENFORCE(NEWTOY(getenforce, ">0", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_GETFATTR(NEWTOY(getfattr, "dhn:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_GETPROP(NEWTOY(getprop, ">2Z", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh",TOYFLAG_SBIN))
 USE_GREP(NEWTOY(grep, "C#B#A#ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
@@ -92,7 +94,7 @@ USE_HELP(NEWTOY(help, ""USE_HELP_EXTRAS("ah"), TOYFLAG_BIN))
 USE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_HOST(NEWTOY(host, "<1>2avt:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_HOSTID(NEWTOY(hostid, ">0", TOYFLAG_USR|TOYFLAG_BIN))
-USE_HOSTNAME(NEWTOY(hostname, NULL, TOYFLAG_BIN))
+USE_HOSTNAME(NEWTOY(hostname, "bF:", TOYFLAG_BIN))
 USE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_ICONV(NEWTOY(iconv, "cst:f:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_ID(NEWTOY(id, ">1"USE_ID_Z("Z")"nGgru[!"USE_ID_Z("Z")"Ggu]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -120,18 +122,19 @@ USE_LAST(NEWTOY(last, "f:W", TOYFLAG_BIN))
 USE_LINK(NEWTOY(link, "<2>2", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LN(NEWTOY(ln, "<1vnfs", TOYFLAG_BIN))
 USE_LOAD_POLICY(NEWTOY(load_policy, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_LOG(NEWTOY(log, "<1p:t:", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdca[!afj]", TOYFLAG_SBIN))
-USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSacdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSabcdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN))
 USE_LSMOD(NEWTOY(lsmod, NULL, TOYFLAG_SBIN))
-USE_LSOF(NEWTOY(lsof, "lp:t", TOYFLAG_USR|TOYFLAG_BIN))
+USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
 USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN))
 USE_LSUSB(NEWTOY(lsusb, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_MAKEDEVS(NEWTOY(makedevs, "<1>1d:", TOYFLAG_USR|TOYFLAG_BIN))
-USE_MD5SUM(NEWTOY(md5sum, "b", TOYFLAG_USR|TOYFLAG_BIN))
+USE_MD5SUM(NEWTOY(md5sum, "bc*[!bc]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_MDEV(NEWTOY(mdev, "s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_UMASK))
 USE_MIX(NEWTOY(mix, "c:d:l#r#", TOYFLAG_USR|TOYFLAG_BIN))
 USE_MKDIR(NEWTOY(mkdir, "<1"USE_MKDIR_Z("Z:")"vpm:", TOYFLAG_BIN|TOYFLAG_UMASK))
@@ -143,10 +146,10 @@ USE_MKSWAP(NEWTOY(mkswap, "<1>1L:", TOYFLAG_SBIN))
 USE_MKTEMP(NEWTOY(mktemp, ">1uqd(directory)p(tmpdir):", TOYFLAG_BIN))
 USE_MODINFO(NEWTOY(modinfo, "<1b:k:F:0", TOYFLAG_BIN))
 USE_MODPROBE(NEWTOY(modprobe, "alrqvsDb", TOYFLAG_SBIN))
-USE_MORE(NEWTOY(more, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN))
 USE_MOUNT(NEWTOY(mount, "?O:afnrvwt:o*[-rw]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
 USE_MOUNTPOINT(NEWTOY(mountpoint, "<1qdx[-dx]", TOYFLAG_BIN))
-USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN))
 USE_NBD_CLIENT(OLDTOY(nbd-client, nbd_client, TOYFLAG_USR|TOYFLAG_BIN))
 USE_NBD_CLIENT(NEWTOY(nbd_client, "<3>3ns", 0))
 USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
@@ -157,23 +160,23 @@ USE_NL(NEWTOY(nl, "v#<1=1l#b:n:s:w#<0=6E", TOYFLAG_BIN))
 USE_NOHUP(NEWTOY(nohup, "<1^", TOYFLAG_USR|TOYFLAG_BIN))
 USE_NPROC(NEWTOY(nproc, "(all)", TOYFLAG_USR|TOYFLAG_BIN))
 USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
-USE_OD(NEWTOY(od, "j#vN#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
+USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
 USE_ONEIT(NEWTOY(oneit, "^<1nc:p3[!pn]", TOYFLAG_SBIN))
 USE_OPENVT(NEWTOY(openvt, "c#<1>63sw", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 USE_PARTPROBE(NEWTOY(partprobe, "<1", TOYFLAG_SBIN))
 USE_PASSWD(NEWTOY(passwd, ">1a:dlu", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
 USE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_BIN))
-USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PATCH(NEWTOY(patch, "(dry-run)"USE_TOYBOX_DEBUG("x")"d:ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_BIN))
 USE_PING(NEWTOY(ping, "<1>1t#<0>255c#<0s#<0>65535I:W#<0w#<0q46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
 USE_PIVOT_ROOT(NEWTOY(pivot_root, "<2>2", TOYFLAG_SBIN))
-USE_PKILL(NEWTOY(pkill,     "Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PKILL(NEWTOY(pkill,    "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PMAP(NEWTOY(pmap, "<1xq", TOYFLAG_BIN))
 USE_REBOOT(OLDTOY(poweroff, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_PRINTENV(NEWTOY(printenv, "0(null)", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PRINTF(NEWTOY(printf, "<1?^", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_PWD(NEWTOY(pwd, ">0LP[-LP]", TOYFLAG_BIN))
 USE_PWDX(NEWTOY(pwdx, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
 USE_READAHEAD(NEWTOY(readahead, NULL, TOYFLAG_BIN))
@@ -182,6 +185,7 @@ USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN))
 USE_REBOOT(NEWTOY(reboot, "fn", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_RENICE(NEWTOY(renice, "<1gpun#|", TOYFLAG_USR|TOYFLAG_BIN))
 USE_RESET(NEWTOY(reset, 0, TOYFLAG_USR|TOYFLAG_BIN))
+USE_RESIZE(NEWTOY(resize, "c#<1>63sw", TOYFLAG_BIN|TOYFLAG_USR))
 USE_RESTORECON(NEWTOY(restorecon, "<1DFnRrv", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_REV(NEWTOY(rev, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_RFKILL(NEWTOY(rfkill, "<1>2", TOYFLAG_USR|TOYFLAG_SBIN))
@@ -191,19 +195,27 @@ USE_RMMOD(NEWTOY(rmmod, "<1wf", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
 USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
 USE_RUNCON(NEWTOY(runcon, "<2", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_SED(NEWTOY(sed, "(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+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_SETENFORCE(NEWTOY(setenforce, "<1>1", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SETPROP(NEWTOY(setprop, "<2>2", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN))
-USE_SHA1SUM(NEWTOY(sha1sum, "b", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA1SUM(NEWTOY(sha1sum, "bc*[!bc]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA224SUM(OLDTOY(sha224sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA256SUM(OLDTOY(sha256sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA384SUM(OLDTOY(sha384sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA512SUM(OLDTOY(sha512sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
 USE_SHRED(NEWTOY(shred, "<1zxus#<1n#<1o#<0f", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SKELETON(NEWTOY(skeleton, "(walrus)(blubber):;(also):e@d*c#b:a", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SKELETON_ALIAS(NEWTOY(skeleton_alias, "b#dq", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SLEEP(NEWTOY(sleep, "<1", TOYFLAG_BIN))
 USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")USE_SORT_BIG("S:T:m" "o:k*t:xbMcszdfi") "run", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SPLIT(NEWTOY(split, ">2a#<1=2>9b#<1l#<1", TOYFLAG_USR|TOYFLAG_BIN))
-USE_STAT(NEWTOY(stat, "<1c:f", TOYFLAG_BIN)) 
+USE_START(NEWTOY(start, "", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_STAT(NEWTOY(stat, "<1c:fLt", TOYFLAG_BIN)) 
+USE_STOP(NEWTOY(stop, "", TOYFLAG_USR|TOYFLAG_SBIN))
 USE_STRINGS(NEWTOY(strings, "an#=4<1fo", TOYFLAG_USR|TOYFLAG_BIN))
 USE_SU(NEWTOY(su, "lmpc:s:", TOYFLAG_BIN|TOYFLAG_ROOTONLY))
 USE_SULOGIN(NEWTOY(sulogin, "t#<0=0", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
@@ -222,22 +234,23 @@ USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TELNET(NEWTOY(telnet, "<1>2", TOYFLAG_BIN))
 USE_TELNETD(NEWTOY(telnetd, "w#<0b:p#<0>65535=23f:l:FSKi[!wi]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TEST(NEWTOY(test, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_TEST_HUMAN_READABLE(NEWTOY(test_human_readable, "<1>1ibs", 0))
-USE_TEST_MANY_OPTIONS(NEWTOY(test_many_options, "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba", TOYFLAG_USR|TOYFLAG_BIN))
-USE_TEST_SCANKEY(NEWTOY(test_scankey, 0, 0))
+USE_TEST_HUMAN_READABLE(NEWTOY(test_human_readable, "<1>1ibs", TOYFLAG_BIN))
+USE_TEST_MANY_OPTIONS(NEWTOY(test_many_options, "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba", TOYFLAG_BIN))
+USE_TEST_SCANKEY(NEWTOY(test_scankey, 0, TOYFLAG_BIN))
 USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
 USE_TIME(NEWTOY(time, "<1^p", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_BIN))
-USE_TOP(NEWTOY(top, ">0m" "k*o*p*u*s#<1=9d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_TOUCH(NEWTOY(touch, "acd:mr:t:h[!dtr]", TOYFLAG_BIN))
 USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN))
 USE_TR(NEWTOY(tr, "^>2<1Ccsd[+cC]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_TRACEROUTE(NEWTOY(traceroute, "<1>2i:f#<1>255=1z#<0>86400=0g*w#<0>86400=5t#<0>255=0s:q#<1>255=3p#<1>65535=33434m#<1>255=30rvndlIUF64", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
 USE_TRACEROUTE(OLDTOY(traceroute6,traceroute, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
-USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN))
+USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN|TOYFLAG_NOHELP))
 USE_TRUNCATE(NEWTOY(truncate, "<1s:|c", TOYFLAG_BIN))
 USE_TTY(NEWTOY(tty, "s", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TUNCTL(NEWTOY(tunctl, "<1>1t|d|u:T[!td]", TOYFLAG_USR|TOYFLAG_BIN))
 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, "ndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
@@ -257,12 +270,13 @@ USE_VI(NEWTOY(vi, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
 USE_VMSTAT(NEWTOY(vmstat, ">2n", TOYFLAG_BIN))
 USE_W(NEWTOY(w, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_WATCH(NEWTOY(watch, "^<1n#<0=2te", TOYFLAG_USR|TOYFLAG_BIN))
-USE_WC(NEWTOY(wc, USE_TOYBOX_I18N("m")"cwl[!cm]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN))
 USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
 USE_WHO(NEWTOY(who, "a", TOYFLAG_USR|TOYFLAG_BIN))
 USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN))
 USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2pr", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_XZCAT(NEWTOY(xzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 USE_ZCAT(NEWTOY(zcat, 0, TOYFLAG_USR|TOYFLAG_BIN))
index e1c957b..31e7773 100644 (file)
 #define _PS_SCH                                     (1<<14)
 #define PS_CPU                                      15
 #define _PS_CPU                                     (1<<15)
-#define PS_COMM                                     16
-#define _PS_COMM                                    (1<<16)
-#define PS_TTY                                      17
-#define _PS_TTY                                     (1<<17)
-#define PS_WCHAN                                    18
-#define _PS_WCHAN                                   (1<<18)
-#define PS_LABEL                                    19
-#define _PS_LABEL                                   (1<<19)
-#define PS_COMMAND                                  20
-#define _PS_COMMAND                                 (1<<20)
-#define PS_CMDLINE                                  21
-#define _PS_CMDLINE                                 (1<<21)
-#define PS_ARGS                                     22
-#define _PS_ARGS                                    (1<<22)
+#define PS_TID                                      16
+#define _PS_TID                                     (1<<16)
+#define PS_TCNT                                     17
+#define _PS_TCNT                                    (1<<17)
+#define PS_BIT                                      18
+#define _PS_BIT                                     (1<<18)
+#define PS_TTY                                      19
+#define _PS_TTY                                     (1<<19)
+#define PS_WCHAN                                    20
+#define _PS_WCHAN                                   (1<<20)
+#define PS_LABEL                                    21
+#define _PS_LABEL                                   (1<<21)
+#define PS_COMM                                     22
+#define _PS_COMM                                    (1<<22)
 #define PS_NAME                                     23
 #define _PS_NAME                                    (1<<23)
-#define PS_CMD                                      24
-#define _PS_CMD                                     (1<<24)
-#define PS_UID                                      25
-#define _PS_UID                                     (1<<25)
-#define PS_USER                                     26
-#define _PS_USER                                    (1<<26)
-#define PS_RUID                                     27
-#define _PS_RUID                                    (1<<27)
-#define PS_RUSER                                    28
-#define _PS_RUSER                                   (1<<28)
-#define PS_GID                                      29
-#define _PS_GID                                     (1<<29)
-#define PS_GROUP                                    30
-#define _PS_GROUP                                   (1<<30)
-#define PS_RGID                                     31
-#define _PS_RGID                                    (1<<31)
-#define PS_RGROUP                                   32
-#define _PS_RGROUP                                  (1LL<<32)
-#define PS_TIME                                     33
-#define _PS_TIME                                    (1LL<<33)
-#define PS_ELAPSED                                  34
-#define _PS_ELAPSED                                 (1LL<<34)
-#define PS_TIME_                                    35
-#define _PS_TIME_                                   (1LL<<35)
-#define PS_C                                        36
-#define _PS_C                                       (1LL<<36)
-#define PS__VSZ                                     37
-#define _PS__VSZ                                    (1LL<<37)
-#define PS__MEM                                     38
-#define _PS__MEM                                    (1LL<<38)
-#define PS__CPU                                     39
-#define _PS__CPU                                    (1LL<<39)
-#define PS_VIRT                                     40
-#define _PS_VIRT                                    (1LL<<40)
-#define PS_RES                                      41
-#define _PS_RES                                     (1LL<<41)
-#define PS_SHR                                      42
-#define _PS_SHR                                     (1LL<<42)
-#define PS_READ                                     43
-#define _PS_READ                                    (1LL<<43)
-#define PS_WRITE                                    44
-#define _PS_WRITE                                   (1LL<<44)
-#define PS_IO                                       45
-#define _PS_IO                                      (1LL<<45)
-#define PS_DREAD                                    46
-#define _PS_DREAD                                   (1LL<<46)
-#define PS_DWRITE                                   47
-#define _PS_DWRITE                                  (1LL<<47)
-#define PS_SWAP                                     48
-#define _PS_SWAP                                    (1LL<<48)
-#define PS_DIO                                      49
-#define _PS_DIO                                     (1LL<<49)
-#define PS_STIME                                    50
-#define _PS_STIME                                   (1LL<<50)
-#define PS_F                                        51
-#define _PS_F                                       (1LL<<51)
-#define PS_S                                        52
-#define _PS_S                                       (1LL<<52)
-#define PS_STAT                                     53
-#define _PS_STAT                                    (1LL<<53)
+#define PS_COMMAND                                  24
+#define _PS_COMMAND                                 (1<<24)
+#define PS_CMDLINE                                  25
+#define _PS_CMDLINE                                 (1<<25)
+#define PS_ARGS                                     26
+#define _PS_ARGS                                    (1<<26)
+#define PS_CMD                                      27
+#define _PS_CMD                                     (1<<27)
+#define PS_UID                                      28
+#define _PS_UID                                     (1<<28)
+#define PS_USER                                     29
+#define _PS_USER                                    (1<<29)
+#define PS_RUID                                     30
+#define _PS_RUID                                    (1<<30)
+#define PS_RUSER                                    31
+#define _PS_RUSER                                   (1<<31)
+#define PS_GID                                      32
+#define _PS_GID                                     (1LL<<32)
+#define PS_GROUP                                    33
+#define _PS_GROUP                                   (1LL<<33)
+#define PS_RGID                                     34
+#define _PS_RGID                                    (1LL<<34)
+#define PS_RGROUP                                   35
+#define _PS_RGROUP                                  (1LL<<35)
+#define PS_TIME                                     36
+#define _PS_TIME                                    (1LL<<36)
+#define PS_ELAPSED                                  37
+#define _PS_ELAPSED                                 (1LL<<37)
+#define PS_TIME_                                    38
+#define _PS_TIME_                                   (1LL<<38)
+#define PS_C                                        39
+#define _PS_C                                       (1LL<<39)
+#define PS__VSZ                                     40
+#define _PS__VSZ                                    (1LL<<40)
+#define PS__MEM                                     41
+#define _PS__MEM                                    (1LL<<41)
+#define PS__CPU                                     42
+#define _PS__CPU                                    (1LL<<42)
+#define PS_VIRT                                     43
+#define _PS_VIRT                                    (1LL<<43)
+#define PS_RES                                      44
+#define _PS_RES                                     (1LL<<44)
+#define PS_SHR                                      45
+#define _PS_SHR                                     (1LL<<45)
+#define PS_READ                                     46
+#define _PS_READ                                    (1LL<<46)
+#define PS_WRITE                                    47
+#define _PS_WRITE                                   (1LL<<47)
+#define PS_IO                                       48
+#define _PS_IO                                      (1LL<<48)
+#define PS_DREAD                                    49
+#define _PS_DREAD                                   (1LL<<49)
+#define PS_DWRITE                                   50
+#define _PS_DWRITE                                  (1LL<<50)
+#define PS_SWAP                                     51
+#define _PS_SWAP                                    (1LL<<51)
+#define PS_DIO                                      52
+#define _PS_DIO                                     (1LL<<52)
+#define PS_STIME                                    53
+#define _PS_STIME                                   (1LL<<53)
+#define PS_F                                        54
+#define _PS_F                                       (1LL<<54)
+#define PS_S                                        55
+#define _PS_S                                       (1LL<<55)
+#define PS_STAT                                     56
+#define _PS_STAT                                    (1LL<<56)
+#define PS_PCY                                      57
+#define _PS_PCY                                     (1LL<<57)
 #define KEY_UP                                       0
 #define _KEY_UP                                      (1<<0)
 #define KEY_DOWN                                     1
index 1f42cdd..e6378be 100644 (file)
@@ -360,6 +360,7 @@ void parse_optflaglist(struct getoptflagstate *gof)
           if (!opt) break;
           if (bits&(1<<i)) opt->dex[idx] |= bits&~(1<<i);
         } else {
+          if (*options==1) break;
           if (CFG_TOYBOX_DEBUG && !opt)
             error_exit("[] unknown target %c", *options);
           if (opt->c == *options) {
index 8b9f299..8f235ed 100644 (file)
@@ -17,7 +17,7 @@ static int notdotdot(char *name)
 int dirtree_notdotdot(struct dirtree *catch)
 {
   // Should we skip "." and ".."?
-  return notdotdot(catch->name) ? DIRTREE_SAVE|DIRTREE_RECURSE : 0;
+  return notdotdot(catch->name)*(DIRTREE_SAVE|DIRTREE_RECURSE);
 }
 
 // Create a dirtree node from a path, with stat and symlink info.
@@ -96,29 +96,23 @@ int dirtree_parentfd(struct dirtree *node)
   return node->parent ? node->parent->dirfd : AT_FDCWD;
 }
 
-// Handle callback for a node in the tree. Returns saved node(s) or NULL.
-//
-// By default, allocates a tree of struct dirtree, not following symlinks
-// If callback==NULL, or callback always returns 0, allocate tree of struct
-// dirtree and return root of tree.  Otherwise call callback(node) on each
-// hit, free structures after use, and return NULL.
-//
+// Handle callback for a node in the tree. Returns saved node(s) if
+// callback returns DIRTREE_SAVE, otherwise frees consumed nodes and
+// returns NULL. If !callback return top node unchanged.
+// If !new return DIRTREE_ABORTVAL
 
 struct dirtree *dirtree_handle_callback(struct dirtree *new,
           int (*callback)(struct dirtree *node))
 {
   int flags;
 
-  if (!new) return 0;
-  if (!callback) callback = dirtree_notdotdot;
+  if (!new) return DIRTREE_ABORTVAL;
+  if (!callback) return new;
   flags = callback(new);
 
-  if (S_ISDIR(new->st.st_mode)) {
-    if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) {
-      new->dirfd = openat(dirtree_parentfd(new), new->name, O_CLOEXEC);
-      flags = dirtree_recurse(new, callback, flags);
-    }
-  }
+  if (S_ISDIR(new->st.st_mode) && (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)))
+    flags = dirtree_recurse(new, callback,
+      openat(dirtree_parentfd(new), new->name, O_CLOEXEC), flags);
 
   // If this had children, it was callback's job to free them already.
   if (!(flags & DIRTREE_SAVE)) {
@@ -133,12 +127,13 @@ struct dirtree *dirtree_handle_callback(struct dirtree *new,
 // callback(). Uses and closes supplied ->dirfd.
 
 int dirtree_recurse(struct dirtree *node,
-          int (*callback)(struct dirtree *node), int flags)
+          int (*callback)(struct dirtree *node), int dirfd, int flags)
 {
   struct dirtree *new, **ddt = &(node->child);
   struct dirent *entry;
   DIR *dir;
 
+  node->dirfd = dirfd;
   if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) {
     if (!(flags & DIRTREE_SHUTUP)) {
       char *path = dirtree_path(node, 0);
@@ -176,19 +171,21 @@ int dirtree_recurse(struct dirtree *node,
   return flags;
 }
 
-// Create dirtree root
-struct dirtree *dirtree_start(char *name, int symfollow)
+// Create dirtree from path, using callback to filter nodes. If !callback
+// return just the top node. Use dirtree_notdotdot callback to allocate a
+// tree of struct dirtree nodes and return pointer to root node for later
+// processing.
+// Returns DIRTREE_ABORTVAL if path didn't exist (use DIRTREE_SHUTUP to handle
+// error message yourself).
+
+struct dirtree *dirtree_flagread(char *path, int flags,
+  int (*callback)(struct dirtree *node))
 {
-  return dirtree_add_node(0, name, DIRTREE_SYMFOLLOW*!!symfollow);
+  return dirtree_handle_callback(dirtree_add_node(0, path, flags), callback);
 }
 
-// Create dirtree from path, using callback to filter nodes.
-// If callback == NULL allocate a tree of struct dirtree nodes and return
-// pointer to root node.
-
+// Common case
 struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node))
 {
-  struct dirtree *root = dirtree_start(path, 0);
-
-  return root ? dirtree_handle_callback(root, callback) : DIRTREE_ABORTVAL;
+  return dirtree_flagread(path, 0, callback);
 }
index 9ff575b..d018567 100644 (file)
@@ -2,15 +2,16 @@
 
 #include "toys.h"
 
-#if !CFG_TOYBOX_HELP
-void show_help(FILE *out) {;}
-#else
 #include "generated/help.h"
 
 #undef NEWTOY
 #undef OLDTOY
 #define NEWTOY(name,opt,flags) HELP_##name "\0"
+#if CFG_TOYBOX
 #define OLDTOY(name,oldname,flags) "\xff" #oldname "\0"
+#else
+#define OLDTOY(name, oldname, flags) HELP_##oldname "\0"
+#endif
 static char *help_data =
 #include "generated/newtoys.h"
 ;
@@ -20,19 +21,15 @@ void show_help(FILE *out)
   int i = toys.which-toy_list;
   char *s;
 
-  for (;;) {
-    s = help_data;
-    while (i--) s += strlen(s) + 1;
-    // If it's an alias, restart search for real name
-    if (*s != 255) break;
-    if (!CFG_TOYBOX) {
-      s = xmprintf("See %s --help\n", ++s);
-
-      break;
+  if (CFG_TOYBOX_HELP) {
+    for (;;) {
+      s = help_data;
+      while (i--) s += strlen(s) + 1;
+      // If it's an alias, restart search for real name
+      if (*s != 255) break;
+      i = toy_find(++s)-toy_list;
     }
-    i = toy_find(++s)-toy_list;
-  }
 
-  fprintf(out, "%s", s);
+    fprintf(out, "%s", s);
+  }
 }
-#endif
index c4ea2c2..62670cb 100644 (file)
@@ -227,7 +227,7 @@ void tty_jump(int x, int y)
 
 void tty_reset(void)
 {
-  set_terminal(1, 0, 0);
+  set_terminal(0, 0, 0);
   tty_esc("?25h");
   tty_esc("0m");
   tty_jump(0, 999);
@@ -239,5 +239,5 @@ void tty_reset(void)
 void tty_sigreset(int i)
 {
   tty_reset();
-  _exit(128+i);
+  _exit(i ? 128+i : 0);
 }
index 43db2e3..fe85cbe 100644 (file)
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -273,16 +273,16 @@ struct string_list *find_in_path(char *path, char *filename)
   return rlist;
 }
 
-long estrtol(char *str, char **end, int base)
+long long estrtol(char *str, char **end, int base)
 {
   errno = 0;
 
-  return strtol(str, end, base);
+  return strtoll(str, end, base);
 }
 
-long xstrtol(char *str, char **end, int base)
+long long xstrtol(char *str, char **end, int base)
 {
-  long l = estrtol(str, end, base);
+  long long l = estrtol(str, end, base);
 
   if (errno) perror_exit_raw(str);
 
@@ -291,31 +291,32 @@ long xstrtol(char *str, char **end, int base)
 
 // atol() with the kilo/mega/giga/tera/peta/exa extensions.
 // (zetta and yotta don't fit in 64 bits.)
-long atolx(char *numstr)
+long long atolx(char *numstr)
 {
-  char *c, *suffixes="cbkmgtpe", *end;
-  long val;
+  char *c = numstr, *suffixes="cbkmgtpe", *end;
+  long long val;
 
   val = xstrtol(numstr, &c, 0);
-  if (*c) {
-    if (c != numstr && (end = strchr(suffixes, tolower(*c)))) {
-      int shift = end-suffixes-2;
-      if (shift >= 0) val *= 1024L<<(shift*10);
-    } else {
-      while (isspace(*c)) c++;
-      if (*c) error_exit("not integer: %s", numstr);
+  if (c != numstr && *c && (end = strchr(suffixes, tolower(*c)))) {
+    int shift = end-suffixes-2;
+
+    if (shift >= 0) {
+      if (toupper(*++c)=='d') do val *= 1000; while (shift--);
+      else val *= 1024LL<<(shift*10);
     }
   }
+  while (isspace(*c)) c++;
+  if (c==numstr || *c) error_exit("not integer: %s", numstr);
 
   return val;
 }
 
-long atolx_range(char *numstr, long low, long high)
+long long atolx_range(char *numstr, long long low, long long high)
 {
-  long val = atolx(numstr);
+  long long val = atolx(numstr);
 
-  if (val < low) error_exit("%ld < %ld", val, low);
-  if (val > high) error_exit("%ld > %ld", val, high);
+  if (val < low) error_exit("%lld < %lld", val, low);
+  if (val > high) error_exit("%lld > %lld", val, high);
 
   return val;
 }
@@ -444,7 +445,8 @@ off_t fdlength(int fd)
 
 // Read contents of file as a single nul-terminated string.
 // measure file size if !len, allocate buffer if !buf
-// note: for existing buffers use len = size-1, will set buf[len] = 0
+// Existing buffers need len in *plen
+// Returns amount of data read in *plen
 char *readfileat(int dirfd, char *name, char *ibuf, off_t *plen)
 {
   off_t len, rlen;
@@ -545,16 +547,20 @@ void poke(void *ptr, uint64_t val, int size)
 }
 
 // Iterate through an array of files, opening each one and calling a function
-// on that filehandle and name.  The special filename "-" means stdin if
-// flags is O_RDONLY, stdout otherwise.  An empty argument list calls
+// on that filehandle and name. The special filename "-" means stdin if
+// flags is O_RDONLY, stdout otherwise. An empty argument list calls
 // function() on just stdin/stdout.
 //
 // Note: pass O_CLOEXEC to automatically close filehandles when function()
-// returns, otherwise filehandles must be closed by function()
-void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+// returns, otherwise filehandles must be closed by function().
+// pass WARN_ONLY to produce warning messages about files it couldn't
+// open/create, and skip them. Otherwise function is called with fd -1.
+void loopfiles_rw(char **argv, int flags, int permissions,
   void (*function)(int fd, char *name))
 {
-  int fd;
+  int fd, failok = !(flags&WARN_ONLY);
+
+  flags &= ~WARN_ONLY;
 
   // If no arguments, read from stdin.
   if (!*argv) function((flags & O_ACCMODE) != O_RDONLY ? 1 : 0, "-");
@@ -562,20 +568,20 @@ void loopfiles_rw(char **argv, int flags, int permissions, int failok,
     // Filename "-" means read from stdin.
     // Inability to open a file prints a warning, but doesn't exit.
 
-    if (!strcmp(*argv, "-")) fd=0;
-    else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
+    if (!strcmp(*argv, "-")) fd = 0;
+    else if (0>(fd = notstdio(open(*argv, flags, permissions))) && !failok) {
       perror_msg_raw(*argv);
       continue;
     }
     function(fd, *argv);
-    if (flags & O_CLOEXEC) close(fd);
+    if ((flags & O_CLOEXEC) && fd) close(fd);
   } while (*++argv);
 }
 
-// Call loopfiles_rw with O_RDONLY|O_CLOEXEC and !failok (common case).
+// Call loopfiles_rw with O_RDONLY|O_CLOEXEC|WARN_ONLY (common case)
 void loopfiles(char **argv, void (*function)(int fd, char *name))
 {
-  loopfiles_rw(argv, O_RDONLY|O_CLOEXEC, 0, 0, function);
+  loopfiles_rw(argv, O_RDONLY|O_CLOEXEC|WARN_ONLY, 0, function);
 }
 
 // Slow, but small.
@@ -618,10 +624,9 @@ int wfchmodat(int fd, char *name, mode_t mode)
 }
 
 static char *tempfile2zap;
-static void tempfile_handler(int i)
+static void tempfile_handler(void)
 {
   if (1 < (long)tempfile2zap) unlink(tempfile2zap);
-  _exit(1);
 }
 
 // Open a temporary file to copy an existing file into.
@@ -757,11 +762,25 @@ void generic_signal(int sig)
   toys.signal = sig;
 }
 
-// Install the same handler on every signal that defaults to killing the process
+void exit_signal(int sig)
+{
+  if (sig) toys.exitval = sig|128;
+  xexit();
+}
+
+// Install the same handler on every signal that defaults to killing the
+// process, calling the handler on the way out. Calling multiple times
+// adds the handlers to a list, to be called in order.
 void sigatexit(void *handler)
 {
+  struct arg_list *al = xmalloc(sizeof(struct arg_list));
   int i;
-  for (i=0; signames[i].num != SIGCHLD; i++) signal(signames[i].num, handler);
+
+  for (i=0; signames[i].num != SIGCHLD; i++)
+    signal(signames[i].num, exit_signal);
+  al->next = toys.xexit;
+  al->arg = handler;
+  toys.xexit = al;
 }
 
 // Convert name to signal number.  If name == NULL print names.
@@ -906,11 +925,14 @@ void mode_to_string(mode_t mode, char *buf)
   *buf = c;
 }
 
-char *basename_r(char *name)
+// basename() can modify its argument or return a pointer to a constant string
+// This just gives after the last '/' or the whole stirng if no /
+char *getbasename(char *name)
 {
   char *s = strrchr(name, '/');
 
   if (s) return s+1;
+
   return name;
 }
 
@@ -932,7 +954,7 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
 
     for (curname = names; *curname; curname++)
       if (**curname == '/' ? !strcmp(cmd, *curname)
-          : !strcmp(basename_r(cmd), basename_r(*curname)))
+          : !strcmp(getbasename(cmd), getbasename(*curname)))
         if (callback(u, *curname)) break;
     if (*curname) break;
   }
@@ -990,7 +1012,7 @@ int qstrcmp(const void *a, const void *b)
 void create_uuid(char *uuid)
 {
   // Read 128 random bits
-  int fd = xopen("/dev/urandom", O_RDONLY);
+  int fd = xopenro("/dev/urandom");
   xreadall(fd, uuid, 16);
   close(fd);
 
@@ -1043,3 +1065,170 @@ char *strnstr(char *line, char *str)
 
   return *s ? s : 0;
 }
+
+int dev_minor(int dev)
+{
+  return ((dev&0xfff00000)>>12)|(dev&0xff);
+}
+
+int dev_major(int dev)
+{
+  return (dev&0xfff00)>>8;
+}
+
+int dev_makedev(int major, int minor)
+{
+  return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
+}
+
+// Return cached passwd entries.
+struct passwd *bufgetpwuid(uid_t uid)
+{
+  struct pwuidbuf_list {
+    struct pwuidbuf_list *next;
+    struct passwd pw;
+  } *list;
+  struct passwd *temp;
+  static struct pwuidbuf_list *pwuidbuf;
+
+  for (list = pwuidbuf; list; list = list->next)
+    if (list->pw.pw_uid == uid) return &(list->pw);
+
+  list = xmalloc(512);
+  list->next = pwuidbuf;
+
+  errno = getpwuid_r(uid, &list->pw, sizeof(*list)+(char *)list,
+    512-sizeof(*list), &temp);
+  if (!temp) {
+    free(list);
+
+    return 0;
+  }
+  pwuidbuf = list;
+
+  return &list->pw;
+}
+
+// Return cached passwd entries.
+struct group *bufgetgrgid(gid_t gid)
+{
+  struct grgidbuf_list {
+    struct grgidbuf_list *next;
+    struct group gr;
+  } *list;
+  struct group *temp;
+  static struct grgidbuf_list *grgidbuf;
+
+  for (list = grgidbuf; list; list = list->next)
+    if (list->gr.gr_gid == gid) return &(list->gr);
+
+  list = xmalloc(512);
+  list->next = grgidbuf;
+
+  errno = getgrgid_r(gid, &list->gr, sizeof(*list)+(char *)list,
+    512-sizeof(*list), &temp);
+  if (!temp) {
+    free(list);
+
+    return 0;
+  }
+  grgidbuf = list;
+
+  return &list->gr;
+}
+
+// Always null terminates, returns 0 for failure, len for success
+int readlinkat0(int dirfd, char *path, char *buf, int len)
+{
+  if (!len) return 0;
+
+  len = readlinkat(dirfd, path, buf, len-1);
+  if (len<1) return 0;
+  buf[len] = 0;
+
+  return len;
+}
+
+int readlink0(char *path, char *buf, int len)
+{
+  return readlinkat0(AT_FDCWD, path, buf, len);
+}
+
+// Do regex matching handling embedded NUL bytes in string (hence extra len
+// argument). Note that neither the pattern nor the match can currently include
+// NUL bytes (even with wildcards) and string must be null terminated at
+// string[len]. But this can find a match after the first NUL.
+int regexec0(regex_t *preg, char *string, long len, int nmatch,
+  regmatch_t pmatch[], int eflags)
+{
+  char *s = string;
+
+  for (;;) {
+    long ll = 0;
+    int rc;
+
+    while (len && !*s) {
+      s++;
+      len--;
+    }
+    while (s[ll] && ll<len) ll++;
+
+    rc = regexec(preg, s, nmatch, pmatch, eflags);
+    if (!rc) {
+      for (rc = 0; rc<nmatch && pmatch[rc].rm_so!=-1; rc++) {
+        pmatch[rc].rm_so += s-string;
+        pmatch[rc].rm_eo += s-string;
+      }
+
+      return 0;
+    }
+    if (ll==len) return rc;
+
+    s += ll;
+    len -= ll;
+  }
+}
+
+// Return user name or string representation of number, returned buffer
+// lasts until next call.
+char *getusername(uid_t uid)
+{
+  struct passwd *pw = bufgetpwuid(uid);
+  static char unum[12];
+
+  sprintf(unum, "%u", (unsigned)uid);
+  return pw ? pw->pw_name : unum;
+}
+
+// Return group name or string representation of number, returned buffer
+// lasts until next call.
+char *getgroupname(gid_t gid)
+{
+  struct group *gr = bufgetgrgid(gid);
+  static char gnum[12];
+
+  sprintf(gnum, "%u", (unsigned)gid);
+  return gr ? gr->gr_name : gnum;
+}
+
+// Iterate over lines in file, calling function. Function can write 0 to
+// the line pointer if they want to keep it, or 1 to terminate processing,
+// otherwise line is freed. Passed file descriptor is closed at the end.
+void do_lines(int fd, void (*call)(char **pline, long len))
+{
+  FILE *fp = fd ? xfdopen(fd, "r") : stdin;
+
+  for (;;) {
+    char *line = 0;
+    ssize_t len;
+
+    len = getline(&line, (void *)&len, fp);
+    if (len > 0) {
+      call(&line, len);
+      if (line == (void *)1) break;
+      free(line);
+    } else break;
+  }
+
+  if (fd) fclose(fp);
+}
index dac3b67..0ca764e 100644 (file)
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -34,6 +34,12 @@ struct double_list {
   char *data;
 };
 
+struct num_cache {
+  struct num_cache *next;
+  long long num;
+  char data[];
+};
+
 void llist_free_arg(void *node);
 void llist_free_double(void *node);
 void llist_traverse(void *list, void (*using)(void *node));
@@ -42,6 +48,9 @@ void *dlist_pop(void *list);  // actually struct double_list **list
 void dlist_add_nomalloc(struct double_list **list, struct double_list *new);
 struct double_list *dlist_add(struct double_list **list, char *data);
 void *dlist_terminate(void *list);
+struct num_cache *get_num_cache(struct num_cache *cache, long long num);
+struct num_cache *add_num_cache(struct num_cache **cache, long long num,
+  void *data, int len);
 
 // args.c
 void get_optflags(void);
@@ -62,6 +71,8 @@ void get_optflags(void);
 #define DIRTREE_SYMFOLLOW    8
 // Don't warn about failure to stat
 #define DIRTREE_SHUTUP      16
+// Breadth first traversal, conserves filehandles at the expense of memory
+#define DIRTREE_BREADTH     32
 // Don't look at any more files in this directory.
 #define DIRTREE_ABORT      256
 
@@ -77,24 +88,29 @@ struct dirtree {
   char name[];
 };
 
-struct dirtree *dirtree_start(char *name, int symfollow);
 struct dirtree *dirtree_add_node(struct dirtree *p, char *name, int flags);
 char *dirtree_path(struct dirtree *node, int *plen);
 int dirtree_notdotdot(struct dirtree *catch);
 int dirtree_parentfd(struct dirtree *node);
-struct dirtree *dirtree_handle_callback(struct dirtree *new,
-  int (*callback)(struct dirtree *node));
 int dirtree_recurse(struct dirtree *node, int (*callback)(struct dirtree *node),
-  int symfollow);
+  int dirfd, int symfollow);
+struct dirtree *dirtree_flagread(char *path, int flags,
+  int (*callback)(struct dirtree *node));
 struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node));
 
 // help.c
 
 void show_help(FILE *out);
 
+// Tell xopen and friends to print warnings but return -1 as necessary
+// The largest O_BLAH flag so far is arch/alpha's O_PATH at 0x800000 so
+// plenty of headroom.
+#define WARN_ONLY (1<<31)
+
 // xwrap.c
 void xstrncpy(char *dest, char *src, size_t size);
 void xstrncat(char *dest, char *src, size_t size);
+void _xexit(void) noreturn;
 void xexit(void) noreturn;
 void *xmalloc(size_t size);
 void *xzalloc(size_t size);
@@ -119,9 +135,14 @@ void xaccess(char *path, int flags);
 void xunlink(char *path);
 int xcreate(char *path, int flags, int mode);
 int xopen(char *path, int flags);
+int xcreate_stdio(char *path, int flags, int mode);
+int xopen_stdio(char *path, int flags);
+int openro(char *path, int flags);
+int xopenro(char *path);
 void xpipe(int *pp);
 void xclose(int fd);
 int xdup(int fd);
+int notstdio(int fd);
 FILE *xfdopen(int fd, char *mode);
 FILE *xfopen(char *path, char *mode);
 size_t xread(int fd, void *buf, size_t len);
@@ -139,8 +160,8 @@ struct passwd *xgetpwuid(uid_t uid);
 struct group *xgetgrgid(gid_t gid);
 struct passwd *xgetpwnam(char *name);
 struct group *xgetgrnam(char *name);
-struct passwd *xgetpwnamid(char *user);
-struct group *xgetgrnamid(char *group);
+unsigned xgetuid(char *name);
+unsigned xgetgid(char *name);
 void xsetuser(struct passwd *pwd);
 char *xreadlink(char *name);
 long xparsetime(char *arg, long units, long *fraction);
@@ -173,10 +194,10 @@ int64_t peek_be(void *ptr, unsigned size);
 int64_t peek(void *ptr, unsigned size);
 void poke(void *ptr, uint64_t val, int size);
 struct string_list *find_in_path(char *path, char *filename);
-long estrtol(char *str, char **end, int base);
-long xstrtol(char *str, char **end, int base);
-long atolx(char *c);
-long atolx_range(char *numstr, long low, long high);
+long long estrtol(char *str, char **end, int base);
+long long xstrtol(char *str, char **end, int base);
+long long atolx(char *c);
+long long atolx_range(char *numstr, long long low, long long high);
 int stridx(char *haystack, char needle);
 char *strlower(char *s);
 char *strafter(char *haystack, char *needle);
@@ -184,7 +205,7 @@ char *chomp(char *s);
 int unescape(char c);
 int strstart(char **a, char *b);
 off_t fdlength(int fd);
-void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+void loopfiles_rw(char **argv, int flags, int permissions,
   void (*function)(int fd, char *name));
 void loopfiles(char **argv, void (*function)(int fd, char *name));
 void xsendfile(int in, int out);
@@ -200,6 +221,18 @@ void create_uuid(char *uuid);
 char *show_uuid(char *uuid);
 char *next_printf(char *s, char **start);
 char *strnstr(char *line, char *str);
+int dev_minor(int dev);
+int dev_major(int dev);
+int dev_makedev(int major, int minor);
+struct passwd *bufgetpwuid(uid_t uid);
+struct group *bufgetgrgid(gid_t gid);
+int readlinkat0(int dirfd, char *path, char *buf, int len);
+int readlink0(char *path, char *buf, int len);
+int regexec0(regex_t *preg, char *string, long len, int nmatch,
+  regmatch_t pmatch[], int eflags);
+char *getusername(uid_t uid);
+char *getgroupname(gid_t gid);
+void do_lines(int fd, void (*call)(char **pline, long len));
 
 #define HR_SPACE 1 // Space between number and units
 #define HR_B     2 // Use "B" for single byte units
@@ -218,11 +251,15 @@ void linestack_addstack(struct linestack **lls, struct linestack *throw,
 void linestack_insert(struct linestack **lls, long pos, char *line, long len);
 void linestack_append(struct linestack **lls, char *line);
 struct linestack *linestack_load(char *name);
-int crunch_str(char **str, int width, FILE *out,
-  int (*escout)(FILE *out, int cols, char **buf));
+int crunch_escape(FILE *out, int cols, int wc);
+int crunch_rev_escape(FILE *out, int cols, int wc);
+int crunch_str(char **str, int width, FILE *out, char *escmore,
+  int (*escout)(FILE *out, int cols, int wc));
 int draw_str(char *start, int width);
 int utf8len(char *str);
 int utf8skip(char *str, int width);
+int draw_trim_esc(char *str, int padto, int width, char *escmore,
+  int (*escout)(FILE *out, int cols,int wc));
 int draw_trim(char *str, int padto, int width);
 
 // interestingtimes.c
@@ -271,13 +308,15 @@ struct mtab_list *xgetmountlist(char *path);
 // signal
 
 void generic_signal(int signal);
+void exit_signal(int signal);
 void sigatexit(void *handler);
 int sig_to_num(char *pidstr);
 char *num_to_sig(int sig);
 
 mode_t string_to_mode(char *mode_str, mode_t base);
 void mode_to_string(mode_t mode, char *buf);
-char *basename_r(char *name);
+
+char *getbasename(char *name);
 void names_to_pid(char **names, int (*callback)(pid_t pid, char *name));
 
 pid_t xvforkwrap(pid_t pid);
index 241a437..eef790b 100644 (file)
@@ -81,56 +81,75 @@ struct linestack *linestack_load(char *name)
 // if escout, send it unprintable chars, returns columns output or -1 for
 // standard escape: ^X if <32, <XX> if invliad UTF8, U+XXXX if UTF8 !iswprint()
 // Returns width in columns, moves *str to end of data consumed.
-int crunch_str(char **str, int width, FILE *out,
-  int (*escout)(FILE *out, int cols, char **buf))
+int crunch_str(char **str, int width, FILE *out, char *escmore,
+  int (*escout)(FILE *out, int cols, int wc))
 {
   int columns = 0, col, bytes;
   char *start, *end;
 
-  for (end = start = *str; *end;) {
-    wchar_t wc = *end;
+  for (end = start = *str; *end; columns += col, end += bytes) {
+    wchar_t wc;
 
-    bytes = 0;
-    if (*end >= ' ' && (bytes = mbrtowc(&wc, end, 99,0))>0
-        && (col = wcwidth(wc))>=0)
+    if ((bytes = mbrtowc(&wc, end, MB_CUR_MAX, 0))>0 && (col = wcwidth(wc))>=0)
     {
-      if (width-columns<col) break;
-      if (out) fwrite(end, bytes, 1, out);
-    } else if (!escout || 0>(col = escout(out, width-columns, &end))) {
-      char buf[32];
-
-      tty_esc("7m");
-      if (*end < ' ') {
-        bytes = 1;
-        sprintf(buf, "^%c", '@'+*end);
-      } else if (bytes<1) {
-        bytes = 1;
-        sprintf(buf, "<%02X>", *end);
-      } else sprintf(buf, "U+%04X", wc);
-      col = strlen(buf);
-      if (width-columns<col) buf[col = width-columns] = 0;
-      if (out) fputs(buf, out);
-      tty_esc("27m");
-    } else continue;
-    columns += col;
-    end += bytes;
+      if (!escmore || wc>255 || !strchr(escmore, wc)) {
+        if (width-columns<col) break;
+        if (out) fwrite(end, bytes, 1, out);
+
+        continue;
+      }
+    }
+
+    if (bytes<1) {
+      bytes = 1;
+      wc = *end;
+    }
+    col = width-columns;
+    if (col<1) break;
+    col = escout(out, col, wc);
   }
   *str = end;
 
   return columns;
 }
 
+int crunch_escape(FILE *out, int cols, int wc)
+{
+  char buf[8];
+  int rc;
+
+  if (wc<' ') rc = sprintf(buf, "^%c", '@'+wc);
+  else if (wc<256) rc = sprintf(buf, "<%02X>", wc);
+  else rc = sprintf(buf, "U+%04X", wc);
+
+  if (rc > cols) buf[rc = cols] = 0;
+  if (out) fputs(buf, out);
+
+  return rc;
+}
+
+int crunch_rev_escape(FILE *out, int cols, int wc)
+{
+  int rc;
+
+  tty_esc("7m");
+  rc = crunch_escape(out, cols, wc);
+  tty_esc("27m");
+
+  return rc;
+}
+
 // Write width chars at start of string to strdout with standard escapes
 // Returns length in columns so caller can pad it out with spaces.
 int draw_str(char *start, int width)
 {
-  return crunch_str(&start, width, stdout, 0);
+  return crunch_str(&start, width, stdout, 0, crunch_rev_escape);
 }
 
 // Return utf8 columns
 int utf8len(char *str)
 {
-  return crunch_str(&str, INT_MAX, 0, 0);
+  return crunch_str(&str, INT_MAX, 0, 0, crunch_rev_escape);
 }
 
 // Return bytes used by (up to) this many columns
@@ -138,14 +157,15 @@ int utf8skip(char *str, int width)
 {
   char *s = str;
 
-  crunch_str(&s, width, 0, 0);
+  crunch_str(&s, width, 0, 0, crunch_rev_escape);
 
   return s-str;
 }
 
-// Print utf8 to stdout with standard escapes,trimmed to width and padded
+// Print utf8 to stdout with standard escapes, trimmed to width and padded
 // out to padto. If padto<0 left justify. Returns columns printed
-int draw_trim(char *str, int padto, int width)
+int draw_trim_esc(char *str, int padto, int width, char *escmore,
+  int (*escout)(FILE *out, int cols, int wc))
 {
   int apad = abs(padto), len = utf8len(str);
 
@@ -154,8 +174,14 @@ int draw_trim(char *str, int padto, int width)
 
   // Left pad if right justified 
   if (padto>0 && apad>len) printf("%*s", apad-len, "");
-  crunch_str(&str, len, stdout, 0);
+  crunch_str(&str, len, stdout, 0, crunch_rev_escape);
   if (padto<0 && apad>len) printf("%*s", apad-len, "");
 
   return (apad > len) ? apad : len;
 }
+
+// draw_trim_esc() with default escape
+int draw_trim(char *str, int padto, int width)
+{
+  return draw_trim_esc(str, padto, width, 0, 0);
+}
index 6b4b8f2..dbb5352 100644 (file)
@@ -99,3 +99,32 @@ void *dlist_terminate(void *list)
 
   return end;
 }
+
+// Find num in cache
+struct num_cache *get_num_cache(struct num_cache *cache, long long num)
+{
+  while (cache) {
+    if (num==cache->num) return cache;
+    cache = cache->next;
+  }
+
+  return 0;
+}
+
+// Uniquely add num+data to cache. Updates *cache, returns pointer to existing
+// entry if it was already there.
+struct num_cache *add_num_cache(struct num_cache **cache, long long num,
+  void *data, int len)
+{
+  struct num_cache *old = get_num_cache(*cache, num);
+
+  if (old) return old;
+
+  old = xzalloc(sizeof(struct num_cache)+len);
+  old->next = *cache;
+  old->num = num;
+  memcpy(old->data, data, len);
+  *cache = old;
+
+  return 0;
+}
index d7e7de9..e21d424 100644 (file)
--- a/lib/lsm.h
+++ b/lib/lsm.h
@@ -3,6 +3,8 @@
  * Copyright 2015 Rob Landley <rob@landley.net>
  */
 
+#include <sys/xattr.h>
+
 #if CFG_TOYBOX_SELINUX
 #include <selinux/selinux.h>
 #else
 
 #if CFG_TOYBOX_SMACK
 #include <sys/smack.h>
-#include <sys/xattr.h>
 #include <linux/xattr.h>
 #else
+#ifndef XATTR_NAME_SMACK
 #define XATTR_NAME_SMACK 0
+#endif
 //ssize_t fgetxattr (int fd, char *name, void *value, size_t size);
 #define smack_smackfs_path(...) (-1)
 #define smack_new_label_from_self(...) (-1)
index bf13c44..eab2d66 100644 (file)
@@ -24,7 +24,7 @@ int get_salt(char *salt, char *algo)
       if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
 
       // Read appropriate number of random bytes for salt
-      i = xopen("/dev/urandom", O_RDONLY);
+      i = xopenro("/dev/urandom");
       xreadall(i, libbuf, ((len*6)+7)/8);
       close(i);
 
index d0d0bd9..1c2052b 100644 (file)
@@ -9,7 +9,7 @@
 
 // Test for gcc (using compiler builtin #define)
 
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(__APPLE__)
 #define noreturn       __attribute__((noreturn))
 #define printf_format  __attribute__((format(printf, 1, 2)))
 #else
@@ -22,7 +22,9 @@
 
 // This isn't in the spec, but it's how we determine what libc we're using.
 
+#ifndef __APPLE__
 #include <features.h>
+#endif
 
 // Types various replacement prototypes need
 #include <sys/types.h>
@@ -116,6 +118,9 @@ pid_t getsid(pid_t pid);
 #ifndef MS_SHARED
 #define MS_SHARED     (1<<20)
 #endif
+#ifndef MS_RELATIME
+#define MS_RELATIME (1<<21)
+#endif
 
 // When building under obsolete glibc (Ubuntu 8.04-ish), hold its hand a bit.
 #elif __GLIBC__ == 2 && __GLIBC_MINOR__ < 10
@@ -154,19 +159,11 @@ int utimensat(int fd, const char *path, const struct timespec times[2], int flag
 
 #endif // glibc in general
 
-#if !defined(__GLIBC__) && !defined(__BIONIC__)
+#if !defined(__GLIBC__)
 // POSIX basename.
 #include <libgen.h>
 #endif
 
-// glibc was handled above; for 32-bit bionic we need to avoid a collision
-// with toybox's basename_r so we can't include <libgen.h> even though that
-// would give us a POSIX basename(3).
-#if defined(__BIONIC__)
-char *basename(char *path);
-char *dirname(char *path);
-#endif
-
 // Work out how to do endianness
 
 #ifndef __APPLE__
@@ -210,13 +207,16 @@ int clearenv(void);
 
 #if defined(__APPLE__) \
     || (defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 10)
+#include <stdio.h>
 ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
 ssize_t getline(char **lineptr, size_t *n, FILE *stream);
 #endif
 
 // Linux headers not listed by POSIX or LSB
 #include <sys/mount.h>
+#ifndef __APPLE__
 #include <sys/swap.h>
+#endif
 
 // Android is missing some headers and functions
 // "generated/config.h" is included first
@@ -275,3 +275,9 @@ pid_t xfork(void);
 //#define strncpy(...) @@strncpyisbadmmkay@@
 //#define strncat(...) @@strncatisbadmmkay@@
 
+#ifdef __ANDROID__
+#include <cutils/sched_policy.h>
+#else
+static inline int get_sched_policy(int tid, void *policy) {return 0;}
+static inline char *get_sched_policy_name(int policy) {return "unknown";}
+#endif
index 963295c..e809901 100644 (file)
@@ -25,3 +25,6 @@
 // Call setlocale to listen to environment variables.
 // This invalidates sprintf("%.*s", size, string) as a valid length constraint.
 #define TOYFLAG_LOCALE   (1<<8)
+
+// Suppress default --help processing
+#define TOYFLAG_NOHELP   (1<<9)
index 7810355..66972f2 100644 (file)
@@ -28,13 +28,34 @@ void xstrncat(char *dest, char *src, size_t size)
   strcpy(dest+len, src);
 }
 
+// We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
+// sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
+// instead of exiting, lets xexit() report stdout flush failures to stderr
+// and change the exit code to indicate error, lets our toys.exit function
+// change happen for signal exit paths and lets us remove the functions
+// after we've called them.
+
+void _xexit(void)
+{
+  if (toys.rebound) siglongjmp(*toys.rebound, 1);
+
+  _exit(toys.exitval);
+}
+
 void xexit(void)
 {
-  if (toys.rebound) longjmp(*toys.rebound, 1);
+  // Call toys.xexit functions in reverse order added.
+  while (toys.xexit) {
+    // This is typecasting xexit->arg to a function pointer,then calling it.
+    // Using the invalid signal number 0 lets the signal handlers distinguish
+    // an actual signal from a regular exit.
+    ((void (*)(int))(toys.xexit->arg))(0);
+
+    free(llist_pop(&toys.xexit));
+  }
   if (fflush(NULL) || ferror(stdout))
     if (!toys.exitval) perror_msg("write");
-
-  exit(toys.exitval);
+  _xexit();
 }
 
 // Die unless we can allocate memory.
@@ -297,17 +318,20 @@ void xunlink(char *path)
 }
 
 // Die unless we can open/create a file, returning file descriptor.
-int xcreate(char *path, int flags, int mode)
+// The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable)
+// and WARN_ONLY tells us not to exit.
+int xcreate_stdio(char *path, int flags, int mode)
 {
-  int fd = open(path, flags^O_CLOEXEC, mode);
-  if (fd == -1) perror_exit_raw(path);
+  int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode);
+
+  if (fd == -1) ((mode&WARN_ONLY) ? perror_msg_raw : perror_exit_raw)(path);
   return fd;
 }
 
 // Die unless we can open a file, returning file descriptor.
-int xopen(char *path, int flags)
+int xopen_stdio(char *path, int flags)
 {
-  return xcreate(path, flags, 0);
+  return xcreate_stdio(path, flags, 0);
 }
 
 void xpipe(int *pp)
@@ -329,6 +353,49 @@ int xdup(int fd)
   return fd;
 }
 
+// Move file descriptor above stdin/stdout/stderr, using /dev/null to consume
+// old one. (We should never be called with stdin/stdout/stderr closed, but...)
+int notstdio(int fd)
+{
+  if (fd<0) return fd;
+
+  while (fd<3) {
+    int fd2 = xdup(fd);
+
+    close(fd);
+    xopen_stdio("/dev/null", O_RDWR);
+    fd = fd2;
+  }
+
+  return fd;
+}
+
+// Create a file but don't return stdin/stdout/stderr
+int xcreate(char *path, int flags, int mode)
+{
+  return notstdio(xcreate_stdio(path, flags, mode));
+}
+
+// Open a file descriptor NOT in stdin/stdout/stderr
+int xopen(char *path, int flags)
+{
+  return notstdio(xopen_stdio(path, flags));
+}
+
+// Open read only, treating "-" as a synonym for stdin, defaulting to warn only
+int openro(char *path, int flags)
+{
+  if (!strcmp(path, "-")) return 0;
+
+  return xopen(path, flags^WARN_ONLY);
+}
+
+// Open read only, treating "-" as a synonym for stdin.
+int xopenro(char *path)
+{
+  return openro(path, O_RDONLY|WARN_ONLY);
+}
+
 FILE *xfdopen(int fd, char *mode)
 {
   FILE *f = fdopen(fd, mode);
@@ -398,7 +465,7 @@ char *xabspath(char *path, int exact)
 {
   struct string_list *todo, *done = 0;
   int try = 9999, dirfd = open("/", 0);;
-  char buf[4096], *ret;
+  char *ret;
 
   // If this isn't an absolute path, start with cwd.
   if (*path != '/') {
@@ -429,7 +496,7 @@ char *xabspath(char *path, int exact)
       } else continue;
 
     // Is this a symlink?
-    } else len=readlinkat(dirfd, new->str, buf, 4096);
+    } else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
 
     if (len>4095) goto error;
     if (len<1) {
@@ -453,8 +520,8 @@ char *xabspath(char *path, int exact)
     }
 
     // If this symlink is to an absolute path, discard existing resolved path
-    buf[len] = 0;
-    if (*buf == '/') {
+    libbuf[len] = 0;
+    if (*libbuf == '/') {
       llist_traverse(done, free);
       done=0;
       close(dirfd);
@@ -463,7 +530,7 @@ char *xabspath(char *path, int exact)
     free(new);
 
     // prepend components of new path. Note symlink to "/" will leave new NULL
-    tail = splitpath(buf, &new);
+    tail = splitpath(libbuf, &new);
 
     // symlink to "/" will return null and leave tail alone
     if (new) {
@@ -533,36 +600,32 @@ struct group *xgetgrgid(gid_t gid)
   return group;
 }
 
-struct passwd *xgetpwnamid(char *user)
+unsigned xgetuid(char *name)
 {
-  struct passwd *up = getpwnam(user);
-  uid_t uid;
+  struct passwd *up = getpwnam(name);
+  char *s = 0;
+  long uid;
 
-  if (!up) {
-    char *s = 0;
+  if (up) return up->pw_uid;
 
-    uid = estrtol(user, &s, 10);
-    if (!errno && s && !*s) up = getpwuid(uid);
-  }
-  if (!up) perror_exit("user '%s'", user);
+  uid = estrtol(name, &s, 10);
+  if (!errno && s && !*s && uid>=0 && uid<=UINT_MAX) return uid;
 
-  return up;
+  error_exit("bad user '%s'", name);
 }
 
-struct group *xgetgrnamid(char *group)
+unsigned xgetgid(char *name)
 {
-  struct group *gr = getgrnam(group);
-  gid_t gid;
+  struct group *gr = getgrnam(name);
+  char *s = 0;
+  long gid;
 
-  if (!gr) {
-    char *s = 0;
+  if (gr) return gr->gr_gid;
 
-    gid = estrtol(group, &s, 10);
-    if (!errno && s && !*s) gr = getgrgid(gid);
-  }
-  if (!gr) perror_exit("group '%s'", group);
+  gid = estrtol(name, &s, 10);
+  if (!errno && s && !*s && gid>=0 && gid<=UINT_MAX) return gid;
 
-  return gr;
+  error_exit("bad group '%s'", name);
 }
 
 struct passwd *xgetpwnam(char *name)
@@ -621,6 +684,8 @@ char *xreadfile(char *name, char *buf, off_t len)
   return buf;
 }
 
+// The data argument to ioctl() is actually long, but it's usually used as
+// a pointer. If you need to feed in a number, do (void *)(long) typecast.
 int xioctl(int fd, int request, void *data)
 {
   int rc;
diff --git a/main.c b/main.c
index cf82872..c0c1b82 100644 (file)
--- a/main.c
+++ b/main.c
@@ -6,14 +6,14 @@
 #include "toys.h"
 
 #ifndef TOYBOX_VERSION
-#define TOYBOX_VERSION "0.7.0"
+#define TOYBOX_VERSION "0.7.1"
 #endif
 
 // Populate toy_list[].
 
 #undef NEWTOY
 #undef OLDTOY
-#define NEWTOY(name, opts, flags) {#name, name##_main, opts, flags},
+#define NEWTOY(name, opts, flags) {#name, name##_main, OPTSTR_##name, flags},
 #define OLDTOY(name, oldname, flags) \
   {#name, oldname##_main, OPTSTR_##oldname, flags},
 
@@ -75,7 +75,9 @@ static void toy_singleinit(struct toy_list *which, char *argv[])
 
   if (CFG_TOYBOX_I18N) setlocale(LC_ALL, "C"+!!(which->flags & TOYFLAG_LOCALE));
 
-  if (CFG_TOYBOX_HELP_DASHDASH && argv[1] && !strcmp(argv[1], "--help")) {
+  if (CFG_TOYBOX_HELP_DASHDASH && !(which->flags & TOYFLAG_NOHELP)
+    && argv[1] && !strcmp(argv[1], "--help"))
+  {
     if (CFG_TOYBOX && toys.which == toy_list && toys.argv[2])
       if (!(toys.which = toy_find(toys.argv[2]))) return;
     show_help(stdout);
@@ -137,8 +139,11 @@ void toy_exec(char *argv[])
   if (!(which = toy_find(*argv))) return;
 
   // Return if stack depth getting noticeable (proxy for leaked heap, etc).
-  if (toys.stacktop && labs((char *)toys.stacktop-(char *)&which)>6000)
-    return;
+
+  // Compiler writers have decided subtracting char * is undefined behavior,
+  // so convert to integers. (LP64 says sizeof(long)==sizeof(pointer).)
+  if (!CFG_TOYBOX_NORECURSE)
+    if (toys.stacktop && labs((long)toys.stacktop-(long)&which)>6000) return;
 
   // Return if we need to re-exec to acquire root via suid bit.
   if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && toys.wasroot) return;
@@ -202,7 +207,7 @@ int main(int argc, char *argv[])
 
     toys.stacktop = &stack;
   }
-  *argv = basename_r(*argv);
+  *argv = getbasename(*argv);
 
   // If nommu can't fork, special reentry path.
   // Use !stacktop to signal "vfork happened", both before and after xexec()
index 7bc3bad..6ec5e84 100644 (file)
@@ -1,5 +1,6 @@
 //#include "toys.h"
 
+#include <ctype.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -10,6 +11,7 @@
 #include <inttypes.h>
 #include <termios.h>
 #include <poll.h>
+#include <sys/socket.h>
 struct statvfs {int i;};
 #include "lib/portability.h"
 #include "lib/lib.h"
index 5b0715f..c55e7d1 100755 (executable)
@@ -90,12 +90,19 @@ EOF
     int main(int argc, char *argv[]) { return fork(); }
 EOF
   echo -e '\tdepends on !TOYBOX_MUSL_NOMMU_IS_BROKEN'
+
+  probesymbol TOYBOX_PRLIMIT << EOF
+    #include <sys/time.h>
+    #include <sys/resource.h>
+
+    int main(int argc, char *argv[]) { prlimit(0, 0, 0, 0); }
+EOF
 }
 
 genconfig()
 {
   # Reverse sort puts posix first, examples last.
-  for j in $(ls toys/*/README | sort -r)
+  for j in $(ls toys/*/README | sort -s -r)
   do
     DIR="$(dirname "$j")"
 
@@ -139,12 +146,9 @@ do
   [ "${FILE/pending//}" != "$FILE" ] &&
     PENDING="$PENDING $NAME" ||
     WORKING="$WORKING $NAME"
-done > .singlemake &&
-echo -e "clean::\n\trm -f $WORKING $PENDING" >> .singlemake &&
-echo -e "list:\n\t@echo $(echo $WORKING $PENDING | tr ' ' '\n' | sort | xargs)"\
-  >> .singlemake &&
-echo -e "list_working:\n\t@echo $(echo $WORKING | tr ' ' '\n' | sort | xargs)" \
-  >> .singlemake &&
-echo -e "list_pending:\n\t@echo $(echo $PENDING | tr ' ' '\n' | sort | xargs)" \
-  >> .singlemake
-)
+done &&
+echo -e "clean::\n\trm -f $WORKING $PENDING" &&
+echo -e "list:\n\t@echo $(echo $WORKING | tr ' ' '\n' | sort | xargs)" &&
+echo -e "list_pending:\n\t@echo $(echo $PENDING | tr ' ' '\n' | sort | xargs)" &&
+echo -e ".PHONY: $WORKING $PENDING" | sed 's/ \([^ ]\)/ test_\1/g'
+) > .singlemake
index 8891a31..961341e 100755 (executable)
@@ -4,31 +4,24 @@
 
 source ./configure
 
-# Parse command line arguments.
+[ -z "$PREFIX" ] && PREFIX="/usr/toybox"
 
-[ -z "$PREFIX" ] && PREFIX="."
+# Parse command line arguments.
 
 LONG_PATH=""
 while [ ! -z "$1" ]
 do
   # Create symlinks instead of hardlinks?
-
   [ "$1" == "--symlink" ] && LINK_TYPE="-s"
 
   # Uninstall?
-
-  [ "$1" == "--uninstall" ] && UNINSTALL=1
+  [ "$1" == "--uninstall" ] && UNINSTALL=Uninstall
 
   # Delete destination command if it exists?
-
   [ "$1" == "--force" ] && DO_FORCE="-f"
 
   # Use {,usr}/{bin,sbin} paths instead of all files in one directory?
-
-  if [ "$1" == "--long" ]
-  then
-    LONG_PATH="bin/"
-  fi
+  [ "$1" == "--long" ] && LONG_PATH="bin/"
 
   shift
 done
@@ -38,22 +31,24 @@ echo "Compile instlist..."
 $DEBUG $HOSTCC -I . scripts/install.c -o generated/instlist || exit 1
 COMMANDS="$(generated/instlist $LONG_PATH)"
 
-echo "Install commands..."
+echo "${UNINSTALL:-Install} commands..."
 
 # Copy toybox itself
 
 if [ -z "$UNINSTALL" ]
 then
-  mkdir -p ${PREFIX}/${LONG_PATH} || exit 1
+  mkdir -p "${PREFIX}/${LONG_PATH}" &&
+  rm -f "${PREFIX}/${LONG_PATH}/toybox" &&
   cp toybox ${PREFIX}/${LONG_PATH} || exit 1
 else
-  rm "${PREFIX}/${LONG_PATH}/toybox" 2>/dev/null
-  rmdir "${PREFIX}/${LONG_PATH}" 2>/dev/null
+  rm -f "${PREFIX}/${LONG_PATH}/toybox" 2>/dev/null
 fi
-cd "${PREFIX}"
+cd "$PREFIX" || exit 1
 
 # Make links to toybox
 
+EXIT=0
+
 for i in $COMMANDS
 do
   # Figure out target of link
@@ -64,20 +59,19 @@ do
   else
     # Create subdirectory for command to go in (if necessary)
 
-    DOTPATH="$(echo $i | sed 's@\(.*/\).*@\1@')"
+    DOTPATH="$(dirname "$i")"/
     if [ -z "$UNINSTALL" ]
     then
       mkdir -p "$DOTPATH" || exit 1
-    else
-      rmdir "$DOTPATH" 2>/dev/null
     fi
 
     if [ -z "$LINK_TYPE" ]
     then
-      dotpath="bin/"
+      DOTPATH="bin/"
     else
       if [ "$DOTPATH" != "$LONG_PATH" ]
       then
+        # For symlinks we need ../../bin style relative paths
         DOTPATH="$(echo $DOTPATH | sed -e 's@[^/]*/@../@g')"$LONG_PATH
       else
         DOTPATH=""
@@ -86,7 +80,12 @@ do
   fi
 
   # Create link
-  [ -z "$UNINSTALL" ] &&
-    ln $DO_FORCE $LINK_TYPE ${DOTPATH}toybox $i ||
-    rm $i 2>/dev/null
+  if [ -z "$UNINSTALL" ]
+  then
+    ln $DO_FORCE $LINK_TYPE ${DOTPATH}toybox $i || EXIT=1
+  else
+    rm -f $i || EXIT=1
+  fi
 done
+
+exit $EXIT
index 84dda34..50415e5 100755 (executable)
@@ -7,7 +7,9 @@ export LC_ALL=C
 set -o pipefail
 source ./configure
 
-[ -z "$KCONFIG_CONFIG" ] && KCONFIG_CONFIG=".config"
+[ -z "$KCONFIG_CONFIG" ] && KCONFIG_CONFIG=.config
+[ -z "$OUTNAME" ] && OUTNAME=toybox
+UNSTRIPPED="generated/unstripped/$(basename "$OUTNAME")"
 
 # Since each cc invocation is short, launch half again as many processes
 # as we have processors so they don't exit faster than we can start them.
@@ -25,12 +27,14 @@ do_loudly()
 # Is anything under directory $2 newer than file $1
 isnewer()
 {
- [ ! -z "$(find "$2" -newer "$1" 2>/dev/null || echo yes)" ]
+  CHECK="$1"
+  shift
+  [ ! -z "$(find "$@" -newer "$CHECK" 2>/dev/null || echo yes)" ]
 }
 
 echo "Generate headers from toys/*/*.c..."
 
-mkdir -p generated
+mkdir -p generated/unstripped
 
 if isnewer generated/Config.in toys
 then
@@ -64,7 +68,8 @@ TOYFILES="$(sed -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr
 TOYFILES="$(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c)"
 CFLAGS="$CFLAGS $(cat generated/cflags)"
 BUILD="$(echo ${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE $GITHASH)"
-FILES="$(echo lib/*.c main.c $TOYFILES)"
+LIBFILES="$(ls lib/*.c | grep -v lib/help.c)"
+TOYFILES="lib/help.c main.c $TOYFILES"
 
 if [ "${TOYFILES/pending//}" != "$TOYFILES" ]
 then
@@ -77,11 +82,11 @@ genbuildsh()
 
   echo "#!/bin/sh"
   echo
-  echo "BUILD=\"$BUILD\""
+  echo "BUILD='$BUILD'"
   echo
-  echo "FILES=\"$FILES\""
+  echo "FILES='$LIBFILES $TOYFILES'"
   echo
-  echo "LINK=\"$LINK\""
+  echo "LINK='$LINK'"
   echo
   echo
   echo '$BUILD $FILES $LINK'
@@ -98,7 +103,7 @@ then
   # for it.
 
   > generated/optlibs.dat
-  for i in util crypt m resolv selinux smack attr rt
+  for i in util crypt m resolv selinux smack attr rt crypto
   do
     echo "int main(int argc, char *argv[]) {return 0;}" | \
     ${CROSS_COMPILE}${CC} $CFLAGS -xc - -o generated/libprobe -Wl,--as-needed -l$i > /dev/null 2>/dev/null &&
@@ -111,80 +116,91 @@ fi
 
 # LINK needs optlibs.dat, above
 
-LINK="$(echo $LDOPTIMIZE $LDFLAGS -o toybox_unstripped -Wl,--as-needed $(cat generated/optlibs.dat))"
+LINK="$(echo $LDOPTIMIZE $LDFLAGS -o "$UNSTRIPPED" -Wl,--as-needed $(cat generated/optlibs.dat))"
 genbuildsh > generated/build.sh && chmod +x generated/build.sh || exit 1
 
-echo "Make generated/config.h from $KCONFIG_CONFIG."
-
-# This long and roundabout sed invocation is to make old versions of sed happy.
-# New ones have '\n' so can replace one line with two without all the branches
-# and tedious mucking about with hold space.
-
-sed -n \
-  -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \
-  -e 't notset' \
-  -e 's/^CONFIG_\(.*\)=y.*/\1/' \
-  -e 't isset' \
-  -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \
-  -e 'd' \
-  -e ':notset' \
-  -e 'h' \
-  -e 's/.*/#define CFG_& 0/p' \
-  -e 'g' \
-  -e 's/.*/#define USE_&(...)/p' \
-  -e 'd' \
-  -e ':isset' \
-  -e 'h' \
-  -e 's/.*/#define CFG_& 1/p' \
-  -e 'g' \
-  -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \
-  $KCONFIG_CONFIG > generated/config.h || exit 1
+if isnewer generated/config.h "$KCONFIG_CONFIG"
+then
+  echo "Make generated/config.h from $KCONFIG_CONFIG."
+
+  # This long and roundabout sed invocation is to make old versions of sed
+  # happy. New ones have '\n' so can replace one line with two without all
+  # the branches and tedious mucking about with hold space.
+
+  sed -n \
+    -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \
+    -e 't notset' \
+    -e 's/^CONFIG_\(.*\)=y.*/\1/' \
+    -e 't isset' \
+    -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \
+    -e 'd' \
+    -e ':notset' \
+    -e 'h' \
+    -e 's/.*/#define CFG_& 0/p' \
+    -e 'g' \
+    -e 's/.*/#define USE_&(...)/p' \
+    -e 'd' \
+    -e ':isset' \
+    -e 'h' \
+    -e 's/.*/#define CFG_& 1/p' \
+    -e 'g' \
+    -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \
+    $KCONFIG_CONFIG > generated/config.h || exit 1
+fi
 
 if [ generated/mkflags -ot scripts/mkflags.c ]
 then
   do_loudly $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1
 fi
 
-echo -n "generated/flags.h "
-
 # Process config.h and newtoys.h to generate FLAG_x macros. Note we must
 # always #define the relevant macro, even when it's disabled, because we
 # allow multiple NEWTOY() in the same C file. (When disabled the FLAG is 0,
 # so flags&0 becomes a constant 0 allowing dead code elimination.)
 
-# Parse files through C preprocessor twice, once to get flags for current
-# .config and once to get flags for allyesconfig
-for I in A B
-do
-  (
-  # define macros and select header files with option string data
-
-  echo "#define NEWTOY(aa,bb,cc) aa $I bb"
-  echo '#define OLDTOY(...)'
-  if [ "$I" == A ]
-  then
-    cat generated/config.h
-  else
-    sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h
-  fi
-  cat generated/newtoys.h
-
-  # Run result through preprocessor, glue together " " gaps leftover from USE
-  # macros, delete comment lines, print any line with a quoted optstring,
-  # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't
-  # handle "" with nothing in it, and mkflags uses that).
-
-  ) | ${CROSS_COMPILE}${CC} -E - | \
+make_flagsh()
+{
+  # Parse files through C preprocessor twice, once to get flags for current
+  # .config and once to get flags for allyesconfig
+  for I in A B
+  do
+    (
+    # define macros and select header files with option string data
+
+    echo "#define NEWTOY(aa,bb,cc) aa $I bb"
+    echo '#define OLDTOY(...)'
+    if [ "$I" == A ]
+    then
+      cat generated/config.h
+    else
+      sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h
+    fi
+    cat generated/newtoys.h
+
+    # Run result through preprocessor, glue together " " gaps leftover from USE
+    # macros, delete comment lines, print any line with a quoted optstring,
+    # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't
+    # handle "" with nothing in it, and mkflags uses that).
+
+    ) | ${CROSS_COMPILE}${CC} -E - | \
     sed -n -e 's/" *"//g;/^#/d;t clear;:clear;s/"/"/p;t;s/\( [AB] \).*/\1 " "/p'
 
-# Sort resulting line pairs and glue them together into triplets of
-#   command "flags" "allflags"
-# to feed into mkflags C program that outputs actual flag macros
-# If no pair (because command's disabled in config), use " " for flags
-# so allflags can define the appropriate zero macros.
+  # Sort resulting line pairs and glue them together into triplets of
+  #   command "flags" "allflags"
+  # to feed into mkflags C program that outputs actual flag macros
+  # If no pair (because command's disabled in config), use " " for flags
+  # so allflags can define the appropriate zero macros.
 
-done | sort -s | sed -n 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x;b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | tee generated/flags.raw | \
-generated/mkflags > generated/flags.h || exit 1
+  done | sort -s | sed -n -e 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x' \
+    -e 'b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | \
+    tee generated/flags.raw | generated/mkflags > generated/flags.h || exit 1
+}
+
+if isnewer generated/flags.h toys "$KCONFIG_CONFIG"
+then
+  echo -n "generated/flags.h "
+  make_flagsh
+fi
 
 # Extract global structure definitions and flag definitions from toys/*/*.c
 
@@ -228,13 +244,16 @@ then
     toys/*/*.c lib/*.c | generated/mktags > generated/tags.h
 fi
 
-echo "generated/help.h"
 if [ generated/config2help -ot scripts/config2help.c ]
 then
   do_loudly $HOSTCC scripts/config2help.c -I . lib/xwrap.c lib/llist.c \
     lib/lib.c lib/portability.c -o generated/config2help || exit 1
 fi
-generated/config2help Config.in $KCONFIG_CONFIG > generated/help.h || exit 1
+if isnewer generated/help.h generated/Config.in
+then
+  echo "generated/help.h"
+  generated/config2help Config.in $KCONFIG_CONFIG > generated/help.h || exit 1
+fi
 
 [ ! -z "$NOBUILD" ] && exit 0
 
@@ -244,37 +263,52 @@ DOTPROG=.
 
 # This is a parallel version of: do_loudly $BUILD $FILES $LINK || exit 1
 
+# Any headers newer than the oldest generated/obj file?
 X="$(ls -1t generated/obj/* 2>/dev/null | tail -n 1)"
+# TODO: redo this
 if [ ! -e "$X" ] || [ ! -z "$(find toys -name "*.h" -newer "$X")" ]
 then
   rm -rf generated/obj && mkdir -p generated/obj || exit 1
 else
   rm -f generated/obj/{main,lib_help}.o || exit 1
 fi
+
+# build each generated/obj/*.o file in parallel
+
 PENDING=
-LFILES=
+LNKFILES=
 DONE=0
-for i in $FILES
+COUNT=0
+CLICK=
+
+for i in $LIBFILES click $TOYFILES
 do
-  # build each generated/obj/*.o file in parallel
+  [ "$i" == click ] && CLICK=1 && continue
 
   X=${i/lib\//lib_}
   X=${X##*/}
   OUT="generated/obj/${X%%.c}.o"
-  LFILES="$LFILES $OUT"
-  [ "$OUT" -nt "$i" ] && continue
+  LNKFILES="$LNKFILES $OUT"
+
+  # $LIBFILES doesn't need to be rebuilt if newer than .config, $TOYFILES does
+
+  [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] &&
+    continue
+
   do_loudly $BUILD -c $i -o $OUT &
+  PENDING="$PENDING $!"
+  COUNT=$(($COUNT+1))
 
   # ratelimit to $CPUS many parallel jobs, detecting errors
 
-  while true
+  for j in $PENDING
   do
-    PENDING="$(echo $PENDING $(jobs -rp) | tr ' ' '\n' | sort -u)"
-    [ $(echo -n "$PENDING" | wc -l) -lt "$CPUS" ] && break;
+    [ "$COUNT" -lt "$CPUS" ] && break;
 
-    wait $(echo "$PENDING" | head -n 1)
+    wait $j
     DONE=$(($DONE+$?))
-    PENDING="$(echo "$PENDING" | tail -n +2)"
+    COUNT=$(($COUNT-1))
+    PENDING="${PENDING## $j}"
   done
   [ $DONE -ne 0 ] && break
 done
@@ -289,10 +323,11 @@ done
 
 [ $DONE -ne 0 ] && exit 1
 
-do_loudly $BUILD $LFILES $LINK || exit 1
-if [ ! -z "$NOSTRIP" ] || ! do_loudly ${CROSS_COMPILE}strip toybox_unstripped -o toybox
+do_loudly $BUILD $LNKFILES $LINK || exit 1
+if [ ! -z "$NOSTRIP" ] ||
+  ! do_loudly ${CROSS_COMPILE}strip "$UNSTRIPPED" -o "$OUTNAME"
 then
-  echo "strip failed, using unstripped" && cp toybox_unstripped toybox ||
+  echo "strip failed, using unstripped" && cp "$UNSTRIPPED" "$OUTNAME" ||
   exit 1
 fi
 
@@ -300,6 +335,6 @@ fi
 # its output the way SUSv4 suggests it do so. While we're at it, make sure
 # we don't have the "w" bit set so things like bzip2's "cp -f" install don't
 # overwrite our binary through the symlink.
-do_loudly chmod 555 toybox || exit 1
+do_loudly chmod 555 "$OUTNAME" || exit 1
 
 echo
index da3b2f9..39b935b 100644 (file)
@@ -122,7 +122,7 @@ int main(int argc, char *argv[])
 
   for (;;) {
     struct flag *flist, *aflist, *offlist;
-    char *gaps, *mgaps, c;
+    char *mgaps = 0;
     unsigned bit;
 
     *command = *flags = *allflags = 0;
@@ -141,16 +141,13 @@ int main(int argc, char *argv[])
 
     bit = 0;
     printf("// %s %s %s\n", command, flags, allflags);
-    mgaps = mark_gaps(flags, allflags);
-    for (gaps = mgaps; *gaps == 1; gaps++);
-    if (*gaps) c = '"';
-    else {
-      c = ' ';
-      gaps = "0";
-    }
-    printf("#undef OPTSTR_%s\n#define OPTSTR_%s %c%s%c\n",
-            command, command, c, gaps, c);
-    free(mgaps);
+    if (*flags != ' ') mgaps = mark_gaps(flags, allflags);
+    else if (*allflags != ' ') mgaps = allflags;
+    // If command disabled, use allflags for OLDTOY()
+    printf("#undef OPTSTR_%s\n#define OPTSTR_%s ", command, command);
+    if (mgaps) printf("\"%s\"\n", mgaps);
+    else printf("0\n");
+    if (mgaps != allflags) free(mgaps);
 
     flist = digest(flags);
     offlist = aflist = digest(allflags);
@@ -194,7 +191,7 @@ int main(int argc, char *argv[])
         }
       // Output normal flag macro
       } else if (aflist->command) {
-        if (flist && (!flist->command || *aflist->command == *flist->command)) {
+        if (flist && flist->command && *aflist->command == *flist->command) {
           if (aflist->command)
             sprintf(out, "#define FLAG_%c (1%s<<%d)\n", *aflist->command,
               llstr, bit);
index 884af9c..6e94d94 100644 (file)
@@ -64,7 +64,7 @@ optional()
 
 testing()
 {
-  NAME="$1"
+  NAME="$CMDNAME $1"
   [ -z "$1" ] && NAME=$2
 
   if [ $# -ne 5 ]
@@ -83,23 +83,23 @@ testing()
 
   echo -ne "$3" > expected
   echo -ne "$4" > input
-  echo -ne "$5" | eval "$2" > actual
+  echo -ne "$5" | ${EVAL:-eval} "$2" > actual
   RETVAL=$?
 
   # Catch segfaults
   [ $RETVAL -gt 128 ] && [ $RETVAL -lt 255 ] &&
     echo "exited with signal (or returned $RETVAL)" >> actual
  
-  cmp expected actual > /dev/null 2>&1
-  if [ $? -ne 0 ]
+  DIFF="$(diff -au${NOSPACE:+b} expected actual)"
+  if [ ! -z "$DIFF" ]
   then
     FAILCOUNT=$[$FAILCOUNT+1]
     echo "$SHOWFAIL: $NAME"
     if [ -n "$VERBOSE" ]
     then
       [ ! -z "$4" ] && echo "echo -ne \"$4\" > input"
-      echo "echo -ne '$5' | $2"
-      diff -au expected actual
+      echo "echo -ne '$5' |$EVAL $2"
+      echo "$DIFF"
       [ "$VERBOSE" == fail ] && exit 1
     fi
   else
@@ -109,7 +109,7 @@ testing()
 
   [ -n "$DEBUG" ] && set +x
 
-  return $RETVAL
+  return 0
 }
 
 # Recursively grab an executable and all the libraries needed to run it.
index 522a713..cadb2fc 100755 (executable)
@@ -27,10 +27,10 @@ do
     exit 1
   fi
 
-  DEPENDS="$(sed -n 's/^[ \t]*depends on //;T;s/[!][A-Z0-9_]*//g;s/ *&& */|/g;p' $TOYFILE | grep -v SMACK | xargs | tr ' ' '|')"
+  # Enable stuff this command depends on
+  DEPENDS="$(sed -n "/^config *$i"'$/,/^$/{s/^[ \t]*depends on //;T;s/[!][A-Z0-9_]*//g;s/ *&& */|/g;p}' $TOYFILE | xargs | tr ' ' '|')"
 
   NAME=$(echo $i | tr a-z- A-Z_)
-
   make allnoconfig > /dev/null &&
   sed -ri -e '/CONFIG_TOYBOX/d' \
     -e "s/# (CONFIG_($NAME|${NAME}_.*${DEPENDS:+|$DEPENDS})) is not set/\1=y/" \
@@ -38,6 +38,6 @@ do
   echo "# CONFIG_TOYBOX is not set" >> "$KCONFIG_CONFIG" &&
   grep "CONFIG_TOYBOX_" .config >> "$KCONFIG_CONFIG" &&
 
-  make &&
-  mv -f toybox $PREFIX$i || exit 1
+  rm -f "$PREFIX$i" &&
+  OUTNAME="$PREFIX$i" scripts/make.sh || exit 1
 done
index 2276619..1337c52 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/bash
 
-[ -z "$TOPDIR" ] && TOPDIR="$(pwd)"
+TOPDIR="$PWD"
+FILES="$PWD"/tests/files
 
 trap 'kill $(jobs -p) 2>/dev/null; exit 1' INT
 
@@ -29,12 +30,15 @@ if [ $# -ne 0 ]
 then
   for i in "$@"
   do
+    CMDNAME="${i##*/}"
+    CMDNAME="${CMDNAME%.test}"
     . "$TOPDIR"/tests/$i.test
   done
 else
   for i in "$TOPDIR"/tests/*.test
   do
-    CMDNAME="$(echo "$i" | sed 's@.*/\(.*\)\.test@\1@')"
+    CMDNAME="${i##*/}"
+    CMDNAME="${CMDNAME%.test}"
     if [ -h ../$CMDNAME ] || [ ! -z "$TEST_HOST" ]
     then
       cd .. && rm -rf testdir && mkdir testdir && cd testdir || exit 1
diff --git a/tests/base64.test b/tests/base64.test
new file mode 100755 (executable)
index 0000000..2058198
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "simple" "base64" "c2ltcGxlCg==\n" "" "simple\n"
+testing "file" "base64 input" "c2ltcGxlCg==\n" "simple\n" ""
+testing "simple -d" "base64 -d" "simple\n" "" "c2ltcGxlCg==\n"
+testing "simple -d" "base64 -d input" "simple\n" "c2ltcGxlCg==" ""
+testing "default wrap" "base64" \
+  "V2UndmUgcmVwbGFjZWQgdGhlIGRpbGl0aGl1bSB0aGV5IG5vcm1hbGx5IHVzZSB3aXRoIEZvbGdl\ncidzIENyeXN0YWxzLg==\n" \
+  "" "We've replaced the dilithium they normally use with Folger's Crystals."
+testing "multiline -d " "base64 -d" \
+ "We've replaced the dilithium they normally use with Folger's Crystals." "" \
+  "V2UndmUgcmVwbGFjZWQgdGhlIGRpbGl0aGl1bSB0aGV5IG5vcm1hbGx5IHVzZSB3aXRoIEZvbGdl\ncidzIENyeXN0YWxzLg==\n"
+
+testing "-w" "base64 -w 10" \
+  "TWFyY2hpbm\ncgdG8gdGhl\nIGJlYXQgb2\nYgYSBkaWZm\nZXJlbnQga2\nV0dGxlIG9m\nIGZpc2guCg\n==\n" \
+  "" "Marching to the beat of a different kettle of fish.\n"
+
+testing "-w0" "base64 -w0 input" \
+  "VmlraW5ncz8gVGhlcmUgYWluJ3Qgbm8gdmlraW5ncyBoZXJlLiBKdXN0IHVzIGhvbmVzdCBmYXJtZXJzLiBUaGUgdG93biB3YXMgYnVybmluZywgdGhlIHZpbGxhZ2VycyB3ZXJlIGRlYWQuIFRoZXkgZGlkbid0IG5lZWQgdGhvc2Ugc2hlZXAgYW55d2F5LiBUaGF0J3Mgb3VyIHN0b3J5IGFuZCB3ZSdyZSBzdGlja2luZyB0byBpdC4K" \
+ "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" ""
index 2f7a2ec..ab2cc20 100755 (executable)
@@ -5,19 +5,22 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 # Removal of extra /'s
-testing "basename /-only" "basename ///////" "/\n" "" ""
-testing "basename trailing /" "basename a//////" "a\n" "" ""
-testing "basename combined" "basename /////a///b///c///d/////" "d\n" "" ""
+testing "/-only" "basename ///////" "/\n" "" ""
+testing "trailing /" "basename a//////" "a\n" "" ""
+testing "combined" "basename /////a///b///c///d/////" "d\n" "" ""
 
 # Standard suffix behavior.
-testing "basename suffix" "basename a/b/c/d.suffix .suffix" "d\n" "" ""
+testing "suffix" "basename a/b/c/d.suffix .suffix" "d\n" "" ""
 
 # A suffix cannot be the entire result.
-testing "basename suffix=result" "basename .txt .txt" ".txt\n" "" ""
+testing "suffix=result" "basename .txt .txt" ".txt\n" "" ""
 
 # Deal with suffix appearing in the filename
-testing "basename reappearing suffix 1" "basename a.txt.txt .txt" "a.txt\n" "" ""
-testing "basename reappearing suffix 2" "basename a.txt.old .txt" "a.txt.old\n" "" ""
+testing "reappearing suffix 1" "basename a.txt.txt .txt" "a.txt\n" "" ""
+testing "reappearing suffix 2" "basename a.txt.old .txt" "a.txt.old\n" "" ""
 
 # A suffix should be a real suffix, only a the end.
-testing "basename invalid suffix" "basename isthisasuffix? suffix" "isthisasuffix?\n" "" ""
+testing "invalid suffix" "basename isthisasuffix? suffix" "isthisasuffix?\n" "" ""
+
+# Zero-length suffix
+testing "zero-length suffix" "basename a/b/c ''" "c\n" "" ""
index b80cf4f..d3bf6a1 100755 (executable)
@@ -4,41 +4,41 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-BDIR="$TOPDIR/tests/blkid"
+BDIR="$FILES/blkid"
 
 bzcat "$BDIR"/squashfs.bz2 > temp.img
-testing "blkid file" "blkid temp.img" 'temp.img: TYPE="squashfs"\n' "" ""
+testing "file" "blkid temp.img" 'temp.img: TYPE="squashfs"\n' "" ""
 rm temp.img
 
-testing "blkid cramfs" 'bzcat "$BDIR"/cramfs.bz2 | blkid -' \
+testing "cramfs" 'bzcat "$BDIR"/cramfs.bz2 | blkid -' \
   '-: LABEL="mycramfs" TYPE="cramfs"\n' "" ""
-testing "blkid ext2" 'bzcat "$BDIR"/ext2.bz2 | blkid -' \
+testing "ext2" 'bzcat "$BDIR"/ext2.bz2 | blkid -' \
   '-: LABEL="myext2" UUID="e59093ba-4135-4fdb-bcc4-f20beae4dfaf" TYPE="ext2"\n' \
   "" "" 
-testing "blkid ext3" 'bzcat "$BDIR"/ext3.bz2 | blkid -' \
+testing "ext3" 'bzcat "$BDIR"/ext3.bz2 | blkid -' \
   '-: LABEL="myext3" UUID="79d1c877-1a0f-4e7d-b21d-fc32ae3ef101" TYPE="ext3"\n' \
   "" "" 
-testing "blkid ext4" 'bzcat "$BDIR"/ext4.bz2 | blkid -' \
+testing "ext4" 'bzcat "$BDIR"/ext4.bz2 | blkid -' \
   '-: LABEL="myext4" UUID="dc4b7c00-c0c0-4600-af7e-0335f09770fa" TYPE="ext4"\n' \
   "" ""
-testing "blkid f2fs" 'bzcat "$BDIR"/f2fs.bz2 | blkid -' \
+testing "f2fs" 'bzcat "$BDIR"/f2fs.bz2 | blkid -' \
   '-: LABEL="" UUID="b53d3619-c204-4c0b-8504-36363578491c" TYPE="f2fs"\n' \
   "" ""
-testing "blkid msdos" 'bzcat "$BDIR"/msdos.bz2 | blkid -' \
-  '-: LABEL="mymsdos    " UUID="5108-1e6e" TYPE="vfat"\n' "" ""
-testing "blkid ntfs" 'bzcat "$BDIR"/ntfs.bz2 | blkid -' \
+testing "msdos" 'bzcat "$BDIR"/msdos.bz2 | blkid -' \
+  '-: LABEL="mymsdos" UUID="5108-1e6e" TYPE="vfat"\n' "" ""
+testing "ntfs" 'bzcat "$BDIR"/ntfs.bz2 | blkid -' \
   '-: UUID="8585600838bfe16e" TYPE="ntfs"\n' "" ""
-testing "blkid reiserfs" 'bzcat "$BDIR"/reiser3.bz2 | blkid -' \
+testing "reiserfs" 'bzcat "$BDIR"/reiser3.bz2 | blkid -' \
   '-: LABEL="myreiser" UUID="a5b99bec-45cc-41d7-986e-32f4b6fc28f2" TYPE="reiserfs"\n' \
   "" ""
-testing "blkid squashfs" 'bzcat "$BDIR"/squashfs.bz2 | blkid -' \
+testing "squashfs" 'bzcat "$BDIR"/squashfs.bz2 | blkid -' \
   '-: TYPE="squashfs"\n' "" ""
-testing "blkid vfat" 'bzcat "$BDIR"/vfat.bz2 | blkid -' \
-  '-: LABEL="myvfat     " UUID="1db9-5673" TYPE="vfat"\n' "" ""
-testing "blkid xfs" 'bzcat "$BDIR"/xfs.bz2 | blkid -' \
+testing "vfat" 'bzcat "$BDIR"/vfat.bz2 | blkid -' \
+  '-: LABEL="myvfat" UUID="1db9-5673" TYPE="vfat"\n' "" ""
+testing "xfs" 'bzcat "$BDIR"/xfs.bz2 | blkid -' \
   '-: LABEL="XFS_test" UUID="d63a1dc3-27d5-4dd4-8b38-f4f97f495c6f" TYPE="xfs"\n' \
   "" ""
 
-#testing "blkid minix" 'bzcat "$BDIR"/minix.bz2 | blkid -'
+#testing "minix" 'bzcat "$BDIR"/minix.bz2 | blkid -'
 #adfs bfs btrfs cramfs jfs nilfs romfs
 #vfat  // fat32 fat12 fat16
index 8bc5f5c..0975f3d 100755 (executable)
@@ -1,26 +1,16 @@
 #!/bin/bash
 
-# Copyright 2014 Divya Kothari <divya.s.kothari@gmail.com>
-# Copyright 2014 Naha Maggu <maggu.neha@gmail.com>
-
 [ -f testing.sh ] && . testing.sh
 
 #testing "name" "command" "result" "infile" "stdin"
-echo "hello" > file
-tar -cjf file.tar.bz2 file
-# Get system bzcat
-bzcatExe=`which bzcat`
-$bzcatExe file.tar.bz2 > bzcatOut
-testing "bzcat - decompresses a single file" "bzcat file.tar.bz2 > Tempfile && echo "yes"; diff Tempfile bzcatOut && echo "yes"; rm -rf file* bzcatOut Tempfile" "yes\nyes\n" "" ""
+testing "2 known files" \
+ 'bzcat "$FILES/blkid/"{minix,ntfs}.bz2 | sha1sum | '"awk '{print \$1}'" \
+ 'c0b7469c9660d6056a988ef8a7fe73925efc9266\n' '' ''
 
-#testing "name" "command" "result" "infile" "stdin"
-echo "hello" > file1
-echo "hi" > file2
-echo "Hi, Good morning !! I am a bzcat tester" > file3
-tar -cjf file1.tar.bz2 file1
-tar -cjf file2.tar.bz2 file2
-tar -cjf file3.tar.bz2 file3
-# Get system bzcat
-bzcatExe=`which bzcat`
-$bzcatExe file1.tar.bz2 file2.tar.bz2 file3.tar.bz2 > bzcatOut
-testing "bzcat - decompresses multiple files" "bzcat file1.tar.bz2 file2.tar.bz2 file3.tar.bz2 > Tempfile && echo "yes" ; diff Tempfile bzcatOut && echo "yes"; rm -rf file* bzcatOut Tempfile " "yes\nyes\n" "" ""
+testing "overflow" \
+  'bzcat "$FILES/bzcat/overflow.bz2" >/dev/null 2>/dev/null ;
+   [ $? -ne 0 ] && echo good' "good\n" "" ""
+
+testing "badcrc" \
+  'bzcat "$FILES/bzcat/badcrc.bz2" > /dev/null 2>/dev/null ;
+   [ $? -ne 0 ] && echo good' "good\n" "" ""
index 7be515d..8845070 100755 (executable)
@@ -7,25 +7,25 @@
 echo "one" > file1
 echo "two" > file2
 testing "cat" "cat && echo yes" "oneyes\n" "" "one"
-testing "cat -" "cat - && echo yes" "oneyes\n" "" "one"
-testing "cat file1 file2" "cat file1 file2" "one\ntwo\n"  "" ""
-testing "cat - file"      "cat - file1"     "zero\none\n" "" "zero\n"
-testing "cat file -"      "cat file1 -"     "one\nzero\n" "" "zero\n"
+testing "-" "cat - && echo yes" "oneyes\n" "" "one"
+testing "file1 file2" "cat file1 file2" "one\ntwo\n"  "" ""
+testing "- file"      "cat - file1"     "zero\none\n" "" "zero\n"
+testing "file -"      "cat file1 -"     "one\nzero\n" "" "zero\n"
 
-testing "cat file1 notfound file2" \
+testing "file1 notfound file2" \
         "cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
         "one\ntwo\ncat: notfound: No such file or directory\n" "" ""
 
 FILE="$(readlink -f /proc/self/exe)"
-testing "cat file1" \
+testing "file1" \
         'cat "$FILE" > file1 && cmp "$FILE" file1 && echo yes' \
         "yes\n" "" ""
 
-testing "cat - file1" \
+testing "- file1" \
         "cat - file1 | diff -a -U 0 - file1 | tail -n 1" \
         "-hello\n" "" "hello\n"
 
-testing "cat > /dev/full" \
+testing "> /dev/full" \
         "cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
         "cat: xwrite: No space left on device\n" "" "zero\n"
 
diff --git a/tests/chattr.test b/tests/chattr.test
new file mode 100755 (executable)
index 0000000..067014d
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+if [ "$(id -u)" -ne 0 ]
+then
+  echo "$SHOWSKIP: chattr (not root)"
+  continue 2>/dev/null
+  exit
+fi
+
+# chattr - Testcases
+
+mkdir testattr
+IN="cd testattr"
+OUT="cd .."
+_t="abcdefghijklmnopqrstuvwxyz"
+
+testing "[-/+]i FILE[write]" "$IN && echo "$_t" > testFile &&
+         chattr +i testFile && lsattr testFile && echo "$_t" > testFile; 
+         chattr -i testFile; rm -rf testFile; $OUT " "----i-------- testFile\n" "" ""
+testing "[-/+]i FILE[re-write]" "$IN && echo "$_t" > testFile && 
+         chattr +i testFile && echo \"$_t\" > testFile || chattr -i testFile && 
+         echo \"$_t\" > testFile && lsattr testFile; rm -rf testFile; $OUT" \
+         "------------- testFile\n" "" ""
+testing "[-/+]i FILE[append]" "$IN && echo "$_t" > testFile && 
+         chattr +i testFile && echo \"$_t\" >> testFile || lsattr testFile && 
+         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
+testing "[-/+]i FILE[move]" "$IN && echo "$_t" > testFile && 
+         chattr +i testFile && mv testFile testFile1 || lsattr testFile && 
+         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
+testing "[-/+]i FILE[delete]" "$IN && echo "$_t" > testFile && 
+         chattr +i testFile && rm -f testFile || lsattr testFile && 
+         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
+testing "[-/+]i FILE[read]" "$IN && echo "$_t" > testFile && 
+         chattr +i testFile && cat testFile && lsattr testFile && 
+         chattr -i testFile; rm -rf testFile; $OUT" "$_t\n----i-------- testFile\n" "" ""
+testing "[-/+]a FILE[write]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && echo $_t > testFile || lsattr testFile && 
+         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
+testing "[-/+]a FILE[re-write]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && echo $_t > testFile || lsattr testFile && 
+         chattr -a testFile && echo $_t > testFile && cat testFile && 
+         lsattr testFile; rm -rf testFile; 
+         $OUT" "-----a------- testFile\n$_t\n------------- testFile\n" "" ""
+testing "[-/+]a FILE[append]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && echo $_t >> testFile && cat testFile && 
+         lsattr testFile && chattr -a testFile; rm -rf testFile; $OUT" \
+         "$_t\n$_t\n-----a------- testFile\n" "" ""
+testing "[-/+]a FILE[move]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && mv testFile testFile1 || lsattr testFile && 
+         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
+testing "[-/+]a FILE[delete]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && rm -f testFile || lsattr testFile && 
+         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
+testing "[-/+]a FILE[read]" "$IN && echo "$_t" > testFile && 
+         chattr +a testFile && cat testFile && lsattr testFile && chattr -a testFile; 
+         rm -rf testFile; $OUT" "$_t\n-----a------- testFile\n" "" ""
+
+for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
+do
+  testing "[-/+]$attr FILE" "$IN && echo "$_t" > testFile && 
+           chattr +$attr testFile && cat testFile && chattr -$attr testFile && 
+           lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
+done
+
+for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
+do
+  testing "-$attr FILE" "$IN && echo "$_t" > testFile && chattr -$attr testFile &&
+           cat testFile && lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
+done
+
+testing "[-/+]AacDdijsStTu FILE" "$IN && echo "$_t" > testFile && 
+         chattr +AacDdijsStTu testFile && cat testFile && chattr -AacDdijsStTu testFile && 
+         lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
+testing "[-/+]AacDdijsStTu(random) FILE" \
+        "$IN && echo "$_t" > testFile && 
+        chattr +AacDdijsStTu testFile && cat testFile && chattr -A testFile &&
+        chattr -a testFile && chattr -c testFile && chattr -D testFile &&
+        chattr -d testFile && chattr -i testFile && chattr -j testFile &&
+        chattr -s testFile && chattr -S testFile && chattr -t testFile &&
+        chattr -T testFile && chattr -u testFile && lsattr testFile &&
+        chattr -AacDdijsStTu testFile; rm -rf testFile; $OUT" \
+        "$_t\n------------- testFile\n" "" ""
+testing "[-/+]AacDdijsStTu FILE*" "$IN && 
+        echo "$_t" > testFile && echo "$_t" > testFile1 &&
+        echo "$_t" > testFile2 && echo "$_t" > testFile3 &&
+        echo "$_t" > testFile4 && echo "$_t" > testFile5 &&
+        echo "$_t" > testFile6 && echo "$_t" > testFile7 &&
+        echo "$_t" > testFile8 && echo "$_t" > testFile9 &&
+        echo "$_t" > testFile10 && echo "$_t" > testFile11 &&
+       chattr +AacDdijsStTu testFile* &&
+       cat testFile9 && chattr -AacDdijsStTu testFile* && lsattr testFile*; rm -rf testFile*; $OUT" \
+       "$_t\n------------- testFile\n------------- testFile1\n------------- testFile10\n------------- testFile11\n------------- testFile2\n------------- testFile3\n------------- testFile4\n------------- testFile5\n------------- testFile6\n------------- testFile7\n------------- testFile8\n------------- testFile9\n" "" ""
+testing "[-/+]AacDdijsStTu(random) FILE*" \
+        "$IN && echo "$_t" > testFile &&
+        chattr +AacDdijsStTu testFile* && cat testFile && chattr -A testFile* &&
+       chattr -a testFile* && chattr -c testFile* && chattr -D testFile* &&
+       chattr -d testFile* && chattr -i testFile* && chattr -j testFile* &&
+       chattr -s testFile* && chattr -S testFile* && chattr -t testFile* &&
+       chattr -T testFile* && chattr -u testFile* && lsattr testFile;
+        rm -rf testFile; $OUT" \
+       "$_t\n------------- testFile\n" "" ""
+testing "[-/+]i FILE[write]" \
+       "$IN && echo "$_t" > testFile &&
+       chattr +i testFile &&
+       echo \"$_t\" > testFile || lsattr testFile && chattr -i testFile;
+       rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
+testing "[-/+]A FILE[write]" \
+       "$IN && echo "$_t" > testFile && chattr +A testFile &&
+       echo \"$_t\" > testFile && lsattr testFile && chattr -A testFile;
+       rm -rf testFile; $OUT" "-------A----- testFile\n" "" ""
+testing "[-/+]s FILE[write]" \
+       "$IN && echo "$_t" > testFile && chattr +s testFile &&
+       echo \"$_t\" > testFile && lsattr testFile && chattr -s testFile
+       rm -rf testFile; $OUT" "s------------ testFile\n" "" ""
+testing "-v version FILE[write]" \
+       "$IN && echo "$_t" > testFile &&
+       chattr -v 1234 testFile && echo \"$_t\" > testFile && 
+       lsattr -v testFile; rm -rf testFile" \
+       " 1234 ------------- testFile\n" "" ""
+
+_a="-------A-----"
+testing "-R [-/+]a FILE" "$IN && touch aa && chattr -R +A aa && lsattr aa &&
+       chattr -R -A aa; rm -rf aa; $OUT" "$_a aa\n" "" ""
+testing "-R [-/+]a FILE.." "$IN && touch aa bb &&
+       chattr -R +A aa bb && lsattr aa bb &&
+       chattr -R -A aa bb; rm -rf aa bb; $OUT" "$_a aa\n$_a bb\n" "" ""
+
+# Clean up
+rm -rf testattr
index 5a741d9..16cd776 100755 (executable)
@@ -32,71 +32,71 @@ OUT="&& cd .. && echo \$(ls -lR testdir | awk '{print \$4}')"
 #testing "name" "command" "result" "infile" "stdin"
 
 # Basic smoketest
-testing "chgrp dir" "$IN chgrp root dir $OUT" \
+testing "dir" "$IN chgrp root dir $OUT" \
        "root $GRP $GRP $GRP $GRP $GRP $GRP\n" "" ""
-testing "chgrp file" "$IN chgrp root dir/file $OUT" \
+testing "file" "$IN chgrp root dir/file $OUT" \
        "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
-testing "chgrp dir and file" "$IN chgrp root dir dir/file $OUT" \
+testing "dir and file" "$IN chgrp root dir dir/file $OUT" \
        "root $GRP $GRP root $GRP $GRP $GRP\n" "" ""
 
 # symlinks (affect target, not symlink)
-testing "chgrp symlink->file" "$IN chgrp root dir2/file $OUT" \
+testing "symlink->file" "$IN chgrp root dir2/file $OUT" \
        "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
-testing "chgrp symlink->dir" "$IN chgrp root dir2/dir $OUT" \
+testing "symlink->dir" "$IN chgrp root dir2/dir $OUT" \
        "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
-testing "chgrp -h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
+testing "-h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
        "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
 
 # What does -h do (affect symlink, not target)
-testing "chgrp -h symlink->file" "$IN chgrp -h root dir2/file $OUT" \
+testing "-h symlink->file" "$IN chgrp -h root dir2/file $OUT" \
        "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
-testing "chgrp -h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
+testing "-h symlink->dir" "$IN chgrp -h root dir2/dir $OUT" \
        "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
 
 # chgrp -R (note, -h is implied by -R)
 
-testing "chgrp -R dir" "$IN chgrp -R root dir $OUT" \
+testing "-R dir" "$IN chgrp -R root dir $OUT" \
        "root $GRP root root root $GRP $GRP\n" "" ""
-testing "chgrp -R dir2" "$IN chgrp -R root dir2 $OUT" \
+testing "-R dir2" "$IN chgrp -R root dir2 $OUT" \
        "$GRP root $GRP $GRP $GRP root root\n" "" ""
-testing "chgrp -R symlink->dir" "$IN chgrp -R root dir2/dir $OUT" \
+testing "-R symlink->dir" "$IN chgrp -R root dir2/dir $OUT" \
        "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
-testing "chgrp -R symlink->file" "$IN chgrp -R root dir2/file $OUT" \
+testing "-R symlink->file" "$IN chgrp -R root dir2/file $OUT" \
        "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
 
 # chgrp -RP (same as -R by itself)
 
-testing "chgrp -RP dir2" "$IN chgrp -RP root dir2 $OUT" \
+testing "-RP dir2" "$IN chgrp -RP root dir2 $OUT" \
        "$GRP root $GRP $GRP $GRP root root\n" "" ""
-testing "chgrp -RP symlink->dir" "$IN chgrp -RP root dir2/dir $OUT" \
+testing "-RP symlink->dir" "$IN chgrp -RP root dir2/dir $OUT" \
        "$GRP $GRP $GRP $GRP $GRP root $GRP\n" "" ""
-testing "chgrp -RP symlink->file" "$IN chgrp -RP root dir2/file $OUT" \
+testing "-RP symlink->file" "$IN chgrp -RP root dir2/file $OUT" \
        "$GRP $GRP $GRP $GRP $GRP $GRP root\n" "" ""
 
 # chgrp -RH (change target but only recurse through symlink->dir on cmdline)
 
-testing "chgrp -RH dir2" "$IN chgrp -RH root dir2 $OUT" \
+testing "-RH dir2" "$IN chgrp -RH root dir2 $OUT" \
        "$GRP root root root $GRP $GRP $GRP\n" "" ""
-testing "chgrp -RH symlink->dir" "$IN chgrp -RH root dir2/dir $OUT" \
+testing "-RH symlink->dir" "$IN chgrp -RH root dir2/dir $OUT" \
        "$GRP $GRP root $GRP root $GRP $GRP\n" "" ""
-testing "chgrp -RH symlink->file" "$IN chgrp -RH root dir2/file $OUT" \
+testing "-RH symlink->file" "$IN chgrp -RH root dir2/file $OUT" \
        "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
 
 # chgrp -RL (change target and always recurse through symlink->dir)
 
-testing "chgrp -RL dir2" "$IN chgrp -RL root dir2 $OUT" \
+testing "-RL dir2" "$IN chgrp -RL root dir2 $OUT" \
        "$GRP root root root root $GRP $GRP\n" "" ""
-testing "chgrp -RL symlink->dir" "$IN chgrp -RL root dir2/dir $OUT" \
+testing "-RL symlink->dir" "$IN chgrp -RL root dir2/dir $OUT" \
        "$GRP $GRP root $GRP root $GRP $GRP\n" "" ""
-testing "chgrp -RL symlink->file" "$IN chgrp -RL root dir2/file $OUT" \
+testing "-RL symlink->file" "$IN chgrp -RL root dir2/file $OUT" \
        "$GRP $GRP $GRP root $GRP $GRP $GRP\n" "" ""
 
 # -HLP are NOPs without -R
-testing "chgrp -H without -R" "$IN chgrp -H root dir2/dir $OUT" \
+testing "-H without -R" "$IN chgrp -H root dir2/dir $OUT" \
        "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
-testing "chgrp -L without -R" "$IN chgrp -L root dir2/dir $OUT" \
+testing "-L without -R" "$IN chgrp -L root dir2/dir $OUT" \
        "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
-testing "chgrp -P without -R" "$IN chgrp -P root dir2/dir $OUT" \
+testing "-P without -R" "$IN chgrp -P root dir2/dir $OUT" \
        "$GRP $GRP root $GRP $GRP $GRP $GRP\n" "" ""
 
 rm -rf testdir
index 887f738..c2b64bd 100755 (executable)
@@ -41,205 +41,205 @@ do
         DASH=-
       fi
       DASHES=$(num2perm $u$g$o)
-      testing "chmod $u$g$o $type" "chmod $u$g$o $type && 
+      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
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod 750 dir 640 file" \
+testing "750 dir 640 file" \
   "chmod 750 dir 640 file 2>/dev/null ||
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-x---\n-rwxr-x---\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod 666 dir file" \
+testing "666 dir file" \
   "chmod 666 dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drw-rw-rw-\n-rw-rw-rw-\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod 765 *" "chmod 765 * &&
+testing "765 *" "chmod 765 * &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxrw-r-x\n-rwxrw-r-x\n" "" ""
 
 ##### u,g,o,a=r,w,x
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u=r dir file" "chmod u=r dir file &&
+testing "u=r dir file" "chmod u=r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr--r-xr-x\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u=w dir file" "chmod u=w dir file &&
+testing "u=w dir file" "chmod u=w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-w-r-xr-x\n--w-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u=x dir file" "chmod u=x dir file &&
+testing "u=x dir file" "chmod u=x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d--xr-xr-x\n---xr--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u+r dir file" "chmod u+r dir file &&
+testing "u+r dir file" "chmod u+r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u+w dir file" "chmod u+w dir file &&
+testing "u+w dir file" "chmod u+w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u+x dir file" "chmod u+x dir file &&
+testing "u+x dir file" "chmod u+x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rwxr--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u-r dir file" "chmod u-r dir file &&
+testing "u-r dir file" "chmod u-r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-wxr-xr-x\n--w-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u-w dir file" "chmod u-w dir file &&
+testing "u-w dir file" "chmod u-w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr-xr-xr-x\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod u-x dir file" "chmod u-x dir file &&
+testing "u-x dir file" "chmod u-x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drw-r-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g=r dir file" "chmod g=r dir file &&
+testing "g=r dir file" "chmod g=r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr--r-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g=w dir file" "chmod g=w dir file &&
+testing "g=w dir file" "chmod g=w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwx-w-r-x\n-rw--w-r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g=x dir file" "chmod g=x dir file &&
+testing "g=x dir file" "chmod g=x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwx--xr-x\n-rw---xr--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g+r dir file" "chmod g+r dir file &&
+testing "g+r dir file" "chmod g+r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g+w dir file" "chmod g+w dir file &&
+testing "g+w dir file" "chmod g+w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxrwxr-x\n-rw-rw-r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g+x dir file" "chmod g+x dir file &&
+testing "g+x dir file" "chmod g+x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r-xr--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g-r dir file" "chmod g-r dir file &&
+testing "g-r dir file" "chmod g-r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwx--xr-x\n-rw----r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g-w dir file" "chmod g-w dir file &&
+testing "g-w dir file" "chmod g-w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod g-x dir file" "chmod g-x dir file &&
+testing "g-x dir file" "chmod g-x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr--r-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o=r dir file" "chmod o=r dir file &&
+testing "o=r dir file" "chmod o=r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr--\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o=w dir file" "chmod o=w dir file &&
+testing "o=w dir file" "chmod o=w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-x-w-\n-rw-r---w-\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o=x dir file" "chmod o=x dir file &&
+testing "o=x dir file" "chmod o=x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-x--x\n-rw-r----x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o+r dir file" "chmod o+r dir file &&
+testing "o+r dir file" "chmod o+r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o+w dir file" "chmod o+w dir file &&
+testing "o+w dir file" "chmod o+w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xrwx\n-rw-r--rw-\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o+x dir file" "chmod o+x dir file &&
+testing "o+x dir file" "chmod o+x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r-x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o-r dir file" "chmod o-r dir file &&
+testing "o-r dir file" "chmod o-r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-x--x\n-rw-r-----\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o-w dir file" "chmod o-w dir file &&
+testing "o-w dir file" "chmod o-w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod o-x dir file" "chmod o-x dir file &&
+testing "o-x dir file" "chmod o-x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr--\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a=r dir file" "chmod a=r dir file &&
+testing "a=r dir file" "chmod a=r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr--r--r--\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a=w dir file" "chmod a=w dir file &&
+testing "a=w dir file" "chmod a=w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-w--w--w-\n--w--w--w-\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a=x dir file" "chmod a=x dir file &&
+testing "a=x dir file" "chmod a=x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d--x--x--x\n---x--x--x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a+r dir file" "chmod a+r dir file &&
+testing "a+r dir file" "chmod a+r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a+w dir file" "chmod a+w dir file &&
+testing "a+w dir file" "chmod a+w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxrwxrwx\n-rw-rw-rw-\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a+x dir file" "chmod a+x dir file &&
+testing "a+x dir file" "chmod a+x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rwxr-xr-x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a-r dir file" "chmod a-r dir file &&
+testing "a-r dir file" "chmod a-r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-wx--x--x\n--w-------\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a-w dir file" "chmod a-w dir file &&
+testing "a-w dir file" "chmod a-w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr-xr-xr-x\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod a-x dir file" "chmod a-x dir file &&
+testing "a-x dir file" "chmod a-x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drw-r--r--\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod =r dir file" "chmod =r dir file &&
+testing "=r dir file" "chmod =r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr--r--r--\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod =w dir file" "chmod =w dir file &&
+testing "=w dir file" "chmod =w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-w-------\n--w-------\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod =x dir file" "chmod =x dir file &&
+testing "=x dir file" "chmod =x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d--x--x--x\n---x--x--x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod +r dir file" "chmod +r dir file &&
+testing "+r dir file" "chmod +r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod +w dir file" "chmod +w dir file &&
+testing "+w dir file" "chmod +w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rw-r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod +x dir file" "chmod +x dir file &&
+testing "+x dir file" "chmod +x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drwxr-xr-x\n-rwxr-xr-x\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod -r dir file" "chmod -r dir file &&
+testing "-r dir file" "chmod -r dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "d-wx--x--x\n--w-------\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod -w dir file" "chmod -w dir file &&
+testing "-w dir file" "chmod -w dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "dr-xr-xr-x\n-r--r--r--\n" "" ""
 
 rm -rf dir file && mkdir dir && touch file
-testing "chmod -x dir file" "chmod -x dir file &&
+testing "-x dir file" "chmod -x dir file &&
    ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" "drw-r--r--\n-rw-r--r--\n" "" ""
 
 # Removing test files for cleanup purpose
index 408d5f3..95ad727 100755 (executable)
@@ -29,13 +29,13 @@ OUT="&& echo \$(ls -l testdir/file | awk '{print \$3,\$4}')"
 #testing "name" "command" "result" "infile" "stdin"
 
 # Basic smoketest
-testing "chown initial" "chown root:root $F $OUT" "root root\n" "" ""
-testing "chown usr:grp" "chown $USR:$GRP $F $OUT" "$USR $GRP\n" "" ""
-testing "chown root"    "chown root $F $OUT" "root $GRP\n" "" ""
+testing "initial" "chown root:root $F $OUT" "root root\n" "" ""
+testing "usr:grp" "chown $USR:$GRP $F $OUT" "$USR $GRP\n" "" ""
+testing "root"    "chown root $F $OUT" "root $GRP\n" "" ""
 # TODO: can we test "owner:"?
-testing "chown :grp"    "chown root:root $F && chown :$GRP $F $OUT" \
+testing ":grp"    "chown root:root $F && chown :$GRP $F $OUT" \
     "root $GRP\n" "" ""
-testing "chown :"       "chown $USR:$GRP $F && chown : $F $OUT" \
+testing ":"       "chown $USR:$GRP $F && chown : $F $OUT" \
     "$USR $GRP\n" "" ""
 
 rm -rf testdir
index 152dc12..01938f4 100755 (executable)
@@ -6,24 +6,24 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 # Default behavior on stdin and on files.
-testing "cksum on stdin" "echo -n hello | cksum" "3287646509 5\n" "" ""
+testing "on stdin" "echo -n hello | cksum" "3287646509 5\n" "" ""
 echo -n "hello" > tmpfile
-testing "cksum on file" "cksum tmpfile" "3287646509 5 tmpfile\n" "" ""
+testing "on file" "cksum tmpfile" "3287646509 5 tmpfile\n" "" ""
 rm -f tmpfile
 touch one two
-testing "cksum on multiple files" "cksum one two" "4294967295 0 one\n4294967295 0 two\n" "" ""
+testing "on multiple files" "cksum one two" "4294967295 0 one\n4294967295 0 two\n" "" ""
 rm -f one two
 
 # Check the length suppression, both calculate the CRC on 'abc' but the second
 # option has length suppression on and has the length concatenated to 'abc'.
-testing "cksum on abc including length" "echo -n 'abc' | cksum" "1219131554 3\n" "" ""
-testing "cksum on abc excluding length" "echo -ne 'abc\x3' | cksum -N" "1219131554 4\n" "" ""
+testing "on abc including length" "echo -n 'abc' | cksum" "1219131554 3\n" "" ""
+testing "on abc excluding length" "echo -ne 'abc\x3' | cksum -N" "1219131554 4\n" "" ""
 
 # cksum on no contents gives 0xffffffff (=4294967295)
-testing "cksum on no data post-inversion" "echo -n "" | cksum" "4294967295 0\n" "" ""
+testing "on no data post-inversion" "echo -n "" | cksum" "4294967295 0\n" "" ""
 # If we do preinversion we will then get 0.
-testing "cksum on no data pre+post-inversion" "echo -n "" | cksum -P" "0 0\n" "" ""
+testing "on no data pre+post-inversion" "echo -n "" | cksum -P" "0 0\n" "" ""
 # If we skip the post-inversion we also get 0
-testing "cksum on no data no inversion" "echo -n "" | cksum -I" "0 0\n" "" ""
+testing "on no data no inversion" "echo -n "" | cksum -I" "0 0\n" "" ""
 # Two wrongs make a right.
-testing "cksum on no data pre-inversion" "echo -n "" | cksum -PI" "4294967295 0\n" "" ""
+testing "on no data pre-inversion" "echo -n "" | cksum -PI" "4294967295 0\n" "" ""
index ef53b37..3b2dd1b 100755 (executable)
@@ -2,34 +2,36 @@
 
 [ -f testing.sh ] && . testing.sh
 
-testing "cmp not enough arguments [fail]" "cmp input 2>/dev/null || echo yes" "yes\n" "foo" ""
-testing "cmp missing file1 [fail]" "cmp file1 input 2>/dev/null || echo yes" "yes\n" "foo" ""
+testing "not enough arguments [fail]" "cmp input 2>/dev/null || echo yes" "yes\n" "foo" ""
+testing "missing file1 [fail]" "cmp file1 input 2>/dev/null || echo yes" "yes\n" "foo" ""
 
 #mkdir dir
-#testing "cmp directory [fail]" "cmp dir dir 2>/dev/null || echo yes" \
+#testing "directory [fail]" "cmp dir dir 2>/dev/null || echo yes" \
        #"yes\n" "" ""
 #rmdir dir
 
 echo "ab
 c" > input2
 
-testing "cmp identical files, stdout" "cmp input input2" "" "ab\nc\n" ""
-testing "cmp identical files, return code" "cmp input input2 && echo yes" "yes\n" "ab\nc\n" ""
+testing "identical files, stdout" "cmp input input2" "" "ab\nc\n" ""
+testing "identical files, return code" "cmp input input2 && echo yes" "yes\n" "ab\nc\n" ""
 
-testing "cmp EOF, stderr" "cmp input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
-testing "cmp EOF, return code" "cmp input input2 2>/dev/null || echo yes" "yes\n" "ab\nc\nx" ""
+testing "EOF, stderr" "cmp input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
+testing "EOF, return code" "cmp input input2 2>/dev/null || echo yes" "yes\n" "ab\nc\nx" ""
 # The gnu/dammit version fails this because posix says "char" and they don't.
-testing "cmp diff, stdout" "cmp input input2" "input input2 differ: char 4, line 2\n" "ab\nx\nx" ""
-testing "cmp diff, return code" "cmp input input2 > /dev/null || echo yes" "yes\n" "ab\nx\nx" ""
+testing "diff, stdout" "cmp input input2" "input input2 differ: char 4, line 2\n" "ab\nx\nx" ""
+testing "diff, return code" "cmp input input2 > /dev/null || echo yes" "yes\n" "ab\nx\nx" ""
 
-testing "cmp -s EOF, return code" "cmp -s input input2 || echo yes"  "yes\n" "ab\nc\nx" ""
-testing "cmp -s diff, return code" "cmp -s input input2 || echo yes" "yes\n" "ab\nx\nx" ""
+testing "-s EOF, return code" "cmp -s input input2 2>&1 || echo yes"  "yes\n" "ab\nc\nx" ""
+testing "-s diff, return code" "cmp -s input input2 2>&1 || echo yes" "yes\n" "ab\nx\nx" ""
 
-testing "cmp -l EOF, stderr" "cmp -l input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
-testing "cmp -l diff and EOF, stdout and stderr" "cmp -l input input2 2>&1 | sort" "4 170 143\ncmp: EOF on input2\n" "ab\nx\nx" ""
+testing "-l EOF, stderr" "cmp -l input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
+testing "-l diff and EOF, stdout and stderr" "cmp -l input input2 2>&1 | sort" "4 170 143\ncmp: EOF on input2\n" "ab\nx\nx" ""
+
+testing "-s not exist" "cmp -s input doesnotexist 2>&1 || echo yes" "yes\n" "" ""
 
 rm input2
 
-testing "cmp stdin and file" "cmp input -" "input - differ: char 4, line 2\n" "ab\nc\n" "ab\nx\n"
-#testing "cmp stdin and stdin" "cmp input -" "" "" "ab\nc\n"
+testing "stdin and file" "cmp input -" "input - differ: char 4, line 2\n" "ab\nc\n" "ab\nx\n"
+#testing "stdin and stdin" "cmp input -" "" "" "ab\nc\n"
 
index 4327256..2b89e2f 100755 (executable)
@@ -7,45 +7,45 @@ dd if=/dev/urandom of=random bs=64 count=1 2> /dev/null
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "cp not enough arguments [fail]" "cp one 2>/dev/null || echo yes" \
+testing "not enough arguments [fail]" "cp one 2>/dev/null || echo yes" \
        "yes\n" "" ""
-testing "cp -missing source [fail]" "cp missing two 2>/dev/null || echo yes" \
+testing "-missing source [fail]" "cp missing two 2>/dev/null || echo yes" \
        "yes\n" "" ""
-testing "cp file->file" "cp random two && cmp random two && echo yes" \
+testing "file->file" "cp random two && cmp random two && echo yes" \
        "yes\n" "" ""
 rm two
 
 mkdir two
-testing "cp file->dir" "cp random two && cmp random two/random && echo yes" \
+testing "file->dir" "cp random two && cmp random two/random && echo yes" \
        "yes\n" "" ""
 rm two/random
-testing "cp file->dir/file" \
+testing "file->dir/file" \
        "cp random two/random && cmp random two/random && echo yes" \
        "yes\n" "" ""
-testing "cp -r dir->missing" \
+testing "-r dir->missing" \
        "cp -r two three && cmp random three/random && echo yes" \
        "yes\n" "" ""
 touch walrus
-testing "cp -r dir->file [fail]" \
+testing "-r dir->file [fail]" \
        "cp -r two walrus 2>/dev/null || echo yes" "yes\n" "" ""
 touch two/three
-testing "cp -r dir hits file." \
+testing "-r dir hits file." \
        "cp -r three two 2>/dev/null || echo yes" "yes\n" "" ""
 rm -rf two three walrus
 
 touch two
 chmod 000 two
-testing "cp file->inaccessable [fail]" \
+testing "file->inaccessable [fail]" \
        "cp random two 2>/dev/null || echo yes" "yes\n" "" ""
 rm -f two
 
 touch two
 chmod 000 two
-testing "cp -f file->inaccessable" \
+testing "-f file->inaccessable" \
        "cp -f random two && cmp random two && echo yes" "yes\n" "" ""
 mkdir sub
 chmod 000 sub
-testing "cp file->inaccessable_dir [fail]" \
+testing "file->inaccessable_dir [fail]" \
        "cp random sub 2>/dev/null || echo yes" "yes\n" "" ""
 rm two
 rmdir sub
@@ -56,21 +56,21 @@ rmdir sub
 
 #mkdir dir
 #touch file
-#testing "cp -rf dir file [fail]" "cp -rf dir file 2>/dev/null || echo yes" \
+#testing "-rf dir file [fail]" "cp -rf dir file 2>/dev/null || echo yes" \
 #      "yes\n" "" ""
 #rm -rf dir file
 
 touch one two
-testing "cp file1 file2 missing [fail]" \
+testing "file1 file2 missing [fail]" \
        "cp one two missing 2>/dev/null || echo yes" "yes\n" "" ""
 mkdir dir
-testing "cp dir file missing [fail]" \
+testing "dir file missing [fail]" \
        "cp dir two missing 2>/dev/null || echo yes" "yes\n" "" ""
-testing "cp -rf dir file missing [fail]" \
+testing "-rf dir file missing [fail]" \
        "cp dir two missing 2>/dev/null || echo yes" "yes\n" "" ""
-testing "cp file1 file2 file [fail]" \
+testing "file1 file2 file [fail]" \
        "cp random one two 2>/dev/null || echo yes" "yes\n" "" ""
-testing "cp file1 file2 dir" \
+testing "file1 file2 dir" \
        "cp random one dir && cmp random dir/random && cmp one dir/one && echo yes" \
        "yes\n" "" ""
 rm one two random
@@ -79,13 +79,13 @@ rm -rf dir
 mkdir -p one/two/three/four
 touch one/two/three/five
 touch one/{six,seven,eight}
-testing "cp -r /abspath dest" \
+testing "-r /abspath dest" \
        "cp -r \"$(readlink -f one)\" dir && diff -r one dir && echo yes" \
        "yes\n" "" ""
-testing "cp -r dir again" "cp -r one/. dir && diff -r one dir && echo yes" \
+testing "-r dir again" "cp -r one/. dir && diff -r one dir && echo yes" \
        "yes\n" "" ""
 mkdir dir2
-testing "cp -r dir1/* dir2" \
+testing "-r dir1/* dir2" \
        "cp -r one/* dir2 && diff -r one dir2 && echo yes" "yes\n" "" ""
 rm -rf one dir dir2
 
index 2d91b06..5158efa 100755 (executable)
@@ -7,14 +7,14 @@
 # plus file sizes of at least 0-4.
 
 touch a bb ccc dddd
-testing "cpio name padding" "cpio -o -H newc|cpio -it" "a\nbb\nccc\ndddd\n" "" "a\nbb\nccc\ndddd\n"
+testing "name padding" "cpio -o -H newc|cpio -it" "a\nbb\nccc\ndddd\n" "" "a\nbb\nccc\ndddd\n"
 rm a bb ccc dddd
 
 touch a
 printf '1' >b
 printf '22' >c
 printf '333' >d
-testing "cpio file padding" "cpio -o -H newc|cpio -it" "a\nb\nc\nd\n" "" "a\nb\nc\nd\n"
+testing "file padding" "cpio -o -H newc|cpio -it" "a\nb\nc\nd\n" "" "a\nb\nc\nd\n"
 rm a b c d
 
 touch a
@@ -25,15 +25,15 @@ printf '333' >dddd
 # the relevant bit should be here:
 # 110*5 + 4*3 + 2 + 6*3 = 550 + 12 + 20 = 582
 # files are padded to n*4, names are padded to 2 + n*4 due to the header length
-testing "cpio archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5 2>/dev/null" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
-testing "cpio archive magic" "cpio -o -H newc|dd ibs=2 count=3 2>/dev/null" "070701" "" "a\n"
+testing "archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5 2>/dev/null" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
+testing "archive magic" "cpio -o -H newc|dd ibs=2 count=3 2>/dev/null" "070701" "" "a\n"
 # check name length (8 bytes before the empty "crc")
-testing "cpio name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4 2>/dev/null" "00000002" "" "a\n"
+testing "name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4 2>/dev/null" "00000002" "" "a\n"
 rm a bb ccc dddd
 
 # archive dangling symlinks and empty files even if we cannot open them
 touch a; chmod a-rwx a; ln -s a/cant b
-testing "cpio archives unreadable empty files" "cpio -o -H newc|cpio -it" "a\nb\n" "" "a\nb\n"
+testing "archives unreadable empty files" "cpio -o -H newc|cpio -it" "a\nb\n" "" "a\nb\n"
 chmod u+rw a; rm -f a b
 
 
index a001952..f84acea 100755 (executable)
@@ -14,20 +14,20 @@ echo "one:two:three:four:five:six:seven
 alpha:beta:gamma:delta:epsilon:zeta:eta:teta:iota:kappa:lambda:mu
 the quick brown fox jumps over the lazy dog" >abc.txt
 
-testing "cut with -c (a-b)" "cut -c 4-10 abc.txt" ":two:th\nha:beta\n quick \n" "" ""
-testing "cut with -f (a-)" "cut -d ':' -f 5- abc.txt" "five:six:seven\nepsilon:zeta:eta:teta:iota:kappa:lambda:mu\nthe quick brown fox jumps over the lazy dog\n" "" ""
+testing "with -c (a-b)" "cut -c 4-10 abc.txt" ":two:th\nha:beta\n quick \n" "" ""
+testing "with -f (a-)" "cut -d ':' -f 5- abc.txt" "five:six:seven\nepsilon:zeta:eta:teta:iota:kappa:lambda:mu\nthe quick brown fox jumps over the lazy dog\n" "" ""
 
-testing "cut with -f (a)" "cut -d ' ' -f 3 abc.txt" "one:two:three:four:five:six:seven\nalpha:beta:gamma:delta:epsilon:zeta:eta:teta:iota:kappa:lambda:mu\nbrown\n" "" ""
+testing "with -f (a)" "cut -d ' ' -f 3 abc.txt" "one:two:three:four:five:six:seven\nalpha:beta:gamma:delta:epsilon:zeta:eta:teta:iota:kappa:lambda:mu\nbrown\n" "" ""
 
-testing "cut with echo, -c (a-b)" "echo 'ref_categorie=test' | cut -c 1-15 " "ref_categorie=t\n" "" ""
-testing "cut with echo, -c (a)" "echo 'ref_categorie=test' | cut -c 14" "=\n" "" ""
+testing "with echo, -c (a-b)" "echo 'ref_categorie=test' | cut -c 1-15 " "ref_categorie=t\n" "" ""
+testing "with echo, -c (a)" "echo 'ref_categorie=test' | cut -c 14" "=\n" "" ""
 
 # Modifying abc.txt data as per new testcase
 echo "abcdefghijklmnopqrstuvwxyz" >abc.txt
 
-testing "cut with -c (a,b,c)" "cut -c 4,5,20 abc.txt" "det\n" "" ""
+testing "with -c (a,b,c)" "cut -c 4,5,20 abc.txt" "det\n" "" ""
 
-testing "cut with -b (a,b,c)" "cut -b 4,5,20 abc.txt" "det\n" "" ""
+testing "with -b (a,b,c)" "cut -b 4,5,20 abc.txt" "det\n" "" ""
 
 # Modifying abc.txt data as per testcase
 echo "406378:Sales:Itorre:Jan
@@ -35,13 +35,13 @@ echo "406378:Sales:Itorre:Jan
 636496:Research:Ancholie:Mel
 396082:Sales:Jucacion:Ed" >abc.txt
 
-testing "cut with -d -f(:) -s" "cut -d: -f3 -s abc.txt" "Itorre\nNasium\nAncholie\nJucacion\n" "" ""
+testing "with -d -f(:) -s" "cut -d: -f3 -s abc.txt" "Itorre\nNasium\nAncholie\nJucacion\n" "" ""
 
-testing "cut with -d -f( ) -s" "cut -d' ' -f3 -s abc.txt && echo yes" "yes\n" "" ""
+testing "with -d -f( ) -s" "cut -d' ' -f3 -s abc.txt && echo yes" "yes\n" "" ""
 
-testing "cut with -d -f(a) -s" "cut -da -f3 -s abc.txt" "n\nsium:Jim\n\ncion:Ed\n" "" ""
+testing "with -d -f(a) -s" "cut -da -f3 -s abc.txt" "n\nsium:Jim\n\ncion:Ed\n" "" ""
 
-testing "cut with -d -f(a) -s -n" "cut -da -f3 -s -n abc.txt" "n\nsium:Jim\n\ncion:Ed\n" "" ""
+testing "with -d -f(a) -s -n" "cut -da -f3 -s -n abc.txt" "n\nsium:Jim\n\ncion:Ed\n" "" ""
 
 # Removing abc.txt file for cleanup purpose
 rm abc.txt
index 62c0ed6..213174e 100644 (file)
@@ -5,22 +5,22 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 # Test Unix date parsing.
-testing "date -d @0" "TZ=UTC date -d @0 2>&1" "Thu Jan  1 00:00:00 GMT 1970\n" "" ""
-testing "date -d @0x123" "TZ=UTC date -d @0x123 2>&1" "date: bad date '@0x123'\n" "" ""
+testing "-d @0" "TZ=UTC date -d @0 2>&1" "Thu Jan  1 00:00:00 GMT 1970\n" "" ""
+testing "-d @0x123" "TZ=UTC date -d @0x123 2>&1" "date: bad date '@0x123'\n" "" ""
 
 # Test basic date parsing.
 # Note that toybox's -d format is not the same as coreutils'.
-testing "date -d 06021234" "TZ=UTC date -d 06021234 2>&1" "Sun Jun  2 12:34:00 UTC 1900\n" "" ""
-testing "date -d 060212341982" "TZ=UTC date -d 060212341982 2>&1" "Sun Jun  2 12:34:00 UTC 1982\n" "" ""
-testing "date -d 123" "TZ=UTC date -d 123 2>&1" "date: bad date '123'\n" "" ""
+testing "-d 06021234" "TZ=UTC date -d 06021234 2>&1" "Sun Jun  2 12:34:00 UTC 1900\n" "" ""
+testing "-d 060212341982" "TZ=UTC date -d 060212341982 2>&1" "Sun Jun  2 12:34:00 UTC 1982\n" "" ""
+testing "-d 123" "TZ=UTC date -d 123 2>&1" "date: bad date '123'\n" "" ""
 
 # Test parsing 2- and 4-digit years.
-testing "date -d 1110143115.30" "TZ=UTC date -d 1110143115.30 2>&1" "Sun Nov 10 14:31:30 UTC 1915\n" "" ""
-testing "date -d 111014312015.30" "TZ=UTC date -d 111014312015.30 2>&1" "Sun Nov 10 14:31:30 UTC 2015\n" "" ""
+testing "-d 1110143115.30" "TZ=UTC date -d 1110143115.30 2>&1" "Sun Nov 10 14:31:30 UTC 1915\n" "" ""
+testing "-d 111014312015.30" "TZ=UTC date -d 111014312015.30 2>&1" "Sun Nov 10 14:31:30 UTC 2015\n" "" ""
 
 # Accidentally given a Unix time, we should trivially reject that.
-testing "date Unix time missing @" "TZ=UTC date 1438053157 2>&1" \
-  "date: bad date '1438053157'; Wed February 38 05:31:00 UTC 2057 != Sun Mar 10 05:31:00 UTC 2058\n" "" ""
+testing "Unix time missing @" "TZ=UTC date 1438053157 2>/dev/null || echo no" \
+  "no\n" "" ""
 # But some invalid dates are more subtle, like Febuary 29th in a non-leap year.
-testing "date Feb 29th" "TZ=UTC date 022900001975 2>&1" \
-  "date: bad date '022900001975'; Wed Feb 29 00:00:00 UTC 1975 != Sat Mar  1 00:00:00 UTC 1975\n" "" ""
+testing "Feb 29th" "TZ=UTC date 022900001975 2>/dev/null || echo no" \
+  "no\n" "" ""
index 9bdcac5..9da2108 100755 (executable)
@@ -10,74 +10,84 @@ opt="2>/dev/null"
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "dd if=(file)" "dd if=input $opt" "I WANT\n" "I WANT\n" ""
-testing "dd of=(file)" "dd of=file $opt && cat file" "I WANT\n" "" "I WANT\n"
-testing "dd if=file of=file" "dd if=input of=foo $opt && cat foo && rm -f foo" \
+# Test suffixed number parsing; `count` is representative.
+testing "count=2" "dd if=input count=2 ibs=1 $opt" "hi" "high\n" ""
+testing "count= 2" "dd if=input 'count= 2' ibs=1 $opt" "hi" "high\n" ""
+testing "count=0x2" "dd if=input 'count=0x2' ibs=1 $opt" "hi" "high\n" ""
+testing "count=-2" "dd if=input 'count=-2' ibs=1 2>&1" "dd: invalid number '-2'\n" "" ""
+
+testing "if=(file)" "dd if=input $opt" "I WANT\n" "I WANT\n" ""
+testing "of=(file)" "dd of=file $opt && cat file" "I WANT\n" "" "I WANT\n"
+testing "if=file of=file" "dd if=input of=foo $opt && cat foo && rm -f foo" \
   "I WANT\n" "I WANT\n" ""
-testing "dd if=file | dd of=file" "dd if=input $opt | dd of=foo $opt &&
+testing "if=file | dd of=file" "dd if=input $opt | dd of=foo $opt &&
    cat foo && rm -f foo" "I WANT\n" "I WANT\n" ""
-testing "dd (stdout)" "dd $opt" "I WANT\n" "" "I WANT\n"
-testing "dd sync,noerror" \
+testing "(stdout)" "dd $opt" "I WANT\n" "" "I WANT\n"
+testing "sync,noerror" \
   "dd if=input of=outFile seek=8860 bs=1M conv=sync,noerror $opt &&
    stat -c \"%s\" outFile && rm -f outFile" "9291431936\n" "I WANT\n" ""
-testing "dd if=file of=(null)" \
+testing "if=file of=(null)" \
   "dd if=input of=/dev/null $opt && echo 'yes'" "yes\n" "I WANT\n" ""
-testing "dd with if of bs" \
+testing "with if of bs" \
   "dd if=/dev/zero of=sda.txt bs=512 count=1 $opt &&
    stat -c '%s' sda.txt && rm -f sda.txt" "512\n" "" ""
-testing "dd with if of ibs obs" \
+testing "with if of ibs obs" \
   "dd if=/dev/zero of=sda.txt ibs=512 obs=256 count=1 $opt &&
    stat -c '%s' sda.txt && rm -f sda.txt" "512\n" "" ""
-testing "dd with if of ibs obs count" \
+testing "with if of ibs obs count" \
   "dd if=/dev/zero of=sda.txt ibs=512 obs=256 count=3 $opt &&
    stat -c '%s' sda.txt && rm -f sda.txt" "1536\n" "" ""
 
 ln -s input softlink
-testing "dd if=softlink" "dd if=softlink $opt" "I WANT\n" "I WANT\n" ""
+testing "if=softlink" "dd if=softlink $opt" "I WANT\n" "I WANT\n" ""
 unlink softlink
 
 ln -s file softlink
-testing "dd if=file of=softlink" "dd if=input of=softlink $opt &&
+testing "if=file of=softlink" "dd if=input of=softlink $opt &&
    [ -f file -a -L softlink ] && cat softlink" "I WANT\n" "I WANT\n" ""
 unlink softlink && rm -f file
 
-testing "dd if=file of=file (same file)" "dd if=input of=input $opt &&
+testing "if=file of=file (same file)" "dd if=input of=input $opt &&
    [ -f input ] && cat input && echo 'yes'" "yes\n" "I WANT\n" ""
+testing "same file notrunc" \
+  "dd if=input of=input conv=notrunc $opt && cat input" \
+  "I WANT\n" "I WANT\n" ""
 
-testing "dd with ibs obs bs" "dd ibs=2 obs=5 bs=9 $opt" "I WANT\n" "" "I WANT\n"
-testing "dd with ibs obs bs if" "dd ibs=2 obs=5 bs=9 if=input $opt" \
+testing "with ibs obs bs" "dd ibs=2 obs=5 bs=9 $opt" "I WANT\n" "" "I WANT\n"
+testing "with ibs obs bs if" "dd ibs=2 obs=5 bs=9 if=input $opt" \
   "I WANT\n" "I WANT\n" ""
 
-testing "dd with ibs obs count" "dd ibs=1 obs=1 count=1 $opt" "I" "" "I WANT\n"
-testing "dd with ibs obs count if" "dd ibs=1 obs=1 count=3 if=input $opt" \
+testing "with ibs obs count" "dd ibs=1 obs=1 count=1 $opt" "I" "" "I WANT\n"
+testing "with ibs obs count if" "dd ibs=1 obs=1 count=3 if=input $opt" \
   "I W" "I WANT\n" ""
 
-testing "dd with count" "dd count=1 $opt" "I WANT\n" "" "I WANT\n"
-testing "dd with count if" "dd count=1 if=input $opt" "I WANT\n" "I WANT\n" ""
-
-testing "dd with skip" "dd skip=0 $opt" "I WANT\n" "" "I WANT\n"
-testing "dd with skip if" "dd skip=0 if=input $opt" "I WANT\n" "I WANT\n" ""
+testing "with count" "dd count=1 $opt" "I WANT\n" "" "I WANT\n"
+testing "with count if" "dd count=1 if=input $opt" "I WANT\n" "I WANT\n" ""
 
-testing "dd with seek" "dd seek=0 $opt" "I WANT\n" "" "I WANT\n"
-testing "dd with seek if" "dd seek=0 if=input $opt" "I WANT\n" "I WANT\n" ""
+testing "with skip" "dd skip=0 $opt" "I WANT\n" "" "I WANT\n"
+testing "with skip if" "dd skip=0 if=input $opt" "I WANT\n" "I WANT\n" ""
 
-# These type of conv is not supported in toybox: 'ascii', 'ebcdic', 'ibm',
-# 'block', 'unblock', 'nocreat', 'notronc', 'lcase', 'ucase', 'excl', 'swab'
+testing "with seek" "dd seek=0 $opt" "I WANT\n" "" "I WANT\n"
+testing "with seek if" "dd seek=0 if=input $opt" "I WANT\n" "I WANT\n" ""
 
 # Testing only 'notrunc', 'noerror', 'fsync', 'sync'
 
-testing "dd conv=notrunc" "dd conv=notrunc $opt" "I WANT\n" "" "I WANT\n"
-testing "dd conv=notrunc with IF" "dd conv=notrunc if=input $opt" "I WANT\n" \
+testing "conv=notrunc" "dd conv=notrunc $opt" "I WANT\n" "" "I WANT\n"
+testing "conv=notrunc with IF" "dd conv=notrunc if=input $opt" "I WANT\n" \
   "I WANT\n" ""
 
-testing "dd conv=noerror" "dd conv=noerror $opt" "I WANT\n" "" "I WANT\n"
-testing "dd conv=noerror with IF" "dd conv=noerror if=input $opt" "I WANT\n" \
+testing "conv=noerror" "dd conv=noerror $opt" "I WANT\n" "" "I WANT\n"
+testing "conv=noerror with IF" "dd conv=noerror if=input $opt" "I WANT\n" \
   "I WANT\n" ""
 
-testing "dd conv=fsync" "dd conv=fsync $opt" "I WANT\n" "" "I WANT\n"
-testing "dd conv=fsync with IF" "dd conv=fsync if=input $opt" "I WANT\n" \
+testing "conv=fsync" "dd conv=fsync $opt" "I WANT\n" "" "I WANT\n"
+testing "conv=fsync with IF" "dd conv=fsync if=input $opt" "I WANT\n" \
   "I WANT\n" ""
 
-testing "dd conv=sync" "dd conv=sync $opt | head -n 1" "I WANT\n" "" "I WANT\n"
-testing "dd conv=sync with IF" "dd conv=sync if=input $opt | head -n 1" "I WANT\n" \
+testing "conv=sync" "dd conv=sync $opt | head -n 1" "I WANT\n" "" "I WANT\n"
+testing "conv=sync with IF" "dd conv=sync if=input $opt | head -n 1" "I WANT\n" \
   "I WANT\n" ""
+
+# status=noxfer|none
+testing "status=noxfer" "dd if=input status=noxfer ibs=1 2>&1" "input\n6+0 records in\n0+1 records out\n" "input\n" ""
+testing "status=none" "dd if=input status=none ibs=1 2>&1" "input\n" "input\n" ""
diff --git a/tests/diff.test b/tests/diff.test
new file mode 100755 (executable)
index 0000000..ca0b682
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+#testing "name" "command" "result" "infile" "stdin"
+
+seq 10 > left
+seq 11 > right
+
+expected='--- left
++++ right
+@@ -8,3 +8,4 @@
+ 8
+ 9
+ 10
++11
+'
+# Hm this only gives unified diffs?
+testing "simple" "diff left right" "$expected" "" ""
+
+
+expected='--- tree1/file
++++ tree2/file
+@@ -1 +1 @@
+-foo
++food
+'
+mkdir -p tree1 tree2
+echo foo > tree1/file
+echo food > tree2/file
+
+testing "simple" "diff -r tree1 tree2 |tee out" "$expected" "" ""
index a007538..3ea8919 100755 (executable)
@@ -4,7 +4,7 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "dirname /-only" "dirname ///////" "/\n" "" ""
-testing "dirname trailing /" "dirname a//////" ".\n" "" ""
-testing "dirname combined" "dirname /////a///b///c///d/////" "/////a///b///c\n" "" ""
-testing "dirname /a/" "dirname /////a///" "/\n" "" ""
+testing "/-only" "dirname ///////" "/\n" "" ""
+testing "trailing /" "dirname a//////" ".\n" "" ""
+testing "combined" "dirname /////a///b///c///d/////" "/////a///b///c\n" "" ""
+testing "/a/" "dirname /////a///" "/\n" "" ""
index 81fb528..a096074 100755 (executable)
@@ -9,23 +9,23 @@
 # while -k is the default on most Linux systems
 
 mkdir -p du_test/test du_2/foo
-testing "du (no options)" "du -k du_test" "4\tdu_test/test\n8\tdu_test\n" "" ""
-testing "du -s" "du -k -s du_test" "8\tdu_test\n" "" ""
+testing "(no options)" "du -k du_test" "4\tdu_test/test\n8\tdu_test\n" "" ""
+testing "-s" "du -k -s du_test" "8\tdu_test\n" "" ""
 ln -s ../du_2 du_test/xyz
 # "du shall count the size of the symbolic link"
 # The tests assume that like for most POSIX systems symbolic
 # links are stored directly in the inode so that the
 # allocated file space is zero.
-testing "du counts symlinks without following" "du -ks du_test" "8\tdu_test\n" "" ""
-testing "du -L follows symlinks" "du -ksL du_test" "16\tdu_test\n" "" ""
+testing "counts symlinks without following" "du -ks du_test" "8\tdu_test\n" "" ""
+testing "-L follows symlinks" "du -ksL du_test" "16\tdu_test\n" "" ""
 ln -s . du_test/up
-testing "du -L avoid endless loop" "du -ksL du_test" "16\tdu_test\n" "" ""
+testing "-L avoid endless loop" "du -ksL du_test" "16\tdu_test\n" "" ""
 rm du_test/up
 # if -H and -L are specified, the last takes priority
-testing "du -HL follows symlinks" "du -ksHL du_test" "16\tdu_test\n" "" ""
-testing "du -H does not follow unspecified symlinks" "du -ksH du_test" "8\tdu_test\n" "" ""
-testing "du -LH does not follow unspecified symlinks" "du -ksLH du_test" "8\tdu_test\n" "" ""
-testing "du -H follows specified symlinks" "du -ksH du_test/xyz" "8\tdu_test/xyz\n" "" ""
+testing "-HL follows symlinks" "du -ksHL du_test" "16\tdu_test\n" "" ""
+testing "-H does not follow unspecified symlinks" "du -ksH du_test" "8\tdu_test\n" "" ""
+testing "-LH does not follow unspecified symlinks" "du -ksLH du_test" "8\tdu_test\n" "" ""
+testing "-H follows specified symlinks" "du -ksH du_test/xyz" "8\tdu_test/xyz\n" "" ""
 
 rm -rf du_test du_2
 
index 3f562fd..5849964 100755 (executable)
@@ -12,26 +12,26 @@ CMD="$(which echo)"
 #testing "name" "command" "result" "infile" "stdin"
 
 testing "echo" "$CMD && echo yes" "\nyes\n" "" ""
-testing "echo 1 2 3" "$CMD one  two    three" "one two three\n" "" ""
-testing "echo with spaces" "$CMD 'one  two     three'" \
+testing "1 2 3" "$CMD one  two three" "one two three\n" "" ""
+testing "with spaces" "$CMD 'one  two  three'" \
        "one  two       three\n" "" ""
-testing "echo -n" "$CMD -n" "" "" ""
-testing "echo -n one" "$CMD -n one" "one" "" ""
-testing "echo one -n" "$CMD one -n" "one -n\n" "" ""
-testing "echo -en" "$CMD -en 'one\ntwo'" "one\ntwo" "" ""
-testing "echo --hello" "$CMD --hello" "--hello\n" "" ""
-testing "echo -e all" "$CMD -e '\a\b\c\f\n\r\t\v\\\0123'" \
+testing "-n" "$CMD -n" "" "" ""
+testing "-n one" "$CMD -n one" "one" "" ""
+testing "one -n" "$CMD one -n" "one -n\n" "" ""
+testing "-en" "$CMD -en 'one\ntwo'" "one\ntwo" "" ""
+testing "--hello" "$CMD --hello" "--hello\n" "" ""
+testing "-e all" "$CMD -e '\a\b\c\f\n\r\t\v\\\0123'" \
        "\a\b\c\f\n\r\t\v\\\0123\n" "" ""
-testing "echo -nex hello" "$CMD -nex hello" "-nex hello\n" "" ""
+testing "-nex hello" "$CMD -nex hello" "-nex hello\n" "" ""
 
 # Octal formatting tests
-testing "echo -e octal values" \
+testing "-e octal values" \
        "$CMD -ne '\01234 \0060 \060 \0130\0131\0132 \077\012'" \
        "S4 0 0 XYZ ?\n" "" ""
 
 # Hexadecimal value tests
-testing "echo -e hexadecimal values" \
+testing "-e hexadecimal values" \
        "$CMD -ne '\x534 \x30 \x58\x59\x5a \x3F\x0A'"\
        "S4 0 XYZ ?\n" "" ""
 
-testing "echo -e \p" "$CMD -e '\\p'" "\\p\n" "" ""
+testing "-e \p" "$CMD -e '\\p'" "\\p\n" "" ""
index 510f89f..d93d070 100755 (executable)
@@ -7,11 +7,11 @@
 
 # some basic tests
 
-testing "expand default" "expand input" "        foo     bar\n" "\tfoo\tbar\n" ""
-testing "expand default stdin" "expand"  "        foo     bar\n" "" "\tfoo\tbar\n"
-testing "expand single" "expand -t 2 input" "  foo bar\n" "\tfoo\tbar\n" ""
-testing "expand tablist" "expand -t 5,10,12 input" "     foo  bar foo\n" "\tfoo\tbar\tfoo\n" ""
-testing "expand backspace" "expand input" "foobarfoo\b\b bar\n" "foobarfoo\b\b\tbar\n" ""
+testing "default" "expand input" "        foo     bar\n" "\tfoo\tbar\n" ""
+testing "default stdin" "expand"  "        foo     bar\n" "" "\tfoo\tbar\n"
+testing "single" "expand -t 2 input" "  foo bar\n" "\tfoo\tbar\n" ""
+testing "tablist" "expand -t 5,10,12 input" "     foo  bar foo\n" "\tfoo\tbar\tfoo\n" ""
+testing "backspace" "expand input" "foobarfoo\b\b bar\n" "foobarfoo\b\b\tbar\n" ""
 
 # advanced tests
 
@@ -22,12 +22,12 @@ for i in $(seq $POW); do
     BIGTAB=$BIGTAB$BIGTAB
     TABSTOP=$[$TABSTOP*2]
 done
-testing "expand long tab single" "expand -t $TABSTOP input" "${BIGTAB}foo\n" "\tfoo\n" ""
-testing "expand long tab tablist" "expand -t $TABSTOP,$[TABSTOP+5] input" \
+testing "long tab single" "expand -t $TABSTOP input" "${BIGTAB}foo\n" "\tfoo\n" ""
+testing "long tab tablist" "expand -t $TABSTOP,$[TABSTOP+5] input" \
         "${BIGTAB}foo  bar\n" "\tfoo\tbar\n" ""
 
-testing "expand multiline single" "expand -t 4 input" "foo \n    bar\n" "foo\t\n\tbar\n" ""
-testing "expand multiline tablist" "expand -t 4,8 input" \
+testing "multiline single" "expand -t 4 input" "foo \n    bar\n" "foo\t\n\tbar\n" ""
+testing "multiline tablist" "expand -t 4,8 input" \
         "foo     bar\n    bar foo\n" "foo\t\tbar\n\tbar\tfoo\n" ""
 POW=15
 BIGLINE="foo "
@@ -40,5 +40,5 @@ else
     EXPANDLINE="${BIGLINE}    foo\n"
 fi
 BIGLINE="${BIGLINE}\tfoo\n"
-testing "expand long line single" "expand input" \
+testing "long line single" "expand input" \
         "${EXPANDLINE}" "$BIGLINE" ""
index 33900d7..da3feea 100755 (executable)
@@ -2,20 +2,61 @@
 
 [ -f testing.sh ] && . testing.sh
 
-testing "expr integer" "expr 5" "5\n" "" ""
-testing "expr integer negative" "expr -5" "-5\n" "" ""
-testing "expr string" "expr astring" "astring\n" "" ""
-testing "expr 1 + 3" "expr 1 + 3" "4\n" "" ""
-testing "expr 5 + 6 * 3" "expr 5 + 6 \* 3" "23\n" "" ""
-testing "expr ( 5 + 6 ) * 3" "expr \( 5 + 6 \) \* 3" "33\n" "" ""
-testing "expr * / same priority" "expr 4 \* 3 / 2"  "6\n" "" ""
-testing "expr / * same priority" "expr 3 / 2 \* 4" "4\n" "" ""
-testing "expr & before |" "expr 0 \| 1 \& 0" "0\n" "" ""
-testing "expr | after &" "expr 1 \| 0 \& 0" "1\n" "" ""
-testing "expr | & same priority" "expr 0 \& 0 \| 1" "1\n" "" ""
-testing "expr % * same priority" "expr 3 % 2 \* 4" "4\n" "" ""
-testing "expr * % same priority" "expr 3 \* 2 % 4" "2\n" "" ""
-testing "expr = > same priority" "expr 0 = 2 \> 3" "0\n" "" ""
-testing "expr > = same priority" "expr 3 \> 2 = 1" "1\n" "" ""
-testing "expr string becomes integer" "expr ab21xx : '[^0-9]*\([0-9]*\)' + 3" \
+testing "integer" "expr 5" "5\n" "" ""
+testing "integer negative" "expr -5" "-5\n" "" ""
+testing "string" "expr astring" "astring\n" "" ""
+testing "addition" "expr 1 + 3" "4\n" "" ""
+testing "5 + 6 * 3" "expr 5 + 6 \* 3" "23\n" "" ""
+testing "( 5 + 6 ) * 3" "expr \( 5 + 6 \) \* 3" "33\n" "" ""
+testing ">" "expr 3 \> 2" "1\n" "" ""
+testing "* / same priority" "expr 4 \* 3 / 2"  "6\n" "" ""
+testing "/ * same priority" "expr 3 / 2 \* 4" "4\n" "" ""
+testing "& before |" "expr 0 \| 1 \& 0" "0\n" "" ""
+testing "| after &" "expr 1 \| 0 \& 0" "1\n" "" ""
+testing "| & same priority" "expr 0 \& 0 \| 1" "1\n" "" ""
+testing "% * same priority" "expr 3 % 2 \* 4" "4\n" "" ""
+testing "* % same priority" "expr 3 \* 2 % 4" "2\n" "" ""
+testing "= > same priority" "expr 0 = 2 \> 3" "0\n" "" ""
+testing "> = same priority" "expr 3 \> 2 = 1" "1\n" "" ""
+
+testing "00 | 1" "expr 00 \| 1" "1\n" "" ""
+testing "-0 | 1" "expr -0 \| 2" "2\n" "" ""
+testing '"" | 1' 'expr "" \| 3' "3\n" "" ""
+testing "/ by zero" "expr 1 / 0 2>/dev/null; echo \$?" "2\n" "" ""
+testing "% by zero" "expr 1 % 0 2>/dev/null; echo \$?" "2\n" "" ""
+
+testing "regex position" "expr ab21xx : '[^0-9]*[0-9]*'" "4\n" "" ""
+testing "regex extraction" "expr ab21xx : '[^0-9]*\([0-9]*\).*'" "21\n" "" ""
+testing "regex no match" "expr ab21xx : x" "0\n" "" ""
+testing "long str" "expr abcdefghijklmnopqrstuvwxyz : '\(.*\)' = a" "0\n" "" ""
+
+# result of ':' regex match can subsequently be used for arithmetic
+testing "string becomes integer" "expr ab21xx : '[^0-9]*\([0-9]*\)' + 3" \
        "24\n" "" ""
+
+testing "integer comparison" "expr -3 \< -2" "1\n" "" ""
+testing "string comparison" "expr -3 \< -2s" "0\n" "" ""
+testing "integer expression comparison" "expr 2 - 5 \< -2" "1\n" "" ""
+# result of arithmetic can subsequently be compared as a string
+testing "string expr comparison" "expr 2 - 5 \< -2s" "0\n" "" ""
+
+testing "parens around literal" "expr \( a \)" "a\n" "" ""
+
+testing "exit code when true" "expr a; echo \$?" "a\n0\n" "" ""
+testing "exit code when false" "expr 0; echo \$?" "0\n1\n" "" ""
+testing "exit code with syntax error" "expr \( 2>/dev/null; echo \$?" \
+       "2\n" "" ""
+testing "exit code when evaluating to 0" "expr -1 + 1; echo \$?" "0\n1\n" "" ""
+
+# BUG: segfaults because '3' is coerced to integer and regexc gets NULL
+testing "regex segfault" "expr 3 : '\(.\)'" "3\n" "" ""
+
+# syntax errors
+testing "no expression" "expr 2>/dev/null; echo \$?" "2\n" "" ""
+testing "empty ()" "expr \( \) 2>/dev/null; echo \$?" "2\n" "" ""
+testing "missing )" "expr \( 1 2>/dev/null; echo \$?" "2\n" "" ""
+testing "missing outer )" "expr \( 1 + \( 2 \* 3 \) 2>/dev/null; echo \$?" \
+       "2\n" "" ""
+testing "bad operator" "expr 1 @ 2 2>/dev/null; echo \$?" "2\n" "" ""
+testing "adjacent literals" "expr 1 2 2>/dev/null; echo \$?" "2\n" "" ""
+testing "non-integer argument" "expr 1 + a 2>/dev/null; echo \$?" "2\n" "" ""
index ed1cc22..2ec557a 100755 (executable)
@@ -4,19 +4,19 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "factor -32" "factor -32" "-32: -1 2 2 2 2 2\n" "" ""
-testing "factor 0" "factor 0" "0: 0\n" "" ""
-testing "factor 1" "factor 1" "1: 1\n" "" ""
-testing "factor 2" "factor 2" "2: 2\n" "" ""
-testing "factor 3" "factor 3" "3: 3\n" "" ""
-testing "factor 4" "factor 4" "4: 2 2\n" "" ""
-testing "factor 10000000017" "factor 10000000017" \
+testing "-32" "factor -32" "-32: -1 2 2 2 2 2\n" "" ""
+testing "0" "factor 0" "0: 0\n" "" ""
+testing "1" "factor 1" "1: 1\n" "" ""
+testing "2" "factor 2" "2: 2\n" "" ""
+testing "3" "factor 3" "3: 3\n" "" ""
+testing "4" "factor 4" "4: 2 2\n" "" ""
+testing "10000000017" "factor 10000000017" \
         "10000000017: 3 3 3 7 7 7 1079797\n" "" ""
-testing "factor 10000000018" "factor 10000000018" \
+testing "10000000018" "factor 10000000018" \
         "10000000018: 2 131 521 73259\n" "" ""
-testing "factor 10000000019" "factor 10000000019" \
+testing "10000000019" "factor 10000000019" \
         "10000000019: 10000000019\n" "" ""
 
-testing "factor 3 6 from stdin" "factor" "3: 3\n6: 2 3\n" "" "3 6"
-testing "factor stdin newline" "factor" "3: 3\n6: 2 3\n" "" "3\n6\n"
+testing "3 6 from stdin" "factor" "3: 3\n6: 2 3\n" "" "3 6"
+testing "stdin newline" "factor" "3: 3\n6: 2 3\n" "" "3\n6\n"
 
diff --git a/tests/file.test b/tests/file.test
new file mode 100644 (file)
index 0000000..4c7f001
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+touch empty
+echo "#!/bin/bash" > bash.script
+echo "#!  /bin/bash" > bash.script2
+echo "#!  /usr/bin/env python" > env.python.script
+echo "Hello, world!" > ascii
+echo "cafebabe000000310000" | xxd -r -p > java.class
+ln -s java.class symlink
+
+testing "empty" "file empty" "empty: empty\n" "" ""
+testing "bash.script" "file bash.script" "bash.script: /bin/bash script\n" "" ""
+testing "bash.script with spaces" "file bash.script2" "bash.script2: /bin/bash script\n" "" ""
+testing "env python script" "file env.python.script" "env.python.script: python script\n" "" ""
+testing "ascii" "file ascii" "ascii: ASCII text\n" "" ""
+testing "java class" "file java.class" "java.class: Java class file, version 49.0\n" "" ""
+testing "symlink" "file symlink" "symlink: symbolic link\n" "" ""
+testing "symlink -h" "file -h symlink" "symlink: symbolic link\n" "" ""
+testing "symlink -L" "file -L symlink" "symlink: Java class file, version 49.0\n" "" ""
+
+rm empty bash.script bash.script2 env.python.script ascii java.class
diff --git a/tests/files/bzcat/overflow.bz2 b/tests/files/bzcat/overflow.bz2
new file mode 100644 (file)
index 0000000..9ac7947
Binary files /dev/null and b/tests/files/bzcat/overflow.bz2 differ
index 710684e..25c2440 100755 (executable)
@@ -18,30 +18,30 @@ chmod a=r perm/all-read-only
 
 # Testing operators
 
-testing "find -type l -a -type d -o -type p" \
+testing "-type l -a -type d -o -type p" \
        "find dir -type l -a -type d -o -type p" "dir/fifo\n" "" ""
-testing "find -type l -type d -o -type p" "find dir -type l -type d -o -type p" \
+testing "-type l -type d -o -type p" "find dir -type l -type d -o -type p" \
        "dir/fifo\n" "" ""
-testing "find -type l -o -type d -a -type p" \
+testing "-type l -o -type d -a -type p" \
        "find dir -type l -o -type d -a -type p" "dir/link\n" "" ""
-testing "find -type l -o -type d -type p" "find dir -type l -o -type d -type p" \
+testing "-type l -o -type d -type p" "find dir -type l -o -type d -type p" \
        "dir/link\n" "" ""
-testing "find -type l ( -type d -o -type l )" \
+testing "-type l ( -type d -o -type l )" \
        "find dir -type l \( -type d -o -type l \)" "dir/link\n" "" ""
-testing "find extra parentheses" \
+testing "extra parentheses" \
        "find dir \( \( -type l \) \( -type d -o \( \( -type l \) \) \) \)" \
        "dir/link\n" "" ""
-testing "find ( -type p -o -type d ) -type p" \
+testing "( -type p -o -type d ) -type p" \
        "find dir \( -type p -o -type d \) -type p" "dir/fifo\n" "" ""
-testing "find -type l -o -type d -type p -o -type f" \
+testing "-type l -o -type d -type p -o -type f" \
        "find dir -type l -o -type d -type p -o -type f | sort" \
        "dir/file\ndir/link\n" "" ""
 
 # Testing short-circuit evaluations
 
-testing "find -type f -a -print" \
+testing "-type f -a -print" \
        "find dir -type f -a -print" "dir/file\n" "" ""
-testing "find -print -o -print" \
+testing "-print -o -print" \
        "find dir -type f -a \( -print -o -print \)" "dir/file\n" "" ""
 
 # these were erroring or segfaulting:
@@ -50,44 +50,48 @@ testing "find -print -o -print" \
 
 # Testing previous failures
 
-testing "find -type f -user -exec" \
+testing "-type f -user -exec" \
   "find dir -type f -user $USER -exec ls {} \\;" "dir/file\n" "" ""
-testing "find -type l -newer -exec" \
+testing "-type l -newer -exec" \
   "find dir -type l -newer dir/file -exec ls {} \\;" "dir/link\n" "" ""
-testing "find -perm (exact success)" \
+testing "-perm (exact success)" \
   "find perm -type f -perm 0444" "perm/all-read-only\n" "" ""
-testing "find -perm (exact failure)" \
+testing "-perm (exact failure)" \
   "find perm -type f -perm 0400" "" "" ""
-testing "find -perm (min success)" \
+testing "-perm (min success)" \
   "find perm -type f -perm -0400" "perm/all-read-only\n" "" ""
-testing "find -perm (min failure)" \
+testing "-perm (min failure)" \
   "find perm -type f -perm -0600" "" "" ""
-testing "find -perm (any success)" \
+testing "-perm (any success)" \
   "find perm -type f -perm -0444" "perm/all-read-only\n" "" ""
-testing "find -perm (any failure)" \
+testing "-perm (any failure)" \
   "find perm -type f -perm -0222" "" "" ""
 
 # Still fails
 
-testing "find unterminated -exec {}" \
+testing "unterminated -exec {}" \
   "find dir -type f -exec ls {} 2>/dev/null || echo bad" "bad\n" "" ""
-testing "find -exec {} +" \
+testing "-exec {} +" \
   "find dir -type f -exec ls {} +" "dir/file\n" "" ""
 
 # `find . -iname` was segfaulting
-testing "find -name file" \
+testing "-name file" \
   "find dir -name file" "dir/file\n" "" ""
-testing "find -name FILE" \
+testing "-name FILE" \
   "find dir -name FILE" "" "" ""
 
-testing "find -iname file" \
+testing "-iname file" \
   "find dir -iname FILE" "dir/file\n" "" ""
-testing "find -iname FILE" \
+testing "-iname FILE" \
   "find dir -iname FILE" "dir/file\n" "" ""
 
 
-testing "find -name (no arguments)" \
-  "find dir -name 2>&1" "find: '-name' needs 1 arg\n" "" ""
-testing "find -iname (no arguments)" \
-  "find dir -iname 2>&1" "find: '-iname' needs 1 arg\n" "" ""
+testing "-name (no arguments)" \
+  "find dir -name 2>&1 | grep -o '[-]name'" "-name\n" "" ""
+testing "-iname (no arguments)" \
+  "find dir -iname 2>&1 | grep -o '[-]iname'" "-iname\n" "" ""
+
+testing "" "find dir \( -iname file -o -iname missing \) -exec echo {} \;" \
+  "dir/file\n" "" ""
+
 rm -rf dir
diff --git a/tests/fstype.test b/tests/fstype.test
new file mode 100755 (executable)
index 0000000..3df26b1
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+BDIR="$FILES/blkid"
+
+bzcat "$BDIR"/squashfs.bz2 > temp.img
+testing "file" "fstype temp.img" 'squashfs\n' "" ""
+rm temp.img
+
+for i in cramfs ext2 ext3 ext4 f2fs ntfs squashfs vfat xfs
+do
+  testing "$i" 'bzcat "$BDIR"/$i.bz2 | fstype -' "$i\n" "" ""
+done
+
+testing "msdos" 'bzcat "$BDIR"/msdos.bz2 | fstype -' 'vfat\n' "" ""
+testing "reiserfs" 'bzcat "$BDIR"/reiser3.bz2 | fstype -' 'reiserfs\n' "" ""
+
+#testing "blkid minix" 'bzcat "$BDIR"/minix.bz2 | blkid -'
+#adfs bfs btrfs cramfs jfs nilfs romfs
+#vfat  // fat32 fat12 fat16
diff --git a/tests/getfattr.test b/tests/getfattr.test
new file mode 100755 (executable)
index 0000000..cb0f947
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+mkdir attrs
+touch attrs/file
+setfattr -n user.empty attrs/file
+setfattr -n user.data -v hello attrs/file
+
+testing "" "getfattr attrs/file" \
+    "# file: attrs/file\nuser.data\nuser.empty\n\n" "" ""
+testing "-d" "getfattr -d attrs/file" \
+    "# file: attrs/file\nuser.data=\"hello\"\nuser.empty\n\n" "" ""
+testing "-n" "getfattr -n user.empty attrs/file" \
+    "# file: attrs/file\nuser.empty\n\n" "" ""
+testing "-d -n" "getfattr -d -n user.data attrs/file" \
+    "# file: attrs/file\nuser.data=\"hello\"\n\n" "" ""
+
+rm -rf attrs
index 4b52b5b..2a4f178 100755 (executable)
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "grep -c" "grep -c 123 input" "3\n" "123\ncount 123\n123\nfasdfasdf" ""
+testing "-c" "grep -c 123 input" "3\n" "123\ncount 123\n123\nfasdfasdf" ""
 
 echo -e "this is test" > foo
 echo -e "this is test2" > foo2
 echo -e "this is foo3" > foo3
-testing "grep -l" "grep -l test foo foo2 foo3" "foo\nfoo2\n" "" ""
+testing "-l" "grep -l test foo foo2 foo3" "foo\nfoo2\n" "" ""
 rm foo foo2 foo3
 
-testing "grep -q" "grep -q test input && echo yes" "yes\n" "this is a test\n" ""
-testing "grep -E" "grep -E '[0-9]' input" "1234123asdfas123123\n1\n" \
+testing "-q" "grep -q test input && echo yes" "yes\n" "this is a test\n" ""
+testing "-E" "grep -E '[0-9]' input" "1234123asdfas123123\n1\n" \
   "1234123asdfas123123\nabc\n1\nabcde" ""
-testing "grep -e" "grep -e '[0-9]' input" "1234123asdfas123123\n1\n" \
+testing "-e" "grep -e '[0-9]' input" "1234123asdfas123123\n1\n" \
   "1234123asdfas123123\nabc\n1\nabcde" ""
-testing "grep -e -e" "grep -e one -e two -e three input" \
+testing "-e -e" "grep -e one -e two -e three input" \
   "two\ntwo\nthree\none\n" "two\ntwo\nthree\nand\none\n" ""
-testing "grep -F" "grep -F is input" "this is test\nthis is test2\n" \
+testing "-F" "grep -F is input" "this is test\nthis is test2\n" \
   "this is test\nthis is test2\ntest case" ""
 
 echo -e "this is test\nthis is test2\ntest case" > foo
 echo -e "hello this is test" > foo2
 echo -e "hi hello" > foo3
-testing "grep -H" "grep -H is foo foo2 foo3" "foo:this is test\nfoo:this is test2\nfoo2:hello this is test\n" "" ""
+testing "-H" "grep -H is foo foo2 foo3" "foo:this is test\nfoo:this is test2\nfoo2:hello this is test\n" "" ""
 rm foo foo2 foo3
 
-testing "grep -b" "grep -b is input" "0:this is test\n13:this is test2\n" \
+testing "-b" "grep -b is input" "0:this is test\n13:this is test2\n" \
   "this is test\nthis is test2\ntest case" ""
-testing "grep -i" "grep -i is input" "thisIs test\nthis is test2\n" \
+testing "-i" "grep -i is input" "thisIs test\nthis is test2\n" \
   "thisIs test\nthis is test2\ntest case" ""
-testing "grep -n" "grep -n is input" "1:this is test\n2:this is test2\n" \
+testing "-n" "grep -n is input" "1:this is test\n2:this is test2\n" \
   "this is test\nthis is test2\ntest case" ""
-testing "grep -o" "grep -o is input" "is\nis\nis\nis\n" \
+testing "-o" "grep -o is input" "is\nis\nis\nis\n" \
   "this is test\nthis is test2\ntest case" ""
-testing "grep -s" "grep -hs hello asdf input 2>&1" "hello\n" "hello\n" ""
-testing "grep -v" "grep -v abc input" "1234123asdfas123123\n1ABa\n" \
+testing "-s" "grep -hs hello asdf input 2>&1" "hello\n" "hello\n" ""
+testing "-v" "grep -v abc input" "1234123asdfas123123\n1ABa\n" \
   "1234123asdfas123123\n1ABabc\nabc\n1ABa\nabcde" ""
-testing "grep -w" "grep -w abc input" "abc\n123 abc\nabc 123\n123 abc 456\n" \
+testing "-w" "grep -w abc input" "abc\n123 abc\nabc 123\n123 abc 456\n" \
   "1234123asdfas123123\n1ABabc\nabc\n1ABa\nabcde\n123 abc\nabc 123\n123 abc 456\n" ""
-testing "grep -x" "grep -x abc input" "abc\n" \
+testing "-x" "grep -x abc input" "abc\n" \
   "aabcc\nabc\n" ""
 
-testing "grep -H (standard input)" "grep -H abc" "(standard input):abc\n" \
+testing "-H (standard input)" "grep -H abc" "(standard input):abc\n" \
   "" "abc\n"
-testing "grep -l (standard input)" "grep -l abc" "(standard input)\n" \
+testing "-l (standard input)" "grep -l abc" "(standard input)\n" \
   "" "abc\n"
-testing "grep -n two inputs" "grep -hn def - input" "2:def\n2:def\n" \
+testing "-n two inputs" "grep -hn def - input" "2:def\n2:def\n" \
   "abc\ndef\n" "abc\ndef\n"
 
-testing "grep pattern with newline" "grep 'abc
+testing "pattern with newline" "grep 'abc
 def' input" "aabcc\nddeff\n" \
   "aaaa\naabcc\n\dddd\nddeff\nffff\n" ""
 
-testing "grep -lH" "grep -lH abc input" "input\n" "abc\n" ""
-testing "grep -cn" "grep -cn abc input" "1\n" "abc\n" ""
-testing "grep -cH" "grep -cH abc input" "input:1\n" "abc\n" ""
-testing "grep -qs" "grep -qs abc none input && echo yes" "yes\n" "abc\n" ""
-testing "grep -hl" "grep -hl abc input" "input\n" "abc\n" ""
-testing "grep -b stdin" "grep -b one" "0:one\n4:one\n8:one\n" "" "one\none\none\n"
-testing "grep -o overlap" "grep -bo aaa" "1:aaa\n" "" "baaaa\n"
+testing "-lH" "grep -lH abc input" "input\n" "abc\n" ""
+testing "-cn" "grep -cn abc input" "1\n" "abc\n" ""
+testing "-cH" "grep -cH abc input" "input:1\n" "abc\n" ""
+testing "-qs" "grep -qs abc none input && echo yes" "yes\n" "abc\n" ""
+testing "-hl" "grep -hl abc input" "input\n" "abc\n" ""
+testing "-b stdin" "grep -b one" "0:one\n4:one\n8:one\n" "" "one\none\none\n"
+testing "-o overlap" "grep -bo aaa" "1:aaa\n" "" "baaaa\n"
 # nonobvious: -co counts lines, not matches
-testing "grep -co" "grep -co one input" "1\n" "one one one\n" ""
-testing "grep -nom" "grep -nom 2 one" "1:one\n1:one\n1:one\n2:one\n2:one\n" \
+testing "-co" "grep -co one input" "1\n" "one one one\n" ""
+testing "-nom" "grep -nom 2 one" "1:one\n1:one\n1:one\n2:one\n2:one\n" \
   "" "one one one\none one\none"
-testing "grep -vo" "grep -vo one input" "two\nthree\n" "onetwoonethreeone\n" ""
-testing "grep no newline" "grep -h one input -" \
+testing "-vo" "grep -vo one input" "two\nthree\n" "onetwoonethreeone\n" ""
+testing "no newline" "grep -h one input -" \
   "hello one\nthere one\n" "hello one" "there one"
 
-testing "grep -e multi" "grep -e one -ethree input" \
+testing "-e multi" "grep -e one -ethree input" \
   "three\none\n" "three\ntwo\none\n" ""
 # Suppress filenames for recursive test because dunno order they'd occur in
 mkdir sub
 echo -e "one\ntwo\nthree" > sub/one
 echo -e "three\ntwo\none" > sub/two
-testing "grep -hr" "grep -hr one sub" "one\none\n" "" ""
-testing "grep -r file" "grep -r three sub/two" "three\n" "" ""
-testing "grep -r dir" "grep -r one sub | sort" "sub/one:one\nsub/two:one\n" \
+testing "-hr" "grep -hr one sub" "one\none\n" "" ""
+testing "-r file" "grep -r three sub/two" "three\n" "" ""
+testing "-r dir" "grep -r one sub | sort" "sub/one:one\nsub/two:one\n" \
   "" ""
 rm -rf sub
 
 # -x exact match trumps -F's "empty string matches whole line" behavior
-testing "grep -Fx ''" "grep -Fx '' input" "" "one one one\n" ""
-testing "grep -F ''" "grep -F '' input" "one one one\n" "one one one\n" ""
-testing "grep -F -e blah -e ''" "grep -F -e blah -e '' input" "one one one\n" \
+testing "-Fx ''" "grep -Fx '' input" "" "one one one\n" ""
+testing "-F ''" "grep -F '' input" "one one one\n" "one one one\n" ""
+testing "-F -e blah -e ''" "grep -F -e blah -e '' input" "one one one\n" \
   "one one one\n" ""
-testing "grep -e blah -e ''" "grep -e blah -e '' input" "one one one\n" \
+testing "-e blah -e ''" "grep -e blah -e '' input" "one one one\n" \
   "one one one\n" ""
-testing "grep -w ''" "grep -w '' input" "" "one one one\n" ""
-testing "grep -w '' 2" "grep -w '' input" "one  two\n" "one  two\n" ""
-testing "grep -w \\1" "grep -wo '\\(x\\)\\1'" "xx\n" "" "xx"
-testing "grep -o ''" "grep -o '' input" "" "one one one\n" ""
-testing "grep backref" 'grep -e "a\(b\)" -e "b\(c\)\1"' "bcc\nab\n" \
+testing "-w ''" "grep -w '' input" "" "one one one\n" ""
+testing "-w '' 2" "grep -w '' input" "one  two\n" "one  two\n" ""
+testing "-w \\1" "grep -wo '\\(x\\)\\1'" "xx\n" "" "xx"
+testing "-o ''" "grep -o '' input" "" "one one one\n" ""
+testing "backref" 'grep -e "a\(b\)" -e "b\(c\)\1"' "bcc\nab\n" \
   "" "bcc\nbcb\nab\n"
 
-testing "grep -A" "grep -A 2 yes" "yes\nno\nno\n--\nyes\nno\nno\nyes\nno\n" \
+testing "-A" "grep -A 2 yes" "yes\nno\nno\n--\nyes\nno\nno\nyes\nno\n" \
   "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno"
-testing "grep -B" "grep -B 1 yes" "no\nyes\n--\nno\nyes\nno\nyes\n" \
+testing "-B" "grep -B 1 yes" "no\nyes\n--\nno\nyes\nno\nyes\n" \
   "" "no\nno\nno\nyes\nno\nno\nyes\nno\nyes"
-testing "grep -C" "grep -C 1 yes" \
+testing "-C" "grep -C 1 yes" \
   "yes\nno\n--\nno\nyes\nno\nno\nyes\nno\nyes\nno\n" \
   "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno\nyes\nno\nno"
-testing "grep -HnC" "grep -HnC1 two" \
+testing "-HnC" "grep -HnC1 two" \
   "(standard input)-1-one\n(standard input):2:two\n(standard input)-3-three\n" \
   "" "one\ntwo\nthree"
+
+# Context lines weren't showing -b
+testing "-HnbB1" "grep -HnbB1 f input" \
+  "input-3-8-three\ninput:4:14:four\ninput:5:19:five\n" \
+  "one\ntwo\nthree\nfour\nfive\n" ""
+
+testing "-q match overrides error" \
+  "grep -q hello missing input 2>/dev/null && echo yes" "yes\n" "hello\n" ""
+testing "-q not found is 1" \
+  'grep -q hello input || echo $?' "1\n" "" ""
+testing "-q missing is 2" \
+  'grep -q hello missing missing 2>/dev/null || echo $?' "2\n" "" ""
+testing "-q missing survives exists but not found" \
+  'grep -q hello missing missing input 2>/dev/null || echo $?' "2\n" "" ""
+testing "not found retained past match" \
+  'grep hello missing input 2>/dev/null || echo $?' \
+  "input:hello\n2\n" "hello\n" ""
+touch empty
+testing "one match good enough for 0" \
+  'grep hello input empty && echo $?' 'input:hello\n0\n' 'hello\n' ''
+rm empty
+
+testing "-o ''" "grep -o ''" "" "" "one two three\none two\none\n"
+testing '' "grep -o -e '' -e two" "two\ntwo\n" "" \
+  "one two three\none two\none\n"
+
+echo "one\ntwo\nthree" > test
+testing "-l trumps -C" "grep -l -C1 two test input" "test\ninput\n" \
+  "three\ntwo\none\n" ""
+rm test
+
+# match after NUL byte
+testing "match after NUL byte" "grep -a two" "one\0and two three\n" \
+  "" 'one\0and two three'
index 0395e01..091dccf 100755 (executable)
@@ -15,33 +15,33 @@ arg="&>/dev/null"
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "groupadd group_name (text)" "groupadd toyTestGroup &&
+testing "group_name (text)" "groupadd toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (alphanumeric)" "groupadd toy1Test2Group3 &&
+testing "group_name (alphanumeric)" "groupadd toy1Test2Group3 &&
    grep '^toy1Test2Group3:' /etc/group $arg && groupdel toy1Test2Group3 $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (numeric)" "groupadd 987654321 &&
+testing "group_name (numeric)" "groupadd 987654321 &&
    grep '^987654321:' /etc/group $arg && groupdel 987654321 $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (with ./-)" "groupadd toy.1Test-2Group.3 &&
+testing "group_name (with ./-)" "groupadd toy.1Test-2Group.3 &&
    grep '^toy.1Test-2Group.3:' /etc/group $arg &&
    groupdel toy.1Test-2Group.3 $arg && echo 'yes'" "yes\n" "" ""
 
 _s210=`echo $_s70$_s70$_s70`
-testing "groupadd group_name (long string)" "groupadd $_s210 &&
+testing "group_name (long string)" "groupadd $_s210 &&
    grep '^$_s210:' /etc/group $arg && groupdel $_s210 $arg && echo 'yes'" \
   "yes\n" "" ""
-testing "groupadd group_name with group_id" "groupadd -g 49999 toyTestGroup &&
+testing "group_name with group_id" "groupadd -g 49999 toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name with group_id (system_group)" \
+testing "group_name with group_id (system_group)" \
   "groupadd -g 49999 -S toyTestGroup && grep '^toyTestGroup:' /etc/group $arg &&
    groupdel toyTestGroup $arg && echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (system_group)" "groupadd -S toyTestGroup &&
+testing "group_name (system_group)" "groupadd -S toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (add/del user)" "groupadd toyTestGroup &&
+testing "group_name (add/del user)" "groupadd toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupadd $USER toyTestGroup &&
    grep '^toyTestGroup:.*:.*:.*$USER.*' /etc/group $arg &&
    groupdel $USER toyTestGroup $arg || groupdel toyTestGroup &&
@@ -50,7 +50,7 @@ testing "groupadd group_name (add/del user)" "groupadd toyTestGroup &&
  echo "Testing to add single group multiple times after removing it..."
  for each in {01..20}
  do
-   testing "groupadd group_name ($each)" "groupadd toyTestGroup &&
+   testing "group_name ($each)" "groupadd toyTestGroup &&
       grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
       echo 'yes'" "yes\n" "" ""
  done
index d46db53..ea9ee9a 100755 (executable)
@@ -10,18 +10,18 @@ arg="&>/dev/null"
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "groupadd group_name (text)" "groupadd toyTestGroup &&
+testing "group_name (text)" "groupadd toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (alphanumeric)" "groupadd toy1Test2Group3 &&
+testing "group_name (alphanumeric)" "groupadd toy1Test2Group3 &&
    grep '^toy1Test2Group3:' /etc/group $arg && groupdel toy1Test2Group3 $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (numeric)" "groupadd 987654321 &&
+testing "group_name (numeric)" "groupadd 987654321 &&
    grep '^987654321:' /etc/group $arg && groupdel 987654321 $arg &&
    echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name (with ./-)" "groupadd toy.1Test-2Group.3 &&
+testing "group_name (with ./-)" "groupadd toy.1Test-2Group.3 &&
    grep '^toy.1Test-2Group.3:' /etc/group $arg &&
    groupdel toy.1Test-2Group.3 $arg && echo 'yes'" "yes\n" "" ""
-testing "groupadd group_name with group_id" "groupadd -g 49999 toyTestGroup &&
+testing "group_name with group_id" "groupadd -g 49999 toyTestGroup &&
    grep '^toyTestGroup:' /etc/group $arg && groupdel toyTestGroup $arg &&
    echo 'yes'" "yes\n" "" ""
index d4eecd9..83d9399 100755 (executable)
@@ -7,7 +7,7 @@
 testing "head, stdin" "head -n 1 && echo yes" "one\nyes\n" "" "one\ntwo"
 testing "head, stdin via -" "head -n 1 - && echo yes" "one\nyes\n" "" "one\ntwo"
 testing "head, file" "head input -n 1 && echo yes" "one\nyes\n" "one\ntwo" ""
-testing "head -number" "head -2 input && echo yes" "one\ntwo\nyes\n" \
+testing "-number" "head -2 input && echo yes" "one\ntwo\nyes\n" \
        "one\ntwo\nthree\nfour" ""
 testing "head, default lines" "head" "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" "" "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12"
 
index 1194845..5107ce2 100755 (executable)
@@ -14,5 +14,5 @@ hostnameOut=`$hostnameExe`
 # New hostname
 NewHostname="NewHostName.system"
 
-testing "Hostname - Get" "hostname" "$hostnameOut\n" "" ""
-testing "Hostname - Set, Get and then Reset" "hostname $NewHostname; hostname; hostname $hostnameOut; hostname" "$NewHostname\n$hostnameOut\n" "" ""
+testing "get" "hostname" "$hostnameOut\n" "" ""
+testing "set, Get and then Reset" "hostname $NewHostname; hostname; hostname $hostnameOut; hostname" "$NewHostname\n$hostnameOut\n" "" ""
index c30c74c..34fcde0 100755 (executable)
@@ -35,76 +35,76 @@ ifconfig dummy0 up
 
 # Test Description: Disable the dummy0 interface
 # Results Expected: After calling ifconfig, no lines with dummy0 are displayed
-testing "ifconfig dummy0 down and if config /-only" \
+testing "dummy0 down and if config /-only" \
 "ifconfig dummy0 down && ifconfig dummy0 | grep dummy | wc -l" \
 "0\n" "" ""
 
 # Test Description: Enable the dummy0 interface
 # Results Expected: After calling ifconfig, one line with dummy0 is displayed
-testing "ifconfig dummy0 up" \
+testing "dummy0 up" \
 "ifconfig dummy0 up && ifconfig dummy0 | grep dummy | wc -l" \
 "1\n" "" ""
 
 # Test Description: Set the ip address of the dummy0 interface
 # Results Expected: After calling ifconfig dummy0, one line displays the ip
 #                   address selected
-testing "ifconfig dummy0 10.240.240.240" \
+testing "dummy0 10.240.240.240" \
 "ifconfig dummy0 10.240.240.240 && ifconfig dummy0 | grep 10\.240\.240\.240 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Change the netmask to the interface
 # Results Expected: After calling ifconfig dummy0, one line displays the
 #                   netmask selected
-testing "ifconfig dummy0 netmask 255.255.240.0" \
+testing "dummy0 netmask 255.255.240.0" \
 "ifconfig dummy0 netmask 255.255.240.0 && ifconfig dummy0 | grep 255\.255\.240\.0 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Change the broadcast address to the interface
 # Results Expected: After calling ifconfig dummy0, one line displays the
 #                   broadcast address selected
-testing "ifconfig dummy0 broadcast 10.240.240.255" \
+testing "dummy0 broadcast 10.240.240.255" \
 "ifconfig dummy0 broadcast 10.240.240.255 && ifconfig dummy0 | grep 10\.240\.240\.255 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Revert to the default ip address
 # Results Expected: After calling ifconfig dummy0, there are no lines
 #                   displaying the ip address previously selected
-testing "ifconfig dummy0 default" \
+testing "dummy0 default" \
 "ifconfig dummy0 default && ifconfig dummy0 | grep 10\.240\.240\.240 | wc -l" \
 "0\n" "" ""
 
 # Test Description: Change the Maximum transmission unit (MTU) of the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   selected MTU
-testing "ifconfig dummy0 mtu 1269" \
+testing "dummy0 mtu 1269" \
 "ifconfig dummy0 mtu 1269 && ifconfig dummy0 | grep 1269 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Verify ifconfig add fails with such a small mtu
 # Results Expected: There is one line of error message containing
 #                   "No buffer space available"
-testing "ifconfig dummy0 add ::2 -- too small mtu" \
+testing "dummy0 add ::2 -- too small mtu" \
 "ifconfig dummy0 add ::2 2>&1 | grep No\ buffer\ space\ available | wc -l" \
 "1\n" "" ""
 
 # Test Description: Change the Maximum transmission unit (MTU) of the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   selected MTU
-testing "ifconfig dummy0 mtu 2000" \
+testing "dummy0 mtu 2000" \
 "ifconfig dummy0 mtu 2000 && ifconfig dummy0 | grep 2000 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Verify ifconfig add succeeds with a larger mtu
 # Results Expected: after calling ifconfig dummy0, there is one line with the
 #                   selected ip address
-testing "ifconfig dummy0 add ::2" \
+testing "dummy0 add ::2" \
 "ifconfig dummy0 add ::2/126 && ifconfig dummy0 | grep \:\:2\/126 | wc -l" \
 "1\n" "" ""
 
 # Test Description: Verify ifconfig del removes the selected ip6 address
 # Results Expected: after calling ifconfig dummy0, there are no lines with the
 #                   selected ip address
-testing "ifconfig dummy0 del ::2" \
+testing "dummy0 del ::2" \
 "ifconfig dummy0 del ::2/126 && ifconfig dummy0 | grep \:\:2 | wc -l" \
 "0\n" "" ""
 
@@ -112,21 +112,21 @@ testing "ifconfig dummy0 del ::2" \
 #                   preparation for the next test
 # Results Expected: After calling ifconfig dummy0, there are no lines with the
 #                   NOARP flag
-testing "ifconfig dummy0 arp down" \
+testing "dummy0 arp down" \
 "ifconfig dummy0 arp down && ifconfig dummy0 | grep -i NOARP | wc -l" \
 "0\n" "" ""
 
 # Test Description: Call the pointtopoint option with no argument
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   NOARP and UP flags
-testing "ifconfig dummy0 pointtopoint" \
+testing "dummy0 pointtopoint" \
 "ifconfig dummy0 pointtopoint && ifconfig dummy0 | grep -i NOARP | grep -i UP | wc -l" \
 "1\n" "" ""
 
 # Test Description: Test the pointtopoint option and set the ipaddress
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   word inet and the selected ip address
-testing "ifconfig dummy0 pointtopoint 127.0.0.2" \
+testing "dummy0 pointtopoint 127.0.0.2" \
 "ifconfig dummy0 pointtopoint 127.0.0.2 && ifconfig dummy0 | grep -i inet | grep -i 127\.0\.0\.2 | wc -l" \
 "1\n" "" ""
 
@@ -135,56 +135,56 @@ testing "ifconfig dummy0 pointtopoint 127.0.0.2" \
 # Test Description: Enable allmulti mode on the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   allmulti flag
-testing "ifconfig dummy0 allmulti" \
+testing "dummy0 allmulti" \
 "ifconfig dummy0 allmulti && ifconfig dummy0 | grep -i allmulti | wc -l" "1\n" \
 "" ""
 
 # Test Description: Disable multicast mode the interface
 # Results Expected: After calling ifconfig dummy0, there are no lines with the
 #                   allmulti flag
-testing "ifconfig dummy0 -allmulti" \
+testing "dummy0 -allmulti" \
 "ifconfig dummy0 -allmulti && ifconfig dummy0 | grep -i allmulti | wc -l" "0\n" \
 "" ""
 
 # Test Description: Disable NOARP mode on the interface
 # Results Expected: After calling ifconfig dummy0, there are no lines with the
 #                   NOARP flag
-testing "ifconfig dummy0 arp" \
+testing "dummy0 arp" \
 "ifconfig dummy0 arp && ifconfig dummy0 | grep -i NOARP | wc -l" "0\n" \
 "" ""
 
 # Test Description: Enable NOARP mode on the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   NOARP flag
-testing "ifconfig dummy0 -arp" \
+testing "dummy0 -arp" \
 "ifconfig dummy0 -arp && ifconfig dummy0 | grep -i NOARP | wc -l" "1\n" \
 "" ""
 
 # Test Description: Enable multicast mode on the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   multicast flag
-testing "ifconfig dummy0 multicast" \
+testing "dummy0 multicast" \
 "ifconfig dummy0 multicast && ifconfig dummy0 | grep -i multicast | wc -l" \
 "1\n" "" ""
 
 # Test Description: Disable multicast mode the interface
 # Results Expected: After calling ifconfig dummy0, there are no lines with the
 #                   multicast flag
-testing "ifconfig dummy0 -multicast" \
+testing "dummy0 -multicast" \
 "ifconfig dummy0 -multicast && ifconfig dummy0 | grep -i multicast | wc -l" \
 "0\n" "" ""
 
 # Test Description: Enable promiscuous mode the interface
 # Results Expected: After calling ifconfig dummy0, there is one line with the
 #                   promisc flag
-testing "ifconfig dummy0 promisc" \
+testing "dummy0 promisc" \
 "ifconfig dummy0 promisc && ifconfig dummy0 | grep -i promisc | wc -l" "1\n" \
 "" ""
 
 # Disable promiscuous mode the interface
 # Results Expected: After calling ifconfig dummy0, there are no lines with the
 #                   promisc flag
-testing "ifconfig dummy0 -promisc" \
+testing "dummy0 -promisc" \
 "ifconfig dummy0 -promisc && ifconfig dummy0 | grep -i promisc | wc -l" "0\n" \
 "" ""
 
index 769a7b6..d91b794 100755 (executable)
@@ -8,19 +8,19 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 echo "" >foo
-testing "link fails on non-existent file" "link foo/foo baz || echo GOOD" "GOOD\n" "" ""
+testing "fails on non-existent file" "link foo/foo baz || echo GOOD" "GOOD\n" "" ""
 rm -f foo bar
 
 echo file1 > file
-testing "ln create_hardlink" "link file hlink && [ file -ef hlink ] &&
+testing "create_hardlink" "link file hlink && [ file -ef hlink ] &&
           echo 'yes'; rm  -rf hlink" "yes\n" "" ""
 
 echo hlink1 > hlink
 set +e
-testing "ln preserves_hardlinks" "link file hlink 2>/dev/null || echo 'yes'; rm -rf hlink" \
+testing "preserves_hardlinks" "link file hlink 2>/dev/null || echo 'yes'; rm -rf hlink" \
           "yes\n" "" ""
 
 echo file1 > file
-testing "ln create_hardlink_and_remove_sourcefile" "link file hlink &&
+testing "create_hardlink_and_remove_sourcefile" "link file hlink &&
           [ file -ef hlink ] && rm -rf file && [ -f hlink ] && echo 'yes'; rm -f file hlink" \
           "yes\n" "" ""
index aea3ab0..3e70cd8 100755 (executable)
@@ -9,62 +9,62 @@
 #set -x
 
 echo file1 > file
-testing "ln create_hardlink" "ln file hlink && [ file -ef hlink ] &&
+testing "create_hardlink" "ln file hlink && [ file -ef hlink ] &&
           echo 'yes'" "yes\n" "" ""
-testing "ln create_softlink" "ln -s file slink && [ -L slink ] &&
+testing "create_softlink" "ln -s file slink && [ -L slink ] &&
           readlink slink" "file\n" "" ""
 rm slink hlink
 
 echo hlink1 > hlink
-testing "ln force_create_hardlink" "ln -f file hlink &&
+testing "force_create_hardlink" "ln -f file hlink &&
           [ file -ef hlink ] && cat hlink 2>/dev/null" "file1\n" "" ""
 
 echo slink1 > slink
-testing "ln force_create_softlink" "ln -f -s file slink &&
+testing "force_create_softlink" "ln -f -s file slink &&
           [ -L slink ] && readlink slink" "file\n" "" ""
 rm slink hlink
 
 echo hlink1 > hlink
 set +e
-testing "ln preserves_hardlinks" "ln file hlink 2>/dev/null || echo 'yes'" \
+testing "preserves_hardlinks" "ln file hlink 2>/dev/null || echo 'yes'" \
           "yes\n" "" ""
 
 echo slink1 > slink
 set +e
-testing "ln preserves_softlinks" "ln -s file slink 2>/dev/null || echo 'yes'" \
+testing "preserves_softlinks" "ln -s file slink 2>/dev/null || echo 'yes'" \
           "yes\n" "" ""
 rm slink hlink
 
 mkdir dir
-testing "ln multilevel_symbolic_links" "ln -s dir slink &&
+testing "multilevel_symbolic_links" "ln -s dir slink &&
           ln -s file slink && [ -L slink -a -L slink/file ] &&
           readlink slink && readlink slink/file" "dir\nfile\n" "" ""
 rm slink
 
-testing "ln no_dereference" "ln -s dir slink &&
+testing "no_dereference" "ln -s dir slink &&
           ln -n -s file slink 2>/dev/null || [ -L slink ] && readlink slink" \
           "dir\n" "" ""
 rm -rf file dir slink
 
 touch file1 file2 && mkdir dir
-testing "ln create_multiple_hardlinks" "ln file* dir/ &&
+testing "create_multiple_hardlinks" "ln file* dir/ &&
    [ file1 -ef dir/file1 -a file2 -ef dir/file2 ] && echo 'yes'" "yes\n" "" ""
 rm -rf file* dir
 
 touch file1 file2 && mkdir dir
-testing "ln create_multiple_softlinks" "ln -s file* dir/ &&
+testing "create_multiple_softlinks" "ln -s file* dir/ &&
           [ -L dir/file1 -a -L dir/file2 ] && readlink dir/file1 &&
           readlink dir/file2" "file1\nfile2\n" "" ""
 rm -rf file* dir
 
 echo file1 > file
-testing "ln create_softlink_and_remove_sourcefile" "ln -s file slink &&
+testing "create_softlink_and_remove_sourcefile" "ln -s file slink &&
           [ -L slink ] && rm file && cat slink 2>/dev/null || echo 'yes' " \
           "yes\n" "" ""
 rm -f file slink
 
 echo file1 > file
-testing "ln create_hardlink_and_remove_sourcefile" "ln file hlink &&
+testing "create_hardlink_and_remove_sourcefile" "ln file hlink &&
           [ file -ef hlink ] && rm file && [ -f hlink ] && echo 'yes'" \
           "yes\n" "" ""
 rm -f file hlink
index 84ac4b1..651c693 100755 (executable)
@@ -19,27 +19,6 @@ NODE="$(awk '{print $7}')"
 losetup -f 
 losetup -f -s
 losetup -f file
-testing "cat" "cat && echo yes" "oneyes\n" "" "one"
-testing "cat -" "cat - && echo yes" "oneyes\n" "" "one"
-testing "cat file1 file2" "cat file1 file2" "one\ntwo\n"  "" ""
-testing "cat - file"      "cat - file1"     "zero\none\n" "" "zero\n"
-testing "cat file -"      "cat file1 -"     "one\nzero\n" "" "zero\n"
-
-testing "cat file1 notfound file2" \
-        "cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
-        "one\ntwo\ncat: notfound: No such file or directory\n" "" ""
-
-testing "cat file1" \
-        "cat /proc/self/exe > file1 && cmp /proc/self/exe file1 && echo yes" \
-        "yes\n" "" ""
-
-testing "cat - file1" \
-        "cat - file1 | diff -a -U 0 - file1 | tail -n 1" \
-        "-hello\n" "" "hello\n"
-
-testing "cat > /dev/full" \
-        "cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
-        "cat: xwrite: No space left on device\n" "" "zero\n"
 
 losetup -d
 
index 9866eb7..07850de 100755 (executable)
@@ -17,36 +17,36 @@ echo "hidden file1" > lstest/.hfile1
 IN="cd lstest"
 OUT="cd .. "
 
-testing "ls no argument" "$IN && ls; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
-testing "ls with wild char" "$IN && ls file*; $OUT" "file1.txt\nfile2.txt\n" "" ""
-testing "ls with wild char - long listing" "$IN && ls -1 file*; $OUT" "file1.txt\nfile2.txt\n" "" ""
-testing "ls with -p" "$IN && ls -p; $OUT" "dir1/\ndir2/\nfile1.txt\nfile2.txt\n" "" ""
-testing "ls with -a" "$IN && ls -a; $OUT" \
+testing "no argument" "$IN && ls; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
+testing "with wild char" "$IN && ls file*; $OUT" "file1.txt\nfile2.txt\n" "" ""
+testing "with wild char - long listing" "$IN && ls -1 file*; $OUT" "file1.txt\nfile2.txt\n" "" ""
+testing "with -p" "$IN && ls -p; $OUT" "dir1/\ndir2/\nfile1.txt\nfile2.txt\n" "" ""
+testing "with -a" "$IN && ls -a; $OUT" \
         ".\n..\ndir1\ndir2\nfile1.txt\nfile2.txt\n.hfile1\n" "" ""
-testing "ls with -A" "$IN && ls -A; $OUT" \
+testing "with -A" "$IN && ls -A; $OUT" \
         "dir1\ndir2\nfile1.txt\nfile2.txt\n.hfile1\n" "" ""
-testing "ls with -d" "$IN && ls -d; $OUT" ".\n" "" ""
-testing "ls with wild char and -d *" "$IN && ls -d *; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
-testing "ls with -k" "$IN && ls -k; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
-testing "ls with -m" "$IN && ls -m; $OUT" "dir1, dir2, file1.txt, file2.txt\n" "" ""
-testing "ls with -F" "$IN && ls -F; $OUT" "dir1/\ndir2/\nfile1.txt\nfile2.txt\n" "" ""
-testing "ls with -dk *" "$IN && ls -dk *; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
+testing "with -d" "$IN && ls -d; $OUT" ".\n" "" ""
+testing "with wild char and -d *" "$IN && ls -d *; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
+testing "with -k" "$IN && ls -k; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
+testing "with -m" "$IN && ls -m; $OUT" "dir1, dir2, file1.txt, file2.txt\n" "" ""
+testing "with -F" "$IN && ls -F; $OUT" "dir1/\ndir2/\nfile1.txt\nfile2.txt\n" "" ""
+testing "with -dk *" "$IN && ls -dk *; $OUT" "dir1\ndir2\nfile1.txt\nfile2.txt\n" "" ""
 
 ln -s file1.txt lstest/slink
-testing "ls softlink - long listing" "$IN && ls -l slink | awk '{ print \$NF }' ; $OUT" \
+testing "softlink - long listing" "$IN && ls -l slink | awk '{ print \$NF }' ; $OUT" \
           "file1.txt\n" "" ""
 rm -f lstest/slink
 
 ln -s /dev/null/nosuchfile lstest/nosuchfile
-testing "ls with -d - broken softlink" "$IN && ls -d nosuchfile; $OUT" "nosuchfile\n" "" ""
+testing "with -d - broken softlink" "$IN && ls -d nosuchfile; $OUT" "nosuchfile\n" "" ""
 rm -f lstest/nosuchfile
 
 rm -rf lstest/* && mkdir -p lstest/dir1 && touch lstest/file1.txt
-testing "ls nested recursively" "$IN && ls -R; $OUT" \
+testing "nested recursively" "$IN && ls -R; $OUT" \
           ".:\ndir1\nfile1.txt\n\n./dir1:\n" "" ""
 
 rm -rf lstest/* && touch lstest/file1.txt && INODE=`stat -c %i lstest/file1.txt`
-testing "ls with -i" "$IN && ls -i 2>/dev/null; $OUT" "$INODE file1.txt\n" "" ""
+testing "with -i" "$IN && ls -i 2>/dev/null; $OUT" "$INODE file1.txt\n" "" ""
 unset INODE
 
 # Removing test dir for cleanup purpose
index 870b3f6..c2c7c61 100755 (executable)
@@ -4,127 +4,6 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-# chattr - Testcases
-
-mkdir testattr
-IN="cd testattr"
-OUT="cd .."
-_t="abcdefghijklmnopqrstuvwxyz"
-
-testing "chattr [-/+]i FILE[write]" "$IN && echo "$_t" > testFile &&
-         chattr +i testFile && lsattr testFile && echo "$_t" > testFile; 
-         chattr -i testFile; rm -rf testFile; $OUT " "----i-------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[re-write]" "$IN && echo "$_t" > testFile && 
-         chattr +i testFile && echo \"$_t\" > testFile || chattr -i testFile && 
-         echo \"$_t\" > testFile && lsattr testFile; rm -rf testFile; $OUT" \
-         "------------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[append]" "$IN && echo "$_t" > testFile && 
-         chattr +i testFile && echo \"$_t\" >> testFile || lsattr testFile && 
-         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[move]" "$IN && echo "$_t" > testFile && 
-         chattr +i testFile && mv testFile testFile1 || lsattr testFile && 
-         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[delete]" "$IN && echo "$_t" > testFile && 
-         chattr +i testFile && rm -f testFile || lsattr testFile && 
-         chattr -i testFile; rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[read]" "$IN && echo "$_t" > testFile && 
-         chattr +i testFile && cat testFile && lsattr testFile && 
-         chattr -i testFile; rm -rf testFile; $OUT" "$_t\n----i-------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[write]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && echo $_t > testFile || lsattr testFile && 
-         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[re-write]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && echo $_t > testFile || lsattr testFile && 
-         chattr -a testFile && echo $_t > testFile && cat testFile && 
-         lsattr testFile; rm -rf testFile; 
-         $OUT" "-----a------- testFile\n$_t\n------------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[append]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && echo $_t >> testFile && cat testFile && 
-         lsattr testFile && chattr -a testFile; rm -rf testFile; $OUT" \
-         "$_t\n$_t\n-----a------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[move]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && mv testFile testFile1 || lsattr testFile && 
-         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[delete]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && rm -f testFile || lsattr testFile && 
-         chattr -a testFile; rm -rf testFile; $OUT" "-----a------- testFile\n" "" ""
-testing "chattr [-/+]a FILE[read]" "$IN && echo "$_t" > testFile && 
-         chattr +a testFile && cat testFile && lsattr testFile && chattr -a testFile; 
-         rm -rf testFile; $OUT" "$_t\n-----a------- testFile\n" "" ""
-
-for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
-do
-  testing "chattr [-/+]$attr FILE" "$IN && echo "$_t" > testFile && 
-           chattr +$attr testFile && cat testFile && chattr -$attr testFile && 
-           lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
-done
-
-for attr in "A" "a" "c" "D" "d" "i" "j" "s" "S" "t" "T" "u"
-do
-  testing "chattr -$attr FILE" "$IN && echo "$_t" > testFile && chattr -$attr testFile &&
-           cat testFile && lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
-done
-
-testing "chattr [-/+]AacDdijsStTu FILE" "$IN && echo "$_t" > testFile && 
-         chattr +AacDdijsStTu testFile && cat testFile && chattr -AacDdijsStTu testFile && 
-         lsattr testFile; rm -rf testFile; $OUT" "$_t\n------------- testFile\n" "" ""
-testing "chattr [-/+]AacDdijsStTu(random) FILE" \
-        "$IN && echo "$_t" > testFile && 
-        chattr +AacDdijsStTu testFile && cat testFile && chattr -A testFile &&
-        chattr -a testFile && chattr -c testFile && chattr -D testFile &&
-        chattr -d testFile && chattr -i testFile && chattr -j testFile &&
-        chattr -s testFile && chattr -S testFile && chattr -t testFile &&
-        chattr -T testFile && chattr -u testFile && lsattr testFile &&
-        chattr -AacDdijsStTu testFile; rm -rf testFile; $OUT" \
-        "$_t\n------------- testFile\n" "" ""
-testing "chattr [-/+]AacDdijsStTu FILE*" "$IN && 
-        echo "$_t" > testFile && echo "$_t" > testFile1 &&
-        echo "$_t" > testFile2 && echo "$_t" > testFile3 &&
-        echo "$_t" > testFile4 && echo "$_t" > testFile5 &&
-        echo "$_t" > testFile6 && echo "$_t" > testFile7 &&
-        echo "$_t" > testFile8 && echo "$_t" > testFile9 &&
-        echo "$_t" > testFile10 && echo "$_t" > testFile11 &&
-       chattr +AacDdijsStTu testFile* &&
-       cat testFile9 && chattr -AacDdijsStTu testFile* && lsattr testFile*; rm -rf testFile*; $OUT" \
-       "$_t\n------------- testFile\n------------- testFile1\n------------- testFile10\n------------- testFile11\n------------- testFile2\n------------- testFile3\n------------- testFile4\n------------- testFile5\n------------- testFile6\n------------- testFile7\n------------- testFile8\n------------- testFile9\n" "" ""
-testing "chattr [-/+]AacDdijsStTu(random) FILE*" \
-        "$IN && echo "$_t" > testFile &&
-        chattr +AacDdijsStTu testFile* && cat testFile && chattr -A testFile* &&
-       chattr -a testFile* && chattr -c testFile* && chattr -D testFile* &&
-       chattr -d testFile* && chattr -i testFile* && chattr -j testFile* &&
-       chattr -s testFile* && chattr -S testFile* && chattr -t testFile* &&
-       chattr -T testFile* && chattr -u testFile* && lsattr testFile;
-        rm -rf testFile; $OUT" \
-       "$_t\n------------- testFile\n" "" ""
-testing "chattr [-/+]i FILE[write]" \
-       "$IN && echo "$_t" > testFile &&
-       chattr +i testFile &&
-       echo \"$_t\" > testFile || lsattr testFile && chattr -i testFile;
-       rm -rf testFile; $OUT" "----i-------- testFile\n" "" ""
-testing "chattr [-/+]A FILE[write]" \
-       "$IN && echo "$_t" > testFile && chattr +A testFile &&
-       echo \"$_t\" > testFile && lsattr testFile && chattr -A testFile;
-       rm -rf testFile; $OUT" "-------A----- testFile\n" "" ""
-testing "chattr [-/+]s FILE[write]" \
-       "$IN && echo "$_t" > testFile && chattr +s testFile &&
-       echo \"$_t\" > testFile && lsattr testFile && chattr -s testFile
-       rm -rf testFile; $OUT" "s------------ testFile\n" "" ""
-testing "chattr -v version FILE[write]" \
-       "$IN && echo "$_t" > testFile &&
-       chattr -v 1234 testFile && echo \"$_t\" > testFile && 
-       lsattr -v testFile; rm -rf testFile" \
-       " 1234 ------------- testFile\n" "" ""
-
-_a="-------A-----"
-testing "chattr -R [-/+]a FILE" "$IN && touch aa && chattr -R +A aa && lsattr aa &&
-       chattr -R -A aa; rm -rf aa; $OUT" "$_a aa\n" "" ""
-testing "chattr -R [-/+]a FILE.." "$IN && touch aa bb &&
-       chattr -R +A aa bb && lsattr aa bb &&
-       chattr -R -A aa bb; rm -rf aa bb; $OUT" "$_a aa\n$_a bb\n" "" ""
-
-# Clean up
-rm -rf testattr
-
 # lsattr - Testcases
 mkdir dir && cd dir && touch file
 chattr +A file &>/dev/null
@@ -133,24 +12,24 @@ _p=$PWD
 _b="-------------"
 _A="-------A-----"
 
-testing "lsattr file" "lsattr file" "$_A file\n" "" ""
-testing "lsattr file_path" "lsattr $_p/file" "$_A $_p/file\n" "" ""
-testing "lsattr -R file" "lsattr -R file" "$_A file\n" "" ""
-testing "lsattr -R file_path" "lsattr -R $_p/file" "$_A $_p/file\n" "" ""
-testing "lsattr -a file" "lsattr -a file" "$_A file\n" "" ""
-testing "lsattr -a file_path" "lsattr -a $_p/file" "$_A $_p/file\n" "" ""
-testing "lsattr -d ." "lsattr -d ." "$_b .\n" "" ""
-testing "lsattr -d dir_path" "lsattr -d $_p" "$_b $_p\n" "" ""
-testing "lsattr -d file" "lsattr -d file" "$_A file\n" "" ""
-testing "lsattr -d file_path" "lsattr -d $_p/file" "$_A $_p/file\n" "" ""
+testing "file" "lsattr file" "$_A file\n" "" ""
+testing "file_path" "lsattr $_p/file" "$_A $_p/file\n" "" ""
+testing "-R file" "lsattr -R file" "$_A file\n" "" ""
+testing "-R file_path" "lsattr -R $_p/file" "$_A $_p/file\n" "" ""
+testing "-a file" "lsattr -a file" "$_A file\n" "" ""
+testing "-a file_path" "lsattr -a $_p/file" "$_A $_p/file\n" "" ""
+testing "-d ." "lsattr -d ." "$_b .\n" "" ""
+testing "-d dir_path" "lsattr -d $_p" "$_b $_p\n" "" ""
+testing "-d file" "lsattr -d file" "$_A file\n" "" ""
+testing "-d file_path" "lsattr -d $_p/file" "$_A $_p/file\n" "" ""
 sp_44="                                            "
-testing "lsattr -l file" "lsattr -l file" "file  $sp_44 No_Atime\n" "" ""
+testing "-l file" "lsattr -l file" "file  $sp_44 No_Atime\n" "" ""
 _v="12345"
-testing "lsattr -v file" "chattr -v $_v * && lsattr -v file" \
+testing "-v file" "chattr -v $_v * && lsattr -v file" \
   "$_v $_A file\n" "" ""
-testing "lsattr -v file_path" "chattr -v $_v * && lsattr -v $_p/file" \
+testing "-v file_path" "chattr -v $_v * && lsattr -v $_p/file" \
   "$_v $_A $_p/file\n" "" ""
-testing "lsattr -Radlv file1 file2" "chattr -v $_v * &&
+testing "-Radlv file1 file2" "chattr -v $_v * &&
    lsattr -Radlv file input" \
   "$_v file  $sp_44 No_Atime\n$_v input $sp_44 ---\n" "" ""
 
index 741ec62..f159d1e 100755 (executable)
@@ -7,16 +7,24 @@
 # These tests are from RFC 1321 appendix 5, reshuffled slightly to test
 # varying argument numbers
 
-testing "md5sum ''" "md5sum" "d41d8cd98f00b204e9800998ecf8427e  -\n" "" ""
-testing "md5sum infile" "md5sum input" \
+testing "''" "md5sum" "d41d8cd98f00b204e9800998ecf8427e  -\n" "" ""
+testing "infile" "md5sum input" \
   "0cc175b9c0f1b6a831c399e269772661  input\n" "a" ""
-testing "md5sum two files" "md5sum - input" \
+testing "two files" "md5sum - input" \
   "900150983cd24fb0d6963f7d28e17f72  -\nf96b697d7cb7938d525a2f31aaf161d0  input\n" \
   "message digest" "abc"
-testing "md5sum 4" "md5sum" "c3fcd3d76192e4007dfb496cca67e13b  -\n" \
+testing "4" "md5sum" "c3fcd3d76192e4007dfb496cca67e13b  -\n" \
   "" "abcdefghijklmnopqrstuvwxyz"
-testing "md5sum 5" "md5sum" "d174ab98d277d9f5a5611c2c9f419d9f  -\n" \
+testing "5" "md5sum" "d174ab98d277d9f5a5611c2c9f419d9f  -\n" \
   "" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
-testing "md5sum 6" "md5sum" "57edf4a22be3c955ac49da2e2107b67a  -\n" \
+testing "6" "md5sum" "57edf4a22be3c955ac49da2e2107b67a  -\n" \
   "" "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
 
+echo -n "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" > "te st"
+touch empty
+testing "-c spaces" "md5sum -c input || echo ok" \
+  "te st: OK\nempty: FAILED\n-: OK\nok\n" \
+"$(printf "d174ab98d277d9f5a5611c2c9f419d9f te st\n12345678901234567890123456789012 empty\nd41d8cd98f00b204e9800998ecf8427e -\n")" ""
+rm "te st" empty
+
+testing "-c nolines" "md5sum -c input 2>/dev/null || echo ok" "ok\n" "" ""
index 8b2f99f..e1bdb88 100755 (executable)
@@ -8,66 +8,66 @@ testing "mkdir" "mkdir one && [ -d one ] && echo yes" "yes\n" "" ""
 rmdir one
 
 touch existing
-testing "mkdir existing" \
+testing "existing" \
        "mkdir existing 2> /dev/null || [ -f existing ] && echo yes" "yes\n" "" ""
 rm existing
 
-testing "mkdir one two" \
+testing "one two" \
        "mkdir one two && [ -d one ] && [ -d two ] && echo yes" "yes\n" "" ""
 rmdir one two
 
-testing "mkdir missing/one" \
+testing "missing/one" \
        "mkdir missing/one 2> /dev/null || [ ! -d missing ] && echo yes" "yes\n" "" ""
 
-testing "mkdir -p" \
+testing "-p" \
        "mkdir -p one/two/three && [ -d one/two/three ] && echo yes" "yes\n" "" ""
 rm -rf one
 
 mkdir existing
-testing "mkdir -p existing" "mkdir -p existing && echo yes" "yes\n" "" ""
+testing "-p existing" "mkdir -p existing && echo yes" "yes\n" "" ""
 rmdir existing
 
 umask 123
-testing "mkdir (default permissions)" \
+testing "(default permissions)" \
        "mkdir one && stat -c %a one" "654\n" "" ""
 rmdir one
 
-testing "mkdir -m 124" \
+testing "-m 124" \
        "mkdir -m 124 one && stat -c %a one" "124\n" "" ""
 rmdir one
 
 umask 000
-testing "mkdir -p -m 653" \
+testing "-p -m 653" \
        "mkdir -p -m 653 one/two && stat -c %a one && stat -c %a one/two" \
        "777\n653\n" "" ""
 rm -rf one
 
-testing "mkdir -p one/two/ (trailing slash)" \
+testing "-p one/two/ (trailing slash)" \
        "mkdir -p one/two/ &&  [ -d one/two ] && echo yes" "yes\n" "" ""
 rm -rf one
 
 umask 022
-testing "mkdir -p -m 777 (022 umask)" \
+testing "-p -m 777 (022 umask)" \
        "mkdir -p -m 777 one/two && stat -c %a one && stat -c %a one/two" \
        "755\n777\n" "" ""
 rm -rf one
 
 umask 377
-testing "mkdir -p -m 777 (377 umask)" \
+testing "-p -m 777 (377 umask)" \
        "mkdir -p -m 777 one/two && stat -c %a one && stat -c %a one/two" \
        "700\n777\n" "" ""
 umask 002
 rm -rf one
 
-testing "mkdir -vp" "mkdir -vp walrus 2>&1" \
+testing "-vp" "mkdir -vp walrus 2>&1" \
        "mkdir: created directory 'walrus'\n" "" ""
 
-testing "mkdir -vp exists" "mkdir -vp walrus 2>&1" \
+testing "-vp exists" "mkdir -vp walrus 2>&1" \
        "" "" ""
 rm -rf walrus
 
 touch two
-testing "mkdir continue after fail" \
+testing "continue after fail" \
        "mkdir -m 777 one two three 2>/dev/null || stat -c %a three" \
        "777\n" "" ""
 rm -rf one two three
index b0253fe..faaad1f 100755 (executable)
@@ -8,21 +8,21 @@ testing "mkfifo" "mkfifo one && [ -p one ] && echo yes" "yes\n" "" ""
 rm one
 
 touch existing
-testing "mkfifo existing" \
+testing "existing" \
        "mkfifo existing 2> /dev/null || [ -f existing ] && echo yes" "yes\n" "" ""
 rm existing
 
-testing "mkfifo one two" \
+testing "one two" \
        "mkfifo one two && [ -p one ] && [ -p two ] && echo yes" "yes\n" "" ""
 rm one two
 
 umask 123
-testing "mkfifo (default permissions)" \
+testing "(default permissions)" \
        "mkfifo one && stat -c %a one" "644\n" "" ""
 rm one
 
 umask 000
 
-testing "mkfifo -m 124" \
+testing "-m 124" \
        "mkfifo -m 124 one && stat -c %a one" "124\n" "" ""
 rm -f one
index ac94ce0..0a8c2be 100755 (executable)
 
 #We expect they have ne2k-pci as a module.
 
-testing "modinfo gets right number of fields" "modinfo ne2k-pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
-testing "modinfo treats - and _ as equivalent" "modinfo ne2k_pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
+testing "gets right number of fields" "modinfo ne2k-pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
+testing "treats - and _ as equivalent" "modinfo ne2k_pci |cut -d: -f1 |grep -v ver|sort" "alias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nalias\nauthor\ndepends\ndescription\nfilename\nlicense\nparm\nparm\nparm\n" "" ""
 
 # Output of -F filename should be an absolute path to the module.
 # Otherwise, initrd generating scripts will break.
 
-testing "modinfo -F filename gets absolute path" "[ -e `modinfo -F filename ne2k-pci` ] && echo ne2k-pci " "ne2k-pci\n" "" ""
+testing "-F filename gets absolute path" "[ -e `modinfo -F filename ne2k-pci` ] && echo ne2k-pci " "ne2k-pci\n" "" ""
 
-testing "modinfo supports multiple modules" "modinfo -F filename ne2k-pci 8390 | wc -l" "2\n" "" ""
+testing "supports multiple modules" "modinfo -F filename ne2k-pci 8390 | wc -l" "2\n" "" ""
 
-testing "modinfo does not output filename for bad module" "modinfo -F filename zxcvbnm__9753" "" "" ""
+testing "does not output filename for bad module" "modinfo -F filename zxcvbnm__9753" "" "" ""
 
 
 
diff --git a/tests/more.test b/tests/more.test
new file mode 100755 (executable)
index 0000000..4dcf6d8
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+cat >file1 <<EOF
+line1
+line2
+EOF
+
+testing "non-tty" "more file1 | cat -" "line1\nline2\n" "" ""
+
+rm file1
index d64bfc6..6590b94 100755 (executable)
@@ -20,69 +20,69 @@ reCreateTmpFs() {
 }
 reCreateTmpFs
 
-testing "mount $root_fs /mnt" \
+testing "$root_fs /mnt" \
   "mount $root_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && test -e /testDir && rmdir /testDir" "" "" ""
-testing "mount $tmp_b_fs /mnt" \
+testing "$tmp_b_fs /mnt" \
   "mount $tmp_b_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && ! test -e /mnt/testDir" "" "" ""
 reCreateTmpFs
 
 chmod 444 /mnt
-testing "mount $root_fs /mnt (read_only dir)" \
+testing "$root_fs /mnt (read_only dir)" \
   "mount $root_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && test -e /testDir && rmdir /testDir" "" "" ""
-testing "mount $tmp_b_fs /mnt (read_only dir)" \
+testing "$tmp_b_fs /mnt (read_only dir)" \
   "mount $tmp_b_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && ! test -e /mnt/testDir" "" "" ""
 reCreateTmpFs
 chmod 755 /mnt
-testing "mount -w $root_fs /mnt (write_only mode)" \
+testing "-w $root_fs /mnt (write_only mode)" \
   "mount -w $root_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && test -e /testDir && rmdir /testDir" "" "" ""
-testing "mount -w $tmp_b_fs /mnt (write_only mode)" \
+testing "-w $tmp_b_fs /mnt (write_only mode)" \
   "mount -w $tmp_b_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && ! test -e /mnt/testDir" "" "" ""
 reCreateTmpFs
-testing "mount -rw $tmp_b_fs /mnt (read_write mode)" \
+testing "-rw $tmp_b_fs /mnt (read_write mode)" \
   'mount -rw $tmp_b_fs /mnt >/dev/null && mkdir /mnt/testDir && \
    sleep 1 && ! test -e /mnt/testDir && umount /mnt' "" "" ""
 reCreateTmpFs
-testing "mount $tmp_b_fs /mnt -t fs_type" \
+testing "$tmp_b_fs /mnt -t fs_type" \
   "mount $tmp_b_fs /mnt -t $tmp_b_fs_type >/dev/null 2>&1 &&
    mkdir /mnt/testDir && sleep 1 && umount /mnt &&
    ! test -e /mnt/testDir" "" "" ""
 reCreateTmpFs
 mkdir -p testDir1/testDir2 testDir
 echo "abcdefghijklmnopqrstuvwxyz" > testDir1/testDir2/testFile
-testing "mount -o bind dir1 dir2" \
+testing "-o bind dir1 dir2" \
   'mount -o bind testDir1 testDir >/dev/null 2>&1 && \
    cat testDir/testDir2/testFile && sleep 1 && umount testDir' \
   "abcdefghijklmnopqrstuvwxyz\n" "" ""
-testing "mount -o rbind dir1 dir2" \
+testing "-o rbind dir1 dir2" \
   'mount -o rbind testDir1 testDir >/dev/null 2>&1 && \
    cat testDir/testDir2/testFile && sleep 1 && umount testDir' \
   "abcdefghijklmnopqrstuvwxyz\n" "" ""
-testing "mount -o loop $tmp_b_fs /mnt" \
+testing "-o loop $tmp_b_fs /mnt" \
   "mount -o loop $tmp_b_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDirp &&
    sleep 1 && umount -d /mnt && ! test -e /mnt/testDirp" "" "" ""
 reCreateTmpFs
 
 mkdir testDir2
-testing "mount -o move mount_1 mount_2" \
+testing "-o move mount_1 mount_2" \
   "mount $tmp_b_fs testDir1 && mkdir testDir1/testDirr &&
    mount -o move testDir1 testDir2 && test -r testDir2/testDirr &&
    sleep 1 && umount testDir2" "" "" ""
 reCreateTmpFs
-testing "mount -o rw $tmp_b_fs /mnt" \
+testing "-o rw $tmp_b_fs /mnt" \
   "mount -o rw $tmp_b_fs /mnt >/dev/null 2>&1 && mkdir /mnt/testDir &&
    sleep 1 && umount /mnt && ! test -e /mnt/testDir" "" "" ""
 reCreateTmpFs
-testing "mount -o ro $tmp_b_fs /mnt" \
+testing "-o ro $tmp_b_fs /mnt" \
   "mount -o ro $tmp_b_fs /mnt >/dev/null 2>&1 &&
    mkdir /mnt/testDir 2>/dev/null || sleep 1 && umount /mnt" "" "" ""
 reCreateTmpFs
-testing "mount -o ro,remount $tmp_b_fs /mnt" \
+testing "-o ro,remount $tmp_b_fs /mnt" \
   "mount -o ro $tmp_b_fs /mnt >/dev/null 2>&1 &&
    mkdir /mnt/testDir 2>/dev/null || sleep 1 && umount /mnt" "" "" ""
 reCreateTmpFs
index 030e9cc..036fd48 100755 (executable)
@@ -1,5 +1,10 @@
 #!/bin/bash
 
+# TODO: needs root to mount tmpfs to test moving across filesystems.
+# check handling of chattr +i immutable bit
+# "touch two; chmod -w two; mv one two" shouldn't prompt to delete two if
+#   one doesn't exist.
+
 # Copyright 2013 Robin Mittal <robinmittal.it@gmail.com>
 # Copyright 2013 Divya Kothari <divya.s.kothari@gmail.com>
 
 #testing "name" "command" "result" "infile" "stdin"
 
 touch file
-testing "Move old_file to  new_file" "mv file file1 && [ ! -e file -a -f file1 ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "file to file" \
+  "mv file file1 && [ ! -e file -a -f file1 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file
 mkdir dir
-testing "Move file to a dir" "mv file dir && [ ! -e file -a -f dir/file ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "file to dir" \
+  "mv file dir && [ ! -e file -a -f dir/file ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* dir*
 
 mkdir dir
-testing "Move old_dir to new_dir" "mv dir dir1 && [ ! -e dir -a -d dir1 ] &&
-   echo 'yes'" "yes\n" "" ""
+testing "dir to dir" \
+  "mv dir dir1 && [ ! -e dir -a -d dir1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf dir*
 
 mkdir dir1 dir2
 touch file1 file2 dir1/file3
 ln -s file1 link1
-testing "Move multiple files/dir to a dir" "mv file1 file2 link1 dir1 dir2 &&
-   [ ! -e file1 -a ! -e file2 -a ! -e link1 -a ! -e dir1 ] &&
-   [ -f dir2/file1 -a -f dir2/file2 -a -L dir2/link1 -a -d dir2/dir1 ] &&
-   [ -f dir2/dir1/file3 ] && readlink dir2/link1" "file1\n" "" ""
+testing "multiple files/dirs to a dir" \
+  "mv file1 file2 link1 dir1 dir2 &&
+  [ ! -e file1 -a ! -e file2 -a ! -e link1 -a ! -e dir1 ] &&
+  [ -f dir2/file1 -a -f dir2/file2 -a -L dir2/link1 -a -d dir2/dir1 ] &&
+  [ -f dir2/dir1/file3 ] && readlink dir2/link1" \
+  "file1\n" "" ""
 rm -rf file* link* dir*
 
-touch file1
-testing "Move a empty file to new_file" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && stat -c %s file2" "0\n" "" ""
-rm -rf file*
-
-mkdir dir1
-testing "Move enpty dir to new_dir" "mv dir1 dir2 &&
-   [ ! -d dir1 -a -d dir2 ] && echo 'yes'" "yes\n" "" ""
-rm -rf dir*
-
 dd if=/dev/zero of=file1 seek=10k count=1 >/dev/null 2>&1
-testing "Move file new_file (random file)" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && stat -c %s file2" "5243392\n" "" ""
+testing "random file to new file" \
+  "mv file1 file2 && [ ! -e file1 -a -f file2 ] && stat -c %s file2" \
+  "5243392\n" "" ""
 rm -f file*
 
 touch file1
 ln -s file1 link1
-testing "Move link new_link (softlink)" "mv link1 link2 &&
-   [ ! -e link1 -a -L link2 ] && readlink link2" "file1\n" "" ""
+testing "symlink to new symlink" \
+  "mv link1 link2 && [ ! -e link1 -a -L link2 ] && readlink link2" \
+  "file1\n" "" ""
 unlink tLink2 &>/dev/null
 rm -f file* link*
 
 touch file1
 ln file1 link1
-testing "Move link new_link (hardlink)" "mv link1 link2 &&
-   [ ! -e link1 -a -f link2 -a file1 -ef link2 ] && echo 'yes'" "yes\n" "" ""
+testing "hard link to new hardlink" \
+  "mv link1 link2 && [ ! -e link1 -a -f link2 -a file1 -ef link2 ] && echo yes" \
+  "yes\n" "" ""
 unlink link2 &>/dev/null
 rm -f file* link*
 
 touch file1
 chmod a-r file1
-testing "Move file new_file (unreadable)" "mv file1 file2 &&
-   [ ! -e file1 -a -f file2 ] && echo 'yes'" "yes\n" "" ""
+testing "file to unreadable file" \
+  "mv file1 file2 && [ ! -e file1 -a -f file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file1
 ln file1 link1
 mkdir dir1
-testing "Move file link dir (hardlink)" "mv file1 link1 dir1 &&
-   [ ! -e file1 -a ! -e link1 -a -f dir1/file1 -a -f dir1/link1 ] &&
-   [ dir1/file1 -ef dir1/link1 ] && echo 'yes'" "yes\n" "" ""
+testing "file hardlink dir" \
+  "mv file1 link1 dir1 &&
+  [ ! -e file1 -a ! -e link1 -a -f dir1/file1 -a -f dir1/link1 ] &&
+  [ dir1/file1 -ef dir1/link1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* link* dir*
 
 mkdir -p dir1/dir2 dir3
 touch dir1/dir2/file1 dir1/dir2/file2
-testing "Move dir1/dir2 dir3/new_dir" "mv dir1/dir2 dir3/dir4 &&
-   [ ! -e dir1/dir2 -a -d dir3/dir4 -a -f dir3/dir4/file1 ] &&
-   [ -f dir3/dir4/file2 ] && echo 'yes'" "yes\n" "" ""
+testing "dir to new dir" \
+  "mv dir1/dir2 dir3/new &&
+  [ ! -e dir1/dir2 -a -d dir3/new -a -f dir3/new/file1 ] &&
+  [ -f dir3/new/file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf file* dir*
 
 mkdir dir1 dir2
-testing "Move dir new_dir (already exist)" "mv dir1 dir2 &&
-   [ ! -e dir1 -a -d dir2/dir1 ] && echo 'yes'" "yes\n" "" ""
+testing "dir to existing dir" \
+  "mv dir1 dir2 && [ ! -e dir1 -a -d dir2/dir1 ] && echo yes" \
+  "yes\n" "" ""
 rm -rf dir*
 
 touch file1 file2
-testing "Move -f file new_file (exist)" "mv -f file1 file2 &&
-   [ ! -e file1 -a -e file2 ] && echo 'yes'" "yes\n" "" ""
+chmod 400 file1 file2
+testing "force over unwritable" \
+  "mv -f file1 file2 && [ ! -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" ""
+rm -f file*
+
+touch file1 file2
+testing "no clobber (dest exists)" \
+  "mv -n file1 file2 && [ -e file1 -a -e file2 ] && echo yes"\
+  "yes\n" "" ""
 rm -f file*
 
+touch file1
+testing "no clobber (dest doesn't exist)" \
+  "mv -n file1 new-dest && [ ! -e file1 -a -e new-dest ] && echo yes"\
+  "yes\n" "" ""
+rm -f file*
+
+# If there is stdin, it prompts.  If no stdin, it moves anyway and file2 won't
+# exist.
 touch file1 file2
-testing "Move -n file new_file (exist)" "mv -n file1 file2 &&
-   [ -e file1 -a -e file2 ] && echo 'yes'" "yes\n" "" ""
+chmod 400 file1 file2
+testing "mv over unwritable file: no stdin" \
+  "mv file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" ""
 rm -f file*
 
 touch file1 file2
 chmod 400 file1 file2
-testing "Move file over unwritable file with no stdin" \
-   "</dev/null mv file2 file1 && [ -e file -a ! -e file2 ] && echo 'yes'" \
-   "yes\n" "" ""
+testing "mv over unwritable file: answered YES" \
+  "mv file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" "y\n"
+rm -f file*
+
+touch file1 file2
+chmod 400 file1 file2
+testing "mv over unwritable file: answered NO" \
+  "mv file2 file1 && [ -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" "n\n"
+rm -f file*
+
+touch file1 file2
+testing "interactive: no stdin" \
+  "mv -i file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" ""
+rm -f file*
+
+touch file1 file2
+testing "interactive: answered YES" \
+  "mv -i file2 file1 && [ -e file1 -a ! -e file2 ] && echo yes" \
+  "yes\n" "" "y\n"
+rm -f file*
+
+touch file1 file2
+testing "interactive: answered NO" \
+  "mv -i file2 file1 && [ -e file1 -a -e file2 ] && echo yes" \
+  "yes\n" "" "n\n"
 rm -f file*
index 958cb41..499734d 100755 (executable)
@@ -7,20 +7,20 @@
 testing "nl" "nl" "     1\tone\n     2\ttwo\n     3\tthree\n" \
   "" "one\ntwo\nthree\n"
 
-testing "nl explicit defaults" "nl -nrn -b a" \
+testing "explicit defaults" "nl -nrn -b a" \
   "     1\tone\n     2\ttwo\n     3\tthree\n" "" "one\ntwo\nthree\n"
 
 # -n ln rn rz
 
-testing "nl -nln" "nl -nln" "1     \tone\n2     \ttwo\n3     \tthree\n" \
+testing "-nln" "nl -nln" "1     \tone\n2     \ttwo\n3     \tthree\n" \
   "" "one\ntwo\nthree\n"
-testing "nl -nln -w" "nl -nln -w 8" \
+testing "-nln -w" "nl -nln -w 8" \
   "1       \tone\n2       \ttwo\n3       \tthree\n" "" "one\ntwo\nthree\n"
 
-testing "nl -nrz" "nl -nrz" "000001\tone\n000002\ttwo\n000003\tthree\n" \
+testing "-nrz" "nl -nrz" "000001\tone\n000002\ttwo\n000003\tthree\n" \
   "" "one\ntwo\nthree\n"
 
-testing "nl -nrz -w" "nl -w3 -nrz" "001\tone\n002\ttwo\n003\tthree\n" \
+testing "-nrz -w" "nl -w3 -nrz" "001\tone\n002\ttwo\n003\tthree\n" \
   "" "one\ntwo\nthree\n"
 
 
@@ -31,20 +31,20 @@ testing "nl -nrz -w" "nl -w3 -nrz" "001\tone\n002\ttwo\n003\tthree\n" \
 #   nl -w 3 -bpthe README
 # Yeah. And I doubt utf8 fontmetrics are used either.
 
-testing "nl -b t" "nl -b t" "       \n     1\tone\n       \n     2\ttwo\n" \
+testing "-b t" "nl -b t" "       \n     1\tone\n       \n     2\ttwo\n" \
   "" "\none\n\ntwo\n"
-testing "nl -b n" "nl -b n" "       one\n       two\n       three\n" \
+testing "-b n" "nl -b n" "       one\n       two\n       three\n" \
   "" "one\ntwo\nthree\n"
-testing "nl -sook -b p" "nl -sook -bpoing" \
+testing "-sook -b p" "nl -sook -bpoing" \
   "         one\n     1ookboing\n     2ooksproingy\n" \
   "" "one\nboing\nsproingy\n"
 
-testing "nl -v" "nl -v 42" "    42\tone\n    43\ttwo\n    44\tthree\n" \
+testing "-v" "nl -v 42" "    42\tone\n    43\ttwo\n    44\tthree\n" \
   "" "one\ntwo\nthree\n"
-testing "nl -l" "nl -ba -l2 -w2 - input" \
+testing "-l" "nl -ba -l2 -w2 - input" \
   " 1\tone\n   \n 2\t\n 3\ttwo\n   \n 4\t\n   \n 5\tthree\n 6\tfour\n   \n 7\t\n   \n 8\tbang\n   \n" \
   "\n\nbang\n\n" "one\n\n\ntwo\n\n\n\nthree\nfour\n\n"
-testing "nl no space" "nl -w 1 -v 42" "42\tline\n" "" "line\n"
+testing "no space" "nl -w 1 -v 42" "42\tline\n" "" "line\n"
 
 # Should test for -E but no other implementation seems to have it?
-#testing "nl -E" "nl -w2 -sx -Ebp'(one|two)'" " 1x" "one\nand\ntwo\n"
+#testing "-E" "nl -w2 -sx -Ebp'(one|two)'" " 1x" "one\nand\ntwo\n"
index 36df573..3319c79 100755 (executable)
@@ -13,110 +13,25 @@ killall yes >/dev/null 2>&1
 # Starting processes to test pgrep command
 yes >/dev/null &
 proc=$!
-echo "# Process created with id: $proc"
-sleep 1
+#echo "# Process created with id: $proc"
+sleep .1
 session_id=0
 proc_parent=`cat /proc/${proc}/stat | awk '{ print $4 }'`
-echo "# Parent Process id of $proc is $proc_parent"
+#echo "# Parent Process id of $proc is $proc_parent"
 
 # Testcases for pgrep command
-testing "pgrep pattern" "pgrep yes" "$proc\n" "" ""
-testing "pgrep wildCardPattern" "pgrep ^y.*s$" "$proc\n" "" ""
-testing "pgrep -l pattern" "pgrep -l yes" "$proc yes\n" "" ""
-testing "pgrep -f pattern" "pgrep -f yes" "$proc\n" "" ""
-testing "pgrep -n pattern" "pgrep -n yes" "$proc\n" "" ""
-testing "pgrep -o pattern" "pgrep -o yes" "$proc\n" "" ""
-testing "pgrep -s" "pgrep -s $session_id yes" "$proc\n" "" ""
-testing "pgrep -P" "pgrep -P $proc_parent yes" "$proc\n" "" ""
+testing "pattern" "pgrep yes" "$proc\n" "" ""
+testing "wildCardPattern" "pgrep ^y.*s$" "$proc\n" "" ""
+testing "-l pattern" "pgrep -l yes" "$proc yes\n" "" ""
+testing "-f pattern" "pgrep -f yes" "$proc\n" "" ""
+testing "-n pattern" "pgrep -n yes" "$proc\n" "" ""
+testing "-o pattern" "pgrep -o yes" "$proc\n" "" ""
+testing "-s" "pgrep -s $session_id yes" "$proc\n" "" ""
+testing "-P" "pgrep -P $proc_parent yes" "$proc\n" "" ""
+
+testing "return success" "pgrep yes && echo success" "$proc\nsuccess\n" "" ""
+testing "return failure" "pgrep almost-certainly-not || echo failure" \
+    "failure\n" "" ""
 
 #Clean-up
 killall yes >/dev/null 2>&1
-
-# Testcases for pkill command
-
-yes >/dev/null &
-sleep 1
-testing "pkill pattern" "pkill yes && sleep 1 && (pgrep yes || echo 'yes')" \
-  "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-yes print1 >/dev/null &
-yes print2 >/dev/null &
-sleep 1
-testing "pkill pattern (multiple)" "pkill yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-sleep 1
-testing "pkill -f pattern (one)" "pkill -f yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes print1 >/dev/null &
-sleep 1
-testing "pkill -f pattern args" "pkill -f \"yes print1\" && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-yes print1 >/dev/null &
-yes print2 >/dev/null &
-sleep 1
-testing "pkill -f pattern (multiple)" "pkill -f yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-sleep 1
-testing "pkill -s 0 -f pattern (regexp)" "pkill -s 0 -f ye* && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-proc1=$!
-yes >/dev/null &
-proc2=$!
-sleep 1
-testing "pkill -n pattern" "pkill -n yes && sleep 1 && pgrep yes" \
-  "$proc1\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-proc1=$!
-yes >/dev/null &
-proc2=$!
-sleep 1
-testing "pkill -o pattern" "pkill -o yes && sleep 1 && pgrep yes" \
-  "$proc2\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-sleep 1
-testing "pkill -s (blank) pattern" "pkill -s '' yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-sleep 1
-testing "pkill -s 0 pattern" "pkill -s 0 yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-proc=$!
-proc_p=`cat /proc/${proc}/stat | awk '{ print $4 }'`
-sleep 1
-testing "pkill -P parent_prodId pattern" "pkill -P $proc_p yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
-yes >/dev/null &
-proc=$!
-proc_parent=`cat /proc/${proc}/stat | awk '{ print $4 }'`
-sleep 1
-testing "pkill -9 pattern" "pkill -9 yes && sleep 1 &&
-   (pgrep yes || echo 'yes')" "yes\n" "" ""
-killall yes >/dev/null 2>&1
-
diff --git a/tests/pkill.test b/tests/pkill.test
new file mode 100755 (executable)
index 0000000..3f20d26
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# Copyright 2013 Divya Kothari <divya.s.kothari@gmail.com>
+# Copyright 2013 Robin Mittal <robinmittal.it@gmail.com>
+
+#cleaning 'yes' processes
+killall yes >/dev/null 2>&1
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Testcases for pkill command
+
+yes >/dev/null &
+sleep 1
+testing "pattern" "pkill yes && sleep 1 && (pgrep yes || echo 'yes')" \
+  "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+yes print1 >/dev/null &
+yes print2 >/dev/null &
+sleep 1
+testing "pattern (multiple)" "pkill yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "-f pattern (one)" "pkill -f yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes print1 >/dev/null &
+sleep 1
+testing "-f pattern args" "pkill -f \"yes print1\" && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+yes print1 >/dev/null &
+yes print2 >/dev/null &
+sleep 1
+testing "-f pattern (multiple)" "pkill -f yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "-s 0 -f pattern (regexp)" "pkill -s 0 -f ye* && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+proc1=$!
+yes >/dev/null &
+proc2=$!
+sleep 1
+testing "-n pattern" "pkill -n yes && sleep 1 && pgrep yes" \
+  "$proc1\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+proc1=$!
+sleep 1
+yes >/dev/null &
+proc2=$!
+sleep 1
+testing "-o pattern" "pkill -o yes && sleep 1 && pgrep yes" \
+  "$proc2\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "-s (blank) pattern" "pkill -s '' yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "-s 0 pattern" "pkill -s 0 yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+proc=$!
+proc_p=`cat /proc/${proc}/stat | awk '{ print $4 }'`
+sleep 1
+testing "-P parent_prodId pattern" "pkill -P $proc_p yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "-9 pattern" "pkill -9 yes && sleep 1 &&
+   (pgrep yes || echo 'yes')" "yes\n" "" ""
+killall yes >/dev/null 2>&1
+
+yes >/dev/null &
+sleep 1
+testing "return success" "pkill yes && echo success" "success\n" "" ""
+killall yes >/dev/null 2>&1
+
+testing "return failure" "pkill almost-certainly-not || echo failure" \
+    "failure\n" "" ""
+
index 20d5982..a333174 100755 (executable)
 # Disable shell builtin
 PRINTF="$(which printf)"
 
-testing "printf text" "$PRINTF TEXT" "TEXT" "" ""
-testing "printf escapes" "$PRINTF 'one\ntwo\n\v\t\r\f\e\b\athree'" \
+testing "text" "$PRINTF TEXT" "TEXT" "" ""
+testing "escapes" "$PRINTF 'one\ntwo\n\v\t\r\f\e\b\athree'" \
   "one\ntwo\n\v\t\r\f\e\b\athree" "" ""
-testing "printf %b escapes" "$PRINTF %b 'one\ntwo\n\v\t\r\f\e\b\athree'" \
+testing "%b escapes" "$PRINTF %b 'one\ntwo\n\v\t\r\f\e\b\athree'" \
   "one\ntwo\n\v\t\r\f\e\b\athree" "" ""
-testing "printf null" "$PRINTF 'x\0y' | od -An -tx1" ' 78 00 79\n' "" ""
-testing "printf trailing slash" "$PRINTF 'abc\'" 'abc\' "" ""
-testing "printf octal" "$PRINTF ' \1\002\429\045x'" ' \001\002"9%x' "" ""
-testing "printf not octal" "$PRINTF '\9'" '\9' "" ""
-testing "printf hex" "$PRINTF 'A\x1b\x2B\x3Q\xa' | od -An -tx1" \
+testing "null" "$PRINTF 'x\0y' | od -An -tx1" ' 78 00 79\n' "" ""
+testing "trailing slash" "$PRINTF 'abc\'" 'abc\' "" ""
+testing "octal" "$PRINTF ' \1\002\429\045x'" ' \001\002"9%x' "" ""
+testing "not octal" "$PRINTF '\9'" '\9' "" ""
+testing "hex" "$PRINTF 'A\x1b\x2B\x3Q\xa' | od -An -tx1" \
   ' 41 1b 2b 03 51 0a\n' "" ""
-testing "printf %x" "$PRINTF '%x\n' 0x2a" "2a\n" "" ""
+testing "%x" "$PRINTF '%x\n' 0x2a" "2a\n" "" ""
 
-testing "printf %d 42" "$PRINTF %d 42" "42" "" ""
-testing "printf %d 0x2a" "$PRINTF %d 0x2a" "42" "" ""
-testing "printf %d 052" "$PRINTF %d 052" "42" "" ""
+testing "%d 42" "$PRINTF %d 42" "42" "" ""
+testing "%d 0x2a" "$PRINTF %d 0x2a" "42" "" ""
+testing "%d 052" "$PRINTF %d 052" "42" "" ""
 
-testing "printf %s width precision" \
+testing "%s width precision" \
   "$PRINTF '%3s,%.3s,%10s,%10.3s' abcde fghij klmno pqrst" \
   "abcde,fgh,     klmno,       pqr" "" ""
 
 # posix: "The format operand shall be reused as often as necessary to satisfy
 # the argument operands."
 
-testing "printf extra args" "$PRINTF 'abc%s!%ddef\n' X 42 ARG 36" \
+testing "extra args" "$PRINTF 'abc%s!%ddef\n' X 42 ARG 36" \
        "abcX!42def\nabcARG!36def\n" "" ""
 
-testing "printf '%3c'" "$PRINTF '%3c' x" "  x" "" ""
-testing "printf '%-3c'" "$PRINTF '%-3c' x" "x  " "" ""
-testing "printf '%+d'" "$PRINTF '%+d' 5" "+5" "" ""
+testing "'%3c'" "$PRINTF '%3c' x" "  x" "" ""
+testing "'%-3c'" "$PRINTF '%-3c' x" "x  " "" ""
+testing "'%+d'" "$PRINTF '%+d' 5" "+5" "" ""
 
 
-testing "printf '%5d%4d' 1 21 321 4321 54321" \
+testing "'%5d%4d' 1 21 321 4321 54321" \
   "$PRINTF '%5d%4d' 1 21 321 4321 54321" "    1  21  321432154321   0" "" ""
-testing "printf '%c %c' 78 79" "$PRINTF '%c %c' 78 79" "7 7" "" ""
-testing "printf '%d %d' 78 79" "$PRINTF '%d %d' 78 79" "78 79" "" ""
-testing "printf '%f %f' 78 79" "$PRINTF '%f %f' 78 79" \
+testing "'%c %c' 78 79" "$PRINTF '%c %c' 78 79" "7 7" "" ""
+testing "'%d %d' 78 79" "$PRINTF '%d %d' 78 79" "78 79" "" ""
+testing "'%f %f' 78 79" "$PRINTF '%f %f' 78 79" \
   "78.000000 79.000000" "" ""
-testing "printf 'f f' 78 79" "$PRINTF 'f f' 78 79" "f f" "" ""
-testing "printf '%i %i' 78 79" "$PRINTF '%i %i' 78 79" "78 79" "" ""
-testing "printf '%o %o' 78 79" "$PRINTF '%o %o' 78 79" "116 117" "" ""
-testing "printf '%u %u' 78 79" "$PRINTF '%u %u' 78 79" "78 79" "" ""
-testing "printf '%u %u' -1 -2" "$PRINTF '%u %u' -1 -2" \
+testing "'f f' 78 79" "$PRINTF 'f f' 78 79" "f f" "" ""
+testing "'%i %i' 78 79" "$PRINTF '%i %i' 78 79" "78 79" "" ""
+testing "'%o %o' 78 79" "$PRINTF '%o %o' 78 79" "116 117" "" ""
+testing "'%u %u' 78 79" "$PRINTF '%u %u' 78 79" "78 79" "" ""
+testing "'%u %u' -1 -2" "$PRINTF '%u %u' -1 -2" \
   "18446744073709551615 18446744073709551614" "" ""
-testing "printf '%x %X' 78 79" "$PRINTF '%x %X' 78 79" "4e 4F" "" ""
-testing "printf '%g %G' 78 79" "$PRINTF '%g %G' 78 79" "78 79" "" ""
-testing "printf '%s %s' 78 79" "$PRINTF '%s %s' 78 79" "78 79" "" ""
+testing "'%x %X' 78 79" "$PRINTF '%x %X' 78 79" "4e 4F" "" ""
+testing "'%g %G' 78 79" "$PRINTF '%g %G' 78 79" "78 79" "" ""
+testing "'%s %s' 78 79" "$PRINTF '%s %s' 78 79" "78 79" "" ""
 
-testing "printf %.s acts like %.0s" "$PRINTF %.s_ 1 2 3 4 5" "_____" "" ""
+testing "%.s acts like %.0s" "$PRINTF %.s_ 1 2 3 4 5" "_____" "" ""
+testing "corner case" "$PRINTF '\\8'" '\8' '' ''
+
+# The posix spec explicitly specifies inconsistent behavior,
+# so treating the \0066 in %b like the \0066 not in %b is wrong because posix.
+testing "printf posix inconsistency" "$PRINTF '\\0066-%b' '\\0066'" "\x066-6" \
+  "" ""
index b02055b..68d22e3 100755 (executable)
@@ -8,7 +8,7 @@
 
 testing "pwd" "[ $(stat -c %i "$(pwd)") = $(stat -c %i .) ] && echo yes" \
        "yes\n" "" ""
-testing "pwd -P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i .) ] && echo yes" \
+testing "-P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i .) ] && echo yes" \
        "yes\n" "" ""
 
 
@@ -16,11 +16,11 @@ ln -s . sym
 cd sym
 testing "pwd" "[ $(stat -c %i "$(pwd)") = $(stat -c %i "$PWD") ] && echo yes" \
        "yes\n" "" ""
-testing "pwd -P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i "$PWD") ] || echo yes" \
+testing "-P" "[ $(stat -c %i "$(pwd -P)") = $(stat -c %i "$PWD") ] || echo yes" \
        "yes\n" "" ""
 cd ..
 rm sym
 
 export PWD=walrus
-testing "pwd (bad PWD)" "[ "$(pwd)" = "$(cd . ; pwd)" ] && echo yes" \
+testing "(bad PWD)" "[ "$(pwd)" = "$(cd . ; pwd)" ] && echo yes" \
        "yes\n" "" ""
index 6c7b147..bb25985 100755 (executable)
@@ -6,54 +6,54 @@
 
 APWD="$(pwd -P)"
 
-testing "readlink missing" "readlink notfound || echo yes" "yes\n" "" ""
+testing "missing" "readlink notfound || echo yes" "yes\n" "" ""
 
 # simple tests on a file
 
 touch file
-testing "readlink file" "readlink file || echo yes" "yes\n" "" ""
-testing "readlink -f dir" "readlink -f ." "$APWD\n" "" ""
-testing "readlink -f missing" "readlink -f notfound" "$APWD/notfound\n" "" ""
+testing "file" "readlink file || echo yes" "yes\n" "" ""
+testing "-f dir" "readlink -f ." "$APWD\n" "" ""
+testing "-f missing" "readlink -f notfound" "$APWD/notfound\n" "" ""
 
 ln -sf notfound link
-testing "readlink link" "readlink link" "notfound\n" "" ""
-testing "readlink link->missing" "readlink -f link" "$APWD/notfound\n" "" ""
+testing "link" "readlink link" "notfound\n" "" ""
+testing "link->missing" "readlink -f link" "$APWD/notfound\n" "" ""
 ln -sf ../../ link
-testing "readlink stays relative" "readlink link" "../../\n" "" ""
+testing "stays relative" "readlink link" "../../\n" "" ""
 rm link
 ln -sf file link
-testing "readlink -f link->file" "readlink -f link" "$APWD/file\n" "" ""
+testing "-f link->file" "readlink -f link" "$APWD/file\n" "" ""
 ln -sf . link
-testing "readlink -f link->dir" "readlink -f link" "$APWD\n" "" ""
+testing "-f link->dir" "readlink -f link" "$APWD\n" "" ""
 ln -snf link link
-testing "readlink link->link (recursive)" "readlink link" "link\n" "" ""
-testing "readlink -f link->link (recursive)" \
+testing "link->link (recursive)" "readlink link" "link\n" "" ""
+testing "-f link->link (recursive)" \
   "readlink -f link 2>/dev/null || echo yes" "yes\n" "" ""
 
-testing "readlink -q notlink" "readlink -q file || echo yes" "yes\n" "" ""
-testing "readlink -q link" "readlink -q link && echo yes" "yes\n" "" ""
-testing "readlink -q notfound" "readlink -q notfound || echo yes" "yes\n" "" ""
-testing "readlink -e found" "readlink -e file" "$APWD/file\n" "" ""
-testing "readlink -e notfound" \
+testing "-q notlink" "readlink -q file || echo yes" "yes\n" "" ""
+testing "-q link" "readlink -q link && echo yes" "yes\n" "" ""
+testing "-q notfound" "readlink -q notfound || echo yes" "yes\n" "" ""
+testing "-e found" "readlink -e file" "$APWD/file\n" "" ""
+testing "-e notfound" \
   "readlink -e notfound 2>/dev/null || echo yes" "yes\n" "" ""
-testing "readlink -nf ." "readlink -nf ." "$APWD" "" ""
+testing "-nf ." "readlink -nf ." "$APWD" "" ""
 
 mkdir sub &&
 ln -s . here &&
 ln -s ./sub dir &&
 touch sub/bang || exit 1
-testing "readlink -f multi" "readlink -f dir/../here/dir/bang" \
+testing "-f multi" "readlink -f dir/../here/dir/bang" \
   "$APWD/sub/bang\n" "" ""
-testing "readlink -f link/missing" "readlink -f dir/boing" \
+testing "-f link/missing" "readlink -f dir/boing" \
   "$APWD/sub/boing\n" "" ""
-testing "readlink -f /dev/null/file" \
+testing "-f /dev/null/file" \
   "readlink -f /dev/null/file 2>/dev/null || echo yes" "yes\n" "" ""
 ln -sf / link || exit 1
-testing "readlink -f link->/" "readlink -e link/dev" "/dev\n" "" ""
-testing "readlink -f /dev/null/.." \
+testing "-f link->/" "readlink -e link/dev" "/dev\n" "" ""
+testing "-f /dev/null/.." \
   "readlink -f link/null/.. 2>/dev/null || echo yes" "yes\n" "" ""
 rm -f link && ln -sf link link || exit 1
-testing "readlink recurse" "readlink link" "link\n" "" ""
+testing "recurse" "readlink link" "link\n" "" ""
 
 rm file link sub/bang dir here
 rmdir sub
@@ -62,6 +62,6 @@ rmdir sub
 
 ln -s link1 link2
 ln -s link2 link1
-testing "readlink follow recursive2" "readlink -f link1 || echo yes" \
+testing "follow recursive2" "readlink -f link1 || echo yes" \
        "yes\n" "" ""
 rm link1 link2
index e87111c..e350c27 100755 (executable)
@@ -45,7 +45,7 @@ do
     nice_val3=$((`fun_nice_val $proc3` + $n_v))
     nice_val4=$((`fun_nice_val $proc4` + $n_v))
     nice_val5=$((`fun_nice_val $proc5` + $n_v))
-    testing "renice with -n=$n_v and with$n_o multiple_pids" \
+    testing "with -n=$n_v and with$n_o multiple_pids" \
       "renice -n $n_v$n_o $proc1 $proc2 $proc3 $proc4 $proc5 &&
        fun_nice_val $proc1 $proc2 $proc3 $proc4 $proc5" \
       "$nice_val1\n$nice_val2\n$nice_val3\n$nice_val4\n$nice_val5\n" "" ""
@@ -55,7 +55,7 @@ do
     nice_val3=$((`fun_nice_val $proc3` + $n_v))
     nice_val4=$((`fun_nice_val $proc4` + $n_v))
     nice_val5=$((`fun_nice_val $proc5` + $n_v))
-    testing "renice with -n=$n_v and with$n_o multiple_pids (some invalid)" \
+    testing "with -n=$n_v and with$n_o multiple_pids (some invalid)" \
       "renice -n $n_v$n_o $proc1 $proc2 88888 99999 $proc3 $proc4 $proc5 $arg ||
        fun_nice_val $proc1 $proc2 $proc3 $proc4 $proc5" \
       "$nice_val1\n$nice_val2\n$nice_val3\n$nice_val4\n$nice_val5\n" "" ""
index c7622b9..ede78c2 100755 (executable)
@@ -7,17 +7,17 @@
 echo -e "one" > file1 
 echo -e "two" > file2
 testing "rev" "rev && echo yes" "orez\nyes\n" "" "zero\n"
-testing "rev -" "rev - && echo yes" "orez\nyes\n" "" "zero\n"
-testing "rev file1 file2" "rev file1 file2" "eno\nowt\n" "" ""
-testing "rev - file"      "rev - file1"     "orez\neno\n" "" "zero\n"
-testing "rev file -"      "rev file1 -"     "eno\norez\n" "" "zero\n"
-testing "rev no trailing newline" "rev -" "cba\nfed\n" "" "abc\ndef"
+testing "-" "rev - && echo yes" "orez\nyes\n" "" "zero\n"
+testing "file1 file2" "rev file1 file2" "eno\nowt\n" "" ""
+testing "- file"      "rev - file1"     "orez\neno\n" "" "zero\n"
+testing "file -"      "rev file1 -"     "eno\norez\n" "" "zero\n"
+testing "no trailing newline" "rev -" "cba\nfed\n" "" "abc\ndef"
 
-testing "rev file1 notfound file2" \
+testing "file1 notfound file2" \
         "rev file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
         "eno\nowt\nrev: notfound: No such file or directory\n" "" ""
 
-testing "rev different input sizes"\
+testing "different input sizes"\
         "rev"\
         "\n1\n21\n321\n4321\n54321\n4321\n321\n21\n1\n\n"\
         "" "\n1\n12\n123\n1234\n12345\n1234\n123\n12\n1\n\n"
index 0dca853..624d60f 100755 (executable)
@@ -8,21 +8,21 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 echo "abcdefghijklmnopqrstuvwxyz" > file.txt
-testing "Remove text-file" "rm file.txt && [ ! -e file.txt ] && echo 'yes'" "yes\n" "" ""
+testing "text-file" "rm file.txt && [ ! -e file.txt ] && echo 'yes'" "yes\n" "" ""
 rm -f file*
 
 mkdir dir
-testing "Remove empty directory" "rm -r dir && [ ! -d dir ] && echo 'yes'" "yes\n" "" ""
+testing "empty directory" "rm -r dir && [ ! -d dir ] && echo 'yes'" "yes\n" "" ""
 rm -rf dir
 
 echo "abcdefghijklmnopqrstuvwxyz" > file.txt && chmod 000 file.txt
-testing "Remove text file(mode 000)" "rm -f file.txt && [ ! -e file.txt ] && echo 'yes'" \
+testing "text file(mode 000)" "rm -f file.txt && [ ! -e file.txt ] && echo 'yes'" \
   "yes\n" "" ""
 rm -f file*
 
 touch file1.txt file2.txt
 mkdir dir1 dir2
-testing "rm -r (multiple files and dirs)" \
+testing "-r (multiple files and dirs)" \
   "rm -r file1.txt file2.txt dir1 dir2 2>/dev/null &&
    [ ! -e file1.txt -a ! -e file2.txt -a ! -d dir1 -a ! -d dir2 ] && echo 'yes'" \
   "yes\n" "" ""
@@ -30,7 +30,7 @@ rm -rf file* dir*
 
 touch file1.txt file2.txt
 mkdir dir1 dir2
-testing "rm -rf (present + missing files and dirs)" \
+testing "-rf (present + missing files and dirs)" \
   "rm -rf file1.txt file2.txt file3.txt dir1 dir2 dir3 2>/dev/null &&
   [ ! -e file1.txt -a ! -e file2.txt -a ! -d dir1 -a ! -d dir2 ] && echo 'yes'" \
   "yes\n" "" ""
@@ -39,7 +39,7 @@ rm -rf file* dir*
 # testing with nested dirs.
 mkdir -p dir1/dir2/dir3 dir1/dir2/dir4
 touch dir1/file1.txt dir1/dir2/file2.txt dir1/dir2/dir3/file3.txt
-testing "rm -r nested_dir" "rm -r dir1/dir2/ 2>/dev/null &&
+testing "-r nested_dir" "rm -r dir1/dir2/ 2>/dev/null &&
   [ -d dir1 -a -f dir1/file1.txt -a ! -d dir1/dir2 ] && echo 'yes'" \
   "yes\n" "" ""
 rm -rf dir*
index a7b027e..5b36bbc 100755 (executable)
@@ -8,48 +8,48 @@ mkdir one
 testing "rmdir" "rmdir one && [ ! -d one ] && echo yes" "yes\n" "" ""
 
 touch walrus
-testing "rmdir file" \
+testing "file" \
        "rmdir walrus 2> /dev/null || [ -f walrus ] && echo yes" "yes\n" "" ""
 
 mkdir one two
-testing "rmdir one two" \
+testing "one two" \
        "rmdir one two 2> /dev/null && [ ! -d one ] && [ ! -d two ] && echo yes" \
        "yes\n" "" ""
 
 mkdir one two three
-testing "rmdir one missing two file three" \
+testing "one missing two file three" \
        "rmdir one missing two walrus three 2> /dev/null || [ ! -d three ] && echo yes" \
        "yes\n" "" ""
 rm walrus
 
 mkdir one
 chmod 000 one
-testing "rmdir mode 000" "rmdir one && [ ! -d one ] && echo yes" "yes\n" "" ""
+testing "mode 000" "rmdir one && [ ! -d one ] && echo yes" "yes\n" "" ""
 
 mkdir temp
 touch temp/thing
-testing "rmdir non-empty" \
+testing "non-empty" \
        "rmdir temp 2>/dev/null || [ -d temp ] && echo yes" "yes\n" "" ""
-testing "rmdir -p dir/file" \
+testing "-p dir/file" \
        "rmdir -p temp/thing 2>/dev/null || [ -f temp/thing ] && echo yes" \
        "yes\n" "" ""
 
 mkdir -p temp/one/two/three
-testing "rmdir -p part of path" \
+testing "-p part of path" \
        "rmdir -p temp/one/two/three 2>/dev/null || [ -d temp ] && [ ! -e temp/one ] && echo yes" \
        "yes\n" "" ""
 rm -rf temp
 
 
 mkdir -p one/two/three
-testing "rmdir -p one/two/three" \
+testing "-p one/two/three" \
        "rmdir -p one/two/three && [ ! -e one ] && echo yes" "yes\n" "" ""
 
 mkdir -p one/two/three
-testing "rmdir -p one/two/three/" \
+testing "-p one/two/three/" \
        "rmdir -p one/two/three/ && [ ! -e one ] && echo yes" "yes\n" "" ""
 
 #mkdir -p one/two/three
 #chmod 000 one/two/three one/two one
-#testing "rmdir -p one/two/three" \
+#testing "-p one/two/three" \
 #      "rmdir -p one/two/three && [ ! -e one ] && echo yes" "yes\n" "" ""
index c62f9c4..f48b73e 100755 (executable)
@@ -2,26 +2,26 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing 'sed as cat' 'sed ""' "one\ntwo\nthree" "" "one\ntwo\nthree"
+testing 'as cat' 'sed ""' "one\ntwo\nthree" "" "one\ntwo\nthree"
 # This segfaults ubuntu 12.04's sed. No really.
 SKIP_HOST=1 testing 'sed - - twice' 'sed "" - -' "hello\n" "" "hello\n"
-testing 'sed -n' 'sed -n ""' "" "" "one\ntwo\nthree"
-testing 'sed -n p' 'sed -n p' "one\ntwo\nthree" "" "one\ntwo\nthree"
-testing 'sed explicit pattern' 'sed -e p -n' "one\ntwo\nthree" "" \
+testing '-n' 'sed -n ""' "" "" "one\ntwo\nthree"
+testing '-n p' 'sed -n p' "one\ntwo\nthree" "" "one\ntwo\nthree"
+testing 'explicit pattern' 'sed -e p -n' "one\ntwo\nthree" "" \
        "one\ntwo\nthree"
 
 # Exploring the wonders of sed addressing modes
 testing '' 'sed -n 1p' "one\n" "" "one\ntwo\nthree"
 testing '' 'sed 2p' "one\ntwo\ntwo\nthree" "" "one\ntwo\nthree"
 testing '' 'sed -n 2p' "two\n" "" "one\ntwo\nthree"
-testing 'sed -n $p' 'sed -n \$p' "three" "" "one\ntwo\nthree"
-testing 'sed as cat #2' "sed -n '1,\$p'" "one\ntwo\nthree" "" "one\ntwo\nthree"
-testing 'sed no input means no last line' "sed '\$a boing'" "" "" ""
-testing 'sed -n $,$p' 'sed -n \$,\$p' 'three' '' 'one\ntwo\nthree'
+testing '-n $p' 'sed -n \$p' "three" "" "one\ntwo\nthree"
+testing 'as cat #2' "sed -n '1,\$p'" "one\ntwo\nthree" "" "one\ntwo\nthree"
+testing 'no input means no last line' "sed '\$a boing'" "" "" ""
+testing '-n $,$p' 'sed -n \$,\$p' 'three' '' 'one\ntwo\nthree'
 testing '' 'sed -n 1,2p' "one\ntwo\n" "" "one\ntwo\nthree"
 testing '' 'sed -n 2,3p' "two\nthree" "" "one\ntwo\nthree"
 testing '' 'sed -n 2,1p' "two\n" "" "one\ntwo\nthree"
-testing 'sed $ with 2 inputs' 'sed -n \$p - input' "four\n" "four\n" \
+testing '$ with 2 inputs' 'sed -n \$p - input' "four\n" "four\n" \
        "one\ntwo\nthree"
 testing '' 'sed -n /two/p' "two\n" "" "one\ntwo\nthree"
 testing '' 'sed -n 1,/two/p' 'one\ntwo\n' '' 'one\ntwo\nthree'
@@ -33,54 +33,57 @@ testing 'sed -n /two/,$p' 'sed -n /two/,\$p' 'two\nthree' '' 'one\ntwo\nthree'
 
 # Fun with newlines!
 testing '' 'sed -n 3p' "three" "" "one\ntwo\nthree"
-testing 'sed prodigal newline' "sed -n '1,\$p' - input" \
+testing 'prodigal newline' "sed -n '1,\$p' - input" \
        "one\ntwo\nthree\nfour\n" "four\n" "one\ntwo\nthree"
-testing 'sed Newline only added if further output' "sed -n 3p - input" "three" \
+testing 'Newline only added if further output' "sed -n 3p - input" "three" \
        "four\n" "one\ntwo\nthree"
 
 # Fun with match delimiters and escapes
-testing 'sed match \t tab' "sed -n '/\t/p'" "\tx\n" "" "\tx\n"
-testing 'sed match t delim disables \t tab' "sed -n '\t\txtp'" "" "" "\tx\n"
-testing 'sed match t delim makes \t literal t' \
+testing 'match \t tab' "sed -n '/\t/p'" "\tx\n" "" "\tx\n"
+testing 'match t delim disables \t tab' "sed -n '\t\txtp'" "" "" "\tx\n"
+testing 'match t delim makes \t literal t' \
        "sed -n '\t\txtp'" "tx\n" "" "tx\n"
-testing 'sed match n delim' "sed -n '\n\txnp'" "\tx\n" "" "\tx\n"
-testing 'sed match n delim disables \n newline' "sed -n '\n\nxnp'" "" "" "\nx\n"
-SKIP_HOST=1 testing 'sed match \n literal n' "sed -n '\n\nxnp'" "nx\n" "" "nx\n"
-testing 'sed end match does not check starting match line' \
+testing 'match n delim' "sed -n '\n\txnp'" "\tx\n" "" "\tx\n"
+testing 'match n delim disables \n newline' "sed -n '\n\nxnp'" "" "" "\nx\n"
+SKIP_HOST=1 testing 'match \n literal n' "sed -n '\n\nxnp'" "nx\n" "" "nx\n"
+testing 'end match does not check starting match line' \
        "sed -n '/two/,/two/p'" "two\nthree" "" "one\ntwo\nthree"
-testing 'sed end match/start match mixing number/letter' \
+testing 'end match/start match mixing number/letter' \
        "sed -n '2,/two/p'" "two\nthree" "" "one\ntwo\nthree"
-testing 'sed num then regex' 'sed -n 2,/d/p' 'b\nc\nd\n' '' 'a\nb\nc\nd\ne\nf\n'
-testing 'sed regex then num' 'sed -n /b/,4p' 'b\nc\nd\n' '' 'a\nb\nc\nd\ne\nf\n'
-testing 'sed multiple regex address match' 'sed -n /on/,/off/p' \
+testing 'num then regex' 'sed -n 2,/d/p' 'b\nc\nd\n' '' 'a\nb\nc\nd\ne\nf\n'
+testing 'regex then num' 'sed -n /b/,4p' 'b\nc\nd\n' '' 'a\nb\nc\nd\ne\nf\n'
+testing 'multiple regex address match' 'sed -n /on/,/off/p' \
        'bone\nturtle\scoff\ntron\nlurid\noffer\n'  "" \
        'zap\nbone\nturtle\scoff\nfred\ntron\nlurid\noffer\nbecause\n'
-testing 'sed regex address overlap' 'sed -n /on/,/off/p' "on\nzap\noffon\n" "" \
+testing 'regex address overlap' 'sed -n /on/,/off/p' "on\nzap\noffon\n" "" \
        'on\nzap\noffon\nping\noff\n'
+testing 'getdelim with nested [:blah:]' 'sed -n "sa\a[a[:space:]bc]*aXXagp"' \
+       "ABXXCDXXEFXXGHXXIXX" "" "ABaaCDa EFaa aGHa a Ia "
 
 # gGhHlnNpPqrstwxy:=
 # s///#comment
 # abcdDi
 
-testing 'sed prodigaler newline' 'sed -e a\\ -e woo' 'one\nwoo\n' '' 'one'
-testing "sed aci" \
+testing 'prodigaler newline' 'sed -e a\\ -e woo' 'one\nwoo\n' '' 'one'
+testing "aci" \
        "sed -e '3a boom' -e '/hre/i bang' -e '3a whack' -e '3c bong'" \
        "one\ntwo\nbang\nbong\nboom\nwhack\nfour\n" "" \
        "one\ntwo\nthree\nfour\n"
-testing "sed b loop" "sed ':woo;=;b woo' | head -n 5" '1\n1\n1\n1\n1\n' "" "X"
-testing "sed b skip" "sed -n '2b zap;d;:zap;p'" "two\n" "" "one\ntwo\nthree"
-testing "sed b end" "sed -n '2b;p'" "one\nthree" "" "one\ntwo\nthree"
-testing "sed c range" "sed '2,4c blah'" "one\nblah\nfive\nsix" "" \
+testing "b loop" "sed ':woo;=;b woo' | head -n 5" '1\n1\n1\n1\n1\n' "" "X"
+testing "b skip" "sed -n '2b zap;d;:zap;p'" "two\n" "" "one\ntwo\nthree"
+testing "b end" "sed -n '2b;p'" "one\nthree" "" "one\ntwo\nthree"
+testing "c range" "sed '2,4c blah'" "one\nblah\nfive\nsix" "" \
        "one\ntwo\nthree\nfour\nfive\nsix"
-testing "sed c {range}" "sed -e '2,4{c blah' -e '}'" \
+testing "c {range}" "sed -e '2,4{c blah' -e '}'" \
        "one\nblah\nblah\nblah\nfive\nsix" \
         "" "one\ntwo\nthree\nfour\nfive\nsix"
-testing "sed c multiple continuation" \
+testing "c multiple continuation" \
        "sed -e 'c\\' -e 'two\\' -e ''" "two\n\n" "" "hello"
-testing "sed D further processing depends on whether line is blank" \
+SKIP_HOST=1 testing "c empty continuation" "sed -e 'c\\'" "\n" "" "hello"
+testing "D further processing depends on whether line is blank" \
        "sed -e '/one/,/three/{' -e 'i meep' -e'N;2D;}'" \
        "meep\nmeep\ntwo\nthree\n" "" "one\ntwo\nthree\n"
-testing 'sed newline staying away' 'sed s/o/x/' 'xne\ntwx' '' 'one\ntwo'
+testing 'newline staying away' 'sed s/o/x/' 'xne\ntwx' '' 'one\ntwo'
 
 # Why on _earth_ is this not an error? There's a \ with no continuation!
 #testing 'sed what, _really_?' 'sed -e a\\ && echo yes really' \
@@ -88,36 +91,36 @@ testing 'sed newline staying away' 'sed s/o/x/' 'xne\ntwx' '' 'one\ntwo'
 
 # all the s/// test
 
-testing "sed match empty line" "sed -e 's/^\$/@/'" "@\n" "" "\n"
+testing "match empty line" "sed -e 's/^\$/@/'" "@\n" "" "\n"
 
-testing 'sed \1' "sed 's/t\\(w\\)o/za\\1py/'" "one\nzawpy\nthree" "" \
+testing '\1' "sed 's/t\\(w\\)o/za\\1py/'" "one\nzawpy\nthree" "" \
        "one\ntwo\nthree"
-testing 'sed \1 p' "sed 's/t\\(w\\)o/za\\1py/p'" "one\nzawpy\nzawpy\nthree" \
+testing '\1 p' "sed 's/t\\(w\\)o/za\\1py/p'" "one\nzawpy\nzawpy\nthree" \
        "" "one\ntwo\nthree"
-testing 'sed \1 no newline' "sed 's/t\\(w\\)o/za\\1py/'" "one\nzawpy" "" \
+testing '\1 no newline' "sed 's/t\\(w\\)o/za\\1py/'" "one\nzawpy" "" \
        "one\ntwo"
-testing 'sed \1 p no newline' "sed 's/t\\(w\\)o/za\\1py/p'" \
+testing '\1 p no newline' "sed 's/t\\(w\\)o/za\\1py/p'" \
        "one\nzawpy\nzawpy" "" "one\ntwo"
-testing 'sed -n s//\1/p' "sed -n 's/t\\(w\\)o/za\\1py/p'" "zawpy" "" "one\ntwo"
-testing 'sed -n s//\1/p no newline' "sed -n 's/t\\(w\\)o/za\\1py/p'" "zawpy" \
+testing '-n s//\1/p' "sed -n 's/t\\(w\\)o/za\\1py/p'" "zawpy" "" "one\ntwo"
+testing '-n s//\1/p no newline' "sed -n 's/t\\(w\\)o/za\\1py/p'" "zawpy" \
        "" "one\ntwo"
-testing 'sed backref error' \
+testing 'backref error' \
        "sed 's/w/ale \2 ha/' >/dev/null 2>/dev/null || echo no" \
        "no\n" "" "one\ntwo\nthree"
-testing 'sed empty match after nonempty match' "sed -e 's/a*/c/g'" 'cbcncgc' \
+testing 'empty match after nonempty match' "sed -e 's/a*/c/g'" 'cbcncgc' \
        '' 'baaang'
-testing 'sed empty match' "sed -e 's/[^ac]*/A/g'" 'AaAcA' '' 'abcde'
-testing 'sed s///#comment' "sed -e 's/TWO/four/i#comment'" "one\nfour\nthree" \
+testing 'empty match' "sed -e 's/[^ac]*/A/g'" 'AaAcA' '' 'abcde'
+testing 's///#comment' "sed -e 's/TWO/four/i#comment'" "one\nfour\nthree" \
        "" "one\ntwo\nthree"
 
-testing 'sed N flushes pending a and advances match counter' \
+testing 'N flushes pending a and advances match counter' \
        "sed -e 'a woo' -e 'N;\$p'" 'woo\none\ntwo\none\ntwo' "" 'one\ntwo'
-testing "sed delimiter in regex [char range] doesn't count" "sed -e 's/[/]//'" \
+testing "delimiter in regex [char range] doesn't count" "sed -e 's/[/]//'" \
        "onetwo\n" "" 'one/two\n'
-testing "sed delete regex range start line after trigger" \
+testing "delete regex range start line after trigger" \
        "sed -e '/one/,/three/{' -e 'i meep' -e '1D;}'" \
        "meep\nmeep\ntwo\nmeep\nthree" "" "one\ntwo\nthree"
-testing "sed blank pattern repeats last pattern" \
+testing "blank pattern repeats last pattern" \
        "sed -e '/^three/s//abc&def/'" \
        "one two three\nabcthreedef four five\nfive six seven\n" "" \
        "one two three\nthree four five\nfive six seven\n"
@@ -126,28 +129,32 @@ testing "sed blank pattern repeats last pattern" \
 
 testing "" "sed -e '1a\' -e 'huh'" "meep\nhuh\n" "" "meep"
 testing "" "sed -f input" "blah\nboom\n" '1a\\\nboom' 'blah'
+testing "" "sed -f - input" "blah\nboom\n" 'blah' '1a\\\nboom'
 testing "" "sed '1a\
 hello'" "merp\nhello\n" "" "merp"
 
 testing "" "sed -e '/x/c\' -e 'y'" 'y\n' '' 'x\n'
 testing "" "sed -e 's/a[([]*b/X/'" 'X' '' 'a[(b'
 testing "" "sed 'y/a\\bc/de\f/'" "db\f" "" "abc"
-testing "sed [a-a] (for perl)" "sed '"'s/\([^a-zA-Z0-9.:_\-\/]\)/\\\1/g'"'" \
+testing "[a-a] (for perl)" "sed '"'s/\([^a-zA-Z0-9.:_\-\/]\)/\\\1/g'"'" \
   'he\ llo' "" "he llo"
 
+# Debian bug https://bugs.debian.org/635570 added code to ensure a file
+# ends with a newline via "sed -e '$a\'". Apparently all a\ with no additional
+# pattern lines after it does (other than making posix throw up) is
+# flush the pending newline as _if_ it had added another line. *shrug* Ok?
+testing "trailing a\ (for debian)" "sed 'a\\'" "hello\n" "" "hello"
+
 # You have to match the first line of a range in order to activate
 # the range, numeric and ascii work the same way
-testing "sed skip start of range" "sed -e n -e '1,2s/b/c/'" "a\nb\n" "" "a\nb\n"
+testing "skip start of range" "sed -e n -e '1,2s/b/c/'" "a\nb\n" "" "a\nb\n"
 
 #echo meep | sed/sed -e '1a\' -e 'huh'
 #echo blah | sed/sed -f <(echo -e "1a\\\\\nboom")
 #echo merp | sed/sed "1a\\
 #hello"
 
-testing "sed bonus backslashes" \
+testing "bonus backslashes" \
   "sed -e 'a \l \x\' -e \"\$(echo -e 'ab\\\nc')\"" \
   "hello\nl x\nab\nc\n" "" "hello\n"
 # -i with $ last line test
-
-
-exit $FAILCOUNT
index c9e1c01..7107978 100755 (executable)
@@ -4,45 +4,45 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "seq (exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" ""
-testing "seq (exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \
+testing "(exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" ""
+testing "(exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \
         "yes\n" "" ""
-testing "seq one argument" "seq 3" "1\n2\n3\n" "" ""
-testing "seq two arguments" "seq 5 7" "5\n6\n7\n" "" ""
-testing "seq two arguments reversed" "seq 7 5" "" "" ""
-testing "seq two arguments equal" "seq 3 3" "3\n" "" ""
-testing "seq two arguments equal, arbitrary negative step" "seq 1 -15 1" \
+testing "one argument" "seq 3" "1\n2\n3\n" "" ""
+testing "two arguments" "seq 5 7" "5\n6\n7\n" "" ""
+testing "two arguments reversed" "seq 7 5" "" "" ""
+testing "two arguments equal" "seq 3 3" "3\n" "" ""
+testing "two arguments equal, arbitrary negative step" "seq 1 -15 1" \
         "1\n" "" ""
-testing "seq two arguments equal, arbitrary positive step" "seq 1 +15 1" \
+testing "two arguments equal, arbitrary positive step" "seq 1 +15 1" \
         "1\n" "" ""
-testing "seq count up by 2" "seq 4 2 8" "4\n6\n8\n" "" ""
-testing "seq count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" ""
-testing "seq count wrong way #1" "seq 4 -2 8" "" "" ""
-testing "seq count wrong way #2" "seq 8 2 4" "" "" ""
-testing "seq count by .3" "seq 3 .3 4" "3\n3.3\n3.6\n3.9\n" "" ""
-testing "seq count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2\n" "" ""
-testing "seq count by zero" "seq 4 0 8 | head -n 10" "" "" ""
-testing "seq separator -" "seq -s - 1 3" "1-2-3\n" "" ""
-testing "seq format string" 'seq -f %+01g -10 5 10' "-10\n-5\n+0\n+5\n+10\n" \
+testing "count up by 2" "seq 4 2 8" "4\n6\n8\n" "" ""
+testing "count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" ""
+testing "count wrong way #1" "seq 4 -2 8" "" "" ""
+testing "count wrong way #2" "seq 8 2 4" "" "" ""
+testing "count by .3" "seq 3 .3 4" "3\n3.3\n3.6\n3.9\n" "" ""
+testing "count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2\n" "" ""
+testing "count by zero" "seq 4 0 8 | head -n 10" "" "" ""
+testing "separator -" "seq -s - 1 3" "1-2-3\n" "" ""
+testing "format string" 'seq -f %+01g -10 5 10' "-10\n-5\n+0\n+5\n+10\n" \
   "" ""
-testing "seq separator and format string" "seq -f \%03g -s \; 5 -1 0" "005;004;003;002;001;000\n" "" ""
-testing "seq padding" "seq -s, -w -2 19 120" "-02,017,036,055,074,093,112\n" \
+testing "separator and format string" "seq -f \%03g -s \; 5 -1 0" "005;004;003;002;001;000\n" "" ""
+testing "padding" "seq -s, -w -2 19 120" "-02,017,036,055,074,093,112\n" \
   "" ""
-testing "seq padding" "seq -s, -w -2 3 12" "-2,01,04,07,10\n" "" ""
-testing "seq padding" "seq -s, -w -2.2 3.3 12" "-2.2,01.1,04.4,07.7,11.0\n" \
+testing "padding" "seq -s, -w -2 3 12" "-2,01,04,07,10\n" "" ""
+testing "padding" "seq -s, -w -2.2 3.3 12" "-2.2,01.1,04.4,07.7,11.0\n" \
   "" ""
 
 # Test -f format filtering
 for i in %f %e %g "boo %f yah" "% f" %-1.2f %+-f "%+ - f" %.2f %3.f "%'.2f" \
        %%%f%%
 do
-  testing "seq filter -f \"$i\"" "seq -f \"$i\" 1 3 > /dev/null && echo yes" \
+  testing "filter -f \"$i\"" "seq -f \"$i\" 1 3 > /dev/null && echo yes" \
     "yes\n" "" ""
 done
 # Test -f format filtering failures
 for i in %d %s "" "boo %f %f yah" "%*f" %-1.2.3f '%2$f' %1-f "%1 f" \
        %2..2f %%%f%%%
 do
-  testing "seq filter reject -f '$i'" \
+  testing "filter reject -f '$i'" \
     "seq -f '$i' 1 3 2>/dev/null || echo no" "no\n" "" ""
 done
diff --git a/tests/setfattr.test b/tests/setfattr.test
new file mode 100755 (executable)
index 0000000..6a044ea
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+mkdir attrs
+touch attrs/file
+setfattr -n user.empty attrs/file
+setfattr -n user.data -v hello attrs/file
+setfattr -n user.delete-me -v hello attrs/file
+
+testing "-x" \
+    "setfattr -x user.delete-me attrs/file && getfattr attrs/file" \
+    "# file: attrs/file\nuser.data\nuser.empty\n\n" "" ""
+testing "-n" "setfattr -n user.new attrs/file && getfattr -d attrs/file" \
+    "# file: attrs/file\nuser.data=\"hello\"\nuser.empty\nuser.new\n\n" "" ""
+testing "-n -v" "setfattr -n user.new -v data attrs/file && getfattr -d attrs/file" \
+    "# file: attrs/file\nuser.data=\"hello\"\nuser.empty\nuser.new=\"data\"\n\n" "" ""
+
+rm -rf attrs
diff --git a/tests/sh.test b/tests/sh.test
new file mode 100755 (executable)
index 0000000..4f1ecb2
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+shellit()
+{
+  EVAL="bash -c" testing "$2" "$1 printf %s $2" "$3" "$4" "$5"
+}
+
+# $'' expands special chars but doesn't do so inside double quotes.
+
+shellit "" "\$'a\\tb'" "a\tb" "" ""
+shellit "" "\"\$'a\\tb'\"" '$'"'"'a\\tb'"'" "" "" 
+
+# $(( )) tests
+
+shellit 'x=1;' '$((-x))' '-1' '' ''
+shellit 'x=0;' '$((x++)); echo $x' '01\n' '' ''
+shellit 'x=0;' '$((++x))' '1' '' ''
+shellit 'x=0;' '$((~x))' '-1' '' ''
+shellit 'x=1;' '$((!x))' '0' '' ''
+shellit 'x=0;' '$((!x))' '1' '' ''
+shellit 'x=2;' '$((2*x))' '4' '' ''
+shellit 'x=9;' '$((x/4))' '2' '' ''
+shellit 'x=9;' '$((x%4))' '1' '' ''
+shellit 'x=4;' '$((x+2))' '6' '' ''
+shellit 'x=4;' '$((x-2))' '2' '' ''
+shellit 'x=4;' '$((1<<x))' '16' '' ''
+shellit 'x=4;' '$((x>>1))' '2' '' ''
+shellit '' '$((3**4))' '81' '' ''
+shellit '' '$((3<=4))' '1' '' ''
+shellit '' '$((3>=4))' '0' '' ''
+shellit '' '$((3<4))' '1' '' ''
+shellit '' '$((3>4))' '0' '' ''
+shellit '' '$((3==4))' '0' '' ''
+shellit '' '$((3!=4))' '1' '' ''
+shellit '' '$((6&4))' '4' '' ''
+shellit '' '$((4|2))' '6' '' ''
+shellit '' '$((6&&2))' '1' '' ''
+shellit '' '$((6||4))' '1' '' ''
+shellit '' '$((1?2:3))' '2' '' ''
+shellit 'x=2;' '$((x=3)); echo $x' '33\n' '' ''
+shellit 'x=2;' '$((x*=3)); echo $x' '66\n' '' ''
+shellit 'x=5;' '$((x/=2)); echo $x' '22\n' '' ''
+shellit 'x=9;' '$((x%=5)); echo $x' '44\n' '' ''
+shellit 'x=9;' '$((x-=3)); echo $x' '66\n' '' ''
+shellit 'x=3;' '$((x+=2)); echo $x' '55\n' '' ''
+shellit 'x=7;' '$((x&=13)); echo $x' '55\n' '' ''
+shellit 'x=5;' '$((x|=12)); echo $x' '1313\n' '' ''
+shellit 'x=5;' '$((x^=12)); echo $x' '99\n' '' ''
+shellit 'x=2;' '$((x<<=2)); echo $x' '88\n' '' ''
+shellit 'x=12;' '$((x>>=2)); echo $x' '33\n' '' ''
+shellit 'x=2;' '$((x++,5)); echo $x' '53\n' '' ''
index cd621fd..b4d4b75 100755 (executable)
@@ -6,22 +6,22 @@
 
 # These tests are based on RFC3174 which were based on FIPS PUB 180-1
 
-testing "sha1sum TEST1" \
+testing "TEST1" \
         "sha1sum" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
         "" "abc"
 
-testing "sha1sum TEST2" \
+testing "TEST2" \
         "sha1sum" \
         "84983e441c3bd26ebaae4aa1f95129e5e54670f1  -\n" \
         "" "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
 
-testing "sha1sum TEST3" \
+testing "TEST3" \
         'dd if=/dev/zero bs=1000 count=1000 2>/dev/null | tr \\0 a | sha1sum' \
         "34aa973cd4c4daa4f61eeb2bdbad27316534016f  -\n" \
         "" ""
 
-testing "sha1sum TEST4" \
+testing "TEST4" \
         'for i in `seq 1 10`; do echo -n 0123456701234567012345670123456701234567012345670123456701234567 ; done | sha1sum' \
         "dea356a2cddd90c7a7ecedc5ebb563934f460452  -\n" \
         "" ""
@@ -33,22 +33,22 @@ testing "sha1sum" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
         "" "abc"
 
-testing "sha1sum -" \
+testing "-" \
         "sha1sum -" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
         "" "abc"
 
-testing "sha1sum file" \
+testing "file" \
         "sha1sum file1" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n" \
         "" ""
 
-testing "sha1sum file1 file2" \
+testing "file1 file2" \
         "sha1sum file1 file2" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n589c22335a381f122d129225f5c0ba3056ed5811  file2\n" \
         "" ""
 
-testing "sha1sum file1 file2 -" \
+testing "file1 file2 -" \
         "sha1sum file1 file2 -" \
         "a9993e364706816aba3e25717850c26c9cd0d89d  file1\n589c22335a381f122d129225f5c0ba3056ed5811  file2\na9993e364706816aba3e25717850c26c9cd0d89d  -\n" \
         "" "abc"
index 2845e79..cda3db5 100755 (executable)
@@ -8,10 +8,10 @@
 # The basic tests.  These should work even with the small config.
 
 testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" ""
-testing "sort #2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
-testing "sort stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
-testing "sort numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" ""
-testing "sort reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+testing "#2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
+testing "numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \
        "point\nwook\npabst\naargh\nwalrus\n" ""
 
 # These tests require the full option set.
@@ -29,7 +29,7 @@ egg   1       2       papyrus
 
 # Sorting with keys
 
-testing "sort one key" "sort -k4,4 input" \
+testing "one key" "sort -k4,4 input" \
 "999   3       0       algebra
 egg    1       2       papyrus
 7      3       42      soup
@@ -41,7 +41,7 @@ egg   1       2       papyrus
 # at the whitespace), then the global fallback sort does an alpha sort on
 # the whole string (starting at the beginning of the line).
 
-testing "sort key range with numeric option" "sort -k2,3n input" \
+testing "key range with numeric option" "sort -k2,3n input" \
 "42    1       010     zoology
 42     1       3       woot
 egg    1       2       papyrus
@@ -52,7 +52,7 @@ egg   1       2       papyrus
 # Numeric sort on field 2 (again, ignore field 3 because it's numeric),
 # then do a _reversed_ alpha sort on the whole line as a tiebreaker.
 
-testing "sort key range with numeric option and global reverse" \
+testing "key range with numeric option and global reverse" \
 "sort -k2,3n -r input" \
 "egg   1       2       papyrus
 42     1       3       woot
@@ -64,7 +64,7 @@ testing "sort key range with numeric option and global reverse" \
 # Reversed numeric sort on field 2 (numeric ignores field 3), then
 # break ties with alpha sort on whole line.
 
-testing "sort key range with multiple options" "sort -k2,3rn input" \
+testing "key range with multiple options" "sort -k2,3rn input" \
 "7     3       42      soup
 999    3       0       algebra
 42     1       010     zoology
@@ -72,12 +72,12 @@ testing "sort key range with multiple options" "sort -k2,3rn input" \
 egg    1       2       papyrus
 " "$data" ""
 
-testing "sort key doesn't strip leading blanks, disables fallback global sort" \
+testing "key doesn't strip leading blanks, disables fallback global sort" \
 "sort -n -k2 -t ' '" " a \n 1 \n 2 \n" "" " 2 \n 1 \n a \n"
 
 # Test case contributed by Joey Hess:
 
-testing "sort key edge case with -t" "sort -n -k4 -t/" \
+testing "key edge case with -t" "sort -n -k4 -t/" \
 "/usr/lib/finish-install.d/1
 /usr/lib/finish-install.d/4
 /usr/lib/prebaseconfig.d/2
@@ -88,7 +88,7 @@ testing "sort key edge case with -t" "sort -n -k4 -t/" \
 /usr/lib/prebaseconfig.d/6
 "
 
-testing "sort -x" "sort -x" "010\na0\n 0c0\n" "" "a0\n010\n 0c0\n"
+testing "-x" "sort -x" "010\na0\n 0c0\n" "" "a0\n010\n 0c0\n"
 
 # Test that -f applies to key or fallback independently
 
@@ -99,7 +99,7 @@ testing "" "sort -f -k2,2" "A b b\na B C\na B a\n" "" "a B a\nA b b\na B C\n"
 optional SORT_FLOAT
 
 # not numbers < NaN < -infinity < numbers < +infinity
-testing "sort -g" "sort -g" \
+testing "-g" "sort -g" \
   "bork\nNaN\n-inf\n0.4\n1.222\n01.37\n2.1\n+infinity\n" "" \
   "01.37\n1.222\n2.1\n0.4\nNaN\nbork\n-inf\n+infinity\n"
 
index d86b025..2a27c09 100755 (executable)
@@ -7,24 +7,24 @@
 testing "split" "seq 1 12345 | split && ls xa[a-z] | wc -l" "13\n" "" ""
 rm xa[a-z]
 
-testing "split -" "seq 1 12345 | split - && ls xa[a-z] | wc -l" "13\n" "" ""
+testing "-" "seq 1 12345 | split - && ls xa[a-z] | wc -l" "13\n" "" ""
 rm xa[a-z]
 
 seq 1 12345 > file
-testing "split file" "split file && ls xa[a-z] | wc -l" "13\n" "" ""
+testing "file" "split file && ls xa[a-z] | wc -l" "13\n" "" ""
 rm xa[a-z]
 
-testing "split -l" "split file -l 10k && wc -l xab" "2105 xab\n" "" ""
+testing "-l" "split file -l 10k && wc -l xab" "2105 xab\n" "" ""
 rm xa[ab]
 
-testing "split suffix exhaustion" \
+testing "suffix exhaustion" \
   "split file -l 10 -a 1 walrus 2>/dev/null || ls walrus* | wc -l" "26\n" "" ""
 rm walrus*
 
-testing "split bytes" \
+testing "bytes" \
   "toybox seq 1 20000 | split -b 100 -a 3 - whang && ls whang* | wc -l && wc -c whangbpw" "1089\n94 whangbpw\n" "" ""
 
-testing "split reassembly" \
+testing "reassembly" \
   'diff -u <(ls whang* | sort | xargs cat) <(seq 1 20000) && echo yes' \
   "yes\n" "" ""
 
index 96f2531..5c095b4 100755 (executable)
@@ -8,19 +8,19 @@
 echo -e "one-A\none-B" > file1 
 echo -e "two-A\ntwo-B" > file2
 testing "tac" "tac && echo yes" "one-B\none-A\nyes\n" "" "one-A\none-B\n"
-testing "tac -" "tac - && echo yes" "one-B\none-A\nyes\n" "" "one-A\none-B\n"
-testing "tac file1 file2" "tac file1 file2" "one-B\none-A\ntwo-B\ntwo-A\n"  "" ""
-testing "tac - file"      "tac - file1"     "zero-B\nzero-A\none-B\none-A\n" "" "zero-A\nzero-B\n"
-testing "tac file -"      "tac file1 -"     "one-B\none-A\nzero-B\nzero-A\n" "" "zero-A\nzero-B\n"
+testing "-" "tac - && echo yes" "one-B\none-A\nyes\n" "" "one-A\none-B\n"
+testing "file1 file2" "tac file1 file2" "one-B\none-A\ntwo-B\ntwo-A\n"  "" ""
+testing "- file"      "tac - file1"     "zero-B\nzero-A\none-B\none-A\n" "" "zero-A\nzero-B\n"
+testing "file -"      "tac file1 -"     "one-B\none-A\nzero-B\nzero-A\n" "" "zero-A\nzero-B\n"
 
-testing "tac file1 notfound file2" \
+testing "file1 notfound file2" \
         "tac file1 notfound file2 2>stderr && echo ok ; tac stderr; rm stderr" \
         "one-B\none-A\ntwo-B\ntwo-A\ntac: notfound: No such file or directory\n" "" ""
 
-testing "tac no trailing newline" "tac -" "defabc\n" "" "abc\ndef"
+testing "no trailing newline" "tac -" "defabc\n" "" "abc\ndef"
 
 # xputs used by tac does not propagate this error condition properly. 
-#testing "tac > /dev/full" \
+#testing "> /dev/full" \
 #        "tac - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
 #        "tac: write: No space left on device\n" "" "zero\n"
 
index f4b8667..81dc871 100755 (executable)
@@ -7,38 +7,39 @@
 BIGTEST="one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n"
 echo -ne "$BIGTEST" > file1
 testing "tail" "tail && echo yes" "oneyes\n" "" "one"
-testing "tail file" "tail file1" \
+testing "file" "tail file1" \
        "two\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" ""
-testing "tail -n in bounds" "tail -n 3 file1" "nine\nten\neleven\n" "" ""
-testing "tail -n out of bounds" "tail -n 999 file1" "$BIGTEST" "" ""
-testing "tail -n+ in bounds" "tail -n +3 file1" \
+testing "-n in bounds" "tail -n 3 file1" "nine\nten\neleven\n" "" ""
+testing "-n out of bounds" "tail -n 999 file1" "$BIGTEST" "" ""
+testing "-n+ in bounds" "tail -n +3 file1" \
        "three\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" ""
-testing "tail -n+ outof bounds" "tail -n +999 file1" "" "" ""
-testing "tail -c in bounds" "tail -c 27 file1" \
+testing "-n+ outof bounds" "tail -n +999 file1" "" "" ""
+testing "-c in bounds" "tail -c 27 file1" \
        "even\neight\nnine\nten\neleven\n" "" ""
-testing "tail -c out of bounds" "tail -c 999 file1" "$BIGTEST" "" ""
-testing "tail -c+ in bounds" "tail -c +27 file1" \
+testing "-c out of bounds" "tail -c 999 file1" "$BIGTEST" "" ""
+testing "-c+ in bounds" "tail -c +27 file1" \
        "x\nseven\neight\nnine\nten\neleven\n" "" ""
-testing "tail -c+ out of bonds" "tail -c +999 file1" "" "" ""
+testing "-c+ out of bonds" "tail -c +999 file1" "" "" ""
+testing "-N" "tail -1 file1" "eleven\n" "" ""
 rm file1
 
-testing "tail stdin no trailing newline" "tail -n 1 - " "c" "" "a\nb\nc"
-testing "tail file no trailing newline" "tail -n 1 input" "c" "a\nb\nc" ""
+testing "stdin no trailing newline" "tail -n 1 - " "c" "" "a\nb\nc"
+testing "file no trailing newline" "tail -n 1 input" "c" "a\nb\nc" ""
 
 optional TAIL_SEEK
-testing "tail noseek -n in bounds" "tail -n 3" "nine\nten\neleven\n" \
+testing "noseek -n in bounds" "tail -n 3" "nine\nten\neleven\n" \
        "" "$BIGTEST"
-testing "tail noseek -n out of bounds" "tail -n 999" "$BIGTEST" "" "$BIGTEST"
-testing "tail noseek -n+ in bounds" "tail -n +3" \
+testing "noseek -n out of bounds" "tail -n 999" "$BIGTEST" "" "$BIGTEST"
+testing "noseek -n+ in bounds" "tail -n +3" \
        "three\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\n" "" \
        "$BIGTEST"
-testing "tail noseek -n+ outof bounds" "tail -n +999" "" "" "$BIGTEST"
-testing "tail noseek -c in bounds" "tail -c 27" \
+testing "noseek -n+ outof bounds" "tail -n +999" "" "" "$BIGTEST"
+testing "noseek -c in bounds" "tail -c 27" \
        "even\neight\nnine\nten\neleven\n" "" "$BIGTEST"
-testing "tail noseek -c out of bounds" "tail -c 999" "$BIGTEST" "" "$BIGTEST"
-testing "tail noseek -c+ in bounds" "tail -c +27" \
+testing "noseek -c out of bounds" "tail -c 999" "$BIGTEST" "" "$BIGTEST"
+testing "noseek -c+ in bounds" "tail -c +27" \
        "x\nseven\neight\nnine\nten\neleven\n" "" "$BIGTEST"
-testing "tail noseek -c+ out of bonds" "tail -c +999" "" "" "$BIGTEST"
+testing "noseek -c+ out of bonds" "tail -c +999" "" "" "$BIGTEST"
 
 makebigfile()
 {
@@ -55,7 +56,22 @@ makebigfile()
 }
 makebigfile > bigfile
 
-testing "tail -c 12345 -n 3 bigfile" "tail -c 12345 -n 3 bigfile | md5sum" \
+testing "-c 12345 -n 3 bigfile" "tail -c 12345 -n 3 bigfile | md5sum" \
   "347bbdcbad8a313f4dc7bd558c5bfcb8  -\n" "" ""
-testing "tail -n 3 -c 12345 bigfile" "tail -n 3 -c 12345 bigfile | md5sum" \
+testing "-n 3 -c 12345 bigfile" "tail -n 3 -c 12345 bigfile | md5sum" \
   "1698825a750288284ec3ba7d8a59f302  -\n" "" ""
+rm bigfile
+
+echo 111 > one
+testing "-f one" \
+  'tail -f one & sleep .25 ; echo two >> one; sleep .25; echo three >> one; sleep .25; kill $! >/dev/null' \
+  "111\ntwo\nthree\n" "" ""
+rm one
+
+echo uno > one
+echo dos > two
+echo tres > three
+testing "-f one two three" \
+  'tail -f one two three & sleep .25 ; echo more >> three ; echo also >> one; sleep .25; kill $! >/dev/null' \
+  "==> one <==\nuno\n\n==> two <==\ndos\n\n==> three <==\ntres\nmore\n\n==> one <==\nalso\n" "" ""
+rm one two three
index 5b10b01..80ba555 100755 (executable)
 #Creating  dir
 mkdir dir/dir1 -p
 echo "This is testdata" > dir/dir1/file
-testing "tar tgz - compession, extraction and data validation" "tar -czf dir.tgz dir/ && [ -e dir.tgz ] && echo 'yes'; rm -rf dir; tar -xf dir.tgz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tgz" "yes\nThis is testdata\n" "" ""
+testing "tgz - compession, extraction and data validation" "tar -czf dir.tgz dir/ && [ -e dir.tgz ] && echo 'yes'; rm -rf dir; tar -xf dir.tgz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tgz" "yes\nThis is testdata\n" "" ""
 
 #Creating  dir
 mkdir dir/dir1 -p
 echo "This is testdata" > dir/dir1/file
-testing "tar tar.gz - compession, extraction and data validation" "tar -czf dir.tar.gz dir/ && [ -e dir.tar.gz ] && echo 'yes'; rm -rf dir; tar -xf dir.tar.gz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tar.gz" "yes\nThis is testdata\n" "" ""
+testing "tar.gz - compession, extraction and data validation" "tar -czf dir.tar.gz dir/ && [ -e dir.tar.gz ] && echo 'yes'; rm -rf dir; tar -xf dir.tar.gz && [ -f dir/dir1/file ] && cat dir/dir1/file; rm -rf dir.tar.gz" "yes\nThis is testdata\n" "" ""
 
 #Creating  dir
 mkdir dir/dir1 -p
 echo "This is testdata" > dir/dir1/file
-testing "tar verbose compression" "tar -cvzf dir.tgz dir/; rm -rf dir.tgz" "dir/\ndir/dir1/\ndir/dir1/file\n" "" ""
+testing "verbose compression" "tar -cvzf dir.tgz dir/; rm -rf dir.tgz" "dir/\ndir/dir1/\ndir/dir1/file\n" "" ""
 rm -rf dir/
 
 #creating test file
 dd if=/dev/zero of=testFile ibs=4096 obs=4096 count=1000 2>/dev/null
-testing "tar - compession and extraction of a file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
+testing "- compession and extraction of a file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
 
 #creating empty test file
 touch testFile
-testing "tar - compession and extraction of a empty file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
+testing "- compession and extraction of a empty file" "tar -czf testFile.tgz testFile && [ -e testFile.tgz ] && echo 'yes'; rm -rf testFile; tar -xf testFile.tgz && [ -f testFile ] && echo 'yes'; rm -rf testFile.tgz" "yes\nyes\n" "" ""
 
 #Creating  dir
 mkdir dir/dir1 -p
 touch dir/dir1/file1 dir/dir1/file2 dir/dir1/file3 dir/dir1/file4
-testing "tar -t option" "tar -czf dir.tar.gz dir/; rm -rf dir; tar -tf dir.tar.gz | sort; rm -rf dir.tar.gz" "dir/\ndir/dir1/\ndir/dir1/file1\ndir/dir1/file2\ndir/dir1/file3\ndir/dir1/file4\n" "" ""
+testing "-t option" "tar -czf dir.tar.gz dir/; rm -rf dir; tar -tf dir.tar.gz | sort; rm -rf dir.tar.gz" "dir/\ndir/dir1/\ndir/dir1/file1\ndir/dir1/file2\ndir/dir1/file3\ndir/dir1/file4\n" "" ""
 rm -rf dir/
 
 #Creating nested directory 
@@ -42,20 +42,20 @@ mkdir -p dir/dir1 dir/dir2 dir/dir3 dir/dir4
 echo "This is testdata" > dir/dir1/file; echo "Dont exclude me" >  dir/dir3/file1 ; 
 echo "Exclude me" >  dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "Dont" > dir/dir2/file1
 echo -ne "dir/dir4\ndir/dir3/file2\n" > exclude_file
-testing "Creating tar with files excluded : -X" "tar -czf dir.tgz dir/ -X exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\n" "" ""
+testing "create with files excluded : -X" "tar -czf dir.tgz dir/ -X exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\n" "" ""
 rm -rf exclude_file
 
 #Creating nested directory
 mkdir dir/dir1 -p ;  mkdir dir/dir2 ;  mkdir dir/dir3 ; mkdir dir/dir4
 echo "This is testdata" > dir/dir1/file
 echo "Dont exclude me" >  dir/dir3/file1 ; echo "Exclude me" >  dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "Dont" > dir/dir2/file1
-testing "tar with pattern --exclude" "tar --exclude=dir/dir3/* -czf dir.tgz dir/ ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\n" "" ""
+testing "with pattern --exclude" "tar --exclude=dir/dir3/* -czf dir.tgz dir/ ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\n" "" ""
 
 #Creating directory to be compressed
 mkdir dir/dir1 -p
 echo "This is testdata" > dir/dir1/file
 mkdir temp
-testing "Extraction with -C Dir" "tar -czf dir.tgz dir/ ;rm -rf dir ;tar -xf dir.tgz -C temp/ ; [ -e temp/dir ] && echo 'yes' ; rm -rf dir dir.tgz" "yes\n" "" ""
+testing "extract with -C Dir" "tar -czf dir.tgz dir/ ;rm -rf dir ;tar -xf dir.tgz -C temp/ ; [ -e temp/dir ] && echo 'yes' ; rm -rf dir dir.tgz" "yes\n" "" ""
 rm -rf temp
 
 #Creating nested directory 
@@ -63,28 +63,28 @@ mkdir dir/dir1 -p ;  mkdir dir/dir2 ;  mkdir dir/dir3 ; mkdir dir/dir4 ; mkdir t
 echo "dir1/file" > dir/dir1/file ; echo "temp_dir/file" > temp_dir/file 
 echo "dir3/file1" >  dir/dir3/file1 ; echo "dir3/file2" >  dir/dir3/file2 ; echo "YO" > dir/dir4/file1 ; echo "Hello" >dir/dir4/file2; echo "dir2/file1" > dir/dir2/file1
 echo  "temp_dir/file" > exclude_file
-testing "Creating tar extra files/directory included : -T" "tar -czf dir.tgz dir/ -T exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\ndir/dir3/file2\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\ntemp_dir/file\n" "" ""
+testing "create with extra files/directory included : -T" "tar -czf dir.tgz dir/ -T exclude_file ; rm -rf dir ; tar -tf dir.tgz | sort; rm -rf dir.tgz " "dir/\ndir/dir1/\ndir/dir1/file\ndir/dir2/\ndir/dir2/file1\ndir/dir3/\ndir/dir3/file1\ndir/dir3/file2\ndir/dir4/\ndir/dir4/file1\ndir/dir4/file2\ntemp_dir/file\n" "" ""
 rm -rf exclude_file
 rm -rf temp_dir
 
 #Creating dir
 mkdir dir/dir1 -p 
 echo "Inside dir/dir1" > dir/dir1/file ; echo "Hello Inside dir" > dir/file
-testing "Extraction on STDOUT : -O" " tar -czf dir.tgz dir/ ; rm -rf dir ; tar -xf dir.tgz -O ; [ -e 'Inside dir/dir1/\nHello Inside dir\n' ] && echo 'yes'; rm -rf dir.tgz "  "" "" ""
+testing "extract to STDOUT : -O" " tar -czf dir.tgz dir/ ; rm -rf dir ; tar -xf dir.tgz -O ; [ -e 'Inside dir/dir1/\nHello Inside dir\n' ] && echo 'yes'; rm -rf dir.tgz "  "" "" ""
 
 #Creating short filename
 f="filename_with_100_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 echo "This is testdata" > $f
-testing "tar shortname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\n" "" ""
+testing "shortname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\n" "" ""
 
 #Creating long filename
 f="filename_with_101_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 echo "This is testdata" > $f
-testing "tar longname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\nLongLink\n" "" ""
+testing "longname filename" "tar -cf testFile.tar $f && [ -e testFile.tar ] && echo 'yes'; rm -f $f; tar -xf testFile.tar && [ -f $f ] && cat $f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -f $f" "yes\nThis is testdata\nLongLink\n" "" ""
 
 #Creating long pathname
 d="dirname_with_50_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 f="filename_with_50_chars_xxxxxxxxxxxxxxxxxxxxxxxxxxx"
 mkdir $d
 echo "This is testdata" > $d/$f
-testing "tar longname pathname" "tar -cf testFile.tar $d/$f && [ -e testFile.tar ] && echo 'yes'; rm -rf $d; tar -xf testFile.tar && [ -f $d/$f ] && cat $d/$f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -rf $d" "yes\nThis is testdata\nLongLink\n" "" ""
+testing "longname pathname" "tar -cf testFile.tar $d/$f && [ -e testFile.tar ] && echo 'yes'; rm -rf $d; tar -xf testFile.tar && [ -f $d/$f ] && cat $d/$f && strings testFile.tar | grep -o LongLink; rm -f testFile.tar; rm -rf $d" "yes\nThis is testdata\nLongLink\n" "" ""
index f501157..40a9086 100755 (executable)
@@ -25,17 +25,17 @@ type_test()
   printf "%s" $result
 }
 
-testing "test -b" "type_test -b" "" "" ""
-testing "test -c" "type_test -c" "L" "" ""
-testing "test -d" "type_test -d" "d" "" ""
-testing "test -f" "type_test -f" "fs" "" ""
-testing "test -h" "type_test -h" "L" "" ""
-testing "test -L" "type_test -L" "L" "" ""
-testing "test -s" "type_test -s" "ds" "" ""
-testing "test -S" "type_test -S" "" "" ""
-testing "test -p" "type_test -p" "p" "" ""
-testing "test -e" "type_test -e" "dfLsp" "" ""
-testing "test ! -e" "type_test ! -e" "n" "" ""
+testing "-b" "type_test -b" "" "" ""
+testing "-c" "type_test -c" "L" "" ""
+testing "-d" "type_test -d" "d" "" ""
+testing "-f" "type_test -f" "fs" "" ""
+testing "-h" "type_test -h" "L" "" ""
+testing "-L" "type_test -L" "L" "" ""
+testing "-s" "type_test -s" "ds" "" ""
+testing "-S" "type_test -S" "" "" ""
+testing "-p" "type_test -p" "p" "" ""
+testing "-e" "type_test -e" "dfLsp" "" ""
+testing "! -e" "type_test ! -e" "n" "" ""
 
 rm f L s p
 rmdir d
@@ -43,10 +43,10 @@ rmdir d
 # TODO: Test rwx gu t
 
 testing "test" "test "" || test a && echo yes" "yes\n" "" ""
-testing "test -n" "test -n "" || test -n a && echo yes" "yes\n" "" ""
-testing "test -z" "test -n a || test -n "" && echo yes" "yes\n" "" ""
-testing "test a = b" "test a = b || test "" = "" && echo yes" "yes\n" "" ""
-testing "test a != b" "test "" != "" || test a = b && echo yes" "yes\n" "" ""
+testing "-n" "test -n "" || test -n a && echo yes" "yes\n" "" ""
+testing "-z" "test -n a || test -n "" && echo yes" "yes\n" "" ""
+testing "a = b" "test a = b || test "" = "" && echo yes" "yes\n" "" ""
+testing "a != b" "test "" != "" || test a = b && echo yes" "yes\n" "" ""
 
 arith_test()
 {
@@ -55,12 +55,12 @@ arith_test()
   test -3 $1 -5 && echo g
 }
 
-testing "test -eq" "arith_test -eq" "e\n" "" ""
-testing "test -ne" "arith_test -ne" "l\ng\n" "" ""
-testing "test -gt" "arith_test -gt" "g\n" "" ""
-testing "test -ge" "arith_test -ge" "e\ng\n" "" ""
-testing "test -lt" "arith_test -lt" "l\n" "" ""
-testing "test -le" "arith_test -le" "l\ne\n" "" ""
+testing "-eq" "arith_test -eq" "e\n" "" ""
+testing "-ne" "arith_test -ne" "l\ng\n" "" ""
+testing "-gt" "arith_test -gt" "g\n" "" ""
+testing "-ge" "arith_test -ge" "e\ng\n" "" ""
+testing "-lt" "arith_test -lt" "l\n" "" ""
+testing "-le" "arith_test -le" "l\ne\n" "" ""
 
 # test ! = -o a
 # test ! \( = -o a \)
index a7533b7..0f1bfb7 100755 (executable)
@@ -4,16 +4,16 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "human_readable l 1024" "test_human_readable 123456789" "118M\n" "" ""
-testing "human_readable l 1000" "test_human_readable -i 123456789" "123M\n" "" ""
-testing "human_readable s 1024" "test_human_readable 5675" "5.5K\n" "" ""
-testing "human_readable s 1000" "test_human_readable -i 5675" "5.6k\n" "" ""
+testing "l 1024" "test_human_readable 123456789" "118M\n" "" ""
+testing "l 1000" "test_human_readable -i 123456789" "123M\n" "" ""
+testing "s 1024" "test_human_readable 5675" "5.5K\n" "" ""
+testing "s 1000" "test_human_readable -i 5675" "5.6k\n" "" ""
 
 # An example input where we give a better result than coreutils.
 # 267350/1024=261.08. We say 261K and coreutils says 262K.
-testing "human_readable" "test_human_readable 267350" "261K\n" "" ""
+testing "test_human_readable" "test_human_readable 267350" "261K\n" "" ""
 
-testing "human_readable -b" "test_human_readable -b 123" "123B\n" "" ""
-testing "human_readable -b" "test_human_readable -b 123456789" "118M\n" "" ""
-testing "human_readable -s" "test_human_readable -s 123456789" "118 M\n" "" ""
-testing "human_readable -bs" "test_human_readable -bs 123456789" "118 M\n" "" ""
+testing "-b" "test_human_readable -b 123" "123B\n" "" ""
+testing "-b" "test_human_readable -b 123456789" "118M\n" "" ""
+testing "-s" "test_human_readable -s 123456789" "118 M\n" "" ""
+testing "-bs" "test_human_readable -bs 123456789" "118 M\n" "" ""
diff --git a/tests/top.test b/tests/top.test
new file mode 100755 (executable)
index 0000000..11eea33
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "batch termination" "top -b -n1 | tail -c 1" "\n" "" ""
index d6430d0..d193d7c 100755 (executable)
@@ -5,26 +5,38 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 testing "touch" "touch walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
-testing "touch 1 2 3" "touch one two three && rm one two three && echo yes" "yes\n" \
+testing "1 2 3" "touch one two three && rm one two three && echo yes" "yes\n" \
   "" ""
-testing "touch -c" "touch -c walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
-testing "touch -c missing" "touch -c warrus && [ ! -e warrus ] && echo yes" \
+testing "-c" "touch -c walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
+testing "-c missing" "touch -c warrus && [ ! -e warrus ] && echo yes" \
   "yes\n" "" ""
 
-testing "touch -d" \
+testing "-t" \
+  "touch -t 201201231234 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \
+  "20120123-123400.000000000\n" "" ""
+
+testing "-t seconds" \
+  "touch -t 201201231234.56 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \
+  "20120123-123456.000000000\n" "" ""
+
+testing "-t nanoseconds" \
+  "touch -t 201201231234.56123456789 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \
+  "20120123-123456.123456789\n" "" ""
+
+testing "-d" \
   "touch -d 2009-02-13T23:31:30Z walrus && date -r walrus +%s" \
   "1234567890\n" "" ""
 
-testing "touch -d nanoseconds" \
+testing "-d nanoseconds" \
   "touch -d 2009-02-13T23:31:30.123456789Z walrus && date -r walrus +%s.%N" \
   "1234567890.123456789\n" "" ""
 
-testing "touch -r" \
+testing "-r" \
   "touch -r walrus walrus2 && date -r walrus2 +%s.%N" \
    "1234567890.123456789\n" "" ""
 
-#testing "touch -a"
-#testing "touch -m"
-#testing "touch -am"
-#testing "touch -t"
+#testing "-a"
+#testing "-m"
+#testing "-am"
+#testing "-t"
 rm walrus walrus2
index ee2b2bb..1d19aa1 100755 (executable)
@@ -5,24 +5,24 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 SIZE='&& stat -c %s freep'
-testing "truncate 0" "truncate -s 0 freep $SIZE" "0\n" "" ""
-testing "truncate 12345" "truncate -s 12345 freep $SIZE" "12345\n" "" ""
-testing "truncate 1m" "truncate -s 1m freep $SIZE" "1048576\n" "" ""
-testing "truncate is sparse" "truncate -s 1g freep && stat -c %b freep" \
+testing "0" "truncate -s 0 freep $SIZE" "0\n" "" ""
+testing "12345" "truncate -s 12345 freep $SIZE" "12345\n" "" ""
+testing "1m" "truncate -s 1m freep $SIZE" "1048576\n" "" ""
+testing "is sparse" "truncate -s 1g freep && stat -c %b freep" \
        "0\n" "" ""
-testing "truncate +" "truncate -s 1k freep && truncate -s +1k freep $SIZE" \
+testing "+" "truncate -s 1k freep && truncate -s +1k freep $SIZE" \
        "2048\n" "" ""
-testing "truncate -" "truncate -s 4k freep && truncate -s -1k freep $SIZE" \
+testing "-" "truncate -s 4k freep && truncate -s -1k freep $SIZE" \
        "3072\n" "" ""
-testing "truncate < hit" \
+testing "< hit" \
        "truncate -s 5k freep && truncate -s \<4k freep $SIZE" "4096\n" "" ""
-testing "truncate < miss" \
+testing "< miss" \
        "truncate -s 4k freep && truncate -s \<6k freep $SIZE" "4096\n" "" ""
-testing "truncate > hit" \
+testing "> hit" \
        "truncate -s 3k freep && truncate -s \>4k freep $SIZE" "4096\n" "" ""
-testing "truncate > miss" \
+testing "> miss" \
        "truncate -s 4k freep && truncate -s \>2k freep $SIZE" "4096\n" "" ""
-testing "truncate /" "truncate -s 7k freep && truncate -s /3k freep $SIZE" \
+testing "/" "truncate -s 7k freep && truncate -s /3k freep $SIZE" \
        "6144\n" "" ""
-testing "truncate %" "truncate -s 7k freep && truncate -s %3k freep $SIZE" \
+testing "%" "truncate -s 7k freep && truncate -s %3k freep $SIZE" \
        "9216\n" "" ""
index 6ffe707..d0b68ee 100755 (executable)
@@ -21,83 +21,83 @@ arg="&>/dev/null"
 pass=`echo -ne 'password\npassword\n'`
 
 user="toyTestUser"
-testing "adduser user_name (text)" "useradd $user $arg || 
+testing "(text)" "useradd $user $arg || 
    grep '^$user:' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 user="toy1Test2User3"
-testing "adduser user_name (alphanumeric)" "useradd $user $arg ||
+testing "(alphanumeric)" "useradd $user $arg ||
    grep '^$user:' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 user="987654321"
-testing "adduser user_name (numeric)" "useradd $user $arg ||
+testing "(numeric)" "useradd $user $arg ||
    grep '^$user:' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 user="toy.1Test-2User_3"
-testing "adduser user_name (with ./-/_)" "useradd $user $arg ||
+testing "(with ./-/_)" "useradd $user $arg ||
    grep '^$user:' /etc/passwd $arg && [ -d /home/$user ] && 
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 # 70 characters long string; hereafter, we will use it as per our need.
 user="abcdefghijklmnopqrstuvwxyz123456789abcdefghijklmnopqrstuvwxyz123456789"
-testing "adduser user_name (long string)" "useradd $user $arg ||
+testing "(long string)" "useradd $user $arg ||
    grep '^$user:' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 user="toyTestUser"
-testing "adduser user_name with dir" "useradd -h $PWD/dir $user $arg ||
+testing "dir" "useradd -h $PWD/dir $user $arg ||
    grep '^$user:.*dir' /etc/passwd $arg && [ -d $PWD/dir ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 rm -rf $PWD/dir
 
 gecos="aaa,bbb,ccc,ddd,eee"
-testing "adduser user_name with gecos" "useradd -g '$gecos' $user $arg ||
+testing "gecos" "useradd -g '$gecos' $user $arg ||
    grep '^$user:.*$gecos' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 shl="/bin/sh"
-testing "adduser user_name with shell" "useradd -s $shl $user $arg ||
+testing "shell" "useradd -s $shl $user $arg ||
    grep '^$user:.*$shl$' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
 g_name="root"
 g_id=`grep $g_name':.*:.*' /etc/group | cut -d : -f 3`
-testing "adduser user_name with group" "useradd -G $g_name $user $arg ||
+testing "group" "useradd -G $g_name $user $arg ||
    grep '^$user:.*:.*:$g_id:.*' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
-testing "adduser user_name (system user)" "useradd -S $user $arg ||
+testing "(system user)" "useradd -S $user $arg ||
    grep '^$user:.*:.*:.*' /etc/passwd $arg && [ ! -e /home/$user ] && 
    echo 'yes'" "yes\n" "" "$pass"   
 userdel -r $user $arg
 
-testing "adduser user_name with -D" "useradd -D $user $arg ||
+testing "-D" "useradd -D $user $arg ||
    grep '^$user:.*:.*:.*' /etc/passwd $arg && [ -d /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
-testing "adduser user_name with -H" "useradd -H $user $arg ||
+testing "-H" "useradd -H $user $arg ||
    grep '^$user:.*:.*:.*' /etc/passwd $arg && [ ! -e /home/$user ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
-testing "adduser user_name with dir and -H" "useradd -H -h $PWD/dir $user $arg ||
+testing "dir and -H" "useradd -H -h $PWD/dir $user $arg ||
    grep '^$user:.*dir' /etc/passwd $arg && [ ! -e $PWD/dir ] &&
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
 
-testing "adduser user_name with user_id" "useradd -u 49999 $user $arg ||
+testing "-u" "useradd -u 49999 $user $arg ||
    grep '^$user:x:49999:.*' /etc/passwd $arg && [ -d /home/$user ] && 
    echo 'yes'" "yes\n" "" "$pass"
 userdel -r $user $arg
index b9d7d01..2c3bba6 100755 (executable)
@@ -4,24 +4,24 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "uudecode uu empty file" "uudecode -o /dev/stdout && echo yes" \
+testing "uu empty file" "uudecode -o /dev/stdout && echo yes" \
        "yes\n" "" "begin 744 test\n\`\nend\n"
-testing "uudecode uu 1-char" "uudecode -o /dev/stdout" "a" "" \
+testing "uu 1-char" "uudecode -o /dev/stdout" "a" "" \
        "begin 744 test\n!80  \n\`\nend\n"
-testing "uudecode uu 2-char" "uudecode -o /dev/stdout" "ab" "" \
+testing "uu 2-char" "uudecode -o /dev/stdout" "ab" "" \
        "begin 744 test\n\"86( \n\`\nend\n"
-testing "uudecode uu 3-char" "uudecode -o /dev/stdout" "abc" "" \
+testing "uu 3-char" "uudecode -o /dev/stdout" "abc" "" \
        "begin 744 test\n#86)C\n\`\nend\n" 
 
-testing "uudecode b64 empty file" "uudecode -o /dev/stdout && echo yes" \
+testing "b64 empty file" "uudecode -o /dev/stdout && echo yes" \
         "yes\n" "" "begin-base64 744 test\n====\n" 
-testing "uudecode b64 1-char" "uudecode -o /dev/stdout" "a" "" \
+testing "b64 1-char" "uudecode -o /dev/stdout" "a" "" \
        "begin-base64 744 test\nYQ==\n====\n"
-testing "uudecode b64 2-char" "uudecode -o /dev/stdout" "ab" "" \
+testing "b64 2-char" "uudecode -o /dev/stdout" "ab" "" \
        "begin-base64 744 test\nYWI=\n====\n"
-testing "uudecode b64 3-char" "uudecode -o /dev/stdout" "abc" "" \
+testing "b64 3-char" "uudecode -o /dev/stdout" "abc" "" \
        "begin-base64 744 test\nYWJj\n====\n"
 
-testing "uudecode filename" "uudecode && echo -ne 'abc' | cmp uudecode-fn-test /dev/stdin && echo -ne yes && rm uudecode-fn-test" \
+testing "filename" "uudecode && echo -ne 'abc' | cmp uudecode-fn-test /dev/stdin && echo -ne yes && rm uudecode-fn-test" \
        "yes" "" "begin-base64 744 uudecode-fn-test\nYWJj\n====\n"
 
index 7c19faa..b2a61dc 100755 (executable)
@@ -4,23 +4,23 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
-testing "uuencode not enough args [fail]" "uuencode 2>/dev/null" "" "" ""
+testing "not enough args [fail]" "uuencode 2>/dev/null" "" "" ""
 
-testing "uuencode uu empty file" "uuencode test" \
+testing "uu empty file" "uuencode test" \
        "begin 744 test\nend\n" "" "" 
-testing "uuencode uu 1-char" "uuencode test" \
+testing "uu 1-char" "uuencode test" \
        "begin 744 test\n!80\`\`\nend\n" "" "a" 
-testing "uuencode uu 2-char" "uuencode test" \
+testing "uu 2-char" "uuencode test" \
        "begin 744 test\n\"86(\`\nend\n" "" "ab" 
-testing "uuencode uu 3-char" "uuencode test" \
+testing "uu 3-char" "uuencode test" \
        "begin 744 test\n#86)C\nend\n" "" "abc" 
 
-testing "uuencode b64 empty file" "uuencode -m test" \
+testing "b64 empty file" "uuencode -m test" \
        "begin-base64 744 test\n====\n" "" "" 
-testing "uuencode b64 1-char" "uuencode -m test" \
+testing "b64 1-char" "uuencode -m test" \
        "begin-base64 744 test\nYQ==\n====\n" "" "a" 
-testing "uuencode b64 2-char" "uuencode -m test" \
+testing "b64 2-char" "uuencode -m test" \
        "begin-base64 744 test\nYWI=\n====\n" "" "ab" 
-testing "uuencode b64 3-char" "uuencode -m test" \
+testing "b64 3-char" "uuencode -m test" \
        "begin-base64 744 test\nYWJj\n====\n" "" "abc" 
 
index 4089132..d227b9c 100755 (executable)
@@ -12,16 +12,14 @@ lines
 EOF
 
 testing "wc" "wc >/dev/null && echo yes" "yes\n" "" ""
-testing "wc empty file" "wc" "0 0 0\n" "" ""
-testing "wc standard input" "wc" "1 3 5\n" "" "a b\nc"
-testing "wc -c" "wc -c file1" "26 file1\n" "" ""
-testing "wc -l" "wc -l file1" "4 file1\n" "" ""
-testing "wc -w" "wc -w file1" "5 file1\n" "" ""
-testing "wc format" "wc file1" "4 5 26 file1\n" "" ""
-testing "wc multiple files" "wc input - file1" \
-        "1 2 3 input\n0 2 3 -\n4 5 26 file1\n5 9 32 total\n" "a\nb" "a b"
-
-optional TOYBOX_I18N
+testing "empty file" "wc" "      0       0       0\n" "" ""
+testing "standard input" "wc" "      1       3       5\n" "" "a b\nc"
+testing "-c" "wc -c file1" "26 file1\n" "" ""
+testing "-l" "wc -l file1" "4 file1\n" "" ""
+testing "-w" "wc -w file1" "5 file1\n" "" ""
+NOSPACE=1 testing "format" "wc file1" " 4 5 26 file1\n" "" ""
+testing "multiple files" "wc input - file1" \
+        "      1       2       3 input\n      0       2       3 -\n      4       5      26 file1\n      5       9      32 total\n" "a\nb" "a b"
 
 #Tests for wc -m
 if printf "%s" "$LANG" | grep -q UTF-8
@@ -32,14 +30,15 @@ for i in $(seq 1 8192)
 do
   printf "ü" >> file1
 done
-testing "wc -m" "wc -m file1" "8193 file1\n" "" ""
+testing "-m" "wc -m file1" "8193 file1\n" "" ""
+testing "-m 2" 'cat "$FILES/utf8/test2.txt" | wc -m' "169\n" "" ""
 printf " " > file1
 for i in $(seq 1 8192)
 do
   printf "ü" >> file1
 done
-testing "wc -m (invalid chars)" "wc -m file1" "8193 file1\n" "" ""
-testing "wc -mlw" "wc -mlw input" "1 2 11 input\n" "hello, ä¸–ç•Œ!\n" ""
+testing "-m (invalid chars)" "wc -m file1" "8193 file1\n" "" ""
+NOSPACE=1 testing "-mlw" "wc -mlw input" " 1 2 11 input\n" "hello, ä¸–ç•Œ!\n" ""
 
 else
 printf "skipping tests for wc -m"
index e49445d..966bc5d 100755 (executable)
@@ -5,31 +5,29 @@
 #testing "name" "command" "result" "infile" "stdin"
 
 testing "xargs" "xargs && echo yes" "hello\nyes\n" "" "hello"
-testing "xargs spaces" "xargs" \
+testing "spaces" "xargs" \
        "one two three four\n" "" "one two\tthree  \nfour\n\n"
 
-testing "xargs -n 0" "xargs -n 0 2>/dev/null || echo ok" "ok\n" \
+testing "-n 0" "xargs -n 0 2>/dev/null || echo ok" "ok\n" \
        "" "one \ntwo\n three"
-testing "xargs -n 2" "xargs -n 2" "one two\nthree\n" "" "one \ntwo\n three"
-testing "xargs -n exact match" "xargs -n 3" "one two three\n" "" "one two three"
+testing "-n 2" "xargs -n 2" "one two\nthree\n" "" "one \ntwo\n three"
+testing "-n exact match" "xargs -n 3" "one two three\n" "" "one two three"
 testing "xargs2" "xargs -n2" "one two\nthree four\nfive\n" "" \
        "one two three four five"
-testing "xargs -s too long" "xargs -s 9 echo 2>/dev/null || echo ok" \
+testing "-s too long" "xargs -s 9 echo 2>/dev/null || echo ok" \
        "one\ntwo\nok\n" "" "one two three"
-testing "xargs -s 13" "xargs -s 13 echo" "one two\nthree\n" "" "one \ntwo\n three"
-testing "xargs -s 12" "xargs -s 12 echo" "one\ntwo\nthree\n" "" "one \ntwo\n three"
+testing "-s 13" "xargs -s 13 echo" "one two\nthree\n" "" "one \ntwo\n three"
+testing "-s 12" "xargs -s 12 echo" "one\ntwo\nthree\n" "" "one \ntwo\n three"
 
 touch one two three
-testing "xargs command -opt" "xargs -n2 ls -1" "one\ntwo\nthree\n" "" \
+testing "command -opt" "xargs -n2 ls -1" "one\ntwo\nthree\n" "" \
        "one two three"
 rm one two three
 
-exit
-
-testing "xargs -n exact match"
-testing "xargs -s exact match"
-testing "xargs -s 0"
-testing "xargs -s impossible"
+#testing "-n exact match"
+#testing "-s exact match"
+#testing "-s 0"
+#testing "-s impossible"
 
 # xargs command_not_found - returns 127
 # xargs false - returns 1
index 68f52be..5f43b8b 100644 (file)
@@ -9,38 +9,40 @@ echo -n > file2
 
 # Note that the xxd in vim-common on Ubuntu 14 uses %07x for the file offset.
 
-testing "xxd file1" "xxd file1" \
+testing "file1" "xxd file1" \
     "00000000: 7468 6973 2069 7320 736f 6d65 2074 6578  this is some tex\n00000010: 740a                                     t.\n" \
     "" ""
-testing "xxd file1 -l" "xxd -l 2 file1" \
+testing "file1 -l" "xxd -l 2 file1" \
     "00000000: 7468                                     th\n" \
     "" ""
-testing "xxd file2" "xxd file2" "" "" ""
-testing "xxd -" "xxd -" \
+testing "file2" "xxd file2" "" "" ""
+testing "-" "xxd -" \
     "00000000: 6865 6c6c 6f                             hello\n" "" "hello"
 testing "xxd" "xxd" \
     "00000000: 776f 726c 64                             world\n" "" "world"
-testing "xxd -c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
+testing "-c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
     "00000000: 74686973 20697320  this is \n00000008: 736f6d65 20746578  some tex\n00000010: 740a               t.\n" "" ""
-testing "xxd -c 8 -g 3 file1" "xxd -c 8 -g 3 file1" \
+testing "-c 8 -g 3 file1" "xxd -c 8 -g 3 file1" \
     "00000000: 746869 732069 7320 this is \n00000008: 736f6d 652074 6578 some tex\n00000010: 740a               t.\n" "" ""
 
-testing "xxd -p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""
+testing "-p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""
 
-testing "xxd -r" "xxd file1 | xxd -r" "this is some text\n" "" ""
-testing "xxd -r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""
+testing "-s" "xxd -s 13 file1" "0000000d: 7465 7874 0a                             text.\n" "" ""
 
-testing "xxd -r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""
+testing "-r" "xxd file1 | xxd -r" "this is some text\n" "" ""
+testing "-r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""
+
+testing "-r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""
 
 # -r will only read -c bytes (default 16) before skipping to the next line,
 # ignoring the rest.
-testing "xxd -r long" \
+testing "-r long" \
     "echo '0000: 40404040404040404040404040404040404040404040404040404040404040404040404040404040' | xxd -r -" \
     "@@@@@@@@@@@@@@@@" "" ""
 
 # -r -p ignores the usual -p 30-byte/line limit (or any limit set by -c) and
 # will take as many bytes as you give it.
-testing "xxd -r -p long" \
+testing "-r -p long" \
     "echo '40404040404040404040404040404040404040404040404040404040404040404040404040404040' | xxd -r -p -" \
     "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" "" ""
 
index e82ca88..4aaf35f 100755 (executable)
@@ -11,7 +11,7 @@ tar -cJf file.xz file
 # Get system xzcat
 xzcatExe=`which xzcat`
 $xzcatExe file.xz > xzcatOut
-testing "xzcat - decompresses a single file" "xzcat file.xz > Tempfile && echo "yes"; diff Tempfile xzcatOut && echo "yes"; rm -rf file* xzcatOut Tempfile" "yes\nyes\n" "" ""
+testing "- decompresses a single file" "xzcat file.xz > Tempfile && echo "yes"; diff Tempfile xzcatOut && echo "yes"; rm -rf file* xzcatOut Tempfile" "yes\nyes\n" "" ""
 
 #testing "name" "command" "result" "infile" "stdin"
 echo "hello" > file1
@@ -23,4 +23,4 @@ tar -cJf file3.xz file3
 # Get system xzcat
 xzcatExe=`which xzcat`
 $xzcatExe file1.xz file2.xz file3.xz > xzcatOut
-testing "xzcat - decompresses multiple files" "xzcat file1.xz file2.xz file3.xz > Tempfile && echo "yes" ; diff Tempfile xzcatOut && echo "yes"; rm -rf file* xzcatOut Tempfile " "yes\nyes\n" "" ""
+testing "- decompresses multiple files" "xzcat file1.xz file2.xz file3.xz > Tempfile && echo "yes" ; diff Tempfile xzcatOut && echo "yes"; rm -rf file* xzcatOut Tempfile " "yes\nyes\n" "" ""
index c1297c9..ccd472c 100755 (executable)
@@ -11,7 +11,7 @@ tar -czf file.gz file
 # Get system zcat
 zcatExe=`which zcat`
 $zcatExe file.gz > zcatOut
-testing "zcat - decompresses a single file" "zcat file.gz > Tempfile && echo "yes"; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile" "yes\nyes\n" "" ""
+testing "- decompresses a single file" "zcat file.gz > Tempfile && echo "yes"; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile" "yes\nyes\n" "" ""
 
 #testing "name" "command" "result" "infile" "stdin"
 echo "hello" > file1
@@ -23,4 +23,4 @@ tar -czf file3.gz file3
 # Get system zcat
 zcatExe=`which zcat`
 $zcatExe file1.gz file2.gz file3.gz > zcatOut
-testing "zcat - decompresses multiple files" "zcat file1.gz file2.gz file3.gz > Tempfile && echo "yes" ; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile " "yes\nyes\n" "" ""
+testing "- decompresses multiple files" "zcat file1.gz file2.gz file3.gz > Tempfile && echo "yes" ; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile " "yes\nyes\n" "" ""
diff --git a/toys.h b/toys.h
index 8a29730..0ca270f 100644 (file)
--- a/toys.h
+++ b/toys.h
 #include <wctype.h>
 
 // LSB 4.1 headers
+#ifndef __APPLE__
 #include <pty.h>
-#include <sys/ioctl.h>
 #include <sys/statfs.h>
 #include <sys/sysinfo.h>
+#endif
+#include <sys/ioctl.h>
 
 #include "lib/lib.h"
 #include "lib/lsm.h"
@@ -113,6 +115,7 @@ extern struct toy_context {
 
   // This is at the end so toy_init() doesn't zero it.
   jmp_buf *rebound;        // longjmp here instead of exit when do_rebound set
+  struct arg_list *xexit;  // atexit() functions for xexit(), set by sigatexit()
   void *stacktop;          // nested toy_exec() call count, or 0 if vforked
 } toys;
 
index efb1e44..9675d9f 100644 (file)
@@ -54,7 +54,8 @@ static void add_property(char *name, char *value, void *unused)
 }
 
 // Needed to supress extraneous "Loaded property_contexts from" message
-int selinux_log_callback(int type, const char *fmt, ...) {
+static int selinux_log_callback_local(int type, const char *fmt, ...)
+{
   va_list ap;
 
   if (type == SELINUX_INFO) return 0;
@@ -69,7 +70,7 @@ void getprop_main(void)
   if (toys.optflags & FLAG_Z) {
     union selinux_callback cb;
 
-    cb.func_log = selinux_log_callback;
+    cb.func_log = selinux_log_callback_local;
     selinux_set_callback(SELINUX_CB_LOG, cb);
     TT.handle = selinux_android_prop_context_handle();
     if (!TT.handle) error_exit("unable to get selinux property context handle");
index 8496736..d9ff148 100644 (file)
@@ -19,14 +19,13 @@ config LOAD_POLICY
 
 void load_policy_main(void)
 {
-  char *path = *toys.optargs;
-  int fd = xopen(path, O_RDONLY);
+  int fd = xopenro(*toys.optargs);
   off_t policy_len = fdlength(fd);
   char *policy_data = mmap(0, policy_len, PROT_READ, MAP_PRIVATE, fd, 0);
 
   close(fd);
   if (!policy_data || security_load_policy(policy_data, policy_len) < 0)
-    perror_exit("Couldn't %s %s", policy_data ? "load" : "read", path);
+    perror_exit("Couldn't %s %s", policy_data ? "load" : "read", *toys.optargs);
 
   munmap(policy_data, policy_len);
 }
diff --git a/toys/android/log.c b/toys/android/log.c
new file mode 100644 (file)
index 0000000..7da37b6
--- /dev/null
@@ -0,0 +1,58 @@
+/* log.c - Log to logcat.
+ *
+ * Copyright 2016 The Android Open Source Project
+
+USE_LOG(NEWTOY(log, "<1p:t:", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config LOG
+  bool "log"
+  depends on TOYBOX_ON_ANDROID
+  default y
+  help
+    usage: log [-p PRI] [-t TAG] MESSAGE...
+
+    Logs message to logcat.
+
+    -p use the given priority instead of INFO:
+       d: DEBUG  e: ERROR  f: FATAL  i: INFO  v: VERBOSE  w: WARN  s: SILENT
+    -t use the given tag instead of "log"
+*/
+
+#define FOR_log
+#include "toys.h"
+#include <android/log.h>
+
+GLOBALS(
+  char *tag;
+  char *pri;
+)
+
+void log_main(void)
+{
+  android_LogPriority pri = ANDROID_LOG_INFO;
+  char *s = toybuf;
+  int i;
+
+  if (TT.pri) {
+    i = stridx("defisvw", tolower(*TT.pri));
+    if (i==-1 || strlen(TT.pri)!=1) error_exit("bad -p '%s'", TT.pri);
+    pri = (android_LogPriority []){ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR,
+      ANDROID_LOG_FATAL, ANDROID_LOG_INFO, ANDROID_LOG_SILENT,
+      ANDROID_LOG_VERBOSE, ANDROID_LOG_WARN}[i];
+  }
+  if (!TT.tag) TT.tag = "log";
+
+  for (i = 0; toys.optargs[i]; i++) {
+    if (i) *s++ = ' ';
+    if ((s-toybuf)+strlen(toys.optargs[i])>=1024) {
+      memcpy(s, toys.optargs[i], 1024-(s-toybuf));
+      toybuf[1024] = 0;
+      perror_msg("log cut at 1024 bytes");
+
+      break;
+    }
+    s = stpcpy(s, toys.optargs[i]);
+  }
+
+  __android_log_write(pri, TT.tag, toybuf);
+}
diff --git a/toys/android/sendevent.c b/toys/android/sendevent.c
new file mode 100644 (file)
index 0000000..8e982e0
--- /dev/null
@@ -0,0 +1,37 @@
+/* sendevent.c - Send Linux input events.
+ *
+ * Copyright 2016 The Android Open Source Project
+
+USE_SENDEVENT(NEWTOY(sendevent, "<4>4", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config SENDEVENT
+  bool "sendevent"
+  default y
+  depends on TOYBOX_ON_ANDROID
+  help
+    usage: sendevent DEVICE TYPE CODE VALUE
+
+    Sends a Linux input event.
+*/
+
+#define FOR_sendevent
+#include "toys.h"
+
+#include <linux/input.h>
+
+void sendevent_main(void)
+{
+  int fd = xopen(*toys.optargs, O_RDWR);
+  int version;
+  struct input_event ev;
+
+  if (ioctl(fd, EVIOCGVERSION, &version))
+    perror_exit("EVIOCGVERSION failed for %s", *toys.optargs);
+  
+  memset(&ev, 0, sizeof(ev));
+  // TODO: error checking and support for named constants.
+  ev.type = atoi(toys.optargs[1]);
+  ev.code = atoi(toys.optargs[2]);
+  ev.value = atoi(toys.optargs[3]);
+  xwrite(fd, &ev, sizeof(ev));
+}
diff --git a/toys/android/start.c b/toys/android/start.c
new file mode 100644 (file)
index 0000000..20fa2b9
--- /dev/null
@@ -0,0 +1,61 @@
+/* start.c - Start/stop system services.
+ *
+ * Copyright 2016 The Android Open Source Project
+
+USE_START(NEWTOY(start, "", TOYFLAG_USR|TOYFLAG_SBIN))
+USE_STOP(NEWTOY(stop, "", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config START
+  bool "start"
+  depends on TOYBOX_ON_ANDROID
+  default y
+  help
+    usage: start [SERVICE...]
+
+    Starts the given system service, or netd/surfaceflinger/zygotes.
+
+config STOP
+  bool "stop"
+  depends on TOYBOX_ON_ANDROID
+  default y
+  help
+    usage: stop [SERVICE...]
+
+    Stop the given system service, or netd/surfaceflinger/zygotes.
+*/
+
+#define FOR_start
+#include "toys.h"
+
+#include <cutils/properties.h>
+
+static void start_stop(int start)
+{
+  char *property = start ? "ctl.start" : "ctl.stop";
+  // null terminated in both directions
+  char *services[] = {0,"netd","surfaceflinger","zygote","zygote_secondary",0},
+       **ss = toys.optargs;
+  int direction = 1;
+
+  if (getuid()) error_exit("must be root");
+
+  if (!*ss) {
+    // If we don't have optargs, iterate through services forward/backward.
+    ss = services+1;
+    if (!start) ss = services+ARRAY_LEN(services)-2, direction = -1;
+  }
+
+  for (; *ss; ss += direction)
+    if (property_set(property, *ss))
+      error_exit("failed to set property '%s' to '%s'", property, *ss);
+}
+
+void start_main(void)
+{
+  start_stop(1);
+}
+
+void stop_main(void)
+{
+  start_stop(0);
+}
index 99e771a..ee67b58 100644 (file)
@@ -1,4 +1,5 @@
 /* skeleton.c - Example program to act as template for new commands.
+ *              (Although really, half the time copying hello.c is easier.)
  *
  * Copyright 2014 Rob Landley <rob@landley.net>
  *
@@ -70,7 +71,7 @@ void skeleton_main(void)
 
   // Command line options parsing is done for you by lib/args.c called
   // from main.c using the optstring in the NEWTOY macros. Display results.
-  if (toys.optflags) printf("flags=%x\n", toys.optflags);
+  if (toys.optflags) printf("flags=%llx\n", toys.optflags);
   if (toys.optflags & FLAG_a) printf("Saw a\n");
   if (toys.optflags & FLAG_b) printf("b=%s\n", TT.s.b_string);
   if (toys.optflags & FLAG_c) printf("c=%ld\n", TT.s.c_number);
@@ -95,7 +96,7 @@ void skeleton_main(void)
 void skeleton_alias_main(void)
 {
   printf("Ran %s\n", toys.which->name);
-  printf("flags=%x\n", toys.optflags);
+  printf("flags=%llx\n", toys.optflags);
 
   // Note, this FLAG_b is a different bit position than the other FLAG_b,
   // and fills out a different variable of a different type.
index ae8e788..9fff262 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2015 Rob Landley <rob@landley.net>
 
-USE_TEST_HUMAN_READABLE(NEWTOY(test_human_readable, "<1>1ibs", 0))
+USE_TEST_HUMAN_READABLE(NEWTOY(test_human_readable, "<1>1ibs", TOYFLAG_BIN))
 
 config TEST_HUMAN_READABLE
   bool "test_human_readable"
index d2f5c84..e071d26 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2015 Rob Landley <rob@landley.net>
 
-USE_TEST_MANY_OPTIONS(NEWTOY(test_many_options, "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TEST_MANY_OPTIONS(NEWTOY(test_many_options, "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba", TOYFLAG_BIN))
 
 config TEST_MANY_OPTIONS
   bool "test_many_options"
index 17eb0cf..db90027 100644 (file)
@@ -4,7 +4,7 @@
  *
  * TODO sigwinch
 
-USE_TEST_SCANKEY(NEWTOY(test_scankey, 0, 0))
+USE_TEST_SCANKEY(NEWTOY(test_scankey, 0, TOYFLAG_BIN))
 
 config TEST_SCANKEY
   bool "test_scankey"
index 23467fb..f30d1fb 100644 (file)
@@ -4,28 +4,48 @@
  *
  * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/hostname.html
 
-USE_HOSTNAME(NEWTOY(hostname, NULL, TOYFLAG_BIN))
+USE_HOSTNAME(NEWTOY(hostname, "bF:", TOYFLAG_BIN))
 
 config HOSTNAME
   bool "hostname"
   default y
   help
-    usage: hostname [newname]
+    usage: hostname [-b] [-F FILENAME] [newname]
 
     Get/Set the current hostname
+
+    -b  Set hostname to 'localhost' if otherwise unset
+    -F  Set hostname to contents of FILENAME
 */
 
 #define FOR_hostname
 #include "toys.h"
 
+GLOBALS(
+  char *fname;
+)
+
 void hostname_main(void)
 {
-  const char *hostname = toys.optargs[0];
+  char *hostname = *toys.optargs;
+
+  if (TT.fname && (hostname = xreadfile(TT.fname, 0, 0))) {
+    if (!*chomp(hostname)) {
+      if (CFG_TOYBOX_FREE) free(hostname);
+      if (!(toys.optflags&FLAG_b)) error_exit("empty '%s'", TT.fname);
+      hostname = 0;
+    }
+  }
+
+  if (!hostname && (toys.optflags&FLAG_b))
+    if (gethostname(toybuf, sizeof(toybuf)-1) || !*toybuf)
+      hostname = "localhost";
+
   if (hostname) {
     if (sethostname(hostname, strlen(hostname)))
-      perror_exit("set failed '%s'", hostname);
+      perror_exit("set '%s'", hostname);
   } else {
-    if (gethostname(toybuf, sizeof(toybuf))) perror_exit("get failed");
+    if (gethostname(toybuf, sizeof(toybuf)-1)) perror_exit("gethostname");
     xputs(toybuf);
   }
 }
index 12f0450..9efdc87 100644 (file)
@@ -6,40 +6,91 @@
  * and http://www.ietf.org/rfc/rfc1321.txt
  *
  * They're combined this way to share infrastructure, and because md5sum is
- * and LSB standard command, sha1sum is just a good idea.
+ * and LSB standard command (but sha1sum and newer hashes are a good idea,
+ * see http://valerieaurora.org/hash.html).
+ *
+ * We optionally use openssl (or equivalent) to access assembly optimized
+ * versions of these functions, but provide a built-in version to reduce
+ * required dependencies.
 
-USE_MD5SUM(NEWTOY(md5sum, "b", TOYFLAG_USR|TOYFLAG_BIN))
-USE_SHA1SUM(NEWTOY(sha1sum, "b", TOYFLAG_USR|TOYFLAG_BIN))
+USE_MD5SUM(NEWTOY(md5sum, "bc*[!bc]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA1SUM(NEWTOY(sha1sum, "bc*[!bc]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA224SUM(OLDTOY(sha224sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA256SUM(OLDTOY(sha256sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA384SUM(OLDTOY(sha384sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
+USE_SHA512SUM(OLDTOY(sha512sum, sha1sum, TOYFLAG_USR|TOYFLAG_BIN))
 
 config MD5SUM
   bool "md5sum"
   default y
   help
-    usage: md5sum [FILE]...
+    usage: md5sum [-b] [-c FILE] [FILE]...
 
     Calculate md5 hash for each input file, reading from stdin if none.
-    Output one hash (16 hex digits) for each input file, followed by
-    filename.
+    Output one hash (32 hex digits) for each input file, followed by filename.
 
     -b brief (hash only, no filename)
+    -c Check each line of FILE is the same hash+filename we'd output.
 
 config SHA1SUM
   bool "sha1sum"
   default y
   help
-    usage: sha1sum [FILE]...
+    usage: sha?sum [-b] [-c FILE] [FILE]...
 
-    calculate sha1 hash for each input file, reading from stdin if none.
-    Output one hash (20 hex digits) for each input file, followed by
-    filename.
+    calculate sha hash for each input file, reading from stdin if none. Output
+    one hash (40 hex digits for sha1, 56 for sha224, 64 for sha256, 96 for sha384,
+    and 128 for sha512) for each input file, followed by filename.
 
     -b brief (hash only, no filename)
+    -c Check each line of FILE is the same hash+filename we'd output.
+
+config SHA224SUM
+  bool "sha224sum"
+  default y
+  depends on TOYBOX_LIBCRYPTO
+  help
+    See sha1sum
+
+config SHA256SUM
+  bool "sha256sum"
+  default y
+  depends on TOYBOX_LIBCRYPTO
+  help
+    See sha1sum
+
+config SHA384SUM
+  bool "sha384sum"
+  default y
+  depends on TOYBOX_LIBCRYPTO
+  help
+    See sha1sum
+
+config SHA512SUM
+  bool "sha512sum"
+  default y
+  depends on TOYBOX_LIBCRYPTO
+  help
+    See sha1sum
 */
 
+#define FORCE_FLAGS
 #define FOR_md5sum
 #include "toys.h"
 
+#if CFG_TOYBOX_LIBCRYPTO
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+#else
+typedef int SHA512_CTX;
+#endif
+
 GLOBALS(
+  struct arg_list *c;
+
+  int sawline;
+
+  // Crypto variables blanked after summing
   unsigned state[5];
   unsigned oldstate[5];
   uint64_t count;
@@ -185,12 +236,58 @@ static void hash_update(char *data, unsigned int len, void (*transform)(void))
   }
 }
 
+// Initialize array tersely
+#define HASH_INIT(name, prefix) { name, (void *)prefix##_Init, \
+  (void *)prefix##_Update, (void *)prefix##_Final, \
+  prefix##_DIGEST_LENGTH, }
+#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
+
+// Call the assembly optimized library code when CFG_TOYBOX_LIBCRYPTO
+static void do_lib_hash(int fd, char *name)
+{
+  // Largest context
+  SHA512_CTX ctx;
+  struct hash {
+    char *name;
+    int (*init)(void *);
+    int (*update)(void *, void *, size_t);
+    int (*final)(void *, void *);
+    int digest_length;
+  } algorithms[] = {
+    USE_TOYBOX_LIBCRYPTO(
+      USE_MD5SUM(HASH_INIT("md5sum", MD5),)
+      USE_SHA1SUM(HASH_INIT("sha1sum", SHA1),)
+      USE_SHA224SUM(HASH_INIT("sha224sum", SHA224),)
+      USE_SHA256SUM(HASH_INIT("sha256sum", SHA256),)
+      USE_SHA384SUM(HASH_INIT("sha384sum", SHA384),)
+      USE_SHA512SUM(HASH_INIT("sha512sum", SHA512),)
+    )
+  }, * hash;
+  int i;
+
+  // This should never NOT match, so no need to check
+  for (i = 0; i<ARRAY_LEN(algorithms); i++)
+    if (!strcmp(toys.which->name, algorithms[i].name)) break;
+  hash = algorithms+i;
+
+  hash->init(&ctx);
+  for (;;) {
+      i = read(fd, toybuf, sizeof(toybuf));
+      if (i<1) break;
+      hash->update(&ctx, toybuf, i);
+  }
+  hash->final(toybuf+128, &ctx);
+
+  for (i = 0; i<hash->digest_length; i++)
+    sprintf(toybuf+2*i, "%02x", toybuf[i+128]);
+}
+
 // Callback for loopfiles()
 
-static void do_hash(int fd, char *name)
+static void do_builtin_hash(int fd, char *name)
 {
   uint64_t count;
-  int i, sha1=toys.which->name[0]=='s';;
+  int i, sha1=toys.which->name[0]=='s';
   char buf;
   void (*transform)(void);
 
@@ -228,18 +325,97 @@ static void do_hash(int fd, char *name)
 
   if (sha1)
     for (i = 0; i < 20; i++)
-      printf("%02x", 255&(TT.state[i>>2] >> ((3-(i & 3)) * 8)));
-  else for (i=0; i<4; i++) printf("%08x", bswap_32(TT.state[i]));
+      sprintf(toybuf+2*i, "%02x", 255&(TT.state[i>>2] >> ((3-(i & 3)) * 8)));
+  else for (i=0; i<4; i++) sprintf(toybuf+8*i, "%08x", bswap_32(TT.state[i]));
 
   // Wipe variables. Cryptographer paranoia.
-  memset(&TT, 0, sizeof(TT));
+  memset(TT.state, 0, sizeof(TT)-((long)TT.state-(long)&TT));
+  i = strlen(toybuf)+1;
+  memset(toybuf+i, 0, sizeof(toybuf)-i);
+}
 
-  printf((toys.optflags & FLAG_b) ? "\n" : "  %s\n", name);
+// Call builtin or lib hash function, then display output if necessary
+static void do_hash(int fd, char *name)
+{
+  if (CFG_TOYBOX_LIBCRYPTO) do_lib_hash(fd, name);
+  else do_builtin_hash(fd,name);
+
+  if (name)
+    printf((toys.optflags & FLAG_b) ? "%s\n" : "%s  %s\n", toybuf, name);
+}
+
+static int do_c(char *line, size_t len)
+{
+  int space = 0, fail = 0;
+  char *name;
+
+  for (name = line; *name; name++) {
+    if (isspace(*name)) {
+      space++;
+      *name = 0;
+    } else if (space) break;
+  }
+
+  if (!space || !*line || !*name) error_msg("bad line %s", line);
+  else {
+    int fd = !strcmp(name, "-") ? 0 : open(name, O_RDONLY);
+
+    TT.sawline = 1;
+    if (fd==-1) {
+      perror_msg_raw(name);
+      *toybuf = 0;
+    } else do_hash(fd, 0);
+    if (strcasecmp(line, toybuf)) toys.exitval = fail = 1;
+    printf("%s: %s\n", name, fail ? "FAILED" : "OK");
+    if (fd>0) close(fd);
+  }
+
+  return 0;
+}
+
+// Open file, read each line, and call do_line(). Returns 0 if file existed
+// and we read it to the end, 1 if interrupted by callback, 2 of didn't exist
+// do_line returns 0 to free line, 1 to keep line, 2 to end loop
+int looplines(char *name, int trim, int (*do_line)(char *line, size_t len))
+{
+  FILE *fp = !strcmp(name, "-") ? stdin : fopen(name, "r");
+  int rc = 0;
+
+  if (!fp) {
+    perror_msg_raw(name);
+
+    return 2;
+  }
+
+  for (;;) {
+    char *line = 0;
+    ssize_t len;
+
+    if ((len = getline(&line, (void *)&len, fp))<1) break;
+    if (line[len-1]=='\n') len--;
+    if (trim) line[len] = 0;
+    len = do_line(line, len);
+    if (!len) free(line);
+    if (len==2) {
+      rc = 2;
+      break;
+    }
+  }
+  if (fp!=stdin) fclose(fp);
+
+  return rc;
 }
 
 void md5sum_main(void)
 {
-  loopfiles(toys.optargs, do_hash);
+  struct arg_list *al;
+
+  if (!TT.c) loopfiles(toys.optargs, do_hash);
+  else for (al = TT.c; al; al = al->next) {
+    TT.sawline = 0;
+    looplines(al->arg, 1, do_c);
+    if (!TT.sawline) error_msg("%s: no lines", al->arg);
+  }
 }
 
 void sha1sum_main(void)
index 1a467b0..8148c85 100644 (file)
@@ -53,6 +53,6 @@ void mknod_main(void)
   if (toys.optflags & FLAG_Z)
     if (-1 == lsm_set_create(TT.arg_context))
       perror_exit("-Z '%s' failed", TT.arg_context);
-  if (mknod(*toys.optargs, mode|modes[type], makedev(major, minor)))
+  if (mknod(*toys.optargs, mode|modes[type], dev_makedev(major, minor)))
     perror_exit_raw(*toys.optargs);
 }
index 9510c5e..628bed5 100644 (file)
@@ -5,6 +5,7 @@
  * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/mount.html
  * Note: -hV is bad spec, haven't implemented -FsLU yet
  * no mtab (/proc/mounts does it) so -n is NOP.
+ * TODO mount -o loop,autoclear (linux git 96c5865559ce)
 
 USE_MOUNT(NEWTOY(mount, "?O:afnrvwt:o*[-rw]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
 //USE_NFSMOUNT(NEWTOY(nfsmount, "?<2>2", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
@@ -13,7 +14,7 @@ config MOUNT
   bool "mount"
   default y
   help
-    usage: mount [-afFrsvw] [-t TYPE] [-o OPTIONS...] [[DEVICE] DIR]
+    usage: mount [-afFrsvw] [-t TYPE] [-o OPTION,] [[DEVICE] DIR]
 
     Mount new filesystem(s) on directories. With no arguments, display existing
     mounts.
@@ -195,9 +196,10 @@ static void mount_filesystem(char *dev, char *dir, char *type,
     if (toys.optflags & FLAG_v)
       printf("try '%s' type '%s' on '%s'\n", dev, type, dir);
     for (;;) {
+      errno = 0;
       rc = mount(dev, dir, type, flags, opts);
       // Did we succeed, fail unrecoverably, or already try read-only?
-      if (rc == 0 || (errno != EACCES && errno != EROFS) || (flags&MS_RDONLY))
+      if (!rc || (errno != EACCES && errno != EROFS) || (flags&MS_RDONLY))
         break;
       // If we haven't already tried it, use the BLKROSET ioctl to ensure
       // that the underlying device isn't read-only.
@@ -207,7 +209,7 @@ static void mount_filesystem(char *dev, char *dir, char *type,
         if (-1 != (fd = open(dev, O_RDONLY))) {
           rc = ioctl(fd, BLKROSET, &ro);
           close(fd);
-          if (rc == 0) continue;
+          if (!rc) continue;
         }
       }
       fprintf(stderr, "'%s' is read-only\n", dev);
@@ -309,7 +311,6 @@ void mount_main(void)
 
     for (mm = remount ? remount : mtl; mm; mm = (remount ? mm->prev : mm->next))
     {
-      char *aopts = 0;
       struct mtab_list *mmm = 0;
       int aflags, noauto, len;
 
@@ -341,13 +342,20 @@ void mount_main(void)
  
       // user only counts from fstab, not opts.
       if (!mmm) {
+        char *aopts = 0;
         TT.okuser = comma_scan(mm->opts, "user", 1);
         aflags = flag_opts(mm->opts, flags, &aopts);
         aflags = flag_opts(opts, aflags, &aopts);
-
+        if (remount) {
+            // clear type and opts for remount
+            aflags |= MS_REMOUNT;
+            strcpy(mm->type, "none");
+            aopts = NULL;
+        }
         mount_filesystem(mm->device, mm->dir, mm->type, aflags, aopts);
+        free(aopts);
+        if (!remount && errno == EINVAL) continue;
       } // TODO else if (getuid()) error_msg("already there") ?
-      free(aopts);
 
       if (!(toys.optflags & FLAG_a)) break;
     }
@@ -375,7 +383,8 @@ void mount_main(void)
   } else {
     char *more = 0;
 
-    mount_filesystem(dev, dir, TT.type, flag_opts(opts, flags, &more), more);
+    flags = flag_opts(opts, flags, &more);
+    mount_filesystem(dev, dir, TT.type, flags, more);
     if (CFG_TOYBOX_FREE) free(more);
   }
 }
index 4e40186..0e0f460 100644 (file)
@@ -38,7 +38,7 @@ static void insanitize(char *f)
   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.
-    error_exit("bad -f '%s'@%ld", f, s-f+1);
+    error_exit("bad -f '%s'@%d", f, (int)(s-f+1));
   }
 }
 
diff --git a/toys/net/README b/toys/net/README
new file mode 100644 (file)
index 0000000..8708e4b
--- /dev/null
@@ -0,0 +1 @@
+Networking
similarity index 100%
rename from toys/other/ifconfig.c
rename to toys/net/ifconfig.c
similarity index 100%
rename from toys/other/netcat.c
rename to toys/net/netcat.c
diff --git a/toys/net/netstat.c b/toys/net/netstat.c
new file mode 100644 (file)
index 0000000..4e5c884
--- /dev/null
@@ -0,0 +1,383 @@
+/* netstat.c - Display Linux networking subsystem.
+ *
+ * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
+ * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ *
+ * Not in SUSv4.
+ *
+USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
+config NETSTAT
+  bool "netstat"
+  default y
+  help
+    usage: netstat [-pWrxwutneal]
+
+    Display networking information. Default is netsat -tuwx
+
+    -r  routing table
+    -a  all sockets (not just connected)
+    -l  listening server sockets
+    -t  TCP sockets
+    -u  UDP sockets
+    -w  raw sockets
+    -x  unix sockets
+    -e  extended info
+    -n  don't resolve names
+    -W  wide display
+    -p  PID/Program name of sockets
+*/
+
+#define FOR_netstat
+#include "toys.h"
+#include <net/route.h>
+
+GLOBALS(
+  struct num_cache *inodes;
+  int wpad;
+);
+
+// convert address into text format.
+static void addr2str(int af, void *addr, unsigned port, char *buf, int len,
+  char *proto)
+{
+  int pos, count;
+  struct servent *ser = 0;
+
+  // Convert to numeric address
+  if (!inet_ntop(af, addr, buf, 256)) {
+    *buf = 0;
+
+    return;
+  }
+  buf[len] = 0;
+  pos = strlen(buf);
+
+  // If there's no port number, it's a local :* binding, nothing to look up.
+  if (!port) {
+    if (len-pos<2) pos = len-2;
+    strcpy(buf+pos, ":*");
+
+    return;
+  }
+
+  if (!(toys.optflags & FLAG_n)) {
+    struct addrinfo hints, *result, *rp;
+    char cut[4];
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = af;
+
+    if (!getaddrinfo(buf, NULL, &hints, &result)) {
+      socklen_t sock_len = (af == AF_INET) ? sizeof(struct sockaddr_in)
+        : sizeof(struct sockaddr_in6);
+
+      // We assume that a failing getnameinfo dosn't stomp "buf" here.
+      for (rp = result; rp; rp = rp->ai_next)
+        if (!getnameinfo(rp->ai_addr, sock_len, buf, 256, 0, 0, 0)) break;
+      freeaddrinfo(result);
+      buf[len] = 0;
+      pos = strlen(buf);
+    }
+
+    // Doesn't understand proto "tcp6", so truncate
+    memcpy(cut, proto, 3);
+    cut[3] = 0;
+    ser = getservbyport(htons(port), cut);
+  }
+
+  // Append :service
+  count = snprintf(0, 0, ":%u", port);
+  if (ser) {
+    count = snprintf(0, 0, ":%s", ser->s_name);
+    // sheer paranoia
+    if (count>=len) {
+      count = len-1;
+      ser->s_name[count] = 0;
+    }
+  }
+  if (len-pos<count) pos = len-count;
+  if (ser) sprintf(buf+pos, ":%s", ser->s_name);
+  else sprintf(buf+pos, ":%u", port);
+}
+
+// Display info for tcp/udp/raw
+static void show_ip(char *fname)
+{
+  char *ss_state = "UNKNOWN", buf[12], *s, *label = strrchr(fname, '/')+1;
+  char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1",
+                         "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT",
+                         "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"};
+  struct passwd *pw;
+  FILE *fp = fopen(fname, "r");
+
+  if (!fp) {
+     perror_msg("'%s'", fname);
+     return;
+  }
+
+  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
+
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+    char lip[256], rip[256];
+    union {
+      struct {unsigned u; unsigned char b[4];} i4;
+      struct {struct {unsigned a, b, c, d;} u; unsigned char b[16];} i6;
+    } laddr, raddr;
+    unsigned lport, rport, state, txq, rxq, num, uid, nitems;
+    unsigned long inode;
+
+    // Try ipv6, then try ipv4
+    nitems = sscanf(toybuf,
+      " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
+      &num, &laddr.i6.u.a, &laddr.i6.u.b, &laddr.i6.u.c,
+      &laddr.i6.u.d, &lport, &raddr.i6.u.a, &raddr.i6.u.b,
+      &raddr.i6.u.c, &raddr.i6.u.d, &rport, &state, &txq, &rxq,
+      &uid, &inode);
+
+    if (nitems!=16) {
+      nitems = sscanf(toybuf,
+        " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
+        &num, &laddr.i4.u, &lport, &raddr.i4.u, &rport, &state, &txq,
+        &rxq, &uid, &inode);
+
+      if (nitems!=10) continue;
+      nitems = AF_INET;
+    } else nitems = AF_INET6;
+
+    // Should we display this? (listening or all or TCP/UDP/RAW)
+    if (!((toys.optflags & FLAG_l) && (!rport && (state & 0xA)))
+      && !(toys.optflags & FLAG_a) && !(rport & (0x10 | 0x20 | 0x40)))
+        continue;
+
+    addr2str(nitems, &laddr, lport, lip, TT.wpad, label);
+    addr2str(nitems, &raddr, rport, rip, TT.wpad, label);
+
+    // Display data
+    s = label;
+    if (strstart(&s, "tcp")) {
+      int sz = ARRAY_LEN(state_label);
+      if (!state || state >= sz) state = sz-1;
+      ss_state = state_label[state];
+    } else if (strstart(&s, "udp")) {
+      if (state == 1) ss_state = state_label[state];
+      else if (state == 7) ss_state = "";
+    } else if (strstart(&s, "raw")) sprintf(ss_state = buf, "%u", state);
+
+    if (!(toys.optflags & FLAG_n) && (pw = bufgetpwuid(uid)))
+      snprintf(toybuf, sizeof(toybuf), "%s", pw->pw_name);
+    else snprintf(toybuf, sizeof(toybuf), "%d", uid);
+
+    printf("%-6s%6d%7d ", label, rxq, txq);
+    printf("%*.*s %*.*s ", -TT.wpad, TT.wpad, lip, -TT.wpad, TT.wpad, rip);
+    printf("%-11s", ss_state);
+    if ((toys.optflags & FLAG_e)) printf(" %-10s %-11ld", toybuf, inode);
+    if ((toys.optflags & FLAG_p)) {
+      struct num_cache *nc = get_num_cache(TT.inodes, inode);
+
+      printf(" %s", nc ? nc->data : "-");
+    }
+    xputc('\n');
+  }
+  fclose(fp);
+}
+
+static void show_unix_sockets(void)
+{
+  char *types[] = {"","STREAM","DGRAM","RAW","RDM","SEQPACKET","DCCP","PACKET"},
+       *states[] = {"","LISTENING","CONNECTING","CONNECTED","DISCONNECTING"},
+       *s, *ss;
+  unsigned long refcount, flags, type, state, inode;
+  FILE *fp = xfopen("/proc/net/unix", "r");
+
+  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
+
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+    unsigned offset = 0;
+
+    // count = 6 or 7 (first field ignored, sockets don't always have filenames)
+    if (6<sscanf(toybuf, "%*p: %lX %*X %lX %lX %lX %lu %n",
+      &refcount, &flags, &type, &state, &inode, &offset))
+        continue;
+
+    // Linux exports only SO_ACCEPTCON since 2.3.15pre3 in 1999, but let's
+    // filter in case they add more someday.
+    flags &= 1<<16;
+
+    // Only show unconnected listening sockets with -a
+    if (state==1 && flags && !(toys.optflags&FLAG_a)) continue;
+
+    if (type==10) type = 7; // move SOCK_PACKET into line
+    if (type>ARRAY_LEN(types)) type = 0;
+    if (state>ARRAY_LEN(states) || (state==1 && !flags)) state = 0;
+    sprintf(toybuf, "[ %s]", flags ? "ACC " : "");
+
+    printf("unix  %-6ld %-11s %-10s %-13s %8lu ",
+      refcount, toybuf, types[type], states[state], inode);
+    if (toys.optflags & FLAG_p) {
+      struct num_cache *nc = get_num_cache(TT.inodes, inode);
+
+      printf("%-19.19s", nc ? nc->data : "-");
+    }
+
+    if (offset) {
+      if ((ss = strrchr(s = toybuf+offset, '\n'))) *ss = 0;
+      printf("%s", s);
+    }
+    xputc('\n');
+  }
+
+  fclose(fp);
+}
+
+static int scan_pids(struct dirtree *node)
+{
+  char *s = toybuf+256;
+  struct dirent *entry;
+  DIR *dp;
+  int pid, dirfd;
+
+  if (!node->parent) return DIRTREE_RECURSE;
+  if (!(pid = atol(node->name))) return 0;
+
+  sprintf(toybuf, "/proc/%d/cmdline", pid);
+  if (!(readfile(toybuf, toybuf, 256))) return 0;
+
+  sprintf(s, "%d/fd", pid);
+  if (-1==(dirfd = openat(dirtree_parentfd(node), s, O_RDONLY))) return 0;
+  if (!(dp = fdopendir(dirfd))) {
+    close(dirfd);
+
+    return 0;
+  }
+
+  while ((entry = readdir(dp))) {
+    s = toybuf+256;
+    if (!readlinkat0(dirfd, entry->d_name, s, sizeof(toybuf)-256)) continue;
+    // Can the "[0000]:" happen in a modern kernel?
+    if (strstart(&s, "socket:[") || strstart(&s, "[0000]:")) {
+      long long ll = atoll(s);
+
+      sprintf(s, "%d/%s", pid, getbasename(toybuf));
+      add_num_cache(&TT.inodes, ll, s, strlen(s)+1);
+    }
+  }
+  closedir(dp);
+
+  return 0;
+}
+
+/*
+ * extract inet4 route info from /proc/net/route file and display it.
+ */
+static void display_routes(void)
+{
+  static const char flagchars[] = "GHRDMDAC";
+  static const unsigned flagarray[] = {
+    RTF_GATEWAY, RTF_HOST, RTF_REINSTATE, RTF_DYNAMIC, RTF_MODIFIED
+  };
+  unsigned long dest, gate, mask;
+  int flags, ref, use, metric, mss, win, irtt;
+  char *out = toybuf, *flag_val;
+  char iface[64]={0};
+  FILE *fp = xfopen("/proc/net/route", "r");
+
+  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
+
+  printf("Kernel IP routing table\n"
+          "Destination\tGateway \tGenmask \tFlags %s Iface\n",
+          !(toys.optflags&FLAG_e) ? "  MSS Window  irtt" : "Metric Ref    Use");
+
+  while (fgets(toybuf, sizeof(toybuf), fp)) {
+     char *destip = 0, *gateip = 0, *maskip = 0;
+
+     if (11 != sscanf(toybuf, "%63s%lx%lx%X%d%d%d%lx%d%d%d", iface, &dest,
+       &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt))
+         break;
+
+    // skip down interfaces.
+    if (!(flags & RTF_UP)) continue;
+
+// TODO /proc/net/ipv6_route
+
+    if (dest) {
+      if (inet_ntop(AF_INET, &dest, out, 16)) destip = out;
+    } else destip = (toys.optflags&FLAG_n) ? "0.0.0.0" : "default";
+    out += 16;
+
+    if (gate) {
+      if (inet_ntop(AF_INET, &gate, out, 16)) gateip = out;
+    } else gateip = (toys.optflags&FLAG_n) ? "0.0.0.0" : "*";
+    out += 16;
+
+// TODO /24
+    //For Mask
+    if (inet_ntop(AF_INET, &mask, out, 16)) maskip = out;
+    else maskip = "?";
+    out += 16;
+
+    //Get flag Values
+    flag_val = out;
+    *out++ = 'U';
+    for (dest = 0; dest < ARRAY_LEN(flagarray); dest++)
+      if (flags&flagarray[dest]) *out++ = flagchars[dest];
+    *out = 0;
+    if (flags & RTF_REJECT) *flag_val = '!';
+
+    printf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
+    if (!(toys.optflags & FLAG_e))
+      printf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
+    else printf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
+  }
+
+  fclose(fp);
+}
+
+void netstat_main(void)
+{
+  int tuwx = FLAG_t|FLAG_u|FLAG_w|FLAG_x;
+  char *type = "w/o";
+
+  TT.wpad = (toys.optflags&FLAG_W) ? 51 : 23;
+  if (!(toys.optflags&(FLAG_r|tuwx))) toys.optflags |= tuwx;
+  if (toys.optflags & FLAG_r) display_routes();
+  if (!(toys.optflags&tuwx)) return;
+
+  if (toys.optflags & FLAG_a) type = "established and";
+  else if (toys.optflags & FLAG_l) type = "only";
+
+  if (toys.optflags & FLAG_p) dirtree_read("/proc", scan_pids);
+
+  if (toys.optflags&(FLAG_t|FLAG_u|FLAG_w)) {
+    printf("Active %s (%s servers)\n", "Internet connections", type);
+    printf("Proto Recv-Q Send-Q %*s %*s State      ", -TT.wpad, "Local Address",
+      -TT.wpad, "Foreign Address");
+    if (toys.optflags & FLAG_e) printf(" User       Inode      ");
+    if (toys.optflags & FLAG_p) printf(" PID/Program Name");
+    xputc('\n');
+
+    if (toys.optflags & FLAG_t) {
+      show_ip("/proc/net/tcp");
+      show_ip("/proc/net/tcp6");
+    }
+    if (toys.optflags & FLAG_u) {
+      show_ip("/proc/net/udp");
+      show_ip("/proc/net/udp6");
+    }
+    if (toys.optflags & FLAG_w) {
+      show_ip("/proc/net/raw");
+      show_ip("/proc/net/raw6");
+    }
+  }
+
+  if (toys.optflags & FLAG_x) {
+    printf("Active %s (%s servers)\n", "UNIX domain sockets", type);
+
+    printf("Proto RefCnt Flags\t Type\t    State\t    %s Path\n",
+      (toys.optflags&FLAG_p) ? "PID/Program Name" : "I-Node");
+    show_unix_sockets();
+  }
+
+  if ((toys.optflags & FLAG_p) && CFG_TOYBOX_FREE)
+    llist_traverse(TT.inodes, free);
+  toys.exitval = 0;
+}
similarity index 98%
rename from toys/other/rfkill.c
rename to toys/net/rfkill.c
index 36fe320..56e5768 100644 (file)
@@ -80,7 +80,7 @@ void rfkill_main(void)
         continue;
 
       sprintf(toybuf, "/sys/class/rfkill/rfkill%u/uevent", rfevent.idx);
-      tvar = xopen(toybuf, O_RDONLY);
+      tvar = xopenro(toybuf);
       while ((line = get_line(tvar))) {
         char *s = line;
 
diff --git a/toys/net/tunctl.c b/toys/net/tunctl.c
new file mode 100644 (file)
index 0000000..1aafebf
--- /dev/null
@@ -0,0 +1,53 @@
+/* tunctl.c - Control tap/tun network devices.
+ *
+ * Copyright 2016 Rob Landley <rob@landley.net>
+ *
+ * See http://kernel.org/doc/Documentation/networking/tuntap.txt
+ *
+ * This is useful for things like "kvm -netdev tap" and containers.
+ * See https://landley.net/lxc/02-networking.html for example usage.
+ *
+ * todo: bridge mode 
+ *  -b bridge daemon (forwards packets between NAME and NAME2 interfaces)
+
+
+USE_TUNCTL(NEWTOY(tunctl, "<1>1t|d|u:T[!td]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TUNCTL
+  bool "tunctl"
+  default y
+  help
+    usage: tunctl [-dtT] [-u USER] NAME
+
+    Create and delete tun/tap virtual ethernet devices.
+
+    -T Use tap (ethernet frames) instead of tun (ip packets)
+    -d Delete tun/tap device
+    -t Create tun/tap device
+    -u Set owner (user who can read/write device without root access)
+*/
+
+#define FOR_tunctl
+#include "toys.h"
+#include <linux/if_tun.h>
+
+GLOBALS(
+  char *user;
+)
+
+void tunctl_main(void)
+{
+  struct ifreq *ifr = (void *)toybuf;
+  uid_t u = TT.user ?  xgetuid(TT.user) : 0;
+  int fd = xopen("/dev/net/tun", O_RDWR);
+
+  // Associate filehandle with device
+  ifr->ifr_flags = ((toys.optflags&FLAG_T) ? IFF_TUN : IFF_TAP)|IFF_NO_PI;
+  strncpy(ifr->ifr_name, *toys.optargs, sizeof(ifr->ifr_name));
+  xioctl(fd, TUNSETIFF, toybuf);
+
+  if (toys.optflags&FLAG_t) {
+    xioctl(fd, TUNSETPERSIST, (void *)1);
+    xioctl(fd, TUNSETOWNER, (void *)(long)u);
+  } else xioctl(fd, TUNSETPERSIST, (void *)0);
+}
index 7dc6114..33155bc 100644 (file)
@@ -4,7 +4,7 @@
  *
  * No standard
 
-USE_BASE64(NEWTOY(base64, "diw#<1[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_BASE64(NEWTOY(base64, "diw#<0=76[!dw]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config BASE64
   bool "base64"
@@ -16,7 +16,7 @@ config BASE64
 
     -d decode
     -i ignore non-alphabetic characters
-    -w wrap output at COLUMNS (default 76)
+    -w wrap output at COLUMNS (default 76 or 0 for no wrap)
 */
 
 #define FOR_base64
@@ -24,26 +24,39 @@ config BASE64
 
 GLOBALS(
   long columns;
+
+  unsigned total;
 )
 
+static void wraputchar(int c, int *x)
+{
+  putchar(c);
+  TT.total++;
+  if (TT.columns && ++*x == TT.columns) {
+    *x = 0;
+    xputc('\n');
+  };
+}
+
 static void do_base64(int fd, char *name)
 {
   int out = 0, bits = 0, x = 0, i, len;
   char *buf = toybuf+128;
 
+  TT.total = 0;
+
   for (;;) {
+    // If no more data, flush buffer
     if (!(len = xread(fd, buf, sizeof(toybuf)-128))) {
       if (!(toys.optflags & FLAG_d)) {
-        if (bits) {
-          putchar(toybuf[out<<(6-bits)]);
-          x++;
-        }
-        while (x++&3)  putchar('=');
-        if (x != 1) xputc('\n');
+        if (bits) wraputchar(toybuf[out<<(6-bits)], &x);
+        while (TT.total&3) wraputchar('=', &x);
+        if (x) xputc('\n');
       }
 
       return;
     }
+
     for (i=0; i<len; i++) {
       if (toys.optflags & FLAG_d) {
         if (buf[i] == '=') return;
@@ -66,12 +79,8 @@ static void do_base64(int fd, char *name)
         out = (out<<8) + buf[i];
         bits += 8;
         while (bits >= 6) {
-          putchar(toybuf[out >> (bits -= 6)]);
+          wraputchar(toybuf[out >> (bits -= 6)], &x);
           out &= (1<<bits)-1;
-          if (TT.columns == ++x) {
-            xputc('\n');
-            x = 0;
-          }
         }
       }
     }
@@ -80,8 +89,6 @@ static void do_base64(int fd, char *name)
 
 void base64_main(void)
 {
-  if (!TT.columns) TT.columns = 76;
-
   base64_init(toybuf);
   loopfiles(toys.optargs, do_base64);
 }
index 2880812..a527b2e 100644 (file)
@@ -57,15 +57,33 @@ static const struct fstype fstypes[] = {
   {"vfat", 0x31544146, 4, 54, 39+(4<<24), 11, 43}     // fat1
 };
 
+static const char *vfat_empty   = "           ";
+static const char *vfat_no_name = "NO NAME    ";
+
+static int valid_label(int i, int off)
+{
+  int label_len = fstypes[i].label_len;
+  const char *label = toybuf + fstypes[i].label_off - off;
+
+  if (!strcmp(fstypes[i].name, "vfat")) {
+    if (!memcmp(label, vfat_empty, label_len))
+      return 0;
+    if (!memcmp(label, vfat_no_name, label_len))
+      return 0;
+    return 1;
+  }
+  return 1;
+}
+
 static void do_blkid(int fd, char *name)
 {
-  int off, i, j;
+  int off, i, j, len;
   char *type;
 
   off = i = 0;
 
   for (;;) {
-    int pass = 0, len;
+    int pass = 0;
 
     // Read next block of data
     len = readall(fd, toybuf, sizeof(toybuf));
@@ -99,7 +117,7 @@ static void do_blkid(int fd, char *name)
 
   // distinguish ext2/3/4
   type = fstypes[i].name;
-  if (!i) {
+  if (!strcmp(type, "ext2")) {
     if (toybuf[1116]&4) type = "ext3";
     if (toybuf[1120]&64) type = "ext4";
   }
@@ -115,9 +133,16 @@ static void do_blkid(int fd, char *name)
   // output for blkid
   printf("%s:",name);
 
-  if (fstypes[i].label_len)
-    printf(" LABEL=\"%.*s\"", fstypes[i].label_len,
-           toybuf+fstypes[i].label_off-off);
+  if (fstypes[i].label_len) {
+    char *s = toybuf+fstypes[i].label_off-off;;
+
+    len = fstypes[i].label_len;
+    if (!strcmp(type, "vfat")) {
+      while (len && s[len-1]==' ') len--;
+      if (strstart(&s, "NO NAME")) len=0;
+    }
+    if (len) printf(" LABEL=\"%.*s\"", len, s);
+  }
 
   if (fstypes[i].uuid_off) {
     int bits = 0x550, size = fstypes[i].uuid_off >> 24,
index e5a504e..38e0993 100644 (file)
@@ -46,7 +46,7 @@ void blockdev_main(void)
   if (!toys.optflags) help_exit("need --option");
 
   for (ss = toys.optargs;  *ss; ss++) {
-    int fd = xopen(*ss, O_RDONLY), i;
+    int fd = xopenro(*ss), i;
 
     // Command line order discarded so perform multiple operations in flag order
     for (i = 0; i < 32; i++) {
index 1081b5e..ff59849 100644 (file)
@@ -319,9 +319,9 @@ static int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
 static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
 {
   struct group_data *hufGroup;
-  int hh, ii, jj, kk, runPos, dbufCount, symCount, selector, nextSym,
+  int ii, jj, kk, runPos, dbufCount, symCount, selector, nextSym,
     *byteCount, *base, *limit;
-  unsigned int *dbuf = bw->dbuf;
+  unsigned hh, *dbuf = bw->dbuf;
   unsigned char uc;
 
   // We've finished reading and digesting the block header.  Now read this
@@ -401,7 +401,9 @@ static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
        literal used is the one at the head of the mtfSymbol array.) */
     if (runPos) {
       runPos = 0;
-      if (dbufCount+hh > bd->dbufSize) return RETVAL_DATA_ERROR;
+      // Check for integer overflow
+      if (hh>bd->dbufSize || dbufCount+hh>bd->dbufSize)
+        return RETVAL_DATA_ERROR;
 
       uc = bd->symToByte[bd->mtfSymbol[0]];
       byteCount[uc] += hh;
@@ -453,9 +455,6 @@ static void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
   unsigned int *dbuf = bw->dbuf;
   int *byteCount = bw->byteCount;
 
-  // Technically this part is preparation for the burrows-wheeler
-  // transform, but it's quick and convenient to do here.
-
   // Turn byteCount into cumulative occurrence counts of 0 to n-1.
   jj = 0;
   for (ii=0; ii<256; ii++) {
@@ -649,7 +648,10 @@ static char *bunzipStream(int src_fd, int dst_fd)
 
   if (!(i = start_bunzip(&bd,src_fd, 0, 0))) {
     i = write_bunzip_data(bd,bd->bwdata, dst_fd, 0, 0);
-    if (i==RETVAL_LAST_BLOCK && bd->bwdata[0].headerCRC==bd->totalCRC) i = 0;
+    if (i==RETVAL_LAST_BLOCK) {
+      if (bd->bwdata[0].headerCRC==bd->totalCRC) i = 0;
+      else i = RETVAL_DATA_ERROR;
+    }
   }
   flush_bunzip_outbuf(bd, dst_fd);
 
index 8aa1d93..0e07d71 100644 (file)
@@ -19,32 +19,31 @@ config FACTOR
 
 static void factor(char *s)
 {
-  unsigned long l, ll;
+  unsigned long long l, ll;
 
   for (;;) {
     char *err = s;
+    int dash = 0;
 
     while(isspace(*s)) s++;
+    if (*s=='-') dash = *s++;
     if (!*s) return;
 
-    l = strtoul(s, &s, 0);
+    l = strtoull(s, &s, 0);
     if (*s && !isspace(*s)) {
       error_msg("%s: not integer", err);
-
-      return;
+      while (*s && !isspace(*s)) s++;
+      continue;
     }
 
-    printf("%lu:", l);
+    printf("-%llu:"+!dash, l);
 
     // Negative numbers have -1 as a factor
-    if (l < 0) {
-      printf(" -1");
-      l *= -1;
-    }
+    if (dash) printf(" -1");
 
     // Nothing below 4 has factors
     if (l < 4) {
-      printf(" %lu\n", l);
+      printf(" %llu\n", l);
       continue;
     }
 
@@ -54,16 +53,16 @@ static void factor(char *s)
       l >>= 1;
     }
 
-    // test odd numbers.
+    // test odd numbers until square is > remainder or integer wrap.
     for (ll=3; ;ll += 2) {
       long lll = ll*ll;
 
       if (lll>l || lll<ll) {
-        if (l>1) printf(" %lu", l);
+        if (l>1) printf(" %llu", l);
         break;
       }
       while (!(l%ll)) {
-        printf(" %lu", ll);
+        printf(" %llu", ll);
         l /= ll;
       }
     }
index e169554..dfe17fb 100644 (file)
@@ -23,7 +23,7 @@ config FSFREEZE
 
 void fsfreeze_main(void)
 {
-  int fd = xopen(*toys.optargs, O_RDONLY); 
+  int fd = xopenro(*toys.optargs); 
   long p = 1;
 
   xioctl(fd, (toys.optflags & FLAG_f) ? FIFREEZE : FITHAW, &p);
index e6f6c8d..bd73f39 100644 (file)
@@ -28,6 +28,6 @@ static void do_fsync(int fd, char *name)
 
 void fsync_main(void)
 {
-  loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC,
-      0, 0, do_fsync);
+  loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC|WARN_ONLY,
+      0, do_fsync);
 }
index a9d97aa..ffa304c 100644 (file)
@@ -50,9 +50,9 @@ static int draw_char(FILE *fp, wchar_t broiled)
         if (broiled==127) broiled = 32;
         else broiled += 64;
       }
-      printf("%c", broiled);
+      printf("%c", (int)broiled);
       tty_esc("0m");
-    } else printf("%c", broiled);
+    } else printf("%c", (int)broiled);
   }
 
   return 1;
index 098d2cf..9a3f595 100644 (file)
@@ -16,12 +16,16 @@ config INSMOD
 #include "toys.h"
 
 #include <sys/syscall.h>
+#ifdef SYS_finit_module
 #define finit_module(fd, opts, flags) syscall(SYS_finit_module, fd, opts, flags)
+#else
+#define finit_module(a, b, c) (errno = ENOSYS)
+#endif
 #define init_module(mod, len, opts) syscall(SYS_init_module, mod, len, opts)
 
 void insmod_main(void)
 {
-  int fd = !strcmp(*toys.optargs, "-") ? 0 : xopen(*toys.optargs, O_RDONLY);
+  int fd = xopenro(*toys.optargs);
   int i, rc;
 
   i = 1;
index 4e467a9..6b0a757 100644 (file)
@@ -76,7 +76,7 @@ static void loopback_setup(char *device, char *file)
     // mount -o loop depends on found device being at the start of toybuf.
     if (cfd != -1) {
       if (0 <= (i = ioctl(cfd, 0x4C82))) // LOOP_CTL_GET_FREE
-        sprintf(device = toybuf, "/dev/loop%d", i);
+        sprintf(device = toybuf, "/dev/block/loop%d", i);
       close(cfd);
     }
   }
index 077ce75..a067179 100644 (file)
@@ -50,10 +50,9 @@ static int do_lspci(struct dirtree *new)
   if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY)))
     return 0;
 
-  // it's ok for the driver link not to be there, whatever fortify says
   *driver = 0;
   if (toys.optflags & FLAG_k)
-    if (readlinkat(dirfd, "driver", driver, sizeof(driver))) {};
+    readlinkat0(dirfd, "driver", driver, sizeof(driver));
 
   for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) {
     int fd, size = 6 + 2*((toys.optflags & FLAG_e) && p == toybuf);
@@ -122,8 +121,7 @@ void lspci_main(void)
 {
   if (CFG_LSPCI_TEXT && TT.numeric != 1) {
     if (!TT.ids) TT.ids = "/usr/share/misc/pci.ids";
-    if (!(TT.db = fopen(TT.ids, "r")))
-      perror_msg("could not open PCI ID db");
+    if (!(TT.db = fopen(TT.ids, "r"))) perror_msg("%s", TT.ids);
   }
 
   dirtree_read("/sys/bus/pci/devices", do_lspci);
index 0f0a661..5e6a982 100644 (file)
@@ -47,7 +47,7 @@ void makedevs_main()
   // Open file and chdir, verbosely
   xprintf("rootdir = %s\n", *toys.optargs);
   if (toys.optflags & FLAG_d && strcmp(TT.fname, "-")) {
-    fd = xopen(TT.fname, O_RDONLY);
+    fd = xopenro(TT.fname);
     xprintf("table = %s\n", TT.fname);
   } else xprintf("table = <stdin>\n");
   xchdir(*toys.optargs);
@@ -78,8 +78,8 @@ void makedevs_main()
       continue;
     } else mode |= (mode_t[]){S_IFIFO, S_IFCHR, S_IFBLK, 0, 0}[i];
 
-    uid = *user ? xgetpwnamid(user)->pw_uid : getuid();
-    gid = *group ? xgetgrnamid(group)->gr_gid : getgid();
+    uid = *user ? xgetuid(user) : getuid();
+    gid = *group ? xgetgid(group) : getgid();
 
     while (*node == '/') node++; // using relative path
 
@@ -99,7 +99,7 @@ void makedevs_main()
           perror_msg("line %d: file '%s' does not exist", line_no, ptr);
           continue;
         }
-      } else if (mknod(ptr, mode, makedev(major, minor + i*incr))) {
+      } else if (mknod(ptr, mode, dev_makedev(major, minor + i*incr))) {
         perror_msg("line %d: can't create node '%s'", line_no, ptr);
         continue;
       }
index ce1d23c..98e1d30 100644 (file)
@@ -37,7 +37,8 @@ void mountpoint_main(void)
 
   if (toys.optflags & FLAG_x) {
     if (S_ISBLK(st1.st_mode)) {
-      if (!quiet) printf("%u:%u\n", major(st1.st_rdev), minor(st1.st_rdev));
+      if (!quiet)
+        printf("%u:%u\n", dev_major(st1.st_rdev), dev_minor(st1.st_rdev));
 
       return;
     }
@@ -57,7 +58,7 @@ void mountpoint_main(void)
   // absence of a spec I guess that's the expected behavior?
   toys.exitval = !(st1.st_dev != st2.st_dev || st1.st_ino == st2.st_ino);
   if (toys.optflags & FLAG_d)
-    printf("%u:%u\n", major(st1.st_dev), minor(st1.st_dev));
+    printf("%u:%u\n", dev_major(st1.st_dev), dev_minor(st1.st_dev));
   else if (!quiet)
     printf("%s is %sa mountpoint\n", *toys.optargs, toys.exitval ? "not " : "");
 }
index 1375728..78a9d91 100644 (file)
@@ -152,8 +152,7 @@ void unshare_main(void)
           filename = toybuf;
         }
 
-        if (setns(fd = xopen(filename, O_RDONLY), flags[i]))
-          perror_exit("setns");
+        if (setns(fd = xopenro(filename), flags[i])) perror_exit("setns");
         close(fd);
       }
       nsnames += strlen(nsnames)+1;
index 0e95a10..9be67c0 100644 (file)
@@ -68,11 +68,11 @@ void oneit_main(void)
   for (i = 0; i<ARRAY_LEN(pipes); i++) xsignal(pipes[i], oneit_signaled);
 
   if (toys.optflags & FLAG_3) {
-    // Ensure next available filehandle is #3
-    while (open("/", 0) < 3);
+    // Ensure next available filehandles are #3 and #4
+    while (xopen_stdio("/", 0) < 3);
     close(3);
     close(4);
-    if (pipe(pipes)) perror_exit("pipe");
+    xpipe(pipes);
     fcntl(4, F_SETFD, FD_CLOEXEC);
   }
 
index bde16e7..2a72dba 100644 (file)
@@ -20,20 +20,14 @@ void pwdx_main(void)
   char **optargs;
 
   for (optargs = toys.optargs; *optargs; optargs++) {
-    char *path;
-    int num_bytes;
+    char *path = toybuf;
 
-    path = xmprintf("/proc/%s/cwd", *optargs);
-    num_bytes = readlink(path, toybuf, sizeof(toybuf)-1);
-    free(path);
-
-    if (num_bytes==-1) {
+    sprintf(toybuf, "/proc/%d/cwd", atoi(*optargs));
+    if (!readlink0(path, toybuf, sizeof(toybuf))) {
       path = strerror(errno);
       toys.exitval = 1;
-    } else {
-      path = toybuf;
-      toybuf[num_bytes] = 0;
     }
+
     xprintf("%s: %s\n", *optargs, path);
   }
 }
index 4cf7214..1506631 100644 (file)
@@ -20,11 +20,11 @@ static void do_rev(int fd, char *name)
   char *c;
 
   for (;;) {
-    int len, i;
+    unsigned len, i;
 
     if (!(c = get_line(fd))) break;
-    len = strlen(c) - 1;
-    for (i = 0; i <= len/2; i++) {
+    len = strlen(c);
+    if (len--) for (i = 0; i <= len/2; i++) {
       char tmp = c[i];
 
       c[i] = c[len-i];
diff --git a/toys/other/setfattr.c b/toys/other/setfattr.c
new file mode 100644 (file)
index 0000000..080991f
--- /dev/null
@@ -0,0 +1,47 @@
+/* setfattr.c - Write POSIX extended attributes.
+ *
+ * Copyright 2016 Android Open Source Project.
+ *
+ * No standard
+
+USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SETFATTR
+  bool "setfattr"
+  default y
+  help
+    usage: setfattr [-h] [-x|-n NAME] [-v VALUE] FILE...
+
+    Write POSIX extended attributes.
+
+    -h Do not dereference symlink.
+    -n Set given attribute.
+    -x Remove given attribute.
+    -v Set value for attribute -n (default is empty).
+*/
+
+#define FOR_setfattr
+#include "toys.h"
+
+GLOBALS(
+  char *x, *v, *n;
+)
+
+static void do_setfattr(char *file)
+{
+  int h = toys.optflags & FLAG_h;
+
+  if (toys.optflags&FLAG_x) {
+    if ((h ? lremovexattr : removexattr)(file, TT.x))
+      perror_msg("removexattr failed");
+  } else 
+    if ((h ? lsetxattr : setxattr)(file, TT.n, TT.v, TT.v?strlen(TT.v):0, 0))
+      perror_msg("setxattr failed");
+}
+
+void setfattr_main(void)
+{
+  char **s;
+
+  for (s=toys.optargs; *s; s++) do_setfattr(*s);
+}
index 5b018ea..30b5e7d 100644 (file)
@@ -42,7 +42,7 @@ void shred_main(void)
   char **try;
 
   if (!(toys.optflags & FLAG_n)) TT.iterations++;
-  TT.ufd = xopen("/dev/urandom", O_RDONLY);
+  TT.ufd = xopenro("/dev/urandom");
 
   // We don't use loopfiles() here because "-" isn't stdin, and want to
   // respond to files we can't open via chmod.
index fb5922c..b5dfcc8 100644 (file)
@@ -2,26 +2,30 @@
  * Copyright 2012 <warior.linux@gmail.com>
  * Copyright 2013 <anand.sinha85@gmail.com>
 
-USE_STAT(NEWTOY(stat, "<1c:f", TOYFLAG_BIN)) 
+USE_STAT(NEWTOY(stat, "<1c:fLt", TOYFLAG_BIN)) 
 
 config STAT
   bool stat
   default y
   help
-    usage: stat [-f] [-c FORMAT] FILE...
+    usage: stat [-tfL] [-c FORMAT] FILE...
 
     Display status of files or filesystems.
 
-    -f display filesystem status instead of file status
-    -c Output specified FORMAT string instead of default
+    -c Output specified FORMAT string instead of default
+    -f display filesystem status instead of file status
+    -L Follow symlinks
+    -t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o")
+             (with -f = -c "%n %i %l %t %s %S %b %f %a %c %d")
 
     The valid format escape sequences for files:
-    %a  Access bits (octal) |%A  Access bits (flags)|%b  Blocks allocated
-    %B  Bytes per block     |%d  Device ID (dec)    |%D  Device ID (hex)
+    %a  Access bits (octal) |%A  Access bits (flags)|%b  Size/512
+    %B  Bytes per %b (512)  |%d  Device ID (dec)    |%D  Device ID (hex)
     %f  All mode bits (hex) |%F  File type          |%g  Group ID
     %G  Group name          |%h  Hard links         |%i  Inode
-    %n  Filename            |%N  Long filename      |%o  I/O block size
-    %s  Size (bytes)        |%u  User ID            |%U  User name
+    %m  Mount point         |%n  Filename           |%N  Long filename
+    %o  I/O block size      |%s  Size (bytes)       |%t  Devtype major (hex)
+    %T  Devtype minor (hex) |%u  User ID            |%U  User name
     %x  Access time         |%X  Access unix time   |%y  File write time
     %Y  File write unix time|%z  Dir change time    |%Z  Dir change unix time
 
@@ -29,7 +33,7 @@ config STAT
     %a  Available blocks    |%b  Total blocks       |%c  Total inodes
     %d  Free inodes         |%f  Free blocks        |%i  File system ID
     %l  Max filename length |%n  File name          |%s  Fragment size
-    %S  Best transfer size  |%t  Filesystem type    |%T  Filesystem type name
+    %S  Best transfer size  |%t  FS type (hex)      |%T  FS type (driver name)
 */
 
 #define FOR_stat
@@ -42,10 +46,24 @@ GLOBALS(
     struct stat st;
     struct statfs sf;
   } stat;
-  struct passwd *user_name;
-  struct group *group_name;
+  char *file, *pattern;
+  int patlen;
 )
 
+// Force numeric output to long long instead of manually typecasting everything
+// and safely parse length prefix
+static void out(char c, long long val)
+{
+  sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c);
+  printf(toybuf, val);
+}
+
+// Output string with parsed length prefix
+static void strout(char *val)
+{
+  sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern);
+  printf(toybuf, val);
+}
 
 // Note: the atime, mtime, and ctime fields in struct stat are the start
 // of embedded struct timespec, but posix won't let them use that
@@ -53,16 +71,11 @@ GLOBALS(
 
 static void date_stat_format(struct timespec *ts)
 {
-  strftime(toybuf, sizeof(toybuf), "%Y-%m-%d %H:%M:%S",
+  char *s = toybuf+128;
+  strftime(s, sizeof(toybuf), "%Y-%m-%d %H:%M:%S",
     localtime(&(ts->tv_sec)));
-  xprintf("%s.%09ld", toybuf, ts->tv_nsec);
-}
-
-// Force numeric output to long long instead of manually typecasting everything
-static void out(char c, long long val)
-{
-  sprintf(toybuf, "%%ll%c", c);
-  printf(toybuf, val);
+  sprintf(s+strlen(s), ".%09ld", ts->tv_nsec);
+  strout(s);
 }
 
 static void print_stat(char type)
@@ -74,9 +87,9 @@ static void print_stat(char type)
     char str[11];
 
     mode_to_string(stat->st_mode, str);
-    xprintf("%s", str);
+    strout(str);
   } else if (type == 'b') out('u', stat->st_blocks);
-  else if (type == 'B') out('u', stat->st_blksize);
+  else if (type == 'B') out('d', 512);
   else if (type == 'd') out('d', stat->st_dev);
   else if (type == 'D') out('x', stat->st_dev);
   else if (type == 'f') out('x', stat->st_mode);
@@ -87,20 +100,32 @@ static void print_stat(char type)
 
     for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1;
     if (!stat->st_size && filetype == S_IFREG) t = "regular empty file";
-    xprintf("%s", t);
+    strout(t);
   } else if (type == 'g') out('u', stat->st_gid);
-  else if (type == 'G') xprintf("%8s", TT.group_name->gr_name);
+  else if (type == 'G') strout(getgroupname(stat->st_gid));
   else if (type == 'h') out('u', stat->st_nlink);
   else if (type == 'i') out('u', stat->st_ino);
-  else if (type == 'N') {
-    xprintf("`%s'", *toys.optargs);
+  else if (type == 'm') {
+    struct mtab_list *mt = xgetmountlist(0);
+    dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev;
+
+    // This mount point could exist multiple times, so show oldest.
+    for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) {
+      strout(mt->dir);
+      break;
+    }
+    llist_traverse(mt, free);
+  } else if (type == 'N') {
+    xprintf("`%s'", TT.file);
     if (S_ISLNK(stat->st_mode))
-      if (0<readlink(*toys.optargs, toybuf, sizeof(toybuf)))
+      if (readlink0(TT.file, toybuf, sizeof(toybuf)))
         xprintf(" -> `%s'", toybuf);
   } else if (type == 'o') out('u', stat->st_blksize);
   else if (type == 's') out('u', stat->st_size);
+  else if (type == 't') out('x', dev_major(stat->st_rdev));
+  else if (type == 'T') out('x', dev_minor(stat->st_rdev));
   else if (type == 'u') out('u', stat->st_uid);
-  else if (type == 'U') xprintf("%8s", TT.user_name->pw_name);
+  else if (type == 'U') strout(getusername(stat->st_uid));
   else if (type == 'x') date_stat_format((void *)&stat->st_atime);
   else if (type == 'X') out('u', stat->st_atime);
   else if (type == 'y') date_stat_format((void *)&stat->st_mtime);
@@ -136,48 +161,56 @@ static void print_statfs(char type) {
 
     for (i=0; i<ARRAY_LEN(nn); i++)
       if (nn[i].num == statfs->f_type) s = nn[i].name;
-    fputs(s, stdout);
-  } else if (type == 'i')
-    xprintf("%08x%08x", statfs->f_fsid.__val[0], statfs->f_fsid.__val[1]);
-  else if (type == 's') out('d', statfs->f_frsize);
+    strout(s);
+  } else if (type == 'i') {
+    char buf[32];
+
+    sprintf(buf, "%08x%08x", statfs->f_fsid.__val[0], statfs->f_fsid.__val[1]);
+    strout(buf);
+  } else if (type == 's') out('d', statfs->f_frsize);
   else if (type == 'S') out('d', statfs->f_bsize);
-  else xprintf("?");
+  else strout("?");
 }
 
 void stat_main(void)
 {
-  int flagf = toys.optflags & FLAG_f;
-  char *format = flagf
+  int flagf = toys.optflags & FLAG_f, i;
+  char *format, *f;
+
+  if (toys.optflags&FLAG_t) {
+    format = flagf ? "%n %i %l %t %s %S %b %f %a %c %d" :
+                     "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
+  } else format = flagf
     ? "  File: \"%n\"\n    ID: %i Namelen: %l    Type: %t\n"
       "Block Size: %s    Fundamental block size: %S\n"
       "Blocks: Total: %b\tFree: %f\tAvailable: %a\n"
       "Inodes: Total: %c\tFree: %d"
     : "  File: %N\n  Size: %s\t Blocks: %b\t IO Blocks: %B\t%F\n"
       "Device: %Dh/%dd\t Inode: %i\t Links: %h\n"
-      "Access: (%a/%A)\tUid: (%u/%U)\tGid: (%g/%G)\n"
+      "Access: (%a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n"
       "Access: %x\nModify: %y\nChange: %z";
+  char *terse_format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
 
   if (toys.optflags & FLAG_c) format = TT.fmt;
+  if (toys.optflags & FLAG_t) format = terse_format;
 
-  for (; *toys.optargs; toys.optargs++) {
-    char *f;
-
-    if (flagf && !statfs(*toys.optargs, (void *)&TT.stat));
-    else if (!flagf && !lstat(*toys.optargs, (void *)&TT.stat)) {
-      struct stat *stat = (struct stat*)&TT.stat;
+  for (i = 0; toys.optargs[i]; i++) {
+    int L = toys.optflags & FLAG_L;
 
-      // check user and group name
-      TT.user_name = getpwuid(stat->st_uid);
-      TT.group_name = getgrgid(stat->st_gid);
-    } else {
-      perror_msg("'%s'", *toys.optargs);
+    TT.file = toys.optargs[i];
+    if (flagf && !statfs(TT.file, (void *)&TT.stat));
+    else if (flagf || (L ? stat : lstat)(TT.file, (void *)&TT.stat)) {
+      perror_msg("'%s'", TT.file);
       continue;
     }
 
     for (f = format; *f; f++) {
       if (*f != '%') putchar(*f);
       else {
-        if (*++f == 'n') xprintf("%s", *toys.optargs);
+        f = next_printf(f, &TT.pattern);
+        TT.patlen = f-TT.pattern;
+        if (TT.patlen>99) error_exit("bad %s", TT.pattern);
+        if (*f == 'n') strout(TT.file);
         else if (flagf) print_statfs(*f);
         else print_stat(*f);
       }
index a123110..ad643c9 100644 (file)
@@ -50,7 +50,7 @@ static void key_error(char *key)
 
 static int write_key(char *path, char *key, char *value)
 {
-  int fd = open(path, O_WRONLY);;
+  int fd = open(path, O_WRONLY);
 
   if (fd < 0) {
     key_error(key);
index 0e912f7..e39dc7a 100644 (file)
@@ -39,7 +39,8 @@ GLOBALS(
 
 static void handler(int i)
 {
-  fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
+  if (toys.optflags & FLAG_v)
+    fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
   kill(TT.pid, TT.nextsig);
   
   if (TT.k_timeout) {
index bfe1f10..6092d5a 100644 (file)
@@ -53,13 +53,13 @@ static void do_truncate(int fd, char *name)
 
 void truncate_main(void)
 {
-  int cr = !(toys.optflags&1);
+  int cr = !(toys.optflags&FLAG_c);
 
   if (-1 != (TT.type = stridx("+-<>/%", *TT.s))) TT.s++;
   TT.size = atolx(TT.s);
 
   // Create files with mask rwrwrw.
   // Nonexistent files are only an error if we're supposed to create them.
-  loopfiles_rw(toys.optargs, O_WRONLY|O_CLOEXEC|(cr ? O_CREAT : 0), 0666, cr,
-    do_truncate);
+  loopfiles_rw(toys.optargs, O_WRONLY|O_CLOEXEC|(cr ? O_CREAT|WARN_ONLY : 0),
+    0666, do_truncate);
 }
index 91887df..cf5e425 100644 (file)
@@ -10,7 +10,6 @@ USE_UPTIME(NEWTOY(uptime, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 config UPTIME
   bool "uptime"
   default y
-  depends on TOYBOX_UTMPX
   help
     usage: uptime
 
index 0250f8f..99de2ee 100644 (file)
@@ -4,17 +4,14 @@
  *
  * No obvious standard, output looks like:
  * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e  Linux version 3.
- *
- * TODO: support for reversing a hexdump back into the original data.
- * TODO: -s seek
 
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2pr", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config XXD
   bool "xxd"
   default y
   help
-    usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [file]
+    usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]
 
     Hexdump a file to stdout.  If no file is listed, copy from stdin.
     Filename "-" is a synonym for stdin.
@@ -24,12 +21,14 @@ config XXD
     -l n       Limit of n bytes before stopping (default is no limit).
     -p Plain hexdump (30 bytes/line, no grouping).
     -r Reverse operation: turn a hexdump into a binary file.
+    -s n       Skip to offset n.
 */
 
 #define FOR_xxd
 #include "toys.h"
 
 GLOBALS(
+  long s;
   long g;
   long l;
   long c;
@@ -38,9 +37,17 @@ GLOBALS(
 static void do_xxd(int fd, char *name)
 {
   long long pos = 0;
+  long long limit = TT.l;
   int i, len, space;
 
-  while (0<(len = readall(fd, toybuf, (TT.l && TT.l-pos<TT.c)?TT.l-pos:TT.c))) {
+  if (toys.optflags&FLAG_s) {
+    xlseek(fd, TT.s, SEEK_SET);
+    pos = TT.s;
+    if (limit) limit += TT.s;
+  }
+
+  while (0<(len = readall(fd, toybuf,
+                          (limit && limit-pos<TT.c)?limit-pos:TT.c))) {
     if (!(toys.optflags&FLAG_p)) printf("%08llx: ", pos);
     pos += len;
     space = 2*TT.c+TT.c/TT.g+1;
index e725112..6b57c3c 100644 (file)
@@ -245,7 +245,7 @@ void arp_main(void)
   if ((toys.optflags & FLAG_d) && !delete_entry()) return; 
 
   //show arp chache
-  fd = xopen("/proc/net/arp", O_RDONLY);
+  fd = xopenro("/proc/net/arp");
   buf = get_line(fd);
   free(buf); //skip first line
 
index d903405..7e5a136 100644 (file)
@@ -10,6 +10,7 @@ USE_BOOTCHARTD(NEWTOY(bootchartd, 0, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
 config BOOTCHARTD
   bool "bootchartd"
   default n
+  depends on TOYBOX_FORK
   help
     usage: bootchartd {start [PROG ARGS]}|stop|init
 
diff --git a/toys/pending/chrt.c b/toys/pending/chrt.c
new file mode 100644 (file)
index 0000000..0c25f48
--- /dev/null
@@ -0,0 +1,92 @@
+/* chrt.c - Get/set real-time (scheduling) attributes
+ *
+ * Copyright 2016 The Android Open Source Project
+
+USE_CHRT(NEWTOY(chrt, "mp#bfiorR[!bfior]", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config CHRT
+  bool "chrt"
+  default y
+  help
+    usage: chrt [-m] [-p PID] [POLICY PRIO] [COMMAND [ARGS...]]
+
+    Get/set a process' real-time (scheduling) attributes.
+
+    -p Apply to given pid
+    -R Set SCHED_RESET_ON_FORK
+    -m Show min/max priorities available
+
+    Policies:
+      -b  SCHED_BATCH    -f  SCHED_FIFO    -i  SCHED_IDLE
+      -o  SCHED_OTHER    -r  SCHED_RR
+*/
+
+#define FOR_chrt
+#include "toys.h"
+
+#include <linux/sched.h>
+
+GLOBALS(
+  long pid;
+)
+
+static char *policy_name(int policy) {
+  char *policy_names[] = { "SCHED_OTHER", "SCHED_FIFO", "SCHED_RR",
+    "SCHED_BATCH", "4", "SCHED_IDLE", "SCHED_DEADLINE" };
+
+  return policy < ARRAY_LEN(policy_names) ? policy_names[policy] : "???";
+}
+
+void chrt_main(void)
+{
+  int policy = SCHED_RR;
+  struct sched_param p;
+
+  // Show min/maxes?
+  if (toys.optflags&FLAG_m) {
+    for (policy = SCHED_OTHER; policy <= SCHED_IDLE; ++policy)
+      if (policy != 4) // There's an unused hole in the priorities.
+        printf("%s min/max priority\t: %d/%d\n", policy_name(policy),
+               sched_get_priority_min(policy), sched_get_priority_max(policy));
+    return;
+  }
+
+  // If we have a pid but no command or policy, we're just querying.
+  if (TT.pid && !*(toys.optargs+1) &&
+      !(toys.optflags&(FLAG_b|FLAG_f|FLAG_i|FLAG_o|FLAG_r))) {
+    policy = sched_getscheduler(TT.pid);
+    if (policy == -1) perror_exit("sched_getscheduler");
+    policy &= ~SCHED_RESET_ON_FORK;
+    printf("pid %ld's current scheduling policy: %s\n",
+           TT.pid, policy_name(policy));
+
+    if (sched_getparam(TT.pid, &p)) perror_exit("sched_getparam");
+    printf("pid %ld's current scheduling priority: %d\n",
+           TT.pid, p.sched_priority);
+
+    return;
+  }
+
+  // Did we get a meaningful combination of arguments?
+  if (!*toys.optargs) help_exit("missing priority");
+  if (TT.pid && *(toys.optargs+1)) help_exit("-p and command");
+  if (!TT.pid && !*(toys.optargs+1)) help_exit("missing command");
+
+  // Translate into policy and priority.
+  if (toys.optflags&FLAG_b) policy = SCHED_BATCH;
+  else if (toys.optflags&FLAG_f) policy = SCHED_FIFO;
+  else if (toys.optflags&FLAG_i) policy = SCHED_IDLE;
+  else if (toys.optflags&FLAG_o) policy = SCHED_OTHER;
+
+  if (toys.optflags&FLAG_R) policy |= SCHED_RESET_ON_FORK;
+
+  p.sched_priority = atolx_range(*toys.optargs, sched_get_priority_min(policy),
+                                 sched_get_priority_max(policy));
+
+  if (sched_setscheduler(TT.pid, policy, &p)) perror_exit("sched_setscheduler");
+
+  if (*(toys.optargs+1)) {
+    toys.stacktop = 0;
+    xexec(++toys.optargs);
+  }
+}
index 1b5e419..df8b5f1 100644 (file)
@@ -79,10 +79,11 @@ static void loginfo(uint8_t loglevel, char *msg, ...)
 
     if (!TT.flagd && TT.logfile) {
       int fd = open(TT.logfile, O_WRONLY | O_CREAT | O_APPEND, 0666);
-      if (fd >=0 && fd != 2) {
+      if (fd==-1) perror_msg("'%s", TT.logfile);
+      else {
         dup2(fd, 2);
         close(fd);
-      } else if (fd < 0) perror_msg("'%s", TT.logfile);
+      }
     }
     used = vsnprintf(NULL, 0, msg, d);
     smsg = xzalloc(++used);
index 67c8a54..0b1c47d 100644 (file)
@@ -9,6 +9,7 @@ USE_CRONTAB(NEWTOY(crontab, "c:u:elr[!elr]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STA
 config CRONTAB
   bool "crontab"
   default n
+  depends on TOYBOX_FORK
   help
     usage: crontab [-u user] FILE
                    [-u user] [-e | -l | -r]
@@ -113,7 +114,7 @@ static int validate_component(int min, int max, char *src)
 static int parse_crontab(char *fname)
 {
   char *line;
-  int lno, fd = xopen(fname, O_RDONLY);
+  int lno, fd = xopenro(fname);
   long plen = 0;
 
   for (lno = 1; (line = get_rawline(fd, &plen, '\n')); lno++,free(line)) {
@@ -213,8 +214,7 @@ static void do_list(char *name)
   int fdin;
 
   snprintf(toybuf, sizeof(toybuf), "%s%s", TT.cdir, name);
-  if ((fdin = open(toybuf, O_RDONLY)) == -1)
-    error_exit("No crontab for '%s'", name);
+  fdin = xopenro(toybuf);
   xsendfile(fdin, 1);
   xclose(fdin);
 }
@@ -232,7 +232,7 @@ static void update_crontab(char *src, char *dest)
 
   snprintf(toybuf, sizeof(toybuf), "%s%s", TT.cdir, dest);
   fdout = xcreate(toybuf, O_WRONLY|O_CREAT|O_TRUNC, 0600);
-  fdin = xopen(src, O_RDONLY);
+  fdin = xopenro(src);
   xsendfile(fdin, fdout);
   xclose(fdin);
 
@@ -276,7 +276,7 @@ static void do_edit(struct passwd *pwd)
 
   if (!stat(toybuf, &sb)) { // file exists and have some content.
     if (sb.st_size) {
-      srcfd = xopen(toybuf, O_RDONLY);
+      srcfd = xopenro(toybuf);
       xsendfile(srcfd, destfd);
       xclose(srcfd);
     }
index 24d9565..0bb4849 100644 (file)
@@ -4,14 +4,17 @@
  * Copyright 2013 Kyungwan Han <asura321@gmail.com>
  *
  * See  http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
+ *
+ * todo: ctrl-c doesn't work, the read() is restarting.
+
 USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 
 config DD
   bool "dd"
   default n
-    help
+  help
     usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
-            [seek=N] [conv=notrunc|noerror|sync|fsync]
+            [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
 
     Options:
     if=FILE   Read from FILE instead of stdin
@@ -26,44 +29,35 @@ config DD
     conv=noerror  Continue after read errors
     conv=sync     Pad blocks with zeros
     conv=fsync    Physically write data out before finishing
+    status=noxfer Don't show transfer rate
+    status=none   Don't show transfer rate or records in/out
 
-    Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),
-    MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)
-    Copy a file, converting and formatting according to the operands.
+    Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),
+    MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).
 */
+
 #define FOR_dd
 #include "toys.h"
 
 GLOBALS(
-  int sig;
-)
-#define C_CONV    0x0000
-#define C_BS      0x0001
-#define C_COUNT   0x0002
-#define C_IBS     0x0004
-#define C_OBS     0x0008
-#define C_IF      0x0010
-#define C_OF      0x0020
-#define C_SEEK    0x0040
-#define C_SKIP    0x0080
+  int show_xfer;
+  int show_records;
+  unsigned long long bytes, c_count, in_full, in_part, out_full, out_part;
+  struct timeval start;
+  struct {
+    char *name;
+    int fd;
+    unsigned char *buff, *bp;
+    long sz, count;
+    unsigned long long offset;
+  } in, out;
+);
+
 #define C_SYNC    0x0100
 #define C_FSYNC   0x0200
 #define C_NOERROR 0x0400
 #define C_NOTRUNC 0x0800
 
-struct io {
-  char *name;
-  int fd;
-  unsigned char *buff, *bp;
-  long sz, count;
-  unsigned long long offset;
-};
-
-struct iostat {
-  unsigned long long in_full, in_part, out_full, out_part, bytes;
-  struct timeval start;
-};
-
 struct pair {
   char *name;
   unsigned val;
@@ -83,266 +77,224 @@ static struct pair clist[] = {
   { "sync",     C_SYNC },
 };
 
-static struct pair operands[] = {
-  // keep the array sorted by name, bsearch() can be used.
-  { "bs",    C_BS   },
-  { "conv",  C_CONV },
-  { "count", C_COUNT},
-  { "ibs",   C_IBS  },
-  { "if",    C_IF   },
-  { "obs",   C_OBS  },
-  { "of",    C_OF   },
-  { "seek",  C_SEEK },
-  { "skip",  C_SKIP },
-};
-
-static struct io in, out;
-static struct iostat st;
-static unsigned long long c_count;
-
-static unsigned long long strsuftoll(char* arg, int def, unsigned long long max)
+static unsigned long long strsuftoll(char *arg, int def, unsigned long long max)
 {
   unsigned long long result;
-  char *endp, *ch = arg;
+  char *p = arg;
   int i, idx = -1;
-  errno = 0;
 
-  while (isspace(*ch)) ch++;
-  if (*ch == '-') error_exit("invalid number '%s'",arg);
-  result = strtoull(arg, &endp, 10);
+  while (isspace(*p)) p++;
+  if (*p == '-') error_exit("invalid number '%s'", arg);
+
+  errno = 0;
+  result = strtoull(p, &p, 0);
   if (errno == ERANGE || result > max || result < def)
-    perror_exit("invalid number '%s'",arg);
-  if (*endp != '\0') {
+    perror_exit("invalid number '%s'", arg);
+  if (*p != '\0') {
     for (i = 0; i < ARRAY_LEN(suffixes); i++)
-      if (!strcmp(endp, suffixes[i].name)) idx = i;
+      if (!strcmp(p, suffixes[i].name)) idx = i;
     if (idx == -1 || (max/suffixes[idx].val < result)) 
-      error_exit("invalid number '%s'",arg);
-    result = result* suffixes[idx].val;
+      error_exit("invalid number '%s'", arg);
+    result *= suffixes[idx].val;
   }
   return result;
 }
 
-static void summary()
+static void status()
 {
-  double seconds = 5.0;
+  double seconds;
   struct timeval now;
 
   gettimeofday(&now, NULL);
-  seconds = ((now.tv_sec * 1000000 + now.tv_usec) - (st.start.tv_sec * 1000000
-        + st.start.tv_usec))/1000000.0;
-  //out to STDERR
-  fprintf(stderr,"%llu+%llu records in\n%llu+%llu records out\n", st.in_full, st.in_part,
-      st.out_full, st.out_part);
-  human_readable(toybuf, st.bytes, HR_SPACE|HR_B);
-  fprintf(stderr, "%llu bytes (%s) copied, ",st.bytes, toybuf);
-  human_readable(toybuf, st.bytes/seconds, HR_SPACE|HR_B);
-  fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
+  seconds = ((now.tv_sec * 1000000 + now.tv_usec) -
+      (TT.start.tv_sec * 1000000 + TT.start.tv_usec))/1000000.0;
+
+  if (TT.show_records)
+    fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
+            TT.in_full, TT.in_part, TT.out_full, TT.out_part);
+
+  if (TT.show_xfer) {
+    human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
+    fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
+    human_readable(toybuf, TT.bytes/seconds, HR_SPACE|HR_B);
+    fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
+  }
 }
 
-static void sig_handler(int sig)
+static void write_out(int all)
 {
-  TT.sig = sig;
+  TT.out.bp = TT.out.buff;
+  while (TT.out.count) {
+    ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
+
+    all = 0; //further writes will be on obs
+    if (nw <= 0) perror_exit("%s: write error", TT.out.name);
+    if (nw == TT.out.sz) TT.out_full++;
+    else TT.out_part++;
+    TT.out.count -= nw;
+    TT.out.bp += nw;
+    TT.bytes += nw;
+    if (TT.out.count < TT.out.sz) break;
+  }
+  if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
 }
 
-static int xmove_fd(int fd)
+int strstarteq(char **a, char *b)
 {
-  int newfd;
+  char *aa = *a;
 
-  if (fd > STDERR_FILENO) return fd;
-  if ((newfd = fcntl(fd, F_DUPFD, 3) < 0)) perror_exit("dupfd IO");
-  close(fd);
-  return newfd;
+  if (!strstart(&aa, b)) return 0;
+  if (*aa != '=') return 0;
+  *a = ++aa;
+
+  return 1;
 }
 
-static void setup_inout()
+static int comp(const void *a, const void *b) //const to shut compiler up
+{
+  return strcmp(((struct pair*)a)->name, ((struct pair*)b)->name);
+}
+
+void dd_main()
 {
-  ssize_t n;
+  struct pair *res, key;
+  char **args;
+  unsigned long long bs = 0;
+  int trunc = O_TRUNC;
+
+  TT.show_xfer = TT.show_records = 1;
+  TT.c_count = ULLONG_MAX;
+
+  TT.in.sz = TT.out.sz = 512; //default io block size
+  for (args = toys.optargs; *args; args++) {
+    char *arg = *args;
+
+    if (strstarteq(&arg, "bs")) bs = strsuftoll(arg, 1, LONG_MAX);
+    else if (strstarteq(&arg, "ibs")) TT.in.sz = strsuftoll(arg, 1, LONG_MAX);
+    else if (strstarteq(&arg, "obs")) TT.out.sz = strsuftoll(arg, 1, LONG_MAX);
+    else if (strstarteq(&arg, "count")) TT.c_count = strsuftoll(arg, 0, ULLONG_MAX-1);
+    else if (strstarteq(&arg, "if")) TT.in.name = arg;
+    else if (strstarteq(&arg, "of")) TT.out.name = arg;
+    else if (strstarteq(&arg, "seek"))
+      TT.out.offset = strsuftoll(arg, 0, ULLONG_MAX);
+    else if (strstarteq(&arg, "skip"))
+      TT.in.offset = strsuftoll(arg, 0, ULLONG_MAX);
+    else if (strstarteq(&arg, "status")) {
+      if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
+      else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
+      else error_exit("unknown status '%s'", arg);
+    } else if (strstarteq(&arg, "conv")) {
+      while (arg) {
+        key.name = strsep(&arg, ",");
+        if (!(res = bsearch(&key, clist, ARRAY_LEN(clist), 
+                sizeof(struct pair), comp)))
+          error_exit("unknown conversion %s", key.name);
+
+        toys.optflags |= res->val;
+      }
+    } else error_exit("bad arg %s", arg);
+  }
+  if (bs) TT.in.sz = TT.out.sz = bs;
+
+  signal(SIGINT, generic_signal);
+  signal(SIGUSR1, generic_signal);
+  gettimeofday(&TT.start, NULL);
 
-  /* for C_BS, in/out is done as it is. so only in.sz is enough.
+  /* for bs=, in/out is done as it is. so only in.sz is enough.
    * With Single buffer there will be overflow in a read following partial read
    */
-  in.buff = out.buff = xmalloc(in.sz + ((toys.optflags & C_BS)? 0: out.sz));
-  in.bp = out.bp = in.buff;
-  atexit(summary);
+  TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
+  TT.in.bp = TT.out.bp = TT.in.buff;
   //setup input
-  if (!in.name) {
-    in.name = "stdin";
-    in.fd = STDIN_FILENO;
-  } else {
-    in.fd = xopen(in.name, O_RDONLY);
-    in.fd = xmove_fd(in.fd);
-  }
-  //setup outout
-  if (!out.name) {
-    out.name = "stdout";
-    out.fd = STDOUT_FILENO;
-  } else {
-    out.fd = xcreate(out.name, O_WRONLY | O_CREAT, 0666);
-    out.fd = xmove_fd(out.fd);
-  }
-
-  if (in.offset) {
-    if (lseek(in.fd, (off_t)(in.offset * in.sz), SEEK_CUR) < 0) {
-      while (in.offset--) {
-        if ((n = read(in.fd, in.bp, in.sz)) < 0) {
-          if (toys.optflags & C_NOERROR) { //warn message and summary
-            error_msg("%s: read error", in.name);
-            summary();
-          } else perror_exit("%s: read error", in.name);
+  if (!TT.in.name) TT.in.name = "stdin";
+  else TT.in.fd = xopenro(TT.in.name);
+
+  if (toys.optflags&C_NOTRUNC) trunc = 0;
+
+  //setup output
+  if (!TT.out.name) {
+    TT.out.name = "stdout";
+    TT.out.fd = 1;
+  } else TT.out.fd = xcreate(TT.out.name,
+    O_WRONLY|O_CREAT|(trunc*!TT.out.offset), 0666);
+
+  // Implement skip=
+  if (TT.in.offset) {
+    if (lseek(TT.in.fd, (off_t)(TT.in.offset * TT.in.sz), SEEK_CUR) < 0) {
+      while (TT.in.offset--) {
+        ssize_t n = read(TT.in.fd, TT.in.bp, TT.in.sz);
+
+        if (n < 0) {
+          perror_msg("%s", TT.in.name);
+          if (toys.optflags & C_NOERROR) status();
+          else return;
         } else if (!n) {
-          xprintf("%s: Can't skip\n", in.name);
-          exit(0);
+          xprintf("%s: Can't skip\n", TT.in.name);
+          return;
         }
       }
     }
   }
 
-  if (out.offset) xlseek(out.fd, (off_t)(out.offset * out.sz), SEEK_CUR);
-}
-
-static void write_out(int all)
-{
-  ssize_t nw;
-  out.bp = out.buff;
-  while (out.count) {
-    nw = writeall(out.fd, out.bp, ((all)? out.count : out.sz));
-    all = 0; //further writes will be on obs
-    if (nw <= 0) perror_exit("%s: write error",out.name);
-    if (nw == out.sz) st.out_full++;
-    else st.out_part++;
-    out.count -= nw;
-    out.bp += nw;
-    st.bytes += nw;
-    if (out.count < out.sz) break;
+  // seek/truncate as necessary. We handled position zero truncate with
+  // O_TRUNC on open, so output to /dev/null and such doesn't error.
+  if (TT.out.fd!=1 && (bs = TT.out.offset*TT.out.sz)) {
+    xlseek(TT.out.fd, bs, SEEK_CUR);
+    if (trunc && ftruncate(TT.out.fd, bs)) perror_exit("ftruncate");
   }
-  if (out.count) memmove(out.buff, out.bp, out.count); //move remainder to front
-}
 
-static void do_dd(void)
-{
-  ssize_t n;
-  struct sigaction sa;
-
-  memset(&sa, 0, sizeof(sa));
-  sa.sa_handler = sig_handler;
-  sigaction(SIGINT, &sa, NULL);
-  sigaction(SIGUSR1, &sa, NULL);
-  setup_inout();
-  gettimeofday(&st.start, NULL);
-
-  if (toys.optflags & (C_OF | C_SEEK) && !(toys.optflags & C_NOTRUNC))
-    ftruncate(out.fd, (off_t)out.offset * out.sz);
-
-  while (!(toys.optflags & C_COUNT) || (st.in_full + st.in_part) < c_count) {
-    if (TT.sig == SIGUSR1) {
-      summary();
-      TT.sig = 0;
-    } else if (TT.sig == SIGINT) exit(TT.sig | 128);
-    in.bp = in.buff + in.count;
-    if (toys.optflags & C_SYNC) memset(in.bp, 0, in.sz);
-    if (!(n = read(in.fd, in.bp, in.sz))) break;
+  while (TT.c_count==ULLONG_MAX || (TT.in_full + TT.in_part) < TT.c_count) {
+    ssize_t n;
+
+    // Show progress and exit on SIGINT or just continue on SIGUSR1.
+    if (toys.signal) {
+      status();
+      if (toys.signal==SIGINT) exit_signal(toys.signal);
+      toys.signal = 0;
+    }
+
+    TT.in.bp = TT.in.buff + TT.in.count;
+    if (toys.optflags & C_SYNC) memset(TT.in.bp, 0, TT.in.sz);
+    if (!(n = read(TT.in.fd, TT.in.bp, TT.in.sz))) break;
     if (n < 0) { 
       if (errno == EINTR) continue;
       //read error case.
-      perror_msg("%s: read error", in.name);
+      perror_msg("%s: read error", TT.in.name);
       if (!(toys.optflags & C_NOERROR)) exit(1);
-      summary();
-      xlseek(in.fd, in.sz, SEEK_CUR);
+      status();
+      xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
       if (!(toys.optflags & C_SYNC)) continue;
       // if SYNC, then treat as full block of nuls
-      n = in.sz;
+      n = TT.in.sz;
     }
-    if (n == in.sz) {
-      st.in_full++;
-      in.count += n;
+    if (n == TT.in.sz) {
+      TT.in_full++;
+      TT.in.count += n;
     } else {
-      st.in_part++;
-      if (toys.optflags & C_SYNC) in.count += in.sz;
-      else in.count += n;
+      TT.in_part++;
+      if (toys.optflags & C_SYNC) TT.in.count += TT.in.sz;
+      else TT.in.count += n;
     }
 
-    out.count = in.count;
-    if (toys.optflags & C_BS) {
+    TT.out.count = TT.in.count;
+    if (bs) {
       write_out(1);
-      in.count = 0;
+      TT.in.count = 0;
       continue;
     }
 
-    if (in.count >= out.sz) {
+    if (TT.in.count >= TT.out.sz) {
       write_out(0);
-      in.count = out.count;
+      TT.in.count = TT.out.count;
     }
   }
-  if (out.count) write_out(1); //write any remaining input blocks
-  if (toys.optflags & C_FSYNC && fsync(out.fd) < 0) 
-    perror_exit("%s: fsync fail", out.name);
-
-  close(in.fd);
-  close(out.fd);
-  if (in.buff) free(in.buff);
-}
-
-static int comp(const void *a, const void *b) //const to shut compiler up
-{
-  return strcmp(((struct pair*)a)->name, ((struct pair*)b)->name);
-}
+  if (TT.out.count) write_out(1); //write any remaining input blocks
+  if (toys.optflags & C_FSYNC && fsync(TT.out.fd) < 0) 
+    perror_exit("%s: fsync fail", TT.out.name);
 
-void dd_main()
-{
-  struct pair *res, key;
-  char *arg;
-  long sz;
-
-  in.sz = out.sz = 512; //default io block size
-  while (*toys.optargs) {
-    if (!(arg = strchr(*toys.optargs, '='))) error_exit("unknown arg %s", *toys.optargs);
-    *arg++ = '\0';
-    if (!*arg) help_exit(0);
-    key.name = *toys.optargs;
-    if (!(res = bsearch(&key, operands, ARRAY_LEN(operands), sizeof(struct pair),
-            comp))) error_exit("unknown arg %s", key.name);
-
-    toys.optflags |= res->val;
-    switch(res->val) {
-      case C_BS:
-        in.sz = out.sz = strsuftoll(arg, 1, LONG_MAX);
-        break;
-      case C_IBS:
-        sz = strsuftoll(arg, 1, LONG_MAX);
-        if (!(toys.optflags & C_BS)) in.sz = sz;
-        break;
-      case C_OBS:
-        sz = strsuftoll(arg, 1, LONG_MAX);
-        if (!(toys.optflags & C_BS)) out.sz = sz;
-        break;
-      case C_COUNT:
-        c_count = strsuftoll(arg, 0, ULLONG_MAX);
-        break;
-      case C_IF:
-        in.name = arg;
-        break;
-      case C_OF:
-        out.name = arg;
-        break;
-      case C_SEEK:
-        out.offset = strsuftoll(arg, 0, ULLONG_MAX);
-        break;
-      case C_SKIP:
-        in.offset = strsuftoll(arg, 0, ULLONG_MAX);
-        break;
-      case C_CONV:
-        while (arg) {
-          key.name = strsep(&arg, ",");
-          if (!(res = bsearch(&key, clist, ARRAY_LEN(clist), 
-                  sizeof(struct pair), comp)))
-            error_exit("unknown conversion %s", key.name);
-
-          toys.optflags |= res->val;
-        }            
-        break;
-    }
-    toys.optargs++;
-  }
+  close(TT.in.fd);
+  close(TT.out.fd);
+  if (TT.in.buff) free(TT.in.buff);
 
-  do_dd();
+  status();
 }
index cb9d15a..b99bb4c 100644 (file)
@@ -566,7 +566,9 @@ static void run_script(dhcpc_result_t *res,  char *name)
 static uint32_t getxid(void)
 {
   uint32_t randnum;
-  int fd = xopen("/dev/urandom", O_RDONLY);
+  int fd = xopenro("/dev/urandom");
+
+// TODO xreadfile
   xreadall(fd, &randnum, sizeof(randnum));
   xclose(fd);
   return randnum;
index 8023861..53bdbce 100644 (file)
@@ -59,7 +59,7 @@ struct diff {
   long a, b, c, d, prev, suff;
 };
 
-static struct dir {
+static struct dir_t {
   char **list;
   int nr_elm;
 } dir[2];
@@ -69,7 +69,7 @@ struct candidate {
   struct candidate *prev, *next;
 };
 
-static struct file {
+static struct file_t {
   FILE *fp;
   int len;
 } file[2];
@@ -797,8 +797,8 @@ void diff_main(void)
 
   if (S_ISDIR(st[0].st_mode) && S_ISDIR(st[1].st_mode)) {
     for (j = 0; j < 2; j++) {
-      memset(&dir[j], 0, sizeof(dir));
-      dirtree_handle_callback(dirtree_start(files[j], 1), list_dir);
+      memset(&dir[j], 0, sizeof(struct dir_t));
+      dirtree_flagread(files[j], DIRTREE_SYMFOLLOW, list_dir);
       dir[j].nr_elm = TT.size; //size updated in list_dir
       qsort(&(dir[j].list[1]), (TT.size - 1), sizeof(char*), cmp);
 
index 86cb4e7..ef17aab 100644 (file)
@@ -42,7 +42,7 @@ void dumpleases_main(void)
   int i, fd; 
   
   if(!(toys.optflags & FLAG_f)) TT.file = "/var/lib/misc/dhcpd.leases"; //DEF_LEASE_FILE
-  fd = xopen(TT.file, O_RDONLY);
+  fd = xopenro(TT.file);
   xprintf("Mac Address       IP Address      Host Name           Expires %s\n", (toys.optflags & FLAG_a) ? "at" : "in");
   xread(fd, &written_time, sizeof(written_time));
   current_time = time(NULL);
index 763ad02..743a953 100644 (file)
@@ -1,11 +1,22 @@
 /* expr.c - evaluate expression
  *
+ * Copyright 2016 Google Inc.
  * Copyright 2013 Daniel Verkamp <daniel@drv.nu>
  *
  * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
  *
  * The web standard is incomplete (precedence grouping missing), see:
  * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
+ *
+ * eval_expr() uses the recursive "Precedence Climbing" algorithm:
+ *
+ * Clarke, Keith. "The top-down parsing of expressions." University of London.
+ * Queen Mary College. Department of Computer Science and Statistics, 1986.
+ *
+ * http://www.antlr.org/papers/Clarke-expr-parsing-1986.pdf
+ *
+ * Nice explanation and Python implementation:
+ * http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing
 
 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
 
@@ -45,232 +56,205 @@ config EXPR
 #define FOR_expr
 #include "toys.h"
 
-
 GLOBALS(
-  int argidx;
+  char **tok; // current token, not on the stack since recursive calls mutate it
+
+  char *refree;
 )
 
-// Scalar value.
-// If s is NULL, the value is an integer (i).
-// If s is not NULL, the value is a string (s).
+// Scalar value.  If s != NULL, it's a string, otherwise it's an int.
 struct value {
   char *s;
   long long i;
 };
 
-// check if v is the integer 0 or the empty string
-static int is_zero(struct value *v)
-{
-  return v->s ? !*v->s : !v->i;
-}
-
-static char *num_to_str(long long num)
-{
-  static char num_buf[21];
-  snprintf(num_buf, sizeof(num_buf), "%lld", num);
-  return num_buf;
-}
-
-static int cmp(struct value *lhs, struct value *rhs)
-{
-  if (lhs->s || rhs->s) {
-    // at least one operand is a string
-    char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
-    char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
-    return strcmp(ls, rs);
-  } else return lhs->i - rhs->i;
-}
-
-static void re(struct value *lhs, struct value *rhs)
-{
-  regex_t rp;
-  regmatch_t rm[2];
-
-  xregcomp(&rp, rhs->s, 0);
-  if (!regexec(&rp, lhs->s, 2, rm, 0) && rm[0].rm_so == 0) {
-    if (rp.re_nsub > 0 && rm[1].rm_so >= 0) 
-      lhs->s = xmprintf("%.*s", rm[1].rm_eo - rm[1].rm_so, lhs->s+rm[1].rm_so);
-    else {
-      lhs->i = rm[0].rm_eo;
-      lhs->s = 0;
-    }
-  } else {
-    if (!rp.re_nsub) {
-      lhs->i = 0;
-      lhs->s = 0;
-    } else lhs->s = "";
-  }
-}
-
-static void mod(struct value *lhs, struct value *rhs)
-{
-  if (lhs->s || rhs->s) error_exit("non-integer argument");
-  if (is_zero(rhs)) error_exit("division by zero");
-  lhs->i %= rhs->i;
-}
-
-static void divi(struct value *lhs, struct value *rhs)
-{
-  if (lhs->s || rhs->s) error_exit("non-integer argument");
-  if (is_zero(rhs)) error_exit("division by zero");
-  lhs->i /= rhs->i;
-}
-
-static void mul(struct value *lhs, struct value *rhs)
-{
-  if (lhs->s || rhs->s) error_exit("non-integer argument");
-  lhs->i *= rhs->i;
-}
-
-static void sub(struct value *lhs, struct value *rhs)
-{
-  if (lhs->s || rhs->s) error_exit("non-integer argument");
-  lhs->i -= rhs->i;
-}
-
-static void add(struct value *lhs, struct value *rhs)
+// Get the value as a string.
+char *get_str(struct value *v)
 {
-  if (lhs->s || rhs->s) error_exit("non-integer argument");
-  lhs->i += rhs->i;
+  if (v->s) return v->s;
+  else return xmprintf("%lld", v->i);
 }
 
-static void ne(struct value *lhs, struct value *rhs)
+// Get the value as an integer and return 1, or return 0 on error.
+int get_int(struct value *v, long long *ret)
 {
-  lhs->i = cmp(lhs, rhs) != 0;
-  lhs->s = NULL;
-}
+  if (v->s) {
+    char *endp;
 
-static void lte(struct value *lhs, struct value *rhs)
-{
-  lhs->i = cmp(lhs, rhs) <= 0;
-  lhs->s = NULL;
-}
+    *ret = strtoll(v->s, &endp, 10);
 
-static void lt(struct value *lhs, struct value *rhs)
-{
-  lhs->i = cmp(lhs, rhs) < 0;
-  lhs->s = NULL;
-}
+    if (*endp) return 0; // If endp points to NUL, all chars were converted
+  } else *ret = v->i;
 
-static void gte(struct value *lhs, struct value *rhs)
-{
-  lhs->i = cmp(lhs, rhs) >= 0;
-  lhs->s = NULL;
+  return 1;
 }
 
-static void gt(struct value *lhs, struct value *rhs)
+// Preserve the invariant that v.s is NULL when the value is an integer.
+void assign_int(struct value *v, long long i)
 {
-  lhs->i = cmp(lhs, rhs) > 0;
-  lhs->s = NULL;
+  v->i = i;
+  v->s = NULL;
 }
 
-static void eq(struct value *lhs, struct value *rhs)
+// Check if v is 0 or the empty string.
+static int is_false(struct value *v)
 {
-  lhs->i = !cmp(lhs, rhs);
-  lhs->s = NULL;
+  return get_int(v, &v->i) && !v->i;
 }
 
-static void and(struct value *lhs, struct value *rhs)
+// 'ret' is filled with a string capture or int match position.
+static void re(char *target, char *pattern, struct value *ret)
 {
-  if (is_zero(lhs) || is_zero(rhs)) {
-    lhs->i = 0;
-    lhs->s = NULL;
+  regex_t pat;
+  regmatch_t m[2];
+
+  xregcomp(&pat, pattern, 0);
+  // must match at pos 0
+  if (!regexec(&pat, target, 2, m, 0) && !m[0].rm_so) {
+    // Return first parenthesized subexpression as string, or length of match
+    if (pat.re_nsub>0) {
+      ret->s = xmprintf("%.*s", m[1].rm_eo-m[1].rm_so, target+m[1].rm_so);
+      if (TT.refree) free(TT.refree);
+      TT.refree = ret->s;
+    } else assign_int(ret, m[0].rm_eo);
+  } else {
+    if (pat.re_nsub>0) ret->s = "";
+    else assign_int(ret, 0);
   }
+  regfree(&pat);
 }
 
-static void or(struct value *lhs, struct value *rhs)
-{
-  if (is_zero(lhs)) *lhs = *rhs;
-}
+// 4 different signatures of operators.  S = string, I = int, SI = string or
+// int.
+enum { SI_TO_SI = 1, SI_TO_I, I_TO_I, S_TO_SI };
 
-static void get_value(struct value *v)
-{
-  char *endp, *arg;
-
-  if (TT.argidx == toys.optc) {
-    v->i = 0;
-    v->s = ""; // signal end of expression
-    return;
-  }
+enum { OR = 1, AND, EQ, NE, GT, GTE, LT, LTE, ADD, SUB, MUL, DIVI, MOD, RE };
 
-//  can't happen, the increment is after the == test
-//  if (TT.argidx >= toys.optc) error_exit("syntax error");
+// operators grouped by precedence
+static struct op_def {
+  char *tok;
+  char prec, sig, op; // precedence, signature for type coercion, operator ID
+} OPS[] = {
+  // logical ops, precedence 1 and 2, signature SI_TO_SI
+  {"|", 1, SI_TO_SI, OR  },
+  {"&", 2, SI_TO_SI, AND },
+  // comparison ops, precedence 3, signature SI_TO_I
+  {"=", 3, SI_TO_I, EQ }, {"==", 3, SI_TO_I, EQ  }, {"!=", 3, SI_TO_I, NE },
+  {">", 3, SI_TO_I, GT }, {">=", 3, SI_TO_I, GTE },
+  {"<", 3, SI_TO_I, LT }, {"<=", 3, SI_TO_I, LTE }, 
+  // arithmetic ops, precedence 4 and 5, signature I_TO_I
+  {"+", 4, I_TO_I, ADD }, {"-",  4, I_TO_I, SUB },
+  {"*", 5, I_TO_I, MUL }, {"/",  5, I_TO_I, DIVI }, {"%", 5, I_TO_I, MOD },
+  // regex match, precedence 6, signature S_TO_SI
+  {":", 6, S_TO_SI, RE },
+  {NULL, 0, 0, 0}, // sentinel
+};
 
-  arg = toys.optargs[TT.argidx++];
+void eval_op(struct op_def *o, struct value *ret, struct value *rhs)
+{
+  long long a, b, x = 0; // x = a OP b for ints.
+  char *s, *t; // string operands
+  int cmp;
 
-  v->i = strtoll(arg, &endp, 10);
-  v->s = *endp ? arg : NULL;
-}
+  switch (o->sig) {
 
-// check if v matches a token, and consume it if so
-static int match(struct value *v, char *tok)
-{
-  if (v->s && !strcmp(v->s, tok)) {
-    get_value(v);
-    return 1;
+  case SI_TO_SI:
+    switch (o->op) {
+    case OR:  if (is_false(ret)) *ret = *rhs; break;
+    case AND: if (is_false(ret) || is_false(rhs)) assign_int(ret, 0); break;
+    }
+    break;  
+
+  case SI_TO_I:
+    if (get_int(ret, &a) && get_int(rhs, &b)) { // both are ints
+      cmp = a - b;
+    } else { // otherwise compare both as strings
+      cmp = strcmp(s = get_str(ret), t = get_str(rhs));
+      if (ret->s != s) free(s);
+      if (rhs->s != t) free(t);
+    }
+    switch (o->op) {
+    case EQ:  x = cmp == 0; break;
+    case NE:  x = cmp != 0; break;
+    case GT:  x = cmp >  0; break;
+    case GTE: x = cmp >= 0; break;
+    case LT:  x = cmp <  0; break;
+    case LTE: x = cmp <= 0; break;
+    }
+    assign_int(ret, x);
+    break;
+
+  case I_TO_I:
+    if (!get_int(ret, &a) || !get_int(rhs, &b))
+      error_exit("non-integer argument");
+    switch (o->op) {
+    case ADD: x = a + b; break;
+    case SUB: x = a - b; break;
+    case MUL: x = a * b; break;
+    case DIVI: if (b == 0) error_exit("division by zero"); x = a / b; break;
+    case MOD:  if (b == 0) error_exit("division by zero"); x = a % b; break;
+    }
+    assign_int(ret, x);
+    break;
+
+  case S_TO_SI: // op == RE
+    s = get_str(ret);
+    cmp = ret->s!=s; // ret overwritten by re so check now
+    re(s, t = get_str(rhs), ret);
+    if (cmp) free(s);
+    if (rhs->s!=t) free(t);
+    break;
   }
-
-  return 0;
 }
 
-// operators in order of increasing precedence
-static struct op {
-  char *tok;
-
-  // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
-  void (*calc)(struct value *lhs, struct value *rhs);
-} ops[] = {
-  {"|",   or  }, {"&",   and }, {"=",   eq  }, {"==",  eq  }, {">",   gt  },
-  {">=",  gte }, {"<",   lt  }, {"<=",  lte }, {"!=",  ne  }, {"+",   add },
-  {"-",   sub }, {"*",   mul }, {"/",   divi}, {"%",   mod }, {":",   re  },
-  {"(",   NULL}, // special case - must be last
-};
-
-// "|,&,= ==> >=< <= !=,+-,*/%,:"
-
-static void parse_op(struct value *lhs, struct value *tok, struct op *op)
+// Evalute a compound expression using recursive "Precedence Climbing"
+// algorithm, setting 'ret'.
+static void eval_expr(struct value *ret, int min_prec)
 {
-  if (!op) op = ops;
-
-  // special case parsing for parentheses
-  if (*op->tok == '(') {
-    if (match(tok, "(")) {
-      parse_op(lhs, tok, 0);
-      if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
-    } else {
-      // tok is a string or integer - return it and get the next token
-      *lhs = *tok;
-      get_value(tok);
+  if (!*TT.tok) error_exit("Unexpected end of input");
+
+  // Evaluate LHS atom, setting 'ret'.
+  if (!strcmp(*TT.tok, "(")) { // parenthesized expression
+    TT.tok++;  // consume (
+
+    eval_expr(ret, 1);        // We're inside ( ), so min_prec = 1
+    if (ret->s && !strcmp(ret->s, ")")) error_exit("empty ( )");
+    if (!*TT.tok) error_exit("Expected )");
+    if (strcmp(*TT.tok, ")")) error_exit("Expected ) but got %s", *TT.tok);
+  } else ret->s = *TT.tok;  // simple literal, all values start as strings
+  TT.tok++;
+
+  // Evaluate RHS and apply operator until precedence is too low.
+  struct value rhs;
+  while (*TT.tok) {
+    struct op_def *o = OPS;
+
+    while (o->tok) { // Look up operator
+      if (!strcmp(*TT.tok, o->tok)) break;
+      o++;
     }
+    if (!o->tok) break; // Not an operator (extra input will fail later)
+    if (o->prec < min_prec) break; // Precedence too low, pop a stack frame
+    TT.tok++;
 
-    return;
-  }
-
-  parse_op(lhs, tok, op + 1);
-  while (match(tok, op->tok)) {
-    struct value rhs;
-    parse_op(&rhs, tok, op + 1);
-    if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
-    op->calc(lhs, &rhs);
+    eval_expr(&rhs, o->prec + 1); // Evaluate RHS, with higher min precedence
+    eval_op(o, ret, &rhs); // Apply operator, setting 'ret'
   }
 }
 
 void expr_main(void)
 {
-  struct value tok, ret = {0};
+  struct value ret = {0};
 
-  toys.exitval = 2; // if exiting early, indicate invalid expression
-
-  TT.argidx = 0;
-
-  get_value(&tok); // warm up the parser with the initial value
-  parse_op(&ret, &tok, 0);
-
-  // final token should be end of expression
-  if (!tok.s || *tok.s) error_exit("syntax error");
+  toys.exitval = 2; // if exiting early, indicate error
+  TT.tok = toys.optargs; // initialize global token
+  eval_expr(&ret, 1);
+  if (*TT.tok) error_exit("Unexpected extra input '%s'\n", *TT.tok);
 
   if (ret.s) printf("%s\n", ret.s);
   else printf("%lld\n", ret.i);
 
-  exit(is_zero(&ret));
+  toys.exitval = is_false(&ret);
+
+  if (TT.refree) free(TT.refree);
 }
index d000c05..08cfa71 100644 (file)
@@ -1486,7 +1486,7 @@ void fdisk_main(void)
     toys.exitval = 0;
     return;
   } else {
-    if (toys.optc != 1) help_exit(stdout);
+    if (toys.optc != 1) help_exit(0);
     if (read_mbr(toys.optargs[0], 1)) return;
     while (1) {
       xputc('\n');
diff --git a/toys/pending/file.c b/toys/pending/file.c
deleted file mode 100644 (file)
index 1d09471..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/* file.c - describe file type
- *
- * Copyright 2016 The Android Open Source Project
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html
- *
- * TODO: ar
-
-USE_FILE(NEWTOY(file, "<1", TOYFLAG_USR|TOYFLAG_BIN))
-
-config FILE
-  bool "file"
-  default n
-  help
-    usage: file [file...]
-
-    Examine the given files and describe their content types.
-*/
-
-#define FOR_file
-#include "toys.h"
-
-GLOBALS(
-  int max_name_len;
-)
-
-// We don't trust elf.h to be there, and two codepaths for 32/64 is awkward
-// anyway, so calculate struct offsets manually. (It's a fixed ABI.)
-static void do_elf_file(int fd)
-{
-  int endian = toybuf[5], bits = toybuf[4], i, j;
-  int64_t (*elf_int)(void *ptr, unsigned size) = peek_le;
-  // Values from include/linux/elf-em.h (plus arch/*/include/asm/elf.h)
-  // Names are linux/arch/ directory name
-  struct {int val; char *name;} type[] = {{0x9026, "alpha"},
-    {40, "arm"}, {183, "arm"}, {0x18ad, "avr32"}, {106, "blackfin"},
-    {76, "cris"}, {0x5441, "frv"}, {46, "h8300"}, {50, "ia64"},//ia intel ftaghn
-    {88, "m32r"}, {4, "m68k"}, {0xbaab, "microblaze"}, {8, "mips"},
-    {10, "mips"}, {89, "mn10300"}, {15, "parisc"}, {22, "s390"},
-    {135, "score"}, {42, "sh"}, {2, "sparc"}, {18, "sparc"}, {43, "sparc"},
-    {187, "tile"}, {188, "tile"}, {191, "tile"}, {3, "x86"}, {6, "x86"},
-    {62, "x86"}, {94, "xtensa"}, {0xabc7, "xtensa"}};
-
-  xprintf("ELF ");
-
-  // "64-bit"
-  if (bits == 1) xprintf("32-bit ");
-  else if (bits == 2) xprintf("64-bit ");
-  else {
-    xprintf("(bad class %d) ", bits);
-    bits = 0;
-  }
-
-  // e_machine, ala "x86", from big table above
-  j = elf_int(toybuf+18, 2);
-  for (i = 0; i<ARRAY_LEN(type); i++) if (j==type[i].val) break;
-  if (i<ARRAY_LEN(type)) xprintf("%s ", type[i].name);
-  else xprintf("(unknown arch %d) ", j);
-
-  // "LSB"
-  if (endian == 1) xprintf("LSB ");
-  else if (endian == 2) {
-    xprintf("MSB ");
-    elf_int = peek_be;
-  } else {
-    xprintf("(bad endian %d)\n", endian);
-    endian = 0;
-  }
-
-  // ", executable"
-  i = elf_int(toybuf+16, 2);
-  if (i == 1) xprintf("relocatable");
-  else if (i == 2) xprintf("executable");
-  else if (i == 3) xprintf("shared object");
-  else if (i == 4) xprintf("core dump");
-  else xprintf("(bad type %d)", i);
-
-  bits--;
-  // If we know our bits and endianness and phentsize agrees show dynamic linker
-  if ((bits&1)==bits && endian &&
-      (i = elf_int(toybuf+42+12*bits, 2)) == 32+24*bits)
-  {
-    char *map, *phdr;
-    int phsize = i, phnum = elf_int(toybuf+44+12*bits, 2),
-        psz = sysconf(_SC_PAGE_SIZE), lib = 0;
-    off_t phoff = elf_int(toybuf+28+4*bits, 4+4*bits),
-          mapoff = phoff^(phoff&(psz-1));
-
-    // map e_phentsize*e_phnum bytes at e_phoff
-    map = mmap(0, phsize*phnum, PROT_READ, MAP_SHARED, fd, mapoff);
-    if (map) {
-      // Find PT_INTERP entry. (Not: fields got reordered for 64 bit)
-      for (i = 0; i<phnum; i++) {
-        long long dlpos, dllen;
-
-        // skip non-PT_INTERP entries
-        j = elf_int(phdr = map+(phoff-mapoff)+i*phsize, 4);
-        if (j==2) lib++;
-        if (j!=3) continue;
-
-        // Read p_offset and p_filesz
-        j = bits+1;
-        dlpos = elf_int(phdr+4*j, 4*j);
-        dllen = elf_int(phdr+16*j, 4*j);
-        if (dllen<0 || dllen>sizeof(toybuf)-128
-            || dlpos!=lseek(fd, dlpos, SEEK_SET)
-            || dllen!=readall(fd, toybuf+128, dllen)) break;
-        printf(", dynamic (%.*s)", (int)dllen, toybuf+128);
-      }
-      if (!lib) printf(", static");
-      else printf(", needs %d lib%s", lib, lib>1 ? "s" : "");
-      munmap(map, phsize*phnum);
-    }
-  }
-
-  // TODO: we'd need to actually parse the ELF file to report the rest...
-  // ", dynamically linked"
-  // " (uses shared libs)"
-  // ", for Linux 2.6.24"
-  // ", BuildID[sha1]=SHA"
-  // ", stripped"
-  xputc('\n');
-}
-
-static void do_regular_file(int fd, char *name)
-{
-  char *s;
-  int len = read(fd, s = toybuf, sizeof(toybuf)-256);
-
-  if (len<0) perror_msg("%s", name);
-
-  if (len>40 && strstart(&s, "\177ELF")) do_elf_file(fd);
-  else if (len>28 && strstart(&s, "\x89PNG\x0d\x0a\x1a\x0a")) {
-    // PNG is big-endian: https://www.w3.org/TR/PNG/#7Integers-and-byte-order
-    int chunk_length = peek_be(s, 4);
-
-    xprintf("PNG image data");
-
-    // The IHDR chunk comes first: https://www.w3.org/TR/PNG/#11IHDR
-    s += 4;
-    if (chunk_length == 13 && strstart(&s, "IHDR")) {
-      // https://www.w3.org/TR/PNG/#6Colour-values
-      char *c = 0, *colors[] = {"grayscale", 0, "color RGB", "indexed color",
-                                "grayscale with alpha", 0, "color RGBA"};
-
-      if (s[9]<ARRAY_LEN(colors)) c = colors[s[9]];
-      if (!c) c = "unknown";
-
-      xprintf(", %d x %d, %d-bit/%s, %sinterlaced", (int)peek_be(s, 4),
-        (int)peek_be(s+4, 4), s[8], c, s[12] ? "" : "non-");
-    }
-
-    xputc('\n');
-
-  // https://www.w3.org/Graphics/GIF/spec-gif89a.txt
-  } else if (len>16 && (strstart(&s, "GIF87a") || strstart(&s, "GIF89a")))
-    xprintf("GIF image data, %d x %d\n",
-      (int)peek_le(s, 2), (int)peek_le(s+8, 2));
-
-  // TODO: parsing JPEG for width/height is harder than GIF or PNG.
-  else if (len>32 && memcmp(toybuf, "\xff\xd8", 2) == 0)
-    xprintf("JPEG image data\n");
-
-  // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
-  else if (len>8 && strstart(&s, "\xca\xfe\xba\xbe"))
-    xprintf("Java class file, version %d.%d\n",
-      (int)peek_be(s+6, 2), (int)peek_be(s, 2));
-
-    // TODO: cpio archive.
-    // TODO: tar archive.
-    // TODO: zip/jar/apk archive.
-  else {
-    char *what = 0;
-    int i, bytes;
-
-    // If shell script, report which interpreter
-    if (len>3 && strstart(&s, "#!")) {
-      for (what = s; (s-toybuf)<len && !isspace(*s); s++);
-      strcpy(s, " script");
-
-    // Distinguish ASCII text, UTF-8 text, or data
-    } else for (i = 0; i<len; ++i) {
-      if (!(isprint(toybuf[i]) || isspace(toybuf[i]))) {
-        wchar_t wc;
-        if ((bytes = mbrtowc(&wc, s+i, len-i, 0))>0 && wcwidth(wc)>=0) {
-          i += bytes-1;
-          if (!what) what = "UTF-8 text";
-        } else {
-          what = "data";
-          break;
-        }
-      }
-    }
-    xputs(what ? what : "ASCII text");
-  }
-}
-
-static void do_file(int fd, char *name)
-{
-  struct stat sb;
-  char *what = "unknown";
-
-  xprintf("%s: %*s", name, (int)(TT.max_name_len - strlen(name)), "");
-
-  if (!fstat(fd, &sb)) what = "cannot open";
-  if (S_ISREG(sb.st_mode)) {
-    if (sb.st_size == 0) what = "empty";
-    else {
-      do_regular_file(fd, name);
-      return;
-    }
-  } else if (S_ISBLK(sb.st_mode)) what = "block special";
-  else if (S_ISCHR(sb.st_mode)) what = "character special";
-  else if (S_ISDIR(sb.st_mode)) what = "directory";
-  else if (S_ISFIFO(sb.st_mode)) what = "fifo";
-  else if (S_ISSOCK(sb.st_mode)) what = "socket";
-  else if (S_ISLNK(sb.st_mode)) what = "symbolic link";
-  xputs(what);
-}
-
-void file_main(void)
-{
-  char **name;
-
-  for (name = toys.optargs; *name; ++name) {
-    int name_len = strlen(*name);
-
-    if (name_len > TT.max_name_len) TT.max_name_len = name_len;
-  }
-
-  loopfiles(toys.optargs, do_file);
-}
diff --git a/toys/pending/getfattr.c b/toys/pending/getfattr.c
new file mode 100644 (file)
index 0000000..efec53a
--- /dev/null
@@ -0,0 +1,95 @@
+/* getfattr.c - Read POSIX extended attributes.
+ *
+ * Copyright 2016 Android Open Source Project.
+ *
+ * No standard
+
+USE_GETFATTR(NEWTOY(getfattr, "dhn:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config GETFATTR
+  bool "getfattr"
+  default y
+  help
+    usage: getfattr [-d] [-h] [-n NAME] FILE...
+
+    Read POSIX extended attributes.
+
+    -d Show values as well as names.
+    -h Do not dereference symbolic links.
+    -n Show only attributes with the given name.
+*/
+
+#define FOR_getfattr
+#include "toys.h"
+
+GLOBALS(
+  char *n;
+)
+
+// TODO: factor out the lister and getter loops and use them in cp too.
+static void do_getfattr(char *file)
+{
+  ssize_t (*getter)(const char *, const char *, void *, size_t) = getxattr;
+  ssize_t (*lister)(const char *, char *, size_t) = listxattr;
+  char **sorted_keys;
+  ssize_t keys_len;
+  char *keys, *key;
+  int i, key_count;
+
+  if (toys.optflags&FLAG_h) {
+    getter = lgetxattr;
+    lister = llistxattr;
+  }
+
+  // Collect the keys.
+  while ((keys_len = lister(file, NULL, 0))) {
+    if (keys_len == -1) perror_msg("listxattr failed");
+    keys = xmalloc(keys_len);
+    if (lister(file, keys, keys_len) == keys_len) break;
+    free(keys);
+  }
+
+  if (keys_len == 0) return;
+
+  // Sort the keys.
+  for (key = keys, key_count = 0; key-keys < keys_len; key += strlen(key)+1)
+    key_count++;
+  sorted_keys = xmalloc(key_count * sizeof(char *));
+  for (key = keys, i = 0; key-keys < keys_len; key += strlen(key)+1)
+    sorted_keys[i++] = key;
+  qsort(sorted_keys, key_count, sizeof(char *), qstrcmp);
+
+  printf("# file: %s\n", file);
+
+  for (i = 0; i < key_count; i++) {
+    key = sorted_keys[i];
+
+    if (TT.n && strcmp(TT.n, key)) continue;
+
+    if (toys.optflags&FLAG_d) {
+      ssize_t value_len;
+      char *value = NULL;
+
+      while ((value_len = getter(file, key, NULL, 0))) {
+        if (value_len == -1) perror_msg("getxattr failed");
+        value = xzalloc(value_len+1);
+        if (getter(file, key, value, value_len) == value_len) break;
+        free(value);
+      }
+
+      if (!value) puts(key);
+      else printf("%s=\"%s\"\n", key, value);
+      free(value);
+    } else puts(key); // Just list names.
+  }
+
+  xputc('\n');
+  free(sorted_keys);
+}
+
+void getfattr_main(void)
+{
+  char **s;
+
+  for (s=toys.optargs; *s; s++) do_getfattr(*s);
+}
index c737628..25d04ea 100644 (file)
@@ -128,7 +128,7 @@ static void open_tty(void)
     if ((setsid() < 0) && (getpid() != getsid(0))) 
       perror_exit("setsid");
     xclose(0);
-    xopen(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
+    xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
     fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read
     dup2(0, 1);
     dup2(0, 2);
index 50de1d3..0f4c0f4 100644 (file)
@@ -29,6 +29,7 @@ GLOBALS(
 )
 
 #include <resolv.h>
+#include "resolv_private.h"
 
 #define PL_IP 1
 #define PL_NAME 2
index 2c84288..d950981 100644 (file)
@@ -74,7 +74,7 @@ void klogd_main(void)
     syslog(LOG_NOTICE, "KLOGD: started with Kernel ring buffer as log source\n");
     klogctl(1, NULL, 0);
   } else {
-    TT.fd = xopen("/proc/kmsg", O_RDONLY); //_PATH_KLOG in paths.h
+    TT.fd = xopenro("/proc/kmsg"); //_PATH_KLOG in paths.h
     syslog(LOG_NOTICE, "KLOGD: started with /proc/kmsg as log source\n");
   }
   openlog("Kernel", 0, LOG_KERN);    //open connection to system logger..
index b207afe..4b7b472 100644 (file)
@@ -96,7 +96,7 @@ void last_main(void)
 
   pwidth = (toys.optflags & FLAG_W) ? 46 : 16;
   *tm = time(tm+1);
-  fd = xopen(file, O_RDONLY);
+  fd = xopenro(file);
   loc = xlseek(fd, 0, SEEK_END);
 
   // Loop through file structures in reverse order.
index 0a9fc9c..436fdd2 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2015 The Android Open Source Project
 
-USE_LSOF(NEWTOY(lsof, "lp:t", TOYFLAG_USR|TOYFLAG_BIN))
+USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
 
 config LSOF
   bool "lsof"
@@ -22,10 +22,11 @@ config LSOF
 #include "toys.h"
 
 GLOBALS(
-  char *pids;
+  struct arg_list *p;
 
   struct stat *sought_files;
 
+  struct double_list *all_sockets;
   struct double_list *files;
   int last_shown_pid;
   int shown_header;
@@ -42,62 +43,51 @@ struct file_info {
 
   // For output.
   struct proc_info pi;
-  char fd[8];
-  char rw;
-  char locks;
-  char type[10];
-  char device[32];
-  char size_off[32];
-  char node[32];
   char* name;
+  char fd[8], rw, locks, type[10], device[32], size_off[32], node[32];
 
   // For filtering.
   dev_t st_dev;
   ino_t st_ino;
 };
 
-static int filter_matches(struct file_info *fi)
-{
-  struct stat *sb = TT.sought_files;
-
-  for (; sb != &(TT.sought_files[toys.optc]); ++sb) {
-    if (sb->st_dev == fi->st_dev && sb->st_ino == fi->st_ino) return 1;
-  }
-  return 0;
-}
-
-static void print_header()
-{
-  // TODO: llist_traverse to measure the columns first.
-  char* names[] = {
-    "COMMAND", "PID", "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME"
-  };
-  printf("%-9s %5s %10.10s %4s   %7s %18s %9s %10s %s\n", names[0], names[1],
-         names[2], names[3], names[4], names[5], names[6], names[7], names[8]);
-  TT.shown_header = 1;
-}
-
 static void print_info(void *data)
 {
   struct file_info *fi = data;
 
-  if (toys.optc && !filter_matches(fi)) return;
+  // Filter matches
+  if (toys.optc) {
+    int i;
+
+    for (i = 0; i<toys.optc; i++)
+      if (TT.sought_files[i].st_dev==fi->st_dev)
+        if (TT.sought_files[i].st_ino==fi->st_ino) break;
+
+    if (i==toys.optc) return;
+  }
 
   if (toys.optflags&FLAG_t) {
     if (fi->pi.pid != TT.last_shown_pid)
-      printf("%d\n", (TT.last_shown_pid = fi->pi.pid));
+      printf("%d\n", TT.last_shown_pid = fi->pi.pid);
   } else {
-    if (!TT.shown_header) print_header();
+    if (!TT.shown_header) {
+      // TODO: llist_traverse to measure the columns first.
+      printf("%-9s %5s %10.10s %4s   %7s %18s %9s %10s %s\n", "COMMAND", "PID",
+        "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME");
+      TT.shown_header = 1;
+    }
+
     printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n",
            fi->pi.cmd, fi->pi.pid, fi->pi.user,
            fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off,
            fi->node, fi->name);
   }
+}
 
-  if (CFG_FREE) {
-    free(((struct file_info *)data)->name);
-    free(data);
-  }
+static void free_info(void *data)
+{
+  free(((struct file_info *)data)->name);
+  free(data);
 }
 
 static void fill_flags(struct file_info *fi)
@@ -121,46 +111,49 @@ static void fill_flags(struct file_info *fi)
   fclose(fp);
 }
 
-static int scan_proc_net_file(char *path, int family, char type,
-    void (*fn)(char *, int, char, struct file_info *, long),
-    struct file_info *fi, long sought_inode)
+static void scan_proc_net_file(char *path, int family, char type,
+    void (*fn)(char *, int, char))
 {
   FILE *fp = fopen(path, "r");
   char *line = NULL;
   size_t line_length = 0;
 
-  if (!fp) return 0;
+  if (!fp) return;
 
-  if (!getline(&line, &line_length, fp)) return 0; // Skip header.
+  if (!getline(&line, &line_length, fp)) return; // Skip header.
 
   while (getline(&line, &line_length, fp) > 0) {
-    fn(line, family, type, fi, sought_inode);
-    if (fi->name != 0) break;
+    fn(line, family, type);
   }
 
   free(line);
   fclose(fp);
+}
 
-  return fi->name != 0;
+static struct file_info *add_socket(ino_t inode, const char *type)
+{
+  struct file_info *fi = xzalloc(sizeof(struct file_info));
+
+  dlist_add_nomalloc(&TT.all_sockets, (struct double_list *)fi);
+  fi->st_ino = inode;
+  strcpy(fi->type, type);
+  return fi;
 }
 
-static void match_unix(char *line, int af, char type, struct file_info *fi,
-                       long sought_inode)
+static void scan_unix(char *line, int af, char type)
 {
   long inode;
   int path_pos;
 
-  if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1 &&
-        inode == sought_inode) {
+  if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1) {
+    struct file_info *fi = add_socket(inode, "unix");
     char *name = chomp(line + path_pos);
 
-    strcpy(fi->type, "unix");
     fi->name = strdup(*name ? name : "socket");
   }
 }
 
-static void match_netlink(char *line, int af, char type, struct file_info *fi,
-                          long sought_inode)
+static void scan_netlink(char *line, int af, char type)
 {
   unsigned state;
   long inode;
@@ -171,18 +164,17 @@ static void match_netlink(char *line, int af, char type, struct file_info *fi,
     "ENCRYPTFS", "RDMA", "CRYPTO"
   };
 
-  if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu",
-             &state, &inode) < 2 || inode != sought_inode) {
+  if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu", &state, &inode)
+      < 2) {
     return;
   }
 
-  strcpy(fi->type, "netlink");
+  struct file_info *fi = add_socket(inode, "netlink");
   fi->name =
       strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?");
 }
 
-static void match_ip(char *line, int af, char type, struct file_info *fi,
-                     long sought_inode)
+static void scan_ip(char *line, int af, char type)
 {
   char *tcp_states[] = {
     "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
@@ -210,9 +202,9 @@ static void match_ip(char *line, int af, char type, struct file_info *fi,
                 &(remote.s6_addr32[2]), &(remote.s6_addr32[3]),
                 &remote_port, &state, &inode) == 12;
   }
-  if (!ok || inode != sought_inode) return;
+  if (!ok) return;
 
-  strcpy(fi->type, af == 4 ? "IPv4" : "IPv6");
+  struct file_info *fi = add_socket(inode, af == 4 ? "IPv4" : "IPv6");
   inet_ntop(af, &local, local_ip, sizeof(local_ip));
   inet_ntop(af, &remote, remote_ip, sizeof(remote_ip));
   if (type == 't') {
@@ -231,18 +223,35 @@ static void match_ip(char *line, int af, char type, struct file_info *fi,
 
 static int find_socket(struct file_info *fi, long inode)
 {
-  // TODO: other protocols (packet).
-  return scan_proc_net_file("/proc/net/tcp", 4, 't', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/tcp6", 6, 't', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/udp", 4, 'u', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/udp6", 6, 'u', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/raw", 4, 'r', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/raw6", 6, 'r', match_ip, fi, inode) ||
-    scan_proc_net_file("/proc/net/unix", 0, 0, match_unix, fi, inode) ||
-    scan_proc_net_file("/proc/net/netlink", 0, 0, match_netlink, fi, inode);
+  static int cached;
+  if (!cached) {
+    scan_proc_net_file("/proc/net/tcp", 4, 't', scan_ip);
+    scan_proc_net_file("/proc/net/tcp6", 6, 't', scan_ip);
+    scan_proc_net_file("/proc/net/udp", 4, 'u', scan_ip);
+    scan_proc_net_file("/proc/net/udp6", 6, 'u', scan_ip);
+    scan_proc_net_file("/proc/net/raw", 4, 'r', scan_ip);
+    scan_proc_net_file("/proc/net/raw6", 6, 'r', scan_ip);
+    scan_proc_net_file("/proc/net/unix", 0, 0, scan_unix);
+    scan_proc_net_file("/proc/net/netlink", 0, 0, scan_netlink);
+    cached = 1;
+  }
+  void* list = TT.all_sockets;
+
+  while (list) {
+    struct file_info *s = (struct file_info*) llist_pop(&list);
+
+    if (s->st_ino == inode) {
+      fi->name = s->name ? strdup(s->name) : NULL;
+      strcpy(fi->type, s->type);
+      return 1;
+    }
+    if (list == TT.all_sockets) break;
+  }
+
+  return 0;
 }
 
-static void fill_stat(struct file_info *fi, const charpath)
+static void fill_stat(struct file_info *fi, const char *path)
 {
   struct stat sb;
   long dev;
@@ -268,8 +277,8 @@ static void fill_stat(struct file_info *fi, const char* path)
   // Fill DEVICE.
   dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev;
   if (!S_ISSOCK(sb.st_mode))
-    snprintf(fi->device, sizeof(fi->device), "%ld,%ld",
-             (long)major(dev), (long)minor(dev));
+    snprintf(fi->device, sizeof(fi->device), "%d,%d",
+             dev_major(dev), dev_minor(dev));
 
   // Fill SIZE/OFF.
   if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
@@ -284,7 +293,7 @@ static void fill_stat(struct file_info *fi, const char* path)
   fi->st_ino = sb.st_ino;
 }
 
-struct file_info *new_file_info(struct proc_info *pi, const charfd)
+struct file_info *new_file_info(struct proc_info *pi, const char *fd)
 {
   struct file_info *fi = xzalloc(sizeof(struct file_info));
 
@@ -300,7 +309,7 @@ struct file_info *new_file_info(struct proc_info *pi, const char* fd)
   return fi;
 }
 
-static void visit_symlink(struct proc_info *pi, char* name, char* path)
+static void visit_symlink(struct proc_info *pi, char *name, char *path)
 {
   struct file_info *fi = new_file_info(pi, "");
 
@@ -382,19 +391,15 @@ static void visit_fds(struct proc_info *pi)
 static void lsof_pid(int pid)
 {
   struct proc_info pi;
-  FILE *fp;
   char *line;
   struct stat sb;
 
-  // Does this process even exist?
-  snprintf(toybuf, sizeof(toybuf), "/proc/%d/stat", pid);
-  fp = fopen(toybuf, "r");
-  if (!fp) return;
+  // Skip nonexistent pids
+  sprintf(toybuf, "/proc/%d/stat", pid);
+  if (!(line = readfile(toybuf, toybuf, sizeof(toybuf)))) return;
 
   // Get COMMAND.
   strcpy(pi.cmd, "?");
-  line = fgets(toybuf, sizeof(toybuf), fp);
-  fclose(fp);
   if (line) {
     char *open_paren = strchr(toybuf, '(');
     char *close_paren = strrchr(toybuf, ')');
@@ -425,35 +430,42 @@ static void lsof_pid(int pid)
   visit_fds(&pi);
 }
 
-static int scan_slash_proc(struct dirtree *node)
+static int scan_proc(struct dirtree *node)
 {
   int pid;
 
-  if (!node->parent) return DIRTREE_RECURSE;
+  if (!node->parent) return DIRTREE_RECURSE|DIRTREE_SHUTUP;
   if ((pid = atol(node->name))) lsof_pid(pid);
+
   return 0;
 }
 
 void lsof_main(void)
 {
+  struct arg_list *pp;
   int i;
 
   // lsof will only filter on paths it can stat (because it filters by inode).
   TT.sought_files = xmalloc(toys.optc*sizeof(struct stat));
-  for (i = 0; i < toys.optc; ++i) {
-    xstat(toys.optargs[i], &(TT.sought_files[i]));
-  }
+  for (i = 0; i<toys.optc; ++i) xstat(toys.optargs[i], TT.sought_files+i);
 
-  if (toys.optflags&FLAG_p) {
-    char *pid_str;
+  if (!TT.p) dirtree_read("/proc", scan_proc);
+  else for (pp = TT.p; pp; pp = pp->next) {
+    char *start, *end, *next = pp->arg;
     int length, pid;
 
-    while ((pid_str = comma_iterate(&TT.pids, &length))) {
-      pid_str[length] = 0;
-      if (!(pid = atoi(pid_str))) error_exit("bad pid '%s'", pid_str);
+    while ((start = comma_iterate(&next, &length))) {
+      pid = strtol(start, &end, 10);
+      if (pid<1 || (*end && *end!=','))
+        error_exit("bad -p '%.*s'", (int)(end-start), start);
       lsof_pid(pid);
     }
-  } else dirtree_read("/proc", scan_slash_proc);
+  }
 
   llist_traverse(TT.files, print_info);
+
+  if (CFG_TOYBOX_FREE) {
+    llist_traverse(TT.files, free_info);
+    llist_traverse(TT.all_sockets, free_info);
+  }
 }
index e7adc99..2688cf3 100644 (file)
@@ -40,8 +40,8 @@ static void make_device(char *path)
   gid_t gid = 0;
 
   if (path) {
-    // Try to read major/minor string
 
+    // Try to read major/minor string, returning if we can't
     temp = strrchr(path, '/');
     fd = open(path, O_RDONLY);
     *temp = 0;
@@ -190,7 +190,7 @@ found_device:
 
   if (strchr(device_name, '/'))
     mkpathat(AT_FDCWD, toybuf, 0, 2);
-  if (mknod(toybuf, mode | type, makedev(major, minor)) && errno != EEXIST)
+  if (mknod(toybuf, mode | type, dev_makedev(major, minor)) && errno != EEXIST)
     perror_exit("mknod %s failed", toybuf);
 
  
index 6813dec..ebf17a0 100644 (file)
@@ -70,7 +70,7 @@ static char *path2mod(char *file, char *mod)
   if (!file) return NULL;
   if (!mod) mod = xmalloc(MODNAME_LEN);
        
-  from = basename_r(file);
+  from = getbasename(file);
   
   for (i = 0; i < (MODNAME_LEN-1) && from[i] && from[i] != '.'; i++)
     mod[i] = (from[i] == '-') ? '_' : from[i];
@@ -367,7 +367,9 @@ static int ins_mod(char *modules, char *flags)
 {
   char *buf = NULL;
   int len, res;
-  int fd = xopen(modules, O_RDONLY);
+  int fd = xopenro(modules);
+
+  // TODO xreadfile()
 
   len = fdlength(fd);
   buf = xmalloc(len);
index f0e7907..7923ee4 100644 (file)
@@ -2,17 +2,17 @@
  *
  * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com>
  *
- * No Standard
+ * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html
 
-USE_MORE(NEWTOY(more, NULL, TOYFLAG_USR|TOYFLAG_BIN))
+USE_MORE(NEWTOY(more, 0, TOYFLAG_USR|TOYFLAG_BIN))
 
 config MORE
   bool "more"
   default n
   help
-    usage: more [FILE]...
+    usage: more [FILE...]
 
-    View FILE (or stdin) one screenful at a time.
+    View FILE(s) (or stdin) one screenful at a time.
 */
 
 #define FOR_more
@@ -25,7 +25,12 @@ GLOBALS(
 
 static void signal_handler(int sig)
 {
+  // Reset the terminal whether we were signalled or exited normally.
   tcsetattr(TT.cin_fd, TCSANOW, &TT.inf);
+
+  if (sig == 0) _exit(0);
+
+  // We were actually signalled, so move to a new line and re-raise the signal.
   xputc('\n');
   signal(sig, SIG_DFL);
   raise(sig);
@@ -62,10 +67,8 @@ static int prompt(FILE *cin, const char* fmt, ...)
 
 static void do_cat_operation(int fd, char *name)
 {
-  char *buf = NULL;
-  
   if (toys.optc > 1) show_file_header(name);
-  for (; (buf = get_line(fd)); free(buf)) printf("%s\n", buf);
+  xsendfile(fd, 1);
 }
 
 void more_main()
@@ -76,7 +79,7 @@ void more_main()
   struct termios newf;
   FILE *fp, *cin;
 
-  if (!isatty(STDOUT_FILENO) || !(cin = fopen("/dev/tty", "r"))) {
+  if (!isatty(1) || !(cin = fopen("/dev/tty", "r"))) {
     loopfiles(toys.optargs, do_cat_operation);
     return;
   }
@@ -125,7 +128,8 @@ void more_main()
       }
 
       putchar(ch);
-      if (ch == '\t') col = (col | 0x7) + 1; else col++;
+      if (ch == '\t') col = (col | 0x7) + 1;
+      else col++;
       if (col == cols) putchar(ch = '\n');
       if (ch == '\n') {
         col = 0;
diff --git a/toys/pending/netstat.c b/toys/pending/netstat.c
deleted file mode 100644 (file)
index d6acd7a..0000000
+++ /dev/null
@@ -1,661 +0,0 @@
-/* netstat.c - Display Linux networking subsystem.
- *
- * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
- * Copyright 2013 Kyungwan Han <asura321@gmail.com>
- *
- * Not in SUSv4.
- *
-USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
-config NETSTAT
-  bool "netstat"
-  default n
-  help
-    usage: netstat [-pWrxwutneal]
-
-    Display networking information.
-
-    -r  Display routing table.
-    -a  Display all sockets (Default: Connected).
-    -l  Display listening server sockets.
-    -t  Display TCP sockets.
-    -u  Display UDP sockets.
-    -w  Display Raw sockets.
-    -x  Display Unix sockets.
-    -e  Display other/more information.
-    -n  Don't resolve names.
-    -W  Wide Display.
-    -p  Display PID/Program name for sockets.
-*/
-
-#define FOR_netstat
-#include "toys.h"
-
-#include <net/route.h>
-
-GLOBALS(
-  char current_name[21];
-  int some_process_unidentified;
-);
-
-typedef union _iaddr {
-  unsigned u;
-  unsigned char b[4];
-} iaddr;
-
-typedef union _iaddr6 {
-  struct {
-    unsigned a;
-    unsigned b;
-    unsigned c;
-    unsigned d;
-  } u;
-  unsigned char b[16];
-} iaddr6;
-
-#define ADDR_LEN (INET6_ADDRSTRLEN + 1 + 5 + 1)//IPv6 addr len + : + port + '\0'
-
-//For unix states
-enum {
-       SOCK_ACCEPTCON = (1 << 16),  //performed a listen.
-       SOCK_WAIT_DATA = (1 << 17),  //wait data to read.
-       SOCK_NO_SPACE = (1 << 18),  //no space to write.
-};
-
-#define SOCK_NOT_CONNECTED 1
-
-typedef struct _pidlist {
-  struct _pidlist *next;
-  long inode;
-  char name[21];
-} PID_LIST;
-
-PID_LIST *pid_list = NULL;
-
-/*
- * used to convert string into int and
- * validate the input str for invalid int value or out-of-range.
- */
-static unsigned long get_strtou(char *str, char **endp, int base)
-{
-  unsigned long uli;
-  char *endptr;
-
-  if (!isalnum(str[0])) {
-    errno = ERANGE;
-    return UINT_MAX;
-  }
-  errno = 0;
-  uli = strtoul(str, &endptr, base);
-  if (uli > UINT_MAX) {
-    errno = ERANGE;
-    return UINT_MAX;
-  }
-
-  if (endp) *endp = endptr;
-  if (endptr[0]) {
-    if (isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
-      errno = ERANGE;
-      return UINT_MAX;
-    }
-    errno = EINVAL;
-  }
-  return uli;
-}
-
-/*
- * used to retrive pid name from pid list.
- */
-static const char *get_pid_name(unsigned long inode)
-{
-  PID_LIST *tmp;
-
-  for (tmp = pid_list; tmp; tmp = tmp->next)
-    if (tmp->inode == inode) return tmp->name;
-
-  return "-";
-}
-
-/*
- * For TCP/UDP/RAW display data.
- */
-static void display_data(unsigned rport, char *label,
-                         unsigned rxq, unsigned txq, char *lip, char *rip,
-                         unsigned state, unsigned uid, unsigned long inode)
-{
-  char *ss_state = "UNKNOWN", buf[12];
-  char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1",
-                         "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT",
-                         "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"};
-  char user[11];
-  struct passwd *pw;
-
-  if (!strcmp(label, "tcp")) {
-    int sz = ARRAY_LEN(state_label);
-    if (!state || state >= sz) state = sz-1;
-    ss_state = state_label[state];
-  }
-  else if (!strcmp(label, "udp")) {
-    if (state == 1) ss_state = state_label[state];
-    else if (state == 7) ss_state = "";
-  }
-  else if (!strcmp(label, "raw")) sprintf(ss_state = buf, "%u", state);
-
-  if (!(toys.optflags & FLAG_n) && (pw = getpwuid(uid))) {
-    snprintf(user, sizeof(user), "%s", pw->pw_name);
-  } else snprintf(user, sizeof(user), "%d", uid);
-
-  xprintf("%3s   %6d %6d ", label, rxq, txq);
-  xprintf((toys.optflags & FLAG_W) ? "%-51.51s %-51.51s " : "%-23.23s %-23.23s "
-           , lip, rip);
-  xprintf("%-11s ", ss_state);
-  if ((toys.optflags & FLAG_e)) xprintf("%-10s %-11ld ", user, inode);
-  if ((toys.optflags & FLAG_p)) xprintf("%s", get_pid_name(inode));
-  xputc('\n');
-}
-
-/*
- * For TCP/UDP/RAW show data.
- */
-static void show_data(unsigned rport, char *label, unsigned rxq, unsigned txq,
-                      char *lip, char *rip, unsigned state, unsigned uid,
-                      unsigned long inode)
-{
-  if (toys.optflags & FLAG_l) {
-    if (!rport && (state & 0xA))
-      display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
-  } else if (toys.optflags & FLAG_a)
-    display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
-  //rport && (TCP | UDP | RAW)
-  else if (rport & (0x10 | 0x20 | 0x40))
-    display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
-}
-
-/*
- * used to get service name.
- */
-static char *get_servname(int port, char *label)
-{
-  int lport = htons(port);
-  if (!lport) return xmprintf("%s", "*");
-  struct servent *ser = getservbyport(lport, label);
-  if (ser) return xmprintf("%s", ser->s_name);
-  return xmprintf("%u", (unsigned)ntohs(lport));
-}
-
-/*
- * used to convert address into text format.
- */
-static void addr2str(int af, void *addr, unsigned port, char *buf, char *label)
-{
-  char ip[ADDR_LEN] = {0,};
-  if (!inet_ntop(af, addr, ip, ADDR_LEN)) {
-    *buf = '\0';
-    return;
-  }
-  size_t iplen = strlen(ip);
-  if (!port) {
-    strncat(ip+iplen, ":*", ADDR_LEN-iplen-1);
-    memcpy(buf, ip, ADDR_LEN);
-    return;
-  }
-
-  if (!(toys.optflags & FLAG_n)) {
-    struct addrinfo hints, *result, *rp;
-
-    memset(&hints, 0, sizeof(struct addrinfo));
-    hints.ai_family = af;
-
-    if (!getaddrinfo(ip, NULL, &hints, &result)) {
-      char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
-      socklen_t sock_len;
-      char *sname = NULL;
-      int plen = 0;
-
-      if (af == AF_INET) sock_len = sizeof(struct sockaddr_in);
-      else sock_len = sizeof(struct sockaddr_in6);
-
-      for (rp = result; rp; rp = rp->ai_next)
-        if (!getnameinfo(rp->ai_addr, sock_len, hbuf, sizeof(hbuf), sbuf,
-            sizeof(sbuf), NI_NUMERICSERV))
-          break;
-
-      freeaddrinfo(result);
-      sname = get_servname(port, label);
-      plen = strlen(sname);
-      if (*hbuf) {
-        memset(ip, 0, ADDR_LEN);
-        memcpy(ip, hbuf, (ADDR_LEN - plen - 2));
-        iplen = strlen(ip);
-      }
-      snprintf(ip + iplen, ADDR_LEN-iplen, ":%s", sname);
-      free(sname);
-    }
-  }
-  else snprintf(ip+iplen, ADDR_LEN-iplen, ":%d", port);
-  memcpy(buf, ip, ADDR_LEN);
-}
-
-/*
- * display ipv4 info for TCP/UDP/RAW.
- */
-static void show_ipv4(char *fname, char *label)
-{
-  FILE *fp = fopen(fname, "r");
-  if (!fp) {
-     perror_msg("'%s'", fname);
-     return;
-  }
-
-  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
-
-  while (fgets(toybuf, sizeof(toybuf), fp)) {
-    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
-    iaddr laddr, raddr;
-    unsigned lport, rport, state, txq, rxq, num, uid;
-    unsigned long inode;
-
-    int nitems = sscanf(toybuf, " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
-                        &num, &laddr.u, &lport, &raddr.u, &rport, &state, &txq,
-                        &rxq, &uid, &inode);
-    if (nitems == 10) {
-      addr2str(AF_INET, &laddr, lport, lip, label);
-      addr2str(AF_INET, &raddr, rport, rip, label);
-      show_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
-    }
-  }//End of While
-  fclose(fp);
-}
-
-/*
- * display ipv6 info for TCP/UDP/RAW.
- */
-static void show_ipv6(char *fname, char *label)
-{
-  FILE *fp = fopen(fname, "r");
-  if (!fp) {
-    perror_msg("'%s'", fname);
-    return;
-  }
-
-  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
-
-  while (fgets(toybuf, sizeof(toybuf), fp)) {
-    char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
-    iaddr6 laddr6, raddr6;
-    unsigned lport, rport, state, txq, rxq, num, uid;
-    unsigned long inode;
-    int nitems = sscanf(toybuf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x "
-                                "%*X:%*X %*X %d %*d %ld",
-                        &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c,
-                        &laddr6.u.d, &lport, &raddr6.u.a, &raddr6.u.b,
-                        &raddr6.u.c, &raddr6.u.d, &rport, &state, &txq, &rxq,
-                        &uid, &inode);
-    if (nitems == 16) {
-      addr2str(AF_INET6, &laddr6, lport, lip, label);
-      addr2str(AF_INET6, &raddr6, rport, rip, label);
-      show_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
-    }
-  }//End of While
-  fclose(fp);
-}
-
-/*
- * display unix socket info.
- */
-static void show_unix_sockets(char *fname, char *label)
-{
-  FILE *fp = fopen((char *)fname, "r");
-  if (!fp) {
-    perror_msg("'%s'", fname);
-    return;
-  }
-
-  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
-
-  while (fgets(toybuf, sizeof(toybuf), fp)) {
-    unsigned long refcount, label, flags, inode;
-    int nitems = 0, path_offset = 0, type, state;
-    char sock_flags[32] = {0,}, *sock_type, *sock_state, *bptr = toybuf, *term;
-
-    if (!toybuf[0]) continue;
-
-    nitems = sscanf(toybuf, "%*p: %lX %lX %lX %X %X %lu %n",
-        &refcount, &label, &flags, &type, &state, &inode, &path_offset);
-
-    //for state one less
-    if (nitems < 6) break;
-
-    if (toys.optflags & FLAG_l) {
-      if ( !((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) )
-        continue;
-    } else if (!(toys.optflags & FLAG_a)) {
-      if ((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) continue;
-    }
-
-    //prepare socket type, state and flags.
-    {
-      char *ss_type[] = { "", "STREAM", "DGRAM", "RAW", "RDM", "SEQPACKET",
-                          "UNKNOWN"};
-      char *ss_state[] = { "FREE", "LISTENING", "CONNECTING", "CONNECTED",
-                           "DISCONNECTING", "UNKNOWN"};
-
-      int sz = ARRAY_LEN(ss_type);//sizeof(ss_type)/sizeof(ss_type[0]);
-      if ( (type < SOCK_STREAM) || (type > SOCK_SEQPACKET) )
-        sock_type = ss_type[sz-1];
-      else sock_type = ss_type[type];
-
-      sz = ARRAY_LEN(ss_state);//sizeof(ss_state)/sizeof(ss_state[0]);
-      if ((state < 0) || (state > sz-2)) sock_state = ss_state[sz-1];
-      else if (state == SOCK_NOT_CONNECTED) {
-        if (flags & SOCK_ACCEPTCON) sock_state = ss_state[state];
-        else sock_state = " ";
-      } else sock_state = ss_state[state];
-
-      strcpy(sock_flags, "[ ");
-      if (flags & SOCK_ACCEPTCON) strcat(sock_flags, "ACC ");
-      if (flags & SOCK_WAIT_DATA) strcat(sock_flags, "W ");
-      if (flags & SOCK_NO_SPACE) strcat(sock_flags, "N ");
-      strcat(sock_flags, "]");
-    }
-    xprintf("%-5s %-6ld %-11s %-10s %-13s %8lu ", (!label ? "unix" : "??"),
-      refcount, sock_flags, sock_type, sock_state, inode);
-    if (toys.optflags & FLAG_p) xprintf("%-20s", get_pid_name(inode));
-
-    bptr += path_offset;
-    if ((term = strchr(bptr, '\n'))) *term = '\0';
-    xprintf("%s\n", bptr);
-  }//End of while
-  fclose(fp);
-}
-
-/*
- * extract inode value from the link.
- */
-static long ss_inode(char *link)
-{
-  long inode = -1;
-  //"link = socket:[12345]", get "12345" as inode.
-  if (!strncmp(link, "socket:[", sizeof("socket:[")-1)) {
-    inode = get_strtou(link + sizeof("socket:[")-1, (char**)&link, 0);
-    if (*link != ']') inode = -1;
-  }
-  //"link = [0000]:12345", get "12345" as inode.
-  else if (!strncmp(link, "[0000]:", sizeof("[0000]:")-1)) {
-    inode = get_strtou(link + sizeof("[0000]:")-1, NULL, 0);
-    //if not NULL terminated.
-    if (errno) inode = -1;
-  }
-  return inode;
-}
-
-/*
- * add inode and progname in the pid list.
- */
-static void add2list(long inode)
-{
-  PID_LIST *node = pid_list;
-
-  for(; node; node = node->next) {
-    if(node->inode == inode)
-      return;
-  }
-
-  PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
-  new->inode = inode;
-  xstrncpy(new->name, TT.current_name, sizeof(new->name));
-  new->next = pid_list;
-  pid_list = new;
-}
-
-static void scan_pid_inodes(char *path)
-{
-  DIR *dp;
-  struct dirent *entry;
-
-  if (!(dp = opendir(path))) {
-    if (errno == EACCES) {
-      TT.some_process_unidentified = 1;
-      return;
-    } else perror_exit("%s", path);
-  }
-  while ((entry = readdir(dp))) {
-    char link_name[64], *link;
-    long inode;
-
-    if (!isdigit(entry->d_name[0])) continue;
-    snprintf(link_name, sizeof(link_name), "%s/%s", path, entry->d_name);
-    link = xreadlink(link_name);
-    if ((inode = ss_inode(link)) != -1) add2list(inode);
-    free(link);
-  }
-  closedir(dp);
-}
-
-static void scan_pid(int pid)
-{
-  char *line, *p, *fd_dir;
-
-  snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", pid);
-  line = xreadfile(toybuf, 0, 0);
-
-  if ((p = strchr(line, ' '))) *p = 0; // "/bin/netstat -ntp" -> "/bin/netstat"
-  snprintf(TT.current_name, sizeof(TT.current_name), "%d/%s",
-           pid, basename_r(line)); // "584/netstat"
-  free(line);
-
-  fd_dir = xmprintf("/proc/%d/fd", pid);
-  scan_pid_inodes(fd_dir);
-  free(fd_dir);
-}
-
-static int scan_pids(struct dirtree *node)
-{
-  int pid;
-
-  if (!node->parent) return DIRTREE_RECURSE;
-  if ((pid = atol(node->name))) scan_pid(pid);
-
-  return 0;
-}
-
-/*
- * Dealloc pid list.
- */
-static void clean_pid_list(void)
-{
-  PID_LIST *tmp;
-  while (pid_list) {
-    tmp = pid_list->next;
-    free(pid_list);
-    pid_list = tmp;
-  }
-}
-
-/*
- * For TCP/UDP/RAW show the header.
- */
-static void show_header(void)
-{
-  xprintf("Proto Recv-Q Send-Q ");
-  xprintf((toys.optflags & FLAG_W) ? "%-51s %-51s" : "%-23s %-23s",
-          "Local Address", "Foreign Address");
-  xprintf(" State      ");
-  if (toys.optflags & FLAG_e) xprintf(" User       Inode      ");
-  if (toys.optflags & FLAG_p) xprintf(" PID/Program Name");
-  xputc('\n');
-}
-
-/*
- * used to get the flag values for route command.
- */
-static void get_flag_value(char *flagstr, int flags)
-{
-  int i = 0;
-  char *str = flagstr;
-  static const char flagchars[] = "GHRDMDAC";
-  static const unsigned flagarray[] = {
-    RTF_GATEWAY,
-    RTF_HOST,
-    RTF_REINSTATE,
-    RTF_DYNAMIC,
-    RTF_MODIFIED,
-    RTF_DEFAULT,
-    RTF_ADDRCONF,
-    RTF_CACHE
-  };
-  *str++ = 'U';
-
-  while ( (*str = flagchars[i]) ) {
-    if (flags & flagarray[i++]) ++str;
-  }
-}
-
-/*
- * extract inet4 route info from /proc/net/route file and display it.
- */
-static void display_routes(int is_more_info, int notresolve)
-{
-#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
-  unsigned long dest, gate, mask;
-  int flags, ref, use, metric, mss, win, irtt;
-  char iface[64]={0,};
-  char flag_val[10]={0,}; //there are 9 flags "UGHRDMDAC" for route.
-
-  FILE *fp = xfopen("/proc/net/route", "r");
-
-  if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
-
-  xprintf("Kernel IP routing table\n"
-          "Destination     Gateway         Genmask         Flags %s Iface\n",
-          is_more_info ? "  MSS Window  irtt" : "Metric Ref    Use");
-
-  while (fgets(toybuf, sizeof(toybuf), fp)) {
-     int nitems = 0;
-     char *destip = NULL, *gateip = NULL, *maskip = NULL;
-
-     nitems = sscanf(toybuf, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,
-       &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt);
-     if (nitems != 11) {//EOF with no (nonspace) chars read.
-       if ((nitems < 0) && feof(fp)) break;
-      perror_exit("sscanf");
-    }
-
-    //skip down interfaces.
-    if (!(flags & RTF_UP)) continue;
-
-    if (dest) {//For Destination
-      if (inet_ntop(AF_INET, &dest, toybuf, sizeof(toybuf)) )
-        destip = xstrdup(toybuf);
-    } else {
-      if (!notresolve) destip = xstrdup("default");
-      else destip = xstrdup("0.0.0.0");
-    }
-
-    if (gate) {//For Gateway
-      if (inet_ntop(AF_INET, &gate, toybuf, sizeof(toybuf)) )
-        gateip = xstrdup(toybuf);
-    } else {
-      if (!notresolve) gateip = xstrdup("*");
-      else gateip = xstrdup("0.0.0.0");
-    }
-
-    //For Mask
-    if (inet_ntop(AF_INET, &mask, toybuf, sizeof(toybuf)) )
-      maskip = xstrdup(toybuf);
-
-    //Get flag Values
-    get_flag_value(flag_val, flags & IPV4_MASK);
-    if (flags & RTF_REJECT) flag_val[0] = '!';
-
-    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
-    if (is_more_info) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
-    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
-
-    if (destip) free(destip);
-    if (gateip) free(gateip);
-    if (maskip) free(maskip);
-  }//end of while.
-  fclose(fp);
-#undef IPV4_MASK
-}
-
-/*
- * netstat utility main function.
- */
-void netstat_main(void)
-{
-#define IS_NETSTAT_PROTO_FLAGS_UP (toys.optflags & (FLAG_t | FLAG_u | FLAG_w \
-                                                    | FLAG_x))
-
-  // For no parameter, add 't', 'u', 'w', 'x' options as default
-  if (!toys.optflags) toys.optflags = FLAG_t | FLAG_u | FLAG_w | FLAG_x;
-
-  // For both 'a' and 'l' are set, remove 'l' option
-  if (toys.optflags & FLAG_a && toys.optflags & FLAG_l)
-      toys.optflags &= ~FLAG_l;
-
-  // For each 'a', 'l', 'e', 'n', 'W', 'p' options
-  // without any 't', 'u', 'w', 'x' option, add 't', 'u', 'w', 'x' options
-  if (((toys.optflags & FLAG_a) || (toys.optflags & FLAG_l) ||
-       (toys.optflags & FLAG_e) || (toys.optflags & FLAG_n) ||
-       (toys.optflags & FLAG_W) || (toys.optflags & FLAG_p)) &&
-         (!IS_NETSTAT_PROTO_FLAGS_UP) )
-    toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
-
-  //Display routing table.
-  if (toys.optflags & FLAG_r) {
-    display_routes(!(toys.optflags & FLAG_e), (toys.optflags & FLAG_n));
-    return;
-  }
-
-  if (toys.optflags & FLAG_p) {
-    dirtree_read("/proc", scan_pids);
-    // TODO: we probably shouldn't warn if all the processes we're going to
-    // list were identified.
-    if (TT.some_process_unidentified)
-      fprintf(stderr,
-        "(Not all processes could be identified, non-owned process info\n"
-        " will not be shown, you would have to be root to see it all.)\n");
-  }
-
-  //For TCP/UDP/RAW.
-  if ( (toys.optflags & FLAG_t) || (toys.optflags & FLAG_u) ||
-       (toys.optflags & FLAG_w) ) {
-    xprintf("Active Internet connections ");
-
-    if (toys.optflags & FLAG_a) xprintf("(servers and established)\n");
-    else if (toys.optflags & FLAG_l) xprintf("(only servers)\n");
-    else xprintf("(w/o servers)\n");
-
-    show_header();
-    if (toys.optflags & FLAG_t) {//For TCP
-      show_ipv4("/proc/net/tcp",  "tcp");
-      show_ipv6("/proc/net/tcp6", "tcp");
-    }
-    if (toys.optflags & FLAG_u) {//For UDP
-      show_ipv4("/proc/net/udp",  "udp");
-      show_ipv6("/proc/net/udp6", "udp");
-    }
-    if (toys.optflags & FLAG_w) {//For raw
-      show_ipv4("/proc/net/raw",  "raw");
-      show_ipv6("/proc/net/raw6", "raw");
-    }
-  }
-  if (toys.optflags & FLAG_x) {//For UNIX
-    xprintf("Active UNIX domain sockets ");
-    if (toys.optflags & FLAG_a) xprintf("(servers and established)\n");
-    else if (toys.optflags & FLAG_l) xprintf("(only servers)\n");
-    else xprintf("(w/o servers)\n");
-
-    if (toys.optflags & FLAG_p)
-      xprintf("Proto RefCnt Flags       Type       State           "
-              "I-Node PID/Program Name    Path\n");
-    else
-      xprintf("Proto RefCnt Flags       Type       State           "
-              "I-Node Path\n");
-    show_unix_sockets("/proc/net/unix", "unix");
-  }
-  if (toys.optflags & FLAG_p) clean_pid_list();
-  if (toys.exitval) toys.exitval = 0;
-#undef IS_NETSTAT_PROTO_FLAGS_UP
-}
index 0eb0c25..29f8a17 100644 (file)
@@ -10,6 +10,7 @@ USE_DEALLOCVT(NEWTOY(deallocvt, ">1", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NEEDROOT))
 config OPENVT
   bool "openvt"
   default n
+  depends on TOYBOX_FORK
   help
     usage: openvt [-c N] [-sw] [command [command_options]]
 
@@ -99,7 +100,7 @@ void openvt_main(void)
   xioctl(fd, VT_GETSTATE, &vstate);
 
   close(0);  //new vt becomes stdin
-  vt_fd = xopen(toybuf, O_RDWR);
+  vt_fd = xopen_stdio(toybuf, O_RDWR);
   if (toys.optflags & FLAG_s) {
     ioctl(vt_fd, VT_ACTIVATE, TT.vt_num);
     ioctl(vt_fd, VT_WAITACTIVE, TT.vt_num);
diff --git a/toys/pending/resize.c b/toys/pending/resize.c
new file mode 100644 (file)
index 0000000..70c7704
--- /dev/null
@@ -0,0 +1,39 @@
+/* resize.c - Set maximum terminal width and height
+ *
+ *
+
+USE_RESIZE(NEWTOY(resize, "c#<1>63sw", TOYFLAG_BIN|TOYFLAG_USR))
+
+config RESIZE
+  bool "resize"
+  default n
+  help
+    usage: resize
+
+    reset terminal to maximum width and height
+
+*/
+
+#define FOR_resize
+#include "toys.h"
+
+void resize_main(void)
+{
+  struct winsize w = { 0, 0, 0, 0 };
+  struct termios *old = (struct termios *)toybuf;
+  struct termios new;
+
+  tcgetattr(2, old);
+  memcpy(&new, old, sizeof(new));
+  new.c_cflag |= (CLOCAL | CREAD);
+  new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+  tcsetattr(2, TCSANOW, &new);
+
+  // set cursor to 999,999, read back where it landed
+  fprintf(stderr, "\0337\033[r\033[999;999H\033[6n");
+  scanf("\033[%hu;%huR", &w.ws_row, &w.ws_col);
+  fprintf(stderr, "\0338");
+
+  ioctl(2, TIOCSWINSZ, &w);
+  tcsetattr(2, TCSANOW, old);
+}
index e221960..76b4e13 100644 (file)
  *
  * Things like the bash man page are good to read too.
  *
+ * TODO: "make sh" doesn't work (nofork builtins need to be included)
+ * TODO: test that $PS1 color changes work without stupid \[ \] hack
+ * TODO: make fake pty wrapper for test infrastructure
  * TODO: // Handle embedded NUL bytes in the command line.
+ * TODO: var=val command
+ * existing but considered builtins: false kill pwd true
+ * buitins: alias bg command fc fg getopts jobs newgrp read umask unalias wait
+ * "special" builtins: break continue : . eval exec export readonly return set
+ *   shift times trap unset
+ * | & ; < > ( ) $ ` \ " ' <space> <tab> <newline>
+ * * ? [ # ~ = %
+ * ! { } case do done elif else esac fi for if in then until while
+ * [[ ]] function select
+ * $@ $* $# $? $- $$ $! $0
+ * ENV HOME IFS LANG LC_ALL LINENO PATH PPID PS1 PS2 PS4 PWD
+ * label:
+ * TODO: test exit from "trap EXIT" doesn't recurse
 
 USE_SH(NEWTOY(cd, NULL, TOYFLAG_NOFORK))
 USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
@@ -42,16 +58,6 @@ config SH
     -c command line to execute
     -i interactive mode (default when STDIN is a tty)
 
-config EXIT
-  bool
-  default n
-  depends on SH
-  help
-    usage: exit [status]
-
-    Exit shell.  If no return value supplied on command line, use value
-    of most recent command, or 0 if none.
-
 config CD
   bool
   default n
@@ -63,112 +69,16 @@ config CD
 
     -P Physical path: resolve symlinks in path.
     -L Local path: .. trims directories off $PWD (default).
-*/
-
-/*
-This level of micromanagement is silly, it adds more complexity than it's
-worth. (Not just to the code, but decision fatigue configuring it.)
-
-That said, the following list is kept for the moment as a todo list of
-features I need to implement.
-
-config SH_PROFILE
-  bool "Profile support"
-  default n
-  depends on SH_TTY
-  help
-    Read /etc/profile and ~/.profile when running interactively.
-
-    Also enables the built-in command "source".
-
-config SH_JOBCTL
-  bool "Job Control (fg, bg, jobs)"
-  default n
-  depends on SH_TTY
-  help
-    Add job control to toysh.  This lets toysh handle CTRL-Z, and enables
-    the built-in commands "fg", "bg", and "jobs".
-
-    With pipe support, enable use of "&" to run background processes.
-
-config SH_FLOWCTL
-  bool "Flow control (if, while, for, functions)"
-  default n
-  depends on SH
-  help
-    Add flow control to toysh.  This enables the if/then/else/fi,
-    while/do/done, and for/do/done constructs.
-
-    With pipe support, this enables the ability to define functions
-    using the "function name" or "name()" syntax, plus curly brackets
-    "{ }" to group commands.
-
-config SH_QUOTES
-  bool "Smarter argument parsing (quotes)"
-  default n
-  depends on SH
-  help
-    Add support for parsing "" and '' style quotes to the toysh command
-    parser, with lets arguments have spaces in them.
-
-config SH_WILDCARDS
-  bool "Wildcards ( ?*{,} )"
-  default n
-  depends on SH_QUOTES
-  help
-    Expand wildcards in argument names, ala "ls -l *.t?z" and
-    "rm subdir/{one,two,three}.txt".
-
-config SH_PROCARGS
-  bool "Executable arguments ( `` and $() )"
-  default n
-  depends on SH_QUOTES
-  help
-    Add support for executing arguments contianing $() and ``, using
-    the output of the command as the new argument value(s).
-
-    (Bash calls this "command substitution".)
-
-config SH_ENVVARS
-  bool "Environment variable support"
-  default n
-  depends on SH_QUOTES
-  help
-    Substitute environment variable values for $VARNAME or ${VARNAME},
-    and enable the built-in command "export".
-
-config SH_LOCALS
-  bool "Local variables"
-  default n
-  depends on SH_ENVVARS
-  help
-    Support for local variables, fancy prompts ($PS1), the "set" command,
-    and $?.
-
-config SH_ARRAYS
-  bool "Array variables"
-  default n
-  depends on SH_LOCALS
-  help
-    Support for ${blah[blah]} style array variables.
 
-config SH_PIPES
-  bool "Pipes and redirects ( | > >> < << & && | || () ; )"
+config EXIT
+  bool
   default n
   depends on SH
   help
-    Support multiple commands on the same command line.  This includes
-    | pipes, > >> < redirects, << here documents, || && conditional
-    execution, () subshells, ; sequential execution, and (with job
-    control) & background processes.
+    usage: exit [status]
 
-config SH_BUILTINS
-  bool "Builtin commands"
-  default n
-  depends on SH
-  help
-    Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set,
-    unset, read, alias.
+    Exit shell.  If no return value supplied on command line, use value
+    of most recent command, or 0 if none.
 */
 
 #define FOR_sh
@@ -176,17 +86,9 @@ config SH_BUILTINS
 
 GLOBALS(
   char *command;
-)
 
-// A single executable, its arguments, and other information we know about it.
-#define SH_FLAG_EXIT    1
-#define SH_FLAG_SUSPEND 2
-#define SH_FLAG_PIPE    4
-#define SH_FLAG_AND     8
-#define SH_FLAG_OR      16
-#define SH_FLAG_AMP     32
-#define SH_FLAG_SEMI    64
-#define SH_FLAG_PAREN   128
+  long lineno;
+)
 
 // What we know about a single process.
 struct command {
@@ -206,6 +108,18 @@ struct pipeline {
   int cmdlinelen;        // How long is cmdline?
 };
 
+void cd_main(void)
+{
+  char *dest = *toys.optargs ? *toys.optargs : getenv("HOME");
+
+  xchdir(dest ? dest : "/");
+}
+
+void exit_main(void)
+{
+  exit(*toys.optargs ? atoi(*toys.optargs) : 0);
+}
+
 // Parse one word from the command line, appending one or more argv[] entries
 // to struct command.  Handles environment variable substitution and
 // substrings.  Returns pointer to next used byte, or NULL if it
@@ -287,6 +201,7 @@ static void run_pipeline(struct pipeline *line)
   if (!cmd || !cmd->argc) return;
 
   tl = toy_find(cmd->argv[0]);
+
   // Is this command a builtin that should run in this process?
   if (tl && (tl->flags & TOYFLAG_NOFORK)) {
     struct toy_context temp;
@@ -351,34 +266,65 @@ static void handle(char *command)
   }
 }
 
-void cd_main(void)
+static void do_prompt(void)
 {
-  char *dest = *toys.optargs ? *toys.optargs : getenv("HOME");
+  char *prompt = getenv("PS1"), *s, c, cc;
 
-  xchdir(dest ? dest : "/");
-}
+  if (!prompt) prompt = "\\$ ";
+  while (*prompt) {
+    c = *(prompt++);
 
-void exit_main(void)
-{
-  exit(*toys.optargs ? atoi(*toys.optargs) : 0);
+    if (c=='!') {
+      if (*prompt=='!') prompt++;
+      else {
+        printf("%ld", TT.lineno);
+        continue;
+      }
+    } else if (c=='\\') {
+      cc = *(prompt++);
+      if (!cc) goto down;
+
+      // \nnn \dD{}hHjlstT@AuvVwW!#$
+      // Ignore bash's "nonprintable" hack; query our cursor position instead.
+      if (cc=='[' || cc==']') continue;
+      else if (cc=='$') putchar(getuid() ? '$' : '#');
+      else if (cc=='h' || cc=='H') {
+        *toybuf = 0;
+        gethostname(toybuf, sizeof(toybuf)-1);
+        if (cc=='h' && (s = strchr(toybuf, '.'))) *s = 0;
+        fputs(toybuf, stdout);
+      } else if (cc=='s') fputs(getbasename(*toys.argv), stdout);
+      else {
+        if (!(c = unescape(cc))) {
+          c = '\\';
+          prompt--;
+        }
+
+        goto down;
+      }
+      continue;
+    }
+down:
+    putchar(c);
+  }
 }
 
 void sh_main(void)
 {
-  FILE *f;
+  FILE *f = 0;
 
   // Set up signal handlers and grab control of this tty.
   if (isatty(0)) toys.optflags |= FLAG_i;
 
-  f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL;
-  if (TT.command) handle(TT.command);
+  if (*toys.optargs) f = xfopen(*toys.optargs, "r");
+  if (TT.command) handle(xstrdup(TT.command));
   else {
     size_t cmdlen = 0;
     for (;;) {
-      char *prompt = getenv("PS1"), *command = 0;
+      char *command = 0;
 
       // TODO: parse escapes in prompt
-      if (!f) printf("%s", prompt ? prompt : "$ ");
+      if (!f) do_prompt();
       if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
       handle(command);
       free(command);
index e6cb831..bc3638e 100644 (file)
@@ -88,7 +88,7 @@ void sulogin_main(void)
   if (toys.optargs[0]) {
     int fd;
 
-    dup2((fd = xopen(toys.optargs[0], O_RDWR)), 0);
+    dup2((fd = xopen_stdin(toys.optargs[0], O_RDWR)), 0);
     if (!isatty(0)) error_exit("%s: it is not a tty", toys.optargs[0]);
     dup2( fd, 1);
     dup2( fd, 2);
index 4f4de0b..384199e 100644 (file)
@@ -228,8 +228,8 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
   else if (S_ISFIFO(st->st_mode)) hdr.type = '6';
   else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
     hdr.type = (S_ISCHR(st->st_mode))?'3':'4';
-    itoo(hdr.major, sizeof(hdr.major), major(st->st_rdev));
-    itoo(hdr.minor, sizeof(hdr.minor), minor(st->st_rdev));
+    itoo(hdr.major, sizeof(hdr.major), dev_major(st->st_rdev));
+    itoo(hdr.minor, sizeof(hdr.minor), dev_minor(st->st_rdev));
   } else {
     error_msg("unknown file type '%o'", st->st_mode & S_IFMT);
     return;
@@ -623,7 +623,7 @@ CHECK_MAGIC:
     file_hdr->gname = xstrdup(tar.gname);
     maj = otoi(tar.major, sizeof(tar.major));
     min = otoi(tar.minor, sizeof(tar.minor));
-    file_hdr->device = makedev(maj, min);
+    file_hdr->device = dev_makedev(maj, min);
 
     if (tar.type <= '7') {
       if (tar.link[0]) {
@@ -788,7 +788,7 @@ void tar_main(void)
     for (tmp = TT.inc; tmp; tmp = tmp->next) {
       TT.handle = tar_hdl;
       //recurse thru dir and add files to archive
-      dirtree_handle_callback(dirtree_start(tmp->arg, toys.optflags & FLAG_h),
+      dirtree_flagread(tmp->arg, DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_h),
         add_to_tar);
     }
     memset(toybuf, 0, 1024);
index 16110e5..31c2761 100644 (file)
@@ -12,6 +12,7 @@ USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
 config TCPSVD
   bool "tcpsvd"
   default n
+  depends on TOYBOX_FORK
   help
     usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog
     usage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog
index 60d5f17..1c6fe9e 100644 (file)
@@ -381,7 +381,7 @@ int file_put(void)
 
   sd = init_tftp(&server);
   packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
-  fd = xopen(TT.local_file, O_RDONLY);
+  fd = xopenro(TT.local_file);
 
   for (;;) {  //first loop for request send and confirmation from server.
     packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
index 4a4f55d..9a818eb 100644 (file)
@@ -242,7 +242,7 @@ static void do_trace()
           - (t1.tv_sec * USEC + t1.tv_usec);
 
         if (pfd[0].revents) {
-          unsigned addrlen = sizeof(struct sockaddr_storage);
+          socklen_t addrlen = sizeof(struct sockaddr_storage);
           int rcv_len, icmp_res = 0;
 
           rcv_len = recvfrom(TT.recv_sock, toybuf, sizeof(toybuf),
diff --git a/toys/pending/wget.c b/toys/pending/wget.c
new file mode 100644 (file)
index 0000000..4283fe4
--- /dev/null
@@ -0,0 +1,178 @@
+/* wget.c - Simple downloader to get the resource file in HTTP server
+ *
+ * Copyright 2016 Lipi C.H. Lee <lipisoft@gmail.com>
+ *
+
+USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WGET
+  bool "wget"
+  default n
+  help
+    usage: wget -f filename URL
+    -f filename: specify the filename to be saved
+    URL: HTTP uniform resource location and only HTTP, not HTTPS
+
+    examples:
+      wget -f index.html http://www.example.com
+      wget -f sample.jpg http://www.example.com:8080/sample.jpg
+*/
+
+#define FOR_wget
+#include "toys.h"
+
+GLOBALS(
+  char *filename;
+)
+
+// extract hostname from url
+static unsigned get_hn(const char *url, char *hostname) {
+  unsigned i;
+
+  for (i = 0; url[i] != '\0' && url[i] != ':' && url[i] != '/'; i++) {
+    if(i >= 1024) error_exit("too long hostname in URL");
+    hostname[i] = url[i];
+  }
+  hostname[i] = '\0';
+
+  return i;
+}
+
+// extract port number
+static unsigned get_port(const char *url, char *port, unsigned url_i) {
+  unsigned i;
+
+  for (i = 0; url[i] != '\0' && url[i] != '/'; i++, url_i++) {
+    if('0' <= url[i] && url[i] <= '9') port[i] = url[i];
+    else error_exit("wrong decimal port number");
+  }
+  if(i <= 6) port[i] = '\0';
+  else error_exit("too long port number");
+
+  return url_i;
+}
+
+// get http infos in URL
+static void get_info(const char *url, char* hostname, char *port, char *path) {
+  unsigned i = 7, len;
+
+  if (strncmp(url, "http://", i)) error_exit("only HTTP support");
+  len = get_hn(url+i, hostname);
+  i += len;
+
+  // get port if exists
+  if (url[i] == ':') {
+    i++;
+    i = get_port(url+i, port, i);
+  } else strcpy(port, "80");
+
+  // get uri in URL
+  if (url[i] == '\0') strcpy(path, "/");
+  else if (url[i] == '/') {
+    if (strlen(url+i) < 1024) strcpy(path, url+i);
+    else error_exit("too long path in URL");
+  } else error_exit("wrong URL");
+}
+
+// connect to any IPv4 or IPv6 server
+static int conn_svr(const char *hostname, const char *port) {
+  struct addrinfo hints, *result, *rp;
+  int sock;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = 0;
+  hints.ai_protocol = 0;
+
+  if ((errno = getaddrinfo(hostname, port, &hints, &result)))
+    error_exit("getaddrinfo: %s", gai_strerror(errno));
+
+  // try all address list(IPv4 or IPv6) until success
+  for (rp = result; rp; rp = rp->ai_next) {
+    if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))
+        == -1) {
+      perror_msg("socket error");
+      continue;
+    }
+    if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
+      break; // succeed in connecting to any server IP 
+    else perror_msg("connect error");
+    close(sock);
+  }
+  freeaddrinfo(result);
+  if(!rp) error_exit("can't connect");
+
+  return sock;
+}
+
+// make HTTP request header field
+static void mk_fld(char *name, char *value) {
+  strcat(toybuf, name);
+  strcat(toybuf, ": ");
+  strcat(toybuf, value);
+  strcat(toybuf, "\r\n");
+}
+
+// get http response body starting address and its length
+static char *get_body(ssize_t len, ssize_t *body_len) {
+  int i;
+
+  for (i = 0; i < len-4; i++)
+    if (!strncmp(toybuf+i, "\r\n\r\n", 4)) break;
+
+  *body_len = len - i - 4;
+  return toybuf+i+4;
+}
+
+void wget_main(void)
+{
+  int sock;
+  FILE *fp;
+  ssize_t len, body_len;
+  char *body, *result, *rc, *r_str;
+  char ua[18] = "toybox wget/", ver[6], hostname[1024], port[6], path[1024];
+
+  // TODO extract filename to be saved from URL
+  if (!(toys.optflags & FLAG_f)) help_exit("no filename");
+  if (fopen(TT.filename, "r")) perror_exit("file already exists");
+
+  if(!toys.optargs[0]) help_exit("no URL");
+  get_info(toys.optargs[0], hostname, port, path);
+
+  sock = conn_svr(hostname, port);
+
+  // compose HTTP request
+  sprintf(toybuf, "GET %s HTTP/1.1\r\n", path);
+  mk_fld("Host", hostname);
+  strncpy(ver, TOYBOX_VERSION, 5);
+  strcat(ua, ver);
+  mk_fld("User-Agent", ua); 
+  mk_fld("Connection", "close");
+  strcat(toybuf, "\r\n");
+
+  // send the HTTP request
+  len = strlen(toybuf);
+  if (write(sock, toybuf, len) != len) perror_exit("write error");
+
+  // read HTTP response
+  if ((len = read(sock, toybuf, 4096)) == -1) perror_exit("read error");
+  if (!strstr(toybuf, "\r\n\r\n")) error_exit("too long HTTP response");
+  body = get_body(len, &body_len);
+  result = strtok(toybuf, "\r");
+  strtok(result, " ");
+  rc = strtok(NULL, " ");
+  r_str = strtok(NULL, " ");
+
+  // HTTP res code check
+  // TODO handle HTTP 302 Found(Redirection)
+  if (strcmp(rc, "200")) error_exit("res: %s(%s)", rc, r_str);
+
+  if (!(fp = fopen(TT.filename, "w"))) perror_exit("fopen error");
+  if (fwrite(body, 1, body_len, fp) != body_len)
+    error_exit("fwrite error");
+  while ((len = read(sock, toybuf, 4096)) > 0)
+    if (fwrite(toybuf, 1, len, fp) != len)
+      error_exit("fwrite error");
+  if (fclose(fp) == EOF) perror_exit("fclose error");
+}
index 1a27a23..c123cc7 100644 (file)
@@ -23,7 +23,7 @@ void basename_main(void)
   char *base = basename(*toys.optargs), *suffix = toys.optargs[1];
 
   // chop off the suffix if provided
-  if (suffix) {
+  if (suffix && *suffix) {
     long bl = strlen(base), sl = strlen(suffix);
     char *s = base + bl - sl;
 
index 6b95c6a..62e9eb1 100644 (file)
@@ -87,15 +87,15 @@ void chgrp_main(void)
       *(grp++) = 0;
       TT.group_name = grp;
     }
-    if (*own) TT.owner = xgetpwnamid(TT.owner_name = own)->pw_uid;
+    if (*own) TT.owner = xgetuid(TT.owner_name = own);
   } else TT.group_name = *toys.optargs;
 
   if (TT.group_name && *TT.group_name)
-    TT.group = xgetgrnamid(TT.group_name)->gr_gid;
+    TT.group = xgetgid(TT.group_name);
 
   for (s=toys.optargs+1; *s; s++)
-    dirtree_handle_callback(dirtree_start(*s, toys.optflags&(FLAG_H|FLAG_L)),
-      do_chgrp);;
+    dirtree_flagread(*s, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
+      do_chgrp);
 
   if (CFG_TOYBOX_FREE && ischown) free(own);
 }
index 829da75..f4c3409 100644 (file)
@@ -4,7 +4,7 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/cmp.html
 
-USE_CMP(NEWTOY(cmp, "<2>2ls", TOYFLAG_USR|TOYFLAG_BIN))
+USE_CMP(NEWTOY(cmp, "<2>2ls[!ls]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config CMP
   bool "cmp"
@@ -43,6 +43,8 @@ static void do_cmp(int fd, char *name)
     return;
   }
 
+  toys.exitval = 0;
+
   for (;;) {
     len1 = readall(TT.fd, toybuf, size);
     len2 = readall(fd, buf2, size);
@@ -54,11 +56,9 @@ static void do_cmp(int fd, char *name)
         if (toys.optflags & FLAG_l)
           printf("%ld %o %o\n", byte_no, toybuf[i], buf2[i]);
         else {
-          if (!(toys.optflags & FLAG_s)) {
+          if (!(toys.optflags & FLAG_s)) 
             printf("%s %s differ: char %ld, line %ld\n",
               TT.name, name, byte_no, line_no);
-            toys.exitval++;
-          }
           goto out;
         }
       }
@@ -79,6 +79,8 @@ out:
 
 void cmp_main(void)
 {
-  loopfiles_rw(toys.optargs, O_CLOEXEC, 0, toys.optflags&FLAG_s, do_cmp);
+  toys.exitval = 2;
+  loopfiles_rw(toys.optargs, O_CLOEXEC|(WARN_ONLY*!(toys.optflags&FLAG_s)), 0,
+    do_cmp);
 }
 
index 6c726cf..ded262f 100644 (file)
@@ -48,8 +48,7 @@ void comm_main(void)
   if (toys.optflags == 7) return;
 
   for (i = 0; i < 2; i++) {
-    file[i] = strcmp("-", toys.optargs[i])
-      ? xopen(toys.optargs[i], O_RDONLY) : 0;
+    file[i] = xopenro(toys.optargs[i]);
     line[i] = get_line(file[i]);
   }
 
index cb7e6e3..0dd63a7 100644 (file)
@@ -6,21 +6,24 @@
  *
  * Posix says "cp -Rf dir file" shouldn't delete file, but our -f does.
  *
+ * Deviations from posix: -adlnrsvF, --preserve... about half the
+ * functionality in this cp isn't in posix. Posix is stuck in the 1970's.
+ *
  * TODO: --preserve=links
  * TODO: what's this _CP_mode system.posix_acl_ business? We chmod()?
 
 // options shared between mv/cp must be in same order (right to left)
 // for FLAG macros to work out right in shared infrastructure.
 
-USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
-USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
+USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN))
 USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
 
 config CP
   bool "cp"
   default y
   help
-    usage: cp [-fipRHLP] SOURCE... DEST
+    usage: cp [-adlnrsvfipRHLP] SOURCE... DEST
 
     Copy files from SOURCE to DEST.  If more than one SOURCE, DEST must
     be a directory.
@@ -33,14 +36,6 @@ config CP
     -H Follow symlinks listed on command line
     -L Follow all symlinks
     -P Do not follow symlinks [default]
-
-config CP_MORE
-  bool "cp -adlnrsv options"
-  default y
-  depends on CP
-  help
-    usage: cp [-adlnrsv]
-
     -a same as -dpr
     -d don't dereference symlinks
     -l hard link instead of copy
@@ -52,7 +47,7 @@ config CP_MORE
 config CP_PRESERVE
   bool "cp --preserve support"
   default y
-  depends on CP_MORE
+  depends on CP
   help
     usage: cp [--preserve=motcxa]
 
@@ -69,27 +64,17 @@ config CP_PRESERVE
 config MV
   bool "mv"
   default y
-  depends on CP
   help
-    usage: mv [-fi] SOURCE... DEST"
+    usage: mv [-fivn] SOURCE... DEST"
 
     -f force copy by deleting destination file
     -i interactive, prompt before overwriting existing DEST
-
-config MV_MORE
-  bool
-  default y
-  depends on MV && CP_MORE
-  help
-    usage: mv [-vn]
-
     -v verbose
     -n no clobber (don't overwrite DEST)
 
 config INSTALL
   bool "install"
   default y
-  depends on CP && CP_MORE
   help
     usage: install [-dDpsv] [-o USER] [-g GROUP] [-m MODE] [SOURCE...] DEST
 
@@ -105,11 +90,9 @@ config INSTALL
     -v Verbose
 */
 
+#define FORCE_FLAGS
 #define FOR_cp
 #include "toys.h"
-#if CFG_CP_PRESERVE
-#include <sys/xattr.h>
-#endif
 
 GLOBALS(
   union {
@@ -172,7 +155,7 @@ int cp_node(struct dirtree *try)
       return 0;
     }
 
-    // Handle -inv
+    // Handle -invF
 
     if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
       char *s;
@@ -268,8 +251,8 @@ int cp_node(struct dirtree *try)
 
         // make symlink, or make block/char/fifo/socket
         if (S_ISLNK(try->st.st_mode)
-            ? (0 < (i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf))) &&
-               sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))
+            ? ((i = readlinkat0(tfd, try->name, toybuf, sizeof(toybuf))) &&
+               !symlinkat(toybuf, cfd, catch))
             : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))
         {
           err = 0;
@@ -335,7 +318,7 @@ int cp_node(struct dirtree *try)
         rc = fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
                       AT_SYMLINK_NOFOLLOW);
       else rc = fchown(fdout, try->st.st_uid, try->st.st_gid);
-      if (rc) {
+      if (rc && !geteuid()) {
         char *pp;
 
         perror_msg("chown '%s'", pp = dirtree_path(try, 0));
@@ -362,7 +345,19 @@ int cp_node(struct dirtree *try)
         err = "%s";
   }
 
-  if (err) perror_msg(err, catch);
+  if (err) {
+    char *f = 0;
+
+    if (catch == try->name) {
+      f = dirtree_path(try, 0);
+      while (try->parent) try = try->parent;
+      catch = xmprintf("%s%s", TT.destname, f+strlen(try->name));
+      free(f);
+      f = catch;
+    }
+    perror_msg(err, catch);
+    free(f);
+  }
   return 0;
 }
 
@@ -404,7 +399,6 @@ void cp_main(void)
 
   // Loop through sources
   for (i=0; i<toys.optc; i++) {
-    struct dirtree *new;
     char *src = toys.optargs[i];
     int rc = 1;
 
@@ -413,29 +407,31 @@ void cp_main(void)
 
     errno = EXDEV;
     if (CFG_MV && toys.which->name[0] == 'm') {
-      if (!(toys.optflags & FLAG_f)) {
+      int force = toys.optflags & FLAG_f, no_clobber = toys.optflags & FLAG_n;
+
+      if (!force || no_clobber) {
         struct stat st;
+        int exists = !stat(TT.destname, &st);
 
-        // Technically "is writeable" is more complicated (022 is not writeable
-        // by the owner, just everybody _else_) but I don't care.
-        if (!stat(TT.destname, &st)
-          && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222)))
-        {
+        // Prompt if -i or file isn't writable.  Technically "is writable" is
+        // more complicated (022 is not writeable by the owner, just everybody
+        // _else_) but I don't care.
+        if (exists && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222))) {
           fprintf(stderr, "%s: overwrite '%s'", toys.which->name, TT.destname);
           if (!yesno(1)) rc = 0;
           else unlink(TT.destname);
         }
+        // if -n and dest exists, don't try to rename() or copy
+        if (exists && no_clobber) rc = 0;
       }
-
       if (rc) rc = rename(src, TT.destname);
     }
 
-    // Skip nonexistent sources
+    // Copy if we didn't mv, skipping nonexistent sources
     if (rc) {
-      if (errno!=EXDEV ||
-        !(new = dirtree_start(src, toys.optflags&(FLAG_H|FLAG_L))))
+      if (errno!=EXDEV || dirtree_flagread(src, DIRTREE_SHUTUP+
+        DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), TT.callback))
           perror_msg("bad '%s'", src);
-      else dirtree_handle_callback(new, TT.callback);
     }
     if (destdir) free(TT.destname);
   }
@@ -503,8 +499,8 @@ void install_main(void)
   if (flags & FLAG_v) toys.optflags |= cp_flag_v();
   if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= cp_flag_p();
 
-  if (TT.i.user) TT.uid = xgetpwnamid(TT.i.user)->pw_uid;
-  if (TT.i.group) TT.gid = xgetgrnamid(TT.i.group)->gr_gid;
+  if (TT.i.user) TT.uid = xgetuid(TT.i.user);
+  if (TT.i.group) TT.gid = xgetgid(TT.i.group);
 
   TT.callback = install_node;
   cp_main();
index 981d6c5..3bd40f4 100644 (file)
@@ -114,6 +114,7 @@ void cpio_main(void)
 
     // Read header and name.
     xreadall(afd, toybuf, 110);
+    if (memcmp(toybuf, "070701", 6)) error_exit("bad cpio magic");
     tofree = name = strpad(afd, x8u(toybuf+94), 110);
     if (!strcmp("TRAILER!!!", name)) {
       if (CFG_TOYBOX_FREE) free(tofree);
@@ -180,7 +181,7 @@ void cpio_main(void)
         close(fd);
       }
     } else if (!test)
-      err = mknod(name, mode, makedev(x8u(toybuf+78), x8u(toybuf+86)));
+      err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
 
     // Set ownership and timestamp.
     if (!test && !err) {
@@ -243,8 +244,9 @@ void cpio_main(void)
         llen = sprintf(toybuf,
           "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
           (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink,
-          (int)st.st_mtime, (int)st.st_size, major(st.st_dev),
-          minor(st.st_dev), major(st.st_rdev), minor(st.st_rdev), nlen, 0);
+          (int)st.st_mtime, (int)st.st_size, dev_major(st.st_dev),
+          dev_minor(st.st_dev), dev_major(st.st_rdev), dev_minor(st.st_rdev),
+          nlen, 0);
         xwrite(afd, toybuf, llen);
         xwrite(afd, name, nlen);
 
index 1acefe2..13be5e7 100644 (file)
@@ -77,13 +77,13 @@ static void parse_list(char *list)
     if (!ctoken) break;
     if (!*ctoken) continue;
 
-    //Get start position.
+    // Get start position.
     if (*(dtoken = strsep(&ctoken, "-"))) {
       start = atolx_range(dtoken, 0, INT_MAX);
       start = (start?(start-1):start);
     }
 
-    //Get end position.
+    // Get end position.
     if (!ctoken) end = -1; //case e.g. 1,2,3
     else if (*ctoken) {//case e.g. N-M
       end = atolx_range(ctoken, 0, INT_MAX);
@@ -94,7 +94,7 @@ static void parse_list(char *list)
     add_to_list(start, end);
     TT.nelem++;
   }
-  //if list is missing in command line.
+  // if list is missing in command line.
   if (!TT.nelem) error_exit("missing positions list");
 }
 
@@ -112,7 +112,7 @@ static void get_data(void)
       if(strcmp(*argv, "-") == 0) TT.do_cut(0); //for stdin
       else {
         int fd = open(*argv, O_RDONLY, 0);
-        if(fd < 0) {//if file not present then continue with other files.
+        if (fd < 0) {//if file not present then continue with other files.
           perror_msg_raw(*argv);
           continue;
         }
index 115de52..fad46a3 100644 (file)
@@ -160,7 +160,7 @@ void du_main(void)
 
   // Loop over command line arguments, recursing through children
   for (args = toys.optc ? toys.optargs : noargs; *args; args++)
-    dirtree_handle_callback(dirtree_start(*args, toys.optflags&(FLAG_H|FLAG_L)),
+    dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
       do_du);
   if (toys.optflags & FLAG_c) print(TT.total*512, 0);
 
index 45890dc..33de678 100644 (file)
@@ -3,6 +3,11 @@
  * Copyright 2007 Rob Landley <rob@landley.net>
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/echo.html
+ *
+ * Deviations from posix: we parse command line options, as Linux has
+ * consistently done since 1992. Posix defaults -e to on, we require -e.
+ * We also honor -- to _stop_ option parsing (bash doesn't, we go with
+ * consistency over compatibility here).
 
 USE_ECHO(NEWTOY(echo, "^?en", TOYFLAG_BIN))
 
index 73458be..0853b01 100644 (file)
@@ -4,7 +4,7 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/false.html
 
-USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN))
+USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN|TOYFLAG_NOHELP))
 
 config FALSE
   bool "false"
diff --git a/toys/posix/file.c b/toys/posix/file.c
new file mode 100644 (file)
index 0000000..4827f8f
--- /dev/null
@@ -0,0 +1,305 @@
+/* file.c - describe file type
+ *
+ * Copyright 2016 The Android Open Source Project
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/file.html
+
+USE_FILE(NEWTOY(file, "<1hL[!hL]", TOYFLAG_USR|TOYFLAG_BIN))
+
+config FILE
+  bool "file"
+  default y
+  help
+    usage: file [-hL] [file...]
+
+    Examine the given files and describe their content types.
+
+    -h don't follow symlinks (default)
+    -L follow symlinks
+*/
+
+#define FOR_file
+#include "toys.h"
+
+GLOBALS(
+  int max_name_len;
+)
+
+// We don't trust elf.h to be there, and two codepaths for 32/64 is awkward
+// anyway, so calculate struct offsets manually. (It's a fixed ABI.)
+static void do_elf_file(int fd, struct stat *sb)
+{
+  int endian = toybuf[5], bits = toybuf[4], i, j;
+  int64_t (*elf_int)(void *ptr, unsigned size) = peek_le;
+  // Values from include/linux/elf-em.h (plus arch/*/include/asm/elf.h)
+  // Names are linux/arch/ directory (sometimes before 32/64 bit merges)
+  struct {int val; char *name;} type[] = {{0x9026, "alpha"}, {93, "arc"},
+    {195, "arcv2"}, {40, "arm"}, {183, "arm64"}, {0x18ad, "avr32"},
+    {106, "blackfin"}, {140, "c6x"}, {23, "cell"}, {76, "cris"},
+    {0x5441, "frv"}, {46, "h8300"}, {164, "hexagon"}, {50, "ia64"},
+    {88, "m32r"}, {0x9041, "m32r"}, {4, "m68k"}, {174, "metag"},
+    {0xbaab, "microblaze"}, {8, "mips"}, {10, "mips-old"}, {89, "mn10300"},
+    {0xbeef, "mn10300-old"}, {113, "nios2"}, {92, "openrisc"},
+    {0x8472, "openrisc-old"}, {15, "parisc"}, {20, "ppc"}, {21, "ppc64"},
+    {22, "s390"}, {0xa390, "s390-old"}, {135, "score"}, {42, "sh"},
+    {2, "sparc"}, {18, "sparc8+"}, {43, "sparc9"}, {188, "tile"},
+    {191, "tilegx"}, {3, "386"}, {6, "486"}, {62, "x86-64"}, {94, "xtensa"},
+    {0xabc7, "xtensa-old"}
+  };
+  int dynamic = 0;
+  int stripped = 1;
+  char *map;
+  off_t phoff, shoff;
+  int phsize, phnum, shsize, shnum;
+
+  printf("ELF ");
+
+  // executable (ELF says this is short but reality says byte, not MSB swapped)
+  i = toybuf[16];
+  if (i == 1) printf("relocatable");
+  else if (i == 2) printf("executable");
+  else if (i == 3) printf("shared object");
+  else if (i == 4) printf("core dump");
+  else printf("(bad type %d)", i);
+  printf(", ");
+
+  // "64-bit"
+  if (bits == 1) printf("32-bit ");
+  else if (bits == 2) printf("64-bit ");
+  else {
+    printf("(bad class %d) ", bits);
+    bits = 0;
+  }
+
+  // "LSB"
+  if (endian == 1) printf("LSB ");
+  else if (endian == 2) {
+    printf("MSB ");
+    elf_int = peek_be;
+  } else {
+    printf("(bad endian %d) \n", endian);
+    endian = 0;
+  }
+
+  // e_machine, ala "x86", from big table above
+  j = elf_int(toybuf+18, 2);
+  for (i = 0; i<ARRAY_LEN(type); i++) if (j==type[i].val) break;
+  if (i<ARRAY_LEN(type)) printf("%s", type[i].name);
+  else printf("(unknown arch %d)", j);
+
+  bits--;
+  // If what we've seen so far doesn't seem consistent, bail.
+  if (!((bits&1)==bits && endian &&
+       (i = elf_int(toybuf+42+12*bits, 2)) == 32+24*bits)) {
+    printf(", corrupt?\n");
+    return;
+  }
+
+  // Stash what we need from the header; it's okay to reuse toybuf after this.
+  phsize = i;
+  phnum = elf_int(toybuf+44+12*bits, 2);
+  phoff = elf_int(toybuf+28+4*bits, 4+4*bits);
+  shsize = elf_int(toybuf+46+12*bits, 2);
+  shnum = elf_int(toybuf+48+12*bits, 2);
+  shoff = elf_int(toybuf+32+8*bits, 4+4*bits);
+
+  map = mmap(0, sb->st_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (!map) perror_exit("mmap");
+
+  // We need to read the phdrs for dynamic vs static and any notes.
+  // (Note: fields got reordered for 64 bit)
+  for (i = 0; i<phnum; i++) {
+    char *phdr = map+phoff+i*phsize;
+    int p_type = elf_int(phdr, 4);
+    long long p_offset, p_filesz;
+
+    if (p_type==2 /*PT_DYNAMIC*/) dynamic = 1;
+    if (p_type!=3 /*PT_INTERP*/ && p_type!=4 /*PT_NOTE*/) continue;
+
+    j = bits+1;
+    p_offset = elf_int(phdr+4*j, 4*j);
+    p_filesz = elf_int(phdr+16*j, 4*j);
+
+    if (p_type==3 /*PT_INTERP*/)
+      printf(", dynamic (%.*s)", (int)p_filesz, map+p_offset);
+    else {
+      char *note = map+p_offset;
+
+      // A PT_NOTE phdr is a sequence of entries, each consisting of an
+      // ndhr followed by n_namesz+n_descsz bytes of data (each of those
+      // rounded up to the next 4 bytes, without this being reflected in
+      // the header byte counts themselves).
+      while (p_filesz >= 3*4) { // Don't try to read a truncated entry.
+        int n_namesz = elf_int(note, 4);
+        int n_descsz = elf_int(note+4, 4);
+        int n_type = elf_int(note+8, 4);
+        int notesz = 3*4 + ((n_namesz+3)&~3) + ((n_descsz+3)&~3);
+
+        if (n_namesz==4 && !memcmp(note+12, "GNU", 4)) {
+          if (n_type == 3 /*NT_GNU_BUILD_ID*/) {
+            printf(", BuildID=");
+            for (j = 0; j < n_descsz; ++j) printf("%02x", note[16 + j]);
+          }
+        } else if (n_namesz==8 && !memcmp(note+12, "Android", 8)) {
+          if (n_type==1) printf(", for Android %d", (int)elf_int(note+20, 4));
+        }
+
+        note += notesz;
+        p_filesz -= notesz;
+      }
+    }
+  }
+  if (!dynamic) printf(", static");
+
+  // We need to read the shdrs for stripped/unstripped.
+  // (Note: fields got reordered for 64 bit)
+  for (i = 0; i<shnum; i++) {
+    char *shdr = map+shoff+i*shsize;
+    int sh_type = elf_int(shdr+4, 4);
+
+    if (sh_type == 2 /*SHT_SYMTAB*/) {
+      stripped = 0;
+      break;
+    }
+  }
+  printf(", %sstripped", stripped ? "" : "not ");
+  xputc('\n');
+
+  munmap(map, sb->st_size);
+}
+
+static void do_regular_file(int fd, char *name, struct stat *sb)
+{
+  char *s;
+  int len = read(fd, s = toybuf, sizeof(toybuf)-256);
+  int magic;
+
+  if (len<0) perror_msg("%s", name);
+
+  if (len>40 && strstart(&s, "\177ELF")) do_elf_file(fd, sb);
+  else if (len>=8 && strstart(&s, "!<arch>\n")) xprintf("ar archive\n");
+  else if (len>28 && strstart(&s, "\x89PNG\x0d\x0a\x1a\x0a")) {
+    // PNG is big-endian: https://www.w3.org/TR/PNG/#7Integers-and-byte-order
+    int chunk_length = peek_be(s, 4);
+
+    xprintf("PNG image data");
+
+    // The IHDR chunk comes first: https://www.w3.org/TR/PNG/#11IHDR
+    s += 4;
+    if (chunk_length == 13 && strstart(&s, "IHDR")) {
+      // https://www.w3.org/TR/PNG/#6Colour-values
+      char *c = 0, *colors[] = {"grayscale", 0, "color RGB", "indexed color",
+                                "grayscale with alpha", 0, "color RGBA"};
+
+      if (s[9]<ARRAY_LEN(colors)) c = colors[s[9]];
+      if (!c) c = "unknown";
+
+      xprintf(", %d x %d, %d-bit/%s, %sinterlaced", (int)peek_be(s, 4),
+        (int)peek_be(s+4, 4), s[8], c, s[12] ? "" : "non-");
+    }
+
+    xputc('\n');
+
+  // https://www.w3.org/Graphics/GIF/spec-gif89a.txt
+  } else if (len>16 && (strstart(&s, "GIF87a") || strstart(&s, "GIF89a")))
+    xprintf("GIF image data, %d x %d\n",
+      (int)peek_le(s, 2), (int)peek_le(s+8, 2));
+
+  // TODO: parsing JPEG for width/height is harder than GIF or PNG.
+  else if (len>32 && memcmp(toybuf, "\xff\xd8", 2) == 0)
+    xprintf("JPEG image data\n");
+
+  // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
+  else if (len>8 && strstart(&s, "\xca\xfe\xba\xbe"))
+    xprintf("Java class file, version %d.%d\n",
+      (int)peek_be(s+2, 2), (int)peek_be(s, 2));
+
+  // https://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt
+  // the lengths for cpio are size of header + 9 bytes, since any valid
+  // cpio archive ends with a record for "TARGET!!!"
+  else if (len>85 && strstart(&s, "07070")) {
+    char *cpioformat = "unknown type";
+    if (toybuf[5] == '7') cpioformat = "pre-SVR4 or odc";
+    else if (toybuf[5] == '1') cpioformat = "SVR4 with no CRC";
+    else if (toybuf[5] == '2') cpioformat = "SVR4 with CRC";
+    xprintf("ASCII cpio archive (%s)\n", cpioformat);
+  } else if (len>33 && (magic=peek(&s,2), magic==0143561 || magic==070707)) {
+    if (magic == 0143561) printf("byte-swapped ");
+    xprintf("cpio archive\n");
+  // tar archive (ustar/pax or gnu)
+  } else if (len>500 && !strncmp(s+257, "ustar", 5)) {
+    xprintf("POSIX tar archive%s\n", strncmp(s+262,"  ",2)?"":" (GNU)");
+  // zip/jar/apk archive, ODF/OOXML document, or such
+  } else if (len>5 && strstart(&s, "PK\03\04")) {
+    int ver = (int)(char)(toybuf[4]);
+    xprintf("Zip archive data");
+    if (ver)
+      xprintf(", requires at least v%d.%d to extract", ver/10, ver%10);
+    xputc('\n');
+  } else {
+    char *what = 0;
+    int i, bytes;
+
+    // If shell script, report which interpreter
+    if (len>3 && strstart(&s, "#!")) {
+      // Whitespace is allowed between the #! and the interpreter
+      while (isspace(*s)) s++;
+      if (strstart(&s, "/usr/bin/env")) while (isspace(*s)) s++;
+      for (what = s; (s-toybuf)<len && !isspace(*s); s++);
+      strcpy(s, " script");
+
+    // Distinguish ASCII text, UTF-8 text, or data
+    } else for (i = 0; i<len; ++i) {
+      if (!(isprint(toybuf[i]) || isspace(toybuf[i]))) {
+        wchar_t wc;
+        if ((bytes = mbrtowc(&wc, s+i, len-i, 0))>0 && wcwidth(wc)>=0) {
+          i += bytes-1;
+          if (!what) what = "UTF-8 text";
+        } else {
+          what = "data";
+          break;
+        }
+      }
+    }
+    xputs(what ? what : "ASCII text");
+  }
+}
+
+void file_main(void)
+{
+  char **arg;
+
+  for (arg = toys.optargs; *arg; ++arg) {
+    int name_len = strlen(*arg);
+
+    if (name_len > TT.max_name_len) TT.max_name_len = name_len;
+  }
+
+  // Can't use loopfiles here because it doesn't call function when can't open
+  for (arg = toys.optargs; *arg; arg++) {
+    char *name = *arg, *what = "cannot open";
+    struct stat sb;
+    int fd = !strcmp(name, "-");
+
+    xprintf("%s: %*s", name, (int)(TT.max_name_len - strlen(name)), "");
+
+    if (fd || !((toys.optflags & FLAG_L) ? stat : lstat)(name, &sb)) {
+      if (fd || S_ISREG(sb.st_mode)) {
+        if (!sb.st_size) what = "empty";
+        else if ((fd = openro(name, O_RDONLY)) != -1) {
+          do_regular_file(fd, name, &sb);
+          if (fd) close(fd);
+          continue;
+        }
+      } else if (S_ISFIFO(sb.st_mode)) what = "fifo";
+      else if (S_ISBLK(sb.st_mode)) what = "block special";
+      else if (S_ISCHR(sb.st_mode)) what = "character special";
+      else if (S_ISDIR(sb.st_mode)) what = "directory";
+      else if (S_ISSOCK(sb.st_mode)) what = "socket";
+      else if (S_ISLNK(sb.st_mode)) what = "symbolic link";
+      else what = "unknown";
+    }
+
+    xputs(what);
+  }
+}
index a9c35f4..86fc141 100644 (file)
@@ -295,7 +295,7 @@ static int do_find(struct dirtree *new)
       if (check) not = !not;
       continue;
     // Mostly ignore NOP argument
-    } else if (!strcmp(s, "a") || !strcmp(s, "and")) {
+    } else if (!strcmp(s, "a") || !strcmp(s, "and") || !strcmp(s, "noleaf")) {
       if (not) goto error;
 
     } else if (!strcmp(s, "print") || !strcmp("print0", s)) {
@@ -303,9 +303,9 @@ static int do_find(struct dirtree *new)
       if (check) do_print(new, s[5] ? 0 : '\n');
 
     } else if (!strcmp(s, "nouser")) {
-      if (check) if (getpwuid(new->st.st_uid)) test = 0;
+      if (check) if (bufgetpwuid(new->st.st_uid)) test = 0;
     } else if (!strcmp(s, "nogroup")) {
-      if (check) if (getgrgid(new->st.st_gid)) test = 0;
+      if (check) if (bufgetgrgid(new->st.st_gid)) test = 0;
     } else if (!strcmp(s, "prune")) {
       if (check && S_ISDIR(new->st.st_mode) && !TT.depth) recurse = 0;
 
@@ -320,20 +320,16 @@ static int do_find(struct dirtree *new)
         // Handle path expansion and case flattening
         if (new && s[i] == 'p') name = path = dirtree_path(new, 0);
         if (i) {
-          if (check || !new) {
-            if (name) name = strlower(name);
-            if (!new) {
-              dlist_add(&TT.argdata, name);
-              free(path);
-            } else arg = ((struct double_list *)llist_pop(&argdata))->data;
-          }
+          if ((check || !new) && name) name = strlower(name);
+          if (!new) dlist_add(&TT.argdata, name);
+          else arg = ((struct double_list *)llist_pop(&argdata))->data;
         }
 
         if (check) {
           test = !fnmatch(arg, name, FNM_PATHNAME*(s[i] == 'p'));
-          free(path);
           if (i) free(name);
         }
+        free(path);
       } else if (!strcmp(s, "perm")) {
         if (check) {
           char *m = ss[1];
@@ -402,8 +398,8 @@ static int do_find(struct dirtree *new)
             udl = xmalloc(sizeof(*udl));
             dlist_add_nomalloc(&TT.argdata, (void *)udl);
 
-            if (*s == 'u') udl->u.uid = xgetpwnamid(ss[1])->pw_uid;
-            else if (*s == 'g') udl->u.gid = xgetgrnamid(ss[1])->gr_gid;
+            if (*s == 'u') udl->u.uid = xgetuid(ss[1]);
+            else if (*s == 'g') udl->u.gid = xgetgid(ss[1]);
             else {
               struct stat st;
 
@@ -458,7 +454,7 @@ static int do_find(struct dirtree *new)
           ss += len;
           aa->arglen = len;
           aa->dir = !!strchr(s, 'd');
-          if (TT.topdir == -1) TT.topdir = xopen(".", 0);
+          if (TT.topdir == -1) TT.topdir = xopenro(".");
 
         // collect names and execute commands
         } else {
@@ -556,7 +552,7 @@ void find_main(void)
 
   // Loop through paths
   for (i = 0; i < len; i++)
-    dirtree_handle_callback(dirtree_start(ss[i], toys.optflags&(FLAG_H|FLAG_L)),
+    dirtree_flagread(ss[i], DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
       do_find);
 
   execdir(0, 1);
index 2ca02d2..2fe6ac2 100644 (file)
@@ -74,24 +74,19 @@ static void outline(char *line, char dash, char *name, long lcount, long bcount,
   if (!line || (lcount && (toys.optflags&FLAG_n)))
     printf("%ld%c", lcount, line ? dash : TT.outdelim);
   if (bcount && (toys.optflags&FLAG_b)) printf("%ld%c", bcount-1, dash);
-  if (line) xprintf("%.*s%c", trim ? trim : INT_MAX/2, line, TT.outdelim);
+  if (line) xprintf("%.*s%c", trim, line, TT.outdelim);
 }
 
 // Show matches in one file
 static void do_grep(int fd, char *name)
 {
   struct double_list *dlb = 0;
-  FILE *file = fdopen(fd, "r");
+  FILE *file = xfdopen(fd, "r");
   long lcount = 0, mcount = 0, offset = 0, after = 0, before = 0;
   char *bars = 0;
 
   if (!fd) name = "(standard input)";
 
-  if (!file) {
-    perror_msg_raw(name);
-    return;
-  }
-
   // Loop through lines of input
   for (;;) {
     char *line = 0, *start;
@@ -201,13 +196,13 @@ static void do_grep(int fd, char *name)
           while (dlb) {
             struct double_list *dl = dlist_pop(&dlb);
 
-            outline(dl->data, '-', name, lcount-before, 0, 0);
+            outline(dl->data, '-', name, lcount-before, 0, -1);
             free(dl->data);
             free(dl);
             before--;
           }
 
-          outline(line, ':', name, lcount, bcount, 0);
+          outline(line, ':', name, lcount, bcount, -1);
           if (TT.a) after = TT.a+1;
         } else outline(start+matches.rm_so, ':', name, lcount, bcount,
                        matches.rm_eo-matches.rm_so);
@@ -223,7 +218,7 @@ static void do_grep(int fd, char *name)
       int discard = (after || TT.b);
 
       if (after && --after) {
-        outline(line, '-', name, lcount, 0, 0);
+        outline(line, '-', name, lcount, 0, -1);
         discard = 0;
       }
       if (discard && TT.b) {
@@ -247,7 +242,7 @@ static void do_grep(int fd, char *name)
     if ((toys.optflags & FLAG_m) && mcount >= TT.m) break;
   }
 
-  if (toys.optflags & FLAG_c) outline(0, ':', name, mcount, 0, 0);
+  if (toys.optflags & FLAG_c) outline(0, ':', name, mcount, 0, -1);
 
   // loopfiles will also close the fd, but this frees an (opaque) struct.
   fclose(file);
@@ -359,7 +354,7 @@ void grep_main(void)
   toys.exitval = 1;
   if (toys.optflags & FLAG_s) {
     close(2);
-    xopen("/dev/null", O_RDWR);
+    xopen_stdio("/dev/null", O_RDWR);
   }
 
   if (toys.optflags & FLAG_r) {
@@ -368,5 +363,5 @@ void grep_main(void)
       if (!strcmp(*ss, "-")) do_grep(0, *ss);
       else dirtree_read(*ss, do_grep_r);
     }
-  } else loopfiles_rw(ss, O_RDONLY, 0, 1, do_grep);
+  } else loopfiles_rw(ss, O_RDONLY|WARN_ONLY, 0, do_grep);
 }
index 01610bc..4b732b1 100644 (file)
@@ -73,7 +73,7 @@ static void s_or_u(char *s, unsigned u, int done)
   else printf("%u", u);
   if (done) {
     xputc('\n');
-    exit(0);
+    xexit();
   }
 }
 
@@ -143,7 +143,7 @@ static void do_id(char *username)
     }
     if (toys.optflags&FLAG_G) {
       xputc('\n');
-      exit(0);
+      xexit();
     }
   }
 
index 19a08f9..b052eac 100644 (file)
@@ -5,7 +5,7 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html
 
-USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSacdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL]", TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_LS(NEWTOY(ls, USE_LS_COLOR("(color):;")"ZgoACFHLRSabcdfhiklmnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
 
 config LS
   bool "ls"
@@ -16,24 +16,24 @@ config LS
     list files
 
     what to show:
-    -a all files including .hidden             -c  use ctime for timestamps
-    -d directory, not contents                 -i  inode number
-    -k block sizes in kilobytes                -p  put a '/' after dir names
-    -q unprintable chars as '?'                -s  size (in blocks)
-    -u use access time for timestamps          -A  list all files but . and ..
-    -H follow command line symlinks            -L  follow symlinks
-    -R recursively list files in subdirs       -F  append /dir *exe @sym |FIFO
-    -Z security context
+    -a  all files including .hidden    -b  escape nongraphic chars
+    -c  use ctime for timestamps       -d  directory, not contents
+    -i  inode number                   -p  put a '/' after dir names
+    -q  unprintable chars as '?'       -s  storage used (1024 byte units)
+    -u  use access time for timestamps -A  list all files but . and ..
+    -H  follow command line symlinks   -L  follow symlinks
+    -R  recursively list in subdirs    -F  append /dir *exe @sym |FIFO
+    -Z  security context
 
     output formats:
-    -1 list one file per line                  -C  columns (sorted vertically)
-    -g like -l but no owner                    -h  human readable sizes
-    -l long (show full details)                -m  comma separated
-    -n like -l but numeric uid/gid             -o  like -l but no group
-    -x columns (horizontal sort)
+    -1  list one file per line         -C  columns (sorted vertically)
+    -g  like -l but no owner           -h  human readable sizes
+    -l  long (show full details)       -m  comma separated
+    -n  like -l but numeric uid/gid    -o  like -l but no group
+    -x  columns (horizontal sort)
 
     sorting (default is alphabetical):
-    -f unsorted        -r  reverse     -t  timestamp   -S  size
+    -f  unsorted    -r  reverse    -t  timestamp    -S  size
 
 config LS_COLOR
   bool "ls --color"
@@ -61,34 +61,53 @@ GLOBALS(
 
   unsigned screen_width;
   int nl_title;
-  char uid_buf[12], gid_buf[12];
+  char *escmore;
 )
 
-// Does two things: 1) Returns wcwidth(utf8) version of strlen,
-// 2) replaces unprintable characters input string with '?' wildcard char.
-int strwidth(char *s)
+// Callback from crunch_str to represent unprintable chars
+int crunch_qb(FILE *out, int cols, int wc)
 {
-  int total = 0, width, len;
-  wchar_t c;
-
-  if (!CFG_TOYBOX_I18N) {
-    total = strlen(s);
-    if (toys.optflags & FLAG_q) for (; *s; s++) if (!isprint(*s)) *s = '?';
-  } else while (*s) {
-    len = mbrtowc(&c, s, MB_CUR_MAX, 0);
-    if (len < 1 || (width = wcwidth(c)) < 0) {
-      total++;
-      if (toys.optflags & FLAG_q) *s = '?';
-      s++;
-    } else {
-      s += len;
-      total += width;
+  unsigned len = 1;
+  char buf[32];
+
+  if (toys.optflags&FLAG_q) *buf = '?';
+  else {
+    if (wc<256) *buf = wc;
+    // scrute the inscrutable, eff the ineffable, print the unprintable
+    else len = wcrtomb(buf, wc, 0);
+    if (toys.optflags&FLAG_b) {
+      char *to = buf, *from = buf+24;
+      int i, j;
+
+      memcpy(from, to, 8);
+      for (i = 0; i<len; i++) {
+        *to++ = '\\';
+        if (strchr(TT.escmore, from[i])) *to++ = from[i];
+        else if (-1 != (j = stridx("\\\a\b\033\f\n\r\t\v", from[i])))
+          *to++ = "\\abefnrtv"[j];
+        else to += sprintf(to, "%03o", from[i]);
+      }
+      len = to-buf;
     }
   }
 
-  return total;
+  if (cols<len) len = cols;
+  if (out) fwrite(buf, len, 1, out);
+
+  return len;
+}
+
+// Returns wcwidth(utf8) version of strlen with -qb escapes
+int strwidth(char *s)
+{
+  return crunch_str(&s, INT_MAX, 0, TT.escmore, crunch_qb);
 }
 
+void qbstr(char *s, int width)
+{
+  draw_trim_esc(s, width, abs(width), TT.escmore, crunch_qb);
+} 
+
 static char endtype(struct stat *st)
 {
   mode_t mode = st->st_mode;
@@ -102,25 +121,15 @@ static char endtype(struct stat *st)
   return 0;
 }
 
-static char *getusername(uid_t uid)
-{
-  struct passwd *pw = getpwuid(uid);
-
-  sprintf(TT.uid_buf, "%u", (unsigned)uid);
-  return pw ? pw->pw_name : TT.uid_buf;
-}
-
-static char *getgroupname(gid_t gid)
+static int numlen(long long ll)
 {
-  struct group *gr = getgrgid(gid);
-
-  sprintf(TT.gid_buf, "%u", (unsigned)gid);
-  return gr ? gr->gr_name : TT.gid_buf;
+  return snprintf(0, 0, "%llu", ll);
 }
 
-static int numlen(long long ll)
+static int print_with_h(char *s, long long value, int units)
 {
-  return snprintf(0, 0, "%llu", ll);
+  if (toys.optflags&FLAG_h) return human_readable(s, value*units, 0);
+  else return sprintf(s, "%lld", value);
 }
 
 // Figure out size of printable entry fields for display indent/wrap
@@ -144,14 +153,11 @@ static void entrylen(struct dirtree *dt, unsigned *len)
     if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
       // cheating slightly here: assuming minor is always 3 digits to avoid
       // tracking another column
-      len[5] = numlen(major(st->st_rdev))+5;
-    } else if (flags & FLAG_h) {
-        human_readable(tmp, st->st_size, 0);
-        len[5] = strwidth(tmp);
-    } else len[5] = numlen(st->st_size);
+      len[5] = numlen(dev_major(st->st_rdev))+5;
+    } else len[5] = print_with_h(tmp, st->st_size, 1);
   }
 
-  len[6] = (flags & FLAG_s) ? numlen(st->st_blocks) : 0;
+  len[6] = (flags & FLAG_s) ? print_with_h(tmp, st->st_blocks, 512) : 0;
   len[7] = (flags & FLAG_Z) ? strwidth((char *)dt->extra) : 0;
 }
 
@@ -216,7 +222,7 @@ static int filter(struct dirtree *new)
 
   if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime;
   if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime;
-  if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
+  new->st.st_blocks >>= 1;
 
   if (flags & (FLAG_a|FLAG_f)) return DIRTREE_SAVE;
   if (!(flags & FLAG_A) && new->name[0]=='.') return 0;
@@ -292,7 +298,6 @@ static void listfiles(int dirfd, struct dirtree *indir)
   char tmp[64];
 
   if (-1 == dirfd) {
-    strwidth(indir->name);
     perror_msg_raw(indir->name);
 
     return;
@@ -316,12 +321,10 @@ static void listfiles(int dirfd, struct dirtree *indir)
     // Do preprocessing (Dirtree didn't populate, so callback wasn't called.)
     for (;dt; dt = dt->next) filter(dt);
     if (flags == (FLAG_1|FLAG_f)) return;
-  } else {
-    // Read directory contents. We dup() the fd because this will close it.
-    // This reads/saves contents to display later, except for in "ls -1f" mode.
-    indir->dirfd = dup(dirfd);
-    dirtree_recurse(indir, filter, DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
-  }
+  // Read directory contents. We dup() the fd because this will close it.
+  // This reads/saves contents to display later, except for in "ls -1f" mode.
+  } else  dirtree_recurse(indir, filter, dup(dirfd),
+      DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
 
   // Copy linked list to array and sort it. Directories go in array because
   // we visit them in sorted order too. (The nested loops let us measure and
@@ -355,10 +358,8 @@ static void listfiles(int dirfd, struct dirtree *indir)
     }
     totpad = totals[1]+!!totals[1]+totals[6]+!!totals[6]+totals[7]+!!totals[7];
     if ((flags&(FLAG_h|FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) && indir->parent) {
-      if (flags&FLAG_h) {
-        human_readable(tmp, blocks*512, 0);
-        xprintf("total %s\n", tmp);
-      } else xprintf("total %llu\n", blocks);
+      print_with_h(tmp, blocks, 512);
+      xprintf("total %s\n", tmp);
     }
   }
 
@@ -377,7 +378,7 @@ static void listfiles(int dirfd, struct dirtree *indir)
       memset(colsizes, 0, columns*sizeof(unsigned));
       for (ul=0; ul<dtlen; ul++) {
         entrylen(sort[next_column(ul, dtlen, columns, &c)], len);
-        *len += totpad;
+        *len += totpad+1;
         if (c == columns) break;
         // Expand this column if necessary, break if that puts us over budget
         if (*len > colsizes[c]) {
@@ -395,11 +396,12 @@ static void listfiles(int dirfd, struct dirtree *indir)
   memset(toybuf, ' ', 256);
   width = 0;
   for (ul = 0; ul<dtlen; ul++) {
+    int ii;
     unsigned curcol, color = 0;
     unsigned long next = next_column(ul, dtlen, columns, &curcol);
     struct stat *st = &(sort[next]->st);
     mode_t mode = st->st_mode;
-    char et = endtype(st);
+    char et = endtype(st), *ss;
 
     // Skip directories at the top of the tree when -d isn't set
     if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue;
@@ -411,24 +413,25 @@ static void listfiles(int dirfd, struct dirtree *indir)
       if (flags & FLAG_m) xputc(',');
       if (flags & (FLAG_C|FLAG_x)) {
         if (!curcol) xputc('\n');
-      } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) {
+      } else if ((flags & FLAG_1) || width+2+*len > TT.screen_width) {
         xputc('\n');
         width = 0;
       } else {
-        xputc(' ');
-        width++;
+        printf("  ");
+        width += 2;
       }
     }
     width += *len;
 
-    if (flags & FLAG_i)
-      xprintf("%*lu ", totals[1], (unsigned long)st->st_ino);
-    if (flags & FLAG_s)
-      xprintf("%*lu ", totals[6], (unsigned long)st->st_blocks);
+    if (flags & FLAG_i) printf("%*lu ", totals[1], (unsigned long)st->st_ino);
+
+    if (flags & FLAG_s) {
+      print_with_h(tmp, st->st_blocks, 512);
+      printf("%*s ", totals[6], tmp);
+    }
 
     if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
       struct tm *tm;
-      char *ss;
 
       // (long) is to coerce the st types into something we know we can print.
       mode_to_string(mode, tmp);
@@ -436,16 +439,20 @@ static void listfiles(int dirfd, struct dirtree *indir)
 
       // print user
       if (!(flags&FLAG_g)) {
-        if (flags&FLAG_n) sprintf(ss = tmp, "%u", (unsigned)st->st_uid);
-        else strwidth(ss = getusername(st->st_uid));
-        printf(" %-*s", (int)totals[3], ss);
+        putchar(' ');
+        ii = -totals[3];
+        if (flags&FLAG_n) printf("%*u", ii, (unsigned)st->st_uid);
+        else draw_trim_esc(getusername(st->st_uid), ii, abs(ii), TT.escmore,
+                           crunch_qb);
       }
 
       // print group
       if (!(flags&FLAG_o)) {
-        if (flags&FLAG_n) sprintf(ss = tmp, "%u", (unsigned)st->st_gid);
-        else strwidth(ss = getgroupname(st->st_gid));
-        printf(" %-*s", (int)totals[4], ss);
+        putchar(' ');
+        ii = -totals[4];
+        if (flags&FLAG_n) printf("%*u", ii, (unsigned)st->st_gid);
+        else draw_trim_esc(getgroupname(st->st_gid), ii, abs(ii), TT.escmore,
+                           crunch_qb);
       }
 
       if (flags & FLAG_Z)
@@ -453,16 +460,17 @@ static void listfiles(int dirfd, struct dirtree *indir)
 
       // print major/minor, or size
       if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
-        printf("% *d,% 4d", totals[5]-4, major(st->st_rdev),minor(st->st_rdev));
-      else if (flags&FLAG_h) {
-        human_readable(tmp, st->st_size, 0);
-        xprintf("%*s", totals[5]+1, tmp);
-      } else printf("% *lld", totals[5]+1, (long long)st->st_size);
+        printf("% *d,% 4d", totals[5]-4, dev_major(st->st_rdev),
+          dev_minor(st->st_rdev));
+      else {
+        print_with_h(tmp, st->st_size, 1);
+        printf("%*s", totals[5]+1, tmp);
+      }
 
       // print time, always in --time-style=long-iso
       tm = localtime(&(st->st_mtime));
       strftime(tmp, sizeof(tmp), "%F %H:%M", tm);
-      xprintf(" %s ", tmp);
+      printf(" %s ", tmp);
     } else if (flags & FLAG_Z)
       printf("%-*s ", (int)totals[7], (char *)sort[next]->extra);
 
@@ -471,11 +479,9 @@ static void listfiles(int dirfd, struct dirtree *indir)
       if (color) printf("\033[%d;%dm", color>>8, color&255);
     }
 
-    if (flags & FLAG_q) {
-      char *p;
-      for (p=sort[next]->name; *p; p++) fputc(isprint(*p) ? *p : '?', stdout);
-    } else xprintf("%s", sort[next]->name);
-    if (color) xprintf("\033[0m");
+    ss = sort[next]->name;
+    crunch_str(&ss, INT_MAX, stdout, TT.escmore, crunch_qb);
+    if (color) printf("\033[0m");
 
     if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode)) {
       printf(" -> ");
@@ -497,7 +503,7 @@ static void listfiles(int dirfd, struct dirtree *indir)
     // Pad columns
     if (flags & (FLAG_C|FLAG_x)) {
       curcol = colsizes[curcol]-(*len)-totpad;
-      if (curcol < 255) xprintf("%s", toybuf+255-curcol);
+      if (curcol < 255) printf("%s", toybuf+255-curcol);
     }
   }
 
@@ -525,6 +531,7 @@ void ls_main(void)
   TT.screen_width = 80;
   terminal_size(&TT.screen_width, NULL);
   if (TT.screen_width<2) TT.screen_width = 2;
+  if (toys.optflags&FLAG_b) TT.escmore = " \\";
 
   // Do we have an implied -1
   if (!isatty(1)) {
@@ -539,12 +546,15 @@ void ls_main(void)
   if (toys.optflags & FLAG_d) toys.optflags &= ~FLAG_R;
 
   // Iterate through command line arguments, collecting directories and files.
-  // Non-absolute paths are relative to current directory.
-  TT.files = dirtree_start(0, 0);
+  // Non-absolute paths are relative to current directory. Top of tree is
+  // a dummy node to collect command line arguments into pseudo-directory.
+  TT.files = dirtree_add_node(0, 0, 0);
   TT.files->dirfd = AT_FDCWD;
   for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
-    dt = dirtree_start(*s, !(toys.optflags&(FLAG_l|FLAG_d|FLAG_F)) ||
-                            (toys.optflags&(FLAG_L|FLAG_H)));
+    int sym = !(toys.optflags&(FLAG_l|FLAG_d|FLAG_F))
+      || (toys.optflags&(FLAG_L|FLAG_H));
+
+    dt = dirtree_add_node(0, *s, DIRTREE_SYMFOLLOW*sym);
 
     // note: double_list->prev temporarirly goes in dirtree->parent
     if (dt) dlist_add_nomalloc((void *)&TT.files->child, (void *)dt);
index 4d6d59f..b302cbe 100644 (file)
@@ -36,7 +36,7 @@ void nohup_main(void)
   }
   if (isatty(0)) {
     close(0);
-    open("/dev/null", O_RDONLY);
+    xopen_stdio("/dev/null", O_RDONLY);
   }
   xexec(toys.optargs);
 }
index 99219bf..5ccea58 100644 (file)
@@ -5,21 +5,22 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/od.html
 
-USE_OD(NEWTOY(od, "j#vN#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
+USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
 
 config OD
   bool "od"
   default y
   help
-    usage: od [-bcdosxv] [-j #] [-N #] [-A doxn] [-t acdfoux[#]]
+    usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]]
 
     -A Address base (decimal, octal, hexdecimal, none)
     -j Skip this many bytes of input
     -N Stop dumping after this many bytes
-    -t output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x
+    -t Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x
        plus optional size in bytes
        aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2
     -v Don't collapse repeated lines together
+    -w Total line width in bytes (default 16).
 */
 
 #define FOR_od
@@ -29,12 +30,13 @@ GLOBALS(
   struct arg_list *output_base;
   char *address_base;
   long max_count;
+  long width;
   long jump_bytes;
 
   int address_idx;
   unsigned types, leftover, star;
-  char *buf;
-  uint64_t bufs[4]; // force 64-bit alignment
+  char *buf; // Points to buffers[0] or buffers[1].
+  char *bufs[2]; // Used to detect duplicate lines.
   off_t pos;
 )
 
@@ -129,11 +131,11 @@ static void od_outline(void)
   struct odtype *types = (struct odtype *)toybuf;
   int i, j, len, pad;
 
-  if (TT.leftover<16) memset(TT.buf+TT.leftover, 0, 16-TT.leftover);
+  if (TT.leftover<TT.width) memset(TT.buf+TT.leftover, 0, TT.width-TT.leftover);
 
   // Handle duplciate lines as *
   if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover
-    && !memcmp(TT.bufs, TT.bufs + 2, 16))
+    && !memcmp(TT.bufs[0], TT.bufs[1], TT.width))
   {
     if (!TT.star) {
       xputs("*");
@@ -168,7 +170,6 @@ static void od_outline(void)
   }
 
   // For each output type, print one line
-
   for (i=0; i<TT.types; i++) {
     for (j = 0; j<len;) {
       int bytes = j;
@@ -181,8 +182,8 @@ static void od_outline(void)
     xputc('\n');
   }
 
-  // buffer toggle for "same as last time" check.
-  TT.buf = (char *)((TT.buf == (char *)TT.bufs) ? TT.bufs+2 : TT.bufs);
+  // Toggle buffer for "same as last time" check.
+  TT.buf = (TT.buf == TT.bufs[0]) ? TT.bufs[1] : TT.bufs[0];
 }
 
 // Loop through input files
@@ -198,7 +199,7 @@ static void do_od(int fd, char *name)
 
   for(;;) {
     char *buf = TT.buf + TT.leftover;
-    int len = 16 - TT.leftover;
+    int len = TT.width - TT.leftover;
 
     if (toys.optflags & FLAG_N) {
       if (!TT.max_count) break;
@@ -212,7 +213,7 @@ static void do_od(int fd, char *name)
     }
     if (TT.max_count) TT.max_count -= len;
     TT.leftover += len;
-    if (TT.leftover < 16) break;
+    if (TT.leftover < TT.width) break;
 
     od_outline();
   }
@@ -264,7 +265,9 @@ void od_main(void)
 {
   struct arg_list *arg;
 
-  TT.buf = (char *)TT.bufs;
+  TT.bufs[0] = xzalloc(TT.width);
+  TT.bufs[1] = xzalloc(TT.width);
+  TT.buf = TT.bufs[0];
 
   if (!TT.address_base) TT.address_idx = 2;
   else if (0>(TT.address_idx = stridx("ndox", *TT.address_base)))
@@ -285,4 +288,9 @@ void od_main(void)
 
   if (TT.leftover) od_outline();
   od_outline();
+
+  if (CFG_TOYBOX_FREE) {
+    free(TT.bufs[0]);
+    free(TT.bufs[1]);
+  }
 }
index 23fe83a..fbad1fb 100644 (file)
  * -F fuzz (number, default 2)
  * [file] which file to patch
 
-USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PATCH(NEWTOY(patch, "(dry-run)"USE_TOYBOX_DEBUG("x")"d:ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
 
 config PATCH
   bool "patch"
   default y
   help
-    usage: patch [-i file] [-p depth] [-Ru]
+    usage: patch [-d DIR] [-i file] [-p depth] [-Rlu] [--dry-run]
 
     Apply a unified diff to one or more files.
 
+    -d modify files in DIR
     -i Input file (defaults=stdin)
     -l Loose match (ignore whitespace)
     -p Number of '/' to strip from start of file paths (default=all)
     -R Reverse patch.
     -u Ignored (only handles "unified" diffs)
+    --dry-run Don't change files, just confirm patch applies
 
     This version of patch only handles unified diffs, and only modifies
     a file when all all hunks to that file apply.  Patch prints failed
@@ -48,6 +50,7 @@ config PATCH
 GLOBALS(
   char *infile;
   long prefix;
+  char *dir;
 
   struct double_list *current_hunk;
   long oldline, oldlen, newline, newlen;
@@ -102,7 +105,8 @@ static void fail_hunk(void)
   TT.state = 2;
   llist_traverse(TT.current_hunk, do_line);
   TT.current_hunk = NULL;
-  delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
+  if (!(toys.optflags & FLAG_dry_run))
+    delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
   TT.state = 0;
 }
 
@@ -261,9 +265,11 @@ void patch_main(void)
     strip = 0;
   char *oldname = NULL, *newname = NULL;
 
-  if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY);
+  if (TT.infile) TT.filepatch = xopenro(TT.infile);
   TT.filein = TT.fileout = -1;
 
+  if (TT.dir) xchdir(TT.dir);
+
   // Loop through the lines in the patch
   for (;;) {
     char *patchline;
@@ -396,9 +402,11 @@ void patch_main(void)
             TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
           } else {
             printf("patching %s\n", name);
-            TT.filein = xopen(name, O_RDONLY);
+            TT.filein = xopenro(name);
           }
-          TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
+          if (toys.optflags & FLAG_dry_run)
+            TT.fileout = xopen("/dev/null", O_RDWR);
+          else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
           TT.linenum = 0;
           TT.hunknum = 0;
         }
index 365b8f3..2dd1e2f 100644 (file)
@@ -33,7 +33,7 @@ static int eat(char **s, char c)
 }
 
 // Parse escape sequences.
-static int handle_slash(char **esc_val)
+static int handle_slash(char **esc_val, int posix)
 {
   char *ptr = *esc_val;
   int len, base = 0;
@@ -43,7 +43,10 @@ static int handle_slash(char **esc_val)
 
   // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits.
   if (eat(&ptr, 'x')) base = 16;
-  else if (*ptr >= '0' && *ptr <= '8') base = 8;
+  else {
+    if (posix && *ptr=='0') ptr++;
+    if (*ptr >= '0' && *ptr <= '7') base = 8;
+  }
   len = (char []){0,3,2}[base/8];
 
   // Not a hex or octal escape? (This catches trailing \)
@@ -85,7 +88,7 @@ void printf_main(void)
 
     // Loop through characters in format
     while (*f) {
-      if (eat(&f, '\\')) putchar(handle_slash(&f));
+      if (eat(&f, '\\')) putchar(handle_slash(&f, 0));
       else if (!eat(&f, '%') || *f == '%') putchar(*f++);
 
       // Handle %escape
@@ -110,7 +113,7 @@ void printf_main(void)
 
         // Output %esc using parsed format string
         if (c == 'b') {
-          while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa) : *aa++);
+          while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa, 1) : *aa++);
 
           continue;
         } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa);
index f99a216..6a128bd 100644 (file)
  * TODO: top: thread support and SMP
  * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
 
-USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae][!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 // stayroot because iotop needs root to read other process' proc/$$/io
-USE_TOP(NEWTOY(top, ">0m" "k*o*p*u*s#<1=9d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_TOP(NEWTOY(top, ">0m" "O*Hk*o*p*u*s#<1d#=3<1n#<1bq[!oO]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PKILL(NEWTOY(pkill,     "Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PKILL(NEWTOY(pkill,    "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config PS
   bool "ps"
   default y
   help
-    usage: ps [-AadeflnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
+    usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]
 
     List processes.
 
@@ -70,6 +70,7 @@ config PS
     -P Parent PIDs (--ppid)
     -s In session IDs
     -t Attached to selected TTYs
+    -T Show threads
     -u Owned by USERs
     -U Owned by real USERs (before suid)
 
@@ -82,23 +83,28 @@ config PS
 
     Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
 
-    -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
+    -f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
     -l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
     -o Output FIELDs instead of defaults, each with optional :size and =title
-    -O  Add FIELDS to defaults
+    -O Add FIELDS to defaults
     -Z Include LABEL
 
-    Available -o FIELDs:
+    Command line -o fields:
 
-      ADDR  Instruction pointer               ARGS    Command line (argv[] -path)
-      CMD   COMM without -f, ARGS with -f     CMDLINE Command line (argv[])
-      COMM  Original command name             COMMAND Original command path
+      ARGS     CMDLINE minus initial path     CMD  Command (thread) name (stat[2])
+      CMDLINE  Command line (argv[])          COMM Command filename (/proc/$PID/exe)
+      COMMAND  Command file (/proc/$PID/exe)  NAME Process name (argv[0] of $PID)
+
+    Process attribute -o FIELDs:
+
+      ADDR  Instruction pointer               BIT   Is this process 32 or 64 bits
       CPU   Which processor running on        ETIME   Elapsed time since PID start
       F     Flags (1=FORKNOEXEC 4=SUPERPRIV)  GID     Group id
       GROUP Group name                        LABEL   Security label
       MAJFL Major page faults                 MINFL   Minor page faults
-      NAME  Command name (argv[0])            NI      Niceness (lower is faster)
-      PCPU  Percentage of CPU time used       PGID    Process Group ID
+      NI    Niceness (lower is faster)
+      PCPU  Percentage of CPU time used       PCY     Android scheduling policy
+      PGID  Process Group ID
       PID   Process ID                        PPID    Parent Process ID
       PRI   Priority (higher is faster)       PSR     Processor last executed on
       RGID  Real (before sgid) group ID       RGROUP  Real (before sgid) group name
@@ -113,26 +119,31 @@ config PS
             s session leader         + foreground   l multithreaded
       STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
       SZ    Memory Size (4k pages needed to completely swap out process)
+      TCNT  Thread count                      TID     Thread ID
       TIME  CPU time consumed                 TTY     Controlling terminal
       UID   User id                           USER    User name
       VSZ   Virtual memory size (1k units)    %VSZ    VSZ as % of physical memory
-      WCHAN Waiting in kernel for
+      WCHAN What are we waiting in kernel for
 
 config TOP
   bool "top"
+  depends on TOP_COMMON
   default y
   help
-    usage: top [-m] [ -d seconds ] [ -n iterations ]
+    usage: top [-H] [-k FIELD,] [-o FIELD,] [-s SORT]
 
     Show process activity in real time.
 
+    -H Show threads
     -k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
     -o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
+    -O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
     -s Sort by field number (1-X, default 9)
 
 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
 config IOTOP
   bool "iotop"
+  depends on TOP_COMMON
   default y
   help
     usage: iotop [-AaKO]
@@ -151,7 +162,7 @@ config TOP_COMMON
   bool
   default y
   help
-    usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
+    usage: * [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
 
     -b Batch mode (no tty)
     -d Delay SECONDS between each cycle (default 3)
@@ -178,11 +189,21 @@ config PGREP
     -L Send SIGNAL instead of printing name
     -l Show command name
 
+config PKILL
+  bool "pkill"
+  default y
+  depends on PGKILL_COMMON
+  help
+    usage: pkill [-SIGNAL|-l SIGNAL] [PATTERN]
+
+    -l Send SIGNAL (default SIGTERM)
+    -V verbose
+
 config PGKILL_COMMON
   bool
   default y
   help
-    usage: pgrep [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
+    usage: * [-fnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,]
 
     -f Check full command line for PATTERN
     -G Match real Group ID(s)
@@ -196,15 +217,6 @@ config PGKILL_COMMON
     -u Match effective User ID(s)
     -v Negate the match
     -x Match whole command (not substring)
-
-config PKILL
-  bool "pkill"
-  default y
-  help
-    usage: pkill [-l SIGNAL] [PATTERN]
-
-    -l SIGNAL to send
-    -V verbose
 */
 
 #define FOR_ps
@@ -233,8 +245,9 @@ GLOBALS(
       struct arg_list *p;
       struct arg_list *o;
       struct arg_list *k;
+      struct arg_list *O;
     } top;
-    struct{
+    struct {
       char *L;
       struct arg_list *G;
       struct arg_list *g;
@@ -253,6 +266,7 @@ GLOBALS(
 
   struct sysinfo si;
   struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
+  struct dirtree *threadparent;
   unsigned width, height;
   dev_t tty;
   void *fields, *kfields;
@@ -300,15 +314,19 @@ enum {
  SLOT_rss2,     /*Resident Set Size*/     SLOT_shr,       // Shared memory
  SLOT_rchar,    /*All bytes read*/        SLOT_wchar,     // All bytes written
  SLOT_rbytes,   /*Disk bytes read*/       SLOT_wbytes,    // Disk bytes written
- SLOT_swap,     /*Swap pages used*/
+ SLOT_swap,     /*Swap pages used*/       SLOT_bits,      // 32 or 64
+ SLOT_tid,      /*Thread ID*/             SLOT_tcount,    // Thread count
+ SLOT_pcy,      /*Android sched policy*/
+
+ SLOT_count
 };
 
 // Data layout in toybuf
 struct carveup {
-  long long slot[55];       // data from /proc
-  unsigned short offset[5]; // offset of fields in str[] (skip name, always 0)
+  long long slot[SLOT_count]; // data (see enum above)
+  unsigned short offset[6];   // offset of fields in str[] (skip name, always 0)
   char state;
-  char str[];               // name, tty, command, wchan, attr, cmdline
+  char str[];                 // name, tty, command, wchan, attr, cmdline
 };
 
 // TODO: Android uses -30 for LABEL, but ideally it would auto-size.
@@ -320,15 +338,16 @@ struct typography {
   // Numbers
   {"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
   {"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
-  {"SZ", 5, SLOT_vsize}, {"RSS", 5, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
-  {"VSZ", 6, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
+  {"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
+  {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
   {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
   {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
+  {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
 
   // String fields
-  {"COMM", -15, -1}, {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4},
-  {"COMMAND", -27, -5}, {"CMDLINE", -27, -6}, {"ARGS", -27, -6},
-  {"NAME", -15, -6}, {"CMD", -27, -1},
+  {"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
+  {"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
+  {"ARGS", -27, -6}, {"CMD", -15, -1},
 
   // user/group
   {"UID", 5, SLOT_uid}, {"USER", -8, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
@@ -351,7 +370,7 @@ struct typography {
 
   // Misc
   {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
-  {"STAT", -5, 64},
+  {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
 );
 
 // Return 0 to discard, nonzero to keep
@@ -368,6 +387,7 @@ static int shared_match_process(long long *slot)
   // Do we have -g -G -p -P -s -t -u -U options selecting processes?
   for (i = 0; i < ARRAY_LEN(match); i++) {
     struct ptr_len *mm = match[i].ptr;
+
     if (mm->len) {
       ll = mm->ptr;
       for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
@@ -404,7 +424,7 @@ static char *string_field(struct carveup *tb, struct strawberry *field)
   long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&63] : 0;
 
   // numbers, mostly from /proc/$PID/stat
-  if (which <= PS_CPU) {
+  if (which <= PS_BIT) {
     char *fmt = "%lld";
 
     if (which==PS_PRI) ll = 39-ll;
@@ -413,30 +433,35 @@ static char *string_field(struct carveup *tb, struct strawberry *field)
     else if (which==PS_RSS) ll <<= 2;
     else if (which==PS_VSZ) ll >>= 10;
     else if (which==PS_PR && ll<-9) fmt="RT";
-    else if (which==PS_RTPRIO && ll == 0) fmt="-";
+    else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
     sprintf(out, fmt, ll);
 
   // String fields
   } else if (sl < 0) {
-    if (slot[SLOT_argv0len])
-      tb->str[tb->offset[4]+slot[SLOT_argv0len]] = (which==PS_NAME) ? 0 : ' ';
     out = tb->str;
     sl *= -1;
+    // First string slot has offset 0, others are offset[-slot-2]
     if (--sl) out += tb->offset[--sl];
-    if (which==PS_ARGS)
-      for (s = out; *s && *s != ' '; s++) if (*s == '/') out = s+1;
-    if (which>=PS_COMMAND && !*out) sprintf(out = buf, "[%s]", tb->str);
+    if (which==PS_ARGS || which==PS_COMM) {
+      int i;
+
+      s = out;
+      for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
+        if (out[i] == '/') s = out+i+1;
+      out = s;
+    }
+    if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
 
   // user/group
   } else if (which <= PS_RGROUP) {
     sprintf(out, "%lld", ll);
     if (sl&64) {
       if (which > PS_RUSER) {
-        struct group *gr = getgrgid(ll);
+        struct group *gr = bufgetgrgid(ll);
 
         if (gr) out = gr->gr_name;
       } else {
-        struct passwd *pw = getpwuid(ll);
+        struct passwd *pw = bufgetpwuid(ll);
 
         if (pw) out = pw->pw_name;
       }
@@ -512,7 +537,8 @@ static char *string_field(struct carveup *tb, struct strawberry *field)
     out = out+strlen(out)-3-abs(field->len);
     if (out<buf) out = buf;
 
-  } else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
+  } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
+  else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
 
   return out;
 }
@@ -521,21 +547,44 @@ static char *string_field(struct carveup *tb, struct strawberry *field)
 static void show_ps(struct carveup *tb)
 {
   struct strawberry *field;
-  int pad, len, width = TT.width;
+  int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
 
   // Loop through fields to display
   for (field = TT.fields; field; field = field->next) {
     char *out = string_field(tb, field);
 
     // Output the field, appropriately padded
+
+    // Minimum one space between each field
     if (field != TT.fields) {
       putchar(' ');
       width--;
     }
-    len = width;
-    pad = 0;
-    if (field->next || field->len>0)
-      len = abs(pad = width<abs(field->len) ? width : field->len);
+
+    // Don't truncate number fields, but try to reclaim extra offset from later
+    // fields that can naturally be shorter
+    abslen = abs(field->len);
+    sign = field->len<0 ? -1 : 1;
+    olen = strlen(out);
+    if (field->which<=PS_BIT && olen>abslen) {
+      // overflow but remember by how much
+      extra += olen-abslen;
+      abslen = olen;
+    } else if (extra && olen<abslen) {
+      // If later fields have slack space, take back overflow
+      olen = abslen-olen;
+      if (olen>extra) olen = extra;
+      abslen -= olen;
+      extra -= olen;
+    }
+    if (abslen>width) abslen = width;
+    len = pad = abslen;
+    pad *= sign;
+    // If last field is left justified, no trailing spaces.
+    if (!field->next && sign<0) {
+      pad = 0;
+      len = width;
+    }
 
     if (TT.tty) width -= draw_trim(out, pad, len);
     else width -= printf("%*.*s", pad, len, out);
@@ -550,11 +599,13 @@ static void show_ps(struct carveup *tb)
 static int get_ps(struct dirtree *new)
 {
   struct {
-    char *name;
-    long long bits;
+    char *name;     // Path under /proc/$PID directory
+    long long bits; // Only fetch extra data if an -o field is displaying it
   } fetch[] = {
+    // sources for carveup->offset[] data
     {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
-    {"exe", _PS_COMMAND}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME}
+    {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
+    {"", _PS_NAME}
   };
   struct carveup *tb = (void *)toybuf;
   long long *slot = tb->slot;
@@ -564,10 +615,13 @@ static int get_ps(struct dirtree *new)
 
   // Recurse one level into /proc children, skip non-numeric entries
   if (!new->parent)
-    return DIRTREE_RECURSE|DIRTREE_SHUTUP|(DIRTREE_SAVE*!TT.show_process);
+    return DIRTREE_RECURSE|DIRTREE_SHUTUP
+      |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
 
   memset(slot, 0, sizeof(tb->slot));
-  if (!(*slot = atol(new->name))) return 0;
+  if (!(tb->slot[SLOT_tid] = *slot = atol(new->name))) return 0;
+  if (TT.threadparent && TT.threadparent->extra)
+    if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
   fd = dirtree_parentfd(new);
 
   len = 2048;
@@ -583,7 +637,8 @@ static int get_ps(struct dirtree *new)
 
   // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
   if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
-  for (j = 1; j<50; j++) if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
+  for (j = 1; j<SLOT_count; j++)
+    if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
 
   // Now we've read the data, move status and name right after slot[] array,
   // and convert low chars to ? for non-tty display while we're at it.
@@ -656,12 +711,26 @@ static int get_ps(struct dirtree *new)
       else s += j;
   }
 
+  // Do we need to read "exe"?
+  if (TT.bits&_PS_BIT) {
+    off_t temp = 6;
+
+    sprintf(buf, "%lld/exe", *slot);
+    if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
+      if (buf[4] == 1) slot[SLOT_bits] = 32;
+      else if (buf[4] == 2) slot[SLOT_bits] = 64;
+    }
+  }
+
+  // Do we need Android scheduling policy?
+  if (TT.bits&_PS_PCY) get_sched_policy(*slot, (void *)&slot[SLOT_pcy]);
+
   // Fetch string data while parentfd still available, appending to buf.
   // (There's well over 3k of toybuf left. We could dynamically malloc, but
   // it'd almost never get used, querying length of a proc file is awkward,
   // fixed buffer is nommu friendly... Wait for somebody to complain. :)
   slot[SLOT_argv0len] = 0;
-  for (j = 0; j<ARRAY_LEN(fetch); j++) { 
+  for (j = 0; j<ARRAY_LEN(fetch); j++) {
     tb->offset[j] = buf-(tb->str);
     if (!(TT.bits&fetch[j].bits)) {
       *buf++ = 0;
@@ -673,10 +742,31 @@ static int get_ps(struct dirtree *new)
     len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
     sprintf(buf, "%lld/%s", *slot, fetch[j].name);
 
-    // For cmdline we readlink instead of read contents
-    if (j==3) {
-      if ((len = readlinkat(fd, buf, buf, len))>0) buf[len] = 0;
-      else *buf = 0;
+    // For exe we readlink instead of read contents
+    if (j==3 || j==5) {
+      struct carveup *ptb = 0;
+      int k;
+
+      // Thread doesn't have exe or argv[0], so use parent's
+      if (TT.threadparent && TT.threadparent->extra)
+        ptb = (void *)TT.threadparent->extra;
+
+      if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
+      else {
+        if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
+        else {
+          if (!ptb || tb->slot[SLOT_argv0len]) ptb = tb;
+          i = ptb->slot[SLOT_argv0len];
+          s = ptb->str+ptb->offset[4];
+          while (-1!=(k = stridx(s, '/')) && k<i) {
+            s += k+1;
+            i -= k+1;
+          }
+        }
+        if (i<len) len = i;
+        memcpy(buf, s, len);
+        buf[len] = 0;
+      }
 
     // If it's not the TTY field, data we want is in a file.
     // Last length saved in slot[] is command line (which has embedded NULs)
@@ -691,23 +781,21 @@ static int get_ps(struct dirtree *new)
         for (i = 0; i<3; i++) {
           sprintf(buf, "%lld/fd/%i", *slot, i);
           if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
-            && st.st_rdev == rdev && 0<(len = readlinkat(fd, buf, buf, len)))
-          {
-            buf[len] = 0;
-            break;
-          }
+            && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
+              break;
         }
 
         // Couldn't find it, try all the tty drivers.
         if (i == 3) {
           FILE *fp = fopen("/proc/tty/drivers", "r");
-          int tty_major = 0, maj = major(rdev), min = minor(rdev);
+          int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
 
           if (fp) {
             while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
               // TODO: we could parse the minor range too.
               if (tty_major == maj) {
-                sprintf(buf+strlen(buf), "%d", min);
+                len = strlen(buf);
+                len += sprintf(buf+len, "%d", min);
                 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
                   break;
               }
@@ -717,11 +805,11 @@ static int get_ps(struct dirtree *new)
           }
 
           // Really couldn't find it, so just show major:minor.
-          if (!tty_major) sprintf(buf, "%d:%d", maj, min);
+          if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
         }
 
         s = buf;
-        if (strstart(&s, "/dev/")) memmove(buf, s, strlen(s)+1);
+        if (strstart(&s, "/dev/")) memmove(buf, s, len -= 5);
       }
 
     // Data we want is in a file.
@@ -732,10 +820,14 @@ static int get_ps(struct dirtree *new)
       if (readfileat(fd, buf, buf, &len) && len>0) {
         int temp = 0;
 
-        if (buf[len-1]=='\n') buf[--len] = 0;
+        // Trim trailing whitespace and NUL bytes
+        while (len)
+          if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
+          else break;
 
         // Turn NUL to space, other low ascii to ? (in non-tty mode)
-        for (i=0; i<len; i++) {
+        // cmdline has a trailing NUL that we don't want to turn to space.
+        for (i=0; i<len-1; i++) {
           char c = buf[i];
 
           if (!c) {
@@ -744,17 +836,18 @@ static int get_ps(struct dirtree *new)
           } else if (!TT.tty && c<' ') c = '?';
           buf[i] = c;
         }
-        len = temp; // position of _first_ NUL
+        // Store end of argv[0] so ARGS and CMDLINE can differ.
+        // We do it for each file string slot but last is cmdline, which sticks.
+        slot[SLOT_argv0len] = temp ? temp : len;  // Position of _first_ NUL
       } else *buf = len = 0;
-      // Store end of argv[0] so NAME and CMDLINE can differ.
-      slot[SLOT_argv0len] = len;
     }
 
-    buf += strlen(buf)+1;
+    // Above calculated/retained len, so we don't need to re-strlen.
+    buf += len+1;
   }
 
   TT.kcount++;
-  if (TT.show_process) {
+  if (TT.show_process && !TT.threadparent) {
     TT.show_process(tb);
 
     return 0;
@@ -768,6 +861,56 @@ static int get_ps(struct dirtree *new)
   return DIRTREE_SAVE;
 }
 
+static int get_threads(struct dirtree *new)
+{
+  struct dirtree *dt;
+  struct carveup *tb;
+  unsigned pid, kcount;
+
+  if (!new->parent) return get_ps(new);
+
+  if (!(pid = atol(new->name))) return 0;
+
+  TT.threadparent = new;
+  if (!get_ps(new)) {
+    TT.threadparent = 0;
+
+    return 0;
+  }
+
+  // Recurse down into tasks, retaining thread groups.
+  // Disable show_process at least until we can calculate tcount
+  kcount = TT.kcount;
+  sprintf(toybuf, "/proc/%u/task", pid);
+  new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP, get_ps);
+  TT.threadparent = 0;
+  kcount = TT.kcount-kcount+1;
+  tb = (void *)new->extra;
+  tb->slot[SLOT_tcount] = kcount;
+
+  // Fill out tid and thread count for each entry in group
+  if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
+    tb = (void *)dt->extra;
+    tb->slot[SLOT_pid] = pid;
+    tb->slot[SLOT_tcount] = kcount;
+  }
+
+  // Save or display
+  if (!TT.show_process) return DIRTREE_SAVE;
+  TT.show_process((void *)new->extra);
+  dt = new->child;
+  new->child = 0;
+  while (dt->child) {
+    new = dt->child->next;
+    TT.show_process((void *)dt->child->extra);
+    free(dt->child);
+    dt->child = new;
+  }
+  free(dt);
+
+  return 0;
+}
+
 static char *parse_ko(void *data, char *type, int length)
 {
   struct strawberry *field;
@@ -861,6 +1004,8 @@ static char *parse_rest(void *data, char *str, int len)
   if (isdigit(*str)) {
     ll[pl->len] = xstrtol(str, &end, 10);
     if (end==(len+str)) num++;
+    // For pkill, -s 0 represents pkill's session id.
+    if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
   }
 
   if (pl==&TT.pp || pl==&TT.ss) {
@@ -949,26 +1094,26 @@ static int ksort(void *aa, void *bb)
   return ret;
 }
 
-static struct carveup **collate(int count, struct dirtree *dt,
-  int (*sort)(void *a, void *b))
+static struct carveup **collate_leaves(struct carveup **tb, struct dirtree *dt) 
 {
-  struct dirtree *temp;
-  struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
-  int i;
-
-  // descend into child list
-  *tbsort = (void *)dt;
-  dt = dt->child;
-  free(*tbsort);
+  while (dt) {
+    struct dirtree *next = dt->next;
 
-  // populate array
-  for (i = 0; i < count; i++) {
-    temp = dt->next;
-    tbsort[i] = (void *)dt->extra;
+    if (dt->extra) *(tb++) = (void *)dt->extra;
+    if (dt->child) tb = collate_leaves(tb, dt->child);
     free(dt);
-    dt = temp;
+    dt = next;
   }
 
+  return tb;
+}
+
+static struct carveup **collate(int count, struct dirtree *dt)
+{
+  struct carveup **tbsort = xmalloc(count*sizeof(struct carveup *));
+
+  collate_leaves(tbsort, dt);
+
   return tbsort;
 } 
 
@@ -1007,7 +1152,7 @@ static void shared_main(void)
 void ps_main(void)
 {
   struct dirtree *dt;
-  char *s;
+  char *not_o;
   int i;
 
   if (toys.optflags&FLAG_w) TT.width = 99999;
@@ -1025,15 +1170,21 @@ void ps_main(void)
   comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
   dlist_terminate(TT.kfields);
 
-  // Parse manual field selection, or default/-f/-l, plus -Z and -O
-  if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
-  if (toys.optflags&FLAG_f) s = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
+  // Figure out which fields to display
+  not_o = "%sTTY,TIME,CMD";
+  if (toys.optflags&FLAG_f)
+    sprintf(not_o = toybuf+128, "USER:8=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD",
+      (toys.optflags&FLAG_T) ? "TCNT" : "C");
   else if (toys.optflags&FLAG_l)
-    s = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
+    not_o = "F,S,UID,%sPPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
   else if (CFG_TOYBOX_ON_ANDROID)
-    s = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
-  else s = "PID,TTY,TIME,CMD";
-  default_ko(s, &TT.fields, "bad -o", TT.ps.o);
+    not_o = "USER,%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
+  sprintf(toybuf, not_o, (toys.optflags & FLAG_T) ? "PID,TID," : "PID,");
+
+  // Init TT.fields. This only uses toybuf if TT.ps.o is NULL
+  if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
+  default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
+
   if (TT.ps.O) {
     if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
     comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
@@ -1046,7 +1197,6 @@ void ps_main(void)
     struct strawberry *ever;
 
     for (ever = TT.fields; ever; ever = ever->next) {
-      if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
       if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
         && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
           ever->which--;
@@ -1059,10 +1209,12 @@ void ps_main(void)
   if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
   if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
   TT.match_process = ps_match_process;
-  dt = dirtree_read("/proc", get_ps);
+  dt = dirtree_read("/proc",
+    ((toys.optflags&FLAG_T) || (TT.bits&(_PS_TID|_PS_TCNT)))
+      ? get_threads : get_ps);
 
   if (toys.optflags&(FLAG_k|FLAG_M)) {
-    struct carveup **tbsort = collate(TT.kcount, dt, ksort);
+    struct carveup **tbsort = collate(TT.kcount, dt);
 
     if (toys.optflags&FLAG_M) {
       for (i = 0; i<TT.kcount; i++) {
@@ -1143,6 +1295,8 @@ static int header_line(int line, int rev)
 {
   if (!line) return 0;
 
+  if (toys.optflags&FLAG_b) rev = 0;
+
   printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
     (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
     rev ? "\033[0m" : "");
@@ -1150,13 +1304,11 @@ static int header_line(int line, int rev)
   return line-1;
 }
 
-// Get current time in miliseconds
-static long long militime(void)
+static long long millitime(void)
 {
   struct timespec ts;
 
   clock_gettime(CLOCK_MONOTONIC, &ts);
-
   return ts.tv_sec*1000+ts.tv_nsec/1000000;
 }
 
@@ -1186,9 +1338,11 @@ static void top_common(
 
     plold = plist+(tock++&1);
     plnew = plist+(tock&1);
-    plnew->whence = militime();
-    dt= dirtree_read("/proc", get_ps);
-    plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
+    plnew->whence = millitime();
+    dt = dirtree_read("/proc",
+      ((toys.optflags&FLAG_H) || (TT.bits&(_PS_TID|_PS_TCNT)))
+        ? get_threads : get_ps);
+    plnew->tb = collate(plnew->count = TT.kcount, dt);
     TT.kcount = 0;
 
     if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
@@ -1214,7 +1368,7 @@ static void top_common(
     while (old.count || new.count) {
       struct carveup *otb = *old.tb, *ntb = *new.tb;
 
-      // If we just have old, discard it.
+      // If we just have old for this process, it exited. Discard it.
       if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
         old.tb++;
         old.count--;
@@ -1237,7 +1391,7 @@ static void top_common(
       new.count--;
     }
 
-    // We will re-fetch no data before its time. - Mork calling Orson Welles
+    // Don't re-fetch data if it's not time yet, just re-display existing data.
     for (;;) {
       char was, is;
 
@@ -1253,17 +1407,18 @@ static void top_common(
         lines = TT.height;
       }
       if (recalc && !(toys.optflags&FLAG_q)) {
+        // Display "top" header.
         if (*toys.which->name == 't') {
           struct strawberry alluc;
           long long ll, up = 0;
           long run[6];
           int j;
 
+          // Count running, sleeping, stopped, zombie processes.
           alluc.which = PS_S;
           memset(run, 0, sizeof(run));
           for (i = 0; i<mix.count; i++)
             run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
-
           sprintf(toybuf,
             "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
             "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
@@ -1329,20 +1484,22 @@ static void top_common(
         }
 
         get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
-        for (i = 0, is = *pos; *pos; pos++) {
+        for (i = 0, is = ' '; *pos; pos++) {
           was = is;
           is = *pos;
-          if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
+          if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
+            pos[-1] = '[';
           if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
         }
         *pos = 0;
         lines = header_line(lines, 1);
       }
-      if (!recalc) printf("\033[%dH\033[J", 1+TT.height-lines);
+      if (!recalc && !(toys.optflags&FLAG_b))
+        printf("\033[%dH\033[J", 1+TT.height-lines);
       recalc = 1;
 
       for (i = 0; i<lines && i+topoff<mix.count; i++) {
-        if (i) xputc('\n');
+        if (!(toys.optflags&FLAG_b) && i) xputc('\n');
         show_ps(mix.tb[i+topoff]);
       }
 
@@ -1351,11 +1508,18 @@ static void top_common(
         break;
       }
 
-      // Get current time in miliseconds
-      now = militime();
+      now = millitime();
       if (timeout<=now) timeout = new.whence+TT.top.d;
       if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
 
+      // In batch mode, we ignore the keyboard.
+      if (toys.optflags&FLAG_b) {
+        msleep(timeout-now);
+        // Make an obvious gap between datasets.
+        xputs("\n\n");
+        continue;
+      }
+
       i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
       if (i==-1 || i==3 || toupper(i)=='Q') {
         done++;
@@ -1399,13 +1563,11 @@ static void top_common(
 
 static void top_setup(char *defo, char *defk)
 {
-  int len;
-
-  TT.time = militime();
   TT.top.d *= 1000;
   if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
   else {
-    xset_terminal(0, 1, 0);
+    TT.time = millitime();
+    set_terminal(0, 1, 0);
     sigatexit(tty_sigreset);
     xsignal(SIGWINCH, generic_signal);
     printf("\033[?25l\033[0m");
@@ -1418,8 +1580,6 @@ static void top_setup(char *defo, char *defk)
 
   default_ko(defo, &TT.fields, "bad -o", TT.top.o);
   dlist_terminate(TT.fields);
-  len = strlen(toybuf);
-  if (toybuf[len-1]!=' ' && len<sizeof(toybuf)-1) strcpy(toybuf+len, " ");
 
   // First (dummy) sort field is overwritten by setsort()
   default_ko("-S", &TT.kfields, 0, 0);
@@ -1432,9 +1592,17 @@ void top_main(void)
 {
   // usage: [-h HEADER] -o OUTPUT -k SORT
 
-  top_setup(
-    "PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,ARGS",
-    "-%CPU,-ETIME,-PID");
+  sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,ARGS",
+    TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,");
+  if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
+  top_setup(toybuf, "-%CPU,-ETIME,-PID");
+  if (TT.top.O) {
+    struct strawberry *fields = TT.fields;
+
+    fields = fields->next->next;
+    comma_args(TT.top.O, &fields, "bad -O", parse_ko);
+  }
+
   top_common(merge_deltas);
 }
 
@@ -1445,7 +1613,7 @@ void top_main(void)
 static int iotop_filter(long long *oslot, long long *nslot, int milis)
 {
   if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
-  else oslot[SLOT_upticks] = ((militime()-TT.time)*TT.ticks)/1000;
+  else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
 
   return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
 }
@@ -1514,7 +1682,10 @@ static void match_pgrep(struct carveup *tb)
     if ((toys.optflags&FLAG_v) ? !!reg : !reg) return;
   }
 
-  // Repurpose a field for -c count
+  // pgrep should return success if there's a match.
+  toys.exitval = 0;
+
+  // Repurpose a field for -c count.
   TT.sortpos++;
   if (toys.optflags&(FLAG_n|FLAG_o)) {
     long long ll = tb->slot[SLOT_starttime];
@@ -1567,6 +1738,9 @@ void pgrep_main(void)
   TT.match_process = pgrep_match_process;
   TT.show_process = (void *)match_pgrep;
 
+  // pgrep should return failure if there are no matches.
+  toys.exitval = 1;
+
   dirtree_read("/proc", get_ps);
   if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
   if (TT.pgrep.snapshot) {
@@ -1582,6 +1756,9 @@ void pgrep_main(void)
 
 void pkill_main(void)
 {
+  char **args = toys.optargs;
+
+  if (!(toys.optflags&FLAG_l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
   if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
   if (toys.optflags & FLAG_V) TT.tty = 1;
   pgrep_main();
index 9b5e666..31268ec 100644 (file)
@@ -6,6 +6,9 @@
  *
  * TODO: lines > 2G could wrap signed int length counters. Not just getline()
  * but N and s///
+ * TODO: make y// handle unicode
+ * TODO: handle error return from emit(), error_msg/exit consistently
+ *       What's the right thing to do for -i when write fails? Skip to next?
 
 USE_SED(NEWTOY(sed, "(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 
@@ -179,13 +182,20 @@ GLOBALS(
   unsigned xx;
 )
 
-struct step {
-  struct step *next, *prev;
+// Linked list of parsed sed commands. Offset fields indicate location where
+// regex or string starts, ala offset+(char *)struct, because we remalloc()
+// these to expand them for multiline inputs, and pointers would have to be
+// individually adjusted.
+
+struct sedcmd {
+  struct sedcmd *next, *prev;
 
   // Begin and end of each match
-  long lmatch[2];
-  int rmatch[2], arg1, arg2, w; // offsets because remalloc()
-  unsigned not, hit, sflags;
+  long lmatch[2]; // line number of match
+  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
   char c; // action
 };
 
@@ -195,9 +205,9 @@ static int emit(char *line, long len, int eol)
   int l, old = line[len];
 
   if (TT.noeol && !writeall(TT.fdout, "\n", 1)) return 1;
+  TT.noeol = !eol;
   if (eol) line[len++] = '\n';
   if (!len) return 0;
-  TT.noeol = len && !eol;
   l = writeall(TT.fdout, line, len);
   if (eol) line[len-1] = old;
   if (l != len) {
@@ -209,40 +219,6 @@ static int emit(char *line, long len, int eol)
   return 0;
 }
 
-// Do regex matching handling embedded NUL bytes in string. Note that
-// neither the pattern nor the match can currently include NUL bytes
-// (even with wildcards) and string must be null terminated.
-static int ghostwheel(regex_t *preg, char *string, long len, int nmatch,
-  regmatch_t pmatch[], int eflags)
-{
-  char *s = string;
-
-  for (;;) {
-    long ll = 0;
-    int rc;
-
-    while (len && !*s) {
-      s++;
-      len--;
-    }
-    while (s[ll] && ll<len) ll++;
-
-    rc = regexec(preg, s, nmatch, pmatch, eflags);
-    if (!rc) {
-      for (rc = 0; rc<nmatch && pmatch[rc].rm_so!=-1; rc++) {
-        pmatch[rc].rm_so += s-string;
-        pmatch[rc].rm_eo += s-string;
-      }
-          
-      return 0;
-    }
-    if (ll==len) return rc;
-
-    s += ll;
-    len -= ll;
-  }
-}
-
 // Extend allocation to include new string, with newline between if newlen<0
 
 static char *extend_string(char **old, char *new, int oldlen, int newlen)
@@ -271,7 +247,7 @@ static void *get_regex(void *trump, int offset)
 }
 
 // Apply pattern to line from input file
-static void walk_pattern(char **pline, long plen)
+static void process_line(char **pline, long plen)
 {
   struct append {
     struct append *next, *prev;
@@ -280,7 +256,7 @@ static void walk_pattern(char **pline, long plen)
   } *append = 0;
   char *line = TT.nextline;
   long len = TT.nextlen;
-  struct step *logrus;
+  struct sedcmd *command;
   int eol = 0, tea = 0;
 
   // Grab next line for deferred processing (EOF detection: we get a NULL
@@ -300,45 +276,45 @@ static void walk_pattern(char **pline, long plen)
 
   // The restart-1 is because we added one to make sure it wasn't NULL,
   // otherwise N as last command would restart script
-  logrus = TT.restart ? ((struct step *)TT.restart)-1 : (void *)TT.pattern;
+  command = TT.restart ? ((struct sedcmd *)TT.restart)-1 : (void *)TT.pattern;
   TT.restart = 0;
 
-  while (logrus) {
-    char *str, c = logrus->c;
+  while (command) {
+    char *str, c = command->c;
 
     // Have we got a line or regex matching range for this rule?
-    if (*logrus->lmatch || *logrus->rmatch) {
+    if (*command->lmatch || *command->rmatch) {
       int miss = 0;
       long lm;
 
       // In a match that might end?
-      if (logrus->hit) {
-        if (!(lm = logrus->lmatch[1])) {
-          if (!logrus->rmatch[1]) logrus->hit = 0;
+      if (command->hit) {
+        if (!(lm = command->lmatch[1])) {
+          if (!command->rmatch[1]) command->hit = 0;
           else {
-            void *rm = get_regex(logrus, logrus->rmatch[1]);
+            void *rm = get_regex(command, command->rmatch[1]);
 
             // regex match end includes matching line, so defer deactivation
-            if (line && !ghostwheel(rm, line, len, 0, 0, 0)) miss = 1;
+            if (line && !regexec0(rm, line, len, 0, 0, 0)) miss = 1;
           }
-        } else if (lm > 0 && lm < TT.count) logrus->hit = 0;
+        } else if (lm > 0 && lm < TT.count) command->hit = 0;
 
       // Start a new match?
       } else {
-        if (!(lm = *logrus->lmatch)) {
-          void *rm = get_regex(logrus, *logrus->rmatch);
+        if (!(lm = *command->lmatch)) {
+          void *rm = get_regex(command, *command->rmatch);
 
-          if (line && !ghostwheel(rm, line, len, 0, 0, 0)) logrus->hit++;
-        } else if (lm == TT.count || (lm == -1 && !pline)) logrus->hit++;
+          if (line && !regexec0(rm, line, len, 0, 0, 0)) command->hit++;
+        } else if (lm == TT.count || (lm == -1 && !pline)) command->hit++;
 
-        if (!logrus->lmatch[1] && !logrus->rmatch[1]) miss = 1;
+        if (!command->lmatch[1] && !command->rmatch[1]) miss = 1;
       } 
 
       // Didn't match?
-      lm = !(logrus->hit ^ logrus->not);
+      lm = !(command->hit ^ command->not);
 
       // Deferred disable from regex end match
-      if (miss || logrus->lmatch[1] == TT.count) logrus->hit = 0;
+      if (miss || command->lmatch[1] == TT.count) command->hit = 0;
 
       if (lm) {
         // Handle skipping curly bracket command group
@@ -346,19 +322,19 @@ static void walk_pattern(char **pline, long plen)
           int curly = 1;
 
           while (curly) {
-            logrus = logrus->next;
-            if (logrus->c == '{') curly++;
-            if (logrus->c == '}') curly--;
+            command = command->next;
+            if (command->c == '{') curly++;
+            if (command->c == '}') curly--;
           }
         }
-        logrus = logrus->next;
+        command = command->next;
         continue;
       }
     }
 
     // A deleted line can still update line match state for later commands
     if (!line) {
-      logrus = logrus->next;
+      command = command->next;
       continue;
     }
 
@@ -366,7 +342,7 @@ static void walk_pattern(char **pline, long plen)
 
     if (c=='a' || c=='r') {
       struct append *a = xzalloc(sizeof(struct append));
-      a->str = logrus->arg1+(char *)logrus;
+      if (command->arg1) a->str = command->arg1+(char *)command;
       a->file = c=='r';
       dlist_add_nomalloc((void *)&append, (void *)a);
     } else if (c=='b' || c=='t' || c=='T') {
@@ -374,16 +350,16 @@ static void walk_pattern(char **pline, long plen)
 
       if (c != 'b') tea = 0;
       if (c=='b' || t^(c=='T')) {
-        if (!logrus->arg1) break;
-        str = logrus->arg1+(char *)logrus;
-        for (logrus = (void *)TT.pattern; logrus; logrus = logrus->next)
-          if (logrus->c == ':' && !strcmp(logrus->arg1+(char *)logrus, str))
+        if (!command->arg1) break;
+        str = command->arg1+(char *)command;
+        for (command = (void *)TT.pattern; command; command = command->next)
+          if (command->c == ':' && !strcmp(command->arg1+(char *)command, str))
             break;
-        if (!logrus) error_exit("no :%s", str);
+        if (!command) error_exit("no :%s", str);
       }
     } else if (c=='c') {
-      str = logrus->arg1+(char *)logrus;
-      if (!logrus->hit) emit(str, strlen(str), 1);
+      str = command->arg1+(char *)command;
+      if (!command->hit) emit(str, strlen(str), 1);
       free(line);
       line = 0;
       continue;
@@ -405,7 +381,7 @@ static void walk_pattern(char **pline, long plen)
         line = 0;
       } else {
         line[len] = 0;
-        logrus = (void *)TT.pattern;
+        command = (void *)TT.pattern;
       }
       continue;
     } else if (c=='g') {
@@ -427,7 +403,7 @@ static void walk_pattern(char **pline, long plen)
       memcpy(TT.remember+TT.rememberlen, line, len);
       TT.remember[TT.rememberlen += len] = 0;
     } else if (c=='i') {
-      str = logrus->arg1+(char *)logrus;
+      str = command->arg1+(char *)command;
       emit(str, strlen(str), 1);
     } else if (c=='l') {
       int i, x, off;
@@ -455,14 +431,14 @@ static void walk_pattern(char **pline, long plen)
       toybuf[off++] = '$';
       emit(toybuf, off, 1);
     } else if (c=='n') {
-      TT.restart = logrus->next+1;
+      TT.restart = command->next+1;
 
       break;
     } else if (c=='N') {
       // Can't just grab next line because we could have multiple N and
       // we need to actually read ahead to get N;$p EOF detection right.
       if (pline) {
-        TT.restart = logrus->next+1;
+        TT.restart = command->next+1;
         extend_string(&line, TT.nextline, len, -TT.nextlen);
         free(TT.nextline);
         TT.nextline = line;
@@ -484,13 +460,13 @@ static void walk_pattern(char **pline, long plen)
 
       break;
     } else if (c=='s') {
-      char *rline = line, *new = logrus->arg2 + (char *)logrus, *swap, *rswap;
+      char *rline = line, *new = command->arg2 + (char *)command, *swap, *rswap;
       regmatch_t *match = (void *)toybuf;
-      regex_t *reg = get_regex(logrus, logrus->arg1);
+      regex_t *reg = get_regex(command, command->arg1);
       int mflags = 0, count = 0, zmatch = 1, rlen = len, mlen, off, newlen;
 
       // Find match in remaining line (up to remaining len)
-      while (!ghostwheel(reg, rline, rlen, 10, match, mflags)) {
+      while (!regexec0(reg, rline, rlen, 10, match, mflags)) {
         mflags = REG_NOTBOL;
 
         // Zero length matches don't count immediately after a previous match
@@ -503,7 +479,7 @@ static void walk_pattern(char **pline, long plen)
         } else zmatch = 0;
 
         // If we're replacing only a specific match, skip if this isn't it
-        off = logrus->sflags>>3;
+        off = command->sflags>>3;
         if (off && off != ++count) {
           rline += match[0].rm_eo;
           rlen -= match[0].rm_eo;
@@ -563,15 +539,15 @@ static void walk_pattern(char **pline, long plen)
         line = swap;
 
         // Stop after first substitution unless we have flag g
-        if (!(logrus->sflags & 2)) break;
+        if (!(command->sflags & 2)) break;
       }
 
       if (mflags) {
         // flag p
-        if (logrus->sflags & 4) emit(line, len, eol);
+        if (command->sflags & 4) emit(line, len, eol);
 
         tea = 1;
-        if (logrus->w) goto writenow;
+        if (command->w) goto writenow;
       }
     } else if (c=='w') {
       int fd, noeol;
@@ -583,14 +559,14 @@ writenow:
       noeol = TT.noeol;
 
       // We save filehandle and newline status before filename
-      name = logrus->w + (char *)logrus;
+      name = command->w + (char *)command;
       memcpy(&TT.fdout, name, 4);
       name += 4;
       TT.noeol = *(name++);
 
       // write, then save/restore context
       if (emit(line, len, eol))
-        perror_exit("w '%s'", logrus->arg1+(char *)logrus);
+        perror_exit("w '%s'", command->arg1+(char *)command);
       *(--name) = TT.noeol;
       TT.noeol = noeol;
       TT.fdout = fd;
@@ -603,11 +579,11 @@ writenow:
       TT.rememberlen = len;
       len = swap;
     } else if (c=='y') {
-      char *from, *to = (char *)logrus;
+      char *from, *to = (char *)command;
       int i, j;
 
-      from = to+logrus->arg1;
-      to += logrus->arg2;
+      from = to+command->arg1;
+      to += command->arg2;
 
       for (i = 0; i < len; i++) {
         j = stridx(from, line[i]);
@@ -618,14 +594,12 @@ writenow:
       emit(toybuf, strlen(toybuf), 1);
     }
 
-    logrus = logrus->next;
+    command = command->next;
   }
 
   if (line && !(toys.optflags & FLAG_n)) emit(line, len, eol);
 
 done:
-  free(line);
-
   if (dlist_terminate(append)) while (append) {
     struct append *a = append->next;
 
@@ -639,34 +613,12 @@ done:
         xsendfile(fd, TT.fdout);
         close(fd);
       }
-    } else emit(append->str, strlen(append->str), 1);
+    } else if (append->str) emit(append->str, strlen(append->str), 1);
+    else emit(line, 0, 0);
     free(append);
     append = a;
   }
-}
-
-// Genericish function, can probably get moved to lib.c
-
-// Iterate over lines in file, calling function. Function can write 0 to
-// the line pointer if they want to keep it, or 1 to terminate processing,
-// otherwise line is freed. Passed file descriptor is closed at the end.
-static void do_lines(int fd, char *name, void (*call)(char **pline, long len))
-{
-  FILE *fp = fd ? xfdopen(fd, "r") : stdin;
-
-  for (;;) {
-    char *line = 0;
-    ssize_t len;
-
-    len = getline(&line, (void *)&len, fp);
-    if (len > 0) {
-      call(&line, len);
-      if (line == (void *)1) break;
-      free(line);
-    } else break;
-  }
-
-  if (fd) fclose(fp);
+  free(line);
 }
 
 // Callback called on each input file
@@ -676,7 +628,7 @@ static void do_sed(int fd, char *name)
   char *tmp;
 
   if (i) {
-    struct step *primal;
+    struct sedcmd *command;
 
     if (!fd && !strcmp(name, "-")) {
       error_msg("-i on stdin");
@@ -684,12 +636,12 @@ static void do_sed(int fd, char *name)
     }
     TT.fdout = copy_tempfile(fd, name, &tmp);
     TT.count = 0;
-    for (primal = (void *)TT.pattern; primal; primal = primal->next)
-      primal->hit = 0;
+    for (command = (void *)TT.pattern; command; command = command->next)
+      command->hit = 0;
   }
-  do_lines(fd, name, walk_pattern);
+  do_lines(fd, process_line);
   if (i) {
-    walk_pattern(0, 0);
+    process_line(0, 0);
     replace_tempfile(-1, TT.fdout, &tmp);
     TT.fdout = 1;
     TT.nextline = 0;
@@ -705,6 +657,7 @@ static char *unescape_delimited_string(char **pstr, char *delim)
 {
   char *to, *from, mode = 0, d;
 
+  // Grab leading delimiter (if necessary), allocate space for new string
   from = *pstr;
   if (!delim || !*delim) {
     if (!(d = *(from++))) return 0;
@@ -718,13 +671,23 @@ static char *unescape_delimited_string(char **pstr, char *delim)
     if (!*from) return 0;
 
     // delimiter in regex character range doesn't count
-    if (!mode && *from == '[') {
-      mode = '[';
-      if (from[1]=='-' || from[1]==']') *(to++) = *(from++);
-    } else if (mode && *from == ']') mode = 0;
+    if (*from == '[') {
+      if (!mode) {
+        mode = ']';
+        if (from[1]=='-' || from[1]==']') *(to++) = *(from++);
+      } else if (mode == ']' && strchr(".=:", from[1])) {
+        *(to++) = *(from++);
+        mode = *from;
+      }
+    } else if (*from == mode) {
+      if (mode == ']') mode = 0;
+      else {
+        *(to++) = *(from++);
+        mode = ']';
+      }
     // Length 1 range (X-X with same X) is "undefined" and makes regcomp err,
     // but the perl build does it, so we need to filter it out.
-    else if (mode && *from == '-' && from[-1] == from[1]) {
+    else if (mode && *from == '-' && from[-1] == from[1]) {
       from+=2;
       continue;
     } else if (*from == '\\') {
@@ -751,38 +714,43 @@ static char *unescape_delimited_string(char **pstr, char *delim)
   return delim;
 }
 
-// Translate primal pattern into walkable form.
-static void jewel_of_judgement(char **pline, long len)
+// Translate pattern strings into command structures. Each command structure
+// is a single allocation (which requires some math and remalloc at times).
+static void parse_pattern(char **pline, long len)
 {
-  struct step *corwin = (void *)TT.pattern;
+  struct sedcmd *command = (void *)TT.pattern;
   char *line, *reg, c, *errstart;
   int i;
 
   line = errstart = pline ? *pline : "";
   if (len && line[len-1]=='\n') line[--len] = 0;
 
-  // Append additional line to pattern argument string?
-  // We temporarily repurpose "hit" to indicate line continuations
-  if (corwin && corwin->prev->hit) {
-    if (!*pline) error_exit("unfinished %c", corwin->prev->c);;
+  // Append this line to previous multiline command? (hit indicates type.)
+  // During parsing "hit" stores data about line continuations, but in
+  // process_line() it means the match range attached to this command
+  // is active, so processing the continuation must zero it again.
+  if (command && command->prev->hit) {
     // Remove half-finished entry from list so remalloc() doesn't confuse it
     TT.pattern = TT.pattern->prev;
-    corwin = dlist_pop(&TT.pattern);
-    c = corwin->c;
-    reg = (char *)corwin;
-    reg += corwin->arg1 + strlen(reg + corwin->arg1);
-
-    // Resume parsing for 'a' or 's' command
-    if (corwin->hit < 256) goto resume_s;
+    command = dlist_pop(&TT.pattern);
+    c = command->c;
+    reg = (char *)command;
+    reg += command->arg1 + strlen(reg + command->arg1);
+
+    // Resume parsing for 'a' or 's' command. (Only two that can do this.)
+    // TODO: using 256 to indicate 'a' means our s/// delimiter can't be
+    // a unicode character.
+    if (command->hit < 256) goto resume_s;
     else goto resume_a;
   }
 
-  // Loop through commands in line
+  // Loop through commands in this line.
 
-  corwin = 0;
+  command = 0;
   for (;;) {
-    if (corwin) dlist_add_nomalloc(&TT.pattern, (void *)corwin);
+    if (command) dlist_add_nomalloc(&TT.pattern, (void *)command);
 
+    // If there's no more data on this line, return.
     for (;;) {
       while (isspace(*line) || *line == ';') line++;
       if (*line == '#') while (*line && *line != '\n') line++;
@@ -790,28 +758,31 @@ static void jewel_of_judgement(char **pline, long len)
     }
     if (!*line) return;
 
+    // We start by writing data into toybuf. Later we'll allocate the
+    // ex
+
     errstart = line;
-    memset(toybuf, 0, sizeof(struct step));
-    corwin = (void *)toybuf;
-    reg = toybuf + sizeof(struct step);
+    memset(toybuf, 0, sizeof(struct sedcmd));
+    command = (void *)toybuf;
+    reg = toybuf + sizeof(struct sedcmd);
 
     // Parse address range (if any)
     for (i = 0; i < 2; i++) {
       if (*line == ',') line++;
       else if (i) break;
 
-      if (isdigit(*line)) corwin->lmatch[i] = strtol(line, &line, 0);
+      if (isdigit(*line)) command->lmatch[i] = strtol(line, &line, 0);
       else if (*line == '$') {
-        corwin->lmatch[i] = -1;
+        command->lmatch[i] = -1;
         line++;
       } else if (*line == '/' || *line == '\\') {
         char *s = line;
 
-        if (!(s = unescape_delimited_string(&line, 0))) goto brand;
-        if (!*s) corwin->rmatch[i] = 0;
+        if (!(s = unescape_delimited_string(&line, 0))) goto error;
+        if (!*s) command->rmatch[i] = 0;
         else {
           xregcomp((void *)reg, s, (toys.optflags & FLAG_r)*REG_EXTENDED);
-          corwin->rmatch[i] = reg-toybuf;
+          command->rmatch[i] = reg-toybuf;
           reg += sizeof(regex_t);
         }
         free(s);
@@ -822,58 +793,59 @@ static void jewel_of_judgement(char **pline, long len)
     if (!*line) break;
 
     while (*line == '!') {
-      corwin->not = 1;
+      command->not = 1;
       line++;
     }
     while (isspace(*line)) line++;
 
-    c = corwin->c = *(line++);
+    c = command->c = *(line++);
     if (strchr("}:", c) && i) break;
     if (strchr("aiqr=", c) && i>1) break;
 
     // Add step to pattern
-    corwin = xmemdup(toybuf, reg-toybuf);
-    reg = (reg-toybuf) + (char *)corwin;
+    command = xmemdup(toybuf, reg-toybuf);
+    reg = (reg-toybuf) + (char *)command;
 
     // Parse arguments by command type
     if (c == '{') TT.nextlen++;
     else if (c == '}') {
       if (!TT.nextlen--) break;
     } else if (c == 's') {
-      char *fiona, delim = 0;
+      char *end, delim = 0;
 
       // s/pattern/replacement/flags
 
-      // line continuations use arg1, so we fill out arg2 first (since the
-      // regex part can't be multiple lines) and swap them back later.
+      // line continuations use arg1 (back at the start of the function),
+      // so let's fill out arg2 first (since the regex part can't be multiple
+      // lines) and swap them back later.
 
       // get pattern (just record, we parse it later)
-      corwin->arg2 = reg - (char *)corwin;
+      command->arg2 = reg - (char *)command;
       if (!(TT.remember = unescape_delimited_string(&line, &delim)))
-        goto brand;
+        goto error;
 
       reg += sizeof(regex_t);
-      corwin->arg1 = reg-(char *)corwin;
-      corwin->hit = delim;
+      command->arg1 = reg-(char *)command;
+      command->hit = delim;
 resume_s:
-      // get replacement - don't replace escapes because \1 and \& need
+      // get replacement - don't replace escapes yet because \1 and \& need
       // processing later, after we replace \\ with \ we can't tell \\1 from \1
-      fiona = line;
-      while (*fiona != corwin->hit) {
-        if (!*fiona) goto brand;
-        if (*fiona++ == '\\') {
-          if (!*fiona || *fiona == '\n') {
-            fiona[-1] = '\n';
+      end = line;
+      while (*end != command->hit) {
+        if (!*end) goto error;
+        if (*end++ == '\\') {
+          if (!*end || *end == '\n') {
+            end[-1] = '\n';
             break;
           }
-          fiona++;
+          end++;
         }
       }
 
-      reg = extend_string((void *)&corwin, line, reg-(char *)corwin,fiona-line);
-      line = fiona;
+      reg = extend_string((void *)&command, line, reg-(char *)command,end-line);
+      line = end;
       // line continuation? (note: '\n' can't be a valid delim).
-      if (*line == corwin->hit) corwin->hit = 0;
+      if (*line == command->hit) command->hit = 0;
       else {
         if (!*line) continue;
         reg--;
@@ -882,9 +854,9 @@ resume_s:
       }
 
       // swap arg1/arg2 so they're back in order arguments occur.
-      i = corwin->arg1;
-      corwin->arg1 = corwin->arg2;
-      corwin->arg2 = i;
+      i = command->arg1;
+      command->arg1 = command->arg2;
+      command->arg2 = i;
 
       // get flags
       for (line++; *line; line++) {
@@ -892,18 +864,18 @@ resume_s:
 
         if (isspace(*line) && *line != '\n') continue;
 
-        if (0 <= (l = stridx("igp", *line))) corwin->sflags |= 1<<l;
-        else if (!(corwin->sflags>>3) && 0<(l = strtol(line, &line, 10))) {
-          corwin->sflags |= l << 3;
+        if (0 <= (l = stridx("igp", *line))) command->sflags |= 1<<l;
+        else if (!(command->sflags>>3) && 0<(l = strtol(line, &line, 10))) {
+          command->sflags |= l << 3;
           line--;
         } else break;
       }
 
       // 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) corwin->arg1 = 0;
-      else xregcomp((void *)(corwin->arg1 + (char *)corwin), TT.remember,
-        ((toys.optflags & FLAG_r)*REG_EXTENDED)|((corwin->sflags&1)*REG_ICASE));
+      if (!*TT.remember) command->arg1 = 0;
+      else xregcomp((void *)(command->arg1 + (char *)command), TT.remember,
+        ((toys.optflags & FLAG_r)*REG_EXTENDED)|((command->sflags&1)*REG_ICASE));
       free(TT.remember);
       TT.remember = 0;
       if (*line == 'w') {
@@ -921,16 +893,16 @@ resume_s:
 
 writenow:
       while (isspace(*line)) line++;
-      if (!*line) goto brand;
+      if (!*line) goto error;
       for (cc = line; *cc; cc++) if (*cc == '\\' && cc[1] == ';') break;
       delim = *cc;
       *cc = 0;
       fd = xcreate(line, O_WRONLY|O_CREAT|O_TRUNC, 0644);
       *cc = delim;
 
-      corwin->w = reg - (char *)corwin;
-      corwin = xrealloc(corwin, corwin->w+(cc-line)+6);
-      reg = corwin->w + (char *)corwin;
+      command->w = reg - (char *)command;
+      command = xrealloc(command, command->w+(cc-line)+6);
+      reg = command->w + (char *)command;
 
       memcpy(reg, &fd, 4);
       reg += 4;
@@ -945,38 +917,44 @@ writenow:
       char *s, delim = 0;
       int len;
 
-      if (!(s = unescape_delimited_string(&line, &delim))) goto brand;
-      corwin->arg1 = reg-(char *)corwin;
+      if (!(s = unescape_delimited_string(&line, &delim))) goto error;
+      command->arg1 = reg-(char *)command;
       len = strlen(s);
-      reg = extend_string((void *)&corwin, s, reg-(char *)corwin, len);
+      reg = extend_string((void *)&command, s, reg-(char *)command, len);
       free(s);
-      corwin->arg2 = reg-(char *)corwin;
-      if (!(s = unescape_delimited_string(&line, &delim))) goto brand;
-      if (len != strlen(s)) goto brand;
-      reg = extend_string((void *)&corwin, s, reg-(char*)corwin, len);
+      command->arg2 = reg-(char *)command;
+      if (!(s = unescape_delimited_string(&line, &delim))) goto error;
+      if (len != strlen(s)) goto error;
+      reg = extend_string((void *)&command, s, reg-(char*)command, len);
       free(s);
     } else if (strchr("abcirtTw:", c)) {
       int end;
 
+      // trim leading spaces
       while (isspace(*line) && *line != '\n') line++;
 
       // Resume logic differs from 's' case because we don't add a newline
       // unless it's after something, so we add it on return instead.
 resume_a:
-      corwin->hit = 0;
+      command->hit = 0;
 
-      // Trim whitespace from "b ;" and ": blah " but only first space in "w x "
-      if (!(end = strcspn(line, strchr("btT:", c) ? "; \t\r\n\v\f" : "\n"))) {
+      // btT: end with space or semicolon, aicrw continue to newline.
+      if (!(end = strcspn(line, strchr(":btT", c) ? "; \t\r\n\v\f" : "\n"))) {
+        // Argument's optional for btT
         if (strchr("btT", c)) continue;
-        else if (!corwin->arg1) break;
+        else if (!command->arg1) break;
       }
 
       // Extend allocation to include new string. We use offsets instead of
       // pointers so realloc() moving stuff doesn't break things. Ok to write
       // \n over NUL terminator because call to extend_string() adds it back.
-      if (!corwin->arg1) corwin->arg1 = reg - (char*)corwin;
-      else if (*(corwin->arg1+(char *)corwin)) *(reg++) = '\n';
-      reg = extend_string((void *)&corwin, line, reg - (char *)corwin, end);
+      if (!command->arg1) command->arg1 = reg - (char*)command;
+      else if (*(command->arg1+(char *)command)) *(reg++) = '\n';
+      else if (!pline) {
+        command->arg1 = 0;
+        continue;
+      }
+      reg = extend_string((void *)&command, line, reg - (char *)command, end);
 
       // Recopy data to remove escape sequences and handle line continuation.
       if (strchr("aci", c)) {
@@ -992,7 +970,7 @@ resume_a:
                 line++;
                 goto resume_a;
               }
-              corwin->hit = 256;
+              command->hit = 256;
               break;
             }
             if (!(reg[-1] = unescape(*line))) reg[-1] = *line;
@@ -1006,14 +984,13 @@ resume_a:
     } else if (!strchr("{dDgGhHlnNpPqx=", c)) break;
   }
 
-brand:
-  // Reminisce about chestnut trees.
+error:
   error_exit("bad pattern '%s'@%ld (%c)", errstart, line-errstart+1L, *line);
 }
 
 void sed_main(void)
 {
-  struct arg_list *dworkin;
+  struct arg_list *al;
   char **args = toys.optargs;
 
   // Lie to autoconf when it asks stupid questions, so configure regexes
@@ -1024,7 +1001,9 @@ void sed_main(void)
     return;
   }
 
-  // Need a pattern. If no unicorns about, fight serpent and take its eye.
+  // Parse pattern into commands.
+
+  // If no -e or -f, first argument is the pattern.
   if (!TT.e && !TT.f) {
     if (!*toys.optargs) error_exit("no pattern");
     (TT.e = xzalloc(sizeof(struct arg_list)))->arg = *(args++);
@@ -1033,21 +1012,19 @@ void sed_main(void)
   // Option parsing infrastructure can't interlace "-e blah -f blah -e blah"
   // so handle all -e, then all -f. (At least the behavior's consistent.)
 
-  for (dworkin = TT.e; dworkin; dworkin = dworkin->next)
-    jewel_of_judgement(&dworkin->arg, strlen(dworkin->arg));
-  for (dworkin = TT.f; dworkin; dworkin = dworkin->next)
-    do_lines(xopen(dworkin->arg, O_RDONLY), dworkin->arg, jewel_of_judgement);
-  jewel_of_judgement(0, 0);
+  for (al = TT.e; al; al = al->next) parse_pattern(&al->arg, strlen(al->arg));
+  for (al = TT.f; al; al = al->next) do_lines(xopenro(al->arg), parse_pattern);
+  parse_pattern(0, 0);
   dlist_terminate(TT.pattern);
   if (TT.nextlen) error_exit("no }");  
 
   TT.fdout = 1;
   TT.remember = xstrdup("");
 
-  // Inflict pattern upon input files
-  loopfiles_rw(args, O_RDONLY, 0, 0, do_sed);
+  // Inflict pattern upon input files. Long version because !O_CLOEXEC
+  loopfiles_rw(args, O_RDONLY|WARN_ONLY, 0, do_sed);
 
-  if (!(toys.optflags & FLAG_i)) walk_pattern(0, 0);
+  if (!(toys.optflags & FLAG_i)) process_line(0, 0);
 
   // todo: need to close fd when done for TOYBOX_FREE?
 }
index d1c6250..22b5ac6 100644 (file)
@@ -3,6 +3,8 @@
  * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/tail.html
+ *
+ * Deviations from posix: -f waits for pipe/fifo on stdin (nonblock?).
 
 USE_TAIL(NEWTOY(tail, "?fc-n-[-cn]", TOYFLAG_USR|TOYFLAG_BIN))
 
@@ -145,10 +147,8 @@ static void do_tail(int fd, char *name)
       perror_msg("bad -f on '%s'", name);
   }
 
-  if (toys.optc > 1) {
-    if (TT.file_no++) xputc('\n');
-    xprintf("==> %s <==\n", name);
-  }
+  if (TT.file_no++) xputc('\n');
+  if (toys.optc > 1) xprintf("==> %s <==\n", name);
 
   // Are we measuring from the end of the file?
 
@@ -231,10 +231,10 @@ void tail_main(void)
     if (arg && *arg == '-' && arg[1]) {
       TT.lines = atolx(*(args++));
       toys.optc--;
+    } else {
+      // if nothing specified, default -n to -10
+      TT.lines = -10;
     }
-
-    // if nothing specified, default -n to -10
-    TT.lines = -10;
   }
 
   // Allocate 2 ints per optarg for -f
@@ -242,8 +242,8 @@ void tail_main(void)
     if ((TT.ffd = inotify_init()) < 0) perror_exit("inotify_init");
     TT.files = xmalloc(toys.optc*8);
   }
-  loopfiles_rw(args, O_RDONLY|(O_CLOEXEC*!(toys.optflags&FLAG_f)),
-    0, 0, do_tail);
+  loopfiles_rw(args, O_RDONLY|WARN_ONLY|(O_CLOEXEC*!(toys.optflags&FLAG_f)),
+    0, do_tail);
 
   if ((toys.optflags & FLAG_f) && TT.file_no) {
     int len, last_fd = TT.files[(TT.file_no-1)*2], i, fd;
index d5591b6..6167c8a 100644 (file)
@@ -49,8 +49,8 @@ void tee_main(void)
 
   // Open output files
   loopfiles_rw(toys.optargs,
-    O_RDWR|O_CREAT|((toys.optflags & FLAG_a)?O_APPEND:O_TRUNC),
-    0666, 0, do_tee_open);
+    O_RDWR|O_CREAT|WARN_ONLY|((toys.optflags & FLAG_a)?O_APPEND:O_TRUNC),
+    0666, do_tee_open);
 
   for (;;) {
     struct fd_list *fdl;
index 052448b..cd7dd53 100644 (file)
@@ -80,10 +80,13 @@ void touch_main(void)
         if (s) break;
         toybuf[1]='y';
       }
+      tm.tm_sec = 0;
       ts->tv_nsec = 0;
       if (s && *s=='.' && sscanf(s, ".%2u%n", &(tm.tm_sec), &len) == 1) {
-        sscanf(s += len, "%lu%n", &ts->tv_nsec, &len);
-        len++;
+        if (sscanf(s += len, "%lu%n", &ts->tv_nsec, &len) == 1) {
+          s--;
+          len++;
+        } else len = 0;
       } else len = 0;
     }
     if (len) {
@@ -94,7 +97,7 @@ void touch_main(void)
 
     errno = 0;
     ts->tv_sec = mktime(&tm);
-    if (!s || *s || errno == EOVERFLOW) perror_exit("bad '%s'", date);
+    if (!s || *s || ts->tv_sec == -1) perror_exit("bad '%s'", date);
   }
   ts[1]=ts[0];
 
index 0fbb178..2543b82 100644 (file)
@@ -4,8 +4,8 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/true.html
 
-USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN))
-USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK))
+USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN|TOYFLAG_NOHELP))
+USE_TRUE(OLDTOY(:, true, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
 
 config TRUE
   bool "true"
index b14cd93..0aedd2c 100644 (file)
@@ -21,6 +21,7 @@ USE_ULIMIT(NEWTOY(ulimit, ">1P#<1SHavutsrRqpnmlifedc[-SH][!apvutsrRqnmlifedc]",
 config ULIMIT
   bool "ulimit"
   default y
+  depends on TOYBOX_PRLIMIT
   help
     usage: ulimit [-P PID] [-SHRacdefilmnpqrstuv] [LIMIT]
 
@@ -106,7 +107,7 @@ void ulimit_main(void)
   if (toys.optc) {
     rlim_t val;
 
-    if (tolower(**toys.optargs == 'i')) val = RLIM_INFINITY;
+    if (tolower(**toys.optargs) == 'u') val = RLIM_INFINITY;
     else val = atolx_range(*toys.optargs, 0, LONG_MAX);
 
     if (toys.optflags&FLAG_H) rr.rlim_max = val;
index fd557ec..238e27e 100644 (file)
@@ -30,7 +30,7 @@ void uudecode_main(void)
   char *line = 0, mode[16],
        *class[] = {"begin%*[ ]%15s%*[ ]%n", "begin-base64%*[ ]%15s%*[ ]%n"};
 
-  if (toys.optc) ifd = xopen(*toys.optargs, O_RDONLY);
+  if (toys.optc) ifd = xopenro(*toys.optargs);
 
   while (!idx) {
     free(line);
index 34ca701..06709d5 100644 (file)
@@ -26,7 +26,7 @@ void uuencode_main(void)
 
   int i, m = toys.optflags & FLAG_m, fd = 0;
 
-  if (toys.optc > 1) fd = xopen(toys.optargs[0], O_RDONLY);
+  if (toys.optc > 1) fd = xopenro(toys.optargs[0]);
 
   base64_init(toybuf);
 
index e7afc81..a8c3e45 100644 (file)
@@ -4,7 +4,7 @@
  *
  * See http://opengroup.org/onlinepubs/9699919799/utilities/wc.html
 
-USE_WC(NEWTOY(wc, USE_TOYBOX_I18N("m")"cwl[!cm]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 
 config WC
   bool "wc"
@@ -28,68 +28,75 @@ config WC
 #include "toys.h"
 
 GLOBALS(
-  unsigned long totals[3];
+  unsigned long totals[4];
 )
 
 static void show_lengths(unsigned long *lengths, char *name)
 {
-  int i, nospace = 1;
-  for (i=0; i<3; i++) {
-    if (!toys.optflags || (toys.optflags&(1<<i))) {
-      xprintf(" %ld"+nospace, lengths[i]);
-      nospace = 0;
+  int i, space = 7, first = 1;
+
+  for (i = 0; i<4; i++) if (toys.optflags == (1<<i)) space = 0;
+  for (i = 0; i<4; i++) {
+    if (toys.optflags&(1<<i)) {
+      printf(" %*ld"+first, space, lengths[i]);
+      first = 0;
     }
     TT.totals[i] += lengths[i];
   }
-  if (*toys.optargs) xprintf(" %s", name);
+  if (*toys.optargs) printf(" %s", name);
   xputc('\n');
 }
 
 static void do_wc(int fd, char *name)
 {
-  int i, len, clen=1, space;
-  unsigned long word=0, lengths[]={0,0,0};
+  int len = 0, clen = 1, space = 0;
+  unsigned long word = 0, lengths[] = {0,0,0,0};
 
+  // Speed up common case: wc -c normalfile is file length.
   if (toys.optflags == FLAG_c) {
     struct stat st;
 
     // On Linux, files in /proc often report their size as 0.
-    if (!fstat(fd, &st) && S_ISREG(st.st_mode) && st.st_size > 0) {
+    if (!fstat(fd, &st) && S_ISREG(st.st_mode) && st.st_size) {
       lengths[2] = st.st_size;
       goto show;
     }
   }
 
   for (;;) {
-    len = read(fd, toybuf, sizeof(toybuf));
-    if (len<0) perror_msg_raw(name);
-    if (len<1) break;
-    if (toys.optflags == FLAG_c) {
-      lengths[2] += len;
-      continue;
-    }
-    for (i=0; i<len; i+=clen) {
-      wchar_t wchar;
-
-      if (CFG_TOYBOX_I18N && (toys.optflags&FLAG_m)) {
-        clen = mbrtowc(&wchar, toybuf+i, len-i, 0);
-        if (clen == -1) {
-          clen = 1;
-          continue;
+    int pos, done = 0, len2 = read(fd, toybuf+len, sizeof(toybuf)-len);
+
+    if (len2<0) perror_msg_raw(name);
+    else len += len2;
+    if (len2<1) done++;
+
+    for (pos = 0; pos<len; pos++) {
+      if (toybuf[pos]=='\n') lengths[0]++;
+      lengths[2]++;
+      if (toys.optflags&FLAG_m) {
+        // If we've consumed next wide char
+        if (--clen<1) {
+          wchar_t wchar;
+
+          // next wide size, don't count invalid, fetch more data if necessary
+          clen = mbrtowc(&wchar, toybuf+pos, len-pos, 0);
+          if (clen == -1) continue;
+          if (clen == -2 && !done) break;
+
+          lengths[3]++;
+          space = iswspace(wchar);
         }
-        if (clen == -2) break;
-        if (clen == 0) clen=1;
-        space = iswspace(wchar);
-      } else space = isspace(toybuf[i]);
+      } else space = isspace(toybuf[pos]);
 
-      if (toybuf[i]==10) lengths[0]++;
       if (space) word=0;
       else {
         if (!word) lengths[1]++;
         word=1;
       }
-      lengths[2]++;
     }
+    if (done) break;
+    if (pos != len) memmove(toybuf, toybuf+pos, len-pos);
+    len -= pos;
   }
 
 show:
@@ -98,7 +105,7 @@ show:
 
 void wc_main(void)
 {
-  toys.optflags |= (toys.optflags&8)>>1;
+  if (!toys.optflags) toys.optflags = FLAG_l|FLAG_w|FLAG_c;
   loopfiles(toys.optargs, do_wc);
   if (toys.optc>1) show_lengths(TT.totals, "total");
 }
index 7c599e0..801dfb8 100644 (file)
@@ -72,6 +72,12 @@ This means the pages can be shared between instances.)</li>
 sight does not really help readability."</a> -
 <a href=http://lkml.iu.edu/hypermail/linux/kernel/1407.1/00299.html>Pavel
 Machek</a></li>
+<li>"Infrastructure in search of a user" is a bad thing, so don't put code
+in lib/ that doesn't already have at least two users. Don't preemptively
+factor stuff out, it's easy enough to rewrite it uin future if it needs
+to change. The "extreme programming" fad called this "You Ain't Gonna
+Need It" (while inexplicably screaming at cans of Mountain Dew, back in the
+90's). Here's <a href=https://lwn.net/Articles/683745/>Linus Torvalds' take</a>.</li>
 </ul>
 
 <hr>
index b1c6d3f..77b0b21 100644 (file)
@@ -55,6 +55,13 @@ to the environment will take precedence.</p>
 I.E. "what to build", and "configure" describes the build and installation
 environment, I.E. "how to build it".)</p>
 
+<p>By default "make install" puts files in /usr/toybox. Adding this to the
+$PATH is up to you. The environment variable $PREFIX can change the
+install location, ala "PREFIX=/usr/local/bin make install".</p>
+
+<p>If you need an unstripped (debug) version of any of these binaries,
+look in generated/unstripped.</p>
+
 <p><h1><a name="running"><a href="#running">Running a command</a></h1></p>
 
 <h2>main</h2>
@@ -444,10 +451,15 @@ as specified by the options field off this command's toy_list entry.  See
 the get_optargs() description in lib/args.c for details.</p>
 </li>
 
-<li><b>char toybuf[4096]</b> - a common scratch space buffer so
-commands don't need to allocate their own.  Any command is free to use this,
-and it should never be directly referenced by functions in lib/ (although
-commands are free to pass toybuf in to a library function as an argument).</li>
+<li><b>char toybuf[4096]</b> - a common scratch space buffer guaranteed
+to start zeroed, so commands don't need to allocate/initialize their own.
+Any command is free to use this, and it should never be directly referenced
+by functions in lib/ (although commands are free to pass toybuf in to a
+library function as an argument).</li>
+
+<li><b>char libbuf[4096]</b> - like toybuf, but for use by common code in
+lib/*.c. Commands should never directly reference libbuf, and library
+could should nnever directly reference toybuf.</li>
 </ul>
 
 <p>The following functions are defined in main.c:</p>
@@ -632,13 +644,28 @@ itoa().</p>
 errors, to eliminate common error checking. This prints an error message
 and the strerror() string for the errno encountered.</p>
 
-<p>You can intercept this exit by assigning a setjmp/longjmp buffer to
+<p>We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
+sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
+instead of exiting, lets xexit() report stdout flush failures to stderr
+and change the exit code to indicate error, lets our toys.exit function
+change happen for signal exit paths and lets us remove the functions
+after we've called them.</p>
+
+<p>You can intercept our exit by assigning a setjmp/longjmp buffer to
 toys.rebound (set it back to zero to restore the default behavior).
 If you do this, cleaning up resource leaks is your problem.</p>
 
 <ul>
 <li><b>void xstrncpy(char *dest, char *src, size_t size)</b></li>
-<li><b>void xexit(void)</b></li>
+<li><p><b><p>void _xexit(void)</b></p>
+<p>Calls siglongjmp(toys.rebound, 1), or else _exit(toys.exitval). This
+lets you ignore errors with the NO_EXIT() macro wrapper, or intercept
+them with WOULD_EXIT().</p>
+<li><b><p>void xexit(void)</b></p>
+<p>Calls toys.xexit functions (if any) and flushes stdout/stderr (reporting
+failure to write to stdout both to stderr and in the exit code), then
+calls _xexit().</p>
+</li>
 <li><b>void *xmalloc(size_t size)</b></li>
 <li><b>void *xzalloc(size_t size)</b></li>
 <li><b>void *xrealloc(void *ptr, size_t size)</b></li>
@@ -716,12 +743,8 @@ away in libc.</p></blockquote>
 <li><p><b>struct passwd *xgetpwuid(uid_t uid)<br />
 struct group *xgetgrgid(gid_t gid)<br />
 struct passwd *xgetpwnam(char *name)</b></p>
-
-<p></p>
 </li>
 
-
-
 <li><b>void xsetuser(struct passwd *pwd)</b></li>
 <li><b>char *xreadlink(char *name)</b></li>
 <li><b>char *xreadfile(char *name, char *buf, off_t len)</b></li>
@@ -733,53 +756,7 @@ struct passwd *xgetpwnam(char *name)</b></p>
 </ul>
 
 <a name="lib_lib"><h3>lib/lib.c</h3>
-<p>Eight gazillion common functions:</p>
-
-<ul>
-<li><b>void verror_msg(char *msg, int err, va_list va)</b></li>
-<li><b>void error_msg(char *msg, ...)</b></li>
-<li><b>void perror_msg(char *msg, ...)</b></li>
-<li><b>void error_exit(char *msg, ...)</b></li>
-<li><b>void perror_exit(char *msg, ...)</b></li>
-<li><b>ssize_t readall(int fd, void *buf, size_t len)</b></li>
-<li><b>ssize_t writeall(int fd, void *buf, size_t len)</b></li>
-<li><b>off_t lskip(int fd, off_t offset)</b></li>
-<li><b>int mkpathat(int atfd, char *dir, mode_t lastmode, int flags)</b></li>
-<li><b>struct string_list **splitpath(char *path, struct string_list **list)</b></li>
-<li><b>struct string_list *find_in_path(char *path, char *filename)</b></li>
-<li><b>long atolx(char *numstr)</b></li>
-<li><b>long atolx_range(char *numstr, long low, long high)</b></li>
-<li><b>int numlen(long l)</b></li>
-<li><b>int stridx(char *haystack, char needle)</b></li>
-<li><b>int strstart(char **a, char *b)</b></li>
-<li><b>off_t fdlength(int fd)</b></li>
-<li><b>char *readfile(char *name, char *ibuf, off_t len)</b></li>
-<li><b>void msleep(long miliseconds)</b></li>
-<li><b>int64_t peek_le(void *ptr, unsigned size)</b></li>
-<li><b>int64_t peek_be(void *ptr, unsigned size)</b></li>
-<li><b>int64_t peek(void *ptr, unsigned size)</b></li>
-<li><b>void poke(void *ptr, uint64_t val, int size)</b></li>
-<li><b>void loopfiles_rw(char **argv, int flags, int permissions, int failok,</b></li>
-<li><b>void loopfiles(char **argv, void (*function)(int fd, char *name))</b></li>
-<li><b>char *get_rawline(int fd, long *plen, char end)</b></li>
-<li><b>char *get_line(int fd)</b></li>
-<li><b>int wfchmodat(int fd, char *name, mode_t mode)</b></li>
-<li><b>static void tempfile_handler(int i)</b></li>
-<li><b>int copy_tempfile(int fdin, char *name, char **tempname)</b></li>
-<li><b>void delete_tempfile(int fdin, int fdout, char **tempname)</b></li>
-<li><b>void replace_tempfile(int fdin, int fdout, char **tempname)</b></li>
-<li><b>void crc_init(unsigned int *crc_table, int little_endian)</b></li>
-<li><b>int terminal_size(unsigned *xx, unsigned *yy)</b></li>
-<li><b>int yesno(char *prompt, int def)</b></li>
-<li><b>void generic_signal(int sig)</b></li>
-<li><b>void sigatexit(void *handler)</b></li>
-<li><b>int sig_to_num(char *pidstr)</b></li>
-<li><b>char *num_to_sig(int sig)</b></li>
-<li><b>mode_t string_to_mode(char *modestr, mode_t mode)</b></li>
-<li><b>void mode_to_string(mode_t mode, char *buf)</b></li>
-<li><b>void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))</b></li>
-<li><b>int human_readable(char *buf, unsigned long long num)</b></li>
-</ul>
+<p>Eight gazillion common functions, see lib/lib.h for the moment:</p>
 
 <h3>lib/portability.h</h3>
 
@@ -1060,6 +1037,15 @@ in the same order they're declared, and that padding won't be inserted between
 consecutive variables of register size.  Thus the first few entries can
 be longs or pointers corresponding to the saved arguments.</p>
 
+<p>The main downside is that numeric arguments ("#" and "-" format)
+are limited to +- 2 billion on 32 bit platforms (the "truncate -s 8G"
+problem), because long is only 64 bits on 64 bit hosts, so the capabilities
+of some tools differ when built in 32 bit vs 64 bit mode. Fixing this
+kind of ugly and even embedded designs are slowly moving to 64 bits,
+so our current plan is to document the problem and wait it out. (If
+"x32 mode" and similar becomes popular enough, we may revisit this
+decision.)</p>
+
 <p>See toys/example/*.c for longer examples of parsing options into the
 GLOBALS block.</p>
 
@@ -1198,16 +1184,32 @@ of functions.</p>
 
 <p>These functions do not call chdir() or rely on PATH_MAX. Instead they
 use openat() and friends, using one filehandle per directory level to
-recurseinto subdirectories. (I.E. they can descend 1000 directories deep
+recurse into subdirectories. (I.E. they can descend 1000 directories deep
 if setrlimit(RLIMIT_NOFILE) allows enough open filehandles, and the default
 in /proc/self/limits is generally 1024.)</p>
 
+<p>There are two main ways to use dirtree: 1) assemble a tree of nodes
+representing a snapshot of directory state and traverse them using the
+->next and ->child pointers, or 2) traverse the tree calling a callback
+function on each entry, and freeing its node afterwards. (You can also
+combine the two, using the callback as a filter to determine which nodes
+to keep.)</p>
+
 <p>The basic dirtree functions are:</p>
 
 <ul>
-<li><p><b>dirtree_read(char *path, int (*callback)(struct dirtree node))</b> -
-recursively read directories, either applying callback() or returning
-a tree of struct dirtree if callback is NULL.</p></li>
+<li><p><b>struct dirtree *dirtree_read(char *path, int (*callback)(struct
+dirtree node))</b> - recursively read files and directories, calling
+callback() on each, and returning a tree of saved nodes (if any).
+If path doesn't exist, returns DIRTREE_ABORTVAL. If callback is NULL,
+returns a single node at that path.</p>
+
+<li><p><b>dirtree_notdotdot(struct dirtree *new)</b> - standard callback
+which discards "." and ".." entries and returns DIRTREE_SAVE|DIRTREE_RECURSE
+for everything else. Used directly, this assembles a snapshot tree of
+the contents of this directory and its subdirectories
+to be processed after dirtree_read() returns (by traversing the
+struct dirtree's ->next and ->child pointers from the returned root node).</p>
 
 <li><p><b>dirtree_path(struct dirtree *node, int *plen)</b> - malloc() a
 string containing the path from the root of this tree to this node. If
@@ -1215,21 +1217,21 @@ plen isn't NULL then *plen is how many extra bytes to malloc at the end
 of string.</p></li>
 
 <li><p><b>dirtree_parentfd(struct dirtree *node)</b> - return fd of
-containing directory, for use with openat() and such.</p></li>
+directory containing this node, for use with openat() and such.</p></li>
 </ul>
 
-<p>The <b>dirtree_read()</b> function takes two arguments, a starting path for
-the root of the tree, and a callback function. The callback takes a
-<b>struct dirtree *</b> (from lib/lib.h) as its argument. If the callback is
-NULL, the traversal uses a default callback (dirtree_notdotdot()) which
-recursively assembles a tree of struct dirtree nodes for all files under
-this directory and subdirectories (filtering out "." and ".." entries),
-after which dirtree_read() returns the pointer to the root node of this
-snapshot tree.</p>
+<p>The <b>dirtree_read()</b> function is the standard way to start
+directory traversal. It takes two arguments: a starting path for
+the root of the tree, and a callback function. The callback() is called
+on each directory entry, its argument is a fully populated
+<b>struct dirtree *</b> (from lib/lib.h) describing the node, and its
+return value tells the dirtree infrastructure what to do next.</p>
 
-<p>Otherwise the callback() is called on each entry in the directory,
-with struct dirtree * as its argument. This includes the initial
-node created by dirtree_read() at the top of the tree.</p>
+<p>(There's also a three argument version,
+<b>dirtree_flagread(char *path, int flags, int (*callback)(struct
+dirtree node))</b>, which lets you apply flags like DIRTREE_SYMFOLLOW and
+DIRTREE_SHUTUP to reading the top node, but this only affects the top node.
+Child nodes use the flags returned by callback().</p>
 
 <p><b>struct dirtree</b></p>
 
@@ -1237,12 +1239,13 @@ node created by dirtree_read() at the top of the tree.</p>
 st</b> entries describing a file, plus a <b>char *symlink</b>
 which is NULL for non-symlinks.</p>
 
-<p>During a callback function, the <b>int data</b> field of directory nodes
-contains a dirfd (for use with the openat() family of functions). This is
-generally used by calling dirtree_parentfd() on the callback's node argument.
-For symlinks, data contains the length of the symlink string. On the second
-callback from DIRTREE_COMEAGAIN (depth-first traversal) data = -1 for
-all nodes (that's how you can tell it's the second callback).</p>
+<p>During a callback function, the <b>int dirfd</b> field of directory nodes
+contains a directory file descriptor (for use with the openat() family of
+functions). This isn't usually used directly, intstead call dirtree_parentfd()
+on the callback's node argument. The <b>char again</a> field is 0 for the
+first callback on a node, and 1 on the second callback (triggered by returning
+DIRTREE_COMEAGAIN on a directory, made after all children have been processed).
+</p>
 
 <p>Users of this code may put anything they like into the <b>long extra</b>
 field. For example, "cp" and "mv" use this to store a dirfd for the destination
@@ -1266,15 +1269,17 @@ return DIRTREE_ABORT from parent callbacks too.)</p></li>
 <li><p><b>DIRTREE_RECURSE</b> - Examine directory contents. Ignored for
 non-directory entries. The remaining flags only take effect when
 recursing into the children of a directory.</p></li>
-<li><p><b>DIRTREE_COMEAGAIN</b> - Call the callback a second time after
-examining all directory contents, allowing depth-first traversal.
-On the second call, dirtree->data = -1.</p></li>
+<li><p><b>DIRTREE_COMEAGAIN</b> - Call the callback on this node a second time
+after examining all directory contents, allowing depth-first traversal.
+On the second call, dirtree->again is nonzero.</p></li>
 <li><p><b>DIRTREE_SYMFOLLOW</b> - follow symlinks when populating children's
 <b>struct stat st</b> (by feeding a nonzero value to the symfollow argument of
 dirtree_add_node()), which means DIRTREE_RECURSE treats symlinks to
 directories as directories. (Avoiding infinite recursion is the callback's
 problem: the non-NULL dirtree->symlink can still distinguish between
-them.)</p></li>
+them. The "find" command follows ->parent up the tree to the root node
+each time, checking to make sure that stat's dev and inode pair don't
+match any ancestors.)</p></li>
 </ul>
 
 <p>Each struct dirtree contains three pointers (next, parent, and child)
@@ -1299,15 +1304,15 @@ single malloc() (even char *symlink points to memory at the end of the node),
 so llist_free() works but its callback must descend into child nodes (freeing
 a tree, not just a linked list), plus whatever the user stored in extra.</p>
 
-<p>The <b>dirtree_read</b>() function is a simple wrapper, calling <b>dirtree_add_node</b>()
+<p>The <b>dirtree_flagread</b>() function is a simple wrapper, calling <b>dirtree_add_node</b>()
 to create a root node relative to the current directory, then calling
-<b>handle_callback</b>() on that node (which recurses as instructed by the callback
-return flags). Some commands (such as chgrp) bypass this wrapper, for example
-to control whether or not to follow symlinks to the root node; symlinks
+<b>dirtree_handle_callback</b>() on that node (which recurses as instructed by the callback
+return flags). The flags argument primarily lets you
+control whether or not to follow symlinks to the root node; symlinks
 listed on the command line are often treated differently than symlinks
-encountered during recursive directory traversal).
+encountered during recursive directory traversal.
 
-<p>The ls command not only bypasses the wrapper, but never returns
+<p>The ls command not only bypasses this wrapper, but never returns
 <b>DIRTREE_RECURSE</b> from the callback, instead calling <b>dirtree_recurse</b>() manually
 from elsewhere in the program. This gives ls -lR manual control
 of traversal order, which is neither depth first nor breadth first but
old mode 100755 (executable)
new mode 100644 (file)
index 050c953..993a7f7
@@ -216,6 +216,22 @@ usage), and avoiding unnecessary work makes code run faster. Smaller code
 also tends to run faster on modern hardware due to CPU cacheing: fitting your
 code into L1 cache is great, and staying in L2 cache is still pretty good.</p>
 
+<p>But a simple implementation is not always the smallest or fastest, and
+balancing simplicity vs the other goals can be difficult. For example, the
+atolx_range() function in lib/lib.c uses the always 64 bit "long long" type,
+which produces larger and slower code on 32 bit platforms and
+often assigned into smaller interger types. Although libc has parallel
+implementations for different data sizes (atoi, atol, atoll) we only
+used the largest, which can cover all cases.</p>
+
+<p>On the other hand, the "tail" command has two codepaths, one for seekable
+files and one for nonseekable files. Although the nonseekable case can handle
+all inputs (and is required when input comes from a pipe or similar, so cannot
+be removed), reading through multiple gigabytes of data to reach the end of
+seekable files was both a common case and hugely penalized by a nonseekable
+approach (half-minute wait vs instant results). This is one example
+where performance did outweigh simplicity of implementation.</p>
+
 <p><a href=http://www.joelonsoftware.com/articles/fog0000000069.html>Joel
 Spolsky argues against throwing code out and starting over</a>, and he has
 good points: an existing debugged codebase contains a huge amount of baked
@@ -243,6 +259,13 @@ the GNU tools were yet another rewrite intended for use in the stillborn
 were written in Plan 9, uclinux, klibc, sash, sbase, s6, and of course
 android toolbox...) but maybe toybox can do a better job. :)</p>
 
+<p>As Antoine de St. Exupery (author of "The Little Prince" and an early
+aircraft designer) said, "Perfection is achieved, not when there
+is nothing left to add, but when there is nothing left to take away."
+And Ken Thompson (creator of Unix) said "One of my most productive
+days was throwing away 1000 lines of code." It's always possible to
+come up with a better way to do it.</p>
+
 <p>P.S.  How could I resist linking to an article about
 <a href=http://blog.outer-court.com/archive/2005-08-24-n14.html>why
 programmers should strive to be lazy and dumb</a>?</p>
@@ -266,12 +289,13 @@ older kernels or other implementations (ala BSD), but we don't police their
 corner cases.</p>
 
 <b><h3>32/64 bit</h3></b>
-<p>Toybox should work on both 32 bit and 64 bit systems.  By the end of 2008
-64 bit hardware will be the new desktop standard, but 32 bit hardware will
-continue to be important in embedded devices for years to come.</p>
+<p>Toybox should work on both 32 bit and 64 bit systems. 64 bit desktop
+hardware went mainstream in 2005 and was essentially ubiquitous
+by the end of the decade, but 32 bit hardware will continue to be important
+in embedded devices for several more years.</p>
 
 <p>Toybox relies on the fact that on any Unix-like platform, pointer and long
-are always the same size (on both 32 and 64 bit).  Pointer and int are _not_
+are always the same size (on both 32 and 64 bit). Pointer and int are _not_
 the same size on 64 bit systems, but pointer and long are.</p>
 
 <p>This is guaranteed by the LP64 memory model, a Unix standard (which Linux
@@ -329,6 +353,28 @@ of it.)</p>
 <p>Locale support isn't currently a goal; that's a presentation layer issue
 (I.E. a GUI problem).</p>
 
+<p><h3>Shared Libraries</h3></p>
+
+<p>Toybox's policy on shared libraries is that they should never be
+required, but can optionally be used to improve performance.</p>
+
+<p>Toybox should provide the command line utilities for
+<a href=roadmap.html#dev_env>self-hosting development evirionments</a>,
+and an easy way to set up "hermetic builds" (I.E. builds which provide
+their own dependencies, isolating the build logic from host command version
+skew with a simple known build environment). In both cases, external
+dependencies defeat the purpose.</p>
+
+<p>This means toybox should provide full functionality without relying
+on any external dependencies (other than libc). But toybox may optionally use
+libraries such as zlib and openssl to improve performance for things like
+deflate and sha1sum, which lets the corresponding built-in implementations
+be simple (and thus slow). But the built-in implementations need to exist and
+work.</p>
+
+<p>(This is why we use an external https wrapper program, because depending on
+openssl or similar to be linked in would change the behavior of toybox.)</p>
+
 <a name="codestyle" />
 <h2>Coding style</h2>
 
diff --git a/www/faq.html b/www/faq.html
new file mode 100755 (executable)
index 0000000..52ea6f5
--- /dev/null
@@ -0,0 +1,42 @@
+<html><head><title>toybox news</title>
+<!--#include file="header.html" -->
+
+<h2>Q: "Why is there toybox? What was wrong with busybox?"</h2>
+
+<p>A: To answer the first part: Toybox dates back to when its maintainer
+<a href=https://lwn.net/Articles/202106/>handed off BusyBox maintainership</a>
+and <a href=http://landley.net/notes-2006.html#28-09-2006>started over from
+scratch</a> on a new codebase after a
+<a href=http://lists.busybox.net/pipermail/busybox/2006-September/058617.html>protracted licensing argument</a> took all the fun out of
+working on BusyBox. Toybox was just a personal project until it got
+<a href=https://lwn.net/Articles/478308/>relicensed years
+later</a> after its author did a lot of thinking
+<a href=http://landley.net/talks/ohio-2013.txt>about licenses</a>
+and about <a href=http://landley.net/notes-2011.html#21-03-2011>the
+transition to smartphones</a>. This led to the
+<a href=http://landley.net/talks/celf-2013.txt>2013</a>
+<a href=https://www.youtube.com/watch?v=SGmtP5Lg_t0>talk</a> laying
+out a strategy to make Android self-hosting, which helped
+<a href=https://code.google.com/p/android/issues/detail?id=76861>bring
+it to Android's attention</a>, and they
+<a href=https://lwn.net/Articles/629362/>merged it</a> into Android M.</p>
+
+<p>To answer the second part: 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. Similarly, Apple froze xcode at the last GPLv2 releases
+(GCC 4.2.1 with binutils 2.17) for over 5 years while it sponsored the
+development of new projects (clang/llvm/lld) to replace them,
+implemented its SMB server from scratch to replace samba,
+<a href=http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/>and so
+on</a>. Toybox itself exists because somebody with in a legacy position
+just wouldn't shut up about GPLv3, otherwise its maintainer would probably
+still happily be maintaining BusyBox. (For more on how said maintainer wound
+up working on busybox in the first place,
+<a href=http://landley.net/aboriginal/history.html>see here</a>.)</p>
+
+<!--#include file="footer.html" -->
index ece55fc..4cdafb0 100755 (executable)
@@ -96,7 +96,7 @@ source.)</p>
 alias bg cd command fc fg getopts hash jobs kill read type ulimit umask
 unalias wait exit if while for case export set unset trap exec function source
 </span>
-<b></blockquote>
+</b></blockquote>
 
 <p>A few other commands are judgement calls, providing command-line
 internationalization support (iconv locale localedef), System V inter-process
@@ -193,9 +193,9 @@ su sync tar umount useradd userdel usermod zcat
 <a name="dev_env">
 <h2><a href="#dev_env">Use case: provide a self-hosting development environment</a></h2>
 
-<p>The following commands are enough to build the Aboriginal Linux development
-environment, boot it to a shell prompt, and build Linux From Scratch 6.8 under
-it. (Aboriginal Linux currently uses BusyBox for this, thus provides a
+<p>The following commands are enough to build the <a href=http://landley.net/aboriginal/about.html>Aboriginal Linux</a> development
+environment, boot it to a shell prompt, and build <a href=http://www.linuxfromscratch.org/lfs/view/6.8/>Linux From Scratch 6.8</a> under
+it. (Aboriginal Linux <a href=http://landley.net/aboriginal/history.html>currently uses</a> BusyBox for this, thus providing a
 drop-in test environment for toybox. We install both implementations side
 by side, redirecting the symlinks a command at a time until the older
 package is no longer used, and can be removed.)</p>
@@ -269,11 +269,12 @@ system/core/toolbox/Android.mk</a> the toolbox directory builds the
 following commands:</p>
 
 <blockquote><b>
-dd getevent iftop ioctl log
-nandread newfs_msdos ps prlimit
-sendevent start stop top
+dd getevent newfs_msdos
 </b></blockquote>
 
+<p>The toolbox makefile also builds the BSD grep right now, because toybox
+grep is missing <code>--color</code>.</p>
+
 <h3>Other Android core commands</h3>
 
 <p>Other than the toolbox directory, the currently interesting
@@ -297,10 +298,9 @@ implementing the full commands (fdisk, init, and sudo) come first.</p>
 <p>For reference, combining everything listed above, we get:</p>
 
 <blockquote><b>
-dd getevent iftop init ioctl
-log logcat logwrapper nandread
-newfs_msdos ps prlimit reboot run-as
-sendevent start stop top
+dd getevent init
+logcat logwrapper
+newfs_msdos reboot run-as
 </b></blockquote>
 
 <p>We may eventually implement all of that, but for toybox 1.0 we need to
@@ -312,9 +312,7 @@ for later).</p>
 <p>This means toybox should implement (or finish implementing):</p>
 <blockquote><b>
 <span id=toolbox>
-dd getevent iftop ioctl log logcat logwrapper
-nandread newfs_msdos ps prlimit sendevent
-start stop top
+dd getevent logcat logwrapper newfs_msdos
 </span>
 </b></blockquote>
 
@@ -322,7 +320,13 @@ start stop top
 of "pending". These should be a priority for cleanup:</p>
 
 <blockquote><b>
-dd expr lsof more netstat route tar tr traceroute
+dd expr lsof more tar tr traceroute
+</b></blockquote>
+
+<p>Android wishlist:</p>
+
+<blockquote><b>
+mtools genvfatfs mke2fs gene2fs
 </b></blockquote>
 
 <hr />