Merge remote-tracking branch 'toybox/master' into HEAD
diff --git a/lib/getmountlist.c b/lib/getmountlist.c
index 7a43101..332852a 100644
--- a/lib/getmountlist.c
+++ b/lib/getmountlist.c
@@ -19,7 +19,7 @@
     arg = al->arg;
     while ((next = comma_iterate(&arg, &len)))
       if ((next = callback(data, next, len)))
-        perror_exit("%s '%s'\n%*c", err, al->arg,
+        error_exit("%s '%s'\n%*c", err, al->arg,
           (int)(5+strlen(toys.which->name)+strlen(err)+next-al->arg), '^');
     al = al->next;
   }
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c
index 93e2ec5..c4ea2c2 100644
--- a/lib/interestingtimes.c
+++ b/lib/interestingtimes.c
@@ -67,10 +67,14 @@
 {
   int key;
 
-  while (512&(key = scan_key(scratch, miliwait))) {
-    if (key<0) break;
-    if (xx) *xx = (key>>10)&1023;
-    if (yy) *yy = (key>>20)&1023;
+  if (512&(key = scan_key(scratch, miliwait))) {
+    if (key>0) {
+      if (xx) *xx = (key>>10)&1023;
+      if (yy) *yy = (key>>20)&1023;
+      toys.signal = SIGWINCH;
+
+      return -3;
+    }
   }
 
   return key;
@@ -195,7 +199,8 @@
 
     // Read 1 byte so we don't overshoot sequence match. (We can deviate
     // and fail to match, but match consumes entire buffer.)
-    if (1 != read(0, scratch+1+*scratch, 1)) return -1;
+    if (toys.signal || 1 != read(0, scratch+1+*scratch, 1))
+      return toys.signal ? -3 : -1;
     ++*scratch;
   }
 
diff --git a/lib/lib.c b/lib/lib.c
index b0ac471..6559030 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1015,3 +1015,21 @@
 
   return libbuf;
 }
+
+// Returns pointer to letter at end, 0 if none. *start = initial %
+char *next_printf(char *s, char **start)
+{
+  for (; *s; s++) {
+    if (*s != '%') continue;
+    if (*++s == '%') continue;
+    if (start) *start = s-1;
+    while (0 <= stridx("0'#-+ ", *s)) s++;
+    while (isdigit(*s)) s++;
+    if (*s == '.') s++;
+    while (isdigit(*s)) s++;
+
+    return s;
+  }
+
+  return 0;
+}
diff --git a/lib/lib.h b/lib/lib.h
index f1cea22..5733103 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -101,6 +101,7 @@
 void *xrealloc(void *ptr, size_t size);
 char *xstrndup(char *s, size_t n);
 char *xstrdup(char *s);
+void *xmemdup(void *s, long len);
 char *xmprintf(char *format, ...) printf_format;
 void xprintf(char *format, ...) printf_format;
 void xputs(char *s);
@@ -196,6 +197,7 @@
 int qstrcmp(const void *a, const void *b);
 void create_uuid(char *uuid);
 char *show_uuid(char *uuid);
+char *next_printf(char *s, char **start);
 
 #define HR_SPACE 1 // Space between number and units
 #define HR_B     2 // Use "B" for single byte units
diff --git a/lib/net.c b/lib/net.c
index facb096..2e72b26 100644
--- a/lib/net.c
+++ b/lib/net.c
@@ -49,6 +49,7 @@
 
   for (;;) {
     if (0>(i = poll(fds, nfds, timeout))) {
+      if (toys.signal) return i;
       if (errno != EINTR && errno != ENOMEM) perror_exit("xpoll");
       else if (timeout>0) timeout--;
     } else return i;
diff --git a/lib/xwrap.c b/lib/xwrap.c
index a20d4b6..65e3018 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -21,9 +21,9 @@
 
 void xstrncat(char *dest, char *src, size_t size)
 {
-  long len = strlen(src);
+  long len = strlen(dest);
 
-  if (len+strlen(dest)+1 > size)
+  if (len+strlen(src)+1 > size)
     error_exit("'%s%s' > %ld bytes", dest, src, (long)size);
   strcpy(dest+len, src);
 }
@@ -80,6 +80,14 @@
   return xstrndup(s, strlen(s));
 }
 
+void *xmemdup(void *s, long len)
+{
+  void *ret = xmalloc(len);
+  memcpy(ret, s, len);
+
+  return ret;
+}
+
 // Die unless we can allocate enough space to sprintf() into.
 char *xmprintf(char *format, ...)
 {
diff --git a/main.c b/main.c
index 3949c84..e95c641 100644
--- a/main.c
+++ b/main.c
@@ -96,15 +96,20 @@
 // Full init needed by multiplexer or reentrant calls, calls singleinit at end
 void toy_init(struct toy_list *which, char *argv[])
 {
+  void *oldwhich = toys.which;
+
   // Drop permissions for non-suid commands.
 
   if (CFG_TOYBOX_SUID) {
+    if (!toys.which) toys.which = toy_list;
+
     uid_t uid = getuid(), euid = geteuid();
 
     if (!(which->flags & TOYFLAG_STAYROOT)) {
       if (uid != euid) {
         if (!setuid(uid)) perror_exit("setuid %d->%d", euid, uid); // drop root
-        else euid = uid;
+        euid = uid;
+        toys.wasroot++;
       }
     } else if (CFG_TOYBOX_DEBUG && uid && which != toy_list)
       error_msg("Not installed suid root");
@@ -116,7 +121,7 @@
   // don't blank old optargs if our new argc lives in the old optargs.
   if (argv<toys.optargs || argv>toys.optargs+toys.optc) free(toys.optargs);
   memset(&toys, 0, offsetof(struct toy_context, rebound));
-  if (toys.which) memset(&this, 0, sizeof(this));
+  if (oldwhich) memset(&this, 0, sizeof(this));
 
   // Continue to portion of init needed by standalone commands
   toy_singleinit(which, argv);
@@ -136,7 +141,7 @@
     return;
 
   // Return if we need to re-exec to acquire root via suid bit.
-  if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && getuid()) return;
+  if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && toys.wasroot) return;
 
   // Run command
   toy_init(which, argv);
diff --git a/tests/blkid.test b/tests/blkid.test
index 25ba9d1..b80cf4f 100755
--- a/tests/blkid.test
+++ b/tests/blkid.test
@@ -29,7 +29,7 @@
 testing "blkid ntfs" 'bzcat "$BDIR"/ntfs.bz2 | blkid -' \
   '-: UUID="8585600838bfe16e" TYPE="ntfs"\n' "" ""
 testing "blkid reiserfs" 'bzcat "$BDIR"/reiser3.bz2 | blkid -' \
-  '-: LABEL="" UUID="a5b99bec-45cc-41d7-986e-32f4b6fc28f2" TYPE="reiserfs"\n' \
+  '-: LABEL="myreiser" UUID="a5b99bec-45cc-41d7-986e-32f4b6fc28f2" TYPE="reiserfs"\n' \
   "" ""
 testing "blkid squashfs" 'bzcat "$BDIR"/squashfs.bz2 | blkid -' \
   '-: TYPE="squashfs"\n' "" ""
diff --git a/tests/cat.test b/tests/cat.test
index 3d5842a..7be515d 100755
--- a/tests/cat.test
+++ b/tests/cat.test
@@ -16,8 +16,9 @@
         "cat file1 notfound file2 2>stderr && echo ok ; cat stderr; rm stderr" \
         "one\ntwo\ncat: notfound: No such file or directory\n" "" ""
 
+FILE="$(readlink -f /proc/self/exe)"
 testing "cat file1" \
-        "cat /proc/self/exe > file1 && cmp /proc/self/exe file1 && echo yes" \
+        'cat "$FILE" > file1 && cmp "$FILE" file1 && echo yes' \
         "yes\n" "" ""
 
 testing "cat - file1" \
@@ -28,4 +29,4 @@
         "cat - > /dev/full 2>stderr && echo ok; cat stderr; rm stderr" \
         "cat: xwrite: No space left on device\n" "" "zero\n"
 
-rm file1 file2
\ No newline at end of file
+rm file1 file2
diff --git a/tests/chgrp.test b/tests/chgrp.test
index 2af9385..5a741d9 100755
--- a/tests/chgrp.test
+++ b/tests/chgrp.test
@@ -4,7 +4,7 @@
 
 if [ "$(id -u)" -ne 0 ]
 then
-  echo "SKIPPED: chgrp (not root)"
+  echo "$SHOWSKIP: chgrp (not root)"
   continue 2>/dev/null
   exit
 fi
diff --git a/tests/chmod.test b/tests/chmod.test
index 77106a6..887f738 100755
--- a/tests/chmod.test
+++ b/tests/chmod.test
@@ -5,6 +5,8 @@
 
 #testing "name" "command" "result" "infile" "stdin"
 
+umask 022
+
 PERM="---""--x""-w-""-wx""r--""r-x""rw-""rwx"
 
 num2perm()
@@ -21,7 +23,7 @@
 mkdir dir
 touch file
 
-# We don't need to test all 511 permissions
+# We don't need to test all 512 permissions
 for u in 0 1 2 3 4 5 6 7
 do
   for g in 0 3 6
diff --git a/tests/chown.test b/tests/chown.test
index 40ed7b5..408d5f3 100755
--- a/tests/chown.test
+++ b/tests/chown.test
@@ -4,7 +4,7 @@
 
 if [ "$(id -u)" -ne 0 ]
 then
-  echo "SKIPPED: chown (not root)"
+  echo "$SHOWSKIP: chown (not root)"
   continue 2>/dev/null
   exit
 fi
diff --git a/tests/cpio.test b/tests/cpio.test
index d0528e5..2d91b06 100755
--- a/tests/cpio.test
+++ b/tests/cpio.test
@@ -25,10 +25,10 @@
 # the relevant bit should be here:
 # 110*5 + 4*3 + 2 + 6*3 = 550 + 12 + 20 = 582
 # files are padded to n*4, names are padded to 2 + n*4 due to the header length
-testing "cpio archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
-testing "cpio archive magic" "cpio -o -H newc|dd ibs=2 count=3" "070701" "" "a\n"
+testing "cpio archive length" "cpio -o -H newc|dd ibs=2 skip=291 count=5 2>/dev/null" "TRAILER!!!" "" "a\nbb\nccc\ndddd\n"
+testing "cpio archive magic" "cpio -o -H newc|dd ibs=2 count=3 2>/dev/null" "070701" "" "a\n"
 # check name length (8 bytes before the empty "crc")
-testing "cpio name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4" "00000002" "" "a\n"
+testing "cpio name length" "cpio -o -H newc|dd ibs=2 skip=47 count=4 2>/dev/null" "00000002" "" "a\n"
 rm a bb ccc dddd
 
 # archive dangling symlinks and empty files even if we cannot open them
diff --git a/tests/find.test b/tests/find.test
index 71a3506..4e987b6 100755
--- a/tests/find.test
+++ b/tests/find.test
@@ -70,7 +70,7 @@
 # Still fails
 
 testing "find unterminated -exec {}" \
-  "find dir -type f -exec ls {}" "" "" ""
+  "find dir -type f -exec ls {} 2>/dev/null || echo bad" "bad\n" "" ""
 testing "find -exec {} +" \
   "find dir -type f -exec ls {} +" "dir/file\n" "" ""
 
diff --git a/tests/grep.test b/tests/grep.test
index f3bc9a3..aa11c70 100755
--- a/tests/grep.test
+++ b/tests/grep.test
@@ -97,3 +97,11 @@
 testing "grep -o ''" "grep -o '' input" "" "one one one\n" ""
 testing "grep backref" 'grep -e "a\(b\)" -e "b\(c\)\1"' "bcc\nab\n" \
   "" "bcc\nbcb\nab\n"
+
+testing "grep -A" "grep -A 2 yes" "yes\nno\nno\n--\nyes\nno\nno\nyes\nno\n" \
+  "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno"
+testing "grep -B" "grep -B 1 yes" "no\nyes\n--\nno\nyes\nno\nyes\n" \
+  "" "no\nno\nno\nyes\nno\nno\nyes\nno\nyes"
+testing "grep -C" "grep -C 1 yes" \
+  "yes\nno\n--\nno\nyes\nno\nno\nyes\nno\nyes\nno\n" \
+  "" "yes\nno\nno\nno\nyes\nno\nno\nyes\nno\nyes\nno\nno"
diff --git a/tests/ifconfig.test b/tests/ifconfig.test
index 0c0af46..c30c74c 100755
--- a/tests/ifconfig.test
+++ b/tests/ifconfig.test
@@ -23,7 +23,7 @@
 
 if [ "$(id -u)" -ne 0 ]
 then
-  echo "SKIPPED: ifconfig (not root)"
+  echo "$SHOWSKIP: ifconfig (not root)"
   continue 2>/dev/null
   exit
 fi
diff --git a/tests/losetup.test b/tests/losetup.test
index fe1e520..84ac4b1 100755
--- a/tests/losetup.test
+++ b/tests/losetup.test
@@ -4,7 +4,7 @@
 
 if [ "$(id -u)" -ne 0 ]
 then
-  echo "SKIPPED: losetup (not root)"
+  echo "$SHOWSKIP: losetup (not root)"
   continue 2>/dev/null
   exit
 fi
diff --git a/tests/seq.test b/tests/seq.test
index 4d23f8e..5996617 100755
--- a/tests/seq.test
+++ b/tests/seq.test
@@ -28,14 +28,14 @@
 
 # Test -f format filtering
 for i in %f %e %g "boo %f yah" "% f" %-1.2f %+-f "%+ - f" %.2f %3.f "%'.2f" \
-	%%%f%% %%%f%%%
+	%%%f%%
 do
   testing "seq filter -f \"$i\"" "seq -f \"$i\" 1 3 > /dev/null && echo yes" \
     "yes\n" "" ""
 done
 # Test -f format filtering failures
 for i in %d %s "" "boo %f %f yah" "%*f" %-1.2.3f '%2$f' %1-f "%1 f" \
-	%2..2f
+	%2..2f %%%f%%%
 do
   testing "seq filter reject -f '$i'" \
     "seq -f '$i' 1 3 2>/dev/null || echo no" "no\n" "" ""
diff --git a/tests/useradd.test b/tests/useradd.test
index a954e72..6ffe707 100755
--- a/tests/useradd.test
+++ b/tests/useradd.test
@@ -7,7 +7,7 @@
 
 if [ "$(id -u)" -ne 0 ]
 then
-  echo "SKIPPED: useradd (not root)"
+  echo "$SHOWSKIP: useradd (not root)"
   continue 2>/dev/null
   exit
 fi
diff --git a/toys.h b/toys.h
index 8bd3be2..4bfccb1 100644
--- a/toys.h
+++ b/toys.h
@@ -127,13 +127,14 @@
   int exitval;             // Value error_exit feeds to exit()
   int optc;                // Count of optargs
   int old_umask;           // Old umask preserved by TOYFLAG_UMASK
-  int toycount;            // Total number of commands in this build
-  int signal;              // generic_signal() records what signal it saw here
+  short toycount;          // Total number of commands in this build
+  short signal;            // generic_signal() records what signal it saw here
   int signalfd;            // and writes signal to this fd, if set
+  int wasroot;             // dropped setuid
 
   // This is at the end so toy_init() doesn't zero it.
   jmp_buf *rebound;        // longjmp here instead of exit when do_rebound set
-  void *stacktop;          // nested toy_exec() call count, or -1 if vforked
+  void *stacktop;          // nested toy_exec() call count, or 0 if vforked
 } toys;
 
 // Two big temporary buffers: one for use by commands, one for library functions
diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c
index c620474..3ff9ca4 100644
--- a/toys/lsb/seq.c
+++ b/toys/lsb/seq.c
@@ -32,22 +32,13 @@
 // Ensure there's one %f escape with correct attributes
 static void insanitize(char *f)
 {
-  char *s;
-  int found = 0;
+  char *s = next_printf(f, 0);
 
-  for (s = f; *s; s++) {
-    if (*s != '%') continue;
-    if (*++s == '%') continue;
-    if (found++) break;
-    while (0 <= stridx("0'#-+ ", *s)) s++;
-    while (isdigit(*s)) s++;
-    if (*s == '.') s++;
-    while (isdigit(*s)) s++;
-    if (-1 == stridx("aAeEfFgG", *s)) break;
+  if (!s) error_exit("bad -f no %%f");
+  if (-1 == stridx("aAeEfFgG", *s) || (s = next_printf(s, 0))) {
+    // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain.
+    error_exit("bad -f '%s'@%ld", f, s-f+1);
   }
-
-  // The @ is a byte offset, not utf8 chars. Waiting for somebody to complain...
-  if (*s || !found) error_exit("bad -f '%s'@%ld", f, s-f+1);
 }
 
 void seq_main(void)
diff --git a/toys/other/blkid.c b/toys/other/blkid.c
index 8aab8f7..2880812 100644
--- a/toys/other/blkid.c
+++ b/toys/other/blkid.c
@@ -48,7 +48,7 @@
   {"jfs", 0x3153464a, 4, 32768, 32920, 16, 32904},
   {"nilfs", 0x3434, 2, 1030, 1176, 80, 1192},
   {"reiserfs", 0x724573496552ULL, 6, 8244, 8276, 16, 8292},
-  {"reiserfs", 0x724573496552ULL, 6, 65588, 65620, 16, 65536},
+  {"reiserfs", 0x724573496552ULL, 6, 65588, 65620, 16, 65636},
   {"romfs", 0x2d6d6f72, 4, 0, 0,0,0},
   {"squashfs", 0x73717368, 4, 0, 0,0,0},
   {"xiafs", 0x012fd16d, 4, 572, 0,0,0},
diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c
index bda77ac..1375728 100644
--- a/toys/other/nsenter.c
+++ b/toys/other/nsenter.c
@@ -13,7 +13,7 @@
 
 // Note: flags go in same order (right to left) for shared subset
 USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
-USE_UNSHARE(NEWTOY(unshare, "<1^rimnpuU", TOYFLAG_USR|TOYFLAG_BIN))
+USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
 
 config UNSHARE
   bool "unshare"
@@ -25,13 +25,14 @@
     Create new container namespace(s) for this process and its children, so
     some attribute is not shared with the parent process.
 
-    -i	SysV IPC (message queues, semaphores, shared memory)
-    -m	Mount/unmount tree
-    -n	Network address, sockets, routing, iptables
-    -p	Process IDs and init
-    -r	Become root (map current euid/egid to 0/0, implies -U)
-    -u	Host and domain names
-    -U	UIDs, GIDs, capabilities
+    -f  Fork command in the background (--fork)
+    -i	SysV IPC (message queues, semaphores, shared memory) (--ipc)
+    -m	Mount/unmount tree (--mount)
+    -n	Network address, sockets, routing, iptables (--net)
+    -p	Process IDs and init (--pid)
+    -r	Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
+    -u	Host and domain names (--uts)
+    -U	UIDs, GIDs, capabilities (--user)
 
     A namespace allows a set of processes to have a different view of the
     system than other sets of processes.
@@ -50,7 +51,7 @@
     The namespaces to switch are:
 
     -i	SysV IPC: message queues, semaphores, shared memory (--ipc)
-    -m	Mount/unmount tree (--mnt)
+    -m	Mount/unmount tree (--mount)
     -n	Network address, sockets, routing, iptables (--net)
     -p	Process IDs and init, will fork unless -F is used (--pid)
     -u	Host and domain names (--uts)
@@ -102,6 +103,11 @@
   return toys.optflags & FLAG_r;
 }
 
+static int test_f()
+{
+  return toys.optflags & FLAG_f;
+}
+
 // Shift back to the context GLOBALS lives in (I.E. matching the filename).
 #define CLEANUP_unshare
 #define FOR_nsenter
@@ -127,6 +133,11 @@
     if (unshare(f)) perror_exit(0);
     if (test_r()) handle_r(euid, egid);
 
+    if (test_f()) {
+      toys.exitval = xrun(toys.optargs);
+
+      return;
+    }
   // Bind to existing namespace(s)?
   } else if (CFG_NSENTER) {
     char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc";
diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c
index 830331a..4a4f55d 100644
--- a/toys/pending/traceroute.c
+++ b/toys/pending/traceroute.c
@@ -254,7 +254,7 @@
             struct ip *rcv_pkt = (struct ip*) toybuf;
             struct icmp *ricmp;
 
-            ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2));
+            ricmp = (struct icmp *) ((char*)rcv_pkt + (rcv_pkt->ip_hl << 2));
             if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG)
               pmtu = ntohs(ricmp->icmp_nextmtu);
 
@@ -275,7 +275,7 @@
                   icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 :
                       ricmp->icmp_code);
               } else {
-                hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2));
+                hicmp = (struct icmp *) ((char*)hip + (hip->ip_hl << 2));
                 if (ricmp->icmp_type == ICMP_ECHOREPLY 
                     && ricmp->icmp_id == ntohs(TT.ident)
                     && ricmp->icmp_seq == ntohs(seq))
@@ -481,7 +481,7 @@
   int lsrr = 0, set = 1;
   
   if(!(toys.optflags & FLAG_4) && 
-      (inet_pton(AF_INET6, toys.optargs[0], (void*)&dest)))
+      (inet_pton(AF_INET6, toys.optargs[0], &dest)))
     toys.optflags |= FLAG_6;
 
   memset(&dest, 0, sizeof(dest));
@@ -562,7 +562,7 @@
   if (!TT.istraceroute6) {
     if ((toys.optflags & FLAG_t) && 
         setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
-      perror_exit("IP_TOS %d failed ", TT.tos);
+      perror_exit("IP_TOS %ld failed ", TT.tos);
 
 #ifdef IP_DONTFRAG
     if ((toys.optflags & FLAG_F) &&
@@ -570,7 +570,7 @@
                     sizeof(set)) < 0)) perror_exit("IP_DONTFRAG failed ");
 #endif
   } else if (setsockopt(TT.snd_sock, IPPROTO_IPV6, IPV6_TCLASS, &TT.tos,
-        sizeof(TT.tos)) < 0) perror_exit("IPV6_TCLASS %d failed ", TT.tos);
+        sizeof(TT.tos)) < 0) perror_exit("IPV6_TCLASS %ld failed ", TT.tos);
 
   set_flag_dr(TT.snd_sock);
   TT.packet = xzalloc(TT.msg_len);
@@ -592,7 +592,7 @@
     }
 
     if(TT.first_ttl > TT.max_ttl) 
-      error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl);
+      error_exit("ERROR :Range for -f is 1 to %ld (max ttl)", TT.max_ttl);
 
     xprintf("traceroute to %s(%s)", toys.optargs[0], 
            inet_ntoa(((struct sockaddr_in *)&dest)->sin_addr));
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index f932ca4..cb7e6e3 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -177,7 +177,7 @@
     if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
       char *s;
 
-      if (S_ISDIR(try->st.st_dev)) {
+      if (S_ISDIR(try->st.st_mode)) {
         error_msg("dir at '%s'", s = dirtree_path(try, 0));
         free(s);
         return 0;
diff --git a/toys/posix/find.c b/toys/posix/find.c
index e16c0df..febe688 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -8,7 +8,7 @@
  * Parentheses can only stack 4096 deep
  * Not treating two {} as an error, but only using last
  *
- * TODO: -empty (dirs too!) -delete -execdir +
+ * TODO: -empty (dirs too!)
 
 USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
 
@@ -46,6 +46,7 @@
     -print   Print match with newline  -print0    Print match with null
     -exec    Run command with path     -execdir   Run command in file's dir
     -ok      Ask before exec           -okdir     Ask before execdir
+    -delete  Remove matching file/dir
 
     Commands substitute "{}" with matched file. End with ";" to run each file,
     or "+" (next argument after "{}") to collect and run with multiple files.
@@ -277,7 +278,13 @@
     } else s++;
 
     if (!strcmp(s, "xdev")) TT.xdev = 1;
-    else if (!strcmp(s, "depth")) TT.depth = 1;
+    else if (!strcmp(s, "delete")) {
+      // Delete forces depth first
+      TT.depth = 1;
+      if (new && check)
+        test = !unlinkat(dirtree_parentfd(new), new->name,
+          S_ISDIR(new->st.st_mode) ? AT_REMOVEDIR : 0);
+    } else if (!strcmp(s, "depth")) TT.depth = 1;
     else if (!strcmp(s, "o") || !strcmp(s, "or")) {
       if (not) goto error;
       if (active) {
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index 1cb4f9c..92d723b 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -6,7 +6,7 @@
  *
  * TODO: -ABC
 
-USE_GREP(NEWTOY(grep, "ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
+USE_GREP(NEWTOY(grep, "C#B#A#ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
 USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
 USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
 
@@ -14,7 +14,7 @@
   bool "grep"
   default y
   help
-    usage: grep [-EFivwcloqsHbhn] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
+    usage: grep [-EFivwcloqsHbhn] [-A NUM] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
 
     Show lines matching regular expressions. If no -e, first argument is
     regular expression to match. With no files (or "-" filename) read stdin.
@@ -24,11 +24,12 @@
     -f  File containing regular expressions to match.
 
     match type:
-    -E  extended regex syntax    -F  fixed (match literal string)
-    -i  case insensitive         -m  stop after this many lines matched
-    -r  recursive (on dir)       -v  invert match
-    -w  whole word (implies -E)  -x  whole line
-    -z  input NUL terminated
+    -A  Show NUM lines after     -B  Show NUM lines before match
+    -C  NUM lines context (A+B)  -E  extended regex syntax
+    -F  fixed (literal match)    -i  case insensitive
+    -m  match MAX many lines     -r  recursive (on dir)
+    -v  invert match             -w  whole word (implies -E)
+    -x  whole line               -z  input NUL terminated
 
     display modes: (default: matched line)
     -c  count of matching lines  -l  show matching filenames
@@ -58,15 +59,19 @@
   long m;
   struct arg_list *f;
   struct arg_list *e;
+  long a;
+  long b;
+  long c;
 )
 
 // Show matches in one file
 static void do_grep(int fd, char *name)
 {
+  struct double_list *dlb = 0;
   FILE *file = fdopen(fd, "r");
-  long offset = 0;
+  long offset = 0, after = 0, before = 0;
   int lcount = 0, mcount = 0;
-  char indelim = '\n' * !(toys.optflags&FLAG_z),
+  char *bars = 0, indelim = '\n' * !(toys.optflags&FLAG_z),
        outdelim = '\n' * !(toys.optflags&FLAG_Z);
 
   if (!fd) name = "(standard input)";
@@ -164,6 +169,11 @@
         matches.rm_so = 0;
       } else if (rc) break;
 
+      // At least one line we didn't print since match while -ABC active
+      if (bars) {
+        xputs(bars);
+        bars = 0;
+      }
       mmatch++;
       toys.exitval = 0;
       if (toys.optflags & FLAG_q) xexit();
@@ -183,10 +193,22 @@
         if (toys.optflags & FLAG_b)
           printf("%ld:", offset + (start-line) +
               ((toys.optflags & FLAG_o) ? matches.rm_so : 0));
-        if (!(toys.optflags & FLAG_o)) xprintf("%s%c", line, outdelim);
-        else {
-          xprintf("%.*s%c", matches.rm_eo - matches.rm_so,
-                  start + matches.rm_so, outdelim);
+        if (!(toys.optflags & FLAG_o)) {
+          while (dlb) {
+            struct double_list *dl = dlist_pop(&dlb);
+
+            xprintf("%s%c", dl->data, outdelim);
+            free(dl->data);
+            free(dl);
+            before--;
+          }
+
+          while (before) xprintf("%s%c", line, outdelim);
+          xprintf("%s%c", line, outdelim);
+          if (TT.a) after = TT.a+1;
+        } else {
+          xprintf("%.*s%c", (int)(matches.rm_eo-matches.rm_so),
+                  start+matches.rm_so, outdelim);
         }
       }
 
@@ -195,9 +217,30 @@
     } while (*start);
     offset += len;
 
+    if (mmatch) mcount++;
+    else {
+      int discard = (after || TT.b);
+
+      if (after && --after) {
+        xprintf("%s%c", line, outdelim);
+        discard = 0;
+      }
+      if (discard && TT.b) {
+        dlist_add(&dlb, line);
+        line = 0;
+        if (++before>TT.b) {
+          struct double_list *dl;
+
+          dl = dlist_pop(&dlb);
+          free(dl->data);
+          free(dl);
+          before--;
+        } else discard = 0;
+      }
+      if (discard && mcount) bars = "--";
+    }
     free(line);
 
-    if (mmatch) mcount++;
     if ((toys.optflags & FLAG_m) && mcount >= TT.m) break;
   }
 
@@ -292,6 +335,9 @@
 {
   char **ss = toys.optargs;
 
+  if (!TT.a) TT.a = TT.c;
+  if (!TT.b) TT.b = TT.c;
+
   // Handle egrep and fgrep
   if (*toys.which->name == 'e') toys.optflags |= FLAG_E;
   if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index b06be42..f99a216 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -29,18 +29,26 @@
  * output argv[0] unmodified for -o comm or -o args (but procps violates
  * posix for -o comm anyway, it's stat[2] not argv[0]).
  *
+ * Note: iotop is STAYROOT so it can read other process's /proc/$PID/io
+ *       files (why they're not globally readable when the rest of proc
+ *       data is...?) and get a global I/O picture. Normal top is NOT,
+ *       even though you can -o AIO there, to give sysadmins the option
+ *       to reduce security exposure.)
+ *
  * TODO: ps aux (att & bsd style "ps -ax" vs "ps ax" behavior difference)
  * TODO: switch -fl to -y
  * TODO: thread support /proc/$d/task/%d/stat (and -o stat has "l")
  * TODO: iotop: Window size change: respond immediately. Why not padding
  *       at right edge? (Not adjusting to screen size at all? Header wraps?)
+ * TODO: top: thread support and SMP
+ * TODO: pgrep -f only searches the amount of cmdline that fits in toybuf.
 
-USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflno*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_PS(NEWTOY(ps, "k(sort)*P(ppid)*aAdeflMno*O*p(pid)*s*t*u*U*g*G*wZ[!ol][+Ae]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
 // stayroot because iotop needs root to read other process' proc/$$/io
-USE_TOP(NEWTOY(top, ">0m" "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
-USE_IOTOP(NEWTOY(iotop, ">0Aako"  "p*u*d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
-USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:", TOYFLAG_USR|TOYFLAG_BIN))
-USE_PKILL(NEWTOY(pkill,     "Vu*U*t*s*P*g*G*fnovxl:", TOYFLAG_USR|TOYFLAG_BIN))
+USE_TOP(NEWTOY(top, ">0m" "k*o*p*u*s#<1=9d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
+USE_IOTOP(NEWTOY(iotop, ">0AaKO" "k*o*p*u*s#<1=7d#=3<1n#<1bq", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT|TOYFLAG_LOCALE))
+USE_PGREP(NEWTOY(pgrep, "?cld:u*U*t*s*P*g*G*fnovxL:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_PKILL(NEWTOY(pkill,     "Vu*U*t*s*P*g*G*fnovxl:[-no]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config PS
   bool "ps"
@@ -68,6 +76,7 @@
     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 at terminal width)
 
@@ -75,7 +84,8 @@
 
     -f	Full listing (-o USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD)
     -l	Long listing (-o F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD)
-    -o	Output the listed FIELDs, each with optional :size and/or =title
+    -o	Output FIELDs instead of defaults, each with optional :size and =title
+    -O  Add FIELDS to defaults
     -Z	Include LABEL
 
     Available -o FIELDs:
@@ -114,41 +124,35 @@
   help
     usage: top [-m] [ -d seconds ] [ -n iterations ]
 
-    Provide a view of process activity in real time.
-    Keys
-       N/M/P/T show CPU usage, sort by pid/mem/cpu/time
-       S       show memory
-       R       reverse sort
-       H       toggle threads
-       C,1     toggle SMP
-       Q,^C    exit
+    Show process activity in real time.
 
-    Options
-       -n Iterations before exiting
-       -d Delay between updates
-       -m Same as 's' key
+    -k	Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
+    -o	Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
+    -s	Sort by field number (1-X, default 9)
 
 # Requires CONFIG_IRQ_TIME_ACCOUNTING in the kernel for /proc/$$/io
 config IOTOP
   bool "iotop"
   default y
   help
-    usage: iotop [-Aako]
+    usage: iotop [-AaKO]
 
     Rank processes by I/O.
 
     -A	All I/O, not just disk
     -a	Accumulated I/O (not percentage)
-    -k	Kilobytes
-    -o	Only show processes doing I/O
-
-    Cursor left/right to change sort, space to update, Q to exit.
+    -K	Kilobytes
+    -k	Fallback sort FIELDS (default -[D]IO,-ETIME,-PID)
+    -O	Only show processes doing I/O
+    -o	Show FIELDS (default PID,PR,USER,[D]READ,[D]WRITE,SWAP,[D]IO,COMM)
+    -s	Sort by field number (0-X, default 6)
 
 config TOP_COMMON
   bool
   default y
   help
-    usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]
+    usage: COMMON [-bq] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,] [-s SORT]
+
     -b	Batch mode (no tty)
     -d	Delay SECONDS between each cycle (default 3)
     -n	Exit after NUMBER iterations
@@ -156,6 +160,9 @@
     -u	Show these USERs
     -q	Quiet (no header lines)
 
+    Cursor LEFT/RIGHT to change sort, UP/DOWN move list, space to force
+    update, R to reverse sort, Q to exit.
+
 config PGREP
   bool "pgrep"
   default y
@@ -213,6 +220,7 @@
       struct arg_list *t;
       struct arg_list *s;
       struct arg_list *p;
+      struct arg_list *O;
       struct arg_list *o;
       struct arg_list *P;
       struct arg_list *k;
@@ -220,8 +228,11 @@
     struct {
       long n;
       long d;
+      long s;
       struct arg_list *u;
       struct arg_list *p;
+      struct arg_list *o;
+      struct arg_list *k;
     } top;
     struct{
       char *L;
@@ -234,9 +245,9 @@
       struct arg_list *u;
       char *d;
 
-      void *regexes;
+      void *regexes, *snapshot;
       int signal;
-      pid_t self;
+      pid_t self, match;
     } pgrep;
   };
 
@@ -245,8 +256,7 @@
   unsigned width, height;
   dev_t tty;
   void *fields, *kfields;
-  long long ticks, bits, ioread, iowrite, aioread, aiowrite;
-  size_t header_len;
+  long long ticks, bits, time;
   int kcount, forcek, sortpos;
   int (*match_process)(long long *slot);
   void (*show_process)(void *tb);
@@ -342,8 +352,6 @@
   // Misc
   {"STIME", 5, SLOT_starttime}, {"F", 1, 64|SLOT_flags}, {"S", -1, 64},
   {"STAT", -5, 64},
-
-
 );
 
 // Return 0 to discard, nonzero to keep
@@ -533,7 +541,7 @@
     else width -= printf("%*.*s", pad, len, out);
     if (!width) break;
   }
-  xputc('\n');
+  xputc(TT.time ? '\r' : '\n');
 }
 
 // dirtree callback: read data about process to display, store, or discard it.
@@ -634,7 +642,7 @@
   // systems (or ps | more) we re-fetch uptime as we fetch each /proc line.
   sysinfo(&TT.si);
   slot[SLOT_uptime] = TT.si.uptime;
-  slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_rss];
+  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)) {
@@ -745,6 +753,7 @@
     buf += strlen(buf)+1;
   }
 
+  TT.kcount++;
   if (TT.show_process) {
     TT.show_process(tb);
 
@@ -755,7 +764,6 @@
   s = xmalloc(buf-toybuf);
   new->extra = (long)s;
   memcpy(s, toybuf, buf-toybuf);
-  TT.kcount++;
 
   return DIRTREE_SAVE;
 }
@@ -796,12 +804,10 @@
   }
 
   // Find type
-  if (*(struct strawberry **)data == TT.kfields) {
-    field->reverse = 1;
-    if (*type == '-') field->reverse = -1;
-    else if (*type != '+') type--;
-    type++;
-  }
+  field->reverse = 1;
+  if (*type == '-') field->reverse = -1;
+  else if (*type != '+') type--;
+  type++;
   for (i = 0; i<ARRAY_LEN(typos); i++) {
     field->which = i;
     for (j = 0; j<2; j++) {
@@ -822,15 +828,21 @@
   else if (typos[field->which].width<0) field->len *= -1;
   dlist_add_nomalloc(data, (void *)field);
 
-  // Print padded header for -o.
-  if (*(struct strawberry **)data == TT.fields) {
-    TT.header_len +=
-      snprintf(toybuf + TT.header_len, sizeof(toybuf) - TT.header_len,
-               " %*s" + (field == TT.fields), field->len, field->title);
-    TT.bits |= 1LL<<field->which;
+  return 0;
+}
+
+long long get_headers(struct strawberry *fields, char *buf, int blen)
+{
+  long long bits = 0;
+  int len = 0;
+
+  for (; fields; fields = fields->next) {
+    len += snprintf(buf+len, blen-len, " %*s"+!bits, fields->len,
+      fields->title);
+    bits |= 1LL<<fields->which;
   }
 
-  return 0;
+  return bits;
 }
 
 // Parse -p -s -t -u -U -g -G
@@ -960,13 +972,22 @@
   return tbsort;
 } 
 
+static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
+{
+  struct arg_list def;
+
+  memset(&def, 0, sizeof(struct arg_list));
+  def.arg = s;
+  comma_args(arg ? arg : &def, fields, err, parse_ko);
+}
+
 static void shared_main(void)
 {
   int i;
 
   TT.ticks = sysconf(_SC_CLK_TCK);
   if (!TT.width) {
-    TT.width = 80;
+    TT.width = (toys.which->name[1] == 's') ? 99999 : 80;
     TT.height = 25;
     terminal_size(&TT.width, &TT.height);
   }
@@ -986,6 +1007,7 @@
 void ps_main(void)
 {
   struct dirtree *dt;
+  char *s;
   int i;
 
   if (toys.optflags&FLAG_w) TT.width = 99999;
@@ -1003,56 +1025,63 @@
   comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
   dlist_terminate(TT.kfields);
 
-  // Parse manual field selection, or default/-f/-l, plus -Z,
-  // constructing the header line in toybuf as we go.
-  if (toys.optflags&FLAG_Z) {
-    struct arg_list Z = { 0, "LABEL" };
-
-    comma_args(&Z, &TT.fields, "-Z", parse_ko);
-  }
-  if (TT.ps.o) comma_args(TT.ps.o, &TT.fields, "bad -o field", parse_ko);
-  else {
-    struct arg_list al;
-
-    al.next = 0;
-    if (toys.optflags&FLAG_f)
-      al.arg = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
-    else if (toys.optflags&FLAG_l)
-      al.arg = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
-    else if (CFG_TOYBOX_ON_ANDROID)
-      al.arg = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,CMDLINE";
-    else al.arg = "PID,TTY,TIME,CMD";
-
-    comma_args(&al, &TT.fields, 0, parse_ko);
+  // Parse manual field selection, or default/-f/-l, plus -Z and -O
+  if (toys.optflags&FLAG_Z) default_ko("LABEL", &TT.fields, 0, 0);
+  if (toys.optflags&FLAG_f) s = "USER:8=UID,PID,PPID,C,STIME,TTY,TIME,CMD";
+  else if (toys.optflags&FLAG_l)
+    s = "F,S,UID,PID,PPID,C,PRI,NI,ADDR,SZ,WCHAN,TTY,TIME,CMD";
+  else if (CFG_TOYBOX_ON_ANDROID)
+    s = "USER,PID,PPID,VSIZE,RSS,WCHAN:10,ADDR:10=PC,S,NAME";
+  else s = "PID,TTY,TIME,CMD";
+  default_ko(s, &TT.fields, "bad -o", TT.ps.o);
+  if (TT.ps.O) {
+    if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->prev;
+    comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
+    if (TT.fields) TT.fields = ((struct strawberry *)TT.fields)->next;
   }
   dlist_terminate(TT.fields);
-  printf("%s\n", toybuf);
 
-  // misunderstand fields the flags say to
+  // -f and -n change the meaning of some fields
   if (toys.optflags&(FLAG_f|FLAG_n)) {
     struct strawberry *ever;
 
     for (ever = TT.fields; ever; ever = ever->next) {
-      int alluc = ever->which;
-
-      if ((toys.optflags&FLAG_f) && alluc==PS_CMD) alluc = PS_ARGS;
-      if ((toys.optflags&FLAG_n) && alluc>=PS_UID && alluc<=PS_RGROUP
-          && (typos[alluc].slot&64)) alluc--;
-      if (alluc != ever->which) {
-        TT.bits &= 1LL<<ever->which;
-        TT.bits |= 1LL<<(ever->which = alluc);
-      }
+      if ((toys.optflags&FLAG_f) && ever->which==PS_CMD) ever->which = PS_ARGS;
+      if ((toys.optflags&FLAG_n) && ever->which>=PS_UID
+        && ever->which<=PS_RGROUP && (typos[ever->which].slot&64))
+          ever->which--;
     }
   }
 
-  if (!(toys.optflags&FLAG_k)) TT.show_process = (void *)show_ps;
+  // Calculate seen fields bit array, and if we aren't deferring printing
+  // print headers now (for low memory/nommu systems).
+  TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
+  if (!(toys.optflags&FLAG_M)) printf("%.*s\n", TT.width, toybuf);
+  if (!(toys.optflags&(FLAG_k|FLAG_M))) TT.show_process = (void *)show_ps;
   TT.match_process = ps_match_process;
   dt = dirtree_read("/proc", get_ps);
 
-  if (toys.optflags&FLAG_k) {
+  if (toys.optflags&(FLAG_k|FLAG_M)) {
     struct carveup **tbsort = collate(TT.kcount, dt, ksort);
 
-    qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
+    if (toys.optflags&FLAG_M) {
+      for (i = 0; i<TT.kcount; i++) {
+        struct strawberry *field;
+
+        for (field = TT.fields; field; field = field->next) {
+          int len = strlen(string_field(tbsort[i], field));
+
+          if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
+        }
+      }
+
+      // Now that we've recalculated field widths, re-pad headers again
+      get_headers(TT.fields, toybuf, sizeof(toybuf));
+      printf("%.*s\n", TT.width, toybuf);
+    }
+
+    if (toys.optflags&FLAG_k)
+      qsort(tbsort, TT.kcount, sizeof(struct carveup *), (void *)ksort);
     for (i = 0; i<TT.kcount; i++) {
       show_ps(tbsort[i]);
       free(tbsort[i]);
@@ -1086,7 +1115,7 @@
   if (pos<0) pos = 0;
 
   for (field = TT.fields; field; field = field->next) {
-    if ((TT.sortpos = i++)<pos) continue;
+    if ((TT.sortpos = i++)<pos && field->next) continue;
     going2 = TT.kfields;
     going2->which = field->which;
     going2->len = field->len;
@@ -1097,53 +1126,79 @@
 // If we have both, adjust slot[deltas[]] to be relative to previous
 // measurement rather than process start. Stomping old.data is fine
 // because we free it after displaying.
-static int merge_deltas(long long *oslot, long long *nslot)
+static int merge_deltas(long long *oslot, long long *nslot, int milis)
 {
-  char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_upticks,
-                   SLOT_rchar, SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
+  char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
+                   SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
   int i;
 
   for (i = 0; i<ARRAY_LEN(deltas); i++)
     oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
+  oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
 
   return 1;
 }
 
-static void top_common(char *header,
-  int (*filter)(long long *oslot, long long *nslot))
+static int header_line(int line, int rev)
+{
+  if (!line) return 0;
+
+  printf("%s%*.*s%s\r\n", rev ? "\033[7m" : "",
+    (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf,
+    rev ? "\033[0m" : "");
+
+  return line-1;
+}
+
+// Get current time in miliseconds
+static long long militime(void)
 {
   struct timespec ts;
-  long long timeout = 0, now;
+
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+
+  return ts.tv_sec*1000+ts.tv_nsec/1000000;
+}
+
+static void top_common(
+  int (*filter)(long long *oslot, long long *nslot, int milis))
+{
+  long long timeout = 0, now, stats[16];
   struct proclist {
     struct carveup **tb;
     int count;
+    long long whence;
   } plist[2], *plold, *plnew, old, new, mix;
-  char scratch[16];
+  char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
+    "iow", "irq", "sirq", "host"};
+ 
   unsigned tock = 0;
-  int i, lines, done = 0;
+  int i, lines, topoff = 0, done = 0;
 
+  toys.signal = SIGWINCH;
+  TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
   *scratch = 0;
-  TT.top.d *= 1000;
-  if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
-  else {
-    xset_terminal(0, 1, 0);
-    sigatexit(tty_sigreset);
-  }
-  shared_main();
-
-  comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
-  comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
-
-  TT.match_process = shared_match_process;
   memset(plist, 0, sizeof(plist));
+  memset(stats, 0, sizeof(stats));
   do {
-    struct dirtree *dt = dirtree_read("/proc", get_ps);
+    struct dirtree *dt;
+    int recalc = 1;
 
     plold = plist+(tock++&1);
     plnew = plist+(tock&1);
+    plnew->whence = militime();
+    dt= dirtree_read("/proc", get_ps);
     plnew->tb = collate(plnew->count = TT.kcount, dt, ksort);
     TT.kcount = 0;
 
+    if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
+      long long *st = stats+8*(tock&1);
+
+      // user nice system idle iowait irq softirq host
+      sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
+        st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
+    }
+
     // First time, wait a quarter of a second to collect a little delta data.
     if (!plold->tb) {
       msleep(250);
@@ -1171,7 +1226,7 @@
       if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
       else {
         // Keep or discard
-        if (filter(otb->slot, ntb->slot)) {
+        if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
           mix.tb[mix.count] = otb;
           mix.count++;
         }
@@ -1182,35 +1237,113 @@
       new.count--;
     }
 
-    // Will will re-fetch no data before its time. - Mork calling Orson Welles
+    // We will re-fetch no data before its time. - Mork calling Orson Welles
     for (;;) {
-      char was, is, *pos;
+      char was, is;
 
-      qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
-printf("cheese\n");
-      if (!(toys.optflags&FLAG_b)) printf("\033[H\033[J");
-      if (!(toys.optflags&FLAG_q)) {
-        i = 0;
-        strcpy(pos = toybuf, header);
+      if (recalc) {
+        qsort(mix.tb, mix.count, sizeof(struct carveup *), (void *)ksort);
+        if (!(toys.optflags&FLAG_b)) {
+          printf("\033[H\033[J");
+          if (toys.signal) {
+            toys.signal = 0;
+            terminal_probesize(&TT.width, &TT.height);
+          }
+        }
+        lines = TT.height;
+      }
+      if (recalc && !(toys.optflags&FLAG_q)) {
+        if (*toys.which->name == 't') {
+          struct strawberry alluc;
+          long long ll, up = 0;
+          long run[6];
+          int j;
 
-        for (i=0, is = *pos; *pos; pos++) {
+          alluc.which = PS_S;
+          memset(run, 0, sizeof(run));
+          for (i = 0; i<mix.count; i++)
+            run[1+stridx("RSTZ", *string_field(mix.tb[i], &alluc))]++;
+
+          sprintf(toybuf,
+            "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
+            "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
+          lines = header_line(lines, 0);
+
+          if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
+            for (i=0; i<6; i++) {
+              pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
+                    "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
+              run[i] = pos ? atol(pos) : 0;
+            }
+            sprintf(toybuf,
+             "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
+              run[0], run[0]-run[1], run[1], run[2]);
+            lines = header_line(lines, 0);
+            sprintf(toybuf,
+              "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
+              run[4], run[4]-run[5], run[5], run[3]);
+            lines = header_line(lines, 0);
+          }
+
+          pos = toybuf;
+          i = sysconf(_SC_NPROCESSORS_CONF);
+          pos += sprintf(pos, "%d%%cpu", i*100);
+          j = 4+(i>10);
+
+          // If a processor goes idle it's powered down and its idle ticks don't
+          // advance, so calculate idle time as potential time - used.
+          if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
+          if (!up) up = 1;
+          now = up*i;
+          ll = stats[3] = stats[11] = 0;
+          for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
+          stats[3] = now - llabs(ll);
+
+          for (i = 0; i<8; i++) {
+            ll = (llabs(stats[i]-stats[i+8])*1000)/up;
+            pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
+          }
+          lines = header_line(lines, 0);
+        } else {
+          struct strawberry *fields;
+          struct carveup tb;
+
+          memset(&tb, 0, sizeof(struct carveup));
+          pos = stpcpy(toybuf, "Totals:");
+          for (fields = TT.fields; fields; fields = fields->next) {
+            long long ll, bits = 0;
+            int slot = typos[fields->which].slot&63;
+
+            if (fields->which<PS_C || fields->which>PS_DIO) continue;
+            ll = 1LL<<fields->which;
+            if (bits&ll) continue;
+            bits |= ll;
+            for (i=0; i<mix.count; i++)
+              tb.slot[slot] += mix.tb[i]->slot[slot];
+            pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
+              " %s: %*s,", typos[fields->which].name,
+              fields->len, string_field(&tb, fields));
+          }
+          *--pos = 0;
+          lines = header_line(lines, 0);
+        }
+
+        get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
+        for (i = 0, is = *pos; *pos; pos++) {
           was = is;
           is = *pos;
           if (isspace(was) && !isspace(is) && i++==TT.sortpos) pos[-1] = '[';
           if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
         }
         *pos = 0;
-        printf("\033[7m%*.*s\033[0m\n\r",
-          (toys.optflags&FLAG_b) ? 0 : -TT.width, TT.width, toybuf);
-
-        if (!(toys.optflags&FLAG_b))
-          terminal_probesize(&TT.width, &TT.height);
+        lines = header_line(lines, 1);
       }
-      lines = TT.height-2;
+      if (!recalc) printf("\033[%dH\033[J", 1+TT.height-lines);
+      recalc = 1;
 
-      for (i=0; i<lines && i<mix.count; i++) {
-        show_ps(mix.tb[i]);
-        xputc('\r');
+      for (i = 0; i<lines && i+topoff<mix.count; i++) {
+        if (i) xputc('\n');
+        show_ps(mix.tb[i+topoff]);
       }
 
       if (TT.top.n && !--TT.top.n) {
@@ -1219,10 +1352,9 @@
       }
 
       // Get current time in miliseconds
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      now = ts.tv_sec*1000+ts.tv_nsec/1000000;
-      if (timeout<=now) timeout += TT.top.d;
-      if (timeout<=now) timeout = now+TT.top.d;
+      now = militime();
+      if (timeout<=now) timeout = new.whence+TT.top.d;
+      if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
 
       i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
       if (i==-1 || i==3 || toupper(i)=='Q') {
@@ -1234,12 +1366,25 @@
       // Flush unknown escape sequences.
       if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
       else if (i==' ') {
-        timeout = now;
+        timeout = 0;
         break;
-      } else {
+      } else if (toupper(i)=='R')
+        ((struct strawberry *)TT.kfields)->reverse *= -1;
+      else {
         i -= 256;
         if (i == KEY_LEFT) setsort(TT.sortpos-1);
         else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
+        // KEY_UP is 0, so at end of strchr
+        else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
+          recalc = 0;
+
+          if (i == KEY_UP) topoff--;
+          else if (i == KEY_DOWN) topoff++;
+          else if (i == KEY_PGDN) topoff += lines;
+          else if (i == KEY_PGUP) topoff -= lines;
+          if (topoff<0) topoff = 0; 
+          if (topoff>mix.count) topoff = mix.count;
+        }
       }
       continue;
     }
@@ -1248,67 +1393,77 @@
     for (i=0; i<plold->count; i++) free(plold->tb[i]);
     free(plold->tb);
   } while (!done);
+
   if (!(toys.optflags&FLAG_b)) tty_reset();
 }
 
+static void top_setup(char *defo, char *defk)
+{
+  int len;
+
+  TT.time = militime();
+  TT.top.d *= 1000;
+  if (toys.optflags&FLAG_b) TT.width = TT.height = 99999;
+  else {
+    xset_terminal(0, 1, 0);
+    sigatexit(tty_sigreset);
+    xsignal(SIGWINCH, generic_signal);
+    printf("\033[?25l\033[0m");
+  }
+  shared_main();
+
+  comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
+  comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
+  TT.match_process = shared_match_process;
+
+  default_ko(defo, &TT.fields, "bad -o", TT.top.o);
+  dlist_terminate(TT.fields);
+  len = strlen(toybuf);
+  if (toybuf[len-1]!=' ' && len<sizeof(toybuf)-1) strcpy(toybuf+len, " ");
+
+  // First (dummy) sort field is overwritten by setsort()
+  default_ko("-S", &TT.kfields, 0, 0);
+  default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
+  dlist_terminate(TT.kfields);
+  setsort(TT.top.s-1);
+}
+
 void top_main(void)
 {
-  struct arg_list al;
-  char *header;
+  // usage: [-h HEADER] -o OUTPUT -k SORT
 
-  // Display fields
-  al.next = 0;
-  al.arg = xstrdup("PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE");
-  comma_args(&al, &TT.fields, 0, parse_ko);
-  free(al.arg);
-  dlist_terminate(TT.fields);
-  header=xstrdup(toybuf);
-
-  // Fallback sorts
-  al.arg = xstrdup("-S,-%CPU,-ETIME,-PID");
-  comma_args(&al, &TT.kfields, "bang", parse_ko);
-  dlist_terminate(TT.kfields);
-  setsort(8);
-
-  top_common(header, merge_deltas);
+  top_setup(
+    "PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,ARGS",
+    "-%CPU,-ETIME,-PID");
+  top_common(merge_deltas);
 }
 
 #define CLEANUP_top
 #define FOR_iotop
 #include "generated/flags.h"
 
-static int iotop_filter(long long *oslot, long long *nslot)
+static int iotop_filter(long long *oslot, long long *nslot, int milis)
 {
-  if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot);
+  if (!(toys.optflags&FLAG_a)) merge_deltas(oslot, nslot, milis);
+  else oslot[SLOT_upticks] = ((militime()-TT.time)*TT.ticks)/1000;
 
-  return !(toys.optflags&FLAG_o) || oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
+  return !(toys.optflags&FLAG_o)||oslot[SLOT_iobytes+!(toys.optflags&FLAG_A)];
 }
 
 void iotop_main(void)
 {
-  struct arg_list al;
-  char *header, *d = "D"+!!(toys.optflags&FLAG_A);
+  char *s1 = 0, *s2 = 0, *d = "D"+!!(toys.optflags&FLAG_A);
 
-  if (toys.optflags&FLAG_k) TT.forcek++;
+  if (toys.optflags&FLAG_K) TT.forcek++;
 
-  al.next = 0;
-  al.arg = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM", d, d, d);
-  comma_args(&al, &TT.fields, 0, parse_ko);
-  free(al.arg);
-  dlist_terminate(TT.fields);
-  header = strdup(toybuf);
-
-  // Fallback sorts. First (dummy) field gets overwritten by setsort()
-  al.arg = xmprintf("-S,-%sIO,-ETIME,-PID",d);
-  comma_args(&al, &TT.kfields, 0, parse_ko);
-  free(al.arg);
-  dlist_terminate(TT.kfields);
-  setsort(6);
-
-  top_common(header, iotop_filter);
+  top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
+    s2 = xmprintf("-%sIO,-ETIME,-PID",d));
+  free(s1);
+  free(s2);
+  top_common(iotop_filter);
 }
 
-// pkill's plumbing wrap's pgrep's and thus mostly takes place in pgrep's flag
+// pkill's plumbing wraps pgrep's and thus mostly takes place in pgrep's flag
 // context, so force pgrep's flags on even when building pkill standalone.
 // (All the pgrep/pkill functions drop out when building ps standalone.)
 #define FORCE_FLAGS
@@ -1321,17 +1476,34 @@
   regex_t reg;
 };
 
-static void show_pgrep(struct carveup *tb)
+static void do_pgk(struct carveup *tb)
+{
+  if (TT.pgrep.signal) {
+    if (kill(*tb->slot, TT.pgrep.signal)) {
+      char *s = num_to_sig(TT.pgrep.signal);
+
+      if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
+      perror_msg("%s->%lld", s, *tb->slot);
+    }
+  }
+  if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
+    printf("%lld", *tb->slot);
+    if (toys.optflags&FLAG_l)
+      printf(" %s", tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f));
+    
+    printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
+  }
+}
+
+static void match_pgrep(struct carveup *tb)
 {
   regmatch_t match;
   struct regex_list *reg;
-  char *name = tb->str;
+  char *name = tb->str+tb->offset[4]*!!(toys.optflags&FLAG_f);;
 
   // Never match ourselves.
   if (TT.pgrep.self == *tb->slot) return;
 
-  if (toys.optflags&FLAG_f) name += tb->offset[4];
-
   if (TT.pgrep.regexes) {
     for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
       if (regexec(&reg->reg, name, 1, &match, 0)) continue;
@@ -1344,20 +1516,15 @@
 
   // Repurpose a field for -c count
   TT.sortpos++;
-  if (TT.pgrep.signal) {
-    if (kill(*tb->slot, TT.pgrep.signal)) {
-      char *s = num_to_sig(TT.pgrep.signal);
+  if (toys.optflags&(FLAG_n|FLAG_o)) {
+    long long ll = tb->slot[SLOT_starttime];
 
-      if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
-      perror_msg("%s->%lld", s, *tb->slot);
-    }
-  }
-  if (!(toys.optflags&FLAG_c) && (!TT.pgrep.signal || TT.tty)) {
-    printf("%lld", *tb->slot);
-    if (toys.optflags&FLAG_l) printf(" %s", name);
-    
-    printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
-  }
+    if (toys.optflags&FLAG_o) ll *= -1;
+    if (TT.time && TT.time>ll) return;
+    TT.time = ll;
+    free(TT.pgrep.snapshot);
+    TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
+  } else do_pgk(tb);
 }
 
 static int pgrep_match_process(long long *slot)
@@ -1398,10 +1565,14 @@
     TT.pgrep.regexes = reg;
   }
   TT.match_process = pgrep_match_process;
-  TT.show_process = (void *)show_pgrep;
+  TT.show_process = (void *)match_pgrep;
 
   dirtree_read("/proc", get_ps);
   if (toys.optflags&FLAG_c) printf("%d\n", TT.sortpos);
+  if (TT.pgrep.snapshot) {
+    do_pgk(TT.pgrep.snapshot);
+    if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
+  }
   if (TT.pgrep.d) xputc('\n');
 }
 
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index a57891e..30d8a15 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -827,8 +827,7 @@
     if (strchr("aiqr=", c) && i>1) break;
 
     // Add step to pattern
-    corwin = xmalloc(reg-toybuf);
-    memcpy(corwin, toybuf, reg-toybuf);
+    corwin = xmemdup(toybuf, reg-toybuf);
     reg = (reg-toybuf) + (char *)corwin;
 
     // Parse arguments by command type