#include <u.h>
#include <libc.h>
#include <bio.h>

enum
{
	Charmax = 2097152,
	Bucketsize = 256,
	Buckets = Charmax / Bucketsize, /* 8192 */
};

int **buckets;

Biobuf fin;
Biobuf fout;

void count(int);
void show(void);
int isprint(Rune);

void
main(int argc, char *argv[])
{
	int i, fd;

	buckets = calloc(Buckets, sizeof(int *));
	if(buckets == nil)
		sysfatal("out of memory");

	Binit(&fout, 1, OWRITE);

	if(argc == 1)
		count(0);
	else for(i = 1; i < argc; i++) {
		Bprint(&fout, "%s\n", argv[i]);
		Bflush(&fout);
		fd = open(argv[i], OREAD);
		if(fd < 0) {
			perror("open");
			continue;
		}
		count(fd);
		close(fd);
	}
	show();
	exits(0);
}

void
count(int fd)
{
	int i, j;
	long c;

	Binit(&fin, fd, OREAD);
	while((c = Bgetrune(&fin)) >= 0) {
		i = c / Bucketsize;
		if(buckets[i] == nil) {
			buckets[i] = calloc(Bucketsize, sizeof(int));
			if(buckets[i] == nil)
				sysfatal("out of memory");
		}
		j = c % Bucketsize;
		buckets[i][j]++;
	}	
	Bterm(&fin);
}

void
show(void)
{
	int i, j, *b;
	long c;

	for(i = 0; i < Buckets; i++) {
		b = buckets[i];
		if(b == nil)
			continue;
		for(j = 0; j < Bucketsize; j++) {
			if(b[j] == 0)
				continue;
			c = (i * Bucketsize) + j;
			Bprint(&fout, "%04x\t", c);
			if(c == 0x9)
				Bprint(&fout, "ht");
			else if(c == 0xA)
				Bprint(&fout, "nl");
			else if(c == 0x20)
				Bprint(&fout, "sp");
			else if(isprint(c))
				Bprint(&fout, "%C", c);
			else
				Bprint(&fout, "np");
			Bprint(&fout, "\t%d\n", b[j]);
		}
	}
}

/* from strings.c */
int
isprint(Rune r)
{
	if (r != Runeerror)
	if ((r >= ' ' && r < 0x7F) || r > 0xA0)
		return 1;
	return 0;
}
