// Copyright (c) 2004 David Muse
// See the COPYING file for more information
#include <rudiments/directory.h>
#include <rudiments/charstring.h>
#include <rudiments/error.h>
// for DIR
#ifdef RUDIMENTS_HAVE_DIRENT_H
#include <dirent.h>
#else
#include <direct.h>
#endif
#include <stdlib.h>
#ifdef RUDIMENTS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif
class directoryprivate {
friend class directory;
private:
DIR *_dir;
uint64_t _currentindex;
};
// LAME: not in the class
#if !defined(RUDIMENTS_HAVE_READDIR_R)
static mutex *_rdmutex;
#endif
// lame that this isn't part of the class, but I can't think of another way to
// keep #ifndef RUDIMENTS_HAVE_DIRENT_H out of the header file
#ifdef RUDIMENTS_HAVE_READDIR_R
static int64_t bufferSize(directory *d, DIR *dirp) {
int64_t name_max=d->maxFileNameLength();
if (name_max==-1) {
return -1;
}
#ifdef RUDIMENTS_HAVE_DIRENT_H
//return offsetof(struct dirent, d_name)+name_max+1;
return sizeof(struct dirent)+name_max+1;
#else
return sizeof(struct direct)+name_max+1;
#endif
}
#endif
directory::directory() {
pvt=new directoryprivate;
pvt->_dir=NULL;
pvt->_currentindex=0;
}
directory::~directory() {
close();
delete pvt;
}
bool directory::open(const char *path) {
do {
pvt->_dir=opendir(path);
} while (pvt->_dir==NULL && error::getErrorNumber()==EINTR);
return (pvt->_dir!=NULL);
}
bool directory::close() {
bool retval=true;
if (pvt->_dir) {
do {
retval=!closedir(pvt->_dir);
} while (!retval && error::getErrorNumber()==EINTR);
pvt->_dir=NULL;
pvt->_currentindex=0;
}
return retval;
}
uint64_t directory::getChildCount() {
// handle unopened directory
if (!pvt->_dir) {
return 0;
}
// rewind
rewinddir(pvt->_dir);
pvt->_currentindex=0;
#ifdef RUDIMENTS_HAVE_READDIR_R
// get the size of the buffer
int64_t size=bufferSize(this,pvt->_dir);
if (size==-1) {
return 0;
}
#ifdef RUDIMENTS_HAVE_DIRENT_H
dirent *entry=reinterpret_cast<dirent *>(
new unsigned char[size]);
dirent *result;
#else
direct *entry=reinterpret_cast<direct *>(
new unsigned char[size]);
dirent *result;
#endif
for (;;) {
int rdresult;
do {
rdresult=readdir_r(pvt->_dir,entry,&result);
} while (rdresult==-1 &&
error::getErrorNumber()==EINTR);
if (!result) {
delete[] entry;
return pvt->_currentindex;
}
pvt->_currentindex++;
}
#else
#ifdef RUDIMENTS_HAVE_DIRENT_H
dirent *entry;
#else
direct *entry;
#endif
if (_rdmutex && !_rdmutex->lock()) {
return 0;
}
for (;;) {
do {
entry=readdir(pvt->_dir);
} while (!entry && error::getErrorNumber()==EINTR);
if (!entry) {
if (_rdmutex) {
_rdmutex->unlock();
}
return pvt->_currentindex;
}
pvt->_currentindex++;
}
#endif
}
char *directory::getChildName(uint64_t index) {
// directory entries are 1-based
uint64_t actualindex=index+1;
if (actualindex<pvt->_currentindex) {
// handle unopened directory
if (!pvt->_dir) {
return NULL;
}
// rewind
rewinddir(pvt->_dir);
pvt->_currentindex=0;
}
#ifdef RUDIMENTS_HAVE_READDIR_R
// get the size of the buffer
int64_t size=bufferSize(this,pvt->_dir);
if (size==-1) {
return NULL;
}
#ifdef RUDIMENTS_HAVE_DIRENT_H
dirent *entry=reinterpret_cast<dirent *>(
new unsigned char[size]);
dirent *result;
#else
direct *entry=reinterpret_cast<direct *>(
new unsigned char[size]);
dirent *result;
#endif
for (uint64_t i=pvt->_currentindex; i<actualindex; i++) {
int rdresult;
do {
rdresult=readdir_r(pvt->_dir,entry,&result);
} while (rdresult==-1 &&
error::getErrorNumber()==EINTR);
if (rdresult || !result) {
delete[] entry;
return NULL;
}
pvt->_currentindex++;
}
char *retval=charstring::duplicate(result->d_name);
delete[] entry;
return retval;
#else
#ifdef RUDIMENTS_HAVE_DIRENT_H
dirent *entry;
#else
direct *entry;
#endif
if (_rdmutex && !_rdmutex->lock()) {
return NULL;
}
for (uint64_t i=pvt->_currentindex; i<actualindex; i++) {
do {
entry=readdir(pvt->_dir);
} while (!entry && error::getErrorNumber()==EINTR);
if (!entry) {
return NULL;
}
pvt->_currentindex++;
}
char *retval=charstring::duplicate(entry->d_name);
if (_rdmutex) {
_rdmutex->unlock();
}
return retval;
#endif
}
bool directory::create(const char *path, mode_t perms) {
int result;
do {
result=mkdir(path,perms);
} while (result==-1 && error::getErrorNumber()==EINTR);
return !result;
}
bool directory::remove(const char *path) {
int result;
do {
result=rmdir(path);
} while (result==-1 && error::getErrorNumber()==EINTR);
return !result;
}
char *directory::getCurrentWorkingDirectory() {
size_t size=1024;
for (;;) {
char *buffer=new char[size];
char *result;
do {
result=getcwd(buffer,size);
} while (!result && error::getErrorNumber()==EINTR);
if (result) {
return buffer;
} else {
delete[] buffer;
size=size+1024;
if (size>10240) {
return NULL;
}
}
}
}
bool directory::changeDirectory(const char *path) {
int result;
do {
result=chdir(path);
} while (result==-1 && error::getErrorNumber()==EINTR);
return !result;
}
bool directory::changeRoot(const char *path) {
int result;
do {
result=chroot(path);
} while (result==-1 && error::getErrorNumber()==EINTR);
return !result;
}
bool directory::needsMutex() {
#if !defined(RUDIMENTS_HAVE_READDIR_R)
return true;
#else
return false;
#endif
}
void directory::setMutex(mutex *mtx) {
#if !defined(RUDIMENTS_HAVE_READDIR_R)
_rdmutex=mtx;
#endif
}
int64_t directory::maxFileNameLength(const char *pathname) {
int64_t retval=pathConf(pathname,_PC_NAME_MAX);
#if defined(NAME_MAX)
if (retval==-1) {
retval = NAME_MAX;
}
#endif
return retval;
}
int64_t directory::maxPathLength(const char *pathname) {
return pathConf(pathname,_PC_PATH_MAX);
}
bool directory::canAccessLongFileNames(const char *pathname) {
return !pathConf(pathname,_PC_NO_TRUNC);
}
int64_t directory::pathConf(const char *pathname, int name) {
int64_t result;
do {
result=pathconf(pathname,name);
} while (result==-1 && error::getErrorNumber()==EINTR);
return result;
}
int64_t directory::maxFileNameLength() {
int64_t retval=fpathConf(_PC_NAME_MAX);
#if defined(NAME_MAX)
if (retval==-1) {
retval = NAME_MAX;
}
#endif
return retval;
}
int64_t directory::maxPathLength() {
return fpathConf(_PC_PATH_MAX);
}
bool directory::canAccessLongFileNames() {
return !fpathConf(_PC_NO_TRUNC);
}
int64_t directory::fpathConf(int name) {
int64_t result;
do {
result=fpathconf(
#if defined(RUDIMENTS_HAVE_DIRFD)
dirfd(pvt->_dir)
#elif defined(RUDIMENTS_HAVE_DIR_DD_FD)
pvt->_dir->dd_fd
#elif defined(RUDIMENTS_HAVE_DIR_D_FD)
pvt->_dir->d_fd
#else
#error need dirfd replacement
#endif
,name);
} while (result==-1 && error::getErrorNumber()==EINTR);
return result;
}
#ifdef RUDIMENTS_NAMESPACE
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1