]> git.neil.brown.name Git - edlib.git/blobdiff - core-keymap.c
TODO: clean out done items.
[edlib.git] / core-keymap.c
index cc23171b42795487c2d8fab7522446fa09fff49c..fb7101b0511c7e51b9879df5b045a6666446512a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright Neil Brown ©2015-2020 <neil@brown.name>
+ * Copyright Neil Brown ©2015-2023 <neil@brown.name>
  * May be distributed under terms of GPLv2 - see file:COPYING
  *
  * Keymaps for edlib.
  *
  */
 
+#define _GNU_SOURCE /*  for asprintf */
 #include <unistd.h>
 #include <stdlib.h>
 #include <memory.h>
+#include <stdio.h>
 
 #include "core.h"
 #include "misc.h"
@@ -54,7 +56,8 @@ inline static int qhash(char key, unsigned int start)
 
 struct map {
        unsigned long   bloom[256 / (sizeof(unsigned long)*8) ];
-       short           changed;
+       bool            changed;
+       bool            check_all;
        short           size;
        struct map      *chain;
        char            * safe *keys safe;
@@ -122,20 +125,24 @@ void key_free(struct map *m safe)
        free(m);
 }
 
-static void hash_str(const char *key safe, int len, unsigned int *hashes safe)
+static bool hash_str(const char *key safe, int len, unsigned int *hashes safe)
 {
        int i;
        int h = 0;
+       bool prefix = False;
 
        for (i = 0; (len < 0 || i < len) && key[i]; i++) {
                h = qhash(key[i], h);
-               if (key[i] == '-' || key[i] == ':')
+               if (key[i] == '-' || key[i] == ':') {
+                       prefix = True;
                        break;
+               }
        }
        hashes[1] = h;
        for (; (len < 0 || i < len) && key[i]; i++)
                h = qhash(key[i], h);
        hashes[0] = h;
+       return prefix;
 }
 
 inline static void set_bit(unsigned long *set safe, int bit)
@@ -154,10 +161,13 @@ static bool key_present(struct map *map safe, unsigned int *hashes safe)
 {
        if (map->changed) {
                int i;
+               map->check_all = False;
                for (i = 0; i < map->size; i++) {
                        unsigned int h[2];
-                       hash_str(map->keys[i], -1, h);
+                       bool prefix = hash_str(map->keys[i], -1, h);
                        if (IS_RANGE(map->comms[i])) {
+                               if (!prefix)
+                                       map->check_all = True;
                                set_bit(map->bloom, h[1]&0xff);
                                set_bit(map->bloom, (h[1]>>8)&0xff);
                                set_bit(map->bloom, (h[1]>>16)&0xff);
@@ -167,8 +177,10 @@ static bool key_present(struct map *map safe, unsigned int *hashes safe)
                                set_bit(map->bloom, (h[0]>>16)&0xff);
                        }
                }
-               map->changed = 0;
+               map->changed = False;
        }
+       if (map->check_all)
+               return True;
 
        if (test_bit(map->bloom, hashes[0]&0xff) &&
            test_bit(map->bloom, (hashes[0]>>8)&0xff) &&
@@ -210,6 +222,11 @@ void key_add(struct map *map safe, const char *k safe, struct command *comm)
 
        if (!comm)
                return;
+       if (strcmp(k, "Close") == 0 &&
+           !comm->closed_ok) {
+               LOG("WARNING: Command %s registered for \"Close\" but not marked closed_ok",
+                   comm->name);
+       }
 
        pos = key_find(map, k);
        /* cases:
@@ -261,7 +278,7 @@ void key_add(struct map *map safe, const char *k safe, struct command *comm)
                map->comms[pos+1] = SET_RANGE(command_get(GETCOMM(comm2)));
        }
        map->size += ins_cnt;
-       map->changed = 1;
+       map->changed = True;
 }
 
 void key_add_range(struct map *map safe,
@@ -312,7 +329,7 @@ void key_add_range(struct map *map safe,
        map->keys[pos+1] = strdup(last);
        map->comms[pos+1] = command_get(comm);
        map->size += move_size;
-       map->changed = 1;
+       map->changed = True;
 }
 
 void key_add_chain(struct map *map safe, struct map *chain)
@@ -322,7 +339,6 @@ void key_add_chain(struct map *map safe, struct map *chain)
        map->chain = chain;
 }
 
-
 #if 0
 void key_del(struct map *map, wint_t k)
 {
@@ -337,7 +353,7 @@ void key_del(struct map *map, wint_t k)
        memmove(map->comms+pos, map->comms+pos+1,
                (map->size-pos-1) * sizeof(struct command *));
        map->size -= 1;
-       map->changed = 1;
+       map->changed = True;
 }
 #endif
 
@@ -371,6 +387,28 @@ static struct backtrace {
        const struct cmd_info *ci safe;
        struct backtrace *prev;
 } *backtrace;
+static int backtrace_depth;
+
+static char *mark_info(struct mark *m)
+{
+       char *ret = NULL;
+
+       if (!m) {
+               asprintf(&ret, "M-");
+               return ret;
+       }
+       if (!mark_valid(m)) {
+               asprintf(&ret, "M-FREED");
+               return ret;
+       }
+       ret = pane_call_ret(str, m->owner, "doc:debug:mark",
+                           m->owner, 0, m);
+       if (ret)
+               return ret;
+
+       asprintf(&ret, "M:%d<%p>%d", m->seq, m, m->ref.i);
+       return ret;
+}
 
 void LOG_BT(void)
 {
@@ -380,29 +418,52 @@ void LOG_BT(void)
                const struct cmd_info *ci = bt->ci;
                struct command *h = ci->home->handle;
                struct command *f = ci->focus->handle;
-               LOG(" H:%s \"%s\" F:%s: %d %p \"%s\" %d %p \"%s\" (%d,%d) %s",
+               char *m1 = mark_info(ci->mark);
+               char *m2 = mark_info(ci->mark2);
+
+               LOG(" H:%s \"%s\" F:%s: %d %s \"%s\" %d %s \"%s\" (%d,%d) %s",
                    h ? h->name : "?",
                    ci->key,
                    f ? f->name : "?",
-                   ci->num, ci->mark, ci->str,
-                   ci->num2, ci->mark2, ci->str2,
-                   ci->x, ci->y, ci->comm2 ? ci->comm2->name : "");
+                   ci->num, m1, ci->str,
+                   ci->num2, m2, ci->str2,
+                   ci->x, ci->y,
+                   ci->comm2 ? ci->comm2->name : "");
+               free(m1);
+               free(m2);
        }
        LOG("End Backtrace");
 }
 
-static int do_comm_call(struct command *comm safe,
-                       const struct cmd_info *ci safe)
+int do_comm_call(struct command *comm safe, const struct cmd_info *ci safe)
 {
        struct backtrace bt;
        int ret;
 
+       if (ci->home->damaged & DAMAGED_DEAD)
+               return Efail;
+       if (times_up_fast(ci->home))
+               return Efail;
+       if ((ci->home->damaged & DAMAGED_CLOSED) &&
+           !comm->closed_ok)
+               return Efallthrough;
+
+       if (backtrace_depth > 100) {
+               backtrace_depth = 0;
+               LOG("Recursion limit of 100 reached");
+               LOG_BT();
+               backtrace_depth = 100;
+               pane_root(ci->home)->timestamp = 1;
+               return Efail;
+       }
        bt.comm = comm;
        bt.ci = ci;
        bt.prev = backtrace;
        backtrace = &bt;
+       backtrace_depth += 1;
        ret = comm->func(ci);
        backtrace = bt.prev;
+       backtrace_depth -= 1;
        return ret;
 }
 
@@ -430,25 +491,38 @@ int key_lookup(struct map *m safe, const struct cmd_info *ci safe)
        }
 }
 
-int key_lookup_prefix(struct map *m safe, const struct cmd_info *ci safe)
+int key_lookup_prefix(struct map *m safe, const struct cmd_info *ci safe,
+                     bool simple)
 {
-       int pos = key_find(m, ci->key);
-       struct command *comm, *prev = NULL;
-       int len = strlen(ci->key);
+       /* A "Simple" lookup avoids the backtrace.  It is used in
+        * signal handlers.
+        */
        const char *k = ci->key;
-       int ret = 0;
+       int len = strlen(k);
+       int pos = key_find(m, k);
+       struct command *comm, *prev = NULL;
+       int ret = Efallthrough;
+       int i;
 
-       while (ret == 0 && pos < m->size &&
-              strncmp(m->keys[pos], k, len) == 0) {
-               comm = GETCOMM(m->comms[pos]);
+       for (i = 0;
+            ret == Efallthrough && pos+i < m->size &&
+            strncmp(m->keys[pos+i], k, len) == 0;
+            i++) {
+               comm = GETCOMM(m->comms[pos+i]);
                if (comm && comm != prev) {
                        ((struct cmd_info*)ci)->comm = comm;
-                       ((struct cmd_info*)ci)->key = m->keys[pos];
-                       ret = do_comm_call(comm, ci);
-                       ASSERT(ret >= 0 || ret < Eunused);
+                       ((struct cmd_info*)ci)->key = m->keys[pos+i];
+                       if (simple)
+                               ret = comm->func(ci);
+                       else
+                               ret = do_comm_call(comm, ci);
+                       ASSERT(ret >= Efallthrough || ret < Eunused);
                        prev = comm;
+                       /* something might have been added, recalc
+                        * start pos.
+                        */
+                       pos = key_find(m, k);
                }
-               pos += 1;
        }
        ((struct cmd_info*)ci)->key = k;
        return ret;
@@ -475,6 +549,13 @@ int key_handle(const struct cmd_info *ci safe)
        struct pane *p;
        unsigned int hash[2];
 
+       if (ci->mark && !mark_valid(ci->mark))
+               return Einval;
+       if (ci->mark2 && !mark_valid(ci->mark2))
+               return Einval;
+
+       if (times_up(ci->home))
+               return Efail;
        time_start_key(ci->key);
        if ((void*) ci->comm) {
                int ret = do_comm_call(ci->comm, ci);
@@ -494,11 +575,13 @@ int key_handle(const struct cmd_info *ci safe)
 
        while (p) {
                int ret = Efallthrough;
-               if (p->handle && !(p->damaged & DAMAGED_DEAD)) {
+               if (p->handle &&
+                   (p->handle->closed_ok ||
+                    !(p->damaged & DAMAGED_CLOSED))) {
                        vci->home = p;
                        vci->comm = p->handle;
                        /* Don't add this to the call stack as it
-                        * should simple call the desired function and
+                        * should simply call the desired function and
                         * that will appear on the call stack.
                         */
                        ret = p->handle->func(ci);