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