From 4e5bfb865aae6654a480b4d8e0db694f6993e081 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Fri, 19 May 2017 21:30:06 -0400 Subject: [PATCH] reworked logic to report errors when writing the file instead of just dieing --- TODO.md | 2 -- inc/edit.h | 39 +++++++++++++++++++++---------------- inc/win.h | 4 ++-- lib/buf.c | 39 ++++++++++++++++++++----------------- lib/utils.c | 51 +++++++++++++++++++++++++------------------------ lib/view.c | 6 +++--- lib/win.c | 12 ++++++------ lib/x11.c | 2 +- term.c | 6 +++++- tests/lib/buf.c | 13 ++++++++----- tests/xedit.c | 10 +++++----- xedit.c | 10 +++++++--- xpick.c | 8 ++++++-- 13 files changed, 114 insertions(+), 88 deletions(-) diff --git a/TODO.md b/TODO.md index 492a17f..563aadd 100644 --- a/TODO.md +++ b/TODO.md @@ -14,8 +14,6 @@ Up Next: Straight-up Bugs: -* fix crash on saving read-only file -* fix crash on save to file that can't be created * tab inserts dont coalesce like one would expect The Future: diff --git a/inc/edit.h b/inc/edit.h index 55a4196..fd5a2c5 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -1,5 +1,6 @@ /* Utility Functions *****************************************************************************/ +/* Memory-mapped file representation */ typedef struct { uint8_t* buf; /* memory mapped byte buffer */ size_t len; /* length of the buffer */ @@ -15,6 +16,7 @@ char* chomp(char* in); /* Buffer management functions *****************************************************************************/ +/* undo/redo list item */ typedef struct Log { struct Log* next; /* pointer to next operation in the stack */ bool insert; /* whether this operation was an insert or delete */ @@ -32,30 +34,33 @@ typedef struct Log { } data; } Log; +/* gap buffer main data structure */ typedef struct buf { - char* path; /* the path to the open file */ - int charset; /* the character set of the buffer */ - int crlf; /* tracks whether the file uses dos style line endings */ - size_t bufsize; /* size of the buffer in runes */ - Rune* bufstart; /* start of the data buffer */ - Rune* bufend; /* end of the data buffer */ - Rune* gapstart; /* start of the gap */ - Rune* gapend; /* end of the gap */ - Log* undo; /* undo list */ - Log* redo; /* redo list */ - bool modified; /* tracks whether the buffer has been modified */ - bool expand_tabs; /* tracks current mode */ - bool copy_indent; /* copy the indent level from the previous line on new lines */ - uint transid; /* tracks the last used transaction id for log entries */ + char* path; /* the path to the open file */ + int charset; /* the character set of the buffer */ + int crlf; /* tracks whether the file uses dos style line endings */ + size_t bufsize; /* size of the buffer in runes */ + Rune* bufstart; /* start of the data buffer */ + Rune* bufend; /* end of the data buffer */ + Rune* gapstart; /* start of the gap */ + Rune* gapend; /* end of the gap */ + Log* undo; /* undo list */ + Log* redo; /* redo list */ + bool modified; /* tracks whether the buffer has been modified */ + bool expand_tabs; /* tracks current mode */ + bool copy_indent; /* copy the indent level from the previous line on new lines */ + uint transid; /* tracks the last used transaction id for log entries */ + void (*errfn)(char*); /* callback for error messages */ } Buf; +/* cursor/selection representation */ typedef struct { size_t beg; size_t end; size_t col; } Sel; -void buf_init(Buf* buf); +void buf_init(Buf* buf, void (*errfn)(char*)); unsigned buf_load(Buf* buf, char* path); void buf_save(Buf* buf); Rune buf_get(Buf* buf, unsigned pos); @@ -91,6 +96,8 @@ void buf_lastins(Buf* buf, size_t* beg, size_t* end); enum { BINARY = 0, /* binary encoded file */ UTF_8, /* UTF-8 encoded file */ + + /* these arent used but are reserved for later */ UTF_16BE, /* UTF-16 encoding, big-endian */ UTF_16LE, /* UTF-16 encoding, little-endian */ UTF_32BE, /* UTF-32 encoding, big-endian */ @@ -134,7 +141,7 @@ enum { DOWN = +1 }; -void view_init(View* view, char* file); +void view_init(View* view, char* file, void (*errfn)(char*)); size_t view_limitrows(View* view, size_t maxrows, size_t ncols); void view_resize(View* view, size_t nrows, size_t ncols); void view_update(View* view, size_t* csrx, size_t* csry); diff --git a/inc/win.h b/inc/win.h index a3f8d06..a29a5b4 100644 --- a/inc/win.h +++ b/inc/win.h @@ -32,8 +32,8 @@ typedef struct { MouseFunc right; } MouseConfig; -void win_window(char* name); -void win_dialog(char* name); +void win_window(char* name, void (*errfn)(char*)); +void win_dialog(char* name, void (*errfn)(char*)); void win_loop(void); void win_settext(WinRegion id, char* text); void win_setruler(size_t ruler); diff --git a/lib/buf.c b/lib/buf.c index 00655f1..9f6e877 100644 --- a/lib/buf.c +++ b/lib/buf.c @@ -170,7 +170,7 @@ static void swaplog(Buf* buf, Log** from, Log** to, Sel* sel) { /*****************************************************************************/ -void buf_init(Buf* buf) { +void buf_init(Buf* buf, void (*errfn)(char*)) { /* cleanup old data if there is any */ if (buf->bufstart) { free(buf->bufstart); @@ -191,6 +191,7 @@ void buf_init(Buf* buf) { buf->gapend = buf->bufend; buf->undo = NULL; buf->redo = NULL; + buf->errfn = errfn; assert(buf->bufstart); } @@ -214,9 +215,6 @@ unsigned buf_load(Buf* buf, char* path) { FMap file = mmap_readonly(buf->path); filetype(buf, file); - if (buf->charset > UTF_8) - die("Unsupported character set"); - /* read the file contents into the buffer */ buf_resize(buf, next_size(file.len)); for (size_t i = 0; i < file.len;) { @@ -249,22 +247,29 @@ void buf_save(Buf* buf) { buf_insert(buf, false, buf_end(buf), '\n'); size_t wrlen = 0; - if (!buf->path) return; - FMap file = mmap_readwrite(buf->path, buf_end(buf) * UTF_MAX); - for (unsigned i = 0, end = buf_end(buf); i < end; i++) { - Rune r = buf_get(buf, i); - if (r == RUNE_CRLF) { - file.buf[wrlen++] = '\r'; - file.buf[wrlen++] = '\n'; - } else if (buf->charset == BINARY) { - file.buf[wrlen++] = (char)r; + if (buf->path) { + FMap file = mmap_readwrite(buf->path, buf_end(buf) * UTF_MAX); + if (file.buf) { + for (unsigned i = 0, end = buf_end(buf); i < end; i++) { + Rune r = buf_get(buf, i); + if (r == RUNE_CRLF) { + file.buf[wrlen++] = '\r'; + file.buf[wrlen++] = '\n'; + } else if (buf->charset == BINARY) { + file.buf[wrlen++] = (char)r; + } else { + wrlen += utf8encode((char*)&(file.buf[wrlen]), r); + } + } + mmap_close(file); + truncate(buf->path, wrlen); + buf->modified = false; } else { - wrlen += utf8encode((char*)&(file.buf[wrlen]), r); + buf->errfn("Failed to open file for writing"); } + } else { + buf->errfn("Need a filename: SaveAs "); } - mmap_close(file); - truncate(buf->path, wrlen); - buf->modified = false; } /*****************************************************************************/ diff --git a/lib/utils.c b/lib/utils.c index 35fb8a8..6f029f8 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -18,24 +18,6 @@ static size_t pagealign(size_t sz) { return sz; } -uint64_t getmillis(void) { - struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - uint64_t ms = ((uint64_t)time.tv_sec * (uint64_t)1000); - ms += ((uint64_t)time.tv_nsec / (uint64_t)1000000); - return ms; -} - -void die(const char* msgfmt, ...) { - va_list args; - va_start(args, msgfmt); - fprintf(stderr, "Error: "); - vfprintf(stderr, msgfmt, args); - fprintf(stderr, "\n"); - va_end(args); - exit(EXIT_FAILURE); -} - FMap mmap_readonly(char* path) { FMap file = { .buf = NULL, .len = 0 }; int fd; @@ -56,13 +38,14 @@ FMap mmap_readonly(char* path) { FMap mmap_readwrite(char* path, size_t sz) { FMap file = { .buf = NULL, .len = 0 }; int fd = open(path, O_CREAT|O_RDWR, 0644); - if (fd < 0) die("could not open/create file"); - ftruncate(fd, sz); - file.buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - file.len = sz; - if (file.buf == MAP_FAILED) - die("memory mapping of file failed"); - close(fd); + if (fd >= 0) { + ftruncate(fd, sz); + void* buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) { + file.buf = buf; + file.len = sz; + } + close(fd); + } return file; } @@ -71,6 +54,24 @@ void mmap_close(FMap file) { munmap(file.buf, file.len); } +uint64_t getmillis(void) { + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + uint64_t ms = ((uint64_t)time.tv_sec * (uint64_t)1000); + ms += ((uint64_t)time.tv_nsec / (uint64_t)1000000); + return ms; +} + +void die(const char* msgfmt, ...) { + va_list args; + va_start(args, msgfmt); + fprintf(stderr, "Error: "); + vfprintf(stderr, msgfmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(EXIT_FAILURE); +} + char* stringdup(const char* s) { char* ns = (char*)malloc(strlen(s) + 1); assert(ns); diff --git a/lib/view.c b/lib/view.c index 4e06d56..adfe927 100644 --- a/lib/view.c +++ b/lib/view.c @@ -175,13 +175,13 @@ static size_t getoffset(View* view, size_t row, size_t col) { return pos; } -void view_init(View* view, char* file) { +void view_init(View* view, char* file, void (*errfn)(char*)) { if (view->nrows) { for (size_t i = 0; i < view->nrows; i++) free(view->rows[i]); free(view->rows); } - buf_init(&(view->buffer)); + buf_init(&(view->buffer), errfn); view->selection = (Sel){ 0 }; if (file) { view->selection.end = buf_load(&(view->buffer), file); @@ -438,7 +438,7 @@ void view_setln(View* view, size_t line) { static bool selvisible(View* view) { if (!view->nrows) return true; unsigned beg = view->rows[0]->off; - unsigned end = view->rows[view->nrows-1]->off + + unsigned end = view->rows[view->nrows-1]->off + view->rows[view->nrows-1]->rlen; return (view->selection.beg >= beg && view->selection.end <= end); } diff --git a/lib/win.c b/lib/win.c index 00ded98..baeebdf 100644 --- a/lib/win.c +++ b/lib/win.c @@ -45,20 +45,20 @@ static Region Regions[NREGIONS] = {0}; static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0}; KeyBinding* Keys = NULL; -static void win_init(void) { +static void win_init(void (*errfn)(char*)) { for (int i = 0; i < SCROLL; i++) - view_init(&(Regions[i].view), NULL); + view_init(&(Regions[i].view), NULL, errfn); x11_init(&Config); Font = x11_font_load(FONTNAME); } -void win_window(char* name) { - win_init(); +void win_window(char* name, void (*errfn)(char*)) { + win_init(errfn); x11_window(name, Width, Height); } -void win_dialog(char* name) { - win_init(); +void win_dialog(char* name, void (*errfn)(char*)) { + win_init(errfn); x11_dialog(name, Width, Height); } diff --git a/lib/x11.c b/lib/x11.c index aefb585..dbef0b8 100644 --- a/lib/x11.c +++ b/lib/x11.c @@ -370,7 +370,7 @@ XFont x11_font_load(char* name) { die("Could not init fontconfig.\n"); FcPattern* pattern = FcNameParse((FcChar8 *)name); if (!pattern) - die("st: can't open font %s\n", name); + die("can't open font %s\n", name); /* load the base font */ FcResult result; diff --git a/term.c b/term.c index eba9e62..6ea26a9 100644 --- a/term.c +++ b/term.c @@ -31,9 +31,13 @@ void onshutdown(void) { x11_deinit(); } +void onerror(char* msg) { + +} + #ifndef TEST int main(int argc, char** argv) { - win_window("term"); + win_window("term", onerror); //win_setkeys(&Bindings); //win_setmouse(&MouseHandlers); win_loop(); diff --git a/tests/lib/buf.c b/tests/lib/buf.c index b5f665a..80eb600 100644 --- a/tests/lib/buf.c +++ b/tests/lib/buf.c @@ -5,9 +5,12 @@ static Buf TestBuf; +static void onerror(char* msg) { +} + static void set_buffer_text(char* str) { int i = 0; - buf_init(&TestBuf); + buf_init(&TestBuf, onerror); TestBuf.crlf = 1; for (Rune* curr = TestBuf.bufstart; curr < TestBuf.bufend; curr++) *curr = '-'; @@ -35,27 +38,27 @@ TEST_SUITE(BufferTests) { /* Insertions *************************************************************************/ TEST(buf_insert should insert at 0 in empty buf) { - buf_init(&TestBuf); + buf_init(&TestBuf, onerror); buf_insert(&TestBuf, false, 0, 'a'); CHECK(buf_text_eq("a")); } TEST(buf_insert should insert at 0) { - buf_init(&TestBuf); + buf_init(&TestBuf, onerror); buf_insert(&TestBuf, false, 0, 'b'); buf_insert(&TestBuf, false, 0, 'a'); CHECK(buf_text_eq("ab")); } TEST(buf_insert should insert at 1) { - buf_init(&TestBuf); + buf_init(&TestBuf, onerror); buf_insert(&TestBuf, false, 0, 'a'); buf_insert(&TestBuf, false, 1, 'b'); CHECK(buf_text_eq("ab")); } TEST(buf_insert should insert at 1) { - buf_init(&TestBuf); + buf_init(&TestBuf, onerror); buf_insert(&TestBuf, false, 0, 'a'); buf_insert(&TestBuf, false, 1, 'c'); buf_insert(&TestBuf, false, 1, 'b'); diff --git a/tests/xedit.c b/tests/xedit.c index 0dac7ed..e917d9f 100644 --- a/tests/xedit.c +++ b/tests/xedit.c @@ -23,7 +23,7 @@ Display* XDisplay; static void initialize(void) { ShellCmd[0] = "/bin/sh"; - win_window("edit"); + win_window("edit", onerror); XDisplay = XOpenDisplay(NULL); win_setkeys(Bindings); //win_setmouse(&MouseHandlers); @@ -796,19 +796,19 @@ TEST_SUITE(UnitTests) { TEST(Save should save changes to disk with crlf line endings) { setup_view(TAGS, "", CRLF, 0); - view_init(win_view(EDIT), "testdocs/crlf.txt"); + view_init(win_view(EDIT), "testdocs/crlf.txt", onerror); CHECK(verify_text(EDIT, "this file\r\nuses\r\ndos\r\nline\r\nendings\r\n")); exec("Save"); - view_init(win_view(EDIT), "testdocs/crlf.txt"); + view_init(win_view(EDIT), "testdocs/crlf.txt", onerror); CHECK(verify_text(EDIT, "this file\r\nuses\r\ndos\r\nline\r\nendings\r\n")); } TEST(Save should save changes to disk with lf line endings) { setup_view(TAGS, "", CRLF, 0); - view_init(win_view(EDIT), "testdocs/lf.txt"); + view_init(win_view(EDIT), "testdocs/lf.txt", onerror); CHECK(verify_text(EDIT, "this file\nuses\nunix\nline\nendings\n")); exec("Save"); - view_init(win_view(EDIT), "testdocs/lf.txt"); + view_init(win_view(EDIT), "testdocs/lf.txt", onerror); CHECK(verify_text(EDIT, "this file\nuses\nunix\nline\nendings\n")); } diff --git a/xedit.c b/xedit.c index fb39564..e8c6a55 100644 --- a/xedit.c +++ b/xedit.c @@ -106,6 +106,10 @@ static void exec(char* cmd) { /* Action Callbacks ******************************************************************************/ +static void onerror(char* msg) { + view_append(win_view(TAGS), msg); +} + static void quit(void) { static uint64_t before = 0; uint64_t now = getmillis(); @@ -236,7 +240,7 @@ static void pick_symbol(char* symbol) { win_setregion(EDIT); } else { if (!buf->path && !buf->modified) { - view_init(win_view(EDIT), pick); + view_init(win_view(EDIT), pick, onerror); } else { OpenCmd[1] = chomp(pick); cmdrun(OpenCmd, NULL); @@ -452,11 +456,11 @@ int main(int argc, char** argv) { ShellCmd[0] = getenv("SHELL"); if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh"; /* Create the window and enter the event loop */ - win_window("edit"); + win_window("edit", onerror); char* tags = getenv("EDITTAGS"); win_settext(TAGS, (tags ? tags : DEFAULT_TAGS)); win_setruler(80); - view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL)); + view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL), onerror); win_setkeys(Bindings); win_loop(); return 0; diff --git a/xpick.c b/xpick.c index a19f5fd..1454e8e 100644 --- a/xpick.c +++ b/xpick.c @@ -133,7 +133,7 @@ void onupdate(void) { View* view = win_view(EDIT); view->selection = (Sel){0,0,0}; Sel selection = (Sel){0,0,0}; - + score(); unsigned off = (ChoiceIdx >= view->nrows ? ChoiceIdx-view->nrows+1 : 0); for (int i = 0; (i < vec_size(&Choices)) && (i < view->nrows); i++) { @@ -169,6 +169,10 @@ void onshutdown(void) { /* Main Routine *****************************************************************************/ +static void onerror(char* msg) { + +} + static void accept(void) { x11_deinit(); } @@ -216,7 +220,7 @@ int main(int argc, char** argv) { char* title = getenv("XPICKTITLE"); load_choices(); if (vec_size(&Choices) > 1) { - win_dialog("xpick"); + win_dialog("xpick", onerror); win_setkeys(Bindings); win_settext(STATUS, (title ? title : "xpick")); if (argc >= 2) { -- 2.52.0