1 #include <stdio.h>
2 #include <ctype.h>
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 #include <X11/keysym.h>
6 
7 #include "ztypes.h"
8 #include "xio.h"
9 
10 #define NUMCOMMANDS (768) /* 3*256 */
11 
12 cmdentry *keycmds[NUMCOMMANDS];
13 char *keycmdargs[NUMCOMMANDS];
14 
15 static cmdentry mastertable[] = {
16     {xted_insert, -1, 0, "insert-self"},
17     {xted_enter, op_Enter, 0, "enter"},
18 
19     {xted_movecursor, op_ForeChar, 0, "forward-char"},
20     {xted_movecursor, op_BackChar, 0, "backward-char"},
21     {xted_movecursor, op_ForeWord, 0, "forward-word"},
22     {xted_movecursor, op_BackWord, 0, "backward-word"},
23     {xted_movecursor, op_ForeLine, 0, "forward-line"},
24     {xted_movecursor, op_BackLine, 0, "backward-line"},
25     {xted_movecursor, op_BeginLine, 0, "beginning-of-line"},
26     {xted_movecursor, op_EndLine, 0, "end-of-line"},
27 
28     {xted_scroll, op_DownPage, 0, "scroll-down"},
29     {xted_scroll, op_UpPage, 0, "scroll-up"},
30     {xted_scroll, op_DownLine, 0, "scroll-down-line"},
31     {xted_scroll, op_UpLine, 0, "scroll-up-line"},
32     {xted_scroll, op_ToBottom, 0, "scroll-to-bottom"},
33     {xted_scroll, op_ToTop, 0, "scroll-to-top"},
34 
35     {xted_delete, op_ForeChar, 0, "delete-next-char"},
36     {xted_delete, op_BackChar, 0, "delete-char"},
37     {xted_delete, op_ForeWord, 0, "delete-next-word"},
38     {xted_delete, op_BackWord, 0, "delete-word"},
39 
40     {xted_cutbuf, op_Yank, 0, "yank"},
41     {xted_cutbuf, op_Wipe, 0, "kill-region"},
42     {xted_cutbuf, op_Copy, 0, "copy-region"},
43     {xted_cutbuf, op_YankReplace, 0, "yank-pop"},
44     {xted_cutbuf, op_Kill, 0, "kill-line"},
45     {xted_cutbuf, op_Untype, 0, "kill-input"},
46 
47     {xted_history, op_BackLine, 0, "backward-history"},
48     {xted_history, op_ForeLine, 0, "forward-history"},
49 
50     {xted_macro, -1, 0, "macro"},
51 
52     {xtexted_meta, op_Cancel, 1, "cancel"},
53     {xtexted_meta, op_Escape, 1, "escape"},
54     {xtexted_meta, op_ExplainKey, 0, "explain-key"},
55     {xtexted_meta, op_DefineMacro, 0, "define-macro"},
56     {xtexted_redraw, op_Screen, 0, "redraw-screen"},
57     {xtexted_redraw, op_Status, 0, "redraw-status"},
58     {xtexted_redraw, op_AllWindows, 0, "redraw-all-windows"},
59     {xstat_reset_window_size, op_Zoom, 0, "zoom-status"},
60     {xstat_reset_window_size, op_Shrink, 0, "shrink-status"},
61     {xstat_reset_window_size, op_Clear, 0, "clear-status"},
62 
63     {xted_noop, -1, 0, "no-op"},
64 
65     {NULL, 0, 0, NULL}
66 };
67 
68 typedef struct binding_t {
69     unsigned int key; /* or keysym */
70     int which; /* keytype_main, keytype_meta, keytype_sym */
71     char *name;
72 } binding;
73 
74 static binding defaultbindings[] = {
75     {XK_Left, keytype_sym, "backward-char"},
76     {XK_KP_Left, keytype_sym, "backward-char"},
77     {XK_Right, keytype_sym, "forward-char"},
78     {XK_KP_Right, keytype_sym, "forward-char"},
79     {XK_Up, keytype_sym, "backward-history"},
80     {XK_KP_Up, keytype_sym, "backward-history"},
81     {XK_Down, keytype_sym, "forward-history"},
82     {XK_KP_Down, keytype_sym, "forward-history"},
83     {XK_Begin, keytype_sym, "beginning-of-line"},
84     {XK_KP_Begin, keytype_sym, "beginning-of-line"},
85     {XK_End, keytype_sym, "end-of-line"},
86     {XK_KP_End, keytype_sym, "end-of-line"},
87     {XK_Prior, keytype_sym, "scroll-up"},
88     {XK_KP_Prior, keytype_sym, "scroll-up"},
89     {XK_Next, keytype_sym, "scroll-down"},
90     {XK_KP_Next, keytype_sym, "scroll-down"},
91     {XK_Delete, keytype_sym, "delete-char"},
92     {XK_KP_Delete, keytype_sym, "delete-char"},
93     {XK_Help, keytype_sym, "explain-key"},
94 
95     {'w', keytype_meta, "copy-region"},
96     {'y', keytype_meta, "yank-pop"},
97     {'b', keytype_meta, "backward-word"},
98     {'f', keytype_meta, "forward-word"},
99     {'v', keytype_meta, "scroll-up"},
100     {'d', keytype_meta, "delete-next-word"},
101     {'`', keytype_meta, "forward-history"},
102     {'=', keytype_meta, "backward-history"},
103     {'z', keytype_meta, "zoom-status"},
104     {'s', keytype_meta, "shrink-status"},
105     {'c', keytype_meta, "clear-status"},
106     {'r', keytype_meta, "define-macro"},
107     {'0', keytype_meta, "macro"},
108     {'1', keytype_meta, "macro"},
109     {'2', keytype_meta, "macro"},
110     {'3', keytype_meta, "macro"},
111     {'4', keytype_meta, "macro"},
112     {'5', keytype_meta, "macro"},
113     {'6', keytype_meta, "macro"},
114     {'7', keytype_meta, "macro"},
115     {'8', keytype_meta, "macro"},
116     {'9', keytype_meta, "macro"},
117 
118     {'\177', keytype_meta, "delete-word"},
119     {'\010', keytype_meta, "delete-word"},
120 
121     {'\007' /* ctrl-G */, keytype_main, "cancel"},
122     {'\033' /* Escape */, keytype_main, "escape"},
123     {'\007' /* ctrl-G */, keytype_meta, "cancel"},
124     {'\037' /* ctrl-_ */, keytype_main, "explain-key"},
125 #ifdef TESTING
126     {'\021' /* ctrl-Q */, keytype_main, "scroll-up-line"},
127     {'\032' /* ctrl-Z */, keytype_main, "scroll-down-line"},
128 #endif
129     {'\001' /* ctrl-A */, keytype_main, "beginning-of-line"},
130     {'\005' /* ctrl-E */, keytype_main, "end-of-line"},
131     {'\002' /* ctrl-B */, keytype_main, "backward-char"},
132     {'\006' /* ctrl-F */, keytype_main, "forward-char"},
133     {'\014' /* ctrl-L */, keytype_main, "redraw-all-windows"},
134     {'\031' /* ctrl-Y */, keytype_main, "yank"},
135     {'\027' /* ctrl-W */, keytype_main, "kill-region"},
136     {'\013' /* ctrl-K */, keytype_main, "kill-line"},
137     {'\025' /* ctrl-U */, keytype_main, "kill-input"},
138     {'\026' /* ctrl-V */, keytype_main, "scroll-down"},
139     {'\016' /* ctrl-N */, keytype_main, "forward-line"},
140     {'\020' /* ctrl-P */, keytype_main, "backward-line"},
141     {'\177' /* del */,    keytype_main, "delete-char"},
142     {'\010' /* del */,    keytype_main, "delete-char"},
143     {'\004' /* ctrl-D */, keytype_main, "delete-next-char"},
144     {'\n' /* newline */,  keytype_main, "enter"},
145     {'\r' /* return */,   keytype_main, "enter"},
146 
147     {0, 0, NULL}
148 };
149 
150 #ifdef __STDC__
151 static void parse_one_binding(char *key, char *proc, char *marg);
152 #else
153 static void parse_one_binding();
154 #endif
155 
156 /* initialize key tables */
157 #ifdef __STDC__
xkey_init()158 void xkey_init()
159 #else
160 void xkey_init()
161 #endif
162 {
163     int ix, keynum;
164     cmdentry *cmd;
165     binding *bx;
166 
167     for (ix=0; ix<NUMCOMMANDS; ix++) {
168 	keycmds[ix] = NULL;
169 	keycmdargs[ix] = NULL;
170     }
171 
172     cmd = xkey_find_cmd_by_name("insert-self");
173     for (ix=' '; ix<='~'; ix++) {
174 	keycmds[ix | keytype_main] = cmd;
175     }
176 
177     for (bx=defaultbindings; bx->name; bx++) {
178 	ix = (bx->key & 255);
179 	cmd = xkey_find_cmd_by_name(bx->name);
180 	if (!cmd) {
181 	    fprintf(stderr, "%s: unknown function <%s> in default bindings\n", PROGRAMNAME, bx->name);
182 	}
183 	else {
184 	    keynum = ix | bx->which;
185 	    if (keycmds[keynum]) {
186 		fprintf(stderr, "%s: key <%s> (%d) defined twice in default bindings\n", PROGRAMNAME,
187 			xkey_get_key_name(keynum), ix);
188 	    }
189 	    keycmds[keynum] = cmd;
190 	}
191     }
192 }
193 
194 #ifdef __STDC__
xkey_get_key_name(int key)195 char *xkey_get_key_name(int key)
196 #else
197 char *xkey_get_key_name(key)
198 int key;
199 #endif
200 {
201     static char buf[32];
202     char *prefix, *name;
203     int which;
204 
205     which = key & keytype_Mask;
206     key = key & (~keytype_Mask);
207 
208     if (which==keytype_sym) {
209 	KeySym ksym = (KeySym)((XK_Home & (~255)) | key); /* I bet this is a stupid thing to do */
210 	name = XKeysymToString(ksym);
211 	if (!name)
212 	    name = "Unknown key";
213 	strcpy(buf, name);
214 	return buf;
215     }
216 
217     if (which==keytype_meta)
218 	prefix = "meta-";
219     else
220 	prefix = "";
221 
222     if (iscntrl(key)) {
223 	sprintf(buf, "%sctrl-%c", prefix, key+'A'-1);
224     }
225     else {
226 	sprintf(buf, "%s%c", prefix, key);
227     }
228 
229     return buf;
230 }
231 
232 /* parse stuff of the form key=proc-name;key=proc-name;...
233  Spaces are optional, c-X and m-X are abbreviations, /123 codes are accepted, and I'm getting a headache. */
234 #ifdef __STDC__
xkey_parse_bindings(char * str)235 void xkey_parse_bindings(char *str)
236 #else
237 void xkey_parse_bindings(str)
238 char *str;
239 #endif
240 {
241     char *cx, *cx2, *key, *proc, *marg;
242     char buf[64], nbuf[256], margbuf[256];
243     int escaped;
244     int keylen, proclen;
245     int hasmarg;
246 
247     cx = str;
248     for (; *cx && isspace(*cx); cx++);
249     while (*cx) {
250 
251 	key = cx;
252 
253 	escaped = FALSE;
254 	for (; *cx && *cx != '=' && !isspace(*cx); cx++) {
255 	    if (*cx == '\\' && *(cx+1) != '\0') {
256 		cx++;
257 		if (isdigit(*cx)) {
258 		    cx++;
259 		    if (isdigit(*cx)) {
260 			cx++;
261 		    }
262 		}
263 	    }
264 	}
265 	keylen = cx - key;
266 	if (keylen >= sizeof(buf)) {
267 	    keylen = sizeof(buf)-1;
268 	}
269 	strncpy(buf, key, keylen);
270 	buf[keylen] = '\0';
271 	if (*cx == '\0') {
272 	    fprintf(stderr, "%s: binding for <%s> has no procedure\n", PROGRAMNAME, buf);
273 	    break;
274 	}
275 
276 	for (; *cx && isspace(*cx); cx++);
277 	if (*cx != '=') {
278 	    fprintf(stderr, "%s: unexpected '%c' instead of '=' after <%s>\n", PROGRAMNAME, *cx, buf);
279 	    break;
280 	}
281 	cx++; /* skip equals */
282 	for (; *cx && isspace(*cx); cx++);
283 	proc = cx;
284 	for (; *cx && (*cx=='-' || isalnum(*cx)); cx++);
285 	proclen = cx - proc;
286 	if (proclen >= sizeof(nbuf)) {
287 	    proclen = sizeof(nbuf)-1;
288 	}
289 	strncpy(nbuf, proc, proclen);
290 	nbuf[proclen] = '\0';
291 
292 	for (; *cx && isspace(*cx); cx++);
293 	hasmarg = FALSE;
294 	if (*cx == ',') {
295 	    cx++; /* skip comma */
296 	    for (; *cx && isspace(*cx); cx++);
297 	    if (*cx != '"') {
298 		fprintf(stderr, "%s: unexpected '%c' instead of '\"' after ','\n", PROGRAMNAME, *cx);
299 		break;
300 	    }
301 	    cx++; /* skip open-quote */
302 	    marg = cx;
303 	    for (; *cx && *cx!='\"'; cx++);
304 	    if (*cx == '\0') {
305 		fprintf(stderr, "%s: unexpected end of string after '\"'\n", PROGRAMNAME);
306 		break;
307 	    }
308 	    proclen = cx - marg;
309 	    if (proclen >= sizeof(margbuf)) {
310 		proclen = sizeof(margbuf)-1;
311 	    }
312 	    strncpy(margbuf, marg, proclen);
313 	    margbuf[proclen] = '\0';
314 	    hasmarg = TRUE;
315 
316 	    cx++; /* skip close-quote */
317 	    for (; *cx && isspace(*cx); cx++);
318 	}
319 
320 	parse_one_binding(buf, nbuf, hasmarg ? margbuf : NULL);
321 
322 	if (*cx == '\0')
323 	    break;
324 	if (*cx != ';') {
325 	    fprintf(stderr, "%s: unexpected '%c' instead of ';' after <%s>\n", PROGRAMNAME, *cx, nbuf);
326 	    break;
327 	}
328 	cx++; /* skip semicolon */
329 	for (; *cx && isspace(*cx); cx++);
330     }
331 
332 }
333 
334 #ifdef __STDC__
parse_one_binding(char * key,char * proc,char * marg)335 static void parse_one_binding(char *key, char *proc, char *marg)
336 #else
337 static void parse_one_binding(key, proc, marg)
338 char *key;
339 char *proc;
340 char *marg;
341 #endif
342 {
343     cmdentry *cmd;
344     int ismeta = FALSE;
345     int isctrl = FALSE;
346     int keynum;
347     KeySym ksym;
348 
349     cmd = xkey_find_cmd_by_name(proc);
350     if (!cmd) {
351 	fprintf(stderr, "%s: unknown procedure <%s>\n", PROGRAMNAME, proc);
352 	return;
353     }
354 
355     while ((*key=='c' || *key=='C' || *key=='m' || *key=='M') && *(key+1)=='-') {
356 	if (*key=='c' || *key=='C')
357 	    isctrl = TRUE;
358 	else
359 	    ismeta = TRUE;
360 	key += 2;
361     }
362     if (strlen(key)==1) {
363 	keynum = (*key);
364 	if (isctrl) {
365 	    if (islower(keynum))
366 		keynum = toupper(keynum);
367 	    if (keynum >= 'A'-1)
368 		keynum -= ('A'-1);
369 	}
370 	if (ismeta)
371 	    keynum |= keytype_meta;
372     }
373     else {
374 	ksym = XStringToKeysym(key);
375 	if (ksym==NoSymbol) {
376 	    fprintf(stderr, "%s: unknown key <%s>\n", PROGRAMNAME, key);
377 	    return;
378 	}
379 	if ((ksym & (~255)) == (XK_Home & (~255))) {
380 	    keynum = keytype_sym | (ksym & (255));
381 	}
382 	else if ((ksym & (~255)) == (XK_A & (~255))) {
383 	    keynum = keytype_main | (ksym & (255));
384 	    if (ismeta)
385 		keynum |= keytype_meta;
386 	}
387 	else {
388 	    fprintf(stderr, "%s: unknown key <%s>\n", PROGRAMNAME, key);
389 	    return;
390 	}
391     }
392 
393     keycmds[keynum] = cmd;
394 
395     if (keycmdargs[keynum]) {
396 	free(keycmdargs[keynum]);
397 	keycmdargs[keynum] = NULL;
398     }
399 
400     if (marg) {
401 	keycmdargs[keynum] = (char *)malloc(sizeof(char) * (1+strlen(marg)));
402 	strcpy(keycmdargs[keynum], marg);
403     }
404 }
405 
406 #ifdef __STDC__
xkey_find_cmd_by_name(char * str)407 cmdentry *xkey_find_cmd_by_name(char *str)
408 #else
409 cmdentry *xkey_find_cmd_by_name(str)
410 char *str;
411 #endif
412 {
413     cmdentry *retval;
414 
415     for (retval = mastertable; retval->func; retval++) {
416 	if (!strcmp(str, retval->name))
417 	    return retval;
418     }
419     return NULL;
420 }
421