home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / keymaps.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  22KB  |  950 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "list.h"
  10. #include "fp.h"
  11. #include "jctype.h"
  12. #include "chars.h"
  13. #include "disp.h"
  14. #include "re.h"
  15. #include "ask.h"
  16. #include "commands.h"
  17. #include "macros.h"
  18. #include "extend.h"
  19. #include "fmt.h"
  20. #include "screen.h"        /* for Placur */
  21. #include "vars.h"
  22.  
  23. #ifdef IPROCS
  24. # include "sysprocs.h"    /* this and below ... */
  25. # include "iproc.h"    /* ... for definition of ProcNewline() */
  26. #endif
  27.  
  28. #ifdef PCNONASCII
  29. private const char *const altseq[] = {
  30. /*000*/NULL, "Alt Esc", NULL, "Ctl-@", NULL, NULL, NULL, "Ctl-^",
  31. /*010*/NULL, NULL, NULL, NULL, "Ctl-_", "Enter", "Alt-Bksp", "Left",
  32. /*020*/"Alt-Q", "Alt-W", "Alt-E", "Alt-R", "Alt-T", "Alt-Y", "Alt-U", "Alt-I",
  33. /*030*/"Alt-O", "Alt-P", "Alt-[", "Alt-]", "Alt-Enter", NULL, "Alt-A", "Alt-S",
  34. /*040*/"Alt-D", "Alt-F", "Alt-G", "Alt-H", "Alt-J", "Alt-K", "Alt-L", "Alt-;",
  35. /*050*/"Alt-'", "Alt-~", NULL, "Alt-\\", "Alt-Z", "Alt-X", "Alt-C", "Alt-V",
  36. /*060*/"Alt-B", "Alt-N", "Alt-M", "Alt-,", "Alt-.", "Alt-/", NULL, "Alt KP-",
  37. /*070*/NULL, NULL, NULL, "F1", "F2", "F3", "F4", "F5",
  38. /*100*/"F6", "F7", "F8", "F9", "F10", NULL, NULL, "Home",
  39. /*110*/"Up", "PgUp", "Alt KP-", "Left", "Shift KP5", "Right", "Alt KP+", "End",
  40. /*120*/"Down", "PgDn", "Ins", "Del", "Shift F1", "Shift F2", "Shift F3", "Shift F4",
  41. /*130*/"Shift F5", "Shift F6", "Shift F7", "Shift F8", "Shift F9", "Shift F10", "Ctl F1", "Ctl F2",
  42. /*140*/"Ctl F3", "Ctl F4", "Ctl F5", "Ctl F6", "Ctl F7", "Ctl F8", "Ctl F9", "Ctl F10",
  43. /*150*/"Alt F1", "Alt F2", "Alt F3", "Alt F4", "Alt F5", "Alt F6", "Alt F7", "Alt F8",
  44. /*160*/"Alt F9", "Alt F10", "Ctl PrtSc", "Ctl Left", "Ctl Right", "Ctl End", "Ctl PgDn", "Ctl Home",
  45. /*170*/"Alt 1", "Alt 2", "Alt 3", "Alt 4", "Alt 5", "Alt 6", "Alt 7", "Alt 8",
  46. /*200*/"Alt 9", "Alt 0", "Alt Minus", "Alt Equals", "Ctl PgUp", "F11", "F12", "Shift F11",
  47. /*210*/"Shift F12", "Ctl F11", "Ctl F12", "Alt F11", "Alt F12", "Ctl Up", "Ctl KP-", "Ctl KP5",
  48. /*220*/"Ctl KP+", "Ctl Down", "Ctl Ins", "Ctl Del", "Ctl Tab", "Ctl KP/", "Ctl KP*", "Alt Home",
  49. /*230*/"Alt Up", "Alt PgUp", NULL, "Alt Left", NULL, "Alt Right", NULL, "Alt End",
  50. /*240*/"Alt Down", "Alt PgDn", "Alt Ins", "Alt Del", "Alt KP/", "Alt Tab", "Alt KP Enter", NULL,
  51. /*250*/NULL, NULL, NULL, "Shift Home", "Shift Up", "Shift PgUp", "Shift Alt KP-", "Shift Left",
  52. /*260*/"Shift KP5", "Shift Right", "Shift Alt KP+", "Shift End", "Shift Down", "Shift PgDn", "Shift Ins", "Shift Del"
  53. };
  54. #endif    /* PCNONASCII */
  55.  
  56. int this_cmd, last_cmd;
  57.  
  58. /*
  59.  * A data_obj can be a command, a macro, a variable or a keymap. So a keymap
  60.  * can be bound to a key just like commands, macros and variables can.  Users
  61.  * don't bind keymaps to keys, though, they just bind the other things to
  62.  * keys, and this package automatically creates keymaps as they are needed to
  63.  * accomodate the specified key sequences.
  64.  *
  65.  * NOTE: Since users don't bind keymaps to keys, there is no reason for keymaps
  66.  * to have names.  Right now they do, but perhaps that can be fixed later. At
  67.  * least the names need not be made visible.
  68.  */
  69.  
  70. /*
  71.  * A keymap is either a sparse keymap or a full keymap.  The main keymap as
  72.  * well as escape and control X maps are full maps, because they are almost
  73.  * full.  All other keymaps that the system creates on behalf of users in
  74.  * order to accomodate an arbitrary key binding will be sparse maps, on the
  75.  * theory that they will be pretty small.  Sparse keymaps are sorted.
  76.  */
  77.  
  78. #define    KEYMAP_QUANTUM    8    /* sparse keymap allocation batch size */
  79. #define    SPARSE_LIMIT    (NCHARS/3 - KEYMAP_QUANTUM)    /* threshold for conversion */
  80.  
  81. struct keybinding {
  82.     ZXchar    key;
  83.     data_obj    *value;
  84. };
  85.  
  86. struct keymap {
  87.     int    Type;        /* keymap type (sparse or full) */
  88.     char    *Name;        /* keymap name */
  89.     struct keymap    *next_map;
  90.     bool    unfreeable;
  91.     bool    mark;    /* used for cycle avoidance */
  92.     union {
  93.         struct {
  94.             data_obj **map;    /* keys array indexed by key */
  95.         } full;
  96.         struct {
  97.             struct keybinding    *bindings;
  98.             short nused;    /* number of keybindings used */
  99.             short nalloc;    /* number of keybindings allocated */
  100.         } sparse;
  101.     } u;
  102. };
  103.  
  104. #define IsKeymap(/* data_obj* */ o)    ((o) != NULL && \
  105.              ((o)->Type == FULL_KEYMAP || \
  106.               (o)->Type == SPARSE_KEYMAP))
  107.  
  108. private struct keymap    *keymaps = NULL;        /* list of all keymaps */
  109.  
  110. private struct keymap *mainmap;
  111.  
  112. #ifdef IPROCS
  113. private struct keymap *procsmap;
  114. #endif
  115.  
  116. /*
  117.  * Make a new keymap.  Type is either FULL_KEYMAP or SPARSE_KEYMAP. If it's
  118.  * full, the keys is either NULL or a pointer to a valid keymap of length
  119.  * NCHARS.
  120.  */
  121.  
  122. private void
  123. km_init(km, kind, keys)
  124. struct keymap *km;
  125. int kind;
  126. data_obj **keys;
  127. {
  128.     byte_zero((UnivPtr) &km->u, sizeof(km->u));    /* zero integral fields */
  129.     if ((km->Type = kind) == FULL_KEYMAP) {
  130.         if (keys == NULL) {
  131.             data_obj    **p;
  132.  
  133.             keys = (data_obj **) emalloc(NCHARS * sizeof(data_obj *));
  134.             p = &keys[NCHARS];
  135.             do *--p = NULL; while (p != keys);
  136.         } else {
  137.             km->unfreeable = YES;
  138.         }
  139.         km->u.full.map = keys;
  140.     } else {
  141.         km->u.sparse.bindings = NULL;
  142.     }
  143. }
  144.  
  145. private struct keymap *
  146. km_new(kind, keys, name)
  147. int kind;
  148. data_obj **keys;
  149. char    *name;
  150. {
  151.     struct keymap    *km = (struct keymap *) emalloc(sizeof(struct keymap));
  152.  
  153.     km->Name = name;
  154.     km->next_map = keymaps;
  155.     keymaps = km;
  156.     km->unfreeable = NO;    /* provisional */
  157.     km_init(km, kind, keys);
  158.     return km;
  159. }
  160.  
  161. /* km_getkey: look up key in keymap
  162.  *
  163.  * If the keymap is sparse, km_getkey sets km_sparsepos to the index
  164.  * of the entry found (if any), or the index of the entry which would
  165.  * be its successor (if not found).
  166.  */
  167.  
  168. private int    km_sparsepos;
  169.  
  170. private data_obj *
  171. km_getkey(km, key)
  172. struct keymap *km;
  173. ZXchar key;
  174. {
  175.     if (km->Type == FULL_KEYMAP) {
  176.         return (km->u.full.map[key]);
  177.     } else {
  178.         struct keybinding    *b = km->u.sparse.bindings;
  179.         int    lwb = 0;    /* closed lower bound */
  180.         int upb = km->u.sparse.nused;    /* open upper bound */
  181.  
  182.         for (;;) {
  183.             int mid = (lwb + upb) >> 1;    /* fast average */
  184.  
  185.             if (mid == lwb)
  186.                 break;
  187.             if (b[mid].key <= key)
  188.                 lwb = mid;
  189.             else
  190.                 upb = mid;
  191.         }
  192.         if (lwb != upb) {
  193.             if (b[lwb].key == key) {
  194.                 /* found */
  195.                 km_sparsepos = lwb;
  196.                 return b[lwb].value;
  197.             }
  198.             if (b[lwb].key > key)
  199.                 upb = lwb;    /* make upb tight */
  200.         }
  201.         /* not found; insertion would be before upb */
  202.         km_sparsepos = upb;
  203.         return NULL;
  204.     }
  205. }
  206.  
  207. private ZXchar
  208. km_nextkey(km, key)
  209. struct keymap *km;
  210. ZXchar key;
  211. {
  212.     if (km->Type == FULL_KEYMAP) {
  213.         while (key != NCHARS && km->u.full.map[key] == NULL)
  214.             key++;
  215.     } else {
  216.         (void) km_getkey(km, key);
  217.         key = km_sparsepos == km->u.sparse.nused
  218.             ? NCHARS : km->u.sparse.bindings[km_sparsepos].key;
  219.     }
  220.     return key;
  221. }
  222.  
  223. /* Free a dataobj reference formerly bound in or as a keymap.
  224.  * At the present time, only values that represent keymaps
  225.  * can be usefully freed, and only those that are not marked as
  226.  * unfreeable.  unfreeable ones have other references (mainmap
  227.  * or procsmap), or they include a non-heap (static) allocation.
  228.  */
  229.  
  230. void
  231. DelObjRef(value)
  232. data_obj    *value;
  233. {
  234.     if (IsKeymap(value)) {
  235.         struct keymap    *km = (struct keymap *)value;
  236.  
  237.         if (!km->unfreeable) {
  238.             ZXchar    k;
  239.  
  240.             for (k = 0; (k = km_nextkey(km, k)) != NCHARS; k++)
  241.                 DelObjRef(km_getkey(km, k));
  242.  
  243.             switch (km->Type) {
  244.             case FULL_KEYMAP:
  245.                 free((UnivPtr) km->u.full.map);
  246.                 break;
  247.             case SPARSE_KEYMAP:
  248.                 free((UnivPtr) km->u.sparse.bindings);
  249.                 break;
  250.             }
  251.             {
  252.                 /* remove km from list of all keymaps */
  253.                 struct keymap    **p;
  254.  
  255.                 for (p = &keymaps; *p != km; p = &(*p)->next_map)
  256.                     ;
  257.                 *p = (*p)->next_map;
  258.             }
  259.             free((UnivPtr) km);
  260.         }
  261.     }
  262. }
  263.  
  264. private void
  265. km_setkey(km, key, d)
  266. struct keymap *km;
  267. ZXchar key;
  268. data_obj *d;
  269. {
  270.     if (km->Type == FULL_KEYMAP) {
  271.         DelObjRef(km->u.full.map[key]);
  272.         km->u.full.map[key] = d;
  273.     } else {
  274.         struct keybinding    *b = km->u.sparse.bindings;
  275.         int nused = km->u.sparse.nused;
  276.  
  277.         if (km_getkey(km, key) != NULL) {
  278.             /* overwriting a binding */
  279.             int    i = km_sparsepos;
  280.  
  281.             DelObjRef(b[i].value);    /* note: overwrites km_sparsepos */
  282.             if (d == NULL) {
  283.                 /* overwriting with NULL is actually deleting an entry */
  284.                 while (++i < nused)
  285.                     b[i-1] = b[i];
  286.                 km->u.sparse.nused -= 1;
  287.             } else {
  288.                 b[i].value = d;
  289.             }
  290.         } else if (d != NULL) {
  291.             /* inserting a new binding. */
  292.             if (nused == km->u.sparse.nalloc) {
  293.                 /* grow the keymap */
  294.                 if (nused >= SPARSE_LIMIT) {
  295.                     /* convert to full keymap (in place!) */
  296.                     km_init(km, FULL_KEYMAP, (data_obj **) NULL);
  297.                     while (nused-- != 0)
  298.                         km->u.full.map[b[nused].key] = b[nused].value;
  299.                     free((UnivPtr) b);
  300.                     km->u.full.map[key] = d;
  301.                     return;
  302.                 } else {
  303.                     km->u.sparse.bindings = b = (struct keybinding *) erealloc(
  304.                         (UnivPtr) km->u.sparse.bindings,
  305.                         (km->u.sparse.nalloc += KEYMAP_QUANTUM)
  306.                             * sizeof(struct keybinding));
  307.                 }
  308.             }
  309.             km->u.sparse.nused += 1;
  310.             for (; nused > km_sparsepos; nused--)
  311.                 b[nused] = b[nused-1];
  312.             b[km_sparsepos].key = key;
  313.             b[km_sparsepos].value = d;
  314.         }
  315.     }
  316. }
  317.  
  318. private void
  319. UnmarkMaps()
  320. {
  321.     struct keymap    *km;
  322.  
  323.     for (km = keymaps; km != NULL; km = km->next_map)
  324.         km->mark = NO;
  325. }
  326.  
  327. /* get the currently active keymaps into km_buf */
  328.  
  329. #define MAX_KEYMAPS    3    /* bound on number of keymaps found by get_keymaps */
  330.  
  331. private int
  332. get_keymaps(km_buf)
  333. struct keymap **km_buf;
  334. {
  335.     int nmaps = 0;
  336.  
  337.     /* add maps to array in order of decreasing priority (and specificity) */
  338.  
  339.     if (curbuf->b_map != NULL)
  340.         km_buf[nmaps++] = curbuf->b_map;
  341.  
  342. #ifdef IPROCS
  343.     if (curbuf->b_process != NULL)
  344.         km_buf[nmaps++] = procsmap;
  345. #endif
  346.  
  347.     km_buf[nmaps++] = mainmap;
  348.  
  349.     return nmaps;
  350. }
  351.  
  352. bool
  353. IsPrefixChar(c)
  354. ZXchar    c;
  355. {
  356.     return IsKeymap(km_getkey(mainmap, c));
  357. }
  358.  
  359. private struct keymap *
  360. GetKeymap(m, key)
  361. struct keymap *m;
  362. ZXchar key;
  363. {
  364.     data_obj *val = km_getkey(m, key);
  365.  
  366.     return IsKeymap(val)? (struct keymap *) val : (struct keymap *) NULL;
  367. }
  368.  
  369. private data_obj *
  370. findmap(fmt)
  371. const char    *fmt;
  372. {
  373.     struct keymap    *km;
  374.     char    *strings[128];
  375.     int    i;
  376.  
  377.     for (km = keymaps, i = 0; km != NULL; km = km->next_map)
  378.         if (i < (int) elemsof(strings) - 1 && km->Name != NULL)
  379.             strings[i++] = km->Name;
  380.     strings[i] = NULL;
  381.  
  382.     i = complete(strings, (char *)NULL, fmt, ALLOW_OLD | ALLOW_INDEX);
  383.  
  384.     for (km = keymaps; ; km = km->next_map) {
  385.         if (km->Name != NULL && i-- == 0) {
  386.             km->unfreeable = YES;
  387.             return (data_obj *) km;
  388.         }
  389.     }
  390. }
  391.  
  392. private void
  393. BindSequence(m, keys, k_len, obj)
  394. struct keymap *m;
  395. char *keys;
  396. int k_len;
  397. data_obj *obj;
  398. {
  399.     int i;
  400.  
  401.     if (k_len == 0)
  402.         complain("can't bind empty key sequence");
  403.     for (i = 0; i < k_len - 1; i++) {
  404.         struct keymap *submap = GetKeymap(m, ZXC(keys[i]));
  405.  
  406.         if (submap == NULL) {
  407.             submap = km_new(SPARSE_KEYMAP, (data_obj **)NULL, (char *)NULL);
  408.             km_setkey(m, ZXC(keys[i]), (data_obj *) submap);
  409.         }
  410.         m = submap;
  411.     }
  412.     km_setkey(m, ZXC(keys[i]), obj);
  413. #ifdef MAC
  414.     /* info for About Jove ... */
  415.     if (obj != NULL && obj_type(obj) == COMMAND) {
  416.         struct cmd    *cmd = (struct cmd *) obj;
  417.         char    map = 0;
  418.  
  419.         if (m->Type == FULL_KEYMAP) {
  420.             if (m->u.full.map == MainKeys)
  421.                 map = F_MAINMAP;
  422.             else if (m->u.full.map == EscKeys)
  423.                 map = F_PREF1MAP;
  424.             else if (m->u.full.map == CtlxKeys)
  425.                 map = F_PREF2MAP;
  426.         }
  427.         if (map != 0) {
  428.             cmd->c_map = map;
  429.             cmd->c_key = ZXC(keys[i]);    /* see about_j() in mac.c */
  430.         }
  431.     }
  432. #endif
  433. }
  434.  
  435. private void
  436. DoBind(findproc, map)
  437. data_obj *(*findproc) ptrproto((const char *));
  438. struct keymap *map;
  439. {
  440.     data_obj *d = (*findproc) (ProcFmt);
  441.     char keys[64];
  442.     int i;
  443.     struct keymap *m;
  444.  
  445.     s_mess(": %f %s ", d->Name);
  446.     if (obj_type(d) == COMMAND && ((struct cmd *)d)->c_proc == Unbound)
  447.         d = NULL;
  448.     i = 0;
  449.     m = map;
  450.     for (;;) {
  451.         ZXchar c = addgetc();
  452.  
  453.         if (c == EOF)
  454.             break;
  455.         if (i == sizeof(keys) - 1)
  456.             complain("key sequence too long");
  457.         keys[i++] = c;
  458.         if (!InJoverc) {
  459.             if (is_an_arg()) {
  460.                 /* Disgusting hack to allow more interactive power.
  461.                  * This ought to be replaced by a cleaner mechanism.
  462.                  */
  463.                 if (c == '\r' || c == '\n') {
  464.                     i -= 1;
  465.                     break;
  466.                 }
  467.                 if (c == '\\')
  468.                     keys[i-1] = addgetc();
  469.             } else {
  470.                 if ((m = GetKeymap(m, c)) == NULL)
  471.                     break;
  472.             }
  473.         }
  474.     }
  475.     BindSequence(map, keys, i, d);
  476. }
  477.  
  478. private void
  479. DoLBind(findproc)
  480. data_obj *(*findproc) ptrproto((const char *));
  481. {
  482.     if (curbuf->b_map == NULL)
  483.         curbuf->b_map = km_new(SPARSE_KEYMAP, (data_obj **)NULL, (char *)NULL);
  484.     DoBind(findproc, curbuf->b_map);
  485. }
  486.  
  487. /* bind a command to a key in the buffer's local keymap. */
  488. void
  489. LBindAKey()
  490. {
  491.     DoLBind(findcom);
  492. }
  493.  
  494. /* bind a macro to a key in the buffers local keymap. */
  495. void
  496. LBindMac()
  497. {
  498.     DoLBind(findmac);
  499. }
  500.  
  501. void
  502. LBindMap()
  503. {
  504.     DoLBind(findmap);
  505. }
  506.  
  507. void
  508. BindAKey()
  509. {
  510.     DoBind(findcom, mainmap);
  511. }
  512.  
  513. void
  514. BindMac()
  515. {
  516.     DoBind(findmac, mainmap);
  517. }
  518.  
  519. void
  520. BindMap()
  521. {
  522.     DoBind(findmap, mainmap);
  523. }
  524.  
  525. #ifdef IPROCS
  526.  
  527. void
  528. PBindAKey()
  529. {
  530.     DoBind(findcom, procsmap);
  531. }
  532.  
  533. void
  534. PBindMac()
  535. {
  536.     DoBind(findmac, procsmap);
  537. }
  538.  
  539. void
  540. PBindMap()
  541. {
  542.     DoBind(findmap, procsmap);
  543. }
  544.  
  545. #endif
  546.  
  547. void
  548. Unbound()
  549. {
  550.     complain("%f");
  551. }
  552.  
  553. void
  554. KeyDesc()
  555. {
  556.     struct keymap *maps[MAX_KEYMAPS];
  557.     int nmaps;
  558.  
  559.     nmaps = get_keymaps(maps);
  560.     s_mess(ProcFmt);
  561.     while (YES) {
  562.         int i;
  563.         bool    still_hope = NO;
  564.         ZXchar    key = addgetc();
  565.  
  566.         for (i = 0; i < nmaps; i++) {
  567.             if (maps[i] != NULL) {
  568.                 data_obj *cp = km_getkey(maps[i], key);
  569.  
  570.                 if (cp != NULL) {
  571.                     if (!IsKeymap(cp)) {
  572.                         add_mess("is bound to %s.", cp->Name);
  573.                         stickymsg = YES;
  574.                         return;
  575.                     }
  576.                     still_hope = YES;
  577.                 }
  578.                 maps[i] = (struct keymap *) cp;
  579.             }
  580.         }
  581.         if (!still_hope)
  582.             break;
  583.     }
  584.     add_mess("is unbound.");
  585. }
  586.  
  587. private void
  588. DescMap(map, pref)
  589. struct keymap *map;
  590. char *pref;
  591. {
  592.     if (map != NULL && !map->mark) {
  593.         ZXchar    c1, c2;
  594.  
  595.         map->mark = YES;
  596.         for (c1 = km_nextkey(map, 0); c1 < NCHARS; c1 = km_nextkey(map, c2 + 1)) {
  597.             data_obj    *c1obj = km_getkey(map, c1);
  598.             char keydescbuf[40];
  599.  
  600.             c2 = c1;
  601.             do; while (++c2 < NCHARS && c1obj == km_getkey(map, c2));
  602.             c2 -= 1;
  603.             swritef(keydescbuf, sizeof(keydescbuf),
  604.                 c1 == c2 ? "%s %p" : c1 + 1 == c2 ? "%s {%p,%p}" : "%s [%p-%p]",
  605.                 pref, c1, c2);
  606.             if (IsKeymap(c1obj)) {
  607. #ifdef PCNONASCII
  608.                 /* horrible kludge to handle PC non-ASCII keys */
  609.                 if (c1 == PCNONASCII) {
  610.                     ZXchar    pc;
  611.                     struct keymap    *pcm = (struct keymap *)c1obj;
  612.  
  613.                     c2 = c1;    /* don't handle range */
  614.                     for (pc = km_nextkey(pcm, 0); pc < NCHARS; pc = km_nextkey(pcm, pc + 1)) {
  615.                         data_obj    *pcobj = km_getkey(pcm, pc);
  616.                         const char    *as = pc < elemsof(altseq)? altseq[pc] : NULL;
  617.                         char pckeydescbuf[40];
  618.  
  619.                         if (as == NULL)
  620.                             swritef(pckeydescbuf, sizeof(pckeydescbuf),
  621.                                 "%s %p %p", pref, PCNONASCII, pc);
  622.                         else
  623.                             swritef(pckeydescbuf, sizeof(pckeydescbuf),
  624.                                 "%s %s", pref, as);
  625.                         if (IsKeymap(pcobj))
  626.                             DescMap((struct keymap *)pcobj, pckeydescbuf);
  627.                         else
  628.                             Typeout("%-18s %s", pckeydescbuf, pcobj->Name);
  629.                     }
  630.                     continue;
  631.                 }
  632. #endif /* PCNONASCII */
  633.                 DescMap((struct keymap *)c1obj, keydescbuf);
  634.             } else {
  635.                 Typeout("%-18s %s", keydescbuf, c1obj->Name);
  636.             }
  637.         }
  638.         map->mark = NO;
  639.     }
  640. }
  641.  
  642. void
  643. DescBindings()
  644. {
  645.     TOstart("Key Bindings");
  646.     UnmarkMaps();
  647.     DescMap(mainmap, NullStr);
  648.     DescMap(curbuf->b_map, "Local:");
  649. #ifdef IPROCS
  650.     DescMap(procsmap, "Proc:");
  651. #endif
  652.     TOstop();
  653. }
  654.  
  655. private char *
  656. fb_aux(cp, map, prefix, buf, room)
  657. register data_obj    *cp;
  658. struct keymap    *map;
  659. char    *prefix,
  660.     *buf;
  661. size_t    room;
  662. {
  663.     char    *bufp = buf;
  664.  
  665.     if (map != NULL && !map->mark) {
  666.         ZXchar    c1, c2;
  667.  
  668.         map->mark = YES;
  669.         for (c1 = km_nextkey(map, 0); c1 < NCHARS; c1 = km_nextkey(map, c2 + 1)) {
  670.             data_obj    *c1obj = km_getkey(map, c1);
  671.  
  672.             c2 = c1;
  673.             if (c1obj == cp) {
  674.                 do ; while (++c2 < NCHARS && c1obj == km_getkey(map, c2));
  675.                 c2 -= 1;
  676.                 swritef(bufp, room - (bufp-buf),
  677.                     c1==c2? "%s%p, " : c1+1==c2? "%s{%p,%p}, " : "%s[%p-%p], ",
  678.                     prefix, c1, c2);
  679.                 bufp += strlen(bufp);
  680.             }
  681.             if (IsKeymap(c1obj)) {
  682.                 char    prefbuf[20];
  683.  
  684. #ifdef PCNONASCII
  685.                 /* horrible kludge to handle PC non-ASCII keys */
  686.                 if (c1 == PCNONASCII) {
  687.                     struct keymap    *pcm = (struct keymap *)c1obj;
  688.                     ZXchar    pc;
  689.  
  690.                     c2 = c1;    /* don't handle range */
  691.                     for (pc = km_nextkey(pcm, 0); pc < NCHARS; pc = km_nextkey(pcm, pc + 1)) {
  692.                         const char    *as = pc < elemsof(altseq)? altseq[pc] : NULL;
  693.                         data_obj    *pcobj = km_getkey(pcm, pc);
  694.  
  695.                         if (pcobj == cp) {
  696.                             if (as == NULL)
  697.                                 swritef(bufp, room - (bufp-buf),
  698.                                     "%s%p %p, ", prefix, PCNONASCII, pc);
  699.                             else
  700.                                 swritef(bufp, room - (bufp-buf),
  701.                                     "%s%s, ", prefix, as);
  702.                             bufp += strlen(bufp);
  703.                         }
  704.                         if (IsKeymap(pcobj)) {
  705.                             if (as == NULL)
  706.                                 swritef(prefbuf, sizeof(prefbuf),
  707.                                     "%s%p %p ", prefix, PCNONASCII, pc);
  708.                             else
  709.                                 swritef(prefbuf, sizeof(prefbuf),
  710.                                     "%s%s ", prefix, as);
  711.                             bufp = fb_aux(cp, (struct keymap *) pcobj,
  712.                                 prefbuf, bufp, room-(bufp-buf));
  713.                         }
  714.                     }
  715.                     continue;
  716.                 }
  717. #endif /* PCNONASCII */
  718.                 swritef(prefbuf, sizeof(prefbuf), "%s%p ", prefix, c1);
  719.                 bufp = fb_aux(cp, (struct keymap *) c1obj, prefbuf, bufp,
  720.                     room-(bufp-buf));
  721.             }
  722.         }
  723.         map->mark = NO;
  724.     }
  725.     return bufp;
  726. }
  727.  
  728. private void
  729. find_binds(dp, buf, size)
  730. data_obj *dp;
  731. char *buf;
  732. size_t size;
  733. {
  734.     char    *endp;
  735.  
  736.     UnmarkMaps();
  737.     buf[0] = '\0';
  738.     endp = fb_aux(dp, mainmap, NullStr, buf, size);
  739.     endp = fb_aux(dp, curbuf->b_map, "Local:", endp, size - (endp - buf));
  740. #ifdef IPROCS
  741.     endp = fb_aux(dp, procsmap, "Proc:", endp, size - (endp - buf));
  742. #endif
  743.     if ((endp > buf+2) && (strcmp(endp-2, ", ") == 0))
  744.         endp[-2] = '\0';
  745. }
  746.  
  747. private void
  748. ShowDoc(doc_type, dp, show_bindings)
  749. char *doc_type;
  750. data_obj *dp;
  751. bool show_bindings;
  752. {
  753.     char pattern[100];
  754.     char    CmdDb[FILESIZE];    /* path for cmds.doc */
  755.     File *fp;
  756.  
  757.     swritef(CmdDb, sizeof(CmdDb), "%s/cmds.doc", ShareDir);
  758.     fp = open_file(CmdDb, iobuff, F_READ, YES);
  759.     Placur(ILI, 0);
  760.     flushscreen();
  761.     swritef(pattern, sizeof(pattern), "^:entry \"%s\" \"%s\"$",
  762.         dp->Name, doc_type);
  763.     TOstart("Help");
  764.     for (;;) {
  765.         if (f_gets(fp, genbuf, (size_t) LBSIZE)) {
  766.             Typeout("There is no documentation for \"%s\".", dp->Name);
  767.             break;
  768.         }
  769.         if (genbuf[0] == ':' && LookingAt(pattern, genbuf, 0)) {
  770.             /* found it ... let's print it */
  771.             static const char entrystr[] = ":entry";
  772.  
  773.             if (show_bindings) {
  774.                 char binding[128];
  775.  
  776.                 find_binds(dp, binding, sizeof(binding));
  777.                 if (blnkp(binding)) {
  778.                     Typeout("To invoke %s, type \"ESC X %s<cr>\".",
  779.                         dp->Name, dp->Name);
  780.                 } else {
  781.                     Typeout("Type \"%s\" to invoke %s.",
  782.                         binding, dp->Name);
  783.                 }
  784.             } else
  785.                 Typeout("%s", dp->Name);
  786.             Typeout(NullStr);
  787.             while (!f_gets(fp, genbuf, (size_t) LBSIZE)
  788.                     && strncmp(genbuf, entrystr, sizeof(entrystr) - 1) != 0)
  789.                 Typeout("%s", genbuf);
  790.             break;
  791.         }
  792.     }
  793.     f_close(fp);
  794.     TOstop();
  795. }
  796.  
  797. void
  798. DescCom()
  799. {
  800.     ShowDoc("Command", findcom(ProcFmt), YES);
  801. }
  802.  
  803. void
  804. DescVar()
  805. {
  806.     ShowDoc("Variable", findvar(ProcFmt), NO);
  807. }
  808.  
  809. void
  810. Apropos()
  811. {
  812.     register const struct cmd *cp;
  813.     register struct macro *m;
  814.     register const struct variable *v;
  815.     char *ans;
  816.     bool
  817.         anyfs = NO,
  818.         anyvs = NO,
  819.         anyms = NO;
  820.     char buf[MAXCOLS];
  821.  
  822.     ans = ask((char *) NULL, ": %f (keyword) ");
  823.     if (strcmp(ans, "?") == 0)
  824.         ans = NullStr;    /* matches everything */
  825.     TOstart("Help");
  826.     for (cp = commands; cp->Name != NULL && !TOabort; cp++)
  827.         if (sindex(ans, cp->Name)) {
  828.             if (!anyfs) {
  829.                 anyfs = YES;
  830.                 Typeout("Commands");
  831.                 Typeout("--------");
  832.             }
  833.             find_binds((data_obj *) cp, buf, sizeof(buf));
  834.             if (buf[0] != '\0')
  835.                 Typeout(": %-35s (%s)", cp->Name, buf);
  836.             else
  837.                 Typeout(": %s", cp->Name);
  838.         }
  839.     for (v = variables; v->Name != NULL && !TOabort; v++)
  840.         if (sindex(ans, v->Name)) {
  841.             if (!anyvs) {
  842.                 anyvs = YES;
  843.                 if (anyfs)
  844.                     Typeout(NullStr);
  845.                 Typeout("Variables");
  846.                 Typeout("---------");
  847.             }
  848.             vpr_aux(v, buf, sizeof(buf));
  849.             Typeout(": set %-26s %s", v->Name, buf);
  850.         }
  851.     for (m = macros; m != NULL && !TOabort; m = m->m_nextm)
  852.         if (sindex(ans, m->Name)) {
  853.             if (!anyms) {
  854.                 anyms = YES;
  855.                 if (anyfs || anyvs)
  856.                     Typeout(NullStr);
  857.                 Typeout("Macros");
  858.                 Typeout("------");
  859.             }
  860.             find_binds((data_obj *) m, buf, sizeof(buf));
  861.             if (buf[0])
  862.                 Typeout(": %-35s (%s)", m->Name, buf);
  863.             else
  864.                 Typeout(": %s", m->Name);
  865.         }
  866.     TOstop();
  867. }
  868.  
  869. void
  870. InitKeymaps()
  871. {
  872.     struct keymap *km;
  873.  
  874.     mainmap = km_new(FULL_KEYMAP, MainKeys, "global-map");
  875.  
  876.     /* setup ESC map */
  877.     km = km_new(FULL_KEYMAP, EscKeys, "ESC-map");
  878.     km_setkey(mainmap, ESC, (data_obj *) km);
  879.  
  880.     /* setup Ctlx map */
  881.     km = km_new(FULL_KEYMAP, CtlxKeys, "CtlX-map");
  882.     km_setkey(mainmap, CTL('X'), (data_obj *) km);
  883.  
  884. #ifdef  PCNONASCII
  885.     km = km_new(FULL_KEYMAP, NonASCIIKeys, "non-ASCII-map");
  886.     km_setkey(mainmap, PCNONASCII, (data_obj *) km);
  887. #endif
  888.  
  889. #ifdef IPROCS
  890.     procsmap = km_new(SPARSE_KEYMAP, (data_obj **) NULL, "process-map");
  891.     procsmap->unfreeable = YES;
  892.     BindSequence(procsmap, "\r", 1, (data_obj *) FindCmd(ProcNewline));
  893. #endif
  894. }
  895.  
  896. /*
  897.  * Dispatch a character.  If the specified character maps to a sub keymap,
  898.  * this routine reads more characters until (1) a command is found, or (2) an
  899.  * unbound key error occurs.  Callers of this routine can be assured that it
  900.  * won't return before completing a key sequence.
  901.  */
  902. void
  903. dispatch(c)
  904. ZXchar c;
  905. {
  906.     struct keymap *maps[MAX_KEYMAPS];
  907.     int nmaps;
  908.  
  909.     if (InMacDefine)
  910.         note_dispatch();
  911.     this_cmd = OTHER_CMD;
  912.     nmaps = get_keymaps(maps);
  913.  
  914.     while (YES) {
  915.         int    i;
  916.         bool    still_hope = NO;
  917.  
  918.         for (i = 0; i < nmaps; i++) {
  919.             if (maps[i] != NULL) {
  920.                 data_obj *cp = km_getkey(maps[i], c);
  921.  
  922.                 if (cp != NULL) {
  923.                     if (!IsKeymap(cp)) {
  924.                         ExecCmd(cp);
  925.                         return;
  926.                     }
  927.                     still_hope = YES;
  928.                 }
  929.                 maps[i] = (struct keymap *) cp;
  930.             }
  931.         }
  932.         if (!still_hope) {
  933.             char strokes[128];
  934.  
  935.             pp_key_strokes(strokes, sizeof(strokes));
  936.             s_mess("[%sunbound]", strokes);
  937.             rbell();
  938.             clr_arg_value();
  939.             stickymsg = NO;
  940.             break;
  941.         }
  942.         c = waitchar();
  943.         if (c == AbortChar) {
  944.             message("[Aborted]");
  945.             rbell();
  946.             break;
  947.         }
  948.     }
  949. }
  950.