]> git.mdlowis.com Git - projs/tide.git/commitdiff
reworked logic to report errors when writing the file instead of just dieing
authorMichael D. Lowis <mike@mdlowis.com>
Sat, 20 May 2017 01:30:06 +0000 (21:30 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Sat, 20 May 2017 01:30:06 +0000 (21:30 -0400)
13 files changed:
TODO.md
inc/edit.h
inc/win.h
lib/buf.c
lib/utils.c
lib/view.c
lib/win.c
lib/x11.c
term.c
tests/lib/buf.c
tests/xedit.c
xedit.c
xpick.c

diff --git a/TODO.md b/TODO.md
index 492a17f58a51bd847795e47f8f11d2ce7e8bc96d..563aadd1fd33c28b7348017969968a2302d78787 100644 (file)
--- 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:
index 55a4196b482c2503c25bfe22f8cbc7adc73357e4..fd5a2c59d47310a938b239af1eca7f71eedd9f1c 100644 (file)
@@ -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);
index a3f8d06672e4a1c8828da73d9f114f544a103008..a29a5b4fb1adc80f5bb93ed94be7de3482e96677 100644 (file)
--- 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);
index 00655f14f8624967f29295c64627aaccd2407345..9f6e877f81731bbbccfc5ee47fc58eb0612fd929 100644 (file)
--- 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;
 }
 
 /*****************************************************************************/
index 35fb8a871065a2fe28a777e46c730edd114a4527..6f029f818c57a092b4360fff07b6fce1d2936e39 100644 (file)
@@ -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);
index 4e06d561588f8210202fab344cfe4ee5d7728024..adfe9271cfdb52ccd3d0e217539b103bcacce710 100644 (file)
@@ -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);
 }
index 00ded98a5737b1c5612c1f82b808352e7bf6b513..baeebdfc44653bbb6e9b98e540e1afa70675f0d4 100644 (file)
--- 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);
 }
 
index aefb585233c5589828f0f10c56f757c56a981b4d..dbef0b8ba9bccba539c25bcad18412d391e97f21 100644 (file)
--- 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 eba9e62b287499268809c7b69da627aff6673955..6ea26a9fcbf279246f721b7e2a4a7d82f54f8f3d 100644 (file)
--- 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();
index b5f665a63f2898061bb42ac89d6f6f906d9cb8b8..80eb6000745a40d1559c40011d0dac1ad695ae3c 100644 (file)
@@ -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');
index 0dac7eded92a65829e52eec2545ded142356d6b8..e917d9f9fc07bec382d3db4ae6ff651508e0bc29 100644 (file)
@@ -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 fb395649b1df27725c99b233382915357322d4af..e8c6a55d678b17b155fe8061d90279da104e1df2 100644 (file)
--- 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 a19f5fd54bfd71f5b41b3a189691de4186cab47f..1454e8e9a255ddd91bcf0ecb5469e9771bbb6f3b 100644 (file)
--- 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) {