xref: /dragonfly/contrib/nvi2/vi/vs_smap.c (revision b1ac2ebb)
1*e0b8e63eSJohn Marino /*-
2*e0b8e63eSJohn Marino  * Copyright (c) 1993, 1994
3*e0b8e63eSJohn Marino  *	The Regents of the University of California.  All rights reserved.
4*e0b8e63eSJohn Marino  * Copyright (c) 1993, 1994, 1995, 1996
5*e0b8e63eSJohn Marino  *	Keith Bostic.  All rights reserved.
6*e0b8e63eSJohn Marino  *
7*e0b8e63eSJohn Marino  * See the LICENSE file for redistribution information.
8*e0b8e63eSJohn Marino  */
9*e0b8e63eSJohn Marino 
10*e0b8e63eSJohn Marino #include "config.h"
11*e0b8e63eSJohn Marino 
12*e0b8e63eSJohn Marino #include <sys/types.h>
13*e0b8e63eSJohn Marino #include <sys/queue.h>
14*e0b8e63eSJohn Marino #include <sys/time.h>
15*e0b8e63eSJohn Marino 
16*e0b8e63eSJohn Marino #include <bitstring.h>
17*e0b8e63eSJohn Marino #include <limits.h>
18*e0b8e63eSJohn Marino #include <stdio.h>
19*e0b8e63eSJohn Marino #include <stdlib.h>
20*e0b8e63eSJohn Marino #include <string.h>
21*e0b8e63eSJohn Marino 
22*e0b8e63eSJohn Marino #include "../common/common.h"
23*e0b8e63eSJohn Marino #include "vi.h"
24*e0b8e63eSJohn Marino 
25*e0b8e63eSJohn Marino static int	vs_deleteln(SCR *, int);
26*e0b8e63eSJohn Marino static int	vs_insertln(SCR *, int);
27*e0b8e63eSJohn Marino static int	vs_sm_delete(SCR *, recno_t);
28*e0b8e63eSJohn Marino static int	vs_sm_down(SCR *, MARK *, recno_t, scroll_t, SMAP *);
29*e0b8e63eSJohn Marino static int	vs_sm_erase(SCR *);
30*e0b8e63eSJohn Marino static int	vs_sm_insert(SCR *, recno_t);
31*e0b8e63eSJohn Marino static int	vs_sm_reset(SCR *, recno_t);
32*e0b8e63eSJohn Marino static int	vs_sm_up(SCR *, MARK *, recno_t, scroll_t, SMAP *);
33*e0b8e63eSJohn Marino 
34*e0b8e63eSJohn Marino /*
35*e0b8e63eSJohn Marino  * vs_change --
36*e0b8e63eSJohn Marino  *	Make a change to the screen.
37*e0b8e63eSJohn Marino  *
38*e0b8e63eSJohn Marino  * PUBLIC: int vs_change(SCR *, recno_t, lnop_t);
39*e0b8e63eSJohn Marino  */
40*e0b8e63eSJohn Marino int
vs_change(SCR * sp,recno_t lno,lnop_t op)41*e0b8e63eSJohn Marino vs_change(SCR *sp, recno_t lno, lnop_t op)
42*e0b8e63eSJohn Marino {
43*e0b8e63eSJohn Marino 	VI_PRIVATE *vip;
44*e0b8e63eSJohn Marino 	SMAP *p;
45*e0b8e63eSJohn Marino 	size_t cnt, oldy, oldx;
46*e0b8e63eSJohn Marino 
47*e0b8e63eSJohn Marino 	vip = VIP(sp);
48*e0b8e63eSJohn Marino 
49*e0b8e63eSJohn Marino 	/*
50*e0b8e63eSJohn Marino 	 * XXX
51*e0b8e63eSJohn Marino 	 * Very nasty special case.  The historic vi code displays a single
52*e0b8e63eSJohn Marino 	 * space (or a '$' if the list option is set) for the first line in
53*e0b8e63eSJohn Marino 	 * an "empty" file.  If we "insert" a line, that line gets scrolled
54*e0b8e63eSJohn Marino 	 * down, not repainted, so it's incorrect when we refresh the screen.
55*e0b8e63eSJohn Marino 	 * The vi text input functions detect it explicitly and don't insert
56*e0b8e63eSJohn Marino 	 * a new line.
57*e0b8e63eSJohn Marino 	 *
58*e0b8e63eSJohn Marino 	 * Check for line #2 before going to the end of the file.
59*e0b8e63eSJohn Marino 	 */
60*e0b8e63eSJohn Marino 	if (((op == LINE_APPEND && lno == 0) ||
61*e0b8e63eSJohn Marino 	    (op == LINE_INSERT && lno == 1)) &&
62*e0b8e63eSJohn Marino 	    !db_exist(sp, 2)) {
63*e0b8e63eSJohn Marino 		lno = 1;
64*e0b8e63eSJohn Marino 		op = LINE_RESET;
65*e0b8e63eSJohn Marino 	}
66*e0b8e63eSJohn Marino 
67*e0b8e63eSJohn Marino 	/* Appending is the same as inserting, if the line is incremented. */
68*e0b8e63eSJohn Marino 	if (op == LINE_APPEND) {
69*e0b8e63eSJohn Marino 		++lno;
70*e0b8e63eSJohn Marino 		op = LINE_INSERT;
71*e0b8e63eSJohn Marino 	}
72*e0b8e63eSJohn Marino 
73*e0b8e63eSJohn Marino 	/* Ignore the change if the line is after the map. */
74*e0b8e63eSJohn Marino 	if (lno > TMAP->lno)
75*e0b8e63eSJohn Marino 		return (0);
76*e0b8e63eSJohn Marino 
77*e0b8e63eSJohn Marino 	/*
78*e0b8e63eSJohn Marino 	 * If the line is before the map, and it's a decrement, decrement
79*e0b8e63eSJohn Marino 	 * the map.  If it's an increment, increment the map.  Otherwise,
80*e0b8e63eSJohn Marino 	 * ignore it.
81*e0b8e63eSJohn Marino 	 */
82*e0b8e63eSJohn Marino 	if (lno < HMAP->lno) {
83*e0b8e63eSJohn Marino 		switch (op) {
84*e0b8e63eSJohn Marino 		case LINE_APPEND:
85*e0b8e63eSJohn Marino 			abort();
86*e0b8e63eSJohn Marino 			/* NOTREACHED */
87*e0b8e63eSJohn Marino 		case LINE_DELETE:
88*e0b8e63eSJohn Marino 			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
89*e0b8e63eSJohn Marino 				--p->lno;
90*e0b8e63eSJohn Marino 			if (sp->lno >= lno)
91*e0b8e63eSJohn Marino 				--sp->lno;
92*e0b8e63eSJohn Marino 			F_SET(vip, VIP_N_RENUMBER);
93*e0b8e63eSJohn Marino 			break;
94*e0b8e63eSJohn Marino 		case LINE_INSERT:
95*e0b8e63eSJohn Marino 			for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
96*e0b8e63eSJohn Marino 				++p->lno;
97*e0b8e63eSJohn Marino 			if (sp->lno >= lno)
98*e0b8e63eSJohn Marino 				++sp->lno;
99*e0b8e63eSJohn Marino 			F_SET(vip, VIP_N_RENUMBER);
100*e0b8e63eSJohn Marino 			break;
101*e0b8e63eSJohn Marino 		case LINE_RESET:
102*e0b8e63eSJohn Marino 			break;
103*e0b8e63eSJohn Marino 		}
104*e0b8e63eSJohn Marino 		return (0);
105*e0b8e63eSJohn Marino 	}
106*e0b8e63eSJohn Marino 
107*e0b8e63eSJohn Marino 	F_SET(vip, VIP_N_REFRESH);
108*e0b8e63eSJohn Marino 
109*e0b8e63eSJohn Marino 	/*
110*e0b8e63eSJohn Marino 	 * Invalidate the line size cache, and invalidate the cursor if it's
111*e0b8e63eSJohn Marino 	 * on this line,
112*e0b8e63eSJohn Marino 	 */
113*e0b8e63eSJohn Marino 	VI_SCR_CFLUSH(vip);
114*e0b8e63eSJohn Marino 	if (sp->lno == lno)
115*e0b8e63eSJohn Marino 		F_SET(vip, VIP_CUR_INVALID);
116*e0b8e63eSJohn Marino 
117*e0b8e63eSJohn Marino 	/*
118*e0b8e63eSJohn Marino 	 * If ex modifies the screen after ex output is already on the screen
119*e0b8e63eSJohn Marino 	 * or if we've switched into ex canonical mode, don't touch it -- we'll
120*e0b8e63eSJohn Marino 	 * get scrolling wrong, at best.
121*e0b8e63eSJohn Marino 	 */
122*e0b8e63eSJohn Marino 	if (!F_ISSET(sp, SC_TINPUT_INFO) &&
123*e0b8e63eSJohn Marino 	    (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
124*e0b8e63eSJohn Marino 		F_SET(vip, VIP_N_EX_REDRAW);
125*e0b8e63eSJohn Marino 		return (0);
126*e0b8e63eSJohn Marino 	}
127*e0b8e63eSJohn Marino 
128*e0b8e63eSJohn Marino 	/* Save and restore the cursor for these routines. */
129*e0b8e63eSJohn Marino 	(void)sp->gp->scr_cursor(sp, &oldy, &oldx);
130*e0b8e63eSJohn Marino 
131*e0b8e63eSJohn Marino 	switch (op) {
132*e0b8e63eSJohn Marino 	case LINE_DELETE:
133*e0b8e63eSJohn Marino 		if (vs_sm_delete(sp, lno))
134*e0b8e63eSJohn Marino 			return (1);
135*e0b8e63eSJohn Marino 		if (sp->lno > lno)
136*e0b8e63eSJohn Marino 			--sp->lno;
137*e0b8e63eSJohn Marino 		F_SET(vip, VIP_N_RENUMBER);
138*e0b8e63eSJohn Marino 		break;
139*e0b8e63eSJohn Marino 	case LINE_INSERT:
140*e0b8e63eSJohn Marino 		if (vs_sm_insert(sp, lno))
141*e0b8e63eSJohn Marino 			return (1);
142*e0b8e63eSJohn Marino 		if (sp->lno > lno)
143*e0b8e63eSJohn Marino 			++sp->lno;
144*e0b8e63eSJohn Marino 		F_SET(vip, VIP_N_RENUMBER);
145*e0b8e63eSJohn Marino 		break;
146*e0b8e63eSJohn Marino 	case LINE_RESET:
147*e0b8e63eSJohn Marino 		if (vs_sm_reset(sp, lno))
148*e0b8e63eSJohn Marino 			return (1);
149*e0b8e63eSJohn Marino 		break;
150*e0b8e63eSJohn Marino 	default:
151*e0b8e63eSJohn Marino 		abort();
152*e0b8e63eSJohn Marino 	}
153*e0b8e63eSJohn Marino 
154*e0b8e63eSJohn Marino 	(void)sp->gp->scr_move(sp, oldy, oldx);
155*e0b8e63eSJohn Marino 	return (0);
156*e0b8e63eSJohn Marino }
157*e0b8e63eSJohn Marino 
158*e0b8e63eSJohn Marino /*
159*e0b8e63eSJohn Marino  * vs_sm_fill --
160*e0b8e63eSJohn Marino  *	Fill in the screen map, placing the specified line at the
161*e0b8e63eSJohn Marino  *	right position.  There isn't any way to tell if an SMAP
162*e0b8e63eSJohn Marino  *	entry has been filled in, so this routine had better be
163*e0b8e63eSJohn Marino  *	called with P_FILL set before anything else is done.
164*e0b8e63eSJohn Marino  *
165*e0b8e63eSJohn Marino  * !!!
166*e0b8e63eSJohn Marino  * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
167*e0b8e63eSJohn Marino  * slot is already filled in, P_BOTTOM means that the TMAP slot is
168*e0b8e63eSJohn Marino  * already filled in, and we just finish up the job.
169*e0b8e63eSJohn Marino  *
170*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_fill(SCR *, recno_t, pos_t);
171*e0b8e63eSJohn Marino  */
172*e0b8e63eSJohn Marino int
vs_sm_fill(SCR * sp,recno_t lno,pos_t pos)173*e0b8e63eSJohn Marino vs_sm_fill(SCR *sp, recno_t lno, pos_t pos)
174*e0b8e63eSJohn Marino {
175*e0b8e63eSJohn Marino 	SMAP *p, tmp;
176*e0b8e63eSJohn Marino 	size_t cnt;
177*e0b8e63eSJohn Marino 
178*e0b8e63eSJohn Marino 	/* Flush all cached information from the SMAP. */
179*e0b8e63eSJohn Marino 	for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
180*e0b8e63eSJohn Marino 		SMAP_FLUSH(p);
181*e0b8e63eSJohn Marino 
182*e0b8e63eSJohn Marino 	/*
183*e0b8e63eSJohn Marino 	 * If the map is filled, the screen must be redrawn.
184*e0b8e63eSJohn Marino 	 *
185*e0b8e63eSJohn Marino 	 * XXX
186*e0b8e63eSJohn Marino 	 * This is a bug.  We should try and figure out if the desired line
187*e0b8e63eSJohn Marino 	 * is already in the map or close by -- scrolling the screen would
188*e0b8e63eSJohn Marino 	 * be a lot better than redrawing.
189*e0b8e63eSJohn Marino 	 */
190*e0b8e63eSJohn Marino 	F_SET(sp, SC_SCR_REDRAW);
191*e0b8e63eSJohn Marino 
192*e0b8e63eSJohn Marino 	switch (pos) {
193*e0b8e63eSJohn Marino 	case P_FILL:
194*e0b8e63eSJohn Marino 		tmp.lno = 1;
195*e0b8e63eSJohn Marino 		tmp.coff = 0;
196*e0b8e63eSJohn Marino 		tmp.soff = 1;
197*e0b8e63eSJohn Marino 
198*e0b8e63eSJohn Marino 		/* See if less than half a screen from the top. */
199*e0b8e63eSJohn Marino 		if (vs_sm_nlines(sp,
200*e0b8e63eSJohn Marino 		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
201*e0b8e63eSJohn Marino 			lno = 1;
202*e0b8e63eSJohn Marino 			goto top;
203*e0b8e63eSJohn Marino 		}
204*e0b8e63eSJohn Marino 
205*e0b8e63eSJohn Marino 		/* See if less than half a screen from the bottom. */
206*e0b8e63eSJohn Marino 		if (db_last(sp, &tmp.lno))
207*e0b8e63eSJohn Marino 			return (1);
208*e0b8e63eSJohn Marino 		tmp.coff = 0;
209*e0b8e63eSJohn Marino 		tmp.soff = vs_screens(sp, tmp.lno, NULL);
210*e0b8e63eSJohn Marino 		if (vs_sm_nlines(sp,
211*e0b8e63eSJohn Marino 		    &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
212*e0b8e63eSJohn Marino 			TMAP->lno = tmp.lno;
213*e0b8e63eSJohn Marino 			TMAP->coff = tmp.coff;
214*e0b8e63eSJohn Marino 			TMAP->soff = tmp.soff;
215*e0b8e63eSJohn Marino 			goto bottom;
216*e0b8e63eSJohn Marino 		}
217*e0b8e63eSJohn Marino 		goto middle;
218*e0b8e63eSJohn Marino 	case P_TOP:
219*e0b8e63eSJohn Marino 		if (lno != OOBLNO) {
220*e0b8e63eSJohn Marino top:			HMAP->lno = lno;
221*e0b8e63eSJohn Marino 			HMAP->coff = 0;
222*e0b8e63eSJohn Marino 			HMAP->soff = 1;
223*e0b8e63eSJohn Marino 		} else {
224*e0b8e63eSJohn Marino 			/*
225*e0b8e63eSJohn Marino 			 * If number of lines HMAP->lno (top line) spans
226*e0b8e63eSJohn Marino 			 * changed due to, say reformatting, and now is
227*e0b8e63eSJohn Marino 			 * fewer than HMAP->soff, reset so the line is
228*e0b8e63eSJohn Marino 			 * redrawn at the top of the screen.
229*e0b8e63eSJohn Marino 			 */
230*e0b8e63eSJohn Marino 			cnt = vs_screens(sp, HMAP->lno, NULL);
231*e0b8e63eSJohn Marino 			if (cnt < HMAP->soff)
232*e0b8e63eSJohn Marino 				HMAP->soff = 1;
233*e0b8e63eSJohn Marino 		}
234*e0b8e63eSJohn Marino 		/* If we fail, just punt. */
235*e0b8e63eSJohn Marino 		for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
236*e0b8e63eSJohn Marino 			if (vs_sm_next(sp, p, p + 1))
237*e0b8e63eSJohn Marino 				goto err;
238*e0b8e63eSJohn Marino 		break;
239*e0b8e63eSJohn Marino 	case P_MIDDLE:
240*e0b8e63eSJohn Marino 		/* If we fail, guess that the file is too small. */
241*e0b8e63eSJohn Marino middle:		p = HMAP + sp->t_rows / 2;
242*e0b8e63eSJohn Marino 		p->lno = lno;
243*e0b8e63eSJohn Marino 		p->coff = 0;
244*e0b8e63eSJohn Marino 		p->soff = 1;
245*e0b8e63eSJohn Marino 		for (; p > HMAP; --p)
246*e0b8e63eSJohn Marino 			if (vs_sm_prev(sp, p, p - 1)) {
247*e0b8e63eSJohn Marino 				lno = 1;
248*e0b8e63eSJohn Marino 				goto top;
249*e0b8e63eSJohn Marino 			}
250*e0b8e63eSJohn Marino 
251*e0b8e63eSJohn Marino 		/* If we fail, just punt. */
252*e0b8e63eSJohn Marino 		p = HMAP + sp->t_rows / 2;
253*e0b8e63eSJohn Marino 		for (; p < TMAP; ++p)
254*e0b8e63eSJohn Marino 			if (vs_sm_next(sp, p, p + 1))
255*e0b8e63eSJohn Marino 				goto err;
256*e0b8e63eSJohn Marino 		break;
257*e0b8e63eSJohn Marino 	case P_BOTTOM:
258*e0b8e63eSJohn Marino 		if (lno != OOBLNO) {
259*e0b8e63eSJohn Marino 			TMAP->lno = lno;
260*e0b8e63eSJohn Marino 			TMAP->coff = 0;
261*e0b8e63eSJohn Marino 			TMAP->soff = vs_screens(sp, lno, NULL);
262*e0b8e63eSJohn Marino 		}
263*e0b8e63eSJohn Marino 		/* If we fail, guess that the file is too small. */
264*e0b8e63eSJohn Marino bottom:		for (p = TMAP; p > HMAP; --p)
265*e0b8e63eSJohn Marino 			if (vs_sm_prev(sp, p, p - 1)) {
266*e0b8e63eSJohn Marino 				lno = 1;
267*e0b8e63eSJohn Marino 				goto top;
268*e0b8e63eSJohn Marino 			}
269*e0b8e63eSJohn Marino 		break;
270*e0b8e63eSJohn Marino 	default:
271*e0b8e63eSJohn Marino 		abort();
272*e0b8e63eSJohn Marino 	}
273*e0b8e63eSJohn Marino 	return (0);
274*e0b8e63eSJohn Marino 
275*e0b8e63eSJohn Marino 	/*
276*e0b8e63eSJohn Marino 	 * Try and put *something* on the screen.  If this fails, we have a
277*e0b8e63eSJohn Marino 	 * serious hard error.
278*e0b8e63eSJohn Marino 	 */
279*e0b8e63eSJohn Marino err:	HMAP->lno = 1;
280*e0b8e63eSJohn Marino 	HMAP->coff = 0;
281*e0b8e63eSJohn Marino 	HMAP->soff = 1;
282*e0b8e63eSJohn Marino 	for (p = HMAP; p < TMAP; ++p)
283*e0b8e63eSJohn Marino 		if (vs_sm_next(sp, p, p + 1))
284*e0b8e63eSJohn Marino 			return (1);
285*e0b8e63eSJohn Marino 	return (0);
286*e0b8e63eSJohn Marino }
287*e0b8e63eSJohn Marino 
288*e0b8e63eSJohn Marino /*
289*e0b8e63eSJohn Marino  * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
290*e0b8e63eSJohn Marino  * screen contains only a single line (whether because the screen is small
291*e0b8e63eSJohn Marino  * or the line large), it gets fairly exciting.  Skip the fun, set a flag
292*e0b8e63eSJohn Marino  * so the screen map is refilled and the screen redrawn, and return.  This
293*e0b8e63eSJohn Marino  * is amazingly slow, but it's not clear that anyone will care.
294*e0b8e63eSJohn Marino  */
295*e0b8e63eSJohn Marino #define	HANDLE_WEIRDNESS(cnt) {						\
296*e0b8e63eSJohn Marino 	if (cnt >= sp->t_rows) {					\
297*e0b8e63eSJohn Marino 		F_SET(sp, SC_SCR_REFORMAT);				\
298*e0b8e63eSJohn Marino 		return (0);						\
299*e0b8e63eSJohn Marino 	}								\
300*e0b8e63eSJohn Marino }
301*e0b8e63eSJohn Marino 
302*e0b8e63eSJohn Marino /*
303*e0b8e63eSJohn Marino  * vs_sm_delete --
304*e0b8e63eSJohn Marino  *	Delete a line out of the SMAP.
305*e0b8e63eSJohn Marino  */
306*e0b8e63eSJohn Marino static int
vs_sm_delete(SCR * sp,recno_t lno)307*e0b8e63eSJohn Marino vs_sm_delete(SCR *sp, recno_t lno)
308*e0b8e63eSJohn Marino {
309*e0b8e63eSJohn Marino 	SMAP *p, *t;
310*e0b8e63eSJohn Marino 	size_t cnt_orig;
311*e0b8e63eSJohn Marino 
312*e0b8e63eSJohn Marino 	/*
313*e0b8e63eSJohn Marino 	 * Find the line in the map, and count the number of screen lines
314*e0b8e63eSJohn Marino 	 * which display any part of the deleted line.
315*e0b8e63eSJohn Marino 	 */
316*e0b8e63eSJohn Marino 	for (p = HMAP; p->lno != lno; ++p);
317*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT))
318*e0b8e63eSJohn Marino 		cnt_orig = 1;
319*e0b8e63eSJohn Marino 	else
320*e0b8e63eSJohn Marino 		for (cnt_orig = 1, t = p + 1;
321*e0b8e63eSJohn Marino 		    t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
322*e0b8e63eSJohn Marino 
323*e0b8e63eSJohn Marino 	HANDLE_WEIRDNESS(cnt_orig);
324*e0b8e63eSJohn Marino 
325*e0b8e63eSJohn Marino 	/* Delete that many lines from the screen. */
326*e0b8e63eSJohn Marino 	(void)sp->gp->scr_move(sp, p - HMAP, 0);
327*e0b8e63eSJohn Marino 	if (vs_deleteln(sp, cnt_orig))
328*e0b8e63eSJohn Marino 		return (1);
329*e0b8e63eSJohn Marino 
330*e0b8e63eSJohn Marino 	/* Shift the screen map up. */
331*e0b8e63eSJohn Marino 	memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
332*e0b8e63eSJohn Marino 
333*e0b8e63eSJohn Marino 	/* Decrement the line numbers for the rest of the map. */
334*e0b8e63eSJohn Marino 	for (t = TMAP - cnt_orig; p <= t; ++p)
335*e0b8e63eSJohn Marino 		--p->lno;
336*e0b8e63eSJohn Marino 
337*e0b8e63eSJohn Marino 	/* Display the new lines. */
338*e0b8e63eSJohn Marino 	for (p = TMAP - cnt_orig;;) {
339*e0b8e63eSJohn Marino 		if (p < TMAP && vs_sm_next(sp, p, p + 1))
340*e0b8e63eSJohn Marino 			return (1);
341*e0b8e63eSJohn Marino 		/* vs_sm_next() flushed the cache. */
342*e0b8e63eSJohn Marino 		if (vs_line(sp, ++p, NULL, NULL))
343*e0b8e63eSJohn Marino 			return (1);
344*e0b8e63eSJohn Marino 		if (p == TMAP)
345*e0b8e63eSJohn Marino 			break;
346*e0b8e63eSJohn Marino 	}
347*e0b8e63eSJohn Marino 	return (0);
348*e0b8e63eSJohn Marino }
349*e0b8e63eSJohn Marino 
350*e0b8e63eSJohn Marino /*
351*e0b8e63eSJohn Marino  * vs_sm_insert --
352*e0b8e63eSJohn Marino  *	Insert a line into the SMAP.
353*e0b8e63eSJohn Marino  */
354*e0b8e63eSJohn Marino static int
vs_sm_insert(SCR * sp,recno_t lno)355*e0b8e63eSJohn Marino vs_sm_insert(SCR *sp, recno_t lno)
356*e0b8e63eSJohn Marino {
357*e0b8e63eSJohn Marino 	SMAP *p, *t;
358*e0b8e63eSJohn Marino 	size_t cnt_orig, cnt, coff;
359*e0b8e63eSJohn Marino 
360*e0b8e63eSJohn Marino 	/* Save the offset. */
361*e0b8e63eSJohn Marino 	coff = HMAP->coff;
362*e0b8e63eSJohn Marino 
363*e0b8e63eSJohn Marino 	/*
364*e0b8e63eSJohn Marino 	 * Find the line in the map, find out how many screen lines
365*e0b8e63eSJohn Marino 	 * needed to display the line.
366*e0b8e63eSJohn Marino 	 */
367*e0b8e63eSJohn Marino 	for (p = HMAP; p->lno != lno; ++p);
368*e0b8e63eSJohn Marino 
369*e0b8e63eSJohn Marino 	cnt_orig = vs_screens(sp, lno, NULL);
370*e0b8e63eSJohn Marino 	HANDLE_WEIRDNESS(cnt_orig);
371*e0b8e63eSJohn Marino 
372*e0b8e63eSJohn Marino 	/*
373*e0b8e63eSJohn Marino 	 * The lines left in the screen override the number of screen
374*e0b8e63eSJohn Marino 	 * lines in the inserted line.
375*e0b8e63eSJohn Marino 	 */
376*e0b8e63eSJohn Marino 	cnt = (TMAP - p) + 1;
377*e0b8e63eSJohn Marino 	if (cnt_orig > cnt)
378*e0b8e63eSJohn Marino 		cnt_orig = cnt;
379*e0b8e63eSJohn Marino 
380*e0b8e63eSJohn Marino 	/* Push down that many lines. */
381*e0b8e63eSJohn Marino 	(void)sp->gp->scr_move(sp, p - HMAP, 0);
382*e0b8e63eSJohn Marino 	if (vs_insertln(sp, cnt_orig))
383*e0b8e63eSJohn Marino 		return (1);
384*e0b8e63eSJohn Marino 
385*e0b8e63eSJohn Marino 	/* Shift the screen map down. */
386*e0b8e63eSJohn Marino 	memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
387*e0b8e63eSJohn Marino 
388*e0b8e63eSJohn Marino 	/* Increment the line numbers for the rest of the map. */
389*e0b8e63eSJohn Marino 	for (t = p + cnt_orig; t <= TMAP; ++t)
390*e0b8e63eSJohn Marino 		++t->lno;
391*e0b8e63eSJohn Marino 
392*e0b8e63eSJohn Marino 	/* Fill in the SMAP for the new lines, and display. */
393*e0b8e63eSJohn Marino 	for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
394*e0b8e63eSJohn Marino 		t->lno = lno;
395*e0b8e63eSJohn Marino 		t->coff = coff;
396*e0b8e63eSJohn Marino 		t->soff = cnt;
397*e0b8e63eSJohn Marino 		SMAP_FLUSH(t);
398*e0b8e63eSJohn Marino 		if (vs_line(sp, t, NULL, NULL))
399*e0b8e63eSJohn Marino 			return (1);
400*e0b8e63eSJohn Marino 	}
401*e0b8e63eSJohn Marino 	return (0);
402*e0b8e63eSJohn Marino }
403*e0b8e63eSJohn Marino 
404*e0b8e63eSJohn Marino /*
405*e0b8e63eSJohn Marino  * vs_sm_reset --
406*e0b8e63eSJohn Marino  *	Reset a line in the SMAP.
407*e0b8e63eSJohn Marino  */
408*e0b8e63eSJohn Marino static int
vs_sm_reset(SCR * sp,recno_t lno)409*e0b8e63eSJohn Marino vs_sm_reset(SCR *sp, recno_t lno)
410*e0b8e63eSJohn Marino {
411*e0b8e63eSJohn Marino 	SMAP *p, *t;
412*e0b8e63eSJohn Marino 	size_t cnt_orig, cnt_new, cnt, diff;
413*e0b8e63eSJohn Marino 
414*e0b8e63eSJohn Marino 	/*
415*e0b8e63eSJohn Marino 	 * See if the number of on-screen rows taken up by the old display
416*e0b8e63eSJohn Marino 	 * for the line is the same as the number needed for the new one.
417*e0b8e63eSJohn Marino 	 * If so, repaint, otherwise do it the hard way.
418*e0b8e63eSJohn Marino 	 */
419*e0b8e63eSJohn Marino 	for (p = HMAP; p->lno != lno; ++p);
420*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT)) {
421*e0b8e63eSJohn Marino 		t = p;
422*e0b8e63eSJohn Marino 		cnt_orig = cnt_new = 1;
423*e0b8e63eSJohn Marino 	} else {
424*e0b8e63eSJohn Marino 		for (cnt_orig = 0,
425*e0b8e63eSJohn Marino 		    t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
426*e0b8e63eSJohn Marino 		cnt_new = vs_screens(sp, lno, NULL);
427*e0b8e63eSJohn Marino 	}
428*e0b8e63eSJohn Marino 
429*e0b8e63eSJohn Marino 	HANDLE_WEIRDNESS(cnt_orig);
430*e0b8e63eSJohn Marino 
431*e0b8e63eSJohn Marino 	if (cnt_orig == cnt_new) {
432*e0b8e63eSJohn Marino 		do {
433*e0b8e63eSJohn Marino 			SMAP_FLUSH(p);
434*e0b8e63eSJohn Marino 			if (vs_line(sp, p, NULL, NULL))
435*e0b8e63eSJohn Marino 				return (1);
436*e0b8e63eSJohn Marino 		} while (++p < t);
437*e0b8e63eSJohn Marino 		return (0);
438*e0b8e63eSJohn Marino 	}
439*e0b8e63eSJohn Marino 
440*e0b8e63eSJohn Marino 	if (cnt_orig < cnt_new) {
441*e0b8e63eSJohn Marino 		/* Get the difference. */
442*e0b8e63eSJohn Marino 		diff = cnt_new - cnt_orig;
443*e0b8e63eSJohn Marino 
444*e0b8e63eSJohn Marino 		/*
445*e0b8e63eSJohn Marino 		 * The lines left in the screen override the number of screen
446*e0b8e63eSJohn Marino 		 * lines in the inserted line.
447*e0b8e63eSJohn Marino 		 */
448*e0b8e63eSJohn Marino 		cnt = (TMAP - p) + 1;
449*e0b8e63eSJohn Marino 		if (diff > cnt)
450*e0b8e63eSJohn Marino 			diff = cnt;
451*e0b8e63eSJohn Marino 
452*e0b8e63eSJohn Marino 		/* If there are any following lines, push them down. */
453*e0b8e63eSJohn Marino 		if (cnt > 1) {
454*e0b8e63eSJohn Marino 			(void)sp->gp->scr_move(sp, p - HMAP, 0);
455*e0b8e63eSJohn Marino 			if (vs_insertln(sp, diff))
456*e0b8e63eSJohn Marino 				return (1);
457*e0b8e63eSJohn Marino 
458*e0b8e63eSJohn Marino 			/* Shift the screen map down. */
459*e0b8e63eSJohn Marino 			memmove(p + diff, p,
460*e0b8e63eSJohn Marino 			    (((TMAP - p) - diff) + 1) * sizeof(SMAP));
461*e0b8e63eSJohn Marino 		}
462*e0b8e63eSJohn Marino 
463*e0b8e63eSJohn Marino 		/* Fill in the SMAP for the replaced line, and display. */
464*e0b8e63eSJohn Marino 		for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
465*e0b8e63eSJohn Marino 			t->lno = lno;
466*e0b8e63eSJohn Marino 			t->soff = cnt;
467*e0b8e63eSJohn Marino 			SMAP_FLUSH(t);
468*e0b8e63eSJohn Marino 			if (vs_line(sp, t, NULL, NULL))
469*e0b8e63eSJohn Marino 				return (1);
470*e0b8e63eSJohn Marino 		}
471*e0b8e63eSJohn Marino 	} else {
472*e0b8e63eSJohn Marino 		/* Get the difference. */
473*e0b8e63eSJohn Marino 		diff = cnt_orig - cnt_new;
474*e0b8e63eSJohn Marino 
475*e0b8e63eSJohn Marino 		/* Delete that many lines from the screen. */
476*e0b8e63eSJohn Marino 		(void)sp->gp->scr_move(sp, p - HMAP, 0);
477*e0b8e63eSJohn Marino 		if (vs_deleteln(sp, diff))
478*e0b8e63eSJohn Marino 			return (1);
479*e0b8e63eSJohn Marino 
480*e0b8e63eSJohn Marino 		/* Shift the screen map up. */
481*e0b8e63eSJohn Marino 		memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
482*e0b8e63eSJohn Marino 
483*e0b8e63eSJohn Marino 		/* Fill in the SMAP for the replaced line, and display. */
484*e0b8e63eSJohn Marino 		for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
485*e0b8e63eSJohn Marino 			t->lno = lno;
486*e0b8e63eSJohn Marino 			t->soff = cnt;
487*e0b8e63eSJohn Marino 			SMAP_FLUSH(t);
488*e0b8e63eSJohn Marino 			if (vs_line(sp, t, NULL, NULL))
489*e0b8e63eSJohn Marino 				return (1);
490*e0b8e63eSJohn Marino 		}
491*e0b8e63eSJohn Marino 
492*e0b8e63eSJohn Marino 		/* Display the new lines at the bottom of the screen. */
493*e0b8e63eSJohn Marino 		for (t = TMAP - diff;;) {
494*e0b8e63eSJohn Marino 			if (t < TMAP && vs_sm_next(sp, t, t + 1))
495*e0b8e63eSJohn Marino 				return (1);
496*e0b8e63eSJohn Marino 			/* vs_sm_next() flushed the cache. */
497*e0b8e63eSJohn Marino 			if (vs_line(sp, ++t, NULL, NULL))
498*e0b8e63eSJohn Marino 				return (1);
499*e0b8e63eSJohn Marino 			if (t == TMAP)
500*e0b8e63eSJohn Marino 				break;
501*e0b8e63eSJohn Marino 		}
502*e0b8e63eSJohn Marino 	}
503*e0b8e63eSJohn Marino 	return (0);
504*e0b8e63eSJohn Marino }
505*e0b8e63eSJohn Marino 
506*e0b8e63eSJohn Marino /*
507*e0b8e63eSJohn Marino  * vs_sm_scroll
508*e0b8e63eSJohn Marino  *	Scroll the SMAP up/down count logical lines.  Different
509*e0b8e63eSJohn Marino  *	semantics based on the vi command, *sigh*.
510*e0b8e63eSJohn Marino  *
511*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_scroll(SCR *, MARK *, recno_t, scroll_t);
512*e0b8e63eSJohn Marino  */
513*e0b8e63eSJohn Marino int
vs_sm_scroll(SCR * sp,MARK * rp,recno_t count,scroll_t scmd)514*e0b8e63eSJohn Marino vs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd)
515*e0b8e63eSJohn Marino {
516*e0b8e63eSJohn Marino 	SMAP *smp;
517*e0b8e63eSJohn Marino 
518*e0b8e63eSJohn Marino 	/*
519*e0b8e63eSJohn Marino 	 * Invalidate the cursor.  The line is probably going to change,
520*e0b8e63eSJohn Marino 	 * (although for ^E and ^Y it may not).  In any case, the scroll
521*e0b8e63eSJohn Marino 	 * routines move the cursor to draw things.
522*e0b8e63eSJohn Marino 	 */
523*e0b8e63eSJohn Marino 	F_SET(VIP(sp), VIP_CUR_INVALID);
524*e0b8e63eSJohn Marino 
525*e0b8e63eSJohn Marino 	/* Find the cursor in the screen. */
526*e0b8e63eSJohn Marino 	if (vs_sm_cursor(sp, &smp))
527*e0b8e63eSJohn Marino 		return (1);
528*e0b8e63eSJohn Marino 
529*e0b8e63eSJohn Marino 	switch (scmd) {
530*e0b8e63eSJohn Marino 	case CNTRL_B:
531*e0b8e63eSJohn Marino 	case CNTRL_U:
532*e0b8e63eSJohn Marino 	case CNTRL_Y:
533*e0b8e63eSJohn Marino 	case Z_CARAT:
534*e0b8e63eSJohn Marino 		if (vs_sm_down(sp, rp, count, scmd, smp))
535*e0b8e63eSJohn Marino 			return (1);
536*e0b8e63eSJohn Marino 		break;
537*e0b8e63eSJohn Marino 	case CNTRL_D:
538*e0b8e63eSJohn Marino 	case CNTRL_E:
539*e0b8e63eSJohn Marino 	case CNTRL_F:
540*e0b8e63eSJohn Marino 	case Z_PLUS:
541*e0b8e63eSJohn Marino 		if (vs_sm_up(sp, rp, count, scmd, smp))
542*e0b8e63eSJohn Marino 			return (1);
543*e0b8e63eSJohn Marino 		break;
544*e0b8e63eSJohn Marino 	default:
545*e0b8e63eSJohn Marino 		abort();
546*e0b8e63eSJohn Marino 	}
547*e0b8e63eSJohn Marino 
548*e0b8e63eSJohn Marino 	/*
549*e0b8e63eSJohn Marino 	 * !!!
550*e0b8e63eSJohn Marino 	 * If we're at the start of a line, go for the first non-blank.
551*e0b8e63eSJohn Marino 	 * This makes it look like the old vi, even though we're moving
552*e0b8e63eSJohn Marino 	 * around by logical lines, not physical ones.
553*e0b8e63eSJohn Marino 	 *
554*e0b8e63eSJohn Marino 	 * XXX
555*e0b8e63eSJohn Marino 	 * In the presence of a long line, which has more than a screen
556*e0b8e63eSJohn Marino 	 * width of leading spaces, this code can cause a cursor warp.
557*e0b8e63eSJohn Marino 	 * Live with it.
558*e0b8e63eSJohn Marino 	 */
559*e0b8e63eSJohn Marino 	if (scmd != CNTRL_E && scmd != CNTRL_Y &&
560*e0b8e63eSJohn Marino 	    rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
561*e0b8e63eSJohn Marino 		return (1);
562*e0b8e63eSJohn Marino 
563*e0b8e63eSJohn Marino 	return (0);
564*e0b8e63eSJohn Marino }
565*e0b8e63eSJohn Marino 
566*e0b8e63eSJohn Marino /*
567*e0b8e63eSJohn Marino  * vs_sm_up --
568*e0b8e63eSJohn Marino  *	Scroll the SMAP up count logical lines.
569*e0b8e63eSJohn Marino  */
570*e0b8e63eSJohn Marino static int
vs_sm_up(SCR * sp,MARK * rp,recno_t count,scroll_t scmd,SMAP * smp)571*e0b8e63eSJohn Marino vs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
572*e0b8e63eSJohn Marino {
573*e0b8e63eSJohn Marino 	int cursor_set, echanged, zset;
574*e0b8e63eSJohn Marino 	SMAP *ssmp, s1, s2;
575*e0b8e63eSJohn Marino 
576*e0b8e63eSJohn Marino 	/*
577*e0b8e63eSJohn Marino 	 * Check to see if movement is possible.
578*e0b8e63eSJohn Marino 	 *
579*e0b8e63eSJohn Marino 	 * Get the line after the map.  If that line is a new one (and if
580*e0b8e63eSJohn Marino 	 * O_LEFTRIGHT option is set, this has to be true), and the next
581*e0b8e63eSJohn Marino 	 * line doesn't exist, and the cursor doesn't move, or the cursor
582*e0b8e63eSJohn Marino 	 * isn't even on the screen, or the cursor is already at the last
583*e0b8e63eSJohn Marino 	 * line in the map, it's an error.  If that test succeeded because
584*e0b8e63eSJohn Marino 	 * the cursor wasn't at the end of the map, test to see if the map
585*e0b8e63eSJohn Marino 	 * is mostly empty.
586*e0b8e63eSJohn Marino 	 */
587*e0b8e63eSJohn Marino 	if (vs_sm_next(sp, TMAP, &s1))
588*e0b8e63eSJohn Marino 		return (1);
589*e0b8e63eSJohn Marino 	if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
590*e0b8e63eSJohn Marino 		if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
591*e0b8e63eSJohn Marino 			v_eof(sp, NULL);
592*e0b8e63eSJohn Marino 			return (1);
593*e0b8e63eSJohn Marino 		}
594*e0b8e63eSJohn Marino 		if (vs_sm_next(sp, smp, &s1))
595*e0b8e63eSJohn Marino 			return (1);
596*e0b8e63eSJohn Marino 		if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
597*e0b8e63eSJohn Marino 			v_eof(sp, NULL);
598*e0b8e63eSJohn Marino 			return (1);
599*e0b8e63eSJohn Marino 		}
600*e0b8e63eSJohn Marino 	}
601*e0b8e63eSJohn Marino 
602*e0b8e63eSJohn Marino 	/*
603*e0b8e63eSJohn Marino 	 * Small screens: see vs_refresh.c section 6a.
604*e0b8e63eSJohn Marino 	 *
605*e0b8e63eSJohn Marino 	 * If it's a small screen, and the movement isn't larger than a
606*e0b8e63eSJohn Marino 	 * screen, i.e some context will remain, open up the screen and
607*e0b8e63eSJohn Marino 	 * display by scrolling.  In this case, the cursor moves down one
608*e0b8e63eSJohn Marino 	 * line for each line displayed.  Otherwise, erase/compress and
609*e0b8e63eSJohn Marino 	 * repaint, and move the cursor to the first line in the screen.
610*e0b8e63eSJohn Marino 	 * Note, the ^F command is always in the latter case, for historical
611*e0b8e63eSJohn Marino 	 * reasons.
612*e0b8e63eSJohn Marino 	 */
613*e0b8e63eSJohn Marino 	cursor_set = 0;
614*e0b8e63eSJohn Marino 	if (IS_SMALL(sp)) {
615*e0b8e63eSJohn Marino 		if (count >= sp->t_maxrows || scmd == CNTRL_F) {
616*e0b8e63eSJohn Marino 			s1 = TMAP[0];
617*e0b8e63eSJohn Marino 			if (vs_sm_erase(sp))
618*e0b8e63eSJohn Marino 				return (1);
619*e0b8e63eSJohn Marino 			for (; count--; s1 = s2) {
620*e0b8e63eSJohn Marino 				if (vs_sm_next(sp, &s1, &s2))
621*e0b8e63eSJohn Marino 					return (1);
622*e0b8e63eSJohn Marino 				if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
623*e0b8e63eSJohn Marino 					break;
624*e0b8e63eSJohn Marino 			}
625*e0b8e63eSJohn Marino 			TMAP[0] = s2;
626*e0b8e63eSJohn Marino 			if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
627*e0b8e63eSJohn Marino 				return (1);
628*e0b8e63eSJohn Marino 			return (vs_sm_position(sp, rp, 0, P_TOP));
629*e0b8e63eSJohn Marino 		}
630*e0b8e63eSJohn Marino 		cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
631*e0b8e63eSJohn Marino 		for (; count &&
632*e0b8e63eSJohn Marino 		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
633*e0b8e63eSJohn Marino 			if (vs_sm_next(sp, TMAP, &s1))
634*e0b8e63eSJohn Marino 				return (1);
635*e0b8e63eSJohn Marino 			if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
636*e0b8e63eSJohn Marino 				break;
637*e0b8e63eSJohn Marino 			*++TMAP = s1;
638*e0b8e63eSJohn Marino 			/* vs_sm_next() flushed the cache. */
639*e0b8e63eSJohn Marino 			if (vs_line(sp, TMAP, NULL, NULL))
640*e0b8e63eSJohn Marino 				return (1);
641*e0b8e63eSJohn Marino 
642*e0b8e63eSJohn Marino 			if (!cursor_set)
643*e0b8e63eSJohn Marino 				++ssmp;
644*e0b8e63eSJohn Marino 		}
645*e0b8e63eSJohn Marino 		if (!cursor_set) {
646*e0b8e63eSJohn Marino 			rp->lno = ssmp->lno;
647*e0b8e63eSJohn Marino 			rp->cno = ssmp->c_sboff;
648*e0b8e63eSJohn Marino 		}
649*e0b8e63eSJohn Marino 		if (count == 0)
650*e0b8e63eSJohn Marino 			return (0);
651*e0b8e63eSJohn Marino 	}
652*e0b8e63eSJohn Marino 
653*e0b8e63eSJohn Marino 	for (echanged = zset = 0; count; --count) {
654*e0b8e63eSJohn Marino 		/* Decide what would show up on the screen. */
655*e0b8e63eSJohn Marino 		if (vs_sm_next(sp, TMAP, &s1))
656*e0b8e63eSJohn Marino 			return (1);
657*e0b8e63eSJohn Marino 
658*e0b8e63eSJohn Marino 		/* If the line doesn't exist, we're done. */
659*e0b8e63eSJohn Marino 		if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
660*e0b8e63eSJohn Marino 			break;
661*e0b8e63eSJohn Marino 
662*e0b8e63eSJohn Marino 		/* Scroll the screen cursor up one logical line. */
663*e0b8e63eSJohn Marino 		if (vs_sm_1up(sp))
664*e0b8e63eSJohn Marino 			return (1);
665*e0b8e63eSJohn Marino 		switch (scmd) {
666*e0b8e63eSJohn Marino 		case CNTRL_E:
667*e0b8e63eSJohn Marino 			if (smp > HMAP)
668*e0b8e63eSJohn Marino 				--smp;
669*e0b8e63eSJohn Marino 			else
670*e0b8e63eSJohn Marino 				echanged = 1;
671*e0b8e63eSJohn Marino 			break;
672*e0b8e63eSJohn Marino 		case Z_PLUS:
673*e0b8e63eSJohn Marino 			if (zset) {
674*e0b8e63eSJohn Marino 				if (smp > HMAP)
675*e0b8e63eSJohn Marino 					--smp;
676*e0b8e63eSJohn Marino 			} else {
677*e0b8e63eSJohn Marino 				smp = TMAP;
678*e0b8e63eSJohn Marino 				zset = 1;
679*e0b8e63eSJohn Marino 			}
680*e0b8e63eSJohn Marino 			/* FALLTHROUGH */
681*e0b8e63eSJohn Marino 		default:
682*e0b8e63eSJohn Marino 			break;
683*e0b8e63eSJohn Marino 		}
684*e0b8e63eSJohn Marino 	}
685*e0b8e63eSJohn Marino 
686*e0b8e63eSJohn Marino 	if (cursor_set)
687*e0b8e63eSJohn Marino 		return(0);
688*e0b8e63eSJohn Marino 
689*e0b8e63eSJohn Marino 	switch (scmd) {
690*e0b8e63eSJohn Marino 	case CNTRL_E:
691*e0b8e63eSJohn Marino 		/*
692*e0b8e63eSJohn Marino 		 * On a ^E that was forced to change lines, try and keep the
693*e0b8e63eSJohn Marino 		 * cursor as close as possible to the last position, but also
694*e0b8e63eSJohn Marino 		 * set it up so that the next "real" movement will return the
695*e0b8e63eSJohn Marino 		 * cursor to the closest position to the last real movement.
696*e0b8e63eSJohn Marino 		 */
697*e0b8e63eSJohn Marino 		if (echanged) {
698*e0b8e63eSJohn Marino 			rp->lno = smp->lno;
699*e0b8e63eSJohn Marino 			rp->cno = vs_colpos(sp, smp->lno,
700*e0b8e63eSJohn Marino 			    (O_ISSET(sp, O_LEFTRIGHT) ?
701*e0b8e63eSJohn Marino 			    smp->coff : (smp->soff - 1) * sp->cols) +
702*e0b8e63eSJohn Marino 			    sp->rcm % sp->cols);
703*e0b8e63eSJohn Marino 		}
704*e0b8e63eSJohn Marino 		return (0);
705*e0b8e63eSJohn Marino 	case CNTRL_F:
706*e0b8e63eSJohn Marino 		/*
707*e0b8e63eSJohn Marino 		 * If there are more lines, the ^F command is positioned at
708*e0b8e63eSJohn Marino 		 * the first line of the screen.
709*e0b8e63eSJohn Marino 		 */
710*e0b8e63eSJohn Marino 		if (!count) {
711*e0b8e63eSJohn Marino 			smp = HMAP;
712*e0b8e63eSJohn Marino 			break;
713*e0b8e63eSJohn Marino 		}
714*e0b8e63eSJohn Marino 		/* FALLTHROUGH */
715*e0b8e63eSJohn Marino 	case CNTRL_D:
716*e0b8e63eSJohn Marino 		/*
717*e0b8e63eSJohn Marino 		 * The ^D and ^F commands move the cursor towards EOF
718*e0b8e63eSJohn Marino 		 * if there are more lines to move.  Check to be sure
719*e0b8e63eSJohn Marino 		 * the lines actually exist.  (They may not if the
720*e0b8e63eSJohn Marino 		 * file is smaller than the screen.)
721*e0b8e63eSJohn Marino 		 */
722*e0b8e63eSJohn Marino 		for (; count; --count, ++smp)
723*e0b8e63eSJohn Marino 			if (smp == TMAP || !db_exist(sp, smp[1].lno))
724*e0b8e63eSJohn Marino 				break;
725*e0b8e63eSJohn Marino 		break;
726*e0b8e63eSJohn Marino 	case Z_PLUS:
727*e0b8e63eSJohn Marino 		 /* The z+ command moves the cursor to the first new line. */
728*e0b8e63eSJohn Marino 		break;
729*e0b8e63eSJohn Marino 	default:
730*e0b8e63eSJohn Marino 		abort();
731*e0b8e63eSJohn Marino 	}
732*e0b8e63eSJohn Marino 
733*e0b8e63eSJohn Marino 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
734*e0b8e63eSJohn Marino 		return (1);
735*e0b8e63eSJohn Marino 	rp->lno = smp->lno;
736*e0b8e63eSJohn Marino 	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
737*e0b8e63eSJohn Marino 	return (0);
738*e0b8e63eSJohn Marino }
739*e0b8e63eSJohn Marino 
740*e0b8e63eSJohn Marino /*
741*e0b8e63eSJohn Marino  * vs_sm_1up --
742*e0b8e63eSJohn Marino  *	Scroll the SMAP up one.
743*e0b8e63eSJohn Marino  *
744*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_1up(SCR *);
745*e0b8e63eSJohn Marino  */
746*e0b8e63eSJohn Marino int
vs_sm_1up(SCR * sp)747*e0b8e63eSJohn Marino vs_sm_1up(SCR *sp)
748*e0b8e63eSJohn Marino {
749*e0b8e63eSJohn Marino 	/*
750*e0b8e63eSJohn Marino 	 * Delete the top line of the screen.  Shift the screen map
751*e0b8e63eSJohn Marino 	 * up and display a new line at the bottom of the screen.
752*e0b8e63eSJohn Marino 	 */
753*e0b8e63eSJohn Marino 	(void)sp->gp->scr_move(sp, 0, 0);
754*e0b8e63eSJohn Marino 	if (vs_deleteln(sp, 1))
755*e0b8e63eSJohn Marino 		return (1);
756*e0b8e63eSJohn Marino 
757*e0b8e63eSJohn Marino 	/* One-line screens can fail. */
758*e0b8e63eSJohn Marino 	if (IS_ONELINE(sp)) {
759*e0b8e63eSJohn Marino 		if (vs_sm_next(sp, TMAP, TMAP))
760*e0b8e63eSJohn Marino 			return (1);
761*e0b8e63eSJohn Marino 	} else {
762*e0b8e63eSJohn Marino 		memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
763*e0b8e63eSJohn Marino 		if (vs_sm_next(sp, TMAP - 1, TMAP))
764*e0b8e63eSJohn Marino 			return (1);
765*e0b8e63eSJohn Marino 	}
766*e0b8e63eSJohn Marino 	/* vs_sm_next() flushed the cache. */
767*e0b8e63eSJohn Marino 	return (vs_line(sp, TMAP, NULL, NULL));
768*e0b8e63eSJohn Marino }
769*e0b8e63eSJohn Marino 
770*e0b8e63eSJohn Marino /*
771*e0b8e63eSJohn Marino  * vs_deleteln --
772*e0b8e63eSJohn Marino  *	Delete a line a la curses, make sure to put the information
773*e0b8e63eSJohn Marino  *	line and other screens back.
774*e0b8e63eSJohn Marino  */
775*e0b8e63eSJohn Marino static int
vs_deleteln(SCR * sp,int cnt)776*e0b8e63eSJohn Marino vs_deleteln(SCR *sp, int cnt)
777*e0b8e63eSJohn Marino {
778*e0b8e63eSJohn Marino 	GS *gp;
779*e0b8e63eSJohn Marino 	size_t oldy, oldx;
780*e0b8e63eSJohn Marino 
781*e0b8e63eSJohn Marino 	gp = sp->gp;
782*e0b8e63eSJohn Marino 
783*e0b8e63eSJohn Marino 	/* If the screen is vertically split, we can't scroll it. */
784*e0b8e63eSJohn Marino 	if (IS_VSPLIT(sp)) {
785*e0b8e63eSJohn Marino 		F_SET(sp, SC_SCR_REDRAW);
786*e0b8e63eSJohn Marino 		return (0);
787*e0b8e63eSJohn Marino 	}
788*e0b8e63eSJohn Marino 
789*e0b8e63eSJohn Marino 	if (IS_ONELINE(sp))
790*e0b8e63eSJohn Marino 		(void)gp->scr_clrtoeol(sp);
791*e0b8e63eSJohn Marino 	else {
792*e0b8e63eSJohn Marino 		(void)gp->scr_cursor(sp, &oldy, &oldx);
793*e0b8e63eSJohn Marino 		while (cnt--) {
794*e0b8e63eSJohn Marino 			(void)gp->scr_deleteln(sp);
795*e0b8e63eSJohn Marino 			(void)gp->scr_move(sp, LASTLINE(sp), 0);
796*e0b8e63eSJohn Marino 			(void)gp->scr_insertln(sp);
797*e0b8e63eSJohn Marino 			(void)gp->scr_move(sp, oldy, oldx);
798*e0b8e63eSJohn Marino 		}
799*e0b8e63eSJohn Marino 	}
800*e0b8e63eSJohn Marino 	return (0);
801*e0b8e63eSJohn Marino }
802*e0b8e63eSJohn Marino 
803*e0b8e63eSJohn Marino /*
804*e0b8e63eSJohn Marino  * vs_sm_down --
805*e0b8e63eSJohn Marino  *	Scroll the SMAP down count logical lines.
806*e0b8e63eSJohn Marino  */
807*e0b8e63eSJohn Marino static int
vs_sm_down(SCR * sp,MARK * rp,recno_t count,scroll_t scmd,SMAP * smp)808*e0b8e63eSJohn Marino vs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
809*e0b8e63eSJohn Marino {
810*e0b8e63eSJohn Marino 	SMAP *ssmp, s1, s2;
811*e0b8e63eSJohn Marino 	int cursor_set, ychanged, zset;
812*e0b8e63eSJohn Marino 
813*e0b8e63eSJohn Marino 	/* Check to see if movement is possible. */
814*e0b8e63eSJohn Marino 	if (HMAP->lno == 1 &&
815*e0b8e63eSJohn Marino 	    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
816*e0b8e63eSJohn Marino 	    (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
817*e0b8e63eSJohn Marino 		v_sof(sp, NULL);
818*e0b8e63eSJohn Marino 		return (1);
819*e0b8e63eSJohn Marino 	}
820*e0b8e63eSJohn Marino 
821*e0b8e63eSJohn Marino 	/*
822*e0b8e63eSJohn Marino 	 * Small screens: see vs_refresh.c section 6a.
823*e0b8e63eSJohn Marino 	 *
824*e0b8e63eSJohn Marino 	 * If it's a small screen, and the movement isn't larger than a
825*e0b8e63eSJohn Marino 	 * screen, i.e some context will remain, open up the screen and
826*e0b8e63eSJohn Marino 	 * display by scrolling.  In this case, the cursor moves up one
827*e0b8e63eSJohn Marino 	 * line for each line displayed.  Otherwise, erase/compress and
828*e0b8e63eSJohn Marino 	 * repaint, and move the cursor to the first line in the screen.
829*e0b8e63eSJohn Marino 	 * Note, the ^B command is always in the latter case, for historical
830*e0b8e63eSJohn Marino 	 * reasons.
831*e0b8e63eSJohn Marino 	 */
832*e0b8e63eSJohn Marino 	cursor_set = scmd == CNTRL_Y;
833*e0b8e63eSJohn Marino 	if (IS_SMALL(sp)) {
834*e0b8e63eSJohn Marino 		if (count >= sp->t_maxrows || scmd == CNTRL_B) {
835*e0b8e63eSJohn Marino 			s1 = HMAP[0];
836*e0b8e63eSJohn Marino 			if (vs_sm_erase(sp))
837*e0b8e63eSJohn Marino 				return (1);
838*e0b8e63eSJohn Marino 			for (; count--; s1 = s2) {
839*e0b8e63eSJohn Marino 				if (vs_sm_prev(sp, &s1, &s2))
840*e0b8e63eSJohn Marino 					return (1);
841*e0b8e63eSJohn Marino 				if (s2.lno == 1 &&
842*e0b8e63eSJohn Marino 				    (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
843*e0b8e63eSJohn Marino 					break;
844*e0b8e63eSJohn Marino 			}
845*e0b8e63eSJohn Marino 			HMAP[0] = s2;
846*e0b8e63eSJohn Marino 			if (vs_sm_fill(sp, OOBLNO, P_TOP))
847*e0b8e63eSJohn Marino 				return (1);
848*e0b8e63eSJohn Marino 			return (vs_sm_position(sp, rp, 0, P_BOTTOM));
849*e0b8e63eSJohn Marino 		}
850*e0b8e63eSJohn Marino 		cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
851*e0b8e63eSJohn Marino 		for (; count &&
852*e0b8e63eSJohn Marino 		    sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
853*e0b8e63eSJohn Marino 			if (HMAP->lno == 1 &&
854*e0b8e63eSJohn Marino 			    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
855*e0b8e63eSJohn Marino 				break;
856*e0b8e63eSJohn Marino 			++TMAP;
857*e0b8e63eSJohn Marino 			if (vs_sm_1down(sp))
858*e0b8e63eSJohn Marino 				return (1);
859*e0b8e63eSJohn Marino 		}
860*e0b8e63eSJohn Marino 		if (!cursor_set) {
861*e0b8e63eSJohn Marino 			rp->lno = ssmp->lno;
862*e0b8e63eSJohn Marino 			rp->cno = ssmp->c_sboff;
863*e0b8e63eSJohn Marino 		}
864*e0b8e63eSJohn Marino 		if (count == 0)
865*e0b8e63eSJohn Marino 			return (0);
866*e0b8e63eSJohn Marino 	}
867*e0b8e63eSJohn Marino 
868*e0b8e63eSJohn Marino 	for (ychanged = zset = 0; count; --count) {
869*e0b8e63eSJohn Marino 		/* If the line doesn't exist, we're done. */
870*e0b8e63eSJohn Marino 		if (HMAP->lno == 1 &&
871*e0b8e63eSJohn Marino 		    (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
872*e0b8e63eSJohn Marino 			break;
873*e0b8e63eSJohn Marino 
874*e0b8e63eSJohn Marino 		/* Scroll the screen and cursor down one logical line. */
875*e0b8e63eSJohn Marino 		if (vs_sm_1down(sp))
876*e0b8e63eSJohn Marino 			return (1);
877*e0b8e63eSJohn Marino 		switch (scmd) {
878*e0b8e63eSJohn Marino 		case CNTRL_Y:
879*e0b8e63eSJohn Marino 			if (smp < TMAP)
880*e0b8e63eSJohn Marino 				++smp;
881*e0b8e63eSJohn Marino 			else
882*e0b8e63eSJohn Marino 				ychanged = 1;
883*e0b8e63eSJohn Marino 			break;
884*e0b8e63eSJohn Marino 		case Z_CARAT:
885*e0b8e63eSJohn Marino 			if (zset) {
886*e0b8e63eSJohn Marino 				if (smp < TMAP)
887*e0b8e63eSJohn Marino 					++smp;
888*e0b8e63eSJohn Marino 			} else {
889*e0b8e63eSJohn Marino 				smp = HMAP;
890*e0b8e63eSJohn Marino 				zset = 1;
891*e0b8e63eSJohn Marino 			}
892*e0b8e63eSJohn Marino 			/* FALLTHROUGH */
893*e0b8e63eSJohn Marino 		default:
894*e0b8e63eSJohn Marino 			break;
895*e0b8e63eSJohn Marino 		}
896*e0b8e63eSJohn Marino 	}
897*e0b8e63eSJohn Marino 
898*e0b8e63eSJohn Marino 	if (scmd != CNTRL_Y && cursor_set)
899*e0b8e63eSJohn Marino 		return(0);
900*e0b8e63eSJohn Marino 
901*e0b8e63eSJohn Marino 	switch (scmd) {
902*e0b8e63eSJohn Marino 	case CNTRL_B:
903*e0b8e63eSJohn Marino 		/*
904*e0b8e63eSJohn Marino 		 * If there are more lines, the ^B command is positioned at
905*e0b8e63eSJohn Marino 		 * the last line of the screen.  However, the line may not
906*e0b8e63eSJohn Marino 		 * exist.
907*e0b8e63eSJohn Marino 		 */
908*e0b8e63eSJohn Marino 		if (!count) {
909*e0b8e63eSJohn Marino 			for (smp = TMAP; smp > HMAP; --smp)
910*e0b8e63eSJohn Marino 				if (db_exist(sp, smp->lno))
911*e0b8e63eSJohn Marino 					break;
912*e0b8e63eSJohn Marino 			break;
913*e0b8e63eSJohn Marino 		}
914*e0b8e63eSJohn Marino 		/* FALLTHROUGH */
915*e0b8e63eSJohn Marino 	case CNTRL_U:
916*e0b8e63eSJohn Marino 		/*
917*e0b8e63eSJohn Marino 		 * The ^B and ^U commands move the cursor towards SOF
918*e0b8e63eSJohn Marino 		 * if there are more lines to move.
919*e0b8e63eSJohn Marino 		 */
920*e0b8e63eSJohn Marino 		if (count < smp - HMAP)
921*e0b8e63eSJohn Marino 			smp -= count;
922*e0b8e63eSJohn Marino 		else
923*e0b8e63eSJohn Marino 			smp = HMAP;
924*e0b8e63eSJohn Marino 		break;
925*e0b8e63eSJohn Marino 	case CNTRL_Y:
926*e0b8e63eSJohn Marino 		/*
927*e0b8e63eSJohn Marino 		 * On a ^Y that was forced to change lines, try and keep the
928*e0b8e63eSJohn Marino 		 * cursor as close as possible to the last position, but also
929*e0b8e63eSJohn Marino 		 * set it up so that the next "real" movement will return the
930*e0b8e63eSJohn Marino 		 * cursor to the closest position to the last real movement.
931*e0b8e63eSJohn Marino 		 */
932*e0b8e63eSJohn Marino 		if (ychanged) {
933*e0b8e63eSJohn Marino 			rp->lno = smp->lno;
934*e0b8e63eSJohn Marino 			rp->cno = vs_colpos(sp, smp->lno,
935*e0b8e63eSJohn Marino 			    (O_ISSET(sp, O_LEFTRIGHT) ?
936*e0b8e63eSJohn Marino 			    smp->coff : (smp->soff - 1) * sp->cols) +
937*e0b8e63eSJohn Marino 			    sp->rcm % sp->cols);
938*e0b8e63eSJohn Marino 		}
939*e0b8e63eSJohn Marino 		return (0);
940*e0b8e63eSJohn Marino 	case Z_CARAT:
941*e0b8e63eSJohn Marino 		 /* The z^ command moves the cursor to the first new line. */
942*e0b8e63eSJohn Marino 		break;
943*e0b8e63eSJohn Marino 	default:
944*e0b8e63eSJohn Marino 		abort();
945*e0b8e63eSJohn Marino 	}
946*e0b8e63eSJohn Marino 
947*e0b8e63eSJohn Marino 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
948*e0b8e63eSJohn Marino 		return (1);
949*e0b8e63eSJohn Marino 	rp->lno = smp->lno;
950*e0b8e63eSJohn Marino 	rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
951*e0b8e63eSJohn Marino 	return (0);
952*e0b8e63eSJohn Marino }
953*e0b8e63eSJohn Marino 
954*e0b8e63eSJohn Marino /*
955*e0b8e63eSJohn Marino  * vs_sm_erase --
956*e0b8e63eSJohn Marino  *	Erase the small screen area for the scrolling functions.
957*e0b8e63eSJohn Marino  */
958*e0b8e63eSJohn Marino static int
vs_sm_erase(SCR * sp)959*e0b8e63eSJohn Marino vs_sm_erase(SCR *sp)
960*e0b8e63eSJohn Marino {
961*e0b8e63eSJohn Marino 	GS *gp;
962*e0b8e63eSJohn Marino 
963*e0b8e63eSJohn Marino 	gp = sp->gp;
964*e0b8e63eSJohn Marino 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
965*e0b8e63eSJohn Marino 	(void)gp->scr_clrtoeol(sp);
966*e0b8e63eSJohn Marino 	for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
967*e0b8e63eSJohn Marino 		(void)gp->scr_move(sp, TMAP - HMAP, 0);
968*e0b8e63eSJohn Marino 		(void)gp->scr_clrtoeol(sp);
969*e0b8e63eSJohn Marino 	}
970*e0b8e63eSJohn Marino 	return (0);
971*e0b8e63eSJohn Marino }
972*e0b8e63eSJohn Marino 
973*e0b8e63eSJohn Marino /*
974*e0b8e63eSJohn Marino  * vs_sm_1down --
975*e0b8e63eSJohn Marino  *	Scroll the SMAP down one.
976*e0b8e63eSJohn Marino  *
977*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_1down(SCR *);
978*e0b8e63eSJohn Marino  */
979*e0b8e63eSJohn Marino int
vs_sm_1down(SCR * sp)980*e0b8e63eSJohn Marino vs_sm_1down(SCR *sp)
981*e0b8e63eSJohn Marino {
982*e0b8e63eSJohn Marino 	/*
983*e0b8e63eSJohn Marino 	 * Insert a line at the top of the screen.  Shift the screen map
984*e0b8e63eSJohn Marino 	 * down and display a new line at the top of the screen.
985*e0b8e63eSJohn Marino 	 */
986*e0b8e63eSJohn Marino 	(void)sp->gp->scr_move(sp, 0, 0);
987*e0b8e63eSJohn Marino 	if (vs_insertln(sp, 1))
988*e0b8e63eSJohn Marino 		return (1);
989*e0b8e63eSJohn Marino 
990*e0b8e63eSJohn Marino 	/* One-line screens can fail. */
991*e0b8e63eSJohn Marino 	if (IS_ONELINE(sp)) {
992*e0b8e63eSJohn Marino 		if (vs_sm_prev(sp, HMAP, HMAP))
993*e0b8e63eSJohn Marino 			return (1);
994*e0b8e63eSJohn Marino 	} else {
995*e0b8e63eSJohn Marino 		memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
996*e0b8e63eSJohn Marino 		if (vs_sm_prev(sp, HMAP + 1, HMAP))
997*e0b8e63eSJohn Marino 			return (1);
998*e0b8e63eSJohn Marino 	}
999*e0b8e63eSJohn Marino 	/* vs_sm_prev() flushed the cache. */
1000*e0b8e63eSJohn Marino 	return (vs_line(sp, HMAP, NULL, NULL));
1001*e0b8e63eSJohn Marino }
1002*e0b8e63eSJohn Marino 
1003*e0b8e63eSJohn Marino /*
1004*e0b8e63eSJohn Marino  * vs_insertln --
1005*e0b8e63eSJohn Marino  *	Insert a line a la curses, make sure to put the information
1006*e0b8e63eSJohn Marino  *	line and other screens back.
1007*e0b8e63eSJohn Marino  */
1008*e0b8e63eSJohn Marino static int
vs_insertln(SCR * sp,int cnt)1009*e0b8e63eSJohn Marino vs_insertln(SCR *sp, int cnt)
1010*e0b8e63eSJohn Marino {
1011*e0b8e63eSJohn Marino 	GS *gp;
1012*e0b8e63eSJohn Marino 	size_t oldy, oldx;
1013*e0b8e63eSJohn Marino 
1014*e0b8e63eSJohn Marino 	gp = sp->gp;
1015*e0b8e63eSJohn Marino 
1016*e0b8e63eSJohn Marino 	/* If the screen is vertically split, we can't scroll it. */
1017*e0b8e63eSJohn Marino 	if (IS_VSPLIT(sp)) {
1018*e0b8e63eSJohn Marino 		F_SET(sp, SC_SCR_REDRAW);
1019*e0b8e63eSJohn Marino 		return (0);
1020*e0b8e63eSJohn Marino 	}
1021*e0b8e63eSJohn Marino 
1022*e0b8e63eSJohn Marino 	if (IS_ONELINE(sp)) {
1023*e0b8e63eSJohn Marino 		(void)gp->scr_move(sp, LASTLINE(sp), 0);
1024*e0b8e63eSJohn Marino 		(void)gp->scr_clrtoeol(sp);
1025*e0b8e63eSJohn Marino 	} else {
1026*e0b8e63eSJohn Marino 		(void)gp->scr_cursor(sp, &oldy, &oldx);
1027*e0b8e63eSJohn Marino 		while (cnt--) {
1028*e0b8e63eSJohn Marino 			(void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
1029*e0b8e63eSJohn Marino 			(void)gp->scr_deleteln(sp);
1030*e0b8e63eSJohn Marino 			(void)gp->scr_move(sp, oldy, oldx);
1031*e0b8e63eSJohn Marino 			(void)gp->scr_insertln(sp);
1032*e0b8e63eSJohn Marino 		}
1033*e0b8e63eSJohn Marino 	}
1034*e0b8e63eSJohn Marino 	return (0);
1035*e0b8e63eSJohn Marino }
1036*e0b8e63eSJohn Marino 
1037*e0b8e63eSJohn Marino /*
1038*e0b8e63eSJohn Marino  * vs_sm_next --
1039*e0b8e63eSJohn Marino  *	Fill in the next entry in the SMAP.
1040*e0b8e63eSJohn Marino  *
1041*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_next(SCR *, SMAP *, SMAP *);
1042*e0b8e63eSJohn Marino  */
1043*e0b8e63eSJohn Marino int
vs_sm_next(SCR * sp,SMAP * p,SMAP * t)1044*e0b8e63eSJohn Marino vs_sm_next(SCR *sp, SMAP *p, SMAP *t)
1045*e0b8e63eSJohn Marino {
1046*e0b8e63eSJohn Marino 	size_t lcnt;
1047*e0b8e63eSJohn Marino 
1048*e0b8e63eSJohn Marino 	SMAP_FLUSH(t);
1049*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT)) {
1050*e0b8e63eSJohn Marino 		t->lno = p->lno + 1;
1051*e0b8e63eSJohn Marino 		t->coff = p->coff;
1052*e0b8e63eSJohn Marino 	} else {
1053*e0b8e63eSJohn Marino 		lcnt = vs_screens(sp, p->lno, NULL);
1054*e0b8e63eSJohn Marino 		if (lcnt == p->soff) {
1055*e0b8e63eSJohn Marino 			t->lno = p->lno + 1;
1056*e0b8e63eSJohn Marino 			t->soff = 1;
1057*e0b8e63eSJohn Marino 		} else {
1058*e0b8e63eSJohn Marino 			t->lno = p->lno;
1059*e0b8e63eSJohn Marino 			t->soff = p->soff + 1;
1060*e0b8e63eSJohn Marino 		}
1061*e0b8e63eSJohn Marino 	}
1062*e0b8e63eSJohn Marino 	return (0);
1063*e0b8e63eSJohn Marino }
1064*e0b8e63eSJohn Marino 
1065*e0b8e63eSJohn Marino /*
1066*e0b8e63eSJohn Marino  * vs_sm_prev --
1067*e0b8e63eSJohn Marino  *	Fill in the previous entry in the SMAP.
1068*e0b8e63eSJohn Marino  *
1069*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_prev(SCR *, SMAP *, SMAP *);
1070*e0b8e63eSJohn Marino  */
1071*e0b8e63eSJohn Marino int
vs_sm_prev(SCR * sp,SMAP * p,SMAP * t)1072*e0b8e63eSJohn Marino vs_sm_prev(SCR *sp, SMAP *p, SMAP *t)
1073*e0b8e63eSJohn Marino {
1074*e0b8e63eSJohn Marino 	SMAP_FLUSH(t);
1075*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT)) {
1076*e0b8e63eSJohn Marino 		t->lno = p->lno - 1;
1077*e0b8e63eSJohn Marino 		t->coff = p->coff;
1078*e0b8e63eSJohn Marino 	} else {
1079*e0b8e63eSJohn Marino 		if (p->soff != 1) {
1080*e0b8e63eSJohn Marino 			t->lno = p->lno;
1081*e0b8e63eSJohn Marino 			t->soff = p->soff - 1;
1082*e0b8e63eSJohn Marino 		} else {
1083*e0b8e63eSJohn Marino 			t->lno = p->lno - 1;
1084*e0b8e63eSJohn Marino 			t->soff = vs_screens(sp, t->lno, NULL);
1085*e0b8e63eSJohn Marino 		}
1086*e0b8e63eSJohn Marino 	}
1087*e0b8e63eSJohn Marino 	return (t->lno == 0);
1088*e0b8e63eSJohn Marino }
1089*e0b8e63eSJohn Marino 
1090*e0b8e63eSJohn Marino /*
1091*e0b8e63eSJohn Marino  * vs_sm_cursor --
1092*e0b8e63eSJohn Marino  *	Return the SMAP entry referenced by the cursor.
1093*e0b8e63eSJohn Marino  *
1094*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_cursor(SCR *, SMAP **);
1095*e0b8e63eSJohn Marino  */
1096*e0b8e63eSJohn Marino int
vs_sm_cursor(SCR * sp,SMAP ** smpp)1097*e0b8e63eSJohn Marino vs_sm_cursor(SCR *sp, SMAP **smpp)
1098*e0b8e63eSJohn Marino {
1099*e0b8e63eSJohn Marino 	SMAP *p;
1100*e0b8e63eSJohn Marino 
1101*e0b8e63eSJohn Marino 	/* See if the cursor is not in the map. */
1102*e0b8e63eSJohn Marino 	if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
1103*e0b8e63eSJohn Marino 		return (1);
1104*e0b8e63eSJohn Marino 
1105*e0b8e63eSJohn Marino 	/* Find the first occurence of the line. */
1106*e0b8e63eSJohn Marino 	for (p = HMAP; p->lno != sp->lno; ++p);
1107*e0b8e63eSJohn Marino 
1108*e0b8e63eSJohn Marino 	/* Fill in the map information until we find the right line. */
1109*e0b8e63eSJohn Marino 	for (; p <= TMAP; ++p) {
1110*e0b8e63eSJohn Marino 		/* Short lines are common and easy to detect. */
1111*e0b8e63eSJohn Marino 		if (p != TMAP && (p + 1)->lno != p->lno) {
1112*e0b8e63eSJohn Marino 			*smpp = p;
1113*e0b8e63eSJohn Marino 			return (0);
1114*e0b8e63eSJohn Marino 		}
1115*e0b8e63eSJohn Marino 		if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
1116*e0b8e63eSJohn Marino 			return (1);
1117*e0b8e63eSJohn Marino 		if (p->c_eboff >= sp->cno) {
1118*e0b8e63eSJohn Marino 			*smpp = p;
1119*e0b8e63eSJohn Marino 			return (0);
1120*e0b8e63eSJohn Marino 		}
1121*e0b8e63eSJohn Marino 	}
1122*e0b8e63eSJohn Marino 
1123*e0b8e63eSJohn Marino 	/* It was past the end of the map after all. */
1124*e0b8e63eSJohn Marino 	return (1);
1125*e0b8e63eSJohn Marino }
1126*e0b8e63eSJohn Marino 
1127*e0b8e63eSJohn Marino /*
1128*e0b8e63eSJohn Marino  * vs_sm_position --
1129*e0b8e63eSJohn Marino  *	Return the line/column of the top, middle or last line on the screen.
1130*e0b8e63eSJohn Marino  *	(The vi H, M and L commands.)  Here because only the screen routines
1131*e0b8e63eSJohn Marino  *	know what's really out there.
1132*e0b8e63eSJohn Marino  *
1133*e0b8e63eSJohn Marino  * PUBLIC: int vs_sm_position(SCR *, MARK *, u_long, pos_t);
1134*e0b8e63eSJohn Marino  */
1135*e0b8e63eSJohn Marino int
vs_sm_position(SCR * sp,MARK * rp,u_long cnt,pos_t pos)1136*e0b8e63eSJohn Marino vs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos)
1137*e0b8e63eSJohn Marino {
1138*e0b8e63eSJohn Marino 	SMAP *smp;
1139*e0b8e63eSJohn Marino 	recno_t last;
1140*e0b8e63eSJohn Marino 
1141*e0b8e63eSJohn Marino 	switch (pos) {
1142*e0b8e63eSJohn Marino 	case P_TOP:
1143*e0b8e63eSJohn Marino 		/*
1144*e0b8e63eSJohn Marino 		 * !!!
1145*e0b8e63eSJohn Marino 		 * Historically, an invalid count to the H command failed.
1146*e0b8e63eSJohn Marino 		 * We do nothing special here, just making sure that H in
1147*e0b8e63eSJohn Marino 		 * an empty screen works.
1148*e0b8e63eSJohn Marino 		 */
1149*e0b8e63eSJohn Marino 		if (cnt > TMAP - HMAP)
1150*e0b8e63eSJohn Marino 			goto sof;
1151*e0b8e63eSJohn Marino 		smp = HMAP + cnt;
1152*e0b8e63eSJohn Marino 		if (cnt && !db_exist(sp, smp->lno)) {
1153*e0b8e63eSJohn Marino sof:			msgq(sp, M_BERR, "220|Movement past the end-of-screen");
1154*e0b8e63eSJohn Marino 			return (1);
1155*e0b8e63eSJohn Marino 		}
1156*e0b8e63eSJohn Marino 		break;
1157*e0b8e63eSJohn Marino 	case P_MIDDLE:
1158*e0b8e63eSJohn Marino 		/*
1159*e0b8e63eSJohn Marino 		 * !!!
1160*e0b8e63eSJohn Marino 		 * Historically, a count to the M command was ignored.
1161*e0b8e63eSJohn Marino 		 * If the screen isn't filled, find the middle of what's
1162*e0b8e63eSJohn Marino 		 * real and move there.
1163*e0b8e63eSJohn Marino 		 */
1164*e0b8e63eSJohn Marino 		if (!db_exist(sp, TMAP->lno)) {
1165*e0b8e63eSJohn Marino 			if (db_last(sp, &last))
1166*e0b8e63eSJohn Marino 				return (1);
1167*e0b8e63eSJohn Marino 			for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
1168*e0b8e63eSJohn Marino 			if (smp > HMAP)
1169*e0b8e63eSJohn Marino 				smp -= (smp - HMAP) / 2;
1170*e0b8e63eSJohn Marino 		} else
1171*e0b8e63eSJohn Marino 			smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
1172*e0b8e63eSJohn Marino 		break;
1173*e0b8e63eSJohn Marino 	case P_BOTTOM:
1174*e0b8e63eSJohn Marino 		/*
1175*e0b8e63eSJohn Marino 		 * !!!
1176*e0b8e63eSJohn Marino 		 * Historically, an invalid count to the L command failed.
1177*e0b8e63eSJohn Marino 		 * If the screen isn't filled, find the bottom of what's
1178*e0b8e63eSJohn Marino 		 * real and try to offset from there.
1179*e0b8e63eSJohn Marino 		 */
1180*e0b8e63eSJohn Marino 		if (cnt > TMAP - HMAP)
1181*e0b8e63eSJohn Marino 			goto eof;
1182*e0b8e63eSJohn Marino 		smp = TMAP - cnt;
1183*e0b8e63eSJohn Marino 		if (!db_exist(sp, smp->lno)) {
1184*e0b8e63eSJohn Marino 			if (db_last(sp, &last))
1185*e0b8e63eSJohn Marino 				return (1);
1186*e0b8e63eSJohn Marino 			for (; smp->lno > last && smp > HMAP; --smp);
1187*e0b8e63eSJohn Marino 			if (cnt > smp - HMAP) {
1188*e0b8e63eSJohn Marino eof:				msgq(sp, M_BERR,
1189*e0b8e63eSJohn Marino 			    "221|Movement past the beginning-of-screen");
1190*e0b8e63eSJohn Marino 				return (1);
1191*e0b8e63eSJohn Marino 			}
1192*e0b8e63eSJohn Marino 			smp -= cnt;
1193*e0b8e63eSJohn Marino 		}
1194*e0b8e63eSJohn Marino 		break;
1195*e0b8e63eSJohn Marino 	default:
1196*e0b8e63eSJohn Marino 		abort();
1197*e0b8e63eSJohn Marino 	}
1198*e0b8e63eSJohn Marino 
1199*e0b8e63eSJohn Marino 	/* Make sure that the cached information is valid. */
1200*e0b8e63eSJohn Marino 	if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
1201*e0b8e63eSJohn Marino 		return (1);
1202*e0b8e63eSJohn Marino 	rp->lno = smp->lno;
1203*e0b8e63eSJohn Marino 	rp->cno = smp->c_sboff;
1204*e0b8e63eSJohn Marino 
1205*e0b8e63eSJohn Marino 	return (0);
1206*e0b8e63eSJohn Marino }
1207*e0b8e63eSJohn Marino 
1208*e0b8e63eSJohn Marino /*
1209*e0b8e63eSJohn Marino  * vs_sm_nlines --
1210*e0b8e63eSJohn Marino  *	Return the number of screen lines from an SMAP entry to the
1211*e0b8e63eSJohn Marino  *	start of some file line, less than a maximum value.
1212*e0b8e63eSJohn Marino  *
1213*e0b8e63eSJohn Marino  * PUBLIC: recno_t vs_sm_nlines(SCR *, SMAP *, recno_t, size_t);
1214*e0b8e63eSJohn Marino  */
1215*e0b8e63eSJohn Marino recno_t
vs_sm_nlines(SCR * sp,SMAP * from_sp,recno_t to_lno,size_t max)1216*e0b8e63eSJohn Marino vs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max)
1217*e0b8e63eSJohn Marino {
1218*e0b8e63eSJohn Marino 	recno_t lno, lcnt;
1219*e0b8e63eSJohn Marino 
1220*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT))
1221*e0b8e63eSJohn Marino 		return (from_sp->lno > to_lno ?
1222*e0b8e63eSJohn Marino 		    from_sp->lno - to_lno : to_lno - from_sp->lno);
1223*e0b8e63eSJohn Marino 
1224*e0b8e63eSJohn Marino 	if (from_sp->lno == to_lno)
1225*e0b8e63eSJohn Marino 		return (from_sp->soff - 1);
1226*e0b8e63eSJohn Marino 
1227*e0b8e63eSJohn Marino 	if (from_sp->lno > to_lno) {
1228*e0b8e63eSJohn Marino 		lcnt = from_sp->soff - 1;	/* Correct for off-by-one. */
1229*e0b8e63eSJohn Marino 		for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
1230*e0b8e63eSJohn Marino 			lcnt += vs_screens(sp, lno, NULL);
1231*e0b8e63eSJohn Marino 	} else {
1232*e0b8e63eSJohn Marino 		lno = from_sp->lno;
1233*e0b8e63eSJohn Marino 		lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
1234*e0b8e63eSJohn Marino 		for (; ++lno < to_lno && lcnt <= max;)
1235*e0b8e63eSJohn Marino 			lcnt += vs_screens(sp, lno, NULL);
1236*e0b8e63eSJohn Marino 	}
1237*e0b8e63eSJohn Marino 	return (lcnt);
1238*e0b8e63eSJohn Marino }
1239