]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/list_keys.c
96b02e14f355e29fcb448dc6ce003720a936dd0a
[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.32 2023/06/24 13:57:11 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 static bool x_opt = FALSE;
56
57 typedef enum {
58     ktCursor
59     ,ktFunction
60     ,ktOther
61 #if HAVE_USE_EXTENDED_NAMES
62     ,ktExtended
63 #endif
64 } KEYTYPE;
65
66 typedef struct {
67     KEYTYPE type;
68     const char *name;
69 } KEYNAMES;
70
71 #define Type(n) list[n].type
72 #define Name(n) list[n].name
73
74 static void
75 failed(const char *msg)
76 {
77     perror(msg);
78     ExitProgram(EXIT_FAILURE);
79 }
80
81 static const char *
82 full_name(const char *name)
83 {
84     const char *result = name;
85     int n;
86     for (n = 0; strnames[n] != 0; ++n) {
87         if (!strcmp(name, strnames[n])) {
88             result = strfnames[n];
89             break;
90         }
91     }
92     return result;
93 }
94
95 static int
96 show_key(const char *name, bool show)
97 {
98     int width = 0;
99     NCURSES_CONST char *value = tigetstr((NCURSES_CONST char *) name);
100
101     if (show && t_opt)
102         fputc('"', stdout);
103
104     if (value != 0 && value != (char *) -1) {
105         while (*value != 0) {
106             char buffer[10];
107             int ch = UChar(*value++);
108             switch (ch) {
109             case '\177':
110                 _nc_STRCPY(buffer, "^?", sizeof(buffer));
111                 break;
112             case '\033':
113                 _nc_STRCPY(buffer, "\\E", sizeof(buffer));
114                 break;
115             case '\b':
116                 _nc_STRCPY(buffer, "\\b", sizeof(buffer));
117                 break;
118             case '\f':
119                 _nc_STRCPY(buffer, "\\f", sizeof(buffer));
120                 break;
121             case '\n':
122                 _nc_STRCPY(buffer, "\\n", sizeof(buffer));
123                 break;
124             case '\r':
125                 _nc_STRCPY(buffer, "\\r", sizeof(buffer));
126                 break;
127             case ' ':
128                 _nc_STRCPY(buffer, "\\s", sizeof(buffer));
129                 break;
130             case '\t':
131                 _nc_STRCPY(buffer, "\\t", sizeof(buffer));
132                 break;
133             case '^':
134                 _nc_STRCPY(buffer, "\\^", sizeof(buffer));
135                 break;
136             case ':':
137                 _nc_STRCPY(buffer, "\\072", sizeof(buffer));
138                 break;
139             case '\\':
140                 _nc_STRCPY(buffer, "\\\\", sizeof(buffer));
141                 break;
142             default:
143                 if (t_opt && ch == '"') {
144                     _nc_STRCPY(buffer, "\"\"", sizeof(buffer));
145                 } else if (isgraph(ch)) {
146                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
147                                 "%c", ch);
148                 } else if (ch < 32) {
149                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
150                                 "^%c", ch + '@');
151                 } else {
152                     _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
153                                 "\\%03o", ch);
154                 }
155                 break;
156             }
157             width += (int) strlen(buffer);
158             if (show)
159                 fputs(buffer, stdout);
160         }
161     }
162
163     if (show && t_opt)
164         fputc('"', stdout);
165
166     return width;
167 }
168
169 static bool
170 valid_key(const char *name, TERMINAL **terms, int count)
171 {
172     bool result = FALSE;
173     if (*name == 'k') {
174         int k;
175         for (k = 0; k < count; ++k) {
176             set_curterm(terms[k]);
177             if (show_key(name, FALSE)) {
178                 result = TRUE;
179                 break;
180             }
181         }
182     }
183     return result;
184 }
185
186 static int
187 compare_keys(const void *a, const void *b)
188 {
189     const KEYNAMES *p = (const KEYNAMES *) a;
190     const KEYNAMES *q = (const KEYNAMES *) b;
191     int result = (int) (p->type - q->type);
192     int pn, qn;
193     if (result == 0) {
194         if (p->type == ktFunction &&
195             sscanf(p->name, "kf%d", &pn) == 1 &&
196             sscanf(q->name, "kf%d", &qn) == 1) {
197             result = (pn - qn);
198         } else {
199             result = strcmp(p->name, q->name);
200         }
201     }
202     return result;
203 }
204
205 static void
206 draw_line(int width)
207 {
208     if (!t_opt) {
209         int j;
210         for (j = 0; j < width; ++j) {
211             printf("-");
212         }
213         printf("\n");
214     }
215 }
216
217 static const char *
218 modified_key(const char *name)
219 {
220     static char result[100];
221     char buffer[sizeof(result) - 10];
222     int value;
223     char chr;
224     static const char *modifiers[][2] =
225     {
226         {"", ""},
227         {"s-", "shift-"},
228         {"a-", "alt-"},
229         {"as-", "alt-shift-"},
230         {"c-", "ctrl-"},
231         {"sc-", "ctrl-shift-"},
232         {"ac-", "alt-ctrl-"},
233         {"acs-" "alt-ctrl-shift-"},
234     };
235
236     if (strlen(name) > (sizeof(result) - 3)) {
237         *result = '\0';
238     } else if (sscanf(name, "kf%d%c", &value, &chr) == 1 &&
239                value >= 1 &&
240                value <= 63) {
241         /* map 1,2,3,4,5,6,7 to 1,2,5,... */
242         int map = ((value - 1) / 12);
243         int key = ((value - 1) % 12);
244         int bit1 = (map & 2);
245         int bit2 = (map & 4);
246         map &= ~6;
247         map |= (bit1 << 1) | (bit2 >> 1);
248         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
249                     "%sF%d", modifiers[map][(unsigned) f_opt], 1 + key);
250     } else if (sscanf(name, "k%80[A-Z]%d%c", buffer, &value, &chr) == 2 &&
251                (value > 1 &&
252                 value <= 8) &&
253                (!strcmp(buffer, "UP") ||
254                 !strcmp(buffer, "DN") ||
255                 !strcmp(buffer, "LFT") ||
256                 !strcmp(buffer, "RIT") ||
257                 !strcmp(buffer, "IC") ||
258                 !strcmp(buffer, "DC") ||
259                 !strcmp(buffer, "HOM") ||
260                 !strcmp(buffer, "END") ||
261                 !strcmp(buffer, "NXT") ||
262                 !strcmp(buffer, "PRV"))) {
263         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
264                     "%sk%s", modifiers[value - 1][(unsigned) f_opt], buffer);
265     } else if (sscanf(name, "k%80[A-Z]%c", buffer, &chr) == 1 &&
266                (!strcmp(buffer, "UP") ||
267                 !strcmp(buffer, "DN"))) {
268         _nc_SPRINTF(result, _nc_SLIMIT(sizeof(result))
269                     "%sk%s", modifiers[1][(unsigned) f_opt], buffer);
270     } else {
271         *result = '\0';
272     }
273     return result;
274 }
275
276 static void
277 list_keys(TERMINAL **terms, int count)
278 {
279     int j, k;
280     int widths0 = 0;
281     int widths1 = 0;
282     int widths2 = 0;
283     int widthsx;
284     int check;
285     size_t total = 0;
286     size_t actual = 0;
287     const char *name = f_opt ? "strfname" : "strname";
288     const char *modifier = "extended";
289     KEYNAMES *list;
290
291     for (total = 0; strnames[total]; ++total) {
292         ;
293     }
294 #if NCURSES_XNAMES
295     if (x_opt) {
296         for (k = 0; k < count; ++k) {
297             TERMTYPE *term;
298             set_curterm(terms[k]);
299             term = (TERMTYPE *) cur_term;
300             total += (size_t) (NUM_STRINGS(term) - STRCOUNT);
301         }
302     }
303 #endif
304     list = typeCalloc(KEYNAMES, total + 1);
305     for (j = 0; strnames[j]; ++j) {
306         Type(j) = ktOther;
307         if (sscanf(strnames[j], "kf%d", &k) == 1) {
308             Type(j) = ktFunction;
309         } else if (!(strncmp) (strnames[j], "kcu", 3)) {
310             Type(j) = ktCursor;
311         }
312         Name(j) = strnames[j];
313     }
314 #if NCURSES_XNAMES
315     if (x_opt) {
316         int m, n;
317
318         for (k = 0; k < count; ++k) {
319             TERMTYPE *term;
320
321             set_curterm(terms[k]);
322             term = (TERMTYPE *) cur_term;
323             for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
324                 bool found = FALSE;
325                 const char *estr = ExtStrname(term, (int) n, strnames);
326                 for (m = STRCOUNT; m < j; ++m) {
327                     if (!strcmp(estr, Name(m))) {
328                         found = TRUE;
329                         break;
330                     }
331                 }
332                 if (!found) {
333                     Type(j) = ktExtended;
334                     Name(j++) = estr;
335                 }
336             }
337         }
338     }
339 #endif
340     actual = (size_t) j;
341     qsort(list, actual, sizeof(KEYNAMES), compare_keys);
342
343     widths0 = (int) strlen(name);
344     if (m_opt)
345         widths1 = (int) strlen(modifier);
346
347     for (k = 0; k < count; ++k) {
348         char *value;
349         set_curterm(terms[k]);
350         if ((value = termname()) == NULL)
351             failed("termname");
352         check = (int) strlen(value);
353         if (widths2 < check)
354             widths2 = check;
355     }
356     for (j = 0; Name(j) != 0; ++j) {
357         if (valid_key(Name(j), terms, count)) {
358             const char *label = f_opt ? full_name(Name(j)) : Name(j);
359             check = (int) strlen(label);
360             if (widths0 < check)
361                 widths0 = check;
362             for (k = 0; k < count; ++k) {
363                 set_curterm(terms[k]);
364                 check = show_key(Name(j), FALSE) + 1;
365                 if (widths2 < check)
366                     widths2 = check;
367                 if (m_opt) {
368                     check = (int) strlen(modified_key(Name(j)));
369                     if (widths1 < check)
370                         widths1 = check;
371                 }
372             }
373         }
374     }
375
376     if (t_opt) {
377         printf("\"%s\"", name);
378         if (m_opt)
379             printf(",\"%s\"", modifier);
380     } else {
381         printf("%-*s", widths0, name);
382         if (m_opt)
383             printf(" %-*s", widths1, modifier);
384     }
385     for (k = 0; k < count; ++k) {
386         set_curterm(terms[k]);
387         if (t_opt) {
388             printf(",\"%s\"", termname());
389         } else if (k + 1 >= count) {
390             printf(" %s", termname());
391         } else {
392             printf(" %-*s", widths2, termname());
393         }
394     }
395     printf("\n");
396
397     widthsx = widths0 + ((count + 1) * widths2);
398
399     for (j = 0; Name(j) != 0; ++j) {
400         if (j == 0 || (Type(j) != Type(j - 1)))
401             draw_line(widthsx);
402         if (valid_key(Name(j), terms, count)) {
403             const char *label = f_opt ? full_name(Name(j)) : Name(j);
404             if (t_opt) {
405                 printf("\"%s\"", label);
406                 if (m_opt)
407                     printf(",\"%s\"", modified_key(Name(j)));
408             } else {
409                 printf("%-*s", widths0, label);
410                 if (m_opt)
411                     printf(" %-*s", widths1, modified_key(Name(j)));
412             }
413             for (k = 0; k < count; ++k) {
414                 printf(t_opt ? "," : " ");
415                 set_curterm(terms[k]);
416                 check = show_key(Name(j), TRUE);
417                 if (!t_opt) {
418                     if (k + 1 < count) {
419                         printf("%*s", widths2 - check, " ");
420                     }
421                 }
422             }
423             printf("\n");
424         }
425     }
426     free(list);
427 }
428
429 static void
430 usage(int ok)
431 {
432     static const char *msg[] =
433     {
434         "Usage: list_keys [options] [terminal [terminal2 [...]]]"
435         ,""
436         ,"Print capabilities for terminal special keys."
437         ,""
438         ,USAGE_COMMON
439         ,"Options:"
440         ," -f       print full names"
441         ," -m       print modifier-column for shift/control keys"
442         ," -t       print result as CSV table"
443 #ifdef NCURSES_VERSION
444         ," -x       print extended capabilities"
445 #endif
446     };
447     unsigned n;
448     for (n = 0; n < SIZEOF(msg); ++n) {
449         fprintf(stderr, "%s\n", msg[n]);
450     }
451     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
452 }
453 /* *INDENT-OFF* */
454 VERSION_COMMON()
455 /* *INDENT-ON* */
456
457 int
458 main(int argc, char *argv[])
459 {
460     int ch;
461     TERMINAL **terms = typeCalloc(TERMINAL *, argc + 1);
462
463     while ((ch = getopt(argc, argv, OPTS_COMMON "fmtx")) != -1) {
464         switch (ch) {
465         case 'f':
466             f_opt = TRUE;
467             break;
468         case 'm':
469             m_opt = TRUE;
470             break;
471         case 't':
472             t_opt = TRUE;
473             break;
474 #ifdef NCURSES_VERSION
475         case 'x':
476             x_opt = TRUE;
477             break;
478 #endif
479         case OPTS_VERSION:
480             show_version(argv);
481             ExitProgram(EXIT_SUCCESS);
482         default:
483             usage(ch == OPTS_USAGE);
484             /* NOTREACHED */
485         }
486     }
487
488 #if HAVE_USE_EXTENDED_NAMES
489     use_extended_names(x_opt);
490 #endif
491
492     if (optind < argc) {
493         int found = 0;
494         int status;
495         int n;
496         for (n = optind; n < argc; ++n) {
497             setupterm((NCURSES_CONST char *) argv[n], 1, &status);
498             if (status > 0 && cur_term != 0) {
499                 terms[found++] = cur_term;
500             }
501         }
502         if (found)
503             list_keys(terms, found);
504     } else {
505         setupterm(NULL, 1, (int *) 0);
506         terms[0] = cur_term;
507         list_keys(terms, 1);
508     }
509
510     free(terms);
511
512     ExitProgram(EXIT_SUCCESS);
513 }
514
515 #else
516 int
517 main(void)
518 {
519     printf("This program requires the terminfo arrays\n");
520     ExitProgram(EXIT_FAILURE);
521 }
522 #endif
523 #else /* !HAVE_TIGETSTR */
524 int
525 main(void)
526 {
527     printf("This program requires the terminfo functions such as tigetstr\n");
528     ExitProgram(EXIT_FAILURE);
529 }
530 #endif /* HAVE_TIGETSTR */