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

#undef asctime
#undef ctime
#undef gmtime
#undef localtime

#include <sys/stat.h>
#include <dirent.h>

extern int _p9dir(struct stat*, char*, Dir*, char**, char*);

static int
countde(char *p, int n)
{
	char *e;
	int m;
	struct dirent *de;

	e = p+n;
	m = 0;
	while(p < e){
		de = (struct dirent*)p;
		if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e)
			break;
		if(de->d_namlen == 1 && de->d_name[0]=='.')
			de->d_namlen = 0;
		else if(de->d_namlen == 2 && de->d_name[0]=='.' && de->d_name[1]=='.')
			de->d_namlen = 0;
		else
			m++;
		p += de->d_reclen;
	}
	return m;
}

static int
dirpackage(int fd, char *buf, int n, Dir **dp)
{
	int oldwd;
	char *p, *str, *estr;
	int i, nstr, m;
	struct dirent *de;
	struct stat st;
	Dir *d;

	n = countde(buf, n);
	if(n <= 0)
		return n;

	if((oldwd = open(".", O_RDONLY)) < 0)
		return -1;
	if(fchdir(fd) < 0)
		return -1;
		
	p = buf;
	nstr = 0;
	for(i=0; i<n; i++){
		de = (struct dirent*)p;
		if(stat(de->d_name, &st) < 0)
			de->d_namlen = 0;
		else
			nstr += _p9dir(&st, de->d_name, nil, nil, nil);
		p += de->d_reclen;
	}

	d = malloc(sizeof(Dir)*n+nstr);
	if(d == nil){
		fchdir(oldwd);
		close(oldwd);
		return -1;
	}
	str = (char*)&d[n];
	estr = str+nstr;

	p = buf;
	m = 0;
	for(i=0; i<n; i++){
		de = (struct dirent*)p;
		if(de->d_namlen != 0 && stat(de->d_name, &st) >= 0)
			_p9dir(&st, de->d_name, &d[m++], &str, estr);
		p += de->d_reclen;
	}

	fchdir(oldwd);
	close(oldwd);
	*dp = d;
	return m;
}

long
dirread(int fd, Dir **dp)
{
	char *buf;
	struct stat st;
	int n;

	*dp = 0;

	if(fstat(fd, &st) < 0)
		return -1;

	if(st.st_blksize < 8192)
		st.st_blksize = 8192;

	buf = malloc(st.st_blksize);
	if(buf == nil)
		return -1;

	n = getdents(fd, buf, st.st_blksize);
	if(n < 0){
		free(buf);
		return -1;
	}
	n = dirpackage(fd, buf, n, dp);
	free(buf);
	return n;
}


long
dirreadall(int fd, Dir **d)
{
	uchar *buf, *nbuf;
	long n, ts;
	struct stat st;

	if(fstat(fd, &st) < 0)
		return -1;

	if(st.st_blksize < 8192)
		st.st_blksize = 8192;

	buf = nil;
	ts = 0;
	for(;;){
		nbuf = realloc(buf, ts+st.st_blksize);
		if(nbuf == nil){
			free(buf);
			return -1;
		}
		buf = nbuf;
		n = getdents(fd, buf+ts, st.st_blksize);
		if(n <= 0)
			break;
		ts += n;
	}
	if(ts >= 0)
		ts = dirpackage(fd, buf, ts, d);
	free(buf);
	if(ts == 0 && n < 0)
		return -1;
	return ts;
}
