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