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