when lines are inserted or deleted, the rest of the lines have
[nvi.git] / vi / vs_smap.c
bloba68a8be4e328cce4c2c68d7433ccdd5496bc2f6d
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.28 1993/11/22 17:30:13 bostic Exp $ (Berkeley) $Date: 1993/11/22 17:30:13 $";
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 if (svi_line(sp, ep, ++p, NULL, NULL))
278 return (1);
279 if (p == TMAP)
280 break;
282 return (0);
286 * svi_sm_insert --
287 * Insert a line into the SMAP.
289 static int
290 svi_sm_insert(sp, ep, lno)
291 SCR *sp;
292 EXF *ep;
293 recno_t lno;
295 SMAP *p, *t;
296 size_t cnt_orig, cnt;
299 * Find the line in the map, find out how many screen lines
300 * needed to display the line.
302 for (p = HMAP; p->lno != lno; ++p);
303 cnt_orig = svi_screens(sp, ep, lno, NULL);
305 TOO_WEIRD;
308 * The lines left in the screen override the number of screen
309 * lines in the inserted line.
311 cnt = (TMAP - p) + 1;
312 if (cnt_orig > cnt)
313 cnt_orig = cnt;
315 /* Push down that many lines. */
316 MOVE(sp, p - HMAP, 0);
317 if (svi_insertln(sp, cnt_orig))
318 return (1);
320 /* Shift the screen map down. */
321 memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
323 /* Increment the line numbers for the rest of the map. */
324 for (t = p + cnt_orig; t <= TMAP; ++t)
325 ++t->lno;
327 /* Fill in the SMAP for the new lines, and display. */
328 for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
329 t->lno = lno;
330 t->off = cnt;
331 SMAP_FLUSH(t);
332 if (svi_line(sp, ep, t, NULL, NULL))
333 return (1);
335 return (0);
339 * svi_sm_reset --
340 * Reset a line in the SMAP.
342 static int
343 svi_sm_reset(sp, ep, lno)
344 SCR *sp;
345 EXF *ep;
346 recno_t lno;
348 SMAP *p, *t;
349 size_t cnt_orig, cnt_new, cnt, diff;
352 * See if the number of on-screen rows taken up by the old display
353 * for the line is the same as the number needed for the new one.
354 * If so, repaint, otherwise do it the hard way.
356 for (p = HMAP; p->lno != lno; ++p);
357 for (cnt_orig = 0, t = p;
358 t <= TMAP && t->lno == lno; ++cnt_orig, ++t)
359 SMAP_FLUSH(t);
360 cnt_new = svi_screens(sp, ep, lno, NULL);
362 TOO_WEIRD;
364 if (cnt_orig == cnt_new) {
365 do {
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 if (svi_line(sp, ep, t, NULL, NULL))
397 return (1);
399 } else {
400 /* Get the difference. */
401 diff = cnt_orig - cnt_new;
403 /* Delete that many lines from the screen. */
404 MOVE(sp, p - HMAP, 0);
405 if (svi_deleteln(sp, diff))
406 return (1);
408 /* Shift the screen map up. */
409 memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
411 /* Fill in the SMAP for the replaced line, and display. */
412 for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
413 t->lno = lno;
414 t->off = cnt;
415 if (svi_line(sp, ep, t, NULL, NULL))
416 return (1);
419 /* Display the new lines at the bottom of the screen. */
420 for (t = TMAP - diff;;) {
421 if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
422 return (1);
423 if (svi_line(sp, ep, ++t, NULL, NULL))
424 return (1);
425 if (t == TMAP)
426 break;
429 return (0);
433 * svi_sm_up --
434 * Scroll the SMAP up count logical lines.
437 svi_sm_up(sp, ep, rp, count, cursor_move)
438 SCR *sp;
439 EXF *ep;
440 MARK *rp;
441 recno_t count;
442 int cursor_move;
444 SMAP *p, svmap, tmp;
445 int ignore_cursor;
447 /* Set the default return position. */
448 rp->lno = sp->lno;
449 rp->cno = sp->cno;
452 * Invalidate the cursor. The line is probably going to change,
453 * but if cursor_move isn't set it may not. In any case, this
454 * routine moves the cursor to draw things.
456 F_SET(SVP(sp), SVI_CUR_INVALID);
459 * There are two forms of this command, one where the cursor tries to
460 * follow the line, and one where it doesn't. In the latter, we try
461 * and keep the cursor at the same position on the screen, but, if the
462 * screen is small enough and the line length large enough, the cursor
463 * can end up in very strange places. Probably not worth fixing.
465 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
466 * screen.
468 if (svi_sm_cursor(sp, ep, &p))
469 return (1);
470 if (p == NULL)
471 ignore_cursor = 1;
472 else {
473 svmap = *p;
474 ignore_cursor = 0;
478 * Check to see if movement is possible. Lots of checks...
480 * Find out if it's possible to move past the end of the map. If
481 * that's okay because we think that we can move the cursor down
482 * in the map, check to make sure that the map isn't mostly empty.
484 if (svi_sm_next(sp, ep, TMAP, &tmp))
485 return (1);
486 if (tmp.lno > TMAP->lno &&
487 !file_gline(sp, ep, tmp.lno, NULL) ||
488 tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
489 if (!cursor_move || ignore_cursor || p == TMAP) {
490 v_eof(sp, ep, NULL);
491 return (1);
493 if (svi_sm_next(sp, ep, p, &tmp))
494 return (1);
495 if (!file_gline(sp, ep, tmp.lno, NULL) ||
496 tmp.off > svi_screens(sp, ep, tmp.lno, NULL)) {
497 v_eof(sp, ep, NULL);
498 return (1);
503 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
505 * If it's a small screen, and the movement is small, open up the
506 * screen. Otherwise, compress and repaint. If we compress, we
507 * ignore the cursor, the movement is too large to care.
509 if (ISSMALLSCREEN(sp))
510 if (count <= HALFTEXT(sp)) {
511 for (; count && sp->t_rows != sp->t_maxrows;
512 --count, ++sp->t_rows) {
513 if (svi_sm_next(sp, ep, TMAP, &tmp))
514 return (1);
515 if (TMAP->lno != tmp.lno &&
516 !file_gline(sp, ep, tmp.lno, NULL))
517 break;
518 *++TMAP = tmp;
519 if (svi_line(sp, ep, TMAP, NULL, NULL))
520 return (1);
522 if (count == 0)
523 return (0);
524 } else {
525 MOVE(sp, INFOLINE(sp), 0);
526 clrtoeol();
527 for (;
528 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
529 MOVE(sp, TMAP - HMAP, 0);
530 clrtoeol();
532 ignore_cursor = 1;
535 for (; count; --count) {
536 /* Decide what would show up on the screen. */
537 if (svi_sm_next(sp, ep, TMAP, &tmp))
538 return (1);
540 /* If the line doesn't exist, we're done. */
541 if (TMAP->lno != tmp.lno && !file_gline(sp, ep, tmp.lno, NULL))
542 break;
544 /* Scroll the screen cursor up one logical line. */
545 if (svi_sm_1up(sp, ep))
546 return (1);
547 if (!cursor_move && !ignore_cursor && p > HMAP)
548 --p;
551 /* If ignoring the cursor, we're done. */
552 if (ignore_cursor)
553 return (0);
555 if (cursor_move) {
557 * If we didn't move enough, head toward EOF. Check to make
558 * sure the lines actually, if the file is smaller than the
559 * screen they may not.
561 for (; count; --count, ++p)
562 if (p == TMAP || !file_gline(sp, ep, p[1].lno, NULL))
563 break;
564 } else {
566 * If the line itself moved, invalidate the cursor, because
567 * the comparison with the old line/new line won't be right
569 F_SET(SVP(sp), SVI_CUR_INVALID);
571 /* If didn't move enough, it's an error. */
572 if (count) {
573 v_eof(sp, ep, NULL);
574 return (1);
577 /* If the cursor moved off the screen, move it to the top. */
578 if (sp->lno < HMAP->lno)
579 p = HMAP;
582 * On a logical movement, we try and keep the cursor as close as
583 * possible to the last position, but also set it up so that the
584 * next "real" movement will return the cursor to the closest position
585 * to the last real movement.
587 if (p->lno != svmap.lno || p->off != svmap.off) {
588 rp->lno = p->lno;
589 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
591 return (0);
595 * svi_sm_1up --
596 * Scroll the SMAP up one.
599 svi_sm_1up(sp, ep)
600 SCR *sp;
601 EXF *ep;
604 * Delete the top line of the screen. Shift the screen map up.
605 * Display a new line at the bottom of the screen.
607 MOVE(sp, 0, 0);
608 if (svi_deleteln(sp, 1))
609 return (1);
611 /* One-line screens can fail. */
612 if (HMAP == TMAP) {
613 if (svi_sm_next(sp, ep, TMAP, TMAP))
614 return (1);
615 } else {
616 memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
617 if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
618 return (1);
620 if (svi_line(sp, ep, TMAP, NULL, NULL))
621 return (1);
622 return (0);
626 * svi_deleteln --
627 * Delete a line a la curses, make sure to put the information
628 * line and other screens back.
630 static int
631 svi_deleteln(sp, cnt)
632 SCR *sp;
633 int cnt;
635 size_t oldy, oldx;
637 getyx(stdscr, oldy, oldx);
638 while (cnt--) {
639 deleteln();
640 MOVE(sp, INFOLINE(sp) - 1, 0);
641 insertln();
642 MOVEA(sp, oldy, oldx);
644 return (0);
648 * svi_sm_down --
649 * Scroll the SMAP down count logical lines.
652 svi_sm_down(sp, ep, rp, count, cursor_move)
653 SCR *sp;
654 EXF *ep;
655 MARK *rp;
656 recno_t count;
657 int cursor_move;
659 SMAP *p, svmap;
660 int ignore_cursor;
662 /* Set the default return position. */
663 rp->lno = sp->lno;
664 rp->cno = sp->cno;
667 * Invalidate the cursor. The line is probably going to change,
668 * but if cursor_move isn't set it may not. In any case, this
669 * routine moves the cursor to draw things.
671 F_SET(SVP(sp), SVI_CUR_INVALID);
674 * There are two forms of this command, one where the cursor tries to
675 * follow the line, and one where it doesn't. In the latter, we try
676 * and keep the cursor at the same position on the screen, but, if the
677 * screen is small enough and the line length large enough, the cursor
678 * can end up in very strange places. Probably not worth fixing.
680 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
681 * screen.
683 if (svi_sm_cursor(sp, ep, &p))
684 return (1);
685 if (p == NULL)
686 ignore_cursor = 1;
687 else {
688 svmap = *p;
689 ignore_cursor = 0;
692 /* Check to see if movement is possible. */
693 if (HMAP->lno == 1 && HMAP->off == 1 &&
694 (!cursor_move || ignore_cursor || p == HMAP)) {
695 v_sof(sp, NULL);
696 return (1);
700 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
702 * If it's a small screen, and the movement is small, open up the
703 * screen. Otherwise, compress and repaint. If we compress, we
704 * ignore the cursor, the movement is too large to care.
706 if (ISSMALLSCREEN(sp))
707 if (count <= HALFTEXT(sp)) {
708 for (; count && sp->t_rows != sp->t_maxrows &&
709 (HMAP->lno > 1 || HMAP->off > 1);
710 --count, ++sp->t_rows) {
711 ++TMAP;
712 if (svi_sm_1down(sp, ep))
713 return (1);
714 if (!cursor_move)
715 ++p;
717 if (count == 0)
718 return (0);
719 } else {
720 MOVE(sp, INFOLINE(sp), 0);
721 clrtoeol();
722 for (;
723 sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
724 MOVE(sp, TMAP - HMAP, 0);
725 clrtoeol();
727 ignore_cursor = 1;
730 for (; count; --count) {
731 /* If the line doesn't exist, we're done. */
732 if (HMAP->lno == 1 && HMAP->off == 1)
733 break;
735 /* Scroll the screen and cursor down one logical line. */
736 if (svi_sm_1down(sp, ep))
737 return (1);
738 if (!cursor_move && !ignore_cursor && p < TMAP)
739 ++p;
742 /* If ignoring the cursor, we're done. */
743 if (ignore_cursor)
744 return (0);
746 if (cursor_move) {
747 /* If we didn't move enough, move to SOF. */
748 if (count)
749 p = HMAP;
750 } else {
752 * If the line itself moved, invalidate the cursor, because
753 * the comparison with the old line/new line won't be right.
755 F_SET(SVP(sp), SVI_CUR_INVALID);
757 /* If didn't move enough, it's an error. */
758 if (count) {
759 v_sof(sp, NULL);
760 return (1);
763 /* If the cursor moved off the screen, move it to the bottom. */
764 if (sp->lno > TMAP->lno)
765 p = TMAP;
769 * On a logical movement, we try and keep the cursor as close as
770 * possible to the last position, but also set it up so that the
771 * next "real" movement will return the cursor to the closest position
772 * to the last real movement.
774 if (p->lno != svmap.lno || p->off != svmap.off) {
775 rp->lno = p->lno;
776 rp->cno = svi_lrelative(sp, ep, p->lno, p->off);
778 return (0);
782 * svi_sm_1down --
783 * Scroll the SMAP down one.
786 svi_sm_1down(sp, ep)
787 SCR *sp;
788 EXF *ep;
791 * Clear the bottom line of the screen, insert a line at the top
792 * of the screen. Shift the screen map down, display a new line
793 * at the top of the screen.
795 MOVE(sp, sp->t_rows, 0);
796 clrtoeol();
797 MOVE(sp, 0, 0);
798 if (svi_insertln(sp, 1))
799 return (1);
800 memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
801 if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
802 return (1);
803 if (svi_line(sp, ep, HMAP, NULL, NULL))
804 return (1);
805 return (0);
809 * svi_insertln --
810 * Insert a line a la curses, make sure to put the information
811 * line and other screens back.
813 static int
814 svi_insertln(sp, cnt)
815 SCR *sp;
816 int cnt;
818 size_t oldy, oldx;
820 getyx(stdscr, oldy, oldx);
821 while (cnt--) {
822 MOVE(sp, INFOLINE(sp) - 1, 0);
823 deleteln();
824 MOVEA(sp, oldy, oldx);
825 insertln();
827 return (0);
831 * svi_sm_next --
832 * Fill in the next entry in the SMAP.
835 svi_sm_next(sp, ep, p, t)
836 SCR *sp;
837 EXF *ep;
838 SMAP *p, *t;
840 size_t lcnt;
842 SMAP_FLUSH(t);
843 if (O_ISSET(sp, O_LEFTRIGHT)) {
844 t->lno = p->lno + 1;
845 t->off = p->off;
846 } else {
847 lcnt = svi_screens(sp, ep, p->lno, NULL);
848 if (lcnt == p->off) {
849 t->lno = p->lno + 1;
850 t->off = 1;
851 } else {
852 t->lno = p->lno;
853 t->off = p->off + 1;
856 return (0);
860 * svi_sm_prev --
861 * Fill in the previous entry in the SMAP.
864 svi_sm_prev(sp, ep, p, t)
865 SCR *sp;
866 EXF *ep;
867 SMAP *p, *t;
869 SMAP_FLUSH(t);
870 if (O_ISSET(sp, O_LEFTRIGHT)) {
871 t->lno = p->lno - 1;
872 t->off = p->off;
873 } else if (p->off != 1) {
874 t->lno = p->lno;
875 t->off = p->off - 1;
876 } else {
877 t->lno = p->lno - 1;
878 t->off = svi_screens(sp, ep, t->lno, NULL);
880 return (t->lno == 0);
884 * svi_sm_cursor --
885 * Return the SMAP entry referenced by the cursor.
888 svi_sm_cursor(sp, ep, smp)
889 SCR *sp;
890 EXF *ep;
891 SMAP **smp;
893 SMAP *p;
895 /* See if the cursor is not in the map. */
896 if (sp->lno < HMAP->lno || sp->lno > TMAP->lno) {
897 *smp = NULL;
898 return (0);
901 /* Find the first occurence of the line. */
902 for (p = HMAP; p->lno != sp->lno; ++p);
904 /* Fill in the map information until we find the right line. */
905 for (; p <= TMAP; ++p) {
906 /* Short lines are common and easy to detect. */
907 if (p != TMAP && (p + 1)->lno != p->lno) {
908 *smp = p;
909 return (0);
911 if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
912 return (1);
913 if (p->c_eboff >= sp->cno) {
914 *smp = p;
915 return (0);
919 /* It was past the end of the map after all. */
920 *smp = NULL;
921 return (0);
925 * svi_sm_position --
926 * Return the line/column of the top, middle or last line on the screen.
927 * (The vi H, M and L commands.) Here because only the screen routines
928 * know what's really out there.
931 svi_sm_position(sp, ep, rp, cnt, pos)
932 SCR *sp;
933 EXF *ep;
934 MARK *rp;
935 u_long cnt;
936 enum position pos;
938 SMAP *smp;
939 recno_t last;
941 switch (pos) {
942 case P_TOP:
943 if (cnt > TMAP - HMAP)
944 goto err;
945 smp = HMAP + cnt;
946 break;
947 case P_MIDDLE:
948 if (cnt > (TMAP - HMAP) / 2)
949 goto err;
950 smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
951 goto eof;
952 case P_BOTTOM:
953 if (cnt > TMAP - HMAP) {
954 err: msgq(sp, M_BERR, "Movement past the end-of-screen.");
955 return (1);
957 smp = TMAP - cnt;
958 eof: if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
959 if (file_lline(sp, ep, &last))
960 return (1);
961 for (; smp->lno > last && smp > HMAP; --smp);
963 break;
964 default:
965 abort();
968 if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
969 return (1);
970 rp->lno = smp->lno;
971 rp->cno = smp->c_sboff;
973 return (0);
977 * svi_sm_nlines --
978 * Return the number of screen lines from an SMAP entry to the
979 * start of some file line, less than a maximum value.
981 recno_t
982 svi_sm_nlines(sp, ep, from_sp, to_lno, max)
983 SCR *sp;
984 EXF *ep;
985 SMAP *from_sp;
986 recno_t to_lno;
987 size_t max;
989 recno_t lno, lcnt;
991 if (O_ISSET(sp, O_LEFTRIGHT))
992 return (from_sp->lno > to_lno ?
993 from_sp->lno - to_lno : to_lno - from_sp->lno);
995 if (from_sp->lno == to_lno)
996 return (from_sp->off - 1);
998 if (from_sp->lno > to_lno) {
999 lcnt = from_sp->off - 1; /* Correct for off-by-one. */
1000 for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
1001 lcnt += svi_screens(sp, ep, lno, NULL);
1002 } else {
1003 lno = from_sp->lno;
1004 lcnt = (svi_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
1005 for (; ++lno < to_lno && lcnt <= max;)
1006 lcnt += svi_screens(sp, ep, lno, NULL);
1008 return (lcnt);