#include <sys/types.h>
#include <dirent.h>
#include <sys/time.h> 
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>

#define NODEFINE
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include "fd.h"

static int
dstat2buf(File *dir, char *name, uchar *buf, uint nbuf)
{
	struct stat s;
	char *p;

	if(nbuf <= BIT16SZ)
		return 0;
	
	p = pm_malloc(strlen(dir->path)+1+strlen(name)+1);
	if(p == nil)
		return -1;
	strcpy(p, dir->path);
	strcat(p, "/");
	strcat(p, name);

	if(stat(p, &s) < 0){
		pm_free(p);
		return -1;
	}
	pm_free(p);
	return pm_stat2buf(&s, name, buf, nbuf);
}

long
pm_read(int fd, void *a, long n)
{
	int tot, m, ufd;
	File *f;
	struct dirent *de;
	
	if((f = pm_lockfile(fd, 0)) == nil)
		return -1;

	switch(f->type){
	default:
		pm_werrstr("cannot happen in pm_read");
		n = -1;
		break;
	case Tdir:
		if(f->mdents == 0){
			f->dents = pm_malloc(8*1024);
			if(f->dents == nil){
				n = -1;
				break;
			}
			f->mdents = 8*1024;
		}
		tot = 0;
		for(;;){
			for(; f->rdents+8 <= f->wdents; f->rdents+=de->d_reclen){
				de = (struct dirent*)(f->dents+f->rdents);
				if(de->d_name[0]=='.' && (de->d_name[1]=='\0' ||
				  (de->d_name[1]=='.' && de->d_name[2]=='\0')))
					continue;
				switch((m=dstat2buf(f, de->d_name, (uchar*)a+tot, n-tot))){
				case -1:
					break;
				case 0:
					goto BreakWhile;
				default:
					tot += m;
					break;
				}
			}
		BreakWhile:;
			f->rdents = 0;
			f->wdents = getdents(f->ufd, f->dents, f->mdents);
			if(f->wdents < 0){
				if(tot == 0){
					pm_oserror();
					tot = -1;
				}
				break;
			}
			if(f->wdents == 0)
				break;
		}
		n = tot;
		break;
	case Tfile:
	case Tpipe:
		ufd = f->ufd;
		pm_qunlock(&f->lk);
		n = read(ufd, a, n);
		if(n < 0){
			pm_oserror();
			n = -1;
		}
		return n;
	}
	pm_qunlock(&f->lk);
	return n;
}
