+ }
+ last = test;
+ assert(strlen(show) < (MAX_KP * 4));
+ switch (kk) {
+ case 0:
+ strcat(show, " ka1");
+ break;
+ case 1:
+ strcat(show, " ka3");
+ break;
+ case 2:
+ strcat(show, " kb2");
+ break;
+ case 3:
+ strcat(show, " kc1");
+ break;
+ case 4:
+ strcat(show, " kc3");
+ break;
+ }
+ }
+
+ _nc_warning("vt100 keypad order inconsistent: %s", show);
+ }
+
+ } else if (VALID_STRING(key_a1) ||
+ VALID_STRING(key_a3) ||
+ VALID_STRING(key_b2) ||
+ VALID_STRING(key_c1) ||
+ VALID_STRING(key_c3)) {
+ show[0] = '\0';
+ if (keypad_index(key_a1) >= 0)
+ strcat(show, " ka1");
+ if (keypad_index(key_a3) >= 0)
+ strcat(show, " ka3");
+ if (keypad_index(key_b2) >= 0)
+ strcat(show, " kb2");
+ if (keypad_index(key_c1) >= 0)
+ strcat(show, " kc1");
+ if (keypad_index(key_c3) >= 0)
+ strcat(show, " kc3");
+ if (*show != '\0')
+ _nc_warning("vt100 keypad map incomplete:%s", show);
+ }
+}
+
+static void
+check_printer(TERMTYPE *tp)
+{
+ PAIRED(enter_doublewide_mode, exit_doublewide_mode);
+ PAIRED(enter_italics_mode, exit_italics_mode);
+ PAIRED(enter_leftward_mode, exit_leftward_mode);
+ PAIRED(enter_micro_mode, exit_micro_mode);
+ PAIRED(enter_shadow_mode, exit_shadow_mode);
+ PAIRED(enter_subscript_mode, exit_subscript_mode);
+ PAIRED(enter_superscript_mode, exit_superscript_mode);
+ PAIRED(enter_upward_mode, exit_upward_mode);
+
+ ANDMISSING(start_char_set_def, stop_char_set_def);
+
+ /* if we have a parameterized form, then the non-parameterized is easy */
+ ANDMISSING(set_bottom_margin_parm, set_bottom_margin);
+ ANDMISSING(set_left_margin_parm, set_left_margin);
+ ANDMISSING(set_right_margin_parm, set_right_margin);
+ ANDMISSING(set_top_margin_parm, set_top_margin);
+
+ ANDMISSING(parm_down_micro, micro_down);
+ ANDMISSING(parm_left_micro, micro_left);
+ ANDMISSING(parm_right_micro, micro_right);
+ ANDMISSING(parm_up_micro, micro_up);
+}
+
+/*
+ * Returns the expected number of parameters for the given capability.
+ */
+static int
+expected_params(const char *name)
+{
+ /* *INDENT-OFF* */
+ static const struct {
+ const char *name;
+ int count;
+ } table[] = {
+ { "S0", 1 }, /* 'screen' extension */
+ { "birep", 2 },
+ { "chr", 1 },
+ { "colornm", 1 },
+ { "cpi", 1 },
+ { "csnm", 1 },
+ { "csr", 2 },
+ { "cub", 1 },
+ { "cud", 1 },
+ { "cuf", 1 },
+ { "cup", 2 },
+ { "cuu", 1 },
+ { "cvr", 1 },
+ { "cwin", 5 },
+ { "dch", 1 },
+ { "defc", 3 },
+ { "dial", 1 },
+ { "dispc", 1 },
+ { "dl", 1 },
+ { "ech", 1 },
+ { "getm", 1 },
+ { "hpa", 1 },
+ { "ich", 1 },
+ { "il", 1 },
+ { "indn", 1 },
+ { "initc", 4 },
+ { "initp", 7 },
+ { "lpi", 1 },
+ { "mc5p", 1 },
+ { "mrcup", 2 },
+ { "mvpa", 1 },
+ { "pfkey", 2 },
+ { "pfloc", 2 },
+ { "pfx", 2 },
+ { "pfxl", 3 },
+ { "pln", 2 },
+ { "qdial", 1 },
+ { "rcsd", 1 },
+ { "rep", 2 },
+ { "rin", 1 },
+ { "sclk", 3 },
+ { "scp", 1 },
+ { "scs", 1 },
+ { "scsd", 2 },
+ { "setab", 1 },
+ { "setaf", 1 },
+ { "setb", 1 },
+ { "setcolor", 1 },
+ { "setf", 1 },
+ { "sgr", 9 },
+ { "sgr1", 6 },
+ { "slength", 1 },
+ { "slines", 1 },
+ { "smgbp", 1 }, /* 2 if smgtp is not given */
+ { "smglp", 1 },
+ { "smglr", 2 },
+ { "smgrp", 1 },
+ { "smgtb", 2 },
+ { "smgtp", 1 },
+ { "tsl", 1 },
+ { "u6", -1 },
+ { "vpa", 1 },
+ { "wind", 4 },
+ { "wingo", 1 },
+ };
+ /* *INDENT-ON* */
+
+ unsigned n;
+ int result = 0; /* function-keys, etc., use none */
+
+ for (n = 0; n < SIZEOF(table); n++) {
+ if (!strcmp(name, table[n].name)) {
+ result = table[n].count;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Make a quick sanity check for the parameters which are used in the given
+ * strings. If there are no "%p" tokens, then there should be no other "%"
+ * markers.
+ */
+static void
+check_params(TERMTYPE *tp, const char *name, char *value)
+{
+ int expected = expected_params(name);
+ int actual = 0;
+ int n;
+ bool params[10];
+ char *s = value;
+
+#ifdef set_top_margin_parm
+ if (!strcmp(name, "smgbp")
+ && set_top_margin_parm == 0)
+ expected = 2;
+#endif
+
+ for (n = 0; n < 10; n++)
+ params[n] = FALSE;
+
+ while (*s != 0) {
+ if (*s == '%') {
+ if (*++s == '\0') {
+ _nc_warning("expected character after %% in %s", name);
+ break;
+ } else if (*s == 'p') {
+ if (*++s == '\0' || !isdigit((int) *s)) {
+ _nc_warning("expected digit after %%p in %s", name);
+ return;
+ } else {
+ n = (*s - '0');
+ if (n > actual)
+ actual = n;
+ params[n] = TRUE;
+ }
+ }
+ }
+ s++;
+ }
+
+ if (params[0]) {
+ _nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name);
+ }
+ if (value == set_attributes || expected < 0) {
+ ;
+ } else if (expected != actual) {
+ _nc_warning("%s uses %d parameters, expected %d", name,
+ actual, expected);
+ for (n = 1; n < actual; n++) {
+ if (!params[n])
+ _nc_warning("%s omits parameter %d", name, n);
+ }
+ }
+}
+
+static char *
+skip_delay(char *s)
+{
+ while (*s == '/' || isdigit(UChar(*s)))
+ ++s;
+ return s;
+}
+
+/*
+ * Skip a delay altogether, e.g., when comparing a simple string to sgr,
+ * the latter may have a worst-case delay on the end.
+ */
+static char *
+ignore_delays(char *s)
+{
+ int delaying = 0;
+
+ do {
+ switch (*s) {
+ case '$':
+ if (delaying == 0)
+ delaying = 1;
+ break;
+ case '<':
+ if (delaying == 1)
+ delaying = 2;
+ break;
+ case '\0':
+ delaying = 0;
+ break;
+ default:
+ if (delaying) {
+ s = skip_delay(s);
+ if (*s == '>')
+ ++s;
+ delaying = 0;
+ }
+ break;
+ }
+ if (delaying)
+ ++s;
+ } while (delaying);
+ return s;
+}
+
+/*
+ * An sgr string may contain several settings other than the one we're
+ * interested in, essentially sgr0 + rmacs + whatever. As long as the
+ * "whatever" is contained in the sgr string, that is close enough for our
+ * sanity check.
+ */
+static bool
+similar_sgr(int num, char *a, char *b)
+{
+ static const char *names[] =
+ {
+ "none"
+ ,"standout"
+ ,"underline"
+ ,"reverse"
+ ,"blink"
+ ,"dim"
+ ,"bold"
+ ,"invis"
+ ,"protect"
+ ,"altcharset"
+ };
+ char *base_a = a;
+ char *base_b = b;
+ int delaying = 0;
+
+ while (*b != 0) {
+ while (*a != *b) {
+ if (*a == 0) {
+ if (b[0] == '$'
+ && b[1] == '<') {
+ _nc_warning("Did not find delay %s", _nc_visbuf(b));
+ } else {
+ _nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s",
+ names[num], _nc_visbuf2(1, base_a),
+ _nc_visbuf2(2, base_b),
+ _nc_visbuf2(3, b));
+ }
+ return FALSE;
+ } else if (delaying) {
+ a = skip_delay(a);
+ b = skip_delay(b);
+ } else if ((*b == '0' || (*b == ';')) && *a == 'm') {
+ b++;
+ } else {
+ a++;
+ }
+ }
+ switch (*a) {
+ case '$':
+ if (delaying == 0)
+ delaying = 1;
+ break;
+ case '<':
+ if (delaying == 1)
+ delaying = 2;
+ break;
+ default:
+ delaying = 0;
+ break;
+ }
+ a++;
+ b++;
+ }
+ /* ignore delays on the end of the string */
+ a = ignore_delays(a);
+ return ((num != 0) || (*a == 0));
+}
+
+static char *
+check_sgr(TERMTYPE *tp, char *zero, int num, char *cap, const char *name)
+{
+ char *test;
+
+ _nc_tparm_err = 0;
+ test = TPARM_9(set_attributes,
+ num == 1,
+ num == 2,
+ num == 3,
+ num == 4,
+ num == 5,
+ num == 6,
+ num == 7,
+ num == 8,
+ num == 9);
+ if (test != 0) {
+ if (PRESENT(cap)) {
+ if (!similar_sgr(num, test, cap)) {
+ _nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s",
+ name, num,
+ name, _nc_visbuf2(1, cap),
+ num, _nc_visbuf2(2, test));
+ }
+ } else if (_nc_capcmp(test, zero)) {
+ _nc_warning("sgr(%d) present, but not %s", num, name);
+ }
+ } else if (PRESENT(cap)) {
+ _nc_warning("sgr(%d) missing, but %s present", num, name);
+ }
+ if (_nc_tparm_err)
+ _nc_warning("stack error in sgr(%d) string", num);
+ return test;
+}
+
+#define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name)
+
+#ifdef TRACE
+/*
+ * If tic is compiled with TRACE, we'll be able to see the output from the
+ * DEBUG() macro. But since it doesn't use traceon(), it always goes to
+ * the standard error. Use this function to make it simpler to follow the
+ * resulting debug traces.
+ */
+static void
+show_where(unsigned level)
+{
+ if (_nc_tracing >= DEBUG_LEVEL(level)) {
+ char my_name[256];
+ _nc_get_type(my_name);
+ _tracef("\"%s\", line %d, '%s'",
+ _nc_get_source(),
+ _nc_curr_line, my_name);
+ }
+}
+
+#else
+#define show_where(level) /* nothing */
+#endif
+
+/* other sanity-checks (things that we don't want in the normal
+ * logic that reads a terminfo entry)
+ */
+static void
+check_termtype(TERMTYPE *tp, bool literal)
+{
+ bool conflict = FALSE;
+ unsigned j, k;
+ char fkeys[STRCOUNT];
+
+ /*
+ * A terminal entry may contain more than one keycode assigned to
+ * a given string (e.g., KEY_END and KEY_LL). But curses will only
+ * return one (the last one assigned).
+ */
+ if (!(_nc_syntax == SYN_TERMCAP && capdump)) {
+ memset(fkeys, 0, sizeof(fkeys));
+ for (j = 0; _nc_tinfo_fkeys[j].code; j++) {
+ char *a = tp->Strings[_nc_tinfo_fkeys[j].offset];
+ bool first = TRUE;
+ if (!VALID_STRING(a))
+ continue;
+ for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) {
+ char *b = tp->Strings[_nc_tinfo_fkeys[k].offset];
+ if (!VALID_STRING(b)
+ || fkeys[k])
+ continue;
+ if (!_nc_capcmp(a, b)) {
+ fkeys[j] = 1;
+ fkeys[k] = 1;
+ if (first) {
+ if (!conflict) {
+ _nc_warning("Conflicting key definitions (using the last)");
+ conflict = TRUE;