commit b8557a969d0da663fe3175d19269cb2f37eb9e72
parent 7501cea8774e0b58705da53357947723f213cbf5
Author: lumidify <nobody@lumidify.org>
Date: Thu, 9 Dec 2021 23:13:50 +0100
Change assert to dump contents of buffer before abort
Diffstat:
14 files changed, 170 insertions(+), 52 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,6 +10,7 @@ BIN = ${NAME}
MAN1 = ${BIN:=.1}
OBJ = \
+ assert.o \
buffer.o \
view.o \
cache.o \
@@ -27,6 +28,7 @@ OBJ = \
pango-compat.o
HDR = \
+ assert.h \
buffer.h \
view.h \
cache.h \
diff --git a/assert.c b/assert.c
@@ -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 */
+}
diff --git a/assert.h b/assert.h
@@ -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
diff --git a/buffer.c b/buffer.c
@@ -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>
@@ -25,6 +25,7 @@
#include "theme.h"
#include "window.h"
#include "buffer.h"
+#include "assert.h"
/*
* Important notes:
@@ -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);
@@ -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;
@@ -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';
}
@@ -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);
@@ -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);
@@ -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];
@@ -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) {
@@ -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);
@@ -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);
diff --git a/buffer.h b/buffer.h
@@ -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.
diff --git a/cache.c b/cache.c
@@ -1,5 +1,4 @@
#include <stdio.h>
-#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <X11/Xlib.h>
@@ -10,6 +9,7 @@
#include "common.h"
#include "memory.h"
#include "cache.h"
+#include "assert.h"
ledit_cache *
cache_create(Display *dpy) {
@@ -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];
}
diff --git a/cleanup.h b/cleanup.h
@@ -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);
diff --git a/keys_basic.c b/keys_basic.c
@@ -11,7 +11,6 @@
them reliably yet */
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
diff --git a/keys_command.c b/keys_command.c
@@ -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;
}
diff --git a/ledit.c b/ledit.c
@@ -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 */
@@ -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>
@@ -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();
diff --git a/memory.c b/memory.c
@@ -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) {
@@ -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);
@@ -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) {
@@ -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 */
diff --git a/undo.c b/undo.c
@@ -1,5 +1,4 @@
#include <string.h>
-#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
@@ -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 */
@@ -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) {
diff --git a/view.c b/view.c
@@ -4,7 +4,6 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <assert.h>
#include <limits.h>
#include <stdlib.h>
@@ -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;
@@ -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];
@@ -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(
@@ -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)
@@ -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);
@@ -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);
@@ -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)
@@ -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)
@@ -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? */
@@ -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);
diff --git a/window.c b/window.c
@@ -2,7 +2,6 @@
#include <time.h>
#include <math.h>
#include <stdio.h>
-#include <assert.h>
#include <stdlib.h>
#include <X11/Xlib.h>
@@ -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 {
@@ -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);
@@ -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(