// Copyright (c) 2002 David Muse
// See the COPYING file for more information

#include <rudiments/file.h>
#include <rudiments/passwdentry.h>
#include <rudiments/groupentry.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>
#include <rudiments/error.h>

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#ifdef RUDIMENTS_HAVE_UNISTD_H
	#include <unistd.h>
#endif
#include <sys/time.h>
#ifdef RUDIMENTS_HAVE_XATTRS
	#include <sys/xattr.h>
#endif

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class fileprivate {
	friend class file;
	private:
		struct	stat	_st;
		bool		_getcurrentpropertiesonopen;
};

file::file() : filedescriptor() {
	pvt=new fileprivate;
	rawbuffer::zero(&pvt->_st,sizeof(pvt->_st));
	pvt->_getcurrentpropertiesonopen=true;
	type("file");
}

file::file(const file &f) : filedescriptor(f) {
	pvt=new fileprivate;
	fileClone(f);
	type("file");
}

file &file::operator=(const file &f) {
	if (this!=&f) {
		filedescriptor::operator=(f);
		fileClone(f);
	}
	return *this;
}

void file::fileClone(const file &f) {
	pvt->_st=f.pvt->_st;
	pvt->_getcurrentpropertiesonopen=f.pvt->_getcurrentpropertiesonopen;
}

file::~file() {
	delete pvt;
}

bool file::create(const char *name, mode_t perms) {
	return open(name,O_CREAT|O_TRUNC|O_RDWR,perms);
}

ssize_t file::create(const char *name, mode_t perms, unsigned short number) {
	return create(name,perms,&number,sizeof(unsigned short));
}

ssize_t file::create(const char *name, mode_t perms, unsigned long number) {
	return create(name,perms,&number,sizeof(unsigned long));
}

ssize_t file::create(const char *name, mode_t perms, float number) {
	return create(name,perms,&number,sizeof(float));
}

ssize_t file::create(const char *name, mode_t perms, double number) {
	return create(name,perms,&number,sizeof(double));
}

ssize_t file::create(const char *name, mode_t perms, char number) {
	return create(name,perms,&number,sizeof(char));
}

ssize_t file::create(const char *name, mode_t perms,
					const char *string, size_t size) {
	return create(name,perms,static_cast<const void *>(string),size);
}

ssize_t file::create(const char *name, mode_t perms, const char *string) {
	return create(name,perms,static_cast<const void *>(string),
						charstring::length(string));
}

ssize_t file::create(const char *name, mode_t perms,
					const void *data, size_t size) {
	size_t	retval;
	fd(openInternal(name,O_CREAT|O_TRUNC|O_RDWR,perms));
	if (fd()!=-1 &&
		((retval=write(data,size))==size) &&
		((pvt->_getcurrentpropertiesonopen)?
				getCurrentProperties():true)) {
		return retval;
	}
	close();
	return -1;
}

bool file::createFile(const char *name, mode_t perms) {
	file	fl;
	return fl.create(name,perms);
}

ssize_t file::createFile(const char *name, mode_t perms,
						unsigned short number) {
	file	fl;
	return fl.create(name,perms,number);
}

ssize_t file::createFile(const char *name, mode_t perms, unsigned long number) {
	file	fl;
	return fl.create(name,perms,number);
}

ssize_t file::createFile(const char *name, mode_t perms, float number) {
	file	fl;
	return fl.create(name,perms,number);
}

ssize_t file::createFile(const char *name, mode_t perms, double number) {
	file	fl;
	return fl.create(name,perms,number);
}

ssize_t file::createFile(const char *name, mode_t perms, char number) {
	file	fl;
	return fl.create(name,perms,number);
}

ssize_t file::createFile(const char *name, mode_t perms,
					const char *string, size_t size) {
	file	fl;
	return fl.create(name,perms,string,size);
}

ssize_t file::createFile(const char *name, mode_t perms, const char *string) {
	file	fl;
	return fl.create(name,perms,string);
}

ssize_t file::createFile(const char *name, mode_t perms,
					const void *data, size_t size) {
	file	fl;
	return fl.create(name,perms,data,size);
}

int file::openInternal(const char *name, int flags) {
	int	result;
	do {
		result=::open(name,flags);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

int file::openInternal(const char *name, int flags, mode_t perms) {
	int	result;
	do {
		result=::open(name,flags,perms);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

bool file::open(const char *name, int flags) {
	fd(openInternal(name,flags));
	return (fd()!=-1 &&
		((pvt->_getcurrentpropertiesonopen)?
				getCurrentProperties():true));
}

bool file::open(const char *name, int flags, mode_t perms) {
	fd(openInternal(name,flags,perms));
	return (fd()!=-1 &&
		((pvt->_getcurrentpropertiesonopen)?
				getCurrentProperties():true));
}

char *file::getContents() {
	off64_t	size=(fd()>-1)?pvt->_st.st_size:0;
	char	*contents=new char[size+1];
	contents[size]='\0';
	return (size==0 || read(contents,size)==size)?contents:NULL;
}

char *file::getContents(const char *name) {
	file	fl;
	fl.open(name,O_RDONLY);
	char	*contents=fl.getContents();
	fl.close();
	return contents;
}

ssize_t file::getContents(const char *name, unsigned char *buffer,
						size_t buffersize) {
	file	fl;
	fl.open(name,O_RDONLY);
	ssize_t	bytes=fl.getContents(buffer,buffersize);
	fl.close();
	return bytes;
}

ssize_t file::getContents(unsigned char *buffer, size_t buffersize) {
	return read(buffer,(buffersize<(size_t)getSize())?buffersize:getSize());
}


bool file::tryLockFile(short type) const {
	return tryLockRegion(type,0,0);
}

bool file::lockFile(short type) const {
	return lockRegion(type,0,0);
}

bool file::checkLockFile(short type, struct flock *retlck) const {
	return checkLockRegion(type,0,0,retlck);
}

bool file::unlockFile() const {
	return unlockRegion(0,0);
}

bool file::tryLockRegion(short type, off64_t start, off64_t len) const {
	return lock(F_SETLK,type,SEEK_SET,start,len);
}

bool file::lockRegion(short type, off64_t start, off64_t len) const {
	return lock(F_SETLKW,type,SEEK_SET,start,len);
}

bool file::checkLockRegion(short type, off64_t start, off64_t len,
						struct flock *retlck) const {
	return checkLock(type,SEEK_SET,start,len,retlck);
}

bool file::unlockRegion(off64_t start, off64_t len) const {
	return unlock(SEEK_SET,start,len);
}

bool file::tryLockFromCurrent(short type, off64_t len) const {
	return tryLockFromCurrent(type,0,len);
}

bool file::tryLockFromCurrent(short type, off64_t start, off64_t len) const {
	return lock(F_SETLK,type,SEEK_CUR,start,len);
}

bool file::lockFromCurrent(short type, off64_t len) const {
	return lockFromCurrent(type,0,len);
}

bool file::lockFromCurrent(short type, off64_t start, off64_t len) const {
	return lock(F_SETLKW,type,SEEK_CUR,start,len);
}

bool file::checkLockFromCurrent(short type, off64_t len,
				struct flock *retlck) const {
	return checkLockFromCurrent(type,0,len,retlck);
}

bool file::checkLockFromCurrent(short type, off64_t start, off64_t len,
						struct flock *retlck) const {
	return checkLock(type,SEEK_CUR,start,len,retlck);
}

bool file::unlockFromCurrent(off64_t len) const {
	return unlockFromCurrent(0,len);
}

bool file::unlockFromCurrent(off64_t start, off64_t len) const {
	return unlock(SEEK_CUR,start,len);
}

bool file::tryLockFromEnd(short type, off64_t len) const {
	return tryLockFromEnd(type,0,len);
}

bool file::tryLockFromEnd(short type, off64_t start, off64_t len) const {
	return lock(F_SETLK,type,SEEK_END,start,len);
}

bool file::lockFromEnd(short type, off64_t len) const {
	return lockFromEnd(type,0,len);
}

bool file::lockFromEnd(short type, off64_t start, off64_t len) const {
	return lock(F_SETLKW,type,SEEK_END,start,len);
}

bool file::checkLockFromEnd(short type, off64_t len, struct flock *retlck) const {
	return checkLockFromEnd(type,0,len,retlck);
}

bool file::checkLockFromEnd(short type, off64_t start, off64_t len,
						struct flock *retlck) const {
	return checkLock(type,SEEK_END,start,len,retlck);
}

bool file::unlockFromEnd(off64_t len) const {
	return unlockFromEnd(0,len);
}

bool file::unlockFromEnd(off64_t start, off64_t len) const {
	return unlock(SEEK_END,start,len);
}

bool file::tryLockRemainder(short type, off64_t start) const {
	return lock(F_SETLK,type,SEEK_SET,start,0);
}

bool file::lockRemainder(short type, off64_t start) const {
	return lock(F_SETLKW,type,SEEK_SET,start,0);
}

bool file::checkLockRemainder(short type, off64_t start,
					struct flock *retlck) const {
	return checkLock(type,SEEK_SET,start,0,retlck);
}

bool file::unlockRemainder(off64_t start) const {
	return unlock(SEEK_SET,start,0);
}

bool file::tryLockRemainderFromCurrent(short type) const {
	return tryLockRemainderFromCurrent(type,0);
}

bool file::tryLockRemainderFromCurrent(short type, off64_t start) const {
	return lock(F_SETLK,type,SEEK_CUR,start,0);
}

bool file::lockRemainderFromCurrent(short type) const {
	return lockRemainderFromCurrent(type,0);
}

bool file::lockRemainderFromCurrent(short type, off64_t start) const {
	return lock(F_SETLKW,type,SEEK_CUR,start,0);
}

bool file::checkLockRemainderFromCurrent(short type,
					struct flock *retlck) const {
	return checkLockRemainderFromCurrent(type,0,retlck);
}

bool file::checkLockRemainderFromCurrent(short type, off64_t start,
					struct flock *retlck) const {
	return checkLock(type,SEEK_CUR,start,0,retlck);
}

bool file::unlockRemainderFromCurrent() const {
	return unlockRemainderFromCurrent(0);
}

bool file::unlockRemainderFromCurrent(off64_t start) const {
	return unlock(SEEK_CUR,start,0);
}

bool file::tryLockRemainderFromEnd(short type) const {
	return tryLockRemainderFromEnd(type,0);
}

bool file::tryLockRemainderFromEnd(short type, off64_t start) const {
	return lock(F_SETLK,type,SEEK_END,start,0);
}

bool file::lockRemainderFromEnd(short type) const {
	return lockRemainderFromEnd(type,0);
}

bool file::lockRemainderFromEnd(short type, off64_t start) const {
	return lock(F_SETLKW,type,SEEK_END,start,0);
}

bool file::checkLockRemainderFromEnd(short type, struct flock *retlck) const {
	return checkLockRemainderFromEnd(type,0,retlck);
}

bool file::checkLockRemainderFromEnd(short type, off64_t start,
						struct flock *retlck) const {
	return checkLock(type,SEEK_END,start,0,retlck);
}

bool file::sequentialAccess(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_SEQUENTIAL)
	return posixFadvise(start,len,POSIX_FADV_SEQUENTIAL);
	#else
	return true;
	#endif
}

bool file::randomAccess(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_RANDOM)
	return posixFadvise(start,len,POSIX_FADV_RANDOM);
	#else
	return true;
	#endif
}

bool file::onlyOnce(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_NOREUSE)
	return posixFadvise(start,len,POSIX_FADV_NOREUSE);
	#else
	return true;
	#endif
}

bool file::willNeed(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_WILLNEED)
	return posixFadvise(start,len,POSIX_FADV_WILLNEED);
	#else
	return true;
	#endif
}

bool file::wontNeed(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_DONTNEED)
	return posixFadvise(start,len,POSIX_FADV_DONTNEED);
	#else
	return true;
	#endif
}

bool file::normalAccess(off64_t start, size_t len) const {
	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
			defined(POSIX_FADV_NORMAL)
	return posixFadvise(start,len,POSIX_FADV_NORMAL);
	#else
	return true;
	#endif
}

bool file::reserve(off64_t start, size_t len) const {
	#ifdef RUDIMENTS_HAVE_POSIX_FALLOCATE
	int	result;
	do {
		result=posix_fallocate(fd(),start,len);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

bool file::truncate(const char *filename) {
	return truncate(filename,0);
}

bool file::truncate(const char *filename, off64_t length) {
	int	result;
	do {
		result=::truncate(filename,length);
	} while (result==-1 && error::getErrorNumber()==-1);
	return !result;
}

bool file::truncate() const {
	return ftruncate(fd(),0);
}

bool file::truncate(off64_t length) const {
	int	result;
	do {
		result=::ftruncate(fd(),length);
	} while (result==-1 && error::getErrorNumber()==-1);
	return !result;
}

bool file::unlockRemainderFromEnd() const {
	return unlockRemainderFromEnd(0);
}

bool file::unlockRemainderFromEnd(off64_t start) const {
	return unlock(SEEK_END,start,0);
}

off64_t file::setPositionRelativeToBeginning(off64_t offset) const {
	return lseek(offset,SEEK_SET);
}

off64_t file::setPositionRelativeToCurrent(off64_t offset) const {
	return lseek(offset,SEEK_CUR);
}

off64_t file::setPositionRelativeToEnd(off64_t offset) const {
	return lseek(offset,SEEK_END);
}

off64_t file::getCurrentPosition() const {
	return lseek(0,SEEK_CUR);
}

off64_t file::lseek(off64_t offset, int whence) const {
	int	result;
	do {
		result=::lseek(fd(),offset,whence);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

bool file::exists(const char *filename) {
	return accessible(filename,F_OK);
}

bool file::readable(const char *filename) {
	return accessible(filename,R_OK);
}

bool file::writeable(const char *filename) {
	return accessible(filename,W_OK);
}

bool file::executable(const char *filename) {
	return accessible(filename,X_OK);
}

bool file::accessible(const char *filename, int mode) {
	int	result;
	do {
		result=access(filename,mode);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::getCurrentProperties() {
	int	result;
	do {
		result=fstat(fd(),&pvt->_st);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

#define STAT(filename,out,member) \
	struct stat st; \
	int	result; \
	do { \
		result=stat(filename,&st); \
	} while (result==-1 && error::getErrorNumber()==EINTR); \
	if (result==-1) { \
		return false; \
	} \
	*out=st.member; \
	return true;

bool file::getPermissions(const char *filename, mode_t *mode) {
	STAT(filename,mode,st_mode)
}

bool file::getOwnerUserId(const char *filename, uid_t *uid) {
	STAT(filename,uid,st_uid)
}

bool file::getOwnerGroupId(const char *filename, gid_t *gid) {
	STAT(filename,gid,st_gid)
}

bool file::getSize(const char *filename, off64_t *size) {
	STAT(filename,size,st_size)
}

bool file::getBlockSize(const char *filename, blksize_t *size) {
#ifdef RUDIMENTS_HAVE_BLKSIZE_T
	STAT(filename,size,st_blksize)
#else
	*size=-1;
	return true;
#endif
}

bool file::getBlockCount(const char *filename, blkcnt_t *blocks) {
#ifdef RUDIMENTS_HAVE_BLKCNT_T
	STAT(filename,blocks,st_blocks)
#else
	*blocks=-1;
	return true;
#endif
}

#ifndef RUDIMENTS_HAVE_S_ISSOCK
	#define S_ISSOCK(m) ((m&040000==040000)?1:0)
#endif

int file::isSocket(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISSOCK(st.st_mode):-1;
}

int file::isSymbolicLink(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISLNK(st.st_mode):-1;
}

int file::isRegularFile(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISREG(st.st_mode):-1;
}

int file::isBlockDevice(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISBLK(st.st_mode):-1;
}

int file::isDirectory(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISDIR(st.st_mode):-1;
}

int file::isCharacterDevice(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISCHR(st.st_mode):-1;
}

int file::isFifo(const char *filename) {
	struct stat	st;
	return (stat(filename,&st)>-1)?S_ISFIFO(st.st_mode):-1;
}

bool file::getLastAccessTime(const char *filename, time_t *atime) {
	STAT(filename,atime,st_atime)
}

bool file::getLastModificationTime(const char *filename, time_t *mtime) {
	STAT(filename,mtime,st_mtime)
}

bool file::getLastChangeTime(const char *filename, time_t *ctime) {
	STAT(filename,ctime,st_ctime)
}

bool file::getDevice(const char *filename, dev_t *dev) {
	STAT(filename,dev,st_dev)
}

bool file::getDeviceType(const char *filename, dev_t *devtype) {
	STAT(filename,devtype,st_rdev)
}

bool file::getInode(const char *filename, ino_t *inode) {
	STAT(filename,inode,st_ino)
}

bool file::getNumberOfHardLinks(const char *filename, nlink_t *nlink) {
	STAT(filename,nlink,st_nlink)
}

mode_t file::getPermissions() const {
	return pvt->_st.st_mode;
}

uid_t file::getOwnerUserId() const {
	return pvt->_st.st_uid;
}

gid_t file::getOwnerGroupId() const {
	return pvt->_st.st_gid;
}

off64_t file::getSize() const {
	return pvt->_st.st_size;
}


blksize_t file::getBlockSize() const {
#ifdef RUDIMENTS_HAVE_BLKSIZE_T
	return pvt->_st.st_blksize;
#else
	return -1;
#endif
}

blkcnt_t file::getBlockCount() const {
#ifdef RUDIMENTS_HAVE_BLKCNT_T
	return pvt->_st.st_blocks;
#else
	return -1;
#endif
}

int file::isSocket() const {
	return S_ISSOCK(pvt->_st.st_mode);
}

int file::isSymbolicLink() const {
	return S_ISLNK(pvt->_st.st_mode);
}

int file::isRegularFile() const {
	return S_ISREG(pvt->_st.st_mode);
}

int file::isBlockDevice() const {
	return S_ISBLK(pvt->_st.st_mode);
}

int file::isDirectory() const {
	return S_ISDIR(pvt->_st.st_mode);
}

int file::isCharacterDevice() const {
	return S_ISCHR(pvt->_st.st_mode);
}

int file::isFifo() const {
	return S_ISFIFO(pvt->_st.st_mode);
}

time_t file::getLastAccessTime() const {
	return pvt->_st.st_atime;
}

time_t file::getLastModificationTime() const {
	return pvt->_st.st_mtime;
}

time_t file::getLastChangeTime() const {
	return pvt->_st.st_ctime;
}


dev_t file::getDevice() const {
	return pvt->_st.st_dev;
}

dev_t file::getDeviceType() const {
	return pvt->_st.st_rdev;
}

ino_t file::getInode() const {
	return pvt->_st.st_ino;
}

nlink_t file::getNumberOfHardLinks() const {
	return pvt->_st.st_nlink;
}

void file::getCurrentPropertiesOnOpen() {
	pvt->_getcurrentpropertiesonopen=true;
}

void file::dontGetCurrentPropertiesOnOpen() {
	pvt->_getcurrentpropertiesonopen=false;
}

bool file::lock(int method, short type, short whence,
				off64_t start, off64_t len) const {
	struct flock	lck;
	lck.l_type=type;
	lck.l_whence=whence;
	lck.l_start=start;
	lck.l_len=len;
	return !fcntl(method,reinterpret_cast<long>(&lck));
}

bool file::checkLock(short type, short whence, off64_t start, off64_t len,
						struct flock *retlck) const {
	struct flock	lck;
	lck.l_type=type;
	lck.l_whence=whence;
	lck.l_start=start;
	lck.l_len=len;
	int	result=fcntl(F_SETLKW,reinterpret_cast<long>(&lck));
	*retlck=lck;
	return !result;
}

bool file::unlock(short whence, off64_t start, off64_t len) const {
	struct flock	lck;
	lck.l_type=F_UNLCK;
	lck.l_whence=whence;
	lck.l_start=start;
	lck.l_len=len;
	return !fcntl(F_SETLK,reinterpret_cast<long>(&lck));
}

bool file::changeOwner(const char *newuser, const char *newgroup) const {
	uid_t	uid;
	gid_t	gid;
	return (passwdentry::getUserId(newuser,&uid) &&
		groupentry::getGroupId(newgroup,&gid) &&
		changeOwner(uid,gid));
}

bool file::changeOwner(uid_t uid, gid_t gid) const {
	int	result;
	do {
		result=fchown(fd(),uid,gid);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::changeOwner(const char *filename, const char *newuser,
						const char *newgroup) {
	uid_t	uid;
	gid_t	gid;
	return (passwdentry::getUserId(newuser,&uid) &&
		groupentry::getGroupId(newgroup,&gid) &&
		changeOwner(filename,uid,gid));
}

bool file::changeOwner(const char *filename, uid_t uid, gid_t gid) {
	file	fl;
	return (fl.open(filename,O_RDWR) && fl.changeOwner(uid,gid));
}

bool file::changeOwnerUserId(const char *newuser) const {
	uid_t	uid;
	return (passwdentry::getUserId(newuser,&uid) &&
				changeOwnerUserId(uid));
}

bool file::changeOwnerUserId(uid_t uid) const {
	return changeOwner(uid,(gid_t)-1);
}

bool file::changeOwnerUserId(const char *filename, const char *newuser) {
	uid_t	uid;
	return (passwdentry::getUserId(newuser,&uid) &&
				changeOwnerUserId(filename,uid));
}

bool file::changeOwnerUserId(const char *filename, uid_t uid) {
	return changeOwner(filename,uid,(gid_t)-1);
}


bool file::changeOwnerGroupId(const char *newgroup) const {
	gid_t	gid;
	return (groupentry::getGroupId(newgroup,&gid) &&
				changeOwnerGroupId(gid));
}

bool file::changeOwnerGroupId(gid_t gid) const {
	return changeOwner((uid_t)-1,gid);
}

bool file::changeOwnerGroupId(const char *filename, const char *newgroup) {
	gid_t	gid;
	return (groupentry::getGroupId(newgroup,&gid) &&
				changeOwnerGroupId(filename,gid));
}

bool file::changeOwnerGroupId(const char *filename, gid_t gid) {
	return changeOwner(filename,(uid_t)-1,gid);
}

bool file::canChangeOwner(const char *filename) {
	return !pathConf(filename,_PC_CHOWN_RESTRICTED);
}

bool file::canChangeOwner() const {
	return !fpathConf(_PC_CHOWN_RESTRICTED);
}

bool file::rename(const char *oldpath, const char *newpath) {
	int	result;
	do {
		result=::rename(oldpath,newpath);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::remove(const char *filename) {
	int	result;
	do {
		result=unlink(filename);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::createHardLink(const char *oldpath, const char *newpath) {
	int	result;
	do {
		result=link(oldpath,newpath);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::createSymbolicLink(const char *oldpath, const char *newpath) {
	int	result;
	do {
		result=symlink(oldpath,newpath);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

char *file::resolveSymbolicLink(const char *filename) {

	size_t	buffersize=1024;
	for (;;) {

		// create a buffer to store the path
		char	*buffer=new char[buffersize];

		// read the path into the buffer
		int	len;
		do {
			len=::readlink(filename,buffer,buffersize);
		} while (len==-1 && error::getErrorNumber()==EINTR);

		if (len==-1) {

			// if the call to readlink failed, delete the buffer
			// and return NULL
			delete[] buffer;
			return NULL;

		} else if ((size_t)len==buffersize) {

			// if the length of the path was the same as the buffer
			// size the we didn't get the entire path, increase the
			// size of the buffer and try again
			delete[] buffer;
			buffersize=buffersize+1024;

			// if the buffer size exceeds 10k then return failure
			if (buffersize>10240) {
				return NULL;
			}

		} else {
			// NULL-terminate the buffer, readlink()
			// doesn't do this for us
			buffer[len]='\0';
			return buffer;
		}
	}
}

bool file::sync() const {
	int	result;
	do {
		result=fsync(fd());
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::dataSync() const {
	#ifdef RUDIMENTS_HAVE_FDATASYNC
	int	result;
	do {
		result=fdatasync(fd());
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return sync();
	#endif
}

bool file::setLastAccessTime(const char *filename,
					time_t lastaccesstime) {
	time_t	lastmodtime;
	if (!getLastModificationTime(filename,&lastmodtime)) {
		return false;
	}
	return setLastAccessAndModificationTimes(filename,
						lastaccesstime,
						lastmodtime);
}

bool file::setLastModificationTime(const char *filename,
					time_t lastmodtime) {
	time_t	lastaccesstime;
	if (!getLastAccessTime(filename,&lastaccesstime)) {
		return false;
	}
	return setLastAccessAndModificationTimes(filename,
						lastaccesstime,
						lastmodtime);
}

bool file::setLastAccessAndModificationTimes(const char *filename,
						time_t lastaccesstime,
						time_t lastmodtime) {
	timeval	tv[2];
	tv[0].tv_sec=static_cast<long>(lastaccesstime);
	tv[0].tv_usec=0;
	tv[1].tv_sec=static_cast<long>(lastmodtime);
	tv[1].tv_usec=0;
	int	result;
	do {
		result=utimes(filename,tv);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::setLastAccessAndModificationTimes(const char *filename) {
	int	result;
	do {
		result=utimes(filename,NULL);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

bool file::createFifo(const char *filename, mode_t perms) {
	int	result;
	do {
		result=mkfifo(filename,perms);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
}

int file::createTemporaryFile(char *templatefilename) {
	int	result;
	do {
		result=mkstemp(templatefilename);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

const char * const *file::listAttributes() const {

	#ifndef RUDIMENTS_HAVE_XATTRS
	return NULL;
	#else

	// The flistxattr interface is designed such that you have to guess the
	// size of the buffer that it will need, call flistxattr, then see if
	// your guess was correct.  If not, you have to try again.

	// try with a 8 byte buffer to start
	size_t	size=8;
	char	*buffer;

	// try 100 times...
	for (int i=0; i<100; i++) {

		// allocate a buffer
		buffer=new char[size];

		// attempt to read the attribute into the buffer
		ssize_t	newsize;
		do {
			newsize=flistxattr(fd(),buffer,size);
		} while (newsize==-1 && error::getErrorNumber()==EINTR);

		// it's possible that someone changed the attribute between
		// the previous 2 calls to fgetxattr and increased the size
		// of the buffer necessary to retrieve it, if so, try again
		// with a bigger buffer.
		if (newsize==-1) {
			delete[] buffer;
			return NULL;
		} else if (newsize>(ssize_t)size) {
			delete[] buffer;
			size=newsize;
		} else {
			const char * const	*retval=
						attributeArray(buffer,size);
			delete[] buffer;
			return retval;
		}
	}

	// if we couldn't get the attribute after 100 tries, give up
	return NULL;
	#endif
}

bool file::getAttribute(const char *name, unsigned short *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(unsigned short));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, unsigned long *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(unsigned long));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, short *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(short));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, long *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(long));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, float *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(float));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, double *number) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(number,buffer,sizeof(double));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, unsigned char *character) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(character,buffer,sizeof(unsigned char));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, char *character) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(character,buffer,sizeof(char));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, bool *value) const {
	size_t		size;
	unsigned char	*buffer;
	bool	retval=getAttribute(name,
				reinterpret_cast<void **>(&buffer),
				&size);
	if (retval) {
		rawbuffer::copy(value,buffer,sizeof(bool));
		delete[] buffer;
	}
	return retval;
}

bool file::getAttribute(const char *name, unsigned char **string) const {
	size_t	size;
	return getAttribute(name,reinterpret_cast<void **>(string),&size);
}

bool file::getAttribute(const char *name, char **string) const {
	size_t	size;
	return getAttribute(name,reinterpret_cast<void **>(string),&size);
}

bool file::getAttribute(const char *name,
				unsigned char **string, size_t *size) const {
	return getAttribute(name,reinterpret_cast<void **>(string),size);
}

bool file::getAttribute(const char *name, char **string, size_t *size) const {
	return getAttribute(name,reinterpret_cast<void **>(string),size);
}

bool file::getAttribute(const char *name, void **buffer, size_t *size) const {

	#ifndef RUDIMENTS_HAVE_XATTRS
	*buffer=NULL;
	*size=0;
	return false;
	#else

	// The fgetxattr interface is designed such that you have to guess the
	// size of the buffer that it will need, call fgetxattr, then see if
	// your guess was correct.  If not, you have to try again.

	// try with a 8 byte buffer to start
	(*size)=8;

	// try 100 times...
	for (int i=0; i<100; i++) {

		// allocate a buffer
		(*buffer)=static_cast<void *>(new unsigned char[(*size)]);

		// attempt to read the attribute into the buffer
		ssize_t	newsize;
		do {
			newsize=fgetxattr(fd(),name,(*buffer),(*size));
		} while (newsize==-1 && error::getErrorNumber()==EINTR);

		// it's possible that someone changed the attribute between
		// the previous 2 calls to fgetxattr and increased the size
		// of the buffer necessary to retrieve it, if so, try again
		// with a bigger buffer.
		if (newsize==-1) {
			delete[] static_cast<unsigned char *>(*buffer);
			return false;
		} else if (newsize>(ssize_t)(*size)) {
			delete[] static_cast<unsigned char *>(*buffer);
			(*size)=newsize;
		} else {
			return true;
		}
	}

	// if we couldn't get the attribute after 100 tries, give up
	return false;

	#endif
}

bool file::createAttribute(const char *name, unsigned short number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, unsigned long number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, short number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, long number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, float number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, double number) const {
	return createAttribute(name,&number,sizeof(number));
}

bool file::createAttribute(const char *name, unsigned char character) const {
	return createAttribute(name,&character,sizeof(character));
}

bool file::createAttribute(const char *name, char character) const {
	return createAttribute(name,&character,sizeof(character));
}

bool file::createAttribute(const char *name, bool value) const {
	return createAttribute(name,&value,sizeof(value));
}

bool file::createAttribute(const char *name,
				const unsigned char *string) const {
	return createAttribute(name,string,charstring::length(string));
}

bool file::createAttribute(const char *name, const char *string) const {
	return createAttribute(name,string,charstring::length(string));
}

bool file::createAttribute(const char *name, const unsigned char *string,
							size_t size) const {
	return createAttribute(name,static_cast<const void *>(string),size);
}

bool file::createAttribute(const char *name, const char *string,
							size_t size) const {
	return createAttribute(name,static_cast<const void *>(string),size);
}

#ifndef XATTR_CREATE
#define XATTR_CREATE 0
#endif
bool file::createAttribute(const char *name, const void *value,
							size_t size) const {
	return setAttribute(name,value,size,XATTR_CREATE);
}

bool file::replaceAttribute(const char *name, unsigned short number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, unsigned long number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, short number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, long number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, float number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, double number) const {
	return replaceAttribute(name,&number,sizeof(number));
}

bool file::replaceAttribute(const char *name, unsigned char character) const {
	return replaceAttribute(name,&character,sizeof(character));
}

bool file::replaceAttribute(const char *name, char character) const {
	return replaceAttribute(name,&character,sizeof(character));
}

bool file::replaceAttribute(const char *name, bool value) const {
	return replaceAttribute(name,&value,sizeof(value));
}

bool file::replaceAttribute(const char *name,
				const unsigned char *string) const {
	return replaceAttribute(name,string,charstring::length(string));
}

bool file::replaceAttribute(const char *name, const char *string) const {
	return replaceAttribute(name,string,charstring::length(string));
}

bool file::replaceAttribute(const char *name, const unsigned char *string,
							size_t size) const {
	return replaceAttribute(name,static_cast<const void *>(string),size);
}

bool file::replaceAttribute(const char *name, const char *string,
							size_t size) const {
	return replaceAttribute(name,static_cast<const void *>(string),size);
}

#ifndef XATTR_REPLACE
#define XATTR_REPLACE 0
#endif
bool file::replaceAttribute(const char *name, const void *value,
							size_t size) const {
	return setAttribute(name,value,size,XATTR_REPLACE);
}

bool file::setAttribute(const char *name, unsigned short number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, unsigned long number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, short number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, long number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, float number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, double number) const {
	return setAttribute(name,&number,sizeof(number));
}

bool file::setAttribute(const char *name, unsigned char character) const {
	return setAttribute(name,&character,sizeof(character));
}

bool file::setAttribute(const char *name, char character) const {
	return setAttribute(name,&character,sizeof(character));
}

bool file::setAttribute(const char *name, bool value) const {
	return setAttribute(name,&value,sizeof(value));
}

bool file::setAttribute(const char *name, const unsigned char *string) const {
	return setAttribute(name,string,charstring::length(string));
}

bool file::setAttribute(const char *name, const char *string) const {
	return setAttribute(name,string,charstring::length(string));
}

bool file::setAttribute(const char *name, const unsigned char *string,
							size_t size) const {
	return setAttribute(name,static_cast<const void *>(string),size);
}

bool file::setAttribute(const char *name, const char *string,
							size_t size) const {
	return setAttribute(name,static_cast<const void *>(string),size);
}

bool file::setAttribute(const char *name, const void *value,
							size_t size) const {
	return setAttribute(name,value,size,0);
}

bool file::setAttribute(const char *name, const void *value,
						size_t size, int flags) const {

	#ifndef RUDIMENTS_HAVE_XATTRS
	return false;
	#else

	ssize_t	result;
	do {
		result=fsetxattr(fd(),name,value,size,flags);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;

	#endif
}

bool file::removeAttribute(const char *name) const {

	#ifndef RUDIMENTS_HAVE_XATTRS
	return false;
	#else

	ssize_t	result;
	do {
		result=fremovexattr(fd(),name);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;

	#endif
}

const char * const *file::attributeArray(const char *buffer,
						size_t size) const {

	#ifndef RUDIMENTS_HAVE_XATTRS
	return NULL;
	#else


	// count the number of attributes
	int	counter=0;
	for (size_t index=0; index<size; index++) {
		if (!buffer[index]) {
			counter++;
		}
	}

	// create an array to hold them
	char	**attributes=new char *[counter+1];
	attributes[counter]=NULL;

	// copy the attributes into the array
	const char	*ptr=buffer;
	counter=0;
	for (size_t index=0; index<size; index++) {
		if (!buffer[index]) {
			attributes[counter]=charstring::duplicate(ptr);
			if (index<size-1) {
				ptr=&buffer[index+1];
			}
			counter++;
		}
	}

	// return the array
	return attributes;

	#endif
}

struct stat *file::getStat() {
	return &(pvt->_st);
}

char *file::dirname(const char *filename) {

	if (!filename) {
		return NULL;
	} else if (!charstring::contains(filename,'/') ||
			!charstring::compare(filename,".")) {
		return charstring::duplicate(".");
	} else if (!charstring::compare(filename,"..")) {
		return charstring::duplicate("..");
	} else if (!charstring::compare(filename,"/")) {
		return charstring::duplicate("/");
	}

	char	*retval=charstring::duplicate(filename);
	charstring::rightTrim(retval,'/');
	char	*lastslash=charstring::findLast(retval,'/');
	if (lastslash==retval) {
		(*(lastslash+1))='\0';
	} else {
		(*lastslash)='\0';
	}
	return retval;
}

char *file::basename(const char *filename) {

	if (!filename) {
		return NULL;
	}

	char	*copy=charstring::duplicate(filename);
	charstring::rightTrim(copy,'/');

	char	*retval;
	char	*ptr=charstring::findLast(copy,'/');
	if (!ptr) {
		retval=charstring::duplicate(copy);
	} else {
		retval=charstring::duplicate(ptr+1);
	}
	delete[] copy;
	return retval;
}

char *file::basename(const char *filename, const char *suffix) {
	char	*retval=basename(filename);
	char	*ptr=charstring::findLast(retval,suffix);
	if (!(*(ptr+charstring::length(suffix)))) {
		(*ptr)='\0';
	}
	return retval;
}

key_t file::generateKey(const char *filename, int id) {
#ifdef RUDIMENTS_HAVE_FTOK
	#ifdef RUDIMENTS_HAVE_CONST_CHAR_FTOK
		return ::ftok(filename,id);
	#else
		return ::ftok(const_cast<char *>(filename),id);
	#endif
#else
	file	f;
	if (!f.open(filename,O_RDONLY)) {
		return -1;
	}
	return (key_t)((f.getInode() & 0xffff) |
			((f.getDevice() & 0xff) << 16) |
			((id & 0xff)  << 24));
#endif
}

long file::maxLinks(const char *filename) {
	return pathConf(filename,_PC_LINK_MAX);
}

long file::maxLinks() const {
	return fpathConf(_PC_LINK_MAX);
}

bool file::posixFadvise(off64_t offset, off64_t len, int advice) const {
	#ifdef RUDIMENTS_HAVE_POSIX_FADVISE
	int	result;
	do {
		result=posix_fadvise(fd(),offset,len,advice);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return true;
	#endif
}

long file::pathConf(const char *path, int name) {
	long	result;
	do {
		result=pathconf(path,name);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

long file::fpathConf(int name) const {
	long	result;
	do {
		result=fpathconf(fd(),name);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return result;
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1