2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid[] = "$Id: vs_smap.c,v 10.31 2011/02/26 13:56:21 skimo Exp $";
16 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
26 #include "../common/common.h"
29 static int vs_deleteln __P((SCR *, int));
30 static int vs_insertln __P((SCR *, int));
31 static int vs_sm_delete __P((SCR *, recno_t));
32 static int vs_sm_down __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
33 static int vs_sm_erase __P((SCR *));
34 static int vs_sm_insert __P((SCR *, recno_t));
35 static int vs_sm_reset __P((SCR *, recno_t));
36 static int vs_sm_up __P((SCR *, MARK *, recno_t, scroll_t, SMAP *));
40 * Make a change to the screen.
42 * PUBLIC: int vs_change __P((SCR *, recno_t, lnop_t));
45 vs_change(SCR *sp, recno_t lno, lnop_t op)
49 size_t cnt, oldy, oldx;
55 * Very nasty special case. The historic vi code displays a single
56 * space (or a '$' if the list option is set) for the first line in
57 * an "empty" file. If we "insert" a line, that line gets scrolled
58 * down, not repainted, so it's incorrect when we refresh the screen.
59 * The vi text input functions detect it explicitly and don't insert
62 * Check for line #2 before going to the end of the file.
64 if (((op == LINE_APPEND && lno == 0) ||
65 (op == LINE_INSERT && lno == 1)) &&
71 /* Appending is the same as inserting, if the line is incremented. */
72 if (op == LINE_APPEND) {
77 /* Ignore the change if the line is after the map. */
82 * If the line is before the map, and it's a decrement, decrement
83 * the map. If it's an increment, increment the map. Otherwise,
86 if (lno < HMAP->lno) {
92 for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
96 F_SET(vip, VIP_N_RENUMBER);
99 for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
103 F_SET(vip, VIP_N_RENUMBER);
111 F_SET(vip, VIP_N_REFRESH);
114 * Invalidate the line size cache, and invalidate the cursor if it's
119 F_SET(vip, VIP_CUR_INVALID);
122 * If ex modifies the screen after ex output is already on the screen
123 * or if we've switched into ex canonical mode, don't touch it -- we'll
124 * get scrolling wrong, at best.
126 if (!F_ISSET(sp, SC_TINPUT_INFO) &&
127 (F_ISSET(sp, SC_SCR_EXWROTE) || VIP(sp)->totalcount > 1)) {
128 F_SET(vip, VIP_N_EX_REDRAW);
132 /* Save and restore the cursor for these routines. */
133 (void)sp->gp->scr_cursor(sp, &oldy, &oldx);
137 if (vs_sm_delete(sp, lno))
141 F_SET(vip, VIP_N_RENUMBER);
144 if (vs_sm_insert(sp, lno))
148 F_SET(vip, VIP_N_RENUMBER);
151 if (vs_sm_reset(sp, lno))
158 (void)sp->gp->scr_move(sp, oldy, oldx);
164 * Fill in the screen map, placing the specified line at the
165 * right position. There isn't any way to tell if an SMAP
166 * entry has been filled in, so this routine had better be
167 * called with P_FILL set before anything else is done.
170 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
171 * slot is already filled in, P_BOTTOM means that the TMAP slot is
172 * already filled in, and we just finish up the job.
174 * PUBLIC: int vs_sm_fill __P((SCR *, recno_t, pos_t));
177 vs_sm_fill(SCR *sp, recno_t lno, pos_t pos)
182 /* Flush all cached information from the SMAP. */
183 for (p = HMAP, cnt = sp->t_rows; cnt--; ++p)
187 * If the map is filled, the screen must be redrawn.
190 * This is a bug. We should try and figure out if the desired line
191 * is already in the map or close by -- scrolling the screen would
192 * be a lot better than redrawing.
194 F_SET(sp, SC_SCR_REDRAW);
202 /* See if less than half a screen from the top. */
204 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
209 /* See if less than half a screen from the bottom. */
210 if (db_last(sp, &tmp.lno))
213 tmp.soff = vs_screens(sp, tmp.lno, NULL);
215 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
217 TMAP->coff = tmp.coff;
218 TMAP->soff = tmp.soff;
224 top: HMAP->lno = lno;
229 * If number of lines HMAP->lno (top line) spans
230 * changed due to, say reformatting, and now is
231 * fewer than HMAP->soff, reset so the line is
232 * redrawn at the top of the screen.
234 cnt = vs_screens(sp, HMAP->lno, NULL);
235 if (cnt < HMAP->soff)
238 /* If we fail, just punt. */
239 for (p = HMAP, cnt = sp->t_rows; --cnt; ++p)
240 if (vs_sm_next(sp, p, p + 1))
244 /* If we fail, guess that the file is too small. */
245 middle: p = HMAP + sp->t_rows / 2;
249 for (; p > HMAP; --p)
250 if (vs_sm_prev(sp, p, p - 1)) {
255 /* If we fail, just punt. */
256 p = HMAP + sp->t_rows / 2;
257 for (; p < TMAP; ++p)
258 if (vs_sm_next(sp, p, p + 1))
265 TMAP->soff = vs_screens(sp, lno, NULL);
267 /* If we fail, guess that the file is too small. */
268 bottom: for (p = TMAP; p > HMAP; --p)
269 if (vs_sm_prev(sp, p, p - 1)) {
280 * Try and put *something* on the screen. If this fails, we have a
281 * serious hard error.
286 for (p = HMAP; p < TMAP; ++p)
287 if (vs_sm_next(sp, p, p + 1))
293 * For the routines vs_sm_reset, vs_sm_delete and vs_sm_insert: if the
294 * screen contains only a single line (whether because the screen is small
295 * or the line large), it gets fairly exciting. Skip the fun, set a flag
296 * so the screen map is refilled and the screen redrawn, and return. This
297 * is amazingly slow, but it's not clear that anyone will care.
299 #define HANDLE_WEIRDNESS(cnt) { \
300 if (cnt >= sp->t_rows) { \
301 F_SET(sp, SC_SCR_REFORMAT); \
308 * Delete a line out of the SMAP.
311 vs_sm_delete(SCR *sp, recno_t lno)
317 * Find the line in the map, and count the number of screen lines
318 * which display any part of the deleted line.
320 for (p = HMAP; p->lno != lno; ++p);
321 if (O_ISSET(sp, O_LEFTRIGHT))
324 for (cnt_orig = 1, t = p + 1;
325 t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
327 HANDLE_WEIRDNESS(cnt_orig);
329 /* Delete that many lines from the screen. */
330 (void)sp->gp->scr_move(sp, p - HMAP, 0);
331 if (vs_deleteln(sp, cnt_orig))
334 /* Shift the screen map up. */
335 memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
337 /* Decrement the line numbers for the rest of the map. */
338 for (t = TMAP - cnt_orig; p <= t; ++p)
341 /* Display the new lines. */
342 for (p = TMAP - cnt_orig;;) {
343 if (p < TMAP && vs_sm_next(sp, p, p + 1))
345 /* vs_sm_next() flushed the cache. */
346 if (vs_line(sp, ++p, NULL, NULL))
356 * Insert a line into the SMAP.
359 vs_sm_insert(SCR *sp, recno_t lno)
362 size_t cnt_orig, cnt, coff;
364 /* Save the offset. */
368 * Find the line in the map, find out how many screen lines
369 * needed to display the line.
371 for (p = HMAP; p->lno != lno; ++p);
373 cnt_orig = vs_screens(sp, lno, NULL);
374 HANDLE_WEIRDNESS(cnt_orig);
377 * The lines left in the screen override the number of screen
378 * lines in the inserted line.
380 cnt = (TMAP - p) + 1;
384 /* Push down that many lines. */
385 (void)sp->gp->scr_move(sp, p - HMAP, 0);
386 if (vs_insertln(sp, cnt_orig))
389 /* Shift the screen map down. */
390 memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
392 /* Increment the line numbers for the rest of the map. */
393 for (t = p + cnt_orig; t <= TMAP; ++t)
396 /* Fill in the SMAP for the new lines, and display. */
397 for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
402 if (vs_line(sp, t, NULL, NULL))
410 * Reset a line in the SMAP.
413 vs_sm_reset(SCR *sp, recno_t lno)
416 size_t cnt_orig, cnt_new, cnt, diff;
419 * See if the number of on-screen rows taken up by the old display
420 * for the line is the same as the number needed for the new one.
421 * If so, repaint, otherwise do it the hard way.
423 for (p = HMAP; p->lno != lno; ++p);
424 if (O_ISSET(sp, O_LEFTRIGHT)) {
426 cnt_orig = cnt_new = 1;
429 t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
430 cnt_new = vs_screens(sp, lno, NULL);
433 HANDLE_WEIRDNESS(cnt_orig);
435 if (cnt_orig == cnt_new) {
438 if (vs_line(sp, p, NULL, NULL))
444 if (cnt_orig < cnt_new) {
445 /* Get the difference. */
446 diff = cnt_new - cnt_orig;
449 * The lines left in the screen override the number of screen
450 * lines in the inserted line.
452 cnt = (TMAP - p) + 1;
456 /* If there are any following lines, push them down. */
458 (void)sp->gp->scr_move(sp, p - HMAP, 0);
459 if (vs_insertln(sp, diff))
462 /* Shift the screen map down. */
464 (((TMAP - p) - diff) + 1) * sizeof(SMAP));
467 /* Fill in the SMAP for the replaced line, and display. */
468 for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
472 if (vs_line(sp, t, NULL, NULL))
476 /* Get the difference. */
477 diff = cnt_orig - cnt_new;
479 /* Delete that many lines from the screen. */
480 (void)sp->gp->scr_move(sp, p - HMAP, 0);
481 if (vs_deleteln(sp, diff))
484 /* Shift the screen map up. */
485 memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
487 /* Fill in the SMAP for the replaced line, and display. */
488 for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
492 if (vs_line(sp, t, NULL, NULL))
496 /* Display the new lines at the bottom of the screen. */
497 for (t = TMAP - diff;;) {
498 if (t < TMAP && vs_sm_next(sp, t, t + 1))
500 /* vs_sm_next() flushed the cache. */
501 if (vs_line(sp, ++t, NULL, NULL))
512 * Scroll the SMAP up/down count logical lines. Different
513 * semantics based on the vi command, *sigh*.
515 * PUBLIC: int vs_sm_scroll __P((SCR *, MARK *, recno_t, scroll_t));
518 vs_sm_scroll(SCR *sp, MARK *rp, recno_t count, scroll_t scmd)
523 * Invalidate the cursor. The line is probably going to change,
524 * (although for ^E and ^Y it may not). In any case, the scroll
525 * routines move the cursor to draw things.
527 F_SET(VIP(sp), VIP_CUR_INVALID);
529 /* Find the cursor in the screen. */
530 if (vs_sm_cursor(sp, &smp))
538 if (vs_sm_down(sp, rp, count, scmd, smp))
545 if (vs_sm_up(sp, rp, count, scmd, smp))
554 * If we're at the start of a line, go for the first non-blank.
555 * This makes it look like the old vi, even though we're moving
556 * around by logical lines, not physical ones.
559 * In the presence of a long line, which has more than a screen
560 * width of leading spaces, this code can cause a cursor warp.
563 if (scmd != CNTRL_E && scmd != CNTRL_Y &&
564 rp->cno == 0 && nonblank(sp, rp->lno, &rp->cno))
572 * Scroll the SMAP up count logical lines.
575 vs_sm_up(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
577 int cursor_set, echanged, zset;
581 * Check to see if movement is possible.
583 * Get the line after the map. If that line is a new one (and if
584 * O_LEFTRIGHT option is set, this has to be true), and the next
585 * line doesn't exist, and the cursor doesn't move, or the cursor
586 * isn't even on the screen, or the cursor is already at the last
587 * line in the map, it's an error. If that test succeeded because
588 * the cursor wasn't at the end of the map, test to see if the map
591 if (vs_sm_next(sp, TMAP, &s1))
593 if (s1.lno > TMAP->lno && !db_exist(sp, s1.lno)) {
594 if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
598 if (vs_sm_next(sp, smp, &s1))
600 if (s1.lno > smp->lno && !db_exist(sp, s1.lno)) {
607 * Small screens: see vs_refresh.c section 6a.
609 * If it's a small screen, and the movement isn't larger than a
610 * screen, i.e some context will remain, open up the screen and
611 * display by scrolling. In this case, the cursor moves down one
612 * line for each line displayed. Otherwise, erase/compress and
613 * repaint, and move the cursor to the first line in the screen.
614 * Note, the ^F command is always in the latter case, for historical
619 if (count >= sp->t_maxrows || scmd == CNTRL_F) {
623 for (; count--; s1 = s2) {
624 if (vs_sm_next(sp, &s1, &s2))
626 if (s2.lno != s1.lno && !db_exist(sp, s2.lno))
630 if (vs_sm_fill(sp, OOBLNO, P_BOTTOM))
632 return (vs_sm_position(sp, rp, 0, P_TOP));
634 cursor_set = scmd == CNTRL_E || vs_sm_cursor(sp, &ssmp);
636 sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
637 if (vs_sm_next(sp, TMAP, &s1))
639 if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
642 /* vs_sm_next() flushed the cache. */
643 if (vs_line(sp, TMAP, NULL, NULL))
651 rp->cno = ssmp->c_sboff;
657 for (echanged = zset = 0; count; --count) {
658 /* Decide what would show up on the screen. */
659 if (vs_sm_next(sp, TMAP, &s1))
662 /* If the line doesn't exist, we're done. */
663 if (TMAP->lno != s1.lno && !db_exist(sp, s1.lno))
666 /* Scroll the screen cursor up one logical line. */
696 * On a ^E that was forced to change lines, try and keep the
697 * cursor as close as possible to the last position, but also
698 * set it up so that the next "real" movement will return the
699 * cursor to the closest position to the last real movement.
703 rp->cno = vs_colpos(sp, smp->lno,
704 (O_ISSET(sp, O_LEFTRIGHT) ?
705 smp->coff : (smp->soff - 1) * sp->cols) +
711 * If there are more lines, the ^F command is positioned at
712 * the first line of the screen.
721 * The ^D and ^F commands move the cursor towards EOF
722 * if there are more lines to move. Check to be sure
723 * the lines actually exist. (They may not if the
724 * file is smaller than the screen.)
726 for (; count; --count, ++smp)
727 if (smp == TMAP || !db_exist(sp, smp[1].lno))
731 /* The z+ command moves the cursor to the first new line. */
737 if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
740 rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
746 * Scroll the SMAP up one.
748 * PUBLIC: int vs_sm_1up __P((SCR *));
754 * Delete the top line of the screen. Shift the screen map
755 * up and display a new line at the bottom of the screen.
757 (void)sp->gp->scr_move(sp, 0, 0);
758 if (vs_deleteln(sp, 1))
761 /* One-line screens can fail. */
762 if (IS_ONELINE(sp)) {
763 if (vs_sm_next(sp, TMAP, TMAP))
766 memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
767 if (vs_sm_next(sp, TMAP - 1, TMAP))
770 /* vs_sm_next() flushed the cache. */
771 return (vs_line(sp, TMAP, NULL, NULL));
776 * Delete a line a la curses, make sure to put the information
777 * line and other screens back.
780 vs_deleteln(SCR *sp, int cnt)
787 /* If the screen is vertically split, we can't scroll it. */
789 F_SET(sp, SC_SCR_REDRAW);
794 (void)gp->scr_clrtoeol(sp);
796 (void)gp->scr_cursor(sp, &oldy, &oldx);
798 (void)gp->scr_deleteln(sp);
799 (void)gp->scr_move(sp, LASTLINE(sp), 0);
800 (void)gp->scr_insertln(sp);
801 (void)gp->scr_move(sp, oldy, oldx);
809 * Scroll the SMAP down count logical lines.
812 vs_sm_down(SCR *sp, MARK *rp, recno_t count, scroll_t scmd, SMAP *smp)
815 int cursor_set, ychanged, zset;
817 /* Check to see if movement is possible. */
818 if (HMAP->lno == 1 &&
819 (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1) &&
820 (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
826 * Small screens: see vs_refresh.c section 6a.
828 * If it's a small screen, and the movement isn't larger than a
829 * screen, i.e some context will remain, open up the screen and
830 * display by scrolling. In this case, the cursor moves up one
831 * line for each line displayed. Otherwise, erase/compress and
832 * repaint, and move the cursor to the first line in the screen.
833 * Note, the ^B command is always in the latter case, for historical
836 cursor_set = scmd == CNTRL_Y;
838 if (count >= sp->t_maxrows || scmd == CNTRL_B) {
842 for (; count--; s1 = s2) {
843 if (vs_sm_prev(sp, &s1, &s2))
846 (O_ISSET(sp, O_LEFTRIGHT) || s2.soff == 1))
850 if (vs_sm_fill(sp, OOBLNO, P_TOP))
852 return (vs_sm_position(sp, rp, 0, P_BOTTOM));
854 cursor_set = scmd == CNTRL_Y || vs_sm_cursor(sp, &ssmp);
856 sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
857 if (HMAP->lno == 1 &&
858 (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
866 rp->cno = ssmp->c_sboff;
872 for (ychanged = zset = 0; count; --count) {
873 /* If the line doesn't exist, we're done. */
874 if (HMAP->lno == 1 &&
875 (O_ISSET(sp, O_LEFTRIGHT) || HMAP->soff == 1))
878 /* Scroll the screen and cursor down one logical line. */
902 if (scmd != CNTRL_Y && cursor_set)
908 * If there are more lines, the ^B command is positioned at
909 * the last line of the screen. However, the line may not
913 for (smp = TMAP; smp > HMAP; --smp)
914 if (db_exist(sp, smp->lno))
921 * The ^B and ^U commands move the cursor towards SOF
922 * if there are more lines to move.
924 if (count < smp - HMAP)
931 * On a ^Y that was forced to change lines, try and keep the
932 * cursor as close as possible to the last position, but also
933 * set it up so that the next "real" movement will return the
934 * cursor to the closest position to the last real movement.
938 rp->cno = vs_colpos(sp, smp->lno,
939 (O_ISSET(sp, O_LEFTRIGHT) ?
940 smp->coff : (smp->soff - 1) * sp->cols) +
945 /* The z^ command moves the cursor to the first new line. */
951 if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
954 rp->cno = smp->c_scoff == 255 ? 0 : smp->c_sboff;
960 * Erase the small screen area for the scrolling functions.
968 (void)gp->scr_move(sp, LASTLINE(sp), 0);
969 (void)gp->scr_clrtoeol(sp);
970 for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
971 (void)gp->scr_move(sp, TMAP - HMAP, 0);
972 (void)gp->scr_clrtoeol(sp);
979 * Scroll the SMAP down one.
981 * PUBLIC: int vs_sm_1down __P((SCR *));
987 * Insert a line at the top of the screen. Shift the screen map
988 * down and display a new line at the top of the screen.
990 (void)sp->gp->scr_move(sp, 0, 0);
991 if (vs_insertln(sp, 1))
994 /* One-line screens can fail. */
995 if (IS_ONELINE(sp)) {
996 if (vs_sm_prev(sp, HMAP, HMAP))
999 memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
1000 if (vs_sm_prev(sp, HMAP + 1, HMAP))
1003 /* vs_sm_prev() flushed the cache. */
1004 return (vs_line(sp, HMAP, NULL, NULL));
1009 * Insert a line a la curses, make sure to put the information
1010 * line and other screens back.
1013 vs_insertln(SCR *sp, int cnt)
1020 /* If the screen is vertically split, we can't scroll it. */
1021 if (IS_VSPLIT(sp)) {
1022 F_SET(sp, SC_SCR_REDRAW);
1026 if (IS_ONELINE(sp)) {
1027 (void)gp->scr_move(sp, LASTLINE(sp), 0);
1028 (void)gp->scr_clrtoeol(sp);
1030 (void)gp->scr_cursor(sp, &oldy, &oldx);
1032 (void)gp->scr_move(sp, LASTLINE(sp) - 1, 0);
1033 (void)gp->scr_deleteln(sp);
1034 (void)gp->scr_move(sp, oldy, oldx);
1035 (void)gp->scr_insertln(sp);
1043 * Fill in the next entry in the SMAP.
1045 * PUBLIC: int vs_sm_next __P((SCR *, SMAP *, SMAP *));
1048 vs_sm_next(SCR *sp, SMAP *p, SMAP *t)
1053 if (O_ISSET(sp, O_LEFTRIGHT)) {
1054 t->lno = p->lno + 1;
1057 lcnt = vs_screens(sp, p->lno, NULL);
1058 if (lcnt == p->soff) {
1059 t->lno = p->lno + 1;
1063 t->soff = p->soff + 1;
1071 * Fill in the previous entry in the SMAP.
1073 * PUBLIC: int vs_sm_prev __P((SCR *, SMAP *, SMAP *));
1076 vs_sm_prev(SCR *sp, SMAP *p, SMAP *t)
1079 if (O_ISSET(sp, O_LEFTRIGHT)) {
1080 t->lno = p->lno - 1;
1085 t->soff = p->soff - 1;
1087 t->lno = p->lno - 1;
1088 t->soff = vs_screens(sp, t->lno, NULL);
1091 return (t->lno == 0);
1096 * Return the SMAP entry referenced by the cursor.
1098 * PUBLIC: int vs_sm_cursor __P((SCR *, SMAP **));
1101 vs_sm_cursor(SCR *sp, SMAP **smpp)
1105 /* See if the cursor is not in the map. */
1106 if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
1109 /* Find the first occurence of the line. */
1110 for (p = HMAP; p->lno != sp->lno; ++p);
1112 /* Fill in the map information until we find the right line. */
1113 for (; p <= TMAP; ++p) {
1114 /* Short lines are common and easy to detect. */
1115 if (p != TMAP && (p + 1)->lno != p->lno) {
1119 if (!SMAP_CACHE(p) && vs_line(sp, p, NULL, NULL))
1121 if (p->c_eboff >= sp->cno) {
1127 /* It was past the end of the map after all. */
1133 * Return the line/column of the top, middle or last line on the screen.
1134 * (The vi H, M and L commands.) Here because only the screen routines
1135 * know what's really out there.
1137 * PUBLIC: int vs_sm_position __P((SCR *, MARK *, u_long, pos_t));
1140 vs_sm_position(SCR *sp, MARK *rp, u_long cnt, pos_t pos)
1149 * Historically, an invalid count to the H command failed.
1150 * We do nothing special here, just making sure that H in
1151 * an empty screen works.
1153 if (cnt > TMAP - HMAP)
1156 if (cnt && !db_exist(sp, smp->lno)) {
1157 sof: msgq(sp, M_BERR, "220|Movement past the end-of-screen");
1164 * Historically, a count to the M command was ignored.
1165 * If the screen isn't filled, find the middle of what's
1166 * real and move there.
1168 if (!db_exist(sp, TMAP->lno)) {
1169 if (db_last(sp, &last))
1171 for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
1173 smp -= (smp - HMAP) / 2;
1175 smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
1180 * Historically, an invalid count to the L command failed.
1181 * If the screen isn't filled, find the bottom of what's
1182 * real and try to offset from there.
1184 if (cnt > TMAP - HMAP)
1187 if (!db_exist(sp, smp->lno)) {
1188 if (db_last(sp, &last))
1190 for (; smp->lno > last && smp > HMAP; --smp);
1191 if (cnt > smp - HMAP) {
1192 eof: msgq(sp, M_BERR,
1193 "221|Movement past the beginning-of-screen");
1203 /* Make sure that the cached information is valid. */
1204 if (!SMAP_CACHE(smp) && vs_line(sp, smp, NULL, NULL))
1207 rp->cno = smp->c_sboff;
1214 * Return the number of screen lines from an SMAP entry to the
1215 * start of some file line, less than a maximum value.
1217 * PUBLIC: recno_t vs_sm_nlines __P((SCR *, SMAP *, recno_t, size_t));
1220 vs_sm_nlines(SCR *sp, SMAP *from_sp, recno_t to_lno, size_t max)
1224 if (O_ISSET(sp, O_LEFTRIGHT))
1225 return (from_sp->lno > to_lno ?
1226 from_sp->lno - to_lno : to_lno - from_sp->lno);
1228 if (from_sp->lno == to_lno)
1229 return (from_sp->soff - 1);
1231 if (from_sp->lno > to_lno) {
1232 lcnt = from_sp->soff - 1; /* Correct for off-by-one. */
1233 for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
1234 lcnt += vs_screens(sp, lno, NULL);
1237 lcnt = (vs_screens(sp, lno, NULL) - from_sp->soff) + 1;
1238 for (; ++lno < to_lno && lcnt <= max;)
1239 lcnt += vs_screens(sp, lno, NULL);