// binhistogram - Display histograms of binary blobs.
// Copyright (c) 2010 pabr@pabr.org
// See http://www.pabr.org/linmctool/
//
// Compile with: gcc --std=gnu99 -Wall binhistogram.c -o binhistogram

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
  int size = 50;
  int refresh = 10;
  int accumulate = 10;
  for ( int i=1; i<argc; ++i )
    if ( !strcmp(argv[i],"--size") && i+1<argc ) size = atoi(argv[++i]);
    else if ( !strcmp(argv[i],"--refresh") && i+1<argc )
      refresh = atoi(argv[++i]);
    else if ( !strcmp(argv[i],"--accumulate") && i+1<argc )
      accumulate = atoi(argv[++i]);
    else {
      printf("Usage: binhistogram [--refresh N] [--accumulate N] [--size N]\n");
      exit(1);
    }

  int tty = open("/dev/tty", O_RDONLY|O_NONBLOCK);
  
  unsigned int bins[size][256];
  unsigned char or_mask[size];
  unsigned char and_mask[size];
  unsigned int count;
  
  void reset() {
    memset(bins, 0, sizeof(bins));
    memset(or_mask, 0, sizeof(or_mask));
    memset(and_mask, 255, sizeof(and_mask));
    count = 0;
  }
  reset();

  printf("\033[2J");

  unsigned char buf[size];
  while ( read(0, buf, sizeof(buf)) == sizeof(buf) ) {
    for ( int i=0; i<size; ++i ) {
      ++bins[i][buf[i]];
      or_mask[i] |= buf[i];
      and_mask[i] &= buf[i];
    }
    ++count;
    if ( count % refresh == 0 ) {
      printf("\033[0;0f");
      printf("Reading blocks of %d bytes. Displaying 1 in %d. "
	     "Clearing every %d, or on [CR].\n", size, refresh, accumulate);
      for ( int i=0; i<size; ++i ) {
	printf("%3d %3d %02x ", i, buf[i], buf[i]);
	for ( int j=7; j>=0; j-- )
	  if ( ! (or_mask[i]&(1<<j)) ) printf("0");
	  else if ( and_mask[i] & (1<<j) ) printf("1");
	  else printf("#");
	printf(" ");
	for ( int j=0; j<256; ++j ) {
	  unsigned int c = bins[i][j];
	  char s;
	  if ( ! c ) s = '_';
	  else if ( c < count/10 ) s = '+';
	  else s = '#';
	  printf("%c", s);
	}
	printf("\n");
      }
      fflush(stdout);
    }
    if ( count % accumulate == 0 ) reset();
    char cmd[16];
    if ( tty>=0 && read(tty,&cmd,sizeof(cmd)) > 0 ) reset();
  }
  return 0;
}
