xtermcap.c revision 2eaa94a1
1/* $XTermId: xtermcap.c,v 1.14 2008/10/05 16:43:36 tom Exp $ */
2
3/*
4 * Copyright 2007,2008 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33#include <xtermcap.h>
34
35#include <X11/keysym.h>
36
37#ifdef VMS
38#include <X11/keysymdef.h>
39#endif
40
41#include <xstrings.h>
42
43#ifndef HAVE_TIGETSTR
44#undef USE_TERMINFO
45#endif
46
47#ifndef USE_TERMINFO
48#define USE_TERMINFO 0
49#endif
50
51#if USE_TERMINFO && defined(NCURSES_VERSION) && defined(HAVE_USE_EXTENDED_NAMES)
52#define USE_EXTENDED_NAMES 1
53#else
54#define USE_EXTENDED_NAMES 0
55#endif
56
57#if OPT_TCAP_QUERY || OPT_TCAP_FKEYS
58
59typedef struct {
60    char *tc;
61    char *ti;
62    int code;
63    unsigned state;
64} TCAPINFO;
65/* *INDENT-OFF* */
66#define DATA(tc,ti,x,y) { tc, ti, x, y }
67static TCAPINFO table[] = {
68	/*	tcap	terminfo	code		param */
69	DATA(	"%1",	"khlp",		XK_Help,	0	),
70	DATA(	"#1",	"kHLP",		XK_Help,	2	),
71	DATA(	"@0",	"kfnd",		XK_Find,	0	),
72	DATA(	"*0",	"kFND",		XK_Find,	2	),
73	DATA(	"*6",	"kslt",		XK_Select,	0	),
74	DATA(	"#6",	"kSLT",		XK_Select,	2	),
75
76	DATA(	"kh",	"khome",	XK_Home,	0	),
77	DATA(	"#2",	"kHOM",		XK_Home,	2	),
78	DATA(	"@7",	"kend",		XK_End,		0	),
79	DATA(	"*7",	"kEND",		XK_End,		2	),
80
81	DATA(	"kl",	"kcub1",	XK_Left,	0	),
82	DATA(	"kr",	"kcuf1",	XK_Right,	0	),
83	DATA(	"ku",	"kcuu1",	XK_Up,		0	),
84	DATA(	"kd",	"kcud1",	XK_Down,	0	),
85
86	DATA(	"#4",	"kLFT",		XK_Left,	2	),
87	DATA(	"%i",	"kRIT",		XK_Right,	2	),
88	DATA(	"%e",	"kPRV",		XK_Up,		2	),
89	DATA(	"%c",	"kNXT",		XK_Down,	2	),
90
91	DATA(	"k1",	"kf1",		XK_Fn(1),	0	),
92	DATA(	"k2",	"kf2",		XK_Fn(2),	0	),
93	DATA(	"k3",	"kf3",		XK_Fn(3),	0	),
94	DATA(	"k4",	"kf4",		XK_Fn(4),	0	),
95	DATA(	"k5",	"kf5",		XK_Fn(5),	0	),
96	DATA(	"k6",	"kf6",		XK_Fn(6),	0	),
97	DATA(	"k7",	"kf7",		XK_Fn(7),	0	),
98	DATA(	"k8",	"kf8",		XK_Fn(8),	0	),
99	DATA(	"k9",	"kf9",		XK_Fn(9),	0	),
100	DATA(	"k;",	"kf10",		XK_Fn(10),	0	),
101
102	DATA(	"F1",	"kf11",		XK_Fn(11),	0	),
103	DATA(	"F2",	"kf12",		XK_Fn(12),	0	),
104	DATA(	"F3",	"kf13",		XK_Fn(13),	0	),
105	DATA(	"F4",	"kf14",		XK_Fn(14),	0	),
106	DATA(	"F5",	"kf15",		XK_Fn(15),	0	),
107	DATA(	"F6",	"kf16",		XK_Fn(16),	0	),
108	DATA(	"F7",	"kf17",		XK_Fn(17),	0	),
109	DATA(	"F8",	"kf18",		XK_Fn(18),	0	),
110	DATA(	"F9",	"kf19",		XK_Fn(19),	0	),
111	DATA(	"FA",	"kf20",		XK_Fn(20),	0	),
112	DATA(	"FB",	"kf21",		XK_Fn(21),	0	),
113	DATA(	"FC",	"kf22",		XK_Fn(22),	0	),
114	DATA(	"FD",	"kf23",		XK_Fn(23),	0	),
115	DATA(	"FE",	"kf24",		XK_Fn(24),	0	),
116	DATA(	"FF",	"kf25",		XK_Fn(25),	0	),
117	DATA(	"FG",	"kf26",		XK_Fn(26),	0	),
118	DATA(	"FH",	"kf27",		XK_Fn(27),	0	),
119	DATA(	"FI",	"kf28",		XK_Fn(28),	0	),
120	DATA(	"FJ",	"kf29",		XK_Fn(29),	0	),
121	DATA(	"FK",	"kf30",		XK_Fn(30),	0	),
122	DATA(	"FL",	"kf31",		XK_Fn(31),	0	),
123	DATA(	"FM",	"kf32",		XK_Fn(32),	0	),
124	DATA(	"FN",	"kf33",		XK_Fn(33),	0	),
125	DATA(	"FO",	"kf34",		XK_Fn(34),	0	),
126	DATA(	"FP",	"kf35",		XK_Fn(35),	0	),
127
128	DATA(	"FQ",	"kf36",		-36,		0	),
129	DATA(	"FR",	"kf37",		-37,		0	),
130	DATA(	"FS",	"kf38",		-38,		0	),
131	DATA(	"FT",	"kf39",		-39,		0	),
132	DATA(	"FU",	"kf40",		-40,		0	),
133	DATA(	"FV",	"kf41",		-41,		0	),
134	DATA(	"FW",	"kf42",		-42,		0	),
135	DATA(	"FX",	"kf43",		-43,		0	),
136	DATA(	"FY",	"kf44",		-44,		0	),
137	DATA(	"FZ",	"kf45",		-45,		0	),
138	DATA(	"Fa",	"kf46",		-46,		0	),
139	DATA(	"Fb",	"kf47",		-47,		0	),
140	DATA(	"Fc",	"kf48",		-48,		0	),
141	DATA(	"Fd",	"kf49",		-49,		0	),
142	DATA(	"Fe",	"kf50",		-50,		0	),
143	DATA(	"Ff",	"kf51",		-51,		0	),
144	DATA(	"Fg",	"kf52",		-52,		0	),
145	DATA(	"Fh",	"kf53",		-53,		0	),
146	DATA(	"Fi",	"kf54",		-54,		0	),
147	DATA(	"Fj",	"kf55",		-55,		0	),
148	DATA(	"Fk",	"kf56",		-56,		0	),
149	DATA(	"Fl",	"kf57",		-57,		0	),
150	DATA(	"Fm",	"kf58",		-58,		0	),
151	DATA(	"Fn",	"kf59",		-59,		0	),
152	DATA(	"Fo",	"kf60",		-60,		0	),
153	DATA(	"Fp",	"kf61",		-61,		0	),
154	DATA(	"Fq",	"kf62",		-62,		0	),
155	DATA(	"Fr",	"kf63",		-63,		0	),
156
157	DATA(	"K1",	"ka1",		XK_KP_Home,	0	),
158	DATA(	"K4",	"kc1",		XK_KP_End,	0	),
159	DATA(	"K3",	"ka3",		XK_KP_Prior,	0	),
160	DATA(	"K5",	"kc3",		XK_KP_Next,	0	),
161
162#ifdef XK_ISO_Left_Tab
163	DATA(	"kB",	"kcbt",		XK_ISO_Left_Tab, 0	),
164#endif
165	DATA(	"kC",	"kclr",		XK_Clear,	0	),
166	DATA(	"kD",	"kdch1",	XK_Delete,	0	),
167	DATA(	"kI",	"kich1",	XK_Insert,	0	),
168	DATA(	"kN",	"knp",		XK_Next,	0	),
169	DATA(	"kP",	"kpp",		XK_Prior,	0	),
170	DATA(	"&8",	"kund",		XK_Undo,	0	),
171	DATA(	"kb",	"kbs",		XK_BackSpace,	0	),
172# if OPT_TCAP_QUERY && OPT_ISO_COLORS
173	/* XK_COLORS is a fake code. */
174	DATA(	"Co",	"colors",	XK_COLORS,	0	),
175# endif
176#if USE_EXTENDED_NAMES
177#define DEXT(name, parm, code) DATA("", name, code, parm)
178#define D1ST(name, parm, code) DEXT("k" #name, parm, code)
179#define DMOD(name, parm, code) DEXT("k" #name #parm, parm, code)
180
181#define DGRP(name, code) \
182	D1ST(name, 2, code), \
183	DMOD(name, 3, code), \
184	DMOD(name, 4, code), \
185	DMOD(name, 5, code), \
186	DMOD(name, 6, code), \
187	DMOD(name, 7, code), \
188	DMOD(name, 8, code)
189
190	DGRP(DN,   XK_Down),
191	DGRP(LFT,  XK_Left),
192	DGRP(RIT,  XK_Right),
193	DGRP(UP,   XK_Up),
194	DGRP(DC,   XK_Delete),
195	DGRP(END,  XK_End),
196	DGRP(HOM,  XK_Home),
197	DGRP(IC,   XK_Insert),
198	DGRP(NXT,  XK_Next),
199	DGRP(PRV,  XK_Prior),
200#endif
201};
202#undef DATA
203/* *INDENT-ON* */
204
205#if OPT_TCAP_QUERY
206static int
207hex2int(int c)
208{
209    if (c >= '0' && c <= '9')
210	return c - '0';
211    if (c >= 'a' && c <= 'f')
212	return c - 'a' + 10;
213    if (c >= 'A' && c <= 'F')
214	return c - 'A' + 10;
215    return -1;
216}
217
218static TCAPINFO *
219lookupTcapByName(XtermWidget xw, const char *name)
220{
221    TCAPINFO *result = 0;
222    Cardinal n;
223
224    if (name != 0 && *name != '\0') {
225	for (n = 0; n < XtNumber(table); n++) {
226	    if (!strcmp(table[n].ti, name) || !strcmp(table[n].tc, name)) {
227		result = table + n;
228		break;
229	    }
230	}
231    }
232
233    /*
234     * The vt220-keyboard will not return distinct key sequences for shifted
235     * cursor-keys.  Just pretend they do not exist, since some programs may
236     * be confused if we return the same data for shifted/unshifted keys.
237     */
238    if (xw->keyboard.type == keyboardIsVT220
239	&& result != 0
240	&& result->state == 2) {
241	result = 0;
242    }
243
244    if (result != 0) {
245	TRACE(("lookupTcapByName(%s) tc=%s, ti=%s code %#x, state %#x\n",
246	       name, result->tc, result->ti, result->code, result->state));
247    } else {
248	TRACE(("lookupTcapByName(%s) FAIL\n", name));
249    }
250    return result;
251}
252
253/*
254 * Parse the termcap/terminfo name from the string, returning a positive number
255 * (the keysym) if found, otherwise -1.  Update the string pointer.
256 * Returns the (shift, control) state in *state.
257 *
258 * This does not attempt to construct control/shift modifiers to construct
259 * function-key values.  Instead, it sets the *fkey flag to pass to Input()
260 * and bypass the lookup of keysym altogether.
261 */
262int
263xtermcapKeycode(XtermWidget xw, char **params, unsigned *state, Bool * fkey)
264{
265    TCAPINFO *data;
266    unsigned len = 0;
267    int code = -1;
268#define MAX_TNAME_LEN 6
269    char name[MAX_TNAME_LEN + 1];
270    char *p;
271
272    TRACE(("xtermcapKeycode(%s)\n", *params));
273
274    /* Convert hex encoded name to ascii */
275    for (p = *params; hex2int(p[0]) >= 0 && hex2int(p[1]) >= 0; p += 2) {
276	if (len >= MAX_TNAME_LEN)
277	    break;
278	name[len++] = (hex2int(p[0]) << 4) + hex2int(p[1]);
279    }
280    name[len] = 0;
281    *params = p;
282
283    *state = 0;
284    *fkey = False;
285
286    if (*p == 0 || *p == ';') {
287	if ((data = lookupTcapByName(xw, name)) != 0) {
288	    code = data->code;
289	    *state = xtermParamToState(xw, data->state);
290	    if (IsFunctionKey(code)) {
291		*fkey = True;
292	    } else if (code < 0) {
293		*fkey = True;
294		code = XK_Fn((-code));
295	    }
296#if OPT_SUN_FUNC_KEYS
297	    if (*fkey && xw->keyboard.type == keyboardIsSun) {
298		int num = code - XK_Fn(0);
299
300		/* match function-key case in sunfuncvalue() */
301		if (num > 20) {
302		    if (num <= 30 || num > 47) {
303			code = -1;
304		    } else {
305			code -= 10;
306			switch (num) {
307			case 37:	/* khome */
308			case 39:	/* kpp */
309			case 41:	/* kb2 */
310			case 43:	/* kend */
311			case 45:	/* knp */
312			    code = -1;
313			    break;
314			}
315		    }
316		}
317	    }
318#endif
319	}
320    }
321
322    TRACE(("... xtermcapKeycode(%s, %u, %d) -> %#06x\n",
323	   name, *state, *fkey, code));
324    return code;
325}
326#endif /* OPT_TCAP_QUERY */
327
328#if OPT_TCAP_FKEYS
329static TCAPINFO *
330lookupTcapByCode(int code, unsigned mask)
331{
332    TCAPINFO *result = 0;
333    Cardinal n;
334
335    TRACE(("lookupTcapByCode %d:%#x\n", code, mask));
336    for (n = 0; n < XtNumber(table); n++) {
337	if (table[n].code == code &&
338	    table[n].state == mask) {
339	    TRACE(("lookupTcapByCode %d:%s\n", n, table[n].ti));
340	    result = table + n;
341	    break;
342	}
343    }
344    return result;
345}
346
347#define NO_STRING (char *)(-1)
348
349int
350xtermcapString(XtermWidget xw, int keycode, unsigned mask)
351{
352    int result = 0;
353    TCAPINFO *data;
354    unsigned param = xtermStateToParam(xw, mask);
355
356    if ((data = lookupTcapByCode(keycode, param)) != 0) {
357	TScreen *screen = TScreenOf(xw);
358	Cardinal which = data - table;
359	char *fkey;
360
361	if (screen->tcap_fkeys == 0) {
362	    Cardinal want = XtNumber(table);
363	    Cardinal have;
364#if !(USE_TERMINFO && defined(HAVE_TIGETSTR))
365	    char *area = screen->tcap_area;
366#endif
367
368	    if ((screen->tcap_fkeys = TypeCallocN(char *, want)) != 0) {
369		for (have = 0; have < want; ++have) {
370#if USE_TERMINFO && defined(HAVE_TIGETSTR)
371		    fkey = tigetstr(table[have].ti);
372#else
373		    fkey = tgetstr(table[have].tc, &area);
374#endif
375		    if (fkey != 0 && fkey != NO_STRING) {
376			screen->tcap_fkeys[have] = x_strdup(fkey);
377		    } else {
378			screen->tcap_fkeys[have] = NO_STRING;
379		    }
380		}
381	    }
382	}
383	if (screen->tcap_fkeys != 0) {
384	    if ((fkey = screen->tcap_fkeys[which]) != NO_STRING) {
385		StringInput(xw, (Char *) fkey, strlen(fkey));
386		result = 1;
387	    }
388	}
389    }
390
391    TRACE(("xtermcapString(keycode=%#x, mask=%#x) ->%d\n",
392	   keycode, mask, result));
393
394    return result;
395}
396#endif /* OPT_TCAP_FKEYS */
397
398#endif /* OPT_TCAP_QUERY || OPT_TCAP_FKEYS */
399/*
400 * If we're linked to terminfo, tgetent() will return an empty buffer.  We
401 * cannot use that to adjust the $TERMCAP variable.
402 */
403Bool
404get_termcap(char *name, char *buffer)
405{
406    *buffer = 0;		/* initialize, in case we're using terminfo's tgetent */
407
408#if USE_EXTENDED_NAMES
409    use_extended_names(TRUE);
410#endif
411    if (name != 0) {
412	if (tgetent(buffer, name) == 1) {
413	    TRACE(("get_termcap(%s) succeeded (%s)\n", name,
414		   (*buffer
415		    ? "ok:termcap, we can update $TERMCAP"
416		    : "assuming this is terminfo")));
417	    return True;
418	} else {
419	    *buffer = 0;	/* just in case */
420	}
421    }
422    return False;
423}
424