Merge remote-tracking branch 'toybox/master' into HEAD

Change-Id: I32307d7b629f0e227b4536d873edf1b9693437be
diff --git a/Makefile b/Makefile
index c38c795..26038e9 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@
 
 HOSTCC?=cc
 
-export CROSS_COMPILE CFLAGS OPTIMIZE LDOPTIMIZE CC HOSTCC V
+export CROSS_COMPILE CFLAGS OPTIMIZE LDOPTIMIZE CC HOSTCC V STRIP
 
 all: toybox
 
diff --git a/configure b/configure
index 14c9c65..a9b8897 100755
--- a/configure
+++ b/configure
@@ -28,6 +28,7 @@
 # you call scripts/make.sh and friends directly.
 
 [ -z "$CC" ] && CC=cc
+[ -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/flags.h b/generated/flags.h
index f7363b8..52a584a 100644
--- a/generated/flags.h
+++ b/generated/flags.h
@@ -2064,9 +2064,9 @@
 #undef FLAG_s
 #endif
 
-// ping   <1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]
+// ping   <1>1m#t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]
 #undef OPTSTR_ping
-#define OPTSTR_ping "<1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]"
+#define OPTSTR_ping "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]"
 #ifdef CLEANUP_ping
 #undef CLEANUP_ping
 #undef FOR_ping
@@ -2081,6 +2081,7 @@
 #undef FLAG_s
 #undef FLAG_c
 #undef FLAG_t
+#undef FLAG_m
 #endif
 
 // pivot_root <2>2 <2>2
@@ -5055,6 +5056,7 @@
 #define FLAG_s (FORCED_FLAG<<8)
 #define FLAG_c (FORCED_FLAG<<9)
 #define FLAG_t (FORCED_FLAG<<10)
+#define FLAG_m (FORCED_FLAG<<11)
 #endif
 
 #ifdef FOR_pivot_root
diff --git a/generated/globals.h b/generated/globals.h
index 0993b09..0789b91 100644
--- a/generated/globals.h
+++ b/generated/globals.h
@@ -187,6 +187,24 @@
   int wpad;
 };;
 
+// toys/net/ping.c
+
+struct ping_data {
+  long w;
+  long W;
+  char *i;
+  char *I;
+  long s;
+  long c;
+  long t;
+  long m;
+
+  struct sockaddr *sa;
+  int sock;
+  long i_ms;
+  unsigned long sent, recv, fugit, min, max;
+};
+
 // toys/net/tunctl.c
 
 struct tunctl_data {
@@ -762,21 +780,6 @@
   unsigned long vt_num;
 };
 
-// toys/pending/ping.c
-
-struct ping_data {
-  long w;
-  long W;
-  char *i;
-  char *I;
-  long s;
-  long c;
-  long t;
-
-  int sock;
-  long i_ms;
-};
-
 // toys/pending/route.c
 
 struct route_data {
@@ -1084,6 +1087,8 @@
 
 struct file_data {
   int max_name_len;
+
+  off_t len;
 };
 
 // toys/posix/find.c
@@ -1432,6 +1437,7 @@
 	struct microcom_data microcom;
 	struct netcat_data netcat;
 	struct netstat_data netstat;
+	struct ping_data ping;
 	struct tunctl_data tunctl;
 	struct acpi_data acpi;
 	struct base64_data base64;
@@ -1494,7 +1500,6 @@
 	struct modprobe_data modprobe;
 	struct more_data more;
 	struct openvt_data openvt;
-	struct ping_data ping;
 	struct route_data route;
 	struct sh_data sh;
 	struct stty_data stty;
diff --git a/generated/help.h b/generated/help.h
index 20a3770..6375f4b 100644
--- a/generated/help.h
+++ b/generated/help.h
@@ -116,6 +116,8 @@
 
 #define HELP_rfkill "Usage: rfkill COMMAND [DEVICE]\n\nEnable/disable wireless devices.\n\nCommands:\nlist [DEVICE]   List current state\nblock DEVICE    Disable device\nunblock DEVICE  Enable device\n\nDEVICE is an index number, or one of:\nall, wlan(wifi), bluetooth, uwb(ultrawideband), wimax, wwan, gps, fm.\n\n"
 
+#define HELP_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. Returns true if host alive.\n\nOptions:\n-4, -6      Force IPv4 or IPv6\n-c CNT      Send CNT many packets (default 3, 0 = infinite)\n-f          Flood (print . and \\b to show drops, default -c 15 -i 0.2)\n-i TIME     Interval between packets (default 1, need root for < .2)\n-I IFACE/IP Source interface or address\n-m MARK     Tag outgoing packets using SO_MARK\n-q          Quiet (stops after one returns true if host is alive)\n-s SIZE     Data SIZE in bytes (default 56)\n-t TTL      Set Time To Live (number of hops)\n-W SEC      Seconds to wait for response after -c (default 10)\n-w SEC      Exit after this many seconds\n\n"
+
 #define HELP_netstat "usage: netstat [-pWrxwutneal]\n\nDisplay networking information. Default is netsat -tuwx\n\n-r  routing table\n-a  all sockets (not just connected)\n-l  listening server sockets\n-t  TCP sockets\n-u  UDP sockets\n-w  raw sockets\n-x  unix sockets\n-e  extended info\n-n  don't resolve names\n-W  wide display\n-p  PID/Program name of sockets\n\n"
 
 #define HELP_netcat "usage: netcat [-tu] [-lL COMMAND...] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}\n\n-L	listen for multiple incoming connections (server mode)\n-W	SECONDS timeout for idle connection\n-f	use FILENAME (ala /dev/ttyS0) instead of network\n-l	listen for one incoming connection\n-p	local port number\n-q	quit SECONDS after EOF on stdin, even if stdout hasn't closed yet\n-s	local source address\n-t	allocate tty (must come before -l or -L)\n-w	SECONDS timeout to establish connection\n\nUse \"stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho\" with\nnetcat -f to connect to a serial port.\n\nThe command line after -l or -L is executed (as a child process) to handle\neach incoming connection. If blank -l waits for a connection and forwards\nit to stdin/stdout. If no -p specified, -l prints port it bound to and\nbackgrounds itself (returning immediately).\n\nFor a quick-and-dirty server, try something like:\nnetcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l\n"
@@ -338,8 +340,6 @@
 
 #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. Returns true if host alive.\n\nOptions:\n-4, -6      Force IPv4 or IPv6\n-c CNT      Send CNT many packets (default 3, 0 = infinite)\n-f          Flood (. on send, backspace on receive, to show packet drops)\n-i TIME     Interval between packets (default 1, need root for < .2)\n-I IFACE/IP Source interface or address\n-q          Quiet (stops after one returns true if host is alive)\n-s SIZE     Data SIZE in bytes (default 56)\n-t TTL      Set Time To Live (number of hops)\n-W SEC      Seconds to wait for response after -c (default 10)\n-w SEC      Exit after this many seconds\n\n"
-
 #define HELP_deallocvt "usage: deallocvt [N]\n\nDeallocate unused virtual terminal /dev/ttyN, or all unused consoles.\n\n"
 
 #define HELP_openvt "usage: openvt [-c N] [-sw] [command [command_options]]\n\nstart a program on a new virtual terminal (VT)\n\n-c N  Use VT N\n-s    Switch to new VT\n-w    Wait for command to exit\n\nif -sw used together, switch back to originating VT when command completes\n\n"
@@ -492,7 +492,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 (selections may be comma separated lists):\n\n-A	All processes\n-a	Processes with terminals that aren't session leaders\n-d	All processes that aren't session leaders\n-e	Same as -A\n-g	Belonging to GROUPs\n-G	Belonging to real GROUPs (before sgid)\n-p	PIDs (--pid)\n-P	Parent PIDs (--ppid)\n-s	In session IDs\n-t	Attached to selected TTYs\n-T	Show threads\n-u	Owned by USERs\n-U	Owned by real USERs (before suid)\n\nOutput modifiers:\n\n-k	Sort FIELDs in +increasing or -decreasting order (--sort)\n-M	Measure field widths (expanding as necessary)\n-n	Show numeric USER and GROUP\n-w	Wide output (don't truncate 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 What are we waiting in kernel for\n\n"
+#define HELP_ps "usage: ps [-AadefLlnwZ] [-gG GROUP,] [-k FIELD,] [-o FIELD,] [-p PID,] [-t TTY,] [-uU USER,]\n\nList processes.\n\nWhich processes to show (-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_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"
 
@@ -564,7 +564,7 @@
 
 #define HELP_date "usage: date [-u] [-r FILE] [-d DATE] [+DISPLAY_FORMAT] [-D SET_FORMAT] [SET]\n\nSet/get the current date/time. With no SET shows the current date.\n\nDefault SET format is \"MMDDhhmm[[CC]YY][.ss]\", that's (2 digits each)\nmonth, day, hour (0-23), and minute. Optionally century, year, and second.\nAlso accepts \"@UNIXTIME[.FRACTION]\" as seconds since midnight Jan 1 1970.\n\n-d	Show DATE instead of current time (convert date format)\n-D	+FORMAT for SET or -d (instead of MMDDhhmm[[CC]YY][.ss])\n-r	Use modification time of FILE instead of current date\n-u	Use UTC instead of current timezone\n\n+FORMAT specifies display format string using strftime(3) syntax:\n\n%% literal %             %n newline              %t tab\n%S seconds (00-60)       %M minute (00-59)       %m month (01-12)\n%H hour (0-23)           %I hour (01-12)         %p AM/PM\n%y short year (00-99)    %Y year                 %C century\n%a short weekday name    %A weekday name         %u day of week (1-7, 1=mon)\n%b short month name      %B month name           %Z timezone name\n%j day of year (001-366) %d day of month (01-31) %e day of month ( 1-31)\n%N nanosec (output only)\n\n%U Week of year (0-53 start sunday)   %W Week of year (0-53 start monday)\n%V Week of year (1-53 start monday, week < 4 days not part of this year)\n\n%D = \"%m/%d/%y\"    %r = \"%I : %M : %S %p\"   %T = \"%H:%M:%S\"   %h = \"%b\"\n%x locale date     %X locale time           %c locale date/time\n\n"
 
-#define HELP_cut "usage: cut [-Ds] [-bcfF LIST] [-dO DELIM] [FILE...]\n\nPrint selected parts of lines from each FILE to standard output.\n\nEach selection LIST is comma separated, either numbers (counting from 1)\nor dash separated ranges (inclusive, with X- meaning to end of line and -X\nfrom start). By default selection ranges are sorted and collated, use -D\nto prevent that.\n\n-b	select bytes\n-c	select UTF-8 characters\n-C	select unicode columns\n-d	use DELIM (default is TAB for -f, run of whitespace for -F)\n-D	Don't sort/collate selections\n-f	select fields (words) separated by single DELIM character\n-F	select fields separated by DELIM regex\n-O	output delimiter (default one space for -F, input delim for -f)\n-s	skip lines without delimiters\n\n"
+#define HELP_cut "usage: cut [-Ds] [-bcfF LIST] [-dO DELIM] [FILE...]\n\nPrint selected parts of lines from each FILE to standard output.\n\nEach selection LIST is comma separated, either numbers (counting from 1)\nor dash separated ranges (inclusive, with X- meaning to end of line and -X\nfrom start). By default selection ranges are sorted and collated, use -D\nto prevent that.\n\n-b	select bytes\n-c	select UTF-8 characters\n-C	select unicode columns\n-d	use DELIM (default is TAB for -f, run of whitespace for -F)\n-D	Don't sort/collate selections or match -fF lines without delimiter\n-f	select fields (words) separated by single DELIM character\n-F	select fields separated by DELIM regex\n-O	output delimiter (default one space for -F, input delim for -f)\n-s	skip lines without delimiters\n\n"
 
 #define HELP_cpio "usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner]\n       [ignored: -mdu -H newc]\n\ncopy files into and out of a \"newc\" format cpio archive\n\n-F FILE	use archive FILE instead of stdin/stdout\n-p DEST	copy-pass mode, copy stdin file list to directory DEST\n-i	extract from archive into file system (stdin=archive)\n-o	create archive (stdin=list of files, stdout=archive)\n-t	test files (list only, stdin=archive, stdout=list of files)\n-v	verbose (list files during create/extract)\n--no-preserve-owner (don't set ownership during extract)\n--trailer Add legacy trailer (prevents concatenation).\n\n"
 
diff --git a/generated/newtoys.h b/generated/newtoys.h
index f5072a1..f9851df 100644
--- a/generated/newtoys.h
+++ b/generated/newtoys.h
@@ -178,7 +178,8 @@
 USE_PATCH(NEWTOY(patch, "(dry-run)"USE_TOYBOX_DEBUG("x")"d:ulp#i:R", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PIDOF(NEWTOY(pidof, "<1so:", TOYFLAG_BIN))
-USE_PING(NEWTOY(ping, "<1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
+USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN))
 USE_PIVOT_ROOT(NEWTOY(pivot_root, "<2>2", TOYFLAG_SBIN))
 USE_PKILL(NEWTOY(pkill,    "?Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 USE_PMAP(NEWTOY(pmap, "<1xq", TOYFLAG_BIN))
diff --git a/lib/lib.c b/lib/lib.c
index 3bfe7fc..77d8cc8 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -644,7 +644,7 @@
   loopfiles_rw(argv, O_RDONLY|O_CLOEXEC|WARN_ONLY, 0, function);
 }
 
-// call loopfiles with do_lines()
+// glue to call dl_lines() from loopfiles
 static void (*do_lines_bridge)(char **pline, long len);
 static void loopfile_lines_bridge(int fd, char *name)
 {
@@ -1344,6 +1344,7 @@
 // Iterate over lines in file, calling function. Function can write 0 to
 // the line pointer if they want to keep it, or 1 to terminate processing,
 // otherwise line is freed. Passed file descriptor is closed at the end.
+// At EOF calls function(0, 0)
 void do_lines(int fd, void (*call)(char **pline, long len))
 {
   FILE *fp = fd ? xfdopen(fd, "r") : stdin;
@@ -1359,6 +1360,7 @@
       free(line);
     } else break;
   }
+  call(0, 0);
 
   if (fd) fclose(fp);
 }
diff --git a/scripts/make.sh b/scripts/make.sh
index 1119ed9..72d5370 100755
--- a/scripts/make.sh
+++ b/scripts/make.sh
@@ -368,7 +368,7 @@
 
 do_loudly $BUILD $LNKFILES $LINK || exit 1
 if [ ! -z "$NOSTRIP" ] ||
-  ! do_loudly ${CROSS_COMPILE}strip "$UNSTRIPPED" -o "$OUTNAME"
+  ! do_loudly ${CROSS_COMPILE}${STRIP} "$UNSTRIPPED" -o "$OUTNAME"
 then
   echo "strip failed, using unstripped" && cp "$UNSTRIPPED" "$OUTNAME" ||
   exit 1
diff --git a/tests/file.test b/tests/file.test
index d749d96..9380556 100644
--- a/tests/file.test
+++ b/tests/file.test
@@ -22,4 +22,10 @@
 testing "symlink -h" "file -h symlink" "symlink: symbolic link\n" "" ""
 testing "symlink -L" "file -L symlink" "symlink: Java class file, version 49.0\n" "" ""
 
-rm empty bash.script bash.script2 env.python.script ascii java.class symlink
+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 "/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
diff --git a/toys/pending/ping.c b/toys/net/ping.c
similarity index 71%
rename from toys/pending/ping.c
rename to toys/net/ping.c
index 3802918..8638479 100644
--- a/toys/pending/ping.c
+++ b/toys/net/ping.c
@@ -9,12 +9,14 @@
  * (Android does this by default in its init script.)
  *
  * Yes, I wimped out and capped -s at sizeof(toybuf), waiting for a complaint...
- 
-USE_PING(NEWTOY(ping, "<1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
+
+// -s > 4088 = sizeof(toybuf)-sizeof(struct icmphdr), then kernel adds 20 bytes
+USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN))
  
 config PING
   bool "ping"
-  default n
+  default y
   help
     usage: ping [OPTIONS] HOST
 
@@ -27,9 +29,10 @@
     Options:
     -4, -6      Force IPv4 or IPv6
     -c CNT      Send CNT many packets (default 3, 0 = infinite)
-    -f          Flood (. on send, backspace on receive, to show packet drops)
+    -f          Flood (print . and \b to show drops, default -c 15 -i 0.2)
     -i TIME     Interval between packets (default 1, need root for < .2)
     -I IFACE/IP Source interface or address
+    -m MARK     Tag outgoing packets using SO_MARK
     -q          Quiet (stops after one returns true if host is alive)
     -s SIZE     Data SIZE in bytes (default 56)
     -t TTL      Set Time To Live (number of hops)
@@ -51,9 +54,12 @@
   long s;
   long c;
   long t;
+  long m;
 
+  struct sockaddr *sa;
   int sock;
   long i_ms;
+  unsigned long sent, recv, fugit, min, max;
 )
 
 static void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
@@ -65,6 +71,18 @@
   if (rc != len) perror_exit("sendto");
 }
 
+static void summary(int sig)
+{
+  if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) {
+    printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
+    printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
+      TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/TT.sent);
+    printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
+      TT.min, TT.max, TT.fugit/TT.recv);
+  }
+  TT.sa = 0;
+}
+
 // assumes aligned and can read even number of bytes
 static unsigned short pingchksum(unsigned short *data, int len)
 {
@@ -91,9 +109,9 @@
   } src_addr, src_addr2;
   struct sockaddr *sa = (void *)&src_addr, *sa2 = (void *)&src_addr2;
   struct pollfd pfd;
-  int family = 0, sent = 0, len;
+  int family = 0, len;
   long long tnext, tW, tnow, tw;
-  unsigned short seq = 0;
+  unsigned short seq = 0, pkttime;
   struct icmphdr *ih = (void *)toybuf;
 
   // Interval
@@ -103,14 +121,17 @@
     TT.i_ms = xparsetime(TT.i, 1000, &frac) * 1000;
     TT.i_ms += frac;
     if (TT.i_ms<200 && getuid()) error_exit("need root for -i <200");
-  } else TT.i_ms = 1000;
+  } else TT.i_ms = (toys.optflags&FLAG_f) ? 200 : 1000;
   if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN
+  if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15;
 
   // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
-  if (toys.optflags&FLAG_6) family = AF_INET6;
+  if ((toys.optflags&FLAG_6) || toys.which->name[4] == '6') family = AF_INET6;
   else if (toys.optflags&FLAG_4) family = AF_INET;
   else family = 0;
 
+  sigatexit(summary);
+
   // If -I src_addr look it up. Allow numeric address of correct type.
   memset(&src_addr, 0, sizeof(src_addr));
   if (TT.I) {
@@ -146,23 +167,38 @@
 
   if (!ai)
     error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
+  TT.sa = ai->ai_addr;
 
   // Open DGRAM socket
   sa->sa_family = ai->ai_family;
-  TT.sock = xsocket(ai->ai_family, SOCK_DGRAM,
-    (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
+  TT.sock = socket(ai->ai_family, SOCK_DGRAM,
+    len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
+  if (TT.sock == -1) {
+    perror_msg("socket SOCK_DGRAM %x", len);
+    if (errno == EACCES) {
+      fprintf(stderr, "Kernel bug workaround (as root):\n");
+      fprintf(stderr, "echo 0 9999999 > /proc/sys/net/ipv4/ping_group_range\n");
+    }
+    xexit();
+  }
   if (TT.I && bind(TT.sock, sa, sizeof(src_addr))) perror_exit("bind");
 
+  if (toys.optflags&FLAG_m) {
+      int mark = TT.m;
+
+      xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
+  }
+
   if (TT.t) {
     len = TT.t;
 
     if (ai->ai_family == AF_INET)
-      setsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
-    else setsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
+      xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
+    else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
   }
 
   if (!(toys.optflags&FLAG_q)) {
-    printf("Ping %s (%s)", *toys.optargs, ntop(ai->ai_addr));
+    printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
     if (TT.I) {
       *toybuf = 0;
       printf(" from %s (%s)", TT.I, ntop(sa));
@@ -183,7 +219,7 @@
     // Exit due to timeout? (TODO: timeout is after last packet, waiting if
     // any packets ever dropped. Not timeout since packet was dropped.)
     tnow = millitime();
-    if (tW) if (0>=(waitms = tW-tnow) || !sent) break;
+    if (tW) if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
     if (tw) {
       if (tnow>tw) break;
       else if (waitms>tw-tnow) waitms = tw-tnow;
@@ -199,9 +235,9 @@
 
       ih->checksum = 0;
       ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
-      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), ai->ai_addr);
-      sent++;
-      if (toys.optflags&FLAG_f) printf(".");
+      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
+      TT.sent++;
+      if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.');
 
       // last packet?
       if (TT.c) if (!--TT.c) {
@@ -223,24 +259,27 @@
 
     len = sizeof(src_addr2);
     len = recvfrom(TT.sock, toybuf, sizeof(toybuf), 0, sa2, (void *)&len);
-    sent--;
+    TT.recv++;
+    TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
 
     // reply id == 0 for ipv4, 129 for ipv6
 
     if (!(toys.optflags&FLAG_q)) {
-      printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
-             ih->un.echo.sequence, 0);
-      if (len >= sizeof(*ih)+4) {
-        unsigned lunchtime = millitime()-*(unsigned *)(ih+1);
-
-        printf(" time=%u.%03u", lunchtime/1000, lunchtime%1000);
+      if (toys.optflags&FLAG_f) xputc('\b');
+      else {
+        printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
+               ih->un.echo.sequence, 0);
+        if (len >= sizeof(*ih)+4)
+          printf(" time=%u ms", pkttime);
+        xputc('\n');
       }
-      xputc('\n');
     }
 
     toys.exitval = 0;
   }
 
+  summary(0);
+
   if (CFG_TOYBOX_FREE) {
     freeaddrinfo(ai2);
     if (ifa2) freeifaddrs(ifa2);
diff --git a/toys/posix/cut.c b/toys/posix/cut.c
index 2676d93..83582aa 100644
--- a/toys/posix/cut.c
+++ b/toys/posix/cut.c
@@ -29,7 +29,7 @@
     -c	select UTF-8 characters
     -C	select unicode columns
     -d	use DELIM (default is TAB for -f, run of whitespace for -F)
-    -D	Don't sort/collate selections
+    -D	Don't sort/collate selections or match -fF lines without delimiter
     -f	select fields (words) separated by single DELIM character
     -F	select fields separated by DELIM regex
     -O	output delimiter (default one space for -F, input delim for -f)
@@ -70,14 +70,15 @@
   return ss-start;
 }
 
-
 // Apply selections to an input line, producing output
 static void cut_line(char **pline, long len)
 {
   unsigned *pairs = (void *)toybuf;
-  char *line = *pline;
+  char *line;
   int i, j;
 
+  if (!pline) return;
+  line = *pline;
   if (len && line[len-1]=='\n') line[--len] = 0;
 
   // Loop through selections
@@ -154,6 +155,7 @@
 
       // If we never encountered even one separator, print whole line (posix!)
       if (!j && end == start) {
+        if (toys.optflags&FLAG_D) break;
         if (toys.optflags&FLAG_s) return;
         fwrite(line, len, 1, stdout);
         break;
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 2eeb058..49c7b22 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -23,13 +23,16 @@
 
 GLOBALS(
   int max_name_len;
+
+  off_t len;
 )
 
 // We don't trust elf.h to be there, and two codepaths for 32/64 is awkward
 // anyway, so calculate struct offsets manually. (It's a fixed ABI.)
-static void do_elf_file(int fd, struct stat *sb)
+static void do_elf_file(int fd)
 {
-  int endian = toybuf[5], bits = toybuf[4], i, j;
+  int endian = toybuf[5], bits = toybuf[4], i, j, dynamic = 0, stripped = 1,
+      phentsize, phnum, shsize, shnum;
   int64_t (*elf_int)(void *ptr, unsigned size);
   // Values from include/linux/elf-em.h (plus arch/*/include/asm/elf.h)
   // Names are linux/arch/ directory (sometimes before 32/64 bit merges)
@@ -46,11 +49,8 @@
     {191, "tilegx"}, {3, "386"}, {6, "486"}, {62, "x86-64"}, {94, "xtensa"},
     {0xabc7, "xtensa-old"}
   };
-  int dynamic = 0;
-  int stripped = 1;
-  char *map;
+  char *map = 0;
   off_t phoff, shoff;
-  int phentsize, phnum, shsize, shnum;
 
   printf("ELF ");
   elf_int = (endian==2) ? peek_be : peek_le;
@@ -94,6 +94,12 @@
     return;
   }
 
+  // Parsing ELF means following tables that may point to data earlier in
+  // the file, so sequential reading involves buffering unknown amounts of
+  // data. Just skip it if we can't mmap.
+  if (MAP_FAILED == (map = mmap(0, TT.len, PROT_READ, MAP_SHARED, fd, 0)))
+    goto bad;
+
   // Stash what we need from the header; it's okay to reuse toybuf after this.
   phentsize = elf_int(toybuf+42+12*bits, 2);
   phnum = elf_int(toybuf+44+12*bits, 2);
@@ -105,14 +111,19 @@
   // With binutils, phentsize seems to only be non-zero if phnum is non-zero.
   // Such ELF files are rare, but do exist. (Android's crtbegin files, say.)
   if (phnum && (phentsize != 32+24*bits)) {
-    printf(", corrupt phentsize %d?\n", phentsize);
-    return;
+    printf(", corrupt phentsize %d?", phentsize);
+    goto bad;
   }
 
-  map = xmmap(0, sb->st_size, PROT_READ, MAP_SHARED, fd, 0);
+  // Parsing ELF means following tables that may point to data earlier in
+  // the file, so sequential reading involves buffering unknown amounts of
+  // data. Just skip it if we can't mmap.
+  if (MAP_FAILED == (map = mmap(0, TT.len, PROT_READ, MAP_SHARED, fd, 0)))
+    goto bad;
 
   // We need to read the phdrs for dynamic vs static.
   // (Note: fields got reordered for 64 bit)
+  if (phoff+phnum*phentsize>TT.len) goto bad;
   for (i = 0; i<phnum; i++) {
     char *phdr = map+phoff+i*phentsize;
     int p_type = elf_int(phdr, 4);
@@ -125,8 +136,10 @@
     p_offset = elf_int(phdr+4*j, 4*j);
     p_filesz = elf_int(phdr+16*j, 4*j);
 
-    if (p_type==3 /*PT_INTERP*/)
+    if (p_type==3 /*PT_INTERP*/) {
+      if (p_offset+p_filesz>TT.len) goto bad;
       printf(", dynamic (%.*s)", (int)p_filesz, map+p_offset);
+    }
   }
   if (!dynamic) printf(", static");
 
@@ -134,6 +147,7 @@
   // Notes are in program headers *and* section headers, but some files don't
   // contain program headers, so we prefer to check here.
   // (Note: fields got reordered for 64 bit)
+  if (shoff+i*shnum>TT.len) goto bad;
   for (i = 0; i<shnum; i++) {
     char *shdr = map+shoff+i*shsize;
     int sh_type = elf_int(shdr+4, 4);
@@ -146,6 +160,8 @@
     } 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
@@ -158,11 +174,13 @@
 
         if (n_namesz==4 && !memcmp(note+12, "GNU", 4)) {
           if (n_type==3 /*NT_GNU_BUILD_ID*/) {
+            if (n_descsz+16>sh_size) goto bad;
             printf(", BuildID=");
             for (j = 0; j < n_descsz; ++j) printf("%02x", note[16 + j]);
           }
         } else if (n_namesz==8 && !memcmp(note+12, "Android", 8)) {
           if (n_type==1 /*.android.note.ident*/) {
+            if (n_descsz+24+64>sh_size) goto bad;
             printf(", for Android %d", (int)elf_int(note+20, 4));
             if (n_descsz > 24)
               printf(", built by NDK %.64s (%.64s)", note+24, note+24+64);
@@ -175,20 +193,24 @@
     }
   }
   printf(", %sstripped", stripped ? "" : "not ");
+bad:
   xputc('\n');
 
-  munmap(map, sb->st_size);
+  if (map && map != MAP_FAILED) munmap(map, TT.len);
 }
 
-static void do_regular_file(int fd, char *name, struct stat *sb)
+static void do_regular_file(int fd, char *name)
 {
   char *s;
-  int len = read(fd, s = toybuf, sizeof(toybuf)-256);
-  int magic;
+  int len, magic;
 
-  if (len<0) perror_msg("%s", name);
+  // zero through elf shnum, just in case
+  memset(toybuf, 0, 80);
+  if ((len = readall(fd, s = toybuf, sizeof(toybuf)))<0) perror_msg("%s", name);
 
-  if (len>40 && strstart(&s, "\177ELF")) do_elf_file(fd, sb);
+  if (!len) xputs("empty");
+  // 45 bytes: https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
+  else if (len>=45 && strstart(&s, "\177ELF")) do_elf_file(fd);
   else if (len>=8 && strstart(&s, "!<arch>\n")) xprintf("ar archive\n");
   else if (len>28 && strstart(&s, "\x89PNG\x0d\x0a\x1a\x0a")) {
     // PNG is big-endian: https://www.w3.org/TR/PNG/#7Integers-and-byte-order
@@ -360,11 +382,14 @@
 
     xprintf("%s: %*s", name, (int)(TT.max_name_len - strlen(name)), "");
 
+    sb.st_size = 0;
     if (fd || !((toys.optflags & FLAG_L) ? stat : lstat)(name, &sb)) {
       if (fd || S_ISREG(sb.st_mode)) {
-        if (!sb.st_size) what = "empty";
+        TT.len = sb.st_size;
+        // This test identifies an empty file we don't have permission to read
+        if (!fd && !sb.st_size) what = "empty";
         else if ((fd = openro(name, O_RDONLY)) != -1) {
-          do_regular_file(fd, name, &sb);
+          do_regular_file(fd, name);
           if (fd) close(fd);
           continue;
         }
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index c5d487f..bb491eb 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -61,36 +61,28 @@
 
     List processes.
 
-    Which processes to show (selections may be comma separated lists):
+    Which processes to show (-gGuUpPt selections may be comma separated lists):
 
-    -A	All processes
-    -a	Processes with terminals that aren't session leaders
-    -d	All processes that aren't session leaders
-    -e	Same as -A
-    -g	Belonging to GROUPs
-    -G	Belonging to real GROUPs (before sgid)
-    -p	PIDs (--pid)
-    -P	Parent PIDs (--ppid)
-    -s	In session IDs
-    -t	Attached to selected TTYs
-    -T	Show threads
-    -u	Owned by USERs
-    -U	Owned by real USERs (before suid)
+    -A  all					-a  has terminal not session leader
+    -d  All but session leaders		-e  synonym for -A
+    -g  in GROUPs				-G  in real GROUPs (before sgid)
+    -p  PIDs (--pid)			-P  Parent PIDs (--ppid)
+    -s  In session IDs			-t  Attached to selected TTYs
+    -T  Show threads also			-u  Owned by selected USERs
+    -U  real USERs (before suid)
 
     Output modifiers:
 
-    -k	Sort FIELDs in +increasing or -decreasting order (--sort)
-    -M	Measure field widths (expanding as necessary)
-    -n	Show numeric USER and GROUP
-    -w	Wide output (don't truncate fields)
+    -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)
 
-    -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)
-    -o	Output FIELDs instead of defaults, each with optional :size and =title
-    -O	Add FIELDS to defaults
-    -Z	Include LABEL
+    -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)
+    -o  Output FIELDs instead of defaults, each with optional :size and =title
+    -O  Add FIELDS to defaults
+    -Z  Include LABEL
 
     Command line -o fields:
 
@@ -126,7 +118,7 @@
       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 What are we waiting in kernel for
+      WCHAN Wait location in kernel
 
 config TOP
   bool "top"
@@ -651,7 +643,7 @@
     if (!abslen) putchar('+');
     if (!width) break;
   }
-  xputc(TT.time ? '\r' : '\n');
+  putchar(TT.time ? '\r' : '\n');
 }
 
 // dirtree callback: read data about process to display, store, or discard it.
@@ -1382,6 +1374,10 @@
  
   unsigned tock = 0;
   int i, lines, topoff = 0, done = 0;
+  char stdout_buf[BUFSIZ];
+
+  // Avoid flicker in interactive mode.
+  if (!(toys.optflags&FLAG_b)) setbuf(stdout, stdout_buf);
 
   toys.signal = SIGWINCH;
   TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
@@ -1558,8 +1554,13 @@
       recalc = 1;
 
       for (i = 0; i<lines && i+topoff<mix.count; i++) {
-        if (!(toys.optflags&FLAG_b) && i) xputc('\n');
+        // Running processes are shown in bold.
+        int bold = !(toys.optflags&FLAG_b) && mix.tb[i+topoff]->state == 'R';
+
+        if (!(toys.optflags&FLAG_b) && i) putchar('\n');
+        if (bold) printf("\033[1m");
         show_ps(mix.tb[i+topoff]);
+        if (bold) printf("\033[m");
       }
 
       if (TT.top.n && !--TT.top.n) {
@@ -1577,7 +1578,7 @@
         // Make an obvious gap between datasets.
         xputs("\n\n");
         continue;
-      }
+      } else fflush(stdout);
 
       i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
       if (i==-1 || i==3 || toupper(i)=='Q') {
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index cf7d15e..17d8bcd 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -6,9 +6,10 @@
  *
  * TODO: lines > 2G could wrap signed int length counters. Not just getline()
  * but N and s///
- * TODO: make y// handle unicode
+ * TODO: make y// handle unicode, unicode delimiters
  * TODO: handle error return from emit(), error_msg/exit consistently
  *       What's the right thing to do for -i when write fails? Skip to next?
+ * test '//q' with no previous regex, also repeat previous regex?
 
 USE_SED(NEWTOY(sed, "(help)(version)e*f*inEr[+Er]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE|TOYFLAG_NOHELP))
 
@@ -247,7 +248,7 @@
 }
 
 // Apply pattern to line from input file
-static void process_line(char **pline, long plen)
+static void sed_line(char **pline, long plen)
 {
   struct append {
     struct append *next, *prev;
@@ -259,6 +260,9 @@
   struct sedcmd *command;
   int eol = 0, tea = 0;
 
+  // Ignore EOF for all files before last unless -i
+  if (!pline && !(toys.optflags&FLAG_i)) return;
+
   // Grab next line for deferred processing (EOF detection: we get a NULL
   // pline at EOF to flush last line). Note that only end of _last_ input
   // file matches $ (unless we're doing -i).
@@ -622,7 +626,7 @@
 }
 
 // Callback called on each input file
-static void do_sed(int fd, char *name)
+static void do_sed_file(int fd, char *name)
 {
   int i = toys.optflags & FLAG_i;
   char *tmp;
@@ -630,18 +634,14 @@
   if (i) {
     struct sedcmd *command;
 
-    if (!fd) {
-      error_msg("-i on stdin");
-      return;
-    }
+    if (!fd) return error_msg("-i on stdin");
     TT.fdout = copy_tempfile(fd, name, &tmp);
     TT.count = 0;
     for (command = (void *)TT.pattern; command; command = command->next)
       command->hit = 0;
   }
-  do_lines(fd, process_line);
+  do_lines(fd, sed_line);
   if (i) {
-    process_line(0, 0);
     replace_tempfile(-1, TT.fdout, &tmp);
     TT.fdout = 1;
     TT.nextline = 0;
@@ -727,7 +727,7 @@
 
   // Append this line to previous multiline command? (hit indicates type.)
   // During parsing "hit" stores data about line continuations, but in
-  // process_line() it means the match range attached to this command
+  // sed_line() it means the match range attached to this command
   // is active, so processing the continuation must zero it again.
   if (command && command->prev->hit) {
     // Remove half-finished entry from list so remalloc() doesn't confuse it
@@ -1016,8 +1016,8 @@
   // so handle all -e, then all -f. (At least the behavior's consistent.)
 
   for (al = TT.e; al; al = al->next) parse_pattern(&al->arg, strlen(al->arg));
-  for (al = TT.f; al; al = al->next) do_lines(xopenro(al->arg), parse_pattern);
   parse_pattern(0, 0);
+  for (al = TT.f; al; al = al->next) do_lines(xopenro(al->arg), parse_pattern);
   dlist_terminate(TT.pattern);
   if (TT.nextlen) error_exit("no }");  
 
@@ -1025,9 +1025,13 @@
   TT.remember = xstrdup("");
 
   // Inflict pattern upon input files. Long version because !O_CLOEXEC
-  loopfiles_rw(args, O_RDONLY|WARN_ONLY, 0, do_sed);
+  loopfiles_rw(args, O_RDONLY|WARN_ONLY, 0, do_sed_file);
 
-  if (!(toys.optflags & FLAG_i)) process_line(0, 0);
+  // Provide EOF flush at end of cumulative input for non-i mode.
+  if (!(toys.optflags & FLAG_i)) {
+    toys.optflags |= FLAG_i;
+    sed_line(0, 0);
+  }
 
   // todo: need to close fd when done for TOYBOX_FREE?
 }
diff --git a/www/roadmap.html b/www/roadmap.html
index 020497e..7e3fd1c 100755
--- a/www/roadmap.html
+++ b/www/roadmap.html
@@ -34,6 +34,7 @@
 <li><a href=#dev_env>Development Environment</a></li>
 <li><a href=#android>Android Toolbox</a></li>
 <li><a href=#tizen>Tizen Core</a></li>
+<li><a href=#buildroot>buildroot</a></li>
 <li>Miscelaneous: <a href=#klibc>klibc</a>, <a href=#glibc>glibc</a>,
 <a href=#sash>sash</a>, <a href=#sbase>sbase</a>,
 <a href=#uclinux>uclinux</a>...</li>
@@ -369,6 +370,26 @@
 many of the SELinux options ala ls -Z need smack alternatives in an
 if/else setup.</p>
 
+<hr /><a name=buildroot />
+<h2>buildroot:</h2>
+
+<p>The <a href=https://buildroot.org/downloads/manual/manual.html#requirement-mandatory>mandatory packages</a>
+section of the buildroot manual lists:</p>
+
+<blockquote><p>
+which sed make bash patch gzip bzip2 tar cpio unzip rsync file bc wget
+</p></blockquote>
+
+<p>(It also lists binutils gcc g++ perl python, and for debian it wants
+build-essential. And it wants file to be in /usr/bin because
+<a href=https://git.busybox.net/buildroot/tree/support/dependencies/dependencies.sh?h=2018.02.x#n84>libtool
+breaks otherwise</a>.)</p>
+
+<p>Buildroot does not support a cross toolchain that lives in "/usr/bin"
+with a prefix of "" (if you try, and chop out the test for a blank prefix,
+it dies trying to run "/usr/bin/-gcc"). But you can patch your way to
+making it work if you try.</p>
+
 <hr /><a name=klibc />
 <h2>klibc:</h2>
 
@@ -952,6 +973,7 @@
 deallocvt iorenice
 udpsvd adduser
 microcom tunctl chrt getfattr setfattr
+kexec
 </span>
 </b></blockquote>