aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2018-01-22 21:44:11 +0000
committerandroid-build-merger <android-build-merger@google.com>2018-01-22 21:44:11 +0000
commitf598053f98e163f288f05b122a56e475cd4842b4 (patch)
tree5e0f20158ee14a957e84b6551c7d19f4adedb1eb
parentba146a2fd9022aa9a6ae4b5ff01f6c4eddf12f0a (diff)
parent3c9362b1a007653a1bf8be250e6d17e93cfea831 (diff)
downloadtoybox-f598053f98e163f288f05b122a56e475cd4842b4.tar.gz
Merge remote-tracking branch 'toybox/master' into HEAD am: af478b798e am: 8060e56a63
am: 3c9362b1a0 Change-Id: I763ec8dad8994c5e80e7e44d5c408060b32c08a8
-rw-r--r--.config1
-rw-r--r--Android.mk4
-rw-r--r--Makefile23
-rw-r--r--generated/config.h2
-rw-r--r--generated/flags.h44
-rw-r--r--generated/globals.h57
-rw-r--r--generated/help.h18
-rw-r--r--generated/newtoys.h11
-rw-r--r--lib/interestingtimes.c4
-rw-r--r--lib/lib.c81
-rw-r--r--lib/lib.h3
-rw-r--r--lib/portability.c5
-rw-r--r--lib/portability.h5
-rw-r--r--scripts/help.txt22
-rwxr-xr-xtests/fmt.test21
-rw-r--r--tests/gunzip.test50
-rw-r--r--tests/gzip.test78
-rwxr-xr-xtests/xargs.test3
-rw-r--r--tests/xxd.test7
-rw-r--r--[-rwxr-xr-x]tests/zcat.test34
-rw-r--r--toys/other/reset.c6
-rw-r--r--toys/other/setfattr.c (renamed from toys/pending/setfattr.c)22
-rw-r--r--toys/other/xxd.c122
-rw-r--r--toys/pending/fmt.c78
-rw-r--r--toys/pending/gzip.c173
-rw-r--r--toys/pending/iconv.c74
-rw-r--r--toys/pending/logger.c73
-rw-r--r--toys/pending/syslogd.c1
-rw-r--r--toys/posix/iconv.c74
-rw-r--r--toys/posix/logger.c70
-rw-r--r--toys/posix/ps.c130
-rw-r--r--toys/posix/xargs.c5
32 files changed, 816 insertions, 485 deletions
diff --git a/.config b/.config
index 92ea2ccb..22616a47 100644
--- a/.config
+++ b/.config
@@ -105,6 +105,7 @@ CONFIG_FGREP=y
CONFIG_FILE=y
CONFIG_FIND=y
CONFIG_FLOCK=y
+CONFIG_FMT=y
# CONFIG_FOLD is not set
CONFIG_FREERAMDISK=y
CONFIG_FREE=y
diff --git a/Android.mk b/Android.mk
index f6f49e18..3e00e4c0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -129,6 +129,7 @@ common_SRC_FILES := \
toys/other/realpath.c \
toys/other/rev.c \
toys/other/rmmod.c \
+ toys/other/setfattr.c \
toys/other/setsid.c \
toys/other/stat.c \
toys/other/swapoff.c \
@@ -148,12 +149,12 @@ common_SRC_FILES := \
toys/pending/dd.c \
toys/pending/diff.c \
toys/pending/expr.c \
+ toys/pending/fmt.c \
toys/pending/getfattr.c \
toys/pending/gzip.c \
toys/pending/lsof.c \
toys/pending/modprobe.c \
toys/pending/more.c \
- toys/pending/setfattr.c \
toys/pending/stty.c \
toys/pending/tar.c \
toys/pending/tr.c \
@@ -279,6 +280,7 @@ ALL_TOOLS := \
file \
find \
flock \
+ fmt \
free \
getenforce \
groups \
diff --git a/Makefile b/Makefile
index 9933710b..30219156 100644
--- a/Makefile
+++ b/Makefile
@@ -66,25 +66,4 @@ tests:
scripts/test.sh
help::
- @echo ' toybox - Build toybox.'
- @echo ' COMMANDNAME - Build individual toybox command as a standalone binary.'
- @echo ' list - List COMMANDNAMEs you can build standalone.'
- @echo ' list_pending - List unfinished COMMANDNAMEs out of toys/pending.'
- @echo ' change - Build each command standalone under change/.'
- @echo ' baseline - Create toybox_old for use by bloatcheck.'
- @echo ' bloatcheck - Report size differences between old and current versions'
- @echo ' test_COMMAND - Run tests for COMMAND (test_ps, test_cat, etc.)'
- @echo ' tests - Run test suite against all compiled commands.'
- @echo ' export TEST_HOST=1 to test host command, VERBOSE=1'
- @echo ' to show diff, VERBOSE=fail to stop after first failure.'
- @echo ' clean - Delete temporary files.'
- @echo " distclean - Delete everything that isn't shipped."
- @echo ' install_airlock - Install toybox and host toolchain into $$PREFIX directory'
- @echo ' (providing $$PATH for hermetic builds).'
- @echo ' install_flat - Install toybox into $$PREFIX directory.'
- @echo ' install - Install toybox into subdirectories of $$PREFIX.'
- @echo ' uninstall_flat - Remove toybox from $$PREFIX directory.'
- @echo ' uninstall - Remove toybox from subdirectories of $$PREFIX.'
- @echo ''
- @echo 'example: CFLAGS="--static" CROSS_COMPILE=armv5l- make defconfig toybox install'
- @echo ''
+ @cat scripts/help.txt
diff --git a/generated/config.h b/generated/config.h
index d5472e27..8f4f1130 100644
--- a/generated/config.h
+++ b/generated/config.h
@@ -184,6 +184,8 @@
#define USE_FIND(...) __VA_ARGS__
#define CFG_FLOCK 1
#define USE_FLOCK(...) __VA_ARGS__
+#define CFG_FMT 1
+#define USE_FMT(...) __VA_ARGS__
#define CFG_FOLD 0
#define USE_FOLD(...)
#define CFG_FREERAMDISK 1
diff --git a/generated/flags.h b/generated/flags.h
index f77c67b2..e0c24f35 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -765,6 +765,15 @@
#undef FLAG_n
#endif
+// fmt w# w#
+#undef OPTSTR_fmt
+#define OPTSTR_fmt "w#"
+#ifdef CLEANUP_fmt
+#undef CLEANUP_fmt
+#undef FOR_fmt
+#undef FLAG_w
+#endif
+
// fold bsuw#<1
#undef OPTSTR_fold
#define OPTSTR_fold "bsuw#<1"
@@ -974,9 +983,9 @@
#undef FOR_groups
#endif
-// gunzip cdfk123456789 cdfk123456789
+// gunzip cdfk123456789[-123456789] cdfk123456789[-123456789]
#undef OPTSTR_gunzip
-#define OPTSTR_gunzip "cdfk123456789"
+#define OPTSTR_gunzip "cdfk123456789[-123456789]"
#ifdef CLEANUP_gunzip
#undef CLEANUP_gunzip
#undef FOR_gunzip
@@ -995,9 +1004,9 @@
#undef FLAG_c
#endif
-// gzip cdfk123456789 cdfk123456789
+// gzip cdfk123456789[-123456789] cdfk123456789[-123456789]
#undef OPTSTR_gzip
-#define OPTSTR_gzip "cdfk123456789"
+#define OPTSTR_gzip "cdfk123456789[-123456789]"
#ifdef CLEANUP_gzip
#undef CLEANUP_gzip
#undef FOR_gzip
@@ -3188,9 +3197,9 @@
#undef FLAG_a
#endif
-// xargs ^I:E:L#ptxrn#<1s#0 ^I:E:L#ptxrn#<1s#0
+// xargs ^I:E:L#ptxrn#<1s#0[!0E] ^I:E:L#ptxrn#<1s#0[!0E]
#undef OPTSTR_xargs
-#define OPTSTR_xargs "^I:E:L#ptxrn#<1s#0"
+#define OPTSTR_xargs "^I:E:L#ptxrn#<1s#0[!0E]"
#ifdef CLEANUP_xargs
#undef CLEANUP_xargs
#undef FOR_xargs
@@ -3206,15 +3215,16 @@
#undef FLAG_I
#endif
-// xxd >1c#<1>4096=16l#g#<1=2prs#[!rs] >1c#<1>4096=16l#g#<1=2prs#[!rs]
+// xxd >1c#l#g#<1=2iprs#[!rs] >1c#l#g#<1=2iprs#[!rs]
#undef OPTSTR_xxd
-#define OPTSTR_xxd ">1c#<1>4096=16l#g#<1=2prs#[!rs]"
+#define OPTSTR_xxd ">1c#l#g#<1=2iprs#[!rs]"
#ifdef CLEANUP_xxd
#undef CLEANUP_xxd
#undef FOR_xxd
#undef FLAG_s
#undef FLAG_r
#undef FLAG_p
+#undef FLAG_i
#undef FLAG_g
#undef FLAG_l
#undef FLAG_c
@@ -3236,9 +3246,9 @@
#undef FOR_yes
#endif
-// zcat cdfk123456789 cdfk123456789
+// zcat cdfk123456789[-123456789] cdfk123456789[-123456789]
#undef OPTSTR_zcat
-#define OPTSTR_zcat "cdfk123456789"
+#define OPTSTR_zcat "cdfk123456789[-123456789]"
#ifdef CLEANUP_zcat
#undef CLEANUP_zcat
#undef FOR_zcat
@@ -3894,6 +3904,13 @@
#define FLAG_n (1<<3)
#endif
+#ifdef FOR_fmt
+#ifndef TT
+#define TT this.fmt
+#endif
+#define FLAG_w (1<<0)
+#endif
+
#ifdef FOR_fold
#ifndef TT
#define TT this.fold
@@ -5956,9 +5973,10 @@
#define FLAG_s (1<<0)
#define FLAG_r (1<<1)
#define FLAG_p (1<<2)
-#define FLAG_g (1<<3)
-#define FLAG_l (1<<4)
-#define FLAG_c (1<<5)
+#define FLAG_i (1<<3)
+#define FLAG_g (1<<4)
+#define FLAG_l (1<<5)
+#define FLAG_c (1<<6)
#endif
#ifdef FOR_xzcat
diff --git a/generated/globals.h b/generated/globals.h
index 3a5d249b..c5ddadd9 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -348,6 +348,12 @@ struct oneit_data {
char *console;
};
+// toys/other/setfattr.c
+
+struct setfattr_data {
+ char *x, *v, *n;
+};
+
// toys/other/shred.c
struct shred_data {
@@ -583,6 +589,12 @@ struct fdisk_data {
long cylinders;
};
+// toys/pending/fmt.c
+
+struct fmt_data {
+ int width;
+};
+
// toys/pending/fold.c
struct fold_data {
@@ -645,15 +657,6 @@ struct host_data {
char *type_str;
};
-// toys/pending/iconv.c
-
-struct iconv_data {
- char *from;
- char *to;
-
- void *ic;
-};
-
// toys/pending/ip.c
struct ip_data {
@@ -695,13 +698,6 @@ struct last_data {
struct arg_list *list;
};
-// toys/pending/logger.c
-
-struct logger_data {
- char *priority_arg;
- char *ident;
-};
-
// toys/pending/lsof.c
struct lsof_data {
@@ -785,12 +781,6 @@ struct route_data {
char *family;
};
-// toys/pending/setfattr.c
-
-struct setfattr_data {
- char *x, *v, *n;
-};
-
// toys/pending/sh.c
struct sh_data {
@@ -1128,6 +1118,15 @@ struct head_data {
int file_no;
};
+// toys/posix/iconv.c
+
+struct iconv_data {
+ char *from;
+ char *to;
+
+ void *ic;
+};
+
// toys/posix/id.c
struct id_data {
@@ -1141,6 +1140,13 @@ struct kill_data {
struct arg_list *olist;
};
+// toys/posix/logger.c
+
+struct logger_data {
+ char *priority;
+ char *ident;
+};
+
// toys/posix/ls.c
struct ls_data {
@@ -1443,6 +1449,7 @@ extern union global_union {
struct modinfo_data modinfo;
struct nsenter_data nsenter;
struct oneit_data oneit;
+ struct setfattr_data setfattr;
struct shred_data shred;
struct stat_data stat;
struct swapon_data swapon;
@@ -1465,6 +1472,7 @@ extern union global_union {
struct dumpleases_data dumpleases;
struct expr_data expr;
struct fdisk_data fdisk;
+ struct fmt_data fmt;
struct fold_data fold;
struct fsck_data fsck;
struct getfattr_data getfattr;
@@ -1472,13 +1480,11 @@ extern union global_union {
struct groupadd_data groupadd;
struct gzip_data gzip;
struct host_data host;
- struct iconv_data iconv;
struct ip_data ip;
struct ipcrm_data ipcrm;
struct ipcs_data ipcs;
struct klogd_data klogd;
struct last_data last;
- struct logger_data logger;
struct lsof_data lsof;
struct mke2fs_data mke2fs;
struct modprobe_data modprobe;
@@ -1486,7 +1492,6 @@ extern union global_union {
struct openvt_data openvt;
struct ping_data ping;
struct route_data route;
- struct setfattr_data setfattr;
struct sh_data sh;
struct stty_data stty;
struct sulogin_data sulogin;
@@ -1519,8 +1524,10 @@ extern union global_union {
struct find_data find;
struct grep_data grep;
struct head_data head;
+ struct iconv_data iconv;
struct id_data id;
struct kill_data kill;
+ struct logger_data logger;
struct ls_data ls;
struct mkdir_data mkdir;
struct mkfifo_data mkfifo;
diff --git a/generated/help.h b/generated/help.h
index aec66c9b..f648b83c 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -130,7 +130,7 @@
#define HELP_yes "usage: yes [args...]\n\nRepeatedly output line until killed. If no args, output 'y'.\n\n\n"
-#define HELP_xxd "usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]\n\nHexdump a file to stdout. If no file is listed, copy from stdin.\nFilename \"-\" is a synonym for stdin.\n\n-c n Show n bytes per line (default 16)\n-g n Group bytes by adding a ' ' every n bytes (default 2)\n-l n Limit of n bytes before stopping (default is no limit)\n-p Plain hexdump (30 bytes/line, no grouping)\n-r Reverse operation: turn a hexdump into a binary file\n-s n Skip to offset n\n\n"
+#define HELP_xxd "usage: xxd [-c n] [-g n] [-i] [-l n] [-p] [-r] [-s n] [file]\n\nHexdump a file to stdout. If no file is listed, copy from stdin.\nFilename \"-\" is a synonym for stdin.\n\n-c n Show n bytes per line (default 16)\n-g n Group bytes by adding a ' ' every n bytes (default 2)\n-i Include file output format (comma-separated hex byte literals)\n-l n Limit of n bytes before stopping (default is no limit)\n-p Plain hexdump (30 bytes/line, no grouping)\n-r Reverse operation: turn a hexdump into a binary file\n-s n Skip to offset n\n\n"
#define HELP_which "usage: which [-a] filename ...\n\nSearch $PATH for executable files matching filename(s).\n\n-a Show all matches\n\n"
@@ -168,6 +168,8 @@
#define HELP_setsid "usage: setsid [-t] command [args...]\n\nRun process in a new session.\n\n-t Grab tty (become foreground process, receiving keyboard signals)\n\n"
+#define HELP_setfattr "usage: setfattr [-h] [-x|-n NAME] [-v VALUE] FILE...\n\nWrite POSIX extended attributes.\n\n-h Do not dereference symlink\n-n Set given attribute\n-x Remove given attribute\n-v Set value for attribute -n (default is empty)\n\n"
+
#define HELP_rmmod "usage: rmmod [-wf] [MODULE]\n\nUnload the module named MODULE from the Linux kernel.\n-f Force unload of a module\n-w Wait until the module is no longer used\n\n\n"
#define HELP_rev "usage: rev [FILE...]\n\nOutput each line reversed, when no files are given stdin is used.\n\n"
@@ -332,8 +334,6 @@
#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_setfattr "usage: setfattr [-h] [-x|-n NAME] [-v VALUE] FILE...\n\nWrite POSIX extended attributes.\n\n-h Do not dereference symlink\n-n Set given attribute\n-x Remove given attribute\n-v Set value for attribute -n (default is empty)\n\n"
-
#define HELP_route "usage: route [-ne] [-A [46]] [add|del TARGET [OPTIONS]]\n\nDisplay, add or delete network routes in the \"Forwarding Information Base\".\n\n-n Show numerical addresses (no DNS lookups)\n-e display netstat fields\n\nRouting means sending packets out a network interface to an address.\nThe kernel can tell where to send packets one hop away by examining each\ninterface's address and netmask, so the most common use of this command\nis to identify a \"gateway\" that forwards other traffic.\n\nAssigning an address to an interface automatically creates an appropriate\nnetwork route (\"ifconfig eth0 10.0.2.15/8\" does \"route add 10.0.0.0/8 eth0\"\nfor you), although some devices (such as loopback) won't show it in the\ntable. For machines more than one hop away, you need to specify a gateway\n(ala \"route add default gw 10.0.2.2\").\n\nThe address \"default\" is a wildcard address (0.0.0.0/0) matching all\npackets without a more specific route.\n\nAvailable OPTIONS include:\nreject - blocking route (force match failure)\ndev NAME - force packets out this interface (ala \"eth0\")\nnetmask - old way of saying things like ADDR/24\ngw ADDR - forward packets to gateway ADDR\n\n\n"
#define HELP_ping "usage: ping [OPTIONS] HOST\n\nCheck network connectivity by sending packets to a host and reporting\nits response.\n\nSend ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each\necho it receives back, with round trip time.\n\nOptions:\n-4, -6 Force IPv4 or IPv6\n-c CNT Send CNT many packets\n-I IFACE/IP Source interface or address\n-q Quiet, only displays output at start and when finished\n-s SIZE Packet SIZE in bytes (default 56)\n-t TTL Set Time (number of hops) To Live\n-W SEC Seconds to wait for response after all packets sent (default 10)\n-w SEC Exit after this many seconds\n\n"
@@ -362,8 +362,6 @@
#define HELP_lsof "usage: lsof [-lt] [-p PID1,PID2,...] [FILE...]\n\nList all open files belonging to all active processes, or processes using\nlisted FILE(s).\n\n-l list uids numerically\n-p for given comma-separated pids only (default all pids)\n-t terse (pid only) output\n\n"
-#define HELP_logger "usage: logger [-s] [-t tag] [-p [facility.]priority] [message]\n\nLog message (or stdin) to syslog.\n\n"
-
#define HELP_last "usage: last [-W] [-f FILE]\n\nShow listing of last logged in users.\n\n-W Display the information without host-column truncation\n-f FILE Read from file FILE instead of /var/log/wtmp\n\n"
#define HELP_klogd "usage: klogd [-n] [-c N]\n\n-c N Print to console messages more urgent than prio N (1-8)\"\n-n Run in foreground\n\n"
@@ -376,11 +374,9 @@
#define HELP_init "usage: init\n\nSystem V style init.\n\nFirst program to run (as PID 1) when the system comes up, reading\n/etc/inittab to determine actions.\n\n"
-#define HELP_iconv "usage: iconv [-f FROM] [-t TO] [FILE...]\n\nConvert character encoding of files.\n\n-f convert from (default utf8)\n-t convert to (default utf8)\n\n"
-
#define HELP_host "usage: host [-av] [-t TYPE] NAME [SERVER]\n\nPerform DNS lookup on NAME, which can be a domain name to lookup,\nor an ipv4 dotted or ipv6 colon seprated address to reverse lookup.\nSERVER (if present) is the DNS server to use.\n\n-a no idea\n-t not a clue\n-v verbose\n\n"
-#define HELP_zcat "usage: zcat [FILE...]\n\nDecompress files to stdout. Like `gzip -dc`.\n\n-c Output to stdout (default)\n-f Force: allow read from tty\n\n"
+#define HELP_zcat "usage: zcat [FILE...]\n\nDecompress files to stdout. Like `gzip -dc`.\n\n-f Force: allow read from tty\n\n"
#define HELP_gunzip "usage: gunzip [-cfk] [FILE...]\n\nDecompress files. With no files, decompresses stdin to stdout.\nOn success, the input files are removed and replaced by new\nfiles without the .gz suffix.\n\n-c Output to stdout (act as zcat)\n-f Force: allow read from tty\n-k Keep input files (default is to remove)\n\n"
@@ -398,6 +394,8 @@
#define HELP_fold "usage: fold [-bsu] [-w WIDTH] [FILE...]\n\nFolds (wraps) or unfolds ascii text by adding or removing newlines.\nDefault line width is 80 columns for folding and infinite for unfolding.\n\n-b Fold based on bytes instead of columns\n-s Fold/unfold at whitespace boundaries if possible\n-u Unfold text (and refold if -w is given)\n-w Set lines to WIDTH columns or bytes\n\n"
+#define HELP_fmt "usage: fmt [-w WIDTH] [FILE...]\n\nReformat input to not exceed a maximum line length.\n\n-w WIDTH maximum characters per line (default 75)\n\n"
+
#define HELP_fdisk "usage: fdisk [-lu] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SECTSZ] DISK\n\nChange partition table\n\n-u Start and End are in sectors (instead of cylinders)\n-l Show partition table for each DISK, then exit\n-b size sector size (512, 1024, 2048 or 4096)\n-C CYLINDERS Set number of cylinders/heads/sectors\n-H HEADS\n-S SECTORS\n\n"
#define HELP_expr "usage: expr ARG1 OPERATOR ARG2...\n\nEvaluate expression and print result. For example, \"expr 1 + 2\".\n\nThe supported operators are (grouped from highest to lowest priority):\n\n ( ) : * / % + - != <= < >= > = & |\n\nEach constant and operator must be a separate command line argument.\nAll operators are infix, meaning they expect a constant (or expression\nthat resolves to a constant) on each side of the operator. Operators of\nthe same priority (within each group above) are evaluated left to right.\nParentheses may be used (as separate arguments) to elevate the priority\nof expressions.\n\nCalling expr from a command shell requires a lot of \\( or '*' escaping\nto avoid interpreting shell control characters.\n\nThe & and | operators are logical (not bitwise) and may operate on\nstrings (a blank string is \"false\"). Comparison operators may also\noperate on strings (alphabetical sort).\n\nConstants may be strings or integers. Comparison, logical, and regex\noperators may operate on strings (a blank string is \"false\"), other\noperators require integers.\n\n"
@@ -518,6 +516,8 @@
#define HELP_ls "usage: ls --color[=auto] [-ACFHLRSZacdfhiklmnpqrstux1] [directory...]\n\nlist files\n\nwhat to show:\n-a all files including .hidden -b escape nongraphic chars\n-c use ctime for timestamps -d directory, not contents\n-i inode number -p put a '/' after dir names\n-q unprintable chars as '?' -s storage used (1024 byte units)\n-u use access time for timestamps -A list all files but . and ..\n-H follow command line symlinks -L follow symlinks\n-R recursively list in subdirs -F append /dir *exe @sym |FIFO\n-Z security context\n\noutput formats:\n-1 list one file per line -C columns (sorted vertically)\n-g like -l but no owner -h human readable sizes\n-l long (show full details) -m comma separated\n-n like -l but numeric uid/gid -o like -l but no group\n-x columns (horizontal sort) -ll long with nanoseconds (--full-time)\n\nsorting (default is alphabetical):\n-f unsorted -r reverse -t timestamp -S size\n--color device=yellow symlink=turquoise/red dir=blue socket=purple\n files: exe=green suid=red suidfile=redback stickydir=greenback\n =auto means detect if output is a tty.\n\n"
+#define HELP_logger "usage: logger [-s] [-t TAG] [-p [FACILITY.]PRIORITY] [message...]\n\nLog message (or stdin) to syslog.\n\n-s Also write message to stderr\n-t Use TAG instead of username to identify message source\n-p Specify PRIORITY with optional FACILITY. Default is \"user.notice\"\n\n"
+
#define HELP_ln "usage: ln [-sfnv] [FROM...] TO\n\nCreate a link between FROM and TO.\nWith only one argument, create link in current directory.\n\n-s Create a symbolic link\n-f Force the creation of the link, even if TO already exists\n-n Symlink at destination treated as file\n-v Verbose\n\n"
#define HELP_link "usage: link FILE NEWLINK\n\nCreate hardlink to a file.\n\n"
@@ -534,6 +534,8 @@
#define HELP_id "usage: id [-GZgnru] \n\nPrint user and group ID.\n-G Show only the group IDs\n-Z Show only security context\n-g Show only the effective group ID\n-n print names instead of numeric IDs (to be used with -Ggu)\n-r Show real ID instead of effective ID\n-u Show only the effective user ID\n"
+#define HELP_iconv "usage: iconv [-f FROM] [-t TO] [FILE...]\n\nConvert character encoding of files.\n\n-c Omit invalid chars\n-f convert from (default utf8)\n-t convert to (default utf8)\n\n"
+
#define HELP_head "usage: head [-n number] [file...]\n\nCopy first lines from files to stdout. If no files listed, copy from\nstdin. Filename \"-\" is a synonym for stdin.\n\n-n Number of lines to copy\n-c Number of bytes to copy\n-q Never print headers\n-v Always print headers\n\n"
#define HELP_grep "usage: grep [-EFrivwcloqsHbhn] [-ABC NUM] [-m MAX] [-e REGEX]... [-MS PATTERN]... [-f REGFILE] [FILE]...\n\nShow lines matching regular expressions. If no -e, first argument is\nregular expression to match. With no files (or \"-\" filename) read stdin.\nReturns 0 if matched, 1 if no match found.\n\n-e Regex to match. (May be repeated.)\n-f File listing regular expressions to match.\n\nfile search:\n-r Recurse into subdirectories (defaults FILE to \".\")\n-M Match filename pattern (--include)\n-S Skip filename pattern (--exclude)\n\nmatch type:\n-A Show NUM lines after -B Show NUM lines before match\n-C NUM lines context (A+B) -E extended regex syntax\n-F fixed (literal match) -i case insensitive\n-m match MAX many lines -v invert match\n-w whole word (implies -E) -x whole line\n-z input NUL terminated\n\ndisplay modes: (default: matched line)\n-c count of matching lines -l show matching filenames\n-o only matching part -q quiet (errors only)\n-s silent (no error msg) -Z output NUL terminated\n\noutput prefix (default: filename if checking more than 1 file)\n-H force filename -b byte offset of match\n-h hide filename -n line number of match\n\n"
diff --git a/generated/newtoys.h b/generated/newtoys.h
index 216af530..6743a3b7 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -69,6 +69,7 @@ USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
USE_FILE(NEWTOY(file, "<1hL[!hL]", TOYFLAG_USR|TOYFLAG_BIN))
USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
USE_FLOCK(NEWTOY(flock, "<1>1nsux[-sux]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_FMT(NEWTOY(fmt, "w#", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
USE_FOLD(NEWTOY(fold, "bsuw#<1", TOYFLAG_USR|TOYFLAG_BIN))
USE_FREE(NEWTOY(free, "htgmkb[!htgmkb]", TOYFLAG_USR|TOYFLAG_BIN))
USE_FREERAMDISK(NEWTOY(freeramdisk, "<1>1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
@@ -86,8 +87,8 @@ USE_GREP(NEWTOY(grep, "S(exclude)*M(include)*C#B#A#ZzEFHabhinorsvwclqe*f*m#x[!wx
USE_GROUPADD(NEWTOY(groupadd, "<1>2g#<0S", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_GROUPDEL(NEWTOY(groupdel, "<1>2", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_GROUPS(NEWTOY(groups, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
-USE_GZIP(NEWTOY(gzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
+USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_GZIP(NEWTOY(gzip, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
USE_REBOOT(OLDTOY(halt, reboot, TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
USE_HEAD(NEWTOY(head, "?n#<0=10c#<0qv[-nc]", TOYFLAG_USR|TOYFLAG_BIN))
USE_HELLO(NEWTOY(hello, 0, TOYFLAG_USR|TOYFLAG_BIN))
@@ -278,8 +279,8 @@ USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN))
USE_WHICH(NEWTOY(which, "<1a", TOYFLAG_USR|TOYFLAG_BIN))
USE_WHO(NEWTOY(who, "a", TOYFLAG_USR|TOYFLAG_BIN))
USE_WHOAMI(OLDTOY(whoami, logname, TOYFLAG_USR|TOYFLAG_BIN))
-USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#l#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
USE_XZCAT(NEWTOY(xzcat, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_YES(NEWTOY(yes, NULL, TOYFLAG_USR|TOYFLAG_BIN))
-USE_ZCAT(NEWTOY(zcat, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
+USE_ZCAT(NEWTOY(zcat, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c
index f028f5e9..073a67dd 100644
--- a/lib/interestingtimes.c
+++ b/lib/interestingtimes.c
@@ -5,13 +5,13 @@
#include "toys.h"
-int xgettty(void)
+int tty_fd(void)
{
int i, j;
for (i = 0; i<3; i++) if (isatty(j = (i+1)%3)) return j;
- return xopen("/dev/tty", O_RDWR);
+ return notstdio(open("/dev/tty", O_RDWR));
}
// Quick and dirty query size of terminal, doesn't do ANSI probe fallback.
diff --git a/lib/lib.c b/lib/lib.c
index 44a7cc74..7f5fbbda 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -3,6 +3,7 @@
* Copyright 2006 Rob Landley <rob@landley.net>
*/
+#define SYSLOG_NAMES
#include "toys.h"
void verror_msg(char *msg, int err, va_list va)
@@ -1010,37 +1011,64 @@ char *getbasename(char *name)
return name;
}
-static int argv0_match(char *cmd, char *name)
-{
- return (*name == '/' ? !strcmp(cmd, name)
- : !strcmp(getbasename(cmd), getbasename(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))
{
DIR *dp;
struct dirent *entry;
- if (!(dp = opendir("/proc"))) perror_exit("opendir");
+ if (!(dp = opendir("/proc"))) perror_exit("no /proc");
while ((entry = readdir(dp))) {
- unsigned u;
- char *cmd, *comm, **cur;
+ unsigned u = atoi(entry->d_name);
+ char *cmd = 0, *comm, **cur;
+ off_t len;
- if (!(u = atoi(entry->d_name))) continue;
+ if (!u) continue;
- // For a script, comm and argv[1] will match (argv[0] will be the interp).
+ // Comm is original name of executable (argv[0] could be #! interpreter)
+ // but it's limited to 15 characters
sprintf(libbuf, "/proc/%u/comm", u);
- if (!(comm = readfile(libbuf, libbuf, sizeof(libbuf)))) continue;
- sprintf(libbuf+16, "/proc/%u/cmdline", u);
- if (!(cmd = readfile(libbuf+16, libbuf+16, sizeof(libbuf)-16))) continue;
-
- for (cur = names; *cur; cur++)
- if (argv0_match(cmd, *cur) ||
- (!strncmp(comm, *cur, 15) && argv0_match(cmd+strlen(cmd)+1, *cur)))
- if (callback(u, *cur)) break;
- if (*cur) break;
+ len = sizeof(libbuf);
+ if (!(comm = readfileat(AT_FDCWD, libbuf, libbuf, &len)) || !len)
+ continue;
+ if (libbuf[len-1] == '\n') libbuf[--len] = 0;
+
+ for (cur = names; *cur; cur++) {
+ struct stat st1, st2;
+ char *bb = basename(*cur);
+ off_t len;
+
+ // fast path: only matching a filename (no path) that fits in comm
+ if (strncmp(comm, bb, 15)) continue;
+ len = strlen(bb);
+ if (bb==*cur && len<16) goto match;
+
+ // If we have a path to existing file only match if same inode
+ if (bb!=*cur && !stat(*cur, &st1)) {
+ char buf[32];
+
+ sprintf(buf, "/proc/%u/exe", u);
+ if (stat(buf, &st1)) continue;
+ if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) continue;
+ goto match;
+ }
+
+ // Nope, gotta read command line to confirm
+ if (!cmd) {
+ sprintf(cmd = libbuf+16, "/proc/%u/cmdline", u);
+ len = sizeof(libbuf)-17;
+ if (!(cmd = readfileat(AT_FDCWD, cmd, cmd, &len))) continue;
+ // readfile only guarnatees one null terminator and we need two
+ // (yes the kernel should do this for us, don't care)
+ cmd[len] = 0;
+ }
+ if (!strcmp(bb, basename(cmd))) goto match;
+ if (bb!=*cur && !strcmp(bb, basename(cmd+strlen(cmd)+1))) goto match;
+ continue;
+match:
+ if (callback(u, *cur)) break;
+ }
}
closedir(dp);
}
@@ -1325,7 +1353,16 @@ long environ_bytes()
long bytes = sizeof(char *);
char **ev;
- for (ev = environ; *ev; ev++)
- bytes += sizeof(char *) + strlen(*ev) + 1;
+ for (ev = environ; *ev; ev++) bytes += sizeof(char *) + strlen(*ev) + 1;
+
return bytes;
}
+
+// Return unix time in milliseconds
+long long millitime(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec*1000+ts.tv_nsec/1000000;
+}
diff --git a/lib/lib.h b/lib/lib.h
index 021ab44a..94662983 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -243,6 +243,7 @@ char *getusername(uid_t uid);
char *getgroupname(gid_t gid);
void do_lines(int fd, void (*call)(char **pline, long len));
long environ_bytes();
+long long millitime(void);
#define HR_SPACE 1 // Space between number and units
#define HR_B 2 // Use "B" for single byte units
@@ -273,7 +274,7 @@ int draw_trim_esc(char *str, int padto, int width, char *escmore,
int draw_trim(char *str, int padto, int width);
// interestingtimes.c
-int xgettty(void);
+int tty_fd(void);
int terminal_size(unsigned *xx, unsigned *yy);
int terminal_probesize(unsigned *xx, unsigned *yy);
int scan_key_getsize(char *scratch, int miliwait, unsigned *xx, unsigned *yy);
diff --git a/lib/portability.c b/lib/portability.c
index 78e500b1..38cf5cb9 100644
--- a/lib/portability.c
+++ b/lib/portability.c
@@ -61,9 +61,8 @@ ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream)
line = *linep = new_line;
}
- line[i] = ch;
+ line[i++] = ch;
if (ch == delim) break;
- i += 1;
}
if (i > *np) {
@@ -74,7 +73,7 @@ ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream)
*np = new_len;
line = *linep = new_line;
}
- line[i + 1] = '\0';
+ line[i] = '\0';
return i > 0 ? i : -1;
}
diff --git a/lib/portability.h b/lib/portability.h
index e3e0ef04..e9937179 100644
--- a/lib/portability.h
+++ b/lib/portability.h
@@ -246,3 +246,8 @@ pid_t xfork(void);
static inline int get_sched_policy(int tid, void *policy) {return 0;}
static inline char *get_sched_policy_name(int policy) {return "unknown";}
#endif
+
+#ifndef SYSLOG_NAMES
+typedef struct {char *c_name; int c_val;} CODE;
+extern CODE prioritynames[], facilitynames[];
+#endif
diff --git a/scripts/help.txt b/scripts/help.txt
new file mode 100644
index 00000000..7d52916b
--- /dev/null
+++ b/scripts/help.txt
@@ -0,0 +1,22 @@
+ toybox - Build toybox.
+ COMMANDNAME - Build individual toybox command as a standalone binary.
+ list - List COMMANDNAMEs you can build standalone.
+ list_pending - List unfinished COMMANDNAMEs out of toys/pending.
+ change - Build each command standalone under change/.
+ baseline - Create toybox_old for use by bloatcheck.
+ bloatcheck - Report size differences between old and current versions
+ test_COMMAND - Run tests for COMMAND (test_ps, test_cat, etc.)
+ tests - Run test suite against all compiled commands.
+ export TEST_HOST=1 to test host command, VERBOSE=1
+ to show diff, VERBOSE=fail to stop after first failure.
+ clean - Delete temporary files.
+ distclean - Delete everything that isn't shipped.
+ install_airlock - Install toybox and host toolchain into $PREFIX directory
+ (providing $PATH for hermetic builds).
+ install_flat - Install toybox into $PREFIX directory.
+ install - Install toybox into subdirectories of $PREFIX.
+ uninstall_flat - Remove toybox from $PREFIX directory.
+ uninstall - Remove toybox from subdirectories of $PREFIX.
+
+example: CFLAGS="--static" CROSS_COMPILE=armv5l- make defconfig toybox install
+
diff --git a/tests/fmt.test b/tests/fmt.test
new file mode 100755
index 00000000..0d5c5bd4
--- /dev/null
+++ b/tests/fmt.test
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+echo "hello world " > en.txt
+echo "this is some text " >> en.txt
+# https://en.wikipedia.org/wiki/Aegukga
+echo "동해물과 백두산이 마르고 닳도록" > kr.txt
+echo "하나님이 보우하사 우리나라 만세." >> kr.txt
+
+#testing "name" "command" "result" "infile" "stdin"
+
+testing "join" "fmt en.txt" "hello world this is some text\n" "" ""
+testing "split" "fmt -w 10 en.txt" "hello\nworld\nthis is\nsome text\n" "" ""
+testing "no room" "echo 'hello world' | fmt -w 1" "hello\nworld\n" "" ""
+testing "blank line" "echo -e 'first paragraph of text\n\nand another' | fmt -w 10" "first\nparagraph\nof text\n\nand\nanother\n" "" ""
+testing "ws-only line" "echo -e 'hello\n \nworld' | fmt -w 10" "hello\n\nworld\n" "" ""
+testing "leading space" "echo ' hello world' | fmt -w 5" " hello\n world\n" "" ""
+testing "utf8" "fmt -w 10 kr.txt" "동해물과\n백두산이\n마르고\n닳도록\n하나님이\n보우하사\n우리나라\n만세.\n" "" ""
+
+rm en.txt kr.txt
diff --git a/tests/gunzip.test b/tests/gunzip.test
new file mode 100644
index 00000000..9f9ef5eb
--- /dev/null
+++ b/tests/gunzip.test
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Decompress files.
+# On success, the input files are removed and replaced by new
+# files without the .gz suffix.
+echo -n "foo " | gzip > f1.gz
+echo "bar" | gzip > f2.gz
+testing "with input files" "gunzip f1.gz f2.gz &&
+ ! test -f f1.gz && ! test -f f2.gz &&
+ test -f f1 && test -f f2 &&
+ cat f1 f2" "foo bar\n" "" ""
+rm -f f1 f2 f1.gz f2.gz
+
+# With no files, decompresses stdin to stdout.
+echo "hello world" | gzip > f.gz
+testing "no files (stdin to stdout)" "cat f.gz | gunzip > f &&
+ test -f f.gz && cat f" "hello world\n" "" ""
+rm -f f f.gz
+
+# -c Output to stdout
+echo -n "foo " | gzip > f1.gz
+echo "bar" | gzip > f2.gz
+testing "with input files and -c" "gunzip -c f1.gz f2.gz > out &&
+ test -f f1.gz && test -f f2.gz &&
+ ! test -f f1 && ! test -f f2 &&
+ cat out" "foo bar\n" "" ""
+rm -f f1.gz f2.gz out
+
+# TODO: how to test "gunzip -f"?
+
+# -k Keep input files (don't remove)
+echo "hello world" | gzip > f1.gz
+testing "-k" "gunzip -k f1.gz && cat f1 && zcat f1.gz" \
+ "hello world\nhello world\n" "" ""
+rm -f f1 f1.gz
+
+# Test that gunzip preserves permissions and times.
+export TZ=UTC
+echo "hello world" | gzip > f1.gz
+chmod 0411 f1.gz
+touch -a -t 197801020304 f1.gz
+touch -m -t 198704030201 f1.gz
+testing "permissions/times preservation" \
+ "gunzip -k f1.gz && stat -c '%a %X %Y' f1 && stat -c '%a %Y' f1.gz" \
+ "411 252558240 544413660\n411 544413660\n" "" ""
+rm -f f1 f1.gz
diff --git a/tests/gzip.test b/tests/gzip.test
new file mode 100644
index 00000000..24bd01ed
--- /dev/null
+++ b/tests/gzip.test
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+[ -f testing.sh ] && . testing.sh
+
+#testing "name" "command" "result" "infile" "stdin"
+
+# Compress files.
+# On success, the input files are removed and replaced by new
+# files with the .gz suffix.
+echo -n "foo " > f1
+echo "bar" > f2
+testing "with input files" "gzip f1 f2 &&
+ test -f f1.gz && test -f f2.gz &&
+ ! test -f f1 && ! test -f f2 &&
+ zcat f1.gz f2.gz" "foo bar\n" "" ""
+rm -f f1 f2 f1.gz f2.gz
+
+# With no files, compresses stdin to stdout.
+testing "no files (stdin to stdout)" "echo hello world | gzip > f.gz &&
+ test -f f.gz && zcat f.gz" "hello world\n" "" ""
+rm -f f.gz
+
+# -c Output to stdout
+echo -n "foo " > f1
+echo "bar" > f2
+testing "with input files and -c" "gzip -c f1 f2 > out.gz &&
+ ! test -f f1.gz && ! test -f f2.gz &&
+ test -f f1 && test -f f2 &&
+ zcat out.gz" "foo bar\n" "" ""
+rm -f f1 f2 out.gz
+
+# -d Decompress (act as gunzip)
+echo "hello world" | gzip > f.gz
+testing "-d (act as gunzip)" "gzip -d f.gz &&
+ test -f f && ! test -f f.gz && cat f" "hello world\n" "" ""
+rm -f f.gz f
+
+echo "hello world" | gzip > f.gz
+testing "-dc (act as zcat)" "gzip -dc f.gz &&
+ ! test -f f && test -f f.gz" "hello world\n" "" ""
+rm -f f.gz f
+
+# -f Force: allow overwrite of output file
+echo "hello world" > f1
+echo "precious data" > f1.gz
+testing "no overwrite without -f" \
+ "gzip f1 2>/dev/null || echo refused && cat f1 f1.gz" \
+ "refused\nhello world\nprecious data\n" "" ""
+testing "overwrite with -f" \
+ "gzip -f f1 && echo allowed && ! test -f f1 && zcat f1.gz" \
+ "allowed\nhello world\n" "" ""
+rm -f f1 f1.gz
+
+# -k Keep input files (don't remove)
+echo "hello world" > f1
+testing "-k" "gzip -k f1 && cat f1 && zcat f1.gz" \
+ "hello world\nhello world\n" "" ""
+rm -f f1 f1.gz
+
+# Test that -9 compresses better than -1.
+for i in $(seq 1 1000) ; do echo "hello world" >> x ; done
+gzip -c1 x > x1.gz
+gzip -c9 x > x9.gz
+testing "-1 vs -9" \
+ "test $(stat -c '%s' x1.gz) -gt $(stat -c '%s' x9.gz) && echo okay" \
+ "okay\n" "" ""
+rm -f x x1.gz x9.gz
+
+# Test that gzip preserves permissions and times.
+export TZ=UTC
+echo "hello world" > f1
+chmod 0411 f1
+touch -a -t 197801020304 f1
+touch -m -t 198704030201 f1
+testing "permissions/times preservation" \
+ "gzip -k f1 && TZ=UTC stat -c '%a %Y' f1 && stat -c '%a %X %Y' f1.gz" \
+ "411 544413660\n411 252558240 544413660\n" "" ""
+rm -f f1 f1.gz
diff --git a/tests/xargs.test b/tests/xargs.test
index 966bc5db..407817cc 100755
--- a/tests/xargs.test
+++ b/tests/xargs.test
@@ -24,6 +24,9 @@ testing "command -opt" "xargs -n2 ls -1" "one\ntwo\nthree\n" "" \
"one two three"
rm one two three
+testing "-0 -n1" "printf 'a\0b\0c\0d\0e\0f' | xargs -0 -n1 echo _" "_ a\n_ b\n_ c\n_ d\n_ e\n_ f\n" "" ""
+testing "-0 -n2" "printf 'a\0b\0c\0d\0e\0f' | xargs -0 -n2 echo _" "_ a b\n_ c d\n_ e f\n" "" ""
+
#testing "-n exact match"
#testing "-s exact match"
#testing "-s 0"
diff --git a/tests/xxd.test b/tests/xxd.test
index 5f43b8b5..97634e86 100644
--- a/tests/xxd.test
+++ b/tests/xxd.test
@@ -25,12 +25,15 @@ testing "-c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
testing "-c 8 -g 3 file1" "xxd -c 8 -g 3 file1" \
"00000000: 746869 732069 7320 this is \n00000008: 736f6d 652074 6578 some tex\n00000010: 740a t.\n" "" ""
+testing "-i" "cat file1 | xxd -i -" " 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a\n" "" ""
+
testing "-p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""
testing "-s" "xxd -s 13 file1" "0000000d: 7465 7874 0a text.\n" "" ""
-testing "-r" "xxd file1 | xxd -r" "this is some text\n" "" ""
-testing "-r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""
+testing "-r" "echo -e ' 00000000: 7468 6973 2069 7320 736f 6d65 2074 6578 this is some tex\n00000010: 740a t.' | xxd -r" "this is some text\n" "" ""
+SKIP_HOST=1 testing "-r -i" "echo -e '0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a' | xxd -ri" "this is some text\n" "" ""
+testing "-r -p" "echo 7468697320697320736f6d6520746578740a | xxd -r -p" "this is some text\n" "" ""
testing "-r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""
diff --git a/tests/zcat.test b/tests/zcat.test
index ccd472cd..57af1097 100755..100644
--- a/tests/zcat.test
+++ b/tests/zcat.test
@@ -1,26 +1,20 @@
#!/bin/bash
-# Copyright 2014 Divya Kothari <divya.s.kothari@gmail.com>
-# Copyright 2014 Naha Maggu <maggu.neha@gmail.com>
-
[ -f testing.sh ] && . testing.sh
#testing "name" "command" "result" "infile" "stdin"
-echo "hello" > file
-tar -czf file.gz file
-# Get system zcat
-zcatExe=`which zcat`
-$zcatExe file.gz > zcatOut
-testing "- decompresses a single file" "zcat file.gz > Tempfile && echo "yes"; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile" "yes\nyes\n" "" ""
-#testing "name" "command" "result" "infile" "stdin"
-echo "hello" > file1
-echo "hi" > file2
-echo "Hi, Good morning !! I am a bzcat tester" > file3
-tar -czf file1.gz file1
-tar -czf file2.gz file2
-tar -czf file3.gz file3
-# Get system zcat
-zcatExe=`which zcat`
-$zcatExe file1.gz file2.gz file3.gz > zcatOut
-testing "- decompresses multiple files" "zcat file1.gz file2.gz file3.gz > Tempfile && echo "yes" ; diff Tempfile zcatOut && echo "yes"; rm -rf file* zcatOut Tempfile " "yes\nyes\n" "" ""
+echo -n "foo " | gzip > f1.gz
+echo "bar" | gzip > f2.gz
+
+# zcat is basically just `gzip -dc`...
+testing "files" "zcat f1.gz f2.gz && test -f f1.gz && test -f f2.gz" \
+ "foo bar\n" "" ""
+
+# zcat -c is allowed, but the -c changes nothing.
+testing "-c" "zcat -c f1.gz f2.gz && test -f f1.gz && test -f f2.gz" \
+ "foo bar\n" "" ""
+
+# TODO: how to test "zcat -f"?
+
+rm -f f1 f2 f1.gz f2.gz
diff --git a/toys/other/reset.c b/toys/other/reset.c
index 0c2089cc..aa9b74ea 100644
--- a/toys/other/reset.c
+++ b/toys/other/reset.c
@@ -18,6 +18,8 @@ config RESET
void reset_main(void)
{
- // man 4 console codes: reset terminal is ESC (no left bracket) c
- xwrite(xgettty(), "\033c", 2);
+ int fd = tty_fd();
+
+ // man 4 console_codes: reset terminal is ESC (no left bracket) c
+ xwrite(fd<0 ? 1 : fd, "\033c", 2);
}
diff --git a/toys/pending/setfattr.c b/toys/other/setfattr.c
index 7e8bae24..48817aa2 100644
--- a/toys/pending/setfattr.c
+++ b/toys/other/setfattr.c
@@ -8,7 +8,7 @@ USE_SETFATTR(NEWTOY(setfattr, "hn:|v:x:|[!xv]", TOYFLAG_USR|TOYFLAG_BIN))
config SETFATTR
bool "setfattr"
- default n
+ default y
help
usage: setfattr [-h] [-x|-n NAME] [-v VALUE] FILE...
@@ -27,21 +27,15 @@ GLOBALS(
char *x, *v, *n;
)
-static void do_setfattr(char *file)
-{
- int h = toys.optflags & FLAG_h;
-
- if (toys.optflags&FLAG_x) {
- if ((h ? lremovexattr : removexattr)(file, TT.x))
- perror_msg("removexattr failed");
- } else
- if ((h ? lsetxattr : setxattr)(file, TT.n, TT.v, TT.v?strlen(TT.v):0, 0))
- perror_msg("setxattr failed");
-}
-
void setfattr_main(void)
{
+ int h = toys.optflags & FLAG_h, rc;
char **s;
- for (s=toys.optargs; *s; s++) do_setfattr(*s);
+ for (s=toys.optargs; *s; s++) {
+ if (TT.x) rc = (h?lremovexattr:removexattr)(*s, TT.x);
+ else rc = (h?lsetxattr:setxattr)(*s, TT.n, TT.v, TT.v?strlen(TT.v):0, 0);
+
+ if (rc) perror_msg("%s", *s);
+ }
}
diff --git a/toys/other/xxd.c b/toys/other/xxd.c
index 0ff5947f..edc5772c 100644
--- a/toys/other/xxd.c
+++ b/toys/other/xxd.c
@@ -2,22 +2,28 @@
*
* Copyright 2015 The Android Open Source Project
*
- * No obvious standard, output looks like:
- * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e Linux version 3.
+ * No obvious standard.
+ * Regular output:
+ * "00000000: 4c69 6e75 7820 7665 7273 696f 6e20 342e Linux version 4."
+ * xxd -i "include" or "initializer" output:
+ * " 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,"
+ * xxd -p "plain" output:
+ * "4c696e75782076657273696f6e20342e392e302d342d616d643634202864"
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#l#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
config XXD
bool "xxd"
default y
help
- usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]
+ usage: xxd [-c n] [-g n] [-i] [-l n] [-p] [-r] [-s n] [file]
Hexdump a file to stdout. If no file is listed, copy from stdin.
Filename "-" is a synonym for stdin.
-c n Show n bytes per line (default 16)
-g n Group bytes by adding a ' ' every n bytes (default 2)
+ -i Include file output format (comma-separated hex byte literals)
-l n Limit of n bytes before stopping (default is no limit)
-p Plain hexdump (30 bytes/line, no grouping)
-r Reverse operation: turn a hexdump into a binary file
@@ -70,6 +76,27 @@ static void do_xxd(int fd, char *name)
if (len<0) perror_exit("read");
}
+static void do_xxd_include(int fd, char *name)
+{
+ long long total = 0;
+ int c = 1, i, len;
+
+ // The original xxd outputs a header/footer if given a filename (not stdin).
+ // We don't, which means that unlike the original we can implement -ri.
+ while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
+ total += len;
+ for (i = 0; i < len; ++i) {
+ printf("%s%#.02x", c > 1 ? ", " : " ", toybuf[i]);
+ if (c++ == TT.c) {
+ xprintf(",\n");
+ c = 1;
+ }
+ }
+ }
+ if (len < 0) perror_msg_raw(name);
+ if (c > 1) xputc('\n');
+}
+
static int dehex(char ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
@@ -81,58 +108,71 @@ static int dehex(char ch)
static void do_xxd_reverse(int fd, char *name)
{
FILE *fp = xfdopen(fd, "r");
+ int tmp;
- while (!feof(fp)) {
- int col = 0;
- int tmp;
-
- // Each line of a non-plain hexdump starts with an offset/address.
- if (!(toys.optflags&FLAG_p)) {
- long long pos;
-
- if (fscanf(fp, "%llx: ", &pos) == 1) {
- if (fseek(stdout, pos, SEEK_SET) != 0) {
- // TODO: just write out zeros if non-seekable?
- perror_exit("%s: seek failed", name);
+ if (toys.optflags&FLAG_i) {
+ // -ri is a very easy special case.
+ while (fscanf(fp, " 0x%02x,", &tmp) == 1) {
+ fputc(tmp & 0xff, stdout);
+ }
+ } else {
+ while (!feof(fp)) {
+ int col = 0;
+
+ // Each line of a regular hexdump starts with an offset/address.
+ // Each line of a plain hexdump just goes straight into the bytes.
+ if (!(toys.optflags&FLAG_p)) {
+ long long pos;
+
+ if (fscanf(fp, "%llx: ", &pos) == 1) {
+ if (fseek(stdout, pos, SEEK_SET) != 0) {
+ // TODO: just write out zeros if non-seekable?
+ perror_exit("%s: seek failed", name);
+ }
}
}
- }
- // A plain hexdump can have as many bytes per line as you like,
- // but a non-plain hexdump assumes garbage after it's seen the
- // specified number of bytes.
- while (toys.optflags&FLAG_p || col < TT.c) {
- int n1, n2;
-
- // If we're at EOF or EOL or we read some non-hex...
- if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
- // If we're at EOL, start on that line.
- if (n1 == -2 || n2 == -2) continue;
- // Otherwise, skip to the next line.
- break;
- }
+ // A plain hexdump can have as many bytes per line as you like,
+ // but a non-plain hexdump assumes garbage after it's seen the
+ // specified number of bytes.
+ while (toys.optflags&FLAG_p || col < TT.c) {
+ int n1, n2;
+
+ // If we're at EOF or EOL or we read some non-hex...
+ if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
+ // If we're at EOL, start on that line.
+ if (n1 == -2 || n2 == -2) continue;
+ // Otherwise, skip to the next line.
+ break;
+ }
- fputc((n1 << 4) | (n2 & 0xf), stdout);
- col++;
+ fputc((n1 << 4) | (n2 & 0xf), stdout);
+ col++;
- // Is there any grouping going on? Ignore a single space.
- tmp = fgetc(fp);
- if (tmp != ' ') ungetc(tmp, fp);
- }
+ // Is there any grouping going on? Ignore a single space.
+ tmp = fgetc(fp);
+ if (tmp != ' ') ungetc(tmp, fp);
+ }
- // Skip anything else on this line (such as the ASCII dump).
- while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
- ;
+ // Skip anything else on this line (such as the ASCII dump).
+ while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
+ ;
+ }
}
- if (ferror(fp)) perror_msg_raw(name);
+ if (ferror(fp)) perror_msg_raw(name);
fclose(fp);
}
void xxd_main(void)
{
+ if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c);
+ if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16;
+
// Plain style is 30 bytes/line, no grouping.
if (toys.optflags&FLAG_p) TT.c = TT.g = 30;
- loopfiles(toys.optargs, toys.optflags&FLAG_r ? do_xxd_reverse : do_xxd);
+ loopfiles(toys.optargs,
+ toys.optflags&FLAG_r ? do_xxd_reverse
+ : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd));
}
diff --git a/toys/pending/fmt.c b/toys/pending/fmt.c
new file mode 100644
index 00000000..618642a2
--- /dev/null
+++ b/toys/pending/fmt.c
@@ -0,0 +1,78 @@
+/* fmt.c - Text formatter
+ *
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Deviations from original:
+ * we treat all whitespace as equal (no tab expansion, no runs of spaces)
+ * we don't try to recognize ends of sentences to double-space after ./?/!
+
+USE_FMT(NEWTOY(fmt, "w#", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+
+config FMT
+ bool "fmt"
+ default y
+ help
+ usage: fmt [-w WIDTH] [FILE...]
+
+ Reformat input to not exceed a maximum line length.
+
+ -w WIDTH maximum characters per line (default 75)
+*/
+
+#define FOR_fmt
+#include "toys.h"
+
+GLOBALS(
+ int width;
+)
+
+static void do_fmt(int fd, char *name)
+{
+ FILE *fp = xfdopen(fd, "re");
+ char *line = NULL;
+ size_t allocated_length = 0;
+ int cols = 0, is_first = 1, indent_end = 0, line_length;
+
+ while ((line_length = getline(&line, &allocated_length, fp)) > 0) {
+ int b = 0, e, w;
+
+ while (b < line_length && isspace(line[b])) b++;
+ if (b == line_length) {
+ if (cols > 0) xputc('\n');
+ xputc('\n');
+ is_first = 1;
+ cols = 0;
+ continue;
+ }
+ if (is_first) indent_end = b;
+
+ for (; b < line_length; b = e + 1) {
+ while (isspace(line[b])) b++;
+ for (e = b + 1; e < line_length && !isspace(line[e]);) e++;
+ if (e >= line_length) break;
+
+ line[e] = 0;
+ w = utf8len(line + b);
+
+ if (!is_first && (cols + (is_first?indent_end:1) + w) >= TT.width) {
+ xputc('\n');
+ is_first = 1;
+ cols = 0;
+ }
+ xprintf("%.*s%.*s",is_first?indent_end:1,is_first?line:" ",(e-b),line+b);
+ cols += (is_first?indent_end:1) + w;
+ b = e + 1;
+ is_first = 0;
+ }
+ }
+ if (cols > 0) xputc('\n');
+ fclose(fp);
+}
+
+void fmt_main(void)
+{
+ if (TT.width < 0) error_exit("negative width: %d", TT.width);
+ if (!TT.width) TT.width = 75;
+
+ loopfiles(toys.optargs, do_fmt);
+}
diff --git a/toys/pending/gzip.c b/toys/pending/gzip.c
index f7576c66..f6d797b5 100644
--- a/toys/pending/gzip.c
+++ b/toys/pending/gzip.c
@@ -3,11 +3,13 @@
* Copyright 2017 The Android Open Source Project
*
* GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
+ *
+ * todo: qtv --rsyncable
-// Existing implementations allow all options for all commands.
-USE_GZIP(NEWTOY(gzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
-USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
-USE_ZCAT(NEWTOY(zcat, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN))
+// gzip.net version allows all options for all commands.
+USE_GZIP(NEWTOY(gzip, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_ZCAT(NEWTOY(zcat, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
config GZIP
bool "gzip"
@@ -50,16 +52,15 @@ config ZCAT
Decompress files to stdout. Like `gzip -dc`.
- -c Output to stdout (default)
-f Force: allow read from tty
*/
-#include <zlib.h>
-
#define FORCE_FLAGS
#define FOR_gzip
#include "toys.h"
+#include <zlib.h>
+
GLOBALS(
int level;
)
@@ -71,119 +72,91 @@ static void fix_time(const char *path, struct stat *sb)
if (utimensat(AT_FDCWD, path, times, 0)) perror_exit("utimensat");
}
-static void gzerror_exit(gzFile f, char *what)
+static int do_zlib(int in_fd, int out_fd)
{
- int err;
- const char *msg = gzerror(f, &err);
-
- ((err == Z_ERRNO) ? perror_exit : error_exit)("%s: %s", what, msg);
-}
+ int len, err = 0, dd = toys.optflags&FLAG_d;
+ char *b = "r";
+ gzFile gz;
-static void do_gunzip(int in_fd, char *arg)
-{
- struct stat sb;
- int len, both_files;
- char *in_name, *out_name;
- gzFile in;
- FILE *out;
-
- // "gunzip x.gz" will decompress "x.gz" to "x".
- len = strlen(arg);
- if (len > 3 && !strcmp(arg+len-3, ".gz")) {
- in_name = strdup(arg);
- out_name = strdup(arg);
- out_name[len-3] = '\0';
- } else if (!strcmp(arg, "-")) {
- // "-" means stdin; assume output to stdout.
- // TODO: require -f to read compressed data from tty?
- in_name = strdup("-");
- out_name = strdup("-");
- } else error_exit("unknown suffix: %s", arg);
-
- if (toys.optflags&FLAG_c) {
- free(out_name);
- out_name = strdup("-");
+ if (!dd) {
+ sprintf(b = toybuf, "w%d", TT.level);
+ if (out_fd == 1) out_fd = xdup(out_fd);
+ }
+ if (!(gz = gzdopen(dd ? in_fd : out_fd, b))) perror_exit("gzdopen");
+ if (dd) {
+ while ((len = gzread(gz, toybuf, sizeof(toybuf))) > 0)
+ if (len != writeall(out_fd, toybuf, len)) break;
+ } else {
+ while ((len = read(in_fd, toybuf, sizeof(toybuf))) > 0)
+ if (len != gzwrite(gz, toybuf, len)) break;
}
- both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
- if (both_files) xstat(in_name, &sb);
+ err = !!len;
+ if (len>0 || err == Z_ERRNO) perror_msg(dd ? "write" : "read");
+ if (len<0)
+ error_msg("%s%s: %s", "gz", dd ? "read" : "write", gzerror(gz, &len));
- in = gzdopen(in_fd, "r");
- if (in == NULL) perror_exit("gzdopen");
- if (!strcmp(out_name, "-")) out = stdout;
- else {
- int out_fd = xcreate(out_name,
- O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
- both_files?sb.st_mode:0666);
+ if (gzclose(gz) != Z_OK) perror_msg("gzclose"), err++;
- out = xfdopen(out_fd, "w");
- }
-
- while ((len = gzread(in, toybuf, sizeof(toybuf))) > 0) {
- if (fwrite(toybuf, 1, len, out) != (size_t) len) perror_exit("writing");
- }
- if (len < 0) gzerror_exit(in, "gzread");
- if (out != stdout && fclose(out)) perror_exit("writing");
- if (gzclose(in) != Z_OK) error_exit("gzclose");
-
- if (both_files) fix_time(out_name, &sb);
- if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
- free(in_name);
- free(out_name);
+ return err;
}
-static void do_gzip(int in_fd, char *in_name)
+static void do_gzip(int in_fd, char *arg)
{
- size_t len;
- char *out_name;
- FILE *in = xfdopen(in_fd, "r");
- gzFile out;
struct stat sb;
- int both_files, out_fd;
-
- out_name = (toys.optflags&FLAG_c) ? strdup("-") : xmprintf("%s.gz", in_name);
- both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
- if (both_files) xstat(in_name, &sb);
-
- if (!strcmp(out_name, "-")) out_fd = dup(1);
- else {
- out_fd = open(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
- both_files?sb.st_mode:0);
+ int len, out_fd = 0;
+ char *out_name = 0;
+
+ // Are we writing to stdout?
+ if (!in_fd || (toys.optflags&FLAG_c)) out_fd = 1;
+ if (isatty(in_fd)) {
+ if (!(toys.optflags&FLAG_f)) {
+ error_msg("%s:need -f to read TTY"+3*!!in_fd, arg);
+ return;
+ } else out_fd = 1;
}
- if (out_fd == -1) perror_exit("open %s", out_name);
-
- snprintf(toybuf, sizeof(toybuf), "w%d", TT.level);
- out = gzdopen(out_fd, toybuf);
- if (out == NULL) perror_exit("gzdopen %s", out_name);
- while ((len = fread(toybuf, 1, sizeof(toybuf), in)) > 0) {
- if (gzwrite(out, toybuf, len) != (int) len) gzerror_exit(out, "gzwrite");
+ // Are we reading file.gz to write to file?
+ if (!out_fd) {
+ if (fstat(in_fd, &sb)) {
+ perror_msg("%s", arg);
+ return;
+ }
+
+ if (!(toys.optflags&FLAG_d)) out_name = xmprintf("%s%s", arg, ".gz");
+ else {
+ // "gunzip x.gz" will decompress "x.gz" to "x".
+ if ((len = strlen(arg))<4 || strcmp(arg+len-3, ".gz")) {
+ error_msg("no .gz: %s", arg);
+ return;
+ }
+ out_name = xstrdup(arg);
+ out_name[len-3] = 0;
+ }
+
+ out_fd = xcreate(out_name,
+ O_CREAT|O_WRONLY|WARN_ONLY|(O_EXCL*!(toys.optflags&FLAG_f)), sb.st_mode);
+ if (out_fd == -1) return;
}
- if (ferror(in)) perror_exit("fread");
- if (fclose(in)) perror_exit("fclose");
- if (gzclose(out) != Z_OK) error_exit("gzclose");
- if (both_files) fix_time(out_name, &sb);
- if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
- free(out_name);
-}
+// if (CFG_TOYBOX_LIBZ)
+ if (do_zlib(in_fd, out_fd) && out_name) arg = out_name;
+ if (out_fd != 1) close(out_fd);
-static void do_gz(int fd, char *name)
-{
- if (toys.optflags&FLAG_d) do_gunzip(fd, name);
- else do_gzip(fd, name);
+ if (out_name) {
+ fix_time(out_name, &sb);
+ if (!(toys.optflags&FLAG_k)) if (unlink(arg)) perror_msg("unlink %s", arg);
+ free(out_name);
+ }
}
void gzip_main(void)
{
- int i = (toys.optflags&0x1ff);
-
- for (TT.level = (i == 0) ? 6 : 10; i; i >>= 1) --TT.level;
-
- // With no arguments, go from stdin to stdout.
- if (!*toys.optargs) toys.optflags |= FLAG_c;
+ for (TT.level = 0; TT.level<9; TT.level++)
+ if ((toys.optflags>>TT.level)&1) break;
+ if (!(TT.level = 9-TT.level)) TT.level = 6;
- loopfiles(toys.optargs, do_gz);
+ loopfiles(toys.optargs, do_gzip);
}
void gunzip_main(void)
diff --git a/toys/pending/iconv.c b/toys/pending/iconv.c
deleted file mode 100644
index 75ff6397..00000000
--- a/toys/pending/iconv.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* iconv.c - Convert character encoding
- *
- * Copyright 2014 Felix Janda <felix.janda@posteo.de>
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/iconv.html
-
-USE_ICONV(NEWTOY(iconv, "cst:f:", TOYFLAG_USR|TOYFLAG_BIN))
-
-config ICONV
- bool "iconv"
- default n
- depends on TOYBOX_ICONV
- help
- usage: iconv [-f FROM] [-t TO] [FILE...]
-
- Convert character encoding of files.
-
- -f convert from (default utf8)
- -t convert to (default utf8)
-*/
-
-#define FOR_iconv
-#include "toys.h"
-#include <iconv.h>
-
-GLOBALS(
- char *from;
- char *to;
-
- void *ic;
-)
-
-static void do_iconv(int fd, char *name)
-{
- char *outstart = toybuf+2048;
- size_t inleft = 0;
- int len = 1;
-
- do {
- size_t outleft = 2048;
- char *in = toybuf+inleft, *out = outstart;
-
- len = read(fd, in, 2048-inleft);
-
- if (len < 0) {
- perror_msg("read '%s'", name);
- return;
- }
- inleft += len;
-
- do {
- if (iconv(TT.ic, &in, &inleft, &out, &outleft) == -1
- && (errno == EILSEQ || (in == toybuf+inleft-len && errno == EINVAL)))
- {
- if (outleft) {
- // Skip first byte of illegal sequence to avoid endless loops
- *(out++) = *(in++);
- inleft--;
- }
- }
- xwrite(1, outstart, out-outstart);
- // Top off input buffer
- memmove(in, toybuf, inleft);
- } while (len < 1 && inleft);
- } while (len > 0);
-}
-
-void iconv_main(void)
-{
- TT.ic = iconv_open(TT.to ? TT.to : "utf8", TT.from ? TT.from : "utf8");
- if (TT.ic == (iconv_t)-1) error_exit("bad encoding");
- loopfiles(toys.optargs, do_iconv);
- if (CFG_TOYBOX_FREE) iconv_close(TT.ic);
-}
diff --git a/toys/pending/logger.c b/toys/pending/logger.c
deleted file mode 100644
index d94e6f88..00000000
--- a/toys/pending/logger.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/* logger.c - Log messages.
- *
- * Copyright 2013 Ilya Kuzmich <ilya.kuzmich@gmail.com>
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/logger.html
-
-USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
-
-config LOGGER
- bool "logger"
- depends on SYSLOGD
- default n
- help
- usage: logger [-s] [-t tag] [-p [facility.]priority] [message]
-
- Log message (or stdin) to syslog.
-*/
-
-#define FOR_logger
-#include "toys.h"
-
-GLOBALS(
- char *priority_arg;
- char *ident;
-)
-
-extern int logger_lookup(int where, char *key);
-
-void logger_main(void)
-{
- int facility = LOG_USER, priority = LOG_NOTICE;
- char *message = NULL;
-
- if (toys.optflags & FLAG_p) {
- char *sep = strchr(TT.priority_arg, '.');
-
- if (sep) {
- *sep = '\0';
- if ((facility = logger_lookup(0, TT.priority_arg)) == -1)
- error_exit("bad facility: %s", TT.priority_arg);
- TT.priority_arg = sep+1;
- }
-
- if ((priority = logger_lookup(1, TT.priority_arg)) == -1)
- error_exit("bad priority: %s", TT.priority_arg);
- }
-
- if (!(toys.optflags & FLAG_t)) {
- struct passwd *pw = getpwuid(geteuid());
-
- if (!pw) perror_exit("getpwuid");
- TT.ident = xstrdup(pw->pw_name);
- }
-
- if (toys.optc) {
- int length = 0, pos = 0;
-
- for (;*toys.optargs; toys.optargs++) {
- length += strlen(*(toys.optargs)) + 1; // plus one for the args spacing
- message = xrealloc(message, length + 1); // another one for the null byte
-
- sprintf(message + pos, "%s ", *toys.optargs);
- pos = length;
- }
- } else {
- toybuf[readall(0, toybuf, 4096-1)] = '\0';
- message = toybuf;
- }
-
- openlog(TT.ident, (toys.optflags & FLAG_s ? LOG_PERROR : 0) , facility);
- syslog(priority, "%s", message);
- closelog();
-}
diff --git a/toys/pending/syslogd.c b/toys/pending/syslogd.c
index 50d93189..87aa228c 100644
--- a/toys/pending/syslogd.c
+++ b/toys/pending/syslogd.c
@@ -33,7 +33,6 @@ config SYSLOGD
*/
#define FOR_syslogd
-#define SYSLOG_NAMES
#include "toys.h"
// UNIX Sockets for listening
diff --git a/toys/posix/iconv.c b/toys/posix/iconv.c
new file mode 100644
index 00000000..a31d713b
--- /dev/null
+++ b/toys/posix/iconv.c
@@ -0,0 +1,74 @@
+/* iconv.c - Convert character encoding
+ *
+ * Copyright 2014 Felix Janda <felix.janda@posteo.de>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/iconv.html
+ *
+ * Deviations from posix: no idea how to implement -l
+
+USE_ICONV(NEWTOY(iconv, "cst:f:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config ICONV
+ bool "iconv"
+ default y
+ depends on TOYBOX_ICONV
+ help
+ usage: iconv [-f FROM] [-t TO] [FILE...]
+
+ Convert character encoding of files.
+
+ -c Omit invalid chars
+ -f convert from (default utf8)
+ -t convert to (default utf8)
+*/
+
+#define FOR_iconv
+#include "toys.h"
+#include <iconv.h>
+
+GLOBALS(
+ char *from;
+ char *to;
+
+ void *ic;
+)
+
+static void do_iconv(int fd, char *name)
+{
+ char *outstart = toybuf+2048;
+ size_t outlen, inlen = 0;
+ int readlen = 1;
+
+ for (;;) {
+ char *in = toybuf, *out = outstart;
+
+ if (readlen && 0>(readlen = read(fd, in+inlen, 2048-inlen))) {
+ perror_msg("read '%s'", name);
+ return;
+ }
+ inlen += readlen;
+ if (!inlen) break;
+
+ outlen = 2048;
+ iconv(TT.ic, &in, &inlen, &out, &outlen);
+ if (in == toybuf) {
+ // Skip first byte of illegal sequence to avoid endless loops
+ if (toys.optflags & FLAG_c) in++;
+ else *(out++) = *(in++);
+ inlen--;
+ }
+ if (out != outstart) xwrite(1, outstart, out-outstart);
+ memmove(toybuf, in, inlen);
+ }
+}
+
+void iconv_main(void)
+{
+ if (!TT.to) TT.to = "utf8";
+ if (!TT.from) TT.from = "utf8";
+
+ if ((iconv_t)-1 == (TT.ic = iconv_open(TT.to, TT.from)))
+ perror_exit("%s/%s", TT.to, TT.from);
+ loopfiles(toys.optargs, do_iconv);
+ if (CFG_TOYBOX_FREE) iconv_close(TT.ic);
+}
diff --git a/toys/posix/logger.c b/toys/posix/logger.c
new file mode 100644
index 00000000..16262c9f
--- /dev/null
+++ b/toys/posix/logger.c
@@ -0,0 +1,70 @@
+/* logger.c - Log messages.
+ *
+ * Copyright 2013 Ilya Kuzmich <ilya.kuzmich@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/logger.html
+ *
+ * Deviations from posix: specified manner and format, defined implementation.
+
+USE_LOGGER(NEWTOY(logger, "st:p:", TOYFLAG_USR|TOYFLAG_BIN))
+
+config LOGGER
+ bool "logger"
+ default y
+ help
+ usage: logger [-s] [-t TAG] [-p [FACILITY.]PRIORITY] [message...]
+
+ Log message (or stdin) to syslog.
+
+ -s Also write message to stderr
+ -t Use TAG instead of username to identify message source
+ -p Specify PRIORITY with optional FACILITY. Default is "user.notice"
+*/
+
+#define FOR_logger
+#include "toys.h"
+
+GLOBALS(
+ char *priority;
+ char *ident;
+)
+
+void logger_main(void)
+{
+ int facility = LOG_USER, priority = LOG_NOTICE, len;
+ char *s1, *s2, **arg;
+ CODE *code;
+
+ if (!TT.ident) TT.ident = xstrdup(xgetpwuid(geteuid())->pw_name);
+ if (toys.optflags & FLAG_p) {
+ if (!(s1 = strchr(TT.priority, '.'))) s1 = TT.priority;
+ else {
+ *s1++ = 0;
+ for (code = facilitynames; code->c_name; code++)
+ if (!strcasecmp(TT.priority, code->c_name)) break;
+ if (!code->c_name) error_exit("bad facility: %s", TT.priority);
+ facility = code->c_val;
+ }
+
+ for (code = prioritynames; code->c_name; code++)
+ if (!strcasecmp(s1, code->c_name)) break;
+ if (!code->c_name) error_exit("bad priority: %s", s1);
+ }
+
+
+ if (toys.optc) {
+ for (len = 0, arg = toys.optargs; *arg; arg++) len += strlen(*arg)+1;
+ s1 = s2 = xmalloc(len);
+ for (arg = toys.optargs; *arg; arg++) {
+ if (arg != toys.optargs) *s2++ = ' ';
+ s2 = stpcpy(s2, *arg);
+ }
+ } else {
+ toybuf[readall(0, toybuf, sizeof(toybuf)-1)] = 0;
+ s1 = toybuf;
+ }
+
+ openlog(TT.ident, LOG_PERROR*!!(toys.optflags&FLAG_s), facility);
+ syslog(priority, "%s", s1);
+ closelog();
+}
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index 8d643576..7741dcd3 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -286,6 +286,8 @@ GLOBALS(
void (*show_process)(void *tb);
)
+/* Linked list of fields selected for display, in order, with :len and =title */
+
struct strawberry {
struct strawberry *next, *prev;
short which, len, reverse;
@@ -293,8 +295,15 @@ struct strawberry {
char forever[];
};
-/* The slot[] array is mostly populated from /proc/$PID/stat (kernel proc.txt
- * table 1-4) but we shift and repurpose fields, with the result being: */
+/* The function get_ps() reads all the data about one process, saving it in
+ * toybox as a struct carveup. Simple ps calls then pass toybuf directly to
+ * show_ps(), but features like sorting instead append a copy to a linked list
+ * for further processing once all processes have been read.
+ *
+ * struct carveup contains a slot[] array of 64 bit values, with the following
+ * data at each position in the array. Most is read from /proc/$PID/stat (see
+ * https://kernel.org/doc/Documentation/filesystems/proc.txt table 1-4) but
+ * we we replace several fields with don't use with other data. */
enum {
SLOT_pid, /*process id*/ SLOT_ppid, // parent process id
@@ -303,7 +312,7 @@ enum {
SLOT_flags, /*task flags*/ SLOT_minflt, // minor faults
SLOT_cminflt, /*minor faults+child*/ SLOT_majflt, // major faults
SLOT_cmajflt, /*major faults+child*/ SLOT_utime, // user+kernel jiffies
- SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child
+ SLOT_stime, /*kernel mode jiffies*/ SLOT_cutime, // utime+child utime
SLOT_cstime, /*stime+child*/ SLOT_priority, // priority level
SLOT_nice, /*nice level*/ SLOT_numthreads,// thread count
SLOT_vmlck, /*locked memory*/ SLOT_starttime, // jiffies after boot
@@ -319,7 +328,7 @@ enum {
SLOT_policy, /*man sched_setscheduler*/SLOT_blkioticks,// IO wait time
SLOT_gtime, /*guest jiffies of task*/ SLOT_cgtime, // gtime+child
SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
- SLOT_upticks, /*46-19 (divisor for %)*/ SLOT_argv0len, // argv[0] length
+ SLOT_upticks, /*uptime-starttime*/ SLOT_argv0len, // argv[0] length
SLOT_uptime, /*si.uptime @read time*/ SLOT_vsz, // Virtual mem Size
SLOT_rss2, /*Resident Set Size*/ SLOT_shr, // Shared memory
SLOT_rchar, /*All bytes read*/ SLOT_wchar, // All bytes written
@@ -328,24 +337,52 @@ enum {
SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
SLOT_pcy, /*Android sched policy*/
- SLOT_count
+ SLOT_count /* Size of array */
};
+/* In addition to slot[], carevup contains 6 string fields to display
+ command name, tty device, selinux label... They're stored one after the
+ other in str[] (separated by null terminators), and offset[] contains the
+ starting position of each string after the first (which is always 0). */
+
// Data layout in toybuf
struct carveup {
long long slot[SLOT_count]; // data (see enum above)
- unsigned short offset[6]; // offset of fields in str[] (skip name, always 0)
+ unsigned short offset[6]; // offset of fields in str[] (skip CMD, always 0)
char state;
- char str[]; // name, tty, command, wchan, attr, cmdline
+ char str[]; // CMD, TTY, WCHAN, LABEL, COMM, ARGS, NAME
};
+/* The typos[] array lists all the types understood by "ps -o", I.E all the
+ * columns ps and top know how to display. Each entry has:
+ *
+ * name: the column name, displayed at top and used to select column with -o
+ *
+ * width: the display width. Fields are padded to this width when displaying
+ * to a terminal (negative means right justified). Strings are truncated
+ * to fit, numerical fields are padded but not truncated (although
+ * the display code reclaims unused padding from later fields to try to
+ * get the overflow back).
+ *
+ * slot: which slot[] out of carveup. Negative means it's a string field.
+ * Setting bit |64 requests extra display/sort processing.
+ *
+ * The TAGGED_ARRAY plumbing produces an enum of indexes, the "tag" is the
+ * first string argument and the prefix is the first argument to TAGGED_ARRAY
+ * so in this case "NAME" becomes PS_NAME which is the offset into typos[]
+ * for that entry, and also _PS_NAME (the bit position, 1<<PS_NAME).
+ * We record active columns in TT.bits, ala:
+ *
+ * if (TT.bits & _PS_NAME) printf("-o included PS_NAME");
+ */
+
// TODO: Android uses -30 for LABEL, but ideally it would auto-size.
// 64|slot means compare as string when sorting
struct typography {
char *name;
signed char width, slot;
} static const typos[] = TAGGED_ARRAY(PS,
- // Numbers
+ // Numbers. (What's in slot[] is what's displayed, sorted numerically.)
{"PID", 5, SLOT_pid}, {"PPID", 5, SLOT_ppid}, {"PRI", 3, SLOT_priority},
{"NI", 3, SLOT_nice}, {"ADDR", 4+sizeof(long), SLOT_eip},
{"SZ", 5, SLOT_vsize}, {"RSS", 6, SLOT_rss}, {"PGID", 5, SLOT_pgrp},
@@ -354,31 +391,31 @@ struct typography {
{"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
+ // String fields (-1 is carveup->str, rest are str+offset[1-slot])
{"TTY", -8, -2}, {"WCHAN", -6, -3}, {"LABEL", -30, -4}, {"COMM", -27, -5},
{"NAME", -27, -7}, {"COMMAND", -27, -5}, {"CMDLINE", -27, -6},
{"ARGS", -27, -6}, {"CMD", -15, -1},
- // user/group
+ // user/group (may call getpwuid() or similar)
{"UID", 5, SLOT_uid}, {"USER", -12, 64|SLOT_uid}, {"RUID", 4, SLOT_ruid},
{"RUSER", -8, 64|SLOT_ruid}, {"GID", 8, SLOT_gid}, {"GROUP", -8, 64|SLOT_gid},
{"RGID", 4, SLOT_rgid}, {"RGROUP", -8, 64|SLOT_rgid},
- // clock displays
+ // clock displays (00:00:00)
{"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
{"TIME+", 9, SLOT_utime},
- // Percentage displays
+ // Percentage displays (fixed point, one decimal digit. 123 -> 12.3)
{"C", 1, SLOT_utime2}, {"%VSZ", 5, SLOT_vsize}, {"%MEM", 5, SLOT_rss},
{"%CPU", 4, SLOT_utime2},
- // human_readable
+ // human_readable (function human_readable() in lib, 1.23M, 1.4G, etc)
{"VIRT", 4, SLOT_vsz}, {"RES", 4, SLOT_rss2},
{"SHR", 4, SLOT_shr}, {"READ", 6, SLOT_rchar}, {"WRITE", 6, SLOT_wchar},
{"IO", 6, SLOT_iobytes}, {"DREAD", 6, SLOT_rbytes},
{"DWRITE", 6, SLOT_wbytes}, {"SWAP", 6, SLOT_swap}, {"DIO", 6, SLOT_diobytes},
- // Misc
+ // Misc (special cases)
{"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
{"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
);
@@ -643,8 +680,11 @@ static int get_ps(struct dirtree *new)
memset(slot, 0, sizeof(tb->slot));
tb->slot[SLOT_tid] = *slot = atol(new->name);
- if (TT.threadparent && TT.threadparent->extra)
- if (*slot == *(((struct carveup *)TT.threadparent->extra)->slot)) return 0;
+ if (TT.threadparent && TT.threadparent->extra) {
+ *slot = *(((struct carveup *)TT.threadparent->extra)->slot);
+ // Parent also shows up as a thread, discard duplicate
+ if (*slot == tb->slot[SLOT_tid]) return 0;
+ }
fd = dirtree_parentfd(new);
len = 2048;
@@ -1151,41 +1191,25 @@ static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
comma_args(arg ? arg : &def, fields, err, parse_ko);
}
-static void shared_main(void)
+void ps_main(void)
{
+ char **arg;
+ struct dirtree *dt;
+ char *not_o;
int i;
- TT.ticks = sysconf(_SC_CLK_TCK);
- if (!TT.width) {
- TT.width = 80;
- TT.height = 25;
- // If ps can't query terminal size pad to 80 but do -w
- if (toys.which->name[1] == 's') {
- if (!isatty(1) || !terminal_size(&TT.width, &TT.height))
- toys.optflags |= FLAG_w;
- }
- }
+ TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
- // find controlling tty, falling back to /dev/tty if none
- for (i = 0; !TT.tty && i<4; i++) {
+ if (-1 != (i = tty_fd())) {
struct stat st;
- int fd = i;
-
- if (i==3 && -1==(fd = open("/dev/tty", O_RDONLY))) break;
- if (isatty(fd) && !fstat(fd, &st)) TT.tty = st.st_rdev;
- if (i==3) close(fd);
+ if (!fstat(i, &st)) TT.tty = st.st_rdev;
}
-}
-void ps_main(void)
-{
- char **arg;
- struct dirtree *dt;
- char *not_o;
- int i;
-
- shared_main();
+ // If we can't query terminal size pad to 80 but do -w
+ TT.width = 80;
+ if (!isatty(1) || !terminal_size(&TT.width, 0))
+ toys.optflags |= FLAG_w;
if (toys.optflags&FLAG_w) TT.width = 99999;
// parse command line options other than -o
@@ -1341,14 +1365,6 @@ static int header_line(int line, int rev)
return line-1;
}
-static long long millitime(void)
-{
- struct timespec ts;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec*1000+ts.tv_nsec/1000000;
-}
-
static void top_common(
int (*filter)(long long *oslot, long long *nslot, int milis))
{
@@ -1604,15 +1620,23 @@ static void top_common(
static void top_setup(char *defo, char *defk)
{
TT.top.d *= 1000;
+
+ TT.ticks = sysconf(_SC_CLK_TCK); // units for starttime/uptime
+ TT.tty = tty_fd() != -1;
+
+ // Are we doing "batch" output or interactive?
if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
else {
+ // Grab starting time, make terminal raw, switch off cursor,
+ // set signal handler to put terminal/cursor back to normal at exit.
TT.time = millitime();
set_terminal(0, 1, 0);
sigatexit(tty_sigreset);
xsignal(SIGWINCH, generic_signal);
printf("\033[?25l\033[0m");
+ TT.width = 80;
+ TT.height = 25;
}
- shared_main();
comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
@@ -1752,7 +1776,7 @@ void pgrep_main(void)
TT.pgrep.self = getpid();
- // No signal names start with "L", so no need for "L: " parsing.
+ // No signal names start with "L", so no need for "L: " in optstr.
if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
error_exit("bad -L '%s'", TT.pgrep.L);
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c
index e7dd10b5..cac143cc 100644
--- a/toys/posix/xargs.c
+++ b/toys/posix/xargs.c
@@ -6,7 +6,7 @@
*
* TODO: Rich's whitespace objection, env size isn't fixed anymore.
-USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0[!0E]", TOYFLAG_USR|TOYFLAG_BIN))
config XARGS
bool "xargs"
@@ -99,8 +99,7 @@ static char *handle_entries(char *data, char **entry)
} else {
TT.bytes += sizeof(char *)+strlen(data)+1;
if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data;
- if (TT.max_entries && TT.entries >= TT.max_entries)
- return (char *)1;
+ if (TT.max_entries && TT.entries >= TT.max_entries) return data;
if (entry) entry[TT.entries] = data;
TT.entries++;
}