1 /* $NetBSD: vs_smap.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
3 * Copyright (c) 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
13 #include <sys/cdefs.h>
16 static const char sccsid
[] = "Id: vs_smap.c,v 10.30 2002/01/19 21:59:07 skimo Exp (Berkeley) Date: 2002/01/19 21:59:07 ";
19 __RCSID("$NetBSD: vs_smap.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
22 #include <sys/types.h>
23 #include <sys/queue.h>
26 #include <bitstring.h>
32 #include "../common/common.h"
35 static int vs_deleteln
__P((SCR
*, int));
36 static int vs_insertln
__P((SCR
*, int));
37 static int vs_sm_delete
__P((SCR
*, db_recno_t
));
38 static int vs_sm_down
__P((SCR
*, MARK
*, db_recno_t
, scroll_t
, SMAP
*));
39 static int vs_sm_erase
__P((SCR
*));
40 static int vs_sm_insert
__P((SCR
*, db_recno_t
));
41 static int vs_sm_reset
__P((SCR
*, db_recno_t
));
42 static int vs_sm_up
__P((SCR
*, MARK
*, db_recno_t
, scroll_t
, SMAP
*));
46 * Make a change to the screen.
48 * PUBLIC: int vs_change __P((SCR *, db_recno_t, lnop_t));
51 vs_change(SCR
*sp
, db_recno_t lno
, lnop_t op
)
55 size_t cnt
, oldy
, oldx
;
61 * Very nasty special case. The historic vi code displays a single
62 * space (or a '$' if the list option is set) for the first line in
63 * an "empty" file. If we "insert" a line, that line gets scrolled
64 * down, not repainted, so it's incorrect when we refresh the screen.
65 * The vi text input functions detect it explicitly and don't insert
68 * Check for line #2 before going to the end of the file.
70 if (((op
== LINE_APPEND
&& lno
== 0) ||
71 (op
== LINE_INSERT
&& lno
== 1)) &&
77 /* Appending is the same as inserting, if the line is incremented. */
78 if (op
== LINE_APPEND
) {
83 /* Ignore the change if the line is after the map. */
88 * If the line is before the map, and it's a decrement, decrement
89 * the map. If it's an increment, increment the map. Otherwise,
92 if (lno
< HMAP
->lno
) {
98 for (p
= HMAP
, cnt
= sp
->t_rows
; cnt
--; ++p
)
102 F_SET(vip
, VIP_N_RENUMBER
);
105 for (p
= HMAP
, cnt
= sp
->t_rows
; cnt
--; ++p
)
109 F_SET(vip
, VIP_N_RENUMBER
);
117 F_SET(vip
, VIP_N_REFRESH
);
120 * Invalidate the line size cache, and invalidate the cursor if it's
125 F_SET(vip
, VIP_CUR_INVALID
);
128 * If ex modifies the screen after ex output is already on the screen
129 * or if we've switched into ex canonical mode, don't touch it -- we'll
130 * get scrolling wrong, at best.
132 if (!F_ISSET(sp
, SC_TINPUT_INFO
) &&
133 (F_ISSET(sp
, SC_SCR_EXWROTE
) || VIP(sp
)->totalcount
> 1)) {
134 F_SET(vip
, VIP_N_EX_REDRAW
);
138 /* Save and restore the cursor for these routines. */
139 (void)sp
->gp
->scr_cursor(sp
, &oldy
, &oldx
);
143 if (vs_sm_delete(sp
, lno
))
147 F_SET(vip
, VIP_N_RENUMBER
);
150 if (vs_sm_insert(sp
, lno
))
154 F_SET(vip
, VIP_N_RENUMBER
);
157 if (vs_sm_reset(sp
, lno
))
164 (void)sp
->gp
->scr_move(sp
, oldy
, oldx
);
170 * Fill in the screen map, placing the specified line at the
171 * right position. There isn't any way to tell if an SMAP
172 * entry has been filled in, so this routine had better be
173 * called with P_FILL set before anything else is done.
176 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
177 * slot is already filled in, P_BOTTOM means that the TMAP slot is
178 * already filled in, and we just finish up the job.
180 * PUBLIC: int vs_sm_fill __P((SCR *, db_recno_t, pos_t));
183 vs_sm_fill(SCR
*sp
, db_recno_t lno
, pos_t pos
)
188 /* Flush all cached information from the SMAP. */
189 for (p
= HMAP
, cnt
= sp
->t_rows
; cnt
--; ++p
)
193 * If the map is filled, the screen must be redrawn.
196 * This is a bug. We should try and figure out if the desired line
197 * is already in the map or close by -- scrolling the screen would
198 * be a lot better than redrawing.
200 F_SET(sp
, SC_SCR_REDRAW
);
208 /* See if less than half a screen from the top. */
210 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
215 /* See if less than half a screen from the bottom. */
216 if (db_last(sp
, &tmp
.lno
))
219 tmp
.soff
= vs_screens(sp
, tmp
.lno
, NULL
);
221 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
223 TMAP
->coff
= tmp
.coff
;
224 TMAP
->soff
= tmp
.soff
;
230 top
: HMAP
->lno
= lno
;
235 * If number of lines HMAP->lno (top line) spans
236 * changed due to, say reformatting, and now is
237 * fewer than HMAP->soff, reset so the line is
238 * redrawn at the top of the screen.
240 cnt
= vs_screens(sp
, HMAP
->lno
, NULL
);
241 if (cnt
< HMAP
->soff
)
244 /* If we fail, just punt. */
245 for (p
= HMAP
, cnt
= sp
->t_rows
; --cnt
; ++p
)
246 if (vs_sm_next(sp
, p
, p
+ 1))
250 /* If we fail, guess that the file is too small. */
251 middle
: p
= HMAP
+ sp
->t_rows
/ 2;
255 for (; p
> HMAP
; --p
)
256 if (vs_sm_prev(sp
, p
, p
- 1)) {
261 /* If we fail, just punt. */
262 p
= HMAP
+ sp
->t_rows
/ 2;
263 for (; p
< TMAP
; ++p
)
264 if (vs_sm_next(sp
, p
, p
+ 1))
271 TMAP
->soff
= vs_screens(sp
, lno
, NULL
);
273 /* If we fail, guess that the file is too small. */
274 bottom
: for (p
= TMAP
; p
> HMAP
; --p
)
275 if (vs_sm_prev(sp
, p
, p
- 1)) {
286 * Try and put *something* on the screen. If this fails, we have a
287 * serious hard error.
292 for (p
= HMAP
; p
< TMAP
; ++p
)
293 if (vs_sm_next(sp
, p
, p
+ 1))
299 * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
300 * screen contains only a single line (whether because the screen is small
301 * or the line large), it gets fairly exciting. Skip the fun, set a flag
302 * so the screen map is refilled and the screen redrawn, and return. This
303 * is amazingly slow, but it's not clear that anyone will care.
305 #define HANDLE_WEIRDNESS(cnt) { \
306 if (cnt >= sp->t_rows) { \
307 F_SET(sp, SC_SCR_REFORMAT); \
314 * Delete a line out of the SMAP.
317 vs_sm_delete(SCR
*sp
, db_recno_t lno
)
323 * Find the line in the map, and count the number of screen lines
324 * which display any part of the deleted line.
326 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
327 if (O_ISSET(sp
, O_LEFTRIGHT
))
330 for (cnt_orig
= 1, t
= p
+ 1;
331 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
333 HANDLE_WEIRDNESS(cnt_orig
);
335 /* Delete that many lines from the screen. */
336 (void)sp
->gp
->scr_move(sp
, p
- HMAP
, 0);
337 if (vs_deleteln(sp
, cnt_orig
))
340 /* Shift the screen map up. */
341 memmove(p
, p
+ cnt_orig
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
343 /* Decrement the line numbers for the rest of the map. */
344 for (t
= TMAP
- cnt_orig
; p
<= t
; ++p
)
347 /* Display the new lines. */
348 for (p
= TMAP
- cnt_orig
;;) {
349 if (p
< TMAP
&& vs_sm_next(sp
, p
, p
+ 1))
351 /* vs_sm_next() flushed the cache. */
352 if (vs_line(sp
, ++p
, NULL
, NULL
))
362 * Insert a line into the SMAP.
365 vs_sm_insert(SCR
*sp
, db_recno_t lno
)
368 size_t cnt_orig
, cnt
, coff
;
370 /* Save the offset. */
374 * Find the line in the map, find out how many screen lines
375 * needed to display the line.
377 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
379 cnt_orig
= vs_screens(sp
, lno
, NULL
);
380 HANDLE_WEIRDNESS(cnt_orig
);
383 * The lines left in the screen override the number of screen
384 * lines in the inserted line.
386 cnt
= (TMAP
- p
) + 1;
390 /* Push down that many lines. */
391 (void)sp
->gp
->scr_move(sp
, p
- HMAP
, 0);
392 if (vs_insertln(sp
, cnt_orig
))
395 /* Shift the screen map down. */
396 memmove(p
+ cnt_orig
, p
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
398 /* Increment the line numbers for the rest of the map. */
399 for (t
= p
+ cnt_orig
; t
<= TMAP
; ++t
)
402 /* Fill in the SMAP for the new lines, and display. */
403 for (cnt
= 1, t
= p
; cnt
<= cnt_orig
; ++t
, ++cnt
) {
408 if (vs_line(sp
, t
, NULL
, NULL
))
416 * Reset a line in the SMAP.
419 vs_sm_reset(SCR
*sp
, db_recno_t lno
)
422 size_t cnt_orig
, cnt_new
, cnt
, diff
;
425 * See if the number of on-screen rows taken up by the old display
426 * for the line is the same as the number needed for the new one.
427 * If so, repaint, otherwise do it the hard way.
429 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
430 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
432 cnt_orig
= cnt_new
= 1;
435 t
= p
; t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
436 cnt_new
= vs_screens(sp
, lno
, NULL
);
439 HANDLE_WEIRDNESS(cnt_orig
);
441 if (cnt_orig
== cnt_new
) {
444 if (vs_line(sp
, p
, NULL
, NULL
))
450 if (cnt_orig
< cnt_new
) {
451 /* Get the difference. */
452 diff
= cnt_new
- cnt_orig
;
455 * The lines left in the screen override the number of screen
456 * lines in the inserted line.
458 cnt
= (TMAP
- p
) + 1;
462 /* If there are any following lines, push them down. */
464 (void)sp
->gp
->scr_move(sp
, p
- HMAP
, 0);
465 if (vs_insertln(sp
, diff
))
468 /* Shift the screen map down. */
470 (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
473 /* Fill in the SMAP for the replaced line, and display. */
474 for (cnt
= 1, t
= p
; cnt_new
-- && t
<= TMAP
; ++t
, ++cnt
) {
478 if (vs_line(sp
, t
, NULL
, NULL
))
482 /* Get the difference. */
483 diff
= cnt_orig
- cnt_new
;
485 /* Delete that many lines from the screen. */
486 (void)sp
->gp
->scr_move(sp
, p
- HMAP
, 0);
487 if (vs_deleteln(sp
, diff
))
490 /* Shift the screen map up. */
491 memmove(p
, p
+ diff
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
493 /* Fill in the SMAP for the replaced line, and display. */
494 for (cnt
= 1, t
= p
; cnt_new
--; ++t
, ++cnt
) {
498 if (vs_line(sp
, t
, NULL
, NULL
))
502 /* Display the new lines at the bottom of the screen. */
503 for (t
= TMAP
- diff
;;) {
504 if (t
< TMAP
&& vs_sm_next(sp
, t
, t
+ 1))
506 /* vs_sm_next() flushed the cache. */
507 if (vs_line(sp
, ++t
, NULL
, NULL
))
518 * Scroll the SMAP up/down count logical lines. Different
519 * semantics based on the vi command, *sigh*.
521 * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, db_recno_t, scroll_t));
524 vs_sm_scroll(SCR
*sp
, MARK
*rp
, db_recno_t count
, scroll_t scmd
)
529 * Invalidate the cursor. The line is probably going to change,
530 * (although for ^E and ^Y it may not). In any case, the scroll
531 * routines move the cursor to draw things.
533 F_SET(VIP(sp
), VIP_CUR_INVALID
);
535 /* Find the cursor in the screen. */
536 if (vs_sm_cursor(sp
, &smp
))
544 if (vs_sm_down(sp
, rp
, count
, scmd
, smp
))
551 if (vs_sm_up(sp
, rp
, count
, scmd
, smp
))
560 * If we're at the start of a line, go for the first non-blank.
561 * This makes it look like the old vi, even though we're moving
562 * around by logical lines, not physical ones.
565 * In the presence of a long line, which has more than a screen
566 * width of leading spaces, this code can cause a cursor warp.
569 if (scmd
!= CNTRL_E
&& scmd
!= CNTRL_Y
&&
570 rp
->cno
== 0 && nonblank(sp
, rp
->lno
, &rp
->cno
))
578 * Scroll the SMAP up count logical lines.
581 vs_sm_up(SCR
*sp
, MARK
*rp
, db_recno_t count
, scroll_t scmd
, SMAP
*smp
)
583 int cursor_set
, echanged
, zset
;
586 /* LSC: -Werror=maybe-uninitialized, with -O3 */
588 #endif /* defined(__minix) */
591 * Check to see if movement is possible.
593 * Get the line after the map. If that line is a new one (and if
594 * O_LEFTRIGHT option is set, this has to be true), and the next
595 * line doesn't exist, and the cursor doesn't move, or the cursor
596 * isn't even on the screen, or the cursor is already at the last
597 * line in the map, it's an error. If that test succeeded because
598 * the cursor wasn't at the end of the map, test to see if the map
601 if (vs_sm_next(sp
, TMAP
, &s1
))
603 if (s1
.lno
> TMAP
->lno
&& !db_exist(sp
, s1
.lno
)) {
604 if (scmd
== CNTRL_E
|| scmd
== Z_PLUS
|| smp
== TMAP
) {
608 if (vs_sm_next(sp
, smp
, &s1
))
610 if (s1
.lno
> smp
->lno
&& !db_exist(sp
, s1
.lno
)) {
617 * Small screens: see vs_refresh.c section 6a.
619 * If it's a small screen, and the movement isn't larger than a
620 * screen, i.e some context will remain, open up the screen and
621 * display by scrolling. In this case, the cursor moves down one
622 * line for each line displayed. Otherwise, erase/compress and
623 * repaint, and move the cursor to the first line in the screen.
624 * Note, the ^F command is always in the latter case, for historical
629 if (count
>= sp
->t_maxrows
|| scmd
== CNTRL_F
) {
633 for (; count
--; s1
= s2
) {
634 if (vs_sm_next(sp
, &s1
, &s2
))
636 if (s2
.lno
!= s1
.lno
&& !db_exist(sp
, s2
.lno
))
640 if (vs_sm_fill(sp
, OOBLNO
, P_BOTTOM
))
642 return (vs_sm_position(sp
, rp
, 0, P_TOP
));
644 cursor_set
= scmd
== CNTRL_E
|| vs_sm_cursor(sp
, &ssmp
);
646 sp
->t_rows
!= sp
->t_maxrows
; --count
, ++sp
->t_rows
) {
647 if (vs_sm_next(sp
, TMAP
, &s1
))
649 if (TMAP
->lno
!= s1
.lno
&& !db_exist(sp
, s1
.lno
))
652 /* vs_sm_next() flushed the cache. */
653 if (vs_line(sp
, TMAP
, NULL
, NULL
))
661 rp
->cno
= ssmp
->c_sboff
;
667 for (echanged
= zset
= 0; count
; --count
) {
668 /* Decide what would show up on the screen. */
669 if (vs_sm_next(sp
, TMAP
, &s1
))
672 /* If the line doesn't exist, we're done. */
673 if (TMAP
->lno
!= s1
.lno
&& !db_exist(sp
, s1
.lno
))
676 /* Scroll the screen cursor up one logical line. */
706 * On a ^E that was forced to change lines, try and keep the
707 * cursor as close as possible to the last position, but also
708 * set it up so that the next "real" movement will return the
709 * cursor to the closest position to the last real movement.
713 rp
->cno
= vs_colpos(sp
, smp
->lno
,
714 (O_ISSET(sp
, O_LEFTRIGHT
) ?
715 smp
->coff
: (smp
->soff
- 1) * sp
->cols
) +
721 * If there are more lines, the ^F command is positioned at
722 * the first line of the screen.
731 * The ^D and ^F commands move the cursor towards EOF
732 * if there are more lines to move. Check to be sure
733 * the lines actually exist. (They may not if the
734 * file is smaller than the screen.)
736 for (; count
; --count
, ++smp
)
737 if (smp
== TMAP
|| !db_exist(sp
, smp
[1].lno
))
741 /* The z+ command moves the cursor to the first new line. */
747 if (!SMAP_CACHE(smp
) && vs_line(sp
, smp
, NULL
, NULL
))
750 rp
->cno
= smp
->c_scoff
== 255 ? 0 : smp
->c_sboff
;
756 * Scroll the SMAP up one.
758 * PUBLIC: int vs_sm_1up __P((SCR *));
764 * Delete the top line of the screen. Shift the screen map
765 * up and display a new line at the bottom of the screen.
767 (void)sp
->gp
->scr_move(sp
, 0, 0);
768 if (vs_deleteln(sp
, 1))
771 /* One-line screens can fail. */
772 if (IS_ONELINE(sp
)) {
773 if (vs_sm_next(sp
, TMAP
, TMAP
))
776 memmove(HMAP
, HMAP
+ 1, (sp
->rows
- 1) * sizeof(SMAP
));
777 if (vs_sm_next(sp
, TMAP
- 1, TMAP
))
780 /* vs_sm_next() flushed the cache. */
781 return (vs_line(sp
, TMAP
, NULL
, NULL
));
786 * Delete a line a la curses, make sure to put the information
787 * line and other screens back.
790 vs_deleteln(SCR
*sp
, int cnt
)
797 /* If the screen is vertically split, we can't scroll it. */
799 F_SET(sp
, SC_SCR_REDRAW
);
804 (void)gp
->scr_clrtoeol(sp
);
806 (void)gp
->scr_cursor(sp
, &oldy
, &oldx
);
808 (void)gp
->scr_deleteln(sp
);
809 (void)gp
->scr_move(sp
, LASTLINE(sp
), 0);
810 (void)gp
->scr_insertln(sp
);
811 (void)gp
->scr_move(sp
, oldy
, oldx
);
819 * Scroll the SMAP down count logical lines.
822 vs_sm_down(SCR
*sp
, MARK
*rp
, db_recno_t count
, scroll_t scmd
, SMAP
*smp
)
825 int cursor_set
, ychanged
, zset
;
827 /* Check to see if movement is possible. */
828 if (HMAP
->lno
== 1 &&
829 (O_ISSET(sp
, O_LEFTRIGHT
) || HMAP
->soff
== 1) &&
830 (scmd
== CNTRL_Y
|| scmd
== Z_CARAT
|| smp
== HMAP
)) {
836 * Small screens: see vs_refresh.c section 6a.
838 * If it's a small screen, and the movement isn't larger than a
839 * screen, i.e some context will remain, open up the screen and
840 * display by scrolling. In this case, the cursor moves up one
841 * line for each line displayed. Otherwise, erase/compress and
842 * repaint, and move the cursor to the first line in the screen.
843 * Note, the ^B command is always in the latter case, for historical
846 cursor_set
= scmd
== CNTRL_Y
;
848 if (count
>= sp
->t_maxrows
|| scmd
== CNTRL_B
) {
852 for (; count
--; s1
= s2
) {
853 if (vs_sm_prev(sp
, &s1
, &s2
))
856 (O_ISSET(sp
, O_LEFTRIGHT
) || s2
.soff
== 1))
860 if (vs_sm_fill(sp
, OOBLNO
, P_TOP
))
862 return (vs_sm_position(sp
, rp
, 0, P_BOTTOM
));
864 cursor_set
= scmd
== CNTRL_Y
|| vs_sm_cursor(sp
, &ssmp
);
866 sp
->t_rows
!= sp
->t_maxrows
; --count
, ++sp
->t_rows
) {
867 if (HMAP
->lno
== 1 &&
868 (O_ISSET(sp
, O_LEFTRIGHT
) || HMAP
->soff
== 1))
876 rp
->cno
= ssmp
->c_sboff
;
882 for (ychanged
= zset
= 0; count
; --count
) {
883 /* If the line doesn't exist, we're done. */
884 if (HMAP
->lno
== 1 &&
885 (O_ISSET(sp
, O_LEFTRIGHT
) || HMAP
->soff
== 1))
888 /* Scroll the screen and cursor down one logical line. */
912 if (scmd
!= CNTRL_Y
&& cursor_set
)
918 * If there are more lines, the ^B command is positioned at
919 * the last line of the screen. However, the line may not
923 for (smp
= TMAP
; smp
> HMAP
; --smp
)
924 if (db_exist(sp
, smp
->lno
))
931 * The ^B and ^U commands move the cursor towards SOF
932 * if there are more lines to move.
934 if (count
< (db_recno_t
)(smp
- HMAP
))
941 * On a ^Y that was forced to change lines, try and keep the
942 * cursor as close as possible to the last position, but also
943 * set it up so that the next "real" movement will return the
944 * cursor to the closest position to the last real movement.
948 rp
->cno
= vs_colpos(sp
, smp
->lno
,
949 (O_ISSET(sp
, O_LEFTRIGHT
) ?
950 smp
->coff
: (smp
->soff
- 1) * sp
->cols
) +
955 /* The z^ command moves the cursor to the first new line. */
961 if (!SMAP_CACHE(smp
) && vs_line(sp
, smp
, NULL
, NULL
))
964 rp
->cno
= smp
->c_scoff
== 255 ? 0 : smp
->c_sboff
;
970 * Erase the small screen area for the scrolling functions.
978 (void)gp
->scr_move(sp
, LASTLINE(sp
), 0);
979 (void)gp
->scr_clrtoeol(sp
);
980 for (; sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
981 (void)gp
->scr_move(sp
, TMAP
- HMAP
, 0);
982 (void)gp
->scr_clrtoeol(sp
);
989 * Scroll the SMAP down one.
991 * PUBLIC: int vs_sm_1down __P((SCR *));
997 * Insert a line at the top of the screen. Shift the screen map
998 * down and display a new line at the top of the screen.
1000 (void)sp
->gp
->scr_move(sp
, 0, 0);
1001 if (vs_insertln(sp
, 1))
1004 /* One-line screens can fail. */
1005 if (IS_ONELINE(sp
)) {
1006 if (vs_sm_prev(sp
, HMAP
, HMAP
))
1009 memmove(HMAP
+ 1, HMAP
, (sp
->rows
- 1) * sizeof(SMAP
));
1010 if (vs_sm_prev(sp
, HMAP
+ 1, HMAP
))
1013 /* vs_sm_prev() flushed the cache. */
1014 return (vs_line(sp
, HMAP
, NULL
, NULL
));
1019 * Insert a line a la curses, make sure to put the information
1020 * line and other screens back.
1023 vs_insertln(SCR
*sp
, int cnt
)
1030 /* If the screen is vertically split, we can't scroll it. */
1031 if (IS_VSPLIT(sp
)) {
1032 F_SET(sp
, SC_SCR_REDRAW
);
1036 if (IS_ONELINE(sp
)) {
1037 (void)gp
->scr_move(sp
, LASTLINE(sp
), 0);
1038 (void)gp
->scr_clrtoeol(sp
);
1040 (void)gp
->scr_cursor(sp
, &oldy
, &oldx
);
1042 (void)gp
->scr_move(sp
, LASTLINE(sp
) - 1, 0);
1043 (void)gp
->scr_deleteln(sp
);
1044 (void)gp
->scr_move(sp
, oldy
, oldx
);
1045 (void)gp
->scr_insertln(sp
);
1053 * Fill in the next entry in the SMAP.
1055 * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *));
1058 vs_sm_next(SCR
*sp
, SMAP
*p
, SMAP
*t
)
1063 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
1064 t
->lno
= p
->lno
+ 1;
1067 lcnt
= vs_screens(sp
, p
->lno
, NULL
);
1068 if (lcnt
== p
->soff
) {
1069 t
->lno
= p
->lno
+ 1;
1073 t
->soff
= p
->soff
+ 1;
1081 * Fill in the previous entry in the SMAP.
1083 * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
1086 vs_sm_prev(SCR
*sp
, SMAP
*p
, SMAP
*t
)
1089 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
1090 t
->lno
= p
->lno
- 1;
1095 t
->soff
= p
->soff
- 1;
1097 t
->lno
= p
->lno
- 1;
1098 t
->soff
= vs_screens(sp
, t
->lno
, NULL
);
1101 return (t
->lno
== 0);
1106 * Return the SMAP entry referenced by the cursor.
1108 * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **));
1111 vs_sm_cursor(SCR
*sp
, SMAP
**smpp
)
1115 /* See if the cursor is not in the map. */
1116 if (sp
->lno
< HMAP
->lno
|| sp
->lno
> TMAP
->lno
)
1119 /* Find the first occurence of the line. */
1120 for (p
= HMAP
; p
->lno
!= sp
->lno
; ++p
);
1122 /* Fill in the map information until we find the right line. */
1123 for (; p
<= TMAP
; ++p
) {
1124 /* Short lines are common and easy to detect. */
1125 if (p
!= TMAP
&& (p
+ 1)->lno
!= p
->lno
) {
1129 if (!SMAP_CACHE(p
) && vs_line(sp
, p
, NULL
, NULL
))
1131 if (p
->c_eboff
>= sp
->cno
) {
1137 /* It was past the end of the map after all. */
1143 * Return the line/column of the top, middle or last line on the screen.
1144 * (The vi H, M and L commands.) Here because only the screen routines
1145 * know what's really out there.
1147 * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
1150 vs_sm_position(SCR
*sp
, MARK
*rp
, u_long cnt
, pos_t pos
)
1159 * Historically, an invalid count to the H command failed.
1160 * We do nothing special here, just making sure that H in
1161 * an empty screen works.
1163 if (cnt
> (u_long
)(TMAP
- HMAP
))
1166 if (cnt
&& !db_exist(sp
, smp
->lno
)) {
1167 sof
: msgq(sp
, M_BERR
, "220|Movement past the end-of-screen");
1174 * Historically, a count to the M command was ignored.
1175 * If the screen isn't filled, find the middle of what's
1176 * real and move there.
1178 if (!db_exist(sp
, TMAP
->lno
)) {
1179 if (db_last(sp
, &last
))
1181 for (smp
= TMAP
; smp
->lno
> last
&& smp
> HMAP
; --smp
);
1183 smp
-= (smp
- HMAP
) / 2;
1185 smp
= (HMAP
+ (TMAP
- HMAP
) / 2) + cnt
;
1190 * Historically, an invalid count to the L command failed.
1191 * If the screen isn't filled, find the bottom of what's
1192 * real and try to offset from there.
1194 if (cnt
> (u_long
)(TMAP
- HMAP
))
1197 if (!db_exist(sp
, smp
->lno
)) {
1198 if (db_last(sp
, &last
))
1200 for (; smp
->lno
> last
&& smp
> HMAP
; --smp
);
1201 if (cnt
> (u_long
)(smp
- HMAP
)) {
1202 eof
: msgq(sp
, M_BERR
,
1203 "221|Movement past the beginning-of-screen");
1213 /* Make sure that the cached information is valid. */
1214 if (!SMAP_CACHE(smp
) && vs_line(sp
, smp
, NULL
, NULL
))
1217 rp
->cno
= smp
->c_sboff
;
1224 * Return the number of screen lines from an SMAP entry to the
1225 * start of some file line, less than a maximum value.
1227 * PUBLIC: db_recno_t vs_sm_nlines __P((SCR *, SMAP *, db_recno_t, size_t));
1230 vs_sm_nlines(SCR
*sp
, SMAP
*from_sp
, db_recno_t to_lno
, size_t max
)
1232 db_recno_t lno
, lcnt
;
1234 if (O_ISSET(sp
, O_LEFTRIGHT
))
1235 return (from_sp
->lno
> to_lno
?
1236 from_sp
->lno
- to_lno
: to_lno
- from_sp
->lno
);
1238 if (from_sp
->lno
== to_lno
)
1239 return (from_sp
->soff
- 1);
1241 if (from_sp
->lno
> to_lno
) {
1242 lcnt
= from_sp
->soff
- 1; /* Correct for off-by-one. */
1243 for (lno
= from_sp
->lno
; --lno
>= to_lno
&& lcnt
<= max
;)
1244 lcnt
+= vs_screens(sp
, lno
, NULL
);
1247 lcnt
= (vs_screens(sp
, lno
, NULL
) - from_sp
->soff
) + 1;
1248 for (; ++lno
< to_lno
&& lcnt
<= max
;)
1249 lcnt
+= vs_screens(sp
, lno
, NULL
);