aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Kondik <steve@cyngn.com>2016-05-22 17:01:32 -0700
committerSteve Kondik <steve@cyngn.com>2016-05-22 17:09:49 -0700
commitbaba9cf1ed821ae3e2a0b1e1b0748f0e332850c8 (patch)
tree00f746b420d1ac5bc7a8b95c92de44cd7f93a17d
parent17dcbb7493b46334f3df6adf3a4313b18cc2000b (diff)
parent498e61d5a4361d4a225947386ac8150292dcc466 (diff)
downloadandroid_external_toybox-baba9cf1ed821ae3e2a0b1e1b0748f0e332850c8.tar.gz
android_external_toybox-baba9cf1ed821ae3e2a0b1e1b0748f0e332850c8.tar.bz2
android_external_toybox-baba9cf1ed821ae3e2a0b1e1b0748f0e332850c8.zip
Merge branch 'master' of https://android.googlesource.com/platform/external/toybox into cm-13.0
Change-Id: I231131679eb5a94753c472e8ea6e26583d46b5f6
-rw-r--r--.config2
-rw-r--r--Android.mk2
-rw-r--r--Makefile3
-rw-r--r--README57
-rw-r--r--generated/config.h4
-rw-r--r--generated/flags.h58
-rw-r--r--generated/globals.h9
-rw-r--r--generated/help.h12
-rw-r--r--generated/newtoys.h6
-rw-r--r--generated/tags.h160
-rw-r--r--lib/dirtree.c12
-rw-r--r--lib/interestingtimes.c4
-rw-r--r--lib/lib.c13
-rw-r--r--lib/lib.h10
-rw-r--r--lib/portability.h16
-rw-r--r--lib/xwrap.c4
-rw-r--r--main.c9
-rwxr-xr-xscripts/genconfig.sh3
-rw-r--r--scripts/runtest.sh4
-rwxr-xr-xtests/bzcat.test30
-rwxr-xr-xtests/diff.test30
-rwxr-xr-xtests/find.test8
-rwxr-xr-xtests/more.test14
-rwxr-xr-xtests/mv.test138
-rwxr-xr-xtests/sed.test1
-rwxr-xr-xtests/sh.test55
-rwxr-xr-xtests/tail.test1
-rwxr-xr-xtests/top.test7
-rwxr-xr-xtests/touch.test12
-rw-r--r--toys/android/getprop.c5
-rw-r--r--toys/lsb/hostname.c30
-rw-r--r--toys/other/bzcat.c8
-rw-r--r--toys/other/rev.c6
-rw-r--r--toys/pending/diff.c6
-rw-r--r--toys/pending/modprobe.c2
-rw-r--r--toys/pending/more.c7
-rw-r--r--toys/pending/netstat.c2
-rw-r--r--toys/pending/sh.c212
-rw-r--r--toys/posix/cp.c17
-rw-r--r--toys/posix/find.c12
-rw-r--r--toys/posix/ls.c4
-rw-r--r--toys/posix/ps.c198
-rw-r--r--toys/posix/sed.c307
-rw-r--r--toys/posix/tail.c6
-rw-r--r--toys/posix/touch.c7
-rw-r--r--www/cleanup.html6
-rwxr-xr-xwww/roadmap.html19
47 files changed, 931 insertions, 607 deletions
diff --git a/.config b/.config
index d4967c1b..b83ec1f4 100644
--- a/.config
+++ b/.config
@@ -333,7 +333,7 @@ CONFIG_TOYBOX_HELP=y
CONFIG_TOYBOX_HELP_DASHDASH=y
CONFIG_TOYBOX_I18N=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
diff --git a/Android.mk b/Android.mk
index adbf0d75..31449134 100644
--- a/Android.mk
+++ b/Android.mk
@@ -315,6 +315,6 @@ LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_STEM := toybox
-LOCAL_STATIC_LIBRARIES := libc libtoybox libcutils libselinux libmincrypt
+LOCAL_STATIC_LIBRARIES := libc libtoybox libcutils libselinux libmincrypt liblog
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
diff --git a/Makefile b/Makefile
index c7237405..711d6247 100644
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,8 @@ tests:
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'
diff --git a/README b/README
index 5fc9ec0b..5823309c 100644
--- 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?)
diff --git a/generated/config.h b/generated/config.h
index 2af18fc9..ec72aa08 100644
--- a/generated/config.h
+++ b/generated/config.h
@@ -594,8 +594,8 @@
#define USE_TOYBOX_I18N(...) __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
diff --git a/generated/flags.h b/generated/flags.h
index 52213790..fb0e4f31 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -1004,12 +1004,14 @@
#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]
@@ -1920,9 +1922,9 @@
#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] k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*Tu*U*g*G*wZ[!ol][+Ae]
#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]"
#ifdef CLEANUP_ps
#undef CLEANUP_ps
#undef FOR_ps
@@ -1932,6 +1934,7 @@
#undef FLAG_g
#undef FLAG_U
#undef FLAG_u
+#undef FLAG_T
#undef FLAG_t
#undef FLAG_s
#undef FLAG_pid
@@ -2653,9 +2656,9 @@
#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 >0mHk*o*p*u*s#<1=9d#=3<1n#<1bq >0mHk*o*p*u*s#<1=9d#=3<1n#<1bq
#undef OPTSTR_top
-#define OPTSTR_top ">0mk*o*p*u*s#<1=9d#=3<1n#<1bq"
+#define OPTSTR_top ">0mHk*o*p*u*s#<1=9d#=3<1n#<1bq"
#ifdef CLEANUP_top
#undef CLEANUP_top
#undef FOR_top
@@ -2668,6 +2671,7 @@
#undef FLAG_p
#undef FLAG_o
#undef FLAG_k
+#undef FLAG_H
#undef FLAG_m
#endif
@@ -3912,6 +3916,8 @@
#ifndef TT
#define TT this.hostname
#endif
+#define FLAG_F (1<<0)
+#define FLAG_b (1<<1)
#endif
#ifdef FOR_hwclock
@@ -4688,24 +4694,25 @@
#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
@@ -5310,7 +5317,8 @@
#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_m (1<<10)
#endif
#ifdef FOR_touch
diff --git a/generated/globals.h b/generated/globals.h
index 1884aa2a..bfeccaae 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -39,6 +39,12 @@ struct dmesg_data {
long size;
};
+// toys/lsb/hostname.c
+
+struct hostname_data {
+ char *fname;
+};
+
// toys/lsb/killall.c
struct killall_data {
@@ -732,6 +738,8 @@ struct route_data {
struct sh_data {
char *command;
+
+ long lineno;
};
// toys/pending/sulogin.c
@@ -1317,6 +1325,7 @@ extern union global_union {
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;
diff --git a/generated/help.h b/generated/help.h
index 426cbddb..bf20e956 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -80,7 +80,7 @@
#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"
@@ -282,10 +282,10 @@
#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"
@@ -450,13 +450,13 @@
#define HELP_pgrep "usage: pgrep [-Lcfnovx] [-G GID,] [-g PGRP,] [-P PPID,] [-s SID,] [-t TERM,] [-U UID,] [-u EUID,] [-d DELIM] [-L SIGNAL] [PATTERN]\n\nSearch for process(es). PATTERN is an extended regular expression checked\nagainst command names.\n-G Match real Group ID(s)\n-L Send SIGNAL instead of printing name\n-P Match Parent Process ID(s)\n-U Match real User ID(s)\n-c Show only count of matches\n-d Use DELIM instead of newline\n-f Check full command line for PATTERN\n-g Match Process Group(s) (0 is current user)\n-l Show command name\n-n Newest match only\n-o Oldest match only\n-s Match Session ID(s) (0 for current)\n-t Match Terminal(s)\n-u Match effective User ID(s)\n-v Negate the match\n-x Match whole command (not substring)\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: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]\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_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-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,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 BIT Is this process 32 or 64 bits\n CMD COMM, or 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 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 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"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index de8ba7e7..3ac584a2 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -92,7 +92,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))
@@ -173,7 +173,7 @@ 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]", 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))
@@ -230,7 +230,7 @@ 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" "Hk*o*p*u*s#<1=9d#=3<1n#<1bq", 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))
diff --git a/generated/tags.h b/generated/tags.h
index e1c957b8..69d3d641 100644
--- a/generated/tags.h
+++ b/generated/tags.h
@@ -40,82 +40,90 @@
#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_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_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_COMM 19
+#define _PS_COMM (1<<19)
+#define PS_TTY 20
+#define _PS_TTY (1<<20)
+#define PS_WCHAN 21
+#define _PS_WCHAN (1<<21)
+#define PS_LABEL 22
+#define _PS_LABEL (1<<22)
+#define PS_COMMAND 23
+#define _PS_COMMAND (1<<23)
+#define PS_CMDLINE 24
+#define _PS_CMDLINE (1<<24)
+#define PS_ARGS 25
+#define _PS_ARGS (1<<25)
+#define PS_NAME 26
+#define _PS_NAME (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
diff --git a/lib/dirtree.c b/lib/dirtree.c
index cc1ab90c..8f235ed4 100644
--- a/lib/dirtree.c
+++ b/lib/dirtree.c
@@ -110,12 +110,9 @@ struct dirtree *dirtree_handle_callback(struct dirtree *new,
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)) {
@@ -130,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);
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c
index c4ea2c27..62670cbb 100644
--- a/lib/interestingtimes.c
+++ b/lib/interestingtimes.c
@@ -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);
}
diff --git a/lib/lib.c b/lib/lib.c
index 16aebc40..0df45bf9 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -919,6 +919,17 @@ void mode_to_string(mode_t mode, char *buf)
*buf = c;
}
+// 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;
+}
+
// Execute a callback for each PID that matches a process name from a list.
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
{
@@ -937,7 +948,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;
}
diff --git a/lib/lib.h b/lib/lib.h
index c134535d..798f1829 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -84,7 +84,7 @@ char *dirtree_path(struct dirtree *node, int *plen);
int dirtree_notdotdot(struct dirtree *catch);
int dirtree_parentfd(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));
@@ -286,14 +286,8 @@ 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);
-static inline char *basename_r(char *name)
-{
- char *s = strrchr(name, '/');
-
- if (s) return s+1;
- return name;
-}
+char *getbasename(char *name);
void names_to_pid(char **names, int (*callback)(pid_t pid, char *name));
pid_t xvforkwrap(pid_t pid);
diff --git a/lib/portability.h b/lib/portability.h
index 3e701bbd..1c2052b2 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -159,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__
@@ -283,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
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 0b1ab8e9..36a601c5 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -47,7 +47,9 @@ void xexit(void)
// Call toys.xexit functions in reverse order added.
while (toys.xexit) {
// This is typecasting xexit->arg to a function pointer,then calling it.
- ((void (*)(void))(toys.xexit->arg))();
+ // 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));
}
diff --git a/main.c b/main.c
index cf82872d..eeae2f39 100644
--- a/main.c
+++ b/main.c
@@ -137,8 +137,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 +205,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()
diff --git a/scripts/genconfig.sh b/scripts/genconfig.sh
index e9bb5141..e4c2aad7 100755
--- a/scripts/genconfig.sh
+++ b/scripts/genconfig.sh
@@ -148,8 +148,7 @@ do
WORKING="$WORKING $NAME"
done &&
echo -e "clean::\n\trm -f $WORKING $PENDING" &&
-echo -e "list:\n\t@echo $(echo $WORKING $PENDING | tr ' ' '\n' | sort | xargs)" &&
-echo -e "list_working:\n\t@echo $(echo $WORKING | tr ' ' '\n' | sort | xargs)" &&
+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
diff --git a/scripts/runtest.sh b/scripts/runtest.sh
index 0c933c2c..875ce572 100644
--- a/scripts/runtest.sh
+++ b/scripts/runtest.sh
@@ -83,7 +83,7 @@ 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
@@ -98,7 +98,7 @@ testing()
if [ -n "$VERBOSE" ]
then
[ ! -z "$4" ] && echo "echo -ne \"$4\" > input"
- echo "echo -ne '$5' | $2"
+ echo "echo -ne '$5' |$EVAL $2"
diff -au expected actual
[ "$VERBOSE" == fail ] && exit 1
fi
diff --git a/tests/bzcat.test b/tests/bzcat.test
index f57a2f88..0975f3d6 100755
--- a/tests/bzcat.test
+++ b/tests/bzcat.test
@@ -1,30 +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"
+testing "2 known files" \
+ 'bzcat "$FILES/blkid/"{minix,ntfs}.bz2 | sha1sum | '"awk '{print \$1}'" \
+ 'c0b7469c9660d6056a988ef8a7fe73925efc9266\n' '' ''
+
testing "overflow" \
'bzcat "$FILES/bzcat/overflow.bz2" >/dev/null 2>/dev/null ;
- [ $? -eq 1 ] && echo good' "good\n" "" ""
+ [ $? -ne 0 ] && echo good' "good\n" "" ""
-echo "hello" > file
-tar -cjf file.tar.bz2 file
-# Get system bzcat
-bzcatExe=`which bzcat`
-$bzcatExe file.tar.bz2 > bzcatOut
-testing "- 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 "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 "- 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 "badcrc" \
+ 'bzcat "$FILES/bzcat/badcrc.bz2" > /dev/null 2>/dev/null ;
+ [ $? -ne 0 ] && echo good' "good\n" "" ""
diff --git a/tests/diff.test b/tests/diff.test
new file mode 100755
index 00000000..ca0b682b
--- /dev/null
+++ b/tests/diff.test
@@ -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" "" ""
diff --git a/tests/find.test b/tests/find.test
index 580d1959..25c24400 100755
--- a/tests/find.test
+++ b/tests/find.test
@@ -87,7 +87,11 @@ testing "-iname FILE" \
testing "-name (no arguments)" \
- "find dir -name 2>&1" "find: '-name' needs 1 arg\n" "" ""
+ "find dir -name 2>&1 | grep -o '[-]name'" "-name\n" "" ""
testing "-iname (no arguments)" \
- "find dir -iname 2>&1" "find: '-iname' needs 1 arg\n" "" ""
+ "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/more.test b/tests/more.test
new file mode 100755
index 00000000..4dcf6d85
--- /dev/null
+++ b/tests/more.test
@@ -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
diff --git a/tests/mv.test b/tests/mv.test
index ab2ca5ed..036fd48e 100755
--- a/tests/mv.test
+++ b/tests/mv.test
@@ -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>
@@ -8,98 +13,145 @@
#testing "name" "command" "result" "infile" "stdin"
touch file
-testing "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "-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 "-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 "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*
diff --git a/tests/sed.test b/tests/sed.test
index 7a4a8cda..822cfa37 100755
--- a/tests/sed.test
+++ b/tests/sed.test
@@ -127,6 +127,7 @@ testing "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"
diff --git a/tests/sh.test b/tests/sh.test
new file mode 100755
index 00000000..4f1ecb2c
--- /dev/null
+++ b/tests/sh.test
@@ -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' '' ''
diff --git a/tests/tail.test b/tests/tail.test
index c1c44c6a..81dc871f 100755
--- a/tests/tail.test
+++ b/tests/tail.test
@@ -20,6 +20,7 @@ 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 "-c+ out of bonds" "tail -c +999 file1" "" "" ""
+testing "-N" "tail -1 file1" "eleven\n" "" ""
rm file1
testing "stdin no trailing newline" "tail -n 1 - " "c" "" "a\nb\nc"
diff --git a/tests/top.test b/tests/top.test
new file mode 100755
index 00000000..11eea33b
--- /dev/null
+++ b/tests/top.test
@@ -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" "" ""
diff --git a/tests/touch.test b/tests/touch.test
index d386156d..d193d7cc 100755
--- a/tests/touch.test
+++ b/tests/touch.test
@@ -11,6 +11,18 @@ testing "-c" "touch -c walrus && [ -e walrus ] && echo yes" "yes\n" "" ""
testing "-c missing" "touch -c warrus && [ ! -e warrus ] && echo yes" \
"yes\n" "" ""
+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" "" ""
diff --git a/toys/android/getprop.c b/toys/android/getprop.c
index efb1e447..9675d9f1 100644
--- a/toys/android/getprop.c
+++ b/toys/android/getprop.c
@@ -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");
diff --git a/toys/lsb/hostname.c b/toys/lsb/hostname.c
index 23467fb3..f30d1fbd 100644
--- a/toys/lsb/hostname.c
+++ b/toys/lsb/hostname.c
@@ -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);
}
}
diff --git a/toys/other/bzcat.c b/toys/other/bzcat.c
index fdad9a01..ff598491 100644
--- a/toys/other/bzcat.c
+++ b/toys/other/bzcat.c
@@ -455,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++) {
@@ -651,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);
diff --git a/toys/other/rev.c b/toys/other/rev.c
index 4cf7214f..15066310 100644
--- a/toys/other/rev.c
+++ b/toys/other/rev.c
@@ -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/pending/diff.c b/toys/pending/diff.c
index da6c13a0..53bdbce3 100644
--- a/toys/pending/diff.c
+++ b/toys/pending/diff.c
@@ -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,7 +797,7 @@ 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));
+ 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);
diff --git a/toys/pending/modprobe.c b/toys/pending/modprobe.c
index 6813dec3..7a35c186 100644
--- a/toys/pending/modprobe.c
+++ b/toys/pending/modprobe.c
@@ -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];
diff --git a/toys/pending/more.c b/toys/pending/more.c
index 55bed363..7923ee4e 100644
--- a/toys/pending/more.c
+++ b/toys/pending/more.c
@@ -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);
@@ -63,7 +68,7 @@ static int prompt(FILE *cin, const char* fmt, ...)
static void do_cat_operation(int fd, char *name)
{
if (toys.optc > 1) show_file_header(name);
- xsendfile(0, 1);
+ xsendfile(fd, 1);
}
void more_main()
diff --git a/toys/pending/netstat.c b/toys/pending/netstat.c
index d6acd7a3..02ab4fc2 100644
--- a/toys/pending/netstat.c
+++ b/toys/pending/netstat.c
@@ -440,7 +440,7 @@ static void scan_pid(int pid)
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"
+ pid, getbasename(line)); // "584/netstat"
free(line);
fd_dir = xmprintf("/proc/%d/fd", pid);
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index e221960a..76b4e13b 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -19,7 +19,23 @@
*
* 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);
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 7520392f..d8ae783f 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -389,20 +389,23 @@ 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);
}
diff --git a/toys/posix/find.c b/toys/posix/find.c
index 303aada2..3b27225d 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -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];
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
index 1d4777b0..94359d7a 100644
--- a/toys/posix/ls.c
+++ b/toys/posix/ls.c
@@ -337,8 +337,8 @@ static void listfiles(int dirfd, struct dirtree *indir)
} 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));
+ dirtree_recurse(indir, filter, dup(dirfd),
+ DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
}
// Copy linked list to array and sort it. Directories go in array because
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index 26b4a4ea..bfd6807a 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -43,9 +43,9 @@
* 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]", 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" "Hk*o*p*u*s#<1=9d#=3<1n#<1bq", 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))
@@ -54,7 +54,7 @@ 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)
@@ -85,20 +86,22 @@ config PS
-f Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,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:
ADDR Instruction pointer ARGS Command line (argv[] -path)
- CMD COMM without -f, ARGS with -f CMDLINE Command line (argv[])
+ BIT Is this process 32 or 64 bits
+ CMD COMM, or ARGS with -f CMDLINE Command line (argv[])
COMM Original command name COMMAND Original command path
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
+ 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,6 +116,7 @@ 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
@@ -122,10 +126,11 @@ config TOP
bool "top"
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)
-s Sort by field number (1-X, default 9)
@@ -151,7 +156,7 @@ config TOP_COMMON
bool
default y
help
- usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
+ usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
-b Batch mode (no tty)
-d Delay SECONDS between each cycle (default 3)
@@ -300,15 +305,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[5]; // 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.
@@ -324,6 +333,7 @@ struct typography {
{"VSZ", 6, 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},
@@ -351,7 +361,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
@@ -404,7 +414,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,7 +423,7 @@ 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
@@ -425,7 +435,8 @@ static char *string_field(struct carveup *tb, struct strawberry *field)
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_COMMAND && (!*out || *slot != slot[SLOT_tid]))
+ sprintf(out = buf, "[%s]", tb->str);
// user/group
} else if (which <= PS_RGROUP) {
@@ -512,7 +523,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;
}
@@ -553,6 +565,7 @@ static int get_ps(struct dirtree *new)
char *name;
long long bits;
} 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}
};
@@ -567,7 +580,7 @@ static int get_ps(struct dirtree *new)
return DIRTREE_RECURSE|DIRTREE_SHUTUP|(DIRTREE_SAVE*!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;
fd = dirtree_parentfd(new);
len = 2048;
@@ -656,6 +669,20 @@ 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,
@@ -769,6 +796,47 @@ static int get_ps(struct dirtree *new)
return DIRTREE_SAVE;
}
+static int get_threads(struct dirtree *new)
+{
+ struct dirtree *threads, *dt;
+ unsigned pid, kcount;
+ void (*show_process)(void *tb) = TT.show_process;
+
+ if (!new->parent) return get_ps(new);
+
+ if (!(pid = atol(new->name))) return 0;
+
+ // Recurse down into tasks, retaining thread groups.
+ TT.show_process = 0;
+ sprintf(toybuf, "/proc/%u/task", pid);
+ kcount = TT.kcount;
+ threads = dirtree_read(toybuf, get_ps);
+ if (!threads) return 0;
+
+ // Fill out tid and thread count for each entry in group
+ for (dt = threads->child; dt; dt = dt->next) {
+ struct carveup *tb = (void *)dt->extra;
+
+ tb->slot[SLOT_tid] = tb->slot[SLOT_pid];
+ tb->slot[SLOT_pid] = pid;
+ tb->slot[SLOT_tcount] = TT.kcount - kcount;
+ }
+
+ // Save or display
+ if (!(TT.show_process = show_process)) {
+ new->child = threads;
+
+ return DIRTREE_SAVE;
+ } while (threads->child) {
+ dt = threads->child->next;
+ show_process((void *)threads->child->extra);
+ free(threads->child);
+ threads->child = dt;
+ }
+
+ return 0;
+}
+
static char *parse_ko(void *data, char *type, int length)
{
struct strawberry *field;
@@ -950,26 +1018,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;
+ while (dt) {
+ struct dirtree *next = dt->next;
- // descend into child list
- *tbsort = (void *)dt;
- dt = dt->child;
- free(*tbsort);
-
- // populate array
- for (i = 0; i < count; i++) {
- temp = dt->next;
- tbsort[i] = (void *)dt->extra;
+ if (dt->child) tb = collate_leaves(tb, dt->child);
+ else *(tb++) = (void *)dt->extra;
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;
}
@@ -1008,7 +1076,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;
@@ -1026,15 +1094,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,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);
@@ -1060,10 +1134,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++) {
@@ -1144,6 +1220,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" : "");
@@ -1151,13 +1229,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;
}
@@ -1187,9 +1263,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))) {
@@ -1339,11 +1417,12 @@ static void top_common(
*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]);
}
@@ -1352,11 +1431,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++;
@@ -1402,11 +1488,11 @@ 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");
@@ -1446,7 +1532,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)];
}
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index e1c00bab..7da519d0 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -182,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
};
@@ -214,8 +221,9 @@ static int emit(char *line, long len, int eol)
// 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,
+// (even with wildcards) and string must be null terminated at string[len].
+// But this can find a match after the first NUL.
+static int regex_null(regex_t *preg, char *string, long len, int nmatch,
regmatch_t pmatch[], int eflags)
{
char *s = string;
@@ -274,7 +282,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;
@@ -283,7 +291,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
@@ -303,45 +311,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 && !regex_null(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 && !regex_null(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
@@ -349,19 +357,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;
}
@@ -369,7 +377,7 @@ static void walk_pattern(char **pline, long plen)
if (c=='a' || c=='r') {
struct append *a = xzalloc(sizeof(struct append));
- if (logrus->arg1) 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') {
@@ -377,16 +385,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;
@@ -408,7 +416,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') {
@@ -430,7 +438,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;
@@ -458,14 +466,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;
@@ -487,13 +495,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 (!regex_null(reg, rline, rlen, 10, match, mflags)) {
mflags = REG_NOTBOL;
// Zero length matches don't count immediately after a previous match
@@ -506,7 +514,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;
@@ -566,15 +574,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;
@@ -586,14 +594,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;
@@ -606,11 +614,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]);
@@ -621,7 +629,7 @@ writenow:
emit(toybuf, strlen(toybuf), 1);
}
- logrus = logrus->next;
+ command = command->next;
}
if (line && !(toys.optflags & FLAG_n)) emit(line, len, eol);
@@ -680,7 +688,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");
@@ -688,12 +696,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, name, process_line);
if (i) {
- walk_pattern(0, 0);
+ process_line(0, 0);
replace_tempfile(-1, TT.fdout, &tmp);
TT.fdout = 1;
TT.nextline = 0;
@@ -755,37 +763,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) {
+ // 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++;
@@ -793,28 +807,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);
@@ -825,58 +842,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--;
@@ -885,9 +903,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++) {
@@ -895,18 +913,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') {
@@ -924,16 +942,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;
@@ -948,15 +966,15 @@ 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;
@@ -967,25 +985,25 @@ writenow:
// 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;
// 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';
+ if (!command->arg1) command->arg1 = reg - (char*)command;
+ else if (*(command->arg1+(char *)command)) *(reg++) = '\n';
else if (!pline) {
- corwin->arg1 = 0;
+ command->arg1 = 0;
continue;
}
- reg = extend_string((void *)&corwin, line, reg - (char *)corwin, end);
+ reg = extend_string((void *)&command, line, reg - (char *)command, end);
// Recopy data to remove escape sequences and handle line continuation.
if (strchr("aci", c)) {
@@ -1001,7 +1019,7 @@ resume_a:
line++;
goto resume_a;
}
- corwin->hit = 256;
+ command->hit = 256;
break;
}
if (!(reg[-1] = unescape(*line))) reg[-1] = *line;
@@ -1015,14 +1033,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
@@ -1033,7 +1050,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++);
@@ -1042,11 +1061,11 @@ 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(strcmp(al->arg, "-") ? xopen(al->arg, O_RDONLY) : 0,
+ al->arg, parse_pattern);
+ parse_pattern(0, 0);
dlist_terminate(TT.pattern);
if (TT.nextlen) error_exit("no }");
@@ -1056,7 +1075,7 @@ void sed_main(void)
// Inflict pattern upon input files
loopfiles_rw(args, O_RDONLY, 0, 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?
}
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
index 1204f1c6..787e116e 100644
--- a/toys/posix/tail.c
+++ b/toys/posix/tail.c
@@ -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
diff --git a/toys/posix/touch.c b/toys/posix/touch.c
index 052448ba..22a1e2e8 100644
--- a/toys/posix/touch.c
+++ b/toys/posix/touch.c
@@ -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) {
diff --git a/www/cleanup.html b/www/cleanup.html
index 7c599e09..801dfb80 100644
--- a/www/cleanup.html
+++ b/www/cleanup.html
@@ -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>
diff --git a/www/roadmap.html b/www/roadmap.html
index 2bd62490..56067317 100755
--- a/www/roadmap.html
+++ b/www/roadmap.html
@@ -269,8 +269,8 @@ 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
+dd getevent ioctl log
+nandread newfs_msdos
sendevent start stop top
</b></blockquote>
@@ -297,9 +297,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
+dd getevent init ioctl
log logcat logwrapper nandread
-newfs_msdos ps reboot run-as
+newfs_msdos reboot run-as
sendevent start stop top
</b></blockquote>
@@ -312,9 +312,8 @@ 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 sendevent
-start stop top
+dd getevent ioctl log logcat logwrapper
+nandread newfs_msdos sendevent start stop
</span>
</b></blockquote>
@@ -325,6 +324,12 @@ of "pending". These should be a priority for cleanup:</p>
dd expr lsof more netstat route tar tr traceroute
</b></blockquote>
+<p>Android wishlist:</p>
+
+<blockquote><b>
+mtools genvfatfs mke2fs gene2fs
+</b></blockquote>
+
<hr />
<h2><a name=tizen /><a href="#tizen">Use case: Tizen Core</a></h2>