c6abc028b949ad886d12f494379c9426e1dcf832
[ncurses.git] / test / demo_terminfo.c
1 /****************************************************************************
2  * Copyright (c) 2009-2015,2016 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.41 2016/06/04 23:19:32 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, f_opt ? 24 : 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, const char *show)
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), show ? show : 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), show ? show : 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), show ? show : 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, NULL);
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 static void
316 demo_terminfo(char *name)
317 {
318     unsigned n;
319     NCURSES_CONST char *cap;
320
321     if (db_list) {
322         putenv(next_dbitem());
323     }
324     if (!q_opt)
325         printf("Terminal type \"%s\"\n", name);
326     setupterm(name, 1, (int *) 0);
327
328     if (b_opt) {
329         for (n = 0;; ++n) {
330             cap = my_boolcodes[n];
331             if (cap == 0)
332                 break;
333             dumpit(cap, f_opt ? boolfnames[n] : cap);
334         }
335     }
336
337     if (n_opt) {
338         for (n = 0;; ++n) {
339             cap = my_numcodes[n];
340             if (cap == 0)
341                 break;
342             dumpit(cap, f_opt ? numfnames[n] : cap);
343         }
344     }
345
346     if (s_opt) {
347         for (n = 0;; ++n) {
348             cap = my_strcodes[n];
349             if (cap == 0)
350                 break;
351             dumpit(cap, f_opt ? strfnames[n] : cap);
352         }
353     }
354 #ifdef NCURSES_VERSION
355     if (x_opt && (my_blob == 0)) {
356         int mod;
357         if (y_opt) {
358 #if NCURSES_XNAMES
359             TERMTYPE *term = &(cur_term->type);
360             if (term != 0
361                 && ((NUM_BOOLEANS(term) != BOOLCOUNT)
362                     || (NUM_NUMBERS(term) != NUMCOUNT)
363                     || (NUM_STRINGS(term) != STRCOUNT))) {
364                 for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
365                     dumpit(ExtBoolname(term, (int) n, boolnames), NULL);
366                 }
367                 for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
368                     dumpit(ExtNumname(term, (int) n, numnames), NULL);
369                 }
370                 for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
371                     dumpit(ExtStrname(term, (int) n, strnames), NULL);
372                 }
373             }
374 #endif
375         } else {
376             char temp[80];
377             static const char *xterm_keys[] =
378             {
379                 "kDC", "kDN", "kEND", "kHOM", "kIC",
380                 "kLFT", "kNXT", "kPRV", "kRIT", "kUP",
381             };
382             for (n = 0; n < SIZEOF(xterm_keys); ++n) {
383                 for (mod = 0; mod < 8; ++mod) {
384                     if (mod == 0) {
385                         /* these happen to be standard - avoid duplicates */
386                         if (!strcmp(xterm_keys[n], "kDC") ||
387                             !strcmp(xterm_keys[n], "kEND") ||
388                             !strcmp(xterm_keys[n], "kHOM") ||
389                             !strcmp(xterm_keys[n], "kLFT") ||
390                             !strcmp(xterm_keys[n], "kRIT")) {
391                             continue;
392                         }
393                         sprintf(temp, "%.*s", 8, xterm_keys[n]);
394                     } else {
395                         sprintf(temp, "%.*s%d", 8, xterm_keys[n], mod);
396                     }
397                     dumpit(temp, NULL);
398                 }
399             }
400         }
401     }
402 #endif
403     del_curterm(cur_term);
404 }
405
406 typedef enum {
407     pDefault = 0
408     ,pComment
409     ,pDescription
410     ,pEscaped
411     ,pNewline
412     ,pName
413     ,pNumber
414     ,pString
415 } STATE;
416
417 static void
418 parse_description(const char *input_name)
419 {
420     static char empty[1];
421
422     FILE *fp;
423     struct stat sb;
424     size_t count_bools = 0;
425     size_t count_nums = 0;
426     size_t count_strs = 0;
427     size_t len;
428     size_t j, k, jl;
429     STATE state;
430
431     if (stat(input_name, &sb) != 0
432         || (sb.st_mode & S_IFMT) != S_IFREG) {
433         failed("input is not a file");
434     }
435
436     if (sb.st_size == 0) {
437         failed("input is empty");
438     }
439
440     /*
441      * None of the arrays could be larger than the input-file, and since it
442      * is small, just allocate the maximum for simplicity.
443      */
444     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
445         (my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
446           (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
447           (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
448           (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
449           (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
450         failed("cannot allocate memory for input-file");
451     }
452
453     if ((fp = fopen(input_name, "r")) == 0)
454         failed("cannot open input-file");
455     len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
456     my_blob[sb.st_size] = '\0';
457     fclose(fp);
458
459     /*
460      * First, get rid of comments and escaped newlines, as well as repeated
461      * colons to construct a canonical entry.
462      */
463     state = pNewline;
464     for (j = k = 0; j < len; ++j) {
465         int ch = my_blob[j];
466         if (ch == '\t') {
467             ch = ' ';
468         }
469         switch (state) {
470         case pNewline:
471             if (ch == ' ') {
472                 continue;
473             }
474             if (ch == '#') {
475                 state = pComment;
476                 continue;
477             }
478             state = pDefault;
479             /* FALLTHRU */
480         case pDefault:
481             switch (ch) {
482             case '|':
483                 state = pDescription;
484                 continue;
485             case '\\':
486                 state = pEscaped;
487                 continue;
488             case '\n':
489                 state = pNewline;
490                 continue;
491             case ' ':
492                 break;
493             case ',':
494                 my_blob[k++] = (char) ch;
495                 break;
496             default:
497                 if (isalpha(UChar(ch)))
498                     state = pName;
499                 else
500                     fprintf(stderr, "OOPS @%d:%.20s\n", __LINE__, my_blob + j);
501                 my_blob[k++] = (char) ch;
502                 break;
503             }
504             break;
505         case pComment:
506             if (ch == '\n')
507                 state = pNewline;
508             break;
509         case pDescription:
510             switch (ch) {
511             case ',':
512                 state = pDefault;
513                 break;
514             case '\n':
515                 state = pNewline;
516                 break;
517             }
518             break;
519         case pEscaped:
520             if (ch != '\n') {
521                 my_blob[k++] = (char) ch;
522                 state = pDefault;
523             } else {
524                 state = pNewline;
525             }
526             break;
527         case pName:
528             switch (ch) {
529             case '\n':
530                 state = pNewline;
531                 continue;
532             case ' ':
533             case ',':
534                 state = pDefault;
535                 break;
536             case '#':
537                 state = pNumber;
538                 break;
539             case '=':
540                 state = pString;
541                 break;
542             case '|':
543                 state = pDescription;
544                 continue;
545             }
546             my_blob[k++] = (char) ch;
547             break;
548         case pNumber:
549             switch (ch) {
550             case '\n':
551                 state = pNewline;
552                 continue;
553             case ',':
554                 state = pDefault;
555                 break;
556             case ' ':
557                 state = pDefault;
558                 continue;
559             }
560             my_blob[k++] = (char) ch;
561             break;
562         case pString:
563             switch (ch) {
564             case '\n':
565                 state = pNewline;
566                 break;
567             case ',':
568                 state = pDefault;
569                 my_blob[k++] = (char) ch;
570                 break;
571             default:
572                 my_blob[k++] = (char) ch;
573                 break;
574             }
575             break;
576         default:
577             /* not used */
578             break;
579         }
580     }
581     my_blob[k] = '\0';
582
583     /*
584      * Then, parse what's left, making indexes of the names and values.
585      */
586     state = pDefault;
587     for (j = 0; my_blob[j] != '\0'; ++j) {
588         switch (state) {
589         case pDefault:
590             switch (my_blob[j]) {
591             case '\\':
592                 state = pEscaped;
593                 break;
594             case ',':
595                 my_blob[j] = '\0';
596                 if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ',')
597                     state = pName;
598                 break;
599             case ' ':
600                 break;
601             default:
602                 break;
603             }
604         case pEscaped:
605             break;
606         case pName:
607             state = pDefault;
608             if (isalpha(UChar(my_blob[j]))) {
609                 for (jl = 1; isalnum(UChar(my_blob[j + jl])); ++jl) {
610                     ;
611                 }
612             } else {
613                 jl = 0;
614             }
615             if (jl != 0) {
616                 switch (my_blob[j + jl]) {
617                 case '#':
618                     my_numvalues[count_nums] = &my_blob[j + jl + 1];
619                     my_numcodes[count_nums++] = &my_blob[j];
620                     my_blob[j + jl] = '\0';
621                     state = pNumber;
622                     j += jl;
623                     break;
624                 case '=':
625                     my_strvalues[count_strs] = &my_blob[j + jl + 1];
626                     my_strcodes[count_strs++] = &my_blob[j];
627                     my_blob[j + jl] = '\0';
628                     state = pString;
629                     j += jl;
630                     break;
631                 default:
632                     if (my_blob[j + jl] == '@') {
633                         /*
634                          * We cannot get the type for a cancelled item
635                          * directly, but can infer it assuming the input
636                          * came from infocmp, which puts the data in a
637                          * known order.
638                          */
639                         if (count_strs) {
640                             my_strvalues[count_strs] = empty;
641                             my_strcodes[count_strs++] = &my_blob[j];
642                         } else if (count_nums) {
643                             my_numvalues[count_nums] = empty;
644                             my_numcodes[count_nums++] = &my_blob[j];
645                         } else {
646                             my_boolcodes[count_bools++] = &my_blob[j];
647                         }
648                         my_blob[j + jl] = '\0';
649                         j += jl + 1;
650                     } else {
651                         my_boolcodes[count_bools++] = &my_blob[j];
652                         my_blob[j + jl] = '\0';
653                         j += jl;
654                     }
655                     state = (isCapName(my_blob[j + 1])
656                              ? pName
657                              : pDefault);
658                     break;
659                 }
660             }
661             break;
662         case pNumber:
663             if (!isdigit(UChar(my_blob[j]))) {
664                 --j;
665                 state = pDefault;
666             }
667             break;
668         case pString:
669             switch (my_blob[j]) {
670             case '\\':
671                 if (my_blob[j + 1] != '\0') {
672                     ++j;
673                 } else {
674                     --j;
675                     state = pDefault;
676                 }
677                 break;
678             case ',':
679                 --j;
680                 state = pDefault;
681                 break;
682             }
683             break;
684         case pNewline:
685         case pComment:
686         case pDescription:
687         default:
688             break;
689         }
690     }
691     my_boolcodes[count_bools] = 0;
692     my_numcodes[count_nums] = 0;
693     my_numvalues[count_nums] = 0;
694     my_strcodes[count_strs] = 0;
695     my_strvalues[count_strs] = 0;
696
697 #if 0
698     printf("# bools:%d\n", (int) count_bools);
699     for (j = 0; my_boolcodes[j]; ++j)
700         printf("\t%s,\n", my_boolcodes[j]);
701
702     printf("# numbers:%d\n", (int) count_nums);
703     for (j = 0; my_numcodes[j]; ++j)
704         printf("\t%s#%s,\n", my_numcodes[j], my_numvalues[j]);
705
706     printf("# strings:%d\n", (int) count_strs);
707     for (j = 0; my_strcodes[j]; ++j)
708         printf("\t%s=%s,\n", my_strcodes[j], my_strvalues[j]);
709 #endif
710 }
711
712 #if USE_CODE_LISTS
713 static char **
714 copy_code_list(NCURSES_CONST char *const *list)
715 {
716     int pass;
717     size_t count;
718     size_t length = 1;
719     char **result = 0;
720     char *blob = 0;
721     char *unused = 0;
722
723     for (pass = 0; pass < 2; ++pass) {
724         for (count = 0; list[count] != 0; ++count) {
725             size_t chunk = strlen(list[count]) + 1;
726             if (pass == 0) {
727                 length += chunk;
728             } else {
729                 result[count] = unused;
730                 strcpy(unused, list[count]);
731                 unused += chunk;
732             }
733         }
734         if (pass == 0) {
735             blob = malloc(length);
736             result = typeCalloc(char *, count + 1);
737             unused = blob;
738             if (blob == 0 || result == 0)
739                 failed("copy_code_list failed");
740         }
741     }
742
743     return result;
744 }
745 #endif
746
747 static void
748 usage(void)
749 {
750     static const char *msg[] =
751     {
752         "Usage: demo_terminfo [options] [terminal]",
753         "",
754         "If no options are given, print all (boolean, numeric, string)",
755         "capabilities for the given terminal, using short names.",
756         "",
757         "Options:",
758         " -a       try all names, print capabilities found",
759         " -b       print boolean-capabilities",
760         " -d LIST  colon-separated list of databases to use",
761         " -e NAME  environment variable to set with -d option",
762         " -f       print full names",
763         " -i NAME  terminal description to use as names for \"-a\" option",
764         " -n       print numeric-capabilities",
765         " -q       quiet (prints only counts)",
766         " -r COUNT repeat for given count",
767         " -s       print string-capabilities",
768 #ifdef NCURSES_VERSION
769         " -x       print extended capabilities",
770         " -y       direct-lookup names of extended capabilities",
771 #endif
772     };
773     unsigned n;
774     for (n = 0; n < SIZEOF(msg); ++n) {
775         fprintf(stderr, "%s\n", msg[n]);
776     }
777     ExitProgram(EXIT_FAILURE);
778 }
779
780 int
781 main(int argc, char *argv[])
782 {
783     int n;
784     int repeat;
785     char *name;
786     int r_opt = 1;
787     char *input_name = 0;
788
789     while ((n = getopt(argc, argv, "abd:e:fi:nqr:sxy")) != -1) {
790         switch (n) {
791         case 'a':
792             a_opt = TRUE;
793             break;
794         case 'b':
795             b_opt = TRUE;
796             break;
797         case 'd':
798             d_opt = optarg;
799             break;
800         case 'e':
801             e_opt = optarg;
802             break;
803         case 'f':
804             f_opt = TRUE;
805             break;
806         case 'i':
807             input_name = optarg;
808             break;
809         case 'n':
810             n_opt = TRUE;
811             break;
812         case 'q':
813             q_opt = TRUE;
814             break;
815         case 'r':
816             if ((r_opt = atoi(optarg)) <= 0)
817                 usage();
818             break;
819         case 's':
820             s_opt = TRUE;
821             break;
822 #ifdef NCURSES_VERSION
823         case 'x':
824             x_opt = TRUE;
825             break;
826         case 'y':
827             y_opt = TRUE;
828             x_opt = TRUE;
829             break;
830 #endif
831         default:
832             usage();
833             break;
834         }
835     }
836
837 #if HAVE_USE_EXTENDED_NAMES
838     use_extended_names(x_opt);
839 #endif
840
841     if (!(b_opt || n_opt || s_opt)) {
842         b_opt = TRUE;
843         n_opt = TRUE;
844         s_opt = TRUE;
845     }
846
847     make_dblist();
848
849     if (a_opt) {
850         for (repeat = 0; repeat < r_opt; ++repeat) {
851             if (optind < argc) {
852                 for (n = optind; n < argc; ++n) {
853                     brute_force(argv[n]);
854                 }
855             } else if ((name = getenv("TERM")) != 0) {
856                 brute_force(name);
857             } else {
858                 static char dumb[] = "dumb";
859                 brute_force(dumb);
860             }
861         }
862     } else {
863         if (input_name != 0) {
864             parse_description(input_name);
865         }
866 #if USE_CODE_LISTS
867         else {
868             my_boolcodes = copy_code_list(boolnames);
869             my_numcodes = copy_code_list(numnames);
870             my_strcodes = copy_code_list(strnames);
871         }
872 #else
873         else {
874             failed("no capability-lists available (use -i option)");
875         }
876 #endif /* USE_CODE_LISTS */
877         for (repeat = 0; repeat < r_opt; ++repeat) {
878             if (optind < argc) {
879                 for (n = optind; n < argc; ++n) {
880                     demo_terminfo(argv[n]);
881                 }
882             } else if ((name = getenv("TERM")) != 0) {
883                 demo_terminfo(name);
884             } else {
885                 static char dumb[] = "dumb";
886                 demo_terminfo(dumb);
887             }
888         }
889     }
890
891 #define PLURAL(n) n, (n != 1) ? "s" : ""
892     printf("%ld value%s (%ld boolean%s, %ld number%s, %ld string%s)\n",
893            PLURAL(total_values),
894            PLURAL(total_b_values),
895            PLURAL(total_n_values),
896            PLURAL(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     failed("This program requires the terminfo functions such as tigetstr");
918     ExitProgram(EXIT_FAILURE);
919 }
920 #endif /* HAVE_TIGETSTR */