
/*
 *  cc -o mix mix.c ; ./mix File1 File2 ...
 *
 *  set DESTROY_SEEN_ENTROPY==0 for testing!
 *  else this will clobber the argument files...
 *  (C) 2008 CAcert Inc.
 */

#define BLOCK 512
#define DESTROY_SEEN_ENTROPY 1
static char mix[BLOCK];

void splat(int fd, char * name, long total_to_splat);

main(int argc, char **argv)
{
  int fds[argc];      /* fds[0] is wasted, to align with argv */
  int i;
  int more_data = 1;
  long total = 0;
  const int START = 1;   /* for both argv[] and fds[] */
  const int END = argc;

  if (argc <= 1)
  {
    char *p = "Usage: mix files...\n";
    write(2, p, strlen(p));
    exit(2);
  }

  for (i = START; i < END; i++)   /* open all the input files */
  {
    fds[i] = open(argv[i], 2);    /* 2 == O_RDWR */
    if (fds[i] < 0)
    {
      char *p = "bad file: ";
      write(2, p, strlen(p));     /* 2 == stderr */
      write(2, argv[i], strlen(argv[i]));
      write(2, "\n", 1);
      exit(2);
    }
  }

  /*
   *  Read each Nth block, in parallel, across the files.
   *  Stop at the first end-of-file.
   *  Any entropy longer than the shortest file is wasted.
   */
  while (more_data)
  {
    for (i = START; i < END; i++)   /* for each file */
    {
      int j;
      char next[BLOCK];
      int got;

      got = read(fds[i], next, BLOCK);  /* next block */

      if (got != BLOCK)
      {
        more_data = 0;
        break;
      }
  
      for (j = 0; j < BLOCK; j++)   /* mix in one block */
      {
        mix[j] ^= next[j];
      }
  
    }

    if (!more_data) /* yuch, need a 2-level break */
      break;

    total += BLOCK;
    write(1, mix, BLOCK);      /* 1 == stdout */
  }


  if (DESTROY_SEEN_ENTROPY)
  {
    for (i = START; i < END; i++)
    {
      splat(fds[i], argv[i], total);
    }
  }

}


/*
 *  Yes, the #includes are here, below, deliberately.
 *  That's so that the above code that does the mixing
 *  is totally clean of alien influences.
 */
#include <sys/types.h>
#include <unistd.h>
char zeros[BLOCK];   /* default initialise to zeros */

/*
 *  It is the user's responsibility to really clean the drive,
 *  using e.g., 33 pass or 35 pass.
 *  This just prevents inadvertant re-use of the entropy.
 */
void splat(int fd, char *name, long total)
{
   off_t offset;
   /* rewind to start and write nulls */
   lseek(fd, (off_t) 0, SEEK_SET);

   for (offset = 0; offset < (off_t)total; offset += BLOCK)
     write(fd, zeros, BLOCK);

   /* unlink(name); */
}
