165 lines
3.6 KiB
C
165 lines
3.6 KiB
C
#include "source.h"
|
|
#include <gint/gint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
static void lf_free(loaded_file_t *lf)
|
|
{
|
|
if(lf) {
|
|
free(lf->path);
|
|
buffer_free(lf->buf);
|
|
}
|
|
free(lf);
|
|
}
|
|
|
|
static ssize_t lf_read(void *_cookie, void *buf, off_t offset, size_t size)
|
|
{
|
|
loaded_file_t *lf = _cookie;
|
|
if(offset < 0 || offset >= (int)lf->buf->data_size)
|
|
return -1;
|
|
if(offset + size > lf->buf->data_size)
|
|
size = lf->buf->data_size - offset;
|
|
|
|
memcpy(buf, lf->buf->mem + offset, size);
|
|
return size;
|
|
}
|
|
|
|
static bool lf_write(void *_cookie, void *data, size_t data_size, off_t offset,
|
|
size_t segment_size)
|
|
{
|
|
loaded_file_t *lf = _cookie;
|
|
if(buffer_write(lf->buf, data, data_size, offset, segment_size)) {
|
|
lf->dirty = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static size_t lf_size(void *_cookie)
|
|
{
|
|
loaded_file_t *lf = _cookie;
|
|
return lf->buf->data_size;
|
|
}
|
|
|
|
static bool lf_dirty(void *_cookie)
|
|
{
|
|
loaded_file_t *lf = _cookie;
|
|
return lf->dirty;
|
|
}
|
|
|
|
static bool lf_save_switch(loaded_file_t *lf, char const *outfile)
|
|
{
|
|
if(!lf->dirty && outfile == NULL)
|
|
return true;
|
|
|
|
/* If the new file is larger than the original, we can just overwrite it
|
|
without truncating, which is much faster with Fugue */
|
|
int flags = O_CREAT | O_WRONLY | O_TRUNC;
|
|
if(outfile == NULL && lf->buf->data_size >= lf->original_size)
|
|
flags = O_CREAT | O_WRONLY;
|
|
int fd = open(outfile ? outfile : lf->path, flags);
|
|
if(!fd) goto fail;
|
|
|
|
ssize_t rc = write(fd, lf->buf->mem, lf->buf->data_size);
|
|
if(rc < 0 || (size_t)rc != lf->buf->data_size) goto fail;
|
|
|
|
close(fd);
|
|
lf->dirty = false;
|
|
return true;
|
|
|
|
fail:
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
static bool lf_save(void *_cookie, char const *outfile)
|
|
{
|
|
return gint_world_switch(GINT_CALL(lf_save_switch, _cookie, outfile));
|
|
}
|
|
|
|
static void lf_close(void *_cookie)
|
|
{
|
|
loaded_file_t *lf = _cookie;
|
|
lf_free(lf);
|
|
}
|
|
|
|
static source_intf_t lf_intf = {
|
|
.name = "Loaded File",
|
|
.close_on_return_to_menu = false,
|
|
.read = lf_read,
|
|
.write = lf_write,
|
|
.size = lf_size,
|
|
.dirty = lf_dirty,
|
|
.save = lf_save,
|
|
.close = lf_close,
|
|
};
|
|
|
|
source_t *source_loaded_file_open(char const *path)
|
|
{
|
|
int fd = -1;
|
|
source_t *source = NULL;
|
|
loaded_file_t *lf = NULL;
|
|
|
|
lf = calloc(1, sizeof *lf);
|
|
if(!lf) goto fail;
|
|
|
|
lf->path = strdup(path);
|
|
if(!lf->path) goto fail;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if(fd < 0) goto fail;
|
|
|
|
int size = lseek(fd, 0, SEEK_END);
|
|
if(size < 0) goto fail;
|
|
|
|
lf->original_size = size;
|
|
lf->buf = buffer_create(size, 4096);
|
|
if(!lf->buf) goto fail;
|
|
|
|
lseek(fd, 0, SEEK_SET);
|
|
ssize_t rc = read(fd, lf->buf->mem, size);
|
|
lf->buf->data_size = size;
|
|
if(rc != size) goto fail;
|
|
|
|
source = source_open(&lf_intf, lf, path);
|
|
if(!source) goto fail;
|
|
source->cap = SOURCE_WRITE | SOURCE_RESIZE | SOURCE_SAVEAS;
|
|
|
|
close(fd);
|
|
return source;
|
|
|
|
fail:
|
|
free(source);
|
|
close(fd);
|
|
lf_free(lf);
|
|
return NULL;
|
|
}
|
|
|
|
source_t *source_loaded_file_new(void)
|
|
{
|
|
source_t *source = NULL;
|
|
loaded_file_t *lf = NULL;
|
|
|
|
lf = calloc(1, sizeof *lf);
|
|
if(!lf) goto fail;
|
|
|
|
lf->original_size = 0;
|
|
lf->buf = buffer_create(4096, 4096);
|
|
if(!lf->buf) goto fail;
|
|
lf->buf->data_size = 0;
|
|
|
|
source = source_open(&lf_intf, lf, "<New file>");
|
|
if(!source) goto fail;
|
|
|
|
source->no_origin = true;
|
|
source->cap = SOURCE_WRITE | SOURCE_RESIZE | SOURCE_SAVEAS;
|
|
return source;
|
|
|
|
fail:
|
|
free(source);
|
|
lf_free(lf);
|
|
return NULL;
|
|
}
|