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