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