From 0266ec751090f9523ae676f357095176e293fb16 Mon Sep 17 00:00:00 2001 From: "Thomas E. Dickey" Date: Mon, 1 Sep 2014 01:29:48 +0000 Subject: [PATCH] ncurses 5.9 - patch 20140831 + modify test/demo_termcap.c and test/demo_terminfo.c to make their options more directly comparable, and add "-i" option to specify a terminal description filename to parse for names to lookup. --- NEWS | 7 +- dist.mk | 4 +- package/debian-mingw/changelog | 4 +- package/debian-mingw64/changelog | 4 +- package/debian/changelog | 4 +- package/mingw-ncurses.nsi | 4 +- package/mingw-ncurses.spec | 2 +- package/ncurses.spec | 2 +- test/demo_termcap.c | 488 ++++++++++++++++++++++++-- test/demo_terminfo.c | 566 ++++++++++++++++++++++++++++--- 10 files changed, 988 insertions(+), 97 deletions(-) diff --git a/NEWS b/NEWS index ab79acb6..41429126 100644 --- a/NEWS +++ b/NEWS @@ -25,7 +25,7 @@ -- sale, use or other dealings in this Software without prior written -- -- authorization. -- ------------------------------------------------------------------------------- --- $Id: NEWS,v 1.2262 2014/08/23 19:28:39 tom Exp $ +-- $Id: NEWS,v 1.2264 2014/08/31 23:12:07 tom Exp $ ------------------------------------------------------------------------------- This is a log of changes that ncurses has gone through since Zeyd started @@ -45,6 +45,11 @@ See the AUTHORS file for the corresponding full names. Changes through 1.9.9e did not credit all contributions; it is not possible to add this information. +20140831 + + modify test/demo_termcap.c and test/demo_terminfo.c to make their + options more directly comparable, and add "-i" option to specify + a terminal description filename to parse for names to lookup. + 20140823 + fix special case where double-width character overwrites a single- width character in the first column (report by Egmont Koblinger, diff --git a/dist.mk b/dist.mk index e5d23620..e88b1e6e 100644 --- a/dist.mk +++ b/dist.mk @@ -25,7 +25,7 @@ # use or other dealings in this Software without prior written # # authorization. # ############################################################################## -# $Id: dist.mk,v 1.1002 2014/08/23 16:35:54 tom Exp $ +# $Id: dist.mk,v 1.1004 2014/08/31 21:30:11 tom Exp $ # Makefile for creating ncurses distributions. # # This only needs to be used directly as a makefile by developers, but @@ -37,7 +37,7 @@ SHELL = /bin/sh # These define the major/minor/patch versions of ncurses. NCURSES_MAJOR = 5 NCURSES_MINOR = 9 -NCURSES_PATCH = 20140823 +NCURSES_PATCH = 20140831 # We don't append the patch to the version, since this only applies to releases VERSION = $(NCURSES_MAJOR).$(NCURSES_MINOR) diff --git a/package/debian-mingw/changelog b/package/debian-mingw/changelog index fb212982..dfc3a557 100644 --- a/package/debian-mingw/changelog +++ b/package/debian-mingw/changelog @@ -1,8 +1,8 @@ -ncurses6 (5.9-20140823) unstable; urgency=low +ncurses6 (5.9-20140831) unstable; urgency=low * latest weekly patch - -- Thomas E. Dickey Sat, 23 Aug 2014 12:35:54 -0400 + -- Thomas E. Dickey Sun, 31 Aug 2014 17:30:11 -0400 ncurses6 (5.9-20131005) unstable; urgency=low diff --git a/package/debian-mingw64/changelog b/package/debian-mingw64/changelog index fb212982..dfc3a557 100644 --- a/package/debian-mingw64/changelog +++ b/package/debian-mingw64/changelog @@ -1,8 +1,8 @@ -ncurses6 (5.9-20140823) unstable; urgency=low +ncurses6 (5.9-20140831) unstable; urgency=low * latest weekly patch - -- Thomas E. Dickey Sat, 23 Aug 2014 12:35:54 -0400 + -- Thomas E. Dickey Sun, 31 Aug 2014 17:30:11 -0400 ncurses6 (5.9-20131005) unstable; urgency=low diff --git a/package/debian/changelog b/package/debian/changelog index 6094abd1..d6f3ca25 100644 --- a/package/debian/changelog +++ b/package/debian/changelog @@ -1,8 +1,8 @@ -ncurses6 (5.9-20140823) unstable; urgency=low +ncurses6 (5.9-20140831) unstable; urgency=low * latest weekly patch - -- Thomas E. Dickey Sat, 23 Aug 2014 12:35:54 -0400 + -- Thomas E. Dickey Sun, 31 Aug 2014 17:30:11 -0400 ncurses6 (5.9-20120608) unstable; urgency=low diff --git a/package/mingw-ncurses.nsi b/package/mingw-ncurses.nsi index 3ccd2fe0..fc660e60 100644 --- a/package/mingw-ncurses.nsi +++ b/package/mingw-ncurses.nsi @@ -1,4 +1,4 @@ -; $Id: mingw-ncurses.nsi,v 1.57 2014/08/23 16:35:54 tom Exp $ +; $Id: mingw-ncurses.nsi,v 1.59 2014/08/31 21:30:11 tom Exp $ ; TODO add examples ; TODO bump ABI to 6 @@ -10,7 +10,7 @@ !define VERSION_MAJOR "5" !define VERSION_MINOR "9" !define VERSION_YYYY "2014" -!define VERSION_MMDD "0823" +!define VERSION_MMDD "0831" !define VERSION_PATCH ${VERSION_YYYY}${VERSION_MMDD} !define MY_ABI "5" diff --git a/package/mingw-ncurses.spec b/package/mingw-ncurses.spec index cea7f9ee..67c87cec 100644 --- a/package/mingw-ncurses.spec +++ b/package/mingw-ncurses.spec @@ -3,7 +3,7 @@ Summary: shared libraries for terminal handling Name: mingw32-ncurses6 Version: 5.9 -Release: 20140823 +Release: 20140831 License: X11 Group: Development/Libraries Source: ncurses-%{version}-%{release}.tgz diff --git a/package/ncurses.spec b/package/ncurses.spec index 96b563f0..0a793d52 100644 --- a/package/ncurses.spec +++ b/package/ncurses.spec @@ -1,7 +1,7 @@ Summary: shared libraries for terminal handling Name: ncurses6 Version: 5.9 -Release: 20140823 +Release: 20140831 License: X11 Group: Development/Libraries Source: ncurses-%{version}-%{release}.tgz diff --git a/test/demo_termcap.c b/test/demo_termcap.c index 2b71d8fd..110797e5 100644 --- a/test/demo_termcap.c +++ b/test/demo_termcap.c @@ -29,12 +29,29 @@ /* * Author: Thomas E. Dickey * - * $Id: demo_termcap.c,v 1.26 2014/07/19 22:49:52 tom Exp $ + * $Id: demo_termcap.c,v 1.43 2014/08/31 23:11:39 tom Exp $ * * A simple demo of the termcap interface. */ #define USE_TINFO #include +#include + +#if NCURSES_XNAMES +#if HAVE_TERM_ENTRY_H +#include +#else +#undef NCURSES_XNAMES +#define NCURSES_XNAMES 0 +#endif +#endif + +static void +failed(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + ExitProgram(EXIT_FAILURE); +} #if HAVE_TGETENT @@ -47,22 +64,32 @@ #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; -#endif - static bool q_opt = FALSE; +static bool x_opt = FALSE; +static bool y_opt = FALSE; 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 isCapName(c) (isgraph(c) && strchr("^=:\\", c) == 0) +#define EachCapName(n) n = 33; n < 127; ++n static char * make_dbitem(char *p, char *q) @@ -151,6 +178,7 @@ dumpit(NCURSES_CONST char *cap) if ((str = tgetstr(cap, &ap)) != 0) { total_values++; + total_s_values++; if (!q_opt) { /* * Note that the strings returned are mostly terminfo format, since @@ -207,12 +235,14 @@ dumpit(NCURSES_CONST char *cap) } } else if ((num = tgetnum(cap)) >= 0) { total_values++; + total_n_values++; if (!q_opt) { printf(FNAME(num), cap); printf(" %d\n", num); } } else if (tgetflag(cap) > 0) { - ++total_values; + total_values++; + total_b_values++; if (!q_opt) { printf(FNAME(flg), cap); printf("%s\n", "true"); @@ -231,16 +261,17 @@ brute_force(const char *name) if (db_list) { putenv(next_dbitem()); } - printf("Terminal type %s\n", name); + if (!q_opt) + printf("Terminal type \"%s\"\n", name); 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)) { - for (c2 = 0; c2 < 256; ++c2) { + for (EachCapName(c2)) { cap[1] = (char) c2; if (isCapName(c2)) { dumpit(cap); @@ -251,7 +282,15 @@ 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 demo_termcap(NCURSES_CONST char *name) { @@ -262,12 +301,13 @@ demo_termcap(NCURSES_CONST char *name) if (db_list) { putenv(next_dbitem()); } - printf("Terminal type \"%s\"\n", name); + if (!q_opt) + printf("Terminal type \"%s\"\n", name); if (tgetent(buffer, name) >= 0) { if (b_opt) { for (n = 0;; ++n) { - cap = boolcodes[n]; + cap = my_boolcodes[n]; if (cap == 0) break; dumpit(cap); @@ -276,7 +316,7 @@ demo_termcap(NCURSES_CONST char *name) if (n_opt) { for (n = 0;; ++n) { - cap = numcodes[n]; + cap = my_numcodes[n]; if (cap == 0) break; dumpit(cap); @@ -285,15 +325,370 @@ demo_termcap(NCURSES_CONST char *name) if (s_opt) { for (n = 0;; ++n) { - cap = strcodes[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 = &(cur_term->type); + 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) +{ + 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 ((fp = fopen(input_name, "r")) == 0) + failed("cannot open input-file"); + len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp); + 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; + 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'; + + /* + * 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] = ""; + my_strcodes[count_strs++] = &my_blob[j]; + } else if (count_nums) { + my_numvalues[count_nums] = ""; + 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; + 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 = 0; + 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; + strcpy(unused, list[count]); + 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; +} +#endif + static void usage(void) { @@ -309,12 +704,13 @@ usage(void) " -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", " -q quiet (prints only counts)", " -r COUNT repeat for given count", " -s print string-capabilities", #ifdef NCURSES_VERSION - " -y disable extended capabilities", + " -x print extended capabilities", #endif }; unsigned n; @@ -323,7 +719,6 @@ usage(void) } ExitProgram(EXIT_FAILURE); } -#endif int main(int argc, char *argv[]) @@ -331,12 +726,12 @@ main(int argc, char *argv[]) int n; char *name; bool a_opt = FALSE; + char *input_name = 0; -#if USE_CODE_LISTS int repeat; int r_opt = 1; - while ((n = getopt(argc, argv, "abd:e:nqr:sy")) != -1) { + while ((n = getopt(argc, argv, "abd:e:i:nqr:sxy")) != -1) { switch (n) { case 'a': a_opt = TRUE; @@ -350,6 +745,9 @@ main(int argc, char *argv[]) case 'e': e_opt = optarg; break; + case 'i': + input_name = optarg; + break; case 'n': n_opt = TRUE; break; @@ -364,8 +762,12 @@ main(int argc, char *argv[]) s_opt = TRUE; break; #if NCURSES_XNAMES + case 'x': + x_opt = TRUE; + break; case 'y': - use_extended_names(FALSE); + y_opt = TRUE; + x_opt = TRUE; break; #endif default: @@ -374,31 +776,46 @@ main(int argc, char *argv[]) } } +#if NCURSES_XNAMES + use_extended_names(x_opt); +#endif + 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 (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 - 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) { @@ -412,9 +829,9 @@ main(int argc, char *argv[]) } } } -#endif /* USE_CODE_LISTS */ - printf("%ld values\n", total_values); + printf("%ld values (%ld booleans, %ld numbers, %ld strings)\n", + total_values, total_b_values, total_n_values, total_s_values); free_dblist(); @@ -426,7 +843,6 @@ int main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED) { - printf("This program requires termcap\n"); - ExitProgram(EXIT_FAILURE); + failed("This program requires termcap"); } #endif diff --git a/test/demo_terminfo.c b/test/demo_terminfo.c index 239739a4..533a5f0a 100644 --- a/test/demo_terminfo.c +++ b/test/demo_terminfo.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 2009-2012,2013 Free Software Foundation, Inc. * + * Copyright (c) 2009-2013,2014 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 * @@ -29,12 +29,13 @@ /* * Author: Thomas E. Dickey * - * $Id: demo_terminfo.c,v 1.19 2013/09/28 21:50:01 tom Exp $ + * $Id: demo_terminfo.c,v 1.34 2014/08/31 23:11:39 tom Exp $ * * A simple demo of the terminfo interface. */ #define USE_TINFO #include +#include #if NCURSES_XNAMES #if HAVE_TERM_ENTRY_H @@ -45,22 +46,46 @@ #endif #endif +static void +failed(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + ExitProgram(EXIT_FAILURE); +} + #if HAVE_TIGETSTR + #if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES) +#define USE_CODE_LISTS 1 +#else +#define USE_CODE_LISTS 0 +#endif +static bool a_opt = FALSE; static bool b_opt = FALSE; static bool f_opt = FALSE; static bool n_opt = FALSE; static bool q_opt = FALSE; static bool s_opt = FALSE; static bool x_opt = FALSE; +static bool y_opt = FALSE; 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 FCOLS 8 #define FNAME(type) "%s %-*s = ", #type, FCOLS @@ -124,6 +149,7 @@ next_dbitem(void) return result; } +#ifdef NO_LEAKS static void free_dblist(void) { @@ -135,26 +161,18 @@ free_dblist(void) db_list = 0; } } +#endif + 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. - */ const char *str; int num; if ((str = tigetstr(cap)) != 0 && (str != (char *) -1)) { 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. - */ printf(FNAME(str), cap); while (*str != 0) { int ch = UChar(*str++); @@ -206,12 +224,14 @@ dumpit(NCURSES_CONST char *cap) } } else if ((num = tigetnum(cap)) >= 0) { total_values++; + total_n_values++; if (!q_opt) { printf(FNAME(num), cap); printf(" %d\n", num); } } else if ((num = tigetflag(cap)) >= 0) { total_values++; + total_b_values++; if (!q_opt) { printf(FNAME(flg), cap); printf("%s\n", num ? "true" : "false"); @@ -222,6 +242,70 @@ dumpit(NCURSES_CONST char *cap) fflush(stdout); } +#define isCapName(c) (isalnum(UChar(c)) || ((c) == '_')) +#define LegalItem(c,n) (n) + +static void +brute_force(const char *name) +{ +#define MAX_FORCE 5 /* omit "colors", since CPU-time is a problem */ + static const char legal[] = "\ +0123456789\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ\ +abcdefghijklmnopqrstuvwxyz_"; + int length; + int j, k; + bool carry; + bool changed; + char cap[MAX_FORCE + 1]; + int item[MAX_FORCE + 1]; + + if (db_list) { + putenv(next_dbitem()); + } + if (!q_opt) + printf("Terminal type \"%s\"\n", name); + setupterm((NCURSES_CONST char *) name, 1, (int *) 0); + + for (length = 1; length <= MAX_FORCE; ++length) { + /* set all digits to zeros */ + for (j = 0; j < length; ++j) { + item[j] = LegalItem(j, 0); + } + + do { + changed = FALSE; + /* copy digits to cap-name */ + for (j = 0; j < length; ++j) { + cap[j] = legal[item[j]]; + } + cap[length] = '\0'; + dumpit(cap); + + k = length - 1; + do { + carry = FALSE; + for (; k >= 0; --k) { + item[k] += 1; + if (legal[item[k]]) { + changed = TRUE; + break; + } + if (k > 0 && + legal[item[k - 1] + 1]) { + for (j = k; j < length; ++j) { + item[j] = LegalItem(j, 0); + } + carry = TRUE; + changed = TRUE; + } + } + } while (carry); + } while (changed); + } + del_curterm(cur_term); +} + static void demo_terminfo(char *name) { @@ -231,12 +315,13 @@ demo_terminfo(char *name) if (db_list) { putenv(next_dbitem()); } - printf("Terminal type \"%s\"\n", name); + if (!q_opt) + printf("Terminal type \"%s\"\n", name); setupterm(name, 1, (int *) 0); if (b_opt) { for (n = 0;; ++n) { - cap = f_opt ? boolfnames[n] : boolnames[n]; + cap = f_opt ? boolfnames[n] : my_boolcodes[n]; if (cap == 0) break; dumpit(cap); @@ -245,7 +330,7 @@ demo_terminfo(char *name) if (n_opt) { for (n = 0;; ++n) { - cap = f_opt ? numfnames[n] : numnames[n]; + cap = f_opt ? numfnames[n] : my_numcodes[n]; if (cap == 0) break; dumpit(cap); @@ -254,16 +339,16 @@ demo_terminfo(char *name) if (s_opt) { for (n = 0;; ++n) { - cap = f_opt ? strfnames[n] : strnames[n]; + cap = f_opt ? strfnames[n] : my_strcodes[n]; if (cap == 0) break; dumpit(cap); } } #ifdef NCURSES_VERSION - if (x_opt) { + if (x_opt && (my_blob == 0)) { int mod; - if (f_opt) { + if (y_opt) { #if NCURSES_XNAMES TERMTYPE *term = &(cur_term->type); if (term != 0 @@ -290,18 +375,365 @@ demo_terminfo(char *name) }; for (n = 0; n < SIZEOF(xterm_keys); ++n) { for (mod = 0; mod < 8; ++mod) { - if (mod == 0) + if (mod == 0) { + /* these happen to be standard - avoid duplicates */ + if (!strcmp(xterm_keys[n], "kDC") || + !strcmp(xterm_keys[n], "kEND") || + !strcmp(xterm_keys[n], "kHOM") || + !strcmp(xterm_keys[n], "kLFT") || + !strcmp(xterm_keys[n], "kRIT")) { + continue; + } sprintf(temp, "%.*s", 8, xterm_keys[n]); - else + } else { sprintf(temp, "%.*s%d", 8, xterm_keys[n], mod); + } dumpit(temp); } } } } #endif + del_curterm(cur_term); +} + +typedef enum { + pDefault = 0 + ,pComment + ,pDescription + ,pEscaped + ,pNewline + ,pName + ,pNumber + ,pString +} STATE; + +static void +parse_description(const char *input_name) +{ + 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, jl; + 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 ((fp = fopen(input_name, "r")) == 0) + failed("cannot open input-file"); + len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp); + fclose(fp); + + /* + * First, get rid of comments and escaped newlines, as well as repeated + * colons to construct a canonical entry. + */ + 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 ' ': + break; + case ',': + my_blob[k++] = (char) ch; + break; + default: + if (isalpha(UChar(ch))) + state = pName; + else + fprintf(stderr, "OOPS @%d:%.20s\n", __LINE__, my_blob + j); + my_blob[k++] = (char) ch; + break; + } + 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 = pString; + 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 '\n': + state = pNewline; + break; + case ',': + state = pDefault; + my_blob[k++] = (char) ch; + break; + default: + my_blob[k++] = (char) ch; + break; + } + break; + default: + /* not used */ + break; + } + } + my_blob[k] = '\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; + if (isalpha(UChar(my_blob[j]))) { + for (jl = 1; isalnum(UChar(my_blob[j + jl])); ++jl) { + ; + } + } else { + jl = 0; + } + if (jl != 0) { + switch (my_blob[j + jl]) { + case '#': + my_numvalues[count_nums] = &my_blob[j + jl + 1]; + my_numcodes[count_nums++] = &my_blob[j]; + my_blob[j + jl] = '\0'; + state = pNumber; + j += jl; + break; + case '=': + my_strvalues[count_strs] = &my_blob[j + jl + 1]; + my_strcodes[count_strs++] = &my_blob[j]; + my_blob[j + jl] = '\0'; + state = pString; + j += jl; + break; + default: + if (my_blob[j + jl] == '@') { + /* + * 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] = ""; + my_strcodes[count_strs++] = &my_blob[j]; + } else if (count_nums) { + my_numvalues[count_nums] = ""; + my_numcodes[count_nums++] = &my_blob[j]; + } else { + my_boolcodes[count_bools++] = &my_blob[j]; + } + my_blob[j + jl] = '\0'; + j += jl + 1; + } else { + my_boolcodes[count_bools++] = &my_blob[j]; + my_blob[j + jl] = '\0'; + j += jl; + } + state = (isCapName(my_blob[j + 1]) + ? pName + : pDefault); + 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') { + ++j; + } else { + --j; + state = pDefault; + } + break; + 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("\t%s,\n", my_boolcodes[j]); + + printf("# numbers:%d\n", (int) count_nums); + for (j = 0; my_numcodes[j]; ++j) + printf("\t%s#%s,\n", my_numcodes[j], my_numvalues[j]); + + printf("# strings:%d\n", (int) count_strs); + for (j = 0; my_strcodes[j]; ++j) + printf("\t%s=%s,\n", 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 = 0; + 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; + strcpy(unused, list[count]); + 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; } +#endif static void usage(void) @@ -314,17 +746,19 @@ usage(void) "capabilities for the given terminal, using short names.", "", "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", " -f print full names", + " -i NAME terminal description to use as names for \"-a\" option", " -n print numeric-capabilities", " -q quiet (prints only counts)", " -r COUNT repeat for given count", " -s print string-capabilities", #ifdef NCURSES_VERSION " -x print extended capabilities", - " -y disable extended capabilities", + " -y direct-lookup names of extended capabilities", #endif }; unsigned n; @@ -341,12 +775,13 @@ main(int argc, char *argv[]) int repeat; char *name; int r_opt = 1; -#ifdef NCURSES_VERSION - bool xy_opt = TRUE; /* by default, use_extended_names is true */ -#endif + char *input_name = 0; - while ((n = getopt(argc, argv, "bd:e:fnqr:sxy")) != -1) { + while ((n = getopt(argc, argv, "abd:e:fi:nqr:sxy")) != -1) { switch (n) { + case 'a': + a_opt = TRUE; + break; case 'b': b_opt = TRUE; break; @@ -359,6 +794,9 @@ main(int argc, char *argv[]) case 'f': f_opt = TRUE; break; + case 'i': + input_name = optarg; + break; case 'n': n_opt = TRUE; break; @@ -375,10 +813,10 @@ main(int argc, char *argv[]) #ifdef NCURSES_VERSION case 'x': x_opt = TRUE; - xy_opt = TRUE; break; case 'y': - xy_opt = FALSE; + y_opt = TRUE; + x_opt = TRUE; break; #endif default: @@ -388,10 +826,10 @@ main(int argc, char *argv[]) } #if NCURSES_XNAMES - use_extended_names(xy_opt); + use_extended_names(x_opt); #endif - if (!(b_opt || n_opt || s_opt || x_opt)) { + if (!(b_opt || n_opt || s_opt)) { b_opt = TRUE; n_opt = TRUE; s_opt = TRUE; @@ -399,34 +837,66 @@ main(int argc, char *argv[]) make_dblist(); - for (repeat = 0; repeat < r_opt; ++repeat) { - if (optind < argc) { - for (n = optind; n < argc; ++n) { - demo_terminfo(argv[n]); + if (a_opt) { + 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 (input_name != 0) { + parse_description(input_name); + } +#if USE_CODE_LISTS + else { + my_boolcodes = copy_code_list(boolnames); + my_numcodes = copy_code_list(numnames); + my_strcodes = copy_code_list(strnames); + } +#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) { + demo_terminfo(argv[n]); + } + } else if ((name = getenv("TERM")) != 0) { + demo_terminfo(name); + } else { + static char dumb[] = "dumb"; + demo_terminfo(dumb); } - } else if ((name = getenv("TERM")) != 0) { - demo_terminfo(name); - } else { - static char dumb[] = "dumb"; - demo_terminfo(dumb); } } - printf("%ld values\n", total_values); + printf("%ld values (%ld booleans, %ld numbers, %ld strings)\n", + total_values, total_b_values, total_n_values, total_s_values); +#ifdef NO_LEAKS free_dblist(); + if (my_blob != 0) { + free(my_blob); + free(my_boolcodes); + free(my_numcodes); + free(my_numvalues); + free(my_strcodes); + free(my_strvalues); + } +#endif ExitProgram(EXIT_SUCCESS); } -#else -int -main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED) -{ - printf("This program requires the terminfo arrays\n"); - ExitProgram(EXIT_FAILURE); -} -#endif #else /* !HAVE_TIGETSTR */ int main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED) -- 2.44.0