Merge remote-tracking branch 'toybox/master' into HEAD
diff --git a/toys/other/shred.c b/toys/other/shred.c
new file mode 100644
index 0000000..07d62c3
--- /dev/null
+++ b/toys/other/shred.c
@@ -0,0 +1,104 @@
+/* shred.c - Overwrite a file to securely delete
+ *
+ * Copyright 2014 Rob Landley <rob@landley.net>
+ *
+ * No standard
+
+USE_SHRED(NEWTOY(shred, "<1zxus#<1n#<1o#<0f", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SHRED
+  bool "shred"
+  default n
+  help
+    usage: shred [-fuz] [-n COUNT] [-s SIZE] FILE...
+
+    Securely delete a file by overwriting its contents with random data.
+
+    -f        Force (chmod if necessary)
+    -n COUNT  Random overwrite iterations (default 1)
+    -o OFFSET Start at OFFSET
+    -s SIZE   Use SIZE instead of detecting file size
+    -u        unlink (actually delete file when done)
+    -x        Use exact size (default without -s rounds up to next 4k)
+    -z        zero at end
+
+    Note: data journaling filesystems render this command useless, you must
+    overwrite all free space (fill up disk) to erase old data on those.
+*/
+
+#define FOR_shred
+#include "toys.h"
+
+GLOBALS(
+  long offset;
+  long iterations;
+  long size;
+
+  int ufd;
+)
+
+void shred_main(void)
+{
+  char **try;
+
+  if (!(toys.optflags & FLAG_n)) TT.iterations++;
+  TT.ufd = xopen("/dev/urandom", O_RDONLY);
+
+  // We don't use loopfiles() here because "-" isn't stdin, and want to
+  // respond to files we can't open via chmod.
+
+  for (try = toys.optargs; *try; try++) {
+    off_t pos = 0, len = TT.size;
+    int fd = open(*try, O_RDWR), iter = 0, throw;
+
+    // do -f chmod if necessary
+    if (fd == -1 && (toys.optflags & FLAG_f)) {
+      chmod(*try, 0600);
+      fd = open(*try, O_RDWR);
+    }
+    if (fd == -1) {
+      perror_msg("%s", *try);
+      continue;
+    }
+
+    // determine length
+    if (!len) len = fdlength(fd);
+    if (len<1) {
+      error_msg("%s: needs -s", *try);
+      close(fd);
+      continue;
+    }
+
+    // Loop through, writing to this file
+    for (;;) {
+      // Advance to next -n or -z?
+
+      if (pos >= len) {
+        pos = -1;
+        if (++iter == TT.iterations && (toys.optargs && FLAG_z)) {
+          memset(toybuf, 0, sizeof(toybuf));
+          continue;
+        }
+        if (iter >= TT.iterations) break;
+      }
+
+      if (pos < TT.offset) {
+        if (TT.offset != lseek(fd, TT.offset, SEEK_SET)) {
+          perror_msg("%s", *try);
+          break;
+        }
+        pos = TT.offset;
+      }
+
+      // Determine length, read random data if not zeroing, write.
+
+      throw = sizeof(toybuf);
+      if (toys.optflags & FLAG_x)
+        if (len-pos < throw) throw = len-pos;
+
+      if (iter != TT.iterations) xread(TT.ufd, toybuf, throw);
+      if (throw != writeall(fd, toybuf, throw)) perror_msg("%s");
+      pos += throw;
+    }
+  }
+}
diff --git a/toys/other/vmstat.c b/toys/other/vmstat.c
index 49c7cc7..eed7945 100644
--- a/toys/other/vmstat.c
+++ b/toys/other/vmstat.c
@@ -84,11 +84,13 @@
   if (toys.optc) loop_delay = atolx_range(toys.optargs[0], 0, INT_MAX);
   if (toys.optc > 1) loop_max = atolx_range(toys.optargs[1], 1, INT_MAX) - 1;
 
-  for (loop = 0; loop <= loop_max; loop++) {
+  for (loop = 0; !loop_max || loop <= loop_max; loop++) {
     unsigned idx = loop&1, offset = 0, expected = 0;
     uint64_t units, total_hz, *ptr = (uint64_t *)(top+idx),
              *oldptr = (uint64_t *)(top+!idx);
 
+    if (loop && loop_delay) sleep(loop_delay);
+
     // Print headers
     if (rows>3 && !(loop % (rows-3))) {
       if (isatty(1)) terminal_size(0, &rows);
@@ -147,7 +149,6 @@
     }
     xputc('\n');
 
-    if (loop_delay) sleep(loop_delay);
-    else break;
+    if (!loop_delay) break;
   }
 }
diff --git a/toys/pending/compress.c b/toys/pending/compress.c
index b79699d..45251fc 100644
--- a/toys/pending/compress.c
+++ b/toys/pending/compress.c
@@ -130,10 +130,10 @@
   // Compressed data buffer
   char *data;
   unsigned pos, len;
-  int fd;
+  int infd, outfd;
 
   // Tables only used for deflation
-  unsigned short *head, *chain;
+  unsigned short *hashhead, *hashchain;
 )
 
 // little endian bit buffer
@@ -145,9 +145,8 @@
 // malloc a struct bitbuf
 struct bitbuf *bitbuf_init(int fd, int size)
 {
-  struct bitbuf *bb = xmalloc(sizeof(struct bitbuf)+size);
+  struct bitbuf *bb = xzalloc(sizeof(struct bitbuf)+size);
 
-  memset(bb, 0, sizeof(struct bitbuf));
   bb->max = size;
   bb->fd = fd;
 
@@ -204,13 +203,42 @@
   return result;
 }
 
+void bitbuf_flush(struct bitbuf *bb)
+{
+  if (!bb->bitpos) return;
+
+  xwrite(bb->fd, bb->buf, (bb->bitpos+7)/8);
+  memset(bb->buf, 0, bb->max);
+  bb->bitpos = 0;
+}
+
+void bitbuf_put(struct bitbuf *bb, int data, int len)
+{
+  while (len) {
+    int click = bb->bitpos >> 3, blow, blen;
+
+    // Flush buffer if necessary
+    if (click == bb->max) {
+      bitbuf_flush(bb);
+      click = 0;
+    }
+    blow = bb->bitpos & 7;
+    blen = 8-blow;
+    if (blen > len) blen = len;
+    bb->buf[click] |= data << blow;
+    bb->bitpos += blen;
+    data >>= blen;
+    len -= blen;
+  }
+}
+
 static void data_crc(char sym)
 {
   TT.data[TT.pos++ & 32767] = sym;
 
   if (!(TT.pos & 32767)) {
-    xwrite(TT.fd, TT.data, 32768);
-    if (TT.crcfunc) TT.crcfunc(0, 32768);
+    xwrite(TT.outfd, TT.data, 32768);
+    if (TT.crcfunc) TT.crcfunc(TT.data, 32768);
   }
 }
 
@@ -245,9 +273,9 @@
 }
 
 // Fetch and decode next huffman coded symbol from bitbuf.
-// This takes advantage of the the sorting to navigate the tree as an array:
+// This takes advantage of the sorting to navigate the tree as an array:
 // each time we fetch a bit we have all the codes at that bit level in
-// order with no gaps..
+// order with no gaps.
 static unsigned huff_and_puff(struct bitbuf *bb, struct huff *huff)
 {
   unsigned short *length = huff->length;
@@ -264,36 +292,7 @@
   return huff->symbol[start + offset];
 }
 
-// Deflate from TT.fd to bitbuf
-// For deflate, TT.len = input read, TT.pos = input consumed
-static void deflate(struct bitbuf *bb)
-{
-  char *data = TT.data;
-  int len, end = 0;
-
-  TT.crc = ~0;
-
-  while (!end) {
-    // Read next half-window of data if we haven't hit EOF yet.
-    len = readall(TT.fd, data + (TT.len & 32768), 32768);
-fprintf(stderr, "read %d@%d\n", len, TT.pos);
-    if (len < 0) perror_exit("read"); // todo: add filename
-    if (len != 32768) end++;
-    TT.len += len;
-
-    // repeat until spanked
-    while (TT.pos != TT.len) {
-      unsigned pos = TT.pos & 65535;
-
-      if (!(pos & 32767) && !end) break;
-
-      TT.pos++;
-    }
-  }
-fprintf(stderr, "total %d\n", TT.pos);
-}
-
-// Decompress deflated data from bitbuf to TT.fd.
+// Decompress deflated data from bitbuf to TT.outfd.
 static void inflate(struct bitbuf *bb)
 {
   TT.crc = ~0;
@@ -408,23 +407,60 @@
   }
 
   if (TT.pos & 32767) {
-    xwrite(TT.fd, TT.data, TT.pos & 32767);
-    if (TT.crcfunc) TT.crcfunc(0, TT.pos & 32767);
+    xwrite(TT.outfd, TT.data, TT.pos & 32767);
+    if (TT.crcfunc) TT.crcfunc(TT.data, TT.pos & 32767);
   }
 }
 
+// Deflate from TT.infd to bitbuf
+// For deflate, TT.len = input read, TT.pos = input consumed
+static void deflate(struct bitbuf *bb)
+{
+  char *data = TT.data;
+  int len, final = 0;
+
+  TT.crc = ~0;
+
+  while (!final) {
+    // Read next half-window of data if we haven't hit EOF yet.
+    len = readall(TT.infd, data+(TT.len&32768), 32768);
+    if (len < 0) perror_exit("read"); // todo: add filename
+    if (len != 32768) final++;
+    if (TT.crcfunc) TT.crcfunc(data+(TT.len&32768), len);
+    // TT.len += len;  crcfunc advances len
+
+    // store block as literal
+    bitbuf_put(bb, final, 1);
+    bitbuf_put(bb, 0, 1);
+
+    bitbuf_put(bb, 0, (8-bb->bitpos)&7);
+    bitbuf_put(bb, len, 16);
+    bitbuf_put(bb, 0xffff & ~len, 16);
+
+    // repeat until spanked
+    while (TT.pos != TT.len) {
+      unsigned pos = TT.pos & 65535;
+
+      bitbuf_put(bb, data[pos], 8);
+
+      // need to refill buffer?
+      if (!(32767 & ++TT.pos) && !final) break;
+    }
+  }
+  bitbuf_flush(bb);
+}
+
 // Allocate memory for deflate/inflate.
 static void init_deflate(int compress)
 {
   int i, n = 1;
 
-// only supporting HASH_SIZE = 1 << 15, I.E. size = 32768
-
-  // Ye olde deflate window
-  TT.data = xmalloc(32768*(compress+1));
+  // compress needs 64k data and 32k each for hashhead and hashchain.
+  // decompress just needs 32k data.
+  TT.data = xmalloc(32768*(compress ? 4 : 1));
   if (compress) {
-    TT.head = (unsigned short *)(TT.data+65536);
-    TT.chain = TT.head + 0;
+    TT.hashhead = (unsigned short *)(TT.data + 65536);
+    TT.hashchain = (unsigned short *)(TT.data + 65536 + 32768);
   }
 
   // Calculate lenbits, lenbase, distbits, distbase
@@ -480,12 +516,12 @@
   unsigned crc, *crc_table = (unsigned *)(toybuf+sizeof(toybuf)-1024);
 
   crc = TT.crc;
-  for (i=0; i<len; i++) crc = crc_table[(crc^TT.data[i])&0xff] ^ (crc>>8);
+  for (i=0; i<len; i++) crc = crc_table[(crc^data[i])&0xff] ^ (crc>>8);
   TT.crc = crc;
   TT.len += len;
 }
 
-static void do_compress(int fd, char *name)
+static void do_gzip(int fd, char *name)
 {
   struct bitbuf *bb = bitbuf_init(1, sizeof(toybuf));
 
@@ -494,10 +530,22 @@
   // 4 byte MTIME (zeroed), Extra Flags (2=maximum compression),
   // Operating System (FF=unknown)
  
-  xwrite(1, "\x1f\x8b\x08\0\0\0\0\0\x02\xff", 10);
+  TT.infd = fd;
+  xwrite(bb->fd, "\x1f\x8b\x08\0\0\0\0\0\x02\xff", 10);
+
+  // Use last 1k of toybuf for little endian crc table
+  crc_init((unsigned *)(toybuf+sizeof(toybuf)-1024), 1);
+  TT.crcfunc = gzip_crc;
 
   deflate(bb);
 
+  // tail: crc32, len32
+
+  bitbuf_put(bb, 0, (8-bb->bitpos)&7);
+  bitbuf_put(bb, ~TT.crc, 32);
+  bitbuf_put(bb, TT.len, 32);
+
+  bitbuf_flush(bb);
   free(bb);
 }
 
@@ -506,7 +554,7 @@
   struct bitbuf *bb = bitbuf_init(fd, sizeof(toybuf));
 
   if (!is_gzip(bb)) error_exit("not gzip");
-  TT.fd = 1;
+  TT.outfd = 1;
 
   // Use last 1k of toybuf for little endian crc table
   crc_init((unsigned *)(toybuf+sizeof(toybuf)-1024), 1);
@@ -548,16 +596,9 @@
   loopfiles(toys.optargs, do_zcat);
 }
 
-void do_deflate(int fd, char *name)
-{
-  struct bitbuf *bb = bitbuf_init(1, sizeof(toybuf));
-
-  deflate(bb);
-}
-
 void gzip_main(void)
 {
   init_deflate(1);
 
-  loopfiles(toys.optargs, do_compress);
+  loopfiles(toys.optargs, do_gzip);
 }