+#if NCURSES_XNAMES
+static int
+standard_type(const char *name)
+{
+ int result = -1;
+ const struct name_table_entry *np;
+
+ if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) {
+ result = np->nte_type;
+ }
+ return result;
+}
+
+static const char *
+name_of_type(int type)
+{
+ const char *result = "unknown";
+ switch (type) {
+ case BOOLEAN:
+ result = "boolean";
+ break;
+ case NUMBER:
+ result = "number";
+ break;
+ case STRING:
+ result = "string";
+ break;
+ }
+ return result;
+}
+
+static void
+check_user_capability_type(const char *name, int actual)
+{
+ if (lookup_user_capability(name) == 0) {
+ int expected = standard_type(name);
+ if (expected >= 0) {
+ _nc_warning("expected %s to be %s, but actually %s",
+ name,
+ name_of_type(actual),
+ name_of_type(expected)
+ );
+ } else if (*name != 'k') {
+ _nc_warning("undocumented %s capability %s",
+ name_of_type(actual),
+ name);
+ }
+ }
+}
+#endif
+
+#define IN_DELAY "0123456789*/."
+
+static bool
+check_ANSI_cap(const char *value, int nparams, char final)
+{
+ bool result = FALSE;
+ if (VALID_STRING(value) && csi_length(value) > 0) {
+ char *p_is_s[NUM_PARM];
+ int popcount;
+ int analyzed = _nc_tparm_analyze(NULL, value, p_is_s, &popcount);
+ if (analyzed < popcount) {
+ analyzed = popcount;
+ }
+ if (analyzed == nparams) {
+ bool numbers = TRUE;
+ int p;
+ for (p = 0; p < nparams; ++p) {
+ if (p_is_s[p]) {
+ numbers = FALSE;
+ break;
+ }
+ }
+ if (numbers) {
+ int in_delay = 0;
+ p = (int) strlen(value);
+ while (p-- > 0) {
+ char ch = value[p];
+ if (ch == final) {
+ result = TRUE;
+ break;
+ }
+ switch (in_delay) {
+ case 0:
+ if (ch == '>')
+ in_delay = 1;
+ break;
+ case 1:
+ if (strchr(IN_DELAY, value[p]) != NULL)
+ break;
+ if (ch != '<')
+ p = 0;
+ in_delay = 2;
+ break;
+ case 2:
+ if (ch != '$')
+ p = 0;
+ in_delay = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+
+static const char *
+skip_Delay(const char *value)
+{
+ const char *result = value;
+
+ if (*value == '$') {
+ ++result;
+ if (*result++ == '<') {
+ while (strchr(IN_DELAY, *result) != NULL)
+ ++result;
+ if (*result++ != '>') {
+ result = value;
+ }
+ } else {
+ result = value;
+ }
+ }
+ return result;
+}
+
+static bool
+isValidString(const char *value, const char *expect)
+{
+ bool result = FALSE;
+ if (VALID_STRING(value)) {
+ if (!strcmp(value, expect))
+ result = TRUE;
+ }
+ return result;
+}
+
+static bool
+isValidEscape(const char *value, const char *expect)
+{
+ bool result = FALSE;
+ if (VALID_STRING(value)) {
+ if (*value == '\033') {
+ size_t need = strlen(expect);
+ size_t have = strlen(value) - 1;
+ if (have >= need && !strncmp(value + 1, expect, need)) {
+ if (*skip_Delay(value + need + 1) == '\0') {
+ result = TRUE;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+static int
+guess_ANSI_VTxx(TERMTYPE2 *tp)
+{
+ int result = -1;
+ int checks = 0;
+
+ /* VT100s have scrolling region, but ANSI (ECMA-48) does not specify */
+ if (check_ANSI_cap(change_scroll_region, 2, 'r') &&
+ (isValidEscape(scroll_forward, "D") ||
+ isValidString(scroll_forward, "\n") ||
+ isValidEscape(scroll_forward, "6")) &&
+ (isValidEscape(scroll_reverse, "M") ||
+ isValidEscape(scroll_reverse, "9"))) {
+ checks |= 2;
+ }
+ if (check_ANSI_cap(cursor_address, 2, 'H') &&
+ check_ANSI_cap(cursor_up, 0, 'A') &&
+ (check_ANSI_cap(cursor_down, 0, 'B') ||
+ isValidString(cursor_down, "\n")) &&
+ check_ANSI_cap(cursor_right, 0, 'C') &&
+ (check_ANSI_cap(cursor_left, 0, 'D') ||
+ isValidString(cursor_left, "\b")) &&
+ check_ANSI_cap(clr_eos, 0, 'J') &&
+ check_ANSI_cap(clr_bol, 0, 'K') &&
+ check_ANSI_cap(clr_eol, 0, 'K')) {
+ checks |= 1;
+ }
+ if (checks == 3)
+ result = 1;
+ if (checks == 1)
+ result = 0;
+ return result;
+}
+
+/*
+ * u6/u7 and u8/u9 are query/response extensions which most terminals support.
+ * In particular, any ECMA-48 terminal should support these, though the details
+ * for u9 are implementation dependent.
+ */
+static void
+check_user_6789(TERMTYPE2 *tp)
+{
+ /*
+ * Check if the terminal is known to not
+ */
+#define NO_QUERY(longname,shortname) \
+ if (PRESENT(longname)) _nc_warning(#shortname " is not supported")
+ if (tigetflag("NQ") > 0) {
+ NO_QUERY(user6, u6);
+ NO_QUERY(user7, u7);
+ NO_QUERY(user8, u8);
+ NO_QUERY(user9, u9);
+ return;
+ }
+
+ PAIRED(user6, user7);
+ PAIRED(user8, user9);
+
+ if (strchr(tp->term_names, '+') != NULL)
+ return;
+
+ switch (guess_ANSI_VTxx(tp)) {
+ case 1:
+ if (!PRESENT(user8)) {
+ _nc_warning("expected u8/u9 for device-attributes");
+ }
+ /* FALLTHRU */
+ case 0:
+ if (!PRESENT(user6)) {
+ _nc_warning("expected u6/u7 for cursor-position");
+ }
+ break;
+ }
+}
+