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

Biobuf bout;
int	tflag;	/* long form:  name time size 0 */
int	dflag;	/* include one line per directory */

#define	NCACHE	4096	/* must be power of two */
struct cache
{
	Dir	*cache;
	int	n;
	int	max;
} cache[NCACHE];

void
err(char *s)
{
	fprint(2, "lsr: ");
	perror(s);
	exits(s);
}

int
warn(char *s)
{
	fprint(2, "lsr: ");
	perror(s);
	return 0;
}

int
seen(Dir *dir)
{
	Dir *dp;
	int i;
	struct cache *c;

	c = &cache[dir->qid.path&(NCACHE-1)];
	dp = c->cache;
	for(i=0; i<c->n; i++,dp++)
		if(dir->qid.path==dp->qid.path &&
		   dir->type==dp->type && dir->dev==dp->dev)
			return 1;
	if(c->n == c->max){
		c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir));
		if(cache == 0)
			err("malloc failure");
	}
	c->cache[c->n++] = *dir;
	return 0;
}

int
du(char *name, Dir *dir)
{
	int fd, i, n;
	char file[256], *cleanfile;
	Dir *dirn;

	if(dir==0){
		dir = dirstat(name);
		if(dir==0)
			return warn(name);
		if((dir->mode&DMDIR)==0){
			if(tflag)
				Bprint(&bout, "%s %lud %llud 0\n", name,
					dir->mtime, dir->length);
			else
				Bprint(&bout, "%s\n",name);
			return 0;
		}else if(dflag){
			cleanfile = file;
			if(file[0]=='.' && file[1]=='/')
				cleanfile += 2;
			Bprint(&bout, "%s/\n", cleanfile);
		}
	}
	fd=open(name, OREAD);
	if(fd<0)
		return warn(name);
	while((n=dirread(fd, &dirn))>0){
		for(i = 0; i<n; i++){
			dir = dirn+i;
			if((dir->mode&DMDIR)==0){
				sprint(file, "%s/%s", name, dir->name);
				cleanfile = file;
				if(file[0]=='.' && file[1]=='/')
					cleanfile += 2;
				if(tflag)
					Bprint(&bout, "%s %lud %llud 0\n", cleanfile,
						dir->mtime, dir->length);
				else
					Bprint(&bout, "%s\n",cleanfile);
				continue;
			}else if(dflag){
				cleanfile = file;
				if(file[0]=='.' && file[1]=='/')
					cleanfile += 2;
				Bprint(&bout, "%s/\n", cleanfile);
			}
			if(strcmp(dir->name, ".")==0 || strcmp(dir->name, "..")==0 || seen(dir)){
				continue;
			}
			sprint(file, "%s/%s", name, dir->name);
			du(file, dir);
		}
		free(dirn);
	}
	close(fd);
	return 0;
}

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

	ARGBEGIN{
	case 't':
		tflag=1;
		break;
	case 'd':
		dflag=1;
		break;
	}ARGEND
	Binit(&bout, 1, OWRITE);

	if(argc==0)
		du(".", 0);
	else
	for(i=0; i<argc; i++)
		du(argv[i], 0);
	Bterm(&bout);
	exits(0);
}
