discard the old line before updating the screen so that the line
[nvi.git] / vi / vs_smap.c
blob8962d1ca155be57c4b0ef9ca9b3dfc8a3bcea2c5
1 /*-
2 * Copyright (c) 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: vs_smap.c,v 8.29 1993/11/30 09:27:44 bostic Exp $ (Berkeley) $Date: 1993/11/30 09:27:44 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <curses.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #include "vi.h"
19 #include "vcmd.h"
20 #include "svi_screen.h"
22 static int svi_deleteln __P((SCR *, int));
23 static int svi_insertln __P((SCR *, int));
24 static int svi_sm_delete __P((SCR *, EXF *, recno_t));
25 static int svi_sm_insert __P((SCR *, EXF *, recno_t));
26 static int svi_sm_reset __P((SCR *, EXF *, recno_t));
29 * svi_change --
30 * Make a change to the screen.
32 int
33 svi_change(sp, ep, lno, op)
34 SCR *sp;
35 EXF *ep;
36 recno_t lno;
37 enum operation op;
39 SMAP *p;
40 size_t oldy, oldx;
42 /* Appending is the same as inserting, if the line is incremented. */
43 if (op == LINE_APPEND) {
44 ++lno;
45 op = LINE_INSERT;
48 /* Ignore the change if the line is after the map. */
49 if (lno > TMAP->lno)
50 return (0);
53 * If the line is before the map, and it's a decrement, decrement
54 * the map. If it's an increment, increment the map. Otherwise,
55 * ignore it.
57 if (lno < HMAP->lno) {
58 switch (op) {
59 case LINE_APPEND:
60 abort();
61 /* NOTREACHED */
62 case LINE_DELETE:
63 for (p = HMAP; p <= TMAP; ++p)
64 --p->lno;
65 if (sp->lno >= lno)
66 --sp->lno;
67 F_SET(sp, S_RENUMBER);
68 break;
69 case LINE_INSERT:
70 for (p = HMAP; p <= TMAP; ++p)
71 ++p->lno;
72 if (sp->lno >= lno)
73 ++sp->lno;
74 F_SET(sp, S_RENUMBER);
75 break;
76 case LINE_RESET:
77 break;
79 return (0);
82 F_SET(SVP(sp), SVI_SCREENDIRTY);
84 /* Flush cached information from svi_screens(). */
85 SVP(sp)->ss_lno = OOBLNO;
87 /* Invalidate the cursor, if it's on this line. */
88 if (sp->lno == lno)
89 F_SET(SVP(sp), SVI_CUR_INVALID);
91 getyx(stdscr, oldy, oldx);
93 switch (op) {
94 case LINE_DELETE:
95 if (svi_sm_delete(sp, ep, lno))
96 return (1);
97 F_SET(sp, S_RENUMBER);
98 break;
99 case LINE_INSERT:
100 if (svi_sm_insert(sp, ep, lno))
101 return (1);
102 F_SET(sp, S_RENUMBER);
103 break;
104 case LINE_RESET:
105 if (svi_sm_reset(sp, ep, lno))
106 return (1);
107 break;
108 default:
109 abort();
112 MOVEA(sp, oldy, oldx);
114 return (0);
118 * svi_sm_fill --
119 * Fill in the screen map, placing the specified line at the
120 * right position. There isn't any way to tell if an SMAP
121 * entry has been filled in, so this routine had better be
122 * called with P_FILL set before anything else is done.
124 * !!!
125 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
126 * slot is already filled in, P_BOTTOM means that the TMAP slot is
127 * already filled in, and we just finish up the job.
130 svi_sm_fill(sp, ep, lno, pos)
131 SCR *sp;
132 EXF *ep;
133 recno_t lno;
134 enum position pos;
136 SMAP *p, tmp;
138 /* Flush all cached information from the SMAP. */
139 for (p = HMAP; p <= TMAP; ++p)
140 SMAP_FLUSH(p);
142 switch (pos) {
143 case P_FILL:
144 tmp.lno = 1;
145 tmp.off = 1;
147 /* See if less than half a screen from the top. */
148 if (svi_sm_nlines(sp, ep,
149 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
150 lno = 1;
151 goto top;
154 /* See if less than half a screen from the bottom. */
155 if (file_lline(sp, ep, &tmp.lno))
156 return (1);
157 tmp.off = svi_screens(sp, ep, tmp.lno, NULL);
158 if (svi_sm_nlines(sp, ep,
159 &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
160 TMAP->lno = tmp.lno;
161 TMAP->off = tmp.off;
162 goto bottom;
164 goto middle;
165 case P_TOP:
166 if (lno != OOBLNO) {
167 top: HMAP->lno = lno;
168 HMAP->off = 1;
170 /* If we fail, just punt. */
171 for (p = HMAP; p < TMAP; ++p)
172 if (svi_sm_next(sp, ep, p, p + 1))
173 goto err;
174 break;
175 case P_MIDDLE:
176 /* If we fail, guess that the file is too small. */
177 middle: p = HMAP + (TMAP - HMAP) / 2;
178 for (p->lno = lno, p->off = 1; p > HMAP; --p)
179 if (svi_sm_prev(sp, ep, p, p - 1)) {
180 lno = 1;
181 goto top;
184 /* If we fail, just punt. */
185 p = HMAP + (TMAP - HMAP) / 2;
186 for (; p < TMAP; ++p)
187 if (svi_sm_next(sp, ep, p, p + 1))
188 goto err;
189 break;
190 case P_BOTTOM:
191 if (lno != OOBLNO) {
192 TMAP->lno = lno;
193 TMAP->off = svi_screens(sp, ep, lno, NULL);
195 /* If we fail, guess that the file is too small. */
196 bottom: for (p = TMAP; p > HMAP; --p)
197 if (svi_sm_prev(sp, ep, p, p - 1)) {
198 lno = 1;
199 goto top;
201 break;
203 return (0);
206 * Try and put *something* on the screen. If this fails,
207 * we have a serious hard error.
209 err: HMAP->lno = 1;
210 HMAP->off = 1;
211 for (p = HMAP; p < TMAP; ++p)
212 if (svi_sm_next(sp, ep, p, p + 1))
213 return (1);
214 return (0);
218 * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
219 * screen only contains one line, or, if the line is the entire screen, this
220 * gets fairly exciting. Skip the fun and simply return if there's only one
221 * line in the screen, or just call fill. Fill may not be entirely accurate,
222 * i.e. we may be painting the screen with something not even close to the
223 * cursor, but it's not like we're into serious performance issues here, and
224 * the refresh routine will fix it for us.
226 #define TOO_WEIRD { \
227 if (cnt_orig >= sp->t_rows) { \
228 if (cnt_orig == 1) \
229 return (0); \
230 if (file_gline(sp, ep, lno, NULL) == NULL) \
231 if (file_lline(sp, ep, &lno)) \
232 return (1); \
233 F_SET(sp, S_REDRAW); \
234 return (svi_sm_fill(sp, ep, lno, P_TOP)); \
239 * svi_sm_delete --
240 * Delete a line out of the SMAP.
242 static int
243 svi_sm_delete(sp, ep, lno)
244 SCR *sp;
245 EXF *ep;
246 recno_t lno;
248 SMAP *p, *t;
249 size_t cnt_orig;
252 * Find the line in the map, and count the number of screen lines
253 * which display any part of the deleted line.
255 for (p = HMAP; p->lno != lno; ++p);
256 for (cnt_orig = 1, t = p + 1;
257 t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
259 TOO_WEIRD;
261 /* Delete that many lines from the screen. */
262 MOVE(sp, p - HMAP, 0);
263 if (svi_deleteln(sp, cnt_orig))
264 return (1);
266 /* Shift the screen map up. */
267 memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
269 /* Decrement the line numbers for the rest of the map. */
270 for (t = TMAP - cnt_orig; p <= t; ++p)
271 --p->lno;
273 /* Display the new lines. */
274 for (p = TMAP - cnt_orig;;) {
275 if (p < TMAP && svi_sm_next(sp, ep, p, p + 1))
276 return (1);
277 /* svi_sm_next() flushed the cache. */
278 if (svi_line(sp, ep, ++p, NULL, NULL))
279 return (1);
280 if (p == TMAP)
281 break;
283 return (0);
287 * svi_sm_insert --
288 * Insert a line into the SMAP.
290 static int
291 svi_sm_insert(sp, ep, lno)
292 SCR *sp;
293 EXF *ep;
294 recno_t lno;
296 SMAP *p, *t;
297 size_t cnt_orig, cnt;
300 * Find the line in the map, find out how many screen lines
301 * needed to display the line.
303 for (p = HMAP; p->lno != lno; ++p);
304 cnt_orig = svi_screens(sp, ep, lno, NULL);
306 TOO_WEIRD;
309 * The lines left in the screen override the number of screen
310 * lines in the inserted line.
312 cnt = (TMAP - p) + 1;
313 if (cnt_orig > cnt)
314 cnt_orig = cnt;
316 /* Push down that many lines. */
317 MOVE(sp, p - HMAP, 0);
318 if (svi_insertln(sp, cnt_orig))
319 return (1);
321 /* Shift the screen map down. */
322 memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
324 /* Increment the line numbers for the rest of the map. */
325 for (t = p + cnt_orig; t <= TMAP; ++t)
326 ++t->lno;
328 /* Fill in the SMAP for the new lines, and display. */
329 for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
330 t->lno = lno;
331 t->off = cnt;
332 SMAP_FLUSH(t);
333 if (svi_line(sp, ep, t, NULL, NULL))
334 return (1);
336 return (0);
340 * svi_sm_reset --
341 * Reset a line in the SMAP.
343 static int
344 svi_sm_reset(sp, ep, lno)
345 SCR *sp;
346 EXF *ep;
347 recno_t lno;
349 SMAP *p, *t;
350 size_t cnt_orig, cnt_new, cnt, diff;
353 * See if the number of on-screen rows taken up by the old display
354 * for the line is the same as the number needed for the new one.
355 * If so, repaint, otherwise do it the hard way.
357 for (p = HMAP; p->lno != lno; ++p);
358 for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
359 cnt_new = svi_screens(sp, ep, lno, NULL);
361 TOO_WEIRD;
363 if (cnt_orig == cnt_new) {
364 do {
365 SMAP_FLUSH(p);
366 if (svi_line(sp, ep, p, NULL, NULL))
367 return (1);
368 } while (++p < t);
369 return (0);
372 if (cnt_orig < cnt_new) {
373 /* Get the difference. */
374 diff = cnt_new - 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;
381 if (diff > cnt)
382 diff = cnt;
384 /* Push down the extra lines. */
385 MOVE(sp, p - HMAP, 0);
386 if (svi_insertln(sp, diff))
387 return (1);
389 /* Shift the screen map down. */
390 memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
392 /* Fill in the SMAP for the replaced line, and display. */
393 for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
394 t->lno = lno;
395 t->off = cnt;
396 SMAP_FLUSH(t);
397 if (svi_line(sp, ep, t, NULL, NULL))
398 return (1);
400 } else {
401 /* Get the difference. */
402 diff = cnt_orig - cnt_new;
404 /* Delete that many lines from the screen. */
405 MOVE(sp, p - HMAP, 0);
406 if (svi_deleteln(sp, diff))
407 return (1);
409 /* Shift the screen map up. */
410 memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
412 /* Fill in the SMAP for the replaced line, and display. */
413 for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
414 t->lno = lno;
415 t->off = cnt;
416 SMAP_FLUSH(t);
417 if (svi_line(sp, ep, t, NULL, NULL))
418 return (1);
421 /* Display the new lines at the bottom of the screen. */
422 for (t = TMAP - diff;;) {
423 if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
424 return (1);
425 /* svi_sm_next() flushed the cache. */
426 if (svi_line(sp, ep, ++t, NULL, NULL))
427 return (1);
428 if (t == TMAP)
429 break;
432 return (0);
436 * svi_sm_up --
437 * Scroll the SMAP up count logical lines.
440 svi_sm_up(sp, ep, rp, count, cursor_move)
441 SCR *sp;
442 EXF *ep;
443 MARK *rp;
444 recno_t count;
445 int cursor_move;
447 SMAP *p, svmap, tmp;
448 int ignore_cursor;
450 /* Set the default return position. */
451 rp->lno = sp->lno;
452 rp->cno = sp->cno;
455 * Invalidate the cursor. The line is probably going to change,
456 * but if cursor_move isn't set it may not. In any case, this
457 * routine moves the cursor to draw things.
459 F_SET(SVP(sp), SVI_CUR_INVALID);
462 * There are two forms of this command, one where the cursor tries to
463 * follow the line, and one where it doesn't. In the latter, we try
464 * and keep the cursor at the same position on the screen, but, if the
465 * screen is small enough and the line length large enough, the cursor
466 * can end up in very strange places. Probably not worth fixing.
468 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
469 * screen.
471 if (svi_sm_cursor(sp, ep, &p))
472 return (1);
473 if (p == NULL)
474 ignore_cursor = 1;
475 else {
476 svmap = *p;
477 ignore_cursor = 0;
481 * Check to see if movement is possible. Lots of checks...
483 * Find out if it's possible to move past the end of the map. If
484 * that's okay because we think that we can move the cursor down
485 * in the map, check to make sure that the map isn't mostly empty.
487 if (svi_sm_next(sp, ep, TMAP, &tmp))
488 return (1);
489 if (tmp.lno > TMAP->lno &&
490 !file_gline(sp, ep, tmp.lno, NULL) ||
491 tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
492 if (!cursor_move || ignore_cursor || p == TMAP) {
493 v_eof(sp, ep, NULL);
494 return (1);
496 if (svi_sm_next(sp, ep, p, &tmp))
497 return (1);
498 if (!file_gline(sp, ep, tmp.lno, NULL) ||
499 tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
500 v_eof(sp, ep, NULL);
501 return (1);
506 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
508 * If it's a small screen, and the movement is small, open up the
509 * screen. Otherwise, compress and repaint. If we compress, we
510 * ignore the cursor, the movement is too large to care.
512 if (ISSMALLSCREEN(sp))
513 if (count <= HALFTEXT(sp)) {
514 for (; count && sp->t_rows != sp->t_maxrows;
515 --count, ++sp->t_rows) {
516 if (svi_sm_next(sp, ep, TMAP, &tmp))
517 return (1);
518 if (TMAP->lno != tmp.lno &&
519 !file_gline(sp, ep, tmp.lno, NULL))
520 break;
521 *++TMAP = tmp;
522 /* svi_sm_next() flushed the cache. */
523 if (svi_line(sp, ep, TMAP, NULL, NULL))
524 return (1);
526 if (count == 0)
527 return (0);
528 } else {
529 MOVE(sp, INFOLINE(sp), 0);
530 clrtoeol();
531 for (;
532 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
533 MOVE(sp, TMAP - HMAP, 0);
534 clrtoeol();
536 ignore_cursor = 1;
539 for (; count; --count) {
540 /* Decide what would show up on the screen. */
541 if (svi_sm_next(sp, ep, TMAP, &tmp))
542 return (1);
544 /* If the line doesn't exist, we're done. */
545 if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL))
546 break;
548 /* Scroll the screen cursor up one logical line. */
549 if (svi_sm_1up(sp, ep))
550 return (1);
551 if (!cursor_move && !ignore_cursor && p > HMAP)
552 --p;
555 /* If ignoring the cursor, we're done. */
556 if (ignore_cursor)
557 return (0);
559 if (cursor_move) {
561 * If we didn't move enough, head toward EOF. Check to make
562 * sure the lines actually, if the file is smaller than the
563 * screen they may not.
565 for (; count; --count, ++p)
566 if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL))
567 break;
568 } else {
570 * If the line itself moved, invalidate the cursor, because
571 * the comparison with the old line/new line won't be right
573 F_SET(SVP(sp), SVI_CUR_INVALID);
575 /* If didn't move enough, it's an error. */
576 if (count) {
577 v_eof(sp, ep, NULL);
578 return (1);
581 /* If the cursor moved off the screen, move it to the top. */
582 if (sp->lno < HMAP->lno)
583 p = HMAP;
586 * On a logical movement, we try and keep the cursor as close as
587 * possible to the last position, but also set it up so that the
588 * next "real" movement will return the cursor to the closest position
589 * to the last real movement.
591 if (p->lno != svmap.lno || p->off != svmap.off) {
592 rp->lno = p->lno;
593 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
595 return (0);
599 * svi_sm_1up --
600 * Scroll the SMAP up one.
603 svi_sm_1up(sp, ep)
604 SCR *sp;
605 EXF *ep;
608 * Delete the top line of the screen. Shift the screen map up.
609 * Display a new line at the bottom of the screen.
611 MOVE(sp, 0, 0);
612 if (svi_deleteln(sp, 1))
613 return (1);
615 /* One-line screens can fail. */
616 if (HMAP == TMAP) {
617 if (svi_sm_next(sp, ep, TMAP, TMAP))
618 return (1);
619 } else {
620 memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
621 if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
622 return (1);
624 /* svi_sm_next() flushed the cache. */
625 if (svi_line(sp, ep, TMAP, NULL, NULL))
626 return (1);
627 return (0);
631 * svi_deleteln --
632 * Delete a line a la curses, make sure to put the information
633 * line and other screens back.
635 static int
636 svi_deleteln(sp, cnt)
637 SCR *sp;
638 int cnt;
640 size_t oldy, oldx;
642 getyx(stdscr, oldy, oldx);
643 while (cnt--) {
644 deleteln();
645 MOVE(sp, INFOLINE(sp) - 1, 0);
646 insertln();
647 MOVEA(sp, oldy, oldx);
649 return (0);
653 * svi_sm_down --
654 * Scroll the SMAP down count logical lines.
657 svi_sm_down(sp, ep, rp, count, cursor_move)
658 SCR *sp;
659 EXF *ep;
660 MARK *rp;
661 recno_t count;
662 int cursor_move;
664 SMAP *p, svmap;
665 int ignore_cursor;
667 /* Set the default return position. */
668 rp->lno = sp->lno;
669 rp->cno = sp->cno;
672 * Invalidate the cursor. The line is probably going to change,
673 * but if cursor_move isn't set it may not. In any case, this
674 * routine moves the cursor to draw things.
676 F_SET(SVP(sp), SVI_CUR_INVALID);
679 * There are two forms of this command, one where the cursor tries to
680 * follow the line, and one where it doesn't. In the latter, we try
681 * and keep the cursor at the same position on the screen, but, if the
682 * screen is small enough and the line length large enough, the cursor
683 * can end up in very strange places. Probably not worth fixing.
685 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
686 * screen.
688 if (svi_sm_cursor(sp, ep, &p))
689 return (1);
690 if (p == NULL)
691 ignore_cursor = 1;
692 else {
693 svmap = *p;
694 ignore_cursor = 0;
697 /* Check to see if movement is possible. */
698 if (HMAP->lno == 1 && HMAP->off == 1 &&
699 (!cursor_move || ignore_cursor || p == HMAP)) {
700 v_sof(sp, NULL);
701 return (1);
705 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
707 * If it's a small screen, and the movement is small, open up the
708 * screen. Otherwise, compress and repaint. If we compress, we
709 * ignore the cursor, the movement is too large to care.
711 if (ISSMALLSCREEN(sp))
712 if (count <= HALFTEXT(sp)) {
713 for (; count && sp->t_rows != sp->t_maxrows &&
714 (HMAP->lno > 1 || HMAP->off > 1);
715 --count, ++sp->t_rows) {
716 ++TMAP;
717 if (svi_sm_1down(sp, ep))
718 return (1);
719 if (!cursor_move)
720 ++p;
722 if (count == 0)
723 return (0);
724 } else {
725 MOVE(sp, INFOLINE(sp), 0);
726 clrtoeol();
727 for (;
728 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
729 MOVE(sp, TMAP - HMAP, 0);
730 clrtoeol();
732 ignore_cursor = 1;
735 for (; count; --count) {
736 /* If the line doesn't exist, we're done. */
737 if (HMAP->lno == 1 && HMAP->off == 1)
738 break;
740 /* Scroll the screen and cursor down one logical line. */
741 if (svi_sm_1down(sp, ep))
742 return (1);
743 if (!cursor_move && !ignore_cursor && p < TMAP)
744 ++p;
747 /* If ignoring the cursor, we're done. */
748 if (ignore_cursor)
749 return (0);
751 if (cursor_move) {
752 /* If we didn't move enough, move to SOF. */
753 if (count)
754 p = HMAP;
755 } else {
757 * If the line itself moved, invalidate the cursor, because
758 * the comparison with the old line/new line won't be right.
760 F_SET(SVP(sp), SVI_CUR_INVALID);
762 /* If didn't move enough, it's an error. */
763 if (count) {
764 v_sof(sp, NULL);
765 return (1);
768 /* If the cursor moved off the screen, move it to the bottom. */
769 if (sp->lno > TMAP->lno)
770 p = TMAP;
774 * On a logical movement, we try and keep the cursor as close as
775 * possible to the last position, but also set it up so that the
776 * next "real" movement will return the cursor to the closest position
777 * to the last real movement.
779 if (p->lno != svmap.lno || p->off != svmap.off) {
780 rp->lno = p->lno;
781 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
783 return (0);
787 * svi_sm_1down --
788 * Scroll the SMAP down one.
791 svi_sm_1down(sp, ep)
792 SCR *sp;
793 EXF *ep;
796 * Clear the bottom line of the screen, insert a line at the top
797 * of the screen. Shift the screen map down, display a new line
798 * at the top of the screen.
800 MOVE(sp, sp->t_rows, 0);
801 clrtoeol();
802 MOVE(sp, 0, 0);
803 if (svi_insertln(sp, 1))
804 return (1);
805 memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
806 if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
807 return (1);
808 /* svi_sm_prev() flushed the cache. */
809 if (svi_line(sp, ep, HMAP, NULL, NULL))
810 return (1);
811 return (0);
815 * svi_insertln --
816 * Insert a line a la curses, make sure to put the information
817 * line and other screens back.
819 static int
820 svi_insertln(sp, cnt)
821 SCR *sp;
822 int cnt;
824 size_t oldy, oldx;
826 getyx(stdscr, oldy, oldx);
827 while (cnt--) {
828 MOVE(sp, INFOLINE(sp) - 1, 0);
829 deleteln();
830 MOVEA(sp, oldy, oldx);
831 insertln();
833 return (0);
837 * svi_sm_next --
838 * Fill in the next entry in the SMAP.
841 svi_sm_next(sp, ep, p, t)
842 SCR *sp;
843 EXF *ep;
844 SMAP *p, *t;
846 size_t lcnt;
848 SMAP_FLUSH(t);
849 if (O_ISSET(sp, O_LEFTRIGHT)) {
850 t->lno = p->lno + 1;
851 t->off = p->off;
852 } else {
853 lcnt = svi_screens(sp, ep, p->lno, NULL);
854 if (lcnt == p->off) {
855 t->lno = p->lno + 1;
856 t->off = 1;
857 } else {
858 t->lno = p->lno;
859 t->off = p->off + 1;
862 return (0);
866 * svi_sm_prev --
867 * Fill in the previous entry in the SMAP.
870 svi_sm_prev(sp, ep, p, t)
871 SCR *sp;
872 EXF *ep;
873 SMAP *p, *t;
875 SMAP_FLUSH(t);
876 if (O_ISSET(sp, O_LEFTRIGHT)) {
877 t->lno = p->lno - 1;
878 t->off = p->off;
879 } else if (p->off != 1) {
880 t->lno = p->lno;
881 t->off = p->off - 1;
882 } else {
883 t->lno = p->lno - 1;
884 t->off = svi_screens(sp, ep, t->lno, NULL);
886 return (t->lno == 0);
890 * svi_sm_cursor --
891 * Return the SMAP entry referenced by the cursor.
894 svi_sm_cursor(sp, ep, smp)
895 SCR *sp;
896 EXF *ep;
897 SMAP **smp;
899 SMAP *p;
901 /* See if the cursor is not in the map. */
902 if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) {
903 *smp = NULL;
904 return (0);
907 /* Find the first occurence of the line. */
908 for (p = HMAP; p->lno != sp->lno; ++p);
910 /* Fill in the map information until we find the right line. */
911 for (; p <= TMAP; ++p) {
912 /* Short lines are common and easy to detect. */
913 if (p != TMAP && (p + 1)->lno != p->lno) {
914 *smp = p;
915 return (0);
917 if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
918 return (1);
919 if (p->c_eboff >= sp->cno) {
920 *smp = p;
921 return (0);
925 /* It was past the end of the map after all. */
926 *smp = NULL;
927 return (0);
931 * svi_sm_position --
932 * Return the line/column of the top, middle or last line on the screen.
933 * (The vi H, M and L commands.) Here because only the screen routines
934 * know what's really out there.
937 svi_sm_position(sp, ep, rp, cnt, pos)
938 SCR *sp;
939 EXF *ep;
940 MARK *rp;
941 u_long cnt;
942 enum position pos;
944 SMAP *smp;
945 recno_t last;
947 switch (pos) {
948 case P_TOP:
949 if (cnt > TMAP - HMAP)
950 goto err;
951 smp = HMAP + cnt;
952 break;
953 case P_MIDDLE:
954 if (cnt > (TMAP - HMAP) / 2)
955 goto err;
956 smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
957 goto eof;
958 case P_BOTTOM:
959 if (cnt > TMAP - HMAP) {
960 err: msgq(sp, M_BERR, "Movement past the end-of-screen.");
961 return (1);
963 smp = TMAP - cnt;
964 eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
965 if (file_lline(sp, ep, &last))
966 return (1);
967 for (; smp->lno > last && smp > HMAP; --smp);
969 break;
970 default:
971 abort();
974 if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
975 return (1);
976 rp->lno = smp->lno;
977 rp->cno = smp->c_sboff;
979 return (0);
983 * svi_sm_nlines --
984 * Return the number of screen lines from an SMAP entry to the
985 * start of some file line, less than a maximum value.
987 recno_t
988 svi_sm_nlines(sp, ep, from_sp, to_lno, max)
989 SCR *sp;
990 EXF *ep;
991 SMAP *from_sp;
992 recno_t to_lno;
993 size_t max;
995 recno_t lno, lcnt;
997 if (O_ISSET(sp, O_LEFTRIGHT))
998 return (from_sp->lno > to_lno ?
999 from_sp->lno - to_lno : to_lno - from_sp->lno);
1001 if (from_sp->lno == to_lno)
1002 return (from_sp->off - 1);
1004 if (from_sp->lno > to_lno) {
1005 lcnt = from_sp->off - 1; /* Correct for off-by-one. */
1006 for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
1007 lcnt += svi_screens(sp, ep, lno, NULL);
1008 } else {
1009 lno = from_sp->lno;
1010 lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
1011 for (; ++lno < to_lno && lcnt <= max;)
1012 lcnt += svi_screens(sp, ep, lno, NULL);
1014 return (lcnt);