]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/demo_terminfo.c
ncurses 6.1 - patch 20180224
[ncurses.git] / test / demo_terminfo.c
1 /****************************************************************************
2  * Copyright (c) 2009-2016,2017 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.48 2017/11/24 20:49:11 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     printf("** %s\n", result);
154     return result;
155 }
156
157 #if NO_LEAKS
158 static void
159 free_dblist(void)
160 {
161     if (db_list) {
162         int n;
163         for (n = 0; db_list[n]; ++n)
164             free(db_list[n]);
165         free(db_list);
166         db_list = 0;
167     }
168 }
169 #endif
170
171 static void
172 dumpit(NCURSES_CONST char *cap, const char *show)
173 {
174     const char *str;
175     int num;
176
177     if ((str = tigetstr(cap)) != 0 && (str != (char *) -1)) {
178         total_values++;
179         total_s_values++;
180         if (!q_opt) {
181             printf(FNAME(str), show ? show : cap);
182             while (*str != 0) {
183                 int ch = UChar(*str++);
184                 switch (ch) {
185                 case '\177':
186                     fputs("^?", stdout);
187                     break;
188                 case '\033':
189                     fputs("\\E", stdout);
190                     break;
191                 case '\b':
192                     fputs("\\b", stdout);
193                     break;
194                 case '\f':
195                     fputs("\\f", stdout);
196                     break;
197                 case '\n':
198                     fputs("\\n", stdout);
199                     break;
200                 case '\r':
201                     fputs("\\r", stdout);
202                     break;
203                 case ' ':
204                     fputs("\\s", stdout);
205                     break;
206                 case '\t':
207                     fputs("\\t", stdout);
208                     break;
209                 case '^':
210                     fputs("\\^", stdout);
211                     break;
212                 case ':':
213                     fputs("\\072", stdout);
214                     break;
215                 case '\\':
216                     fputs("\\\\", stdout);
217                     break;
218                 default:
219                     if (isgraph(ch))
220                         fputc(ch, stdout);
221                     else if (ch < 32)
222                         printf("^%c", ch + '@');
223                     else
224                         printf("\\%03o", ch);
225                     break;
226                 }
227             }
228             printf("\n");
229         }
230     } else if ((num = tigetnum(cap)) >= 0) {
231         total_values++;
232         total_n_values++;
233         if (!q_opt) {
234             printf(FNAME(num), show ? show : cap);
235             printf(" %d\n", num);
236         }
237     } else if ((num = tigetflag(cap)) >= 0) {
238         total_values++;
239         total_b_values++;
240         if (!q_opt) {
241             printf(FNAME(flg), show ? show : cap);
242             printf("%s\n", num ? "true" : "false");
243         }
244     }
245
246     if (!q_opt)
247         fflush(stdout);
248 }
249
250 #define isCapName(c) (isalnum(UChar(c)) || ((c) == '_'))
251 #define LegalItem(c,n) (n)
252
253 static void
254 brute_force(const char *name)
255 {
256 #define MAX_FORCE 5             /* omit "colors", since CPU-time is a problem */
257     static const char legal[] = "\
258 0123456789\
259 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
260 abcdefghijklmnopqrstuvwxyz_";
261     int length;
262     int j, k;
263     bool carry;
264     bool changed;
265     char cap[MAX_FORCE + 1];
266     int item[MAX_FORCE + 1];
267
268     if (db_list) {
269         putenv(next_dbitem());
270     }
271     if (!q_opt)
272         printf("Terminal type \"%s\"\n", name);
273     setupterm((NCURSES_CONST char *) name, 1, (int *) 0);
274     if (!q_opt) {
275         if (strcmp(name, ttytype))
276             printf("... actual \"%s\"\n", ttytype);
277     }
278
279     for (length = 1; length <= MAX_FORCE; ++length) {
280         /* set all digits to zeros */
281         for (j = 0; j < length; ++j) {
282             item[j] = LegalItem(j, 0);
283         }
284
285         do {
286             changed = FALSE;
287             /* copy digits to cap-name */
288             for (j = 0; j < length; ++j) {
289                 cap[j] = legal[item[j]];
290             }
291             cap[length] = '\0';
292             dumpit(cap, NULL);
293
294             k = length - 1;
295             do {
296                 carry = FALSE;
297                 for (; k >= 0; --k) {
298                     item[k] += 1;
299                     if (legal[item[k]]) {
300                         changed = TRUE;
301                         break;
302                     }
303                     if (k > 0 &&
304                         legal[item[k - 1] + 1]) {
305                         for (j = k; j < length; ++j) {
306                             item[j] = LegalItem(j, 0);
307                         }
308                         carry = TRUE;
309                         changed = TRUE;
310                     }
311                 }
312             } while (carry);
313         } while (changed);
314     }
315     del_curterm(cur_term);
316 }
317
318 #if USE_CODE_LISTS
319 #define fullname(type,n) f_opt ? type##fnames[n] : cap
320 #else
321 #define fullname(type,n) cap
322 #endif
323
324 static void
325 demo_terminfo(char *name)
326 {
327     unsigned n;
328     NCURSES_CONST char *cap;
329
330     if (db_list) {
331         putenv(next_dbitem());
332     }
333     if (!q_opt)
334         printf("Terminal type \"%s\"\n", name);
335     setupterm(name, 1, (int *) 0);
336
337     if (b_opt) {
338         for (n = 0;; ++n) {
339             cap = my_boolcodes[n];
340             if (cap == 0)
341                 break;
342             dumpit(cap, fullname(bool, n));
343         }
344     }
345
346     if (n_opt) {
347         for (n = 0;; ++n) {
348             cap = my_numcodes[n];
349             if (cap == 0)
350                 break;
351             dumpit(cap, fullname(num, n));
352         }
353     }
354
355     if (s_opt) {
356         for (n = 0;; ++n) {
357             cap = my_strcodes[n];
358             if (cap == 0)
359                 break;
360             dumpit(cap, fullname(str, n));
361         }
362     }
363 #ifdef NCURSES_VERSION
364     if (x_opt && (my_blob == 0)) {
365         int mod;
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                 for (mod = 0; mod < 8; ++mod) {
393                     if (mod == 0) {
394                         /* these happen to be standard - avoid duplicates */
395                         if (!strcmp(xterm_keys[n], "kDC") ||
396                             !strcmp(xterm_keys[n], "kEND") ||
397                             !strcmp(xterm_keys[n], "kHOM") ||
398                             !strcmp(xterm_keys[n], "kLFT") ||
399                             !strcmp(xterm_keys[n], "kRIT")) {
400                             continue;
401                         }
402                         _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
403                                     "%.*s", 8, xterm_keys[n]);
404                     } else {
405                         _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
406                                     "%.*s%d", 8, xterm_keys[n], mod);
407                     }
408                     dumpit(temp, NULL);
409                 }
410             }
411         }
412     }
413 #endif
414     del_curterm(cur_term);
415 }
416
417 typedef enum {
418     pDefault = 0
419     ,pComment
420     ,pDescription
421     ,pEscaped
422     ,pNewline
423     ,pName
424     ,pNumber
425     ,pString
426 } STATE;
427
428 static void
429 parse_description(const char *input_name)
430 {
431     static char empty[1];
432
433     FILE *fp;
434     struct stat sb;
435     size_t count_bools = 0;
436     size_t count_nums = 0;
437     size_t count_strs = 0;
438     size_t len;
439     size_t j, k, jl;
440     STATE state;
441
442     if (stat(input_name, &sb) != 0
443         || (sb.st_mode & S_IFMT) != S_IFREG) {
444         failed("input is not a file");
445     }
446
447     if (sb.st_size == 0) {
448         failed("input is empty");
449     }
450
451     /*
452      * None of the arrays could be larger than the input-file, and since it
453      * is small, just allocate the maximum for simplicity.
454      */
455     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
456         (my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
457           (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
458           (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
459           (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
460           (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
461         failed("cannot allocate memory for input-file");
462     }
463
464     if ((fp = fopen(input_name, "r")) == 0)
465         failed("cannot open input-file");
466     len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
467     my_blob[sb.st_size] = '\0';
468     fclose(fp);
469
470     /*
471      * First, get rid of comments and escaped newlines, as well as repeated
472      * colons to construct a canonical entry.
473      */
474     state = pNewline;
475     for (j = k = 0; j < len; ++j) {
476         int ch = my_blob[j];
477         if (ch == '\t') {
478             ch = ' ';
479         }
480         switch (state) {
481         case pNewline:
482             if (ch == ' ') {
483                 continue;
484             }
485             if (ch == '#') {
486                 state = pComment;
487                 continue;
488             }
489             state = pDefault;
490             /* FALLTHRU */
491         case pDefault:
492             switch (ch) {
493             case '|':
494                 state = pDescription;
495                 continue;
496             case '\\':
497                 state = pEscaped;
498                 continue;
499             case '\n':
500                 state = pNewline;
501                 continue;
502             case ' ':
503                 break;
504             case ',':
505                 my_blob[k++] = (char) ch;
506                 break;
507             default:
508                 if (isalpha(UChar(ch)))
509                     state = pName;
510                 else
511                     fprintf(stderr, "OOPS @%d:%.20s\n", __LINE__, my_blob + j);
512                 my_blob[k++] = (char) ch;
513                 break;
514             }
515             break;
516         case pComment:
517             if (ch == '\n')
518                 state = pNewline;
519             break;
520         case pDescription:
521             switch (ch) {
522             case ',':
523                 state = pDefault;
524                 break;
525             case '\n':
526                 state = pNewline;
527                 break;
528             }
529             break;
530         case pEscaped:
531             if (ch != '\n') {
532                 my_blob[k++] = (char) ch;
533                 state = pDefault;
534             } else {
535                 state = pNewline;
536             }
537             break;
538         case pName:
539             switch (ch) {
540             case '\n':
541                 state = pNewline;
542                 continue;
543             case ' ':
544             case ',':
545                 state = pDefault;
546                 break;
547             case '#':
548                 state = pNumber;
549                 break;
550             case '=':
551                 state = pString;
552                 break;
553             case '|':
554                 state = pDescription;
555                 continue;
556             }
557             my_blob[k++] = (char) ch;
558             break;
559         case pNumber:
560             switch (ch) {
561             case '\n':
562                 state = pNewline;
563                 continue;
564             case ',':
565                 state = pDefault;
566                 break;
567             case ' ':
568                 state = pDefault;
569                 continue;
570             }
571             my_blob[k++] = (char) ch;
572             break;
573         case pString:
574             switch (ch) {
575             case '\n':
576                 state = pNewline;
577                 break;
578             case ',':
579                 state = pDefault;
580                 my_blob[k++] = (char) ch;
581                 break;
582             default:
583                 my_blob[k++] = (char) ch;
584                 break;
585             }
586             break;
587         default:
588             /* not used */
589             break;
590         }
591     }
592     my_blob[k] = '\0';
593
594     /*
595      * Then, parse what's left, making indexes of the names and values.
596      */
597     state = pDefault;
598     for (j = 0; my_blob[j] != '\0'; ++j) {
599         switch (state) {
600         case pDefault:
601             switch (my_blob[j]) {
602             case '\\':
603                 state = pEscaped;
604                 break;
605             case ',':
606                 my_blob[j] = '\0';
607                 if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ',')
608                     state = pName;
609                 break;
610             case ' ':
611                 break;
612             default:
613                 break;
614             }
615         case pEscaped:
616             break;
617         case pName:
618             state = pDefault;
619             if (isalpha(UChar(my_blob[j]))) {
620                 for (jl = 1; isalnum(UChar(my_blob[j + jl])); ++jl) {
621                     ;
622                 }
623             } else {
624                 jl = 0;
625             }
626             if (jl != 0) {
627                 switch (my_blob[j + jl]) {
628                 case '#':
629                     my_numvalues[count_nums] = &my_blob[j + jl + 1];
630                     my_numcodes[count_nums++] = &my_blob[j];
631                     my_blob[j + jl] = '\0';
632                     state = pNumber;
633                     j += jl;
634                     break;
635                 case '=':
636                     my_strvalues[count_strs] = &my_blob[j + jl + 1];
637                     my_strcodes[count_strs++] = &my_blob[j];
638                     my_blob[j + jl] = '\0';
639                     state = pString;
640                     j += jl;
641                     break;
642                 default:
643                     if (my_blob[j + jl] == '@') {
644                         /*
645                          * We cannot get the type for a cancelled item
646                          * directly, but can infer it assuming the input
647                          * came from infocmp, which puts the data in a
648                          * known order.
649                          */
650                         if (count_strs) {
651                             my_strvalues[count_strs] = empty;
652                             my_strcodes[count_strs++] = &my_blob[j];
653                         } else if (count_nums) {
654                             my_numvalues[count_nums] = empty;
655                             my_numcodes[count_nums++] = &my_blob[j];
656                         } else {
657                             my_boolcodes[count_bools++] = &my_blob[j];
658                         }
659                         my_blob[j + jl] = '\0';
660                         j += jl + 1;
661                     } else {
662                         my_boolcodes[count_bools++] = &my_blob[j];
663                         my_blob[j + jl] = '\0';
664                         j += jl;
665                     }
666                     state = (isCapName(my_blob[j + 1])
667                              ? pName
668                              : pDefault);
669                     break;
670                 }
671             }
672             break;
673         case pNumber:
674             if (!isdigit(UChar(my_blob[j]))) {
675                 --j;
676                 state = pDefault;
677             }
678             break;
679         case pString:
680             switch (my_blob[j]) {
681             case '\\':
682                 if (my_blob[j + 1] != '\0') {
683                     ++j;
684                 } else {
685                     --j;
686                     state = pDefault;
687                 }
688                 break;
689             case ',':
690                 --j;
691                 state = pDefault;
692                 break;
693             }
694             break;
695         case pNewline:
696         case pComment:
697         case pDescription:
698         default:
699             break;
700         }
701     }
702     my_boolcodes[count_bools] = 0;
703     my_numcodes[count_nums] = 0;
704     my_numvalues[count_nums] = 0;
705     my_strcodes[count_strs] = 0;
706     my_strvalues[count_strs] = 0;
707
708 #if 0
709     printf("# bools:%d\n", (int) count_bools);
710     for (j = 0; my_boolcodes[j]; ++j)
711         printf("\t%s,\n", my_boolcodes[j]);
712
713     printf("# numbers:%d\n", (int) count_nums);
714     for (j = 0; my_numcodes[j]; ++j)
715         printf("\t%s#%s,\n", my_numcodes[j], my_numvalues[j]);
716
717     printf("# strings:%d\n", (int) count_strs);
718     for (j = 0; my_strcodes[j]; ++j)
719         printf("\t%s=%s,\n", my_strcodes[j], my_strvalues[j]);
720 #endif
721 }
722
723 #if USE_CODE_LISTS
724 static char **
725 copy_code_list(NCURSES_CONST char *const *list)
726 {
727     int pass;
728     size_t count;
729     size_t length = 1;
730     char **result = 0;
731     char *blob = 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             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 */