another update from CVS HEAD, for QA
[alioth/jupp.git] / kbd.c
1 /*
2  *      Key-map handler
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10
11 __RCSID("$MirOS: contrib/code/jupp/kbd.c,v 1.12 2017/12/08 03:24:15 tg Exp $");
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "kbd.h"
17 #include "macro.h"
18 #include "termcap.h"
19 #include "utils.h"
20 #include "vs.h"
21
22 /* Create a KBD */
23
24 KBD *mkkbd(KMAP *kmap)
25 {
26         KBD *kbd = malloc(sizeof(KBD));
27
28         kbd->topmap = kmap;
29         kbd->curmap = kmap;
30         kbd->x = 0;
31         return kbd;
32 }
33
34 /* Eliminate a KBD */
35
36 void rmkbd(KBD *k)
37 {
38         free(k);
39 }
40
41 /* Process next key for KBD */
42
43 void *dokey(KBD *kbd, int n)
44 {
45         void *bind = NULL;
46
47         /* If we were passed a negative character */
48         if (n < 0)
49                 n += 256;
50
51         /* kmap->keys[KEYS]; */
52         if ((size_t)n >= (size_t)(KEYS))
53                 return (NULL);
54
55         /* If we're starting from scratch, clear the keymap sequence buffer */
56         if (kbd->curmap == kbd->topmap)
57                 kbd->x = 0;
58
59         if (kbd->curmap->keys[n].k == 1) {      /* A prefix key was found */
60                 kbd->seq[kbd->x++] = n;
61                 kbd->curmap = kbd->curmap->keys[n].value.submap;
62         } else {                /* A complete key sequence was entered or an unbound key was found */
63                 bind = kbd->curmap->keys[n].value.bind;
64 /*  kbd->seq[kbd->x++]=n; */
65                 kbd->x = 0;
66                 kbd->curmap = kbd->topmap;
67         }
68         return bind;
69 }
70
71 /* Return key code for key name or -1 for syntax error */
72
73 static int keyval(unsigned char *s)
74 {
75         if (s[0] == '^' && s[1] && !s[2])
76                 if (s[1] == '?')
77                         return 127;
78                 else
79                         return s[1] & 0x1F;
80         else if (((s[0] | 0x20) == 's') &&
81             ((s[1] | 0x20) == 'p') && !s[2])
82                 return ' ';
83         else if (s[1] || !s[0])
84                 return -1;
85         else
86                 return (unsigned char) s[0];
87 }
88
89 /* Create an empty keymap */
90
91 KMAP *mkkmap(void)
92 {
93         KMAP *kmap = calloc(1, sizeof(KMAP));
94
95         return kmap;
96 }
97
98 /* Eliminate a keymap */
99
100 void rmkmap(KMAP *kmap)
101 {
102         int x;
103
104         if (!kmap)
105                 return;
106         for (x = 0; x != KEYS; ++x)
107                 if (kmap->keys[x].k == 1)
108                         rmkmap(kmap->keys[x].value.submap);
109         free(kmap);
110 }
111
112 /* Parse a range */
113
114 static unsigned char *range(unsigned char *seq, int *vv, int *ww)
115 {
116         unsigned char c;
117         int x, v, w;
118
119         for (x = 0; seq[x] && seq[x] != ' '; ++x) ;     /* Skip to a space */
120         c = seq[x];
121         seq[x] = 0;             /* Zero terminate the string */
122         v = keyval(seq);        /* Get key */
123         w = v;
124         if (w < 0)
125                 return NULL;
126         seq[x] = c;             /* Restore the space or 0 */
127         for (seq += x; *seq == ' '; ++seq) ;    /* Skip over spaces */
128
129         /* Check for 'TO ' */
130         if ((seq[0] | 0x20) == 't' && (seq[1] | 0x20) == 'o' && seq[2] == ' ') {
131                 for (seq += 2; *seq == ' '; ++seq) ;    /* Skip over spaces */
132                 for (x = 0; seq[x] && seq[x] != ' '; ++x) ;     /* Skip to space */
133                 c = seq[x];
134                 seq[x] = 0;     /* Zero terminate the string */
135                 w = keyval(seq);        /* Get key */
136                 if (w < 0)
137                         return NULL;
138                 seq[x] = c;     /* Restore the space or 0 */
139                 for (seq += x; *seq == ' '; ++seq) ;    /* Skip over spaces */
140         }
141
142         if (v > w)
143                 return NULL;
144
145         *vv = v;
146         *ww = w;
147         return seq;
148 }
149
150 /* Add a binding to a keymap */
151
152 static KMAP *
153 kbuild(CAP *cap, KMAP *kmap, unsigned char *seq, void *bind, int *err,
154     const unsigned char *capseq, int seql)
155 {
156         int v, w;
157
158         if (!seql && seq[0] == '.' && seq[1]) {
159                 int x, c;
160                 const unsigned char *s;
161
162                 for (x = 0; seq[x] && seq[x] != ' '; ++x) ;
163                 c = seq[x];
164                 seq[x] = 0;
165                 s = jgetstr(cap, seq + 1);
166                 seq[x] = c;
167                 if (s && (s = tcompile(cap, s, 0, 0, 0, 0))
168                     && (sLEN(s) > 1 || (signed char)s[0] < 0)) {
169                         capseq = s;
170                         seql = sLEN(s);
171                         for (seq += x; *seq == ' '; ++seq) ;
172                 } else {
173                         *err = -2;
174                         return kmap;
175                 }
176         }
177
178         if (seql) {
179                 v = w = (unsigned char) *capseq++;
180                 --seql;
181         } else {
182                 seq = range(seq, &v, &w);
183                 if (!seq) {
184                         *err = -1;
185                         return kmap;
186                 }
187         }
188
189         if (!kmap)
190                 kmap = mkkmap();        /* Create new keymap if 'kmap' was NULL */
191
192         /* Make bindings between v and w */
193         while (v <= w) {
194                 if (*seq || seql) {
195                         if (kmap->keys[v].k == 0)
196                                 kmap->keys[v].value.submap = NULL;
197                         kmap->keys[v].k = 1;
198                         kmap->keys[v].value.submap = kbuild(cap, kmap->keys[v].value.bind, seq, bind, err, capseq, seql);
199                 } else {
200                         if (kmap->keys[v].k == 1)
201                                 rmkmap(kmap->keys[v].value.submap);
202                         kmap->keys[v].k = 0;
203                         kmap->keys[v].value.bind =
204                             /* This bit of code sticks the key value in the macro */
205                             (v == w ? macstk(bind, v) : dupmacro(macstk(bind, v)));
206                 }
207                 ++v;
208         }
209         return kmap;
210 }
211
212 int kadd(CAP *cap, KMAP *kmap, unsigned char *seq, void *bind)
213 {
214         int err = 0;
215
216         kbuild(cap, kmap, seq, bind, &err, NULL, 0);
217         return err;
218 }
219
220 void kcpy(KMAP *dest, KMAP *src)
221 {
222         int x;
223
224         for (x = 0; x != KEYS; ++x)
225                 if (src->keys[x].k == 1) {
226                         if (dest->keys[x].k != 1) {
227                                 dest->keys[x].k = 1;
228                                 dest->keys[x].value.submap = mkkmap();
229                         }
230                         kcpy(dest->keys[x].value.submap, src->keys[x].value.submap);
231                 } else if (src->keys[x].k == 0 && src->keys[x].value.bind) {
232                         if (dest->keys[x].k == 1)
233                                 rmkmap(dest->keys[x].value.submap);
234                         dest->keys[x].value.bind = src->keys[x].value.bind;
235                         dest->keys[x].k = 0;
236                 }
237 }
238
239 /* Remove a binding from a keymap */
240
241 int kdel(KMAP *kmap, unsigned char *seq)
242 {
243         int err = 1;
244         int v, w;
245
246         seq = range(seq, &v, &w);
247         if (!seq)
248                 return -1;
249
250         /* Clear bindings between v and w */
251         while (v <= w) {
252                 if (*seq) {
253                         if (kmap->keys[v].k == 1) {
254                                 int r = kdel(kmap->keys[v].value.submap, seq);
255
256                                 if (err != -1)
257                                         err = r;
258                         }
259                 } else {
260                         if (kmap->keys[v].k == 1)
261                                 rmkmap(kmap->keys[v].value.submap);
262                         kmap->keys[v].k = 0;
263                         kmap->keys[v].value.bind = NULL;
264                         if (err != -1)
265                                 err = 0;
266                 }
267                 ++v;
268         }
269
270         return err;
271 }