ncurses 6.1 - patch 20191102
[ncurses.git] / test / demo_terminfo.c
1 /****************************************************************************
2  * Copyright (c) 2009-2017,2019 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 /*
30  * Author: Thomas E. Dickey
31  *
32  * $Id: demo_terminfo.c,v 1.49 2019/08/24 23:11:01 tom Exp $
33  *
34  * A simple demo of the terminfo interface.
35  */
36 #define USE_TINFO
37 #include <test.priv.h>
38 #include <sys/stat.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 static void failed(const char *) GCC_NORETURN;
50
51 static void
52 failed(const char *msg)
53 {
54     fprintf(stderr, "%s\n", msg);
55     ExitProgram(EXIT_FAILURE);
56 }
57
58 #if HAVE_TIGETSTR
59
60 #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
61 #define USE_CODE_LISTS 1
62 #else
63 #define USE_CODE_LISTS 0
64 #endif
65
66 static bool a_opt = FALSE;
67 static bool b_opt = FALSE;
68 static bool f_opt = FALSE;
69 static bool n_opt = FALSE;
70 static bool q_opt = FALSE;
71 static bool s_opt = FALSE;
72 #ifdef NCURSES_VERSION
73 static bool x_opt = FALSE;
74 static bool y_opt = FALSE;
75 #endif
76
77 static char *d_opt;
78 static char *e_opt;
79 static char **db_list;
80 static int db_item;
81
82 static char *my_blob;
83 static char **my_boolcodes;
84 static char **my_numcodes;
85 static char **my_numvalues;
86 static char **my_strcodes;
87 static char **my_strvalues;
88
89 static long total_values;
90 static long total_b_values;
91 static long total_n_values;
92 static long total_s_values;
93
94 #define FCOLS 8
95 #define FNAME(type) "%s %-*s = ", #type, f_opt ? 24 : FCOLS
96
97 static char *
98 make_dbitem(char *p, char *q)
99 {
100     size_t need = strlen(e_opt) + 2 + (size_t) (p - q);
101     char *result = malloc(need);
102     _nc_SPRINTF(result, _nc_SLIMIT(need) "%s=%.*s", e_opt, (int) (p - q), q);
103     return result;
104 }
105
106 static void
107 make_dblist(void)
108 {
109     if (d_opt && e_opt) {
110         int pass;
111
112         for (pass = 0; pass < 2; ++pass) {
113             char *p, *q;
114             size_t count = 0;
115
116             for (p = q = d_opt; *p != '\0'; ++p) {
117                 if (*p == ':') {
118                     if (p != q + 1) {
119                         if (pass) {
120                             db_list[count] = make_dbitem(p, q);
121                         }
122                         count++;
123                     }
124                     q = p + 1;
125                 }
126             }
127             if (p != q + 1) {
128                 if (pass) {
129                     db_list[count] = make_dbitem(p, q);
130                 }
131                 count++;
132             }
133             if (!pass) {
134                 db_list = typeCalloc(char *, count + 1);
135             }
136         }
137     }
138 }
139
140 static char *
141 next_dbitem(void)
142 {
143     char *result = 0;
144
145     if (db_list) {
146         if ((result = db_list[db_item]) == 0) {
147             db_item = 0;
148             result = db_list[0];
149         } else {
150             db_item++;
151         }
152     }
153     if (result != 0)
154         printf("** %s\n", result);
155     return result;
156 }
157
158 #if NO_LEAKS
159 static void
160 free_dblist(void)
161 {
162     if (db_list) {
163         int n;
164         for (n = 0; db_list[n]; ++n)
165             free(db_list[n]);
166         free(db_list);
167         db_list = 0;
168     }
169 }
170 #endif
171
172 static void
173 dumpit(NCURSES_CONST char *cap, const char *show)
174 {
175     const char *str;
176     int num;
177
178     if ((str = tigetstr(cap)) != 0 && (str != (char *) -1)) {
179         total_values++;
180         total_s_values++;
181         if (!q_opt) {
182             printf(FNAME(str), show ? show : cap);
183             while (*str != 0) {
184                 int ch = UChar(*str++);
185                 switch (ch) {
186                 case '\177':
187                     fputs("^?", stdout);
188                     break;
189                 case '\033':
190                     fputs("\\E", stdout);
191                     break;
192                 case '\b':
193                     fputs("\\b", stdout);
194                     break;
195                 case '\f':
196                     fputs("\\f", stdout);
197                     break;
198                 case '\n':
199                     fputs("\\n", stdout);
200                     break;
201                 case '\r':
202                     fputs("\\r", stdout);
203                     break;
204                 case ' ':
205                     fputs("\\s", stdout);
206                     break;
207                 case '\t':
208                     fputs("\\t", stdout);
209                     break;
210                 case '^':
211                     fputs("\\^", stdout);
212                     break;
213                 case ':':
214                     fputs("\\072", stdout);
215                     break;
216                 case '\\':
217                     fputs("\\\\", stdout);
218                     break;
219                 default:
220                     if (isgraph(ch))
221                         fputc(ch, stdout);
222                     else if (ch < 32)
223                         printf("^%c", ch + '@');
224                     else
225                         printf("\\%03o", ch);
226                     break;
227                 }
228             }
229             printf("\n");
230         }
231     } else if ((num = tigetnum(cap)) >= 0) {
232         total_values++;
233         total_n_values++;
234         if (!q_opt) {
235             printf(FNAME(num), show ? show : cap);
236             printf(" %d\n", num);
237         }
238     } else if ((num = tigetflag(cap)) >= 0) {
239         total_values++;
240         total_b_values++;
241         if (!q_opt) {
242             printf(FNAME(flg), show ? show : cap);
243             printf("%s\n", num ? "true" : "false");
244         }
245     }
246
247     if (!q_opt)
248         fflush(stdout);
249 }
250
251 #define isCapName(c) (isalnum(UChar(c)) || ((c) == '_'))
252 #define LegalItem(c,n) (n)
253
254 static void
255 brute_force(const char *name)
256 {
257 #define MAX_FORCE 5             /* omit "colors", since CPU-time is a problem */
258     static const char legal[] = "\
259 0123456789\
260 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
261 abcdefghijklmnopqrstuvwxyz_";
262     int length;
263     int j, k;
264     bool carry;
265     bool changed;
266     char cap[MAX_FORCE + 1];
267     int item[MAX_FORCE + 1];
268
269     if (db_list) {
270         putenv(next_dbitem());
271     }
272     if (!q_opt)
273         printf("Terminal type \"%s\"\n", name);
274     setupterm((NCURSES_CONST char *) name, 1, (int *) 0);
275     if (!q_opt) {
276         if (strcmp(name, ttytype))
277             printf("... actual \"%s\"\n", ttytype);
278     }
279
280     for (length = 1; length <= MAX_FORCE; ++length) {
281         /* set all digits to zeros */
282         for (j = 0; j < length; ++j) {
283             item[j] = LegalItem(j, 0);
284         }
285
286         do {
287             changed = FALSE;
288             /* copy digits to cap-name */
289             for (j = 0; j < length; ++j) {
290                 cap[j] = legal[item[j]];
291             }
292             cap[length] = '\0';
293             dumpit(cap, NULL);
294
295             k = length - 1;
296             do {
297                 carry = FALSE;
298                 for (; k >= 0; --k) {
299                     item[k] += 1;
300                     if (legal[item[k]]) {
301                         changed = TRUE;
302                         break;
303                     }
304                     if (k > 0 &&
305                         legal[item[k - 1] + 1]) {
306                         for (j = k; j < length; ++j) {
307                             item[j] = LegalItem(j, 0);
308                         }
309                         carry = TRUE;
310                         changed = TRUE;
311                     }
312                 }
313             } while (carry);
314         } while (changed);
315     }
316     del_curterm(cur_term);
317 }
318
319 #if USE_CODE_LISTS
320 #define fullname(type,n) f_opt ? type##fnames[n] : cap
321 #else
322 #define fullname(type,n) cap
323 #endif
324
325 static void
326 demo_terminfo(char *name)
327 {
328     unsigned n;
329     NCURSES_CONST char *cap;
330
331     if (db_list) {
332         putenv(next_dbitem());
333     }
334     if (!q_opt)
335         printf("Terminal type \"%s\"\n", name);
336     setupterm(name, 1, (int *) 0);
337
338     if (b_opt) {
339         for (n = 0;; ++n) {
340             cap = my_boolcodes[n];
341             if (cap == 0)
342                 break;
343             dumpit(cap, fullname(bool, n));
344         }
345     }
346
347     if (n_opt) {
348         for (n = 0;; ++n) {
349             cap = my_numcodes[n];
350             if (cap == 0)
351                 break;
352             dumpit(cap, fullname(num, n));
353         }
354     }
355
356     if (s_opt) {
357         for (n = 0;; ++n) {
358             cap = my_strcodes[n];
359             if (cap == 0)
360                 break;
361             dumpit(cap, fullname(str, n));
362         }
363     }
364 #ifdef NCURSES_VERSION
365     if (x_opt && (my_blob == 0)) {
366         if (y_opt) {
367 #if NCURSES_XNAMES
368             TERMTYPE *term = (TERMTYPE *) cur_term;
369             if (term != 0
370                 && ((NUM_BOOLEANS(term) != BOOLCOUNT)
371                     || (NUM_NUMBERS(term) != NUMCOUNT)
372                     || (NUM_STRINGS(term) != STRCOUNT))) {
373                 for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
374                     dumpit(ExtBoolname(term, (int) n, boolnames), NULL);
375                 }
376                 for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
377                     dumpit(ExtNumname(term, (int) n, numnames), NULL);
378                 }
379                 for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
380                     dumpit(ExtStrname(term, (int) n, strnames), NULL);
381                 }
382             }
383 #endif
384         } else {
385             char temp[80];
386             static const char *xterm_keys[] =
387             {
388                 "kDC", "kDN", "kEND", "kHOM", "kIC",
389                 "kLFT", "kNXT", "kPRV", "kRIT", "kUP",
390             };
391             for (n = 0; n < SIZEOF(xterm_keys); ++n) {
392                 int mod;
393                 for (mod = 0; mod < 8; ++mod) {
394                     if (mod == 0) {
395                         /* these happen to be standard - avoid duplicates */
396                         if (!strcmp(xterm_keys[n], "kDC") ||
397                             !strcmp(xterm_keys[n], "kEND") ||
398                             !strcmp(xterm_keys[n], "kHOM") ||
399                             !strcmp(xterm_keys[n], "kLFT") ||
400                             !strcmp(xterm_keys[n], "kRIT")) {
401                             continue;
402                         }
403                         _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
404                                     "%.*s", 8, xterm_keys[n]);
405                     } else {
406                         _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
407                                     "%.*s%d", 8, xterm_keys[n], mod);
408                     }
409                     dumpit(temp, NULL);
410                 }
411             }
412         }
413     }
414 #endif
415     del_curterm(cur_term);
416 }
417
418 typedef enum {
419     pDefault = 0
420     ,pComment
421     ,pDescription
422     ,pEscaped
423     ,pNewline
424     ,pName
425     ,pNumber
426     ,pString
427 } STATE;
428
429 static void
430 parse_description(const char *input_name)
431 {
432     static char empty[1];
433
434     FILE *fp;
435     struct stat sb;
436     size_t count_bools = 0;
437     size_t count_nums = 0;
438     size_t count_strs = 0;
439     size_t len;
440     size_t j, k, jl;
441     STATE state;
442
443     if (stat(input_name, &sb) != 0
444         || (sb.st_mode & S_IFMT) != S_IFREG) {
445         failed("input is not a file");
446     }
447
448     if (sb.st_size == 0) {
449         failed("input is empty");
450     }
451
452     /*
453      * None of the arrays could be larger than the input-file, and since it
454      * is small, just allocate the maximum for simplicity.
455      */
456     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
457         (my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
458           (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
459           (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
460           (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
461           (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
462         failed("cannot allocate memory for input-file");
463     }
464
465     if ((fp = fopen(input_name, "r")) == 0)
466         failed("cannot open input-file");
467     len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
468     my_blob[sb.st_size] = '\0';
469     fclose(fp);
470
471     /*
472      * First, get rid of comments and escaped newlines, as well as repeated
473      * colons to construct a canonical entry.
474      */
475     state = pNewline;
476     for (j = k = 0; j < len; ++j) {
477         int ch = my_blob[j];
478         if (ch == '\t') {
479             ch = ' ';
480         }
481         switch (state) {
482         case pNewline:
483             if (ch == ' ') {
484                 continue;
485             }
486             if (ch == '#') {
487                 state = pComment;
488                 continue;
489             }
490             state = pDefault;
491             /* FALLTHRU */
492         case pDefault:
493             switch (ch) {
494             case '|':
495                 state = pDescription;
496                 continue;
497             case '\\':
498                 state = pEscaped;
499                 continue;
500             case '\n':
501                 state = pNewline;
502                 continue;
503             case ' ':
504                 break;
505             case ',':
506                 my_blob[k++] = (char) ch;
507                 break;
508             default:
509                 if (isalpha(UChar(ch)))
510                     state = pName;
511                 else
512                     fprintf(stderr, "OOPS @%d:%.20s\n", __LINE__, my_blob + j);
513                 my_blob[k++] = (char) ch;
514                 break;
515             }
516             break;
517         case pComment:
518             if (ch == '\n')
519                 state = pNewline;
520             break;
521         case pDescription:
522             switch (ch) {
523             case ',':
524                 state = pDefault;
525                 break;
526             case '\n':
527                 state = pNewline;
528                 break;
529             }
530             break;
531         case pEscaped:
532             if (ch != '\n') {
533                 my_blob[k++] = (char) ch;
534                 state = pDefault;
535             } else {
536                 state = pNewline;
537             }
538             break;
539         case pName:
540             switch (ch) {
541             case '\n':
542                 state = pNewline;
543                 continue;
544             case ' ':
545             case ',':
546                 state = pDefault;
547                 break;
548             case '#':
549                 state = pNumber;
550                 break;
551             case '=':
552                 state = pString;
553                 break;
554             case '|':
555                 state = pDescription;
556                 continue;
557             }
558             my_blob[k++] = (char) ch;
559             break;
560         case pNumber:
561             switch (ch) {
562             case '\n':
563                 state = pNewline;
564                 continue;
565             case ',':
566                 state = pDefault;
567                 break;
568             case ' ':
569                 state = pDefault;
570                 continue;
571             }
572             my_blob[k++] = (char) ch;
573             break;
574         case pString:
575             switch (ch) {
576             case '\n':
577                 state = pNewline;
578                 break;
579             case ',':
580                 state = pDefault;
581                 my_blob[k++] = (char) ch;
582                 break;
583             default:
584                 my_blob[k++] = (char) ch;
585                 break;
586             }
587             break;
588         default:
589             /* not used */
590             break;
591         }
592     }
593     my_blob[k] = '\0';
594
595     /*
596      * Then, parse what's left, making indexes of the names and values.
597      */
598     state = pDefault;
599     for (j = 0; my_blob[j] != '\0'; ++j) {
600         switch (state) {
601         case pDefault:
602             switch (my_blob[j]) {
603             case '\\':
604                 state = pEscaped;
605                 break;
606             case ',':
607                 my_blob[j] = '\0';
608                 if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ',')
609                     state = pName;
610                 break;
611             case ' ':
612                 break;
613             default:
614                 break;
615             }
616         case pEscaped:
617             break;
618         case pName:
619             state = pDefault;
620             if (isalpha(UChar(my_blob[j]))) {
621                 for (jl = 1; isalnum(UChar(my_blob[j + jl])); ++jl) {
622                     ;
623                 }
624             } else {
625                 jl = 0;
626             }
627             if (jl != 0) {
628                 switch (my_blob[j + jl]) {
629                 case '#':
630                     my_numvalues[count_nums] = &my_blob[j + jl + 1];
631                     my_numcodes[count_nums++] = &my_blob[j];
632                     my_blob[j + jl] = '\0';
633                     state = pNumber;
634                     j += jl;
635                     break;
636                 case '=':
637                     my_strvalues[count_strs] = &my_blob[j + jl + 1];
638                     my_strcodes[count_strs++] = &my_blob[j];
639                     my_blob[j + jl] = '\0';
640                     state = pString;
641                     j += jl;
642                     break;
643                 default:
644                     if (my_blob[j + jl] == '@') {
645                         /*
646                          * We cannot get the type for a cancelled item
647                          * directly, but can infer it assuming the input
648                          * came from infocmp, which puts the data in a
649                          * known order.
650                          */
651                         if (count_strs) {
652                             my_strvalues[count_strs] = empty;
653                             my_strcodes[count_strs++] = &my_blob[j];
654                         } else if (count_nums) {
655                             my_numvalues[count_nums] = empty;
656                             my_numcodes[count_nums++] = &my_blob[j];
657                         } else {
658                             my_boolcodes[count_bools++] = &my_blob[j];
659                         }
660                         my_blob[j + jl] = '\0';
661                         j += jl + 1;
662                     } else {
663                         my_boolcodes[count_bools++] = &my_blob[j];
664                         my_blob[j + jl] = '\0';
665                         j += jl;
666                     }
667                     state = (isCapName(my_blob[j + 1])
668                              ? pName
669                              : pDefault);
670                     break;
671                 }
672             }
673             break;
674         case pNumber:
675             if (!isdigit(UChar(my_blob[j]))) {
676                 --j;
677                 state = pDefault;
678             }
679             break;
680         case pString:
681             switch (my_blob[j]) {
682             case '\\':
683                 if (my_blob[j + 1] != '\0') {
684                     ++j;
685                 } else {
686                     --j;
687                     state = pDefault;
688                 }
689                 break;
690             case ',':
691                 --j;
692                 state = pDefault;
693                 break;
694             }
695             break;
696         case pNewline:
697         case pComment:
698         case pDescription:
699         default:
700             break;
701         }
702     }
703     my_boolcodes[count_bools] = 0;
704     my_numcodes[count_nums] = 0;
705     my_numvalues[count_nums] = 0;
706     my_strcodes[count_strs] = 0;
707     my_strvalues[count_strs] = 0;
708
709 #if 0
710     printf("# bools:%d\n", (int) count_bools);
711     for (j = 0; my_boolcodes[j]; ++j)
712         printf("\t%s,\n", my_boolcodes[j]);
713
714     printf("# numbers:%d\n", (int) count_nums);
715     for (j = 0; my_numcodes[j]; ++j)
716         printf("\t%s#%s,\n", my_numcodes[j], my_numvalues[j]);
717
718     printf("# strings:%d\n", (int) count_strs);
719     for (j = 0; my_strcodes[j]; ++j)
720         printf("\t%s=%s,\n", my_strcodes[j], my_strvalues[j]);
721 #endif
722 }
723
724 #if USE_CODE_LISTS
725 static char **
726 copy_code_list(NCURSES_CONST char *const *list)
727 {
728     int pass;
729     size_t count;
730     size_t length = 1;
731     char **result = 0;
732     char *unused = 0;
733
734     for (pass = 0; pass < 2; ++pass) {
735         for (count = 0; list[count] != 0; ++count) {
736             size_t chunk = strlen(list[count]) + 1;
737             if (pass == 0) {
738                 length += chunk;
739             } else {
740                 result[count] = unused;
741                 _nc_STRCPY(unused, list[count], length);
742                 unused += chunk;
743             }
744         }
745         if (pass == 0) {
746             char *blob = malloc(length);
747             result = typeCalloc(char *, count + 1);
748             unused = blob;
749             if (blob == 0 || result == 0)
750                 failed("copy_code_list failed");
751         }
752     }
753
754     return result;
755 }
756
757 #if NO_LEAKS
758 static void
759 free_code_list(char **list)
760 {
761     if (list) {
762         free(list[0]);
763         free(list);
764     }
765 }
766 #endif
767 #endif /* USE_CODE_LISTS */
768
769 static void
770 usage(void)
771 {
772     static const char *msg[] =
773     {
774         "Usage: demo_terminfo [options] [terminal]",
775         "",
776         "If no options are given, print all (boolean, numeric, string)",
777         "capabilities for the given terminal, using short names.",
778         "",
779         "Options:",
780         " -a       try all names, print capabilities found",
781         " -b       print boolean-capabilities",
782         " -d LIST  colon-separated list of databases to use",
783         " -e NAME  environment variable to set with -d option",
784         " -f       print full names",
785         " -i NAME  terminal description to use as names for \"-a\" option",
786         " -n       print numeric-capabilities",
787         " -q       quiet (prints only counts)",
788         " -r COUNT repeat for given count",
789         " -s       print string-capabilities",
790 #ifdef NCURSES_VERSION
791         " -x       print extended capabilities",
792         " -y       direct-lookup names of extended capabilities",
793 #endif
794     };
795     unsigned n;
796     for (n = 0; n < SIZEOF(msg); ++n) {
797         fprintf(stderr, "%s\n", msg[n]);
798     }
799     ExitProgram(EXIT_FAILURE);
800 }
801
802 int
803 main(int argc, char *argv[])
804 {
805     int n;
806     int repeat;
807     char *name;
808     int r_opt = 1;
809     char *input_name = 0;
810
811     while ((n = getopt(argc, argv, "abd:e:fi:nqr:sxy")) != -1) {
812         switch (n) {
813         case 'a':
814             a_opt = TRUE;
815             break;
816         case 'b':
817             b_opt = TRUE;
818             break;
819         case 'd':
820             d_opt = optarg;
821             break;
822         case 'e':
823             e_opt = optarg;
824             break;
825         case 'f':
826             f_opt = TRUE;
827             break;
828         case 'i':
829             input_name = optarg;
830             break;
831         case 'n':
832             n_opt = TRUE;
833             break;
834         case 'q':
835             q_opt = TRUE;
836             break;
837         case 'r':
838             if ((r_opt = atoi(optarg)) <= 0)
839                 usage();
840             break;
841         case 's':
842             s_opt = TRUE;
843             break;
844 #ifdef NCURSES_VERSION
845         case 'x':
846             x_opt = TRUE;
847             break;
848         case 'y':
849             y_opt = TRUE;
850             x_opt = TRUE;
851             break;
852 #endif
853         default:
854             usage();
855             break;
856         }
857     }
858
859 #if HAVE_USE_EXTENDED_NAMES
860     use_extended_names(x_opt);
861 #endif
862
863     if (!(b_opt || n_opt || s_opt)) {
864         b_opt = TRUE;
865         n_opt = TRUE;
866         s_opt = TRUE;
867     }
868
869     make_dblist();
870
871     if (a_opt) {
872         for (repeat = 0; repeat < r_opt; ++repeat) {
873             if (optind < argc) {
874                 for (n = optind; n < argc; ++n) {
875                     brute_force(argv[n]);
876                 }
877             } else if ((name = getenv("TERM")) != 0) {
878                 brute_force(name);
879             } else {
880                 static char dumb[] = "dumb";
881                 brute_force(dumb);
882             }
883         }
884     } else {
885         if (input_name != 0) {
886             parse_description(input_name);
887         }
888 #if USE_CODE_LISTS
889         else {
890             my_boolcodes = copy_code_list(boolnames);
891             my_numcodes = copy_code_list(numnames);
892             my_strcodes = copy_code_list(strnames);
893         }
894 #else
895         else {
896             failed("no capability-lists available (use -i option)");
897         }
898 #endif /* USE_CODE_LISTS */
899         for (repeat = 0; repeat < r_opt; ++repeat) {
900             if (optind < argc) {
901                 for (n = optind; n < argc; ++n) {
902                     demo_terminfo(argv[n]);
903                 }
904             } else if ((name = getenv("TERM")) != 0) {
905                 demo_terminfo(name);
906             } else {
907                 static char dumb[] = "dumb";
908                 demo_terminfo(dumb);
909             }
910         }
911     }
912
913 #define PLURAL(n) n, (n != 1) ? "s" : ""
914     printf("%ld value%s (%ld boolean%s, %ld number%s, %ld string%s)\n",
915            PLURAL(total_values),
916            PLURAL(total_b_values),
917            PLURAL(total_n_values),
918            PLURAL(total_s_values));
919
920 #if NO_LEAKS
921     free_dblist();
922     if (input_name != 0) {
923         if (my_blob != 0) {
924             free(my_blob);
925             free(my_boolcodes);
926             free(my_numcodes);
927             free(my_numvalues);
928             free(my_strcodes);
929             free(my_strvalues);
930         }
931     }
932 #if USE_CODE_LISTS
933     else {
934         free_code_list(my_boolcodes);
935         free_code_list(my_numcodes);
936         free_code_list(my_strcodes);
937     }
938 #endif
939 #endif /* NO_LEAKS */
940
941     ExitProgram(EXIT_SUCCESS);
942 }
943
944 #else /* !HAVE_TIGETSTR */
945 int
946 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
947 {
948     failed("This program requires the terminfo functions such as tigetstr");
949     ExitProgram(EXIT_FAILURE);
950 }
951 #endif /* HAVE_TIGETSTR */