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