150276Speter/****************************************************************************
2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1996-on                 *
33262629Sdelphij *     and: Juergen Pfeifer                         2009                    *
3450276Speter ****************************************************************************/
3550276Speter
3650276Speter/*
3750276Speter**	lib_mvcur.c
3850276Speter**
3950276Speter**	The routines for moving the physical cursor and scrolling:
4050276Speter**
4150276Speter**		void _nc_mvcur_init(void)
4250276Speter**
4350276Speter**		void _nc_mvcur_resume(void)
4450276Speter**
4550276Speter**		int mvcur(int old_y, int old_x, int new_y, int new_x)
4650276Speter**
4750276Speter**		void _nc_mvcur_wrap(void)
4850276Speter**
4950276Speter** Comparisons with older movement optimizers:
5050276Speter**    SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin.
5150276Speter**    4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local
5250276Speter** motions.  It doesn't use tactics based on auto_left_margin.  Weirdly
5350276Speter** enough, it doesn't use its own hardware-scrolling routine to scroll up
5450276Speter** destination lines for out-of-bounds addresses!
5550276Speter**    old ncurses optimizer: less accurate cost computations (in fact,
5650276Speter** it was broken and had to be commented out!).
5750276Speter**
5850276Speter** Compile with -DMAIN to build an interactive tester/timer for the movement
5950276Speter** optimizer.  You can use it to investigate the optimizer's behavior.
6050276Speter** You can also use it for tuning the formulas used to determine whether
6150276Speter** or not full optimization is attempted.
6250276Speter**
6350276Speter** This code has a nasty tendency to find bugs in terminfo entries, because it
6450276Speter** exercises the non-cup movement capabilities heavily.  If you think you've
6550276Speter** found a bug, try deleting subsets of the following capabilities (arranged
6650276Speter** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud,
6750276Speter** cuf, cub, cuu1, cud1, cuf1, cub1.  It may be that one or more are wrong.
6850276Speter**
6950276Speter** Note: you should expect this code to look like a resource hog in a profile.
7050276Speter** That's because it does a lot of I/O, through the tputs() calls.  The I/O
7150276Speter** cost swamps the computation overhead (and as machines get faster, this
7250276Speter** will become even more true).  Comments in the test exerciser at the end
7350276Speter** go into detail about tuning and how you can gauge the optimizer's
7450276Speter** effectiveness.
7550276Speter**/
7650276Speter
7750276Speter/****************************************************************************
7850276Speter *
7950276Speter * Constants and macros for optimizer tuning.
8050276Speter *
8150276Speter ****************************************************************************/
8250276Speter
8350276Speter/*
8450276Speter * The average overhead of a full optimization computation in character
8550276Speter * transmission times.  If it's too high, the algorithm will be a bit
8650276Speter * over-biased toward using cup rather than local motions; if it's too
8750276Speter * low, the algorithm may spend more time than is strictly optimal
8850276Speter * looking for non-cup motions.  Profile the optimizer using the `t'
8950276Speter * command of the exerciser (see below), and round to the nearest integer.
9050276Speter *
9150276Speter * Yes, I (esr) thought about computing expected overhead dynamically, say
9250276Speter * by derivation from a running average of optimizer times.  But the
9350276Speter * whole point of this optimization is to *decrease* the frequency of
9450276Speter * system calls. :-)
9550276Speter */
9650276Speter#define COMPUTE_OVERHEAD	1	/* I use a 90MHz Pentium @ 9.6Kbps */
9750276Speter
9850276Speter/*
9950276Speter * LONG_DIST is the distance we consider to be just as costly to move over as a
10050276Speter * cup sequence is to emit.  In other words, it's the length of a cup sequence
10150276Speter * adjusted for average computation overhead.  The magic number is the length
10250276Speter * of "\033[yy;xxH", the typical cup sequence these days.
10350276Speter */
10450276Speter#define LONG_DIST		(8 - COMPUTE_OVERHEAD)
10550276Speter
10650276Speter/*
10750276Speter * Tell whether a motion is optimizable by local motions.  Needs to be cheap to
10850276Speter * compute. In general, all the fast moves go to either the right or left edge
10950276Speter * of the screen.  So any motion to a location that is (a) further away than
11050276Speter * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST,
11150276Speter * we'll consider nonlocal.
11250276Speter */
113262629Sdelphij#define NOT_LOCAL(sp, fy, fx, ty, tx)	((tx > LONG_DIST) \
114262629Sdelphij		 && (tx < screen_columns(sp) - 1 - LONG_DIST) \
115166124Srafan		 && (abs(ty-fy) + abs(tx-fx) > LONG_DIST))
11650276Speter
11750276Speter/****************************************************************************
11850276Speter *
11950276Speter * External interfaces
12050276Speter *
12150276Speter ****************************************************************************/
12250276Speter
12350276Speter/*
12450276Speter * For this code to work OK, the following components must live in the
12550276Speter * screen structure:
12650276Speter *
12750276Speter *	int		_char_padding;	// cost of character put
12850276Speter *	int		_cr_cost;	// cost of (carriage_return)
12950276Speter *	int		_cup_cost;	// cost of (cursor_address)
13050276Speter *	int		_home_cost;	// cost of (cursor_home)
13150276Speter *	int		_ll_cost;	// cost of (cursor_to_ll)
13250276Speter *#if USE_HARD_TABS
13350276Speter *	int		_ht_cost;	// cost of (tab)
13450276Speter *	int		_cbt_cost;	// cost of (back_tab)
13550276Speter *#endif USE_HARD_TABS
13650276Speter *	int		_cub1_cost;	// cost of (cursor_left)
13750276Speter *	int		_cuf1_cost;	// cost of (cursor_right)
13850276Speter *	int		_cud1_cost;	// cost of (cursor_down)
13950276Speter *	int		_cuu1_cost;	// cost of (cursor_up)
14050276Speter *	int		_cub_cost;	// cost of (parm_cursor_left)
14150276Speter *	int		_cuf_cost;	// cost of (parm_cursor_right)
14250276Speter *	int		_cud_cost;	// cost of (parm_cursor_down)
14350276Speter *	int		_cuu_cost;	// cost of (parm_cursor_up)
14450276Speter *	int		_hpa_cost;	// cost of (column_address)
14550276Speter *	int		_vpa_cost;	// cost of (row_address)
14650276Speter *	int		_ech_cost;	// cost of (erase_chars)
14750276Speter *	int		_rep_cost;	// cost of (repeat_char)
14850276Speter *
14950276Speter * The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs
15050276Speter * for local motions.  On many systems, it's not, due to uncertainties about
15150276Speter * tab delays and whether or not tabs will be expanded in raw mode.  If you
15250276Speter * have parm_right_cursor, tab motions don't win you a lot anyhow.
15350276Speter */
15450276Speter
15550276Speter#include <curses.priv.h>
15650276Speter#include <ctype.h>
15750276Speter
158262629Sdelphij#ifndef CUR
159262629Sdelphij#define CUR SP_TERMTYPE
160262629Sdelphij#endif
16150276Speter
162262685SdelphijMODULE_ID("$Id: lib_mvcur.c,v 1.133 2013/05/25 23:59:41 tom Exp $")
16350276Speter
164262629Sdelphij#define WANT_CHAR(sp, y, x) NewScreen(sp)->_line[y].text[x]	/* desired state */
165262629Sdelphij
166262629Sdelphij#if NCURSES_SP_FUNCS
167262629Sdelphij#define BAUDRATE(sp)	sp->_term->_baudrate	/* bits per second */
168262629Sdelphij#else
169262629Sdelphij#define BAUDRATE(sp)	cur_term->_baudrate	/* bits per second */
170262629Sdelphij#endif
171262629Sdelphij
17250276Speter#if defined(MAIN) || defined(NCURSES_TEST)
17350276Speter#include <sys/time.h>
17450276Speter
17550276Speterstatic bool profiling = FALSE;
17650276Speterstatic float diff;
17750276Speter#endif /* MAIN */
17850276Speter
179262685Sdelphij#undef NCURSES_OUTC_FUNC
180262685Sdelphij#define NCURSES_OUTC_FUNC myOutCh
181262685Sdelphij
18250276Speter#define OPT_SIZE 512
18350276Speter
184262629Sdelphijstatic int normalized_cost(NCURSES_SP_DCLx const char *const cap, int affcnt);
18550276Speter
18650276Speter/****************************************************************************
18750276Speter *
18850276Speter * Initialization/wrapup (including cost pre-computation)
18950276Speter *
19050276Speter ****************************************************************************/
19150276Speter
19250276Speter#ifdef TRACE
19350276Speterstatic int
194262629Sdelphijtrace_cost_of(NCURSES_SP_DCLx const char *capname, const char *cap, int affcnt)
19550276Speter{
196262629Sdelphij    int result = NCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_ARGx cap, affcnt);
19762449Speter    TR(TRACE_CHARPUT | TRACE_MOVE,
19866963Speter       ("CostOf %s %d %s", capname, result, _nc_visbuf(cap)));
19962449Speter    return result;
20050276Speter}
201262629Sdelphij#define CostOf(cap,affcnt) trace_cost_of(NCURSES_SP_ARGx #cap, cap, affcnt)
20250276Speter
20350276Speterstatic int
204262629Sdelphijtrace_normalized_cost(NCURSES_SP_DCLx const char *capname, const char *cap, int affcnt)
20550276Speter{
206262629Sdelphij    int result = normalized_cost(NCURSES_SP_ARGx cap, affcnt);
20762449Speter    TR(TRACE_CHARPUT | TRACE_MOVE,
20866963Speter       ("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap)));
20962449Speter    return result;
21050276Speter}
211262629Sdelphij#define NormalizedCost(cap,affcnt) trace_normalized_cost(NCURSES_SP_ARGx #cap, cap, affcnt)
21250276Speter
21350276Speter#else
21450276Speter
215262629Sdelphij#define CostOf(cap,affcnt) NCURSES_SP_NAME(_nc_msec_cost)(NCURSES_SP_ARGx cap, affcnt)
216262629Sdelphij#define NormalizedCost(cap,affcnt) normalized_cost(NCURSES_SP_ARGx cap, affcnt)
21750276Speter
21850276Speter#endif
21950276Speter
22076726SpeterNCURSES_EXPORT(int)
221262629SdelphijNCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_DCLx const char *const cap, int affcnt)
22250276Speter/* compute the cost of a given operation */
22350276Speter{
22450276Speter    if (cap == 0)
22562449Speter	return (INFINITY);
22662449Speter    else {
22762449Speter	const char *cp;
22866963Speter	float cum_cost = 0.0;
22950276Speter
23062449Speter	for (cp = cap; *cp; cp++) {
23150276Speter	    /* extract padding, either mandatory or required */
23262449Speter	    if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>')) {
23366963Speter		float number = 0.0;
23450276Speter
23562449Speter		for (cp += 2; *cp != '>'; cp++) {
23697049Speter		    if (isdigit(UChar(*cp)))
237262629Sdelphij			number = number * 10 + (float) (*cp - '0');
23850276Speter		    else if (*cp == '*')
239262629Sdelphij			number *= (float) affcnt;
24097049Speter		    else if (*cp == '.' && (*++cp != '>') && isdigit(UChar(*cp)))
241262629Sdelphij			number += (float) ((*cp - '0') / 10.0);
24250276Speter		}
24350276Speter
24466963Speter#if NCURSES_NO_PADDING
245262629Sdelphij		if (!GetNoPadding(SP_PARM))
24662449Speter#endif
24762449Speter		    cum_cost += number * 10;
248262629Sdelphij	    } else if (SP_PARM) {
249262629Sdelphij		cum_cost += (float) SP_PARM->_char_padding;
250262629Sdelphij	    }
25150276Speter	}
25250276Speter
25362449Speter	return ((int) cum_cost);
25450276Speter    }
25550276Speter}
25650276Speter
257262629Sdelphij#if NCURSES_SP_FUNCS
258262629SdelphijNCURSES_EXPORT(int)
259262629Sdelphij_nc_msec_cost(const char *const cap, int affcnt)
260262629Sdelphij{
261262629Sdelphij    return NCURSES_SP_NAME(_nc_msec_cost) (CURRENT_SCREEN, cap, affcnt);
262262629Sdelphij}
263262629Sdelphij#endif
264262629Sdelphij
26562449Speterstatic int
266262629Sdelphijnormalized_cost(NCURSES_SP_DCLx const char *const cap, int affcnt)
26750276Speter/* compute the effective character-count for an operation (round up) */
26850276Speter{
269262629Sdelphij    int cost = NCURSES_SP_NAME(_nc_msec_cost) (NCURSES_SP_ARGx cap, affcnt);
27062449Speter    if (cost != INFINITY)
271262629Sdelphij	cost = (cost + SP_PARM->_char_padding - 1) / SP_PARM->_char_padding;
27262449Speter    return cost;
27350276Speter}
27450276Speter
27562449Speterstatic void
276262629Sdelphijreset_scroll_region(NCURSES_SP_DCL0)
27750276Speter/* Set the scroll-region to a known state (the default) */
27850276Speter{
27962449Speter    if (change_scroll_region) {
280262685Sdelphij	NCURSES_PUTP2("change_scroll_region",
281262685Sdelphij		      TPARM_2(change_scroll_region,
282262685Sdelphij			      0, screen_lines(SP_PARM) - 1));
28350276Speter    }
28450276Speter}
28550276Speter
28676726SpeterNCURSES_EXPORT(void)
287262629SdelphijNCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_DCL0)
28850276Speter/* what to do at initialization time and after each shellout */
28950276Speter{
290262685Sdelphij    if (!SP_PARM || !IsTermInfo(SP_PARM))
291262629Sdelphij	return;
292262629Sdelphij
29350276Speter    /* initialize screen for cursor access */
29462449Speter    if (enter_ca_mode) {
295262685Sdelphij	NCURSES_PUTP2("enter_ca_mode", enter_ca_mode);
29650276Speter    }
29750276Speter
29850276Speter    /*
29950276Speter     * Doing this here rather than in _nc_mvcur_wrap() ensures that
30050276Speter     * ncurses programs will see a reset scroll region even if a
30150276Speter     * program that messed with it died ungracefully.
30250276Speter     *
30350276Speter     * This also undoes the effects of terminal init strings that assume
30450276Speter     * they know the screen size.  This is useful when you're running
30550276Speter     * a vt100 emulation through xterm.
30650276Speter     */
307262629Sdelphij    reset_scroll_region(NCURSES_SP_ARG);
308262629Sdelphij    SP_PARM->_cursrow = SP_PARM->_curscol = -1;
30950276Speter
31050276Speter    /* restore cursor shape */
311262629Sdelphij    if (SP_PARM->_cursor != -1) {
312262629Sdelphij	int cursor = SP_PARM->_cursor;
313262629Sdelphij	SP_PARM->_cursor = -1;
314262629Sdelphij	NCURSES_SP_NAME(curs_set) (NCURSES_SP_ARGx cursor);
31550276Speter    }
31650276Speter}
31750276Speter
318262629Sdelphij#if NCURSES_SP_FUNCS
31976726SpeterNCURSES_EXPORT(void)
320262629Sdelphij_nc_mvcur_resume(void)
321262629Sdelphij{
322262629Sdelphij    NCURSES_SP_NAME(_nc_mvcur_resume) (CURRENT_SCREEN);
323262629Sdelphij}
324262629Sdelphij#endif
325262629Sdelphij
326262629SdelphijNCURSES_EXPORT(void)
327262629SdelphijNCURSES_SP_NAME(_nc_mvcur_init) (NCURSES_SP_DCL0)
32850276Speter/* initialize the cost structure */
32950276Speter{
330262685Sdelphij    if (SP_PARM->_ofp && isatty(fileno(SP_PARM->_ofp))) {
331262629Sdelphij	SP_PARM->_char_padding = ((BAUDBYTE * 1000 * 10)
332262629Sdelphij				  / (BAUDRATE(SP_PARM) > 0
333262629Sdelphij				     ? BAUDRATE(SP_PARM)
334262629Sdelphij				     : 9600));
335262685Sdelphij    } else {
336262629Sdelphij	SP_PARM->_char_padding = 1;	/* must be nonzero */
337262685Sdelphij    }
338262629Sdelphij    if (SP_PARM->_char_padding <= 0)
339262629Sdelphij	SP_PARM->_char_padding = 1;	/* must be nonzero */
340262629Sdelphij    TR(TRACE_CHARPUT | TRACE_MOVE, ("char_padding %d msecs", SP_PARM->_char_padding));
34150276Speter
34250276Speter    /* non-parameterized local-motion strings */
343262629Sdelphij    SP_PARM->_cr_cost = CostOf(carriage_return, 0);
344262629Sdelphij    SP_PARM->_home_cost = CostOf(cursor_home, 0);
345262629Sdelphij    SP_PARM->_ll_cost = CostOf(cursor_to_ll, 0);
34650276Speter#if USE_HARD_TABS
347166124Srafan    if (getenv("NCURSES_NO_HARD_TABS") == 0) {
348262629Sdelphij	SP_PARM->_ht_cost = CostOf(tab, 0);
349262629Sdelphij	SP_PARM->_cbt_cost = CostOf(back_tab, 0);
350166124Srafan    } else {
351262629Sdelphij	SP_PARM->_ht_cost = INFINITY;
352262629Sdelphij	SP_PARM->_cbt_cost = INFINITY;
353166124Srafan    }
35450276Speter#endif /* USE_HARD_TABS */
355262629Sdelphij    SP_PARM->_cub1_cost = CostOf(cursor_left, 0);
356262629Sdelphij    SP_PARM->_cuf1_cost = CostOf(cursor_right, 0);
357262629Sdelphij    SP_PARM->_cud1_cost = CostOf(cursor_down, 0);
358262629Sdelphij    SP_PARM->_cuu1_cost = CostOf(cursor_up, 0);
35950276Speter
360262629Sdelphij    SP_PARM->_smir_cost = CostOf(enter_insert_mode, 0);
361262629Sdelphij    SP_PARM->_rmir_cost = CostOf(exit_insert_mode, 0);
362262629Sdelphij    SP_PARM->_ip_cost = 0;
36350276Speter    if (insert_padding) {
364262629Sdelphij	SP_PARM->_ip_cost = CostOf(insert_padding, 0);
36550276Speter    }
36650276Speter
36750276Speter    /*
36850276Speter     * Assumption: if the terminal has memory_relative addressing, the
36950276Speter     * initialization strings or smcup will set single-page mode so we
37050276Speter     * can treat it like absolute screen addressing.  This seems to be true
37150276Speter     * for all cursor_mem_address terminal types in the terminfo database.
37250276Speter     */
373262629Sdelphij    SP_PARM->_address_cursor = cursor_address ? cursor_address : cursor_mem_address;
37450276Speter
37550276Speter    /*
37650276Speter     * Parametrized local-motion strings.  This static cost computation
37750276Speter     * depends on the following assumptions:
37850276Speter     *
37950276Speter     * (1) They never have * padding.  In the entire master terminfo database
38050276Speter     *     as of March 1995, only the obsolete Zenith Z-100 pc violates this.
38162449Speter     *     (Proportional padding is found mainly in insert, delete and scroll
38250276Speter     *     capabilities).
38350276Speter     *
38450276Speter     * (2) The average case of cup has two two-digit parameters.  Strictly,
38550276Speter     *     the average case for a 24 * 80 screen has ((10*10*(1 + 1)) +
38650276Speter     *     (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458
38750276Speter     *     digits of parameters.  On a 25x80 screen the average is 3.6197.
38850276Speter     *     On larger screens the value gets much closer to 4.
38950276Speter     *
39050276Speter     * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters
39150276Speter     *     (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750).
39250276Speter     *
39350276Speter     * (4) The average case of cud/cuu/vpa has 2 digits of parameters
39450276Speter     *     (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833).
39550276Speter     *
39650276Speter     * All these averages depend on the assumption that all parameter values
39750276Speter     * are equally probable.
39850276Speter     */
399262629Sdelphij    SP_PARM->_cup_cost = CostOf(TPARM_2(SP_PARM->_address_cursor, 23, 23), 1);
400262629Sdelphij    SP_PARM->_cub_cost = CostOf(TPARM_1(parm_left_cursor, 23), 1);
401262629Sdelphij    SP_PARM->_cuf_cost = CostOf(TPARM_1(parm_right_cursor, 23), 1);
402262629Sdelphij    SP_PARM->_cud_cost = CostOf(TPARM_1(parm_down_cursor, 23), 1);
403262629Sdelphij    SP_PARM->_cuu_cost = CostOf(TPARM_1(parm_up_cursor, 23), 1);
404262629Sdelphij    SP_PARM->_hpa_cost = CostOf(TPARM_1(column_address, 23), 1);
405262629Sdelphij    SP_PARM->_vpa_cost = CostOf(TPARM_1(row_address, 23), 1);
40650276Speter
40750276Speter    /* non-parameterized screen-update strings */
408262629Sdelphij    SP_PARM->_ed_cost = NormalizedCost(clr_eos, 1);
409262629Sdelphij    SP_PARM->_el_cost = NormalizedCost(clr_eol, 1);
410262629Sdelphij    SP_PARM->_el1_cost = NormalizedCost(clr_bol, 1);
411262629Sdelphij    SP_PARM->_dch1_cost = NormalizedCost(delete_character, 1);
412262629Sdelphij    SP_PARM->_ich1_cost = NormalizedCost(insert_character, 1);
41350276Speter
414166124Srafan    /*
415166124Srafan     * If this is a bce-terminal, we want to bias the choice so we use clr_eol
416166124Srafan     * rather than spaces at the end of a line.
417166124Srafan     */
418166124Srafan    if (back_color_erase)
419262629Sdelphij	SP_PARM->_el_cost = 0;
420166124Srafan
42150276Speter    /* parameterized screen-update strings */
422262629Sdelphij    SP_PARM->_dch_cost = NormalizedCost(TPARM_1(parm_dch, 23), 1);
423262629Sdelphij    SP_PARM->_ich_cost = NormalizedCost(TPARM_1(parm_ich, 23), 1);
424262629Sdelphij    SP_PARM->_ech_cost = NormalizedCost(TPARM_1(erase_chars, 23), 1);
425262629Sdelphij    SP_PARM->_rep_cost = NormalizedCost(TPARM_2(repeat_char, ' ', 23), 1);
42650276Speter
427262629Sdelphij    SP_PARM->_cup_ch_cost = NormalizedCost(
428262629Sdelphij					      TPARM_2(SP_PARM->_address_cursor,
429262629Sdelphij						      23, 23),
430262629Sdelphij					      1);
431262629Sdelphij    SP_PARM->_hpa_ch_cost = NormalizedCost(TPARM_1(column_address, 23), 1);
432262629Sdelphij    SP_PARM->_cuf_ch_cost = NormalizedCost(TPARM_1(parm_right_cursor, 23), 1);
433262629Sdelphij    SP_PARM->_inline_cost = min(SP_PARM->_cup_ch_cost,
434262629Sdelphij				min(SP_PARM->_hpa_ch_cost,
435262629Sdelphij				    SP_PARM->_cuf_ch_cost));
43650276Speter
43750276Speter    /*
43850276Speter     * If save_cursor is used within enter_ca_mode, we should not use it for
43950276Speter     * scrolling optimization, since the corresponding restore_cursor is not
44050276Speter     * nested on the various terminals (vt100, xterm, etc.) which use this
44150276Speter     * feature.
44250276Speter     */
44350276Speter    if (save_cursor != 0
44462449Speter	&& enter_ca_mode != 0
44562449Speter	&& strstr(enter_ca_mode, save_cursor) != 0) {
44650276Speter	T(("...suppressed sc/rc capability due to conflict with smcup/rmcup"));
44750276Speter	save_cursor = 0;
44850276Speter	restore_cursor = 0;
44950276Speter    }
45050276Speter
45150276Speter    /*
452262629Sdelphij     * A different, possibly better way to arrange this would be to set the
453262629Sdelphij     * SCREEN's _endwin to TRUE at window initialization time and let this be
45450276Speter     * called by doupdate's return-from-shellout code.
45550276Speter     */
456262629Sdelphij    NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
45750276Speter}
45850276Speter
459262629Sdelphij#if NCURSES_SP_FUNCS
46076726SpeterNCURSES_EXPORT(void)
461262629Sdelphij_nc_mvcur_init(void)
462262629Sdelphij{
463262629Sdelphij    NCURSES_SP_NAME(_nc_mvcur_init) (CURRENT_SCREEN);
464262629Sdelphij}
465262629Sdelphij#endif
466262629Sdelphij
467262629SdelphijNCURSES_EXPORT(void)
468262629SdelphijNCURSES_SP_NAME(_nc_mvcur_wrap) (NCURSES_SP_DCL0)
46950276Speter/* wrap up cursor-addressing mode */
47050276Speter{
47150276Speter    /* leave cursor at screen bottom */
472262629Sdelphij    TINFO_MVCUR(NCURSES_SP_ARGx -1, -1, screen_lines(SP_PARM) - 1, 0);
47350276Speter
474262629Sdelphij    if (!SP_PARM || !IsTermInfo(SP_PARM))
475262629Sdelphij	return;
476262629Sdelphij
47750276Speter    /* set cursor to normal mode */
478262629Sdelphij    if (SP_PARM->_cursor != -1) {
479262629Sdelphij	int cursor = SP_PARM->_cursor;
480262629Sdelphij	NCURSES_SP_NAME(curs_set) (NCURSES_SP_ARGx 1);
481262629Sdelphij	SP_PARM->_cursor = cursor;
482184989Srafan    }
48350276Speter
48462449Speter    if (exit_ca_mode) {
485262685Sdelphij	NCURSES_PUTP2("exit_ca_mode", exit_ca_mode);
48650276Speter    }
48750276Speter    /*
48850276Speter     * Reset terminal's tab counter.  There's a long-time bug that
48950276Speter     * if you exit a "curses" program such as vi or more, tab
49050276Speter     * forward, and then backspace, the cursor doesn't go to the
49150276Speter     * right place.  The problem is that the kernel counts the
49250276Speter     * escape sequences that reset things as column positions.
49350276Speter     * Utter a \r to reset this invisibly.
49450276Speter     */
495262629Sdelphij    NCURSES_SP_NAME(_nc_outch) (NCURSES_SP_ARGx '\r');
49650276Speter}
49750276Speter
498262629Sdelphij#if NCURSES_SP_FUNCS
499262629SdelphijNCURSES_EXPORT(void)
500262629Sdelphij_nc_mvcur_wrap(void)
501262629Sdelphij{
502262629Sdelphij    NCURSES_SP_NAME(_nc_mvcur_wrap) (CURRENT_SCREEN);
503262629Sdelphij}
504262629Sdelphij#endif
505262629Sdelphij
50650276Speter/****************************************************************************
50750276Speter *
50850276Speter * Optimized cursor movement
50950276Speter *
51050276Speter ****************************************************************************/
51150276Speter
51250276Speter/*
51350276Speter * Perform repeated-append, returning cost
51450276Speter */
515166124Srafanstatic NCURSES_INLINE int
51666963Speterrepeated_append(string_desc * target, int total, int num, int repeat, const char *src)
51750276Speter{
518262629Sdelphij    size_t need = (size_t) repeat * strlen(src);
51950276Speter
52066963Speter    if (need < target->s_size) {
52166963Speter	while (repeat-- > 0) {
52266963Speter	    if (_nc_safe_strcat(target, src)) {
52366963Speter		total += num;
52466963Speter	    } else {
52566963Speter		total = INFINITY;
52666963Speter		break;
52762449Speter	    }
52850276Speter	}
52962449Speter    } else {
53062449Speter	total = INFINITY;
53162449Speter    }
53262449Speter    return total;
53350276Speter}
53450276Speter
53550276Speter#ifndef NO_OPTIMIZE
53650276Speter#define NEXTTAB(fr)	(fr + init_tabs - (fr % init_tabs))
53750276Speter
53850276Speter/*
53950276Speter * Assume back_tab (CBT) does not wrap backwards at the left margin, return
54050276Speter * a negative value at that point to simplify the loop.
54150276Speter */
54250276Speter#define LASTTAB(fr)	((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1)
54350276Speter
54450276Speterstatic int
545262629Sdelphijrelative_move(NCURSES_SP_DCLx
546262629Sdelphij	      string_desc * target,
547262629Sdelphij	      int from_y,
548262629Sdelphij	      int from_x,
549262629Sdelphij	      int to_y,
550262629Sdelphij	      int to_x,
551262685Sdelphij	      int ovw)
55250276Speter/* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */
55350276Speter{
55466963Speter    string_desc save;
55562449Speter    int n, vcost = 0, hcost = 0;
55650276Speter
55766963Speter    (void) _nc_str_copy(&save, target);
55850276Speter
55962449Speter    if (to_y != from_y) {
56050276Speter	vcost = INFINITY;
56150276Speter
56266963Speter	if (row_address != 0
563166124Srafan	    && _nc_safe_strcat(target, TPARM_1(row_address, to_y))) {
564262629Sdelphij	    vcost = SP_PARM->_vpa_cost;
56550276Speter	}
56650276Speter
56762449Speter	if (to_y > from_y) {
56850276Speter	    n = (to_y - from_y);
56950276Speter
57066963Speter	    if (parm_down_cursor
571262629Sdelphij		&& SP_PARM->_cud_cost < vcost
57266963Speter		&& _nc_safe_strcat(_nc_str_copy(target, &save),
573166124Srafan				   TPARM_1(parm_down_cursor, n))) {
574262629Sdelphij		vcost = SP_PARM->_cud_cost;
57550276Speter	    }
57650276Speter
57797049Speter	    if (cursor_down
578262629Sdelphij		&& (*cursor_down != '\n' || SP_PARM->_nl)
579262629Sdelphij		&& (n * SP_PARM->_cud1_cost < vcost)) {
58066963Speter		vcost = repeated_append(_nc_str_copy(target, &save), 0,
581262629Sdelphij					SP_PARM->_cud1_cost, n, cursor_down);
58250276Speter	    }
58362449Speter	} else {		/* (to_y < from_y) */
58450276Speter	    n = (from_y - to_y);
58550276Speter
58666963Speter	    if (parm_up_cursor
587262629Sdelphij		&& SP_PARM->_cuu_cost < vcost
58866963Speter		&& _nc_safe_strcat(_nc_str_copy(target, &save),
589166124Srafan				   TPARM_1(parm_up_cursor, n))) {
590262629Sdelphij		vcost = SP_PARM->_cuu_cost;
59150276Speter	    }
59250276Speter
593262629Sdelphij	    if (cursor_up && (n * SP_PARM->_cuu1_cost < vcost)) {
59466963Speter		vcost = repeated_append(_nc_str_copy(target, &save), 0,
595262629Sdelphij					SP_PARM->_cuu1_cost, n, cursor_up);
59650276Speter	    }
59750276Speter	}
59850276Speter
59950276Speter	if (vcost == INFINITY)
60062449Speter	    return (INFINITY);
60150276Speter    }
60250276Speter
60366963Speter    save = *target;
60450276Speter
60562449Speter    if (to_x != from_x) {
60662449Speter	char str[OPT_SIZE];
60766963Speter	string_desc check;
60850276Speter
60950276Speter	hcost = INFINITY;
61050276Speter
61166963Speter	if (column_address
61266963Speter	    && _nc_safe_strcat(_nc_str_copy(target, &save),
613166124Srafan			       TPARM_1(column_address, to_x))) {
614262629Sdelphij	    hcost = SP_PARM->_hpa_cost;
61550276Speter	}
61650276Speter
61762449Speter	if (to_x > from_x) {
61850276Speter	    n = to_x - from_x;
61950276Speter
62066963Speter	    if (parm_right_cursor
621262629Sdelphij		&& SP_PARM->_cuf_cost < hcost
62266963Speter		&& _nc_safe_strcat(_nc_str_copy(target, &save),
623166124Srafan				   TPARM_1(parm_right_cursor, n))) {
624262629Sdelphij		hcost = SP_PARM->_cuf_cost;
62550276Speter	    }
62650276Speter
62762449Speter	    if (cursor_right) {
62862449Speter		int lhcost = 0;
62950276Speter
63066963Speter		(void) _nc_str_init(&check, str, sizeof(str));
63150276Speter
63250276Speter#if USE_HARD_TABS
63350276Speter		/* use hard tabs, if we have them, to do as much as possible */
63462449Speter		if (init_tabs > 0 && tab) {
63562449Speter		    int nxt, fr;
63650276Speter
63762449Speter		    for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt) {
63866963Speter			lhcost = repeated_append(&check, lhcost,
639262629Sdelphij						 SP_PARM->_ht_cost, 1, tab);
64050276Speter			if (lhcost == INFINITY)
64162449Speter			    break;
64250276Speter		    }
64350276Speter
64450276Speter		    n = to_x - fr;
64550276Speter		    from_x = fr;
64650276Speter		}
64750276Speter#endif /* USE_HARD_TABS */
64850276Speter
64976726Speter		if (n <= 0 || n >= (int) check.s_size)
65076726Speter		    ovw = FALSE;
65166963Speter#if BSD_TPUTS
65250276Speter		/*
65350276Speter		 * If we're allowing BSD-style padding in tputs, don't generate
65450276Speter		 * a string with a leading digit.  Otherwise, that will be
65550276Speter		 * interpreted as a padding value rather than sent to the
65650276Speter		 * screen.
65750276Speter		 */
65850276Speter		if (ovw
65962449Speter		    && n > 0
66066963Speter		    && n < (int) check.s_size
66162449Speter		    && vcost == 0
662166124Srafan		    && str[0] == '\0') {
663262629Sdelphij		    int wanted = CharOf(WANT_CHAR(SP_PARM, to_y, from_x));
664166124Srafan		    if (is8bits(wanted) && isdigit(wanted))
665166124Srafan			ovw = FALSE;
666166124Srafan		}
66750276Speter#endif
66850276Speter		/*
66950276Speter		 * If we have no attribute changes, overwrite is cheaper.
67050276Speter		 * Note: must suppress this by passing in ovw = FALSE whenever
67150276Speter		 * WANT_CHAR would return invalid data.  In particular, this
67250276Speter		 * is true between the time a hardware scroll has been done
67350276Speter		 * and the time the structure WANT_CHAR would access has been
67450276Speter		 * updated.
67550276Speter		 */
67662449Speter		if (ovw) {
67762449Speter		    int i;
67850276Speter
67976726Speter		    for (i = 0; i < n; i++) {
680262629Sdelphij			NCURSES_CH_T ch = WANT_CHAR(SP_PARM, to_y, from_x + i);
681262629Sdelphij			if (!SameAttrOf(ch, SCREEN_ATTRS(SP_PARM))
68276726Speter#if USE_WIDEC_SUPPORT
68397049Speter			    || !Charable(ch)
68476726Speter#endif
68576726Speter			    ) {
68650276Speter			    ovw = FALSE;
68750276Speter			    break;
68850276Speter			}
68976726Speter		    }
69050276Speter		}
69162449Speter		if (ovw) {
69262449Speter		    int i;
69350276Speter
69450276Speter		    for (i = 0; i < n; i++)
695262629Sdelphij			*check.s_tail++ = (char) CharOf(WANT_CHAR(SP_PARM, to_y,
696184989Srafan								  from_x + i));
69766963Speter		    *check.s_tail = '\0';
698262629Sdelphij		    check.s_size -= (size_t) n;
699262629Sdelphij		    lhcost += n * SP_PARM->_char_padding;
700166124Srafan		} else {
701262629Sdelphij		    lhcost = repeated_append(&check, lhcost, SP_PARM->_cuf1_cost,
70266963Speter					     n, cursor_right);
70350276Speter		}
70450276Speter
70566963Speter		if (lhcost < hcost
70666963Speter		    && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
70750276Speter		    hcost = lhcost;
70850276Speter		}
70950276Speter	    }
71062449Speter	} else {		/* (to_x < from_x) */
71150276Speter	    n = from_x - to_x;
71250276Speter
71366963Speter	    if (parm_left_cursor
714262629Sdelphij		&& SP_PARM->_cub_cost < hcost
71566963Speter		&& _nc_safe_strcat(_nc_str_copy(target, &save),
716166124Srafan				   TPARM_1(parm_left_cursor, n))) {
717262629Sdelphij		hcost = SP_PARM->_cub_cost;
71850276Speter	    }
71950276Speter
72062449Speter	    if (cursor_left) {
72162449Speter		int lhcost = 0;
72250276Speter
72366963Speter		(void) _nc_str_init(&check, str, sizeof(str));
72450276Speter
72550276Speter#if USE_HARD_TABS
72662449Speter		if (init_tabs > 0 && back_tab) {
72762449Speter		    int nxt, fr;
72850276Speter
72962449Speter		    for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt) {
73066963Speter			lhcost = repeated_append(&check, lhcost,
731262629Sdelphij						 SP_PARM->_cbt_cost,
732262629Sdelphij						 1, back_tab);
73350276Speter			if (lhcost == INFINITY)
73462449Speter			    break;
73550276Speter		    }
73650276Speter
73750276Speter		    n = fr - to_x;
73850276Speter		}
73950276Speter#endif /* USE_HARD_TABS */
74050276Speter
741262629Sdelphij		lhcost = repeated_append(&check, lhcost,
742262629Sdelphij					 SP_PARM->_cub1_cost,
743262629Sdelphij					 n, cursor_left);
74450276Speter
74566963Speter		if (lhcost < hcost
74666963Speter		    && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
74750276Speter		    hcost = lhcost;
74850276Speter		}
74950276Speter	    }
75050276Speter	}
75150276Speter
75250276Speter	if (hcost == INFINITY)
75362449Speter	    return (INFINITY);
75450276Speter    }
75550276Speter
75662449Speter    return (vcost + hcost);
75750276Speter}
75850276Speter#endif /* !NO_OPTIMIZE */
75950276Speter
76050276Speter/*
76150276Speter * With the machinery set up above, it's conceivable that
76250276Speter * onscreen_mvcur could be modified into a recursive function that does
76350276Speter * an alpha-beta search of motion space, as though it were a chess
76450276Speter * move tree, with the weight function being boolean and the search
76550276Speter * depth equated to length of string.  However, this would jack up the
76650276Speter * computation cost a lot, especially on terminals without a cup
76750276Speter * capability constraining the search tree depth.  So we settle for
76850276Speter * the simpler method below.
76950276Speter */
77050276Speter
771166124Srafanstatic NCURSES_INLINE int
772262685Sdelphijonscreen_mvcur(NCURSES_SP_DCLx
773262685Sdelphij	       int yold, int xold,
774262685Sdelphij	       int ynew, int xnew, int ovw,
775262685Sdelphij	       NCURSES_SP_OUTC myOutCh)
77650276Speter/* onscreen move from (yold, xold) to (ynew, xnew) */
77750276Speter{
77866963Speter    string_desc result;
77966963Speter    char buffer[OPT_SIZE];
78062449Speter    int tactic = 0, newcost, usecost = INFINITY;
78162449Speter    int t5_cr_cost;
78250276Speter
78350276Speter#if defined(MAIN) || defined(NCURSES_TEST)
78450276Speter    struct timeval before, after;
78550276Speter
78650276Speter    gettimeofday(&before, NULL);
78750276Speter#endif /* MAIN */
78850276Speter
78966963Speter#define NullResult _nc_str_null(&result, sizeof(buffer))
79066963Speter#define InitResult _nc_str_init(&result, buffer, sizeof(buffer))
79166963Speter
79250276Speter    /* tactic #0: use direct cursor addressing */
793262629Sdelphij    if (_nc_safe_strcpy(InitResult, TPARM_2(SP_PARM->_address_cursor, ynew, xnew))) {
79450276Speter	tactic = 0;
795262629Sdelphij	usecost = SP_PARM->_cup_cost;
79650276Speter
79750276Speter#if defined(TRACE) || defined(NCURSES_TEST)
79850276Speter	if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
79950276Speter	    goto nonlocal;
80050276Speter#endif /* TRACE */
80150276Speter
80250276Speter	/*
80350276Speter	 * We may be able to tell in advance that the full optimization
80450276Speter	 * will probably not be worth its overhead.  Also, don't try to
80550276Speter	 * use local movement if the current attribute is anything but
80650276Speter	 * A_NORMAL...there are just too many ways this can screw up
80750276Speter	 * (like, say, local-movement \n getting mapped to some obscure
80850276Speter	 * character because A_ALTCHARSET is on).
80950276Speter	 */
810262629Sdelphij	if (yold == -1 || xold == -1 || NOT_LOCAL(SP_PARM, yold, xold, ynew, xnew)) {
81150276Speter#if defined(MAIN) || defined(NCURSES_TEST)
81262449Speter	    if (!profiling) {
81350276Speter		(void) fputs("nonlocal\n", stderr);
81450276Speter		goto nonlocal;	/* always run the optimizer if profiling */
81550276Speter	    }
81650276Speter#else
81750276Speter	    goto nonlocal;
81850276Speter#endif /* MAIN */
81950276Speter	}
82050276Speter    }
82150276Speter#ifndef NO_OPTIMIZE
82250276Speter    /* tactic #1: use local movement */
82350276Speter    if (yold != -1 && xold != -1
824262629Sdelphij	&& ((newcost = relative_move(NCURSES_SP_ARGx
825262629Sdelphij				     NullResult,
826262629Sdelphij				     yold, xold,
827262629Sdelphij				     ynew, xnew, ovw)) != INFINITY)
82862449Speter	&& newcost < usecost) {
82950276Speter	tactic = 1;
83050276Speter	usecost = newcost;
83150276Speter    }
83250276Speter
83350276Speter    /* tactic #2: use carriage-return + local movement */
83450276Speter    if (yold != -1 && carriage_return
835262629Sdelphij	&& ((newcost = relative_move(NCURSES_SP_ARGx
836262629Sdelphij				     NullResult,
837262629Sdelphij				     yold, 0,
838262629Sdelphij				     ynew, xnew, ovw)) != INFINITY)
839262629Sdelphij	&& SP_PARM->_cr_cost + newcost < usecost) {
84050276Speter	tactic = 2;
841262629Sdelphij	usecost = SP_PARM->_cr_cost + newcost;
84250276Speter    }
84350276Speter
84450276Speter    /* tactic #3: use home-cursor + local movement */
84550276Speter    if (cursor_home
846262629Sdelphij	&& ((newcost = relative_move(NCURSES_SP_ARGx
847262629Sdelphij				     NullResult,
848262629Sdelphij				     0, 0,
849262629Sdelphij				     ynew, xnew, ovw)) != INFINITY)
850262629Sdelphij	&& SP_PARM->_home_cost + newcost < usecost) {
85150276Speter	tactic = 3;
852262629Sdelphij	usecost = SP_PARM->_home_cost + newcost;
85350276Speter    }
85450276Speter
85550276Speter    /* tactic #4: use home-down + local movement */
85650276Speter    if (cursor_to_ll
857262629Sdelphij	&& ((newcost = relative_move(NCURSES_SP_ARGx
858262629Sdelphij				     NullResult,
859262629Sdelphij				     screen_lines(SP_PARM) - 1, 0,
860262629Sdelphij				     ynew, xnew, ovw)) != INFINITY)
861262629Sdelphij	&& SP_PARM->_ll_cost + newcost < usecost) {
86250276Speter	tactic = 4;
863262629Sdelphij	usecost = SP_PARM->_ll_cost + newcost;
86450276Speter    }
86550276Speter
86650276Speter    /*
86750276Speter     * tactic #5: use left margin for wrap to right-hand side,
86850276Speter     * unless strange wrap behavior indicated by xenl might hose us.
86950276Speter     */
870262629Sdelphij    t5_cr_cost = (xold > 0 ? SP_PARM->_cr_cost : 0);
87150276Speter    if (auto_left_margin && !eat_newline_glitch
87250276Speter	&& yold > 0 && cursor_left
873262629Sdelphij	&& ((newcost = relative_move(NCURSES_SP_ARGx
874262629Sdelphij				     NullResult,
875262629Sdelphij				     yold - 1, screen_columns(SP_PARM) - 1,
876262629Sdelphij				     ynew, xnew, ovw)) != INFINITY)
877262629Sdelphij	&& t5_cr_cost + SP_PARM->_cub1_cost + newcost < usecost) {
87850276Speter	tactic = 5;
879262629Sdelphij	usecost = t5_cr_cost + SP_PARM->_cub1_cost + newcost;
88050276Speter    }
88150276Speter
88250276Speter    /*
88350276Speter     * These cases are ordered by estimated relative frequency.
88450276Speter     */
88566963Speter    if (tactic)
88666963Speter	InitResult;
88762449Speter    switch (tactic) {
88862449Speter    case 1:
889262629Sdelphij	(void) relative_move(NCURSES_SP_ARGx
890262629Sdelphij			     &result,
891262629Sdelphij			     yold, xold,
892262629Sdelphij			     ynew, xnew, ovw);
89362449Speter	break;
89462449Speter    case 2:
89566963Speter	(void) _nc_safe_strcpy(&result, carriage_return);
896262629Sdelphij	(void) relative_move(NCURSES_SP_ARGx
897262629Sdelphij			     &result,
898262629Sdelphij			     yold, 0,
899262629Sdelphij			     ynew, xnew, ovw);
90062449Speter	break;
90162449Speter    case 3:
90266963Speter	(void) _nc_safe_strcpy(&result, cursor_home);
903262629Sdelphij	(void) relative_move(NCURSES_SP_ARGx
904262629Sdelphij			     &result, 0, 0,
905262629Sdelphij			     ynew, xnew, ovw);
90662449Speter	break;
90762449Speter    case 4:
90866963Speter	(void) _nc_safe_strcpy(&result, cursor_to_ll);
909262629Sdelphij	(void) relative_move(NCURSES_SP_ARGx
910262629Sdelphij			     &result,
911262629Sdelphij			     screen_lines(SP_PARM) - 1, 0,
912262629Sdelphij			     ynew, xnew, ovw);
91362449Speter	break;
91462449Speter    case 5:
91562449Speter	if (xold > 0)
91666963Speter	    (void) _nc_safe_strcat(&result, carriage_return);
91766963Speter	(void) _nc_safe_strcat(&result, cursor_left);
918262629Sdelphij	(void) relative_move(NCURSES_SP_ARGx
919262629Sdelphij			     &result,
920262629Sdelphij			     yold - 1, screen_columns(SP_PARM) - 1,
921262629Sdelphij			     ynew, xnew, ovw);
92262449Speter	break;
92350276Speter    }
92450276Speter#endif /* !NO_OPTIMIZE */
92550276Speter
926166124Srafan  nonlocal:
92750276Speter#if defined(MAIN) || defined(NCURSES_TEST)
92850276Speter    gettimeofday(&after, NULL);
92950276Speter    diff = after.tv_usec - before.tv_usec
93050276Speter	+ (after.tv_sec - before.tv_sec) * 1000000;
93150276Speter    if (!profiling)
93262449Speter	(void) fprintf(stderr,
933166124Srafan		       "onscreen: %d microsec, %f 28.8Kbps char-equivalents\n",
93466963Speter		       (int) diff, diff / 288);
93550276Speter#endif /* MAIN */
93650276Speter
93762449Speter    if (usecost != INFINITY) {
93850276Speter	TPUTS_TRACE("mvcur");
939262629Sdelphij	NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
940262685Sdelphij				buffer, 1, myOutCh);
941262629Sdelphij	SP_PARM->_cursrow = ynew;
942262629Sdelphij	SP_PARM->_curscol = xnew;
94362449Speter	return (OK);
94462449Speter    } else
94562449Speter	return (ERR);
94650276Speter}
94750276Speter
948262685Sdelphij/*
949262685Sdelphij * optimized cursor move from (yold, xold) to (ynew, xnew)
950262685Sdelphij */
951262685Sdelphijstatic int
952262685Sdelphij_nc_real_mvcur(NCURSES_SP_DCLx
953262685Sdelphij	       int yold, int xold,
954262685Sdelphij	       int ynew, int xnew,
955262685Sdelphij	       NCURSES_SP_OUTC myOutCh,
956262685Sdelphij	       int ovw)
95750276Speter{
958166124Srafan    NCURSES_CH_T oldattr;
959166124Srafan    int code;
960166124Srafan
961262629Sdelphij    TR(TRACE_CALLS | TRACE_MOVE, (T_CALLED("_nc_tinfo_mvcur(%p,%d,%d,%d,%d)"),
962262629Sdelphij				  (void *) SP_PARM, yold, xold, ynew, xnew));
96350276Speter
964262629Sdelphij    if (SP_PARM == 0) {
965166124Srafan	code = ERR;
966166124Srafan    } else if (yold == ynew && xold == xnew) {
967166124Srafan	code = OK;
968166124Srafan    } else {
96997049Speter
970166124Srafan	/*
971166124Srafan	 * Most work here is rounding for terminal boundaries getting the
972166124Srafan	 * column position implied by wraparound or the lack thereof and
973166124Srafan	 * rolling up the screen to get ynew on the screen.
974166124Srafan	 */
975262629Sdelphij	if (xnew >= screen_columns(SP_PARM)) {
976262629Sdelphij	    ynew += xnew / screen_columns(SP_PARM);
977262629Sdelphij	    xnew %= screen_columns(SP_PARM);
978166124Srafan	}
97950276Speter
980166124Srafan	/*
981166124Srafan	 * Force restore even if msgr is on when we're in an alternate
982166124Srafan	 * character set -- these have a strong tendency to screw up the CR &
983166124Srafan	 * LF used for local character motions!
984166124Srafan	 */
985262629Sdelphij	oldattr = SCREEN_ATTRS(SP_PARM);
986166124Srafan	if ((AttrOf(oldattr) & A_ALTCHARSET)
987166124Srafan	    || (AttrOf(oldattr) && !move_standout_mode)) {
988166124Srafan	    TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move",
989166124Srafan			       (unsigned long) AttrOf(oldattr),
990166124Srafan			       _traceattr(AttrOf(oldattr))));
991262629Sdelphij	    (void) VIDATTR(SP_PARM, A_NORMAL, 0);
992166124Srafan	}
99350276Speter
994262629Sdelphij	if (xold >= screen_columns(SP_PARM)) {
995166124Srafan	    int l;
99650276Speter
997262629Sdelphij	    if (SP_PARM->_nl) {
998262629Sdelphij		l = (xold + 1) / screen_columns(SP_PARM);
999166124Srafan		yold += l;
1000262629Sdelphij		if (yold >= screen_lines(SP_PARM))
1001262629Sdelphij		    l -= (yold - screen_lines(SP_PARM) - 1);
100250276Speter
1003166124Srafan		if (l > 0) {
100497049Speter		    if (carriage_return) {
1005262685Sdelphij			NCURSES_PUTP2("carriage_return", carriage_return);
1006262685Sdelphij		    } else {
1007262685Sdelphij			myOutCh(NCURSES_SP_ARGx '\r');
1008262685Sdelphij		    }
100997049Speter		    xold = 0;
1010166124Srafan
1011166124Srafan		    while (l > 0) {
1012166124Srafan			if (newline) {
1013262685Sdelphij			    NCURSES_PUTP2("newline", newline);
1014262685Sdelphij			} else {
1015262685Sdelphij			    myOutCh(NCURSES_SP_ARGx '\n');
1016262685Sdelphij			}
1017166124Srafan			l--;
1018166124Srafan		    }
101997049Speter		}
1020166124Srafan	    } else {
1021166124Srafan		/*
1022166124Srafan		 * If caller set nonl(), we cannot really use newlines to
1023166124Srafan		 * position to the next row.
1024166124Srafan		 */
1025166124Srafan		xold = -1;
1026166124Srafan		yold = -1;
102762449Speter	    }
102850276Speter	}
102950276Speter
1030262629Sdelphij	if (yold > screen_lines(SP_PARM) - 1)
1031262629Sdelphij	    yold = screen_lines(SP_PARM) - 1;
1032262629Sdelphij	if (ynew > screen_lines(SP_PARM) - 1)
1033262629Sdelphij	    ynew = screen_lines(SP_PARM) - 1;
103450276Speter
1035166124Srafan	/* destination location is on screen now */
1036262685Sdelphij	code = onscreen_mvcur(NCURSES_SP_ARGx yold, xold, ynew, xnew, ovw, myOutCh);
1037166124Srafan
1038166124Srafan	/*
1039166124Srafan	 * Restore attributes if we disabled them before moving.
1040166124Srafan	 */
1041262629Sdelphij	if (!SameAttrOf(oldattr, SCREEN_ATTRS(SP_PARM))) {
1042166124Srafan	    TR(TRACE_CHARPUT, ("turning on (%#lx) %s after move",
1043166124Srafan			       (unsigned long) AttrOf(oldattr),
1044166124Srafan			       _traceattr(AttrOf(oldattr))));
1045262629Sdelphij	    (void) VIDATTR(SP_PARM, AttrOf(oldattr), GetPair(oldattr));
1046166124Srafan	}
1047166124Srafan    }
1048166124Srafan    returnCode(code);
104950276Speter}
105050276Speter
1051262685Sdelphij/*
1052262685Sdelphij * These entrypoints are used within the library.
1053262685Sdelphij */
1054262629SdelphijNCURSES_EXPORT(int)
1055262685SdelphijNCURSES_SP_NAME(_nc_mvcur) (NCURSES_SP_DCLx
1056262685Sdelphij			    int yold, int xold,
1057262685Sdelphij			    int ynew, int xnew)
1058262685Sdelphij{
1059262685Sdelphij    return _nc_real_mvcur(NCURSES_SP_ARGx yold, xold, ynew, xnew,
1060262685Sdelphij			  NCURSES_SP_NAME(_nc_outch),
1061262685Sdelphij			  TRUE);
1062262685Sdelphij}
1063262685Sdelphij
1064262685Sdelphij#if NCURSES_SP_FUNCS
1065262685SdelphijNCURSES_EXPORT(int)
1066262685Sdelphij_nc_mvcur(int yold, int xold,
1067262685Sdelphij	  int ynew, int xnew)
1068262685Sdelphij{
1069262685Sdelphij    return NCURSES_SP_NAME(_nc_mvcur) (CURRENT_SCREEN, yold, xold, ynew, xnew);
1070262685Sdelphij}
1071262685Sdelphij#endif
1072262685Sdelphij
1073262685Sdelphij#if defined(USE_TERM_DRIVER)
1074262685Sdelphij/*
1075262685Sdelphij * The terminal driver does not support the external "mvcur()".
1076262685Sdelphij */
1077262685SdelphijNCURSES_EXPORT(int)
1078262685SdelphijTINFO_MVCUR(NCURSES_SP_DCLx int yold, int xold, int ynew, int xnew)
1079262685Sdelphij{
1080262685Sdelphij    return _nc_real_mvcur(NCURSES_SP_ARGx
1081262685Sdelphij			  yold, xold,
1082262685Sdelphij			  ynew, xnew,
1083262685Sdelphij			  NCURSES_SP_NAME(_nc_outch),
1084262685Sdelphij			  TRUE);
1085262685Sdelphij}
1086262685Sdelphij
1087262685Sdelphij#else /* !USE_TERM_DRIVER */
1088262685Sdelphij
1089262685Sdelphij/*
1090262685Sdelphij * These entrypoints support users of the library.
1091262685Sdelphij */
1092262685SdelphijNCURSES_EXPORT(int)
1093262685SdelphijNCURSES_SP_NAME(mvcur) (NCURSES_SP_DCLx int yold, int xold, int ynew,
1094262685Sdelphij			int xnew)
1095262685Sdelphij{
1096262685Sdelphij    return _nc_real_mvcur(NCURSES_SP_ARGx
1097262685Sdelphij			  yold, xold,
1098262685Sdelphij			  ynew, xnew,
1099262685Sdelphij			  NCURSES_SP_NAME(_nc_putchar),
1100262685Sdelphij			  FALSE);
1101262685Sdelphij}
1102262685Sdelphij
1103262685Sdelphij#if NCURSES_SP_FUNCS
1104262685SdelphijNCURSES_EXPORT(int)
1105262629Sdelphijmvcur(int yold, int xold, int ynew, int xnew)
1106262629Sdelphij{
1107262629Sdelphij    return NCURSES_SP_NAME(mvcur) (CURRENT_SCREEN, yold, xold, ynew, xnew);
1108262629Sdelphij}
1109262629Sdelphij#endif
1110262685Sdelphij#endif /* USE_TERM_DRIVER */
1111262629Sdelphij
111250276Speter#if defined(TRACE) || defined(NCURSES_TEST)
1113166124SrafanNCURSES_EXPORT_VAR(int) _nc_optimize_enable = OPTIMIZE_ALL;
111450276Speter#endif
111550276Speter
111650276Speter#if defined(MAIN) || defined(NCURSES_TEST)
111750276Speter/****************************************************************************
111850276Speter *
111950276Speter * Movement optimizer test code
112050276Speter *
112150276Speter ****************************************************************************/
112250276Speter
112350276Speter#include <tic.h>
112450276Speter#include <dump_entry.h>
1125174993Srafan#include <time.h>
112650276Speter
1127166124SrafanNCURSES_EXPORT_VAR(const char *) _nc_progname = "mvcur";
112850276Speter
1129166124Srafanstatic unsigned long xmits;
113050276Speter
113162449Speter/* these override lib_tputs.c */
113276726SpeterNCURSES_EXPORT(int)
113397049Spetertputs(const char *string, int affcnt GCC_UNUSED, int (*outc) (int) GCC_UNUSED)
113450276Speter/* stub tputs() that dumps sequences in a visible form */
113550276Speter{
113650276Speter    if (profiling)
113750276Speter	xmits += strlen(string);
113850276Speter    else
113950276Speter	(void) fputs(_nc_visbuf(string), stdout);
114062449Speter    return (OK);
114150276Speter}
114250276Speter
114376726SpeterNCURSES_EXPORT(int)
114462449Speterputp(const char *string)
114550276Speter{
114662449Speter    return (tputs(string, 1, _nc_outch));
114750276Speter}
114850276Speter
114976726SpeterNCURSES_EXPORT(int)
115062449Speter_nc_outch(int ch)
115150276Speter{
115250276Speter    putc(ch, stdout);
115350276Speter    return OK;
115450276Speter}
115550276Speter
115676726SpeterNCURSES_EXPORT(int)
115762449Speterdelay_output(int ms GCC_UNUSED)
115850276Speter{
115962449Speter    return OK;
116062449Speter}
116162449Speter
1162166124Srafanstatic char tname[PATH_MAX];
116362449Speter
116462449Speterstatic void
116562449Speterload_term(void)
116662449Speter{
116750276Speter    (void) setupterm(tname, STDOUT_FILENO, NULL);
116850276Speter}
116950276Speter
117062449Speterstatic int
117162449Speterroll(int n)
117250276Speter{
117350276Speter    int i, j;
117450276Speter
117550276Speter    i = (RAND_MAX / n) * n;
117650276Speter    while ((j = rand()) >= i)
117750276Speter	continue;
117850276Speter    return (j % n);
117950276Speter}
118050276Speter
118162449Speterint
118262449Spetermain(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
118350276Speter{
1184174993Srafan    strcpy(tname, getenv("TERM"));
118550276Speter    load_term();
1186166124Srafan    _nc_setupscreen(lines, columns, stdout, FALSE, 0);
118750276Speter    baudrate();
118850276Speter
118950276Speter    _nc_mvcur_init();
119050276Speter    NC_BUFFERED(FALSE);
119150276Speter
119250276Speter    (void) puts("The mvcur tester.  Type ? for help");
119350276Speter
119450276Speter    fputs("smcup:", stdout);
119550276Speter    putchar('\n');
119650276Speter
119762449Speter    for (;;) {
119862449Speter	int fy, fx, ty, tx, n, i;
119962449Speter	char buf[BUFSIZ], capname[BUFSIZ];
120050276Speter
120150276Speter	(void) fputs("> ", stdout);
120250276Speter	(void) fgets(buf, sizeof(buf), stdin);
120350276Speter
120462449Speter	if (buf[0] == '?') {
120562449Speter	    (void) puts("?                -- display this help message");
120662449Speter	    (void)
120762449Speter		puts("fy fx ty tx      -- (4 numbers) display (fy,fx)->(ty,tx) move");
120862449Speter	    (void) puts("s[croll] n t b m -- display scrolling sequence");
120962449Speter	    (void)
121062449Speter		printf("r[eload]         -- reload terminal info for %s\n",
121166963Speter		       termname());
121262449Speter	    (void)
121362449Speter		puts("l[oad] <term>    -- load terminal info for type <term>");
121462449Speter	    (void) puts("d[elete] <cap>   -- delete named capability");
121562449Speter	    (void) puts("i[nspect]        -- display terminal capabilities");
121662449Speter	    (void)
121762449Speter		puts("c[ost]           -- dump cursor-optimization cost table");
121862449Speter	    (void) puts("o[optimize]      -- toggle movement optimization");
121962449Speter	    (void)
122062449Speter		puts("t[orture] <num>  -- torture-test with <num> random moves");
122162449Speter	    (void) puts("q[uit]           -- quit the program");
122262449Speter	} else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
122350276Speter	    struct timeval before, after;
122450276Speter
122550276Speter	    putchar('"');
122650276Speter
122750276Speter	    gettimeofday(&before, NULL);
122850276Speter	    mvcur(fy, fx, ty, tx);
122950276Speter	    gettimeofday(&after, NULL);
123050276Speter
123150276Speter	    printf("\" (%ld msec)\n",
123266963Speter		   (long) (after.tv_usec - before.tv_usec
123366963Speter			   + (after.tv_sec - before.tv_sec)
123466963Speter			   * 1000000));
123562449Speter	} else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
123650276Speter	    struct timeval before, after;
123750276Speter
123850276Speter	    putchar('"');
123950276Speter
124050276Speter	    gettimeofday(&before, NULL);
124150276Speter	    _nc_scrolln(fy, fx, ty, tx);
124250276Speter	    gettimeofday(&after, NULL);
124350276Speter
124450276Speter	    printf("\" (%ld msec)\n",
124566963Speter		   (long) (after.tv_usec - before.tv_usec + (after.tv_sec -
124666963Speter							     before.tv_sec)
124766963Speter			   * 1000000));
124862449Speter	} else if (buf[0] == 'r') {
124950276Speter	    (void) strcpy(tname, termname());
125050276Speter	    load_term();
125162449Speter	} else if (sscanf(buf, "l %s", tname) == 1) {
125250276Speter	    load_term();
125362449Speter	} else if (sscanf(buf, "d %s", capname) == 1) {
125462449Speter	    struct name_table_entry const *np = _nc_find_entry(capname,
1255174993Srafan							       _nc_get_hash_table(FALSE));
125650276Speter
125750276Speter	    if (np == NULL)
125850276Speter		(void) printf("No such capability as \"%s\"\n", capname);
125962449Speter	    else {
126062449Speter		switch (np->nte_type) {
126150276Speter		case BOOLEAN:
126250276Speter		    cur_term->type.Booleans[np->nte_index] = FALSE;
126362449Speter		    (void)
126462449Speter			printf("Boolean capability `%s' (%d) turned off.\n",
126566963Speter			       np->nte_name, np->nte_index);
126650276Speter		    break;
126750276Speter
126850276Speter		case NUMBER:
126962449Speter		    cur_term->type.Numbers[np->nte_index] = ABSENT_NUMERIC;
127050276Speter		    (void) printf("Number capability `%s' (%d) set to -1.\n",
127166963Speter				  np->nte_name, np->nte_index);
127250276Speter		    break;
127350276Speter
127450276Speter		case STRING:
127562449Speter		    cur_term->type.Strings[np->nte_index] = ABSENT_STRING;
127650276Speter		    (void) printf("String capability `%s' (%d) deleted.\n",
127766963Speter				  np->nte_name, np->nte_index);
127850276Speter		    break;
127950276Speter		}
128050276Speter	    }
128162449Speter	} else if (buf[0] == 'i') {
128262449Speter	    dump_init((char *) NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE);
1283174993Srafan	    dump_entry(&cur_term->type, FALSE, TRUE, 0, 0);
128462449Speter	    putchar('\n');
128562449Speter	} else if (buf[0] == 'o') {
128662449Speter	    if (_nc_optimize_enable & OPTIMIZE_MVCUR) {
128762449Speter		_nc_optimize_enable &= ~OPTIMIZE_MVCUR;
128862449Speter		(void) puts("Optimization is now off.");
128962449Speter	    } else {
129062449Speter		_nc_optimize_enable |= OPTIMIZE_MVCUR;
129162449Speter		(void) puts("Optimization is now on.");
129262449Speter	    }
129350276Speter	}
129450276Speter	/*
129550276Speter	 * You can use the `t' test to profile and tune the movement
129650276Speter	 * optimizer.  Use iteration values in three digits or more.
129750276Speter	 * At above 5000 iterations the profile timing averages are stable
129850276Speter	 * to within a millisecond or three.
129950276Speter	 *
130050276Speter	 * The `overhead' field of the report will help you pick a
130150276Speter	 * COMPUTE_OVERHEAD figure appropriate for your processor and
130250276Speter	 * expected line speed.  The `total estimated time' is
130350276Speter	 * computation time plus a character-transmission time
130450276Speter	 * estimate computed from the number of transmits and the baud
130550276Speter	 * rate.
130650276Speter	 *
130750276Speter	 * Use this together with the `o' command to get a read on the
130850276Speter	 * optimizer's effectiveness.  Compare the total estimated times
130950276Speter	 * for `t' runs of the same length in both optimized and un-optimized
131050276Speter	 * modes.  As long as the optimized times are less, the optimizer
131150276Speter	 * is winning.
131250276Speter	 */
131362449Speter	else if (sscanf(buf, "t %d", &n) == 1) {
131466963Speter	    float cumtime = 0.0, perchar;
131562449Speter	    int speeds[] =
131662449Speter	    {2400, 9600, 14400, 19200, 28800, 38400, 0};
131750276Speter
131862449Speter	    srand((unsigned) (getpid() + time((time_t *) 0)));
131950276Speter	    profiling = TRUE;
132050276Speter	    xmits = 0;
132162449Speter	    for (i = 0; i < n; i++) {
132250276Speter		/*
132350276Speter		 * This does a move test between two random locations,
132450276Speter		 * Random moves probably short-change the optimizer,
132550276Speter		 * which will work better on the short moves probably
132650276Speter		 * typical of doupdate()'s usage pattern.  Still,
132750276Speter		 * until we have better data...
132850276Speter		 */
132950276Speter#ifdef FIND_COREDUMP
133050276Speter		int from_y = roll(lines);
133150276Speter		int to_y = roll(lines);
133250276Speter		int from_x = roll(columns);
133350276Speter		int to_x = roll(columns);
133450276Speter
133550276Speter		printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x);
133650276Speter		mvcur(from_y, from_x, to_y, to_x);
133750276Speter#else
133850276Speter		mvcur(roll(lines), roll(columns), roll(lines), roll(columns));
133950276Speter#endif /* FIND_COREDUMP */
134050276Speter		if (diff)
134150276Speter		    cumtime += diff;
134250276Speter	    }
134350276Speter	    profiling = FALSE;
134450276Speter
134550276Speter	    /*
134650276Speter	     * Average milliseconds per character optimization time.
134750276Speter	     * This is the key figure to watch when tuning the optimizer.
134850276Speter	     */
134950276Speter	    perchar = cumtime / n;
135050276Speter
135150276Speter	    (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n",
135266963Speter			  n, xmits, (int) cumtime, perchar);
135350276Speter
135462449Speter	    for (i = 0; speeds[i]; i++) {
135550276Speter		/*
135650276Speter		 * Total estimated time for the moves, computation and
135750276Speter		 * transmission both. Transmission time is an estimate
135850276Speter		 * assuming 9 bits/char, 8 bits + 1 stop bit.
135950276Speter		 */
136050276Speter		float totalest = cumtime + xmits * 9 * 1e6 / speeds[i];
136150276Speter
136250276Speter		/*
136350276Speter		 * Per-character optimization overhead in character transmits
136450276Speter		 * at the current speed.  Round this to the nearest integer
136550276Speter		 * to figure COMPUTE_OVERHEAD for the speed.
136650276Speter		 */
136750276Speter		float overhead = speeds[i] * perchar / 1e6;
136850276Speter
136962449Speter		(void)
137062449Speter		    printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n",
137166963Speter			   speeds[i], overhead, totalest);
137250276Speter	    }
137362449Speter	} else if (buf[0] == 'c') {
1374262629Sdelphij	    (void) printf("char padding: %d\n", CURRENT_SCREEN->_char_padding);
1375262629Sdelphij	    (void) printf("cr cost: %d\n", CURRENT_SCREEN->_cr_cost);
1376262629Sdelphij	    (void) printf("cup cost: %d\n", CURRENT_SCREEN->_cup_cost);
1377262629Sdelphij	    (void) printf("home cost: %d\n", CURRENT_SCREEN->_home_cost);
1378262629Sdelphij	    (void) printf("ll cost: %d\n", CURRENT_SCREEN->_ll_cost);
137950276Speter#if USE_HARD_TABS
1380262629Sdelphij	    (void) printf("ht cost: %d\n", CURRENT_SCREEN->_ht_cost);
1381262629Sdelphij	    (void) printf("cbt cost: %d\n", CURRENT_SCREEN->_cbt_cost);
138250276Speter#endif /* USE_HARD_TABS */
1383262629Sdelphij	    (void) printf("cub1 cost: %d\n", CURRENT_SCREEN->_cub1_cost);
1384262629Sdelphij	    (void) printf("cuf1 cost: %d\n", CURRENT_SCREEN->_cuf1_cost);
1385262629Sdelphij	    (void) printf("cud1 cost: %d\n", CURRENT_SCREEN->_cud1_cost);
1386262629Sdelphij	    (void) printf("cuu1 cost: %d\n", CURRENT_SCREEN->_cuu1_cost);
1387262629Sdelphij	    (void) printf("cub cost: %d\n", CURRENT_SCREEN->_cub_cost);
1388262629Sdelphij	    (void) printf("cuf cost: %d\n", CURRENT_SCREEN->_cuf_cost);
1389262629Sdelphij	    (void) printf("cud cost: %d\n", CURRENT_SCREEN->_cud_cost);
1390262629Sdelphij	    (void) printf("cuu cost: %d\n", CURRENT_SCREEN->_cuu_cost);
1391262629Sdelphij	    (void) printf("hpa cost: %d\n", CURRENT_SCREEN->_hpa_cost);
1392262629Sdelphij	    (void) printf("vpa cost: %d\n", CURRENT_SCREEN->_vpa_cost);
139362449Speter	} else if (buf[0] == 'x' || buf[0] == 'q')
139450276Speter	    break;
139550276Speter	else
139650276Speter	    (void) puts("Invalid command.");
139750276Speter    }
139850276Speter
139950276Speter    (void) fputs("rmcup:", stdout);
140050276Speter    _nc_mvcur_wrap();
140150276Speter    putchar('\n');
140250276Speter
140362449Speter    return (0);
140450276Speter}
140550276Speter
140650276Speter#endif /* MAIN */
140750276Speter
140850276Speter/* lib_mvcur.c ends here */
1409