#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <unistd.h>

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

File fdtbl[Nfd];

File*
pm_lockfile(int fd, int closedokay)
{
	File *f;
	if(fd < 0 || fd >= Nfd){
		pm_werrstr("bad file descriptor");
		return nil;
	}
	f = &fdtbl[fd];
	pm_qlock(&f->lk);
	if(!closedokay && f->type == Tfree){
		pm_qunlock(&f->lk);
		pm_werrstr("bad file descriptor");
		return nil;
	}
	return f;
}

File*
pm_allocfile(int fd)
{
	File *f;
	if(fd < 0 || fd >= Nfd){
		pm_werrstr("bad file descriptor");
		return nil;
	}
	f = &fdtbl[fd];
	pm_qlock(&f->lk);
	if(f->type != Tfree){
		pm_qunlock(&f->lk);
		pm_werrstr("file descriptor already in use");
		return nil;
	}
	return f;
}

int
pm_closefile(File *f)
{
	int ret;

	switch(f->type){
	default:
		pm_werrstr("cannot happen in pm_close");
		ret = -1;
		break;
	case Tfree:
		pm_werrstr("file descriptor not open");
		ret = -1;
		break;
	case Tpipe:
	case Tfile:
		close(f->ufd);
		f->ufd = -1;
		ret = 0;
		break;
	case Tdir:
		close(f->ufd);
		f->ufd = -1;
		pm_free(f->dents);
		f->dents = 0;
		f->mdents = 0;
		ret = 0;
		break;
	}
	f->type = Tfree;
	pm_free(f->path);
	f->path = nil;
	pm_qunlock(&f->lk);
	return ret;
}

static int
isopen(int ufd)
{
	struct stat s;
	
	if(fstat(ufd, &s) < 0 && errno == EBADF)
		return 0;
	return 1;
}

void
pm_fdinit(void)
{
	int i;
	
	for(i=0; i<Nfd; i++){
		fdtbl[i].type = Tfree;
		fdtbl[i].pmfd = i;
	}
		
	for(i=0; i<=20; i++){
		if(isopen(i)){
			fdtbl[i].ufd = i;
			fdtbl[i].mode = PM_ORDWR;
			fdtbl[i].type = Tfile;
		}
	}
}

int
pm_stat2buf(struct stat *s, char *name, uchar *buf, uint nbuf)
{
	Dir d;
	char *p;
	struct passwd *pw;
	struct group *gp;
	int n;

	d.type = 'M';
	d.dev = s->st_dev;
	d.qid.path = s->st_ino;
	d.qid.vers = s->st_mtime;
	d.mode = (s->st_mode&0777);
	d.length = s->st_size;
	if(S_ISDIR(s->st_mode)){
		d.length = 0;
		d.mode |= PM_DMDIR;
	}
	if(s->st_flags&(UF_APPEND|SF_APPEND))
		d.mode |= PM_DMAPPEND;
	d.qid.type = d.mode>>24;
	d.atime = s->st_atimespec.tv_sec;
	d.mtime = s->st_mtimespec.tv_sec;
	if((p = pm_strrchr(name, '/')) != nil)
		d.name = p+1;
	else
		d.name = name;
	if((pw = getpwuid(s->st_uid)) != nil)
		d.uid = pw->pw_name;
	else
		d.uid = "";
	if((gp = getgrgid(s->st_gid)) != nil)
		d.gid = gp->gr_name;
	else
		d.gid = "";
	d.muid = "";

	if((n=convD2M(&d, buf, nbuf)) <= BIT16SZ)
		return 0;
	return n;
}


char*
pm_openpath(char *p)
{
	return pm_strdup(p);
}

int
pm_pmfd2ufd(int fd)
{
	File *f;
	int ufd;

	if((f = pm_lockfile(fd, 0)) == nil)
		return -1;
	ufd = f->ufd;
	pm_qunlock(&f->lk);
	return ufd;
}
