diff options
Diffstat (limited to 'xtermcap.c')
-rw-r--r-- | xtermcap.c | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/xtermcap.c b/xtermcap.c new file mode 100644 index 0000000..ca537f2 --- /dev/null +++ b/xtermcap.c @@ -0,0 +1,634 @@ +/* $XTermId: xtermcap.c,v 1.47 2011/07/11 00:31:26 tom Exp $ */ + +/* + * Copyright 2007-2010,2011 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + */ + +#include <xtermcap.h> +#include <data.h> + +#include <X11/keysym.h> +#include <ctype.h> + +#ifdef VMS +#include <X11/keysymdef.h> +#endif + +#include <xstrings.h> + +#if USE_TERMINFO && defined(NCURSES_VERSION) && defined(HAVE_USE_EXTENDED_NAMES) +#define USE_EXTENDED_NAMES 1 +#else +#define USE_EXTENDED_NAMES 0 +#endif + +#if USE_TERMINFO +#define TcapInit(buffer, name) (setupterm(name, fileno(stdout), &ignored) == OK) +#else +#define TcapInit(buffer, name) (tgetent(buffer, name) == 1) +#endif + +#define NO_STRING (char *)(-1) + +#if OPT_TCAP_QUERY || OPT_TCAP_FKEYS + +#define SHIFT (MOD_NONE + MOD_SHIFT) + +typedef struct { + const char *tc; + const char *ti; + int code; + unsigned param; /* see xtermStateToParam() */ +} TCAPINFO; +/* *INDENT-OFF* */ +#define DATA(tc,ti,x,y) { tc, ti, x, y } +static const TCAPINFO table[] = { + /* tcap terminfo code state */ + DATA( "%1", "khlp", XK_Help, 0 ), + DATA( "#1", "kHLP", XK_Help, SHIFT ), + DATA( "@0", "kfnd", XK_Find, 0 ), + DATA( "*0", "kFND", XK_Find, SHIFT ), + DATA( "*6", "kslt", XK_Select, 0 ), + DATA( "#6", "kSLT", XK_Select, SHIFT ), + + DATA( "kh", "khome", XK_Home, 0 ), + DATA( "#2", "kHOM", XK_Home, SHIFT ), + DATA( "@7", "kend", XK_End, 0 ), + DATA( "*7", "kEND", XK_End, SHIFT ), + + DATA( "kl", "kcub1", XK_Left, 0 ), + DATA( "kr", "kcuf1", XK_Right, 0 ), + DATA( "ku", "kcuu1", XK_Up, 0 ), + DATA( "kd", "kcud1", XK_Down, 0 ), + + DATA( "#4", "kLFT", XK_Left, SHIFT ), + DATA( "%i", "kRIT", XK_Right, SHIFT ), + DATA( "kF", "kind", XK_Down, SHIFT ), + DATA( "kR", "kri", XK_Up, SHIFT ), + + DATA( "k1", "kf1", XK_Fn(1), 0 ), + DATA( "k2", "kf2", XK_Fn(2), 0 ), + DATA( "k3", "kf3", XK_Fn(3), 0 ), + DATA( "k4", "kf4", XK_Fn(4), 0 ), + DATA( "k5", "kf5", XK_Fn(5), 0 ), + DATA( "k6", "kf6", XK_Fn(6), 0 ), + DATA( "k7", "kf7", XK_Fn(7), 0 ), + DATA( "k8", "kf8", XK_Fn(8), 0 ), + DATA( "k9", "kf9", XK_Fn(9), 0 ), + DATA( "k;", "kf10", XK_Fn(10), 0 ), + + DATA( "F1", "kf11", XK_Fn(11), 0 ), + DATA( "F2", "kf12", XK_Fn(12), 0 ), + DATA( "F3", "kf13", XK_Fn(13), 0 ), + DATA( "F4", "kf14", XK_Fn(14), 0 ), + DATA( "F5", "kf15", XK_Fn(15), 0 ), + DATA( "F6", "kf16", XK_Fn(16), 0 ), + DATA( "F7", "kf17", XK_Fn(17), 0 ), + DATA( "F8", "kf18", XK_Fn(18), 0 ), + DATA( "F9", "kf19", XK_Fn(19), 0 ), + DATA( "FA", "kf20", XK_Fn(20), 0 ), + DATA( "FB", "kf21", XK_Fn(21), 0 ), + DATA( "FC", "kf22", XK_Fn(22), 0 ), + DATA( "FD", "kf23", XK_Fn(23), 0 ), + DATA( "FE", "kf24", XK_Fn(24), 0 ), + DATA( "FF", "kf25", XK_Fn(25), 0 ), + DATA( "FG", "kf26", XK_Fn(26), 0 ), + DATA( "FH", "kf27", XK_Fn(27), 0 ), + DATA( "FI", "kf28", XK_Fn(28), 0 ), + DATA( "FJ", "kf29", XK_Fn(29), 0 ), + DATA( "FK", "kf30", XK_Fn(30), 0 ), + DATA( "FL", "kf31", XK_Fn(31), 0 ), + DATA( "FM", "kf32", XK_Fn(32), 0 ), + DATA( "FN", "kf33", XK_Fn(33), 0 ), + DATA( "FO", "kf34", XK_Fn(34), 0 ), + DATA( "FP", "kf35", XK_Fn(35), 0 ), + + DATA( "FQ", "kf36", -36, 0 ), + DATA( "FR", "kf37", -37, 0 ), + DATA( "FS", "kf38", -38, 0 ), + DATA( "FT", "kf39", -39, 0 ), + DATA( "FU", "kf40", -40, 0 ), + DATA( "FV", "kf41", -41, 0 ), + DATA( "FW", "kf42", -42, 0 ), + DATA( "FX", "kf43", -43, 0 ), + DATA( "FY", "kf44", -44, 0 ), + DATA( "FZ", "kf45", -45, 0 ), + DATA( "Fa", "kf46", -46, 0 ), + DATA( "Fb", "kf47", -47, 0 ), + DATA( "Fc", "kf48", -48, 0 ), + DATA( "Fd", "kf49", -49, 0 ), + DATA( "Fe", "kf50", -50, 0 ), + DATA( "Ff", "kf51", -51, 0 ), + DATA( "Fg", "kf52", -52, 0 ), + DATA( "Fh", "kf53", -53, 0 ), + DATA( "Fi", "kf54", -54, 0 ), + DATA( "Fj", "kf55", -55, 0 ), + DATA( "Fk", "kf56", -56, 0 ), + DATA( "Fl", "kf57", -57, 0 ), + DATA( "Fm", "kf58", -58, 0 ), + DATA( "Fn", "kf59", -59, 0 ), + DATA( "Fo", "kf60", -60, 0 ), + DATA( "Fp", "kf61", -61, 0 ), + DATA( "Fq", "kf62", -62, 0 ), + DATA( "Fr", "kf63", -63, 0 ), + + DATA( "K1", "ka1", XK_KP_Home, 0 ), + DATA( "K4", "kc1", XK_KP_End, 0 ), + DATA( "K3", "ka3", XK_KP_Prior, 0 ), + DATA( "K5", "kc3", XK_KP_Next, 0 ), + +#ifdef XK_ISO_Left_Tab + DATA( "kB", "kcbt", XK_ISO_Left_Tab, 0 ), +#endif + DATA( "kC", "kclr", XK_Clear, 0 ), + DATA( "kD", "kdch1", XK_Delete, 0 ), + DATA( "kI", "kich1", XK_Insert, 0 ), + + DATA( "kN", "knp", XK_Next, 0 ), + DATA( "kP", "kpp", XK_Prior, 0 ), + DATA( "%c", "kNXT", XK_Next, SHIFT ), + DATA( "%e", "kPRV", XK_Prior, SHIFT ), + + DATA( "&8", "kund", XK_Undo, 0 ), + DATA( "kb", "kbs", XK_BackSpace, 0 ), +# if OPT_TCAP_QUERY && OPT_ISO_COLORS + /* XK_COLORS is a fake code. */ + DATA( "Co", "colors", XK_COLORS, 0 ), +# endif + DATA( "TN", "name", XK_TCAPNAME, 0 ), +#if USE_EXTENDED_NAMES +#define DEXT(name, parm, code) DATA("", name, code, parm) +#define D1ST(name, parm, code) DEXT("k" #name, parm, code) +#define DMOD(name, parm, code) DEXT("k" #name #parm, parm, code) + +#define DGRP(name, code) \ + D1ST(name, 2, code), \ + DMOD(name, 3, code), \ + DMOD(name, 4, code), \ + DMOD(name, 5, code), \ + DMOD(name, 6, code), \ + DMOD(name, 7, code), \ + DMOD(name, 8, code) + + /* the terminfo codes here are ncurses extensions */ + /* ignore the termcap names, which are empty */ + DATA( "", "kUP", XK_Up, SHIFT ), + DATA( "", "kDN", XK_Up, SHIFT ), + + DGRP(DN, XK_Down), + DGRP(LFT, XK_Left), + DGRP(RIT, XK_Right), + DGRP(UP, XK_Up), + DGRP(DC, XK_Delete), + DGRP(END, XK_End), + DGRP(HOM, XK_Home), + DGRP(IC, XK_Insert), + DGRP(NXT, XK_Next), + DGRP(PRV, XK_Prior), +#endif +}; +#undef DATA +/* *INDENT-ON* */ + +#if OPT_TCAP_FKEYS +static void +loadTermcapStrings(TScreen * screen) +{ + if (screen->tcap_fkeys == 0) { + char name[80]; + Cardinal want = XtNumber(table); + Cardinal have; + char *fkey; +#ifdef USE_TERMCAP + char *area = screen->tcap_area; +#endif + + TRACE(("loadTermcapStrings\n")); + if ((screen->tcap_fkeys = TypeCallocN(char *, want)) != 0) { + for (have = 0; have < want; ++have) { +#ifndef USE_TERMCAP + fkey = tigetstr(strcpy(name, table[have].ti)); +#else + fkey = tgetstr(strcpy(name, table[have].tc), &area); +#endif + if (fkey != 0 && fkey != NO_STRING) { + screen->tcap_fkeys[have] = x_strdup(fkey); + } else { + screen->tcap_fkeys[have] = NO_STRING; + } + } + } + } +} +#endif + +#if OPT_TCAP_QUERY +static Boolean +keyIsDistinct(XtermWidget xw, int which) +{ + Boolean result = True; + + switch (xw->keyboard.type) { + case keyboardIsTermcap: +#if OPT_TCAP_FKEYS + if (table[which].param == SHIFT) { + TScreen *screen = TScreenOf(xw); + Cardinal k; + char *fkey; + + loadTermcapStrings(screen); + if (screen->tcap_fkeys[which] != NO_STRING) { + for (k = 0; k < XtNumber(table); k++) { + if (table[k].code == table[which].code + && table[k].param == 0) { + if ((fkey = screen->tcap_fkeys[k]) != NO_STRING + && !strcmp(fkey, screen->tcap_fkeys[which])) { + TRACE(("shifted/unshifted keys do not differ\n")); + result = False; + } + break; + } + } + } else { + /* there is no data for the shifted key */ + result = -1; + } + } +#endif + break; + /* + * The vt220-keyboard will not return distinct key sequences for + * shifted cursor-keys. Just pretend they do not exist, since some + * programs may be confused if we return the same data for + * shifted/unshifted keys. + */ + case keyboardIsVT220: + if (table[which].param == SHIFT) { + TRACE(("shifted/unshifted keys do not differ\n")); + result = False; + } + break; + case keyboardIsLegacy: + case keyboardIsDefault: + case keyboardIsHP: + case keyboardIsSCO: + case keyboardIsSun: + break; + } + + return result; +} + +static int +lookupTcapByName(const char *name) +{ + int result = -2; + Cardinal j; + + if (!IsEmpty(name)) { + for (j = 0; j < XtNumber(table); j++) { + if (!strcmp(table[j].ti, name) || !strcmp(table[j].tc, name)) { + result = (int) j; + break; + } + } + } + + if (result >= 0) { + TRACE(("lookupTcapByName(%s) tc=%s, ti=%s code %#x, param %#x\n", + name, + table[result].tc, + table[result].ti, + table[result].code, + table[result].param)); + } else { + TRACE(("lookupTcapByName(%s) FAIL\n", name)); + } + return result; +} + +/* + * Parse the termcap/terminfo name from the string, returning a positive number + * (the keysym) if found, otherwise -1. Update the string pointer. + * Returns the (shift, control) state in *state. + * + * This does not attempt to construct control/shift modifiers to construct + * function-key values. Instead, it sets the *fkey flag to pass to Input() + * and bypass the lookup of keysym altogether. + */ +int +xtermcapKeycode(XtermWidget xw, const char **params, unsigned *state, Bool * fkey) +{ + const TCAPINFO *data; + int which; + int code = -1; + char *name; + const char *p; + + TRACE(("xtermcapKeycode(%s)\n", *params)); + + /* Convert hex encoded name to ascii */ + name = x_decode_hex(*params, &p); + *params = p; + + *state = 0; + *fkey = False; + + if (!IsEmpty(name) && (*p == 0 || *p == ';')) { + if ((which = lookupTcapByName(name)) >= 0) { + if (keyIsDistinct(xw, which)) { + data = table + which; + code = data->code; + *state = xtermParamToState(xw, data->param); + if (IsFunctionKey(code)) { + *fkey = True; + } else if (code < 0) { + *fkey = True; + code = XK_Fn((-code)); + } +#if OPT_SUN_FUNC_KEYS + if (*fkey && xw->keyboard.type == keyboardIsSun) { + int num = code - XK_Fn(0); + + /* match function-key case in sunfuncvalue() */ + if (num > 20) { + if (num <= 30 || num > 47) { + code = -1; + } else { + code -= 10; + switch (num) { + case 37: /* khome */ + case 39: /* kpp */ + case 41: /* kb2 */ + case 43: /* kend */ + case 45: /* knp */ + code = -1; + break; + } + } + } + } +#endif + } else { + TRACE(("... name ok, data not ok\n")); + code = -1; + } + } else { + TRACE(("... name not ok\n")); + code = -2; + } + } else { + TRACE(("... name not ok\n")); + code = -2; + } + + TRACE(("... xtermcapKeycode(%s, %u, %d) -> %#06x\n", + name, *state, *fkey, code)); + free(name); + return code; +} +#endif /* OPT_TCAP_QUERY */ + +#if OPT_TCAP_FKEYS +static int +nextTcapByCode(int code, unsigned param, int last) +{ + int result = -1; + int n; + + TRACE(("lookupTcapByCode %#x:%#x\n", code, param)); + for (n = last + 1; n < (int) XtNumber(table); n++) { + if (table[n].code == code && + table[n].param == param) { + TRACE(("->lookupTcapByCode %d:%s\n", n, table[n].ti)); + result = n; + break; + } + } + return result; +} + +static int +firstTcapByCode(int code, unsigned param) +{ + return nextTcapByCode(code, param, -1); +} + +int +xtermcapString(XtermWidget xw, int keycode, unsigned mask) +{ + int result = 0; + unsigned param = xtermStateToParam(xw, mask); + int which; + + if ((which = firstTcapByCode(keycode, param)) >= 0) { + TScreen *screen = TScreenOf(xw); + char *fkey; + + loadTermcapStrings(screen); + if (screen->tcap_fkeys != 0) { + do { + if ((fkey = screen->tcap_fkeys[which]) != NO_STRING) { + StringInput(xw, (Char *) fkey, strlen(fkey)); + result = 1; + break; + } + } while ((which = nextTcapByCode(keycode, param, which)) >= 0); + } + } + + TRACE(("xtermcapString(keycode=%#x, mask=%#x) ->%d\n", + keycode, mask, result)); + + return result; +} +#endif /* OPT_TCAP_FKEYS */ + +#endif /* OPT_TCAP_QUERY || OPT_TCAP_FKEYS */ + +/* + * If we're linked to terminfo, tgetent() will return an empty buffer. We + * cannot use that to adjust the $TERMCAP variable. + */ +Bool +get_termcap(XtermWidget xw, char *name) +{ +#if USE_TERMINFO + int ignored = 0; +#endif + char *buffer = get_tcap_buffer(xw); + + *buffer = 0; /* initialize, in case we're using terminfo's tgetent */ + +#if USE_EXTENDED_NAMES + use_extended_names(TRUE); +#endif + if (!IsEmpty(name)) { + if (TcapInit(buffer, name)) { + TRACE(("get_termcap(%s) succeeded (%s)\n", name, + (*buffer + ? "ok:termcap, we can update $TERMCAP" + : "assuming this is terminfo"))); + return True; + } else { + *buffer = 0; /* just in case */ + } + } + return False; +} + +/* + * Retrieve the termcap-buffer. + */ +char * +get_tcap_buffer(XtermWidget xw) +{ + TScreen *screen = TScreenOf(xw); + char *buffer; + +#if OPT_TEK4014 + if (TEK4014_ACTIVE(xw)) { + buffer = TekScreenOf(tekWidget)->tcapbuf; + } else +#endif + { + buffer = screen->tcapbuf; + } + return buffer; +} + +/* + * Retrieve the erase-key, for initialization in main program. + */ +char * +get_tcap_erase(XtermWidget xw GCC_UNUSED) +{ +#ifdef USE_TERMCAP + char *area = TScreenOf(xw)->tcap_area; +#endif + char *fkey; + +#ifndef USE_TERMCAP + fkey = tigetstr("kbs"); +#else + fkey = tgetstr("kb", &area); +#endif + + if (fkey == NO_STRING) + fkey = 0; + if (fkey != 0) + fkey = x_strdup(fkey); + return fkey; +} + +/* + * A legal termcap (or terminfo) name consists solely of graphic characters, + * excluding the punctuation used to delimit fields of the source description. + */ +static Bool +isLegalTcapName(const char *name) +{ + Bool result = False; + + if (*name != '\0') { + result = True; + while (*name != '\0') { + if (isgraph(CharOf(*name))) { + if (strchr("\\|,:'\"", *name) != 0) { + result = False; + break; + } + } else { + result = False; + break; + } + ++name; + } + } + + return result; +} + +void +set_termcap(XtermWidget xw, const char *name) +{ + Boolean success = False; +#if USE_TERMINFO + int ignored = 0; +#else + TScreen *screen = TScreenOf(xw); + char buffer[sizeof(screen->tcapbuf)]; +#endif + + TRACE(("set_termcap(%s)\n", NonNull(name))); + if (IsEmpty(name)) { + Bell(xw, XkbBI_MinorError, 0); + } else { + const char *temp; + char *value; + + if ((value = x_decode_hex(name, &temp)) != 0) { + if (*temp == '\0' && isLegalTcapName(value)) { + if (TcapInit(buffer, value)) { +#if !USE_TERMINFO + memcpy(screen->tcapbuf, buffer, sizeof(buffer)); +#endif + free_termcap(xw); + success = True; + } + } + free(value); + } + } + if (!success) + Bell(xw, XkbBI_MinorError, 0); +} + +void +free_termcap(XtermWidget xw) +{ +#if OPT_TCAP_FKEYS + TScreen *screen = TScreenOf(xw); + + if (screen->tcap_fkeys != 0) { + Cardinal want = XtNumber(table); + Cardinal have; + char *fkey; + + for (have = 0; have < want; ++have) { + fkey = screen->tcap_fkeys[have]; + if (fkey != 0 && fkey != NO_STRING) { + free(fkey); + } + } + free(screen->tcap_fkeys); + screen->tcap_fkeys = 0; + } +#endif +} |