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