make_namelist(char *src)
{
    char **dst = 0;

    char *s, *base;
    unsigned pass, n, nn;
    char buffer[BUFSIZ];

    if (src == 0) {
	/* EMPTY */ ;
    } else if (strchr(src, '/') != 0) {		/* a filename */
	FILE *fp = open_input(src, (char *) 0);

	for (pass = 1; pass <= 2; pass++) {
	    nn = 0;
	    while (fgets(buffer, sizeof(buffer), fp) != 0) {
		if ((s = stripped(buffer)) != 0) {
		    if (dst != 0)
			dst[nn] = s;
		    else
			free(s);
		    nn++;
		}
	    }
	    if (pass == 1) {
		if ((dst = typeCalloc(char *, nn + 1)) == 0)
		      failed("make_namelist");
		rewind(fp);
	    }
	}
	fclose(fp);
    } else {			/* literal list of names */
	for (pass = 1; pass <= 2; pass++) {
	    for (n = nn = 0, base = src;; n++) {
		int mark = src[n];
		if (mark == ',' || mark == '\0') {
		    if (pass == 1) {
			nn++;
		    } else {
			src[n] = '\0';
			if ((s = stripped(base)) != 0)
			    dst[nn++] = s;
			base = &src[n + 1];
		    }
		}
		if (mark == '\0')
		    break;
	    }
	    if (pass == 1) {
		if ((dst = typeCalloc(char *, nn + 1)) == 0)
		      failed("make_namelist");
	    }
	}
    }
    if (showsummary && (dst != 0)) {
	fprintf(log_fp, "Entries that will be compiled:\n");
	for (n = 0; dst[n] != 0; n++)
	    fprintf(log_fp, "%u:%s\n", n + 1, dst[n]);
    }
    return dst;
}


ncurses 6.2 - patch 20200531

+ correct configure version-check/warnng for g++ to allow for 10.x
+ re-enable "bel" in konsole-base (report by Nia Huang)
+ add linux-s entry (patch by Alexandre Montaron).
+ drop long-obsolete convert_configure.pl
+ add test/test_parm.c, for checking tparm changes.
+ improve parameter-checking for tparm, adding function _nc_tiparm() to
  handle the most-used case, which accepts only numeric parameters
  (report/testcase by "puppet-meteor").
+ use a more conservative estimate of the buffer-size in lib_tparm.c's
  save_text() and save_number(), in case the sprintf() function
  passes-through unexpected characters from a format specifier
  (report/testcase by "puppet-meteor").
+ add a check for end-of-string in cvtchar to handle a malformed
  string in infotocap (report/testcase by "puppet-meteor").

check_exit_attribute(const char *name, char *test, char *trimmed, char *untrimmed)
{
    if (VALID_STRING(test) && (trimmed != 0)) {
	if (similar_sgr(-1, trimmed, test) ||
	    similar_sgr(-1, untrimmed, test)) {
	    _nc_warning("%s matches exit_attribute_mode", name);
	}
    }
}

An issue was discovered in ncurses through v6.2-1. _nc_captoinfo in captoinfo.c has a heap-based buffer overflow.


check_cursor(TERMTYPE2 *tp)
{
    int count;
    char *list[4];

    if (hard_copy) {
	check_noaddress(tp, "hard_copy");
    } else if (generic_type) {
	check_noaddress(tp, "generic_type");
    } else if (strchr(tp->term_names, '+') == 0) {
	int y = 0;
	int x = 0;
	if (PRESENT(column_address))
	    ++y;
	if (PRESENT(cursor_address))
	    y = x = 10;
	if (PRESENT(cursor_home))
	    ++y, ++x;
	if (PRESENT(cursor_mem_address))
	    y = x = 10;
	if (PRESENT(cursor_to_ll))
	    ++y, ++x;
	if (PRESENT(row_address))
	    ++x;
	if (PRESENT(cursor_down))
	    ++y;
	if (PRESENT(cursor_up))
	    ++y;
	if (PRESENT(cursor_left))
	    ++x;
	if (PRESENT(cursor_right))
	    ++x;
	if (x < 2 && y < 2) {
	    _nc_warning("terminal lacks cursor addressing");
	} else {
	    if (x < 2)
		_nc_warning("terminal lacks cursor column-addressing");
	    if (y < 2)
		_nc_warning("terminal lacks cursor row-addressing");
	}
    }

    /* it is rare to have an insert-line feature without a matching delete */
    ANDMISSING(parm_insert_line, insert_line);
    ANDMISSING(parm_delete_line, delete_line);
    ANDMISSING(parm_insert_line, parm_delete_line);

    /* if we have a parameterized form, then the non-parameterized is easy */
    ANDMISSING(parm_down_cursor, cursor_down);
    ANDMISSING(parm_up_cursor, cursor_up);
    ANDMISSING(parm_left_cursor, cursor_left);
    ANDMISSING(parm_right_cursor, cursor_right);

    /* Given any of a set of cursor movement, the whole set should be present.
     * Technically this is not true (we could use cursor_address to fill in
     * unsupported controls), but it is likely.
     */
    count = 0;
    if (PRESENT(parm_down_cursor)) {
	list[count++] = parm_down_cursor;
    }
    if (PRESENT(parm_up_cursor)) {
	list[count++] = parm_up_cursor;
    }
    if (PRESENT(parm_left_cursor)) {
	list[count++] = parm_left_cursor;
    }
    if (PRESENT(parm_right_cursor)) {
	list[count++] = parm_right_cursor;
    }
    if (count == 4) {
	check_ansi_cursor(list);
    } else if (count != 0) {
	EXPECTED(parm_down_cursor);
	EXPECTED(parm_up_cursor);
	EXPECTED(parm_left_cursor);
	EXPECTED(parm_right_cursor);
    }

    count = 0;
    if (PRESENT(cursor_down)) {
	list[count++] = cursor_down;
    }
    if (PRESENT(cursor_up)) {
	list[count++] = cursor_up;
    }
    if (PRESENT(cursor_left)) {
	list[count++] = cursor_left;
    }
    if (PRESENT(cursor_right)) {
	list[count++] = cursor_right;
    }
    if (count == 4) {
	check_ansi_cursor(list);
    } else if (count != 0) {
	count = 0;
	if (PRESENT(cursor_down) && strcmp(cursor_down, "\n"))
	    ++count;
	if (PRESENT(cursor_left) && strcmp(cursor_left, "\b"))
	    ++count;
	if (PRESENT(cursor_up) && strlen(cursor_up) > 1)
	    ++count;
	if (PRESENT(cursor_right) && strlen(cursor_right) > 1)
	    ++count;
	if (count) {
	    EXPECTED(cursor_down);
	    EXPECTED(cursor_up);
	    EXPECTED(cursor_left);
	    EXPECTED(cursor_right);
	}
    }
}


can_clear_with(NCURSES_SP_DCLx ARG_CH_T ch)
{
    if (!back_color_erase && SP_PARM->_coloron) {
#if NCURSES_EXT_FUNCS
	int pair;

	if (!SP_PARM->_default_color)
	    return FALSE;
	if (!(isDefaultColor(SP_PARM->_default_fg) &&
	      isDefaultColor(SP_PARM->_default_bg)))
	    return FALSE;
	if ((pair = GetPair(CHDEREF(ch))) != 0) {
	    NCURSES_COLOR_T fg, bg;
	    if (NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx
					       (short) pair,
					       &fg, &bg) == ERR
		|| !(isDefaultColor(fg) && isDefaultColor(bg))) {
		return FALSE;
	    }
	}
#else
	if (AttrOfD(ch) & A_COLOR)
	    return FALSE;
#endif
    }
    return (ISBLANK(CHDEREF(ch)) &&
	    (AttrOfD(ch) & ~(NONBLANK_ATTR | A_COLOR)) == BLANK_ATTR);
}


chop_out(char *string, unsigned i, unsigned j)
{
    TR(TRACE_DATABASE, ("chop_out %d..%d from %s", i, j, _nc_visbuf(string)));
    while (string[j] != '\0') {
	string[i++] = string[j++];
    }
    string[i] = '\0';
    return i;
}


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;
}


set_attribute_9(TERMTYPE2 *tp, int flag)
{
    const char *value;
    char *result;

    value = TIPARM_9(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, flag);
    if (PRESENT(value))
	result = strdup(value);
    else
	result = 0;
    return result;
}


NCURSES_SP_NAME(mcprint) (NCURSES_SP_DCLx char *data, int len)
/* ship binary character data to the printer via mc4/mc5/mc5p */
{
    int result;
    char *mybuf, *switchon;
    size_t onsize, offsize;
    size_t need;

    errno = 0;
    if (!HasTInfoTerminal(SP_PARM)
	|| len <= 0
	|| (!prtr_non && (!prtr_on || !prtr_off))) {
	errno = ENODEV;
	return (ERR);
    }

    if (prtr_non) {
	switchon = TIPARM_1(prtr_non, len);
	onsize = strlen(switchon);
	offsize = 0;
    } else {
	switchon = prtr_on;
	onsize = strlen(prtr_on);
	offsize = strlen(prtr_off);
    }

    need = onsize + (size_t) len + offsize;

    if (switchon == 0
	|| (mybuf = typeMalloc(char, need + 1)) == 0) {
	errno = ENOMEM;
	return (ERR);
    }

    _nc_STRCPY(mybuf, switchon, need);
    memcpy(mybuf + onsize, data, (size_t) len);
    if (offsize)
	_nc_STRCPY(mybuf + onsize + len, prtr_off, need);

    /*
     * We're relying on the atomicity of UNIX writes here.  The
     * danger is that output from a refresh() might get interspersed
     * with the printer data after the write call returns but before the
     * data has actually been shipped to the terminal.  If the write(2)
     * operation is truly atomic we're protected from this.
     */
    result = (int) write(TerminalOf(SP_PARM)->Filedes, mybuf, need);

    /*
     * By giving up our scheduler slot here we increase the odds that the
     * kernel will ship the contiguous clist items from the last write
     * immediately.
     */
#ifndef _WIN32
    (void) sleep(0);
#endif
    free(mybuf);
    return (result);
}


_nc_msec_cost(const char *const cap, int affcnt)
{
    return NCURSES_SP_NAME(_nc_msec_cost) (CURRENT_SCREEN, cap, affcnt);
}


NCURSES_SP_NAME(extended_pair_content) (NCURSES_SP_DCLx
					int pair,
					int *f, int *b)
{
    return _nc_pair_content(SP_PARM, pair, f, b);
}


NCURSES_SP_NAME(mousemask) (NCURSES_SP_DCLx mmask_t newmask, mmask_t * oldmask)
/* set the mouse event mask */
{
    mmask_t result = 0;

    T((T_CALLED("mousemask(%p,%#lx,%p)"),
       (void *) SP_PARM,
       (unsigned long) newmask,
       (void *) oldmask));

    if (SP_PARM != 0) {
	if (oldmask)
	    *oldmask = SP_PARM->_mouse_mask;

	if (newmask || SP_PARM->_mouse_initialized) {
	    _nc_mouse_init(SP_PARM);

	    if (SP_PARM->_mouse_type != M_NONE) {
		int b;

		result = newmask &
		    (REPORT_MOUSE_POSITION
		     | BUTTON_ALT
		     | BUTTON_CTRL
		     | BUTTON_SHIFT
		     | BUTTON_PRESSED
		     | BUTTON_RELEASED
		     | BUTTON_CLICKED
		     | BUTTON_DOUBLE_CLICKED
		     | BUTTON_TRIPLE_CLICKED);

		mouse_activate(SP_PARM, (bool) (result != 0));

		SP_PARM->_mouse_mask = result;
		SP_PARM->_mouse_mask2 = result;

		/*
		 * Make a mask corresponding to the states we will need to
		 * retain (temporarily) while building up the state that the
		 * user asked for.
		 */
		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if (SP_PARM->_mouse_mask2 & MASK_TRIPLE_CLICK(b))
			SP_PARM->_mouse_mask2 |= MASK_DOUBLE_CLICK(b);
		    if (SP_PARM->_mouse_mask2 & MASK_DOUBLE_CLICK(b))
			SP_PARM->_mouse_mask2 |= MASK_CLICK(b);
		    if (SP_PARM->_mouse_mask2 & MASK_CLICK(b))
			SP_PARM->_mouse_mask2 |= (MASK_PRESS(b) |
						  MASK_RELEASE(b));
		}
	    }
	}
    }
    returnMMask(result);
}


drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB, int labnum, char *text)
{
    SCREEN *sp = TCB->csp;

    AssertTCB();
    if (labnum > 0 && labnum <= num_labels) {
	NCURSES_PUTP2("plab_norm",
		      TPARM_2(plab_norm, labnum, text));
    }
}


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;
}


write_tabs(int *tab_list)
{
    int stop;

    while ((stop = *tab_list++) > 0 && stop <= max_cols) {
	fputs((stop == 1) ? "*" : "\t*", stdout);
    };
    /* also show a tab _past_ the stops */
    if (stop < max_cols)
	fputs("\t+", stdout);
    putchar('\n');
}


drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int c, int flag)
{
    SCREEN *sp;
    int code = ERR;
    int count = 0;
    char *s;

    AssertTCB();
    SetSP();

    if (c >= 0) {
	unsigned ch = (unsigned) c;
	if (flag) {
	    while ((s = _nc_expand_try(sp->_key_ok,
				       ch, &count, (size_t) 0)) != 0) {
		if (_nc_remove_key(&(sp->_key_ok), ch)) {
		    code = _nc_add_to_try(&(sp->_keytry), s, ch);
		    free(s);
		    count = 0;
		    if (code != OK)
			break;
		} else {
		    free(s);
		}
	    }
	} else {
	    while ((s = _nc_expand_try(sp->_keytry,
				       ch, &count, (size_t) 0)) != 0) {
		if (_nc_remove_key(&(sp->_keytry), ch)) {
		    code = _nc_add_to_try(&(sp->_key_ok), s, ch);
		    free(s);
		    count = 0;
		    if (code != OK)
			break;
		} else {
		    free(s);
		}
	    }
	}
    }
    return (code);
}


write_it(ENTRY * ep)
{
    unsigned n;
    int ch;
    char *s, *d, *t;
    char result[MAX_ENTRY_SIZE];

    /*
     * Look for strings that contain %{number}, convert them to %'char',
     * which is shorter and runs a little faster.
     */
    for (n = 0; n < STRCOUNT; n++) {
	s = ep->tterm.Strings[n];
	if (VALID_STRING(s)
	    && strchr(s, L_BRACE) != 0) {
	    d = result;
	    t = s;
	    while ((ch = *t++) != 0) {
		*d++ = (char) ch;
		if (ch == '\\') {
		    if ((*d++ = *t++) == '\0')
			break;
		} else if ((ch == '%')
			   && (*t == L_BRACE)) {
		    char *v = 0;
		    long value = strtol(t + 1, &v, 0);
		    if (v != 0
			&& *v == R_BRACE
			&& value > 0
			&& value != '\\'	/* FIXME */
			&& value < 127
			&& isprint((int) value)) {
			*d++ = S_QUOTE;
			*d++ = (char) value;
			*d++ = S_QUOTE;
			t = (v + 1);
		    }
		}
	    }
	    *d = 0;
	    if (strlen(result) < strlen(s))
		_nc_STRCPY(s, result, strlen(s) + 1);
	}
    }

    _nc_set_type(_nc_first_name(ep->tterm.term_names));
    _nc_curr_line = (int) ep->startline;
    _nc_write_entry(&ep->tterm);
}


NCURSES_SP_NAME(reset_color_pairs) (NCURSES_SP_DCL0)
{
    if (SP_PARM != 0) {
	if (SP_PARM->_color_pairs) {
	    _nc_free_ordered_pairs(SP_PARM);
	    free(SP_PARM->_color_pairs);
	    SP_PARM->_color_pairs = 0;
	    SP_PARM->_pair_alloc = 0;
	    ReservePairs(SP_PARM, 16);
	    clearok(CurScreen(SP_PARM), TRUE);
	    touchwin(StdScreen(SP_PARM));
	}
    }
}


_nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
{
    size_t len2;
    int i;
    int lastpop = -1;
    int len;
    int number = 0;
    int level = -1;
    const char *cp = string;

    if (cp == 0)
	return 0;

    if ((len2 = strlen(cp)) + 2 > TPS(fmt_size)) {
	TPS(fmt_size) += len2 + 2;
	TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
	if (TPS(fmt_buff) == 0)
	    return 0;
    }

    memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
    *popcount = 0;

    while ((cp - string) < (int) len2) {
	if (*cp == '%') {
	    cp++;
	    cp = parse_format(cp, TPS(fmt_buff), &len);
	    switch (*cp) {
	    default:
		break;

	    case 'd':		/* FALLTHRU */
	    case 'o':		/* FALLTHRU */
	    case 'x':		/* FALLTHRU */
	    case 'X':		/* FALLTHRU */
	    case 'c':		/* FALLTHRU */
#ifdef EXP_XTERM_1005
	    case 'u':
#endif
		if (lastpop <= 0) {
		    tc_BUMP();
		}
		level -= 1;
		lastpop = -1;
		break;

	    case 'l':
	    case 's':
		if (lastpop > 0) {
		    level -= 1;
		    p_is_s[lastpop - 1] = dummy;
		}
		tc_BUMP();
		break;

	    case 'p':
		cp++;
		i = (UChar(*cp) - '0');
		if (i >= 0 && i <= NUM_PARM) {
		    ++level;
		    lastpop = i;
		    if (lastpop > *popcount)
			*popcount = lastpop;
		}
		break;

	    case 'P':
		++cp;
		break;

	    case 'g':
		++level;
		cp++;
		break;

	    case S_QUOTE:
		++level;
		cp += 2;
		lastpop = -1;
		break;

	    case L_BRACE:
		++level;
		cp++;
		while (isdigit(UChar(*cp))) {
		    cp++;
		}
		break;

	    case '+':
	    case '-':
	    case '*':
	    case '/':
	    case 'm':
	    case 'A':
	    case 'O':
	    case '&':
	    case '|':
	    case '^':
	    case '=':
	    case '<':
	    case '>':
		tc_BUMP();
		level -= 1;	/* pop 2, operate, push 1 */
		lastpop = -1;
		break;

	    case '!':
	    case '~':
		tc_BUMP();
		lastpop = -1;
		break;

	    case 'i':
		/* will add 1 to first (usually two) parameters */
		break;
	    }
	}
	if (*cp != '\0')
	    cp++;
    }

    if (number > NUM_PARM)
	number = NUM_PARM;
    return number;
}


drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
{
    SCREEN *sp;

    AssertTCB();
    SetSP();

    /* we know how to recognize mouse events under "xterm" */
    if (sp != 0) {
	if (NonEmpty(key_mouse)) {
	    init_xterm_mouse(sp);
	} else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) {
	    if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
		init_xterm_mouse(sp);
	}
    }
}


TINFO_MVCUR(NCURSES_SP_DCLx int yold, int xold, int ynew, int xnew)
{
    int rc;
    rc = _nc_real_mvcur(NCURSES_SP_ARGx
			yold, xold,
			ynew, xnew,
			NCURSES_SP_NAME(_nc_outch),
			TRUE);
    if ((SP_PARM != 0) && (SP_PARM->_endwin == ewInitial))
	NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
    NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
    return rc;
}


usage(void)
{
#define DATA(s) s "\n"
    static const char options_string[] =
    {
	DATA("Options:")
	DATA("  -0         format translation output all capabilities on one line")
	DATA("  -1         format translation output one capability per line")
#if NCURSES_XNAMES
	DATA("  -a         retain commented-out capabilities (sets -x also)")
#endif
	DATA("  -C         translate entries to termcap source form")
	DATA("  -D         print list of tic's database locations (first must be writable)")
	DATA("  -c         check only, validate input without compiling or translating")
	DATA("  -e<names>  translate/compile only entries named by comma-separated list")
	DATA("  -f         format complex strings for readability")
	DATA("  -G         format %{number} to %'char'")
	DATA("  -g         format %'char' to %{number}")
	DATA("  -I         translate entries to terminfo source form")
	DATA("  -K         translate entries to termcap source form with BSD syntax")
	DATA("  -L         translate entries to full terminfo source form")
	DATA("  -N         disable smart defaults for source translation")
	DATA("  -o<dir>    set output directory for compiled entry writes")
	DATA("  -Q[n]      dump compiled description")
	DATA("  -q    brief listing, removes headers")
	DATA("  -R<name>   restrict translation to given terminfo/termcap version")
	DATA("  -r         force resolution of all use entries in source translation")
	DATA("  -s         print summary statistics")
	DATA("  -T         remove size-restrictions on compiled description")
#if NCURSES_XNAMES
	DATA("  -t         suppress commented-out capabilities")
#endif
	DATA("  -U         suppress post-processing of entries")
	DATA("  -V         print version")
	DATA("  -W         wrap long strings according to -w[n] option")
	DATA("  -v[n]      set verbosity level")
	DATA("  -w[n]      set format width for translation output")
#if NCURSES_XNAMES
	DATA("  -x         treat unknown capabilities as user-defined")
#endif
	DATA("")
	DATA("Parameters:")
	DATA("  <file>     file to translate or compile")
    };
#undef DATA

    fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string);
    fputs(options_string, stderr);
    ExitProgram(EXIT_FAILURE);
}


putp(const char *string)
{
    return (tputs(string, 1, _nc_outch));
}


decode_xterm_1005(SCREEN *sp, MEVENT * eventp)
{
    char kbuf[80];
    size_t grabbed;
    size_t limit = (sizeof(kbuf) - 1);
    unsigned coords[2];
    bool result;

    coords[0] = 0;
    coords[1] = 0;

# if USE_PTHREADS_EINTR
#  if USE_WEAK_SYMBOLS
    if ((pthread_self) && (pthread_kill) && (pthread_equal))
#  endif
	_nc_globals.read_thread = pthread_self();
# endif
    for (grabbed = 0; grabbed < limit;) {
	int res;

	res = (int) read(
#if USE_EMX_MOUSE
			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
#else
			    sp->_ifd,
#endif
			    (kbuf + grabbed), (size_t) 1);
	if (res == -1)
	    break;
	grabbed += (size_t) res;
	if (grabbed > 1) {
	    size_t check = 1;
	    int n;

	    for (n = 0; n < 2; ++n) {
		int rc;

		if (check >= grabbed)
		    break;
		rc = _nc_conv_to_utf32(&coords[n], kbuf + check, (unsigned)
				       (grabbed - check));
		if (!rc)
		    break;
		check += (size_t) rc;
	    }
	    if (n >= 2)
		break;
	}
    }
#if USE_PTHREADS_EINTR
    _nc_globals.read_thread = 0;
#endif

    TR(TRACE_IEVENT,
       ("_nc_mouse_inline sees the following xterm data: %s",
	_nc_visbufn(kbuf, (int) grabbed)));

    /* there's only one mouse... */
    eventp->id = NORMAL_EVENT;

    result = decode_X10_bstate(sp, eventp, UChar(kbuf[0]));

    eventp->x = (int) (coords[0] - ' ') - 1;
    eventp->y = (int) (coords[1] - ' ') - 1;

    return result;
}


getparm(int parm, int n)
/* push n copies of param on the terminfo stack if not already there */
{
    int nn;

    if (seenr) {
	if (parm == 1)
	    parm = 2;
	else if (parm == 2)
	    parm = 1;
    }

    for (nn = 0; nn < n; ++nn) {
	dp = save_string(dp, "%p");
	dp = save_char(dp, '0' + parm);
    }

    if (onstack == parm) {
	if (n > 1) {
	    _nc_warning("string may not be optimal");
	    dp = save_string(dp, "%Pa");
	    while (n-- > 0) {
		dp = save_string(dp, "%ga");
	    }
	}
	return;
    }
    if (onstack != 0)
	push();

    onstack = parm;

    if (seenn && parm < 3) {
	dp = save_string(dp, "%{96}%^");
    }

    if (seenm && parm < 3) {
	dp = save_string(dp, "%{127}%^");
    }
}


onscreen_mvcur(NCURSES_SP_DCLx
	       int yold, int xold,
	       int ynew, int xnew, int ovw,
	       NCURSES_SP_OUTC myOutCh)
/* onscreen move from (yold, xold) to (ynew, xnew) */
{
    string_desc result;
    char buffer[OPT_SIZE];
    int tactic = 0, newcost, usecost = INFINITY;
    int t5_cr_cost;

#if defined(MAIN) || defined(NCURSES_TEST)
    struct timeval before, after;

    gettimeofday(&before, NULL);
#endif /* MAIN */

#define NullResult _nc_str_null(&result, sizeof(buffer))
#define InitResult _nc_str_init(&result, buffer, sizeof(buffer))

    /* tactic #0: use direct cursor addressing */
    if (_nc_safe_strcpy(InitResult, TIPARM_2(SP_PARM->_address_cursor,
					     ynew, xnew))) {
	tactic = 0;
	usecost = SP_PARM->_cup_cost;

#if defined(TRACE) || defined(NCURSES_TEST)
	if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
	    goto nonlocal;
#endif /* TRACE */

	/*
	 * We may be able to tell in advance that the full optimization
	 * will probably not be worth its overhead.  Also, don't try to
	 * use local movement if the current attribute is anything but
	 * A_NORMAL...there are just too many ways this can screw up
	 * (like, say, local-movement \n getting mapped to some obscure
	 * character because A_ALTCHARSET is on).
	 */
	if (yold == -1 || xold == -1 || NOT_LOCAL(SP_PARM, yold, xold, ynew, xnew)) {
#if defined(MAIN) || defined(NCURSES_TEST)
	    if (!profiling) {
		(void) fputs("nonlocal\n", stderr);
		goto nonlocal;	/* always run the optimizer if profiling */
	    }
#else
	    goto nonlocal;
#endif /* MAIN */
	}
    }
#ifndef NO_OPTIMIZE
    /* tactic #1: use local movement */
    if (yold != -1 && xold != -1
	&& ((newcost = relative_move(NCURSES_SP_ARGx
				     NullResult,
				     yold, xold,
				     ynew, xnew, ovw)) != INFINITY)
	&& newcost < usecost) {
	tactic = 1;
	usecost = newcost;
    }

    /* tactic #2: use carriage-return + local movement */
    if (yold != -1 && carriage_return
	&& ((newcost = relative_move(NCURSES_SP_ARGx
				     NullResult,
				     yold, 0,
				     ynew, xnew, ovw)) != INFINITY)
	&& SP_PARM->_cr_cost + newcost < usecost) {
	tactic = 2;
	usecost = SP_PARM->_cr_cost + newcost;
    }

    /* tactic #3: use home-cursor + local movement */
    if (cursor_home
	&& ((newcost = relative_move(NCURSES_SP_ARGx
				     NullResult,
				     0, 0,
				     ynew, xnew, ovw)) != INFINITY)
	&& SP_PARM->_home_cost + newcost < usecost) {
	tactic = 3;
	usecost = SP_PARM->_home_cost + newcost;
    }

    /* tactic #4: use home-down + local movement */
    if (cursor_to_ll
	&& ((newcost = relative_move(NCURSES_SP_ARGx
				     NullResult,
				     screen_lines(SP_PARM) - 1, 0,
				     ynew, xnew, ovw)) != INFINITY)
	&& SP_PARM->_ll_cost + newcost < usecost) {
	tactic = 4;
	usecost = SP_PARM->_ll_cost + newcost;
    }

    /*
     * tactic #5: use left margin for wrap to right-hand side,
     * unless strange wrap behavior indicated by xenl might hose us.
     */
    t5_cr_cost = (xold > 0 ? SP_PARM->_cr_cost : 0);
    if (auto_left_margin && !eat_newline_glitch
	&& yold > 0 && cursor_left
	&& ((newcost = relative_move(NCURSES_SP_ARGx
				     NullResult,
				     yold - 1, screen_columns(SP_PARM) - 1,
				     ynew, xnew, ovw)) != INFINITY)
	&& t5_cr_cost + SP_PARM->_cub1_cost + newcost < usecost) {
	tactic = 5;
	usecost = t5_cr_cost + SP_PARM->_cub1_cost + newcost;
    }

    /*
     * These cases are ordered by estimated relative frequency.
     */
    if (tactic)
	InitResult;
    switch (tactic) {
    case 1:
	(void) relative_move(NCURSES_SP_ARGx
			     &result,
			     yold, xold,
			     ynew, xnew, ovw);
	break;
    case 2:
	(void) _nc_safe_strcpy(&result, carriage_return);
	(void) relative_move(NCURSES_SP_ARGx
			     &result,
			     yold, 0,
			     ynew, xnew, ovw);
	break;
    case 3:
	(void) _nc_safe_strcpy(&result, cursor_home);
	(void) relative_move(NCURSES_SP_ARGx
			     &result, 0, 0,
			     ynew, xnew, ovw);
	break;
    case 4:
	(void) _nc_safe_strcpy(&result, cursor_to_ll);
	(void) relative_move(NCURSES_SP_ARGx
			     &result,
			     screen_lines(SP_PARM) - 1, 0,
			     ynew, xnew, ovw);
	break;
    case 5:
	if (xold > 0)
	    (void) _nc_safe_strcat(&result, carriage_return);
	(void) _nc_safe_strcat(&result, cursor_left);
	(void) relative_move(NCURSES_SP_ARGx
			     &result,
			     yold - 1, screen_columns(SP_PARM) - 1,
			     ynew, xnew, ovw);
	break;
    }
#endif /* !NO_OPTIMIZE */

  nonlocal:
#if defined(MAIN) || defined(NCURSES_TEST)
    gettimeofday(&after, NULL);
    diff = after.tv_usec - before.tv_usec
	+ (after.tv_sec - before.tv_sec) * 1000000;
    if (!profiling)
	(void) fprintf(stderr,
		       "onscreen: %d microsec, %f 28.8Kbps char-equivalents\n",
		       (int) diff, diff / 288);
#endif /* MAIN */

    if (usecost != INFINITY) {
	TR(TRACE_MOVE, ("mvcur tactic %d", tactic));
	TPUTS_TRACE("mvcur");
	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
				buffer, 1, myOutCh);
	SP_PARM->_cursrow = ynew;
	SP_PARM->_curscol = xnew;
	return (OK);
    } else
	return (ERR);
}


parse_format(const char *s, char *format, int *len)
{
    *len = 0;
    if (format != 0) {
	bool done = FALSE;
	bool allowminus = FALSE;
	bool dot = FALSE;
	bool err = FALSE;
	char *fmt = format;
	int my_width = 0;
	int my_prec = 0;
	int value = 0;

	*len = 0;
	*format++ = '%';
	while (*s != '\0' && !done) {
	    switch (*s) {
	    case 'c':		/* FALLTHRU */
	    case 'd':		/* FALLTHRU */
	    case 'o':		/* FALLTHRU */
	    case 'x':		/* FALLTHRU */
	    case 'X':		/* FALLTHRU */
	    case 's':
#ifdef EXP_XTERM_1005
	    case 'u':
#endif
		*format++ = *s;
		done = TRUE;
		break;
	    case '.':
		*format++ = *s++;
		if (dot) {
		    err = TRUE;
		} else {	/* value before '.' is the width */
		    dot = TRUE;
		    my_width = value;
		}
		value = 0;
		break;
	    case '#':
		*format++ = *s++;
		break;
	    case ' ':
		*format++ = *s++;
		break;
	    case ':':
		s++;
		allowminus = TRUE;
		break;
	    case '-':
		if (allowminus) {
		    *format++ = *s++;
		} else {
		    done = TRUE;
		}
		break;
	    default:
		if (isdigit(UChar(*s))) {
		    value = (value * 10) + (*s - '0');
		    if (value > 10000)
			err = TRUE;
		    *format++ = *s++;
		} else {
		    done = TRUE;
		}
	    }
	}

	/*
	 * If we found an error, ignore (and remove) the flags.
	 */
	if (err) {
	    my_width = my_prec = value = 0;
	    format = fmt;
	    *format++ = '%';
	    *format++ = *s;
	}

	/*
	 * Any value after '.' is the precision.  If we did not see '.', then
	 * the value is the width.
	 */
	if (dot)
	    my_prec = value;
	else
	    my_width = value;

	*format = '\0';
	/* return maximum string length in print */
	*len = (my_width > my_prec) ? my_width : my_prec;
    }
    return s;
}


tparm(const char *string, ...)
{
    TPARM_DATA myData;
    va_list ap;
    char *result = NULL;

    _nc_tparm_err = 0;
#ifdef TRACE
    TPS(tname) = "tparm";
#endif /* TRACE */

    if (tparm_setup(string, &myData) == OK) {

	va_start(ap, string);
	tparm_copy_valist(&myData, TRUE, ap);
	va_end(ap);

	result = tparam_internal(string, &myData);
    }
    return result;
}


drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
{
#if HAVE_NANOSLEEP
    {
	struct timespec request, remaining;
	request.tv_sec = ms / 1000;
	request.tv_nsec = (ms % 1000) * 1000000;
	while (nanosleep(&request, &remaining) == -1
	       && errno == EINTR) {
	    request = remaining;
	}
    }
#else
    _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0));
#endif
    return OK;
}


roll(int n)
{
    int i, j;

    i = (RAND_MAX / n) * n;
    while ((j = rand()) >= i)
	continue;
    return (j % n);
}


_nc_retrace_cvoid_ptr(NCURSES_CONST void *code)
{
    T((T_RETURN("%p"), code));
    return code;
}


usage(void)
{
#define DATA(s) s "\n"
    static const char msg[] =
    {
	DATA("Usage: tabs [options] [tabstop-list]")
	DATA("")
	DATA("Options:")
	DATA("  -0       reset tabs")
	DATA("  -8       set tabs to standard interval")
	DATA("  -a       Assembler, IBM S/370, first format")
	DATA("  -a2      Assembler, IBM S/370, second format")
	DATA("  -c       COBOL, normal format")
	DATA("  -c2      COBOL compact format")
	DATA("  -c3      COBOL compact format extended")
	DATA("  -d       debug (show ruler with expected/actual tab positions)")
	DATA("  -f       FORTRAN")
	DATA("  -n       no-op (do not modify terminal settings)")
	DATA("  -p       PL/I")
	DATA("  -s       SNOBOL")
	DATA("  -u       UNIVAC 1100 Assembler")
	DATA("  -T name  use terminal type 'name'")
	DATA("  -V       print version")
	DATA("")
	DATA("A tabstop-list is an ordered list of column numbers, e.g., 1,11,21")
	DATA("or 1,+10,+10 which is the same.")
    };
#undef DATA

    fflush(stdout);
    fputs(msg, stderr);
    ExitProgram(EXIT_FAILURE);
}


free_namelist(char **src)
{
    if (src != 0) {
	int n;
	for (n = 0; src[n] != 0; ++n)
	    free(src[n]);
	free(src);
    }
}


default_fg(SCREEN *sp)
{
#if NCURSES_EXT_FUNCS
    return (sp != 0) ? sp->_default_fg : COLOR_WHITE;
#else
    return COLOR_WHITE;
#endif
}


_nc_change_pair(SCREEN *sp, int pair)
{
    int y, x;

    if (CurScreen(sp)->_clear)
	return;
#if NO_LEAKS
    if (_nc_globals.leak_checking)
	return;
#endif

    for (y = 0; y <= CurScreen(sp)->_maxy; y++) {
	struct ldat *ptr = &(CurScreen(sp)->_line[y]);
	bool changed = FALSE;
	for (x = 0; x <= CurScreen(sp)->_maxx; x++) {
	    if (GetPair(ptr->text[x]) == pair) {
		/* Set the old cell to zero to ensure it will be
		   updated on the next doupdate() */
		SetChar(ptr->text[x], 0, 0);
		CHANGED_CELL(ptr, x);
		changed = TRUE;
	    }
	}
	if (changed)
	    NCURSES_SP_NAME(_nc_make_oldhash) (NCURSES_SP_ARGx y);
    }
}


delay_output(int ms GCC_UNUSED)
{
    return OK;
}


vid_attr(attr_t newmode, NCURSES_PAIRS_T pair_arg, void *opts)
{
    return NCURSES_SP_NAME(vid_attr) (CURRENT_SCREEN, newmode, pair_arg, opts);
}


mouse_activate(SCREEN *sp, int on)
{
    if (!on && !sp->_mouse_initialized)
	return;

    if (!_nc_mouse_init(sp))
	return;

    if (on) {
	sp->_mouse_bstate = 0;
	switch (sp->_mouse_type) {
	case M_XTERM:
#if NCURSES_EXT_FUNCS
	    NCURSES_SP_NAME(keyok) (NCURSES_SP_ARGx KEY_MOUSE, on);
#endif
	    TPUTS_TRACE("xterm mouse initialization");
	    enable_xterm_mouse(sp, 1);
	    break;
#if USE_GPM_SUPPORT
	case M_GPM:
	    if (enable_gpm_mouse(sp, TRUE)) {
		sp->_mouse_fd = *(my_gpm_fd);
		T(("GPM mouse_fd %d", sp->_mouse_fd));
	    }
	    break;
#endif
#if USE_SYSMOUSE
	case M_SYSMOUSE:
	    signal(SIGUSR2, handle_sysmouse);
	    sp->_mouse_active = TRUE;
	    break;
#endif
#ifdef USE_TERM_DRIVER
	case M_TERM_DRIVER:
	    sp->_mouse_active = TRUE;
	    break;
#endif
	case M_NONE:
	    return;
	}
	/* Make runtime binding to cut down on object size of applications that
	 * do not use the mouse (e.g., 'clear').
	 */
	sp->_mouse_event = _nc_mouse_event;
	sp->_mouse_inline = _nc_mouse_inline;
	sp->_mouse_parse = _nc_mouse_parse;
	sp->_mouse_resume = _nc_mouse_resume;
	sp->_mouse_wrap = _nc_mouse_wrap;
    } else {

	switch (sp->_mouse_type) {
	case M_XTERM:
	    TPUTS_TRACE("xterm mouse deinitialization");
	    enable_xterm_mouse(sp, 0);
	    break;
#if USE_GPM_SUPPORT
	case M_GPM:
	    enable_gpm_mouse(sp, FALSE);
	    break;
#endif
#if USE_SYSMOUSE
	case M_SYSMOUSE:
	    signal(SIGUSR2, SIG_IGN);
	    sp->_mouse_active = FALSE;
	    break;
#endif
#ifdef USE_TERM_DRIVER
	case M_TERM_DRIVER:
	    sp->_mouse_active = FALSE;
	    break;
#endif
	case M_NONE:
	    return;
	}
    }
    NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
}


NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_DCL0)
{
    assert(SP_PARM);

    /* make sure terminal is in a sane known state */
    SetAttr(SCREEN_ATTRS(SP_PARM), A_NORMAL);
    NewScreen(SP_PARM)->_clear = TRUE;

    /* reset color pairs and definitions */
    if (SP_PARM->_coloron || SP_PARM->_color_defs)
	NCURSES_SP_NAME(_nc_reset_colors) (NCURSES_SP_ARG);

    /* restore user-defined colors, if any */
    if (SP_PARM->_color_defs < 0 && !SP_PARM->_direct_color.value) {
	int n;
	SP_PARM->_color_defs = -(SP_PARM->_color_defs);
	for (n = 0; n < SP_PARM->_color_defs; ++n) {
	    if (SP_PARM->_color_table[n].init) {
		_nc_init_color(SP_PARM,
			       n,
			       SP_PARM->_color_table[n].r,
			       SP_PARM->_color_table[n].g,
			       SP_PARM->_color_table[n].b);
	    }
	}
    }

    if (exit_attribute_mode)
	NCURSES_PUTP2("exit_attribute_mode", exit_attribute_mode);
    else {
	/* turn off attributes */
	if (exit_alt_charset_mode)
	    NCURSES_PUTP2("exit_alt_charset_mode", exit_alt_charset_mode);
	if (exit_standout_mode)
	    NCURSES_PUTP2("exit_standout_mode", exit_standout_mode);
	if (exit_underline_mode)
	    NCURSES_PUTP2("exit_underline_mode", exit_underline_mode);
    }
    if (exit_insert_mode)
	NCURSES_PUTP2("exit_insert_mode", exit_insert_mode);
    if (enter_am_mode && exit_am_mode) {
	if (auto_right_margin) {
	    NCURSES_PUTP2("enter_am_mode", enter_am_mode);
	} else {
	    NCURSES_PUTP2("exit_am_mode", exit_am_mode);
	}
    }
}


tparam_internal(const char *string, TPARM_DATA * data)
{
    int number;
    int len;
    int level;
    int x, y;
    int i;
    const char *cp = string;
    size_t len2 = strlen(cp);
    bool incremented_two = FALSE;
    bool termcap_hack = tparm_tc_compat(data);

    tparm_trace_call(string, data);

    while ((cp - string) < (int) len2) {
	if (*cp != '%') {
	    save_char(UChar(*cp));
	} else {
	    TPS(tparam_base) = cp++;
	    cp = parse_format(cp, TPS(fmt_buff), &len);
	    switch (*cp) {
	    default:
		break;
	    case '%':
		save_char('%');
		break;

	    case 'd':		/* FALLTHRU */
	    case 'o':		/* FALLTHRU */
	    case 'x':		/* FALLTHRU */
	    case 'X':		/* FALLTHRU */
		save_number(TPS(fmt_buff), npop(), len);
		break;

	    case 'c':		/* FALLTHRU */
		save_char(npop());
		break;

#ifdef EXP_XTERM_1005
	    case 'u':
		{
		    unsigned char target[10];
		    unsigned source = (unsigned) npop();
		    int rc = _nc_conv_to_utf8(target, source, (unsigned)
					      sizeof(target));
		    int n;
		    for (n = 0; n < rc; ++n) {
			save_char(target[n]);
		    }
		}
		break;
#endif
	    case 'l':
		npush((int) strlen(spop()));
		break;

	    case 's':
		save_text(TPS(fmt_buff), spop(), len);
		break;

	    case 'p':
		cp++;
		i = (UChar(*cp) - '1');
		if (i >= 0 && i < NUM_PARM) {
		    if (data->p_is_s[i]) {
			spush(data->p_is_s[i]);
		    } else {
			npush((int) data->param[i]);
		    }
		}
		break;

	    case 'P':
		cp++;
		if (isUPPER(*cp)) {
		    i = (UChar(*cp) - 'A');
		    TPS(static_vars)[i] = npop();
		} else if (isLOWER(*cp)) {
		    i = (UChar(*cp) - 'a');
		    TPS(dynamic_var)[i] = npop();
		}
		break;

	    case 'g':
		cp++;
		if (isUPPER(*cp)) {
		    i = (UChar(*cp) - 'A');
		    npush(TPS(static_vars)[i]);
		} else if (isLOWER(*cp)) {
		    i = (UChar(*cp) - 'a');
		    npush(TPS(dynamic_var)[i]);
		}
		break;

	    case S_QUOTE:
		cp++;
		npush(UChar(*cp));
		cp++;
		break;

	    case L_BRACE:
		number = 0;
		cp++;
		while (isdigit(UChar(*cp))) {
		    number = (number * 10) + (UChar(*cp) - '0');
		    cp++;
		}
		npush(number);
		break;

	    case '+':
		npush(npop() + npop());
		break;

	    case '-':
		y = npop();
		x = npop();
		npush(x - y);
		break;

	    case '*':
		npush(npop() * npop());
		break;

	    case '/':
		y = npop();
		x = npop();
		npush(y ? (x / y) : 0);
		break;

	    case 'm':
		y = npop();
		x = npop();
		npush(y ? (x % y) : 0);
		break;

	    case 'A':
		y = npop();
		x = npop();
		npush(y && x);
		break;

	    case 'O':
		y = npop();
		x = npop();
		npush(y || x);
		break;

	    case '&':
		npush(npop() & npop());
		break;

	    case '|':
		npush(npop() | npop());
		break;

	    case '^':
		npush(npop() ^ npop());
		break;

	    case '=':
		y = npop();
		x = npop();
		npush(x == y);
		break;

	    case '<':
		y = npop();
		x = npop();
		npush(x < y);
		break;

	    case '>':
		y = npop();
		x = npop();
		npush(x > y);
		break;

	    case '!':
		npush(!npop());
		break;

	    case '~':
		npush(~npop());
		break;

	    case 'i':
		/*
		 * Increment the first two parameters -- if they are numbers
		 * rather than strings.  As a side effect, assign into the
		 * stack; if this is termcap, then the stack was populated
		 * using the termcap hack above rather than via the terminfo
		 * 'p' case.
		 */
		if (!incremented_two) {
		    incremented_two = TRUE;
		    if (data->p_is_s[0] == 0) {
			data->param[0]++;
			if (termcap_hack)
			    TPS(stack)[0].data.num = (int) data->param[0];
		    }
		    if (data->p_is_s[1] == 0) {
			data->param[1]++;
			if (termcap_hack)
			    TPS(stack)[1].data.num = (int) data->param[1];
		    }
		}
		break;

	    case '?':
		break;

	    case 't':
		x = npop();
		if (!x) {
		    /* scan forward for %e or %; at level zero */
		    cp++;
		    level = 0;
		    while (*cp) {
			if (*cp == '%') {
			    cp++;
			    if (*cp == '?')
				level++;
			    else if (*cp == ';') {
				if (level > 0)
				    level--;
				else
				    break;
			    } else if (*cp == 'e' && level == 0)
				break;
			}

			if (*cp)
			    cp++;
		    }
		}
		break;

	    case 'e':
		/* scan forward for a %; at level zero */
		cp++;
		level = 0;
		while (*cp) {
		    if (*cp == '%') {
			cp++;
			if (*cp == '?')
			    level++;
			else if (*cp == ';') {
			    if (level > 0)
				level--;
			    else
				break;
			}
		    }

		    if (*cp)
			cp++;
		}
		break;

	    case ';':
		break;

	    }			/* endswitch (*cp) */
	}			/* endelse (*cp == '%') */

	if (*cp == '\0')
	    break;

	cp++;
    }				/* endwhile (*cp) */

    get_space((size_t) 1);
    TPS(out_buff)[TPS(out_used)] = '\0';

    T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
    return (TPS(out_buff));
}


ClrUpdate(NCURSES_SP_DCL0)
{
    TR(TRACE_UPDATE, (T_CALLED("ClrUpdate")));
    if (0 != SP_PARM) {
	int i;
	NCURSES_CH_T blank = ClrBlank(NCURSES_SP_ARGx StdScreen(SP_PARM));
	int nonempty = min(screen_lines(SP_PARM),
			   NewScreen(SP_PARM)->_maxy + 1);

	ClearScreen(NCURSES_SP_ARGx blank);

	TR(TRACE_UPDATE, ("updating screen from scratch"));

	nonempty = ClrBottom(NCURSES_SP_ARGx nonempty);

	for (i = 0; i < nonempty; i++)
	    TransformLine(NCURSES_SP_ARGx i);
    }
    TR(TRACE_UPDATE, (T_RETURN("")));
}


rgb2hls(int r, int g, int b, int *h, int *l, int *s)
/* convert RGB to HLS system */
{
    int min, max, t;

    if ((min = g < r ? g : r) > b)
	min = b;
    if ((max = g > r ? g : r) < b)
	max = b;

    /* calculate lightness */
    *l = ((min + max) / 20);

    if (min == max) {		/* black, white and all shades of gray */
	*h = 0;
	*s = 0;
	return;
    }

    /* calculate saturation */
    if (*l < 50)
	*s = (((max - min) * 100) / (max + min));
    else
	*s = (((max - min) * 100) / (2000 - max - min));

    /* calculate hue */
    if (r == max)
	t = (120 + ((g - b) * 60) / (max - min));
    else if (g == max)
	t = (240 + ((b - r) * 60) / (max - min));
    else
	t = (360 + ((r - g) * 60) / (max - min));

    *h = (t % 360);
}


legal_tab_list(const char *tab_list)
{
    bool result = TRUE;

    if (tab_list != 0 && *tab_list != '\0') {
	if (comma_is_needed(tab_list)) {
	    int n, ch;
	    for (n = 0; tab_list[n] != '\0'; ++n) {
		ch = UChar(tab_list[n]);
		if (!(isdigit(ch) || ch == ',' || ch == '+')) {
		    fprintf(stderr,
			    "%s: unexpected character found '%c'\n",
			    _nc_progname, ch);
		    result = FALSE;
		    break;
		}
	    }
	} else {
	    fprintf(stderr, "%s: trailing comma found '%s'\n", _nc_progname, tab_list);
	    result = FALSE;
	}
    } else {
	fprintf(stderr, "%s: no tab-list given\n", _nc_progname);
	result = FALSE;
    }
    return result;
}


ClrBottom(NCURSES_SP_DCLx int total)
{
    int top = total;
    int last = min(screen_columns(SP_PARM), NewScreen(SP_PARM)->_maxx + 1);
    NCURSES_CH_T blank = NewScreen(SP_PARM)->_line[total - 1].text[last - 1];

    if (clr_eos && can_clear_with(NCURSES_SP_ARGx CHREF(blank))) {
	int row;

	for (row = total - 1; row >= 0; row--) {
	    int col;
	    bool ok;

	    for (col = 0, ok = TRUE; ok && col < last; col++) {
		ok = (CharEq(NewScreen(SP_PARM)->_line[row].text[col], blank));
	    }
	    if (!ok)
		break;

	    for (col = 0; ok && col < last; col++) {
		ok = (CharEq(CurScreen(SP_PARM)->_line[row].text[col], blank));
	    }
	    if (!ok)
		top = row;
	}

	/* don't use clr_eos for just one line if clr_eol available */
	if (top < total) {
	    GoTo(NCURSES_SP_ARGx top, 0);
	    ClrToEOS(NCURSES_SP_ARGx blank);
	    if (SP_PARM->oldhash && SP_PARM->newhash) {
		for (row = top; row < screen_lines(SP_PARM); row++)
		    SP_PARM->oldhash[row] = SP_PARM->newhash[row];
	    }
	}
    }
    return top;
}


uses_SGR_39_49(const char *value)
{
    return (strstr(value, "39;49") != 0
	    || strstr(value, "49;39") != 0);
}


save_string(char *d, const char *const s)
{
    size_t have = (size_t) (d - my_string);
    size_t need = have + strlen(s) + 2;
    if (need > my_length) {
	my_string = (char *) _nc_doalloc(my_string, my_length = (need + need));
	if (my_string == 0)
	    _nc_err_abort(MSG_NO_MEMORY);
	d = my_string + have;
    }
    _nc_STRCPY(d, s, my_length - have);
    return d + strlen(d);
}


term_attrs(void)
{
    return NCURSES_SP_NAME(term_attrs) (CURRENT_SCREEN);
}


init_xterm_mouse(SCREEN *sp)
{
    sp->_mouse_type = M_XTERM;
    sp->_mouse_format = MF_X10;
    sp->_mouse_xtermcap = tigetstr("XM");
    if (VALID_STRING(sp->_mouse_xtermcap)) {
	char *code = strstr(sp->_mouse_xtermcap, "[?");
	if (code != 0) {
	    code += 2;
	    while ((*code >= '0') && (*code <= '9')) {
		char *next = code;
		while ((*next >= '0') && (*next <= '9')) {
		    ++next;
		}
		if (!strncmp(code, "1006", (size_t) (next - code))) {
		    sp->_mouse_format = MF_SGR1006;
		}
#ifdef EXP_XTERM_1005
		if (!strncmp(code, "1005", (size_t) (next - code))) {
		    sp->_mouse_format = MF_XTERM_1005;
		}
#endif
		if (*next == ';') {
		    while (*next == ';') {
			++next;
		    }
		    code = next;
		} else {
		    break;
		}
	    }
	}
    } else {
	int code = tigetnum("XM");
	switch (code) {
	case 1006:
	    break;
	default:
	    code = 1000;
	    break;
	}
	sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
    }
}


NCURSES_PUBLIC_VAR(COLOR_PAIRS) (void)
{
    return CURRENT_SCREEN ? CURRENT_SCREEN->_pair_count : -1;
}


_nc_pair_content(SCREEN *sp, int pair, int *f, int *b)
{
    int result;

    T((T_CALLED("pair_content(%p,%d,%p,%p)"),
       (void *) sp,
       (int) pair,
       (void *) f,
       (void *) b));

    if (!ValidPair(sp, pair)) {
	result = ERR;
    } else {
	int fg;
	int bg;

	ReservePairs(sp, pair);
	fg = FORE_OF(sp->_color_pairs[pair]);
	bg = BACK_OF(sp->_color_pairs[pair]);
#if NCURSES_EXT_FUNCS
	if (isDefaultColor(fg))
	    fg = -1;
	if (isDefaultColor(bg))
	    bg = -1;
#endif

	if (f)
	    *f = fg;
	if (b)
	    *b = bg;

	TR(TRACE_ATTRS, ("...pair_content(%p,%d,%d,%d)",
			 (void *) sp,
			 (int) pair,
			 (int) fg, (int) bg));
	result = OK;
    }
    returnCode(result);
}


vidattr(chtype newmode)
{
    return NCURSES_SP_NAME(vidattr) (CURRENT_SCREEN, newmode);
}


_nc_captoinfo(const char *cap, const char *s, int const parameterized)
{
    const char *capstart;

    stackptr = 0;
    onstack = 0;
    seenm = 0;
    seenn = 0;
    seenr = 0;
    param = 1;

    DEBUG_THIS(("_nc_captoinfo params %d, %s", parameterized, s));

    dp = init_string();

    /* skip the initial padding (if we haven't been told not to) */
    capstart = 0;
    if (s == 0)
	s = "";
    if (parameterized >= 0 && isdigit(UChar(*s)))
	for (capstart = s; *s != '\0'; s++)
	    if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.'))
		break;

    while (*s != '\0') {
	switch (*s) {
	case '%':
	    s++;
	    if (parameterized < 1) {
		dp = save_char(dp, '%');
		break;
	    }
	    switch (*s++) {
	    case '%':
		dp = save_string(dp, "%%");
		break;
	    case 'r':
		if (seenr++ == 1) {
		    _nc_warning("saw %%r twice in %s", cap);
		}
		break;
	    case 'm':
		if (seenm++ == 1) {
		    _nc_warning("saw %%m twice in %s", cap);
		}
		break;
	    case 'n':
		if (seenn++ == 1) {
		    _nc_warning("saw %%n twice in %s", cap);
		}
		break;
	    case 'i':
		dp = save_string(dp, "%i");
		break;
	    case '6':
	    case 'B':
		getparm(param, 1);
		dp = save_string(dp, "%{10}%/%{16}%*");
		getparm(param, 1);
		dp = save_string(dp, "%{10}%m%+");
		break;
	    case '8':
	    case 'D':
		getparm(param, 2);
		dp = save_string(dp, "%{2}%*%-");
		break;
	    case '>':
		/* %?%{x}%>%t%{y}%+%; */
		if (s[0] && s[1]) {
		    getparm(param, 2);
		    dp = save_string(dp, "%?");
		    s += cvtchar(s);
		    dp = save_string(dp, "%>%t");
		    s += cvtchar(s);
		    dp = save_string(dp, "%+%;");
		} else {
		    _nc_warning("expected two characters after %%>");
		    dp = save_string(dp, "%>");
		}
		break;
	    case 'a':
		if ((*s == '=' || *s == '+' || *s == '-'
		     || *s == '*' || *s == '/')
		    && (s[1] == 'p' || s[1] == 'c')
		    && s[2] != '\0') {
		    int l;
		    l = 2;
		    if (*s != '=')
			getparm(param, 1);
		    if (s[1] == 'p') {
			getparm(param + s[2] - '@', 1);
			if (param != onstack) {
			    pop();
			    param--;
			}
			l++;
		    } else
			l += cvtchar(s + 2);
		    switch (*s) {
		    case '+':
			dp = save_string(dp, "%+");
			break;
		    case '-':
			dp = save_string(dp, "%-");
			break;
		    case '*':
			dp = save_string(dp, "%*");
			break;
		    case '/':
			dp = save_string(dp, "%/");
			break;
		    case '=':
			if (seenr) {
			    if (param == 1)
				onstack = 2;
			    else if (param == 2)
				onstack = 1;
			    else
				onstack = param;
			} else
			    onstack = param;
			break;
		    }
		    s += l;
		    break;
		}
		getparm(param, 1);
		s += cvtchar(s);
		dp = save_string(dp, "%+");
		break;
	    case '+':
		getparm(param, 1);
		s += cvtchar(s);
		dp = save_string(dp, "%+%c");
		pop();
		break;
	    case 's':
#ifdef WATERLOO
		s += cvtchar(s);
		getparm(param, 1);
		dp = save_string(dp, "%-");
#else
		getparm(param, 1);
		dp = save_string(dp, "%s");
		pop();
#endif /* WATERLOO */
		break;
	    case '-':
		s += cvtchar(s);
		getparm(param, 1);
		dp = save_string(dp, "%-%c");
		pop();
		break;
	    case '.':
		getparm(param, 1);
		dp = save_string(dp, "%c");
		pop();
		break;
	    case '0':		/* not clear any of the historical termcaps did this */
		if (*s == '3') {
		    ++s;
		    goto see03;
		}
		if (*s == '2') {
		    ++s;
		    goto see02;
		}
		goto invalid;
	    case '2':
	      see02:
		getparm(param, 1);
		dp = save_string(dp, "%2d");
		pop();
		break;
	    case '3':
	      see03:
		getparm(param, 1);
		dp = save_string(dp, "%3d");
		pop();
		break;
	    case 'd':
		getparm(param, 1);
		dp = save_string(dp, "%d");
		pop();
		break;
	    case 'f':
		param++;
		break;
	    case 'b':
		param--;
		break;
	    case '\\':
		dp = save_string(dp, "%\\");
		break;
	    default:
	      invalid:
		dp = save_char(dp, '%');
		s--;
		_nc_warning("unknown %% code %s (%#x) in %s",
			    unctrl((chtype) *s), UChar(*s), cap);
		break;
	    }
	    break;
	default:
	    if (*s != '\0')
		dp = save_char(dp, *s++);
	    break;
	}
    }

    /*
     * Now, if we stripped off some leading padding, add it at the end
     * of the string as mandatory padding.
     */
    if (capstart) {
	dp = save_string(dp, "$<");
	for (s = capstart; *s != '\0'; s++)
	    if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
		dp = save_char(dp, *s);
	    else
		break;
	dp = save_string(dp, "/>");
    }

    (void) save_char(dp, '\0');

    DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string)));

    return (my_string);
}


NCURSES_SP_NAME(_nc_scrolln) (NCURSES_SP_DCLx
			      int n,
			      int top,
			      int bot,
			      int maxy)
/* scroll region from top to bot by n lines */
{
    NCURSES_CH_T blank;
    int i;
    bool cursor_saved = FALSE;
    int res;

    TR(TRACE_MOVE, ("_nc_scrolln(%p, %d, %d, %d, %d)",
		    (void *) SP_PARM, n, top, bot, maxy));

    if (!IsValidScreen(SP_PARM))
	return (ERR);

    blank = ClrBlank(NCURSES_SP_ARGx StdScreen(SP_PARM));

#if USE_XMC_SUPPORT
    /*
     * If we scroll, we might remove a cookie.
     */
    if (magic_cookie_glitch > 0) {
	return (ERR);
    }
#endif

    if (n > 0) {		/* scroll up (forward) */
	/*
	 * Explicitly clear if stuff pushed off top of region might
	 * be saved by the terminal.
	 */
	res = scroll_csr_forward(NCURSES_SP_ARGx n, top, bot, 0, maxy, blank);

	if (res == ERR && change_scroll_region) {
	    if ((((n == 1 && scroll_forward) || parm_index)
		 && (SP_PARM->_cursrow == bot || SP_PARM->_cursrow == bot - 1))
		&& save_cursor && restore_cursor) {
		cursor_saved = TRUE;
		NCURSES_PUTP2("save_cursor", save_cursor);
	    }
	    NCURSES_PUTP2("change_scroll_region",
			  TIPARM_2(change_scroll_region, top, bot));
	    if (cursor_saved) {
		NCURSES_PUTP2("restore_cursor", restore_cursor);
	    } else {
		SP_PARM->_cursrow = SP_PARM->_curscol = -1;
	    }

	    res = scroll_csr_forward(NCURSES_SP_ARGx n, top, bot, top, bot, blank);

	    NCURSES_PUTP2("change_scroll_region",
			  TIPARM_2(change_scroll_region, 0, maxy));
	    SP_PARM->_cursrow = SP_PARM->_curscol = -1;
	}

	if (res == ERR && SP_PARM->_nc_sp_idlok)
	    res = scroll_idl(NCURSES_SP_ARGx n, top, bot - n + 1, blank);

	/*
	 * Clear the newly shifted-in text.
	 */
	if (res != ERR
	    && (non_dest_scroll_region || (memory_below && bot == maxy))) {
	    static const NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
	    if (bot == maxy && clr_eos) {
		GoTo(NCURSES_SP_ARGx bot - n + 1, 0);
		ClrToEOS(NCURSES_SP_ARGx blank2);
	    } else {
		for (i = 0; i < n; i++) {
		    GoTo(NCURSES_SP_ARGx bot - i, 0);
		    ClrToEOL(NCURSES_SP_ARGx blank2, FALSE);
		}
	    }
	}

    } else {			/* (n < 0) - scroll down (backward) */
	res = scroll_csr_backward(NCURSES_SP_ARGx -n, top, bot, 0, maxy, blank);

	if (res == ERR && change_scroll_region) {
	    if (top != 0
		&& (SP_PARM->_cursrow == top ||
		    SP_PARM->_cursrow == top - 1)
		&& save_cursor && restore_cursor) {
		cursor_saved = TRUE;
		NCURSES_PUTP2("save_cursor", save_cursor);
	    }
	    NCURSES_PUTP2("change_scroll_region",
			  TIPARM_2(change_scroll_region, top, bot));
	    if (cursor_saved) {
		NCURSES_PUTP2("restore_cursor", restore_cursor);
	    } else {
		SP_PARM->_cursrow = SP_PARM->_curscol = -1;
	    }

	    res = scroll_csr_backward(NCURSES_SP_ARGx
				      -n, top, bot, top, bot, blank);

	    NCURSES_PUTP2("change_scroll_region",
			  TIPARM_2(change_scroll_region, 0, maxy));
	    SP_PARM->_cursrow = SP_PARM->_curscol = -1;
	}

	if (res == ERR && SP_PARM->_nc_sp_idlok)
	    res = scroll_idl(NCURSES_SP_ARGx -n, bot + n + 1, top, blank);

	/*
	 * Clear the newly shifted-in text.
	 */
	if (res != ERR
	    && (non_dest_scroll_region || (memory_above && top == 0))) {
	    static const NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
	    for (i = 0; i < -n; i++) {
		GoTo(NCURSES_SP_ARGx i + top, 0);
		ClrToEOL(NCURSES_SP_ARGx blank2, FALSE);
	    }
	}
    }

    if (res == ERR)
	return (ERR);

    _nc_scroll_window(CurScreen(SP_PARM), n,
		      (NCURSES_SIZE_T) top,
		      (NCURSES_SIZE_T) bot,
		      blank);

    /* shift hash values too - they can be reused */
    NCURSES_SP_NAME(_nc_scroll_oldhash) (NCURSES_SP_ARGx n, top, bot);

    return (OK);
}


decode_xterm_X10(SCREEN *sp, MEVENT * eventp)
{
#define MAX_KBUF 3
    unsigned char kbuf[MAX_KBUF + 1];
    size_t grabbed;
    int res;
    bool result;

# if USE_PTHREADS_EINTR
#  if USE_WEAK_SYMBOLS
    if ((pthread_self) && (pthread_kill) && (pthread_equal))
#  endif
	_nc_globals.read_thread = pthread_self();
# endif
    for (grabbed = 0; grabbed < MAX_KBUF; grabbed += (size_t) res) {

	/* For VIO mouse we add extra bit 64 to disambiguate button-up. */
	res = (int) read(
#if USE_EMX_MOUSE
			    (M_FD(sp) >= 0) ? M_FD(sp) : sp->_ifd,
#else
			    sp->_ifd,
#endif
			    kbuf + grabbed, (size_t) (MAX_KBUF - (int) grabbed));
	if (res == -1)
	    break;
    }
#if USE_PTHREADS_EINTR
    _nc_globals.read_thread = 0;
#endif
    kbuf[MAX_KBUF] = '\0';

    TR(TRACE_IEVENT,
       ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));

    /* there's only one mouse... */
    eventp->id = NORMAL_EVENT;

    result = decode_X10_bstate(sp, eventp, kbuf[0]);

    eventp->x = (kbuf[1] - ' ') - 1;
    eventp->y = (kbuf[2] - ' ') - 1;

    return result;
}


get_baudrate(TERMINAL *termp)
{
    int my_ospeed;
    int result;
    if (GET_TTY(termp->Filedes, &termp->Nttyb) == OK) {
#ifdef TERMIOS
	termp->Nttyb.c_oflag &= (unsigned) (~OFLAGS_TABS);
#else
	termp->Nttyb.sg_flags &= (unsigned) (~XTABS);
#endif
    }
#ifdef USE_OLD_TTY
    result = (int) cfgetospeed(&(termp->Nttyb));
    my_ospeed = (NCURSES_OSPEED) _nc_ospeed(result);
#else /* !USE_OLD_TTY */
#ifdef TERMIOS
    my_ospeed = (NCURSES_OSPEED) cfgetospeed(&(termp->Nttyb));
#else
    my_ospeed = (NCURSES_OSPEED) termp->Nttyb.sg_ospeed;
#endif
    result = _nc_baudrate(my_ospeed);
#endif
    termp->_baudrate = result;
    ospeed = (NCURSES_OSPEED) my_ospeed;
}


usage(void)
{
    static const char *msg[] =
    {
	"Usage: dots_xcurses [options]"
	,""
	,"Options:"
	," -T TERM  override $TERM"
#if HAVE_USE_DEFAULT_COLORS
	," -d       invoke use_default_colors()"
#endif
#if HAVE_USE_ENV
	," -e       allow environment $LINES / $COLUMNS"
#endif
	," -m SIZE  set margin (default: 2)"
	," -r SECS  self-interrupt/exit after specified number of seconds"
	," -s MSECS delay 1% of the time (default: 1 msecs)"
#if HAVE_ALLOC_PAIR
	," -x       use alloc_pair() rather than init_pair()"
#endif
    };
    size_t n;

    for (n = 0; n < SIZEOF(msg); n++)
	fprintf(stderr, "%s\n", msg[n]);

    ExitProgram(EXIT_FAILURE);
}


quit(int status, const char *fmt, ...)
{
    va_list argp;

    va_start(argp, fmt);
    fprintf(stderr, "%s: ", prg_name);
    vfprintf(stderr, fmt, argp);
    fprintf(stderr, "\n");
    va_end(argp);
    ExitProgram(status);
}


_nc_retrace_unsigned(unsigned code)
{
    T((T_RETURN("%#x"), code));
    return code;
}


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:
#define myParam(n) numbers[n]
	result = TIPARM_9(value,
			  myParam(1),
			  myParam(2),
			  myParam(3),
			  myParam(4),
			  myParam(5),
			  myParam(6),
			  myParam(7),
			  myParam(8),
			  myParam(9));
#undef myParam
	break;
    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));
#undef myParam
	break;
    }
    return strdup(result);
}


failed(const char *msg)
{
    perror(msg);
    ExitProgram(EXIT_FAILURE);
}


_nc_reset_colors(void)
{
    return NCURSES_SP_NAME(_nc_reset_colors) (CURRENT_SCREEN);
}


check_params(TERMTYPE2 *tp, const char *name, char *value, int extended)
{
    int expected = expected_params(name);
    int actual = 0;
    int n;
    bool params[1 + NUM_PARM];
    char *s = value;

#ifdef set_top_margin_parm
    if (!strcmp(name, "smgbp")
	&& set_top_margin_parm == 0)
	expected = 2;
#endif

    for (n = 0; n <= NUM_PARM; 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 NCURSES_XNAMES
    if (extended) {
	int check = is_user_capability(name);
	if (check != actual && (check >= 0 && actual >= 0)) {
	    _nc_warning("extended %s capability has %d parameters, expected %d",
			name, actual, check);
	} else if (debug_level > 1) {
	    _nc_warning("extended %s capability has %d parameters, as expected",
			name, actual);
	}
	expected = actual;
    }
#else
    (void) extended;
#endif

    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);
	}
    }

    /*
     * 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 NCURSES_XNAMES
	    int user_cap = is_user_capability(name);
	    if ((user_cap == analyzed) && using_extensions) {
		;		/* ignore */
	    } else if (user_cap >= 0) {
		_nc_warning("tparm will use %d parameters for %s, expected %d",
			    analyzed, name, user_cap);
	    } else
#endif
	    {
		_nc_warning("tparm analyzed %d parameters for %s, expected %d",
			    analyzed, name, actual);
	    }
	}
    }
}


save_char(char *s, int c)
{
    static char temp[2];
    temp[0] = (char) c;
    return save_string(s, temp);
}


trace_normalized_cost(NCURSES_SP_DCLx const char *capname, const char *cap, int affcnt)
{
    int result = normalized_cost(NCURSES_SP_ARGx cap, affcnt);
    TR(TRACE_CHARPUT | TRACE_MOVE,
       ("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap)));
    return result;
}


drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
{
    AssertTCB();

    /* *INDENT-EQLS* */
    clear_screen     = ABSENT_STRING;
    cursor_address   = ABSENT_STRING;
    cursor_down      = ABSENT_STRING;
    cursor_up        = ABSENT_STRING;
    parm_down_cursor = ABSENT_STRING;
    parm_up_cursor   = ABSENT_STRING;
    row_address      = ABSENT_STRING;
    cursor_home      = carriage_return;

    if (back_color_erase)
	clr_eos = ABSENT_STRING;
}


decode_X10_bstate(SCREEN *sp, MEVENT * eventp, unsigned intro)
{
    bool result;
    int button = 0;
    int wheel = (intro & 96) == 96;

    eventp->bstate = 0;

    if (intro >= 96) {
	if (intro >= 160) {
	    button = (int) (intro - 152);	/* buttons 8-11 */
	} else if (intro >= 96) {
	    button = (int) (intro - 92);	/* buttons 4-7 */
	}
    } else {
	button = (intro & 3);
    }

    if (button > MAX_BUTTONS) {
	eventp->bstate = REPORT_MOUSE_POSITION;
    } else if (!handle_wheel(sp, eventp, (int) intro, wheel)) {

	/*
	 * Release events aren't reported for individual buttons, just for
	 * the button set as a whole.  However, because there are normally
	 * no mouse events under xterm that intervene between press and
	 * release, we can infer the button actually released by looking at
	 * the previous event.
	 */
	if (sp->_mouse_bstate & BUTTON_PRESSED) {
	    int b;

	    eventp->bstate = BUTTON_RELEASED;
	    for (b = 1; b <= MAX_BUTTONS; ++b) {
		if (!(sp->_mouse_bstate & MASK_PRESS(b)))
		    eventp->bstate &= ~MASK_RELEASE(b);
	    }
	    sp->_mouse_bstate = 0;
	} else {
	    /*
	     * xterm will return a stream of release-events to let the
	     * application know where the mouse is going, if private mode
	     * 1002 or 1003 is enabled.
	     */
	    eventp->bstate = REPORT_MOUSE_POSITION;
	}
    }

    if (intro & 4) {
	eventp->bstate |= BUTTON_SHIFT;
    }
    if (intro & 8) {
	eventp->bstate |= BUTTON_ALT;
    }
    if (intro & 16) {
	eventp->bstate |= BUTTON_CTRL;
    }
    result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
    return result;
}


decode_xterm_SGR1006(SCREEN *sp, MEVENT * eventp)
{
    SGR_DATA data;
    bool result = FALSE;
    if (read_SGR(sp, &data)) {
	int b = data.params[0];
	int b3 = 1 + (b & 3);
	int wheel = ((b & 64) == 64);

	if (b >= 132) {
	    b3 = MAX_BUTTONS + 1;
	} else if (b >= 128) {
	    b3 = (b - 120);	/* buttons 8-11 */
	} else if (b >= 64) {
	    b3 = (b - 60);	/* buttons 6-7 */
	}

	eventp->id = NORMAL_EVENT;
	if (data.final == 'M') {
	    (void) handle_wheel(sp, eventp, b, wheel);
	} else if (b3 > MAX_BUTTONS) {
	    eventp->bstate = REPORT_MOUSE_POSITION;
	} else {
	    mmask_t pressed = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_PRESSED);
	    mmask_t release = (mmask_t) NCURSES_MOUSE_MASK(b3, NCURSES_BUTTON_RELEASED);
	    if (sp->_mouse_bstate & pressed) {
		eventp->bstate = release;
		sp->_mouse_bstate &= ~pressed;
	    } else {
		eventp->bstate = REPORT_MOUSE_POSITION;
	    }
	}
	if (b & 4) {
	    eventp->bstate |= BUTTON_SHIFT;
	}
	if (b & 8) {
	    eventp->bstate |= BUTTON_ALT;
	}
	if (b & 16) {
	    eventp->bstate |= BUTTON_CTRL;
	}
	result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;
	eventp->x = (data.params[1] ? (data.params[1] - 1) : 0);
	eventp->y = (data.params[2] ? (data.params[2] - 1) : 0);
    }
    return result;
}


_nc_mouse_resume(SCREEN *sp)
/* re-connect to mouse -- called by doupdate() after shellout */
{
    TR(MY_TRACE, ("_nc_mouse_resume() called"));

    switch (sp->_mouse_type) {
    case M_XTERM:
	/* xterm: re-enable reporting */
	if (sp->_mouse_mask)
	    mouse_activate(sp, TRUE);
	break;

#if USE_GPM_SUPPORT
    case M_GPM:
	/* GPM: reclaim our event set */
	if (sp->_mouse_mask)
	    mouse_activate(sp, TRUE);
	break;
#endif

#if USE_SYSMOUSE
    case M_SYSMOUSE:
	mouse_activate(sp, TRUE);
	break;
#endif

#ifdef USE_TERM_DRIVER
    case M_TERM_DRIVER:
	mouse_activate(sp, TRUE);
	break;
#endif

    case M_NONE:
	break;
    }
}


TPUTS_PROTO(outc, c)
{
    int rc = c;

    if (interrupted) {
	char tmp = (char) c;
	if (write(STDOUT_FILENO, &tmp, (size_t) 1) == -1)
	    rc = EOF;
    } else {
	if (putc(c, stdout) == EOF)
	    rc = EOF;
    }
    TPUTS_RETURN(rc);
}


NCURSES_SP_NAME(pair_content) (NCURSES_SP_DCLx
			       NCURSES_PAIRS_T pair,
			       NCURSES_COLOR_T *f,
			       NCURSES_COLOR_T *b)
{
    int my_f, my_b;
    int rc = _nc_pair_content(SP_PARM, pair, &my_f, &my_b);
    if (rc == OK) {
	*f = limit_COLOR(my_f);
	*b = limit_COLOR(my_b);
    }
    return rc;
}


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;
}


_nc_use_tracef(unsigned mask)
{
    bool result = FALSE;

    _nc_lock_global(tst_tracef);
    if (!MyNested++) {
	if ((result = (_nc_tracing & (mask))) != 0
	    && _nc_try_global(tracef) == 0) {
	    /* we will call _nc_locked_tracef(), no nesting so far */
	} else {
	    /* we will not call _nc_locked_tracef() */
	    MyNested = 0;
	}
    } else {
	/* we may call _nc_locked_tracef(), but with nested_tracef > 0 */
	result = (_nc_tracing & (mask));
    }
    _nc_unlock_global(tst_tracef);
    return result;
}


set_window_size(int fd, short *high, short *wide)
{
    STRUCT_WINSIZE win;
    (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
    if (WINSIZE_ROWS(win) == 0 &&
	WINSIZE_COLS(win) == 0) {
	if (*high > 0 && *wide > 0) {
	    WINSIZE_ROWS(win) = (unsigned short) *high;
	    WINSIZE_COLS(win) = (unsigned short) *wide;
	    (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
	}
    } else if (WINSIZE_ROWS(win) > 0 &&
	       WINSIZE_COLS(win) > 0) {
	*high = (short) WINSIZE_ROWS(win);
	*wide = (short) WINSIZE_COLS(win);
    }
}


validate(const char *name)
{
    char *value = tigetstr(name);
    if (!VALID_STRING(value)) {
	if (v_opt > 1) {
	    printf("? %s %s\n",
		   (value == ABSENT_STRING)
		   ? "absent"
		   : "cancel",
		   name);
	}
	value = 0;
    }
    return value;
}


valid_db_path(const char *nominal)
{
    struct stat sb;
#if USE_HASHED_DB
    char suffix[] = DBM_SUFFIX;
    size_t need = strlen(nominal) + sizeof(suffix);
    char *result = malloc(need);

    if (result == 0)
	failed("valid_db_path");
    _nc_STRCPY(result, nominal, need);
    if (strcmp(result + need - sizeof(suffix), suffix)) {
	_nc_STRCAT(result, suffix, need);
    }
#else
    char *result = strdup(nominal);
#endif

    DEBUG(1, ("** stat(%s)", result));
    if (stat(result, &sb) >= 0) {
#if USE_HASHED_DB
	if (!S_ISREG(sb.st_mode)
	    || access(result, R_OK | W_OK) != 0) {
	    DEBUG(1, ("...not a writable file"));
	    free(result);
	    result = 0;
	}
#else
	if (!S_ISDIR(sb.st_mode)
	    || access(result, R_OK | W_OK | X_OK) != 0) {
	    DEBUG(1, ("...not a writable directory"));
	    free(result);
	    result = 0;
	}
#endif
    } else {
	/* check if parent is directory and is writable */
	unsigned leaf = _nc_pathlast(result);

	DEBUG(1, ("...not found"));
	if (leaf) {
	    char save = result[leaf];
	    result[leaf] = 0;
	    if (stat(result, &sb) >= 0
		&& S_ISDIR(sb.st_mode)
		&& access(result, R_OK | W_OK | X_OK) == 0) {
		result[leaf] = save;
	    } else {
		DEBUG(1, ("...parent directory %s is not writable", result));
		free(result);
		result = 0;
	    }
	} else {
	    DEBUG(1, ("... no parent directory"));
	    free(result);
	    result = 0;
	}
    }
    return result;
}


scroll_idl(NCURSES_SP_DCLx int n, int del, int ins, NCURSES_CH_T blank)
{
    int i;

    if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
	return ERR;

    GoTo(NCURSES_SP_ARGx del, 0);
    UpdateAttrs(SP_PARM, blank);
    if (n == 1 && delete_line) {
	NCURSES_PUTP2("delete_line", delete_line);
    } else if (parm_delete_line) {
	TPUTS_TRACE("parm_delete_line");
	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
				TIPARM_1(parm_delete_line, n),
				n,
				NCURSES_SP_NAME(_nc_outch));
    } else {			/* if (delete_line) */
	for (i = 0; i < n; i++) {
	    NCURSES_PUTP2("delete_line", delete_line);
	}
    }

    GoTo(NCURSES_SP_ARGx ins, 0);
    UpdateAttrs(SP_PARM, blank);
    if (n == 1 && insert_line) {
	NCURSES_PUTP2("insert_line", insert_line);
    } else if (parm_insert_line) {
	TPUTS_TRACE("parm_insert_line");
	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
				TIPARM_1(parm_insert_line, n),
				n,
				NCURSES_SP_NAME(_nc_outch));
    } else {			/* if (insert_line) */
	for (i = 0; i < n; i++) {
	    NCURSES_PUTP2("insert_line", insert_line);
	}
    }

    return OK;
}


drv_initacs(TERMINAL_CONTROL_BLOCK * TCB, chtype *real_map, chtype *fake_map)
{
    SCREEN *sp = TCB->csp;

    AssertTCB();
    assert(sp != 0);
    if (ena_acs != NULL) {
	NCURSES_PUTP2("ena_acs", ena_acs);
    }
#if NCURSES_EXT_FUNCS
    /*
     * Linux console "supports" the "PC ROM" character set by the coincidence
     * that smpch/rmpch and smacs/rmacs have the same values.  ncurses has
     * no codepage support (see SCO Merge for an example).  Outside of the
     * values defined in acsc, there are no definitions for the "PC ROM"
     * character set (assumed by some applications to be codepage 437), but we
     * allow those applications to use those codepoints.
     *
     * test/blue.c uses this feature.
     */
#define PCH_KLUDGE(a,b) (a != 0 && b != 0 && !strcmp(a,b))
    if (PCH_KLUDGE(enter_pc_charset_mode, enter_alt_charset_mode) &&
	PCH_KLUDGE(exit_pc_charset_mode, exit_alt_charset_mode)) {
	size_t i;
	for (i = 1; i < ACS_LEN; ++i) {
	    if (real_map[i] == 0) {
		real_map[i] = (chtype) i;
		if (real_map != fake_map) {
		    if (sp != 0)
			sp->_screen_acs_map[i] = TRUE;
		}
	    }
	}
    }
#endif

    if (acs_chars != NULL) {
	size_t i = 0;
	size_t length = strlen(acs_chars);

	while (i + 1 < length) {
	    if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) {
		real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET;
		T(("#%d real_map[%s] = %s",
		   (int) i,
		   _tracechar(UChar(acs_chars[i])),
		   _tracechtype(real_map[UChar(acs_chars[i])])));
		if (sp != 0) {
		    sp->_screen_acs_map[UChar(acs_chars[i])] = TRUE;
		}
	    }
	    i += 2;
	}
    }
#ifdef TRACE
    /* Show the equivalent mapping, noting if it does not match the
     * given attribute, whether by re-ordering or duplication.
     */
    if (USE_TRACEF(TRACE_CALLS)) {
	size_t n, m;
	char show[ACS_LEN * 2 + 1];
	for (n = 1, m = 0; n < ACS_LEN; n++) {
	    if (real_map[n] != 0) {
		show[m++] = (char) n;
		show[m++] = (char) ChCharOf(real_map[n]);
	    }
	}
	show[m] = 0;
	if (acs_chars == NULL || strcmp(acs_chars, show))
	    _tracef("%s acs_chars %s",
		    (acs_chars == NULL) ? "NULL" : "READ",
		    _nc_visbuf(acs_chars));
	_tracef("%s acs_chars %s",
		(acs_chars == NULL)
		? "NULL"
		: (strcmp(acs_chars, show)
		   ? "DIFF"
		   : "SAME"),
		_nc_visbuf(show));
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */
}


parse_tc_delay(const char *tc, double *delays)
{
    return parse_delay_value(tc, delays, (int *) 0);
}


print_ruler(int *tab_list)
{
    int last = 0;
    int stop;
    int n;

    /* first print a readable ruler */
    for (n = 0; n < max_cols; n += 10) {
	int ch = 1 + (n / 10);
	char buffer[20];
	_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
		    "----+----%c",
		    ((ch < 10)
		     ? (ch + '0')
		     : (ch + 'A' - 10)));
	printf("%.*s", ((max_cols - n) > 10) ? 10 : (max_cols - n), buffer);
    }
    putchar('\n');

    /* now, print '*' for each stop */
    for (n = 0, last = 0; (tab_list[n] > 0) && (last < max_cols); ++n) {
	stop = tab_list[n];
	while (++last < stop) {
	    if (last <= max_cols) {
		putchar('-');
	    } else {
		break;
	    }
	}
	if (last <= max_cols) {
	    putchar('*');
	    last = stop;
	} else {
	    break;
	}
    }
    while (++last <= max_cols)
	putchar('-');
    putchar('\n');
}


cleanup(void)
{
    outs(t_me);
    if (!outs(t_oc))
	outs(t_op);
    outs(t_cl);
    outs(t_ve);

    fflush(stdout);
    fprintf(stderr, "\n\n%ld total cells, rate %.2f/sec\n",
	    total_chars,
	    ((double) (total_chars) / (double) (time((time_t *) 0) - started)));
}


ungetmouse(MEVENT * aevent)
{
    return NCURSES_SP_NAME(ungetmouse) (CURRENT_SCREEN, aevent);
}


has_mouse(void)
{
    return _nc_has_mouse(CURRENT_SCREEN);
}


similar_sgr(char *a, char *b)
{
    bool result = FALSE;
    if (a != 0 && b != 0) {
	int csi_a = is_csi(a);
	int csi_b = is_csi(b);
	size_t len_a;
	size_t len_b;

	TR(TRACE_DATABASE, ("similar_sgr:\n\t%s\n\t%s",
			    _nc_visbuf2(1, a),
			    _nc_visbuf2(2, b)));
	if (csi_a != 0 && csi_b != 0 && csi_a == csi_b) {
	    a += csi_a;
	    b += csi_b;
	    if (*a != *b) {
		a = skip_zero(a);
		b = skip_zero(b);
	    }
	}
	len_a = strlen(a);
	len_b = strlen(b);
	if (len_a && len_b) {
	    if (len_a > len_b)
		result = (strncmp(a, b, len_b) == 0);
	    else
		result = (strncmp(a, b, len_a) == 0);
	}
	TR(TRACE_DATABASE, ("...similar_sgr: %d\n\t%s\n\t%s", result,
			    _nc_visbuf2(1, a),
			    _nc_visbuf2(2, b)));
    }
    return result;
}


wrap_cursor(NCURSES_SP_DCL0)
{
    if (eat_newline_glitch) {
	/*
	 * xenl can manifest two different ways.  The vt100 way is that, when
	 * you'd expect the cursor to wrap, it stays hung at the right margin
	 * (on top of the character just emitted) and doesn't wrap until the
	 * *next* graphic char is emitted.  The c100 way is to ignore LF
	 * received just after an am wrap.
	 *
	 * An aggressive way to handle this would be to emit CR/LF after the
	 * char and then assume the wrap is done, you're on the first position
	 * of the next line, and the terminal out of its weird state.  Here
	 * it's safe to just tell the code that the cursor is in hyperspace and
	 * let the next mvcur() call straighten things out.
	 */
	SP_PARM->_curscol = -1;
	SP_PARM->_cursrow = -1;
    } else if (auto_right_margin) {
	SP_PARM->_curscol = 0;
	SP_PARM->_cursrow++;
	/*
	 * We've actually moved - but may have to work around problems with
	 * video attributes not working.
	 */
	if (!move_standout_mode && AttrOf(SCREEN_ATTRS(SP_PARM))) {
	    TR(TRACE_CHARPUT, ("turning off (%#lx) %s before wrapping",
			       (unsigned long) AttrOf(SCREEN_ATTRS(SP_PARM)),
			       _traceattr(AttrOf(SCREEN_ATTRS(SP_PARM)))));
	    VIDPUTS(SP_PARM, A_NORMAL, 0);
	}
    } else {
	SP_PARM->_curscol--;
    }
    position_check(NCURSES_SP_ARGx
		   SP_PARM->_cursrow,
		   SP_PARM->_curscol,
		   "wrap_cursor");
}


_nc_mouse_event(SCREEN *sp)
{
    MEVENT *eventp = sp->_mouse_eventp;
    bool result = FALSE;

    (void) eventp;

    switch (sp->_mouse_type) {
    case M_XTERM:
	/* xterm: never have to query, mouse events are in the keyboard stream */
#if USE_EMX_MOUSE
	{
	    char kbuf[3];

	    int i, res = read(M_FD(sp), &kbuf, 3);	/* Eat the prefix */
	    if (res != 3)
		printf("Got %d chars instead of 3 for prefix.\n", res);
	    for (i = 0; i < res; i++) {
		if (kbuf[i] != key_mouse[i])
		    printf("Got char %d instead of %d for prefix.\n",
			   (int) kbuf[i], (int) key_mouse[i]);
	    }
	    result = TRUE;
	}
#endif /* USE_EMX_MOUSE */
	break;

#if USE_GPM_SUPPORT
    case M_GPM:
	if (sp->_mouse_fd >= 0) {
	    /* query server for event, return TRUE if we find one */
	    Gpm_Event ev;

	    switch (my_Gpm_GetEvent(&ev)) {
	    case 0:
		/* Connection closed, drop the mouse. */
		sp->_mouse_fd = -1;
		break;
	    case 1:
		/* there's only one mouse... */
		eventp->id = NORMAL_EVENT;

		eventp->bstate = 0;
		switch (ev.type & 0x0f) {
		case (GPM_DOWN):
		    if (ev.buttons & GPM_B_LEFT)
			eventp->bstate |= BUTTON1_PRESSED;
		    if (ev.buttons & GPM_B_MIDDLE)
			eventp->bstate |= BUTTON2_PRESSED;
		    if (ev.buttons & GPM_B_RIGHT)
			eventp->bstate |= BUTTON3_PRESSED;
		    break;
		case (GPM_UP):
		    if (ev.buttons & GPM_B_LEFT)
			eventp->bstate |= BUTTON1_RELEASED;
		    if (ev.buttons & GPM_B_MIDDLE)
			eventp->bstate |= BUTTON2_RELEASED;
		    if (ev.buttons & GPM_B_RIGHT)
			eventp->bstate |= BUTTON3_RELEASED;
		    break;
		default:
		    eventp->bstate |= REPORT_MOUSE_POSITION;
		    break;
		}

		eventp->x = ev.x - 1;
		eventp->y = ev.y - 1;
		eventp->z = 0;

		/* bump the next-free pointer into the circular list */
		sp->_mouse_eventp = NEXT(eventp);
		result = TRUE;
		break;
	    }
	}
	break;
#endif

#if USE_SYSMOUSE
    case M_SYSMOUSE:
	if (sp->_sysmouse_head < sp->_sysmouse_tail) {
	    *eventp = sp->_sysmouse_fifo[sp->_sysmouse_head];

	    /*
	     * Point the fifo-head to the next possible location.  If there
	     * are none, reset the indices.  This may be interrupted by the
	     * signal handler, doing essentially the same reset.
	     */
	    sp->_sysmouse_head += 1;
	    if (sp->_sysmouse_head == sp->_sysmouse_tail) {
		sp->_sysmouse_tail = 0;
		sp->_sysmouse_head = 0;
	    }

	    /* bump the next-free pointer into the circular list */
	    sp->_mouse_eventp = eventp = NEXT(eventp);
	    result = TRUE;
	}
	break;
#endif /* USE_SYSMOUSE */

#ifdef USE_TERM_DRIVER
    case M_TERM_DRIVER:
	while (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
	    *eventp = sp->_drv_mouse_fifo[sp->_drv_mouse_head];

	    /*
	     * Point the fifo-head to the next possible location.  If there
	     * are none, reset the indices.
	     */
	    sp->_drv_mouse_head += 1;
	    if (sp->_drv_mouse_head == sp->_drv_mouse_tail) {
		sp->_drv_mouse_tail = 0;
		sp->_drv_mouse_head = 0;
	    }

	    /* bump the next-free pointer into the circular list */
	    sp->_mouse_eventp = eventp = NEXT(eventp);
	    result = TRUE;
	}
	break;
#endif

    case M_NONE:
	break;
    }

    return result;		/* true if we found an event */
}


NCURSES_SP_NAME(_nc_mvcur) (NCURSES_SP_DCLx
			    int yold, int xold,
			    int ynew, int xnew)
{
    int rc;
    rc = _nc_real_mvcur(NCURSES_SP_ARGx yold, xold, ynew, xnew,
			NCURSES_SP_NAME(_nc_outch),
			TRUE);
    /*
     * With the terminal-driver, we cannot distinguish between internal and
     * external calls.  Flush the output if the screen has not been
     * initialized, e.g., when used from low-level terminfo programs.
     */
    if ((SP_PARM != 0) && (SP_PARM->_endwin == ewInitial))
	NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
    return rc;
}


PutRange(NCURSES_SP_DCLx
	 const NCURSES_CH_T *otext,
	 const NCURSES_CH_T *ntext,
	 int row,
	 int first, int last)
{
    int rc;

    TR(TRACE_CHARPUT, ("PutRange(%p, %p, %p, %d, %d, %d)",
		       (void *) SP_PARM,
		       (const void *) otext,
		       (const void *) ntext,
		       row, first, last));

    if (otext != ntext
	&& (last - first + 1) > SP_PARM->_inline_cost) {
	int i, j, same;

	for (j = first, same = 0; j <= last; j++) {
	    if (!same && isWidecExt(otext[j]))
		continue;
	    if (CharEq(otext[j], ntext[j])) {
		same++;
	    } else {
		if (same > SP_PARM->_inline_cost) {
		    EmitRange(NCURSES_SP_ARGx ntext + first, j - same - first);
		    GoTo(NCURSES_SP_ARGx row, first = j);
		}
		same = 0;
	    }
	}
	i = EmitRange(NCURSES_SP_ARGx ntext + first, j - same - first);
	/*
	 * Always return 1 for the next GoTo() after a PutRange() if we found
	 * identical characters at end of interval
	 */
	rc = (same == 0 ? i : 1);
    } else {
	rc = EmitRange(NCURSES_SP_ARGx ntext + first, last - first + 1);
    }
    return rc;
}


test_tparm(const char *name, int *number)
{
    char *format = tigetstr(name);
    if ((format = validate(name)) != 0) {
	char *result = tparm(format,
			     number[0],
			     number[1],
			     number[2],
			     number[3],
			     number[4],
			     number[5],
			     number[6],
			     number[7],
			     number[8]);
	if (v_opt > 1)
	    printf(".. %2d = %2d %2d %2d %2d %2d %2d %2d %2d %2d %s\n",
		   result != 0 ? (int) strlen(result) : -1,
		   number[0],
		   number[1],
		   number[2],
		   number[3],
		   number[4],
		   number[5],
		   number[6],
		   number[7],
		   number[8],
		   name);
    }
}


allow_gpm_mouse(SCREEN *sp GCC_UNUSED)
{
    bool result = FALSE;

#if USE_WEAK_SYMBOLS
    /* Danger Robinson: do not use dlopen for libgpm if already loaded */
    if ((Gpm_Wgetch) != 0) {
	if (!sp->_mouse_gpm_loaded) {
	    T(("GPM library was already dlopen'd, not by us"));
	}
    } else
#endif
	/* GPM does printf's without checking if stdout is a terminal */
    if (NC_ISATTY(fileno(stdout))) {
	const char *list = getenv("NCURSES_GPM_TERMS");
	const char *env = getenv("TERM");
	if (list != 0) {
	    if (env != 0) {
		result = _nc_name_match(list, env, "|:");
	    }
	} else {
	    /* GPM checks the beginning of the $TERM variable to decide if it
	     * should pass xterm events through.  There is no real advantage in
	     * allowing GPM to do this.  Recent versions relax that check, and
	     * pretend that GPM can work with any terminal having the kmous
	     * capability.  Perhaps that works for someone.  If so, they can
	     * set the environment variable (above).
	     */
	    if (env != 0 && strstr(env, "linux") != 0) {
		result = TRUE;
	    }
	}
    }
    return result;
}


drv_release(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
{
}


EmitRange(NCURSES_SP_DCLx const NCURSES_CH_T *ntext, int num)
{
    int i;

    TR(TRACE_CHARPUT, ("EmitRange %d:%s", num, _nc_viscbuf(ntext, num)));

    if (erase_chars || repeat_char) {
	while (num > 0) {
	    int runcount;
	    NCURSES_CH_T ntext0;

	    while (num > 1 && !CharEq(ntext[0], ntext[1])) {
		PutChar(NCURSES_SP_ARGx CHREF(ntext[0]));
		ntext++;
		num--;
	    }
	    ntext0 = ntext[0];
	    if (num == 1) {
		PutChar(NCURSES_SP_ARGx CHREF(ntext0));
		return 0;
	    }
	    runcount = 2;

	    while (runcount < num && CharEq(ntext[runcount], ntext0))
		runcount++;

	    /*
	     * The cost expression in the middle isn't exactly right.
	     * _cup_ch_cost is an upper bound on the cost for moving to the
	     * end of the erased area, but not the cost itself (which we
	     * can't compute without emitting the move).  This may result
	     * in erase_chars not getting used in some situations for
	     * which it would be marginally advantageous.
	     */
	    if (erase_chars
		&& runcount > SP_PARM->_ech_cost + SP_PARM->_cup_ch_cost
		&& can_clear_with(NCURSES_SP_ARGx CHREF(ntext0))) {
		UpdateAttrs(SP_PARM, ntext0);
		NCURSES_PUTP2("erase_chars", TIPARM_1(erase_chars, runcount));

		/*
		 * If this is the last part of the given interval,
		 * don't bother moving cursor, since it can be the
		 * last update on the line.
		 */
		if (runcount < num) {
		    GoTo(NCURSES_SP_ARGx
			 SP_PARM->_cursrow,
			 SP_PARM->_curscol + runcount);
		} else {
		    return 1;	/* cursor stays in the middle */
		}
	    } else if (repeat_char != 0 &&
#if BSD_TPUTS
		       !isdigit(UChar(CharOf(ntext0))) &&
#endif
#if USE_WIDEC_SUPPORT
		       (!SP_PARM->_screen_unicode &&
			(CharOf(ntext0) < ((AttrOf(ntext0) & A_ALTCHARSET)
					   ? ACS_LEN
					   : 256))) &&
#endif
		       runcount > SP_PARM->_rep_cost) {
		NCURSES_CH_T temp;
		bool wrap_possible = (SP_PARM->_curscol + runcount >=
				      screen_columns(SP_PARM));
		int rep_count = runcount;

		if (wrap_possible)
		    rep_count--;

		UpdateAttrs(SP_PARM, ntext0);
		temp = ntext0;
		if ((AttrOf(temp) & A_ALTCHARSET) &&
		    SP_PARM->_acs_map != 0 &&
		    (SP_PARM->_acs_map[CharOf(temp)] & A_CHARTEXT) != 0) {
		    SetChar(temp,
			    (SP_PARM->_acs_map[CharOf(ntext0)] & A_CHARTEXT),
			    AttrOf(ntext0) | A_ALTCHARSET);
		}
		NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
					TIPARM_2(repeat_char,
						 CharOf(temp),
						 rep_count),
					1,
					NCURSES_SP_NAME(_nc_outch));
		SP_PARM->_curscol += rep_count;

		if (wrap_possible)
		    PutChar(NCURSES_SP_ARGx CHREF(ntext0));
	    } else {
		for (i = 0; i < runcount; i++)
		    PutChar(NCURSES_SP_ARGx CHREF(ntext[i]));
	    }
	    ntext += runcount;
	    num -= runcount;
	}
	return 0;
    }

    for (i = 0; i < num; i++)
	PutChar(NCURSES_SP_ARGx CHREF(ntext[i]));
    return 0;
}


matches(char **needle, const char *haystack)
/* does entry in needle list match |-separated field in haystack? */
{
    bool code = FALSE;
    size_t n;

    if (needle != 0) {
	for (n = 0; needle[n] != 0; n++) {
	    if (_nc_name_match(haystack, needle[n], "|")) {
		code = TRUE;
		break;
	    }
	}
    } else
	code = TRUE;
    return (code);
}


init_extended_pair(int pair, int f, int b)
{
    return NCURSES_SP_NAME(init_extended_pair) (CURRENT_SCREEN, pair, f, b);
}


init_pair(NCURSES_COLOR_T pair, NCURSES_COLOR_T f, NCURSES_COLOR_T b)
{
    return NCURSES_SP_NAME(init_pair) (CURRENT_SCREEN, pair, f, b);
}


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;
}


tgoto_internal(const char *string, int x, int y)
{
    static char *result;
    static size_t length;

    int swap_arg;
    int param[3];
    size_t used = 0;
    size_t need = 10;
    int *value = param;
    bool need_BC = FALSE;

    if (BC)
	need += strlen(BC);

    param[0] = y;
    param[1] = x;
    param[2] = 0;

    while (*string != 0) {
	if ((used + need) > length) {
	    length += (used + need);
	    if ((result = typeRealloc(char, length, result)) == 0) {
		length = 0;
		break;
	    }
	}
	if (*string == '%') {
	    const char *fmt = 0;

	    switch (*++string) {
	    case '\0':
		string--;
		break;
	    case 'd':
		fmt = "%d";
		break;
	    case '2':
		fmt = "%02d";
		*value %= 100;
		break;
	    case '3':
		fmt = "%03d";
		*value %= 1000;
		break;
	    case '+':
		*value += UChar(*++string);
		/* FALLTHRU */
	    case '.':
		/*
		 * Guard against tputs() seeing a truncated string.  The
		 * termcap documentation refers to a similar fixup for \n
		 * and \r, but I don't see that it could work -TD
		 */
		if (*value == 0) {
		    if (BC != 0) {
			*value += 1;
			need_BC = TRUE;
		    } else {
			/* tputs will pretend this is \0, which will almost
			 * always work since ANSI-compatible terminals ignore
			 * the character.  ECMA-48 does not document a C1
			 * control for this value.  A few (obsolete) terminals
			 * can use this value in special cases, such as cursor
			 * addressing using single-byte coordinates.
			 */
			*value = 0200;
		    }
		}
		result[used++] = (char) *value++;
		break;
	    case '%':
		result[used++] = *string;
		break;
	    case 'r':
		swap_arg = param[0];
		param[0] = param[1];
		param[1] = swap_arg;
		break;
	    case 'i':
		param[0] += 1;
		param[1] += 1;
		break;
	    case '>':
		if (*value > string[1])
		    *value += string[2];
		string += 2;
		break;
	    case 'n':		/* Datamedia 2500 */
		param[0] ^= 0140;
		param[1] ^= 0140;
		break;
	    case 'B':		/* BCD */
		*value = 16 * (*value / 10) + (*value % 10);
		break;
	    case 'D':		/* Reverse coding (Delta Data) */
		*value -= 2 * (*value % 16);
		break;
	    }
	    if (fmt != 0) {
		_nc_SPRINTF(result + used, _nc_SLIMIT(length - used)
			    fmt, *value++);
		used += strlen(result + used);
		fmt = 0;
	    }
	    if (value - param > 2) {
		value = param + 2;
		*value = 0;
	    }
	} else {
	    result[used++] = *string;
	}
	string++;
    }
    if (result != 0) {
	if (need_BC) {
	    _nc_STRCPY(result + used, BC, length - used);
	    used += strlen(BC);
	}
	result[used] = '\0';
    }
    return result;
}


similar_sgr(int num, char *a, char *b)
{
    char *base_a = a;
    char *base_b = b;
    int delaying = 0;

    while (*b != 0) {
	while (*a != *b) {
	    if (*a == 0) {
		if (num < 0) {
		    ;
		} else 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",
				sgr_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));
}


curses_trace(unsigned tracelevel)
{
    unsigned result;
#if defined(TRACE)
    result = _nc_tracing;
    if ((MyFP == 0) && tracelevel) {
	MyInit = TRUE;
	if (MyFD >= 0) {
	    MyFP = fdopen(MyFD, BIN_W);
	} else {
	    if (MyPath[0] == '\0') {
		size_t size = sizeof(MyPath) - 12;
		if (getcwd(MyPath, size) == 0) {
		    perror("curses: Can't get working directory");
		    exit(EXIT_FAILURE);
		}
		MyPath[size] = '\0';
		assert(strlen(MyPath) <= size);
		_nc_STRCAT(MyPath, "/trace", sizeof(MyPath));
		if (_nc_is_dir_path(MyPath)) {
		    _nc_STRCAT(MyPath, ".log", sizeof(MyPath));
		}
	    }
	    if (_nc_access(MyPath, W_OK) < 0
		|| (MyFD = open(MyPath, O_CREAT | O_EXCL | O_RDWR, 0600)) < 0
		|| (MyFP = fdopen(MyFD, BIN_W)) == 0) {
		;		/* EMPTY */
	    }
	}
	_nc_tracing = tracelevel;
	/* Try to set line-buffered mode, or (failing that) unbuffered,
	 * so that the trace-output gets flushed automatically at the
	 * end of each line.  This is useful in case the program dies.
	 */
	if (MyFP != 0) {
#if HAVE_SETVBUF		/* ANSI */
	    (void) setvbuf(MyFP, (char *) 0, _IOLBF, (size_t) 0);
#elif HAVE_SETBUF /* POSIX */
	    (void) setbuffer(MyFP, (char *) 0);
#endif
	}
	_tracef("TRACING NCURSES version %s.%d (tracelevel=%#x)",
		NCURSES_VERSION,
		NCURSES_VERSION_PATCH,
		tracelevel);
    } else if (tracelevel == 0) {
	if (MyFP != 0) {
	    MyFD = dup(MyFD);	/* allow reopen of same file */
	    fclose(MyFP);
	    MyFP = 0;
	}
	_nc_tracing = tracelevel;
    } else if (_nc_tracing != tracelevel) {
	_nc_tracing = tracelevel;
	_tracef("tracelevel=%#x", tracelevel);
    }
#else
    (void) tracelevel;
    result = 0;
#endif
    return result;
}


open_tempfile(char *filename)
{
    FILE *result = 0;

    _nc_STRCPY(filename, "/tmp/XXXXXX", PATH_MAX);
#if HAVE_MKSTEMP
    {
	int oldmask = (int) umask(077);
	int fd = mkstemp(filename);
	if (fd >= 0)
	    result = fdopen(fd, "w");
	umask((mode_t) oldmask);
    }
#else
    if (tmpnam(filename) != 0)
	result = fopen(filename, "w");
#endif
    return result;
}


NCURSES_SP_NAME(_nc_reset_colors) (NCURSES_SP_DCL0)
{
    int result = FALSE;

    T((T_CALLED("_nc_reset_colors(%p)"), (void *) SP_PARM));
    if (SP_PARM->_color_defs > 0)
	SP_PARM->_color_defs = -(SP_PARM->_color_defs);
    if (reset_color_pair(NCURSES_SP_ARG))
	result = TRUE;

#ifdef USE_TERM_DRIVER
    result = CallDriver(SP_PARM, td_rescolors);
#else
    if (orig_colors != 0) {
	NCURSES_PUTP2("orig_colors", orig_colors);
	result = TRUE;
    }
#endif
    returnBool(result);
}


is_csi(const char *s)
{
    int result = 0;
    if (s != 0) {
	if (UChar(s[0]) == CSI)
	    result = 1;
	else if (s[0] == ESC && s[1] == L_BRACK)
	    result = 2;
    }
    return result;
}


cleanup(void)
{
    int rc;

#if NO_LEAKS
    free_namelist(namelst);
    _nc_leaks_dump_entry();
#endif
    if (tmp_fp != 0)
	fclose(tmp_fp);
    if (to_remove != 0) {
#if HAVE_REMOVE
	rc = remove(to_remove);
#else
	rc = unlink(to_remove);
#endif
	if (rc != 0)
	    perror(to_remove);
    }
}


_nc_count_outchars(long increment)
{
    if (CURRENT_SCREEN)
	CURRENT_SCREEN->_outchars += increment;
    else
	_nc_prescreen._outchars += increment;
}


main(int argc, char *argv[])
{
    int n;
    int r_run, t_run, n_run;
    char *old_term = getenv("TERM");
    int r_opt = 1;
    char *t_opt = 0;
    int len_names = 0;		/* cur # of items in all_names[] */
    int use_names = 10;		/* max # of items in all_names[] */
    char **all_names = typeCalloc(char *, use_names);
    int all_parms[10];		/* workspace for "-a" option */
    int len_terms = 0;		/* cur # of items in all_terms[] */
    int use_terms = 10;		/* max # of items in all_terms[] */
    char **all_terms = typeCalloc(char *, use_terms);
    int len_parms = 0;		/* cur # of items in num_parms[], str_parms[] */
    int use_parms = argc + 10;	/* max # of items in num_parms[], str_parms[] */
    int *num_parms = typeCalloc(int, use_parms);
    char **str_parms = typeCalloc(char *, use_parms);

    if (all_names == 0 || all_terms == 0 || num_parms == 0 || str_parms == 0)
	failed("no memory");

    while ((n = getopt(argc, argv, "T:ar:v")) != -1) {
	switch (n) {
	case 'T':
	    t_opt = optarg;
	    break;
	case 'a':
	    ++a_opt;
	    break;
	case 'r':
	    r_opt = atoi(optarg);
	    break;
	case 'v':
	    ++v_opt;
	    break;
	default:
	    usage();
	    break;
	}
    }

    /*
     * If there is a nonnumeric parameter after the options, use that as the
     * capability name.
     */
    if (optind < argc) {
	if (!isNumeric(argv[optind])) {
	    all_names[len_names++] = strdup(argv[optind++]);
	}
    }

    /*
     * Any remaining arguments must be possible parameter values.  If numeric,
     * and "-a" is not set, use those as the maximum values within which the
     * test parameters should vary.
     */
    while (optind < argc) {
	if (isNumeric(argv[optind])) {
	    char *dummy = 0;
	    long value = strtol(argv[optind], &dummy, 0);
	    num_parms[len_parms] = (int) value;
	}
	str_parms[len_parms] = argv[optind];
	++optind;
	++len_parms;
    }
    for (n = len_parms; n < use_parms; ++n) {
	static char dummy[1];
	str_parms[n] = dummy;
    }
    if (v_opt) {
	printf("%d parameter%s%s\n", PLURAL(len_parms), COLONS(len_parms));
	for (n = 0; n < len_parms; ++n) {
	    printf(" %d: %d (%s)\n", n + 1, num_parms[n], str_parms[n]);
	}
    }

    /*
     * Make a list of values for $TERM.  Accept "-" for standard input to
     * simplify scripting a check of the whole database.
     */
    old_term = strdup((old_term == 0) ? "unknown" : old_term);
    if (t_opt != 0) {
	if (!strcmp(t_opt, "-")) {
	    char buffer[BUFSIZ];
	    while (fgets(buffer, sizeof(buffer) - 1, stdin) != 0) {
		char *s = buffer;
		char *t;
		while (isspace(UChar(s[0])))
		    ++s;
		t = s + strlen(s);
		while (t != s && isspace(UChar(t[-1])))
		    *--t = '\0';
		s = strdup(s);
		if (len_terms + 2 >= use_terms) {
		    use_terms *= 2;
		    all_terms = typeRealloc(char *, use_terms, all_terms);
		    if (all_terms == 0)
			failed("no memory: all_terms");
		}
		all_terms[len_terms++] = s;
	    }
	} else {
	    char *s = t_opt;
	    char *t;
	    while ((t = strtok(s, ",")) != 0) {
		s = 0;
		if (len_terms + 2 >= use_terms) {
		    use_terms *= 2;
		    all_terms = typeRealloc(char *, use_terms, all_terms);
		    if (all_terms == 0)
			failed("no memory: all_terms");
		}
		all_terms[len_terms++] = strdup(t);
	    }
	}
    } else {
	all_terms[len_terms++] = strdup(old_term);
    }
    all_terms[len_terms] = 0;
    if (v_opt) {
	printf("%d term%s:\n", PLURAL(len_terms));
	for (n = 0; n < len_terms; ++n) {
	    printf(" %d: %s\n", n + 1, all_terms[n]);
	}
    }

    /*
     * If no capability name was selected, use the predefined list of string
     * capabilities.
     *
     * TODO: To address the "other" systems which do not follow SVr4,
     * just use the output from infocmp on $TERM.
     */
    if (len_names == 0) {
#if defined(HAVE_CURSES_DATA_BOOLNAMES) || defined(DECL_CURSES_DATA_BOOLNAMES)
	for (n = 0; strnames[n] != 0; ++n) {
	    if (len_names + 2 >= use_names) {
		use_names *= 2;
		all_names = typeRealloc(char *, use_names, all_names);
		if (all_names == 0) {
		    failed("no memory: all_names");
		}
	    }
	    all_names[len_names++] = strdup(strnames[n]);
	}
#else
	all_names[len_names++] = strdup("cup");
	all_names[len_names++] = strdup("sgr");
#endif
    }
    all_names[len_names] = 0;
    if (v_opt) {
	printf("%d name%s%s\n", PLURAL(len_names), COLONS(len_names));
	for (n = 0; n < len_names; ++n) {
	    printf(" %d: %s\n", n + 1, all_names[n]);
	}
    }

    if (r_opt <= 0)
	r_opt = 1;

    for (r_run = 0; r_run < r_opt; ++r_run) {
	for (t_run = 0; t_run < len_terms; ++t_run) {
	    int errs;

	    if (setupterm(all_terms[t_run], fileno(stdout), &errs) != OK) {
		printf("** skipping %s (errs:%d)\n", all_terms[t_run], errs);
	    }

	    if (v_opt)
		printf("** testing %s\n", all_terms[t_run]);
	    if (len_names == 1) {
		if (a_opt) {
		    /* for each combination of values */
		    memset(all_parms, 0, sizeof(all_parms));
		    do {
			test_tparm(all_names[0], all_parms);
		    }
		    while (increment(all_parms, num_parms, len_parms, 0));
		} else {
		    /* for the given values */
		    test_tparm(all_names[0], num_parms);
		}
	    } else {
		for (n_run = 0; n_run < len_names; ++n_run) {
		    test_tparm(all_names[n_run], num_parms);
		}
	    }
	    if (cur_term != 0) {
		del_curterm(cur_term);
	    } else {
		printf("? no cur_term\n");
	    }
	}
    }
#if NO_LEAKS
    for (n = 0; n < len_names; ++n) {
	free(all_names[n]);
    }
    free(all_names);
    free(old_term);
    for (n = 0; n < len_terms; ++n) {
	free(all_terms[n]);
    }
    free(all_terms);
    free(num_parms);
    free(str_parms);
#endif

    ExitProgram(EXIT_SUCCESS);
}


has_colors(void)
{
    return NCURSES_SP_NAME(has_colors) (CURRENT_SCREEN);
}


_nc_has_mouse(SCREEN *sp)
{
    return (((0 == sp) || (sp->_mouse_type == M_NONE)) ? FALSE : TRUE);
}


drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
{
    bool result = FALSE;
    SCREEN *sp;

    AssertTCB();
    SetSP();

    if (orig_pair != 0) {
	NCURSES_PUTP2("orig_pair", orig_pair);
	result = TRUE;
    }
    return result;
}


_nc_retrace_cptr(const char *code)
{
    T((T_RETURN("%s"), _nc_visbuf(code)));
    return code;
}


reset_color_pairs(void)
{
    NCURSES_SP_NAME(reset_color_pairs) (CURRENT_SCREEN);
}


drv_initpair(TERMINAL_CONTROL_BLOCK * TCB, int pair, int f, int b)
{
    SCREEN *sp;

    AssertTCB();
    SetSP();

    if ((initialize_pair != NULL) && InPalette(f) && InPalette(b)) {
	const color_t *tp = InfoOf(sp).defaultPalette;

	TR(TRACE_ATTRS,
	   ("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)",
	    pair,
	    tp[f].red, tp[f].green, tp[f].blue,
	    tp[b].red, tp[b].green, tp[b].blue));

	NCURSES_PUTP2("initialize_pair",
		      TIPARM_7(initialize_pair,
			       pair,
			       tp[f].red, tp[f].green, tp[f].blue,
			       tp[b].red, tp[b].green, tp[b].blue));
    }
}


_nc_do_color(int old_pair, int pair, int reverse, NCURSES_OUTC outc)
{
    SetSafeOutcWrapper(outc);
    NCURSES_SP_NAME(_nc_do_color) (CURRENT_SCREEN,
				   old_pair,
				   pair,
				   reverse,
				   _nc_outc_wrapper);
}