1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Keyboard (re)mapping
6  */
7 
8 #include <string.h>
9 #include <ctype.h>
10 #include "config.h"
11 #include "global.h"
12 #include "init.h"
13 #include "keymap.h"
14 #include "nn_term.h"
15 
16 extern int      data_bits;
17 
18 /*
19  * KEY MAP LAYOUT:
20  *	128 normal ASCII chars
21  *	0200 -- unused
22  *	4 arrow keys #up/down/left/right
23  *	10 multi keys #0-#9
24  *	17 spare keys
25  *	95 national 8-bit characters (8859/x)
26  *	0377 is unused (since we must be able to test c<KEY_MAP_SIZE)
27  *
28  *	The encoding of the keymap arrays are performed in
29  *	keymap.c (initialization + key_name/parse_key)
30  */
31 
32 /* in keymap.h, MULTI_KEYS include ARROW_KEYS for term.c */
33 #undef MULTI_KEYS
34 
35 #define NORMAL_KEYS	129	/* include 0200 for convenience */
36 #define ARROW_KEYS	4
37 #define MULTI_KEYS	16
38 #define SPARE_KEYS	11
39 #define NATIONAL_KEYS	96
40 #define KEY_MAP_SIZE	255
41 
42 /*
43  * standard keyboard mapping for more()
44  *
45  *	redraw			^L, ^R
46  *	continue		space
47  *	repeat message		^P
48  *	help			?
49  *	shell escape		!
50  *	version			V
51  *	extended command	:
52  *	quit			Q
53  *
54  *	save			S, O
55  *	save, no header		W
56  *	reply			R
57  *	follow up		F
58  *	mail (forward)		M
59  *	cancel			C
60  *	unsubscribe		U
61  *	group overview		Y
62  *	print article		P
63  *	kill handling		K
64  *
65  *	update, goto next group	X
66  *	no update, next group	q, Z
67  *	return to menu		=
68  *	prev article		p
69  *	goto group		G
70  *
71  *	line forward		CR/NL
72  *	half page forward	d/^D
73  *	half page back		u/^U
74  *	full page back		BS, DEL, (up arrow)
75  *	goto line		g
76  *	goto match		/
77  *	next match		.
78  *
79  *	select subject		N, *
80  *
81  *	header			h
82  *	digest header		H
83  *	top			t
84  *	last page		$
85  *	leave article		l
86  *	leave article to next	L
87  *	next article		n
88  *	kill subject		k
89  *
90  *	rot13			D
91  *	compress		c
92  */
93 
94 int             more_key_map[KEY_MAP_SIZE] = {
95 
96      /* NUL ^@ */ K_UNBOUND,
97      /* SOH ^A */ K_UNBOUND,
98      /* STX ^B */ K_UNBOUND,
99      /* ETX ^C */ K_UNBOUND,
100      /* EOT ^D */ K_NEXT_HALF_PAGE,
101      /* ENQ ^E */ K_UNBOUND,
102      /* ACK ^F */ K_UNBOUND,
103      /* BEL ^G */ K_INVALID,
104      /* BS  ^H */ K_PREV_PAGE,
105      /* TAB ^I */ K_SKIP_LINES,
106      /* NL  ^J */ K_NEXT_LINE,
107      /* VT  ^K */ K_UNBOUND,
108      /* FF  ^L */ K_REDRAW,
109      /* CR  ^M */ K_NEXT_LINE,
110      /* SO  ^N */ K_UNBOUND,
111      /* SI  ^O */ K_UNBOUND,
112      /* DLE ^P */ K_LAST_MESSAGE,
113      /* DC1 ^Q */ K_UNBOUND,
114      /* DC2 ^R */ K_REDRAW,
115      /* DC3 ^S */ K_UNBOUND,
116      /* DC4 ^T */ K_UNBOUND,
117      /* NAK ^U */ K_PREV_HALF_PAGE,
118      /* SYN ^V */ K_NEXT_PAGE,
119      /* ETB ^W */ K_UNBOUND,
120      /* CAN ^X */ K_UNBOUND,
121      /* EM  ^Y */ K_UNBOUND,
122      /* SUB ^Z */ K_UNBOUND,
123      /* ESC ^[ */ K_UNBOUND,
124      /* FS  ^\ */ K_UNBOUND,
125      /* GS  ^] */ K_UNBOUND,
126      /* RS  ^^ */ K_UNBOUND,
127      /* US  ^_ */ K_UNBOUND,
128      /* SP  */ K_CONTINUE,
129      /* !   */ K_SHELL,
130      /* "   */ K_UNBOUND,
131      /* #   */ K_UNBOUND,
132      /* $   */ K_LAST_PAGE,
133      /* %   */ K_PREVIEW,
134      /* &   */ K_UNBOUND,
135      /* '   */ K_UNBOUND,
136      /* (   */ K_UNBOUND,
137      /* )   */ K_UNBOUND,
138      /* *   */ K_SELECT_SUBJECT,
139      /* +   */ K_UNBOUND,
140      /* ,   */ K_UNBOUND,
141      /* -   */ K_UNBOUND,
142      /* .   */ K_NEXT_MATCH,
143      /* /   */ K_GOTO_MATCH,
144      /* 0   */ K_UNBOUND,
145      /* 1   */ K_UNBOUND,
146      /* 2   */ K_UNBOUND,
147      /* 3   */ K_UNBOUND,
148      /* 4   */ K_UNBOUND,
149      /* 5   */ K_UNBOUND,
150      /* 6   */ K_UNBOUND,
151      /* 7   */ K_UNBOUND,
152      /* 8   */ K_UNBOUND,
153      /* 9   */ K_UNBOUND,
154      /* :   */ K_EXTENDED_CMD,
155      /* ;   */ K_UNBOUND,
156      /* <   */ K_UNBOUND,
157      /* =   */ K_BACK_TO_MENU,
158      /* >   */ K_UNBOUND,
159      /* ?   */ K_HELP,
160      /* @   */ K_UNBOUND,
161      /* A   */ K_UNBOUND,
162      /* B   */ K_UNBOUND,
163      /* C   */ K_CANCEL,
164      /* D   */ K_ROT13,
165      /* E   */ K_SAVE_HEADER_ONLY,
166      /* F   */ K_FOLLOW_UP,
167      /* G   */ K_GOTO_GROUP,
168      /* H   */ K_FULL_DIGEST,
169      /* I   */ K_UNBOUND,
170      /* J   */ K_UNBOUND,
171      /* K   */ K_KILL_HANDLING,
172      /* L   */ K_LEAVE_NEXT,
173      /* M   */ K_MAIL_OR_FORWARD,
174      /* N   */ K_NEXT_GROUP_NO_UPDATE,
175      /* O   */ K_SAVE_SHORT_HEADER,
176      /* P   */ K_PRINT,
177      /* Q   */ K_QUIT,
178      /* R   */ K_REPLY,
179      /* S   */ K_SAVE_FULL_HEADER,
180      /* T   */ K_UNBOUND,
181      /* U   */ K_UNSUBSCRIBE,
182      /* V   */ K_VERSION,
183      /* W   */ K_SAVE_NO_HEADER,
184      /* X   */ K_READ_GROUP_UPDATE,
185      /* Y   */ K_GROUP_OVERVIEW,
186      /* Z   */ K_BACK_TO_MENU,
187      /* [   */ K_UNBOUND,
188      /* \   */ K_UNBOUND,
189      /* ]   */ K_UNBOUND,
190      /* ^   */ K_FIRST_PAGE,
191      /* _   */ K_UNBOUND,
192      /* `   */ K_UNBOUND,
193      /* a   */ K_FORW_ARTICLE,
194      /* b   */ K_BACK_ARTICLE,
195      /* c   */ K_COMPRESS,
196      /* d   */ K_NEXT_HALF_PAGE,
197      /* e   */ K_SAVE_HEADER_ONLY,
198      /* f   */ K_FOLLOW_UP,
199      /* g   */ K_GOTO_LINE,
200      /* h   */ K_HEADER_PAGE,
201      /* i   */ K_UNBOUND,
202      /* j   */ K_UNBOUND,
203      /* k   */ K_NEXT_SUBJECT,
204      /* l   */ K_LEAVE_ARTICLE,
205      /* m   */ K_MAIL_OR_FORWARD,
206      /* n   */ K_NEXT_ARTICLE,
207      /* o   */ K_SAVE_SHORT_HEADER,
208      /* p   */ K_PREVIOUS /* article */ ,
209      /* q   */ K_NEXT_GROUP_NO_UPDATE,
210      /* r   */ K_REPLY,
211      /* s   */ K_SAVE_FULL_HEADER,
212      /* t   */ K_FIRST_PAGE,
213      /* u   */ K_PREV_HALF_PAGE,
214      /* v   */ K_UNBOUND,
215      /* w   */ K_SAVE_NO_HEADER,
216      /* x   */ K_UNBOUND,
217      /* y   */ K_UNBOUND,
218      /* z   */ K_UNBOUND,
219      /* {   */ K_UNBOUND,
220      /* |   */ K_UNBOUND,
221      /* }   */ K_UNBOUND,
222      /* ~   */ K_UNBOUND,
223      /* DEL */ K_PREV_PAGE,
224      /* 200 */ K_UNBOUND,
225      /* up  */ K_PREV_PAGE,
226      /* down */ K_NEXT_PAGE,
227      /* left */ K_UNBOUND,
228      /* right */ K_UNBOUND,
229      /* #0  */ K_UNBOUND,
230      /* #1  */ K_UNBOUND,
231      /* #2  */ K_UNBOUND,
232      /* #3  */ K_UNBOUND,
233      /* #4  */ K_UNBOUND,
234      /* #5  */ K_UNBOUND,
235      /* #6  */ K_UNBOUND,
236      /* #7  */ K_UNBOUND,
237      /* #8  */ K_UNBOUND,
238      /* #9  */ K_UNBOUND,
239      /* mouse d1 */ K_M_CONTINUE,
240      /* mouse d2 */ K_NEXT_SUBJECT,
241      /* mouse d3 */ K_BACK_TO_MENU,
242      /* mouse u1 */ K_INVALID,
243      /* mouse u2 */ K_INVALID,
244      /* mouse u3 */ K_INVALID
245 };
246 
247 
248 
249 /*
250  * standard keyboard mappings for menu()
251  *
252  *	illegal command
253  *	redraw			^L, ^R
254  *	continue		space
255  *	continue no mark	return, newline
256  *	repeat message		^P
257  *	help			?
258  *	shell escape		!
259  *	version			V
260  *	alternative commands	:
261  *	quit			Q
262  *
263  *	save			S, O
264  *	save, no header		W
265  *	reply			R
266  *	follow up		F
267  *	mail (forward)		M
268  *	cancel			C
269  *	unsubscribe		U
270  *	group overview		Y
271  *	kill handling		K
272  *	junk articles		J
273  *
274  *	read, then next		X
275  *	read, then same		Z
276  *	no update, next		N
277  *	prev group		P
278  *	goto group		G
279  *	advance group		A
280  *	back group		B
281  *
282  *	article identifier	a-z 0-9
283  *	inverse			@
284  *	select current, next	.
285  *	next			, (down arrow)
286  *	prev			/ (up arrow)
287  *	select subject		*
288  *	range			-
289  *	auto select 		+
290  *
291  *	next page		>
292  *	prev page		<
293  *	first page		^
294  *	last page		$
295  *
296  *	pre-view article	%
297  *
298  *	layout			L
299  */
300 
301 
302 int             menu_key_map[KEY_MAP_SIZE] = {
303 
304      /* NUL ^@ */ K_UNBOUND,
305      /* SOH ^A */ K_UNBOUND,
306      /* STX ^B */ K_UNBOUND,
307      /* ETX ^C */ K_UNBOUND,
308      /* EOT ^D */ K_UNBOUND,
309      /* ENQ ^E */ K_UNBOUND,
310      /* ACK ^F */ K_UNBOUND,
311      /* BEL ^G */ K_INVALID,
312      /* BS  ^H */ K_PREV_LINE,
313      /* TAB ^I */ K_UNBOUND,
314      /* NL  ^J */ K_CONTINUE_NO_MARK,
315      /* VT  ^K */ K_UNBOUND,
316      /* FF  ^L */ K_REDRAW,
317      /* CR  ^M */ K_CONTINUE_NO_MARK,
318      /* SO  ^N */ K_UNBOUND,
319      /* SI  ^O */ K_UNBOUND,
320      /* DLE ^P */ K_LAST_MESSAGE,
321      /* DC1 ^Q */ K_UNBOUND,
322      /* DC2 ^R */ K_REDRAW,
323      /* DC3 ^S */ K_UNBOUND,
324      /* DC4 ^T */ K_UNBOUND,
325      /* NAK ^U */ K_UNBOUND,
326      /* SYN ^V */ K_NEXT_PAGE,
327      /* ETB ^W */ K_UNBOUND,
328      /* CAN ^X */ K_UNBOUND,
329      /* EM  ^Y */ K_UNBOUND,
330      /* SUB ^Z */ K_UNBOUND,
331      /* ESC ^[ */ K_UNBOUND,
332      /* FS  ^\ */ K_UNBOUND,
333      /* GS  ^] */ K_UNBOUND,
334      /* RS  ^^ */ K_UNBOUND,
335      /* US  ^_ */ K_UNBOUND,
336      /* SP  */ K_CONTINUE,
337      /* !   */ K_SHELL,
338      /* "   */ K_LAYOUT,
339      /* #   */ K_UNBOUND,
340      /* $   */ K_LAST_PAGE,
341      /* %   */ K_PREVIEW,
342      /* &   */ K_UNBOUND,
343      /* '   */ K_UNBOUND,
344      /* (   */ K_OPEN_SUBJECT,
345      /* )   */ K_CLOSE_SUBJECT,
346      /* *   */ K_SELECT_SUBJECT,
347      /* +   */ K_AUTO_SELECT,
348      /* ,   */ K_NEXT_LINE,
349      /* -   */ K_SELECT_RANGE,
350      /* .   */ K_SELECT,
351      /* /   */ K_PREV_LINE,
352      /* 0   */ K_ARTICLE_ID + 26,
353      /* 1   */ K_ARTICLE_ID + 27,
354      /* 2   */ K_ARTICLE_ID + 28,
355      /* 3   */ K_ARTICLE_ID + 29,
356      /* 4   */ K_ARTICLE_ID + 30,
357      /* 5   */ K_ARTICLE_ID + 31,
358      /* 6   */ K_ARTICLE_ID + 32,
359      /* 7   */ K_ARTICLE_ID + 33,
360      /* 8   */ K_ARTICLE_ID + 34,
361      /* 9   */ K_ARTICLE_ID + 35,
362      /* :   */ K_EXTENDED_CMD,
363      /* ;   */ K_UNBOUND,
364      /* <   */ K_PREV_PAGE,
365      /* =   */ K_GOTO_MATCH,
366      /* >   */ K_NEXT_PAGE,
367      /* ?   */ K_HELP,
368      /* @   */ K_SELECT_INVERT,
369      /* A   */ K_ADVANCE_GROUP,
370      /* B   */ K_BACK_GROUP,
371      /* C   */ K_CANCEL,
372      /* D   */ K_UNBOUND,
373      /* E   */ K_SAVE_HEADER_ONLY,
374      /* F   */ K_FOLLOW_UP,
375      /* G   */ K_GOTO_GROUP,
376      /* H   */ K_UNBOUND,
377      /* I   */ K_UNBOUND,
378      /* J   */ K_JUNK_ARTICLES,
379      /* K   */ K_KILL_HANDLING,
380      /* L   */ K_LEAVE_NEXT,
381      /* M   */ K_MAIL_OR_FORWARD,
382      /* N   */ K_NEXT_GROUP_NO_UPDATE,
383      /* O   */ K_SAVE_SHORT_HEADER,
384      /* P   */ K_PREVIOUS /* group */ ,
385      /* Q   */ K_QUIT,
386      /* R   */ K_REPLY,
387      /* S   */ K_SAVE_FULL_HEADER,
388      /* T   */ K_UNBOUND,
389      /* U   */ K_UNSUBSCRIBE,
390      /* V   */ K_VERSION,
391      /* W   */ K_SAVE_NO_HEADER,
392      /* X   */ K_READ_GROUP_UPDATE,
393      /* Y   */ K_GROUP_OVERVIEW,
394      /* Z   */ K_READ_GROUP_THEN_SAME,
395      /* [   */ K_UNBOUND,
396      /* \   */ K_UNBOUND,
397      /* ]   */ K_UNBOUND,
398      /* ^   */ K_FIRST_PAGE,
399      /* _   */ K_UNBOUND,
400      /* `   */ K_UNBOUND,
401      /* a   */ K_ARTICLE_ID + 0,
402      /* b   */ K_ARTICLE_ID + 1,
403      /* c   */ K_ARTICLE_ID + 2,
404      /* d   */ K_ARTICLE_ID + 3,
405      /* e   */ K_ARTICLE_ID + 4,
406      /* f   */ K_ARTICLE_ID + 5,
407      /* g   */ K_ARTICLE_ID + 6,
408      /* h   */ K_ARTICLE_ID + 7,
409      /* i   */ K_ARTICLE_ID + 8,
410      /* j   */ K_ARTICLE_ID + 9,
411      /* k   */ K_ARTICLE_ID + 10,
412      /* l   */ K_ARTICLE_ID + 11,
413      /* m   */ K_ARTICLE_ID + 12,
414      /* n   */ K_ARTICLE_ID + 13,
415      /* o   */ K_ARTICLE_ID + 14,
416      /* p   */ K_ARTICLE_ID + 15,
417      /* q   */ K_ARTICLE_ID + 16,
418      /* r   */ K_ARTICLE_ID + 17,
419      /* s   */ K_ARTICLE_ID + 18,
420      /* t   */ K_ARTICLE_ID + 19,
421      /* u   */ K_ARTICLE_ID + 20,
422      /* v   */ K_ARTICLE_ID + 21,
423      /* w   */ K_ARTICLE_ID + 22,
424      /* x   */ K_ARTICLE_ID + 23,
425      /* y   */ K_ARTICLE_ID + 24,
426      /* z   */ K_ARTICLE_ID + 25,
427      /* {   */ K_UNBOUND,
428      /* |   */ K_UNBOUND,
429      /* }   */ K_UNBOUND,
430      /* ~   */ K_UNSELECT_ALL,
431      /* DEL */ K_PREV_LINE,
432      /* 200 */ K_UNBOUND,
433      /* up  */ K_PREV_LINE,
434      /* down */ K_NEXT_LINE,
435      /* left */ K_UNBOUND,
436      /* right */ K_UNBOUND,
437      /* #0  */ K_UNBOUND,
438      /* #1  */ K_UNBOUND,
439      /* #2  */ K_UNBOUND,
440      /* #3  */ K_UNBOUND,
441      /* #4  */ K_UNBOUND,
442      /* #5  */ K_UNBOUND,
443      /* #6  */ K_UNBOUND,
444      /* #7  */ K_UNBOUND,
445      /* #8  */ K_UNBOUND,
446      /* #9  */ K_UNBOUND,
447      /* mouse d1 */ K_M_SELECT,
448      /* mouse d2 */ K_M_PREVIEW,
449      /* mouse d3 */ K_NEXT_GROUP_NO_UPDATE,
450      /* mouse u1 */ K_M_SELECT_RANGE,
451      /* mouse u2 */ K_INVALID,
452      /* mouse u3 */ K_INVALID
453 };
454 
455 int             orig_menu_map[KEY_MAP_SIZE];	/* initially empty */
456 
457 
458 static struct command_name_map {
459     char           *cmd_name;
460     int             cmd_code;
461     int             cmd_restriction;
462 }               command_name_map[] = {
463 
464     "advance-article", K_FORW_ARTICLE, K_ONLY_MORE,
465     "advance-group", K_ADVANCE_GROUP, 0,
466     "article", K_ARTICLE_ID, K_ONLY_MENU,
467     "as", K_EQUAL_KEY, 0,
468 
469     "back-article", K_BACK_ARTICLE, K_ONLY_MORE,
470     "back-group", K_BACK_GROUP, 0,
471 
472     "cancel", K_CANCEL, 0,
473     "close-subject", K_CLOSE_SUBJECT, K_ONLY_MENU,
474     "command", K_EXTENDED_CMD, 0,
475     "compress", K_COMPRESS, K_ONLY_MORE,
476     "continue", K_CONTINUE, 0,
477     "continue-no-mark", K_CONTINUE_NO_MARK, K_ONLY_MENU,
478 
479     "decode", K_UUDECODE, 0,
480 
481     "find", K_GOTO_MATCH, 0,
482     "find-next", K_NEXT_MATCH, K_ONLY_MORE,
483     "follow", K_FOLLOW_UP, 0,
484     "full-digest", K_FULL_DIGEST, K_ONLY_MORE,
485 
486     "goto-group", K_GOTO_GROUP, 0,
487     "goto-menu", K_BACK_TO_MENU, K_ONLY_MORE,
488 
489     "help", K_HELP, 0,
490 
491     "junk-articles", K_JUNK_ARTICLES, K_ONLY_MENU,
492 
493     "kill-select", K_KILL_HANDLING, 0,
494 
495     "layout", K_LAYOUT, K_ONLY_MENU,
496     "leave-article", K_LEAVE_ARTICLE, K_ONLY_MORE,
497     "leave-next", K_LEAVE_NEXT, K_ONLY_MORE,
498     "line+1", K_NEXT_LINE, 0,
499     "line-1", K_PREV_LINE, K_ONLY_MENU,
500     "line=@", K_GOTO_LINE, K_ONLY_MORE,
501 
502     "macro", K_MACRO, 0,
503     "mail", K_MAIL_OR_FORWARD, 0,
504     "message", K_LAST_MESSAGE, 0,
505     "mouse-continue", K_M_CONTINUE, K_ONLY_MORE,
506     "mouse-preview", K_M_PREVIEW, K_ONLY_MENU,
507     "mouse-select", K_M_SELECT, K_ONLY_MENU,
508     "mouse-select-range", K_M_SELECT_RANGE, K_ONLY_MENU,
509     "mouse-select-subject", K_M_SELECT_SUBJECT, K_ONLY_MENU,
510     "mouse-toggle", K_M_TOGGLE, 0,
511 
512     "next-article", K_NEXT_ARTICLE, K_ONLY_MORE,
513     "next-group", K_NEXT_GROUP_NO_UPDATE, 0,
514     "next-subject", K_NEXT_SUBJECT, K_ONLY_MORE,
515     "nil", K_UNBOUND, 0,
516 
517     "open-subject", K_OPEN_SUBJECT, K_ONLY_MENU,
518     "overview", K_GROUP_OVERVIEW, 0,
519 
520     "page+1", K_NEXT_PAGE, 0,
521     "page+1/2", K_NEXT_HALF_PAGE, K_ONLY_MORE,
522     "page-1", K_PREV_PAGE, 0,
523     "page-1/2", K_PREV_HALF_PAGE, K_ONLY_MORE,
524     "page=$", K_LAST_PAGE, 0,
525     "page=0", K_HEADER_PAGE, 0,
526     "page=1", K_FIRST_PAGE, 0,
527     "page=@", K_GOTO_PAGE, K_ONLY_MORE,
528 
529     "patch", K_PATCH, 0,
530     "post", K_POST, 0,
531     "prefix", K_PREFIX_KEY, 0,
532     "preview", K_PREVIEW, 0,
533     "previous", K_PREVIOUS, 0,
534     "print", K_PRINT, 0,
535 
536     "quit", K_QUIT, 0,
537 
538     "read-return", K_READ_GROUP_THEN_SAME, K_ONLY_MENU,
539     "read-skip", K_READ_GROUP_UPDATE, 0,
540     "redraw", K_REDRAW, 0,
541     "reply", K_REPLY, 0,
542     "rot13", K_ROT13, K_ONLY_MORE,
543 
544     "save-body", K_SAVE_NO_HEADER, 0,
545     "save-full", K_SAVE_FULL_HEADER, 0,
546     "save-short", K_SAVE_SHORT_HEADER, 0,
547     "save-hdr-only", K_SAVE_HEADER_ONLY, 0,
548     "select", K_SELECT, K_ONLY_MENU,
549     "select-auto", K_AUTO_SELECT, K_ONLY_MENU,
550     "select-invert", K_SELECT_INVERT, K_ONLY_MENU,
551     "select-range", K_SELECT_RANGE, K_ONLY_MENU,
552     "select-subject", K_SELECT_SUBJECT, 0,
553     "shell", K_SHELL, 0,
554     "skip-lines", K_SKIP_LINES, 0,
555 
556     "unselect-all", K_UNSELECT_ALL, K_ONLY_MENU,
557     "unshar", K_UNSHAR, 0,
558     "unsub", K_UNSUBSCRIBE, 0,
559 
560     "version", K_VERSION, 0,
561 
562     (char *) NULL, 0, 0
563 };
564 
565 static int      name_map_size;
566 #define max_cmd_name_length 16	/* recalculate if table is changed */
567 
568 key_type        global_key_map[KEY_MAP_SIZE];
569 
570 
571 void
init_key_map(void)572 init_key_map(void)
573 {
574     register int    c;
575     register struct command_name_map *cnmp;
576 
577     for (c = 0; c < KEY_MAP_SIZE; c++)
578 	global_key_map[c] = c;
579     for (c = NORMAL_KEYS + ARROW_KEYS + MULTI_KEYS; c < KEY_MAP_SIZE; c++) {
580 	menu_key_map[c] = K_UNBOUND;
581 	more_key_map[c] = K_UNBOUND;
582     }
583 
584     for (cnmp = command_name_map; cnmp->cmd_name; cnmp++);
585     name_map_size = cnmp - command_name_map;
586 }
587 
588 
589 int
lookup_command(char * command,int restriction)590 lookup_command(char *command, int restriction)
591 {
592     register struct command_name_map *cnmp;
593     register int    i, j, k, t;
594 
595     i = 0;
596     j = name_map_size - 1;
597 
598     while (i <= j) {
599 	k = (i + j) / 2;
600 	cnmp = &command_name_map[k];
601 
602 	if ((t = strcmp(command, cnmp->cmd_name)) > 0)
603 	    i = k + 1;
604 	else if (t < 0)
605 	    j = k - 1;
606 	else {
607 	    switch (restriction) {
608 		case K_ONLY_MENU:
609 		case K_ONLY_MORE:
610 		    if (cnmp->cmd_restriction == restriction)
611 			break;
612 		    /* FALLTHRU */
613 		case 0:
614 		    if (cnmp->cmd_restriction == 0)
615 			break;
616 		    return K_INVALID - 1;
617 		default:
618 		    break;
619 	    }
620 	    return cnmp->cmd_code;
621 	}
622     }
623 
624     return K_INVALID;
625 }
626 
627 
628 int
cmd_completion(char * path,int index)629 cmd_completion(char *path, int index)
630 {
631     static char    *head, *tail = NULL;
632     static int      len;
633     static struct command_name_map *cmd, *help_cmd;
634 
635     if (index < 0)
636 	return 0;
637 
638     if (path) {
639 	head = path;
640 	tail = path + index;
641 	while (*head && isspace(*head))
642 	    head++;
643 	help_cmd = cmd = command_name_map;
644 	len = tail - head;
645 
646 	return 1;
647     }
648     if (index) {
649 	list_completion((char *) NULL);
650 
651 	if (help_cmd->cmd_name == NULL)
652 	    help_cmd = command_name_map;
653 
654 	for (; help_cmd->cmd_name; help_cmd++) {
655 	    index = strncmp(help_cmd->cmd_name, head, len);
656 	    if (index < 0)
657 		continue;
658 	    if (index > 0) {
659 		help_cmd = command_name_map;
660 		break;
661 	    }
662 	    if (list_completion(help_cmd->cmd_name) == 0)
663 		break;
664 	}
665 	fl;
666 	return 1;
667     }
668     for (; cmd->cmd_name; cmd++) {
669 	if (len == 0)
670 	    index = 0;
671 	else
672 	    index = strncmp(cmd->cmd_name, head, len);
673 	if (index < 0)
674 	    continue;
675 	if (index > 0)
676 	    break;
677 	if (cmd->cmd_code == K_MACRO ||
678 	    cmd->cmd_code == K_ARTICLE_ID ||
679 	    cmd->cmd_code == K_PREFIX_KEY ||
680 	    cmd->cmd_code == K_EQUAL_KEY)
681 	    sprintf(tail, "%s ", cmd->cmd_name + len);
682 	else
683 	    strcpy(tail, cmd->cmd_name + len);
684 	cmd++;
685 	return 1;
686     }
687     return 0;
688 }
689 
690 
691 char           *
command_name(int cmd)692 command_name(int cmd)
693 {
694     register struct command_name_map *cnmp;
695 
696     cmd &= ~GETC_COMMAND;
697 
698     for (cnmp = command_name_map; cnmp->cmd_name; cnmp++)
699 	if (cnmp->cmd_code == cmd)
700 	    return cnmp->cmd_name;
701 
702     return "unknown";
703 }
704 
705 
706 /*
707  * convert key name into ascii code
708  *
709  *	key names are:
710  *		c	character c
711  *		^C	control-C
712  *		0xNN	hex value (0..0x7f)
713  *		0NNN	octal value (0..0177)
714  *		NNN	decimal value (0..127)
715  *		up, down, left, rigth	arrow keys
716  *		#0..#9			function keys (initially undefined)
717  */
718 
719 key_type
parse_key(char * str)720 parse_key(char *str)
721 {
722     int             x;
723 
724     if (str[1] == NUL)
725 	return (data_bits < 8) ? (str[0] & 0177) : str[0];
726 
727     if (str[0] == '^') {
728 	if (str[1] == '?')
729 	    return 0177;
730 	else
731 	    return CONTROL_(str[1]);
732     }
733 
734     if (isdigit(str[0])) {
735 	if (str[0] == '0') {
736 	    if (str[1] == 'x')
737 		sscanf(str + 2, "%d", &x);
738 	    else
739 		sscanf(str + 1, "%d", &x);
740 	} else {
741 	    sscanf(str, "%d", &x);
742 	}
743 
744 	return x;
745     }
746     if (str[0] == '#' && isdigit(str[1]))
747 	return K_function(str[1] - '0');
748 
749     if (str[0] == '#')
750 	str++;
751 
752     if (strcmp(str, "up") == 0)
753 	return K_up_arrow;
754 
755     if (strcmp(str, "down") == 0)
756 	return K_down_arrow;
757 
758     if (strcmp(str, "left") == 0)
759 	return K_left_arrow;
760 
761     if (strcmp(str, "right") == 0)
762 	return K_right_arrow;
763 
764     if (strcmp(str, "mouse_d1") == 0)	/* thp */
765 	return K_m_d1;
766     if (strcmp(str, "mouse_d2") == 0)	/* thp */
767 	return K_m_d2;
768     if (strcmp(str, "mouse_d3") == 0)	/* thp */
769 	return K_m_d3;
770     if (strcmp(str, "mouse_u1") == 0)	/* thp */
771 	return K_m_u1;
772     if (strcmp(str, "mouse_u2") == 0)	/* thp */
773 	return K_m_u2;
774     if (strcmp(str, "mouse_u3") == 0)	/* thp */
775 	return K_m_u3;
776 
777     init_message("unknown key: %s", str);
778 
779     return 0200;
780 }
781 
782 char           *
key_name(key_type c)783 key_name(key_type c)
784 {
785     static char     buf[10];
786 
787     if (c >= NORMAL_KEYS && c <= (key_type) (KEY_MAP_SIZE - NATIONAL_KEYS)) {
788 	switch (c) {
789 	    case K_up_arrow:
790 		return "up";
791 	    case K_down_arrow:
792 		return "down";
793 	    case K_left_arrow:
794 		return "left";
795 	    case K_right_arrow:
796 		return "right";
797 	    case K_m_d1:
798 		return "mouse_d1";
799 	    case K_m_d2:
800 		return "mouse_d2";
801 	    case K_m_d3:
802 		return "mouse_d3";
803 	    case K_m_u1:
804 		return "mouse_u1";
805 	    case K_m_u2:
806 		return "mouse_u2";
807 	    case K_m_u3:
808 		return "mouse_u3";
809 	    default:
810 		buf[0] = '#';
811 		buf[1] = (c - K_function(0))
812 		    + (c >= (key_type) K_function(MULTI_KEYS) ? 'A' - K_function(MULTI_KEYS) : '0');
813 		buf[2] = NUL;
814 		goto out;
815 	}
816     }
817     if (c == SP)
818 	return "space";
819 
820     if (c < SP) {
821 	buf[0] = '^';
822 	buf[1] = c + '@';
823 	buf[2] = NUL;
824 	goto out;
825     }
826     if (c == 0177) {
827 	strcpy(buf, "^?");
828 	goto out;
829     }
830     if (data_bits < 8 && c >= NORMAL_KEYS) {
831 	sprintf(buf, "0x%02x", (uint)c);
832 	goto out;
833     }
834     buf[0] = c;
835     buf[1] = NUL;
836 
837 out:
838     return buf;
839 }
840 
841 
842 void
dump_global_map(void)843 dump_global_map(void)
844 {
845     register key_type c;
846 
847     clrdisp();
848     so_printf("\1REMAPPED KEYS\1\n\n");
849     pg_init(2, 4);
850 
851     for (c = 0; c < KEY_MAP_SIZE; c++)
852 	if (c != global_key_map[c]) {
853 	    if (pg_next() < 0)
854 		break;
855 	    tprintf("%s", key_name(c));
856 	    pg_indent(6);
857 	    tprintf("-> %s", key_name(global_key_map[c]));
858 	}
859     pg_end();
860 }
861 
862 int
dump_key_map(char * where)863 dump_key_map(char *where)
864 {
865     register struct command_name_map *cnmp;
866     register key_type c;
867     register int    code, first_prt;
868     int            *map, restriction;
869 
870     if ((code = lookup_keymap(where)) < 0)
871 	return -1;
872     map = keymaps[code].km_map;
873     restriction = keymaps[code].km_flag & (K_ONLY_MENU | K_ONLY_MORE);
874 
875     clrdisp();
876     so_printf("\1KEY BINDINGS (%s)\1\n\n", where);
877 
878     if (restriction == K_ONLY_MENU) {
879 	tprintf("\rarticle:  ");
880 	for (c = 0; c < KEY_MAP_SIZE; c++)
881 	    if (map[c] & K_ARTICLE_ID)
882 		tprintf("%s", key_name(c));
883     }
884     pg_init(4, 2);
885 
886     for (cnmp = command_name_map; cnmp->cmd_name; cnmp++) {
887 	if (cnmp->cmd_restriction && cnmp->cmd_restriction != restriction)
888 	    continue;
889 	if (cnmp->cmd_code == K_UNBOUND)
890 	    continue;
891 	if (cnmp->cmd_code == K_MACRO)
892 	    continue;
893 
894 	code = cnmp->cmd_code;
895 	first_prt = 1;
896 
897 	for (c = 0; c < KEY_MAP_SIZE; c++)
898 	    if (map[c] == code) {
899 		if (first_prt) {
900 		    if (pg_next() < 0)
901 			goto out;
902 		    tprintf("%s", cnmp->cmd_name);
903 		    pg_indent(max_cmd_name_length);
904 		    first_prt = 0;
905 		}
906 		tprintf(" %s", key_name(c));
907 	    }
908     }
909 
910     for (c = 0; c < KEY_MAP_SIZE; c++)
911 	if (map[c] & K_MACRO) {
912 	    if (pg_next() < 0)
913 		goto out;
914 	    tprintf("macro %d: %s", (map[c] & ~K_MACRO), key_name(c));
915 	    if (map == menu_key_map && orig_menu_map[c] != K_UNBOUND)
916 		tprintf(" (%s)", command_name(orig_menu_map[c]));
917 	} else if (map[c] & K_PREFIX_KEY) {
918 	    if (pg_next() < 0)
919 		goto out;
920 	    tprintf("prefix %s: %s",
921 		    keymaps[(map[c] & ~K_PREFIX_KEY)].km_name, key_name(c));
922 	}
923 out:
924     pg_end();
925     return 0;
926 }
927 
928 #define MAX_KEYMAPS	17
929 
930 struct key_map_def keymaps[MAX_KEYMAPS + 1] = {
931     "#", NULL, K_MULTI_KEY_MAP,
932     "key", NULL, K_GLOBAL_KEY_MAP,
933     "menu", menu_key_map, K_BIND_ORIG | K_ONLY_MENU,
934     "show", more_key_map, K_ONLY_MORE,
935     "both", menu_key_map, K_BOTH_MAPS | K_BIND_ORIG,
936     "more", more_key_map, K_ONLY_MORE,
937     "read", more_key_map, K_ONLY_MORE,
938     NULL,
939 };
940 
941 int
lookup_keymap(char * name)942 lookup_keymap(char *name)
943 {
944     register struct key_map_def *m;
945 
946     if (name[0] == '#')
947 	return 0;
948 
949     for (m = keymaps; m->km_name; m++) {
950 	if (strcmp(name, m->km_name) == 0)
951 	    return m - keymaps;
952     }
953     return keymaps - m;
954 }
955 
956 int
make_keymap(char * name)957 make_keymap(char *name)
958 {
959     register struct key_map_def *m;
960     register int   *kp;
961     int             ix;
962 
963     ix = lookup_keymap(name);
964     if (ix >= 0)
965 	return -1;
966     ix = -ix;
967     if (ix == MAX_KEYMAPS)
968 	return -2;
969 
970     m = &keymaps[ix];
971     m->km_name = copy_str(name);
972     m->km_map = newobj(int, KEY_MAP_SIZE);
973     m->km_flag = K_ONLY_MORE | K_ONLY_MENU;
974     for (kp = m->km_map; kp < &(m->km_map)[KEY_MAP_SIZE];)
975 	*kp++ = K_UNBOUND;
976 
977     return ix;
978 }
979 
980 int
keymap_completion(char * buf,int ix)981 keymap_completion(char *buf, int ix)
982 {
983     static char    *head, *tail = NULL;
984     static int      len;
985     static struct key_map_def *map, *help_map;
986 
987     if (ix < 0)
988 	return 0;
989 
990     if (buf) {
991 	head = buf;
992 	tail = buf + ix;
993 	while (*head && isspace(*head))
994 	    head++;
995 	help_map = map = keymaps;
996 	len = tail - head;
997 
998 	return 1;
999     }
1000     if (ix) {
1001 	list_completion((char *) NULL);
1002 
1003 	if (help_map->km_name == NULL)
1004 	    help_map = keymaps;
1005 
1006 	for (; help_map->km_name; help_map++) {
1007 	    if (strncmp(help_map->km_name, head, len))
1008 		continue;
1009 	    if (list_completion(help_map->km_name) == 0)
1010 		break;
1011 	}
1012 	fl;
1013 	return 1;
1014     }
1015     for (; map->km_name; map++) {
1016 	if (len && strncmp(map->km_name, head, len))
1017 	    continue;
1018 	strcpy(tail, map->km_name + len);
1019 	if (map != keymaps)
1020 	    strcat(tail, " ");
1021 	map++;
1022 	return 1;
1023     }
1024     return 0;
1025 }
1026