URI:
       tChange assert to dump contents of buffer before abort - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit b8557a969d0da663fe3175d19269cb2f37eb9e72
   DIR parent 7501cea8774e0b58705da53357947723f213cbf5
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Thu,  9 Dec 2021 23:13:50 +0100
       
       Change assert to dump contents of buffer before abort
       
       Diffstat:
         M Makefile                            |       2 ++
         A assert.c                            |      31 +++++++++++++++++++++++++++++++
         A assert.h                            |       8 ++++++++
         M buffer.c                            |      54 ++++++++++++++++++++++---------
         M buffer.h                            |      18 ++++++++++++++++--
         M cache.c                             |       6 +++---
         M cleanup.h                           |       1 +
         M keys_basic.c                        |       1 -
         M keys_command.c                      |       2 +-
         M ledit.c                             |      43 +++++++++++++++++++++++++++++--
         M memory.c                            |      14 +++++++-------
         M undo.c                              |       4 ++--
         M view.c                              |      30 +++++++++++++++---------------
         M window.c                            |       8 ++++----
       
       14 files changed, 170 insertions(+), 52 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -10,6 +10,7 @@ BIN = ${NAME}
        MAN1 = ${BIN:=.1}
        
        OBJ = \
       +        assert.o \
                buffer.o \
                view.o \
                cache.o \
       t@@ -27,6 +28,7 @@ OBJ = \
                pango-compat.o
        
        HDR = \
       +        assert.h \
                buffer.h \
                view.h \
                cache.h \
   DIR diff --git a/assert.c b/assert.c
       t@@ -0,0 +1,31 @@
       +/* FIXME: sort out the stupid includes */
       +#include <stdio.h>
       +#include <stdlib.h>
       +
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <X11/Xatom.h>
       +#include <pango/pangoxft.h>
       +#include <X11/extensions/Xdbe.h>
       +
       +#include "pango-compat.h"
       +#include "memory.h"
       +#include "common.h"
       +#include "txtbuf.h"
       +#include "undo.h"
       +#include "cache.h"
       +#include "theme.h"
       +#include "window.h"
       +#include "buffer.h"
       +#include "cleanup.h"
       +
       +void
       +ledit_assert_impl(const char *file, int line, const char *func, const char *failedexpr)
       +{
       +        (void)fprintf(stderr,
       +            "assertion \"%s\" failed: file \"%s\", line %d, function \"%s\"\n",
       +            failedexpr, file, line, func);
       +        ledit_emergencydump();
       +        abort();
       +        /* NOTREACHED */
       +}
   DIR diff --git a/assert.h b/assert.h
       t@@ -0,0 +1,8 @@
       +#ifndef _LEDIT_ASSERT_H_
       +#define _LEDIT_ASSERT_H_
       +
       +/* based on the assert found in OpenBSD */
       +void ledit_assert_impl(const char *file, int line, const char *func, const char *failedexpr);
       +#define ledit_assert(e) ((e) ? (void)0 : ledit_assert_impl(__FILE__, __LINE__, __func__, #e))
       +
       +#endif
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -6,9 +6,9 @@
        #include <stdio.h>
        #include <errno.h>
        #include <string.h>
       -#include <assert.h>
        #include <limits.h>
        #include <stdlib.h>
       +#include <unistd.h>
        
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       t@@ -25,6 +25,7 @@
        #include "theme.h"
        #include "window.h"
        #include "buffer.h"
       +#include "assert.h"
        
        /*
         * Important notes:
       t@@ -388,13 +389,9 @@ errorclose:
                return 1;
        }
        
       -/* FIXME: allow to write only certain lines */
        int
       -buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr) {
       -        FILE *file;
       +buffer_write_to_file(ledit_buffer *buffer, FILE *file, char **errstr) {
                ledit_line *ll;
       -        file = fopen(filename, "w");
       -        if (!file) goto error;
                clearerr(file);
                for (size_t i = 0; i < buffer->lines_num; i++) {
                        ll = buffer_get_line(buffer, i);
       t@@ -414,6 +411,32 @@ errorclose:
                return 1;
        }
        
       +int
       +buffer_write_to_fd(ledit_buffer *buffer, int fd, char **errstr) {
       +        FILE *file = fdopen(fd, "w");
       +        if (!file) goto error;
       +        return buffer_write_to_file(buffer, file, errstr);
       +error:
       +        if (*errstr)
       +                *errstr = strerror(errno);
       +        /* catching errors on the close wouldn't
       +           really make much sense anymore */
       +        close(fd);
       +        return 1;
       +}
       +
       +/* FIXME: allow to write only certain lines */
       +int
       +buffer_write_to_filename(ledit_buffer *buffer, char *filename, char **errstr) {
       +        FILE *file = fopen(filename, "w");
       +        if (!file) goto error;
       +        return buffer_write_to_file(buffer, file, errstr);
       +error:
       +        if (*errstr)
       +                *errstr = strerror(errno);
       +        return 1;
       +}
       +
        void
        buffer_destroy(ledit_buffer *buffer) {
                ledit_line *l;
       t@@ -439,8 +462,9 @@ buffer_normalize_line(ledit_line *line) {
                        );
                        line->gap = line->len;
                }
       -        /* FIXME: check if enough space, just to be sure */
       -        assert(line->len < line->cap);
       +        /* this should never happen because the functions always
       +           make sure to allocate one more for the '\0' */
       +        ledit_assert(line->len < line->cap);
                line->text[line->len] = '\0';
        }
        
       t@@ -455,7 +479,7 @@ buffer_insert_text_from_line_base(
            size_t dst_line, size_t dst_index,
            size_t src_line, size_t src_index, size_t src_len,
            txtbuf *text_ret) {
       -        assert(dst_line != src_line);
       +        ledit_assert(dst_line != src_line);
                ledit_line *ll = buffer_get_line(buffer, src_line);
                if (text_ret != NULL) {
                        txtbuf_grow(text_ret, src_len);
       t@@ -684,9 +708,9 @@ buffer_append_line_base(ledit_buffer *buffer, size_t line_index, size_t text_ind
        static void
        buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t index2) {
                ledit_line *l;
       -        assert (index2 >= index1);
       +        ledit_assert(index2 >= index1);
                /* it isn't allowed to delete all lines */
       -        assert(index2 - index1 != buffer->lines_num);
       +        ledit_assert(index2 - index1 != buffer->lines_num);
                for (size_t i = index1; i <= index2; i++) {
                        l = buffer_get_line(buffer, i);
                        free(l->text);
       t@@ -700,7 +724,7 @@ buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t inde
        
        ledit_line *
        buffer_get_line(ledit_buffer *buffer, size_t index) {
       -        assert(index < buffer->lines_num);
       +        ledit_assert(index < buffer->lines_num);
                return index < buffer->lines_gap ?
                       &buffer->lines[index] :
                       &buffer->lines[index + buffer->lines_cap - buffer->lines_num];
       t@@ -729,7 +753,7 @@ buffer_recalc_all_lines(ledit_buffer *buffer) {
        
        size_t
        buffer_textlen(ledit_buffer *buffer, size_t line1, size_t byte1, size_t line2, size_t byte2) {
       -        assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
       +        ledit_assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
                size_t len = 0;
                ledit_line *ll = buffer_get_line(buffer, line1);
                if (line1 == line2) {
       t@@ -753,7 +777,7 @@ buffer_textlen(ledit_buffer *buffer, size_t line1, size_t byte1, size_t line2, s
              backend is added, it would be good to optimize this, though. */
        void
        buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2) {
       -        assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
       +        ledit_assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
                ledit_line *ll1 = buffer_get_line(buffer, line1);
                ledit_line *ll2 = buffer_get_line(buffer, line2);
                buffer_normalize_line(ll1);
       t@@ -787,7 +811,7 @@ buffer_copy_text_to_txtbuf(
            txtbuf *buf,
            size_t line1, size_t byte1,
            size_t line2, size_t byte2) {
       -        assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
       +        ledit_assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
                size_t len = buffer_textlen(buffer, line1, byte1, line2, byte2);
                txtbuf_grow(buf, len + 1);
                buffer_copy_text(buffer, buf->text, line1, byte1, line2, byte2);
   DIR diff --git a/buffer.h b/buffer.h
       t@@ -95,12 +95,26 @@ void buffer_recalc_all_views_from_line(ledit_buffer *buffer, size_t line);
        int buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errstr);
        
        /*
       - * Write the buffer to a file.
       + * Write the buffer to a FILE pointer.
         * Returns 0 on success and 1 on error. In case of an error, *errstr is filled
         * with an error message which must be copied as soon as possible because it may
         * be overwritten by subsequent function calls.
       + * In all cases, 'file' is closed.
         */
       -int buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr);
       +int buffer_write_to_file(ledit_buffer *buffer, FILE *file, char **errstr);
       +
       +/*
       + * Write the buffer to a filename.
       + * Behaves the same as 'buffer_write_to_file'.
       + */
       +int buffer_write_to_filename(ledit_buffer *buffer, char *filename, char **errstr);
       +
       +/*
       + * Write the buffer to a file descriptor.
       + * Behaves the same as 'buffer_write_to_file', but if the
       + * file descriptor cannot be fdopen'd, it is closed before returning.
       + */
       +int buffer_write_to_fd(ledit_buffer *buffer, int fd, char **errstr);
        
        /*
         * Destroy a buffer.
   DIR diff --git a/cache.c b/cache.c
       t@@ -1,5 +1,4 @@
        #include <stdio.h>
       -#include <assert.h>
        #include <limits.h>
        #include <stdlib.h>
        #include <X11/Xlib.h>
       t@@ -10,6 +9,7 @@
        #include "common.h"
        #include "memory.h"
        #include "cache.h"
       +#include "assert.h"
        
        ledit_cache *
        cache_create(Display *dpy) {
       t@@ -84,13 +84,13 @@ cache_destroy(ledit_cache *cache) {
        
        cache_pixmap *
        cache_get_pixmap(ledit_cache *cache, size_t index) {
       -        assert(index < cache->num_pixmaps);
       +        ledit_assert(index < cache->num_pixmaps);
                return &cache->pixmaps[index];
        }
        
        cache_layout *
        cache_get_layout(ledit_cache *cache, size_t index) {
       -        assert(index < cache->num_layouts);
       +        ledit_assert(index < cache->num_layouts);
                return &cache->layouts[index];
        }
        
   DIR diff --git a/cleanup.h b/cleanup.h
       t@@ -1,3 +1,4 @@
        /* This is here so it can be called from other places
           even though the function definition is in ledit.c */
        void ledit_cleanup(void);
       +void ledit_emergencydump(void);
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -11,7 +11,6 @@
           them reliably yet */
        #include <stdio.h>
        #include <stdlib.h>
       -#include <assert.h>
        
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
   DIR diff --git a/keys_command.c b/keys_command.c
       t@@ -118,7 +118,7 @@ handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
                /* FIXME: Implement properly; handle error */
                char *errstr;
                if (view->buffer->filename)
       -                buffer_write_to_file(view->buffer, view->buffer->filename, &errstr);
       +                buffer_write_to_filename(view->buffer, view->buffer->filename, &errstr);
                return 0;
        }
        
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -1,4 +1,4 @@
       -/* FIXME: Add special assert that dumps currently edited file to backup on error */
       +/* FIXME: clean up asserts a bit; clean up includes */
        /* FIXME: On large files, expose event takes a long time for some reason
           -> but somehow only sometimes... */
        /* FIXME: generally optimize redrawing */
       t@@ -18,7 +18,6 @@
        #include <time.h>
        #include <stdio.h>
        #include <errno.h>
       -#include <assert.h>
        #include <string.h>
        #include <stdlib.h>
        #include <limits.h>
       t@@ -255,6 +254,46 @@ setup(int argc, char *argv[]) {
        }
        
        void
       +ledit_emergencydump(void) {
       +        if (!buffer)
       +                return;
       +        /* FIXME: maybe write assertion message to file? */
       +        char *orig = buffer->filename ? buffer->filename : "ledit";
       +        char *suffix = "-emergency-dump-XXXXXXXXXX";
       +        size_t len1, len2;
       +        len1 = strlen(orig);
       +        len2 = strlen(suffix);
       +        /* This doesn't use ledit_strcat so a memory allocation
       +           failure doesn't interfere with the abort in the assertion
       +           that calls this function. */
       +        char *template = malloc(len1 + len2 + 1);
       +        if (!template)
       +                return;
       +        strcpy(template, orig);
       +        strcpy(template + len1, suffix);
       +        int fd = mkstemp(template);
       +        if (fd == -1) {
       +                fprintf(
       +                    stderr,
       +                    "Unable to open file for emergency dump: %s\n",
       +                    strerror(errno)
       +                );
       +        }
       +        char *errstr;
       +        if (buffer_write_to_fd(buffer, fd, &errstr)) {
       +                fprintf(
       +                    stderr,
       +                    "Unable to perform emergency dump: %s\n",
       +                    errstr
       +                );
       +                /* FIXME: maybe just leave the file in case at
       +                   least part of it was written? */
       +                unlink(template);
       +        }
       +        free(template);
       +}
       +
       +void
        ledit_cleanup(void) {
                /* FIXME: check for other things to clean up */
                search_cleanup();
   DIR diff --git a/memory.c b/memory.c
       t@@ -2,9 +2,9 @@
        #include <stdint.h>
        #include <stdlib.h>
        #include <string.h>
       -#include <assert.h>
        
        #include "cleanup.h"
       +#include "assert.h"
        
        static void
        fatal_err(const char *msg) {
       t@@ -65,7 +65,7 @@ ledit_realloc(void *ptr, size_t size) {
           the actual strcat. Aborts program on error */
        char *
        ledit_strcat(const char *str1, const char *str2) {
       -        int len1, len2;
       +        size_t len1, len2;
                char *ret;
        
                len1 = strlen(str1);
       t@@ -103,9 +103,9 @@ move_gap(
            void *array, size_t elem_size, size_t index,
            size_t gap, size_t cap, size_t len,
            size_t *new_gap_ret) {
       -        assert(array != NULL);
       -        assert(index <= len);
       -        assert(len <= cap);
       +        ledit_assert(array != NULL);
       +        ledit_assert(index <= len);
       +        ledit_assert(len <= cap);
                char *carray = (char *)array; /* cast to char * for pointer arithmetic */
                /* since the array has size cap * elem_size, it is assumed that no overflow happens */
                if (index > gap) {
       t@@ -139,8 +139,8 @@ resize_and_move_gap(
            size_t old_gap, size_t old_cap, size_t len,
            size_t min_size, size_t index,
            size_t *new_gap_ret, size_t *new_cap_ret) {
       -        assert(index <= len);
       -        assert(len <= old_cap);
       +        ledit_assert(index <= len);
       +        ledit_assert(len <= old_cap);
                size_t gap_size = old_cap - len;
                size_t new_cap = old_cap;
                /* FIXME: read up on what the best values are here */
   DIR diff --git a/undo.c b/undo.c
       t@@ -1,5 +1,4 @@
        #include <string.h>
       -#include <assert.h>
        #include <stdlib.h>
        #include <stdint.h>
        
       t@@ -12,6 +11,7 @@
        #include "txtbuf.h"
        #include "cache.h"
        #include "undo.h"
       +#include "assert.h"
        
        /* FIXME: more sanity checks in case text is
           inserted/deleted without adding to undo stack */
       t@@ -66,7 +66,7 @@ undo_stack_destroy(undo_stack *undo) {
        /* FIXME: resize text buffers when they aren't needed anymore */
        static undo_elem *
        push_undo_elem(undo_stack *undo) {
       -        assert(undo->cur >= -1);
       +        ledit_assert(undo->cur >= -1);
                undo->cur++;
                undo->len = undo->cur + 1;
                if (undo->len > undo->cap) {
   DIR diff --git a/view.c b/view.c
       t@@ -4,7 +4,6 @@
        #include <stdio.h>
        #include <errno.h>
        #include <string.h>
       -#include <assert.h>
        #include <limits.h>
        #include <stdlib.h>
        
       t@@ -23,6 +22,7 @@
        #include "theme.h"
        #include "window.h"
        #include "buffer.h"
       +#include "assert.h"
        
        /* Basic attributes set for all text. */
        static PangoAttrList *basic_attrs = NULL;
       t@@ -162,7 +162,7 @@ view_unlock(ledit_view *view) {
        
        ledit_view_line *
        view_get_line(ledit_view *view, size_t index) {
       -        assert(index < view->lines_num);
       +        ledit_assert(index < view->lines_num);
                return index < view->lines_gap ?
                       &view->lines[index] :
                       &view->lines[index + view->lines_cap - view->lines_num];
       t@@ -386,7 +386,7 @@ void
        render_line(ledit_view *view, size_t line_index) {
                /* FIXME: check for <= 0 on size */
                ledit_view_line *ll = view_get_line(view, line_index);
       -        assert(!ll->h_dirty); /* FIXME */
       +        ledit_assert(!ll->h_dirty); /* FIXME */
                PangoLayout *layout = get_pango_layout(view, line_index);
                if (!ll->cache_pixmap_valid) {
                        cache_assign_pixmap_index(
       t@@ -884,9 +884,9 @@ void
        view_get_pos_softline_bounds(
            ledit_view *view, size_t line, size_t pos,
            size_t *start_byte_ret, size_t *end_byte_ret) {
       -        assert(line < view->lines_num);
       +        ledit_assert(line < view->lines_num);
                ledit_line *ll = buffer_get_line(view->buffer, line);
       -        assert(pos <= ll->len);
       +        ledit_assert(pos <= ll->len);
                PangoLayout *layout = get_pango_layout(view, line);
                int x, sli;
                if (pos > INT_MAX)
       t@@ -901,10 +901,10 @@ void
        view_get_softline_bounds(
            ledit_view *view, size_t line, int softline,
            size_t *start_byte_ret, size_t *end_byte_ret) {
       -        assert(line < view->lines_num);
       +        ledit_assert(line < view->lines_num);
                ledit_view_line *vl = view_get_line(view, line);
                PangoLayout *layout = get_pango_layout(view, line);
       -        assert(softline < vl->softlines);
       +        ledit_assert(softline < vl->softlines);
                PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, softline);
                *start_byte_ret = (size_t)pl->start_index;
                *end_byte_ret = (size_t)(pl->start_index + pl->length);
       t@@ -912,7 +912,7 @@ view_get_softline_bounds(
        
        int
        view_get_softline_count(ledit_view *view, size_t line) {
       -        assert(line < view->lines_num);
       +        ledit_assert(line < view->lines_num);
                ledit_view_line *vl = view_get_line(view, line);
                if (vl->text_dirty)
                        set_pango_text_and_highlight(view, line);
       t@@ -921,9 +921,9 @@ view_get_softline_count(ledit_view *view, size_t line) {
        
        int
        view_pos_to_softline(ledit_view *view, size_t line, size_t pos) {
       -        assert(line < view->lines_num);
       +        ledit_assert(line < view->lines_num);
                ledit_line *ll = buffer_get_line(view->buffer, line);
       -        assert(pos <= ll->len);
       +        ledit_assert(pos <= ll->len);
                PangoLayout *layout = get_pango_layout(view, line);
                int x, sli;
                if (pos > INT_MAX)
       t@@ -934,9 +934,9 @@ view_pos_to_softline(ledit_view *view, size_t line, size_t pos) {
        
        void
        view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int *x_ret, int *y_ret, int *h_ret) {
       -        assert(line < view->lines_num);
       +        ledit_assert(line < view->lines_num);
                ledit_line *ll = buffer_get_line(view->buffer, line);
       -        assert(pos <= ll->len);
       +        ledit_assert(pos <= ll->len);
                PangoLayout *layout = get_pango_layout(view, line);
                PangoRectangle strong, weak;
                if (pos > INT_MAX)
       t@@ -1196,8 +1196,8 @@ view_delete_range_base(
                size_t cur_byte = byte_index1;
                view_sort_selection(&line_index1, &byte_index1, &line_index2, &byte_index2);
                size_t new_line = 0, new_byte = 0;
       -        assert(line_index1 < view->lines_num);
       -        assert(line_index2 < view->lines_num);
       +        ledit_assert(line_index1 < view->lines_num);
       +        ledit_assert(line_index2 < view->lines_num);
                ledit_range cur_range = {0, 0, 0, 0};
                /* FIXME: could this be simplified by just calculating the range and then using
                   the non-line-based version? */
       t@@ -1295,7 +1295,7 @@ view_delete_range_base(
                                            rgl1, rgb1, rgl2, rgb2, text_ret
                                        );
                                } else {
       -                                assert(pl2->start_index + pl2->length >= pl1->start_index);
       +                                ledit_assert(pl2->start_index + pl2->length >= pl1->start_index);
                                        rgl1 = rgl2 = line_index1;
                                        rgb1 = (size_t)pl1->start_index;
                                        rgb2 = (size_t)(pl2->start_index + pl2->length);
   DIR diff --git a/window.c b/window.c
       t@@ -2,7 +2,6 @@
        #include <time.h>
        #include <math.h>
        #include <stdio.h>
       -#include <assert.h>
        #include <stdlib.h>
        
        #include <X11/Xlib.h>
       t@@ -21,6 +20,7 @@
        #include "util.h"
        #include "macros.h"
        #include "config.h"
       +#include "assert.h"
        
        /* FIXME: Everything to do with the bottom bar is extremely hacky */
        struct bottom_bar {
       t@@ -103,8 +103,8 @@ recalc_text_size(ledit_window *window) {
        /* FIXME: allow lines longer than window width to be displayed properly */
        void
        window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
       -        assert(len >= -1);
       -        assert(window->bb->line_cur_pos <= window->bb->line_len);
       +        ledit_assert(len >= -1);
       +        ledit_assert(window->bb->line_cur_pos <= window->bb->line_len);
        
                if (len == -1)
                        len = strlen(text);
       t@@ -136,7 +136,7 @@ window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
        
        void
        window_move_bottom_bar_cursor(ledit_window *window, int movement) {
       -        assert(window->bb->line_cur_pos <= window->bb->line_len);
       +        ledit_assert(window->bb->line_cur_pos <= window->bb->line_len);
                int trailing = 0;
                int new_index = window->bb->line_cur_pos;
                pango_layout_move_cursor_visually(