URI: 
       tCheck for XBufferOverflow when handling keys; misc fixes - 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 53f769b1d999f6964bc2431b316cf07a3b8166ec
   DIR parent feafabfe8d714cc8d08d0000a91a7a6e8baf8492
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Thu,  7 Jul 2022 13:14:00 +0200
       
       Check for XBufferOverflow when handling keys; misc fixes
       
       Diffstat:
         M keys.c                              |      49 ++++++++++++++++++++++++++-----
         M keys.h                              |       5 +++--
         M keys_basic.c                        |       6 +++---
         M keys_command.c                      |       8 +++++---
         M ledit.c                             |       2 +-
         M leditrc.5                           |       4 ++--
         M leditrc.example                     |       4 ++--
       
       7 files changed, 58 insertions(+), 20 deletions(-)
       ---
   DIR diff --git a/keys.c b/keys.c
       t@@ -19,16 +19,31 @@
        /* LockMask and Mod2Mask in particular are disabled (caps lock and numlock usually) */
        static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod5Mask;
        
       +#define KEY_TEXT_INITIAL_SIZE 32
       +static char *key_text = NULL;
       +static size_t key_text_alloc = 0;
       +
       +void
       +key_processing_cleanup(void) {
       +        free(key_text);
       +}
       +
        int
        match_key(unsigned int mask, unsigned int state)
        {
                return mask == XK_ANY_MOD || mask == (state & importantmod);
        }
        
       +#ifdef X_HAVE_UTF8_STRING
       +#define LOOKUP_STRING_FUNC Xutf8LookupString
       +#else
       +#define LOOKUP_STRING_FUNC XmbLookupString
       +#endif
       +
        void
        preprocess_key(
       -    ledit_window *window, XEvent *event, KeySym *sym_ret,
       -    char *buf_ret, int buf_size, int *buf_len_ret) {
       +    ledit_window *window, XKeyEvent *event, KeySym *sym_ret,
       +    char **buf_ret, int *buf_len_ret) {
                /*
                 * FIXME: I don't understand how key handling works with different keymaps.
                 * If, for instance, you run "setxkbmap pk" and then type "Ctrl+c", the
       t@@ -52,9 +67,29 @@ preprocess_key(
                 * keys don't use control in normal mode, and it just messes with all the
                 * keys that do use control because ledit does its own mapping.
                 */
       -        event->xkey.state &= ~ControlMask;
       -        /* FIXME: X_HAVE_UTF8_STRING See XmbLookupString(3) */
       -        *buf_len_ret = Xutf8LookupString(
       -            window->xic, &event->xkey, buf_ret, buf_size, sym_ret, NULL
       -        );
       +        if (key_text_alloc == 0) {
       +                key_text = ledit_malloc(KEY_TEXT_INITIAL_SIZE);
       +                key_text_alloc = KEY_TEXT_INITIAL_SIZE;
       +        }
       +        event->state &= ~ControlMask;
       +
       +        int len = 0;
       +        Status status;
       +        if (window->xic && event->type == KeyPress) {
       +                len = LOOKUP_STRING_FUNC(window->xic, event, key_text, key_text_alloc - 1, sym_ret, &status);
       +                if (status == XBufferOverflow) {
       +                        key_text_alloc = ideal_array_size(key_text_alloc, len + 1);
       +                        key_text = ledit_realloc(key_text, key_text_alloc);
       +                        len = LOOKUP_STRING_FUNC(window->xic, event, key_text, key_text_alloc - 1, sym_ret, &status);
       +                }
       +        } else {
       +                /* FIXME: anything equivalent to XBufferOverflow here? */
       +                len = XLookupString(event, key_text, key_text_alloc - 1, sym_ret, NULL);
       +                status = XLookupBoth;
       +        }
       +        *buf_len_ret = len >= (int)key_text_alloc ? (int)key_text_alloc - 1 : len;
       +        key_text[*buf_len_ret] = '\0';
       +        if (status != XLookupBoth && status != XLookupKeySym)
       +                *sym_ret = NoSymbol;
       +        *buf_ret = key_text;
        }
   DIR diff --git a/keys.h b/keys.h
       t@@ -15,9 +15,10 @@ int get_language_index(char *lang);
        int match_key(unsigned int mask, unsigned int state);
        /* preprocess key, i.e. get the KeySym and text corresponding to it */
        void preprocess_key(
       -    ledit_window *window, XEvent *event, KeySym *sym_ret,
       -    char *buf_ret, int buf_size, int *buf_len_ret
       +    ledit_window *window, XKeyEvent *event, KeySym *sym_ret,
       +    char **buf_ret, int *buf_len_ret
        );
       +void key_processing_cleanup(void);
        
        /* FIXME: documentation */
        #define GEN_CB_MAP_HELPERS(name, typename, cmp_entry)                     \
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -2622,12 +2622,12 @@ repeat_command(ledit_view *view, char *text, size_t len) {
        
        struct action
        basic_key_handler(ledit_view *view, XEvent *event, int lang_index) {
       -        char buf[64];
       -        KeySym sym;
       +        char *buf = NULL;
       +        KeySym sym = NoSymbol;
                int n;
        
                unsigned int key_state = event->xkey.state;
       -        preprocess_key(view->window, event, &sym, buf, sizeof(buf), &n);
       +        preprocess_key(view->window, &event->xkey, &sym, &buf, &n);
        
                struct repetition_stack_elem *re = push_repetition_stack();
                re->key_text = ledit_strndup(buf, (size_t)n);
   DIR diff --git a/keys_command.c b/keys_command.c
       t@@ -214,6 +214,8 @@ static int parse_range(
        );
        static int handle_cmd(ledit_view *view, char *cmd, size_t len, size_t lang_index);
        
       +/* FIXME: USE LEN EVERYWHERE INSTEAD OF RELYING ON cmd BEING NUL-TERMINATED */
       +/* FIXME: return error so write_quit knows when to quit */
        static int
        handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
                (void)l1;
       t@@ -946,13 +948,13 @@ edit_discard(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
        
        struct action
        command_key_handler(ledit_view *view, XEvent *event, int lang_index) {
       -        char buf[64];
       -        KeySym sym;
       +        char *buf = NULL;
       +        KeySym sym = NoSymbol;
                int n;
                command_key_array *cur_keys = config_get_command_keys(lang_index);
                size_t num_keys = cur_keys->num_keys;
                unsigned int key_state = event->xkey.state;
       -        preprocess_key(view->window, event, &sym, buf, sizeof(buf), &n);
       +        preprocess_key(view->window, &event->xkey, &sym, &buf, &n);
                int grabkey = 1, found = 0;
                command_key_cb_flags flags = KEY_FLAG_NONE;
                for (size_t i = 0; i < num_keys; i++) {
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -114,7 +114,6 @@ mainloop(void) {
                                        change_kbd = 1;
                                        continue;
                                }
       -                        /* FIXME: why None? */
                                if (XFilterEvent(&event, None))
                                        continue;
                                ledit_view *view = NULL;
       t@@ -450,6 +449,7 @@ ledit_cleanup(void) {
                search_cleanup();
                basic_key_cleanup();
                command_key_cleanup();
       +        key_processing_cleanup();
                if (buffer)
                        buffer_destroy(buffer);
                config_cleanup(&common);
   DIR diff --git a/leditrc.5 b/leditrc.5
       t@@ -619,12 +619,12 @@ Note that this command works with soft lines, regardless of the current mode.
        .It Ar scroll-with-cursor-down Oo normal, insert Oc Oo num Oc
        Move
        .Ar num
       -lines down, attemting to leave the cursor in its current line and character position.
       +lines down, attempting to leave the cursor in its current line and character position.
        Note that this command works with soft lines, regardless of the current mode.
        .It Ar scroll-with-cursor-up Oo normal, insert Oc Oo num Oc
        Move
        .Ar num
       -lines up, attemting to leave the cursor in its current line and character position.
       +lines up, attempting to leave the cursor in its current line and character position.
        Note that this command works with soft lines, regardless of the current mode.
        .It Ar search-next Op normal, insert
        Move to the next search result.
   DIR diff --git a/leditrc.example b/leditrc.example
       t@@ -221,7 +221,7 @@ language-mapping = {
                        map "^" "^"
                }
                command-mapping = {
       -                map "वग" "wq"
       +                map "व‌" "wq"
                        map "व" "w"
                        map "‌" "q"
                        map "ड" "v"
       t@@ -293,7 +293,7 @@ language-mapping = {
                        map "^" "^"
                }
                command-mapping = {
       -                map "وگ" "wq"
       +                map "وق" "wq"
                        map "و" "w"
                        map "ق" "q"
                        map "ط" "v"