#include "KinoSearch/Util/ToolSet.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#define KINO_WANT_FSFOLDER_VTABLE
#include "KinoSearch/Store/FSFolder.r"
#include "KinoSearch/Store/FSFileDes.r"
#include "KinoSearch/Store/InStream.r"
#include "KinoSearch/Store/OutStream.r"
static ByteBuf*
full_path(FSFolder *self, const ByteBuf *filename);
static OutStream*
do_open_outstream(FSFolder *self, const ByteBuf *filename);
FSFolder*
FSFolder_new(const ByteBuf *path)
{
CREATE(self, FSFolder, FSFOLDER);
/* copy path, strip trailing slash or equivalent */
self->path = BB_CLONE(path);
if (strcmp(BBEND(self->path) - 1, DIR_SEP) == 0)
self->path->len -= 1;
return self;
}
void
FSFolder_destroy(FSFolder *self)
{
REFCOUNT_DEC(self->path);
free(self);
}
#ifdef O_LARGEFILE
static const int write_flags
= O_CREAT | O_WRONLY | O_NONBLOCK | O_EXCL | O_LARGEFILE;
static const int read_flags = O_RDONLY | O_NONBLOCK | O_LARGEFILE;
#else
static const int write_flags
= O_CREAT | O_WRONLY | O_NONBLOCK | O_EXCL;
static const int read_flags = O_RDONLY | O_NONBLOCK;
#endif
OutStream*
FSFolder_open_outstream(FSFolder *self, const ByteBuf *filename)
{
OutStream *outstream = do_open_outstream(self, filename);
if (outstream == NULL)
CONFESS("Can't open '%s': %s", filename->ptr, strerror(errno));
return outstream;
}
OutStream*
FSFolder_safe_open_outstream(FSFolder *self, const ByteBuf *filename)
{
return do_open_outstream(self, filename);
}
static OutStream*
do_open_outstream(FSFolder *self, const ByteBuf *filename)
{
ByteBuf *path = full_path(self, filename);
FSFileDes *file_des = FSFileDes_new(path->ptr, write_flags, 0600, "ab");
OutStream *outstream = file_des == NULL
? NULL
: OutStream_new((FileDes*)file_des);
/* leave 1 refcount in file_des's new owner, outstream */
REFCOUNT_DEC(file_des);
REFCOUNT_DEC(path);
return outstream;
}
InStream*
FSFolder_open_instream(FSFolder *self, const ByteBuf *filename)
{
InStream *instream = NULL;
ByteBuf *path = full_path(self, filename);
FSFileDes *file_des
= FSFileDes_new(path->ptr, read_flags, 0600, "rb");
REFCOUNT_DEC(path);
if (file_des == NULL)
CONFESS("Can't open '%s': %s", filename->ptr, strerror(errno));
else
instream = InStream_new((FileDes*)file_des);
/* leave 1 refcount in file_des's new owner, instream */
REFCOUNT_DEC(file_des);
return instream;
}
VArray*
FSFolder_list(FSFolder *self)
{
DIR *dir = opendir(self->path->ptr);
struct dirent *entry;
VArray *dirlist = VA_new(0);
if (dir == NULL) {
CONFESS("Couldn't opendir '%s'", self->path->ptr);
}
while ((entry = readdir(dir)) != NULL ) {
size_t len = strlen(entry->d_name);
if ( (len == 1 && strncmp(entry->d_name, ".", 1) == 0)
|| (len == 2 && strncmp(entry->d_name, "..", 2) == 0)
) {
continue;
}
else {
ByteBuf *bb = BB_new_str(entry->d_name, len);
VA_Push(dirlist, (Obj*)bb);
REFCOUNT_DEC(bb);
}
}
closedir(dir);
return dirlist;
}
bool_t
FSFolder_file_exists(FSFolder *self, const ByteBuf *filename)
{
struct stat sb;
ByteBuf *path = full_path(self, filename);
bool_t retval = false;
if (stat(path->ptr, &sb) != -1)
retval = true;
REFCOUNT_DEC(path);
return retval;
}
void
FSFolder_rename_file(FSFolder *self, const ByteBuf* from, const ByteBuf *to)
{
ByteBuf *from_path = full_path(self, from);
ByteBuf *to_path = full_path(self, to);
if (rename(from_path->ptr, to_path->ptr) ) {
CONFESS("rename from '%s' to '%s' failed: %s", from_path->ptr,
to_path->ptr, strerror(errno));
}
REFCOUNT_DEC(from_path);
REFCOUNT_DEC(to_path);
}
void
FSFolder_delete_file(FSFolder *self, const ByteBuf *filename)
{
ByteBuf *path = full_path(self, filename);
if ( remove(path->ptr) ) {
REFCOUNT_DEC(path);
CONFESS("Couldn't remove file '%s': %s", filename->ptr,
strerror(errno));
}
else {
REFCOUNT_DEC(path);
}
}
ByteBuf*
FSFolder_slurp_file(FSFolder *self, const ByteBuf *filename)
{
ByteBuf *path = full_path(self, filename);
FILE *f = fopen(path->ptr, "rb");
ByteBuf *retval;
size_t len;
int amount_read;
if (f == NULL) {
REFCOUNT_DEC(path);
CONFESS("Couldn't open file '%s': %s", filename->ptr, strerror(errno));
}
/* find length of file, allocate space */
fseeko64(f, 0, SEEK_END);
len = ftello64(f);
fseeko64(f, 0, SEEK_SET);
retval = BB_new(len);
/* read and verify */
amount_read = fread(retval->ptr, sizeof(char), len, f);
if (amount_read < 0 || (size_t)amount_read != len) {
REFCOUNT_DEC(path);
REFCOUNT_DEC(retval);
CONFESS("Expected %d bytes reading %s, got %d", (int)len,
filename->ptr, amount_read);
}
retval->len = len;
/* clean up */
if (fclose(f)) {
REFCOUNT_DEC(path);
REFCOUNT_DEC(retval);
CONFESS("Couldn't fclose file '%s': %s", filename->ptr,
strerror(errno));
}
REFCOUNT_DEC(path);
return retval;
}
void
FSFolder_close_f(FSFolder *self)
{
UNUSED_VAR(self);
}
static ByteBuf*
full_path(FSFolder *self, const ByteBuf *filename)
{
ByteBuf *path = BB_new(200);
BB_Cat_BB(path, self->path);
BB_Cat_Str(path, DIR_SEP, sizeof(DIR_SEP) - 1);
BB_Cat_BB(path, filename);
return path;
}
/* Copyright 2006-2007 Marvin Humphrey
*
* This program is free software; you can redistribute it and/or modify
* under the same terms as Perl itself.
*/