+
+ /*
+ * Counting "%p" markers does not account for termcap expressions which
+ * may not have been fully translated. Also, tparm does its own analysis.
+ * Report differences here.
+ */
+ if (actual >= 0) {
+ char *p_is_s[NUM_PARM];
+ int popcount;
+ int analyzed = _nc_tparm_analyze(value, p_is_s, &popcount);
+ if (analyzed < popcount) {
+ analyzed = popcount;
+ }
+ if (actual != analyzed && expected != analyzed) {
+ if (is_user_capability(name)) {
+ _nc_warning("tparm will use %d parameters for %s",
+ analyzed, name);
+ } else {
+ _nc_warning("tparm analyzed %d parameters for %s, expected %d",
+ analyzed, name, actual);
+ }
+ }
+ }
+}
+
+static bool
+line_capability(const char *name)
+{
+ bool result = FALSE;
+ static const char *table[] =
+ {
+ "csr", /* change_scroll_region */
+ "clear", /* clear_screen */
+ "ed", /* clr_eos */
+ "cwin", /* create_window */
+ "cup", /* cursor_address */
+ "cud1", /* cursor_down */
+ "home", /* cursor_home */
+ "mrcup", /* cursor_mem_address */
+ "ll", /* cursor_to_ll */
+ "cuu1", /* cursor_up */
+ "dl1", /* delete_line */
+ "hd", /* down_half_line */
+ "flash", /* flash_screen */
+ "ff", /* form_feed */
+ "il1", /* insert_line */
+ "nel", /* newline */
+ "dl", /* parm_delete_line */
+ "cud", /* parm_down_cursor */
+ "indn", /* parm_index */
+ "il", /* parm_insert_line */
+ "rin", /* parm_rindex */
+ "cuu", /* parm_up_cursor */
+ "mc0", /* print_screen */
+ "vpa", /* row_address */
+ "ind", /* scroll_forward */
+ "ri", /* scroll_reverse */
+ "hu", /* up_half_line */
+ };
+ size_t n;
+ for (n = 0; n < SIZEOF(table); ++n) {
+ if (!strcmp(name, table[n])) {
+ result = TRUE;
+ break;
+ }
+ }
+ return result;
+}
+
+/*
+ * Check for DEC VT100 private mode for reverse video.
+ */
+static const char *
+skip_DECSCNM(const char *value, int *flag)
+{
+ *flag = -1;
+ if (value != 0) {
+ int skip = csi_length(value);
+ if (skip > 0 &&
+ value[skip++] == '?' &&
+ value[skip++] == '5') {
+ if (value[skip] == 'h') {
+ *flag = 1;
+ } else if (value[skip] == 'l') {
+ *flag = 0;
+ }
+ value += skip + 1;
+ }
+ }
+ return value;
+}
+
+static void
+check_delays(const char *name, const char *value)
+{
+ const char *p, *q;
+ const char *first = 0;
+ const char *last = 0;
+
+ for (p = value; *p != '\0'; ++p) {
+ if (p[0] == '$' && p[1] == '<') {
+ const char *base = p + 2;
+ const char *mark = 0;
+ bool maybe = TRUE;
+ bool mixed = FALSE;
+ int proportional = 0;
+ int mandatory = 0;
+
+ first = p;
+
+ for (q = base; *q != '\0'; ++q) {
+ if (*q == '>') {
+ if (mark == 0)
+ mark = q;
+ break;
+ } else if (*q == '*' || *q == '/') {
+ if (*q == '*')
+ ++proportional;
+ if (*q == '/')
+ ++mandatory;
+ if (mark == 0)
+ mark = q;
+ } else if (!(isalnum(UChar(*q)) || strchr("+-.", *q) != 0)) {
+ maybe = FALSE;
+ break;
+ } else if (proportional || mandatory) {
+ mixed = TRUE;
+ }
+ }
+ last = *q ? (q + 1) : q;
+ if (*q == '\0') {
+ maybe = FALSE; /* just an isolated "$<" */
+ } else if (maybe) {
+ float check_f;
+ char check_c;
+ int rc = sscanf(base, "%f%c", &check_f, &check_c);
+ if ((rc != 2) || (check_c != *mark) || mixed) {
+ _nc_warning("syntax error in %s delay '%.*s'", name,
+ (int) (q - base), base);
+ } else if (*name == 'k') {
+ _nc_warning("function-key %s has delay", name);
+ } else if (proportional && !line_capability(name)) {
+ _nc_warning("non-line capability using proportional delay: %s", name);
+ }
+ } else {
+ p = q - 1; /* restart scan */
+ }
+ }
+ }
+
+ if (!strcmp(name, "flash") ||
+ !strcmp(name, "beep")) {
+
+ if (first != 0) {
+ if (first == value || *last == 0) {
+ /*
+ * Delay is on one end or the other.
+ */
+ _nc_warning("expected delay embedded within %s", name);
+ }
+ } else {
+ int flag;
+
+ /*
+ * Check for missing delay when using VT100 reverse-video.
+ * A real VT100 might not need this, but terminal emulators do.
+ */
+ if ((p = skip_DECSCNM(value, &flag)) != 0 &&
+ flag > 0 &&
+ (q = skip_DECSCNM(p, &flag)) != 0 &&
+ flag == 0) {
+ _nc_warning("expected a delay in %s", name);
+ }
+ }
+ }
+}
+
+static char *
+check_1_infotocap(const char *name, NCURSES_CONST char *value, int count)
+{
+ int k;
+ int ignored;
+ long numbers[1 + NUM_PARM];
+ char *strings[1 + NUM_PARM];
+ char *p_is_s[NUM_PARM];
+ char *result;
+ char blob[NUM_PARM * 10];
+ char *next = blob;
+
+ *next++ = '\0';
+ for (k = 1; k <= NUM_PARM; k++) {
+ numbers[k] = count;
+ _nc_SPRINTF(next,
+ _nc_SLIMIT(sizeof(blob) - (size_t) (next - blob))
+ "XYZ%d", count);
+ strings[k] = next;
+ next += strlen(next) + 1;
+ }
+
+ switch (tparm_type(name)) {
+ case Num_Str:
+ result = TPARM_2(value, numbers[1], strings[2]);
+ break;
+ case Num_Str_Str:
+ result = TPARM_3(value, numbers[1], strings[2], strings[3]);
+ break;
+ case Numbers:
+ default:
+ (void) _nc_tparm_analyze(value, p_is_s, &ignored);
+#define myParam(n) (p_is_s[n - 1] != 0 ? ((TPARM_ARG) strings[n]) : numbers[n])
+ result = TPARM_9(value,
+ myParam(1),
+ myParam(2),
+ myParam(3),
+ myParam(4),
+ myParam(5),
+ myParam(6),
+ myParam(7),
+ myParam(8),
+ myParam(9));
+ break;
+ }
+ return result;
+}
+
+#define IsDelay(ch) ((ch) == '.' || isdigit(UChar(ch)))
+
+static const char *
+parse_delay_value(const char *src, double *delays, int *always)
+{
+ int star = 0;
+
+ *delays = 0.0;
+ if (always)
+ *always = 0;
+
+ while (isdigit(UChar(*src))) {
+ (*delays) = (*delays) * 10 + (*src++ - '0');
+ }
+ if (*src == '.') {
+ int gotdot = 1;
+
+ ++src;
+ while (isdigit(UChar(*src))) {
+ gotdot *= 10;
+ (*delays) += (*src++ - '0') / gotdot;
+ }
+ }
+ while (*src == '*' || *src == '/') {
+ if (always == 0 && *src == '/')
+ break;
+ if (*src++ == '*') {
+ star = 1;
+ } else {
+ *always = 1;
+ }
+ }
+ if (star)
+ *delays = -(*delays);
+ return src;
+}
+
+static const char *
+parse_ti_delay(const char *ti, double *delays)
+{
+ *delays = 0.0;
+ while (*ti != '\0') {
+ if (*ti == '\\') {
+ ++ti;
+ }
+ if (ti[0] == '$'
+ && ti[1] == '<'
+ && IsDelay(UChar(ti[2]))) {
+ int ignored;
+ const char *last = parse_delay_value(ti + 2, delays, &ignored);
+ if (*last == '>') {
+ ti = last;
+ }
+ } else {
+ ++ti;
+ }
+ }
+ return ti;
+}
+
+static const char *
+parse_tc_delay(const char *tc, double *delays)
+{
+ return parse_delay_value(tc, delays, (int *) 0);
+}
+
+/*
+ * Compare terminfo- and termcap-strings, factoring out delays.
+ */
+static bool
+same_ti_tc(const char *ti, const char *tc, bool * embedded)
+{
+ bool same = TRUE;
+ double ti_delay = 0.0;
+ double tc_delay = 0.0;
+ const char *ti_last;
+
+ *embedded = FALSE;
+ ti_last = parse_ti_delay(ti, &ti_delay);
+ tc = parse_tc_delay(tc, &tc_delay);
+
+ while ((ti < ti_last) && *tc) {
+ if (*ti == '\\' && ispunct(UChar(ti[1]))) {
+ ++ti;
+ if ((*ti == '^') && !strncmp(tc, "\\136", 4)) {
+ ti += 1;
+ tc += 4;
+ continue;
+ }
+ } else if (ti[0] == '$' && ti[1] == '<') {
+ double no_delay;
+ const char *ss = parse_ti_delay(ti, &no_delay);
+ if (ss != ti) {
+ *embedded = TRUE;
+ ti = ss;
+ continue;
+ }
+ }
+ if (*tc == '\\' && ispunct(UChar(tc[1]))) {
+ ++tc;
+ }
+ if (*ti++ != *tc++) {
+ same = FALSE;
+ break;
+ }
+ }
+
+ if (*embedded) {
+ if (same) {
+ same = FALSE;
+ } else {
+ *embedded = FALSE; /* report only one problem */
+ }
+ }
+
+ return same;
+}
+
+/*
+ * Check terminfo to termcap translation.
+ */
+static void
+check_infotocap(TERMTYPE2 *tp, int i, const char *value)
+{
+ const char *name = ExtStrname(tp, i, strnames);
+ int params = (((i < (int) SIZEOF(parametrized)) &&
+ (i < STRCOUNT))
+ ? parametrized[i]
+ : ((*value == 'k')
+ ? 0
+ : has_params(value)));
+ int to_char = 0;
+ char *ti_value;
+ char *tc_value;
+ bool embedded;
+
+ if ((ti_value = _nc_tic_expand(value, TRUE, to_char)) == ABSENT_STRING) {
+ _nc_warning("tic-expansion of %s failed", name);
+ } else if ((tc_value = _nc_infotocap(name, ti_value, params)) == ABSENT_STRING) {
+ _nc_warning("tic-conversion of %s failed", name);
+ } else if (params > 0) {
+ int limit = 5;
+ int count;
+ bool first = TRUE;
+
+ if (!strcmp(name, "setf")
+ || !strcmp(name, "setb")
+ || !strcmp(name, "setaf")
+ || !strcmp(name, "setab")) {
+ limit = max_colors;
+ }
+ for (count = 0; count < limit; ++count) {
+ char *ti_check = check_1_infotocap(name, ti_value, count);
+ char *tc_check = check_1_infotocap(name, tc_value, count);
+
+ if (strcmp(ti_check, tc_check)) {
+ if (first) {
+ fprintf(stderr, "check_infotocap(%s)\n", name);
+ fprintf(stderr, "...ti '%s'\n", ti_value);
+ fprintf(stderr, "...tc '%s'\n", tc_value);
+ first = FALSE;
+ }
+ _nc_warning("tparm-conversion of %s(%d) differs between\n\tterminfo %s\n\ttermcap %s",
+ name, count, ti_check, tc_check);
+ }
+ }
+ } else if (params == 0 && !same_ti_tc(ti_value, tc_value, &embedded)) {
+ if (embedded) {
+ _nc_warning("termcap equivalent of %s cannot use embedded delay", name);
+ } else {
+ _nc_warning("tic-conversion of %s changed value\n\tfrom %s\n\tto %s",
+ name, ti_value, tc_value);
+ }
+ }