hex-editor/src/source-loaded-file.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;
}