aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2018-08-06 12:07:19 -0700
committerandroid-build-merger <android-build-merger@google.com>2018-08-06 12:07:19 -0700
commit1ba78f42899c818e69625cc888bc5ae159391265 (patch)
treeee896d95eeddc0157ab61507181eeeb573b363b7
parent04dc9793f79210db6ef0f590ca1c85f1453aa8b2 (diff)
parent7f7fef7c0e9e3ab6b1217ec3f28797263d1e0454 (diff)
downloadtoybox-1ba78f42899c818e69625cc888bc5ae159391265.tar.gz
Merge remote-tracking branch 'toybox/master' into HEADandroid-o-mr1-iot-release-1.0.4android-o-mr1-iot-release-1.0.3
am: 7f7fef7c0e Change-Id: Ibcadc92493e6c5eb3e126f99311dda8a6e00d2d2
-rw-r--r--.config1
-rwxr-xr-xconfigure2
-rw-r--r--generated/config.h2
-rw-r--r--generated/flags.h36
-rw-r--r--generated/globals.h22
-rw-r--r--generated/help.h8
-rw-r--r--generated/newtoys.h3
-rw-r--r--lib/deflate.c (renamed from toys/pending/compress.c)234
-rw-r--r--lib/linestack.c2
-rw-r--r--lib/net.c2
-rw-r--r--lib/portability.c4
-rw-r--r--lib/toyflags.h6
-rw-r--r--scripts/config2help.c103
-rwxr-xr-xscripts/make.sh3
-rw-r--r--tests/file.test12
-rwxr-xr-xtests/grep.test8
-rwxr-xr-xtests/wc.test6
-rw-r--r--toys/net/ifconfig.c111
-rw-r--r--toys/net/netcat.c2
-rw-r--r--toys/pending/gzip.c64
-rw-r--r--toys/pending/modprobe.c6
-rw-r--r--toys/pending/wget.c2
-rw-r--r--toys/posix/file.c24
-rw-r--r--toys/posix/ps.c266
-rw-r--r--toys/posix/wc.c13
25 files changed, 548 insertions, 394 deletions
diff --git a/.config b/.config
index b62f8315..a6e2bd1b 100644
--- a/.config
+++ b/.config
@@ -20,6 +20,7 @@ CONFIG_TOYBOX_HELP=y
CONFIG_TOYBOX_I18N=y
CONFIG_TOYBOX_ICONV=y
CONFIG_TOYBOX_LIBCRYPTO=y
+CONFIG_TOYBOX_LIBZ=y
# CONFIG_TOYBOX_LSM_NONE is not set
# CONFIG_TOYBOX_MUSL_NOMMU_IS_BROKEN is not set
CONFIG_TOYBOX_NORECURSE=y
diff --git a/configure b/configure
index a9b88976..58e754ad 100755
--- a/configure
+++ b/configure
@@ -28,7 +28,7 @@ CFLAGS="$CFLAGS -funsigned-char"
# you call scripts/make.sh and friends directly.
[ -z "$CC" ] && CC=cc
-[ -z "$STRIP" ] & STRIP=strip
+[ -z "$STRIP" ] && STRIP=strip
# If HOSTCC needs CFLAGS or LDFLAGS, just add them to the variable
# ala HOSTCC="blah-cc --static"
diff --git a/generated/config.h b/generated/config.h
index d96b524b..d2f80ce7 100644
--- a/generated/config.h
+++ b/generated/config.h
@@ -26,6 +26,8 @@
#define USE_TOYBOX_ICONV(...) __VA_ARGS__
#define CFG_TOYBOX_LIBCRYPTO 1
#define USE_TOYBOX_LIBCRYPTO(...) __VA_ARGS__
+#define CFG_TOYBOX_LIBZ 1
+#define USE_TOYBOX_LIBZ(...) __VA_ARGS__
#define CFG_TOYBOX_LSM_NONE 0
#define USE_TOYBOX_LSM_NONE(...)
#define CFG_TOYBOX_MUSL_NOMMU_IS_BROKEN 0
diff --git a/generated/flags.h b/generated/flags.h
index 7dce8753..967a6fea 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -308,21 +308,6 @@
#undef FLAG_3
#endif
-// compress zcd9lrg[-cd][!zgLr]
-#undef OPTSTR_compress
-#define OPTSTR_compress "zcd9lrg[-cd][!zgLr]"
-#ifdef CLEANUP_compress
-#undef CLEANUP_compress
-#undef FOR_compress
-#undef FLAG_g
-#undef FLAG_r
-#undef FLAG_l
-#undef FLAG_9
-#undef FLAG_d
-#undef FLAG_c
-#undef FLAG_z
-#endif
-
// count
#undef OPTSTR_count
#define OPTSTR_count 0
@@ -1252,12 +1237,13 @@
#undef FLAG_Z
#endif
-// ifconfig ^?a ^?a
+// ifconfig ^?aS ^?aS
#undef OPTSTR_ifconfig
-#define OPTSTR_ifconfig "^?a"
+#define OPTSTR_ifconfig "^?aS"
#ifdef CLEANUP_ifconfig
#undef CLEANUP_ifconfig
#undef FOR_ifconfig
+#undef FLAG_S
#undef FLAG_a
#endif
@@ -3552,19 +3538,6 @@
#define FLAG_3 (1<<2)
#endif
-#ifdef FOR_compress
-#ifndef TT
-#define TT this.compress
-#endif
-#define FLAG_g (FORCED_FLAG<<0)
-#define FLAG_r (FORCED_FLAG<<1)
-#define FLAG_l (FORCED_FLAG<<2)
-#define FLAG_9 (FORCED_FLAG<<3)
-#define FLAG_d (FORCED_FLAG<<4)
-#define FLAG_c (FORCED_FLAG<<5)
-#define FLAG_z (FORCED_FLAG<<6)
-#endif
-
#ifdef FOR_count
#ifndef TT
#define TT this.count
@@ -4364,7 +4337,8 @@
#ifndef TT
#define TT this.ifconfig
#endif
-#define FLAG_a (1<<0)
+#define FLAG_S (1<<0)
+#define FLAG_a (1<<1)
#endif
#ifdef FOR_init
diff --git a/generated/globals.h b/generated/globals.h
index dd928da8..487aa081 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -490,27 +490,6 @@ struct brctl_data {
int sockfd;
};
-// toys/pending/compress.c
-
-struct compress_data {
- // Huffman codes: base offset and extra bits tables (length and distance)
- char lenbits[29], distbits[30];
- unsigned short lenbase[29], distbase[30];
- void *fixdisthuff, *fixlithuff;
-
- // CRC
- void (*crcfunc)(char *data, int len);
- unsigned crc;
-
- // Compressed data buffer
- char *data;
- unsigned pos, len;
- int infd, outfd;
-
- // Tables only used for deflation
- unsigned short *hashhead, *hashchain;
-};
-
// toys/pending/crond.c
struct crond_data {
@@ -1473,7 +1452,6 @@ extern union global_union {
struct arping_data arping;
struct bootchartd_data bootchartd;
struct brctl_data brctl;
- struct compress_data compress;
struct crond_data crond;
struct crontab_data crontab;
struct dd_data dd;
diff --git a/generated/help.h b/generated/help.h
index 4aaa4555..b6da53bc 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -124,7 +124,7 @@
#define HELP_microcom "usage: microcom [-s SPEED] [-X] DEVICE\n\nSimple serial console.\n\n-s Set baud rate to SPEED\n-X Ignore ^@ (send break) and ^] (exit).\n\n"
-#define HELP_ifconfig "usage: ifconfig [-a] [INTERFACE [ACTION...]]\n\nDisplay or configure network interface.\n\nWith no arguments, display active interfaces. First argument is interface\nto operate on, one argument by itself displays that interface.\n\n-a Show all interfaces, not just active ones\n\nAdditional arguments are actions to perform on the interface:\n\nADDRESS[/NETMASK] - set IPv4 address (1.2.3.4/5)\ndefault - unset ipv4 address\nadd|del ADDRESS[/PREFIXLEN] - add/remove IPv6 address (1111::8888/128)\nup - enable interface\ndown - disable interface\n\nnetmask|broadcast|pointopoint ADDRESS - set more IPv4 characteristics\nhw ether|infiniband ADDRESS - set LAN hardware address (AA:BB:CC...)\ntxqueuelen LEN - number of buffered packets before output blocks\nmtu LEN - size of outgoing packets (Maximum Transmission Unit)\n\nFlags you can set on an interface (or -remove by prefixing with -):\narp - don't use Address Resolution Protocol to map LAN routes\npromisc - don't discard packets that aren't to this LAN hardware address\nmulticast - force interface into multicast mode if the driver doesn't\nallmulti - promisc for multicast packets\n\nObsolete fields included for historical purposes:\nirq|io_addr|mem_start ADDR - micromanage obsolete hardware\noutfill|keepalive INTEGER - SLIP analog dialup line quality monitoring\nmetric INTEGER - added to Linux 0.9.10 with comment \"never used\", still true\n\n"
+#define HELP_ifconfig "usage: ifconfig [-aS] [INTERFACE [ACTION...]]\n\nDisplay or configure network interface.\n\nWith no arguments, display active interfaces. First argument is interface\nto operate on, one argument by itself displays that interface.\n\n-a All interfaces displayed, not just active ones\n-S Short view, one line per interface\n\nStandard ACTIONs to perform on an INTERFACE:\n\nADDR[/MASK] - set IPv4 address (1.2.3.4/5) and activate interface\nadd|del ADDR[/LEN] - add/remove IPv6 address (1111::8888/128)\nup|down - activate or deactivate interface\n\nAdvanced ACTIONs (default values usually suffice):\n\ndefault - remove IPv4 address\nnetmask ADDR - set IPv4 netmask via 255.255.255.0 instead of /24\ntxqueuelen LEN - number of buffered packets before output blocks\nmtu LEN - size of outgoing packets (Maximum Transmission Unit)\nbroadcast ADDR - Set broadcast address\npointopoint ADDR - PPP and PPPOE use this instead of \"route add default gw\"\nhw TYPE ADDR - set hardware (mac) address (type = ether|infiniband)\n\nFlags you can set on an interface (or -remove by prefixing with -):\n\narp - don't use Address Resolution Protocol to map LAN routes\npromisc - don't discard packets that aren't to this LAN hardware address\nmulticast - force interface into multicast mode if the driver doesn't\nallmulti - promisc for multicast packets\n\n"
#define HELP_ftpput "An ftpget that defaults to -s instead of -g\n\n"
@@ -418,10 +418,6 @@
#define HELP_crond "usage: crond [-fbS] [-l N] [-d N] [-L LOGFILE] [-c DIR]\n\nA daemon to execute scheduled commands.\n\n-b Background (default)\n-c crontab dir\n-d Set log level, log to stderr\n-f Foreground\n-l Set log level. 0 is the most verbose, default 8\n-S Log to syslog (default)\n-L Log to file\n\n"
-#define HELP_decompress "usage: compress [-zglrcd9] [FILE]\n\nCompress or decompress file (or stdin) using \"deflate\" algorithm.\n\n-c compress with -g gzip (default) -l zlib -r raw -z zip\n-d decompress (autodetects type)\n\n"
-
-#define HELP_compress "usage: compress [-zgLR19] [FILE]\n\nCompress or decompress file (or stdin) using \"deflate\" algorithm.\n\n-1 min compression\n-9 max compression (default)\n-g gzip (default)\n-L zlib\n-R raw\n-z zip\n\n"
-
#define HELP_brctl "usage: brctl COMMAND [BRIDGE [INTERFACE]]\n\nManage ethernet bridges\n\nCommands:\nshow Show a list of bridges\naddbr BRIDGE Create BRIDGE\ndelbr BRIDGE Delete BRIDGE\naddif BRIDGE IFACE Add IFACE to BRIDGE\ndelif BRIDGE IFACE Delete IFACE from BRIDGE\nsetageing BRIDGE TIME Set ageing time\nsetfd BRIDGE TIME Set bridge forward delay\nsethello BRIDGE TIME Set hello time\nsetmaxage BRIDGE TIME Set max message age\nsetpathcost BRIDGE PORT COST Set path cost\nsetportprio BRIDGE PORT PRIO Set port priority\nsetbridgeprio BRIDGE PRIO Set bridge priority\nstp BRIDGE [1/yes/on|0/no/off] STP on/off\n\n"
#define HELP_bootchartd "usage: bootchartd {start [PROG ARGS]}|stop|init\n\nCreate /var/log/bootlog.tgz with boot chart data\n\nstart: start background logging; with PROG, run PROG,\n then kill logging with USR1\nstop: send USR1 to all bootchartd processes\ninit: start background logging; stop when getty/xdm is seen\n (for init scripts)\n\nUnder PID 1: as init, then exec $bootchart_init, /init, /sbin/init\n\n"
@@ -492,7 +488,7 @@
#define HELP_top "usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-m LINES] [-d SECONDS] [-p PID,] [-u USER,]\n\nShow process activity in real time.\n\n-H Show threads\n-k Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)\n-o Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)\n-O Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)\n-s Sort by field number (1-X, default 9)\n-b Batch mode (no tty)\n-d Delay SECONDS between each cycle (default 3)\n-m Maximum number of tasks to show\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_ps "usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]\n\nList processes.\n\nWhich processes to show (-gGuUpPt selections may be comma separated lists):\n\n-A all -a has terminal not session leader\n-d All but session leaders -e synonym for -A\n-g in GROUPs -G in real GROUPs (before sgid)\n-p PIDs (--pid) -P Parent PIDs (--ppid)\n-s In session IDs -t Attached to selected TTYs\n-T Show threads also -u Owned by selected USERs\n-U real USERs (before suid)\n\nOutput modifiers:\n\n-k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths\n-n Show numeric USER and GROUP -w Wide output (don't truncate fields)\n\nWhich FIELDs to show. (Default = -o PID,TTY,TIME,CMD)\n\n-f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)\n-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)\n-o Output FIELDs instead of defaults, each with optional :size and =title\n-O Add FIELDS to defaults\n-Z Include LABEL\n\nCommand line -o fields:\n\n ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])\n CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)\n COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)\n\nProcess attribute -o FIELDs:\n\n ADDR Instruction pointer BIT Is this process 32 or 64 bits\n CPU Which processor running on ETIME Elapsed time since PID start\n F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id\n GROUP Group name LABEL Security label\n MAJFL Major page faults MINFL Minor page faults\n NI Niceness (lower is faster)\n PCPU Percentage of CPU time used PCY Android scheduling policy\n PGID Process Group ID\n PID Process ID PPID Parent Process ID\n PRI Priority (higher is faster) PSR Processor last executed on\n RGID Real (before sgid) group ID RGROUP Real (before sgid) group name\n RSS Resident Set Size (pages in use) RTPRIO Realtime priority\n RUID Real (before suid) user ID RUSER Real (before suid) user name\n S Process state:\n R (running) S (sleeping) D (device I/O) T (stopped) t (traced)\n Z (zombie) X (deader) x (dead) K (wakekill) W (waking)\n SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)\n STAT Process state (S) plus:\n < high priority N low priority L locked memory\n s session leader + foreground l multithreaded\n STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)\n SZ Memory Size (4k pages needed to completely swap out process)\n TCNT Thread count TID Thread ID\n TIME CPU time consumed TTY Controlling terminal\n UID User id USER User name\n VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory\n WCHAN Wait location in kernel\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 (-gGuUpPt selections may be comma separated lists):\n\n-A all -a has terminal not session leader\n-d All but session leaders -e synonym for -A\n-g in GROUPs -G in real GROUPs (before sgid)\n-p PIDs (--pid) -P Parent PIDs (--ppid)\n-s In session IDs -t Attached to selected TTYs\n-T Show threads also -u Owned by selected USERs\n-U real USERs (before suid)\n\nOutput modifiers:\n\n-k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths\n-n Show numeric USER and GROUP -w Wide output (don't truncate fields)\n\nWhich FIELDs to show. (-o HELP for list, default = -o PID,TTY,TIME,CMD)\n\n-f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)\n-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)\n-o Output FIELDs instead of defaults, each with optional :size and =title\n-O Add FIELDS to defaults\n-Z Include LABEL\n\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 7a1b2821..4dda97a3 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -32,7 +32,6 @@ USE_CKSUM(NEWTOY(cksum, "HIPLN", TOYFLAG_BIN))
USE_CLEAR(NEWTOY(clear, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_CMP(NEWTOY(cmp, "<2>2ls[!ls]", TOYFLAG_USR|TOYFLAG_BIN))
USE_COMM(NEWTOY(comm, "<2>2321", TOYFLAG_USR|TOYFLAG_BIN))
-USE_COMPRESS(NEWTOY(compress, "zcd9lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN))
USE_COUNT(NEWTOY(count, NULL, TOYFLAG_USR|TOYFLAG_BIN))
USE_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"D(parents)RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
USE_CPIO(NEWTOY(cpio, "(no-preserve-owner)(trailer)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
@@ -106,7 +105,7 @@ 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))
-USE_IFCONFIG(NEWTOY(ifconfig, "^?a", TOYFLAG_SBIN))
+USE_IFCONFIG(NEWTOY(ifconfig, "^?aS", TOYFLAG_SBIN))
USE_INIT(NEWTOY(init, "", TOYFLAG_SBIN))
USE_INOTIFYD(NEWTOY(inotifyd, "<2", TOYFLAG_USR|TOYFLAG_BIN))
USE_INSMOD(NEWTOY(insmod, "<1", TOYFLAG_SBIN|TOYFLAG_NEEDROOT))
diff --git a/toys/pending/compress.c b/lib/deflate.c
index bc69a035..2a6274e7 100644
--- a/toys/pending/compress.c
+++ b/lib/deflate.c
@@ -1,72 +1,34 @@
-/* compress.c - deflate/inflate code for zip, gzip, zlib, and raw
+/* deflate.c - deflate/inflate code for gzip and friends
*
* Copyright 2014 Rob Landley <rob@landley.net>
*
- * The inflate/deflate code lives here, so the various things that use it
- * either live here or call these commands to pipe data through them.
- *
- * Divergence from posix: replace obsolete/patented "compress" with mutiplexer.
- * (gzip already replaces "uncompress".)
- *
* See RFCs 1950 (zlib), 1951 (deflate), and 1952 (gzip)
* LSB 4.1 has gzip, gunzip, and zcat
+ *
* TODO: zip -d DIR -x LIST -list -quiet -no overwrite -overwrite -p to stdout
+ */
-// Accept many different kinds of command line argument.
-// Leave Lrg at end so flag values line up.
-
-USE_COMPRESS(NEWTOY(compress, "zcd9lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN))
-
-//zip unzip gzip gunzip zcat
-
-config COMPRESS
- bool "compress"
- default n
- help
- usage: compress [-zgLR19] [FILE]
-
- Compress or decompress file (or stdin) using "deflate" algorithm.
-
- -1 min compression
- -9 max compression (default)
- -g gzip (default)
- -L zlib
- -R raw
- -z zip
-
-config DECOMPRESS
- bool "decompress"
- default n
- help
- usage: compress [-zglrcd9] [FILE]
-
- Compress or decompress file (or stdin) using "deflate" algorithm.
-
- -c compress with -g gzip (default) -l zlib -r raw -z zip
- -d decompress (autodetects type)
-*/
-
-#define FOR_compress
#include "toys.h"
-GLOBALS(
+struct deflate {
// Huffman codes: base offset and extra bits tables (length and distance)
char lenbits[29], distbits[30];
unsigned short lenbase[29], distbase[30];
void *fixdisthuff, *fixlithuff;
// CRC
- void (*crcfunc)(char *data, int len);
- unsigned crc;
+ void (*crcfunc)(struct deflate *dd, char *data, int len);
+ unsigned crctable[256], crc;
- // Compressed data buffer
- char *data;
- unsigned pos, len;
- int infd, outfd;
// Tables only used for deflation
unsigned short *hashhead, *hashchain;
-)
+
+ // Compressed data buffer (extra space malloced at end)
+ unsigned pos, len;
+ int infd, outfd;
+ char data[];
+};
// little endian bit buffer
struct bitbuf {
@@ -86,6 +48,7 @@ struct bitbuf *bitbuf_init(int fd, int size)
}
// Advance bitpos without the overhead of recording bits
+// Loads more data when input buffer empty
void bitbuf_skip(struct bitbuf *bb, int bits)
{
int pos = bb->bitpos + bits, len = bb->len << 3;
@@ -139,7 +102,7 @@ void bitbuf_flush(struct bitbuf *bb)
{
if (!bb->bitpos) return;
- xwrite(bb->fd, bb->buf, (bb->bitpos+7)/8);
+ xwrite(bb->fd, bb->buf, (bb->bitpos+7)>>3);
memset(bb->buf, 0, bb->max);
bb->bitpos = 0;
}
@@ -164,25 +127,26 @@ void bitbuf_put(struct bitbuf *bb, int data, int len)
}
}
-static void output_byte(char sym)
+static void output_byte(struct deflate *dd, char sym)
{
- int pos = TT.pos++ & 32767;
+ int pos = dd->pos++ & 32767;
- TT.data[pos] = sym;
+ dd->data[pos] = sym;
if (pos == 32767) {
- xwrite(TT.outfd, TT.data, 32768);
- if (TT.crcfunc) TT.crcfunc(TT.data, 32768);
+ xwrite(dd->outfd, dd->data, 32768);
+ if (dd->crcfunc) dd->crcfunc(dd, dd->data, 32768);
}
}
// Huffman coding uses bits to traverse a binary tree to a leaf node,
// By placing frequently occurring symbols at shorter paths, frequently
// used symbols may be represented in fewer bits than uncommon symbols.
+// (length[0] isn't used but code's clearer if it's there.)
struct huff {
- unsigned short length[16];
- unsigned short symbol[288];
+ unsigned short length[16]; // How many symbols have this bit length?
+ unsigned short symbol[288]; // sorted by bit length, then ascending order
};
// Create simple huffman tree from array of bit lengths.
@@ -199,10 +163,10 @@ static void len2huff(struct huff *huff, char bitlen[], int len)
memset(huff, 0, sizeof(struct huff));
for (i = 0; i<len; i++) huff->length[bitlen[i]]++;
- // Sort symbols by bit length. (They'll remain sorted by symbol within that.)
+ // Sort symbols by bit length, then symbol. Get list of starting positions
+ // for each group, then write each symbol to next position within its group.
*huff->length = *offset = 0;
for (i = 1; i<16; i++) offset[i] = offset[i-1] + huff->length[i-1];
-
for (i = 0; i<len; i++) if (bitlen[i]) huff->symbol[offset[bitlen[i]]++] = i;
}
@@ -226,10 +190,10 @@ static unsigned huff_and_puff(struct bitbuf *bb, struct huff *huff)
return huff->symbol[start + offset];
}
-// Decompress deflated data from bitbuf to TT.outfd.
-static void inflate(struct bitbuf *bb)
+// Decompress deflated data from bitbuf to dd->outfd.
+static void inflate(struct deflate *dd, struct bitbuf *bb)
{
- TT.crc = ~0;
+ dd->crc = ~0;
// repeat until spanked
for (;;) {
int final, type;
@@ -257,7 +221,7 @@ static void inflate(struct bitbuf *bb)
// dump bytes until done or end of current bitbuf contents
if (bblen > len) bblen = len;
pos = bblen;
- while (pos--) output_byte(*(p++));
+ while (pos--) output_byte(dd, *(p++));
bitbuf_skip(bb, bblen << 3);
len -= bblen;
}
@@ -308,8 +272,8 @@ static void inflate(struct bitbuf *bb)
// Static huffman codes
} else {
- lithuff = TT.fixlithuff;
- disthuff = TT.fixdisthuff;
+ lithuff = dd->fixlithuff;
+ disthuff = dd->fixdisthuff;
}
// Use huffman tables to decode block of compressed symbols
@@ -317,19 +281,19 @@ static void inflate(struct bitbuf *bb)
int sym = huff_and_puff(bb, lithuff);
// Literal?
- if (sym < 256) output_byte(sym);
+ if (sym < 256) output_byte(dd, sym);
// Copy range?
else if (sym > 256) {
int len, dist;
sym -= 257;
- len = TT.lenbase[sym] + bitbuf_get(bb, TT.lenbits[sym]);
+ len = dd->lenbase[sym] + bitbuf_get(bb, dd->lenbits[sym]);
sym = huff_and_puff(bb, disthuff);
- dist = TT.distbase[sym] + bitbuf_get(bb, TT.distbits[sym]);
- sym = TT.pos & 32767;
+ dist = dd->distbase[sym] + bitbuf_get(bb, dd->distbits[sym]);
+ sym = dd->pos & 32767;
- while (len--) output_byte(TT.data[(TT.pos-dist) & 32767]);
+ while (len--) output_byte(dd, dd->data[(dd->pos-dist) & 32767]);
// End of block
} else break;
@@ -340,28 +304,28 @@ static void inflate(struct bitbuf *bb)
if (final) break;
}
- if (TT.pos & 32767) {
- xwrite(TT.outfd, TT.data, TT.pos & 32767);
- if (TT.crcfunc) TT.crcfunc(TT.data, TT.pos & 32767);
+ if (dd->pos & 32767) {
+ xwrite(dd->outfd, dd->data, dd->pos&32767);
+ if (dd->crcfunc) dd->crcfunc(dd, dd->data, dd->pos&32767);
}
}
-// Deflate from TT.infd to bitbuf
-// For deflate, TT.len = input read, TT.pos = input consumed
-static void deflate(struct bitbuf *bb)
+// Deflate from dd->infd to bitbuf
+// For deflate, dd->len = input read, dd->pos = input consumed
+static void deflate(struct deflate *dd, struct bitbuf *bb)
{
- char *data = TT.data;
+ char *data = dd->data;
int len, final = 0;
- TT.crc = ~0;
+ dd->crc = ~0;
while (!final) {
// Read next half-window of data if we haven't hit EOF yet.
- len = readall(TT.infd, data+(TT.len&32768), 32768);
+ len = readall(dd->infd, data+(dd->len&32768), 32768);
if (len < 0) perror_exit("read"); // todo: add filename
if (len != 32768) final++;
- if (TT.crcfunc) TT.crcfunc(data+(TT.len&32768), len);
- // TT.len += len; crcfunc advances len
+ if (dd->crcfunc) dd->crcfunc(dd, data+(dd->len&32768), len);
+ // dd->len += len; crcfunc advances len TODO
// store block as literal
bitbuf_put(bb, final, 1);
@@ -372,57 +336,60 @@ static void deflate(struct bitbuf *bb)
bitbuf_put(bb, 0xffff & ~len, 16);
// repeat until spanked
- while (TT.pos != TT.len) {
- unsigned pos = TT.pos & 65535;
+ while (dd->pos != dd->len) {
+ unsigned pos = dd->pos&65535;
bitbuf_put(bb, data[pos], 8);
// need to refill buffer?
- if (!(32767 & ++TT.pos) && !final) break;
+ if (!(32767 & ++dd->pos) && !final) break;
}
}
bitbuf_flush(bb);
}
// Allocate memory for deflate/inflate.
-static void init_deflate(int compress)
+static struct deflate *init_deflate(int compress)
{
int i, n = 1;
+ struct deflate *dd = xmalloc(sizeof(struct deflate)+32768*(compress ? 4 : 1));
- // compress needs 64k data and 32k each for hashhead and hashchain.
- // decompress just needs 32k data.
- TT.data = xmalloc(32768*(compress ? 4 : 1));
+// TODO sizeof and order of these?
+ // decompress needs 32k history, compress adds 64k hashhead and 32k hashchain
if (compress) {
- TT.hashhead = (unsigned short *)(TT.data + 65536);
- TT.hashchain = (unsigned short *)(TT.data + 65536 + 32768);
+ dd->hashhead = (unsigned short *)(dd->data+65536);
+ dd->hashchain = (unsigned short *)(dd->data+65536+32768);
}
// Calculate lenbits, lenbase, distbits, distbase
- *TT.lenbase = 3;
- for (i = 0; i<sizeof(TT.lenbits)-1; i++) {
+ *dd->lenbase = 3;
+ for (i = 0; i<sizeof(dd->lenbits)-1; i++) {
if (i>4) {
if (!(i&3)) {
- TT.lenbits[i]++;
+ dd->lenbits[i]++;
n <<= 1;
}
if (i == 27) n--;
- else TT.lenbits[i+1] = TT.lenbits[i];
+ else dd->lenbits[i+1] = dd->lenbits[i];
}
- TT.lenbase[i+1] = n + TT.lenbase[i];
+ dd->lenbase[i+1] = n + dd->lenbase[i];
}
n = 0;
- for (i = 0; i<sizeof(TT.distbits); i++) {
- TT.distbase[i] = 1<<n;
- if (i) TT.distbase[i] += TT.distbase[i-1];
+ for (i = 0; i<sizeof(dd->distbits); i++) {
+ dd->distbase[i] = 1<<n;
+ if (i) dd->distbase[i] += dd->distbase[i-1];
if (i>3 && !(i&1)) n++;
- TT.distbits[i] = n;
+ dd->distbits[i] = n;
}
+// TODO layout and lifetime of this?
// Init fixed huffman tables
for (i=0; i<288; i++) toybuf[i] = 8 + (i>143) - ((i>255)<<1) + (i>279);
- len2huff(TT.fixlithuff = ((struct huff *)toybuf)+3, toybuf, 288);
+ len2huff(dd->fixlithuff = ((struct huff *)toybuf)+3, toybuf, 288);
memset(toybuf, 5, 30);
- len2huff(TT.fixdisthuff = ((struct huff *)toybuf)+4, toybuf, 30);
+ len2huff(dd->fixdisthuff = ((struct huff *)toybuf)+4, toybuf, 30);
+
+ return dd;
}
// Return true/false whether we consumed a gzip header.
@@ -444,70 +411,75 @@ static int is_gzip(struct bitbuf *bb)
return 1;
}
-void gzip_crc(char *data, int len)
+void gzip_crc(struct deflate *dd, char *data, int len)
{
int i;
- unsigned crc, *crc_table = (unsigned *)(toybuf+sizeof(toybuf)-1024);
+ unsigned crc, *crc_table = dd->crctable;
- crc = TT.crc;
+ crc = dd->crc;
for (i=0; i<len; i++) crc = crc_table[(crc^data[i])&0xff] ^ (crc>>8);
- TT.crc = crc;
- TT.len += len;
+ dd->crc = crc;
+ dd->len += len;
}
-static void do_gzip(int fd, char *name)
+long long gzip_fd(int infd, int outfd)
{
- struct bitbuf *bb = bitbuf_init(1, sizeof(toybuf));
+ struct bitbuf *bb = bitbuf_init(outfd, 4096);
+ struct deflate *dd = init_deflate(1);
+ long long rc;
// Header from RFC 1952 section 2.2:
// 2 ID bytes (1F, 8b), gzip method byte (8=deflate), FLAG byte (none),
// 4 byte MTIME (zeroed), Extra Flags (2=maximum compression),
// Operating System (FF=unknown)
- TT.infd = fd;
+ dd->infd = infd;
xwrite(bb->fd, "\x1f\x8b\x08\0\0\0\0\0\x02\xff", 10);
- // Use last 1k of toybuf for little endian crc table
- crc_init((unsigned *)(toybuf+sizeof(toybuf)-1024), 1);
- TT.crcfunc = gzip_crc;
+ // Little endian crc table
+ crc_init(dd->crctable, 1);
+ dd->crcfunc = gzip_crc;
- deflate(bb);
+ deflate(dd, bb);
// tail: crc32, len32
bitbuf_put(bb, 0, (8-bb->bitpos)&7);
- bitbuf_put(bb, ~TT.crc, 32);
- bitbuf_put(bb, TT.len, 32);
+ bitbuf_put(bb, ~dd->crc, 32);
+ bitbuf_put(bb, dd->len, 32);
+ rc = dd->len;
bitbuf_flush(bb);
free(bb);
+ free(dd);
+
+ return rc;
}
-static void do_zcat(int fd, char *name)
+long long gunzip_fd(int infd, int outfd)
{
- struct bitbuf *bb = bitbuf_init(fd, sizeof(toybuf));
+ struct bitbuf *bb = bitbuf_init(infd, 4096);
+ struct deflate *dd = init_deflate(0);
+ long long rc;
if (!is_gzip(bb)) error_exit("not gzip");
- TT.outfd = 1;
+ dd->outfd = outfd;
- // Use last 1k of toybuf for little endian crc table
- crc_init((unsigned *)(toybuf+sizeof(toybuf)-1024), 1);
- TT.crcfunc = gzip_crc;
+ // Little endian crc table
+ crc_init(dd->crctable, 1);
+ dd->crcfunc = gzip_crc;
- inflate(bb);
+ inflate(dd, bb);
// tail: crc32, len32
bitbuf_skip(bb, (8-bb->bitpos)&7);
- if (~TT.crc != bitbuf_get(bb, 32) || TT.len != bitbuf_get(bb, 32))
+ if (~dd->crc != bitbuf_get(bb, 32) || dd->len != bitbuf_get(bb, 32))
error_exit("bad crc");
- free(bb);
-}
-// Parse many different kinds of command line argument:
+ rc = dd->len;
+ free(bb);
+ free(dd);
-void compress_main(void)
-{
- // todo: this
- printf("hello world");
+ return rc;
}
diff --git a/lib/linestack.c b/lib/linestack.c
index 91dec564..b533c1f8 100644
--- a/lib/linestack.c
+++ b/lib/linestack.c
@@ -117,7 +117,7 @@ int crunch_str(char **str, int width, FILE *out, char *escmore,
}
-// standard escapes: ^X if <32, <XX> if invliad UTF8, U+XXXX if UTF8 !iswprint()
+// standard escapes: ^X if <32, <XX> if invalid UTF8, U+XXXX if UTF8 !iswprint()
int crunch_escape(FILE *out, int cols, int wc)
{
char buf[8];
diff --git a/lib/net.c b/lib/net.c
index 846be31f..8969306b 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -103,7 +103,7 @@ int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_ti
}
}
-// Return converted numeric address in libbuf
+// Return converted ipv4/ipv6 numeric address in libbuf
char *ntop(struct sockaddr *sa)
{
void *addr;
diff --git a/lib/portability.c b/lib/portability.c
index 1415141f..2ba3d29f 100644
--- a/lib/portability.c
+++ b/lib/portability.c
@@ -35,8 +35,8 @@ void xgetrandom(void *buf, unsigned buflen, unsigned flags)
int fd;
#if CFG_TOYBOX_GETRANDOM
- if (buflen != getrandom(buf, buflen, flags))
- if (!CFG_TOYBOX_ON_ANDROID || errno!=ENOSYS) perror_exit("getrandom");
+ if (buflen == getrandom(buf, buflen, flags)) return;
+ if (!CFG_TOYBOX_ON_ANDROID || errno!=ENOSYS) perror_exit("getrandom");
#endif
fd = xopen(flags ? "/dev/random" : "/dev/urandom", O_RDONLY);
diff --git a/lib/toyflags.h b/lib/toyflags.h
index 859cca2e..bec8078b 100644
--- a/lib/toyflags.h
+++ b/lib/toyflags.h
@@ -6,6 +6,8 @@
// Flags describing command behavior.
+// Where to install (toybox --long outputs absolute paths to commands)
+// If no location bits set, command not listed in "toybox" command's output.
#define TOYFLAG_USR (1<<0)
#define TOYFLAG_BIN (1<<1)
#define TOYFLAG_SBIN (1<<2)
@@ -18,8 +20,8 @@
#define TOYFLAG_UMASK (1<<5)
// This command runs as root.
-#define TOYFLAG_STAYROOT (1<<6)
-#define TOYFLAG_NEEDROOT (1<<7)
+#define TOYFLAG_STAYROOT (1<<6) // Don't drop suid root before running cmd_main
+#define TOYFLAG_NEEDROOT (1<<7) // Refuse to run if real uid != 0
#define TOYFLAG_ROOTONLY (TOYFLAG_STAYROOT|TOYFLAG_NEEDROOT)
// Call setlocale to listen to environment variables.
diff --git a/scripts/config2help.c b/scripts/config2help.c
index d2389392..be53547d 100644
--- a/scripts/config2help.c
+++ b/scripts/config2help.c
@@ -8,6 +8,7 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -18,15 +19,95 @@
#include <poll.h>
#include <sys/socket.h>
-struct statvfs {int i;};
-#include "lib/portability.h"
-#include "lib/lib.h"
+/****************** functions copied from lib/*.c ********************/
-// Humor toys.h (lie through our teeth, C's linker doesn't care).
-char toys[4096], libbuf[4096], toybuf[4096];
-void show_help(FILE *out) {;}
-void toy_exec(char *argv[]) {;}
-void toy_init(void *which, char *argv[]) {;}
+struct double_list {
+ struct double_list *next, *prev;
+ char *data;
+};
+
+// Die unless we can allocate memory.
+void *xmalloc(size_t size)
+{
+ void *ret = malloc(size);
+ if (!ret) {
+ fprintf(stderr, "xmalloc(%ld)", (long)size);
+ exit(1);
+ }
+
+ return ret;
+}
+
+// Die unless we can allocate enough space to sprintf() into.
+char *xmprintf(char *format, ...)
+{
+ va_list va, va2;
+ int len;
+ char *ret;
+
+ va_start(va, format);
+ va_copy(va2, va);
+
+ // How long is it?
+ len = vsnprintf(0, 0, format, va);
+ len++;
+ va_end(va);
+
+ // Allocate and do the sprintf()
+ ret = xmalloc(len);
+ vsnprintf(ret, len, format, va2);
+ va_end(va2);
+
+ return ret;
+}
+
+// Die unless we can open/create a file, returning FILE *.
+FILE *xfopen(char *path, char *mode)
+{
+ FILE *f = fopen(path, mode);
+ if (!f) {
+ fprintf(stderr, "No file %s", path);
+ exit(1);
+ }
+ return f;
+}
+
+void *dlist_pop(void *list)
+{
+ struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist;
+
+ if (dlist->next == dlist) *pdlist = 0;
+ else {
+ dlist->next->prev = dlist->prev;
+ dlist->prev->next = *pdlist = dlist->next;
+ }
+
+ return dlist;
+}
+
+void dlist_add_nomalloc(struct double_list **list, struct double_list *new)
+{
+ if (*list) {
+ new->next = *list;
+ new->prev = (*list)->prev;
+ (*list)->prev->next = new;
+ (*list)->prev = new;
+ } else *list = new->next = new->prev = new;
+}
+
+
+// Add an entry to the end of a doubly linked list
+struct double_list *dlist_add(struct double_list **list, char *data)
+{
+ struct double_list *new = xmalloc(sizeof(struct double_list));
+
+ new->data = data;
+ dlist_add_nomalloc(list, new);
+
+ return new;
+}
+
+/****************** end copies of lib/*.c *************/
// Parse config files into data structures.
@@ -167,7 +248,7 @@ void parse(char *filename)
// source or config keyword at left edge?
if (*line && !isspace(*line)) {
if ((s = keyword("config", line))) {
- new = xzalloc(sizeof(struct symbol));
+ memset(new = xmalloc(sizeof(struct symbol)), 0, sizeof(struct symbol));
new->next = sym;
new->name = s;
sym = new;
@@ -399,7 +480,9 @@ int main(int argc, char *argv[])
if (sym->help) {
int i;
- char *s = xstrdup(sym->name);
+ char *s;
+
+ strcpy(s = xmalloc(strlen(sym->name)+1), sym->name);
for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
printf("#define HELP_%s \"", s);
diff --git a/scripts/make.sh b/scripts/make.sh
index 72d53709..a268eafd 100755
--- a/scripts/make.sh
+++ b/scripts/make.sh
@@ -253,8 +253,7 @@ fi
if [ generated/config2help -ot scripts/config2help.c ]
then
- do_loudly $HOSTCC scripts/config2help.c -I . lib/xwrap.c lib/llist.c \
- lib/lib.c lib/portability.c -o generated/config2help || exit 1
+ do_loudly $HOSTCC scripts/config2help.c -o generated/config2help || exit 1
fi
if isnewer generated/help.h generated/Config.in
then
diff --git a/tests/file.test b/tests/file.test
index 93805569..2d8f04b6 100644
--- a/tests/file.test
+++ b/tests/file.test
@@ -10,6 +10,7 @@ echo "#! /bin/bash" > bash.script2
echo "#! /usr/bin/env python" > env.python.script
echo "Hello, world!" > ascii
echo "cafebabe000000310000" | xxd -r -p > java.class
+echo "6465780a3033350038ca8f6ce910f94e" | xxd -r -p > android.dex
ln -s java.class symlink
testing "empty" "file empty" "empty: empty\n" "" ""
@@ -17,15 +18,16 @@ testing "bash.script" "file bash.script" "bash.script: /bin/bash script\n" "" ""
testing "bash.script with spaces" "file bash.script2" "bash.script2: /bin/bash script\n" "" ""
testing "env python script" "file env.python.script" "env.python.script: python script\n" "" ""
testing "ascii" "file ascii" "ascii: ASCII text\n" "" ""
-testing "java class" "file java.class" "java.class: Java class file, version 49.0\n" "" ""
+testing "java class" "file java.class" "java.class: Java class file, version 49.0 (Java 1.5)\n" "" ""
+testing "Android .dex" "file android.dex" "android.dex: Android dex file, version 035\n" "" ""
testing "symlink" "file symlink" "symlink: symbolic link\n" "" ""
testing "symlink -h" "file -h symlink" "symlink: symbolic link\n" "" ""
-testing "symlink -L" "file -L symlink" "symlink: Java class file, version 49.0\n" "" ""
+testing "symlink -L" "file -L symlink" "symlink: Java class file, version 49.0 (Java 1.5)\n" "" ""
-testing "- pipe" "cat java.class | file -" "-: Java class file, version 49.0\n" "" ""
-testing "- redirect" "file - <java.class" "-: Java class file, version 49.0\n" "" ""
+testing "- pipe" "cat java.class | file -" "-: Java class file, version 49.0 (Java 1.5)\n" "" ""
+testing "- redirect" "file - <java.class" "-: Java class file, version 49.0 (Java 1.5)\n" "" ""
testing "/dev/zero" "file /dev/zero" "/dev/zero: character special\n" "" ""
testing "- </dev/zero" "file - </dev/zero" "-: data\n" "" ""
-rm empty bash.script bash.script2 env.python.script ascii java.class
+rm empty bash.script bash.script2 env.python.script ascii java.class android.dex
diff --git a/tests/grep.test b/tests/grep.test
index 2a4f178b..21cd8bbb 100755
--- a/tests/grep.test
+++ b/tests/grep.test
@@ -143,3 +143,11 @@ rm test
# match after NUL byte
testing "match after NUL byte" "grep -a two" "one\0and two three\n" \
"" 'one\0and two three'
+
+# BREs versus EREs
+testing "implicit BRE |" "grep 'uno|dos'" "uno|dos\n" \
+ "" "uno\ndos\nuno|dos\n"
+testing "explicit BRE |" "grep -e 'uno|dos'" "uno|dos\n" \
+ "" "uno\ndos\nuno|dos\n"
+testing "explicit ERE |" "grep -E 'uno|dos'" "uno\ndos\nuno|dos\n" \
+ "" "uno\ndos\nuno|dos\n"
diff --git a/tests/wc.test b/tests/wc.test
index 8b6365b1..4ab03868 100755
--- a/tests/wc.test
+++ b/tests/wc.test
@@ -14,10 +14,12 @@ EOF
testing "wc" "wc >/dev/null && echo yes" "yes\n" "" ""
testing "empty file" "wc" " 0 0 0\n" "" ""
testing "standard input" "wc" " 1 3 5\n" "" "a b\nc"
+testing "standard input -c" "wc -c" "5\n" "" "a b\nc"
+testing "standard input -cl" "wc -cl" " 1 5\n" "" "a b\nc"
testing "-c" "wc -c file1" "26 file1\n" "" ""
testing "-l" "wc -l file1" "4 file1\n" "" ""
testing "-w" "wc -w file1" "5 file1\n" "" ""
-testing "format" "wc file1" "4 5 26 file1\n" "" ""
+testing "one file" "wc file1" "4 5 26 file1\n" "" ""
testing "multiple files" "wc input - file1" \
" 1 2 3 input\n 0 2 3 -\n 4 5 26 file1\n 5 9 32 total\n" "a\nb" "a b"
@@ -25,7 +27,7 @@ testing "multiple files" "wc input - file1" \
echo -n " " > file1
for i in $(seq 1 512); do echo -n "üüüüüüüüüüüüüüüü" >> file1; done
testing "-m" "wc -m file1" "8193 file1\n" "" ""
-testing "-m 2" 'cat "$FILES/utf8/test2.txt" | wc -m' " 169\n" "" ""
+testing "-m 2" 'cat "$FILES/utf8/test2.txt" | wc -m' "169\n" "" ""
echo -n " " > file1
testing "-mlw" "wc -mlw input" "1 2 11 input\n" "hello, 世界!\n" ""
rm file1
diff --git a/toys/net/ifconfig.c b/toys/net/ifconfig.c
index fe58b653..634b6dd0 100644
--- a/toys/net/ifconfig.c
+++ b/toys/net/ifconfig.c
@@ -5,45 +5,50 @@
* Reviewed by Kyungsu Kim <kaspyx@gmail.com>
*
* Not in SUSv4.
+ *
+ * Obsolete fields included for historical purposes:
+ * irq|io_addr|mem_start ADDR - micromanage obsolete hardware
+ * outfill|keepalive INTEGER - SLIP analog dialup line quality monitoring
+ * metric INTEGER - added to Linux 0.9.10 with comment "never used", still true
-USE_IFCONFIG(NEWTOY(ifconfig, "^?a", TOYFLAG_SBIN))
+USE_IFCONFIG(NEWTOY(ifconfig, "^?aS", TOYFLAG_SBIN))
config IFCONFIG
bool "ifconfig"
default y
help
- usage: ifconfig [-a] [INTERFACE [ACTION...]]
+ usage: ifconfig [-aS] [INTERFACE [ACTION...]]
Display or configure network interface.
With no arguments, display active interfaces. First argument is interface
to operate on, one argument by itself displays that interface.
- -a Show all interfaces, not just active ones
+ -a All interfaces displayed, not just active ones
+ -S Short view, one line per interface
+
+ Standard ACTIONs to perform on an INTERFACE:
- Additional arguments are actions to perform on the interface:
+ ADDR[/MASK] - set IPv4 address (1.2.3.4/5) and activate interface
+ add|del ADDR[/LEN] - add/remove IPv6 address (1111::8888/128)
+ up|down - activate or deactivate interface
- ADDRESS[/NETMASK] - set IPv4 address (1.2.3.4/5)
- default - unset ipv4 address
- add|del ADDRESS[/PREFIXLEN] - add/remove IPv6 address (1111::8888/128)
- up - enable interface
- down - disable interface
+ Advanced ACTIONs (default values usually suffice):
- netmask|broadcast|pointopoint ADDRESS - set more IPv4 characteristics
- hw ether|infiniband ADDRESS - set LAN hardware address (AA:BB:CC...)
- txqueuelen LEN - number of buffered packets before output blocks
- mtu LEN - size of outgoing packets (Maximum Transmission Unit)
+ default - remove IPv4 address
+ netmask ADDR - set IPv4 netmask via 255.255.255.0 instead of /24
+ txqueuelen LEN - number of buffered packets before output blocks
+ mtu LEN - size of outgoing packets (Maximum Transmission Unit)
+ broadcast ADDR - Set broadcast address
+ pointopoint ADDR - PPP and PPPOE use this instead of "route add default gw"
+ hw TYPE ADDR - set hardware (mac) address (type = ether|infiniband)
Flags you can set on an interface (or -remove by prefixing with -):
- arp - don't use Address Resolution Protocol to map LAN routes
- promisc - don't discard packets that aren't to this LAN hardware address
- multicast - force interface into multicast mode if the driver doesn't
- allmulti - promisc for multicast packets
- Obsolete fields included for historical purposes:
- irq|io_addr|mem_start ADDR - micromanage obsolete hardware
- outfill|keepalive INTEGER - SLIP analog dialup line quality monitoring
- metric INTEGER - added to Linux 0.9.10 with comment "never used", still true
+ arp - don't use Address Resolution Protocol to map LAN routes
+ promisc - don't discard packets that aren't to this LAN hardware address
+ multicast - force interface into multicast mode if the driver doesn't
+ allmulti - promisc for multicast packets
*/
#define FOR_ifconfig
@@ -96,6 +101,7 @@ static int get_addrinfo(char *host, sa_family_t af, void *addr)
static void display_ifconfig(char *name, int always, unsigned long long val[])
{
struct ifreq ifre;
+ struct sockaddr_in *si = (void *)&ifre.ifr_addr;
struct {
int type;
char *title;
@@ -114,21 +120,37 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
flags = ifre.ifr_flags;
if (!always && !(flags & IFF_UP)) return;
- // query hardware type and hardware address
- i = ioctl(TT.sockfd, SIOCGIFHWADDR, &ifre);
+ if (toys.optflags&FLAG_S) {
+ unsigned uu = 0;
+ int len;
+
+ ioctl(TT.sockfd, SIOCGIFADDR, &ifre);
+ len = printf("%*s %s", -9, name, inet_ntoa(si->sin_addr));
+ if (!ioctl(TT.sockfd, SIOCGIFNETMASK, &ifre))
+ uu = htonl(*(unsigned *)&(si->sin_addr));
+ for (i = 0; uu; i++) uu <<= 1;
+ len += printf("/%d", i);
+ printf("%*c", 26-len, ' ');
+ }
- for (i=0; i < (sizeof(types)/sizeof(*types))-1; i++)
- if (ifre.ifr_hwaddr.sa_family == types[i].type) break;
+ // query hardware type and hardware address
+ xioctl(TT.sockfd, SIOCGIFHWADDR, &ifre);
- xprintf("%-9s Link encap:%s ", name, types[i].title);
- if(i >= 0 && ifre.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
- xprintf("HWaddr ");
- for (i=0; i<6; i++) xprintf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]);
+ if (toys.optflags&FLAG_S)
+ for (i=0; i<6; i++) printf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]);
+ else {
+ for (i=0; i < ARRAY_LEN(types)-1; i++)
+ if (ifre.ifr_hwaddr.sa_family == types[i].type) break;
+ xprintf("%-9s Link encap:%s ", name, types[i].title);
+ if(ifre.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
+ xprintf("HWaddr ");
+ for (i=0; i<6; i++) xprintf(":%02x"+!i, ifre.ifr_hwaddr.sa_data[i]);
+ }
+ sprintf(toybuf, "/sys/class/net/%.15s/device/driver", name);
+ if (readlink0(toybuf, toybuf, sizeof(toybuf))>0)
+ if ((pp = strrchr(toybuf, '/'))) xprintf(" Driver %s", pp+1);
+ xputc('\n');
}
- sprintf(toybuf, "/sys/class/net/%.15s/device/driver", name);
- if (readlink0(toybuf, toybuf, sizeof(toybuf))>0 && (pp = strrchr(toybuf, '/')))
- xprintf(" Driver %s", pp+1);
- xputc('\n');
// If an address is assigned record that.
@@ -138,7 +160,7 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
pp = (char *)&ifre.ifr_addr;
for (i = 0; i<sizeof(ifre.ifr_addr); i++) if (pp[i]) break;
- if (i != sizeof(ifre.ifr_addr)) {
+ if (!(toys.optflags&FLAG_S) && i != sizeof(ifre.ifr_addr)) {
struct sockaddr_in *si = (struct sockaddr_in *)&ifre.ifr_addr;
struct {
char *name;
@@ -150,10 +172,11 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
{"Mask", 0, SIOCGIFNETMASK}
};
+ // TODO: can this be ipv6? Why are we checking here when ipv6 source later?
xprintf("%10c%s", ' ', (si->sin_family == AF_INET) ? "inet" :
(si->sin_family == AF_INET6) ? "inet6" : "unspec");
- for (i=0; i < sizeof(addr)/sizeof(*addr); i++) {
+ for (i=0; i<ARRAY_LEN(addr); i++) {
if (!addr[i].flag || (flags & addr[i].flag)) {
if (addr[i].ioctl && ioctl(TT.sockfd, addr[i].ioctl, &ifre))
si->sin_family = 0;
@@ -195,10 +218,11 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
char *scopes[] = {"Global","Host","Link","Site","Compat"},
*scope = "Unknown";
- for (i=0; i < sizeof(scopes)/sizeof(*scopes); i++)
+ for (i=0; i<ARRAY_LEN(scopes); i++)
if (iscope == (!!i)<<(i+3)) scope = scopes[i];
- xprintf("%10cinet6 addr: %s/%d Scope: %s\n",
- ' ', toybuf, plen, scope);
+ if (toys.optflags&FLAG_S) xprintf(" %s/%d@%c", toybuf, plen,*scope);
+ else xprintf("%10cinet6 addr: %s/%d Scope: %s\n",
+ ' ', toybuf, plen, scope);
}
}
}
@@ -206,6 +230,11 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
fclose(fp);
}
+ if (toys.optflags&FLAG_S) {
+ xputc('\n');
+ return;
+ }
+
xprintf("%10c", ' ');
if (flags) {
@@ -242,7 +271,7 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
if (ioctl(TT.sockfd, SIOCGIFTXQLEN, &ifre) >= 0) val[16] = ifre.ifr_qlen;
else val[16] = -1;
- for (i = 0; i < sizeof(order); i++) {
+ for (i = 0; i<sizeof(order); i++) {
int j = order[i];
if (j < 0) xprintf("\n%10c", ' ');
@@ -459,7 +488,7 @@ void ifconfig_main(void)
close(fd6);
continue;
// Iterate through table to find/perform operation
- } else for (i = 0; i < ARRAY_LEN(try); i++) {
+ } else for (i = 0; i<ARRAY_LEN(try); i++) {
struct argh *t = try+i;
int on = t->on, off = t->off;
@@ -514,7 +543,7 @@ void ifconfig_main(void)
break;
}
- if (i == sizeof(try)/sizeof(*try)) help_exit("bad argument '%s'", *argv);
+ if (i == ARRAY_LEN(try)) help_exit("bad argument '%s'", *argv);
}
close(TT.sockfd);
}
diff --git a/toys/net/netcat.c b/toys/net/netcat.c
index cabc7e80..039b1944 100644
--- a/toys/net/netcat.c
+++ b/toys/net/netcat.c
@@ -108,7 +108,7 @@ void netcat_main(void)
set_alarm(TT.wait);
// The argument parsing logic can't make "<2" conditional on other
- // arguments like -f and -l, so we do it by hand here.
+ // arguments like -f and -l, so do it by hand here.
if ((toys.optflags&FLAG_f) ? toys.optc :
(!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2))
help_exit("bad argument count");
diff --git a/toys/pending/gzip.c b/toys/pending/gzip.c
index f6d797b5..694ef2e8 100644
--- a/toys/pending/gzip.c
+++ b/toys/pending/gzip.c
@@ -14,7 +14,6 @@ USE_ZCAT(NEWTOY(zcat, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
config GZIP
bool "gzip"
default y
- depends on TOYBOX_LIBZ
help
usage: gzip [-19cdfk] [FILE...]
@@ -31,7 +30,6 @@ config GZIP
config GUNZIP
bool "gunzip"
default y
- depends on TOYBOX_LIBZ
help
usage: gunzip [-cfk] [FILE...]
@@ -46,7 +44,6 @@ config GUNZIP
config ZCAT
bool "zcat"
default y
- depends on TOYBOX_LIBZ
help
usage: zcat [FILE...]
@@ -59,27 +56,23 @@ config ZCAT
#define FOR_gzip
#include "toys.h"
-#include <zlib.h>
-
GLOBALS(
int level;
)
-static void fix_time(const char *path, struct stat *sb)
-{
- struct timespec times[] = { sb->st_atim, sb->st_mtim };
-
- if (utimensat(AT_FDCWD, path, times, 0)) perror_exit("utimensat");
-}
+// Use assembly optimized zlib code?
+#if CFG_TOYBOX_LIBZ
+#include <zlib.h>
-static int do_zlib(int in_fd, int out_fd)
+// Read fron in_fd, write to out_fd, decompress if dd else compress
+static int do_deflate(int in_fd, int out_fd, int dd, int level)
{
- int len, err = 0, dd = toys.optflags&FLAG_d;
+ int len, err = 0;
char *b = "r";
gzFile gz;
if (!dd) {
- sprintf(b = toybuf, "w%d", TT.level);
+ sprintf(b = toybuf, "w%d", level);
if (out_fd == 1) out_fd = xdup(out_fd);
}
if (!(gz = gzdopen(dd ? in_fd : out_fd, b))) perror_exit("gzdopen");
@@ -101,6 +94,22 @@ static int do_zlib(int in_fd, int out_fd)
return err;
}
+// Use toybox's builtin lib/deflate.c
+#else
+
+// Read from in_fd, write to out_fd, decompress if dd else compress
+static int do_deflate(int in_fd, int out_fd, int dd, int level)
+{
+ int x;
+
+ if (dd) WOULD_EXIT(x, gunzip_fd(in_fd, out_fd));
+ else WOULD_EXIT(x, gzip_fd(in_fd, out_fd));
+
+ return x;
+}
+
+#endif
+
static void do_gzip(int in_fd, char *arg)
{
struct stat sb;
@@ -110,26 +119,20 @@ static void do_gzip(int in_fd, char *arg)
// 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 (!(toys.optflags&FLAG_f))
+ return error_msg("%s:need -f to read TTY"+3*!!in_fd, arg);
+ else out_fd = 1;
}
// Are we reading file.gz to write to file?
if (!out_fd) {
- if (fstat(in_fd, &sb)) {
- perror_msg("%s", arg);
- return;
- }
+ if (fstat(in_fd, &sb)) return perror_msg("%s", arg);
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;
- }
+ if ((len = strlen(arg))<4 || strcmp(arg+len-3, ".gz"))
+ return error_msg("no .gz: %s", arg);
out_name = xstrdup(arg);
out_name[len-3] = 0;
}
@@ -139,12 +142,14 @@ static void do_gzip(int in_fd, char *arg)
if (out_fd == -1) return;
}
-// if (CFG_TOYBOX_LIBZ)
- if (do_zlib(in_fd, out_fd) && out_name) arg = out_name;
+ if (do_deflate(in_fd, out_fd, toys.optflags&FLAG_d, TT.level) && out_name)
+ arg = out_name;
if (out_fd != 1) close(out_fd);
if (out_name) {
- fix_time(out_name, &sb);
+ struct timespec times[] = { sb.st_atim, sb.st_mtim };
+
+ if (utimensat(AT_FDCWD, out_name, times, 0)) perror_exit("utimensat");
if (!(toys.optflags&FLAG_k)) if (unlink(arg)) perror_msg("unlink %s", arg);
free(out_name);
}
@@ -152,6 +157,7 @@ static void do_gzip(int in_fd, char *arg)
void gzip_main(void)
{
+ // This depends on 1-9 being at the end of the option list
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;
diff --git a/toys/pending/modprobe.c b/toys/pending/modprobe.c
index 33a2a310..0c0cbc83 100644
--- a/toys/pending/modprobe.c
+++ b/toys/pending/modprobe.c
@@ -482,9 +482,9 @@ static int go_probe(struct module_s *m)
// none of above is true insert the module.
rc = ins_mod(fn, options);
if (toys.optflags&FLAG_v)
- printf("loaded %s '%s', rc:%d\n", fn, options, rc);
- if (rc == EEXIST) rc = 0;
- if (options) free(options);
+ printf("loaded %s '%s': %s\n", fn, options, strerror(errno));
+ if (errno == EEXIST) rc = 0;
+ free(options);
if (rc) {
perror_msg("can't load module %s (%s)", m2->name, fn);
break;
diff --git a/toys/pending/wget.c b/toys/pending/wget.c
index 4283fe40..ced51d9c 100644
--- a/toys/pending/wget.c
+++ b/toys/pending/wget.c
@@ -135,7 +135,7 @@ void wget_main(void)
// TODO extract filename to be saved from URL
if (!(toys.optflags & FLAG_f)) help_exit("no filename");
- if (fopen(TT.filename, "r")) perror_exit("file already exists");
+ if (fopen(TT.filename, "r")) error_exit("'%s' already exists", TT.filename);
if(!toys.optargs[0]) help_exit("no URL");
get_info(toys.optargs[0], hostname, port, path);
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 49c7b22a..333869cd 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -160,17 +160,19 @@ static void do_elf_file(int fd)
} else if (sh_type == 7 /*SHT_NOTE*/) {
char *note = map+sh_offset;
- if (sh_offset+sh_size>TT.len) goto bad;
-
// An ELF note is a sequence of entries, each consisting of an
// ndhr followed by n_namesz+n_descsz bytes of data (each of those
// rounded up to the next 4 bytes, without this being reflected in
// the header byte counts themselves).
while (sh_size >= 3*4) { // Don't try to read a truncated entry.
- int n_namesz = elf_int(note, 4);
- int n_descsz = elf_int(note+4, 4);
- int n_type = elf_int(note+8, 4);
- int notesz = 3*4 + ((n_namesz+3)&~3) + ((n_descsz+3)&~3);
+ unsigned n_namesz, n_descsz, n_type, notesz;
+
+ if (sh_offset+sh_size>TT.len) goto bad;
+
+ n_namesz = elf_int(note, 4);
+ n_descsz = elf_int(note+4, 4);
+ n_type = elf_int(note+8, 4);
+ notesz = 3*4 + ((n_namesz+3)&~3) + ((n_descsz+3)&~3);
if (n_namesz==4 && !memcmp(note+12, "GNU", 4)) {
if (n_type==3 /*NT_GNU_BUILD_ID*/) {
@@ -242,10 +244,14 @@ static void do_regular_file(int fd, char *name)
// TODO: parsing JPEG for width/height is harder than GIF or PNG.
else if (len>32 && !memcmp(toybuf, "\xff\xd8", 2)) xputs("JPEG image data");
- // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
+ // https://en.wikipedia.org/wiki/Java_class_file#General_layout
else if (len>8 && strstart(&s, "\xca\xfe\xba\xbe"))
- xprintf("Java class file, version %d.%d\n",
- (int)peek_be(s+2, 2), (int)peek_be(s, 2));
+ xprintf("Java class file, version %d.%d (Java 1.%d)\n",
+ (int)peek_be(s+2, 2), (int)peek_be(s, 2), (int)peek_be(s+2, 2)-44);
+
+ // https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
+ else if (len>8 && strstart(&s, "dex\n") && s[3] == 0)
+ xprintf("Android dex file, version %s\n", s);
// https://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt
// the lengths for cpio are size of header + 9 bytes, since any valid
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index bb491eba..43c4ab1b 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -76,7 +76,7 @@ config PS
-k Sort FIELDs (-FIELD to reverse) -M Measure/pad future field widths
-n Show numeric USER and GROUP -w Wide output (don't truncate fields)
- Which FIELDs to show. (Default = -o PID,TTY,TIME,CMD)
+ Which FIELDs to show. (-o HELP for list, default = -o PID,TTY,TIME,CMD)
-f Full listing (-o USER:12=UID,PID,PPID,C,STIME,TTY,TIME,ARGS=CMD)
-l Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
@@ -84,42 +84,6 @@ config PS
-O Add FIELDS to defaults
-Z Include LABEL
- Command line -o fields:
-
- ARGS CMDLINE minus initial path CMD Command (thread) name (stat[2])
- CMDLINE Command line (argv[]) COMM Command filename (/proc/$PID/exe)
- COMMAND Command file (/proc/$PID/exe) NAME Process name (argv[0] of $PID)
-
- Process attribute -o FIELDs:
-
- ADDR Instruction pointer BIT Is this process 32 or 64 bits
- CPU Which processor running on ETIME Elapsed time since PID start
- F Flags (1=FORKNOEXEC 4=SUPERPRIV) GID Group id
- GROUP Group name LABEL Security label
- MAJFL Major page faults MINFL Minor page faults
- NI Niceness (lower is faster)
- PCPU Percentage of CPU time used PCY Android scheduling policy
- PGID Process Group ID
- PID Process ID PPID Parent Process ID
- PRI Priority (higher is faster) PSR Processor last executed on
- RGID Real (before sgid) group ID RGROUP Real (before sgid) group name
- RSS Resident Set Size (pages in use) RTPRIO Realtime priority
- RUID Real (before suid) user ID RUSER Real (before suid) user name
- S Process state:
- R (running) S (sleeping) D (device I/O) T (stopped) t (traced)
- Z (zombie) X (deader) x (dead) K (wakekill) W (waking)
- SCHED Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)
- STAT Process state (S) plus:
- < high priority N low priority L locked memory
- s session leader + foreground l multithreaded
- STIME Start time of process in hh:mm (size :19 shows yyyy-mm-dd hh:mm:ss)
- SZ Memory Size (4k pages needed to completely swap out process)
- TCNT Thread count TID Thread ID
- TIME CPU time consumed TTY Controlling terminal
- UID User id USER User name
- VSZ Virtual memory size (1k units) %VSZ VSZ as % of physical memory
- WCHAN Wait location in kernel
-
config TOP
bool "top"
default y
@@ -323,12 +287,11 @@ enum {
SLOT_startbss, /*data/bss address*/ SLOT_endbss, // end addr data+bss
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_shr, /*Shared memory*/ SLOT_pcy, // Android sched pol
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_bits, // 32 or 64
SLOT_tid, /*Thread ID*/ SLOT_tcount, // Thread count
- SLOT_pcy, /*Android sched policy*/
SLOT_count /* Size of array */
};
@@ -372,47 +335,149 @@ struct procpid {
// TODO: Android uses -30 for LABEL, but ideally it would auto-size.
// 64|slot means compare as string when sorting
struct typography {
- char *name;
+ char *name, *help;
signed char width, slot;
} static const typos[] = TAGGED_ARRAY(PS,
// 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},
- {"VSZ", 7, SLOT_vsize}, {"MAJFL", 6, SLOT_majflt}, {"MINFL", 6, SLOT_minflt},
- {"PR", 2, SLOT_priority}, {"PSR", 3, SLOT_taskcpu},
- {"RTPRIO", 6, SLOT_rtprio}, {"SCH", 3, SLOT_policy}, {"CPU", 3, SLOT_taskcpu},
- {"TID", 5, SLOT_tid}, {"TCNT", 4, SLOT_tcount}, {"BIT", 3, SLOT_bits},
+ {"PID", "Process ID", 5, SLOT_pid},
+ {"PPID", "Parent Process ID", 5, SLOT_ppid},
+ {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority},
+ {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice},
+ {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip},
+ {"SZ", "4k pages to swap out", 5, SLOT_vsize},
+ {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss},
+ {"PGID", "Process Group ID", 5, SLOT_pgrp},
+ {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize},
+ {"MAJFL", "Major page faults", 6, SLOT_majflt},
+ {"MINFL", "Minor page faults", 6, SLOT_minflt},
+ {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority},
+ {"PSR", "Processor last executed on", 3, SLOT_taskcpu},
+ {"RTPRIO", "Realtime priority", 6, SLOT_rtprio},
+ {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)",
+ 3, SLOT_policy},
+ {"CPU", "Which processor running on", 3, SLOT_taskcpu},
+ {"TID", "Thread ID", 5, SLOT_tid},
+ {"TCNT", "Thread count", 4, SLOT_tcount},
+ {"BIT", "32 or 64", 3, SLOT_bits},
// String fields (-1 is procpid->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},
+ {"TTY", "Controlling terminal", -8, -2},
+ {"WCHAN", "Wait location in kernel", -6, -3},
+ {"LABEL", "Security label", -30, -4},
+ {"COMM", "EXE filename (/proc/PID/exe)", -27, -5},
+ {"NAME", "Process name (PID's argv[0])", -27, -7},
+ {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5},
+ {"CMDLINE", "Command line (argv[])", -27, -6},
+ {"ARGS", "CMDLINE minus initial path", -27, -6},
+ {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1},
// 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},
+ {"UID", "User id", 5, SLOT_uid},
+ {"USER", "User name", -12, 64|SLOT_uid},
+ {"RUID", "Real (before suid) user ID", 4, SLOT_ruid},
+ {"RUSER", "Real (before suid) user name", -8, 64|SLOT_ruid},
+ {"GID", "Group ID", 8, SLOT_gid},
+ {"GROUP", "Group name", -8, 64|SLOT_gid},
+ {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid},
+ {"RGROUP", "Real (before sgid) group name", -8, 64|SLOT_rgid},
// clock displays (00:00:00)
- {"TIME", 8, SLOT_utime}, {"ELAPSED", 11, SLOT_starttime},
- {"TIME+", 9, SLOT_utime},
+ {"TIME", "CPU time consumed", 8, SLOT_utime},
+ {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime},
+ {"TIME+", "CPU time (high precision)", 9, SLOT_utime},
// 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},
+ {"C", "Total %CPU used since start", 1, SLOT_utime2},
+ {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize},
+ {"%MEM", "RSS as % of physical memory", 5, SLOT_rss},
+ {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2},
// 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},
+ {"VIRT", "Virtual memory size", 4, SLOT_vsz},
+ {"RES", "Short RSS", 4, SLOT_rss},
+ {"SHR", "Shared memory", 4, SLOT_shr},
+ {"READ", "Data read", 6, SLOT_rchar},
+ {"WRITE", "Data written", 6, SLOT_wchar},
+ {"IO", "Data I/O", 6, SLOT_iobytes},
+ {"DREAD", "Disk Read", 6, SLOT_rbytes},
+ {"DWRITE", "Disk write", 6, SLOT_wbytes},
+ {"SWAP", "Swap I/O", 6, SLOT_swap},
+ {"DIO", "Disk I/O", 6, SLOT_diobytes},
// Misc (special cases)
- {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
- {"STAT", -5, 64}, {"PCY", 3, 64|SLOT_pcy},
+ {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime},
+ {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, 64|SLOT_flags},
+ {"S", "Process state:\n"
+ "\t R (running) S (sleeping) D (device I/O) T (stopped) t (traced)\n"
+ "\t Z (zombie) X (deader) x (dead) K (wakekill) W (waking)",
+ -1, 64},
+ {"STAT", "Process state (S) plus:\n"
+ "\t < high priority N low priority L locked memory\n"
+ "\t s session leader + foreground l multithreaded",
+ -5, 64},
+ {"PCY", "Android scheduling policy", 3, 64|SLOT_pcy},
);
+// Show sorted "-o help" text for fields listed in toybuf[len]
+
+static void help_fields(int len, int multi)
+{
+ int i, j, k, left = 0;
+ struct typography *t;
+
+ // Quick and dirty sort of toybuf[] entries (see TODO below)
+ for (j = len; j--; ) {
+ k = -1;
+
+ for (i=0; i<j; i++) {
+ if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) {
+ k = toybuf[i];
+ toybuf[i] = toybuf[i+1];
+ toybuf[i+1] = k;
+ }
+ }
+ if (k == -1) break;
+ }
+
+ // Display loop
+ for (i = j = 0; i<len; i++, j++) {
+ t = (void *)(typos+toybuf[i]);
+ if (strlen(t->help)>30) {
+ if (multi) printf(" %-8s%s\n", t->name, t->help);
+ else j--;
+ } else if (!multi) {
+ left = !(j&1);
+ printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left);
+ }
+ }
+ if (!multi && left) xputc('\n');
+}
+
+// Print help text for all -o field, with categories.
+static void help_help(void)
+{
+ int i, jump = PS_CMD+1-PS_COMM;
+
+ // TODO: sort the array of -o types so they're already alphabetical and
+ // don't need sorting here. A regex to find everything that currently cares
+ // about symbol order might be: "which *[><]=* *PS"
+
+ // First show the half-dozen variants of command line display.
+
+ printf("Command line field types:\n\n");
+ for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i;
+ help_fields(jump, 0);
+
+ // Show the rest of the -o types, starting with the ones that don't columnize
+
+ printf("\nProcess attribute field types:\n\n");
+ for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump;
+ help_fields(ARRAY_LEN(typos)-jump, 1);
+ help_fields(ARRAY_LEN(typos)-jump, 0);
+
+ xexit();
+}
+
// Return 0 to discard, nonzero to keep
static int shared_match_process(long long *slot)
{
@@ -646,9 +711,9 @@ static void show_ps(void *p)
putchar(TT.time ? '\r' : '\n');
}
-// dirtree callback: read data about process to display, store, or discard it.
+// dirtree callback: read data about process, then display or store it.
// Fills toybuf with struct procpid and either DIRTREE_SAVEs a copy to ->extra
-// (in -k mode) or calls show_ps on toybuf (no malloc/copy/free there).
+// (in -k mode) or calls show_ps directly on toybuf (for low memory systems).
static int get_ps(struct dirtree *new)
{
struct {
@@ -671,6 +736,7 @@ static int get_ps(struct dirtree *new)
return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
|(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
+ // Grab PID and figure out if we're a thread or a process
memset(slot, 0, sizeof(tb->slot));
slot[SLOT_tid] = *slot = atol(new->name);
if (TT.threadparent && TT.threadparent->extra) {
@@ -680,20 +746,25 @@ static int get_ps(struct dirtree *new)
}
fd = dirtree_parentfd(new);
+ // Read /proc/$PID/stat into half of toybuf.
len = 2048;
sprintf(buf, "%lld/stat", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &len)) return 0;
- // parse oddball fields (name and state). Name can have embedded ')' so match
- // _last_ ')' in stat (although VFS limits filenames to 255 bytes max).
- // All remaining fields should be numeric.
+ // parse oddball fields: the first field is same as new->name (skip it)
+ // and the second and third (name and state) are the only non-numeric fields.
+ // Name has (parentheses) around it, and can have embedded ')' so match
+ // _last_ ')' (VFS limits filenames to 255 bytes max, sanity check that).
+ // TODO: kernel task struct actually limits name to 16 chars?
if (!(name = strchr(buf, '('))) return 0;
for (s = ++name; *s; s++) if (*s == ')') end = s;
if (!end || end-name>255) return 0;
-
- // Parse numeric fields (starting at 4th field in slot[SLOT_ppid])
if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
- for (j = 1; j<SLOT_count; j++)
+
+ // All remaining fields should be numeric, parse them into slot[] array
+ // (skipping first 3 stat fields and first slot[], both were handled above)
+ // yes this means the alignment's off: stat[4] becomes slot[1]
+ for (j = SLOT_ppid; j<SLOT_count; j++)
if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
// Now we've read the data, move status and name right after slot[] array,
@@ -705,6 +776,8 @@ static int get_ps(struct dirtree *new)
*buf++ = 0;
len = sizeof(toybuf)-(buf-toybuf);
+ // Overwrite useless/obsolete stat fields with more interesting data.
+
// save uid, ruid, gid, gid, and rgid int slots 31-34 (we don't use sigcatch
// or numeric wchan, and the remaining two are always zero), and vmlck into
// 18 (which is "obsolete, always 0" from stat)
@@ -715,8 +788,7 @@ static int get_ps(struct dirtree *new)
slot[SLOT_utime] += slot[SLOT_stime];
slot[SLOT_utime2] = slot[SLOT_utime];
- // If RGROUP RUSER STAT RUID RGID SWAP happening, or -G or -U, parse "status"
- // and save ruid, rgid, and vmlck.
+ // Do we need to read "status"?
if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
|_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
{
@@ -756,14 +828,15 @@ static int get_ps(struct dirtree *new)
slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
// Do we need to read "statm"?
- if (TT.bits&(_PS_VIRT|_PS_RES|_PS_SHR)) {
+ if (TT.bits&(_PS_VIRT|_PS_SHR)) {
off_t temp = len;
sprintf(buf, "%lld/statm", slot[SLOT_tid]);
if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
-
+
+ // Skip redundant RSS field, we got it from stat
for (s = buf, i=0; i<3; i++)
- if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i, &j)) slot[SLOT_vsz+i] = 0;
+ if (!sscanf(s, " %lld%n", slot+SLOT_vsz+i/2, &j)) slot[SLOT_vsz+i/2] = 0;
else s += j;
}
@@ -782,10 +855,18 @@ static int get_ps(struct dirtree *new)
if (TT.bits&_PS_PCY)
get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]);
+ // Done using buf[] (tb->str) as scratch space, now read string data,
+ // saving consective null terminated strings. (Save starting offsets into
+ // str->offset to avoid strlen() loop to find relevant string.)
+
// Fetch string data while parentfd still available, appending to buf.
// (There's well over 3k of toybuf left. We could dynamically malloc, but
// it'd almost never get used, querying length of a proc file is awkward,
// fixed buffer is nommu friendly... Wait for somebody to complain. :)
+
+ // The fetch[] array at the start of the function says what file to read
+ // and what -o display field outputs it (to skip the ones we don't need).
+
slot[SLOT_argv0len] = 0;
for (j = 0; j<ARRAY_LEN(fetch); j++) {
tb->offset[j] = buf-(tb->str);
@@ -794,12 +875,17 @@ static int get_ps(struct dirtree *new)
continue;
}
- // Determine remaining space, reserving minimum of 256 bytes/field and
- // 260 bytes scratch space at the end (for output conversion later).
- len = sizeof(toybuf)-(buf-toybuf)-260-256*(ARRAY_LEN(fetch)-j);
+ // Determine available space: reserve 256 bytes (guaranteed minimum) for
+ // each string we haven't checked yet, tb->str starts after the numeric
+ // arrays in struct procpid, and we reserve 260 bytes scratch space at the
+ // end of toybuf for output conversion in string_field(). Other than that,
+ // each use all available space, and future strings that don't use their
+ // guaranteed minimum add to the pool.
+ len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260;
sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
- // For exe we readlink instead of read contents
+ // For exe (j==3) readlink() instead of reading file's contents
+ // for -o NAME (j==5) copy data from threadparent (PID) into thread (TID).
if (j==3 || j==5) {
struct procpid *ptb = 0;
int k;
@@ -825,8 +911,7 @@ static int get_ps(struct dirtree *new)
buf[len] = 0;
}
- // If it's not the TTY field, data we want is in a file.
- // Last length saved in slot[] is command line (which has embedded NULs)
+ // Turning stat's SLOT_ttynr into a string is an outright heuristic ordeal.
} else if (!j) {
int rdev = slot[SLOT_ttynr];
struct stat st;
@@ -869,8 +954,7 @@ static int get_ps(struct dirtree *new)
if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
}
- // Data we want is in a file.
- // Last length saved in slot[] is command line (which has embedded NULs)
+ // For the rest, the data we want is in a file we can just read.
} else {
int temp = 0;
@@ -882,7 +966,7 @@ static int get_ps(struct dirtree *new)
if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
else break;
- // Turn NUL to space, other low ascii to ? (in non-tty mode)
+ // Turn NUL to space, other low ascii to ? (in non-tty mode), except
// cmdline has a trailing NUL that we don't want to turn to space.
for (i=0; i<len-1; i++) {
char c = buf[i];
@@ -900,10 +984,11 @@ static int get_ps(struct dirtree *new)
slot[SLOT_argv0len] = temp ? temp : len; // Position of _first_ NUL
}
- // Above calculated/retained len, so we don't need to re-strlen.
+ // Each case above calculated/retained len, so we don't need to re-strlen.
buf += len+1;
}
+ // Record that we saw another process, and display/return now if appropriate
TT.kcount++;
if (TT.show_process && !TT.threadparent) {
TT.show_process(tb);
@@ -911,7 +996,7 @@ static int get_ps(struct dirtree *new)
return 0;
}
- // If we need to sort the output, add it to the list and return.
+ // We're retaining data (probably to sort it), save copy in list.
s = xmalloc(buf-toybuf);
new->extra = (long)s;
memcpy(s, toybuf, buf-toybuf);
@@ -976,6 +1061,9 @@ static char *parse_ko(void *data, char *type, int length)
char *width, *title, *end, *s;
int i, j, k;
+ // Caller's WOULD_EXIT catches -o help and prints help
+ if (length==4 && !strncasecmp(type, "HELP", length)) xexit();
+
// Get title, length of title, type, end of type, and display width
// Chip off =name to display
@@ -1180,10 +1268,12 @@ static struct procpid **collate(int count, struct dirtree *dt)
static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
{
struct arg_list def;
+ int x;
memset(&def, 0, sizeof(struct arg_list));
def.arg = s;
- comma_args(arg ? arg : &def, fields, err, parse_ko);
+ WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
+ if (x) help_help();
}
void ps_main(void)
diff --git a/toys/posix/wc.c b/toys/posix/wc.c
index acd0b3a0..b875c1fd 100644
--- a/toys/posix/wc.c
+++ b/toys/posix/wc.c
@@ -33,12 +33,17 @@ GLOBALS(
static void show_lengths(unsigned long *lengths, char *name)
{
- int i, space, first = 1;
+ int i, space = 0, first = 1;
// POSIX says there should never be leading spaces, but accepts that
- // traditional implementations use 7 spaces, unless only one file
- // is being counted, when there should be no leading spaces.
- space = (toys.optc != 1) ? 7 : 0;
+ // traditional implementations use 7 spaces, unless only one file (or
+ // just stdin) is being counted, when there should be no leading spaces,
+ // *except* for the case where we're going to output multiple numbers.
+ // And, yes, folks have test scripts that rely on all this nonsense :-(
+ // Note: sufficiently modern versions of coreutils wc will use the smallest
+ // column width necessary to have all columns be equal width rather than 0.
+ if (!(toys.optc==0 && (toys.optflags & (toys.optflags-1))==0) && toys.optc!=1)
+ space = 7;
for (i = 0; i<4; i++) {
if (toys.optflags&(1<<i)) {