X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=test%2Fdemo_termcap.c;h=e252d1d9814400f1c75dc49547eb2223c5a9ee2a;hp=2b71d8fdb8702f77ac6fe377e6dd3241f2dc2fcf;hb=d66080c21038ad4feb2e41a0c4e517d5b4a03ab2;hpb=0dc2639645b2cc67271247405709161df24a771f diff --git a/test/demo_termcap.c b/test/demo_termcap.c index 2b71d8fd..e252d1d9 100644 --- a/test/demo_termcap.c +++ b/test/demo_termcap.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 2005-2013,2014 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 * @@ -29,12 +29,35 @@ /* * 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.56 2017/12/26 22:41:47 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 + +#if defined(NCURSES_VERSION) && HAVE_TERMCAP_H +#include +#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 @@ -47,28 +70,41 @@ #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; +#ifdef NCURSES_VERSION +static bool x_opt = FALSE; +static bool y_opt = FALSE; +#endif 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) { - char *result = malloc(strlen(e_opt) + 2 + (size_t) (p - q)); - sprintf(result, "%s=%.*s", e_opt, (int) (p - q), 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; } @@ -123,6 +159,7 @@ next_dbitem(void) return result; } +#if NO_LEAKS static void free_dblist(void) { @@ -134,6 +171,73 @@ free_dblist(void) 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); + break; + case '\033': + fputs("\\E", stdout); + break; + case '\b': + fputs("\\b", stdout); + break; + case '\f': + fputs("\\f", stdout); + break; + case '\n': + fputs("\\n", stdout); + break; + case '\r': + fputs("\\r", stdout); + break; + case ' ': + fputs("\\s", stdout); + break; + case '\t': + fputs("\\t", stdout); + break; + case '^': + fputs("\\^", stdout); + break; + case ':': + fputs("\\072", stdout); + break; + case '\\': + fputs("\\\\", stdout); + break; + default: + if (isgraph(ch)) + fputc(ch, stdout); + else if (ch < 32) + printf("^%c", ch + '@'); + else + printf("\\%03o", ch); + break; + } + } + } + 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) @@ -151,68 +255,23 @@ 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 * ncurses does not convert except for a handful of special cases. */ - printf(FNAME(str), cap); - while (*str != 0) { - int ch = UChar(*str++); - switch (ch) { - case '\177': - fputs("^?", stdout); - break; - case '\033': - fputs("\\E", stdout); - break; - case '\b': - fputs("\\b", stdout); - break; - case '\f': - fputs("\\f", stdout); - break; - case '\n': - fputs("\\n", stdout); - break; - case '\r': - fputs("\\r", stdout); - break; - case ' ': - fputs("\\s", stdout); - break; - case '\t': - fputs("\\t", stdout); - break; - case '^': - fputs("\\^", stdout); - break; - case ':': - fputs("\\072", stdout); - break; - case '\\': - fputs("\\\\", stdout); - break; - default: - if (isgraph(ch)) - fputc(ch, stdout); - else if (ch < 32) - printf("^%c", ch + '@'); - else - printf("\\%03o", ch); - break; - } - } - printf("\n"); + show_string(cap, str); } } else if ((num = tgetnum(cap)) >= 0) { total_values++; + total_n_values++; if (!q_opt) { - printf(FNAME(num), cap); - printf(" %d\n", num); + show_number(cap, 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 +290,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 +311,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 +330,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 +345,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,14 +354,383 @@ 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 = (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 ((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; + _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) @@ -309,12 +747,14 @@ 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", + " -v print termcap-variables", #ifdef NCURSES_VERSION - " -y disable extended capabilities", + " -x print extended capabilities", #endif }; unsigned n; @@ -323,7 +763,6 @@ usage(void) } ExitProgram(EXIT_FAILURE); } -#endif int main(int argc, char *argv[]) @@ -331,12 +770,15 @@ main(int argc, char *argv[]) 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; - while ((n = getopt(argc, argv, "abd:e:nqr:sy")) != -1) { + while ((n = getopt(argc, argv, "abd:e:i:nqr:svxy")) != -1) { switch (n) { case 'a': a_opt = TRUE; @@ -350,6 +792,9 @@ main(int argc, char *argv[]) case 'e': e_opt = optarg; break; + case 'i': + input_name = optarg; + break; case 'n': n_opt = TRUE; break; @@ -363,10 +808,21 @@ main(int argc, char *argv[]) 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': - use_extended_names(FALSE); + y_opt = TRUE; + x_opt = TRUE; break; +#endif #endif default: usage(); @@ -374,31 +830,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,11 +883,27 @@ 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); +#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 /* NO_LEAKS */ ExitProgram(EXIT_SUCCESS); } @@ -426,7 +913,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