]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/list_keys.c
ncurses 6.4 - patch 20240420
[ncurses.git] / test / list_keys.c
1 /****************************************************************************
2  * Copyright 2018-2022,2023 Thomas E. Dickey                                *
3  * Copyright 2016,2017 Free Software Foundation, Inc.                       *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 /*
30  * $Id: list_keys.c,v 1.33 2023/11/11 00:35:05 tom Exp $
31  *
32  * Author: Thomas E Dickey
33  *
34  * List function keys for one or more terminals.
35  */
36
37 #define USE_TINFO
38 #include <test.priv.h>
39
40 #if NCURSES_XNAMES
41 #if HAVE_TERM_ENTRY_H
42 #include <term_entry.h>
43 #else
44 #undef NCURSES_XNAMES
45 #define NCURSES_XNAMES 0
46 #endif
47 #endif
48
49 #if HAVE_TIGETSTR
50 #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
51
52 static bool f_opt = FALSE;
53 static bool m_opt = FALSE;
54 static bool t_opt = FALSE;
55
56 #if NCURSES_XNAMES || HAVE_USE_EXTENDED_NAMES
57 static bool x_opt = FALSE;
58 #endif
59
60 typedef enum {
61     ktCursor
62     ,ktFunction
63     ,ktOther
64 #if HAVE_USE_EXTENDED_NAMES
65     ,ktExtended
66 #endif
67 } KEYTYPE;
68
69 typedef struct {
70     KEYTYPE type;
71     const char *name;
72 } KEYNAMES;
73
74 #define Type(n) list[n].type
75 #define Name(n) list[n].name
76
77 static void
78 failed(const char *msg)
79 {
80     perror(msg);
81     ExitProgram(EXIT_FAILURE);
82 }
83
84 static const char *
85 full_name(const char *name)
86 {
87     const char *result = name;
88     int n;
89     for (n = 0; strnames[n] != 0; ++n) {
90         if (!strcmp(name, strnames[n])) {
91             result = strfnames[n];
92             break;
93         }
94     }
95     return result;
96 }
97
98 static int
99 show_key(const char *name, bool show)
100 {
101     int width = 0;
102     NCURSES_CONST char *value = tigetstr((NCURSES_CONST char *) name);
103
104     if (show && t_opt)
105         fputc('"', stdout);
106
107     if (value != 0 && value != (char *) -1) {
108         while (*value != 0) {
109             char buffer[10];
110             int ch = UChar(*value++);
111             switch (ch) {
112             case '\177':
113                 _nc_STRCPY(buffer, "^?", sizeof(buffer));
114                 break;
115             case '\033':
116                 _nc_STRCPY(buffer, "\\E", sizeof(buffer));
117                 break;
118             case '\b':
119                 _nc_STRCPY(buffer, "\\b", sizeof(buffer));
120                 break;
121             case '\f':
122                 _nc_STRCPY(buffer, "\\f", sizeof(buffer));
123                 break;
124             case '\n':
125                 _nc_STRCPY(buffer, "\\n", sizeof(buffer));
126                 break;
127             case '\r':
128                 _nc_STRCPY(buffer, "\\r", sizeof(buffer));
129                 break;
130             case ' ':
131                 _nc_STRCPY(buffer, "\\s", sizeof(buffer));
132                 break;
133             case '\t':
134                 _nc_STRCPY(buffer, "\\t", sizeof(buffer));
135                 break;
136             case '^':
137                 _nc_STRCPY(buffer, "\\^", sizeof(buffer));
138                 break;
139             case ':':
140                 _nc_STRCPY(buffer, "\\072", sizeof(buffer));
141                 break;
142             case '\\':
143                 _nc_STRCPY(buffer, "\\\\", sizeof(buffer));
144                 break;
145             default:
146                 if (t_opt && ch == '"') {
147                     _nc_STRCPY(buffer, "\"\"", sizeof(buffer));
148                 } else if (isgraph(ch)) {
149                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
150                                 "%c", ch);
151                 } else if (ch < 32) {
152                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
153                                 "^%c", ch + '@');
154                 } else {
155                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
156                                 "\\%03o", ch);
157                 }
158                 break;
159             }
160             width += (int) strlen(buffer);
161             if (show)
162                 fputs(buffer, stdout);
163         }
164     }
165
166     if (show && t_opt)
167         fputc('"', stdout);
168
169     return width;
170 }
171
172 static bool
173 valid_key(const char *name, TERMINAL **terms, int count)
174 {
175     bool result = FALSE;
176     if (*name == 'k') {
177         int k;
178         for (k = 0; k < count; ++k) {
179             set_curterm(terms[k]);
180             if (show_key(name, FALSE)) {
181                 result = TRUE;
182                 break;
183             }
184         }
185     }
186     return result;
187 }
188
189 static int
190 compare_keys(const void *a, const void *b)
191 {
192     const KEYNAMES *p = (const KEYNAMES *) a;
193     const KEYNAMES *q = (const KEYNAMES *) b;
194     int result = (int) (p->type - q->type);
195     int pn, qn;
196     if (result == 0) {
197         if (p->type == ktFunction &&
198             sscanf(p->name, "kf%d", &pn) == 1 &&
199             sscanf(q->name, "kf%d", &qn) == 1) {
200             result = (pn - qn);
201         } else {
202             result = strcmp(p->name, q->name);
203         }
204     }
205     return result;
206 }
207
208 static void
209 draw_line(int width)
210 {
211     if (!t_opt) {
212         int j;
213         for (j = 0; j < width; ++j) {
214             printf("-");
215         }
216         printf("\n");
217     }
218 }
219
220 static const char *
221 modified_key(const char *name)
222 {
223     static char result[100];
224     char buffer[sizeof(result) - 10];
225     int value;
226     char chr;
227     static const char *modifiers[][2] =
228     {
229         {"", ""},
230         {"s-", "shift-"},
231         {"a-", "alt-"},
232         {"as-", "alt-shift-"},
233         {"c-", "ctrl-"},
234         {"sc-", "ctrl-shift-"},
235         {"ac-", "alt-ctrl-"},
236         {"acs-" "alt-ctrl-shift-"},
237     };
238
239     if (strlen(name) > (sizeof(result) - 3)) {
240         *result = '\0';
241     } else if (sscanf(name, "kf%d%c", &value, &chr) == 1 &&
242                value >= 1 &&
243                value <= 63) {
244         /* map 1,2,3,4,5,6,7 to 1,2,5,... */
245         int map = ((value - 1) / 12);
246         int key = ((value - 1) % 12);
247         int bit1 = (map & 2);
248         int bit2 = (map & 4);
249         map &= ~6;
250         map |= (bit1 << 1) | (bit2 >> 1);
251         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
252                     "%sF%d", modifiers[map][(unsigned) f_opt], 1 + key);
253     } else if (sscanf(name, "k%80[A-Z]%d%c", buffer, &value, &chr) == 2 &&
254                (value > 1 &&
255                 value <= 8) &&
256                (!strcmp(buffer, "UP") ||
257                 !strcmp(buffer, "DN") ||
258                 !strcmp(buffer, "LFT") ||
259                 !strcmp(buffer, "RIT") ||
260                 !strcmp(buffer, "IC") ||
261                 !strcmp(buffer, "DC") ||
262                 !strcmp(buffer, "HOM") ||
263                 !strcmp(buffer, "END") ||
264                 !strcmp(buffer, "NXT") ||
265                 !strcmp(buffer, "PRV"))) {
266         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
267                     "%sk%s", modifiers[value - 1][(unsigned) f_opt], buffer);
268     } else if (sscanf(name, "k%80[A-Z]%c", buffer, &chr) == 1 &&
269                (!strcmp(buffer, "UP") ||
270                 !strcmp(buffer, "DN"))) {
271         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
272                     "%sk%s", modifiers[1][(unsigned) f_opt], buffer);
273     } else {
274         *result = '\0';
275     }
276     return result;
277 }
278
279 static void
280 list_keys(TERMINAL **terms, int count)
281 {
282     int j, k;
283     int widths0 = 0;
284     int widths1 = 0;
285     int widths2 = 0;
286     int widthsx;
287     int check;
288     size_t total = 0;
289     size_t actual = 0;
290     const char *name = f_opt ? "strfname" : "strname";
291     const char *modifier = "extended";
292     KEYNAMES *list;
293
294     for (total = 0; strnames[total]; ++total) {
295         ;
296     }
297 #if NCURSES_XNAMES
298     if (x_opt) {
299         for (k = 0; k < count; ++k) {
300             TERMTYPE *term;
301             set_curterm(terms[k]);
302             term = (TERMTYPE *) cur_term;
303             total += (size_t) (NUM_STRINGS(term) - STRCOUNT);
304         }
305     }
306 #endif
307     list = typeCalloc(KEYNAMES, total + 1);
308     for (j = 0; strnames[j]; ++j) {
309         Type(j) = ktOther;
310         if (sscanf(strnames[j], "kf%d", &k) == 1) {
311             Type(j) = ktFunction;
312         } else if (!(strncmp) (strnames[j], "kcu", 3)) {
313             Type(j) = ktCursor;
314         }
315         Name(j) = strnames[j];
316     }
317 #if NCURSES_XNAMES
318     if (x_opt) {
319         int m, n;
320
321         for (k = 0; k < count; ++k) {
322             TERMTYPE *term;
323
324             set_curterm(terms[k]);
325             term = (TERMTYPE *) cur_term;
326             for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
327                 bool found = FALSE;
328                 const char *estr = ExtStrname(term, (int) n, strnames);
329                 for (m = STRCOUNT; m < j; ++m) {
330                     if (!strcmp(estr, Name(m))) {
331                         found = TRUE;
332                         break;
333                     }
334                 }
335                 if (!found) {
336                     Type(j) = ktExtended;
337                     Name(j++) = estr;
338                 }
339             }
340         }
341     }
342 #endif
343     actual = (size_t) j;
344     qsort(list, actual, sizeof(KEYNAMES), compare_keys);
345
346     widths0 = (int) strlen(name);
347     if (m_opt)
348         widths1 = (int) strlen(modifier);
349
350     for (k = 0; k < count; ++k) {
351         char *value;
352         set_curterm(terms[k]);
353         if ((value = termname()) == NULL)
354             failed("termname");
355         check = (int) strlen(value);
356         if (widths2 < check)
357             widths2 = check;
358     }
359     for (j = 0; Name(j) != 0; ++j) {
360         if (valid_key(Name(j), terms, count)) {
361             const char *label = f_opt ? full_name(Name(j)) : Name(j);
362             check = (int) strlen(label);
363             if (widths0 < check)
364                 widths0 = check;
365             for (k = 0; k < count; ++k) {
366                 set_curterm(terms[k]);
367                 check = show_key(Name(j), FALSE) + 1;
368                 if (widths2 < check)
369                     widths2 = check;
370                 if (m_opt) {
371                     check = (int) strlen(modified_key(Name(j)));
372                     if (widths1 < check)
373                         widths1 = check;
374                 }
375             }
376         }
377     }
378
379     if (t_opt) {
380         printf("\"%s\"", name);
381         if (m_opt)
382             printf(",\"%s\"", modifier);
383     } else {
384         printf("%-*s", widths0, name);
385         if (m_opt)
386             printf(" %-*s", widths1, modifier);
387     }
388     for (k = 0; k < count; ++k) {
389         set_curterm(terms[k]);
390         if (t_opt) {
391             printf(",\"%s\"", termname());
392         } else if (k + 1 >= count) {
393             printf(" %s", termname());
394         } else {
395             printf(" %-*s", widths2, termname());
396         }
397     }
398     printf("\n");
399
400     widthsx = widths0 + ((count + 1) * widths2);
401
402     for (j = 0; Name(j) != 0; ++j) {
403         if (j == 0 || (Type(j) != Type(j - 1)))
404             draw_line(widthsx);
405         if (valid_key(Name(j), terms, count)) {
406             const char *label = f_opt ? full_name(Name(j)) : Name(j);
407             if (t_opt) {
408                 printf("\"%s\"", label);
409                 if (m_opt)
410                     printf(",\"%s\"", modified_key(Name(j)));
411             } else {
412                 printf("%-*s", widths0, label);
413                 if (m_opt)
414                     printf(" %-*s", widths1, modified_key(Name(j)));
415             }
416             for (k = 0; k < count; ++k) {
417                 printf(t_opt ? "," : " ");
418                 set_curterm(terms[k]);
419                 check = show_key(Name(j), TRUE);
420                 if (!t_opt) {
421                     if (k + 1 < count) {
422                         printf("%*s", widths2 - check, " ");
423                     }
424                 }
425             }
426             printf("\n");
427         }
428     }
429     free(list);
430 }
431
432 static void
433 usage(int ok)
434 {
435     static const char *msg[] =
436     {
437         "Usage: list_keys [options] [terminal [terminal2 [...]]]"
438         ,""
439         ,"Print capabilities for terminal special keys."
440         ,""
441         ,USAGE_COMMON
442         ,"Options:"
443         ," -f       print full names"
444         ," -m       print modifier-column for shift/control keys"
445         ," -t       print result as CSV table"
446 #ifdef NCURSES_VERSION
447         ," -x       print extended capabilities"
448 #endif
449     };
450     unsigned n;
451     for (n = 0; n < SIZEOF(msg); ++n) {
452         fprintf(stderr, "%s\n", msg[n]);
453     }
454     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
455 }
456 /* *INDENT-OFF* */
457 VERSION_COMMON()
458 /* *INDENT-ON* */
459
460 int
461 main(int argc, char *argv[])
462 {
463     int ch;
464     TERMINAL **terms = typeCalloc(TERMINAL *, argc + 1);
465
466     while ((ch = getopt(argc, argv, OPTS_COMMON "fmtx")) != -1) {
467         switch (ch) {
468         case 'f':
469             f_opt = TRUE;
470             break;
471         case 'm':
472             m_opt = TRUE;
473             break;
474         case 't':
475             t_opt = TRUE;
476             break;
477 #if NCURSES_XNAMES || HAVE_USE_EXTENDED_NAMES
478         case 'x':
479             x_opt = TRUE;
480             break;
481 #endif
482         case OPTS_VERSION:
483             show_version(argv);
484             ExitProgram(EXIT_SUCCESS);
485         default:
486             usage(ch == OPTS_USAGE);
487             /* NOTREACHED */
488         }
489     }
490
491 #if HAVE_USE_EXTENDED_NAMES
492     use_extended_names(x_opt);
493 #endif
494
495     if (optind < argc) {
496         int found = 0;
497         int status;
498         int n;
499         for (n = optind; n < argc; ++n) {
500             setupterm((NCURSES_CONST char *) argv[n], 1, &status);
501             if (status > 0 && cur_term != 0) {
502                 terms[found++] = cur_term;
503             }
504         }
505         if (found)
506             list_keys(terms, found);
507     } else {
508         setupterm(NULL, 1, (int *) 0);
509         terms[0] = cur_term;
510         list_keys(terms, 1);
511     }
512
513     free(terms);
514
515     ExitProgram(EXIT_SUCCESS);
516 }
517
518 #else
519 int
520 main(void)
521 {
522     printf("This program requires the terminfo arrays\n");
523     ExitProgram(EXIT_FAILURE);
524 }
525 #endif
526 #else /* !HAVE_TIGETSTR */
527 int
528 main(void)
529 {
530     printf("This program requires the terminfo functions such as tigetstr\n");
531     ExitProgram(EXIT_FAILURE);
532 }
533 #endif /* HAVE_TIGETSTR */