1 |
|
|
/* $OpenBSD: extend.c,v 1.64 2016/09/01 21:06:09 lum Exp $ */ |
2 |
|
|
|
3 |
|
|
/* This file is in the public domain. */ |
4 |
|
|
|
5 |
|
|
/* |
6 |
|
|
* Extended (M-X) commands, rebinding, and startup file processing. |
7 |
|
|
*/ |
8 |
|
|
|
9 |
|
|
#include <sys/queue.h> |
10 |
|
|
#include <sys/types.h> |
11 |
|
|
#include <ctype.h> |
12 |
|
|
#include <limits.h> |
13 |
|
|
#include <signal.h> |
14 |
|
|
#include <stdio.h> |
15 |
|
|
#include <stdlib.h> |
16 |
|
|
#include <string.h> |
17 |
|
|
|
18 |
|
|
#include "chrdef.h" |
19 |
|
|
#include "def.h" |
20 |
|
|
#include "funmap.h" |
21 |
|
|
#include "kbd.h" |
22 |
|
|
#include "key.h" |
23 |
|
|
#include "macro.h" |
24 |
|
|
|
25 |
|
|
static int remap(KEYMAP *, int, PF, KEYMAP *); |
26 |
|
|
static KEYMAP *reallocmap(KEYMAP *); |
27 |
|
|
static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *); |
28 |
|
|
static int dobind(KEYMAP *, const char *, int); |
29 |
|
|
static char *skipwhite(char *); |
30 |
|
|
static char *parsetoken(char *); |
31 |
|
|
static int bindkey(KEYMAP **, const char *, KCHAR *, int); |
32 |
|
|
|
33 |
|
|
/* |
34 |
|
|
* Insert a string, mainly for use from macros (created by selfinsert). |
35 |
|
|
*/ |
36 |
|
|
/* ARGSUSED */ |
37 |
|
|
int |
38 |
|
|
insert(int f, int n) |
39 |
|
|
{ |
40 |
|
|
char buf[128], *bufp, *cp; |
41 |
|
|
int count, c; |
42 |
|
|
|
43 |
|
|
if (inmacro) { |
44 |
|
|
while (--n >= 0) { |
45 |
|
|
for (count = 0; count < maclcur->l_used; count++) { |
46 |
|
|
if ((((c = maclcur->l_text[count]) == '\n') |
47 |
|
|
? lnewline() : linsert(1, c)) != TRUE) |
48 |
|
|
return (FALSE); |
49 |
|
|
} |
50 |
|
|
} |
51 |
|
|
maclcur = maclcur->l_fp; |
52 |
|
|
return (TRUE); |
53 |
|
|
} |
54 |
|
|
if (n == 1) |
55 |
|
|
/* CFINS means selfinsert can tack on the end */ |
56 |
|
|
thisflag |= CFINS; |
57 |
|
|
|
58 |
|
|
if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL) |
59 |
|
|
return (ABORT); |
60 |
|
|
else if (bufp[0] == '\0') |
61 |
|
|
return (FALSE); |
62 |
|
|
while (--n >= 0) { |
63 |
|
|
cp = buf; |
64 |
|
|
while (*cp) { |
65 |
|
|
if (((*cp == '\n') ? lnewline() : linsert(1, *cp)) |
66 |
|
|
!= TRUE) |
67 |
|
|
return (FALSE); |
68 |
|
|
cp++; |
69 |
|
|
} |
70 |
|
|
} |
71 |
|
|
return (TRUE); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
/* |
75 |
|
|
* Bind a key to a function. Cases range from the trivial (replacing an |
76 |
|
|
* existing binding) to the extremely complex (creating a new prefix in a |
77 |
|
|
* map_element that already has one, so the map_element must be split, |
78 |
|
|
* but the keymap doesn't have enough room for another map_element, so |
79 |
|
|
* the keymap is reallocated). No attempt is made to reclaim space no |
80 |
|
|
* longer used, if this is a problem flags must be added to indicate |
81 |
|
|
* malloced versus static storage in both keymaps and map_elements. |
82 |
|
|
* Structure assignments would come in real handy, but K&R based compilers |
83 |
|
|
* don't have them. Care is taken so running out of memory will leave |
84 |
|
|
* the keymap in a usable state. |
85 |
|
|
* Parameters are: |
86 |
|
|
* curmap: pointer to the map being changed |
87 |
|
|
* c: character being changed |
88 |
|
|
* funct: function being changed to |
89 |
|
|
* pref_map: if funct==NULL, map to bind to or NULL for new |
90 |
|
|
*/ |
91 |
|
|
static int |
92 |
|
|
remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map) |
93 |
|
|
{ |
94 |
|
|
int i, n1, n2, nold; |
95 |
|
|
KEYMAP *mp, *newmap; |
96 |
|
|
PF *pfp; |
97 |
|
|
struct map_element *mep; |
98 |
|
|
|
99 |
|
|
if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) { |
100 |
|
|
if (ele > &curmap->map_element[0] && (funct != NULL || |
101 |
|
|
(ele - 1)->k_prefmap == NULL)) |
102 |
|
|
n1 = c - (ele - 1)->k_num; |
103 |
|
|
else |
104 |
|
|
n1 = HUGE; |
105 |
|
|
if (ele < &curmap->map_element[curmap->map_num] && |
106 |
|
|
(funct != NULL || ele->k_prefmap == NULL)) |
107 |
|
|
n2 = ele->k_base - c; |
108 |
|
|
else |
109 |
|
|
n2 = HUGE; |
110 |
|
|
if (n1 <= MAPELEDEF && n1 <= n2) { |
111 |
|
|
ele--; |
112 |
|
|
if ((pfp = calloc(c - ele->k_base + 1, |
113 |
|
|
sizeof(PF))) == NULL) { |
114 |
|
|
dobeep(); |
115 |
|
|
ewprintf("Out of memory"); |
116 |
|
|
return (FALSE); |
117 |
|
|
} |
118 |
|
|
nold = ele->k_num - ele->k_base + 1; |
119 |
|
|
for (i = 0; i < nold; i++) |
120 |
|
|
pfp[i] = ele->k_funcp[i]; |
121 |
|
|
while (--n1) |
122 |
|
|
pfp[i++] = curmap->map_default; |
123 |
|
|
pfp[i] = funct; |
124 |
|
|
ele->k_num = c; |
125 |
|
|
ele->k_funcp = pfp; |
126 |
|
|
} else if (n2 <= MAPELEDEF) { |
127 |
|
|
if ((pfp = calloc(ele->k_num - c + 1, |
128 |
|
|
sizeof(PF))) == NULL) { |
129 |
|
|
dobeep(); |
130 |
|
|
ewprintf("Out of memory"); |
131 |
|
|
return (FALSE); |
132 |
|
|
} |
133 |
|
|
nold = ele->k_num - ele->k_base + 1; |
134 |
|
|
for (i = 0; i < nold; i++) |
135 |
|
|
pfp[i + n2] = ele->k_funcp[i]; |
136 |
|
|
while (--n2) |
137 |
|
|
pfp[n2] = curmap->map_default; |
138 |
|
|
pfp[0] = funct; |
139 |
|
|
ele->k_base = c; |
140 |
|
|
ele->k_funcp = pfp; |
141 |
|
|
} else { |
142 |
|
|
if (curmap->map_num >= curmap->map_max) { |
143 |
|
|
if ((newmap = reallocmap(curmap)) == NULL) |
144 |
|
|
return (FALSE); |
145 |
|
|
curmap = newmap; |
146 |
|
|
} |
147 |
|
|
if ((pfp = malloc(sizeof(PF))) == NULL) { |
148 |
|
|
dobeep(); |
149 |
|
|
ewprintf("Out of memory"); |
150 |
|
|
return (FALSE); |
151 |
|
|
} |
152 |
|
|
pfp[0] = funct; |
153 |
|
|
for (mep = &curmap->map_element[curmap->map_num]; |
154 |
|
|
mep > ele; mep--) { |
155 |
|
|
mep->k_base = (mep - 1)->k_base; |
156 |
|
|
mep->k_num = (mep - 1)->k_num; |
157 |
|
|
mep->k_funcp = (mep - 1)->k_funcp; |
158 |
|
|
mep->k_prefmap = (mep - 1)->k_prefmap; |
159 |
|
|
} |
160 |
|
|
ele->k_base = c; |
161 |
|
|
ele->k_num = c; |
162 |
|
|
ele->k_funcp = pfp; |
163 |
|
|
ele->k_prefmap = NULL; |
164 |
|
|
curmap->map_num++; |
165 |
|
|
} |
166 |
|
|
if (funct == NULL) { |
167 |
|
|
if (pref_map != NULL) |
168 |
|
|
ele->k_prefmap = pref_map; |
169 |
|
|
else { |
170 |
|
|
if ((mp = malloc(sizeof(KEYMAP) + |
171 |
|
|
(MAPINIT - 1) * sizeof(struct map_element))) == NULL) { |
172 |
|
|
dobeep(); |
173 |
|
|
ewprintf("Out of memory"); |
174 |
|
|
ele->k_funcp[c - ele->k_base] = |
175 |
|
|
curmap->map_default; |
176 |
|
|
return (FALSE); |
177 |
|
|
} |
178 |
|
|
mp->map_num = 0; |
179 |
|
|
mp->map_max = MAPINIT; |
180 |
|
|
mp->map_default = rescan; |
181 |
|
|
ele->k_prefmap = mp; |
182 |
|
|
} |
183 |
|
|
} |
184 |
|
|
} else { |
185 |
|
|
n1 = c - ele->k_base; |
186 |
|
|
if (ele->k_funcp[n1] == funct && (funct != NULL || |
187 |
|
|
pref_map == NULL || pref_map == ele->k_prefmap)) |
188 |
|
|
/* no change */ |
189 |
|
|
return (TRUE); |
190 |
|
|
if (funct != NULL || ele->k_prefmap == NULL) { |
191 |
|
|
if (ele->k_funcp[n1] == NULL) |
192 |
|
|
ele->k_prefmap = NULL; |
193 |
|
|
/* easy case */ |
194 |
|
|
ele->k_funcp[n1] = funct; |
195 |
|
|
if (funct == NULL) { |
196 |
|
|
if (pref_map != NULL) |
197 |
|
|
ele->k_prefmap = pref_map; |
198 |
|
|
else { |
199 |
|
|
if ((mp = malloc(sizeof(KEYMAP) + |
200 |
|
|
(MAPINIT - 1) * |
201 |
|
|
sizeof(struct map_element))) == NULL) { |
202 |
|
|
dobeep(); |
203 |
|
|
ewprintf("Out of memory"); |
204 |
|
|
ele->k_funcp[c - ele->k_base] = |
205 |
|
|
curmap->map_default; |
206 |
|
|
return (FALSE); |
207 |
|
|
} |
208 |
|
|
mp->map_num = 0; |
209 |
|
|
mp->map_max = MAPINIT; |
210 |
|
|
mp->map_default = rescan; |
211 |
|
|
ele->k_prefmap = mp; |
212 |
|
|
} |
213 |
|
|
} |
214 |
|
|
} else { |
215 |
|
|
/* |
216 |
|
|
* This case is the splits. |
217 |
|
|
* Determine which side of the break c goes on |
218 |
|
|
* 0 = after break; 1 = before break |
219 |
|
|
*/ |
220 |
|
|
n2 = 1; |
221 |
|
|
for (i = 0; n2 && i < n1; i++) |
222 |
|
|
n2 &= ele->k_funcp[i] != NULL; |
223 |
|
|
if (curmap->map_num >= curmap->map_max) { |
224 |
|
|
if ((newmap = reallocmap(curmap)) == NULL) |
225 |
|
|
return (FALSE); |
226 |
|
|
curmap = newmap; |
227 |
|
|
} |
228 |
|
|
if ((pfp = calloc(ele->k_num - c + !n2, |
229 |
|
|
sizeof(PF))) == NULL) { |
230 |
|
|
dobeep(); |
231 |
|
|
ewprintf("Out of memory"); |
232 |
|
|
return (FALSE); |
233 |
|
|
} |
234 |
|
|
ele->k_funcp[n1] = NULL; |
235 |
|
|
for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++) |
236 |
|
|
pfp[i - n1 - n2] = ele->k_funcp[i]; |
237 |
|
|
for (mep = &curmap->map_element[curmap->map_num]; |
238 |
|
|
mep > ele; mep--) { |
239 |
|
|
mep->k_base = (mep - 1)->k_base; |
240 |
|
|
mep->k_num = (mep - 1)->k_num; |
241 |
|
|
mep->k_funcp = (mep - 1)->k_funcp; |
242 |
|
|
mep->k_prefmap = (mep - 1)->k_prefmap; |
243 |
|
|
} |
244 |
|
|
ele->k_num = c - !n2; |
245 |
|
|
(ele + 1)->k_base = c + n2; |
246 |
|
|
(ele + 1)->k_funcp = pfp; |
247 |
|
|
ele += !n2; |
248 |
|
|
ele->k_prefmap = NULL; |
249 |
|
|
curmap->map_num++; |
250 |
|
|
if (pref_map == NULL) { |
251 |
|
|
if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1) |
252 |
|
|
* sizeof(struct map_element))) == NULL) { |
253 |
|
|
dobeep(); |
254 |
|
|
ewprintf("Out of memory"); |
255 |
|
|
ele->k_funcp[c - ele->k_base] = |
256 |
|
|
curmap->map_default; |
257 |
|
|
return (FALSE); |
258 |
|
|
} |
259 |
|
|
mp->map_num = 0; |
260 |
|
|
mp->map_max = MAPINIT; |
261 |
|
|
mp->map_default = rescan; |
262 |
|
|
ele->k_prefmap = mp; |
263 |
|
|
} else |
264 |
|
|
ele->k_prefmap = pref_map; |
265 |
|
|
} |
266 |
|
|
} |
267 |
|
|
return (TRUE); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
/* |
271 |
|
|
* Reallocate a keymap. Returns NULL (without trashing the current map) |
272 |
|
|
* on failure. |
273 |
|
|
*/ |
274 |
|
|
static KEYMAP * |
275 |
|
|
reallocmap(KEYMAP *curmap) |
276 |
|
|
{ |
277 |
|
|
struct maps_s *mps; |
278 |
|
|
KEYMAP *mp; |
279 |
|
|
int i; |
280 |
|
|
|
281 |
|
|
if (curmap->map_max > SHRT_MAX - MAPGROW) { |
282 |
|
|
dobeep(); |
283 |
|
|
ewprintf("keymap too large"); |
284 |
|
|
return (NULL); |
285 |
|
|
} |
286 |
|
|
if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) * |
287 |
|
|
sizeof(struct map_element))) == NULL) { |
288 |
|
|
dobeep(); |
289 |
|
|
ewprintf("Out of memory"); |
290 |
|
|
return (NULL); |
291 |
|
|
} |
292 |
|
|
mp->map_num = curmap->map_num; |
293 |
|
|
mp->map_max = curmap->map_max + MAPGROW; |
294 |
|
|
mp->map_default = curmap->map_default; |
295 |
|
|
for (i = curmap->map_num; i--;) { |
296 |
|
|
mp->map_element[i].k_base = curmap->map_element[i].k_base; |
297 |
|
|
mp->map_element[i].k_num = curmap->map_element[i].k_num; |
298 |
|
|
mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp; |
299 |
|
|
mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap; |
300 |
|
|
} |
301 |
|
|
for (mps = maps; mps != NULL; mps = mps->p_next) { |
302 |
|
|
if (mps->p_map == curmap) |
303 |
|
|
mps->p_map = mp; |
304 |
|
|
else |
305 |
|
|
fixmap(curmap, mp, mps->p_map); |
306 |
|
|
} |
307 |
|
|
ele = &mp->map_element[ele - &curmap->map_element[0]]; |
308 |
|
|
return (mp); |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
/* |
312 |
|
|
* Fix references to a reallocated keymap (recursive). |
313 |
|
|
*/ |
314 |
|
|
static void |
315 |
|
|
fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt) |
316 |
|
|
{ |
317 |
|
|
int i; |
318 |
|
|
|
319 |
|
|
for (i = mt->map_num; i--;) { |
320 |
|
|
if (mt->map_element[i].k_prefmap != NULL) { |
321 |
|
|
if (mt->map_element[i].k_prefmap == curmap) |
322 |
|
|
mt->map_element[i].k_prefmap = mp; |
323 |
|
|
else |
324 |
|
|
fixmap(curmap, mp, mt->map_element[i].k_prefmap); |
325 |
|
|
} |
326 |
|
|
} |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
/* |
330 |
|
|
* Do the input for local-set-key, global-set-key and define-key |
331 |
|
|
* then call remap to do the work. |
332 |
|
|
*/ |
333 |
|
|
static int |
334 |
|
|
dobind(KEYMAP *curmap, const char *p, int unbind) |
335 |
|
|
{ |
336 |
|
|
KEYMAP *pref_map = NULL; |
337 |
|
|
PF funct; |
338 |
|
|
char bprompt[80], *bufp, *pep; |
339 |
|
|
int c, s, n; |
340 |
|
|
|
341 |
|
|
if (macrodef) { |
342 |
|
|
/* |
343 |
|
|
* Keystrokes aren't collected. Not hard, but pretty useless. |
344 |
|
|
* Would not work for function keys in any case. |
345 |
|
|
*/ |
346 |
|
|
dobeep(); |
347 |
|
|
ewprintf("Can't rebind key in macro"); |
348 |
|
|
return (FALSE); |
349 |
|
|
} |
350 |
|
|
if (inmacro) { |
351 |
|
|
for (s = 0; s < maclcur->l_used - 1; s++) { |
352 |
|
|
if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap) |
353 |
|
|
!= NULL) { |
354 |
|
|
if (remap(curmap, c, NULL, NULL) |
355 |
|
|
!= TRUE) |
356 |
|
|
return (FALSE); |
357 |
|
|
} |
358 |
|
|
} |
359 |
|
|
(void)doscan(curmap, c = maclcur->l_text[s], NULL); |
360 |
|
|
maclcur = maclcur->l_fp; |
361 |
|
|
} else { |
362 |
|
|
n = strlcpy(bprompt, p, sizeof(bprompt)); |
363 |
|
|
if (n >= sizeof(bprompt)) |
364 |
|
|
n = sizeof(bprompt) - 1; |
365 |
|
|
pep = bprompt + n; |
366 |
|
|
for (;;) { |
367 |
|
|
ewprintf("%s", bprompt); |
368 |
|
|
pep[-1] = ' '; |
369 |
|
|
pep = getkeyname(pep, sizeof(bprompt) - |
370 |
|
|
(pep - bprompt), c = getkey(FALSE)); |
371 |
|
|
if (doscan(curmap, c, &curmap) != NULL) |
372 |
|
|
break; |
373 |
|
|
*pep++ = '-'; |
374 |
|
|
*pep = '\0'; |
375 |
|
|
} |
376 |
|
|
} |
377 |
|
|
if (unbind) |
378 |
|
|
funct = rescan; |
379 |
|
|
else { |
380 |
|
|
if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt), |
381 |
|
|
EFFUNC | EFNEW, bprompt)) == NULL) |
382 |
|
|
return (ABORT); |
383 |
|
|
else if (bufp[0] == '\0') |
384 |
|
|
return (FALSE); |
385 |
|
|
if (((funct = name_function(bprompt)) == NULL) ? |
386 |
|
|
(pref_map = name_map(bprompt)) == NULL : funct == NULL) { |
387 |
|
|
dobeep(); |
388 |
|
|
ewprintf("[No match]"); |
389 |
|
|
return (FALSE); |
390 |
|
|
} |
391 |
|
|
} |
392 |
|
|
return (remap(curmap, c, funct, pref_map)); |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
/* |
396 |
|
|
* bindkey: bind key sequence to a function in the specified map. Used by |
397 |
|
|
* excline so it can bind function keys. To close to release to change |
398 |
|
|
* calling sequence, should just pass KEYMAP *curmap rather than |
399 |
|
|
* KEYMAP **mapp. |
400 |
|
|
*/ |
401 |
|
|
static int |
402 |
|
|
bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount) |
403 |
|
|
{ |
404 |
|
|
KEYMAP *curmap = *mapp; |
405 |
|
|
KEYMAP *pref_map = NULL; |
406 |
|
|
PF funct; |
407 |
|
|
int c; |
408 |
|
|
|
409 |
|
|
if (fname == NULL) |
410 |
|
|
funct = rescan; |
411 |
|
|
else if (((funct = name_function(fname)) == NULL) ? |
412 |
|
|
(pref_map = name_map(fname)) == NULL : funct == NULL) { |
413 |
|
|
dobeep(); |
414 |
|
|
ewprintf("[No match: %s]", fname); |
415 |
|
|
return (FALSE); |
416 |
|
|
} |
417 |
|
|
while (--kcount) { |
418 |
|
|
if (doscan(curmap, c = *keys++, &curmap) != NULL) { |
419 |
|
|
if (remap(curmap, c, NULL, NULL) != TRUE) |
420 |
|
|
return (FALSE); |
421 |
|
|
/* |
422 |
|
|
* XXX - Bizzarreness. remap creates an empty KEYMAP |
423 |
|
|
* that the last key is supposed to point to. |
424 |
|
|
*/ |
425 |
|
|
curmap = ele->k_prefmap; |
426 |
|
|
} |
427 |
|
|
} |
428 |
|
|
(void)doscan(curmap, c = *keys, NULL); |
429 |
|
|
return (remap(curmap, c, funct, pref_map)); |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
/* |
433 |
|
|
* Wrapper for bindkey() that converts escapes. |
434 |
|
|
*/ |
435 |
|
|
int |
436 |
|
|
dobindkey(KEYMAP *map, const char *func, const char *str) |
437 |
|
|
{ |
438 |
|
|
int i; |
439 |
|
|
|
440 |
|
|
for (i = 0; *str && i < MAXKEY; i++) { |
441 |
|
|
/* XXX - convert numbers w/ strol()? */ |
442 |
|
|
if (*str == '^' && *(str + 1) != '\0') { |
443 |
|
|
key.k_chars[i] = CCHR(toupper((unsigned char)*++str)); |
444 |
|
|
} else if (*str == '\\' && *(str + 1) != '\0') { |
445 |
|
|
switch (*++str) { |
446 |
|
|
case '^': |
447 |
|
|
key.k_chars[i] = '^'; |
448 |
|
|
break; |
449 |
|
|
case 't': |
450 |
|
|
case 'T': |
451 |
|
|
key.k_chars[i] = '\t'; |
452 |
|
|
break; |
453 |
|
|
case 'n': |
454 |
|
|
case 'N': |
455 |
|
|
key.k_chars[i] = '\n'; |
456 |
|
|
break; |
457 |
|
|
case 'r': |
458 |
|
|
case 'R': |
459 |
|
|
key.k_chars[i] = '\r'; |
460 |
|
|
break; |
461 |
|
|
case 'e': |
462 |
|
|
case 'E': |
463 |
|
|
key.k_chars[i] = CCHR('['); |
464 |
|
|
break; |
465 |
|
|
case '\\': |
466 |
|
|
key.k_chars[i] = '\\'; |
467 |
|
|
break; |
468 |
|
|
} |
469 |
|
|
} else |
470 |
|
|
key.k_chars[i] = *str; |
471 |
|
|
str++; |
472 |
|
|
} |
473 |
|
|
key.k_count = i; |
474 |
|
|
return (bindkey(&map, func, key.k_chars, key.k_count)); |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
/* |
478 |
|
|
* This function modifies the fundamental keyboard map. |
479 |
|
|
*/ |
480 |
|
|
/* ARGSUSED */ |
481 |
|
|
int |
482 |
|
|
bindtokey(int f, int n) |
483 |
|
|
{ |
484 |
|
|
return (dobind(fundamental_map, "Global set key: ", FALSE)); |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
/* |
488 |
|
|
* This function modifies the current mode's keyboard map. |
489 |
|
|
*/ |
490 |
|
|
/* ARGSUSED */ |
491 |
|
|
int |
492 |
|
|
localbind(int f, int n) |
493 |
|
|
{ |
494 |
|
|
return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, |
495 |
|
|
"Local set key: ", FALSE)); |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
/* |
499 |
|
|
* This function redefines a key in any keymap. |
500 |
|
|
*/ |
501 |
|
|
/* ARGSUSED */ |
502 |
|
|
int |
503 |
|
|
redefine_key(int f, int n) |
504 |
|
|
{ |
505 |
|
|
static char buf[48]; |
506 |
|
|
char tmp[32], *bufp; |
507 |
|
|
KEYMAP *mp; |
508 |
|
|
|
509 |
|
|
(void)strlcpy(buf, "Define key map: ", sizeof(buf)); |
510 |
|
|
if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL) |
511 |
|
|
return (ABORT); |
512 |
|
|
else if (bufp[0] == '\0') |
513 |
|
|
return (FALSE); |
514 |
|
|
(void)strlcat(buf, tmp, sizeof(buf)); |
515 |
|
|
if ((mp = name_map(tmp)) == NULL) { |
516 |
|
|
dobeep(); |
517 |
|
|
ewprintf("Unknown map %s", tmp); |
518 |
|
|
return (FALSE); |
519 |
|
|
} |
520 |
|
|
if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf)) |
521 |
|
|
return (FALSE); |
522 |
|
|
|
523 |
|
|
return (dobind(mp, buf, FALSE)); |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
/* ARGSUSED */ |
527 |
|
|
int |
528 |
|
|
unbindtokey(int f, int n) |
529 |
|
|
{ |
530 |
|
|
return (dobind(fundamental_map, "Global unset key: ", TRUE)); |
531 |
|
|
} |
532 |
|
|
|
533 |
|
|
/* ARGSUSED */ |
534 |
|
|
int |
535 |
|
|
localunbind(int f, int n) |
536 |
|
|
{ |
537 |
|
|
return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, |
538 |
|
|
"Local unset key: ", TRUE)); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
/* |
542 |
|
|
* Extended command. Call the message line routine to read in the command |
543 |
|
|
* name and apply autocompletion to it. When it comes back, look the name |
544 |
|
|
* up in the symbol table and run the command if it is found. Print an |
545 |
|
|
* error if there is anything wrong. |
546 |
|
|
*/ |
547 |
|
|
int |
548 |
|
|
extend(int f, int n) |
549 |
|
|
{ |
550 |
|
|
PF funct; |
551 |
|
|
char xname[NXNAME], *bufp; |
552 |
|
|
|
553 |
|
|
if (!(f & FFARG)) |
554 |
|
|
bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC); |
555 |
|
|
else |
556 |
|
|
bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n); |
557 |
|
|
if (bufp == NULL) |
558 |
|
|
return (ABORT); |
559 |
|
|
else if (bufp[0] == '\0') |
560 |
|
|
return (FALSE); |
561 |
|
|
if ((funct = name_function(bufp)) != NULL) { |
562 |
|
|
if (macrodef) { |
563 |
|
|
struct line *lp = maclcur; |
564 |
|
|
macro[macrocount - 1].m_funct = funct; |
565 |
|
|
maclcur = lp->l_bp; |
566 |
|
|
maclcur->l_fp = lp->l_fp; |
567 |
|
|
free(lp); |
568 |
|
|
} |
569 |
|
|
return ((*funct)(f, n)); |
570 |
|
|
} |
571 |
|
|
dobeep(); |
572 |
|
|
ewprintf("[No match]"); |
573 |
|
|
return (FALSE); |
574 |
|
|
} |
575 |
|
|
|
576 |
|
|
/* |
577 |
|
|
* Define the commands needed to do startup-file processing. |
578 |
|
|
* This code is mostly a kludge just so we can get startup-file processing. |
579 |
|
|
* |
580 |
|
|
* If you're serious about having this code, you should rewrite it. |
581 |
|
|
* To wit: |
582 |
|
|
* It has lots of funny things in it to make the startup-file look |
583 |
|
|
* like a GNU startup file; mostly dealing with parens and semicolons. |
584 |
|
|
* This should all vanish. |
585 |
|
|
* |
586 |
|
|
* We define eval-expression because it's easy. It can make |
587 |
|
|
* *-set-key or define-key set an arbitrary key sequence, so it isn't |
588 |
|
|
* useless. |
589 |
|
|
*/ |
590 |
|
|
|
591 |
|
|
/* |
592 |
|
|
* evalexpr - get one line from the user, and run it. |
593 |
|
|
*/ |
594 |
|
|
/* ARGSUSED */ |
595 |
|
|
int |
596 |
|
|
evalexpr(int f, int n) |
597 |
|
|
{ |
598 |
|
|
char exbuf[128], *bufp; |
599 |
|
|
|
600 |
|
|
if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf), |
601 |
|
|
EFNEW | EFCR)) == NULL) |
602 |
|
|
return (ABORT); |
603 |
|
|
else if (bufp[0] == '\0') |
604 |
|
|
return (FALSE); |
605 |
|
|
return (excline(exbuf)); |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
/* |
609 |
|
|
* evalbuffer - evaluate the current buffer as line commands. Useful for |
610 |
|
|
* testing startup files. |
611 |
|
|
*/ |
612 |
|
|
/* ARGSUSED */ |
613 |
|
|
int |
614 |
|
|
evalbuffer(int f, int n) |
615 |
|
|
{ |
616 |
|
|
struct line *lp; |
617 |
|
|
struct buffer *bp = curbp; |
618 |
|
|
int s; |
619 |
|
|
static char excbuf[128]; |
620 |
|
|
|
621 |
|
|
for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) { |
622 |
|
|
if (llength(lp) >= 128) |
623 |
|
|
return (FALSE); |
624 |
|
|
(void)strncpy(excbuf, ltext(lp), llength(lp)); |
625 |
|
|
|
626 |
|
|
/* make sure it's terminated */ |
627 |
|
|
excbuf[llength(lp)] = '\0'; |
628 |
|
|
if ((s = excline(excbuf)) != TRUE) |
629 |
|
|
return (s); |
630 |
|
|
} |
631 |
|
|
return (TRUE); |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
/* |
635 |
|
|
* evalfile - go get a file and evaluate it as line commands. You can |
636 |
|
|
* go get your own startup file if need be. |
637 |
|
|
*/ |
638 |
|
|
/* ARGSUSED */ |
639 |
|
|
int |
640 |
|
|
evalfile(int f, int n) |
641 |
|
|
{ |
642 |
|
|
char fname[NFILEN], *bufp; |
643 |
|
|
|
644 |
|
|
if ((bufp = eread("Load file: ", fname, NFILEN, |
645 |
|
|
EFNEW | EFCR)) == NULL) |
646 |
|
|
return (ABORT); |
647 |
|
|
else if (bufp[0] == '\0') |
648 |
|
|
return (FALSE); |
649 |
|
|
return (load(fname)); |
650 |
|
|
} |
651 |
|
|
|
652 |
|
|
/* |
653 |
|
|
* load - go load the file name we got passed. |
654 |
|
|
*/ |
655 |
|
|
int |
656 |
|
|
load(const char *fname) |
657 |
|
|
{ |
658 |
|
|
int s = TRUE, line, ret; |
659 |
|
|
int nbytes = 0; |
660 |
|
|
char excbuf[128]; |
661 |
|
|
FILE *ffp; |
662 |
|
|
|
663 |
|
|
if ((fname = adjustname(fname, TRUE)) == NULL) |
664 |
|
|
/* just to be careful */ |
665 |
|
|
return (FALSE); |
666 |
|
|
|
667 |
|
|
ret = ffropen(&ffp, fname, NULL); |
668 |
|
|
if (ret != FIOSUC) { |
669 |
|
|
if (ret == FIODIR) |
670 |
|
|
(void)ffclose(ffp, NULL); |
671 |
|
|
return (FALSE); |
672 |
|
|
} |
673 |
|
|
|
674 |
|
|
line = 0; |
675 |
|
|
while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes)) |
676 |
|
|
== FIOSUC) { |
677 |
|
|
line++; |
678 |
|
|
excbuf[nbytes] = '\0'; |
679 |
|
|
if (excline(excbuf) != TRUE) { |
680 |
|
|
s = FIOERR; |
681 |
|
|
dobeep(); |
682 |
|
|
ewprintf("Error loading file %s at line %d", fname, line); |
683 |
|
|
break; |
684 |
|
|
} |
685 |
|
|
} |
686 |
|
|
(void)ffclose(ffp, NULL); |
687 |
|
|
excbuf[nbytes] = '\0'; |
688 |
|
|
if (s != FIOEOF || (nbytes && excline(excbuf) != TRUE)) |
689 |
|
|
return (FALSE); |
690 |
|
|
return (TRUE); |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
/* |
694 |
|
|
* excline - run a line from a load file or eval-expression. |
695 |
|
|
*/ |
696 |
|
|
int |
697 |
|
|
excline(char *line) |
698 |
|
|
{ |
699 |
|
|
PF fp; |
700 |
|
|
struct line *lp, *np; |
701 |
|
|
int status, c, f, n; |
702 |
|
|
char *funcp, *tmp; |
703 |
|
|
char *argp = NULL; |
704 |
|
|
long nl; |
705 |
|
|
int bind; |
706 |
|
|
KEYMAP *curmap; |
707 |
|
|
#define BINDARG 0 /* this arg is key to bind (local/global set key) */ |
708 |
|
|
#define BINDNO 1 /* not binding or non-quoted BINDARG */ |
709 |
|
|
#define BINDNEXT 2 /* next arg " (define-key) */ |
710 |
|
|
#define BINDDO 3 /* already found key to bind */ |
711 |
|
|
#define BINDEXT 1 /* space for trailing \0 */ |
712 |
|
|
|
713 |
|
|
lp = NULL; |
714 |
|
|
|
715 |
|
|
if (macrodef || inmacro) { |
716 |
|
|
dobeep(); |
717 |
|
|
ewprintf("Not now!"); |
718 |
|
|
return (FALSE); |
719 |
|
|
} |
720 |
|
|
f = 0; |
721 |
|
|
n = 1; |
722 |
|
|
funcp = skipwhite(line); |
723 |
|
|
if (*funcp == '\0') |
724 |
|
|
return (TRUE); /* No error on blank lines */ |
725 |
|
|
line = parsetoken(funcp); |
726 |
|
|
if (*line != '\0') { |
727 |
|
|
*line++ = '\0'; |
728 |
|
|
line = skipwhite(line); |
729 |
|
|
if (ISDIGIT(*line) || *line == '-') { |
730 |
|
|
argp = line; |
731 |
|
|
line = parsetoken(line); |
732 |
|
|
} |
733 |
|
|
} |
734 |
|
|
if (argp != NULL) { |
735 |
|
|
f = FFARG; |
736 |
|
|
nl = strtol(argp, &tmp, 10); |
737 |
|
|
if (*tmp != '\0') |
738 |
|
|
return (FALSE); |
739 |
|
|
if (nl >= INT_MAX || nl <= INT_MIN) |
740 |
|
|
return (FALSE); |
741 |
|
|
n = (int)nl; |
742 |
|
|
} |
743 |
|
|
if ((fp = name_function(funcp)) == NULL) { |
744 |
|
|
dobeep(); |
745 |
|
|
ewprintf("Unknown function: %s", funcp); |
746 |
|
|
return (FALSE); |
747 |
|
|
} |
748 |
|
|
if (fp == bindtokey || fp == unbindtokey) { |
749 |
|
|
bind = BINDARG; |
750 |
|
|
curmap = fundamental_map; |
751 |
|
|
} else if (fp == localbind || fp == localunbind) { |
752 |
|
|
bind = BINDARG; |
753 |
|
|
curmap = curbp->b_modes[curbp->b_nmodes]->p_map; |
754 |
|
|
} else if (fp == redefine_key) |
755 |
|
|
bind = BINDNEXT; |
756 |
|
|
else |
757 |
|
|
bind = BINDNO; |
758 |
|
|
/* Pack away all the args now... */ |
759 |
|
|
if ((np = lalloc(0)) == FALSE) |
760 |
|
|
return (FALSE); |
761 |
|
|
np->l_fp = np->l_bp = maclcur = np; |
762 |
|
|
while (*line != '\0') { |
763 |
|
|
argp = skipwhite(line); |
764 |
|
|
if (*argp == '\0') |
765 |
|
|
break; |
766 |
|
|
line = parsetoken(argp); |
767 |
|
|
if (*argp != '"') { |
768 |
|
|
if (*argp == '\'') |
769 |
|
|
++argp; |
770 |
|
|
if ((lp = lalloc((int) (line - argp) + BINDEXT)) == |
771 |
|
|
NULL) { |
772 |
|
|
status = FALSE; |
773 |
|
|
goto cleanup; |
774 |
|
|
} |
775 |
|
|
bcopy(argp, ltext(lp), (int)(line - argp)); |
776 |
|
|
/* don't count BINDEXT */ |
777 |
|
|
lp->l_used--; |
778 |
|
|
if (bind == BINDARG) |
779 |
|
|
bind = BINDNO; |
780 |
|
|
} else { |
781 |
|
|
/* quoted strings are special */ |
782 |
|
|
++argp; |
783 |
|
|
if (bind != BINDARG) { |
784 |
|
|
lp = lalloc((int)(line - argp) + BINDEXT); |
785 |
|
|
if (lp == NULL) { |
786 |
|
|
status = FALSE; |
787 |
|
|
goto cleanup; |
788 |
|
|
} |
789 |
|
|
lp->l_used = 0; |
790 |
|
|
} else |
791 |
|
|
key.k_count = 0; |
792 |
|
|
while (*argp != '"' && *argp != '\0') { |
793 |
|
|
if (*argp != '\\') |
794 |
|
|
c = *argp++; |
795 |
|
|
else { |
796 |
|
|
switch (*++argp) { |
797 |
|
|
case 't': |
798 |
|
|
case 'T': |
799 |
|
|
c = CCHR('I'); |
800 |
|
|
break; |
801 |
|
|
case 'n': |
802 |
|
|
case 'N': |
803 |
|
|
c = CCHR('J'); |
804 |
|
|
break; |
805 |
|
|
case 'r': |
806 |
|
|
case 'R': |
807 |
|
|
c = CCHR('M'); |
808 |
|
|
break; |
809 |
|
|
case 'e': |
810 |
|
|
case 'E': |
811 |
|
|
c = CCHR('['); |
812 |
|
|
break; |
813 |
|
|
case '^': |
814 |
|
|
/* |
815 |
|
|
* split into two statements |
816 |
|
|
* due to bug in OSK cpp |
817 |
|
|
*/ |
818 |
|
|
c = CHARMASK(*++argp); |
819 |
|
|
c = ISLOWER(c) ? |
820 |
|
|
CCHR(TOUPPER(c)) : CCHR(c); |
821 |
|
|
break; |
822 |
|
|
case '0': |
823 |
|
|
case '1': |
824 |
|
|
case '2': |
825 |
|
|
case '3': |
826 |
|
|
case '4': |
827 |
|
|
case '5': |
828 |
|
|
case '6': |
829 |
|
|
case '7': |
830 |
|
|
c = *argp - '0'; |
831 |
|
|
if (argp[1] <= '7' && |
832 |
|
|
argp[1] >= '0') { |
833 |
|
|
c <<= 3; |
834 |
|
|
c += *++argp - '0'; |
835 |
|
|
if (argp[1] <= '7' && |
836 |
|
|
argp[1] >= '0') { |
837 |
|
|
c <<= 3; |
838 |
|
|
c += *++argp |
839 |
|
|
- '0'; |
840 |
|
|
} |
841 |
|
|
} |
842 |
|
|
break; |
843 |
|
|
case 'f': |
844 |
|
|
case 'F': |
845 |
|
|
c = *++argp - '0'; |
846 |
|
|
if (ISDIGIT(argp[1])) { |
847 |
|
|
c *= 10; |
848 |
|
|
c += *++argp - '0'; |
849 |
|
|
} |
850 |
|
|
c += KFIRST; |
851 |
|
|
break; |
852 |
|
|
default: |
853 |
|
|
c = CHARMASK(*argp); |
854 |
|
|
break; |
855 |
|
|
} |
856 |
|
|
argp++; |
857 |
|
|
} |
858 |
|
|
if (bind == BINDARG) |
859 |
|
|
key.k_chars[key.k_count++] = c; |
860 |
|
|
else |
861 |
|
|
lp->l_text[lp->l_used++] = c; |
862 |
|
|
} |
863 |
|
|
if (*line) |
864 |
|
|
line++; |
865 |
|
|
} |
866 |
|
|
switch (bind) { |
867 |
|
|
case BINDARG: |
868 |
|
|
bind = BINDDO; |
869 |
|
|
break; |
870 |
|
|
case BINDNEXT: |
871 |
|
|
lp->l_text[lp->l_used] = '\0'; |
872 |
|
|
if ((curmap = name_map(lp->l_text)) == NULL) { |
873 |
|
|
dobeep(); |
874 |
|
|
ewprintf("No such mode: %s", lp->l_text); |
875 |
|
|
status = FALSE; |
876 |
|
|
free(lp); |
877 |
|
|
goto cleanup; |
878 |
|
|
} |
879 |
|
|
free(lp); |
880 |
|
|
bind = BINDARG; |
881 |
|
|
break; |
882 |
|
|
default: |
883 |
|
|
lp->l_fp = np->l_fp; |
884 |
|
|
lp->l_bp = np; |
885 |
|
|
np->l_fp = lp; |
886 |
|
|
np = lp; |
887 |
|
|
} |
888 |
|
|
} |
889 |
|
|
switch (bind) { |
890 |
|
|
default: |
891 |
|
|
dobeep(); |
892 |
|
|
ewprintf("Bad args to set key"); |
893 |
|
|
status = FALSE; |
894 |
|
|
break; |
895 |
|
|
case BINDDO: |
896 |
|
|
if (fp != unbindtokey && fp != localunbind) { |
897 |
|
|
lp->l_text[lp->l_used] = '\0'; |
898 |
|
|
status = bindkey(&curmap, lp->l_text, key.k_chars, |
899 |
|
|
key.k_count); |
900 |
|
|
} else |
901 |
|
|
status = bindkey(&curmap, NULL, key.k_chars, |
902 |
|
|
key.k_count); |
903 |
|
|
break; |
904 |
|
|
case BINDNO: |
905 |
|
|
inmacro = TRUE; |
906 |
|
|
maclcur = maclcur->l_fp; |
907 |
|
|
status = (*fp)(f, n); |
908 |
|
|
inmacro = FALSE; |
909 |
|
|
} |
910 |
|
|
cleanup: |
911 |
|
|
lp = maclcur->l_fp; |
912 |
|
|
while (lp != maclcur) { |
913 |
|
|
np = lp->l_fp; |
914 |
|
|
free(lp); |
915 |
|
|
lp = np; |
916 |
|
|
} |
917 |
|
|
free(lp); |
918 |
|
|
maclhead = NULL; |
919 |
|
|
macrodef = FALSE; |
920 |
|
|
return (status); |
921 |
|
|
} |
922 |
|
|
|
923 |
|
|
/* |
924 |
|
|
* a pair of utility functions for the above |
925 |
|
|
*/ |
926 |
|
|
static char * |
927 |
|
|
skipwhite(char *s) |
928 |
|
|
{ |
929 |
|
|
while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(') |
930 |
|
|
s++; |
931 |
|
|
if ((*s == ';') || (*s == '#')) |
932 |
|
|
*s = '\0'; |
933 |
|
|
return (s); |
934 |
|
|
} |
935 |
|
|
|
936 |
|
|
static char * |
937 |
|
|
parsetoken(char *s) |
938 |
|
|
{ |
939 |
|
|
if (*s != '"') { |
940 |
|
|
while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(') |
941 |
|
|
s++; |
942 |
|
|
if (*s == ';') |
943 |
|
|
*s = '\0'; |
944 |
|
|
} else |
945 |
|
|
do { |
946 |
|
|
/* |
947 |
|
|
* Strings get special treatment. |
948 |
|
|
* Beware: You can \ out the end of the string! |
949 |
|
|
*/ |
950 |
|
|
if (*s == '\\') |
951 |
|
|
++s; |
952 |
|
|
} while (*++s != '"' && *s != '\0'); |
953 |
|
|
return (s); |
954 |
|
|
} |