we’ll need to distinguish these for sarge/etch as well
[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.5 2017/12/02 02:07:28 tg Exp $");
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "macro.h"
17 #include "termcap.h"
18 #include "utils.h"
19 #include "vs.h"
20
21 /* Create a KBD */
22
23 KBD *mkkbd(KMAP *kmap)
24 {
25         KBD *kbd = (KBD *) joe_malloc(sizeof(KBD));
26
27         kbd->topmap = kmap;
28         kbd->curmap = kmap;
29         kbd->x = 0;
30         return kbd;
31 }
32
33 /* Eliminate a KBD */
34
35 void rmkbd(KBD *k)
36 {
37         joe_free(k);
38 }
39
40 /* Process next key for KBD */
41
42 void *dokey(KBD *kbd, int n)
43 {
44         void *bind = NULL;
45
46         /* If we were passed a negative character */
47         if (n < 0)
48                 n += 256;
49
50         /* kmap->keys[KEYS]; */
51         if ((size_t)n >= (size_t)(KEYS))
52                 return (NULL);
53
54         /* If we're starting from scratch, clear the keymap sequence buffer */
55         if (kbd->curmap == kbd->topmap)
56                 kbd->x = 0;
57
58         if (kbd->curmap->keys[n].k == 1) {      /* A prefix key was found */
59                 kbd->seq[kbd->x++] = n;
60                 kbd->curmap = kbd->curmap->keys[n].value.submap;
61         } else {                /* A complete key sequence was entered or an unbound key was found */
62                 bind = kbd->curmap->keys[n].value.bind;
63 /*  kbd->seq[kbd->x++]=n; */
64                 kbd->x = 0;
65                 kbd->curmap = kbd->topmap;
66         }
67         return bind;
68 }
69
70 /* Return key code for key name or -1 for syntax error */
71
72 static int keyval(unsigned char *s)
73 {
74         if (s[0] == '^' && s[1] && !s[2])
75                 if (s[1] == '?')
76                         return 127;
77                 else
78                         return s[1] & 0x1F;
79         else if ((s[0] == 'S' || s[0] == 's')
80                  && (s[1] == 'P' || s[1] == 'p') && !s[2])
81                 return ' ';
82         else if (s[1] || !s[0])
83                 return -1;
84         else
85                 return (unsigned char) s[0];
86 }
87
88 /* Create an empty keymap */
89
90 KMAP *mkkmap(void)
91 {
92         KMAP *kmap = (KMAP *) joe_calloc(sizeof(KMAP), 1);
93
94         return kmap;
95 }
96
97 /* Eliminate a keymap */
98
99 void rmkmap(KMAP *kmap)
100 {
101         int x;
102
103         if (!kmap)
104                 return;
105         for (x = 0; x != KEYS; ++x)
106                 if (kmap->keys[x].k == 1)
107                         rmkmap(kmap->keys[x].value.submap);
108         joe_free(kmap);
109 }
110
111 /* Parse a range */
112
113 static unsigned char *range(unsigned char *seq, int *vv, int *ww)
114 {
115         unsigned char c;
116         int x, v, w;
117
118         for (x = 0; seq[x] && seq[x] != ' '; ++x) ;     /* Skip to a space */
119         c = seq[x];
120         seq[x] = 0;             /* Zero terminate the string */
121         v = keyval(seq);        /* Get key */
122         w = v;
123         if (w < 0)
124                 return NULL;
125         seq[x] = c;             /* Restore the space or 0 */
126         for (seq += x; *seq == ' '; ++seq) ;    /* Skip over spaces */
127
128         /* Check for 'TO ' */
129         if ((seq[0] == 'T' || seq[0] == 't') && (seq[1] == 'O' || seq[1] == 'o') && seq[2] == ' ') {
130                 for (seq += 2; *seq == ' '; ++seq) ;    /* Skip over spaces */
131                 for (x = 0; seq[x] && seq[x] != ' '; ++x) ;     /* Skip to space */
132                 c = seq[x];
133                 seq[x] = 0;     /* Zero terminate the string */
134                 w = keyval(seq);        /* Get key */
135                 if (w < 0)
136                         return NULL;
137                 seq[x] = c;     /* Restore the space or 0 */
138                 for (seq += x; *seq == ' '; ++seq) ;    /* Skip over spaces */
139         }
140
141         if (v > w)
142                 return NULL;
143
144         *vv = v;
145         *ww = w;
146         return seq;
147 }
148
149 /* Add a binding to a keymap */
150
151 static KMAP *kbuild(CAP *cap, KMAP *kmap, unsigned char *seq, void *bind, int *err, unsigned char *capseq, int seql)
152 {
153         int v, w;
154
155         if (!seql && seq[0] == '.' && seq[1]) {
156                 int x, c;
157                 unsigned char *s;
158
159                 for (x = 0; seq[x] && seq[x] != ' '; ++x) ;
160                 c = seq[x];
161                 seq[x] = 0;
162 #ifdef __MSDOS__
163                 if (!strcmp(seq + 1, "ku")) {
164                         capseq = "\0H";
165                         seql = 2;
166                 } else if (!strcmp(seq + 1, "kd")) {
167                         capseq = "\0P";
168                         seql = 2;
169                 } else if (!strcmp(seq + 1, "kl")) {
170                         capseq = "\0K";
171                         seql = 2;
172                 } else if (!strcmp(seq + 1, "kr")) {
173                         capseq = "\0M";
174                         seql = 2;
175                 } else if (!strcmp(seq + 1, "kI")) {
176                         capseq = "\0R";
177                         seql = 2;
178                 } else if (!strcmp(seq + 1, "kD")) {
179                         capseq = "\0S";
180                         seql = 2;
181                 } else if (!strcmp(seq + 1, "kh")) {
182                         capseq = "\0G";
183                         seql = 2;
184                 } else if (!strcmp(seq + 1, "kH")) {
185                         capseq = "\0O";
186                         seql = 2;
187                 } else if (!strcmp(seq + 1, "kP")) {
188                         capseq = "\0I";
189                         seql = 2;
190                 } else if (!strcmp(seq + 1, "kN")) {
191                         capseq = "\0Q";
192                         seql = 2;
193                 } else if (!strcmp(seq + 1, "k1")) {
194                         capseq = "\0;";
195                         seql = 2;
196                 } else if (!strcmp(seq + 1, "k2")) {
197                         capseq = "\0<";
198                         seql = 2;
199                 } else if (!strcmp(seq + 1, "k3")) {
200                         capseq = "\0=";
201                         seql = 2;
202                 } else if (!strcmp(seq + 1, "k4")) {
203                         capseq = "\0>";
204                         seql = 2;
205                 } else if (!strcmp(seq + 1, "k5")) {
206                         capseq = "\0?";
207                         seql = 2;
208                 } else if (!strcmp(seq + 1, "k6")) {
209                         capseq = "\0@";
210                         seql = 2;
211                 } else if (!strcmp(seq + 1, "k7")) {
212                         capseq = "\0A";
213                         seql = 2;
214                 } else if (!strcmp(seq + 1, "k8")) {
215                         capseq = "\0B";
216                         seql = 2;
217                 } else if (!strcmp(seq + 1, "k9")) {
218                         capseq = "\0C";
219                         seql = 2;
220                 } else if (!strcmp(seq + 1, "k0")) {
221                         capseq = "\0D";
222                         seql = 2;
223                 }
224                 seq[x] = c;
225                 if (seql) {
226                         for (seq += x; *seq == ' '; ++seq) ;
227                 }
228 #else
229                 s = jgetstr(cap, seq + 1);
230                 seq[x] = c;
231                 if (s && (s = tcompile(cap, s, 0, 0, 0, 0))
232                     && (sLEN(s) > 1 || (signed char)s[0] < 0)) {
233                         capseq = s;
234                         seql = sLEN(s);
235                         for (seq += x; *seq == ' '; ++seq) ;
236                 }
237 #endif
238                 else {
239                         *err = -2;
240                         return kmap;
241                 }
242         }
243
244         if (seql) {
245                 v = w = (unsigned char) *capseq++;
246                 --seql;
247         } else {
248                 seq = range(seq, &v, &w);
249                 if (!seq) {
250                         *err = -1;
251                         return kmap;
252                 }
253         }
254
255         if (!kmap)
256                 kmap = mkkmap();        /* Create new keymap if 'kmap' was NULL */
257
258         /* Make bindings between v and w */
259         while (v <= w) {
260                 if (*seq || seql) {
261                         if (kmap->keys[v].k == 0)
262                                 kmap->keys[v].value.submap = NULL;
263                         kmap->keys[v].k = 1;
264                         kmap->keys[v].value.submap = kbuild(cap, kmap->keys[v].value.bind, seq, bind, err, capseq, seql);
265                 } else {
266                         if (kmap->keys[v].k == 1)
267                                 rmkmap(kmap->keys[v].value.submap);
268                         kmap->keys[v].k = 0;
269                         kmap->keys[v].value.bind =
270                             /* This bit of code sticks the key value in the macro */
271                             (v == w ? macstk(bind, v) : dupmacro(macstk(bind, v)));
272                 }
273                 ++v;
274         }
275         return kmap;
276 }
277
278 int kadd(CAP *cap, KMAP *kmap, unsigned char *seq, void *bind)
279 {
280         int err = 0;
281
282         kbuild(cap, kmap, seq, bind, &err, NULL, 0);
283         return err;
284 }
285
286 void kcpy(KMAP *dest, KMAP *src)
287 {
288         int x;
289
290         for (x = 0; x != KEYS; ++x)
291                 if (src->keys[x].k == 1) {
292                         if (dest->keys[x].k != 1) {
293                                 dest->keys[x].k = 1;
294                                 dest->keys[x].value.submap = mkkmap();
295                         }
296                         kcpy(dest->keys[x].value.submap, src->keys[x].value.submap);
297                 } else if (src->keys[x].k == 0 && src->keys[x].value.bind) {
298                         if (dest->keys[x].k == 1)
299                                 rmkmap(dest->keys[x].value.submap);
300                         dest->keys[x].value.bind = src->keys[x].value.bind;
301                         dest->keys[x].k = 0;
302                 }
303 }
304
305 /* Remove a binding from a keymap */
306
307 int kdel(KMAP *kmap, unsigned char *seq)
308 {
309         int err = 1;
310         int v, w;
311
312         seq = range(seq, &v, &w);
313         if (!seq)
314                 return -1;
315
316         /* Clear bindings between v and w */
317         while (v <= w) {
318                 if (*seq) {
319                         if (kmap->keys[v].k == 1) {
320                                 int r = kdel(kmap->keys[v].value.submap, seq);
321
322                                 if (err != -1)
323                                         err = r;
324                         }
325                 } else {
326                         if (kmap->keys[v].k == 1)
327                                 rmkmap(kmap->keys[v].value.submap);
328                         kmap->keys[v].k = 0;
329                         kmap->keys[v].value.bind = NULL;
330                         if (err != -1)
331                                 err = 0;
332                 }
333                 ++v;
334         }
335
336         return err;
337 }