119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: vs_smap.c,v 10.31 2011/02/26 13:56:21 skimo Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <limits.h>
2219304Speter#include <stdio.h>
2319304Speter#include <stdlib.h>
2419304Speter#include <string.h>
2519304Speter
2619304Speter#include "../common/common.h"
2719304Speter#include "vi.h"
2819304Speter
2919304Speterstatic int	vs_deleteln __P((SCR *, int));
3019304Speterstatic int	vs_insertln __P((SCR *, int));
3119304Speterstatic int	vs_sm_delete __P((SCR *, recno_t));
3219304Speterstatic int	vs_sm_down __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
3319304Speterstatic int	vs_sm_erase __P((SCR *));
3419304Speterstatic int	vs_sm_insert __P((SCR *, recno_t));
3519304Speterstatic int	vs_sm_reset __P((SCR *, recno_t));
3619304Speterstatic int	vs_sm_up __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
3719304Speter
3819304Speter/*
3919304Speter * vs_change --
4019304Speter *	Make a change to the screen.
4119304Speter *
4219304Speter * PUBLIC: int vs_change __P((SCR *, recno_t, lnop_t));
4319304Speter */
4419304Speterint
45254225Spetervs_change(SCR *sp, recno_t lno, lnop_t op)
4619304Speter{
4719304Speter	VI_PRIVATE *vip;
4819304Speter	SMAP *p;
4919304Speter	size_t cnt, oldy, oldx;
5019304Speter
5119304Speter	vip = VIP(sp);
5219304Speter
5319304Speter	/*
5419304Speter	 * XXX
5519304Speter	 * Very nasty special case.  The historic vi code displays a single
5619304Speter	 * space (or a '$' if the list option is set) for the first line in
5719304Speter	 * an "empty" file.  If we "insert" a line, that line gets scrolled
5819304Speter	 * down, not repainted, so it's incorrect when we refresh the screen.
5919304Speter	 * The vi text input functions detect it explicitly and don't insert
6019304Speter	 * a new line.
6119304Speter	 *
6219304Speter	 * Check for line #2 before going to the end of the file.
6319304Speter	 */
64254225Speter	if (((op == LINE_APPEND && lno == 0) ||
65254225Speter	    (op == LINE_INSERT && lno == 1)) &&
6619304Speter	    !db_exist(sp, 2)) {
6719304Speter		lno = 1;
6819304Speter		op = LINE_RESET;
6919304Speter	}
7019304Speter
7119304Speter	/* Appending is the same as inserting, if the line is incremented. */
7219304Speter	if (op == LINE_APPEND) {
7319304Speter		++lno;
7419304Speter		op = LINE_INSERT;
7519304Speter	}
7619304Speter
7719304Speter	/* Ignore the change if the line is after the map. */
7819304Speter	if (lno > TMAP->lno)
7919304Speter		return (0);
8019304Speter
8119304Speter	/*
8219304Speter	 * If the line is before the map, and it's a decrement, decrement
8319304Speter	 * the map.  If it's an increment, increment the map.  Otherwise,
8419304Speter	 * ignore it.
8519304Speter	 */
8619304Speter	if (lno < HMAP->lno) {
8719304Speter		switch (op) {
8819304Speter		case LINE_APPEND:
8919304Speter			abort();
9019304Speter			/* NOTREACHED */
9119304Speter		case LINE_DELETE:
9219304Speter			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
9319304Speter				--p->lno;
9419304Speter			if (sp->lno >= lno)
9519304Speter				--sp->lno;
9619304Speter			F_SET(vip, VIP_N_RENUMBER);
9719304Speter			break;
9819304Speter		case LINE_INSERT:
9919304Speter			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
10019304Speter				++p->lno;
10119304Speter			if (sp->lno >= lno)
10219304Speter				++sp->lno;
10319304Speter			F_SET(vip, VIP_N_RENUMBER);
10419304Speter			break;
10519304Speter		case LINE_RESET:
10619304Speter			break;
10719304Speter		}
10819304Speter		return (0);
10919304Speter	}
11019304Speter
11119304Speter	F_SET(vip, VIP_N_REFRESH);
11219304Speter
11319304Speter	/*
11419304Speter	 * Invalidate the line size cache, and invalidate the cursor if it's
11519304Speter	 * on this line,
11619304Speter	 */
11719304Speter	VI_SCR_CFLUSH(vip);
11819304Speter	if (sp->lno == lno)
11919304Speter		F_SET(vip, VIP_CUR_INVALID);
12019304Speter
12119304Speter	/*
12219304Speter	 * If ex modifies the screen after ex output is already on the screen
12319304Speter	 * or if we've switched into ex canonical mode, don't touch it -- we'll
12419304Speter	 * get scrolling wrong, at best.
12519304Speter	 */
12619304Speter	if (!F_ISSET(sp, SC_TINPUT_INFO) &&
12719304Speter	    (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
12819304Speter		F_SET(vip, VIP_N_EX_REDRAW);
12919304Speter		return (0);
13019304Speter	}
13119304Speter
13219304Speter	/* Save and restore the cursor for these routines. */
13319304Speter	(void)sp->gp->scr_cursor(sp, &oldy, &oldx);
13419304Speter
13519304Speter	switch (op) {
13619304Speter	case LINE_DELETE:
13719304Speter		if (vs_sm_delete(sp, lno))
13819304Speter			return (1);
139254225Speter		if (sp->lno > lno)
140254225Speter			--sp->lno;
14119304Speter		F_SET(vip, VIP_N_RENUMBER);
14219304Speter		break;
14319304Speter	case LINE_INSERT:
14419304Speter		if (vs_sm_insert(sp, lno))
14519304Speter			return (1);
146254225Speter		if (sp->lno > lno)
147254225Speter			++sp->lno;
14819304Speter		F_SET(vip, VIP_N_RENUMBER);
14919304Speter		break;
15019304Speter	case LINE_RESET:
15119304Speter		if (vs_sm_reset(sp, lno))
15219304Speter			return (1);
15319304Speter		break;
15419304Speter	default:
15519304Speter		abort();
15619304Speter	}
15719304Speter
15819304Speter	(void)sp->gp->scr_move(sp, oldy, oldx);
15919304Speter	return (0);
16019304Speter}
16119304Speter
16219304Speter/*
16319304Speter * vs_sm_fill --
16419304Speter *	Fill in the screen map, placing the specified line at the
16519304Speter *	right position.  There isn't any way to tell if an SMAP
16619304Speter *	entry has been filled in, so this routine had better be
16719304Speter *	called with P_FILL set before anything else is done.
16819304Speter *
16919304Speter * !!!
17019304Speter * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
17119304Speter * slot is already filled in, P_BOTTOM means that the TMAP slot is
17219304Speter * already filled in, and we just finish up the job.
17319304Speter *
17419304Speter * PUBLIC: int vs_sm_fill __P((SCR *, recno_t, pos_t));
17519304Speter */
17619304Speterint
177254225Spetervs_sm_fill(SCR *sp, recno_t lno, pos_t pos)
17819304Speter{
17919304Speter	SMAP *p, tmp;
18019304Speter	size_t cnt;
18119304Speter
18219304Speter	/* Flush all cached information from the SMAP. */
18319304Speter	for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
18419304Speter		SMAP_FLUSH(p);
18519304Speter
18619304Speter	/*
18719304Speter	 * If the map is filled, the screen must be redrawn.
18819304Speter	 *
18919304Speter	 * XXX
19019304Speter	 * This is a bug.  We should try and figure out if the desired line
19119304Speter	 * is already in the map or close by -- scrolling the screen would
19219304Speter	 * be a lot better than redrawing.
19319304Speter	 */
19419304Speter	F_SET(sp, SC_SCR_REDRAW);
19519304Speter
19619304Speter	switch (pos) {
19719304Speter	case P_FILL:
19819304Speter		tmp.lno = 1;
19919304Speter		tmp.coff = 0;
20019304Speter		tmp.soff = 1;
20119304Speter
20219304Speter		/* See if less than half a screen from the top. */
20319304Speter		if (vs_sm_nlines(sp,
20419304Speter		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
20519304Speter			lno = 1;
20619304Speter			goto top;
20719304Speter		}
20819304Speter
20919304Speter		/* See if less than half a screen from the bottom. */
21019304Speter		if (db_last(sp, &tmp.lno))
21119304Speter			return (1);
21219304Speter		tmp.coff = 0;
21319304Speter		tmp.soff = vs_screens(sp, tmp.lno, NULL);
21419304Speter		if (vs_sm_nlines(sp,
21519304Speter		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
21619304Speter			TMAP->lno = tmp.lno;
21719304Speter			TMAP->coff = tmp.coff;
21819304Speter			TMAP->soff = tmp.soff;
21919304Speter			goto bottom;
22019304Speter		}
22119304Speter		goto middle;
22219304Speter	case P_TOP:
22319304Speter		if (lno != OOBLNO) {
22419304Spetertop:			HMAP->lno = lno;
22519304Speter			HMAP->coff = 0;
22619304Speter			HMAP->soff = 1;
227254225Speter		} else {
228254225Speter			/*
229254225Speter			 * If number of lines HMAP->lno (top line) spans
230254225Speter			 * changed due to, say reformatting, and now is
231254225Speter			 * fewer than HMAP->soff, reset so the line is
232254225Speter			 * redrawn at the top of the screen.
233254225Speter			 */
234254225Speter			cnt = vs_screens(sp, HMAP->lno, NULL);
235254225Speter			if (cnt < HMAP->soff)
236254225Speter				HMAP->soff = 1;
23719304Speter		}
23819304Speter		/* If we fail, just punt. */
23919304Speter		for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
24019304Speter			if (vs_sm_next(sp, p, p + 1))
24119304Speter				goto err;
24219304Speter		break;
24319304Speter	case P_MIDDLE:
24419304Speter		/* If we fail, guess that the file is too small. */
24519304Spetermiddle:		p = HMAP + sp->t_rows / 2;
24619304Speter		p->lno = lno;
24719304Speter		p->coff = 0;
24819304Speter		p->soff = 1;
24919304Speter		for (; p > HMAP; --p)
25019304Speter			if (vs_sm_prev(sp, p, p - 1)) {
25119304Speter				lno = 1;
25219304Speter				goto top;
25319304Speter			}
25419304Speter
25519304Speter		/* If we fail, just punt. */
25619304Speter		p = HMAP + sp->t_rows / 2;
25719304Speter		for (; p < TMAP; ++p)
25819304Speter			if (vs_sm_next(sp, p, p + 1))
25919304Speter				goto err;
26019304Speter		break;
26119304Speter	case P_BOTTOM:
26219304Speter		if (lno != OOBLNO) {
26319304Speter			TMAP->lno = lno;
26419304Speter			TMAP->coff = 0;
26519304Speter			TMAP->soff = vs_screens(sp, lno, NULL);
26619304Speter		}
26719304Speter		/* If we fail, guess that the file is too small. */
26819304Speterbottom:		for (p = TMAP; p > HMAP; --p)
26919304Speter			if (vs_sm_prev(sp, p, p - 1)) {
27019304Speter				lno = 1;
27119304Speter				goto top;
27219304Speter			}
27319304Speter		break;
27419304Speter	default:
27519304Speter		abort();
27619304Speter	}
27719304Speter	return (0);
27819304Speter
27919304Speter	/*
28019304Speter	 * Try and put *something* on the screen.  If this fails, we have a
28119304Speter	 * serious hard error.
28219304Speter	 */
28319304Spetererr:	HMAP->lno = 1;
28419304Speter	HMAP->coff = 0;
28519304Speter	HMAP->soff = 1;
28619304Speter	for (p = HMAP; p < TMAP; ++p)
28719304Speter		if (vs_sm_next(sp, p, p + 1))
28819304Speter			return (1);
28919304Speter	return (0);
29019304Speter}
29119304Speter
29219304Speter/*
29319304Speter * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
29419304Speter * screen contains only a single line (whether because the screen is small
29519304Speter * or the line large), it gets fairly exciting.  Skip the fun, set a flag
29619304Speter * so the screen map is refilled and the screen redrawn, and return.  This
29719304Speter * is amazingly slow, but it's not clear that anyone will care.
29819304Speter */
29919304Speter#define	HANDLE_WEIRDNESS(cnt) {						\
30019304Speter	if (cnt >= sp->t_rows) {					\
30119304Speter		F_SET(sp, SC_SCR_REFORMAT);				\
30219304Speter		return (0);						\
30319304Speter	}								\
30419304Speter}
30519304Speter
30619304Speter/*
30719304Speter * vs_sm_delete --
30819304Speter *	Delete a line out of the SMAP.
30919304Speter */
31019304Speterstatic int
311254225Spetervs_sm_delete(SCR *sp, recno_t lno)
31219304Speter{
31319304Speter	SMAP *p, *t;
31419304Speter	size_t cnt_orig;
31519304Speter
31619304Speter	/*
31719304Speter	 * Find the line in the map, and count the number of screen lines
31819304Speter	 * which display any part of the deleted line.
31919304Speter	 */
32019304Speter	for (p = HMAP; p->lno != lno; ++p);
32119304Speter	if (O_ISSET(sp, O_LEFTRIGHT))
32219304Speter		cnt_orig = 1;
32319304Speter	else
32419304Speter		for (cnt_orig = 1, t = p + 1;
32519304Speter		    t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
32619304Speter
32719304Speter	HANDLE_WEIRDNESS(cnt_orig);
32819304Speter
32919304Speter	/* Delete that many lines from the screen. */
33019304Speter	(void)sp->gp->scr_move(sp, p - HMAP, 0);
33119304Speter	if (vs_deleteln(sp, cnt_orig))
33219304Speter		return (1);
33319304Speter
33419304Speter	/* Shift the screen map up. */
33519304Speter	memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
33619304Speter
33719304Speter	/* Decrement the line numbers for the rest of the map. */
33819304Speter	for (t = TMAP - cnt_orig; p <= t; ++p)
33919304Speter		--p->lno;
34019304Speter
34119304Speter	/* Display the new lines. */
34219304Speter	for (p = TMAP - cnt_orig;;) {
34319304Speter		if (p < TMAP && vs_sm_next(sp, p, p + 1))
34419304Speter			return (1);
34519304Speter		/* vs_sm_next() flushed the cache. */
34619304Speter		if (vs_line(sp, ++p, NULL, NULL))
34719304Speter			return (1);
34819304Speter		if (p == TMAP)
34919304Speter			break;
35019304Speter	}
35119304Speter	return (0);
35219304Speter}
35319304Speter
35419304Speter/*
35519304Speter * vs_sm_insert --
35619304Speter *	Insert a line into the SMAP.
35719304Speter */
35819304Speterstatic int
359254225Spetervs_sm_insert(SCR *sp, recno_t lno)
36019304Speter{
36119304Speter	SMAP *p, *t;
36219304Speter	size_t cnt_orig, cnt, coff;
36319304Speter
36419304Speter	/* Save the offset. */
36519304Speter	coff = HMAP->coff;
36619304Speter
36719304Speter	/*
36819304Speter	 * Find the line in the map, find out how many screen lines
36919304Speter	 * needed to display the line.
37019304Speter	 */
37119304Speter	for (p = HMAP; p->lno != lno; ++p);
37219304Speter
37319304Speter	cnt_orig = vs_screens(sp, lno, NULL);
37419304Speter	HANDLE_WEIRDNESS(cnt_orig);
37519304Speter
37619304Speter	/*
37719304Speter	 * The lines left in the screen override the number of screen
37819304Speter	 * lines in the inserted line.
37919304Speter	 */
38019304Speter	cnt = (TMAP - p) + 1;
38119304Speter	if (cnt_orig > cnt)
38219304Speter		cnt_orig = cnt;
38319304Speter
38419304Speter	/* Push down that many lines. */
38519304Speter	(void)sp->gp->scr_move(sp, p - HMAP, 0);
38619304Speter	if (vs_insertln(sp, cnt_orig))
38719304Speter		return (1);
38819304Speter
38919304Speter	/* Shift the screen map down. */
39019304Speter	memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
39119304Speter
39219304Speter	/* Increment the line numbers for the rest of the map. */
39319304Speter	for (t = p + cnt_orig; t <= TMAP; ++t)
39419304Speter		++t->lno;
39519304Speter
39619304Speter	/* Fill in the SMAP for the new lines, and display. */
39719304Speter	for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
39819304Speter		t->lno = lno;
39919304Speter		t->coff = coff;
40019304Speter		t->soff = cnt;
40119304Speter		SMAP_FLUSH(t);
40219304Speter		if (vs_line(sp, t, NULL, NULL))
40319304Speter			return (1);
40419304Speter	}
40519304Speter	return (0);
40619304Speter}
40719304Speter
40819304Speter/*
40919304Speter * vs_sm_reset --
41019304Speter *	Reset a line in the SMAP.
41119304Speter */
41219304Speterstatic int
413254225Spetervs_sm_reset(SCR *sp, recno_t lno)
41419304Speter{
41519304Speter	SMAP *p, *t;
41619304Speter	size_t cnt_orig, cnt_new, cnt, diff;
41719304Speter
41819304Speter	/*
41919304Speter	 * See if the number of on-screen rows taken up by the old display
42019304Speter	 * for the line is the same as the number needed for the new one.
42119304Speter	 * If so, repaint, otherwise do it the hard way.
42219304Speter	 */
42319304Speter	for (p = HMAP; p->lno != lno; ++p);
42419304Speter	if (O_ISSET(sp, O_LEFTRIGHT)) {
42519304Speter		t = p;
42619304Speter		cnt_orig = cnt_new = 1;
42719304Speter	} else {
42819304Speter		for (cnt_orig = 0,
42919304Speter		    t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
43019304Speter		cnt_new = vs_screens(sp, lno, NULL);
43119304Speter	}
43219304Speter
43319304Speter	HANDLE_WEIRDNESS(cnt_orig);
43419304Speter
43519304Speter	if (cnt_orig == cnt_new) {
43619304Speter		do {
43719304Speter			SMAP_FLUSH(p);
43819304Speter			if (vs_line(sp, p, NULL, NULL))
43919304Speter				return (1);
44019304Speter		} while (++p < t);
44119304Speter		return (0);
44219304Speter	}
44319304Speter
44419304Speter	if (cnt_orig < cnt_new) {
44519304Speter		/* Get the difference. */
44619304Speter		diff = cnt_new - cnt_orig;
44719304Speter
44819304Speter		/*
44919304Speter		 * The lines left in the screen override the number of screen
45019304Speter		 * lines in the inserted line.
45119304Speter		 */
45219304Speter		cnt = (TMAP - p) + 1;
45319304Speter		if (diff > cnt)
45419304Speter			diff = cnt;
45519304Speter
45619304Speter		/* If there are any following lines, push them down. */
45719304Speter		if (cnt > 1) {
45819304Speter			(void)sp->gp->scr_move(sp, p - HMAP, 0);
45919304Speter			if (vs_insertln(sp, diff))
46019304Speter				return (1);
46119304Speter
46219304Speter			/* Shift the screen map down. */
46319304Speter			memmove(p + diff, p,
46419304Speter			    (((TMAP - p) - diff) + 1) * sizeof(SMAP));
46519304Speter		}
46619304Speter
46719304Speter		/* Fill in the SMAP for the replaced line, and display. */
46819304Speter		for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
46919304Speter			t->lno = lno;
47019304Speter			t->soff = cnt;
47119304Speter			SMAP_FLUSH(t);
47219304Speter			if (vs_line(sp, t, NULL, NULL))
47319304Speter				return (1);
47419304Speter		}
47519304Speter	} else {
47619304Speter		/* Get the difference. */
47719304Speter		diff = cnt_orig - cnt_new;
47819304Speter
47919304Speter		/* Delete that many lines from the screen. */
48019304Speter		(void)sp->gp->scr_move(sp, p - HMAP, 0);
48119304Speter		if (vs_deleteln(sp, diff))
48219304Speter			return (1);
48319304Speter
48419304Speter		/* Shift the screen map up. */
48519304Speter		memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
48619304Speter
48719304Speter		/* Fill in the SMAP for the replaced line, and display. */
48819304Speter		for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
48919304Speter			t->lno = lno;
49019304Speter			t->soff = cnt;
49119304Speter			SMAP_FLUSH(t);
49219304Speter			if (vs_line(sp, t, NULL, NULL))
49319304Speter				return (1);
49419304Speter		}
49519304Speter
49619304Speter		/* Display the new lines at the bottom of the screen. */
49719304Speter		for (t = TMAP - diff;;) {
49819304Speter			if (t < TMAP && vs_sm_next(sp, t, t + 1))
49919304Speter				return (1);
50019304Speter			/* vs_sm_next() flushed the cache. */
50119304Speter			if (vs_line(sp, ++t, NULL, NULL))
50219304Speter				return (1);
50319304Speter			if (t == TMAP)
50419304Speter				break;
50519304Speter		}
50619304Speter	}
50719304Speter	return (0);
50819304Speter}
50919304Speter
51019304Speter/*
51119304Speter * vs_sm_scroll
51219304Speter *	Scroll the SMAP up/down count logical lines.  Different
51319304Speter *	semantics based on the vi command, *sigh*.
51419304Speter *
51519304Speter * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t));
51619304Speter */
51719304Speterint
518254225Spetervs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd)
51919304Speter{
52019304Speter	SMAP *smp;
52119304Speter
52219304Speter	/*
52319304Speter	 * Invalidate the cursor.  The line is probably going to change,
52419304Speter	 * (although for ^E and ^Y it may not).  In any case, the scroll
52519304Speter	 * routines move the cursor to draw things.
52619304Speter	 */
52719304Speter	F_SET(VIP(sp), VIP_CUR_INVALID);
52819304Speter
52919304Speter	/* Find the cursor in the screen. */
53019304Speter	if (vs_sm_cursor(sp, &smp))
53119304Speter		return (1);
53219304Speter
53319304Speter	switch (scmd) {
53419304Speter	case CNTRL_B:
53519304Speter	case CNTRL_U:
53619304Speter	case CNTRL_Y:
53719304Speter	case Z_CARAT:
53819304Speter		if (vs_sm_down(sp, rp, count, scmd, smp))
53919304Speter			return (1);
54019304Speter		break;
54119304Speter	case CNTRL_D:
54219304Speter	case CNTRL_E:
54319304Speter	case CNTRL_F:
54419304Speter	case Z_PLUS:
54519304Speter		if (vs_sm_up(sp, rp, count, scmd, smp))
54619304Speter			return (1);
54719304Speter		break;
54819304Speter	default:
54919304Speter		abort();
55019304Speter	}
55119304Speter
55219304Speter	/*
55319304Speter	 * !!!
55419304Speter	 * If we're at the start of a line, go for the first non-blank.
55519304Speter	 * This makes it look like the old vi, even though we're moving
55619304Speter	 * around by logical lines, not physical ones.
55719304Speter	 *
55819304Speter	 * XXX
55919304Speter	 * In the presence of a long line, which has more than a screen
56019304Speter	 * width of leading spaces, this code can cause a cursor warp.
56119304Speter	 * Live with it.
56219304Speter	 */
56319304Speter	if (scmd != CNTRL_E && scmd != CNTRL_Y &&
56419304Speter	    rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
56519304Speter		return (1);
56619304Speter
56719304Speter	return (0);
56819304Speter}
56919304Speter
57019304Speter/*
57119304Speter * vs_sm_up --
57219304Speter *	Scroll the SMAP up count logical lines.
57319304Speter */
57419304Speterstatic int
575254225Spetervs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
57619304Speter{
57719304Speter	int cursor_set, echanged, zset;
57819304Speter	SMAP *ssmp, s1, s2;
57919304Speter
58019304Speter	/*
58119304Speter	 * Check to see if movement is possible.
58219304Speter	 *
58319304Speter	 * Get the line after the map.  If that line is a new one (and if
58419304Speter	 * O_LEFTRIGHT option is set, this has to be true), and the next
58519304Speter	 * line doesn't exist, and the cursor doesn't move, or the cursor
58619304Speter	 * isn't even on the screen, or the cursor is already at the last
58719304Speter	 * line in the map, it's an error.  If that test succeeded because
58819304Speter	 * the cursor wasn't at the end of the map, test to see if the map
58919304Speter	 * is mostly empty.
59019304Speter	 */
59119304Speter	if (vs_sm_next(sp, TMAP, &s1))
59219304Speter		return (1);
59319304Speter	if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
59419304Speter		if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
59519304Speter			v_eof(sp, NULL);
59619304Speter			return (1);
59719304Speter		}
59819304Speter		if (vs_sm_next(sp, smp, &s1))
59919304Speter			return (1);
60019304Speter		if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
60119304Speter			v_eof(sp, NULL);
60219304Speter			return (1);
60319304Speter		}
60419304Speter	}
60519304Speter
60619304Speter	/*
60719304Speter	 * Small screens: see vs_refresh.c section 6a.
60819304Speter	 *
60919304Speter	 * If it's a small screen, and the movement isn't larger than a
61019304Speter	 * screen, i.e some context will remain, open up the screen and
61119304Speter	 * display by scrolling.  In this case, the cursor moves down one
61219304Speter	 * line for each line displayed.  Otherwise, erase/compress and
61319304Speter	 * repaint, and move the cursor to the first line in the screen.
61419304Speter	 * Note, the ^F command is always in the latter case, for historical
61519304Speter	 * reasons.
61619304Speter	 */
61719304Speter	cursor_set = 0;
61819304Speter	if (IS_SMALL(sp)) {
61919304Speter		if (count >= sp->t_maxrows || scmd == CNTRL_F) {
62019304Speter			s1 = TMAP[0];
62119304Speter			if (vs_sm_erase(sp))
62219304Speter				return (1);
62319304Speter			for (; count--; s1 = s2) {
62419304Speter				if (vs_sm_next(sp, &s1, &s2))
62519304Speter					return (1);
62619304Speter				if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
62719304Speter					break;
62819304Speter			}
62919304Speter			TMAP[0] = s2;
63019304Speter			if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
63119304Speter				return (1);
63219304Speter			return (vs_sm_position(sp, rp, 0, P_TOP));
63319304Speter		}
63419304Speter		cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
63519304Speter		for (; count &&
63619304Speter		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
63719304Speter			if (vs_sm_next(sp, TMAP, &s1))
63819304Speter				return (1);
63919304Speter			if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
64019304Speter				break;
64119304Speter			*++TMAP = s1;
64219304Speter			/* vs_sm_next() flushed the cache. */
64319304Speter			if (vs_line(sp, TMAP, NULL, NULL))
64419304Speter				return (1);
64519304Speter
64619304Speter			if (!cursor_set)
64719304Speter				++ssmp;
64819304Speter		}
64919304Speter		if (!cursor_set) {
65019304Speter			rp->lno = ssmp->lno;
65119304Speter			rp->cno = ssmp->c_sboff;
65219304Speter		}
65319304Speter		if (count == 0)
65419304Speter			return (0);
65519304Speter	}
65619304Speter
65719304Speter	for (echanged = zset = 0; count; --count) {
65819304Speter		/* Decide what would show up on the screen. */
65919304Speter		if (vs_sm_next(sp, TMAP, &s1))
66019304Speter			return (1);
66119304Speter
66219304Speter		/* If the line doesn't exist, we're done. */
66319304Speter		if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
66419304Speter			break;
66519304Speter
66619304Speter		/* Scroll the screen cursor up one logical line. */
66719304Speter		if (vs_sm_1up(sp))
66819304Speter			return (1);
66919304Speter		switch (scmd) {
67019304Speter		case CNTRL_E:
67119304Speter			if (smp > HMAP)
67219304Speter				--smp;
67319304Speter			else
67419304Speter				echanged = 1;
67519304Speter			break;
67619304Speter		case Z_PLUS:
67719304Speter			if (zset) {
67819304Speter				if (smp > HMAP)
67919304Speter					--smp;
68019304Speter			} else {
68119304Speter				smp = TMAP;
68219304Speter				zset = 1;
68319304Speter			}
68419304Speter			/* FALLTHROUGH */
68519304Speter		default:
68619304Speter			break;
68719304Speter		}
68819304Speter	}
68919304Speter
69019304Speter	if (cursor_set)
69119304Speter		return(0);
69219304Speter
69319304Speter	switch (scmd) {
69419304Speter	case CNTRL_E:
69519304Speter		/*
69619304Speter		 * On a ^E that was forced to change lines, try and keep the
69719304Speter		 * cursor as close as possible to the last position, but also
69819304Speter		 * set it up so that the next "real" movement will return the
69919304Speter		 * cursor to the closest position to the last real movement.
70019304Speter		 */
70119304Speter		if (echanged) {
70219304Speter			rp->lno = smp->lno;
70319304Speter			rp->cno = vs_colpos(sp, smp->lno,
70419304Speter			    (O_ISSET(sp, O_LEFTRIGHT) ?
70519304Speter			    smp->coff : (smp->soff - 1) * sp->cols) +
70619304Speter			    sp->rcm % sp->cols);
70719304Speter		}
70819304Speter		return (0);
70919304Speter	case CNTRL_F:
71019304Speter		/*
71119304Speter		 * If there are more lines, the ^F command is positioned at
71219304Speter		 * the first line of the screen.
71319304Speter		 */
71419304Speter		if (!count) {
71519304Speter			smp = HMAP;
71619304Speter			break;
71719304Speter		}
71819304Speter		/* FALLTHROUGH */
71919304Speter	case CNTRL_D:
72019304Speter		/*
72119304Speter		 * The ^D and ^F commands move the cursor towards EOF
72219304Speter		 * if there are more lines to move.  Check to be sure
72319304Speter		 * the lines actually exist.  (They may not if the
72419304Speter		 * file is smaller than the screen.)
72519304Speter		 */
72619304Speter		for (; count; --count, ++smp)
72719304Speter			if (smp == TMAP || !db_exist(sp, smp[1].lno))
72819304Speter				break;
72919304Speter		break;
73019304Speter	case Z_PLUS:
73119304Speter		 /* The z+ command moves the cursor to the first new line. */
73219304Speter		break;
73319304Speter	default:
73419304Speter		abort();
73519304Speter	}
73619304Speter
73719304Speter	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
73819304Speter		return (1);
73919304Speter	rp->lno = smp->lno;
74090019Ssheldonh	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
74119304Speter	return (0);
74219304Speter}
74319304Speter
74419304Speter/*
74519304Speter * vs_sm_1up --
74619304Speter *	Scroll the SMAP up one.
74719304Speter *
74819304Speter * PUBLIC: int vs_sm_1up __P((SCR *));
74919304Speter */
75019304Speterint
751254225Spetervs_sm_1up(SCR *sp)
75219304Speter{
75319304Speter	/*
75419304Speter	 * Delete the top line of the screen.  Shift the screen map
75519304Speter	 * up and display a new line at the bottom of the screen.
75619304Speter	 */
75719304Speter	(void)sp->gp->scr_move(sp, 0, 0);
75819304Speter	if (vs_deleteln(sp, 1))
75919304Speter		return (1);
76019304Speter
76119304Speter	/* One-line screens can fail. */
76219304Speter	if (IS_ONELINE(sp)) {
76319304Speter		if (vs_sm_next(sp, TMAP, TMAP))
76419304Speter			return (1);
76519304Speter	} else {
76619304Speter		memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
76719304Speter		if (vs_sm_next(sp, TMAP - 1, TMAP))
76819304Speter			return (1);
76919304Speter	}
77019304Speter	/* vs_sm_next() flushed the cache. */
77119304Speter	return (vs_line(sp, TMAP, NULL, NULL));
77219304Speter}
77319304Speter
77419304Speter/*
77519304Speter * vs_deleteln --
77619304Speter *	Delete a line a la curses, make sure to put the information
77719304Speter *	line and other screens back.
77819304Speter */
77919304Speterstatic int
780254225Spetervs_deleteln(SCR *sp, int cnt)
78119304Speter{
78219304Speter	GS *gp;
78319304Speter	size_t oldy, oldx;
78419304Speter
78519304Speter	gp = sp->gp;
786254225Speter
787254225Speter	/* If the screen is vertically split, we can't scroll it. */
788254225Speter	if (IS_VSPLIT(sp)) {
789254225Speter		F_SET(sp, SC_SCR_REDRAW);
790254225Speter		return (0);
791254225Speter	}
792254225Speter
79319304Speter	if (IS_ONELINE(sp))
79419304Speter		(void)gp->scr_clrtoeol(sp);
79519304Speter	else {
79619304Speter		(void)gp->scr_cursor(sp, &oldy, &oldx);
79719304Speter		while (cnt--) {
79819304Speter			(void)gp->scr_deleteln(sp);
79919304Speter			(void)gp->scr_move(sp, LASTLINE(sp), 0);
80019304Speter			(void)gp->scr_insertln(sp);
80119304Speter			(void)gp->scr_move(sp, oldy, oldx);
80219304Speter		}
80319304Speter	}
80419304Speter	return (0);
80519304Speter}
80619304Speter
80719304Speter/*
80819304Speter * vs_sm_down --
80919304Speter *	Scroll the SMAP down count logical lines.
81019304Speter */
81119304Speterstatic int
812254225Spetervs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
81319304Speter{
81419304Speter	SMAP *ssmp, s1, s2;
81519304Speter	int cursor_set, ychanged, zset;
81619304Speter
81719304Speter	/* Check to see if movement is possible. */
81819304Speter	if (HMAP->lno == 1 &&
81919304Speter	    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
82019304Speter	    (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
82119304Speter		v_sof(sp, NULL);
82219304Speter		return (1);
82319304Speter	}
82419304Speter
82519304Speter	/*
82619304Speter	 * Small screens: see vs_refresh.c section 6a.
82719304Speter	 *
82819304Speter	 * If it's a small screen, and the movement isn't larger than a
82919304Speter	 * screen, i.e some context will remain, open up the screen and
83019304Speter	 * display by scrolling.  In this case, the cursor moves up one
83119304Speter	 * line for each line displayed.  Otherwise, erase/compress and
83219304Speter	 * repaint, and move the cursor to the first line in the screen.
83319304Speter	 * Note, the ^B command is always in the latter case, for historical
83419304Speter	 * reasons.
83519304Speter	 */
83619304Speter	cursor_set = scmd == CNTRL_Y;
83719304Speter	if (IS_SMALL(sp)) {
83819304Speter		if (count >= sp->t_maxrows || scmd == CNTRL_B) {
83919304Speter			s1 = HMAP[0];
84019304Speter			if (vs_sm_erase(sp))
84119304Speter				return (1);
84219304Speter			for (; count--; s1 = s2) {
84319304Speter				if (vs_sm_prev(sp, &s1, &s2))
84419304Speter					return (1);
84519304Speter				if (s2.lno == 1 &&
84619304Speter				    (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
84719304Speter					break;
84819304Speter			}
84919304Speter			HMAP[0] = s2;
85019304Speter			if (vs_sm_fill(sp, OOBLNO, P_TOP))
85119304Speter				return (1);
85219304Speter			return (vs_sm_position(sp, rp, 0, P_BOTTOM));
85319304Speter		}
85419304Speter		cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
85519304Speter		for (; count &&
85619304Speter		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
85719304Speter			if (HMAP->lno == 1 &&
85819304Speter			    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
85919304Speter				break;
86019304Speter			++TMAP;
86119304Speter			if (vs_sm_1down(sp))
86219304Speter				return (1);
86319304Speter		}
86419304Speter		if (!cursor_set) {
86519304Speter			rp->lno = ssmp->lno;
86619304Speter			rp->cno = ssmp->c_sboff;
86719304Speter		}
86819304Speter		if (count == 0)
86919304Speter			return (0);
87019304Speter	}
87119304Speter
87219304Speter	for (ychanged = zset = 0; count; --count) {
87319304Speter		/* If the line doesn't exist, we're done. */
87419304Speter		if (HMAP->lno == 1 &&
87519304Speter		    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
87619304Speter			break;
87719304Speter
87819304Speter		/* Scroll the screen and cursor down one logical line. */
87919304Speter		if (vs_sm_1down(sp))
88019304Speter			return (1);
88119304Speter		switch (scmd) {
88219304Speter		case CNTRL_Y:
88319304Speter			if (smp < TMAP)
88419304Speter				++smp;
88519304Speter			else
88619304Speter				ychanged = 1;
88719304Speter			break;
88819304Speter		case Z_CARAT:
88919304Speter			if (zset) {
89019304Speter				if (smp < TMAP)
89119304Speter					++smp;
89219304Speter			} else {
89319304Speter				smp = HMAP;
89419304Speter				zset = 1;
89519304Speter			}
89619304Speter			/* FALLTHROUGH */
89719304Speter		default:
89819304Speter			break;
89919304Speter		}
90019304Speter	}
90119304Speter
90219304Speter	if (scmd != CNTRL_Y && cursor_set)
90319304Speter		return(0);
90419304Speter
90519304Speter	switch (scmd) {
90619304Speter	case CNTRL_B:
90719304Speter		/*
90819304Speter		 * If there are more lines, the ^B command is positioned at
90919304Speter		 * the last line of the screen.  However, the line may not
91019304Speter		 * exist.
91119304Speter		 */
91219304Speter		if (!count) {
91319304Speter			for (smp = TMAP; smp > HMAP; --smp)
91419304Speter				if (db_exist(sp, smp->lno))
91519304Speter					break;
91619304Speter			break;
91719304Speter		}
91819304Speter		/* FALLTHROUGH */
91919304Speter	case CNTRL_U:
92019304Speter		/*
92119304Speter		 * The ^B and ^U commands move the cursor towards SOF
92219304Speter		 * if there are more lines to move.
92319304Speter		 */
92419304Speter		if (count < smp - HMAP)
92519304Speter			smp -= count;
92619304Speter		else
92719304Speter			smp = HMAP;
92819304Speter		break;
92919304Speter	case CNTRL_Y:
93019304Speter		/*
93119304Speter		 * On a ^Y that was forced to change lines, try and keep the
93219304Speter		 * cursor as close as possible to the last position, but also
93319304Speter		 * set it up so that the next "real" movement will return the
93419304Speter		 * cursor to the closest position to the last real movement.
93519304Speter		 */
93619304Speter		if (ychanged) {
93719304Speter			rp->lno = smp->lno;
93819304Speter			rp->cno = vs_colpos(sp, smp->lno,
93919304Speter			    (O_ISSET(sp, O_LEFTRIGHT) ?
94019304Speter			    smp->coff : (smp->soff - 1) * sp->cols) +
94119304Speter			    sp->rcm % sp->cols);
94219304Speter		}
94319304Speter		return (0);
94419304Speter	case Z_CARAT:
94519304Speter		 /* The z^ command moves the cursor to the first new line. */
94619304Speter		break;
94719304Speter	default:
94819304Speter		abort();
94919304Speter	}
95019304Speter
95119304Speter	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
95219304Speter		return (1);
95319304Speter	rp->lno = smp->lno;
95490019Ssheldonh	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
95519304Speter	return (0);
95619304Speter}
95719304Speter
95819304Speter/*
95919304Speter * vs_sm_erase --
96019304Speter *	Erase the small screen area for the scrolling functions.
96119304Speter */
96219304Speterstatic int
963254225Spetervs_sm_erase(SCR *sp)
96419304Speter{
96519304Speter	GS *gp;
96619304Speter
96719304Speter	gp = sp->gp;
96819304Speter	(void)gp->scr_move(sp, LASTLINE(sp), 0);
96919304Speter	(void)gp->scr_clrtoeol(sp);
97019304Speter	for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
97119304Speter		(void)gp->scr_move(sp, TMAP - HMAP, 0);
97219304Speter		(void)gp->scr_clrtoeol(sp);
97319304Speter	}
97419304Speter	return (0);
97519304Speter}
97619304Speter
97719304Speter/*
97819304Speter * vs_sm_1down --
97919304Speter *	Scroll the SMAP down one.
98019304Speter *
98119304Speter * PUBLIC: int vs_sm_1down __P((SCR *));
98219304Speter */
98319304Speterint
984254225Spetervs_sm_1down(SCR *sp)
98519304Speter{
98619304Speter	/*
98719304Speter	 * Insert a line at the top of the screen.  Shift the screen map
98819304Speter	 * down and display a new line at the top of the screen.
98919304Speter	 */
99019304Speter	(void)sp->gp->scr_move(sp, 0, 0);
99119304Speter	if (vs_insertln(sp, 1))
99219304Speter		return (1);
99319304Speter
99419304Speter	/* One-line screens can fail. */
99519304Speter	if (IS_ONELINE(sp)) {
99619304Speter		if (vs_sm_prev(sp, HMAP, HMAP))
99719304Speter			return (1);
99819304Speter	} else {
99919304Speter		memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
100019304Speter		if (vs_sm_prev(sp, HMAP + 1, HMAP))
100119304Speter			return (1);
100219304Speter	}
100319304Speter	/* vs_sm_prev() flushed the cache. */
100419304Speter	return (vs_line(sp, HMAP, NULL, NULL));
100519304Speter}
100619304Speter
100719304Speter/*
100819304Speter * vs_insertln --
100919304Speter *	Insert a line a la curses, make sure to put the information
101019304Speter *	line and other screens back.
101119304Speter */
101219304Speterstatic int
1013254225Spetervs_insertln(SCR *sp, int cnt)
101419304Speter{
101519304Speter	GS *gp;
101619304Speter	size_t oldy, oldx;
101719304Speter
101819304Speter	gp = sp->gp;
1019254225Speter
1020254225Speter	/* If the screen is vertically split, we can't scroll it. */
1021254225Speter	if (IS_VSPLIT(sp)) {
1022254225Speter		F_SET(sp, SC_SCR_REDRAW);
1023254225Speter		return (0);
1024254225Speter	}
1025254225Speter
102619304Speter	if (IS_ONELINE(sp)) {
102719304Speter		(void)gp->scr_move(sp, LASTLINE(sp), 0);
102819304Speter		(void)gp->scr_clrtoeol(sp);
102919304Speter	} else {
103019304Speter		(void)gp->scr_cursor(sp, &oldy, &oldx);
103119304Speter		while (cnt--) {
103219304Speter			(void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
103319304Speter			(void)gp->scr_deleteln(sp);
103419304Speter			(void)gp->scr_move(sp, oldy, oldx);
103519304Speter			(void)gp->scr_insertln(sp);
103619304Speter		}
103719304Speter	}
103819304Speter	return (0);
103919304Speter}
104019304Speter
104119304Speter/*
104219304Speter * vs_sm_next --
104319304Speter *	Fill in the next entry in the SMAP.
104419304Speter *
104519304Speter * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *));
104619304Speter */
104719304Speterint
1048254225Spetervs_sm_next(SCR *sp, SMAP *p, SMAP *t)
104919304Speter{
105019304Speter	size_t lcnt;
105119304Speter
105219304Speter	SMAP_FLUSH(t);
105319304Speter	if (O_ISSET(sp, O_LEFTRIGHT)) {
105419304Speter		t->lno = p->lno + 1;
105519304Speter		t->coff = p->coff;
105619304Speter	} else {
105719304Speter		lcnt = vs_screens(sp, p->lno, NULL);
105819304Speter		if (lcnt == p->soff) {
105919304Speter			t->lno = p->lno + 1;
106019304Speter			t->soff = 1;
106119304Speter		} else {
106219304Speter			t->lno = p->lno;
106319304Speter			t->soff = p->soff + 1;
106419304Speter		}
106519304Speter	}
106619304Speter	return (0);
106719304Speter}
106819304Speter
106919304Speter/*
107019304Speter * vs_sm_prev --
107119304Speter *	Fill in the previous entry in the SMAP.
107219304Speter *
107319304Speter * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
107419304Speter */
107519304Speterint
1076254225Spetervs_sm_prev(SCR *sp, SMAP *p, SMAP *t)
107719304Speter{
107819304Speter	SMAP_FLUSH(t);
107919304Speter	if (O_ISSET(sp, O_LEFTRIGHT)) {
108019304Speter		t->lno = p->lno - 1;
108119304Speter		t->coff = p->coff;
108219304Speter	} else {
108319304Speter		if (p->soff != 1) {
108419304Speter			t->lno = p->lno;
108519304Speter			t->soff = p->soff - 1;
108619304Speter		} else {
108719304Speter			t->lno = p->lno - 1;
108819304Speter			t->soff = vs_screens(sp, t->lno, NULL);
108919304Speter		}
109019304Speter	}
109119304Speter	return (t->lno == 0);
109219304Speter}
109319304Speter
109419304Speter/*
109519304Speter * vs_sm_cursor --
109619304Speter *	Return the SMAP entry referenced by the cursor.
109719304Speter *
109819304Speter * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **));
109919304Speter */
110019304Speterint
1101254225Spetervs_sm_cursor(SCR *sp, SMAP **smpp)
110219304Speter{
110319304Speter	SMAP *p;
110419304Speter
110519304Speter	/* See if the cursor is not in the map. */
110619304Speter	if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
110719304Speter		return (1);
110819304Speter
110919304Speter	/* Find the first occurence of the line. */
111019304Speter	for (p = HMAP; p->lno != sp->lno; ++p);
111119304Speter
111219304Speter	/* Fill in the map information until we find the right line. */
111319304Speter	for (; p <= TMAP; ++p) {
111419304Speter		/* Short lines are common and easy to detect. */
111519304Speter		if (p != TMAP && (p + 1)->lno != p->lno) {
111619304Speter			*smpp = p;
111719304Speter			return (0);
111819304Speter		}
111919304Speter		if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
112019304Speter			return (1);
112119304Speter		if (p->c_eboff >= sp->cno) {
112219304Speter			*smpp = p;
112319304Speter			return (0);
112419304Speter		}
112519304Speter	}
112619304Speter
112719304Speter	/* It was past the end of the map after all. */
112819304Speter	return (1);
112919304Speter}
113019304Speter
113119304Speter/*
113219304Speter * vs_sm_position --
113319304Speter *	Return the line/column of the top, middle or last line on the screen.
113419304Speter *	(The vi H, M and L commands.)  Here because only the screen routines
113519304Speter *	know what's really out there.
113619304Speter *
113719304Speter * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
113819304Speter */
113919304Speterint
1140254225Spetervs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos)
114119304Speter{
114219304Speter	SMAP *smp;
114319304Speter	recno_t last;
114419304Speter
114519304Speter	switch (pos) {
114619304Speter	case P_TOP:
114719304Speter		/*
114819304Speter		 * !!!
114919304Speter		 * Historically, an invalid count to the H command failed.
115019304Speter		 * We do nothing special here, just making sure that H in
115119304Speter		 * an empty screen works.
115219304Speter		 */
115319304Speter		if (cnt > TMAP - HMAP)
115419304Speter			goto sof;
115519304Speter		smp = HMAP + cnt;
115619304Speter		if (cnt && !db_exist(sp, smp->lno)) {
115719304Spetersof:			msgq(sp, M_BERR, "220|Movement past the end-of-screen");
115819304Speter			return (1);
115919304Speter		}
116019304Speter		break;
116119304Speter	case P_MIDDLE:
116219304Speter		/*
116319304Speter		 * !!!
116419304Speter		 * Historically, a count to the M command was ignored.
116519304Speter		 * If the screen isn't filled, find the middle of what's
116619304Speter		 * real and move there.
116719304Speter		 */
116819304Speter		if (!db_exist(sp, TMAP->lno)) {
116919304Speter			if (db_last(sp, &last))
117019304Speter				return (1);
117119304Speter			for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
117219304Speter			if (smp > HMAP)
117319304Speter				smp -= (smp - HMAP) / 2;
117419304Speter		} else
117519304Speter			smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
117619304Speter		break;
117719304Speter	case P_BOTTOM:
117819304Speter		/*
117919304Speter		 * !!!
118019304Speter		 * Historically, an invalid count to the L command failed.
118119304Speter		 * If the screen isn't filled, find the bottom of what's
118219304Speter		 * real and try to offset from there.
118319304Speter		 */
118419304Speter		if (cnt > TMAP - HMAP)
118519304Speter			goto eof;
118619304Speter		smp = TMAP - cnt;
118719304Speter		if (!db_exist(sp, smp->lno)) {
118819304Speter			if (db_last(sp, &last))
118919304Speter				return (1);
119019304Speter			for (; smp->lno > last && smp > HMAP; --smp);
119119304Speter			if (cnt > smp - HMAP) {
119219304Spetereof:				msgq(sp, M_BERR,
119319304Speter			    "221|Movement past the beginning-of-screen");
119419304Speter				return (1);
119519304Speter			}
119619304Speter			smp -= cnt;
119719304Speter		}
119819304Speter		break;
119919304Speter	default:
120019304Speter		abort();
120119304Speter	}
120219304Speter
120319304Speter	/* Make sure that the cached information is valid. */
120419304Speter	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
120519304Speter		return (1);
120619304Speter	rp->lno = smp->lno;
120719304Speter	rp->cno = smp->c_sboff;
120819304Speter
120919304Speter	return (0);
121019304Speter}
121119304Speter
121219304Speter/*
121319304Speter * vs_sm_nlines --
121419304Speter *	Return the number of screen lines from an SMAP entry to the
121519304Speter *	start of some file line, less than a maximum value.
121619304Speter *
121719304Speter * PUBLIC: recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t));
121819304Speter */
121919304Speterrecno_t
1220254225Spetervs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max)
122119304Speter{
122219304Speter	recno_t lno, lcnt;
122319304Speter
122419304Speter	if (O_ISSET(sp, O_LEFTRIGHT))
122519304Speter		return (from_sp->lno > to_lno ?
122619304Speter		    from_sp->lno - to_lno : to_lno - from_sp->lno);
122719304Speter
122819304Speter	if (from_sp->lno == to_lno)
122919304Speter		return (from_sp->soff - 1);
123019304Speter
123119304Speter	if (from_sp->lno > to_lno) {
123219304Speter		lcnt = from_sp->soff - 1;	/* Correct for off-by-one. */
123319304Speter		for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
123419304Speter			lcnt += vs_screens(sp, lno, NULL);
123519304Speter	} else {
123619304Speter		lno = from_sp->lno;
123719304Speter		lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
123819304Speter		for (; ++lno < to_lno && lcnt <= max;)
123919304Speter			lcnt += vs_screens(sp, lno, NULL);
124019304Speter	}
124119304Speter	return (lcnt);
124219304Speter}
1243