GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libcurses/tty/lib_mvcur.c Lines: 0 263 0.0 %
Date: 2017-11-13 Branches: 0 270 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: lib_mvcur.c,v 1.13 2010/01/12 23:22:07 nicm Exp $ */
2
3
/****************************************************************************
4
 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5
 *                                                                          *
6
 * Permission is hereby granted, free of charge, to any person obtaining a  *
7
 * copy of this software and associated documentation files (the            *
8
 * "Software"), to deal in the Software without restriction, including      *
9
 * without limitation the rights to use, copy, modify, merge, publish,      *
10
 * distribute, distribute with modifications, sublicense, and/or sell       *
11
 * copies of the Software, and to permit persons to whom the Software is    *
12
 * furnished to do so, subject to the following conditions:                 *
13
 *                                                                          *
14
 * The above copyright notice and this permission notice shall be included  *
15
 * in all copies or substantial portions of the Software.                   *
16
 *                                                                          *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24
 *                                                                          *
25
 * Except as contained in this notice, the name(s) of the above copyright   *
26
 * holders shall not be used in advertising or otherwise to promote the     *
27
 * sale, use or other dealings in this Software without prior written       *
28
 * authorization.                                                           *
29
 ****************************************************************************/
30
31
/****************************************************************************
32
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34
 *     and: Thomas E. Dickey                        1996-on                 *
35
 ****************************************************************************/
36
37
/*
38
**	lib_mvcur.c
39
**
40
**	The routines for moving the physical cursor and scrolling:
41
**
42
**		void _nc_mvcur_init(void)
43
**
44
**		void _nc_mvcur_resume(void)
45
**
46
**		int mvcur(int old_y, int old_x, int new_y, int new_x)
47
**
48
**		void _nc_mvcur_wrap(void)
49
**
50
** Comparisons with older movement optimizers:
51
**    SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin.
52
**    4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local
53
** motions.  It doesn't use tactics based on auto_left_margin.  Weirdly
54
** enough, it doesn't use its own hardware-scrolling routine to scroll up
55
** destination lines for out-of-bounds addresses!
56
**    old ncurses optimizer: less accurate cost computations (in fact,
57
** it was broken and had to be commented out!).
58
**
59
** Compile with -DMAIN to build an interactive tester/timer for the movement
60
** optimizer.  You can use it to investigate the optimizer's behavior.
61
** You can also use it for tuning the formulas used to determine whether
62
** or not full optimization is attempted.
63
**
64
** This code has a nasty tendency to find bugs in terminfo entries, because it
65
** exercises the non-cup movement capabilities heavily.  If you think you've
66
** found a bug, try deleting subsets of the following capabilities (arranged
67
** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud,
68
** cuf, cub, cuu1, cud1, cuf1, cub1.  It may be that one or more are wrong.
69
**
70
** Note: you should expect this code to look like a resource hog in a profile.
71
** That's because it does a lot of I/O, through the tputs() calls.  The I/O
72
** cost swamps the computation overhead (and as machines get faster, this
73
** will become even more true).  Comments in the test exerciser at the end
74
** go into detail about tuning and how you can gauge the optimizer's
75
** effectiveness.
76
**/
77
78
/****************************************************************************
79
 *
80
 * Constants and macros for optimizer tuning.
81
 *
82
 ****************************************************************************/
83
84
/*
85
 * The average overhead of a full optimization computation in character
86
 * transmission times.  If it's too high, the algorithm will be a bit
87
 * over-biased toward using cup rather than local motions; if it's too
88
 * low, the algorithm may spend more time than is strictly optimal
89
 * looking for non-cup motions.  Profile the optimizer using the `t'
90
 * command of the exerciser (see below), and round to the nearest integer.
91
 *
92
 * Yes, I (esr) thought about computing expected overhead dynamically, say
93
 * by derivation from a running average of optimizer times.  But the
94
 * whole point of this optimization is to *decrease* the frequency of
95
 * system calls. :-)
96
 */
97
#define COMPUTE_OVERHEAD	1	/* I use a 90MHz Pentium @ 9.6Kbps */
98
99
/*
100
 * LONG_DIST is the distance we consider to be just as costly to move over as a
101
 * cup sequence is to emit.  In other words, it's the length of a cup sequence
102
 * adjusted for average computation overhead.  The magic number is the length
103
 * of "\033[yy;xxH", the typical cup sequence these days.
104
 */
105
#define LONG_DIST		(8 - COMPUTE_OVERHEAD)
106
107
/*
108
 * Tell whether a motion is optimizable by local motions.  Needs to be cheap to
109
 * compute. In general, all the fast moves go to either the right or left edge
110
 * of the screen.  So any motion to a location that is (a) further away than
111
 * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST,
112
 * we'll consider nonlocal.
113
 */
114
#define NOT_LOCAL(fy, fx, ty, tx)	((tx > LONG_DIST) \
115
 		 && (tx < screen_columns - 1 - LONG_DIST) \
116
		 && (abs(ty-fy) + abs(tx-fx) > LONG_DIST))
117
118
/****************************************************************************
119
 *
120
 * External interfaces
121
 *
122
 ****************************************************************************/
123
124
/*
125
 * For this code to work OK, the following components must live in the
126
 * screen structure:
127
 *
128
 *	int		_char_padding;	// cost of character put
129
 *	int		_cr_cost;	// cost of (carriage_return)
130
 *	int		_cup_cost;	// cost of (cursor_address)
131
 *	int		_home_cost;	// cost of (cursor_home)
132
 *	int		_ll_cost;	// cost of (cursor_to_ll)
133
 *#if USE_HARD_TABS
134
 *	int		_ht_cost;	// cost of (tab)
135
 *	int		_cbt_cost;	// cost of (back_tab)
136
 *#endif USE_HARD_TABS
137
 *	int		_cub1_cost;	// cost of (cursor_left)
138
 *	int		_cuf1_cost;	// cost of (cursor_right)
139
 *	int		_cud1_cost;	// cost of (cursor_down)
140
 *	int		_cuu1_cost;	// cost of (cursor_up)
141
 *	int		_cub_cost;	// cost of (parm_cursor_left)
142
 *	int		_cuf_cost;	// cost of (parm_cursor_right)
143
 *	int		_cud_cost;	// cost of (parm_cursor_down)
144
 *	int		_cuu_cost;	// cost of (parm_cursor_up)
145
 *	int		_hpa_cost;	// cost of (column_address)
146
 *	int		_vpa_cost;	// cost of (row_address)
147
 *	int		_ech_cost;	// cost of (erase_chars)
148
 *	int		_rep_cost;	// cost of (repeat_char)
149
 *
150
 * The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs
151
 * for local motions.  On many systems, it's not, due to uncertainties about
152
 * tab delays and whether or not tabs will be expanded in raw mode.  If you
153
 * have parm_right_cursor, tab motions don't win you a lot anyhow.
154
 */
155
156
#include <curses.priv.h>
157
#include <term.h>
158
#include <ctype.h>
159
160
MODULE_ID("$Id: lib_mvcur.c,v 1.13 2010/01/12 23:22:07 nicm Exp $")
161
162
#define WANT_CHAR(y, x)	SP->_newscr->_line[y].text[x]	/* desired state */
163
#define BAUDRATE	cur_term->_baudrate	/* bits per second */
164
165
#if defined(MAIN) || defined(NCURSES_TEST)
166
#include <sys/time.h>
167
168
static bool profiling = FALSE;
169
static float diff;
170
#endif /* MAIN */
171
172
#define OPT_SIZE 512
173
174
static int normalized_cost(const char *const cap, int affcnt);
175
176
/****************************************************************************
177
 *
178
 * Initialization/wrapup (including cost pre-computation)
179
 *
180
 ****************************************************************************/
181
182
#ifdef TRACE
183
static int
184
trace_cost_of(const char *capname, const char *cap, int affcnt)
185
{
186
    int result = _nc_msec_cost(cap, affcnt);
187
    TR(TRACE_CHARPUT | TRACE_MOVE,
188
       ("CostOf %s %d %s", capname, result, _nc_visbuf(cap)));
189
    return result;
190
}
191
#define CostOf(cap,affcnt) trace_cost_of(#cap,cap,affcnt);
192
193
static int
194
trace_normalized_cost(const char *capname, const char *cap, int affcnt)
195
{
196
    int result = normalized_cost(cap, affcnt);
197
    TR(TRACE_CHARPUT | TRACE_MOVE,
198
       ("NormalizedCost %s %d %s", capname, result, _nc_visbuf(cap)));
199
    return result;
200
}
201
#define NormalizedCost(cap,affcnt) trace_normalized_cost(#cap,cap,affcnt);
202
203
#else
204
205
#define CostOf(cap,affcnt) _nc_msec_cost(cap,affcnt);
206
#define NormalizedCost(cap,affcnt) normalized_cost(cap,affcnt);
207
208
#endif
209
210
NCURSES_EXPORT(int)
211
_nc_msec_cost(const char *const cap, int affcnt)
212
/* compute the cost of a given operation */
213
{
214
    if (cap == 0)
215
	return (INFINITY);
216
    else {
217
	const char *cp;
218
	float cum_cost = 0.0;
219
220
	for (cp = cap; *cp; cp++) {
221
	    /* extract padding, either mandatory or required */
222
	    if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>')) {
223
		float number = 0.0;
224
225
		for (cp += 2; *cp != '>'; cp++) {
226
		    if (isdigit(UChar(*cp)))
227
			number = number * 10 + (*cp - '0');
228
		    else if (*cp == '*')
229
			number *= affcnt;
230
		    else if (*cp == '.' && (*++cp != '>') && isdigit(UChar(*cp)))
231
			number += (*cp - '0') / 10.0;
232
		}
233
234
#if NCURSES_NO_PADDING
235
		if (!GetNoPadding(SP))
236
#endif
237
		    cum_cost += number * 10;
238
	    } else
239
		cum_cost += SP->_char_padding;
240
	}
241
242
	return ((int) cum_cost);
243
    }
244
}
245
246
static int
247
normalized_cost(const char *const cap, int affcnt)
248
/* compute the effective character-count for an operation (round up) */
249
{
250
    int cost = _nc_msec_cost(cap, affcnt);
251
    if (cost != INFINITY)
252
	cost = (cost + SP->_char_padding - 1) / SP->_char_padding;
253
    return cost;
254
}
255
256
static void
257
reset_scroll_region(void)
258
/* Set the scroll-region to a known state (the default) */
259
{
260
    if (change_scroll_region) {
261
	TPUTS_TRACE("change_scroll_region");
262
	putp(TPARM_2(change_scroll_region, 0, screen_lines - 1));
263
    }
264
}
265
266
NCURSES_EXPORT(void)
267
_nc_mvcur_resume(void)
268
/* what to do at initialization time and after each shellout */
269
{
270
    /* initialize screen for cursor access */
271
    if (enter_ca_mode) {
272
	TPUTS_TRACE("enter_ca_mode");
273
	putp(enter_ca_mode);
274
    }
275
276
    /*
277
     * Doing this here rather than in _nc_mvcur_wrap() ensures that
278
     * ncurses programs will see a reset scroll region even if a
279
     * program that messed with it died ungracefully.
280
     *
281
     * This also undoes the effects of terminal init strings that assume
282
     * they know the screen size.  This is useful when you're running
283
     * a vt100 emulation through xterm.
284
     */
285
    reset_scroll_region();
286
    SP->_cursrow = SP->_curscol = -1;
287
288
    /* restore cursor shape */
289
    if (SP->_cursor != -1) {
290
	int cursor = SP->_cursor;
291
	SP->_cursor = -1;
292
	curs_set(cursor);
293
    }
294
}
295
296
NCURSES_EXPORT(void)
297
_nc_mvcur_init(void)
298
/* initialize the cost structure */
299
{
300
    if (isatty(fileno(SP->_ofp)))
301
	SP->_char_padding = ((BAUDBYTE * 1000 * 10)
302
			     / (BAUDRATE > 0 ? BAUDRATE : 9600));
303
    else
304
	SP->_char_padding = 1;	/* must be nonzero */
305
    if (SP->_char_padding <= 0)
306
	SP->_char_padding = 1;	/* must be nonzero */
307
    TR(TRACE_CHARPUT | TRACE_MOVE, ("char_padding %d msecs", SP->_char_padding));
308
309
    /* non-parameterized local-motion strings */
310
    SP->_cr_cost = CostOf(carriage_return, 0);
311
    SP->_home_cost = CostOf(cursor_home, 0);
312
    SP->_ll_cost = CostOf(cursor_to_ll, 0);
313
#if USE_HARD_TABS
314
    if (getenv("NCURSES_NO_HARD_TABS") == 0) {
315
	SP->_ht_cost = CostOf(tab, 0);
316
	SP->_cbt_cost = CostOf(back_tab, 0);
317
    } else {
318
	SP->_ht_cost = INFINITY;
319
	SP->_cbt_cost = INFINITY;
320
    }
321
#endif /* USE_HARD_TABS */
322
    SP->_cub1_cost = CostOf(cursor_left, 0);
323
    SP->_cuf1_cost = CostOf(cursor_right, 0);
324
    SP->_cud1_cost = CostOf(cursor_down, 0);
325
    SP->_cuu1_cost = CostOf(cursor_up, 0);
326
327
    SP->_smir_cost = CostOf(enter_insert_mode, 0);
328
    SP->_rmir_cost = CostOf(exit_insert_mode, 0);
329
    SP->_ip_cost = 0;
330
    if (insert_padding) {
331
	SP->_ip_cost = CostOf(insert_padding, 0);
332
    }
333
334
    /*
335
     * Assumption: if the terminal has memory_relative addressing, the
336
     * initialization strings or smcup will set single-page mode so we
337
     * can treat it like absolute screen addressing.  This seems to be true
338
     * for all cursor_mem_address terminal types in the terminfo database.
339
     */
340
    SP->_address_cursor = cursor_address ? cursor_address : cursor_mem_address;
341
342
    /*
343
     * Parametrized local-motion strings.  This static cost computation
344
     * depends on the following assumptions:
345
     *
346
     * (1) They never have * padding.  In the entire master terminfo database
347
     *     as of March 1995, only the obsolete Zenith Z-100 pc violates this.
348
     *     (Proportional padding is found mainly in insert, delete and scroll
349
     *     capabilities).
350
     *
351
     * (2) The average case of cup has two two-digit parameters.  Strictly,
352
     *     the average case for a 24 * 80 screen has ((10*10*(1 + 1)) +
353
     *     (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458
354
     *     digits of parameters.  On a 25x80 screen the average is 3.6197.
355
     *     On larger screens the value gets much closer to 4.
356
     *
357
     * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters
358
     *     (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750).
359
     *
360
     * (4) The average case of cud/cuu/vpa has 2 digits of parameters
361
     *     (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833).
362
     *
363
     * All these averages depend on the assumption that all parameter values
364
     * are equally probable.
365
     */
366
    SP->_cup_cost = CostOf(TPARM_2(SP->_address_cursor, 23, 23), 1);
367
    SP->_cub_cost = CostOf(TPARM_1(parm_left_cursor, 23), 1);
368
    SP->_cuf_cost = CostOf(TPARM_1(parm_right_cursor, 23), 1);
369
    SP->_cud_cost = CostOf(TPARM_1(parm_down_cursor, 23), 1);
370
    SP->_cuu_cost = CostOf(TPARM_1(parm_up_cursor, 23), 1);
371
    SP->_hpa_cost = CostOf(TPARM_1(column_address, 23), 1);
372
    SP->_vpa_cost = CostOf(TPARM_1(row_address, 23), 1);
373
374
    /* non-parameterized screen-update strings */
375
    SP->_ed_cost = NormalizedCost(clr_eos, 1);
376
    SP->_el_cost = NormalizedCost(clr_eol, 1);
377
    SP->_el1_cost = NormalizedCost(clr_bol, 1);
378
    SP->_dch1_cost = NormalizedCost(delete_character, 1);
379
    SP->_ich1_cost = NormalizedCost(insert_character, 1);
380
381
    /*
382
     * If this is a bce-terminal, we want to bias the choice so we use clr_eol
383
     * rather than spaces at the end of a line.
384
     */
385
    if (back_color_erase)
386
	SP->_el_cost = 0;
387
388
    /* parameterized screen-update strings */
389
    SP->_dch_cost = NormalizedCost(TPARM_1(parm_dch, 23), 1);
390
    SP->_ich_cost = NormalizedCost(TPARM_1(parm_ich, 23), 1);
391
    SP->_ech_cost = NormalizedCost(TPARM_1(erase_chars, 23), 1);
392
    SP->_rep_cost = NormalizedCost(TPARM_2(repeat_char, ' ', 23), 1);
393
394
    SP->_cup_ch_cost = NormalizedCost(TPARM_2(SP->_address_cursor, 23, 23), 1);
395
    SP->_hpa_ch_cost = NormalizedCost(TPARM_1(column_address, 23), 1);
396
    SP->_cuf_ch_cost = NormalizedCost(TPARM_1(parm_right_cursor, 23), 1);
397
    SP->_inline_cost = min(SP->_cup_ch_cost,
398
			   min(SP->_hpa_ch_cost,
399
			       SP->_cuf_ch_cost));
400
401
    /*
402
     * If save_cursor is used within enter_ca_mode, we should not use it for
403
     * scrolling optimization, since the corresponding restore_cursor is not
404
     * nested on the various terminals (vt100, xterm, etc.) which use this
405
     * feature.
406
     */
407
    if (save_cursor != 0
408
	&& enter_ca_mode != 0
409
	&& strstr(enter_ca_mode, save_cursor) != 0) {
410
	T(("...suppressed sc/rc capability due to conflict with smcup/rmcup"));
411
	save_cursor = 0;
412
	restore_cursor = 0;
413
    }
414
415
    /*
416
     * A different, possibly better way to arrange this would be to set
417
     * SP->_endwin = TRUE at window initialization time and let this be
418
     * called by doupdate's return-from-shellout code.
419
     */
420
    _nc_mvcur_resume();
421
}
422
423
NCURSES_EXPORT(void)
424
_nc_mvcur_wrap(void)
425
/* wrap up cursor-addressing mode */
426
{
427
    /* leave cursor at screen bottom */
428
    mvcur(-1, -1, screen_lines - 1, 0);
429
430
    /* set cursor to normal mode */
431
    if (SP->_cursor != -1) {
432
	int cursor = SP->_cursor;
433
	curs_set(1);
434
	SP->_cursor = cursor;
435
    }
436
437
    if (exit_ca_mode) {
438
	TPUTS_TRACE("exit_ca_mode");
439
	putp(exit_ca_mode);
440
    }
441
    /*
442
     * Reset terminal's tab counter.  There's a long-time bug that
443
     * if you exit a "curses" program such as vi or more, tab
444
     * forward, and then backspace, the cursor doesn't go to the
445
     * right place.  The problem is that the kernel counts the
446
     * escape sequences that reset things as column positions.
447
     * Utter a \r to reset this invisibly.
448
     */
449
    _nc_outch('\r');
450
}
451
452
/****************************************************************************
453
 *
454
 * Optimized cursor movement
455
 *
456
 ****************************************************************************/
457
458
/*
459
 * Perform repeated-append, returning cost
460
 */
461
static NCURSES_INLINE int
462
repeated_append(string_desc * target, int total, int num, int repeat, const char *src)
463
{
464
    size_t need = repeat * strlen(src);
465
466
    if (need < target->s_size) {
467
	while (repeat-- > 0) {
468
	    if (_nc_safe_strcat(target, src)) {
469
		total += num;
470
	    } else {
471
		total = INFINITY;
472
		break;
473
	    }
474
	}
475
    } else {
476
	total = INFINITY;
477
    }
478
    return total;
479
}
480
481
#ifndef NO_OPTIMIZE
482
#define NEXTTAB(fr)	(fr + init_tabs - (fr % init_tabs))
483
484
/*
485
 * Assume back_tab (CBT) does not wrap backwards at the left margin, return
486
 * a negative value at that point to simplify the loop.
487
 */
488
#define LASTTAB(fr)	((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1)
489
490
static int
491
relative_move(string_desc * target, int from_y, int from_x, int to_y, int
492
	      to_x, bool ovw)
493
/* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */
494
{
495
    string_desc save;
496
    int n, vcost = 0, hcost = 0;
497
498
    (void) _nc_str_copy(&save, target);
499
500
    if (to_y != from_y) {
501
	vcost = INFINITY;
502
503
	if (row_address != 0
504
	    && _nc_safe_strcat(target, TPARM_1(row_address, to_y))) {
505
	    vcost = SP->_vpa_cost;
506
	}
507
508
	if (to_y > from_y) {
509
	    n = (to_y - from_y);
510
511
	    if (parm_down_cursor
512
		&& SP->_cud_cost < vcost
513
		&& _nc_safe_strcat(_nc_str_copy(target, &save),
514
				   TPARM_1(parm_down_cursor, n))) {
515
		vcost = SP->_cud_cost;
516
	    }
517
518
	    if (cursor_down
519
		&& (*cursor_down != '\n' || SP->_nl)
520
		&& (n * SP->_cud1_cost < vcost)) {
521
		vcost = repeated_append(_nc_str_copy(target, &save), 0,
522
					SP->_cud1_cost, n, cursor_down);
523
	    }
524
	} else {		/* (to_y < from_y) */
525
	    n = (from_y - to_y);
526
527
	    if (parm_up_cursor
528
		&& SP->_cuu_cost < vcost
529
		&& _nc_safe_strcat(_nc_str_copy(target, &save),
530
				   TPARM_1(parm_up_cursor, n))) {
531
		vcost = SP->_cuu_cost;
532
	    }
533
534
	    if (cursor_up && (n * SP->_cuu1_cost < vcost)) {
535
		vcost = repeated_append(_nc_str_copy(target, &save), 0,
536
					SP->_cuu1_cost, n, cursor_up);
537
	    }
538
	}
539
540
	if (vcost == INFINITY)
541
	    return (INFINITY);
542
    }
543
544
    save = *target;
545
546
    if (to_x != from_x) {
547
	char str[OPT_SIZE];
548
	string_desc check;
549
550
	hcost = INFINITY;
551
552
	if (column_address
553
	    && _nc_safe_strcat(_nc_str_copy(target, &save),
554
			       TPARM_1(column_address, to_x))) {
555
	    hcost = SP->_hpa_cost;
556
	}
557
558
	if (to_x > from_x) {
559
	    n = to_x - from_x;
560
561
	    if (parm_right_cursor
562
		&& SP->_cuf_cost < hcost
563
		&& _nc_safe_strcat(_nc_str_copy(target, &save),
564
				   TPARM_1(parm_right_cursor, n))) {
565
		hcost = SP->_cuf_cost;
566
	    }
567
568
	    if (cursor_right) {
569
		int lhcost = 0;
570
571
		(void) _nc_str_init(&check, str, sizeof(str));
572
573
#if USE_HARD_TABS
574
		/* use hard tabs, if we have them, to do as much as possible */
575
		if (init_tabs > 0 && tab) {
576
		    int nxt, fr;
577
578
		    for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt) {
579
			lhcost = repeated_append(&check, lhcost,
580
						 SP->_ht_cost, 1, tab);
581
			if (lhcost == INFINITY)
582
			    break;
583
		    }
584
585
		    n = to_x - fr;
586
		    from_x = fr;
587
		}
588
#endif /* USE_HARD_TABS */
589
590
		if (n <= 0 || n >= (int) check.s_size)
591
		    ovw = FALSE;
592
#if BSD_TPUTS
593
		/*
594
		 * If we're allowing BSD-style padding in tputs, don't generate
595
		 * a string with a leading digit.  Otherwise, that will be
596
		 * interpreted as a padding value rather than sent to the
597
		 * screen.
598
		 */
599
		if (ovw
600
		    && n > 0
601
		    && n < (int) check.s_size
602
		    && vcost == 0
603
		    && str[0] == '\0') {
604
		    int wanted = CharOf(WANT_CHAR(to_y, from_x));
605
		    if (is8bits(wanted) && isdigit(wanted))
606
			ovw = FALSE;
607
		}
608
#endif
609
		/*
610
		 * If we have no attribute changes, overwrite is cheaper.
611
		 * Note: must suppress this by passing in ovw = FALSE whenever
612
		 * WANT_CHAR would return invalid data.  In particular, this
613
		 * is true between the time a hardware scroll has been done
614
		 * and the time the structure WANT_CHAR would access has been
615
		 * updated.
616
		 */
617
		if (ovw) {
618
		    int i;
619
620
		    for (i = 0; i < n; i++) {
621
			NCURSES_CH_T ch = WANT_CHAR(to_y, from_x + i);
622
			if (!SameAttrOf(ch, SCREEN_ATTRS(SP))
623
#if USE_WIDEC_SUPPORT
624
			    || !Charable(ch)
625
#endif
626
			    ) {
627
			    ovw = FALSE;
628
			    break;
629
			}
630
		    }
631
		}
632
		if (ovw) {
633
		    int i;
634
635
		    for (i = 0; i < n; i++)
636
			*check.s_tail++ = (char) CharOf(WANT_CHAR(to_y,
637
								  from_x + i));
638
		    *check.s_tail = '\0';
639
		    check.s_size -= n;
640
		    lhcost += n * SP->_char_padding;
641
		} else {
642
		    lhcost = repeated_append(&check, lhcost, SP->_cuf1_cost,
643
					     n, cursor_right);
644
		}
645
646
		if (lhcost < hcost
647
		    && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
648
		    hcost = lhcost;
649
		}
650
	    }
651
	} else {		/* (to_x < from_x) */
652
	    n = from_x - to_x;
653
654
	    if (parm_left_cursor
655
		&& SP->_cub_cost < hcost
656
		&& _nc_safe_strcat(_nc_str_copy(target, &save),
657
				   TPARM_1(parm_left_cursor, n))) {
658
		hcost = SP->_cub_cost;
659
	    }
660
661
	    if (cursor_left) {
662
		int lhcost = 0;
663
664
		(void) _nc_str_init(&check, str, sizeof(str));
665
666
#if USE_HARD_TABS
667
		if (init_tabs > 0 && back_tab) {
668
		    int nxt, fr;
669
670
		    for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt) {
671
			lhcost = repeated_append(&check, lhcost,
672
						 SP->_cbt_cost, 1, back_tab);
673
			if (lhcost == INFINITY)
674
			    break;
675
		    }
676
677
		    n = fr - to_x;
678
		}
679
#endif /* USE_HARD_TABS */
680
681
		lhcost = repeated_append(&check, lhcost, SP->_cub1_cost, n, cursor_left);
682
683
		if (lhcost < hcost
684
		    && _nc_safe_strcat(_nc_str_copy(target, &save), str)) {
685
		    hcost = lhcost;
686
		}
687
	    }
688
	}
689
690
	if (hcost == INFINITY)
691
	    return (INFINITY);
692
    }
693
694
    return (vcost + hcost);
695
}
696
#endif /* !NO_OPTIMIZE */
697
698
/*
699
 * With the machinery set up above, it's conceivable that
700
 * onscreen_mvcur could be modified into a recursive function that does
701
 * an alpha-beta search of motion space, as though it were a chess
702
 * move tree, with the weight function being boolean and the search
703
 * depth equated to length of string.  However, this would jack up the
704
 * computation cost a lot, especially on terminals without a cup
705
 * capability constraining the search tree depth.  So we settle for
706
 * the simpler method below.
707
 */
708
709
static NCURSES_INLINE int
710
onscreen_mvcur(int yold, int xold, int ynew, int xnew, bool ovw)
711
/* onscreen move from (yold, xold) to (ynew, xnew) */
712
{
713
    string_desc result;
714
    char buffer[OPT_SIZE];
715
    int tactic = 0, newcost, usecost = INFINITY;
716
    int t5_cr_cost;
717
718
#if defined(MAIN) || defined(NCURSES_TEST)
719
    struct timeval before, after;
720
721
    gettimeofday(&before, NULL);
722
#endif /* MAIN */
723
724
#define NullResult _nc_str_null(&result, sizeof(buffer))
725
#define InitResult _nc_str_init(&result, buffer, sizeof(buffer))
726
727
    /* tactic #0: use direct cursor addressing */
728
    if (_nc_safe_strcpy(InitResult, TPARM_2(SP->_address_cursor, ynew, xnew))) {
729
	tactic = 0;
730
	usecost = SP->_cup_cost;
731
732
#if defined(TRACE) || defined(NCURSES_TEST)
733
	if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
734
	    goto nonlocal;
735
#endif /* TRACE */
736
737
	/*
738
	 * We may be able to tell in advance that the full optimization
739
	 * will probably not be worth its overhead.  Also, don't try to
740
	 * use local movement if the current attribute is anything but
741
	 * A_NORMAL...there are just too many ways this can screw up
742
	 * (like, say, local-movement \n getting mapped to some obscure
743
	 * character because A_ALTCHARSET is on).
744
	 */
745
	if (yold == -1 || xold == -1 || NOT_LOCAL(yold, xold, ynew, xnew)) {
746
#if defined(MAIN) || defined(NCURSES_TEST)
747
	    if (!profiling) {
748
		(void) fputs("nonlocal\n", stderr);
749
		goto nonlocal;	/* always run the optimizer if profiling */
750
	    }
751
#else
752
	    goto nonlocal;
753
#endif /* MAIN */
754
	}
755
    }
756
#ifndef NO_OPTIMIZE
757
    /* tactic #1: use local movement */
758
    if (yold != -1 && xold != -1
759
	&& ((newcost = relative_move(NullResult, yold, xold, ynew, xnew,
760
				     ovw)) != INFINITY)
761
	&& newcost < usecost) {
762
	tactic = 1;
763
	usecost = newcost;
764
    }
765
766
    /* tactic #2: use carriage-return + local movement */
767
    if (yold != -1 && carriage_return
768
	&& ((newcost = relative_move(NullResult, yold, 0, ynew, xnew, ovw))
769
	    != INFINITY)
770
	&& SP->_cr_cost + newcost < usecost) {
771
	tactic = 2;
772
	usecost = SP->_cr_cost + newcost;
773
    }
774
775
    /* tactic #3: use home-cursor + local movement */
776
    if (cursor_home
777
	&& ((newcost = relative_move(NullResult, 0, 0, ynew, xnew, ovw)) != INFINITY)
778
	&& SP->_home_cost + newcost < usecost) {
779
	tactic = 3;
780
	usecost = SP->_home_cost + newcost;
781
    }
782
783
    /* tactic #4: use home-down + local movement */
784
    if (cursor_to_ll
785
	&& ((newcost = relative_move(NullResult, screen_lines - 1, 0, ynew,
786
				     xnew, ovw)) != INFINITY)
787
	&& SP->_ll_cost + newcost < usecost) {
788
	tactic = 4;
789
	usecost = SP->_ll_cost + newcost;
790
    }
791
792
    /*
793
     * tactic #5: use left margin for wrap to right-hand side,
794
     * unless strange wrap behavior indicated by xenl might hose us.
795
     */
796
    t5_cr_cost = (xold > 0 ? SP->_cr_cost : 0);
797
    if (auto_left_margin && !eat_newline_glitch
798
	&& yold > 0 && cursor_left
799
	&& ((newcost = relative_move(NullResult, yold - 1, screen_columns -
800
				     1, ynew, xnew, ovw)) != INFINITY)
801
	&& t5_cr_cost + SP->_cub1_cost + newcost < usecost) {
802
	tactic = 5;
803
	usecost = t5_cr_cost + SP->_cub1_cost + newcost;
804
    }
805
806
    /*
807
     * These cases are ordered by estimated relative frequency.
808
     */
809
    if (tactic)
810
	InitResult;
811
    switch (tactic) {
812
    case 1:
813
	(void) relative_move(&result, yold, xold, ynew, xnew, ovw);
814
	break;
815
    case 2:
816
	(void) _nc_safe_strcpy(&result, carriage_return);
817
	(void) relative_move(&result, yold, 0, ynew, xnew, ovw);
818
	break;
819
    case 3:
820
	(void) _nc_safe_strcpy(&result, cursor_home);
821
	(void) relative_move(&result, 0, 0, ynew, xnew, ovw);
822
	break;
823
    case 4:
824
	(void) _nc_safe_strcpy(&result, cursor_to_ll);
825
	(void) relative_move(&result, screen_lines - 1, 0, ynew, xnew, ovw);
826
	break;
827
    case 5:
828
	if (xold > 0)
829
	    (void) _nc_safe_strcat(&result, carriage_return);
830
	(void) _nc_safe_strcat(&result, cursor_left);
831
	(void) relative_move(&result, yold - 1, screen_columns - 1, ynew,
832
			     xnew, ovw);
833
	break;
834
    }
835
#endif /* !NO_OPTIMIZE */
836
837
  nonlocal:
838
#if defined(MAIN) || defined(NCURSES_TEST)
839
    gettimeofday(&after, NULL);
840
    diff = after.tv_usec - before.tv_usec
841
	+ (after.tv_sec - before.tv_sec) * 1000000;
842
    if (!profiling)
843
	(void) fprintf(stderr,
844
		       "onscreen: %d microsec, %f 28.8Kbps char-equivalents\n",
845
		       (int) diff, diff / 288);
846
#endif /* MAIN */
847
848
    if (usecost != INFINITY) {
849
	TPUTS_TRACE("mvcur");
850
	tputs(buffer, 1, _nc_outch);
851
	SP->_cursrow = ynew;
852
	SP->_curscol = xnew;
853
	return (OK);
854
    } else
855
	return (ERR);
856
}
857
858
NCURSES_EXPORT(int)
859
mvcur(int yold, int xold, int ynew, int xnew)
860
/* optimized cursor move from (yold, xold) to (ynew, xnew) */
861
{
862
    NCURSES_CH_T oldattr;
863
    int code;
864
865
    TR(TRACE_CALLS | TRACE_MOVE, (T_CALLED("mvcur(%d,%d,%d,%d)"),
866
				  yold, xold, ynew, xnew));
867
868
    if (SP == 0) {
869
	code = ERR;
870
    } else if (yold == ynew && xold == xnew) {
871
	code = OK;
872
    } else {
873
874
	/*
875
	 * Most work here is rounding for terminal boundaries getting the
876
	 * column position implied by wraparound or the lack thereof and
877
	 * rolling up the screen to get ynew on the screen.
878
	 */
879
	if (xnew >= screen_columns) {
880
	    ynew += xnew / screen_columns;
881
	    xnew %= screen_columns;
882
	}
883
884
	/*
885
	 * Force restore even if msgr is on when we're in an alternate
886
	 * character set -- these have a strong tendency to screw up the CR &
887
	 * LF used for local character motions!
888
	 */
889
	oldattr = SCREEN_ATTRS(SP);
890
	if ((AttrOf(oldattr) & A_ALTCHARSET)
891
	    || (AttrOf(oldattr) && !move_standout_mode)) {
892
	    TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move",
893
			       (unsigned long) AttrOf(oldattr),
894
			       _traceattr(AttrOf(oldattr))));
895
	    (void) VIDATTR(A_NORMAL, 0);
896
	}
897
898
	if (xold >= screen_columns) {
899
	    int l;
900
901
	    if (SP->_nl) {
902
		l = (xold + 1) / screen_columns;
903
		yold += l;
904
		if (yold >= screen_lines)
905
		    l -= (yold - screen_lines - 1);
906
907
		if (l > 0) {
908
		    if (carriage_return) {
909
			TPUTS_TRACE("carriage_return");
910
			putp(carriage_return);
911
		    } else
912
			_nc_outch('\r');
913
		    xold = 0;
914
915
		    while (l > 0) {
916
			if (newline) {
917
			    TPUTS_TRACE("newline");
918
			    putp(newline);
919
			} else
920
			    _nc_outch('\n');
921
			l--;
922
		    }
923
		}
924
	    } else {
925
		/*
926
		 * If caller set nonl(), we cannot really use newlines to
927
		 * position to the next row.
928
		 */
929
		xold = -1;
930
		yold = -1;
931
	    }
932
	}
933
934
	if (yold > screen_lines - 1)
935
	    yold = screen_lines - 1;
936
	if (ynew > screen_lines - 1)
937
	    ynew = screen_lines - 1;
938
939
	/* destination location is on screen now */
940
	code = onscreen_mvcur(yold, xold, ynew, xnew, TRUE);
941
942
	/*
943
	 * Restore attributes if we disabled them before moving.
944
	 */
945
	if (!SameAttrOf(oldattr, SCREEN_ATTRS(SP))) {
946
	    TR(TRACE_CHARPUT, ("turning on (%#lx) %s after move",
947
			       (unsigned long) AttrOf(oldattr),
948
			       _traceattr(AttrOf(oldattr))));
949
	    (void) VIDATTR(AttrOf(oldattr), GetPair(oldattr));
950
	}
951
    }
952
    returnCode(code);
953
}
954
955
#if defined(TRACE) || defined(NCURSES_TEST)
956
NCURSES_EXPORT_VAR(int) _nc_optimize_enable = OPTIMIZE_ALL;
957
#endif
958
959
#if defined(MAIN) || defined(NCURSES_TEST)
960
/****************************************************************************
961
 *
962
 * Movement optimizer test code
963
 *
964
 ****************************************************************************/
965
966
#include <tic.h>
967
#include <dump_entry.h>
968
#include <time.h>
969
970
NCURSES_EXPORT_VAR(const char *) _nc_progname = "mvcur";
971
972
static unsigned long xmits;
973
974
/* these override lib_tputs.c */
975
NCURSES_EXPORT(int)
976
tputs(const char *string, int affcnt GCC_UNUSED, int (*outc) (int) GCC_UNUSED)
977
/* stub tputs() that dumps sequences in a visible form */
978
{
979
    if (profiling)
980
	xmits += strlen(string);
981
    else
982
	(void) fputs(_nc_visbuf(string), stdout);
983
    return (OK);
984
}
985
986
NCURSES_EXPORT(int)
987
putp(const char *string)
988
{
989
    return (tputs(string, 1, _nc_outch));
990
}
991
992
NCURSES_EXPORT(int)
993
_nc_outch(int ch)
994
{
995
    putc(ch, stdout);
996
    return OK;
997
}
998
999
NCURSES_EXPORT(int)
1000
delay_output(int ms GCC_UNUSED)
1001
{
1002
    return OK;
1003
}
1004
1005
static char tname[PATH_MAX];
1006
1007
static void
1008
load_term(void)
1009
{
1010
    (void) setupterm(tname, STDOUT_FILENO, NULL);
1011
}
1012
1013
static int
1014
roll(int n)
1015
{
1016
    int i, j;
1017
1018
    i = (RAND_MAX / n) * n;
1019
    while ((j = rand()) >= i)
1020
	continue;
1021
    return (j % n);
1022
}
1023
1024
int
1025
main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
1026
{
1027
    strlcpy(tname, sizeof(tname), getenv("TERM"));
1028
    load_term();
1029
    _nc_setupscreen(lines, columns, stdout, FALSE, 0);
1030
    baudrate();
1031
1032
    _nc_mvcur_init();
1033
    NC_BUFFERED(FALSE);
1034
1035
    (void) puts("The mvcur tester.  Type ? for help");
1036
1037
    fputs("smcup:", stdout);
1038
    putchar('\n');
1039
1040
    for (;;) {
1041
	int fy, fx, ty, tx, n, i;
1042
	char buf[BUFSIZ], capname[BUFSIZ];
1043
1044
	(void) fputs("> ", stdout);
1045
	if (fgets(buf, sizeof(buf), stdin) == NULL) {
1046
		if (ferror(stdin))
1047
			fputs("ferror on stdin", stderr);
1048
		break;
1049
	}
1050
1051
	if (buf[0] == '?') {
1052
	    (void) puts("?                -- display this help message");
1053
	    (void)
1054
		puts("fy fx ty tx      -- (4 numbers) display (fy,fx)->(ty,tx) move");
1055
	    (void) puts("s[croll] n t b m -- display scrolling sequence");
1056
	    (void)
1057
		printf("r[eload]         -- reload terminal info for %s\n",
1058
		       termname());
1059
	    (void)
1060
		puts("l[oad] <term>    -- load terminal info for type <term>");
1061
	    (void) puts("d[elete] <cap>   -- delete named capability");
1062
	    (void) puts("i[nspect]        -- display terminal capabilities");
1063
	    (void)
1064
		puts("c[ost]           -- dump cursor-optimization cost table");
1065
	    (void) puts("o[optimize]      -- toggle movement optimization");
1066
	    (void)
1067
		puts("t[orture] <num>  -- torture-test with <num> random moves");
1068
	    (void) puts("q[uit]           -- quit the program");
1069
	} else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
1070
	    struct timeval before, after;
1071
1072
	    putchar('"');
1073
1074
	    gettimeofday(&before, NULL);
1075
	    mvcur(fy, fx, ty, tx);
1076
	    gettimeofday(&after, NULL);
1077
1078
	    printf("\" (%ld msec)\n",
1079
		   (long) (after.tv_usec - before.tv_usec
1080
			   + (after.tv_sec - before.tv_sec)
1081
			   * 1000000));
1082
	} else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4) {
1083
	    struct timeval before, after;
1084
1085
	    putchar('"');
1086
1087
	    gettimeofday(&before, NULL);
1088
	    _nc_scrolln(fy, fx, ty, tx);
1089
	    gettimeofday(&after, NULL);
1090
1091
	    printf("\" (%ld msec)\n",
1092
		   (long) (after.tv_usec - before.tv_usec + (after.tv_sec -
1093
							     before.tv_sec)
1094
			   * 1000000));
1095
	} else if (buf[0] == 'r') {
1096
	    (void) strlcpy(tname, sizeof(tname), termname());
1097
	    load_term();
1098
	} else if (sscanf(buf, "l %s", tname) == 1) {
1099
	    load_term();
1100
	} else if (sscanf(buf, "d %s", capname) == 1) {
1101
	    struct name_table_entry const *np = _nc_find_entry(capname,
1102
							       _nc_get_hash_table(FALSE));
1103
1104
	    if (np == NULL)
1105
		(void) printf("No such capability as \"%s\"\n", capname);
1106
	    else {
1107
		switch (np->nte_type) {
1108
		case BOOLEAN:
1109
		    cur_term->type.Booleans[np->nte_index] = FALSE;
1110
		    (void)
1111
			printf("Boolean capability `%s' (%d) turned off.\n",
1112
			       np->nte_name, np->nte_index);
1113
		    break;
1114
1115
		case NUMBER:
1116
		    cur_term->type.Numbers[np->nte_index] = ABSENT_NUMERIC;
1117
		    (void) printf("Number capability `%s' (%d) set to -1.\n",
1118
				  np->nte_name, np->nte_index);
1119
		    break;
1120
1121
		case STRING:
1122
		    cur_term->type.Strings[np->nte_index] = ABSENT_STRING;
1123
		    (void) printf("String capability `%s' (%d) deleted.\n",
1124
				  np->nte_name, np->nte_index);
1125
		    break;
1126
		}
1127
	    }
1128
	} else if (buf[0] == 'i') {
1129
	    dump_init((char *) NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE);
1130
	    dump_entry(&cur_term->type, FALSE, TRUE, 0, 0);
1131
	    putchar('\n');
1132
	} else if (buf[0] == 'o') {
1133
	    if (_nc_optimize_enable & OPTIMIZE_MVCUR) {
1134
		_nc_optimize_enable &= ~OPTIMIZE_MVCUR;
1135
		(void) puts("Optimization is now off.");
1136
	    } else {
1137
		_nc_optimize_enable |= OPTIMIZE_MVCUR;
1138
		(void) puts("Optimization is now on.");
1139
	    }
1140
	}
1141
	/*
1142
	 * You can use the `t' test to profile and tune the movement
1143
	 * optimizer.  Use iteration values in three digits or more.
1144
	 * At above 5000 iterations the profile timing averages are stable
1145
	 * to within a millisecond or three.
1146
	 *
1147
	 * The `overhead' field of the report will help you pick a
1148
	 * COMPUTE_OVERHEAD figure appropriate for your processor and
1149
	 * expected line speed.  The `total estimated time' is
1150
	 * computation time plus a character-transmission time
1151
	 * estimate computed from the number of transmits and the baud
1152
	 * rate.
1153
	 *
1154
	 * Use this together with the `o' command to get a read on the
1155
	 * optimizer's effectiveness.  Compare the total estimated times
1156
	 * for `t' runs of the same length in both optimized and un-optimized
1157
	 * modes.  As long as the optimized times are less, the optimizer
1158
	 * is winning.
1159
	 */
1160
	else if (sscanf(buf, "t %d", &n) == 1) {
1161
	    float cumtime = 0.0, perchar;
1162
	    int speeds[] =
1163
	    {2400, 9600, 14400, 19200, 28800, 38400, 0};
1164
1165
	    srand((unsigned) (getpid() + time((time_t *) 0)));
1166
	    profiling = TRUE;
1167
	    xmits = 0;
1168
	    for (i = 0; i < n; i++) {
1169
		/*
1170
		 * This does a move test between two random locations,
1171
		 * Random moves probably short-change the optimizer,
1172
		 * which will work better on the short moves probably
1173
		 * typical of doupdate()'s usage pattern.  Still,
1174
		 * until we have better data...
1175
		 */
1176
#ifdef FIND_COREDUMP
1177
		int from_y = roll(lines);
1178
		int to_y = roll(lines);
1179
		int from_x = roll(columns);
1180
		int to_x = roll(columns);
1181
1182
		printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x);
1183
		mvcur(from_y, from_x, to_y, to_x);
1184
#else
1185
		mvcur(roll(lines), roll(columns), roll(lines), roll(columns));
1186
#endif /* FIND_COREDUMP */
1187
		if (diff)
1188
		    cumtime += diff;
1189
	    }
1190
	    profiling = FALSE;
1191
1192
	    /*
1193
	     * Average milliseconds per character optimization time.
1194
	     * This is the key figure to watch when tuning the optimizer.
1195
	     */
1196
	    perchar = cumtime / n;
1197
1198
	    (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n",
1199
			  n, xmits, (int) cumtime, perchar);
1200
1201
	    for (i = 0; speeds[i]; i++) {
1202
		/*
1203
		 * Total estimated time for the moves, computation and
1204
		 * transmission both. Transmission time is an estimate
1205
		 * assuming 9 bits/char, 8 bits + 1 stop bit.
1206
		 */
1207
		float totalest = cumtime + xmits * 9 * 1e6 / speeds[i];
1208
1209
		/*
1210
		 * Per-character optimization overhead in character transmits
1211
		 * at the current speed.  Round this to the nearest integer
1212
		 * to figure COMPUTE_OVERHEAD for the speed.
1213
		 */
1214
		float overhead = speeds[i] * perchar / 1e6;
1215
1216
		(void)
1217
		    printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n",
1218
			   speeds[i], overhead, totalest);
1219
	    }
1220
	} else if (buf[0] == 'c') {
1221
	    (void) printf("char padding: %d\n", SP->_char_padding);
1222
	    (void) printf("cr cost: %d\n", SP->_cr_cost);
1223
	    (void) printf("cup cost: %d\n", SP->_cup_cost);
1224
	    (void) printf("home cost: %d\n", SP->_home_cost);
1225
	    (void) printf("ll cost: %d\n", SP->_ll_cost);
1226
#if USE_HARD_TABS
1227
	    (void) printf("ht cost: %d\n", SP->_ht_cost);
1228
	    (void) printf("cbt cost: %d\n", SP->_cbt_cost);
1229
#endif /* USE_HARD_TABS */
1230
	    (void) printf("cub1 cost: %d\n", SP->_cub1_cost);
1231
	    (void) printf("cuf1 cost: %d\n", SP->_cuf1_cost);
1232
	    (void) printf("cud1 cost: %d\n", SP->_cud1_cost);
1233
	    (void) printf("cuu1 cost: %d\n", SP->_cuu1_cost);
1234
	    (void) printf("cub cost: %d\n", SP->_cub_cost);
1235
	    (void) printf("cuf cost: %d\n", SP->_cuf_cost);
1236
	    (void) printf("cud cost: %d\n", SP->_cud_cost);
1237
	    (void) printf("cuu cost: %d\n", SP->_cuu_cost);
1238
	    (void) printf("hpa cost: %d\n", SP->_hpa_cost);
1239
	    (void) printf("vpa cost: %d\n", SP->_vpa_cost);
1240
	} else if (buf[0] == 'x' || buf[0] == 'q')
1241
	    break;
1242
	else
1243
	    (void) puts("Invalid command.");
1244
    }
1245
1246
    (void) fputs("rmcup:", stdout);
1247
    _nc_mvcur_wrap();
1248
    putchar('\n');
1249
1250
    return (0);
1251
}
1252
1253
#endif /* MAIN */
1254
1255
/* lib_mvcur.c ends here */