X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=test%2Fdemo_termcap.c;h=2a29266f6bb50ef3b6fe0eff0a4776e9a4c33243;hp=2b71d8fdb8702f77ac6fe377e6dd3241f2dc2fcf;hb=aefc1659d732acf7e62c0c78a443d6d8352a3c6e;hpb=0dc2639645b2cc67271247405709161df24a771f;ds=sidebyside diff --git a/test/demo_termcap.c b/test/demo_termcap.c index 2b71d8fd..2a29266f 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.46 2014/10/10 00:35:43 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,373 @@ 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) +{ + 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 ((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; + 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] = 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; + 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; + 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 +707,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 +722,6 @@ usage(void) } ExitProgram(EXIT_FAILURE); } -#endif int main(int argc, char *argv[]) @@ -331,12 +729,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 +748,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 +765,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 +779,46 @@ main(int argc, char *argv[]) } } +#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; } -#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 +832,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 +846,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