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