15104ee6eSmrg/* $XTermId: xtermcap.c,v 1.63 2024/12/01 20:17:29 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
45104ee6eSmrg * Copyright 2007-2023,2024 by Thomas E. Dickey
5d522f475Smrg *
6d522f475Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg */
32d522f475Smrg
33d522f475Smrg#include <xtermcap.h>
3420d2c4d2Smrg#include <data.h>
35d522f475Smrg
36d522f475Smrg#include <X11/keysym.h>
3720d2c4d2Smrg#include <ctype.h>
38d522f475Smrg
39d522f475Smrg#include <xstrings.h>
40d522f475Smrg
412eaa94a1Schristos#if USE_TERMINFO && defined(NCURSES_VERSION) && defined(HAVE_USE_EXTENDED_NAMES)
422eaa94a1Schristos#define USE_EXTENDED_NAMES 1
432eaa94a1Schristos#else
442eaa94a1Schristos#define USE_EXTENDED_NAMES 0
452eaa94a1Schristos#endif
462eaa94a1Schristos
4720d2c4d2Smrg#if USE_TERMINFO
4820d2c4d2Smrg#define TcapInit(buffer, name) (setupterm(name, fileno(stdout), &ignored) == OK)
49f2e35a3aSmrg#define TcapFree()             (del_curterm(cur_term))
5020d2c4d2Smrg#else
5120d2c4d2Smrg#define TcapInit(buffer, name) (tgetent(buffer, name) == 1)
52f2e35a3aSmrg#define TcapFree()		/*nothing */
5320d2c4d2Smrg#endif
5420d2c4d2Smrg
5520d2c4d2Smrg#define NO_STRING (char *)(-1)
5620d2c4d2Smrg
57d522f475Smrg#if OPT_TCAP_QUERY || OPT_TCAP_FKEYS
58d522f475Smrg
59956cc18dSsnj#define SHIFT (MOD_NONE + MOD_SHIFT)
60956cc18dSsnj
61d522f475Smrgtypedef struct {
6220d2c4d2Smrg    const char *tc;
6320d2c4d2Smrg    const char *ti;
64d522f475Smrg    int code;
65956cc18dSsnj    unsigned param;		/* see xtermStateToParam() */
66d522f475Smrg} TCAPINFO;
67d522f475Smrg/* *INDENT-OFF* */
68d522f475Smrg#define DATA(tc,ti,x,y) { tc, ti, x, y }
69e39b573cSmrgstatic const TCAPINFO table[] = {
70956cc18dSsnj	/*	tcap	terminfo	code		state */
71d522f475Smrg	DATA(	"%1",	"khlp",		XK_Help,	0	),
72956cc18dSsnj	DATA(	"#1",	"kHLP",		XK_Help,	SHIFT	),
73d522f475Smrg	DATA(	"@0",	"kfnd",		XK_Find,	0	),
74956cc18dSsnj	DATA(	"*0",	"kFND",		XK_Find,	SHIFT	),
75d522f475Smrg	DATA(	"*6",	"kslt",		XK_Select,	0	),
76956cc18dSsnj	DATA(	"#6",	"kSLT",		XK_Select,	SHIFT	),
77d522f475Smrg
78d522f475Smrg	DATA(	"kh",	"khome",	XK_Home,	0	),
79956cc18dSsnj	DATA(	"#2",	"kHOM",		XK_Home,	SHIFT	),
80d522f475Smrg	DATA(	"@7",	"kend",		XK_End,		0	),
81956cc18dSsnj	DATA(	"*7",	"kEND",		XK_End,		SHIFT	),
82d522f475Smrg
83d522f475Smrg	DATA(	"kl",	"kcub1",	XK_Left,	0	),
84d522f475Smrg	DATA(	"kr",	"kcuf1",	XK_Right,	0	),
85d522f475Smrg	DATA(	"ku",	"kcuu1",	XK_Up,		0	),
86d522f475Smrg	DATA(	"kd",	"kcud1",	XK_Down,	0	),
87d522f475Smrg
88956cc18dSsnj	DATA(	"#4",	"kLFT",		XK_Left,	SHIFT	),
89956cc18dSsnj	DATA(	"%i",	"kRIT",		XK_Right,	SHIFT	),
90e39b573cSmrg	DATA(	"kF",	"kind",		XK_Down,	SHIFT	),
91e39b573cSmrg	DATA(	"kR",	"kri",		XK_Up,		SHIFT	),
92d522f475Smrg
93d522f475Smrg	DATA(	"k1",	"kf1",		XK_Fn(1),	0	),
94d522f475Smrg	DATA(	"k2",	"kf2",		XK_Fn(2),	0	),
95d522f475Smrg	DATA(	"k3",	"kf3",		XK_Fn(3),	0	),
96d522f475Smrg	DATA(	"k4",	"kf4",		XK_Fn(4),	0	),
97d522f475Smrg	DATA(	"k5",	"kf5",		XK_Fn(5),	0	),
98d522f475Smrg	DATA(	"k6",	"kf6",		XK_Fn(6),	0	),
99d522f475Smrg	DATA(	"k7",	"kf7",		XK_Fn(7),	0	),
100d522f475Smrg	DATA(	"k8",	"kf8",		XK_Fn(8),	0	),
101d522f475Smrg	DATA(	"k9",	"kf9",		XK_Fn(9),	0	),
102d522f475Smrg	DATA(	"k;",	"kf10",		XK_Fn(10),	0	),
103d522f475Smrg
104d522f475Smrg	DATA(	"F1",	"kf11",		XK_Fn(11),	0	),
105d522f475Smrg	DATA(	"F2",	"kf12",		XK_Fn(12),	0	),
106d522f475Smrg	DATA(	"F3",	"kf13",		XK_Fn(13),	0	),
107d522f475Smrg	DATA(	"F4",	"kf14",		XK_Fn(14),	0	),
108d522f475Smrg	DATA(	"F5",	"kf15",		XK_Fn(15),	0	),
109d522f475Smrg	DATA(	"F6",	"kf16",		XK_Fn(16),	0	),
110d522f475Smrg	DATA(	"F7",	"kf17",		XK_Fn(17),	0	),
111d522f475Smrg	DATA(	"F8",	"kf18",		XK_Fn(18),	0	),
112d522f475Smrg	DATA(	"F9",	"kf19",		XK_Fn(19),	0	),
113d522f475Smrg	DATA(	"FA",	"kf20",		XK_Fn(20),	0	),
114d522f475Smrg	DATA(	"FB",	"kf21",		XK_Fn(21),	0	),
115d522f475Smrg	DATA(	"FC",	"kf22",		XK_Fn(22),	0	),
116d522f475Smrg	DATA(	"FD",	"kf23",		XK_Fn(23),	0	),
117d522f475Smrg	DATA(	"FE",	"kf24",		XK_Fn(24),	0	),
118d522f475Smrg	DATA(	"FF",	"kf25",		XK_Fn(25),	0	),
119d522f475Smrg	DATA(	"FG",	"kf26",		XK_Fn(26),	0	),
120d522f475Smrg	DATA(	"FH",	"kf27",		XK_Fn(27),	0	),
121d522f475Smrg	DATA(	"FI",	"kf28",		XK_Fn(28),	0	),
122d522f475Smrg	DATA(	"FJ",	"kf29",		XK_Fn(29),	0	),
123d522f475Smrg	DATA(	"FK",	"kf30",		XK_Fn(30),	0	),
124d522f475Smrg	DATA(	"FL",	"kf31",		XK_Fn(31),	0	),
125d522f475Smrg	DATA(	"FM",	"kf32",		XK_Fn(32),	0	),
126d522f475Smrg	DATA(	"FN",	"kf33",		XK_Fn(33),	0	),
127d522f475Smrg	DATA(	"FO",	"kf34",		XK_Fn(34),	0	),
128d522f475Smrg	DATA(	"FP",	"kf35",		XK_Fn(35),	0	),
129d522f475Smrg
130d522f475Smrg	DATA(	"FQ",	"kf36",		-36,		0	),
131d522f475Smrg	DATA(	"FR",	"kf37",		-37,		0	),
132d522f475Smrg	DATA(	"FS",	"kf38",		-38,		0	),
133d522f475Smrg	DATA(	"FT",	"kf39",		-39,		0	),
134d522f475Smrg	DATA(	"FU",	"kf40",		-40,		0	),
135d522f475Smrg	DATA(	"FV",	"kf41",		-41,		0	),
136d522f475Smrg	DATA(	"FW",	"kf42",		-42,		0	),
137d522f475Smrg	DATA(	"FX",	"kf43",		-43,		0	),
138d522f475Smrg	DATA(	"FY",	"kf44",		-44,		0	),
139d522f475Smrg	DATA(	"FZ",	"kf45",		-45,		0	),
140d522f475Smrg	DATA(	"Fa",	"kf46",		-46,		0	),
141d522f475Smrg	DATA(	"Fb",	"kf47",		-47,		0	),
142d522f475Smrg	DATA(	"Fc",	"kf48",		-48,		0	),
143d522f475Smrg	DATA(	"Fd",	"kf49",		-49,		0	),
144d522f475Smrg	DATA(	"Fe",	"kf50",		-50,		0	),
145d522f475Smrg	DATA(	"Ff",	"kf51",		-51,		0	),
146d522f475Smrg	DATA(	"Fg",	"kf52",		-52,		0	),
147d522f475Smrg	DATA(	"Fh",	"kf53",		-53,		0	),
148d522f475Smrg	DATA(	"Fi",	"kf54",		-54,		0	),
149d522f475Smrg	DATA(	"Fj",	"kf55",		-55,		0	),
150d522f475Smrg	DATA(	"Fk",	"kf56",		-56,		0	),
151d522f475Smrg	DATA(	"Fl",	"kf57",		-57,		0	),
152d522f475Smrg	DATA(	"Fm",	"kf58",		-58,		0	),
153d522f475Smrg	DATA(	"Fn",	"kf59",		-59,		0	),
154d522f475Smrg	DATA(	"Fo",	"kf60",		-60,		0	),
155d522f475Smrg	DATA(	"Fp",	"kf61",		-61,		0	),
156d522f475Smrg	DATA(	"Fq",	"kf62",		-62,		0	),
157d522f475Smrg	DATA(	"Fr",	"kf63",		-63,		0	),
158d522f475Smrg
159d522f475Smrg	DATA(	"K1",	"ka1",		XK_KP_Home,	0	),
160d522f475Smrg	DATA(	"K4",	"kc1",		XK_KP_End,	0	),
1612eaa94a1Schristos	DATA(	"K3",	"ka3",		XK_KP_Prior,	0	),
1622eaa94a1Schristos	DATA(	"K5",	"kc3",		XK_KP_Next,	0	),
163d522f475Smrg
164d522f475Smrg#ifdef XK_ISO_Left_Tab
165d522f475Smrg	DATA(	"kB",	"kcbt",		XK_ISO_Left_Tab, 0	),
166d522f475Smrg#endif
167d522f475Smrg	DATA(	"kC",	"kclr",		XK_Clear,	0	),
168d522f475Smrg	DATA(	"kD",	"kdch1",	XK_Delete,	0	),
169d522f475Smrg	DATA(	"kI",	"kich1",	XK_Insert,	0	),
17020d2c4d2Smrg
171d522f475Smrg	DATA(	"kN",	"knp",		XK_Next,	0	),
172d522f475Smrg	DATA(	"kP",	"kpp",		XK_Prior,	0	),
17320d2c4d2Smrg	DATA(	"%c",	"kNXT",		XK_Next,	SHIFT	),
17420d2c4d2Smrg	DATA(	"%e",	"kPRV",		XK_Prior,	SHIFT	),
17520d2c4d2Smrg
1762eaa94a1Schristos	DATA(	"&8",	"kund",		XK_Undo,	0	),
177d522f475Smrg	DATA(	"kb",	"kbs",		XK_BackSpace,	0	),
178d522f475Smrg# if OPT_TCAP_QUERY && OPT_ISO_COLORS
179d522f475Smrg	/* XK_COLORS is a fake code. */
180d522f475Smrg	DATA(	"Co",	"colors",	XK_COLORS,	0	),
181f2e35a3aSmrg#  if OPT_DIRECT_COLOR
182f2e35a3aSmrg	/* note - termcap cannot support RGB */
183f2e35a3aSmrg	DATA(	"Co",	"RGB",		XK_RGB,		0	),
184f2e35a3aSmrg
185f2e35a3aSmrg#  endif
186d522f475Smrg# endif
18720d2c4d2Smrg	DATA(	"TN",	"name",		XK_TCAPNAME,	0	),
1882eaa94a1Schristos#if USE_EXTENDED_NAMES
189d522f475Smrg#define DEXT(name, parm, code) DATA("", name, code, parm)
190d522f475Smrg#define D1ST(name, parm, code) DEXT("k" #name, parm, code)
191d522f475Smrg#define DMOD(name, parm, code) DEXT("k" #name #parm, parm, code)
192d522f475Smrg
193d522f475Smrg#define DGRP(name, code) \
194d522f475Smrg	D1ST(name, 2, code), \
195d522f475Smrg	DMOD(name, 3, code), \
196d522f475Smrg	DMOD(name, 4, code), \
197d522f475Smrg	DMOD(name, 5, code), \
198d522f475Smrg	DMOD(name, 6, code), \
199d522f475Smrg	DMOD(name, 7, code), \
200d522f475Smrg	DMOD(name, 8, code)
201d522f475Smrg
20220d2c4d2Smrg	/* the terminfo codes here are ncurses extensions */
20320d2c4d2Smrg	/* ignore the termcap names, which are empty */
20420d2c4d2Smrg	DATA(	"",	"kUP",		XK_Up,		SHIFT	),
20520d2c4d2Smrg	DATA(	"",	"kDN",		XK_Up,		SHIFT	),
20620d2c4d2Smrg
207d522f475Smrg	DGRP(DN,   XK_Down),
208d522f475Smrg	DGRP(LFT,  XK_Left),
209d522f475Smrg	DGRP(RIT,  XK_Right),
210d522f475Smrg	DGRP(UP,   XK_Up),
211d522f475Smrg	DGRP(DC,   XK_Delete),
212d522f475Smrg	DGRP(END,  XK_End),
213d522f475Smrg	DGRP(HOM,  XK_Home),
214d522f475Smrg	DGRP(IC,   XK_Insert),
215d522f475Smrg	DGRP(NXT,  XK_Next),
216d522f475Smrg	DGRP(PRV,  XK_Prior),
217d522f475Smrg#endif
218d522f475Smrg};
219d522f475Smrg#undef DATA
220d522f475Smrg/* *INDENT-ON* */
221d522f475Smrg
222956cc18dSsnj#if OPT_TCAP_FKEYS
223894e0ac8Smrgstatic Boolean
224894e0ac8SmrgloadTermcapStrings(TScreen *screen)
225956cc18dSsnj{
226894e0ac8Smrg    Boolean result = True;
227894e0ac8Smrg
2285104ee6eSmrg    if (screen->tcap_fkeys == NULL) {
229956cc18dSsnj	Cardinal want = XtNumber(table);
230956cc18dSsnj	Cardinal have;
231f2e35a3aSmrg#if !USE_TERMINFO
232956cc18dSsnj	char *area = screen->tcap_area;
233956cc18dSsnj#endif
234956cc18dSsnj
23520d2c4d2Smrg	TRACE(("loadTermcapStrings\n"));
2365104ee6eSmrg	if ((screen->tcap_fkeys = TypeCallocN(char *, want)) != NULL) {
2372e4f8982Smrg
238956cc18dSsnj	    for (have = 0; have < want; ++have) {
2392e4f8982Smrg		char name[80];
2402e4f8982Smrg		char *fkey;
2412e4f8982Smrg
242f2e35a3aSmrg#if USE_TERMINFO
24320d2c4d2Smrg		fkey = tigetstr(strcpy(name, table[have].ti));
244956cc18dSsnj#else
24520d2c4d2Smrg		fkey = tgetstr(strcpy(name, table[have].tc), &area);
246956cc18dSsnj#endif
2475104ee6eSmrg		if (fkey != NULL && fkey != NO_STRING) {
248956cc18dSsnj		    screen->tcap_fkeys[have] = x_strdup(fkey);
249956cc18dSsnj		} else {
250956cc18dSsnj		    screen->tcap_fkeys[have] = NO_STRING;
251956cc18dSsnj		}
252956cc18dSsnj	    }
253894e0ac8Smrg	} else {
254894e0ac8Smrg	    result = False;
255956cc18dSsnj	}
256956cc18dSsnj    }
257894e0ac8Smrg    return result;
258956cc18dSsnj}
259956cc18dSsnj#endif
260956cc18dSsnj
261d522f475Smrg#if OPT_TCAP_QUERY
262956cc18dSsnjstatic Boolean
263956cc18dSsnjkeyIsDistinct(XtermWidget xw, int which)
264d522f475Smrg{
265956cc18dSsnj    Boolean result = True;
266d522f475Smrg
267956cc18dSsnj    switch (xw->keyboard.type) {
268956cc18dSsnj    case keyboardIsTermcap:
269956cc18dSsnj#if OPT_TCAP_FKEYS
270956cc18dSsnj	if (table[which].param == SHIFT) {
271956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
272956cc18dSsnj	    Cardinal k;
273956cc18dSsnj
274894e0ac8Smrg	    if (loadTermcapStrings(screen)
275894e0ac8Smrg		&& screen->tcap_fkeys[which] != NO_STRING) {
2762e4f8982Smrg
277956cc18dSsnj		for (k = 0; k < XtNumber(table); k++) {
2782e4f8982Smrg
279956cc18dSsnj		    if (table[k].code == table[which].code
280956cc18dSsnj			&& table[k].param == 0) {
2812e4f8982Smrg			char *fkey;
2822e4f8982Smrg
283956cc18dSsnj			if ((fkey = screen->tcap_fkeys[k]) != NO_STRING
284956cc18dSsnj			    && !strcmp(fkey, screen->tcap_fkeys[which])) {
285956cc18dSsnj			    TRACE(("shifted/unshifted keys do not differ\n"));
286956cc18dSsnj			    result = False;
287956cc18dSsnj			}
288956cc18dSsnj			break;
289956cc18dSsnj		    }
290956cc18dSsnj		}
291956cc18dSsnj	    } else {
292956cc18dSsnj		/* there is no data for the shifted key */
293f2e35a3aSmrg		result = False;
294d522f475Smrg	    }
295d522f475Smrg	}
296956cc18dSsnj#endif
297956cc18dSsnj	break;
298956cc18dSsnj	/*
299956cc18dSsnj	 * The vt220-keyboard will not return distinct key sequences for
300956cc18dSsnj	 * shifted cursor-keys.  Just pretend they do not exist, since some
301956cc18dSsnj	 * programs may be confused if we return the same data for
302956cc18dSsnj	 * shifted/unshifted keys.
303956cc18dSsnj	 */
304956cc18dSsnj    case keyboardIsVT220:
305956cc18dSsnj	if (table[which].param == SHIFT) {
306956cc18dSsnj	    TRACE(("shifted/unshifted keys do not differ\n"));
307956cc18dSsnj	    result = False;
308956cc18dSsnj	}
309956cc18dSsnj	break;
310956cc18dSsnj    case keyboardIsLegacy:
311956cc18dSsnj    case keyboardIsDefault:
312956cc18dSsnj    case keyboardIsHP:
313956cc18dSsnj    case keyboardIsSCO:
314956cc18dSsnj    case keyboardIsSun:
315956cc18dSsnj	break;
316d522f475Smrg    }
3172eaa94a1Schristos
318956cc18dSsnj    return result;
319956cc18dSsnj}
320956cc18dSsnj
321956cc18dSsnjstatic int
322956cc18dSsnjlookupTcapByName(const char *name)
323956cc18dSsnj{
324956cc18dSsnj    int result = -2;
325956cc18dSsnj    Cardinal j;
326956cc18dSsnj
32720d2c4d2Smrg    if (!IsEmpty(name)) {
328956cc18dSsnj	for (j = 0; j < XtNumber(table); j++) {
329956cc18dSsnj	    if (!strcmp(table[j].ti, name) || !strcmp(table[j].tc, name)) {
330956cc18dSsnj		result = (int) j;
331956cc18dSsnj		break;
332956cc18dSsnj	    }
333956cc18dSsnj	}
3342eaa94a1Schristos    }
3352eaa94a1Schristos
336956cc18dSsnj    if (result >= 0) {
337956cc18dSsnj	TRACE(("lookupTcapByName(%s) tc=%s, ti=%s code %#x, param %#x\n",
338956cc18dSsnj	       name,
339956cc18dSsnj	       table[result].tc,
340956cc18dSsnj	       table[result].ti,
341956cc18dSsnj	       table[result].code,
342956cc18dSsnj	       table[result].param));
3432eaa94a1Schristos    } else {
3442eaa94a1Schristos	TRACE(("lookupTcapByName(%s) FAIL\n", name));
3452eaa94a1Schristos    }
346d522f475Smrg    return result;
347d522f475Smrg}
348d522f475Smrg
349d522f475Smrg/*
350d522f475Smrg * Parse the termcap/terminfo name from the string, returning a positive number
351d522f475Smrg * (the keysym) if found, otherwise -1.  Update the string pointer.
352d522f475Smrg * Returns the (shift, control) state in *state.
353d522f475Smrg *
354d522f475Smrg * This does not attempt to construct control/shift modifiers to construct
355d522f475Smrg * function-key values.  Instead, it sets the *fkey flag to pass to Input()
356d522f475Smrg * and bypass the lookup of keysym altogether.
357d522f475Smrg */
358d522f475Smrgint
359894e0ac8SmrgxtermcapKeycode(XtermWidget xw, const char **params, unsigned *state, Bool *fkey)
360d522f475Smrg{
361e39b573cSmrg    const TCAPINFO *data;
362d522f475Smrg    int code = -1;
36320d2c4d2Smrg    char *name;
36420d2c4d2Smrg    const char *p;
365d522f475Smrg
366d522f475Smrg    TRACE(("xtermcapKeycode(%s)\n", *params));
367d522f475Smrg
368d522f475Smrg    /* Convert hex encoded name to ascii */
36920d2c4d2Smrg    name = x_decode_hex(*params, &p);
370d522f475Smrg    *params = p;
371d522f475Smrg
372d522f475Smrg    *state = 0;
373d522f475Smrg    *fkey = False;
374d522f475Smrg
37520d2c4d2Smrg    if (!IsEmpty(name) && (*p == 0 || *p == ';')) {
3762e4f8982Smrg	int which;
3772e4f8982Smrg
378956cc18dSsnj	if ((which = lookupTcapByName(name)) >= 0) {
379956cc18dSsnj	    if (keyIsDistinct(xw, which)) {
380956cc18dSsnj		data = table + which;
381956cc18dSsnj		code = data->code;
382956cc18dSsnj		*state = xtermParamToState(xw, data->param);
383956cc18dSsnj		if (IsFunctionKey(code)) {
384956cc18dSsnj		    *fkey = True;
385956cc18dSsnj		} else if (code < 0) {
386956cc18dSsnj		    *fkey = True;
387956cc18dSsnj		    code = XK_Fn((-code));
388956cc18dSsnj		}
389d522f475Smrg#if OPT_SUN_FUNC_KEYS
390956cc18dSsnj		if (*fkey && xw->keyboard.type == keyboardIsSun) {
391956cc18dSsnj		    int num = code - XK_Fn(0);
392956cc18dSsnj
393956cc18dSsnj		    /* match function-key case in sunfuncvalue() */
394956cc18dSsnj		    if (num > 20) {
395956cc18dSsnj			if (num <= 30 || num > 47) {
396d522f475Smrg			    code = -1;
397956cc18dSsnj			} else {
398956cc18dSsnj			    code -= 10;
399956cc18dSsnj			    switch (num) {
400956cc18dSsnj			    case 37:	/* khome */
401956cc18dSsnj			    case 39:	/* kpp */
402956cc18dSsnj			    case 41:	/* kb2 */
403956cc18dSsnj			    case 43:	/* kend */
404956cc18dSsnj			    case 45:	/* knp */
405956cc18dSsnj				code = -1;
406956cc18dSsnj				break;
407956cc18dSsnj			    }
408d522f475Smrg			}
409d522f475Smrg		    }
410d522f475Smrg		}
411d522f475Smrg#endif
412956cc18dSsnj	    } else {
413956cc18dSsnj		TRACE(("... name ok, data not ok\n"));
414956cc18dSsnj		code = -1;
415956cc18dSsnj	    }
416956cc18dSsnj	} else {
417956cc18dSsnj	    TRACE(("... name not ok\n"));
418956cc18dSsnj	    code = -2;
419d522f475Smrg	}
420956cc18dSsnj    } else {
421956cc18dSsnj	TRACE(("... name not ok\n"));
422956cc18dSsnj	code = -2;
423d522f475Smrg    }
424d522f475Smrg
425d522f475Smrg    TRACE(("... xtermcapKeycode(%s, %u, %d) -> %#06x\n",
426d522f475Smrg	   name, *state, *fkey, code));
42720d2c4d2Smrg    free(name);
428d522f475Smrg    return code;
429d522f475Smrg}
430d522f475Smrg#endif /* OPT_TCAP_QUERY */
431d522f475Smrg
432d522f475Smrg#if OPT_TCAP_FKEYS
43320d2c4d2Smrgstatic int
43420d2c4d2SmrgnextTcapByCode(int code, unsigned param, int last)
435d522f475Smrg{
43620d2c4d2Smrg    int result = -1;
43720d2c4d2Smrg    int n;
438d522f475Smrg
439956cc18dSsnj    TRACE(("lookupTcapByCode %#x:%#x\n", code, param));
44020d2c4d2Smrg    for (n = last + 1; n < (int) XtNumber(table); n++) {
441d522f475Smrg	if (table[n].code == code &&
442956cc18dSsnj	    table[n].param == param) {
443956cc18dSsnj	    TRACE(("->lookupTcapByCode %d:%s\n", n, table[n].ti));
44420d2c4d2Smrg	    result = n;
445d522f475Smrg	    break;
446d522f475Smrg	}
447d522f475Smrg    }
448d522f475Smrg    return result;
449d522f475Smrg}
450d522f475Smrg
45120d2c4d2Smrgstatic int
45220d2c4d2SmrgfirstTcapByCode(int code, unsigned param)
45320d2c4d2Smrg{
45420d2c4d2Smrg    return nextTcapByCode(code, param, -1);
45520d2c4d2Smrg}
45620d2c4d2Smrg
457d522f475Smrgint
458d522f475SmrgxtermcapString(XtermWidget xw, int keycode, unsigned mask)
459d522f475Smrg{
460d522f475Smrg    int result = 0;
461d522f475Smrg    unsigned param = xtermStateToParam(xw, mask);
46220d2c4d2Smrg    int which;
463d522f475Smrg
46420d2c4d2Smrg    if ((which = firstTcapByCode(keycode, param)) >= 0) {
465d522f475Smrg	TScreen *screen = TScreenOf(xw);
466d522f475Smrg
467894e0ac8Smrg	if (loadTermcapStrings(screen)) {
46820d2c4d2Smrg	    do {
4692e4f8982Smrg		char *fkey;
4702e4f8982Smrg
47120d2c4d2Smrg		if ((fkey = screen->tcap_fkeys[which]) != NO_STRING) {
47220d2c4d2Smrg		    StringInput(xw, (Char *) fkey, strlen(fkey));
47320d2c4d2Smrg		    result = 1;
47420d2c4d2Smrg		    break;
47520d2c4d2Smrg		}
47620d2c4d2Smrg	    } while ((which = nextTcapByCode(keycode, param, which)) >= 0);
477d522f475Smrg	}
478d522f475Smrg    }
479d522f475Smrg
480d522f475Smrg    TRACE(("xtermcapString(keycode=%#x, mask=%#x) ->%d\n",
481d522f475Smrg	   keycode, mask, result));
482d522f475Smrg
483d522f475Smrg    return result;
484d522f475Smrg}
485d522f475Smrg#endif /* OPT_TCAP_FKEYS */
486d522f475Smrg
487d522f475Smrg#endif /* OPT_TCAP_QUERY || OPT_TCAP_FKEYS */
48820d2c4d2Smrg
489d522f475Smrg/*
490d522f475Smrg * If we're linked to terminfo, tgetent() will return an empty buffer.  We
491d522f475Smrg * cannot use that to adjust the $TERMCAP variable.
492d522f475Smrg */
493d522f475SmrgBool
49420d2c4d2Smrgget_termcap(XtermWidget xw, char *name)
495d522f475Smrg{
49620d2c4d2Smrg#if USE_TERMINFO
49720d2c4d2Smrg    int ignored = 0;
49820d2c4d2Smrg#endif
49920d2c4d2Smrg    char *buffer = get_tcap_buffer(xw);
50020d2c4d2Smrg
501d522f475Smrg    *buffer = 0;		/* initialize, in case we're using terminfo's tgetent */
502d522f475Smrg
5032eaa94a1Schristos#if USE_EXTENDED_NAMES
504d522f475Smrg    use_extended_names(TRUE);
505d522f475Smrg#endif
50620d2c4d2Smrg    if (!IsEmpty(name)) {
50720d2c4d2Smrg	if (TcapInit(buffer, name)) {
508d522f475Smrg	    TRACE(("get_termcap(%s) succeeded (%s)\n", name,
509d522f475Smrg		   (*buffer
510d522f475Smrg		    ? "ok:termcap, we can update $TERMCAP"
511d522f475Smrg		    : "assuming this is terminfo")));
512d522f475Smrg	    return True;
513d522f475Smrg	} else {
514d522f475Smrg	    *buffer = 0;	/* just in case */
515d522f475Smrg	}
516d522f475Smrg    }
517d522f475Smrg    return False;
518d522f475Smrg}
51920d2c4d2Smrg
52020d2c4d2Smrg/*
52120d2c4d2Smrg * Retrieve the termcap-buffer.
52220d2c4d2Smrg */
52320d2c4d2Smrgchar *
52420d2c4d2Smrgget_tcap_buffer(XtermWidget xw)
52520d2c4d2Smrg{
52620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
52720d2c4d2Smrg    char *buffer;
52820d2c4d2Smrg
52920d2c4d2Smrg#if OPT_TEK4014
53020d2c4d2Smrg    if (TEK4014_ACTIVE(xw)) {
53120d2c4d2Smrg	buffer = TekScreenOf(tekWidget)->tcapbuf;
53220d2c4d2Smrg    } else
53320d2c4d2Smrg#endif
53420d2c4d2Smrg    {
53520d2c4d2Smrg	buffer = screen->tcapbuf;
53620d2c4d2Smrg    }
53720d2c4d2Smrg    return buffer;
53820d2c4d2Smrg}
53920d2c4d2Smrg
54020d2c4d2Smrg/*
54120d2c4d2Smrg * Retrieve the erase-key, for initialization in main program.
54220d2c4d2Smrg */
5435104ee6eSmrg#if OPT_INITIAL_ERASE
54420d2c4d2Smrgchar *
545f2e35a3aSmrgget_tcap_erase(XtermWidget xw)
54620d2c4d2Smrg{
547f2e35a3aSmrg#if !USE_TERMINFO
54820d2c4d2Smrg    char *area = TScreenOf(xw)->tcap_area;
54920d2c4d2Smrg#endif
55020d2c4d2Smrg    char *fkey;
55120d2c4d2Smrg
552f2e35a3aSmrg    (void) xw;
553f2e35a3aSmrg#if USE_TERMINFO
55420d2c4d2Smrg    fkey = tigetstr("kbs");
55520d2c4d2Smrg#else
55620d2c4d2Smrg    fkey = tgetstr("kb", &area);
55720d2c4d2Smrg#endif
55820d2c4d2Smrg
55920d2c4d2Smrg    if (fkey == NO_STRING)
5605104ee6eSmrg	fkey = NULL;
5615104ee6eSmrg    if (fkey != NULL)
56220d2c4d2Smrg	fkey = x_strdup(fkey);
56320d2c4d2Smrg    return fkey;
56420d2c4d2Smrg}
5655104ee6eSmrg#endif /* OPT_INITIAL_ERASE */
56620d2c4d2Smrg
56720d2c4d2Smrg/*
56820d2c4d2Smrg * A legal termcap (or terminfo) name consists solely of graphic characters,
56920d2c4d2Smrg * excluding the punctuation used to delimit fields of the source description.
57020d2c4d2Smrg */
57120d2c4d2Smrgstatic Bool
57220d2c4d2SmrgisLegalTcapName(const char *name)
57320d2c4d2Smrg{
57420d2c4d2Smrg    Bool result = False;
57520d2c4d2Smrg
57620d2c4d2Smrg    if (*name != '\0') {
5775307cd1aSmrg	int length = 0;
57820d2c4d2Smrg	result = True;
57920d2c4d2Smrg	while (*name != '\0') {
5805307cd1aSmrg	    if (++length < 32 && isgraph(CharOf(*name))) {
5815104ee6eSmrg		if (strchr("\\|,:'\"", *name) != NULL) {
58220d2c4d2Smrg		    result = False;
58320d2c4d2Smrg		    break;
58420d2c4d2Smrg		}
58520d2c4d2Smrg	    } else {
58620d2c4d2Smrg		result = False;
58720d2c4d2Smrg		break;
58820d2c4d2Smrg	    }
58920d2c4d2Smrg	    ++name;
59020d2c4d2Smrg	}
59120d2c4d2Smrg    }
59220d2c4d2Smrg
59320d2c4d2Smrg    return result;
59420d2c4d2Smrg}
59520d2c4d2Smrg
59620d2c4d2Smrgvoid
59720d2c4d2Smrgset_termcap(XtermWidget xw, const char *name)
59820d2c4d2Smrg{
59920d2c4d2Smrg    Boolean success = False;
60020d2c4d2Smrg#if USE_TERMINFO
60120d2c4d2Smrg    int ignored = 0;
60220d2c4d2Smrg#else
60320d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
60420d2c4d2Smrg    char buffer[sizeof(screen->tcapbuf)];
60520d2c4d2Smrg#endif
60620d2c4d2Smrg
60720d2c4d2Smrg    TRACE(("set_termcap(%s)\n", NonNull(name)));
60820d2c4d2Smrg    if (IsEmpty(name)) {
60920d2c4d2Smrg	Bell(xw, XkbBI_MinorError, 0);
61020d2c4d2Smrg    } else {
61120d2c4d2Smrg	const char *temp;
61220d2c4d2Smrg	char *value;
61320d2c4d2Smrg
6145104ee6eSmrg	if ((value = x_decode_hex(name, &temp)) != NULL) {
61520d2c4d2Smrg	    if (*temp == '\0' && isLegalTcapName(value)) {
61620d2c4d2Smrg		if (TcapInit(buffer, value)) {
6175307cd1aSmrg		    TRACE(("...set_termcap(%s)\n", NonNull(value)));
61820d2c4d2Smrg#if !USE_TERMINFO
61920d2c4d2Smrg		    memcpy(screen->tcapbuf, buffer, sizeof(buffer));
62020d2c4d2Smrg#endif
62120d2c4d2Smrg		    free_termcap(xw);
62220d2c4d2Smrg		    success = True;
62320d2c4d2Smrg		}
62420d2c4d2Smrg	    }
62520d2c4d2Smrg	    free(value);
62620d2c4d2Smrg	}
62720d2c4d2Smrg    }
62820d2c4d2Smrg    if (!success)
62920d2c4d2Smrg	Bell(xw, XkbBI_MinorError, 0);
63020d2c4d2Smrg}
63120d2c4d2Smrg
63220d2c4d2Smrgvoid
63320d2c4d2Smrgfree_termcap(XtermWidget xw)
63420d2c4d2Smrg{
635a1f3da82Smrg#if OPT_TCAP_FKEYS
63620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
63720d2c4d2Smrg
6385104ee6eSmrg    if (screen->tcap_fkeys != NULL) {
63920d2c4d2Smrg	Cardinal want = XtNumber(table);
64020d2c4d2Smrg	Cardinal have;
64120d2c4d2Smrg
64220d2c4d2Smrg	for (have = 0; have < want; ++have) {
6432e4f8982Smrg	    char *fkey = screen->tcap_fkeys[have];
644f2e35a3aSmrg	    if (fkey != NO_STRING) {
64520d2c4d2Smrg		free(fkey);
64620d2c4d2Smrg	    }
64720d2c4d2Smrg	}
648f2e35a3aSmrg	FreeAndNull(screen->tcap_fkeys);
64920d2c4d2Smrg    }
65004b94745Smrg#else
65104b94745Smrg    (void) xw;
652a1f3da82Smrg#endif
653f2e35a3aSmrg    TcapFree();
65420d2c4d2Smrg}
655