tChange assert to dump contents of buffer before abort - ledit - Text editor (W… | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow) | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit b8557a969d0da663fe3175d19269cb2f37eb9e72 | |
parent 7501cea8774e0b58705da53357947723f213cbf5 | |
Author: lumidify <[email protected]> | |
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(-) | |
--- | |
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 \ | |
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 *fa… | |
+{ | |
+ (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 | |
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 cha… | |
+#define ledit_assert(e) ((e) ? (void)0 : ledit_assert_impl(__FILE__, __LINE__,… | |
+ | |
+#endif | |
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_… | |
static void | |
buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t in… | |
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… | |
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,… | |
- 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 … | |
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 li… | |
- 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); | |
diff --git a/buffer.h b/buffer.h | |
t@@ -95,12 +95,26 @@ void buffer_recalc_all_views_from_line(ledit_buffer *buffe… | |
int buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char *… | |
/* | |
- * 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 m… | |
* 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 **errs… | |
+ | |
+/* | |
+ * 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 | |
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]; | |
} | |
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); | |
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> | |
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_… | |
/* FIXME: Implement properly; handle error */ | |
char *errstr; | |
if (view->buffer->filename) | |
- buffer_write_to_file(view->buffer, view->buffer->filename, &er… | |
+ buffer_write_to_filename(view->buffer, view->buffer->filename,… | |
return 0; | |
} | |
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 err… | |
+/* 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(); | |
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 ove… | |
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 */ | |
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) { | |
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… | |
void | |
view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int *x_re… | |
- 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_in… | |
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 t… | |
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->… | |
+ ledit_assert(pl2->start_index + pl2->length >=… | |
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 | |
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 *… | |
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( |