]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - test/demo_termcap.c
ncurses 6.1 - patch 20180127
[ncurses.git] / test / demo_termcap.c
index 4289de0540e3cfba1a008300646cbc4bafad9992..e252d1d9814400f1c75dc49547eb2223c5a9ee2a 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
 /****************************************************************************
- * Copyright (c) 2005-2008,2009 Free Software Foundation, Inc.              *
+ * Copyright (c) 2005-2016,2017 Free Software Foundation, Inc.              *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
 /*
  * Author: Thomas E. Dickey
  *
 /*
  * Author: Thomas E. Dickey
  *
- * $Id: demo_termcap.c,v 1.12 2009/10/10 16:01:41 tom Exp $
+ * $Id: demo_termcap.c,v 1.56 2017/12/26 22:41:47 tom Exp $
  *
  * A simple demo of the termcap interface.
  */
 #define USE_TINFO
 #include <test.priv.h>
  *
  * A simple demo of the termcap interface.
  */
 #define USE_TINFO
 #include <test.priv.h>
+#include <sys/stat.h>
+
+#if NCURSES_XNAMES
+#if HAVE_TERM_ENTRY_H
+#include <term_entry.h>
+#else
+#undef NCURSES_XNAMES
+#define NCURSES_XNAMES 0
+#endif
+#endif
+
+#if defined(NCURSES_VERSION) && HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+
+static void failed(const char *) GCC_NORETURN;
+
+static void
+failed(const char *msg)
+{
+    fprintf(stderr, "%s\n", msg);
+    ExitProgram(EXIT_FAILURE);
+}
 
 #if HAVE_TGETENT
 
 
 #if HAVE_TGETENT
 
 #define FCOLS 8
 #define FNAME(type) "%s %-*s = ", #type, FCOLS
 
 #define FCOLS 8
 #define FNAME(type) "%s %-*s = ", #type, FCOLS
 
-#if USE_CODE_LISTS
 static bool b_opt = FALSE;
 static bool n_opt = FALSE;
 static bool s_opt = FALSE;
 static bool b_opt = FALSE;
 static bool n_opt = FALSE;
 static bool s_opt = FALSE;
+static bool q_opt = FALSE;
+#ifdef NCURSES_VERSION
+static bool x_opt = FALSE;
+static bool y_opt = FALSE;
 #endif
 
 #endif
 
-#define isCapName(c) (isgraph(c) && strchr("^#=:\\", c) == 0)
+static char *d_opt;
+static char *e_opt;
+static char **db_list;
+static int db_item;
+
+static char *my_blob;
+static char **my_boolcodes;
+static char **my_numcodes;
+static char **my_numvalues;
+static char **my_strcodes;
+static char **my_strvalues;
+
+static long total_values;
+static long total_b_values;
+static long total_n_values;
+static long total_s_values;
+
+#define isCapName(c) (isgraph(c) && strchr("^=:\\", c) == 0)
+#define EachCapName(n) n = 33; n < 127; ++n
+
+static char *
+make_dbitem(char *p, char *q)
+{
+    size_t need = strlen(e_opt) + 2 + (size_t) (p - q);
+    char *result = malloc(need);
+    _nc_SPRINTF(result, _nc_SLIMIT(need) "%s=%.*s", e_opt, (int) (p - q), q);
+    return result;
+}
 
 static void
 
 static void
-dumpit(NCURSES_CONST char *cap)
+make_dblist(void)
 {
 {
-    /*
-     * One of the limitations of the termcap interface is that the library
-     * cannot determine the size of the buffer passed via tgetstr(), nor the
-     * amount of space remaining.  This demo simply reuses the whole buffer
-     * for each call; a normal termcap application would try to use the buffer
-     * to hold all of the strings extracted from the terminal entry.
-     */
-    char area[1024], *ap = area;
-    char *str;
-    int num;
+    if (d_opt && e_opt) {
+       int pass;
 
 
-    if ((str = tgetstr(cap, &ap)) != 0) {
-       /*
-        * Note that the strings returned are mostly terminfo format, since
-        * ncurses does not convert except for a handful of special cases.
-        */
-       printf(FNAME(str), cap);
-       while (*str != 0) {
-           int ch = UChar(*str++);
+       for (pass = 0; pass < 2; ++pass) {
+           char *p, *q;
+           size_t count = 0;
+
+           for (p = q = d_opt; *p != '\0'; ++p) {
+               if (*p == ':') {
+                   if (p != q + 1) {
+                       if (pass) {
+                           db_list[count] = make_dbitem(p, q);
+                       }
+                       count++;
+                   }
+                   q = p + 1;
+               }
+           }
+           if (p != q + 1) {
+               if (pass) {
+                   db_list[count] = make_dbitem(p, q);
+               }
+               count++;
+           }
+           if (!pass) {
+               db_list = typeCalloc(char *, count + 1);
+           }
+       }
+    }
+}
+
+static char *
+next_dbitem(void)
+{
+    char *result = 0;
+
+    if (db_list) {
+       if ((result = db_list[db_item]) == 0) {
+           db_item = 0;
+           result = db_list[0];
+       } else {
+           db_item++;
+       }
+    }
+    printf("** %s\n", result);
+    return result;
+}
+
+#if NO_LEAKS
+static void
+free_dblist(void)
+{
+    if (db_list) {
+       int n;
+       for (n = 0; db_list[n]; ++n)
+           free(db_list[n]);
+       free(db_list);
+       db_list = 0;
+    }
+}
+#endif /* NO_LEAKS */
+
+static void
+show_string(const char *name, const char *value)
+{
+    printf(FNAME(str), name);
+    if (value == ((char *) -1)) {
+       printf("CANCELLED");
+    } else if (value == ((char *) 0)) {
+       printf("ABSENT");
+    } else {
+       while (*value != 0) {
+           int ch = UChar(*value++);
            switch (ch) {
            case '\177':
                fputs("^?", stdout);
            switch (ch) {
            case '\177':
                fputs("^?", stdout);
@@ -121,15 +228,58 @@ dumpit(NCURSES_CONST char *cap)
                break;
            }
        }
                break;
            }
        }
-       printf("\n");
+    }
+    printf("\n");
+}
+
+static void
+show_number(const char *name, int value)
+{
+    printf(FNAME(num), name);
+    printf(" %d\n", value);
+}
+
+static void
+dumpit(NCURSES_CONST char *cap)
+{
+    /*
+     * One of the limitations of the termcap interface is that the library
+     * cannot determine the size of the buffer passed via tgetstr(), nor the
+     * amount of space remaining.  This demo simply reuses the whole buffer
+     * for each call; a normal termcap application would try to use the buffer
+     * to hold all of the strings extracted from the terminal entry.
+     */
+    char area[1024], *ap = area;
+    char *str;
+    int num;
+
+    if ((str = tgetstr(cap, &ap)) != 0) {
+       total_values++;
+       total_s_values++;
+       if (!q_opt) {
+           /*
+            * Note that the strings returned are mostly terminfo format, since
+            * ncurses does not convert except for a handful of special cases.
+            */
+           show_string(cap, str);
+       }
     } else if ((num = tgetnum(cap)) >= 0) {
     } else if ((num = tgetnum(cap)) >= 0) {
-       printf(FNAME(num), cap);
-       printf(" %d\n", num);
-    } else if ((num = tgetflag(cap)) > 0) {
-       printf(FNAME(flg), cap);
-       printf("%s\n", "true");
+       total_values++;
+       total_n_values++;
+       if (!q_opt) {
+           show_number(cap, num);
+       }
+    } else if (tgetflag(cap) > 0) {
+       total_values++;
+       total_b_values++;
+       if (!q_opt) {
+           printf(FNAME(flg), cap);
+           printf("%s\n", "true");
+       }
     }
     }
-    fflush(stdout);
+
+    if (!q_opt)
+       fflush(stdout);
 }
 
 static void
 }
 
 static void
@@ -137,16 +287,20 @@ brute_force(const char *name)
 {
     char buffer[1024];
 
 {
     char buffer[1024];
 
-    printf("Terminal type %s\n", name);
+    if (db_list) {
+       putenv(next_dbitem());
+    }
+    if (!q_opt)
+       printf("Terminal type \"%s\"\n", name);
     if (tgetent(buffer, name) >= 0) {
        char cap[3];
        int c1, c2;
 
        cap[2] = 0;
     if (tgetent(buffer, name) >= 0) {
        char cap[3];
        int c1, c2;
 
        cap[2] = 0;
-       for (c1 = 0; c1 < 256; ++c1) {
+       for (EachCapName(c1)) {
            cap[0] = (char) c1;
            if (isCapName(c1)) {
            cap[0] = (char) c1;
            if (isCapName(c1)) {
-               for (c2 = 0; c2 < 256; ++c2) {
+               for (EachCapName(c2)) {
                    cap[1] = (char) c2;
                    if (isCapName(c2)) {
                        dumpit(cap);
                    cap[1] = (char) c2;
                    if (isCapName(c2)) {
                        dumpit(cap);
@@ -157,50 +311,433 @@ brute_force(const char *name)
     }
 }
 
     }
 }
 
-#if USE_CODE_LISTS
+#if NCURSES_XNAMES
+static void
+dump_xname(NCURSES_CONST char *cap)
+{
+    if (strlen(cap) == 2)
+       dumpit(cap);
+}
+#endif
+
 static void
 static void
-demo_terminfo(NCURSES_CONST char *name)
+demo_termcap(NCURSES_CONST char *name)
 {
     unsigned n;
     NCURSES_CONST char *cap;
 {
     unsigned n;
     NCURSES_CONST char *cap;
+    char buffer[1024];
 
 
-    printf("Terminal type \"%s\"\n", name);
-    setupterm(name, 1, (int *) 0);
+    if (db_list) {
+       putenv(next_dbitem());
+    }
+    if (!q_opt)
+       printf("Terminal type \"%s\"\n", name);
+    if (tgetent(buffer, name) >= 0) {
 
 
-    if (b_opt) {
-       for (n = 0;; ++n) {
-           cap = boolcodes[n];
-           if (cap == 0)
-               break;
-           dumpit(cap);
+       if (b_opt) {
+           for (n = 0;; ++n) {
+               cap = my_boolcodes[n];
+               if (cap == 0)
+                   break;
+               dumpit(cap);
+           }
        }
        }
+
+       if (n_opt) {
+           for (n = 0;; ++n) {
+               cap = my_numcodes[n];
+               if (cap == 0)
+                   break;
+               dumpit(cap);
+           }
+       }
+
+       if (s_opt) {
+           for (n = 0;; ++n) {
+               cap = my_strcodes[n];
+               if (cap == 0)
+                   break;
+               dumpit(cap);
+           }
+       }
+#ifdef NCURSES_VERSION
+       if (x_opt && (my_blob == 0) && y_opt) {
+#if NCURSES_XNAMES
+           TERMTYPE *term = (TERMTYPE *) cur_term;
+           if (term != 0
+               && ((NUM_BOOLEANS(term) != BOOLCOUNT)
+                   || (NUM_NUMBERS(term) != NUMCOUNT)
+                   || (NUM_STRINGS(term) != STRCOUNT))) {
+               for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
+                   dump_xname(ExtBoolname(term, (int) n, boolnames));
+               }
+               for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
+                   dump_xname(ExtNumname(term, (int) n, numnames));
+               }
+               for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
+                   dump_xname(ExtStrname(term, (int) n, strnames));
+               }
+           }
+#endif
+       }
+#endif
+    }
+}
+
+typedef enum {
+    pDefault = 0
+    ,pComment
+    ,pDescription
+    ,pEscaped
+    ,pNewline
+    ,pName
+    ,pNumber
+    ,pString
+} STATE;
+
+static void
+parse_description(const char *input_name)
+{
+    static char empty[1];
+
+    FILE *fp;
+    struct stat sb;
+    size_t count_bools = 0;
+    size_t count_nums = 0;
+    size_t count_strs = 0;
+    size_t len;
+    size_t j, k;
+    STATE state;
+
+    if (stat(input_name, &sb) != 0
+       || (sb.st_mode & S_IFMT) != S_IFREG) {
+       failed("input is not a file");
+    }
+
+    if (sb.st_size == 0) {
+       failed("input is empty");
+    }
+
+    /*
+     * None of the arrays could be larger than the input-file, and since it
+     * is small, just allocate the maximum for simplicity.
+     */
+    if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0 ||
+       (my_boolcodes = typeCalloc(char *, sb.st_size)) == 0 ||
+         (my_numcodes = typeCalloc(char *, sb.st_size)) == 0 ||
+         (my_numvalues = typeCalloc(char *, sb.st_size)) == 0 ||
+         (my_strcodes = typeCalloc(char *, sb.st_size)) == 0 ||
+         (my_strvalues = typeCalloc(char *, sb.st_size)) == 0) {
+       failed("cannot allocate memory for input-file");
     }
 
     }
 
-    if (n_opt) {
-       for (n = 0;; ++n) {
-           cap = numcodes[n];
-           if (cap == 0)
+    if ((fp = fopen(input_name, "r")) == 0)
+       failed("cannot open input-file");
+    len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
+    my_blob[sb.st_size] = '\0';
+    fclose(fp);
+
+    /*
+     * First, get rid of comments and escaped newlines, as well as repeated
+     * colons to construct a canonical entry.
+     *
+     * FIXME: actually this should make an additional pass just to strip
+     * comment-lines and escaped newlines.  But it is workable for infocmp
+     * output.
+     */
+    state = pNewline;
+    for (j = k = 0; j < len; ++j) {
+       int ch = my_blob[j];
+       if (ch == '\t') {
+           ch = ' ';
+       }
+       switch (state) {
+       case pNewline:
+           if (ch == ' ') {
+               continue;
+           }
+           if (ch == '#') {
+               state = pComment;
+               continue;
+           }
+           state = pDefault;
+           /* FALLTHRU */
+       case pDefault:
+           switch (ch) {
+           case '|':
+               state = pDescription;
+               continue;
+           case '\\':
+               state = pEscaped;
+               continue;
+           case '\n':
+               state = pNewline;
+               continue;
+           case ' ':
+           case ':':
                break;
                break;
-           dumpit(cap);
+           default:
+               state = pName;
+               break;
+           }
+           my_blob[k++] = (char) ch;
+           break;
+       case pComment:
+           if (ch == '\n')
+               state = pNewline;
+           break;
+       case pDescription:
+           switch (ch) {
+           case ':':
+               state = pDefault;
+               break;
+           case '\n':
+               state = pNewline;
+               break;
+           }
+           break;
+       case pEscaped:
+           if (ch != '\n') {
+               my_blob[k++] = (char) ch;
+               state = pDefault;
+           } else {
+               state = pNewline;
+           }
+           break;
+       case pName:
+           switch (ch) {
+           case '\n':
+               state = pNewline;
+               continue;
+           case ' ':
+           case ':':
+               state = pDefault;
+               break;
+           case '#':
+               state = pNumber;
+               break;
+           case '|':
+               state = pDescription;
+               continue;
+           }
+           my_blob[k++] = (char) ch;
+           break;
+       case pNumber:
+           switch (ch) {
+           case '\n':
+               state = pNewline;
+               continue;
+           case ':':
+               state = pDefault;
+               break;
+           case ' ':
+               state = pDefault;
+               continue;
+           }
+           my_blob[k++] = (char) ch;
+           break;
+       case pString:
+           switch (ch) {
+           case '\\':
+               if (my_blob[j + 1] == '\0') {
+                   state = pDefault;
+                   continue;
+               }
+               break;
+           case '\n':
+               state = pNewline;
+               continue;
+           case ':':
+               state = pDefault;
+               break;
+           }
+           my_blob[k++] = (char) ch;
+           break;
+       default:
+           /* not used */
+           break;
        }
     }
        }
     }
+    my_blob[k] = '\0';
 
 
-    if (s_opt) {
-       for (n = 0;; ++n) {
-           cap = strcodes[n];
-           if (cap == 0)
+    /*
+     * Then, parse what's left, making indexes of the names and values.
+     */
+    state = pDefault;
+    for (j = 0; my_blob[j] != '\0'; ++j) {
+       switch (state) {
+       case pDefault:
+           switch (my_blob[j]) {
+           case '\\':
+               state = pEscaped;
+               break;
+           case ':':
+               my_blob[j] = '\0';
+               if (my_blob[j + 1] != '\0' && my_blob[j + 1] != ':')
+                   state = pName;
+               break;
+           case ' ':
+               break;
+           default:
+               break;
+           }
+       case pEscaped:
+           break;
+       case pName:
+           state = pDefault;
+           /*
+            * Commented-out capabilities might be accessible (they are in
+            * ncurses).
+            */
+           if (my_blob[j] == '.' && my_blob[j + 1] == '.') {
+               j += 2;
+           }
+           if (my_blob[j + 1] != '\0') {
+               switch (my_blob[j + 2]) {
+               case '#':
+                   my_numvalues[count_nums] = &my_blob[j + 3];
+                   my_numcodes[count_nums++] = &my_blob[j];
+                   my_blob[j + 2] = '\0';
+                   state = pNumber;
+                   j += 2;
+                   break;
+               case '=':
+                   my_strvalues[count_strs] = &my_blob[j + 3];
+                   my_strcodes[count_strs++] = &my_blob[j];
+                   my_blob[j + 2] = '\0';
+                   state = pString;
+                   j += 2;
+                   break;
+               default:
+                   if (my_blob[j + 2] == '@') {
+                       /*
+                        * We cannot get the type for a cancelled item
+                        * directly, but can infer it assuming the input
+                        * came from infocmp, which puts the data in a
+                        * known order.
+                        */
+                       if (count_strs) {
+                           my_strvalues[count_strs] = empty;
+                           my_strcodes[count_strs++] = &my_blob[j];
+                       } else if (count_nums) {
+                           my_numvalues[count_nums] = empty;
+                           my_numcodes[count_nums++] = &my_blob[j];
+                       } else {
+                           my_boolcodes[count_bools++] = &my_blob[j];
+                       }
+                   } else {
+                       my_boolcodes[count_bools++] = &my_blob[j];
+                   }
+                   j++;
+                   break;
+               }
+           }
+           break;
+       case pNumber:
+           if (!isdigit(UChar(my_blob[j]))) {
+               --j;
+               state = pDefault;
+           }
+           break;
+       case pString:
+           switch (my_blob[j]) {
+           case '\\':
+               if (my_blob[j + 1] == '\0') {
+                   state = pDefault;
+                   continue;
+               } else {
+                   ++j;
+               }
                break;
                break;
-           dumpit(cap);
+           case '\n':
+               state = pNewline;
+               continue;
+           case ':':
+               --j;
+               state = pDefault;
+               break;
+           }
+           break;
+       case pNewline:
+       case pComment:
+       case pDescription:
+       default:
+           break;
+       }
+    }
+    my_boolcodes[count_bools] = 0;
+    my_numcodes[count_nums] = 0;
+    my_numvalues[count_nums] = 0;
+    my_strcodes[count_strs] = 0;
+    my_strvalues[count_strs] = 0;
+
+#if 0
+    printf("bools:%d\n", (int) count_bools);
+    for (j = 0; my_boolcodes[j]; ++j)
+       printf("%5d:%s\n", (int) j, my_boolcodes[j]);
+
+    printf("numbers:%d\n", (int) count_nums);
+    for (j = 0; my_numcodes[j]; ++j)
+       printf("%5d:%s(%s)\n", (int) j, my_numcodes[j], my_numvalues[j]);
+
+    printf("strings:%d\n", (int) count_strs);
+    for (j = 0; my_strcodes[j]; ++j)
+       printf("%5d:%s(%s)\n", (int) j, my_strcodes[j], my_strvalues[j]);
+#endif
+}
+
+#if USE_CODE_LISTS
+static char **
+copy_code_list(NCURSES_CONST char *const *list)
+{
+    int pass;
+    size_t count;
+    size_t length = 1;
+    char **result = 0;
+    char *blob = 0;
+    char *unused = 0;
+
+    for (pass = 0; pass < 2; ++pass) {
+       for (count = 0; list[count] != 0; ++count) {
+           size_t chunk = strlen(list[count]) + 1;
+           if (pass == 0) {
+               length += chunk;
+           } else {
+               result[count] = unused;
+               _nc_STRCPY(unused, list[count], length);
+               unused += chunk;
+           }
+       }
+       if (pass == 0) {
+           blob = malloc(length);
+           result = typeCalloc(char *, count + 1);
+           unused = blob;
+           if (blob == 0 || result == 0)
+               failed("copy_code_list failed");
        }
     }
        }
     }
+
+    return result;
+}
+
+#if NO_LEAKS
+static void
+free_code_list(char **list)
+{
+    if (list) {
+       free(list[0]);
+       free(list);
+    }
 }
 }
+#endif /* NO_LEAKS */
+#endif /* USE_CODE_LISTS */
 
 static void
 usage(void)
 {
     static const char *msg[] =
     {
 
 static void
 usage(void)
 {
     static const char *msg[] =
     {
-       "Usage: demo_terminfo [options] [terminal]",
+       "Usage: demo_termcap [options] [terminal]",
        "",
        "If no options are given, print all (boolean, numeric, string)",
        "capabilities for the given terminal, using short names.",
        "",
        "If no options are given, print all (boolean, numeric, string)",
        "capabilities for the given terminal, using short names.",
@@ -208,9 +745,17 @@ usage(void)
        "Options:",
        " -a       try all names, print capabilities found",
        " -b       print boolean-capabilities",
        "Options:",
        " -a       try all names, print capabilities found",
        " -b       print boolean-capabilities",
+       " -d LIST  colon-separated list of databases to use",
+       " -e NAME  environment variable to set with -d option",
+       " -i NAME  terminal description to use as names for \"-a\" option, etc.",
        " -n       print numeric-capabilities",
        " -n       print numeric-capabilities",
+       " -q       quiet (prints only counts)",
        " -r COUNT repeat for given count",
        " -s       print string-capabilities",
        " -r COUNT repeat for given count",
        " -s       print string-capabilities",
+       " -v       print termcap-variables",
+#ifdef NCURSES_VERSION
+       " -x       print extended capabilities",
+#endif
     };
     unsigned n;
     for (n = 0; n < SIZEOF(msg); ++n) {
     };
     unsigned n;
     for (n = 0; n < SIZEOF(msg); ++n) {
@@ -218,7 +763,6 @@ usage(void)
     }
     ExitProgram(EXIT_FAILURE);
 }
     }
     ExitProgram(EXIT_FAILURE);
 }
-#endif
 
 int
 main(int argc, char *argv[])
 
 int
 main(int argc, char *argv[])
@@ -226,12 +770,15 @@ main(int argc, char *argv[])
     int n;
     char *name;
     bool a_opt = FALSE;
     int n;
     char *name;
     bool a_opt = FALSE;
+#if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
+    bool v_opt = FALSE;
+#endif
+    char *input_name = 0;
 
 
-#if USE_CODE_LISTS
     int repeat;
     int r_opt = 1;
 
     int repeat;
     int r_opt = 1;
 
-    while ((n = getopt(argc, argv, "abnr:s")) != -1) {
+    while ((n = getopt(argc, argv, "abd:e:i:nqr:svxy")) != -1) {
        switch (n) {
        case 'a':
            a_opt = TRUE;
        switch (n) {
        case 'a':
            a_opt = TRUE;
@@ -239,9 +786,21 @@ main(int argc, char *argv[])
        case 'b':
            b_opt = TRUE;
            break;
        case 'b':
            b_opt = TRUE;
            break;
+       case 'd':
+           d_opt = optarg;
+           break;
+       case 'e':
+           e_opt = optarg;
+           break;
+       case 'i':
+           input_name = optarg;
+           break;
        case 'n':
            n_opt = TRUE;
            break;
        case 'n':
            n_opt = TRUE;
            break;
+       case 'q':
+           q_opt = TRUE;
+           break;
        case 'r':
            if ((r_opt = atoi(optarg)) <= 0)
                usage();
        case 'r':
            if ((r_opt = atoi(optarg)) <= 0)
                usage();
@@ -249,49 +808,102 @@ main(int argc, char *argv[])
        case 's':
            s_opt = TRUE;
            break;
        case 's':
            s_opt = TRUE;
            break;
+#if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
+       case 'v':
+           v_opt = TRUE;
+           break;
+#endif
+#ifdef NCURSES_VERSION
+#if NCURSES_XNAMES
+       case 'x':
+           x_opt = TRUE;
+           break;
+       case 'y':
+           y_opt = TRUE;
+           x_opt = TRUE;
+           break;
+#endif
+#endif
        default:
            usage();
            break;
        }
     }
 
        default:
            usage();
            break;
        }
     }
 
+#if HAVE_USE_EXTENDED_NAMES
+    use_extended_names(x_opt);
+#endif
+
     if (!(b_opt || n_opt || s_opt)) {
        b_opt = TRUE;
        n_opt = TRUE;
        s_opt = TRUE;
     }
     if (!(b_opt || n_opt || s_opt)) {
        b_opt = TRUE;
        n_opt = TRUE;
        s_opt = TRUE;
     }
-#else
-    a_opt = TRUE;
-#endif
+
+    make_dblist();
 
     if (a_opt) {
 
     if (a_opt) {
-       if (optind < argc) {
-           for (n = optind; n < argc; ++n) {
-               brute_force(argv[n]);
+       for (repeat = 0; repeat < r_opt; ++repeat) {
+           if (optind < argc) {
+               for (n = optind; n < argc; ++n) {
+                   brute_force(argv[n]);
+               }
+           } else if ((name = getenv("TERM")) != 0) {
+               brute_force(name);
+           } else {
+               static char dumb[] = "dumb";
+               brute_force(dumb);
            }
            }
-       } else if ((name = getenv("TERM")) != 0) {
-           brute_force(name);
-       } else {
-           static char dumb[] = "dumb";
-           brute_force(dumb);
        }
        }
-    }
+    } else {
+       if (input_name != 0) {
+           parse_description(input_name);
+       }
 #if USE_CODE_LISTS
 #if USE_CODE_LISTS
-    else {
+       else {
+           my_boolcodes = copy_code_list(boolcodes);
+           my_numcodes = copy_code_list(numcodes);
+           my_strcodes = copy_code_list(strcodes);
+       }
+#else
+       else {
+           failed("no capability-lists available (use -i option)");
+       }
+#endif /* USE_CODE_LISTS */
        for (repeat = 0; repeat < r_opt; ++repeat) {
            if (optind < argc) {
                for (n = optind; n < argc; ++n) {
        for (repeat = 0; repeat < r_opt; ++repeat) {
            if (optind < argc) {
                for (n = optind; n < argc; ++n) {
-                   demo_terminfo(argv[n]);
+                   demo_termcap(argv[n]);
                }
            } else if ((name = getenv("TERM")) != 0) {
                }
            } else if ((name = getenv("TERM")) != 0) {
-               demo_terminfo(name);
+               demo_termcap(name);
            } else {
                static char dumb[] = "dumb";
            } else {
                static char dumb[] = "dumb";
-               demo_terminfo(dumb);
+               demo_termcap(dumb);
            }
        }
     }
            }
        }
     }
+
+    printf("%ld values (%ld booleans, %ld numbers, %ld strings)\n",
+          total_values, total_b_values, total_n_values, total_s_values);
+
+#if defined(NCURSES_VERSION) || defined(HAVE_CURSES_DATA_OSPEED)
+    if (v_opt) {
+       show_number("PC", PC);
+       show_string("UP", UP);
+       show_string("BC", BC);
+       show_number("ospeed", (int) ospeed);
+    }
+#endif
+
+#if NO_LEAKS
+    free_dblist();
+#if USE_CODE_LISTS
+    free_code_list(my_boolcodes);
+    free_code_list(my_numcodes);
+    free_code_list(my_strcodes);
 #endif
 #endif
+#endif /* NO_LEAKS */
 
     ExitProgram(EXIT_SUCCESS);
 }
 
     ExitProgram(EXIT_SUCCESS);
 }
@@ -301,7 +913,6 @@ int
 main(int argc GCC_UNUSED,
      char *argv[]GCC_UNUSED)
 {
 main(int argc GCC_UNUSED,
      char *argv[]GCC_UNUSED)
 {
-    printf("This program requires termcap\n");
-    exit(EXIT_FAILURE);
+    failed("This program requires termcap");
 }
 #endif
 }
 #endif