Home | History | Annotate | Line # | Download | only in dist
      1 /* $XTermId: xtermcap.c,v 1.63 2024/12/01 20:17:29 tom Exp $ */
      2 
      3 /*
      4  * Copyright 2007-2023,2024 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 #include <data.h>
     35 
     36 #include <X11/keysym.h>
     37 #include <ctype.h>
     38 
     39 #include <xstrings.h>
     40 
     41 #if USE_TERMINFO && defined(NCURSES_VERSION) && defined(HAVE_USE_EXTENDED_NAMES)
     42 #define USE_EXTENDED_NAMES 1
     43 #else
     44 #define USE_EXTENDED_NAMES 0
     45 #endif
     46 
     47 #if USE_TERMINFO
     48 #define TcapInit(buffer, name) (setupterm(name, fileno(stdout), &ignored) == OK)
     49 #define TcapFree()             (del_curterm(cur_term))
     50 #else
     51 #define TcapInit(buffer, name) (tgetent(buffer, name) == 1)
     52 #define TcapFree()		/*nothing */
     53 #endif
     54 
     55 #define NO_STRING (char *)(-1)
     56 
     57 #if OPT_TCAP_QUERY || OPT_TCAP_FKEYS
     58 
     59 #define SHIFT (MOD_NONE + MOD_SHIFT)
     60 
     61 typedef struct {
     62     const char *tc;
     63     const char *ti;
     64     int code;
     65     unsigned param;		/* see xtermStateToParam() */
     66 } TCAPINFO;
     67 /* *INDENT-OFF* */
     68 #define DATA(tc,ti,x,y) { tc, ti, x, y }
     69 static const TCAPINFO table[] = {
     70 	/*	tcap	terminfo	code		state */
     71 	DATA(	"%1",	"khlp",		XK_Help,	0	),
     72 	DATA(	"#1",	"kHLP",		XK_Help,	SHIFT	),
     73 	DATA(	"@0",	"kfnd",		XK_Find,	0	),
     74 	DATA(	"*0",	"kFND",		XK_Find,	SHIFT	),
     75 	DATA(	"*6",	"kslt",		XK_Select,	0	),
     76 	DATA(	"#6",	"kSLT",		XK_Select,	SHIFT	),
     77 
     78 	DATA(	"kh",	"khome",	XK_Home,	0	),
     79 	DATA(	"#2",	"kHOM",		XK_Home,	SHIFT	),
     80 	DATA(	"@7",	"kend",		XK_End,		0	),
     81 	DATA(	"*7",	"kEND",		XK_End,		SHIFT	),
     82 
     83 	DATA(	"kl",	"kcub1",	XK_Left,	0	),
     84 	DATA(	"kr",	"kcuf1",	XK_Right,	0	),
     85 	DATA(	"ku",	"kcuu1",	XK_Up,		0	),
     86 	DATA(	"kd",	"kcud1",	XK_Down,	0	),
     87 
     88 	DATA(	"#4",	"kLFT",		XK_Left,	SHIFT	),
     89 	DATA(	"%i",	"kRIT",		XK_Right,	SHIFT	),
     90 	DATA(	"kF",	"kind",		XK_Down,	SHIFT	),
     91 	DATA(	"kR",	"kri",		XK_Up,		SHIFT	),
     92 
     93 	DATA(	"k1",	"kf1",		XK_Fn(1),	0	),
     94 	DATA(	"k2",	"kf2",		XK_Fn(2),	0	),
     95 	DATA(	"k3",	"kf3",		XK_Fn(3),	0	),
     96 	DATA(	"k4",	"kf4",		XK_Fn(4),	0	),
     97 	DATA(	"k5",	"kf5",		XK_Fn(5),	0	),
     98 	DATA(	"k6",	"kf6",		XK_Fn(6),	0	),
     99 	DATA(	"k7",	"kf7",		XK_Fn(7),	0	),
    100 	DATA(	"k8",	"kf8",		XK_Fn(8),	0	),
    101 	DATA(	"k9",	"kf9",		XK_Fn(9),	0	),
    102 	DATA(	"k;",	"kf10",		XK_Fn(10),	0	),
    103 
    104 	DATA(	"F1",	"kf11",		XK_Fn(11),	0	),
    105 	DATA(	"F2",	"kf12",		XK_Fn(12),	0	),
    106 	DATA(	"F3",	"kf13",		XK_Fn(13),	0	),
    107 	DATA(	"F4",	"kf14",		XK_Fn(14),	0	),
    108 	DATA(	"F5",	"kf15",		XK_Fn(15),	0	),
    109 	DATA(	"F6",	"kf16",		XK_Fn(16),	0	),
    110 	DATA(	"F7",	"kf17",		XK_Fn(17),	0	),
    111 	DATA(	"F8",	"kf18",		XK_Fn(18),	0	),
    112 	DATA(	"F9",	"kf19",		XK_Fn(19),	0	),
    113 	DATA(	"FA",	"kf20",		XK_Fn(20),	0	),
    114 	DATA(	"FB",	"kf21",		XK_Fn(21),	0	),
    115 	DATA(	"FC",	"kf22",		XK_Fn(22),	0	),
    116 	DATA(	"FD",	"kf23",		XK_Fn(23),	0	),
    117 	DATA(	"FE",	"kf24",		XK_Fn(24),	0	),
    118 	DATA(	"FF",	"kf25",		XK_Fn(25),	0	),
    119 	DATA(	"FG",	"kf26",		XK_Fn(26),	0	),
    120 	DATA(	"FH",	"kf27",		XK_Fn(27),	0	),
    121 	DATA(	"FI",	"kf28",		XK_Fn(28),	0	),
    122 	DATA(	"FJ",	"kf29",		XK_Fn(29),	0	),
    123 	DATA(	"FK",	"kf30",		XK_Fn(30),	0	),
    124 	DATA(	"FL",	"kf31",		XK_Fn(31),	0	),
    125 	DATA(	"FM",	"kf32",		XK_Fn(32),	0	),
    126 	DATA(	"FN",	"kf33",		XK_Fn(33),	0	),
    127 	DATA(	"FO",	"kf34",		XK_Fn(34),	0	),
    128 	DATA(	"FP",	"kf35",		XK_Fn(35),	0	),
    129 
    130 	DATA(	"FQ",	"kf36",		-36,		0	),
    131 	DATA(	"FR",	"kf37",		-37,		0	),
    132 	DATA(	"FS",	"kf38",		-38,		0	),
    133 	DATA(	"FT",	"kf39",		-39,		0	),
    134 	DATA(	"FU",	"kf40",		-40,		0	),
    135 	DATA(	"FV",	"kf41",		-41,		0	),
    136 	DATA(	"FW",	"kf42",		-42,		0	),
    137 	DATA(	"FX",	"kf43",		-43,		0	),
    138 	DATA(	"FY",	"kf44",		-44,		0	),
    139 	DATA(	"FZ",	"kf45",		-45,		0	),
    140 	DATA(	"Fa",	"kf46",		-46,		0	),
    141 	DATA(	"Fb",	"kf47",		-47,		0	),
    142 	DATA(	"Fc",	"kf48",		-48,		0	),
    143 	DATA(	"Fd",	"kf49",		-49,		0	),
    144 	DATA(	"Fe",	"kf50",		-50,		0	),
    145 	DATA(	"Ff",	"kf51",		-51,		0	),
    146 	DATA(	"Fg",	"kf52",		-52,		0	),
    147 	DATA(	"Fh",	"kf53",		-53,		0	),
    148 	DATA(	"Fi",	"kf54",		-54,		0	),
    149 	DATA(	"Fj",	"kf55",		-55,		0	),
    150 	DATA(	"Fk",	"kf56",		-56,		0	),
    151 	DATA(	"Fl",	"kf57",		-57,		0	),
    152 	DATA(	"Fm",	"kf58",		-58,		0	),
    153 	DATA(	"Fn",	"kf59",		-59,		0	),
    154 	DATA(	"Fo",	"kf60",		-60,		0	),
    155 	DATA(	"Fp",	"kf61",		-61,		0	),
    156 	DATA(	"Fq",	"kf62",		-62,		0	),
    157 	DATA(	"Fr",	"kf63",		-63,		0	),
    158 
    159 	DATA(	"K1",	"ka1",		XK_KP_Home,	0	),
    160 	DATA(	"K4",	"kc1",		XK_KP_End,	0	),
    161 	DATA(	"K3",	"ka3",		XK_KP_Prior,	0	),
    162 	DATA(	"K5",	"kc3",		XK_KP_Next,	0	),
    163 
    164 #ifdef XK_ISO_Left_Tab
    165 	DATA(	"kB",	"kcbt",		XK_ISO_Left_Tab, 0	),
    166 #endif
    167 	DATA(	"kC",	"kclr",		XK_Clear,	0	),
    168 	DATA(	"kD",	"kdch1",	XK_Delete,	0	),
    169 	DATA(	"kI",	"kich1",	XK_Insert,	0	),
    170 
    171 	DATA(	"kN",	"knp",		XK_Next,	0	),
    172 	DATA(	"kP",	"kpp",		XK_Prior,	0	),
    173 	DATA(	"%c",	"kNXT",		XK_Next,	SHIFT	),
    174 	DATA(	"%e",	"kPRV",		XK_Prior,	SHIFT	),
    175 
    176 	DATA(	"&8",	"kund",		XK_Undo,	0	),
    177 	DATA(	"kb",	"kbs",		XK_BackSpace,	0	),
    178 # if OPT_TCAP_QUERY && OPT_ISO_COLORS
    179 	/* XK_COLORS is a fake code. */
    180 	DATA(	"Co",	"colors",	XK_COLORS,	0	),
    181 #  if OPT_DIRECT_COLOR
    182 	/* note - termcap cannot support RGB */
    183 	DATA(	"Co",	"RGB",		XK_RGB,		0	),
    184 
    185 #  endif
    186 # endif
    187 	DATA(	"TN",	"name",		XK_TCAPNAME,	0	),
    188 #if USE_EXTENDED_NAMES
    189 #define DEXT(name, parm, code) DATA("", name, code, parm)
    190 #define D1ST(name, parm, code) DEXT("k" #name, parm, code)
    191 #define DMOD(name, parm, code) DEXT("k" #name #parm, parm, code)
    192 
    193 #define DGRP(name, code) \
    194 	D1ST(name, 2, code), \
    195 	DMOD(name, 3, code), \
    196 	DMOD(name, 4, code), \
    197 	DMOD(name, 5, code), \
    198 	DMOD(name, 6, code), \
    199 	DMOD(name, 7, code), \
    200 	DMOD(name, 8, code)
    201 
    202 	/* the terminfo codes here are ncurses extensions */
    203 	/* ignore the termcap names, which are empty */
    204 	DATA(	"",	"kUP",		XK_Up,		SHIFT	),
    205 	DATA(	"",	"kDN",		XK_Up,		SHIFT	),
    206 
    207 	DGRP(DN,   XK_Down),
    208 	DGRP(LFT,  XK_Left),
    209 	DGRP(RIT,  XK_Right),
    210 	DGRP(UP,   XK_Up),
    211 	DGRP(DC,   XK_Delete),
    212 	DGRP(END,  XK_End),
    213 	DGRP(HOM,  XK_Home),
    214 	DGRP(IC,   XK_Insert),
    215 	DGRP(NXT,  XK_Next),
    216 	DGRP(PRV,  XK_Prior),
    217 #endif
    218 };
    219 #undef DATA
    220 /* *INDENT-ON* */
    221 
    222 #if OPT_TCAP_FKEYS
    223 static Boolean
    224 loadTermcapStrings(TScreen *screen)
    225 {
    226     Boolean result = True;
    227 
    228     if (screen->tcap_fkeys == NULL) {
    229 	Cardinal want = XtNumber(table);
    230 	Cardinal have;
    231 #if !USE_TERMINFO
    232 	char *area = screen->tcap_area;
    233 #endif
    234 
    235 	TRACE(("loadTermcapStrings\n"));
    236 	if ((screen->tcap_fkeys = TypeCallocN(char *, want)) != NULL) {
    237 
    238 	    for (have = 0; have < want; ++have) {
    239 		char name[80];
    240 		char *fkey;
    241 
    242 #if USE_TERMINFO
    243 		fkey = tigetstr(strcpy(name, table[have].ti));
    244 #else
    245 		fkey = tgetstr(strcpy(name, table[have].tc), &area);
    246 #endif
    247 		if (fkey != NULL && fkey != NO_STRING) {
    248 		    screen->tcap_fkeys[have] = x_strdup(fkey);
    249 		} else {
    250 		    screen->tcap_fkeys[have] = NO_STRING;
    251 		}
    252 	    }
    253 	} else {
    254 	    result = False;
    255 	}
    256     }
    257     return result;
    258 }
    259 #endif
    260 
    261 #if OPT_TCAP_QUERY
    262 static Boolean
    263 keyIsDistinct(XtermWidget xw, int which)
    264 {
    265     Boolean result = True;
    266 
    267     switch (xw->keyboard.type) {
    268     case keyboardIsTermcap:
    269 #if OPT_TCAP_FKEYS
    270 	if (table[which].param == SHIFT) {
    271 	    TScreen *screen = TScreenOf(xw);
    272 	    Cardinal k;
    273 
    274 	    if (loadTermcapStrings(screen)
    275 		&& screen->tcap_fkeys[which] != NO_STRING) {
    276 
    277 		for (k = 0; k < XtNumber(table); k++) {
    278 
    279 		    if (table[k].code == table[which].code
    280 			&& table[k].param == 0) {
    281 			char *fkey;
    282 
    283 			if ((fkey = screen->tcap_fkeys[k]) != NO_STRING
    284 			    && !strcmp(fkey, screen->tcap_fkeys[which])) {
    285 			    TRACE(("shifted/unshifted keys do not differ\n"));
    286 			    result = False;
    287 			}
    288 			break;
    289 		    }
    290 		}
    291 	    } else {
    292 		/* there is no data for the shifted key */
    293 		result = False;
    294 	    }
    295 	}
    296 #endif
    297 	break;
    298 	/*
    299 	 * The vt220-keyboard will not return distinct key sequences for
    300 	 * shifted cursor-keys.  Just pretend they do not exist, since some
    301 	 * programs may be confused if we return the same data for
    302 	 * shifted/unshifted keys.
    303 	 */
    304     case keyboardIsVT220:
    305 	if (table[which].param == SHIFT) {
    306 	    TRACE(("shifted/unshifted keys do not differ\n"));
    307 	    result = False;
    308 	}
    309 	break;
    310     case keyboardIsLegacy:
    311     case keyboardIsDefault:
    312     case keyboardIsHP:
    313     case keyboardIsSCO:
    314     case keyboardIsSun:
    315 	break;
    316     }
    317 
    318     return result;
    319 }
    320 
    321 static int
    322 lookupTcapByName(const char *name)
    323 {
    324     int result = -2;
    325     Cardinal j;
    326 
    327     if (!IsEmpty(name)) {
    328 	for (j = 0; j < XtNumber(table); j++) {
    329 	    if (!strcmp(table[j].ti, name) || !strcmp(table[j].tc, name)) {
    330 		result = (int) j;
    331 		break;
    332 	    }
    333 	}
    334     }
    335 
    336     if (result >= 0) {
    337 	TRACE(("lookupTcapByName(%s) tc=%s, ti=%s code %#x, param %#x\n",
    338 	       name,
    339 	       table[result].tc,
    340 	       table[result].ti,
    341 	       table[result].code,
    342 	       table[result].param));
    343     } else {
    344 	TRACE(("lookupTcapByName(%s) FAIL\n", name));
    345     }
    346     return result;
    347 }
    348 
    349 /*
    350  * Parse the termcap/terminfo name from the string, returning a positive number
    351  * (the keysym) if found, otherwise -1.  Update the string pointer.
    352  * Returns the (shift, control) state in *state.
    353  *
    354  * This does not attempt to construct control/shift modifiers to construct
    355  * function-key values.  Instead, it sets the *fkey flag to pass to Input()
    356  * and bypass the lookup of keysym altogether.
    357  */
    358 int
    359 xtermcapKeycode(XtermWidget xw, const char **params, unsigned *state, Bool *fkey)
    360 {
    361     const TCAPINFO *data;
    362     int code = -1;
    363     char *name;
    364     const char *p;
    365 
    366     TRACE(("xtermcapKeycode(%s)\n", *params));
    367 
    368     /* Convert hex encoded name to ascii */
    369     name = x_decode_hex(*params, &p);
    370     *params = p;
    371 
    372     *state = 0;
    373     *fkey = False;
    374 
    375     if (!IsEmpty(name) && (*p == 0 || *p == ';')) {
    376 	int which;
    377 
    378 	if ((which = lookupTcapByName(name)) >= 0) {
    379 	    if (keyIsDistinct(xw, which)) {
    380 		data = table + which;
    381 		code = data->code;
    382 		*state = xtermParamToState(xw, data->param);
    383 		if (IsFunctionKey(code)) {
    384 		    *fkey = True;
    385 		} else if (code < 0) {
    386 		    *fkey = True;
    387 		    code = XK_Fn((-code));
    388 		}
    389 #if OPT_SUN_FUNC_KEYS
    390 		if (*fkey && xw->keyboard.type == keyboardIsSun) {
    391 		    int num = code - XK_Fn(0);
    392 
    393 		    /* match function-key case in sunfuncvalue() */
    394 		    if (num > 20) {
    395 			if (num <= 30 || num > 47) {
    396 			    code = -1;
    397 			} else {
    398 			    code -= 10;
    399 			    switch (num) {
    400 			    case 37:	/* khome */
    401 			    case 39:	/* kpp */
    402 			    case 41:	/* kb2 */
    403 			    case 43:	/* kend */
    404 			    case 45:	/* knp */
    405 				code = -1;
    406 				break;
    407 			    }
    408 			}
    409 		    }
    410 		}
    411 #endif
    412 	    } else {
    413 		TRACE(("... name ok, data not ok\n"));
    414 		code = -1;
    415 	    }
    416 	} else {
    417 	    TRACE(("... name not ok\n"));
    418 	    code = -2;
    419 	}
    420     } else {
    421 	TRACE(("... name not ok\n"));
    422 	code = -2;
    423     }
    424 
    425     TRACE(("... xtermcapKeycode(%s, %u, %d) -> %#06x\n",
    426 	   name, *state, *fkey, code));
    427     free(name);
    428     return code;
    429 }
    430 #endif /* OPT_TCAP_QUERY */
    431 
    432 #if OPT_TCAP_FKEYS
    433 static int
    434 nextTcapByCode(int code, unsigned param, int last)
    435 {
    436     int result = -1;
    437     int n;
    438 
    439     TRACE(("lookupTcapByCode %#x:%#x\n", code, param));
    440     for (n = last + 1; n < (int) XtNumber(table); n++) {
    441 	if (table[n].code == code &&
    442 	    table[n].param == param) {
    443 	    TRACE(("->lookupTcapByCode %d:%s\n", n, table[n].ti));
    444 	    result = n;
    445 	    break;
    446 	}
    447     }
    448     return result;
    449 }
    450 
    451 static int
    452 firstTcapByCode(int code, unsigned param)
    453 {
    454     return nextTcapByCode(code, param, -1);
    455 }
    456 
    457 int
    458 xtermcapString(XtermWidget xw, int keycode, unsigned mask)
    459 {
    460     int result = 0;
    461     unsigned param = xtermStateToParam(xw, mask);
    462     int which;
    463 
    464     if ((which = firstTcapByCode(keycode, param)) >= 0) {
    465 	TScreen *screen = TScreenOf(xw);
    466 
    467 	if (loadTermcapStrings(screen)) {
    468 	    do {
    469 		char *fkey;
    470 
    471 		if ((fkey = screen->tcap_fkeys[which]) != NO_STRING) {
    472 		    StringInput(xw, (Char *) fkey, strlen(fkey));
    473 		    result = 1;
    474 		    break;
    475 		}
    476 	    } while ((which = nextTcapByCode(keycode, param, which)) >= 0);
    477 	}
    478     }
    479 
    480     TRACE(("xtermcapString(keycode=%#x, mask=%#x) ->%d\n",
    481 	   keycode, mask, result));
    482 
    483     return result;
    484 }
    485 #endif /* OPT_TCAP_FKEYS */
    486 
    487 #endif /* OPT_TCAP_QUERY || OPT_TCAP_FKEYS */
    488 
    489 /*
    490  * If we're linked to terminfo, tgetent() will return an empty buffer.  We
    491  * cannot use that to adjust the $TERMCAP variable.
    492  */
    493 Bool
    494 get_termcap(XtermWidget xw, char *name)
    495 {
    496 #if USE_TERMINFO
    497     int ignored = 0;
    498 #endif
    499     char *buffer = get_tcap_buffer(xw);
    500 
    501     *buffer = 0;		/* initialize, in case we're using terminfo's tgetent */
    502 
    503 #if USE_EXTENDED_NAMES
    504     use_extended_names(TRUE);
    505 #endif
    506     if (!IsEmpty(name)) {
    507 	if (TcapInit(buffer, name)) {
    508 	    TRACE(("get_termcap(%s) succeeded (%s)\n", name,
    509 		   (*buffer
    510 		    ? "ok:termcap, we can update $TERMCAP"
    511 		    : "assuming this is terminfo")));
    512 	    return True;
    513 	} else {
    514 	    *buffer = 0;	/* just in case */
    515 	}
    516     }
    517     return False;
    518 }
    519 
    520 /*
    521  * Retrieve the termcap-buffer.
    522  */
    523 char *
    524 get_tcap_buffer(XtermWidget xw)
    525 {
    526     TScreen *screen = TScreenOf(xw);
    527     char *buffer;
    528 
    529 #if OPT_TEK4014
    530     if (TEK4014_ACTIVE(xw)) {
    531 	buffer = TekScreenOf(tekWidget)->tcapbuf;
    532     } else
    533 #endif
    534     {
    535 	buffer = screen->tcapbuf;
    536     }
    537     return buffer;
    538 }
    539 
    540 /*
    541  * Retrieve the erase-key, for initialization in main program.
    542  */
    543 #if OPT_INITIAL_ERASE
    544 char *
    545 get_tcap_erase(XtermWidget xw)
    546 {
    547 #if !USE_TERMINFO
    548     char *area = TScreenOf(xw)->tcap_area;
    549 #endif
    550     char *fkey;
    551 
    552     (void) xw;
    553 #if USE_TERMINFO
    554     fkey = tigetstr("kbs");
    555 #else
    556     fkey = tgetstr("kb", &area);
    557 #endif
    558 
    559     if (fkey == NO_STRING)
    560 	fkey = NULL;
    561     if (fkey != NULL)
    562 	fkey = x_strdup(fkey);
    563     return fkey;
    564 }
    565 #endif /* OPT_INITIAL_ERASE */
    566 
    567 /*
    568  * A legal termcap (or terminfo) name consists solely of graphic characters,
    569  * excluding the punctuation used to delimit fields of the source description.
    570  */
    571 static Bool
    572 isLegalTcapName(const char *name)
    573 {
    574     Bool result = False;
    575 
    576     if (*name != '\0') {
    577 	int length = 0;
    578 	result = True;
    579 	while (*name != '\0') {
    580 	    if (++length < 32 && isgraph(CharOf(*name))) {
    581 		if (strchr("\\|,:'\"", *name) != NULL) {
    582 		    result = False;
    583 		    break;
    584 		}
    585 	    } else {
    586 		result = False;
    587 		break;
    588 	    }
    589 	    ++name;
    590 	}
    591     }
    592 
    593     return result;
    594 }
    595 
    596 void
    597 set_termcap(XtermWidget xw, const char *name)
    598 {
    599     Boolean success = False;
    600 #if USE_TERMINFO
    601     int ignored = 0;
    602 #else
    603     TScreen *screen = TScreenOf(xw);
    604     char buffer[sizeof(screen->tcapbuf)];
    605 #endif
    606 
    607     TRACE(("set_termcap(%s)\n", NonNull(name)));
    608     if (IsEmpty(name)) {
    609 	Bell(xw, XkbBI_MinorError, 0);
    610     } else {
    611 	const char *temp;
    612 	char *value;
    613 
    614 	if ((value = x_decode_hex(name, &temp)) != NULL) {
    615 	    if (*temp == '\0' && isLegalTcapName(value)) {
    616 		if (TcapInit(buffer, value)) {
    617 		    TRACE(("...set_termcap(%s)\n", NonNull(value)));
    618 #if !USE_TERMINFO
    619 		    memcpy(screen->tcapbuf, buffer, sizeof(buffer));
    620 #endif
    621 		    free_termcap(xw);
    622 		    success = True;
    623 		}
    624 	    }
    625 	    free(value);
    626 	}
    627     }
    628     if (!success)
    629 	Bell(xw, XkbBI_MinorError, 0);
    630 }
    631 
    632 void
    633 free_termcap(XtermWidget xw)
    634 {
    635 #if OPT_TCAP_FKEYS
    636     TScreen *screen = TScreenOf(xw);
    637 
    638     if (screen->tcap_fkeys != NULL) {
    639 	Cardinal want = XtNumber(table);
    640 	Cardinal have;
    641 
    642 	for (have = 0; have < want; ++have) {
    643 	    char *fkey = screen->tcap_fkeys[have];
    644 	    if (fkey != NO_STRING) {
    645 		free(fkey);
    646 	    }
    647 	}
    648 	FreeAndNull(screen->tcap_fkeys);
    649     }
    650 #else
    651     (void) xw;
    652 #endif
    653     TcapFree();
    654 }
    655