add save as and lazy file fd safety

This commit is contained in:
Lephenixnoir 2022-06-24 00:51:16 +01:00
parent 9606c2d6b9
commit c1c8bf0fcb
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 91 additions and 56 deletions

View File

@ -148,17 +148,17 @@ bool heditor_save_front_buffer(heditor *e)
return false;
}
bool heditor_save(heditor *e)
bool heditor_save(heditor *e, char const *outfile)
{
if(!e)
return true;
if(e->front_buffer_dirty && !heditor_save_front_buffer(e))
return false;
if(!source_dirty(e->source))
if(!outfile && !source_dirty(e->source))
return true;
please_wait();
return source_save(e->source, NULL);
return source_save(e->source, outfile);
}
void heditor_set_insert_mode(heditor *e, bool insert)

View File

@ -59,7 +59,7 @@ bool heditor_move_to(heditor *e, int offset);
bool heditor_save_front_buffer(heditor *e);
/* Save the data source. */
bool heditor_save(heditor *e);
bool heditor_save(heditor *e, char const *outfile);
/* Change the insertion mode; true for insertion, false for overwrite. If the
source doesn't support insertion, this function is a no-op. */

View File

@ -4,7 +4,7 @@
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/exc.h>
#include <gint/gint.h>
#include <justui/jscene.h>
#include <justui/jlabel.h>
@ -85,7 +85,8 @@ static void update_fkeys(void)
}
}
else if(app.fkmenu == FK_EDITOR_SOURCE) {
jfkeys_set(app.fkeys, "@SAVE;@SAVE AS;;;;");
fkeys_sprintf(app.fkeys, "@SAVE;%s;;;;",
app.source->intf->cap & SOURCE_SAVEAS ? "@SAVE AS" : "");
}
else if(app.fkmenu == FK_EDITOR_OPEN) {
jfkeys_set(app.fkeys, "@FILE;@LAZY;@MEMORY;@NEW");
@ -153,6 +154,8 @@ static void update_source_info(void)
strcat(caps, " WRITE");
if(s->intf->cap & SOURCE_RESIZE)
strcat(caps, " RESIZE");
if(s->intf->cap & SOURCE_SAVEAS)
strcat(caps, " SAVEAS");
jlabel_asprintf(app.source_info,
"%s\n"
@ -184,6 +187,10 @@ static void update_stats(void)
source_t *s = app.source;
if(!s) return update_stats_label("No source.");
bool dirty = app.editor->front_buffer_dirty;
if(dirty)
update_stats_label("Warning: unsaved changes ignored!");
int const BUFFER_SIZE = 4096;
uint8_t *buf = malloc(BUFFER_SIZE);
if(!buf) return update_stats_label("Out of memory!");
@ -191,7 +198,10 @@ static void update_stats(void)
int size = source_size(s);
for(off_t offset = 0; offset < size;) {
int rc = s->intf->read(s->cookie, buf, offset, BUFFER_SIZE);
if(rc <= 0) return update_stats_label("Read error!");
if(rc <= 0) {
if(!dirty) update_stats_label("Read error!");
return;
}
for(int i = 0; i < rc; i++)
app.histogram[buf[i]]++;
@ -200,7 +210,8 @@ static void update_stats(void)
app.source_histogram->widget.update = true;
free(buf);
jlabel_asprintf(app.source_stats, "Bytes processed: %d", size);
if(!dirty)
jlabel_asprintf(app.source_stats, "Bytes processed: %d", size);
}
static void render_stats_histogram(int x, int y)
@ -236,7 +247,7 @@ void hex_view(void)
app.view = VIEW_EDITOR;
app.fkmenu = FK_EDITOR;
bool file_open_lazy = false;
enum { OPEN_LF, OPEN_LZ, SAVE_AS } browser_mode = OPEN_LF;
//
@ -252,6 +263,8 @@ void hex_view(void)
app.title = title;
app.fkeys = fkeys;
jscene_set_mainmenu(scene, false);
jwidget_set_background(title, C_BLACK);
jlabel_set_text_color(title, C_WHITE);
jwidget_set_stretch(title, 1, 0, false);
@ -330,6 +343,7 @@ void hex_view(void)
int key = 0;
while(1) {
/* TODO: Data can be discarded when going back to the main menu */
jevent e = jscene_run(scene);
if(e.type == JSCENE_PAINT) {
@ -355,22 +369,31 @@ void hex_view(void)
if(e.type == JFILESELECT_CANCELED) {
jscene_show_and_focus(scene, mem);
update_status();
app.fkmenu = FK_EDITOR;
update_fkeys();
}
if(e.type == JFILESELECT_VALIDATED) {
char const *file = jfileselect_selected_file(fileselect);
if(file) {
if(app.source)
source_free(app.source);
if(file_open_lazy)
app.source = source_lazy_file_open(file);
else
app.source = source_loaded_file_open(file);
app.fkmenu = FK_EDITOR;
app.insert = false;
source_load(app.source, 0, 2048);
heditor_set_source(mem, app.source);
heditor_set_insert_mode(mem, app.insert);
update_fkeys();
if(browser_mode == OPEN_LF || browser_mode == OPEN_LZ) {
if(app.source)
source_free(app.source);
app.source = (browser_mode == OPEN_LZ)
? source_lazy_file_open(file)
: source_loaded_file_open(file);
app.fkmenu = FK_EDITOR;
app.insert = false;
source_load(app.source, 0, 2048);
heditor_set_source(mem, app.source);
heditor_set_insert_mode(mem, app.insert);
update_fkeys();
}
else if(browser_mode == SAVE_AS) {
if(heditor_save(mem, file)) {
app.fkmenu = FK_EDITOR;
update_fkeys();
}
}
}
jscene_show_and_focus(scene, mem);
update_status();
@ -384,6 +407,18 @@ void hex_view(void)
if(e.type != JSCENE_KEY || e.key.type == KEYEV_UP) continue;
key = e.key.key;
if(key == KEY_MENU) {
if(app.source && app.source->intf->close_on_return_to_menu) {
source_free(app.source);
app.source = NULL;
app.fkmenu = FK_EDITOR;
heditor_set_source(mem, NULL);
update_fkeys();
}
gint_osmenu();
jscene_queue_event(scene, (jevent){ .type = JSCENE_PAINT });
}
if(jscene_focused_widget(scene) == goto_input)
continue;
@ -414,13 +449,18 @@ void hex_view(void)
}
else if(app.fkmenu == FK_EDITOR_SOURCE) {
if(key == KEY_F1 && app.source) {
if(heditor_save(mem)) {
if(heditor_save(mem, NULL)) {
app.fkmenu = FK_EDITOR;
update_fkeys();
}
}
else if(key == KEY_F2 && app.source) {
/* TODO: Source save as */
if(app.source->intf->cap & SOURCE_SAVEAS) {
browser_mode = SAVE_AS;
jfileselect_set_saveas(fileselect, true);
jfileselect_browse(fileselect, "/");
jscene_show_and_focus(scene, fileselect);
}
}
else if(key == KEY_EXIT) {
app.fkmenu = FK_EDITOR;
@ -430,7 +470,8 @@ void hex_view(void)
else if(app.fkmenu == FK_EDITOR_OPEN) {
if(key == KEY_F1) {
if(!has_unsaved_changes() || confirm_discard()) {
file_open_lazy = false;
browser_mode = OPEN_LF;
jfileselect_set_saveas(fileselect, false);
jfileselect_browse(fileselect, "/");
jscene_show_and_focus(scene, fileselect);
}
@ -438,7 +479,8 @@ void hex_view(void)
}
else if(key == KEY_F2) {
if(!has_unsaved_changes() || confirm_discard()) {
file_open_lazy = true;
browser_mode = OPEN_LZ;
jfileselect_set_saveas(fileselect, false);
jfileselect_browse(fileselect, "/");
jscene_show_and_focus(scene, fileselect);
}
@ -498,20 +540,6 @@ void hex_view(void)
update_fkeys();
}
}
#if 0
if(key == KEY_F3 && !input_focus) {
if(!jwidget_visible(popup_source_info)) {
update_source_info();
jwidget_set_visible(popup_source_info, true);
jscene_set_focused_widget(scene, source_info);
}
else {
jwidget_set_visible(popup_source_info, false);
jscene_show_and_focus(scene, mem);
}
}
#endif
}
jwidget_destroy(scene);
}

View File

@ -60,13 +60,13 @@ static bool lz_dirty(void *_cookie)
return false;
}
static bool lz_save(void *_cookie, char const *destination)
static bool lz_save(void *_cookie, char const *outfile)
{
/* When destination is NULL we have nothing to do since your backing is the
/* When `outfile` is NULL we have nothing to do since your backing is the
live file. When it's not then we don't support the save because it would
need to do a chunked copy, which I don't want to write yet */
(void)_cookie;
return (destination == NULL);
return (outfile == NULL);
}
static void lz_close(void *_cookie)
@ -78,6 +78,7 @@ static void lz_close(void *_cookie)
static source_intf_t lz_intf = {
.name = "Lazy File",
.cap = SOURCE_WRITE,
.close_on_return_to_menu = true,
.read = lz_read,
.write = lz_write,
.size = lz_size,

View File

@ -49,17 +49,17 @@ static bool lf_dirty(void *_cookie)
return lf->dirty;
}
static bool lf_save_switch(loaded_file_t *lf, char const *destination)
static bool lf_save_switch(loaded_file_t *lf, char const *outfile)
{
if(!lf->dirty && destination == NULL)
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(destination == NULL && lf->buf->data_size >= lf->original_size)
if(outfile == NULL && lf->buf->data_size >= lf->original_size)
flags = O_CREAT | O_WRONLY;
int fd = open(destination ? destination : lf->path, flags);
int fd = open(outfile ? outfile : lf->path, flags);
if(!fd) goto fail;
ssize_t rc = write(fd, lf->buf->mem, lf->buf->data_size);
@ -74,9 +74,9 @@ fail:
return false;
}
static bool lf_save(void *_cookie, char const *destination)
static bool lf_save(void *_cookie, char const *outfile)
{
return gint_world_switch(GINT_CALL(lf_save_switch, _cookie, destination));
return gint_world_switch(GINT_CALL(lf_save_switch, _cookie, outfile));
}
static void lf_close(void *_cookie)
@ -87,7 +87,8 @@ static void lf_close(void *_cookie)
static source_intf_t lf_intf = {
.name = "Loaded File",
.cap = SOURCE_WRITE | SOURCE_RESIZE,
.cap = SOURCE_WRITE | SOURCE_RESIZE | SOURCE_SAVEAS,
.close_on_return_to_menu = false,
.read = lf_read,
.write = lf_write,
.size = lf_size,

View File

@ -89,9 +89,9 @@ bool source_save_front_buffer(source_t *s)
s->buf_offset, s->buf_segment_size);
}
bool source_save(source_t *s, char const *destination)
bool source_save(source_t *s, char const *outfile)
{
return s && s->intf->save && s->intf->save(s->cookie, destination);
return s && s->intf->save && s->intf->save(s->cookie, outfile);
}
void source_free(source_t *s)

View File

@ -16,6 +16,8 @@
#define SOURCE_WRITE 0x01
/* The data source can by resized, adding or removing new bytes. */
#define SOURCE_RESIZE 0x02
/* The data source can save to an external file */
#define SOURCE_SAVEAS 0x04
// Interface to data sources //
@ -24,6 +26,8 @@ typedef struct {
char const *name;
/* Source capabilities */
int cap;
/* Whether the source should be closed before we return to menu */
bool close_on_return_to_menu;
/* Load a chunk from the data source into a buffer. */
ssize_t (*read)(void *_cookie, void *buf, off_t offset, size_t size);
/* Write from a buffer into a segment of the data source. If the interface
@ -35,8 +39,9 @@ typedef struct {
size_t (*size)(void *_cookie);
/* Whether the source is currently dirty (ie. requires a save). */
bool (*dirty)(void *_cookie);
/* Save the data to its original medium. */
bool (*save)(void *_cookie, char const *destination);
/* Save the data to its original medium, or, if the SOURCE_SAVEAS
capability is available and outfile is not NULL, a file. */
bool (*save)(void *_cookie, char const *outfile);
/* Close the data source. */
void (*close)(void *_cookie);
@ -85,9 +90,9 @@ void source_ensure_loaded(source_t *s, off_t offset, int n);
/* Push the front buffer into the data source. */
bool source_save_front_buffer(source_t *s);
/* Save the source. The destination is NULL for the current origin, or a
source-type-specific string to save as. */
bool source_save(source_t *s, char const *destination);
/* Save the source. The `outfile` is NULL for the current origin, or a file
path to save as (if supported). */
bool source_save(source_t *s, char const *outfile);
/* Free a source and its data. */
void source_free(source_t *s);